1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2008, 2009, 2010, 2012, 2014 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 stringi_map_* routines defined in
18 stringi-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 stringi-map.c, except that one branch caused by hash collision
21 is 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/stringi-map.h"
40 #include "libpspp/hash-functions.h"
41 #include "libpspp/compiler.h"
42 #include "libpspp/i18n.h"
43 #include "libpspp/str.h"
44 #include "libpspp/string-set.h"
45 #include "libpspp/stringi-set.h"
47 /* Exit with a failure code.
48 (Place a breakpoint on this function while debugging.) */
55 /* If OK is not true, prints a message about failure on the
56 current source file and the given LINE and terminates. */
58 check_func (bool ok, int line)
62 fprintf (stderr, "%s:%d: check failed\n", __FILE__, line);
67 /* Verifies that EXPR evaluates to true.
68 If not, prints a message citing the calling line number and
70 #define check(EXPR) check_func ((EXPR), __LINE__)
73 /* Support routines. */
77 MAX_IDX = 1 << IDX_BITS,
78 KEY_MASK = (MAX_IDX - 1),
80 VALUE_MASK = (MAX_IDX - 1) << IDX_BITS,
81 VALUE_SHIFT = IDX_BITS
84 static char *string_table[MAX_IDX];
91 assert (idx >= 0 && idx < MAX_IDX);
92 s = &string_table[idx];
96 str_format_26adic (idx + 1, true, *s, 16);
106 for (i = 0; i < MAX_IDX; i++)
107 free (string_table[i]);
113 return get_string ((value & KEY_MASK) >> KEY_SHIFT);
117 make_value (int value)
119 return get_string ((value & VALUE_MASK) >> VALUE_SHIFT);
123 random_value (unsigned int seed, int basis)
125 return hash_int (seed, basis) & VALUE_MASK;
128 /* Swaps *A and *B. */
130 swap (int *a, int *b)
137 /* Reverses the order of the CNT integers starting at VALUES. */
139 reverse (int *values, size_t cnt)
145 swap (&values[i++], &values[--j]);
148 /* Arranges the CNT elements in VALUES into the lexicographically next greater
149 permutation. Returns true if successful. If VALUES is already the
150 lexicographically greatest permutation of its elements (i.e. ordered from
151 greatest to smallest), arranges them into the lexicographically least
152 permutation (i.e. ordered from smallest to largest) and returns false.
154 Comparisons among elements of VALUES consider only the bits in KEY_MASK. */
156 next_permutation (int *values, size_t cnt)
164 if ((values[i] & KEY_MASK) < (values[i + 1] & KEY_MASK))
168 (values[i] & KEY_MASK) >= (values[j] & KEY_MASK);
171 swap (values + i, values + j);
172 reverse (values + (i + 1), cnt - (i + 1));
177 reverse (values, cnt);
185 factorial (unsigned int n)
187 unsigned int value = 1;
193 /* Randomly shuffles the CNT elements in ARRAY, each of which is
194 SIZE bytes in size. */
196 random_shuffle (void *array_, size_t cnt, size_t size)
198 char *array = array_;
199 char *tmp = xmalloc (size);
202 for (i = 0; i < cnt; i++)
204 size_t j = rand () % (cnt - i) + i;
207 memcpy (tmp, array + j * size, size);
208 memcpy (array + j * size, array + i * size, size);
209 memcpy (array + i * size, tmp, size);
216 /* Checks that MAP has (KEY, VALUE) as a pair. */
218 check_map_contains (struct stringi_map *map,
219 const char *key, const char *value)
221 struct stringi_map_node *node;
222 const char *found_value;
224 check (stringi_map_contains (map, key));
226 node = stringi_map_find_node (map, key);
227 check (node != NULL);
228 check (!utf8_strcasecmp (key, stringi_map_node_get_key (node)));
229 check (!strcmp (value, stringi_map_node_get_value (node)));
231 check (node == stringi_map_insert (map, key, "012"));
232 check (!strcmp (value, stringi_map_node_get_value (node)));
234 check (node == stringi_map_insert_nocopy (map, xstrdup (key),
236 check (!strcmp (value, stringi_map_node_get_value (node)));
238 found_value = stringi_map_find (map, key);
239 check (found_value == stringi_map_node_get_value (node));
240 check (found_value != NULL);
241 check (!strcmp (found_value, value));
244 /* Checks that MAP contains the CNT strings in DATA, that its structure is
245 correct, and that certain operations on MAP produce the expected results. */
247 check_stringi_map (struct stringi_map *map, const int data[], size_t cnt)
251 check (stringi_map_is_empty (map) == (cnt == 0));
252 check (stringi_map_count (map) == cnt);
254 for (i = 0; i < cnt; i++)
256 const char *key = make_key (data[i]);
257 const char *value = make_value (data[i]);
261 check_map_contains (map, key, value);
264 for (p = copy; *p != '\0'; p++)
266 assert (isupper (*p));
268 check_map_contains (map, copy, value);
272 check (!stringi_map_contains (map, "xxx"));
273 check (stringi_map_find (map, "0") == NULL);
274 check (stringi_map_find_node (map, "") == NULL);
275 check (!stringi_map_delete (map, "xyz"));
278 check (stringi_map_first (map) == NULL);
281 const struct stringi_map_node *node;
285 data_copy = xmemdup (data, cnt * sizeof *data);
287 for (node = stringi_map_first (map), i = 0; i < cnt;
288 node = stringi_map_next (map, node), i++)
290 const char *key = stringi_map_node_get_key (node);
291 const char *value = stringi_map_node_get_value (node);
294 for (j = 0; j < left; j++)
295 if (!strcmp (key, make_key (data_copy[j]))
296 || !strcmp (value, make_value (data_copy[j])))
298 data_copy[j] = data_copy[--left];
305 check (node == NULL);
310 /* Inserts the CNT strings from 0 to CNT - 1 (inclusive) into a map in the
311 order specified by INSERTIONS, then deletes them in the order specified by
312 DELETIONS, checking the map's contents for correctness after each
315 test_insert_delete (const int insertions[],
316 const int deletions[],
319 struct stringi_map map;
322 stringi_map_init (&map);
323 check_stringi_map (&map, NULL, 0);
324 for (i = 0; i < cnt; i++)
326 check (stringi_map_insert (&map, make_key (insertions[i]),
327 make_value (insertions[i])));
328 check_stringi_map (&map, insertions, i + 1);
330 for (i = 0; i < cnt; i++)
332 check (stringi_map_delete (&map, make_key (deletions[i])));
333 check_stringi_map (&map, deletions + i + 1, cnt - i - 1);
335 stringi_map_destroy (&map);
338 /* Inserts strings into a map in each possible order, then removes them in each
339 possible order, up to a specified maximum size. */
341 test_insert_any_remove_any (void)
344 const int max_elems = 5;
347 for (cnt = 0; cnt <= max_elems; cnt++)
349 int *insertions, *deletions;
350 unsigned int ins_perm_cnt;
353 insertions = xnmalloc (cnt, sizeof *insertions);
354 deletions = xnmalloc (cnt, sizeof *deletions);
355 for (i = 0; i < cnt; i++)
356 insertions[i] = i | random_value (i, basis);
358 for (ins_perm_cnt = 0;
359 ins_perm_cnt == 0 || next_permutation (insertions, cnt);
362 unsigned int del_perm_cnt;
365 for (i = 0; i < cnt; i++)
366 deletions[i] = i | random_value (i, basis);
368 for (del_perm_cnt = 0;
369 del_perm_cnt == 0 || next_permutation (deletions, cnt);
371 test_insert_delete (insertions, deletions, cnt);
373 check (del_perm_cnt == factorial (cnt));
375 check (ins_perm_cnt == factorial (cnt));
382 /* Inserts strings into a map in each possible order, then removes them in the
383 same order, up to a specified maximum size. */
385 test_insert_any_remove_same (void)
387 const int max_elems = 7;
390 for (cnt = 0; cnt <= max_elems; cnt++)
393 unsigned int permutation_cnt;
396 values = xnmalloc (cnt, sizeof *values);
397 for (i = 0; i < cnt; i++)
398 values[i] = i | random_value (i, 1);
400 for (permutation_cnt = 0;
401 permutation_cnt == 0 || next_permutation (values, cnt);
403 test_insert_delete (values, values, cnt);
404 check (permutation_cnt == factorial (cnt));
410 /* Inserts strings into a map in each possible order, then
411 removes them in reverse order, up to a specified maximum
414 test_insert_any_remove_reverse (void)
416 const int max_elems = 7;
419 for (cnt = 0; cnt <= max_elems; cnt++)
421 int *insertions, *deletions;
422 unsigned int permutation_cnt;
425 insertions = xnmalloc (cnt, sizeof *insertions);
426 deletions = xnmalloc (cnt, sizeof *deletions);
427 for (i = 0; i < cnt; i++)
428 insertions[i] = i | random_value (i, 2);
430 for (permutation_cnt = 0;
431 permutation_cnt == 0 || next_permutation (insertions, cnt);
434 memcpy (deletions, insertions, sizeof *insertions * cnt);
435 reverse (deletions, cnt);
437 test_insert_delete (insertions, deletions, cnt);
439 check (permutation_cnt == factorial (cnt));
446 /* Inserts and removes strings in a map, in random order. */
448 test_random_sequence (void)
451 const int max_elems = 64;
452 const int max_trials = 8;
455 for (cnt = 0; cnt <= max_elems; cnt += 2)
457 int *insertions, *deletions;
461 insertions = xnmalloc (cnt, sizeof *insertions);
462 deletions = xnmalloc (cnt, sizeof *deletions);
463 for (i = 0; i < cnt; i++)
464 insertions[i] = i | random_value (i, basis);
465 for (i = 0; i < cnt; i++)
466 deletions[i] = i | random_value (i, basis);
468 for (trial = 0; trial < max_trials; trial++)
470 random_shuffle (insertions, cnt, sizeof *insertions);
471 random_shuffle (deletions, cnt, sizeof *deletions);
473 test_insert_delete (insertions, deletions, cnt);
481 /* Inserts strings into a map in ascending order, then delete in ascending
484 test_insert_ordered (void)
486 const int max_elems = 64;
488 struct stringi_map map;
491 stringi_map_init (&map);
492 values = xnmalloc (max_elems, sizeof *values);
493 for (i = 0; i < max_elems; i++)
495 values[i] = i | random_value (i, 4);
496 stringi_map_insert_nocopy (&map, xstrdup (make_key (values[i])),
497 xstrdup (make_value (values[i])));
498 check_stringi_map (&map, values, i + 1);
500 for (i = 0; i < max_elems; i++)
502 stringi_map_delete (&map, make_key (i));
503 check_stringi_map (&map, values + i + 1, max_elems - i - 1);
505 stringi_map_destroy (&map);
509 /* Inserts and replaces strings in a map, in random order. */
513 const int basis = 15;
514 enum { MAX_ELEMS = 16 };
515 const int max_trials = 8;
518 for (cnt = 0; cnt <= MAX_ELEMS; cnt++)
520 int insertions[MAX_ELEMS];
524 for (i = 0; i < cnt; i++)
525 insertions[i] = (i / 2) | random_value (i, basis);
527 for (trial = 0; trial < max_trials; trial++)
529 struct stringi_map map;
533 /* Insert with replacement in random order. */
535 stringi_map_init (&map);
536 random_shuffle (insertions, cnt, sizeof *insertions);
537 for (i = 0; i < cnt; i++)
539 const char *key = make_key (insertions[i]);
540 const char *value = make_value (insertions[i]);
543 for (j = 0; j < n_data; j++)
544 if ((data[j] & KEY_MASK) == (insertions[i] & KEY_MASK))
546 data[j] = insertions[i];
549 data[n_data++] = insertions[i];
553 stringi_map_replace (&map, key, value);
555 stringi_map_replace_nocopy (&map,
556 xstrdup (key), xstrdup (value));
557 check_stringi_map (&map, data, n_data);
560 /* Delete in original order. */
561 for (i = 0; i < cnt; i++)
563 const char *expected_value;
567 expected_value = NULL;
568 for (j = 0; j < n_data; j++)
569 if ((data[j] & KEY_MASK) == (insertions[i] & KEY_MASK))
571 expected_value = make_value (data[j]);
572 data[j] = data[--n_data];
576 value = stringi_map_find_and_delete (&map,
577 make_key (insertions[i]));
578 check ((value != NULL) == (expected_value != NULL));
579 check (value == NULL || !strcmp (value, expected_value));
582 assert (stringi_map_is_empty (&map));
584 stringi_map_destroy (&map);
590 make_patterned_map (struct stringi_map *map, unsigned int pattern, int basis,
591 int insertions[], int *np)
596 stringi_map_init (map);
599 for (i = 0; pattern != 0; i++)
600 if (pattern & (1u << i))
602 pattern &= pattern - 1;
603 insertions[n] = i | random_value (i, basis);
604 check (stringi_map_insert (map, make_key (insertions[n]),
605 make_value (insertions[n])));
608 check_stringi_map (map, insertions, n);
614 for_each_map (void (*cb)(struct stringi_map *, int data[], int n),
617 enum { MAX_ELEMS = 5 };
618 unsigned int pattern;
620 for (pattern = 0; pattern < (1u << MAX_ELEMS); pattern++)
623 struct stringi_map map;
626 make_patterned_map (&map, pattern, basis, data, &n);
627 (*cb) (&map, data, n);
628 stringi_map_destroy (&map);
633 for_each_pair_of_maps (
634 void (*cb)(struct stringi_map *a, int a_data[], int n_a,
635 struct stringi_map *b, int b_data[], int n_b),
636 int a_basis, int b_basis)
638 enum { MAX_ELEMS = 5 };
639 unsigned int a_pattern, b_pattern;
641 for (a_pattern = 0; a_pattern < (1u << MAX_ELEMS); a_pattern++)
642 for (b_pattern = 0; b_pattern < (1u << MAX_ELEMS); b_pattern++)
644 int a_data[MAX_ELEMS], b_data[MAX_ELEMS];
645 struct stringi_map a_map, b_map;
648 make_patterned_map (&a_map, a_pattern, a_basis, a_data, &n_a);
649 make_patterned_map (&b_map, b_pattern, b_basis, b_data, &n_b);
650 (*cb) (&a_map, a_data, n_a, &b_map, b_data, n_b);
651 stringi_map_destroy (&a_map);
652 stringi_map_destroy (&b_map);
657 clear_cb (struct stringi_map *map, int data[] UNUSED, int n UNUSED)
659 stringi_map_clear (map);
660 check_stringi_map (map, NULL, 0);
666 for_each_map (clear_cb, 5);
670 clone_cb (struct stringi_map *map, int data[], int n)
672 struct stringi_map clone;
674 stringi_map_clone (&clone, map);
675 check_stringi_map (&clone, data, n);
676 stringi_map_destroy (&clone);
682 for_each_map (clone_cb, 6);
686 node_swap_value_cb (struct stringi_map *map, int data[], int n)
690 for (i = 0; i < n; i++)
692 const char *value = make_value (data[i]);
693 struct stringi_map_node *node;
696 node = stringi_map_find_node (map, make_key (data[i]));
697 check (node != NULL);
698 check (!strcmp (stringi_map_node_get_value (node), value));
699 data[i] = (data[i] & KEY_MASK) | random_value (i, 15);
700 old_value = stringi_map_node_swap_value (node, make_value (data[i]));
701 check (old_value != NULL);
702 check (!strcmp (value, old_value));
708 test_node_swap_value (void)
710 for_each_map (node_swap_value_cb, 14);
714 swap_cb (struct stringi_map *a, int a_data[], int n_a,
715 struct stringi_map *b, int b_data[], int n_b)
717 stringi_map_swap (a, b);
718 check_stringi_map (a, b_data, n_b);
719 check_stringi_map (b, a_data, n_a);
725 for_each_pair_of_maps (swap_cb, 7, 8);
729 insert_map_cb (struct stringi_map *a, int a_data[], int n_a,
730 struct stringi_map *b, int b_data[], int n_b)
734 stringi_map_insert_map (a, b);
736 for (i = 0; i < n_b; i++)
738 for (j = 0; j < n_a; j++)
739 if ((b_data[i] & KEY_MASK) == (a_data[j] & KEY_MASK))
741 a_data[n_a++] = b_data[i];
744 check_stringi_map (a, a_data, n_a);
745 check_stringi_map (b, b_data, n_b);
749 test_insert_map (void)
751 for_each_pair_of_maps (insert_map_cb, 91, 10);
755 replace_map_cb (struct stringi_map *a, int a_data[], int n_a,
756 struct stringi_map *b, int b_data[], int n_b)
760 stringi_map_replace_map (a, b);
762 for (i = 0; i < n_b; i++)
764 for (j = 0; j < n_a; j++)
765 if ((b_data[i] & KEY_MASK) == (a_data[j] & KEY_MASK))
767 a_data[j] = (a_data[j] & KEY_MASK) | (b_data[i] & VALUE_MASK);
770 a_data[n_a++] = b_data[i];
773 check_stringi_map (a, a_data, n_a);
774 check_stringi_map (b, b_data, n_b);
778 test_replace_map (void)
780 for_each_pair_of_maps (replace_map_cb, 11, 12);
784 check_iset (struct stringi_set *set, const int *data, int n_data,
792 unique = xmalloc (n_data * sizeof *unique);
793 for (i = 0; i < n_data; i++)
795 int idx = (data[i] & mask) >> shift;
798 for (j = 0; j < n_unique; j++)
799 if (unique[j] == idx)
801 unique[n_unique++] = idx;
805 check (stringi_set_count (set) == n_unique);
806 for (i = 0; i < n_unique; i++)
807 check (stringi_set_contains (set, get_string (unique[i])));
808 stringi_set_destroy (set);
813 check_set (struct string_set *set, const int *data, int n_data,
821 unique = xmalloc (n_data * sizeof *unique);
822 for (i = 0; i < n_data; i++)
824 int idx = (data[i] & mask) >> shift;
827 for (j = 0; j < n_unique; j++)
828 if (unique[j] == idx)
830 unique[n_unique++] = idx;
834 check (string_set_count (set) == n_unique);
835 for (i = 0; i < n_unique; i++)
836 check (string_set_contains (set, get_string (unique[i])));
837 string_set_destroy (set);
842 get_keys_and_values_cb (struct stringi_map *map, int data[], int n)
844 struct stringi_set keys;
845 struct string_set values;
847 stringi_set_init (&keys);
848 string_set_init (&values);
849 stringi_map_get_keys (map, &keys);
850 stringi_map_get_values (map, &values);
851 check_iset (&keys, data, n, KEY_MASK, KEY_SHIFT);
852 check_set (&values, data, n, VALUE_MASK, VALUE_SHIFT);
856 test_get_keys_and_values (void)
858 for_each_map (get_keys_and_values_cb, 13);
862 test_destroy_null (void)
864 stringi_map_destroy (NULL);
872 const char *description;
873 void (*function) (void);
876 static const struct test tests[] =
879 "insert-any-remove-any",
880 "insert any order, delete any order",
881 test_insert_any_remove_any
884 "insert-any-remove-same",
885 "insert any order, delete same order",
886 test_insert_any_remove_same
889 "insert-any-remove-reverse",
890 "insert any order, delete reverse order",
891 test_insert_any_remove_reverse
895 "insert and delete in random sequence",
900 "insert and replace in random sequence",
905 "insert in ascending order",
939 "get-keys-and-values",
940 "get keys and values",
941 test_get_keys_and_values
945 "destroying null table",
950 enum { N_TESTS = sizeof tests / sizeof *tests };
953 main (int argc, char *argv[])
959 fprintf (stderr, "exactly one argument required; use --help for help\n");
962 else if (!strcmp (argv[1], "--help"))
964 printf ("%s: test case-insensitive string map library\n"
965 "usage: %s TEST-NAME\n"
966 "where TEST-NAME is one of the following:\n",
968 for (i = 0; i < N_TESTS; i++)
969 printf (" %s\n %s\n", tests[i].name, tests[i].description);
974 for (i = 0; i < N_TESTS; i++)
975 if (!strcmp (argv[1], tests[i].name))
977 tests[i].function ();
982 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);