1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 2008, 2009 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 hmap_* routines defined in
18 hmap.c. This test program aims to be as comprehensive as
19 possible. "gcov -a -b" should report 100% coverage of lines,
20 blocks and branches in hmap.c (when compiled with -DNDEBUG).
21 "valgrind --leak-check=yes --show-reachable=yes" should give a
24 /* GCC 4.3 miscompiles some of the tests below, so we do not run
25 these tests on GCC 4.3. This is a bug in GCC 4.3 triggered by
26 the test program, not a bug in the library under test. GCC
27 4.2 or earlier and GCC 4.4 or later do not have this bug.
29 Here is a minimal test program that demonstrates the same or a
30 similar bug in GCC 4.3:
54 check_list (struct list *list)
56 int i __attribute__((unused));
58 for (e = list->head; e != NULL; e = e->next)
59 if (e->data1 != e->data2)
67 struct node *elements = xmalloc (MAX_ELEMS * sizeof *elements);
68 int *values = xmalloc (MAX_ELEMS * sizeof *values);
73 for (i = 0; i < MAX_ELEMS; i++)
75 values[i] = elements[i].data2 = i;
76 elements[i].data1 = elements[i].data2;
77 elements[i].next = list.head;
78 list.head = &elements[i];
89 #include <libpspp/hmap.h>
100 #include <libpspp/compiler.h>
102 /* Currently running test. */
103 static const char *test_name;
105 /* Exit with a failure code.
106 (Place a breakpoint on this function while debugging.) */
113 /* If OK is not true, prints a message about failure on the
114 current source file and the given LINE and terminates. */
116 check_func (bool ok, int line)
120 printf ("Check failed in %s test at %s, line %d\n",
121 test_name, __FILE__, line);
126 /* Verifies that EXPR evaluates to true.
127 If not, prints a message citing the calling line number and
129 #define check(EXPR) check_func ((EXPR), __LINE__)
131 /* Prints a message about memory exhaustion and exits with a
136 printf ("virtual memory exhausted\n");
140 static void *xmalloc (size_t n) MALLOC_LIKE;
141 static void *xnmalloc (size_t n, size_t m) MALLOC_LIKE;
142 static void *xmemdup (const void *p, size_t n) MALLOC_LIKE;
144 /* Allocates and returns N bytes of memory. */
150 void *p = malloc (n);
161 xmemdup (const void *p, size_t n)
163 void *q = xmalloc (n);
168 /* Allocates and returns N * M bytes of memory. */
170 xnmalloc (size_t n, size_t m)
172 if ((size_t) -1 / m <= n)
174 return xmalloc (n * m);
177 /* Node type and support routines. */
179 /* Test data element. */
182 struct hmap_node node; /* Embedded hash table element. */
183 int data; /* Primary value. */
186 /* Returns the `struct element' that NODE is embedded within. */
187 static struct element *
188 hmap_node_to_element (const struct hmap_node *node)
190 return HMAP_DATA (node, struct element, node);
193 /* Compares A and B and returns a strcmp-type return value. */
195 compare_ints (const void *a_, const void *b_)
200 return *a < *b ? -1 : *a > *b;
203 /* Swaps *A and *B. */
205 swap (int *a, int *b)
212 /* Reverses the order of the CNT integers starting at VALUES. */
214 reverse (int *values, size_t cnt)
220 swap (&values[i++], &values[--j]);
223 /* Arranges the CNT elements in VALUES into the lexicographically
224 next greater permutation. Returns true if successful.
225 If VALUES is already the lexicographically greatest
226 permutation of its elements (i.e. ordered from greatest to
227 smallest), arranges them into the lexicographically least
228 permutation (i.e. ordered from smallest to largest) and
231 next_permutation (int *values, size_t cnt)
239 if (values[i] < values[i + 1])
242 for (j = cnt - 1; values[i] >= values[j]; j--)
244 swap (values + i, values + j);
245 reverse (values + (i + 1), cnt - (i + 1));
250 reverse (values, cnt);
258 factorial (unsigned int n)
260 unsigned int value = 1;
266 /* Randomly shuffles the CNT elements in ARRAY, each of which is
267 SIZE bytes in size. */
269 random_shuffle (void *array_, size_t cnt, size_t size)
271 char *array = array_;
272 char *tmp = xmalloc (size);
275 for (i = 0; i < cnt; i++)
277 size_t j = rand () % (cnt - i) + i;
280 memcpy (tmp, array + j * size, size);
281 memcpy (array + j * size, array + i * size, size);
282 memcpy (array + i * size, tmp, size);
289 typedef size_t hash_function (int data);
292 identity_hash (int data)
298 constant_hash (int data UNUSED)
303 static inline uint32_t
304 md4_round (uint32_t a, uint32_t b, uint32_t c, uint32_t d,
305 uint32_t data, uint32_t n)
307 uint32_t x = a + (d ^ (b & (c ^ d))) + data;
308 return (x << n) | (x >> (32 - n));
312 random_hash (int data)
318 a = md4_round (a, b, c, d, 0, 3);
319 d = md4_round (d, a, b, c, 1, 7);
320 c = md4_round (c, d, a, b, 2, 11);
321 b = md4_round (b, c, d, a, 3, 19);
322 return a ^ b ^ c ^ d;
325 static struct hmap_node *
326 find_element (struct hmap *hmap, int data, hash_function *hash)
329 HMAP_FOR_EACH_WITH_HASH (e, struct element, node, hash (data), hmap)
335 /* Checks that HMAP contains the CNT ints in DATA, that its
336 structure is correct, and that certain operations on HMAP
337 produce the expected results. */
339 check_hmap (struct hmap *hmap, const int data[], size_t cnt,
345 check (hmap_count (hmap) == cnt);
346 check (cnt <= hmap_capacity (hmap));
348 order = xmemdup (data, cnt * sizeof *data);
349 qsort (order, cnt, sizeof *order, compare_ints);
351 for (i = 0; i < cnt; i = j)
356 for (j = i + 1; j < cnt; j++)
357 if (order[i] != order[j])
361 HMAP_FOR_EACH_WITH_HASH (e, struct element, node, hash (order[i]), hmap)
362 if (e->data == order[i])
365 check (count == j - i);
368 check (find_element (hmap, -1, hash) == NULL);
371 check (hmap_first (hmap) == NULL);
378 for (p = hmap_first (hmap), i = 0; i < cnt; p = hmap_next (hmap, p), i++)
380 struct element *e = hmap_node_to_element (p);
382 check (hmap_node_hash (&e->node) == hash (e->data));
383 for (j = 0; j < left; j++)
384 if (order[j] == e->data)
386 order[j] = order[--left];
399 /* Inserts the CNT values from 0 to CNT - 1 (inclusive) into an
400 HMAP in the order specified by INSERTIONS, then deletes them in
401 the order specified by DELETIONS, checking the HMAP's contents
402 for correctness after each operation. Uses HASH as the hash
405 test_insert_delete (const int insertions[],
406 const int deletions[],
410 struct element *elements;
414 elements = xnmalloc (cnt, sizeof *elements);
415 for (i = 0; i < cnt; i++)
416 elements[i].data = i;
419 hmap_reserve (&hmap, 1);
420 check_hmap (&hmap, NULL, 0, hash);
421 for (i = 0; i < cnt; i++)
424 hmap_insert (&hmap, &elements[insertions[i]].node, hash (insertions[i]));
425 check_hmap (&hmap, insertions, i + 1, hash);
427 /* A series of insertions should not produce a shrinkable hmap. */
428 capacity = hmap_capacity (&hmap);
430 check (capacity == hmap_capacity (&hmap));
432 for (i = 0; i < cnt; i++)
434 hmap_delete (&hmap, &elements[deletions[i]].node);
435 check_hmap (&hmap, deletions + i + 1, cnt - i - 1, hash);
437 hmap_destroy (&hmap);
442 /* Inserts values into an HMAP in each possible order, then
443 removes them in each possible order, up to a specified maximum
444 size, using hash function HASH. */
446 test_insert_any_remove_any (hash_function *hash)
448 const int max_elems = 5;
451 for (cnt = 0; cnt <= max_elems; cnt++)
453 int *insertions, *deletions;
454 unsigned int ins_perm_cnt;
457 insertions = xnmalloc (cnt, sizeof *insertions);
458 deletions = xnmalloc (cnt, sizeof *deletions);
459 for (i = 0; i < cnt; i++)
462 for (ins_perm_cnt = 0;
463 ins_perm_cnt == 0 || next_permutation (insertions, cnt);
466 unsigned int del_perm_cnt;
469 for (i = 0; i < cnt; i++)
472 for (del_perm_cnt = 0;
473 del_perm_cnt == 0 || next_permutation (deletions, cnt);
475 test_insert_delete (insertions, deletions, cnt, hash);
477 check (del_perm_cnt == factorial (cnt));
479 check (ins_perm_cnt == factorial (cnt));
487 test_insert_any_remove_any_random_hash (void)
489 test_insert_any_remove_any (random_hash);
493 test_insert_any_remove_any_identity_hash (void)
495 test_insert_any_remove_any (identity_hash);
499 test_insert_any_remove_any_constant_hash (void)
501 test_insert_any_remove_any (constant_hash);
504 /* Inserts values into an HMAP in each possible order, then
505 removes them in the same order, up to a specified maximum
506 size, using hash function HASH. */
508 test_insert_any_remove_same (hash_function *hash)
510 const int max_elems = 7;
513 for (cnt = 0; cnt <= max_elems; cnt++)
516 unsigned int permutation_cnt;
519 values = xnmalloc (cnt, sizeof *values);
520 for (i = 0; i < cnt; i++)
523 for (permutation_cnt = 0;
524 permutation_cnt == 0 || next_permutation (values, cnt);
526 test_insert_delete (values, values, cnt, hash);
527 check (permutation_cnt == factorial (cnt));
534 test_insert_any_remove_same_random_hash (void)
536 test_insert_any_remove_same (random_hash);
540 test_insert_any_remove_same_identity_hash (void)
542 test_insert_any_remove_same (identity_hash);
546 test_insert_any_remove_same_constant_hash (void)
548 test_insert_any_remove_same (constant_hash);
551 /* Inserts values into an HMAP in each possible order, then
552 removes them in reverse order, up to a specified maximum
553 size, using hash function HASH. */
555 test_insert_any_remove_reverse (hash_function *hash)
557 const int max_elems = 7;
560 for (cnt = 0; cnt <= max_elems; cnt++)
562 int *insertions, *deletions;
563 unsigned int permutation_cnt;
566 insertions = xnmalloc (cnt, sizeof *insertions);
567 deletions = xnmalloc (cnt, sizeof *deletions);
568 for (i = 0; i < cnt; i++)
571 for (permutation_cnt = 0;
572 permutation_cnt == 0 || next_permutation (insertions, cnt);
575 memcpy (deletions, insertions, sizeof *insertions * cnt);
576 reverse (deletions, cnt);
578 test_insert_delete (insertions, deletions, cnt, hash);
580 check (permutation_cnt == factorial (cnt));
588 test_insert_any_remove_reverse_random_hash (void)
590 test_insert_any_remove_reverse (random_hash);
594 test_insert_any_remove_reverse_identity_hash (void)
596 test_insert_any_remove_reverse (identity_hash);
600 test_insert_any_remove_reverse_constant_hash (void)
602 test_insert_any_remove_reverse (constant_hash);
605 /* Inserts and removes up to MAX_ELEMS values in an hmap, in
606 random order, using hash function HASH. */
608 test_random_sequence (int max_elems, hash_function *hash)
610 const int max_trials = 8;
613 for (cnt = 0; cnt <= max_elems; cnt += 2)
615 int *insertions, *deletions;
619 insertions = xnmalloc (cnt, sizeof *insertions);
620 deletions = xnmalloc (cnt, sizeof *deletions);
621 for (i = 0; i < cnt; i++)
623 for (i = 0; i < cnt; i++)
626 for (trial = 0; trial < max_trials; trial++)
628 random_shuffle (insertions, cnt, sizeof *insertions);
629 random_shuffle (deletions, cnt, sizeof *deletions);
631 test_insert_delete (insertions, deletions, cnt, hash);
640 test_random_sequence_random_hash (void)
642 test_random_sequence (64, random_hash);
646 test_random_sequence_identity_hash (void)
648 test_random_sequence (64, identity_hash);
652 test_random_sequence_constant_hash (void)
654 test_random_sequence (32, constant_hash);
657 /* Inserts MAX_ELEMS elements into an HMAP in ascending order,
658 then delete in ascending order and shrink the hmap at each
659 step, using hash function HASH. */
661 test_insert_ordered (int max_elems, hash_function *hash)
663 struct element *elements;
669 elements = xnmalloc (max_elems, sizeof *elements);
670 values = xnmalloc (max_elems, sizeof *values);
671 for (i = 0; i < max_elems; i++)
673 values[i] = elements[i].data = i;
674 hmap_insert (&hmap, &elements[i].node, hash (elements[i].data));
675 check_hmap (&hmap, values, i + 1, hash);
677 if (hash == identity_hash)
679 /* Check that every every hash bucket has (almost) the
680 same number of nodes in it. */
685 for (j = 0; j <= hmap.mask; j++)
688 struct hmap_node *node;
690 for (node = hmap.buckets[j]; node != NULL; node = node->next)
697 check (max - min <= 1);
700 for (i = 0; i < max_elems; i++)
702 hmap_delete (&hmap, &elements[i].node);
704 check_hmap (&hmap, values + i + 1, max_elems - i - 1, hash);
706 hmap_destroy (&hmap);
712 test_insert_ordered_random_hash (void)
714 test_insert_ordered (1024, random_hash);
718 test_insert_ordered_identity_hash (void)
720 test_insert_ordered (1024, identity_hash);
724 test_insert_ordered_constant_hash (void)
726 test_insert_ordered (128, constant_hash);
729 /* Inserts up to MAX_ELEMS elements into an HMAP, then moves the
730 nodes around in memory, using hash function HASH. */
732 test_moved (int max_elems, hash_function *hash)
734 struct element *e[2];
740 #if __GNUC__ == 4 && __GNUC_MINOR__ == 3
745 e[0] = xnmalloc (max_elems, sizeof *e[0]);
746 e[1] = xnmalloc (max_elems, sizeof *e[1]);
747 values = xnmalloc (max_elems, sizeof *values);
749 for (i = 0; i < max_elems; i++)
751 values[i] = e[cur][i].data = i;
752 hmap_insert (&hmap, &e[cur][i].node, hash (e[cur][i].data));
753 check_hmap (&hmap, values, i + 1, hash);
755 for (j = 0; j <= i; j++)
757 e[!cur][j] = e[cur][j];
758 hmap_moved (&hmap, &e[!cur][j].node, &e[cur][j].node);
759 check_hmap (&hmap, values, i + 1, hash);
763 hmap_destroy (&hmap);
770 test_moved_random_hash (void)
772 test_moved (128, random_hash);
776 test_moved_identity_hash (void)
778 test_moved (128, identity_hash);
782 test_moved_constant_hash (void)
784 test_moved (32, constant_hash);
787 /* Inserts values into an HMAP, then changes their values, using
788 hash function HASH. */
790 test_changed (hash_function *hash)
792 const int max_elems = 6;
795 for (cnt = 0; cnt <= max_elems; cnt++)
797 int *values, *changed_values;
798 struct element *elements;
799 unsigned int permutation_cnt;
802 values = xnmalloc (cnt, sizeof *values);
803 changed_values = xnmalloc (cnt, sizeof *changed_values);
804 elements = xnmalloc (cnt, sizeof *elements);
805 for (i = 0; i < cnt; i++)
808 for (permutation_cnt = 0;
809 permutation_cnt == 0 || next_permutation (values, cnt);
812 for (i = 0; i < cnt; i++)
815 for (j = 0; j <= cnt; j++)
821 /* Add to HMAP in order. */
822 for (k = 0; k < cnt; k++)
825 elements[n].data = n;
826 hmap_insert (&hmap, &elements[n].node,
827 hash (elements[n].data));
829 check_hmap (&hmap, values, cnt, hash);
831 /* Change value i to j. */
832 elements[i].data = j;
833 hmap_changed (&hmap, &elements[i].node,
834 hash (elements[i].data));
835 for (k = 0; k < cnt; k++)
836 changed_values[k] = k;
837 changed_values[i] = j;
838 check_hmap (&hmap, changed_values, cnt, hash);
840 hmap_destroy (&hmap);
844 check (permutation_cnt == factorial (cnt));
847 free (changed_values);
853 test_changed_random_hash (void)
855 test_changed (random_hash);
859 test_changed_identity_hash (void)
861 test_changed (identity_hash);
865 test_changed_constant_hash (void)
867 test_changed (constant_hash);
871 test_swap (int max_elems, hash_function *hash)
873 struct element *elements;
876 struct hmap *working, *empty;
879 #if __GNUC__ == 4 && __GNUC_MINOR__ == 3
887 elements = xnmalloc (max_elems, sizeof *elements);
888 values = xnmalloc (max_elems, sizeof *values);
889 for (i = 0; i < max_elems; i++)
892 values[i] = elements[i].data = i;
893 hmap_insert (working, &elements[i].node, hash (elements[i].data));
894 check_hmap (working, values, i + 1, hash);
895 check_hmap (empty, NULL, 0, hash);
908 test_swap_random_hash (void)
910 test_swap (128, random_hash);
914 test_destroy_null (void)
919 /* Test shrinking an empty hash table. */
921 test_shrink_empty (void)
926 hmap_reserve (&hmap, 123);
928 hmap_destroy (&hmap);
933 /* Runs TEST_FUNCTION and prints a message about NAME. */
935 run_test (void (*test_function) (void), const char *name)
946 run_test (test_insert_any_remove_any_random_hash,
947 "insert any order, delete any order (random hash)");
948 run_test (test_insert_any_remove_any_identity_hash,
949 "insert any order, delete any order (identity hash)");
950 run_test (test_insert_any_remove_any_constant_hash,
951 "insert any order, delete any order (constant hash)");
953 run_test (test_insert_any_remove_same_random_hash,
954 "insert any order, delete same order (random hash)");
955 run_test (test_insert_any_remove_same_identity_hash,
956 "insert any order, delete same order (identity hash)");
957 run_test (test_insert_any_remove_same_constant_hash,
958 "insert any order, delete same order (constant hash)");
960 run_test (test_insert_any_remove_reverse_random_hash,
961 "insert any order, delete reverse order (random hash)");
962 run_test (test_insert_any_remove_reverse_identity_hash,
963 "insert any order, delete reverse order (identity hash)");
964 run_test (test_insert_any_remove_reverse_constant_hash,
965 "insert any order, delete reverse order (constant hash)");
967 run_test (test_random_sequence_random_hash,
968 "insert and delete in random sequence (random hash)");
969 run_test (test_random_sequence_identity_hash,
970 "insert and delete in random sequence (identity hash)");
971 run_test (test_random_sequence_constant_hash,
972 "insert and delete in random sequence (constant hash)");
974 run_test (test_insert_ordered_random_hash,
975 "insert in ascending order (random hash)");
976 run_test (test_insert_ordered_identity_hash,
977 "insert in ascending order (identity hash)");
978 run_test (test_insert_ordered_constant_hash,
979 "insert in ascending order (constant hash)");
981 run_test (test_moved_random_hash,
982 "move elements around in memory (random hash)");
983 run_test (test_moved_identity_hash,
984 "move elements around in memory (identity hash)");
985 run_test (test_moved_constant_hash,
986 "move elements around in memory (constant hash)");
988 run_test (test_changed_random_hash,
989 "change key data in nodes (random hash)");
990 run_test (test_changed_identity_hash,
991 "change key data in nodes (identity hash)");
992 run_test (test_changed_constant_hash,
993 "change key data in nodes (constant hash)");
995 run_test (test_swap_random_hash, "test swapping tables");
997 run_test (test_destroy_null, "test destroying null table");
998 run_test (test_shrink_empty, "test shrinking an empty table");
1002 #if __GNUC__ == 4 && __GNUC_MINOR__ == 3
1003 /* We skipped some of the tests, so return a value that
1004 Automake will interpret as "skipped", instead of one that