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_set_* routines defined in
18 stringi-set.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-set.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. */
26 #include "libpspp/stringi-set.h"
38 #include "libpspp/compiler.h"
39 #include "libpspp/str.h"
41 /* Exit with a failure code.
42 (Place a breakpoint on this function while debugging.) */
49 /* If OK is not true, prints a message about failure on the
50 current source file and the given LINE and terminates. */
52 check_func (bool ok, int line)
56 fprintf (stderr, "%s:%d: check failed\n", __FILE__, line);
61 /* Verifies that EXPR evaluates to true.
62 If not, prints a message citing the calling line number and
64 #define check(EXPR) check_func ((EXPR), __LINE__)
66 /* Prints a message about memory exhaustion and exits with a
71 printf ("virtual memory exhausted\n");
75 static void *xmalloc (size_t n) MALLOC_LIKE;
76 static void *xnmalloc (size_t n, size_t m) MALLOC_LIKE;
77 static void *xmemdup (const void *p, size_t n) MALLOC_LIKE;
79 /* Allocates and returns N bytes of memory. */
96 xmemdup (const void *p, size_t n)
98 void *q = xmalloc (n);
105 xstrdup (const char *string)
107 return xmemdup (string, strlen (string) + 1);
110 /* Allocates and returns N * M bytes of memory. */
112 xnmalloc (size_t n, size_t m)
114 if ((size_t) -1 / m <= n)
116 return xmalloc (n * m);
119 /* Support routines. */
121 enum { MAX_VALUE = 1024 };
123 static char *string_table[MAX_VALUE];
126 make_string (int value)
130 assert (value >= 0 && value < MAX_VALUE);
131 s = &string_table[value];
135 str_format_26adic (value + 1, *s, 16);
145 for (i = 0; i < MAX_VALUE; i++)
146 free (string_table[i]);
149 /* Swaps *A and *B. */
151 swap (int *a, int *b)
158 /* Reverses the order of the CNT integers starting at VALUES. */
160 reverse (int *values, size_t cnt)
166 swap (&values[i++], &values[--j]);
169 /* Arranges the CNT elements in VALUES into the lexicographically
170 next greater permutation. Returns true if successful.
171 If VALUES is already the lexicographically greatest
172 permutation of its elements (i.e. ordered from greatest to
173 smallest), arranges them into the lexicographically least
174 permutation (i.e. ordered from smallest to largest) and
177 next_permutation (int *values, size_t cnt)
185 if (values[i] < values[i + 1])
188 for (j = cnt - 1; values[i] >= values[j]; j--)
190 swap (values + i, values + j);
191 reverse (values + (i + 1), cnt - (i + 1));
196 reverse (values, cnt);
204 factorial (unsigned int n)
206 unsigned int value = 1;
212 /* Randomly shuffles the CNT elements in ARRAY, each of which is
213 SIZE bytes in size. */
215 random_shuffle (void *array_, size_t cnt, size_t size)
217 char *array = array_;
218 char *tmp = xmalloc (size);
221 for (i = 0; i < cnt; i++)
223 size_t j = rand () % (cnt - i) + i;
226 memcpy (tmp, array + j * size, size);
227 memcpy (array + j * size, array + i * size, size);
228 memcpy (array + i * size, tmp, size);
235 /* Checks that SET contains STRING. */
237 check_set_contains (struct stringi_set *set, const char *string)
239 struct stringi_set_node *node;
241 check (stringi_set_contains (set, string));
242 check (!stringi_set_insert (set, string));
243 check (!stringi_set_insert_nocopy (set, xstrdup (string)));
245 node = stringi_set_find_node (set, string);
246 check (node != NULL);
247 check (!strcasecmp (string, stringi_set_node_get_string (node)));
250 /* Checks that SET contains the CNT strings in DATA, that its structure is
251 correct, and that certain operations on SET produce the expected results. */
253 check_stringi_set (struct stringi_set *set, const int data[], size_t cnt)
257 check (stringi_set_is_empty (set) == (cnt == 0));
258 check (stringi_set_count (set) == cnt);
260 for (i = 0; i < cnt; i++)
266 s = make_string (data[i]);
267 check_set_contains (set, s);
270 for (p = copy; *p != '\0'; p++)
272 assert (isupper (*p));
274 check_set_contains (set, copy);
278 check (!stringi_set_contains (set, "xxx"));
279 check (stringi_set_find_node (set, "") == NULL);
283 check (stringi_set_first (set) == NULL);
284 free (stringi_set_get_array (set));
288 const struct stringi_set_node *node;
293 array = stringi_set_get_array (set);
294 data_copy = xmemdup (data, cnt * sizeof *data);
296 for (node = stringi_set_first (set), i = 0; i < cnt;
297 node = stringi_set_next (set, node), i++)
299 const char *s = stringi_set_node_get_string (node);
302 check (s == array[i]);
304 for (j = 0; j < left; j++)
305 if (!strcasecmp (s, make_string (data_copy[j])))
307 data_copy[j] = data_copy[--left];
314 check (node == NULL);
318 array = stringi_set_get_sorted_array (set);
319 for (i = 0; i < cnt; i++)
322 check (strcasecmp (array[i - 1], array[i]) < 0);
323 check (stringi_set_contains (set, array[i]));
329 /* Inserts the CNT strings from 0 to CNT - 1 (inclusive) into a set in the
330 order specified by INSERTIONS, then deletes them in the order specified by
331 DELETIONS, checking the set's contents for correctness after each
334 test_insert_delete (const int insertions[],
335 const int deletions[],
338 struct stringi_set set;
341 stringi_set_init (&set);
342 check_stringi_set (&set, NULL, 0);
343 for (i = 0; i < cnt; i++)
345 check (stringi_set_insert (&set, make_string (insertions[i])));
346 check_stringi_set (&set, insertions, i + 1);
348 for (i = 0; i < cnt; i++)
350 check (stringi_set_delete (&set, make_string (deletions[i])));
351 check_stringi_set (&set, deletions + i + 1, cnt - i - 1);
353 stringi_set_destroy (&set);
356 /* Inserts strings into a set in each possible order, then removes them in each
357 possible order, up to a specified maximum size. */
359 test_insert_any_remove_any (void)
361 const int max_elems = 5;
364 for (cnt = 0; cnt <= max_elems; cnt++)
366 int *insertions, *deletions;
367 unsigned int ins_perm_cnt;
370 insertions = xnmalloc (cnt, sizeof *insertions);
371 deletions = xnmalloc (cnt, sizeof *deletions);
372 for (i = 0; i < cnt; i++)
375 for (ins_perm_cnt = 0;
376 ins_perm_cnt == 0 || next_permutation (insertions, cnt);
379 unsigned int del_perm_cnt;
382 for (i = 0; i < cnt; i++)
385 for (del_perm_cnt = 0;
386 del_perm_cnt == 0 || next_permutation (deletions, cnt);
388 test_insert_delete (insertions, deletions, cnt);
390 check (del_perm_cnt == factorial (cnt));
392 check (ins_perm_cnt == factorial (cnt));
399 /* Inserts strings into a set in each possible order, then removes them in the
400 same order, up to a specified maximum size. */
402 test_insert_any_remove_same (void)
404 const int max_elems = 7;
407 for (cnt = 0; cnt <= max_elems; cnt++)
410 unsigned int permutation_cnt;
413 values = xnmalloc (cnt, sizeof *values);
414 for (i = 0; i < cnt; i++)
417 for (permutation_cnt = 0;
418 permutation_cnt == 0 || next_permutation (values, cnt);
420 test_insert_delete (values, values, cnt);
421 check (permutation_cnt == factorial (cnt));
427 /* Inserts strings into a set in each possible order, then
428 removes them in reverse order, up to a specified maximum
431 test_insert_any_remove_reverse (void)
433 const int max_elems = 7;
436 for (cnt = 0; cnt <= max_elems; cnt++)
438 int *insertions, *deletions;
439 unsigned int permutation_cnt;
442 insertions = xnmalloc (cnt, sizeof *insertions);
443 deletions = xnmalloc (cnt, sizeof *deletions);
444 for (i = 0; i < cnt; i++)
447 for (permutation_cnt = 0;
448 permutation_cnt == 0 || next_permutation (insertions, cnt);
451 memcpy (deletions, insertions, sizeof *insertions * cnt);
452 reverse (deletions, cnt);
454 test_insert_delete (insertions, deletions, cnt);
456 check (permutation_cnt == factorial (cnt));
463 /* Inserts and removes strings in a set, in random order. */
465 test_random_sequence (void)
467 const int max_elems = 64;
468 const int max_trials = 8;
471 for (cnt = 0; cnt <= max_elems; cnt += 2)
473 int *insertions, *deletions;
477 insertions = xnmalloc (cnt, sizeof *insertions);
478 deletions = xnmalloc (cnt, sizeof *deletions);
479 for (i = 0; i < cnt; i++)
481 for (i = 0; i < cnt; i++)
484 for (trial = 0; trial < max_trials; trial++)
486 random_shuffle (insertions, cnt, sizeof *insertions);
487 random_shuffle (deletions, cnt, sizeof *deletions);
489 test_insert_delete (insertions, deletions, cnt);
497 /* Inserts strings into a set in ascending order, then delete in ascending
500 test_insert_ordered (void)
502 const int max_elems = 64;
504 struct stringi_set set;
507 stringi_set_init (&set);
508 values = xnmalloc (max_elems, sizeof *values);
509 for (i = 0; i < max_elems; i++)
512 stringi_set_insert_nocopy (&set, xstrdup (make_string (i)));
513 check_stringi_set (&set, values, i + 1);
515 for (i = 0; i < max_elems; i++)
517 stringi_set_delete (&set, make_string (i));
518 check_stringi_set (&set, values + i + 1, max_elems - i - 1);
520 stringi_set_destroy (&set);
525 test_boolean_ops (void (*function)(struct stringi_set *a, struct stringi_set *b,
526 unsigned int *a_pat, unsigned int *b_pat))
528 enum { MAX_STRINGS = 7 };
529 unsigned int a_pat, b_pat;
531 for (a_pat = 0; a_pat < (1u << MAX_STRINGS); a_pat++)
532 for (b_pat = 0; b_pat < (1u << MAX_STRINGS); b_pat++)
534 unsigned int new_a_pat = a_pat;
535 unsigned int new_b_pat = b_pat;
536 struct stringi_set a, b;
537 int a_strings[MAX_STRINGS], b_strings[MAX_STRINGS];
540 stringi_set_init (&a);
541 stringi_set_init (&b);
542 for (i = 0; i < MAX_STRINGS; i++)
544 if (a_pat & (1u << i))
545 stringi_set_insert (&a, make_string (i));
546 if (b_pat & (1u << i))
547 stringi_set_insert (&b, make_string (i));
550 function (&a, &b, &new_a_pat, &new_b_pat);
553 for (i = 0; i < MAX_STRINGS; i++)
555 if (new_a_pat & (1u << i))
556 a_strings[n_a++] = i;
557 if (new_b_pat & (1u << i))
558 b_strings[n_b++] = i;
560 check_stringi_set (&a, a_strings, n_a);
561 check_stringi_set (&b, b_strings, n_b);
562 stringi_set_destroy (&a);
563 stringi_set_destroy (&b);
568 union_cb (struct stringi_set *a, struct stringi_set *b,
569 unsigned int *a_pat, unsigned int *b_pat)
571 stringi_set_union (a, b);
578 test_boolean_ops (union_cb);
582 union_and_intersection_cb (struct stringi_set *a, struct stringi_set *b,
583 unsigned int *a_pat, unsigned int *b_pat)
585 unsigned int orig_a_pat = *a_pat;
586 unsigned int orig_b_pat = *b_pat;
588 stringi_set_union_and_intersection (a, b);
589 *a_pat = orig_a_pat | orig_b_pat;
590 *b_pat = orig_a_pat & orig_b_pat;
594 test_union_and_intersection (void)
596 test_boolean_ops (union_and_intersection_cb);
600 intersect_cb (struct stringi_set *a, struct stringi_set *b,
601 unsigned int *a_pat, unsigned int *b_pat)
603 stringi_set_intersect (a, b);
608 test_intersect (void)
610 test_boolean_ops (intersect_cb);
614 subtract_cb (struct stringi_set *a, struct stringi_set *b,
615 unsigned int *a_pat, unsigned int *b_pat)
617 stringi_set_subtract (a, b);
624 test_boolean_ops (subtract_cb);
628 swap_cb (struct stringi_set *a, struct stringi_set *b,
629 unsigned int *a_pat, unsigned int *b_pat)
632 stringi_set_swap (a, b);
641 test_boolean_ops (swap_cb);
645 clear_cb (struct stringi_set *a, struct stringi_set *b UNUSED,
646 unsigned int *a_pat, unsigned int *b_pat UNUSED)
648 stringi_set_clear (a);
655 test_boolean_ops (clear_cb);
659 clone_cb (struct stringi_set *a, struct stringi_set *b,
660 unsigned int *a_pat, unsigned int *b_pat)
662 stringi_set_destroy (a);
663 stringi_set_clone (a, b);
670 test_boolean_ops (clone_cb);
674 test_destroy_null (void)
676 stringi_set_destroy (NULL);
684 const char *description;
685 void (*function) (void);
688 static const struct test tests[] =
691 "insert-any-remove-any",
692 "insert any order, delete any order",
693 test_insert_any_remove_any
696 "insert-any-remove-same",
697 "insert any order, delete same order",
698 test_insert_any_remove_same
701 "insert-any-remove-reverse",
702 "insert any order, delete reverse order",
703 test_insert_any_remove_reverse
707 "insert and delete in random sequence",
712 "insert in ascending order",
721 "union-and-intersection",
722 "union and intersection",
723 test_union_and_intersection
752 "destroying null table",
757 enum { N_TESTS = sizeof tests / sizeof *tests };
760 main (int argc, char *argv[])
766 fprintf (stderr, "exactly one argument required; use --help for help\n");
769 else if (!strcmp (argv[1], "--help"))
771 printf ("%s: test case-insensitive string set library\n"
772 "usage: %s TEST-NAME\n"
773 "where TEST-NAME is one of the following:\n",
775 for (i = 0; i < N_TESTS; i++)
776 printf (" %s\n %s\n", tests[i].name, tests[i].description);
781 for (i = 0; i < N_TESTS; i++)
782 if (!strcmp (argv[1], tests[i].name))
784 tests[i].function ();
789 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);