1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* This is a test program for the string_map_* routines defined in
18 string-map.c. This test program aims to be as comprehensive as possible.
19 "gcov -a -b" should report almost complete coverage of lines, blocks and
20 branches in string-map.c, except that one branch caused by hash collision is
21 not exercised because our hash function has so few collisions. "valgrind
22 --leak-check=yes --show-reachable=yes" should give a clean report. */
28 #include <libpspp/string-map.h>
39 #include <libpspp/hash-functions.h>
40 #include <libpspp/compiler.h>
41 #include <libpspp/string-set.h>
43 /* Exit with a failure code.
44 (Place a breakpoint on this function while debugging.) */
51 /* If OK is not true, prints a message about failure on the
52 current source file and the given LINE and terminates. */
54 check_func (bool ok
, int line
)
58 fprintf (stderr
, "%s:%d: check failed\n", __FILE__
, line
);
63 /* Verifies that EXPR evaluates to true.
64 If not, prints a message citing the calling line number and
66 #define check(EXPR) check_func ((EXPR), __LINE__)
68 /* Prints a message about memory exhaustion and exits with a
73 printf ("virtual memory exhausted\n");
77 static void *xmalloc (size_t n
) MALLOC_LIKE
;
78 static void *xnmalloc (size_t n
, size_t m
) MALLOC_LIKE
;
79 static void *xmemdup (const void *p
, size_t n
) MALLOC_LIKE
;
81 /* Allocates and returns N bytes of memory. */
98 xmemdup (const void *p
, size_t n
)
100 void *q
= xmalloc (n
);
107 xstrdup (const char *string
)
109 return xmemdup (string
, strlen (string
) + 1);
112 /* Allocates and returns N * M bytes of memory. */
114 xnmalloc (size_t n
, size_t m
)
116 if ((size_t) -1 / m
<= n
)
118 return xmalloc (n
* m
);
121 /* Support routines. */
125 MAX_IDX
= 1 << IDX_BITS
,
126 KEY_MASK
= (MAX_IDX
- 1),
128 VALUE_MASK
= (MAX_IDX
- 1) << IDX_BITS
,
129 VALUE_SHIFT
= IDX_BITS
132 static char *string_table
[MAX_IDX
];
139 assert (idx
>= 0 && idx
< MAX_IDX
);
140 s
= &string_table
[idx
];
144 sprintf (*s
, "%d", idx
);
154 for (i
= 0; i
< MAX_IDX
; i
++)
155 free (string_table
[i
]);
161 return get_string ((value
& KEY_MASK
) >> KEY_SHIFT
);
165 make_value (int value
)
167 return get_string ((value
& VALUE_MASK
) >> VALUE_SHIFT
);
171 random_value (unsigned int seed
, int basis
)
173 return hash_int (seed
, basis
) & VALUE_MASK
;
176 /* Swaps *A and *B. */
178 swap (int *a
, int *b
)
185 /* Reverses the order of the CNT integers starting at VALUES. */
187 reverse (int *values
, size_t cnt
)
193 swap (&values
[i
++], &values
[--j
]);
196 /* Arranges the CNT elements in VALUES into the lexicographically next greater
197 permutation. Returns true if successful. If VALUES is already the
198 lexicographically greatest permutation of its elements (i.e. ordered from
199 greatest to smallest), arranges them into the lexicographically least
200 permutation (i.e. ordered from smallest to largest) and returns false.
202 Comparisons among elements of VALUES consider only the bits in KEY_MASK. */
204 next_permutation (int *values
, size_t cnt
)
212 if ((values
[i
] & KEY_MASK
) < (values
[i
+ 1] & KEY_MASK
))
216 (values
[i
] & KEY_MASK
) >= (values
[j
] & KEY_MASK
);
219 swap (values
+ i
, values
+ j
);
220 reverse (values
+ (i
+ 1), cnt
- (i
+ 1));
225 reverse (values
, cnt
);
233 factorial (unsigned int n
)
235 unsigned int value
= 1;
241 /* Randomly shuffles the CNT elements in ARRAY, each of which is
242 SIZE bytes in size. */
244 random_shuffle (void *array_
, size_t cnt
, size_t size
)
246 char *array
= array_
;
247 char *tmp
= xmalloc (size
);
250 for (i
= 0; i
< cnt
; i
++)
252 size_t j
= rand () % (cnt
- i
) + i
;
255 memcpy (tmp
, array
+ j
* size
, size
);
256 memcpy (array
+ j
* size
, array
+ i
* size
, size
);
257 memcpy (array
+ i
* size
, tmp
, size
);
264 /* Checks that MAP contains the CNT strings in DATA, that its structure is
265 correct, and that certain operations on MAP produce the expected results. */
267 check_string_map (struct string_map
*map
, const int data
[], size_t cnt
)
271 check (string_map_is_empty (map
) == (cnt
== 0));
272 check (string_map_count (map
) == cnt
);
274 for (i
= 0; i
< cnt
; i
++)
276 struct string_map_node
*node
;
277 const char *key
= make_key (data
[i
]);
278 const char *value
= make_value (data
[i
]);
279 const char *found_value
;
281 check (string_map_contains (map
, key
));
283 node
= string_map_find_node (map
, key
);
284 check (node
!= NULL
);
285 check (!strcmp (key
, string_map_node_get_key (node
)));
286 check (!strcmp (value
, string_map_node_get_value (node
)));
288 check (node
== string_map_insert (map
, key
, "abc"));
289 check (!strcmp (value
, string_map_node_get_value (node
)));
291 check (node
== string_map_insert_nocopy (map
, xstrdup (key
),
293 check (!strcmp (value
, string_map_node_get_value (node
)));
295 found_value
= string_map_find (map
, key
);
296 check (found_value
!= NULL
);
297 check (!strcmp (found_value
, value
));
300 check (!string_map_contains (map
, "xxx"));
301 check (string_map_find (map
, "z") == NULL
);
302 check (string_map_find_node (map
, "") == NULL
);
303 check (!string_map_delete (map
, "xyz"));
306 check (string_map_first (map
) == NULL
);
309 const struct string_map_node
*node
;
313 data_copy
= xmemdup (data
, cnt
* sizeof *data
);
315 for (node
= string_map_first (map
), i
= 0; i
< cnt
;
316 node
= string_map_next (map
, node
), i
++)
318 const char *key
= string_map_node_get_key (node
);
319 const char *value
= string_map_node_get_value (node
);
322 for (j
= 0; j
< left
; j
++)
323 if (!strcmp (key
, make_key (data_copy
[j
]))
324 || !strcmp (value
, make_value (data_copy
[j
])))
326 data_copy
[j
] = data_copy
[--left
];
333 check (node
== NULL
);
338 /* Inserts the CNT strings from 0 to CNT - 1 (inclusive) into a map in the
339 order specified by INSERTIONS, then deletes them in the order specified by
340 DELETIONS, checking the map's contents for correctness after each
343 test_insert_delete (const int insertions
[],
344 const int deletions
[],
347 struct string_map map
;
350 string_map_init (&map
);
351 check_string_map (&map
, NULL
, 0);
352 for (i
= 0; i
< cnt
; i
++)
354 check (string_map_insert (&map
, make_key (insertions
[i
]),
355 make_value (insertions
[i
])));
356 check_string_map (&map
, insertions
, i
+ 1);
358 for (i
= 0; i
< cnt
; i
++)
360 check (string_map_delete (&map
, make_key (deletions
[i
])));
361 check_string_map (&map
, deletions
+ i
+ 1, cnt
- i
- 1);
363 string_map_destroy (&map
);
366 /* Inserts strings into a map in each possible order, then removes them in each
367 possible order, up to a specified maximum size. */
369 test_insert_any_remove_any (void)
372 const int max_elems
= 5;
375 for (cnt
= 0; cnt
<= max_elems
; cnt
++)
377 int *insertions
, *deletions
;
378 unsigned int ins_perm_cnt
;
381 insertions
= xnmalloc (cnt
, sizeof *insertions
);
382 deletions
= xnmalloc (cnt
, sizeof *deletions
);
383 for (i
= 0; i
< cnt
; i
++)
384 insertions
[i
] = i
| random_value (i
, basis
);
386 for (ins_perm_cnt
= 0;
387 ins_perm_cnt
== 0 || next_permutation (insertions
, cnt
);
390 unsigned int del_perm_cnt
;
393 for (i
= 0; i
< cnt
; i
++)
394 deletions
[i
] = i
| random_value (i
, basis
);
396 for (del_perm_cnt
= 0;
397 del_perm_cnt
== 0 || next_permutation (deletions
, cnt
);
399 test_insert_delete (insertions
, deletions
, cnt
);
401 check (del_perm_cnt
== factorial (cnt
));
403 check (ins_perm_cnt
== factorial (cnt
));
410 /* Inserts strings into a map in each possible order, then removes them in the
411 same order, up to a specified maximum size. */
413 test_insert_any_remove_same (void)
415 const int max_elems
= 7;
418 for (cnt
= 0; cnt
<= max_elems
; cnt
++)
421 unsigned int permutation_cnt
;
424 values
= xnmalloc (cnt
, sizeof *values
);
425 for (i
= 0; i
< cnt
; i
++)
426 values
[i
] = i
| random_value (i
, 1);
428 for (permutation_cnt
= 0;
429 permutation_cnt
== 0 || next_permutation (values
, cnt
);
431 test_insert_delete (values
, values
, cnt
);
432 check (permutation_cnt
== factorial (cnt
));
438 /* Inserts strings into a map in each possible order, then
439 removes them in reverse order, up to a specified maximum
442 test_insert_any_remove_reverse (void)
444 const int max_elems
= 7;
447 for (cnt
= 0; cnt
<= max_elems
; cnt
++)
449 int *insertions
, *deletions
;
450 unsigned int permutation_cnt
;
453 insertions
= xnmalloc (cnt
, sizeof *insertions
);
454 deletions
= xnmalloc (cnt
, sizeof *deletions
);
455 for (i
= 0; i
< cnt
; i
++)
456 insertions
[i
] = i
| random_value (i
, 2);
458 for (permutation_cnt
= 0;
459 permutation_cnt
== 0 || next_permutation (insertions
, cnt
);
462 memcpy (deletions
, insertions
, sizeof *insertions
* cnt
);
463 reverse (deletions
, cnt
);
465 test_insert_delete (insertions
, deletions
, cnt
);
467 check (permutation_cnt
== factorial (cnt
));
474 /* Inserts and removes strings in a map, in random order. */
476 test_random_sequence (void)
479 const int max_elems
= 64;
480 const int max_trials
= 8;
483 for (cnt
= 0; cnt
<= max_elems
; cnt
+= 2)
485 int *insertions
, *deletions
;
489 insertions
= xnmalloc (cnt
, sizeof *insertions
);
490 deletions
= xnmalloc (cnt
, sizeof *deletions
);
491 for (i
= 0; i
< cnt
; i
++)
492 insertions
[i
] = i
| random_value (i
, basis
);
493 for (i
= 0; i
< cnt
; i
++)
494 deletions
[i
] = i
| random_value (i
, basis
);
496 for (trial
= 0; trial
< max_trials
; trial
++)
498 random_shuffle (insertions
, cnt
, sizeof *insertions
);
499 random_shuffle (deletions
, cnt
, sizeof *deletions
);
501 test_insert_delete (insertions
, deletions
, cnt
);
509 /* Inserts strings into a map in ascending order, then delete in ascending
512 test_insert_ordered (void)
514 const int max_elems
= 64;
516 struct string_map map
;
519 string_map_init (&map
);
520 values
= xnmalloc (max_elems
, sizeof *values
);
521 for (i
= 0; i
< max_elems
; i
++)
523 values
[i
] = i
| random_value (i
, 4);
524 string_map_insert_nocopy (&map
, xstrdup (make_key (values
[i
])),
525 xstrdup (make_value (values
[i
])));
526 check_string_map (&map
, values
, i
+ 1);
528 for (i
= 0; i
< max_elems
; i
++)
530 string_map_delete (&map
, make_key (i
));
531 check_string_map (&map
, values
+ i
+ 1, max_elems
- i
- 1);
533 string_map_destroy (&map
);
537 /* Inserts and replaces strings in a map, in random order. */
541 const int basis
= 15;
542 enum { MAX_ELEMS
= 16 };
543 const int max_trials
= 8;
546 for (cnt
= 0; cnt
<= MAX_ELEMS
; cnt
++)
548 int insertions
[MAX_ELEMS
];
552 for (i
= 0; i
< cnt
; i
++)
553 insertions
[i
] = (i
/ 2) | random_value (i
, basis
);
555 for (trial
= 0; trial
< max_trials
; trial
++)
557 struct string_map map
;
561 /* Insert with replacement in random order. */
563 string_map_init (&map
);
564 random_shuffle (insertions
, cnt
, sizeof *insertions
);
565 for (i
= 0; i
< cnt
; i
++)
567 const char *key
= make_key (insertions
[i
]);
568 const char *value
= make_value (insertions
[i
]);
571 for (j
= 0; j
< n_data
; j
++)
572 if ((data
[j
] & KEY_MASK
) == (insertions
[i
] & KEY_MASK
))
574 data
[j
] = insertions
[i
];
577 data
[n_data
++] = insertions
[i
];
581 string_map_replace (&map
, key
, value
);
583 string_map_replace_nocopy (&map
,
584 xstrdup (key
), xstrdup (value
));
585 check_string_map (&map
, data
, n_data
);
588 /* Delete in original order. */
589 for (i
= 0; i
< cnt
; i
++)
591 const char *expected_value
;
595 expected_value
= NULL
;
596 for (j
= 0; j
< n_data
; j
++)
597 if ((data
[j
] & KEY_MASK
) == (insertions
[i
] & KEY_MASK
))
599 expected_value
= make_value (data
[j
]);
600 data
[j
] = data
[--n_data
];
604 value
= string_map_find_and_delete (&map
,
605 make_key (insertions
[i
]));
606 check ((value
!= NULL
) == (expected_value
!= NULL
));
607 check (value
== NULL
|| !strcmp (value
, expected_value
));
610 assert (string_map_is_empty (&map
));
612 string_map_destroy (&map
);
618 make_patterned_map (struct string_map
*map
, unsigned int pattern
, int basis
,
619 int insertions
[], int *np
)
624 string_map_init (map
);
627 for (i
= 0; pattern
!= 0; i
++)
628 if (pattern
& (1u << i
))
630 pattern
&= pattern
- 1;
631 insertions
[n
] = i
| random_value (i
, basis
);
632 check (string_map_insert (map
, make_key (insertions
[n
]),
633 make_value (insertions
[n
])));
636 check_string_map (map
, insertions
, n
);
642 for_each_map (void (*cb
)(struct string_map
*, int data
[], int n
),
645 enum { MAX_ELEMS
= 5 };
646 unsigned int pattern
;
648 for (pattern
= 0; pattern
< (1u << MAX_ELEMS
); pattern
++)
651 struct string_map map
;
654 make_patterned_map (&map
, pattern
, basis
, data
, &n
);
655 (*cb
) (&map
, data
, n
);
656 string_map_destroy (&map
);
661 for_each_pair_of_maps (
662 void (*cb
)(struct string_map
*a
, int a_data
[], int n_a
,
663 struct string_map
*b
, int b_data
[], int n_b
),
664 int a_basis
, int b_basis
)
666 enum { MAX_ELEMS
= 5 };
667 unsigned int a_pattern
, b_pattern
;
669 for (a_pattern
= 0; a_pattern
< (1u << MAX_ELEMS
); a_pattern
++)
670 for (b_pattern
= 0; b_pattern
< (1u << MAX_ELEMS
); b_pattern
++)
672 int a_data
[MAX_ELEMS
], b_data
[MAX_ELEMS
];
673 struct string_map a_map
, b_map
;
676 make_patterned_map (&a_map
, a_pattern
, a_basis
, a_data
, &n_a
);
677 make_patterned_map (&b_map
, b_pattern
, b_basis
, b_data
, &n_b
);
678 (*cb
) (&a_map
, a_data
, n_a
, &b_map
, b_data
, n_b
);
679 string_map_destroy (&a_map
);
680 string_map_destroy (&b_map
);
685 clear_cb (struct string_map
*map
, int data
[] UNUSED
, int n UNUSED
)
687 string_map_clear (map
);
688 check_string_map (map
, NULL
, 0);
694 for_each_map (clear_cb
, 5);
698 clone_cb (struct string_map
*map
, int data
[], int n
)
700 struct string_map clone
;
702 string_map_clone (&clone
, map
);
703 check_string_map (&clone
, data
, n
);
704 string_map_destroy (&clone
);
710 for_each_map (clone_cb
, 6);
714 node_swap_value_cb (struct string_map
*map
, int data
[], int n
)
718 for (i
= 0; i
< n
; i
++)
720 const char *value
= make_value (data
[i
]);
721 struct string_map_node
*node
;
724 node
= string_map_find_node (map
, make_key (data
[i
]));
725 check (node
!= NULL
);
726 check (!strcmp (string_map_node_get_value (node
), value
));
727 data
[i
] = (data
[i
] & KEY_MASK
) | random_value (i
, 15);
728 old_value
= string_map_node_swap_value (node
, make_value (data
[i
]));
729 check (old_value
!= NULL
);
730 check (!strcmp (value
, old_value
));
736 test_node_swap_value (void)
738 for_each_map (node_swap_value_cb
, 14);
742 swap_cb (struct string_map
*a
, int a_data
[], int n_a
,
743 struct string_map
*b
, int b_data
[], int n_b
)
745 string_map_swap (a
, b
);
746 check_string_map (a
, b_data
, n_b
);
747 check_string_map (b
, a_data
, n_a
);
753 for_each_pair_of_maps (swap_cb
, 7, 8);
757 insert_map_cb (struct string_map
*a
, int a_data
[], int n_a
,
758 struct string_map
*b
, int b_data
[], int n_b
)
762 string_map_insert_map (a
, b
);
764 for (i
= 0; i
< n_b
; i
++)
766 for (j
= 0; j
< n_a
; j
++)
767 if ((b_data
[i
] & KEY_MASK
) == (a_data
[j
] & KEY_MASK
))
769 a_data
[n_a
++] = b_data
[i
];
772 check_string_map (a
, a_data
, n_a
);
773 check_string_map (b
, b_data
, n_b
);
777 test_insert_map (void)
779 for_each_pair_of_maps (insert_map_cb
, 91, 10);
783 replace_map_cb (struct string_map
*a
, int a_data
[], int n_a
,
784 struct string_map
*b
, int b_data
[], int n_b
)
788 string_map_replace_map (a
, b
);
790 for (i
= 0; i
< n_b
; i
++)
792 for (j
= 0; j
< n_a
; j
++)
793 if ((b_data
[i
] & KEY_MASK
) == (a_data
[j
] & KEY_MASK
))
795 a_data
[j
] = (a_data
[j
] & KEY_MASK
) | (b_data
[i
] & VALUE_MASK
);
798 a_data
[n_a
++] = b_data
[i
];
801 check_string_map (a
, a_data
, n_a
);
802 check_string_map (b
, b_data
, n_b
);
806 test_replace_map (void)
808 for_each_pair_of_maps (replace_map_cb
, 11, 12);
812 check_set (struct string_set
*set
, const int *data
, int n_data
,
820 unique
= xmalloc (n_data
* sizeof *unique
);
821 for (i
= 0; i
< n_data
; i
++)
823 int idx
= (data
[i
] & mask
) >> shift
;
826 for (j
= 0; j
< n_unique
; j
++)
827 if (unique
[j
] == idx
)
829 unique
[n_unique
++] = idx
;
833 check (string_set_count (set
) == n_unique
);
834 for (i
= 0; i
< n_unique
; i
++)
835 check (string_set_contains (set
, get_string (unique
[i
])));
836 string_set_destroy (set
);
841 get_keys_and_values_cb (struct string_map
*map
, int data
[], int n
)
843 struct string_set keys
, values
;
845 string_set_init (&keys
);
846 string_set_init (&values
);
847 string_map_get_keys (map
, &keys
);
848 string_map_get_values (map
, &values
);
849 check_set (&keys
, data
, n
, KEY_MASK
, KEY_SHIFT
);
850 check_set (&values
, data
, n
, VALUE_MASK
, VALUE_SHIFT
);
854 test_get_keys_and_values (void)
856 for_each_map (get_keys_and_values_cb
, 13);
860 test_destroy_null (void)
862 string_map_destroy (NULL
);
870 const char *description
;
871 void (*function
) (void);
874 static const struct test tests
[] =
877 "insert-any-remove-any",
878 "insert any order, delete any order",
879 test_insert_any_remove_any
882 "insert-any-remove-same",
883 "insert any order, delete same order",
884 test_insert_any_remove_same
887 "insert-any-remove-reverse",
888 "insert any order, delete reverse order",
889 test_insert_any_remove_reverse
893 "insert and delete in random sequence",
898 "insert and replace in random sequence",
903 "insert in ascending order",
937 "get-keys-and-values",
938 "get keys and values",
939 test_get_keys_and_values
943 "destroying null table",
948 enum { N_TESTS
= sizeof tests
/ sizeof *tests
};
951 main (int argc
, char *argv
[])
957 fprintf (stderr
, "exactly one argument required; use --help for help\n");
960 else if (!strcmp (argv
[1], "--help"))
962 printf ("%s: test string map library\n"
963 "usage: %s TEST-NAME\n"
964 "where TEST-NAME is one of the following:\n",
966 for (i
= 0; i
< N_TESTS
; i
++)
967 printf (" %s\n %s\n", tests
[i
].name
, tests
[i
].description
);
972 for (i
= 0; i
< N_TESTS
; i
++)
973 if (!strcmp (argv
[1], tests
[i
].name
))
975 tests
[i
].function ();
980 fprintf (stderr
, "unknown test %s; use --help for help\n", argv
[1]);