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 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/str.h"
43 #include "libpspp/string-set.h"
44 #include "libpspp/stringi-set.h"
46 /* Exit with a failure code.
47 (Place a breakpoint on this function while debugging.) */
54 /* If OK is not true, prints a message about failure on the
55 current source file and the given LINE and terminates. */
57 check_func (bool ok, int line)
61 fprintf (stderr, "%s:%d: check failed\n", __FILE__, line);
66 /* Verifies that EXPR evaluates to true.
67 If not, prints a message citing the calling line number and
69 #define check(EXPR) check_func ((EXPR), __LINE__)
71 /* Prints a message about memory exhaustion and exits with a
76 printf ("virtual memory exhausted\n");
80 static void *xmalloc (size_t n) MALLOC_LIKE;
81 static void *xnmalloc (size_t n, size_t m) MALLOC_LIKE;
82 static void *xmemdup (const void *p, size_t n) MALLOC_LIKE;
84 /* Allocates and returns N bytes of memory. */
101 xmemdup (const void *p, size_t n)
103 void *q = xmalloc (n);
110 xstrdup (const char *string)
112 return xmemdup (string, strlen (string) + 1);
115 /* Allocates and returns N * M bytes of memory. */
117 xnmalloc (size_t n, size_t m)
119 if ((size_t) -1 / m <= n)
121 return xmalloc (n * m);
124 /* Support routines. */
128 MAX_IDX = 1 << IDX_BITS,
129 KEY_MASK = (MAX_IDX - 1),
131 VALUE_MASK = (MAX_IDX - 1) << IDX_BITS,
132 VALUE_SHIFT = IDX_BITS
135 static char *string_table[MAX_IDX];
142 assert (idx >= 0 && idx < MAX_IDX);
143 s = &string_table[idx];
147 str_format_26adic (idx + 1, *s, 16);
157 for (i = 0; i < MAX_IDX; i++)
158 free (string_table[i]);
164 return get_string ((value & KEY_MASK) >> KEY_SHIFT);
168 make_value (int value)
170 return get_string ((value & VALUE_MASK) >> VALUE_SHIFT);
174 random_value (unsigned int seed, int basis)
176 return hash_int (seed, basis) & VALUE_MASK;
179 /* Swaps *A and *B. */
181 swap (int *a, int *b)
188 /* Reverses the order of the CNT integers starting at VALUES. */
190 reverse (int *values, size_t cnt)
196 swap (&values[i++], &values[--j]);
199 /* Arranges the CNT elements in VALUES into the lexicographically next greater
200 permutation. Returns true if successful. If VALUES is already the
201 lexicographically greatest permutation of its elements (i.e. ordered from
202 greatest to smallest), arranges them into the lexicographically least
203 permutation (i.e. ordered from smallest to largest) and returns false.
205 Comparisons among elements of VALUES consider only the bits in KEY_MASK. */
207 next_permutation (int *values, size_t cnt)
215 if ((values[i] & KEY_MASK) < (values[i + 1] & KEY_MASK))
219 (values[i] & KEY_MASK) >= (values[j] & KEY_MASK);
222 swap (values + i, values + j);
223 reverse (values + (i + 1), cnt - (i + 1));
228 reverse (values, cnt);
236 factorial (unsigned int n)
238 unsigned int value = 1;
244 /* Randomly shuffles the CNT elements in ARRAY, each of which is
245 SIZE bytes in size. */
247 random_shuffle (void *array_, size_t cnt, size_t size)
249 char *array = array_;
250 char *tmp = xmalloc (size);
253 for (i = 0; i < cnt; i++)
255 size_t j = rand () % (cnt - i) + i;
258 memcpy (tmp, array + j * size, size);
259 memcpy (array + j * size, array + i * size, size);
260 memcpy (array + i * size, tmp, size);
267 /* Checks that MAP has (KEY, VALUE) as a pair. */
269 check_map_contains (struct stringi_map *map,
270 const char *key, const char *value)
272 struct stringi_map_node *node;
273 const char *found_value;
275 check (stringi_map_contains (map, key));
277 node = stringi_map_find_node (map, key);
278 check (node != NULL);
279 check (!strcasecmp (key, stringi_map_node_get_key (node)));
280 check (!strcmp (value, stringi_map_node_get_value (node)));
282 check (node == stringi_map_insert (map, key, "012"));
283 check (!strcmp (value, stringi_map_node_get_value (node)));
285 check (node == stringi_map_insert_nocopy (map, xstrdup (key),
287 check (!strcmp (value, stringi_map_node_get_value (node)));
289 found_value = stringi_map_find (map, key);
290 check (found_value == stringi_map_node_get_value (node));
291 check (found_value != NULL);
292 check (!strcmp (found_value, value));
295 /* Checks that MAP contains the CNT strings in DATA, that its structure is
296 correct, and that certain operations on MAP produce the expected results. */
298 check_stringi_map (struct stringi_map *map, const int data[], size_t cnt)
302 check (stringi_map_is_empty (map) == (cnt == 0));
303 check (stringi_map_count (map) == cnt);
305 for (i = 0; i < cnt; i++)
307 const char *key = make_key (data[i]);
308 const char *value = make_value (data[i]);
312 check_map_contains (map, key, value);
315 for (p = copy; *p != '\0'; p++)
317 assert (isupper (*p));
319 check_map_contains (map, copy, value);
323 check (!stringi_map_contains (map, "xxx"));
324 check (stringi_map_find (map, "0") == NULL);
325 check (stringi_map_find_node (map, "") == NULL);
326 check (!stringi_map_delete (map, "xyz"));
329 check (stringi_map_first (map) == NULL);
332 const struct stringi_map_node *node;
336 data_copy = xmemdup (data, cnt * sizeof *data);
338 for (node = stringi_map_first (map), i = 0; i < cnt;
339 node = stringi_map_next (map, node), i++)
341 const char *key = stringi_map_node_get_key (node);
342 const char *value = stringi_map_node_get_value (node);
345 for (j = 0; j < left; j++)
346 if (!strcmp (key, make_key (data_copy[j]))
347 || !strcmp (value, make_value (data_copy[j])))
349 data_copy[j] = data_copy[--left];
356 check (node == NULL);
361 /* Inserts the CNT strings from 0 to CNT - 1 (inclusive) into a map in the
362 order specified by INSERTIONS, then deletes them in the order specified by
363 DELETIONS, checking the map's contents for correctness after each
366 test_insert_delete (const int insertions[],
367 const int deletions[],
370 struct stringi_map map;
373 stringi_map_init (&map);
374 check_stringi_map (&map, NULL, 0);
375 for (i = 0; i < cnt; i++)
377 check (stringi_map_insert (&map, make_key (insertions[i]),
378 make_value (insertions[i])));
379 check_stringi_map (&map, insertions, i + 1);
381 for (i = 0; i < cnt; i++)
383 check (stringi_map_delete (&map, make_key (deletions[i])));
384 check_stringi_map (&map, deletions + i + 1, cnt - i - 1);
386 stringi_map_destroy (&map);
389 /* Inserts strings into a map in each possible order, then removes them in each
390 possible order, up to a specified maximum size. */
392 test_insert_any_remove_any (void)
395 const int max_elems = 5;
398 for (cnt = 0; cnt <= max_elems; cnt++)
400 int *insertions, *deletions;
401 unsigned int ins_perm_cnt;
404 insertions = xnmalloc (cnt, sizeof *insertions);
405 deletions = xnmalloc (cnt, sizeof *deletions);
406 for (i = 0; i < cnt; i++)
407 insertions[i] = i | random_value (i, basis);
409 for (ins_perm_cnt = 0;
410 ins_perm_cnt == 0 || next_permutation (insertions, cnt);
413 unsigned int del_perm_cnt;
416 for (i = 0; i < cnt; i++)
417 deletions[i] = i | random_value (i, basis);
419 for (del_perm_cnt = 0;
420 del_perm_cnt == 0 || next_permutation (deletions, cnt);
422 test_insert_delete (insertions, deletions, cnt);
424 check (del_perm_cnt == factorial (cnt));
426 check (ins_perm_cnt == factorial (cnt));
433 /* Inserts strings into a map in each possible order, then removes them in the
434 same order, up to a specified maximum size. */
436 test_insert_any_remove_same (void)
438 const int max_elems = 7;
441 for (cnt = 0; cnt <= max_elems; cnt++)
444 unsigned int permutation_cnt;
447 values = xnmalloc (cnt, sizeof *values);
448 for (i = 0; i < cnt; i++)
449 values[i] = i | random_value (i, 1);
451 for (permutation_cnt = 0;
452 permutation_cnt == 0 || next_permutation (values, cnt);
454 test_insert_delete (values, values, cnt);
455 check (permutation_cnt == factorial (cnt));
461 /* Inserts strings into a map in each possible order, then
462 removes them in reverse order, up to a specified maximum
465 test_insert_any_remove_reverse (void)
467 const int max_elems = 7;
470 for (cnt = 0; cnt <= max_elems; cnt++)
472 int *insertions, *deletions;
473 unsigned int permutation_cnt;
476 insertions = xnmalloc (cnt, sizeof *insertions);
477 deletions = xnmalloc (cnt, sizeof *deletions);
478 for (i = 0; i < cnt; i++)
479 insertions[i] = i | random_value (i, 2);
481 for (permutation_cnt = 0;
482 permutation_cnt == 0 || next_permutation (insertions, cnt);
485 memcpy (deletions, insertions, sizeof *insertions * cnt);
486 reverse (deletions, cnt);
488 test_insert_delete (insertions, deletions, cnt);
490 check (permutation_cnt == factorial (cnt));
497 /* Inserts and removes strings in a map, in random order. */
499 test_random_sequence (void)
502 const int max_elems = 64;
503 const int max_trials = 8;
506 for (cnt = 0; cnt <= max_elems; cnt += 2)
508 int *insertions, *deletions;
512 insertions = xnmalloc (cnt, sizeof *insertions);
513 deletions = xnmalloc (cnt, sizeof *deletions);
514 for (i = 0; i < cnt; i++)
515 insertions[i] = i | random_value (i, basis);
516 for (i = 0; i < cnt; i++)
517 deletions[i] = i | random_value (i, basis);
519 for (trial = 0; trial < max_trials; trial++)
521 random_shuffle (insertions, cnt, sizeof *insertions);
522 random_shuffle (deletions, cnt, sizeof *deletions);
524 test_insert_delete (insertions, deletions, cnt);
532 /* Inserts strings into a map in ascending order, then delete in ascending
535 test_insert_ordered (void)
537 const int max_elems = 64;
539 struct stringi_map map;
542 stringi_map_init (&map);
543 values = xnmalloc (max_elems, sizeof *values);
544 for (i = 0; i < max_elems; i++)
546 values[i] = i | random_value (i, 4);
547 stringi_map_insert_nocopy (&map, xstrdup (make_key (values[i])),
548 xstrdup (make_value (values[i])));
549 check_stringi_map (&map, values, i + 1);
551 for (i = 0; i < max_elems; i++)
553 stringi_map_delete (&map, make_key (i));
554 check_stringi_map (&map, values + i + 1, max_elems - i - 1);
556 stringi_map_destroy (&map);
560 /* Inserts and replaces strings in a map, in random order. */
564 const int basis = 15;
565 enum { MAX_ELEMS = 16 };
566 const int max_trials = 8;
569 for (cnt = 0; cnt <= MAX_ELEMS; cnt++)
571 int insertions[MAX_ELEMS];
575 for (i = 0; i < cnt; i++)
576 insertions[i] = (i / 2) | random_value (i, basis);
578 for (trial = 0; trial < max_trials; trial++)
580 struct stringi_map map;
584 /* Insert with replacement in random order. */
586 stringi_map_init (&map);
587 random_shuffle (insertions, cnt, sizeof *insertions);
588 for (i = 0; i < cnt; i++)
590 const char *key = make_key (insertions[i]);
591 const char *value = make_value (insertions[i]);
594 for (j = 0; j < n_data; j++)
595 if ((data[j] & KEY_MASK) == (insertions[i] & KEY_MASK))
597 data[j] = insertions[i];
600 data[n_data++] = insertions[i];
604 stringi_map_replace (&map, key, value);
606 stringi_map_replace_nocopy (&map,
607 xstrdup (key), xstrdup (value));
608 check_stringi_map (&map, data, n_data);
611 /* Delete in original order. */
612 for (i = 0; i < cnt; i++)
614 const char *expected_value;
618 expected_value = NULL;
619 for (j = 0; j < n_data; j++)
620 if ((data[j] & KEY_MASK) == (insertions[i] & KEY_MASK))
622 expected_value = make_value (data[j]);
623 data[j] = data[--n_data];
627 value = stringi_map_find_and_delete (&map,
628 make_key (insertions[i]));
629 check ((value != NULL) == (expected_value != NULL));
630 check (value == NULL || !strcmp (value, expected_value));
633 assert (stringi_map_is_empty (&map));
635 stringi_map_destroy (&map);
641 make_patterned_map (struct stringi_map *map, unsigned int pattern, int basis,
642 int insertions[], int *np)
647 stringi_map_init (map);
650 for (i = 0; pattern != 0; i++)
651 if (pattern & (1u << i))
653 pattern &= pattern - 1;
654 insertions[n] = i | random_value (i, basis);
655 check (stringi_map_insert (map, make_key (insertions[n]),
656 make_value (insertions[n])));
659 check_stringi_map (map, insertions, n);
665 for_each_map (void (*cb)(struct stringi_map *, int data[], int n),
668 enum { MAX_ELEMS = 5 };
669 unsigned int pattern;
671 for (pattern = 0; pattern < (1u << MAX_ELEMS); pattern++)
674 struct stringi_map map;
677 make_patterned_map (&map, pattern, basis, data, &n);
678 (*cb) (&map, data, n);
679 stringi_map_destroy (&map);
684 for_each_pair_of_maps (
685 void (*cb)(struct stringi_map *a, int a_data[], int n_a,
686 struct stringi_map *b, int b_data[], int n_b),
687 int a_basis, int b_basis)
689 enum { MAX_ELEMS = 5 };
690 unsigned int a_pattern, b_pattern;
692 for (a_pattern = 0; a_pattern < (1u << MAX_ELEMS); a_pattern++)
693 for (b_pattern = 0; b_pattern < (1u << MAX_ELEMS); b_pattern++)
695 int a_data[MAX_ELEMS], b_data[MAX_ELEMS];
696 struct stringi_map a_map, b_map;
699 make_patterned_map (&a_map, a_pattern, a_basis, a_data, &n_a);
700 make_patterned_map (&b_map, b_pattern, b_basis, b_data, &n_b);
701 (*cb) (&a_map, a_data, n_a, &b_map, b_data, n_b);
702 stringi_map_destroy (&a_map);
703 stringi_map_destroy (&b_map);
708 clear_cb (struct stringi_map *map, int data[] UNUSED, int n UNUSED)
710 stringi_map_clear (map);
711 check_stringi_map (map, NULL, 0);
717 for_each_map (clear_cb, 5);
721 clone_cb (struct stringi_map *map, int data[], int n)
723 struct stringi_map clone;
725 stringi_map_clone (&clone, map);
726 check_stringi_map (&clone, data, n);
727 stringi_map_destroy (&clone);
733 for_each_map (clone_cb, 6);
737 node_swap_value_cb (struct stringi_map *map, int data[], int n)
741 for (i = 0; i < n; i++)
743 const char *value = make_value (data[i]);
744 struct stringi_map_node *node;
747 node = stringi_map_find_node (map, make_key (data[i]));
748 check (node != NULL);
749 check (!strcmp (stringi_map_node_get_value (node), value));
750 data[i] = (data[i] & KEY_MASK) | random_value (i, 15);
751 old_value = stringi_map_node_swap_value (node, make_value (data[i]));
752 check (old_value != NULL);
753 check (!strcmp (value, old_value));
759 test_node_swap_value (void)
761 for_each_map (node_swap_value_cb, 14);
765 swap_cb (struct stringi_map *a, int a_data[], int n_a,
766 struct stringi_map *b, int b_data[], int n_b)
768 stringi_map_swap (a, b);
769 check_stringi_map (a, b_data, n_b);
770 check_stringi_map (b, a_data, n_a);
776 for_each_pair_of_maps (swap_cb, 7, 8);
780 insert_map_cb (struct stringi_map *a, int a_data[], int n_a,
781 struct stringi_map *b, int b_data[], int n_b)
785 stringi_map_insert_map (a, b);
787 for (i = 0; i < n_b; i++)
789 for (j = 0; j < n_a; j++)
790 if ((b_data[i] & KEY_MASK) == (a_data[j] & KEY_MASK))
792 a_data[n_a++] = b_data[i];
795 check_stringi_map (a, a_data, n_a);
796 check_stringi_map (b, b_data, n_b);
800 test_insert_map (void)
802 for_each_pair_of_maps (insert_map_cb, 91, 10);
806 replace_map_cb (struct stringi_map *a, int a_data[], int n_a,
807 struct stringi_map *b, int b_data[], int n_b)
811 stringi_map_replace_map (a, b);
813 for (i = 0; i < n_b; i++)
815 for (j = 0; j < n_a; j++)
816 if ((b_data[i] & KEY_MASK) == (a_data[j] & KEY_MASK))
818 a_data[j] = (a_data[j] & KEY_MASK) | (b_data[i] & VALUE_MASK);
821 a_data[n_a++] = b_data[i];
824 check_stringi_map (a, a_data, n_a);
825 check_stringi_map (b, b_data, n_b);
829 test_replace_map (void)
831 for_each_pair_of_maps (replace_map_cb, 11, 12);
835 check_iset (struct stringi_set *set, const int *data, int n_data,
843 unique = xmalloc (n_data * sizeof *unique);
844 for (i = 0; i < n_data; i++)
846 int idx = (data[i] & mask) >> shift;
849 for (j = 0; j < n_unique; j++)
850 if (unique[j] == idx)
852 unique[n_unique++] = idx;
856 check (stringi_set_count (set) == n_unique);
857 for (i = 0; i < n_unique; i++)
858 check (stringi_set_contains (set, get_string (unique[i])));
859 stringi_set_destroy (set);
864 check_set (struct string_set *set, const int *data, int n_data,
872 unique = xmalloc (n_data * sizeof *unique);
873 for (i = 0; i < n_data; i++)
875 int idx = (data[i] & mask) >> shift;
878 for (j = 0; j < n_unique; j++)
879 if (unique[j] == idx)
881 unique[n_unique++] = idx;
885 check (string_set_count (set) == n_unique);
886 for (i = 0; i < n_unique; i++)
887 check (string_set_contains (set, get_string (unique[i])));
888 string_set_destroy (set);
893 get_keys_and_values_cb (struct stringi_map *map, int data[], int n)
895 struct stringi_set keys;
896 struct string_set values;
898 stringi_set_init (&keys);
899 string_set_init (&values);
900 stringi_map_get_keys (map, &keys);
901 stringi_map_get_values (map, &values);
902 check_iset (&keys, data, n, KEY_MASK, KEY_SHIFT);
903 check_set (&values, data, n, VALUE_MASK, VALUE_SHIFT);
907 test_get_keys_and_values (void)
909 for_each_map (get_keys_and_values_cb, 13);
913 test_destroy_null (void)
915 stringi_map_destroy (NULL);
923 const char *description;
924 void (*function) (void);
927 static const struct test tests[] =
930 "insert-any-remove-any",
931 "insert any order, delete any order",
932 test_insert_any_remove_any
935 "insert-any-remove-same",
936 "insert any order, delete same order",
937 test_insert_any_remove_same
940 "insert-any-remove-reverse",
941 "insert any order, delete reverse order",
942 test_insert_any_remove_reverse
946 "insert and delete in random sequence",
951 "insert and replace in random sequence",
956 "insert in ascending order",
990 "get-keys-and-values",
991 "get keys and values",
992 test_get_keys_and_values
996 "destroying null table",
1001 enum { N_TESTS = sizeof tests / sizeof *tests };
1004 main (int argc, char *argv[])
1010 fprintf (stderr, "exactly one argument required; use --help for help\n");
1011 return EXIT_FAILURE;
1013 else if (!strcmp (argv[1], "--help"))
1015 printf ("%s: test case-insensitive string map library\n"
1016 "usage: %s TEST-NAME\n"
1017 "where TEST-NAME is one of the following:\n",
1019 for (i = 0; i < N_TESTS; i++)
1020 printf (" %s\n %s\n", tests[i].name, tests[i].description);
1025 for (i = 0; i < N_TESTS; i++)
1026 if (!strcmp (argv[1], tests[i].name))
1028 tests[i].function ();
1033 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);
1034 return EXIT_FAILURE;