63b70ba80d0d6380ac3b66dc576c749c4ddfbe54
[pspp] / tests / libpspp / stringi-map-test.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2008, 2009, 2010, 2012, 2014 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
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. */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include "libpspp/stringi-map.h"
29
30 #include <assert.h>
31 #include <ctype.h>
32 #include <limits.h>
33 #include <stdbool.h>
34 #include <stddef.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
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"
46 \f
47 /* Exit with a failure code.
48    (Place a breakpoint on this function while debugging.) */
49 static void
50 check_die (void)
51 {
52   exit (EXIT_FAILURE);
53 }
54
55 /* If OK is not true, prints a message about failure on the
56    current source file and the given LINE and terminates. */
57 static void
58 check_func (bool ok, int line)
59 {
60   if (!ok)
61     {
62       fprintf (stderr, "%s:%d: check failed\n", __FILE__, line);
63       check_die ();
64     }
65 }
66
67 /* Verifies that EXPR evaluates to true.
68    If not, prints a message citing the calling line number and
69    terminates. */
70 #define check(EXPR) check_func ((EXPR), __LINE__)
71
72 \f
73 /* Support routines. */
74
75 enum {
76   IDX_BITS = 10,
77   MAX_IDX = 1 << IDX_BITS,
78   KEY_MASK = (MAX_IDX - 1),
79   KEY_SHIFT = 0,
80   VALUE_MASK = (MAX_IDX - 1) << IDX_BITS,
81   VALUE_SHIFT = IDX_BITS
82 };
83
84 static char *string_table[MAX_IDX];
85
86 static const char *
87 get_string (int idx)
88 {
89   char **s;
90
91   assert (idx >= 0 && idx < MAX_IDX);
92   s = &string_table[idx];
93   if (*s == NULL)
94     {
95       *s = xmalloc (16);
96       str_format_26adic (idx + 1, true, *s, 16);
97     }
98   return *s;
99 }
100
101 static void
102 free_strings (void)
103 {
104   int i;
105
106   for (i = 0; i < MAX_IDX; i++)
107     free (string_table[i]);
108 }
109
110 static const char *
111 make_key (int value)
112 {
113   return get_string ((value & KEY_MASK) >> KEY_SHIFT);
114 }
115
116 static const char *
117 make_value (int value)
118 {
119   return get_string ((value & VALUE_MASK) >> VALUE_SHIFT);
120 }
121
122 static int
123 random_value (unsigned int seed, int basis)
124 {
125   return hash_int (seed, basis) & VALUE_MASK;
126 }
127
128 /* Swaps *A and *B. */
129 static void
130 swap (int *a, int *b)
131 {
132   int t = *a;
133   *a = *b;
134   *b = t;
135 }
136
137 /* Reverses the order of the CNT integers starting at VALUES. */
138 static void
139 reverse (int *values, size_t cnt)
140 {
141   size_t i = 0;
142   size_t j = cnt;
143
144   while (j > i)
145     swap (&values[i++], &values[--j]);
146 }
147
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.
153
154    Comparisons among elements of VALUES consider only the bits in KEY_MASK. */
155 static bool
156 next_permutation (int *values, size_t cnt)
157 {
158   if (cnt > 0)
159     {
160       size_t i = cnt - 1;
161       while (i != 0)
162         {
163           i--;
164           if ((values[i] & KEY_MASK) < (values[i + 1] & KEY_MASK))
165             {
166               size_t j;
167               for (j = cnt - 1;
168                    (values[i] & KEY_MASK) >= (values[j] & KEY_MASK);
169                    j--)
170                 continue;
171               swap (values + i, values + j);
172               reverse (values + (i + 1), cnt - (i + 1));
173               return true;
174             }
175         }
176
177       reverse (values, cnt);
178     }
179
180   return false;
181 }
182
183 /* Returns N!. */
184 static unsigned int
185 factorial (unsigned int n)
186 {
187   unsigned int value = 1;
188   while (n > 1)
189     value *= n--;
190   return value;
191 }
192
193 /* Randomly shuffles the CNT elements in ARRAY, each of which is
194    SIZE bytes in size. */
195 static void
196 random_shuffle (void *array_, size_t cnt, size_t size)
197 {
198   char *array = array_;
199   char *tmp = xmalloc (size);
200   size_t i;
201
202   for (i = 0; i < cnt; i++)
203     {
204       size_t j = rand () % (cnt - i) + i;
205       if (i != j)
206         {
207           memcpy (tmp, array + j * size, size);
208           memcpy (array + j * size, array + i * size, size);
209           memcpy (array + i * size, tmp, size);
210         }
211     }
212
213   free (tmp);
214 }
215
216 /* Checks that MAP has (KEY, VALUE) as a pair. */
217 static void
218 check_map_contains (struct stringi_map *map,
219                     const char *key, const char *value)
220 {
221   struct stringi_map_node *node;
222   const char *found_value;
223
224   check (stringi_map_contains (map, key));
225
226   node = stringi_map_find_node (map, key, strlen (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)));
230
231   check (node == stringi_map_insert (map, key, "012"));
232   check (!strcmp (value, stringi_map_node_get_value (node)));
233
234   check (node == stringi_map_insert_nocopy (map, xstrdup (key),
235                                             xstrdup ("345")));
236   check (!strcmp (value, stringi_map_node_get_value (node)));
237
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));
242 }
243
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. */
246 static void
247 check_stringi_map (struct stringi_map *map, const int data[], size_t cnt)
248 {
249   size_t i;
250
251   check (stringi_map_is_empty (map) == (cnt == 0));
252   check (stringi_map_count (map) == cnt);
253
254   for (i = 0; i < cnt; i++)
255     {
256       const char *key = make_key (data[i]);
257       const char *value = make_value (data[i]);
258       char copy[16];
259       char *p;
260
261       check_map_contains (map, key, value);
262
263       strcpy (copy, key);
264       for (p = copy; *p != '\0'; p++)
265         {
266           assert (isupper (*p));
267           *p = tolower (*p);
268           check_map_contains (map, copy, value);
269         }
270     }
271
272   check (!stringi_map_contains (map, "xxx"));
273   check (stringi_map_find (map, "0") == NULL);
274   check (stringi_map_find_node (map, "", 0) == NULL);
275   check (!stringi_map_delete (map, "xyz"));
276
277   if (cnt == 0)
278     check (stringi_map_first (map) == NULL);
279   else
280     {
281       const struct stringi_map_node *node;
282       int *data_copy;
283       int left;
284
285       data_copy = xmemdup (data, cnt * sizeof *data);
286       left = cnt;
287       for (node = stringi_map_first (map), i = 0; i < cnt;
288            node = stringi_map_next (map, node), i++)
289         {
290           const char *key = stringi_map_node_get_key (node);
291           const char *value = stringi_map_node_get_value (node);
292           size_t j;
293
294           for (j = 0; j < left; j++)
295             if (!strcmp (key, make_key (data_copy[j]))
296                 || !strcmp (value, make_value (data_copy[j])))
297               {
298                 data_copy[j] = data_copy[--left];
299                 goto next;
300               }
301           check_die ();
302
303         next: ;
304         }
305       check (node == NULL);
306       free (data_copy);
307     }
308 }
309
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
313    operation.  */
314 static void
315 test_insert_delete (const int insertions[],
316                     const int deletions[],
317                     size_t cnt)
318 {
319   struct stringi_map map;
320   size_t i;
321
322   stringi_map_init (&map);
323   check_stringi_map (&map, NULL, 0);
324   for (i = 0; i < cnt; i++)
325     {
326       check (stringi_map_insert (&map, make_key (insertions[i]),
327                                 make_value (insertions[i])));
328       check_stringi_map (&map, insertions, i + 1);
329     }
330   for (i = 0; i < cnt; i++)
331     {
332       check (stringi_map_delete (&map, make_key (deletions[i])));
333       check_stringi_map (&map, deletions + i + 1, cnt - i - 1);
334     }
335   stringi_map_destroy (&map);
336 }
337 \f
338 /* Inserts strings into a map in each possible order, then removes them in each
339    possible order, up to a specified maximum size. */
340 static void
341 test_insert_any_remove_any (void)
342 {
343   const int basis = 0;
344   const int max_elems = 5;
345   int cnt;
346
347   for (cnt = 0; cnt <= max_elems; cnt++)
348     {
349       int *insertions, *deletions;
350       unsigned int ins_n_perms;
351       int i;
352
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);
357
358       for (ins_n_perms = 0;
359            ins_n_perms == 0 || next_permutation (insertions, cnt);
360            ins_n_perms++)
361         {
362           unsigned int del_n_perms;
363           int i;
364
365           for (i = 0; i < cnt; i++)
366             deletions[i] = i | random_value (i, basis);
367
368           for (del_n_perms = 0;
369                del_n_perms == 0 || next_permutation (deletions, cnt);
370                del_n_perms++)
371             test_insert_delete (insertions, deletions, cnt);
372
373           check (del_n_perms == factorial (cnt));
374         }
375       check (ins_n_perms == factorial (cnt));
376
377       free (insertions);
378       free (deletions);
379     }
380 }
381
382 /* Inserts strings into a map in each possible order, then removes them in the
383    same order, up to a specified maximum size. */
384 static void
385 test_insert_any_remove_same (void)
386 {
387   const int max_elems = 7;
388   int cnt;
389
390   for (cnt = 0; cnt <= max_elems; cnt++)
391     {
392       int *values;
393       unsigned int n_permutations;
394       int i;
395
396       values = xnmalloc (cnt, sizeof *values);
397       for (i = 0; i < cnt; i++)
398         values[i] = i | random_value (i, 1);
399
400       for (n_permutations = 0;
401            n_permutations == 0 || next_permutation (values, cnt);
402            n_permutations++)
403         test_insert_delete (values, values, cnt);
404       check (n_permutations == factorial (cnt));
405
406       free (values);
407     }
408 }
409
410 /* Inserts strings into a map in each possible order, then
411    removes them in reverse order, up to a specified maximum
412    size. */
413 static void
414 test_insert_any_remove_reverse (void)
415 {
416   const int max_elems = 7;
417   int cnt;
418
419   for (cnt = 0; cnt <= max_elems; cnt++)
420     {
421       int *insertions, *deletions;
422       unsigned int n_permutations;
423       int i;
424
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);
429
430       for (n_permutations = 0;
431            n_permutations == 0 || next_permutation (insertions, cnt);
432            n_permutations++)
433         {
434           memcpy (deletions, insertions, sizeof *insertions * cnt);
435           reverse (deletions, cnt);
436
437           test_insert_delete (insertions, deletions, cnt);
438         }
439       check (n_permutations == factorial (cnt));
440
441       free (insertions);
442       free (deletions);
443     }
444 }
445
446 /* Inserts and removes strings in a map, in random order. */
447 static void
448 test_random_sequence (void)
449 {
450   const int basis = 3;
451   const int max_elems = 64;
452   const int max_trials = 8;
453   int cnt;
454
455   for (cnt = 0; cnt <= max_elems; cnt += 2)
456     {
457       int *insertions, *deletions;
458       int trial;
459       int i;
460
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);
467
468       for (trial = 0; trial < max_trials; trial++)
469         {
470           random_shuffle (insertions, cnt, sizeof *insertions);
471           random_shuffle (deletions, cnt, sizeof *deletions);
472
473           test_insert_delete (insertions, deletions, cnt);
474         }
475
476       free (insertions);
477       free (deletions);
478     }
479 }
480
481 /* Inserts strings into a map in ascending order, then delete in ascending
482    order. */
483 static void
484 test_insert_ordered (void)
485 {
486   const int max_elems = 64;
487   int *values;
488   struct stringi_map map;
489   int i;
490
491   stringi_map_init (&map);
492   values = xnmalloc (max_elems, sizeof *values);
493   for (i = 0; i < max_elems; i++)
494     {
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);
499     }
500   for (i = 0; i < max_elems; i++)
501     {
502       stringi_map_delete (&map, make_key (i));
503       check_stringi_map (&map, values + i + 1, max_elems - i - 1);
504     }
505   stringi_map_destroy (&map);
506   free (values);
507 }
508
509 /* Inserts and replaces strings in a map, in random order. */
510 static void
511 test_replace (void)
512 {
513   const int basis = 15;
514   enum { MAX_ELEMS = 16 };
515   const int max_trials = 8;
516   int cnt;
517
518   for (cnt = 0; cnt <= MAX_ELEMS; cnt++)
519     {
520       int insertions[MAX_ELEMS];
521       int trial;
522       int i;
523
524       for (i = 0; i < cnt; i++)
525         insertions[i] = (i / 2) | random_value (i, basis);
526
527       for (trial = 0; trial < max_trials; trial++)
528         {
529           struct stringi_map map;
530           int data[MAX_ELEMS];
531           int n_data;
532
533           /* Insert with replacement in random order. */
534           n_data = 0;
535           stringi_map_init (&map);
536           random_shuffle (insertions, cnt, sizeof *insertions);
537           for (i = 0; i < cnt; i++)
538             {
539               const char *key = make_key (insertions[i]);
540               const char *value = make_value (insertions[i]);
541               int j;
542
543               for (j = 0; j < n_data; j++)
544                 if ((data[j] & KEY_MASK) == (insertions[i] & KEY_MASK))
545                   {
546                     data[j] = insertions[i];
547                     goto found;
548                   }
549               data[n_data++] = insertions[i];
550             found:
551
552               if (i % 2)
553                 stringi_map_replace (&map, key, value);
554               else
555                 stringi_map_replace_nocopy (&map,
556                                            xstrdup (key), xstrdup (value));
557               check_stringi_map (&map, data, n_data);
558             }
559
560           /* Delete in original order. */
561           for (i = 0; i < cnt; i++)
562             {
563               const char *expected_value;
564               char *value;
565               int j;
566
567               expected_value = NULL;
568               for (j = 0; j < n_data; j++)
569                 if ((data[j] & KEY_MASK) == (insertions[i] & KEY_MASK))
570                   {
571                     expected_value = make_value (data[j]);
572                     data[j] = data[--n_data];
573                     break;
574                   }
575
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));
580               free (value);
581             }
582           assert (stringi_map_is_empty (&map));
583
584           stringi_map_destroy (&map);
585         }
586     }
587 }
588
589 static void
590 make_patterned_map (struct stringi_map *map, unsigned int pattern, int basis,
591                     int insertions[], int *np)
592 {
593   int n;
594   int i;
595
596   stringi_map_init (map);
597
598   n = 0;
599   for (i = 0; pattern != 0; i++)
600     if (pattern & (1u << i))
601       {
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])));
606         n++;
607       }
608   check_stringi_map (map, insertions, n);
609
610   *np = n;
611 }
612
613 static void
614 for_each_map (void (*cb)(struct stringi_map *, int data[], int n),
615               int basis)
616 {
617   enum { MAX_ELEMS = 5 };
618   unsigned int pattern;
619
620   for (pattern = 0; pattern < (1u << MAX_ELEMS); pattern++)
621     {
622       int data[MAX_ELEMS];
623       struct stringi_map map;
624       int n;
625
626       make_patterned_map (&map, pattern, basis, data, &n);
627       (*cb) (&map, data, n);
628       stringi_map_destroy (&map);
629     }
630 }
631
632 static void
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)
637 {
638   enum { MAX_ELEMS = 5 };
639   unsigned int a_pattern, b_pattern;
640
641   for (a_pattern = 0; a_pattern < (1u << MAX_ELEMS); a_pattern++)
642     for (b_pattern = 0; b_pattern < (1u << MAX_ELEMS); b_pattern++)
643       {
644         int a_data[MAX_ELEMS], b_data[MAX_ELEMS];
645         struct stringi_map a_map, b_map;
646         int n_a, n_b;
647
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);
653       }
654 }
655
656 static void
657 clear_cb (struct stringi_map *map, int data[] UNUSED, int n UNUSED)
658 {
659   stringi_map_clear (map);
660   check_stringi_map (map, NULL, 0);
661 }
662
663 static void
664 test_clear (void)
665 {
666   for_each_map (clear_cb, 5);
667 }
668
669 static void
670 clone_cb (struct stringi_map *map, int data[], int n)
671 {
672   struct stringi_map clone;
673
674   stringi_map_clone (&clone, map);
675   check_stringi_map (&clone, data, n);
676   stringi_map_destroy (&clone);
677 }
678
679 static void
680 test_clone (void)
681 {
682   for_each_map (clone_cb, 6);
683 }
684
685 static void
686 node_swap_value_cb (struct stringi_map *map, int data[], int n)
687 {
688   int i;
689
690   for (i = 0; i < n; i++)
691     {
692       const char *key = make_key (data[i]);
693       const char *value = make_value (data[i]);
694       struct stringi_map_node *node;
695       char *old_value;
696
697       node = stringi_map_find_node (map, key, strlen (key));
698       check (node != NULL);
699       check (!strcmp (stringi_map_node_get_value (node), value));
700       data[i] = (data[i] & KEY_MASK) | random_value (i, 15);
701       old_value = stringi_map_node_swap_value (node, make_value (data[i]));
702       check (old_value != NULL);
703       check (!strcmp (value, old_value));
704       free (old_value);
705     }
706 }
707
708 static void
709 test_node_swap_value (void)
710 {
711   for_each_map (node_swap_value_cb, 14);
712 }
713
714 static void
715 swap_cb (struct stringi_map *a, int a_data[], int n_a,
716          struct stringi_map *b, int b_data[], int n_b)
717 {
718   stringi_map_swap (a, b);
719   check_stringi_map (a, b_data, n_b);
720   check_stringi_map (b, a_data, n_a);
721 }
722
723 static void
724 test_swap (void)
725 {
726   for_each_pair_of_maps (swap_cb, 7, 8);
727 }
728
729 static void
730 insert_map_cb (struct stringi_map *a, int a_data[], int n_a,
731                struct stringi_map *b, int b_data[], int n_b)
732 {
733   int i, j;
734
735   stringi_map_insert_map (a, b);
736
737   for (i = 0; i < n_b; i++)
738     {
739       for (j = 0; j < n_a; j++)
740         if ((b_data[i] & KEY_MASK) == (a_data[j] & KEY_MASK))
741           goto found;
742       a_data[n_a++] = b_data[i];
743     found:;
744     }
745   check_stringi_map (a, a_data, n_a);
746   check_stringi_map (b, b_data, n_b);
747 }
748
749 static void
750 test_insert_map (void)
751 {
752   for_each_pair_of_maps (insert_map_cb, 91, 10);
753 }
754
755 static void
756 replace_map_cb (struct stringi_map *a, int a_data[], int n_a,
757                struct stringi_map *b, int b_data[], int n_b)
758 {
759   int i, j;
760
761   stringi_map_replace_map (a, b);
762
763   for (i = 0; i < n_b; i++)
764     {
765       for (j = 0; j < n_a; j++)
766         if ((b_data[i] & KEY_MASK) == (a_data[j] & KEY_MASK))
767           {
768             a_data[j] = (a_data[j] & KEY_MASK) | (b_data[i] & VALUE_MASK);
769             goto found;
770           }
771       a_data[n_a++] = b_data[i];
772     found:;
773     }
774   check_stringi_map (a, a_data, n_a);
775   check_stringi_map (b, b_data, n_b);
776 }
777
778 static void
779 test_replace_map (void)
780 {
781   for_each_pair_of_maps (replace_map_cb, 11, 12);
782 }
783
784 static void
785 check_iset (struct stringi_set *set, const int *data, int n_data,
786            int mask, int shift)
787 {
788   int *unique;
789   int n_unique;
790   int i;
791
792   n_unique = 0;
793   unique = xmalloc (n_data * sizeof *unique);
794   for (i = 0; i < n_data; i++)
795     {
796       int idx = (data[i] & mask) >> shift;
797       int j;
798
799       for (j = 0; j < n_unique; j++)
800         if (unique[j] == idx)
801           goto found;
802       unique[n_unique++] = idx;
803     found:;
804     }
805
806   check (stringi_set_count (set) == n_unique);
807   for (i = 0; i < n_unique; i++)
808     check (stringi_set_contains (set, get_string (unique[i])));
809   stringi_set_destroy (set);
810   free (unique);
811 }
812
813 static void
814 check_set (struct string_set *set, const int *data, int n_data,
815            int mask, int shift)
816 {
817   int *unique;
818   int n_unique;
819   int i;
820
821   n_unique = 0;
822   unique = xmalloc (n_data * sizeof *unique);
823   for (i = 0; i < n_data; i++)
824     {
825       int idx = (data[i] & mask) >> shift;
826       int j;
827
828       for (j = 0; j < n_unique; j++)
829         if (unique[j] == idx)
830           goto found;
831       unique[n_unique++] = idx;
832     found:;
833     }
834
835   check (string_set_count (set) == n_unique);
836   for (i = 0; i < n_unique; i++)
837     check (string_set_contains (set, get_string (unique[i])));
838   string_set_destroy (set);
839   free (unique);
840 }
841
842 static void
843 get_keys_and_values_cb (struct stringi_map *map, int data[], int n)
844 {
845   struct stringi_set keys;
846   struct string_set values;
847
848   stringi_set_init (&keys);
849   string_set_init (&values);
850   stringi_map_get_keys (map, &keys);
851   stringi_map_get_values (map, &values);
852   check_iset (&keys, data, n, KEY_MASK, KEY_SHIFT);
853   check_set (&values, data, n, VALUE_MASK, VALUE_SHIFT);
854 }
855
856 static void
857 test_get_keys_and_values (void)
858 {
859   for_each_map (get_keys_and_values_cb, 13);
860 }
861
862 static void
863 test_destroy_null (void)
864 {
865   stringi_map_destroy (NULL);
866 }
867 \f
868 /* Main program. */
869
870 struct test
871   {
872     const char *name;
873     const char *description;
874     void (*function) (void);
875   };
876
877 static const struct test tests[] =
878   {
879     {
880       "insert-any-remove-any",
881       "insert any order, delete any order",
882       test_insert_any_remove_any
883     },
884     {
885       "insert-any-remove-same",
886       "insert any order, delete same order",
887       test_insert_any_remove_same
888     },
889     {
890       "insert-any-remove-reverse",
891       "insert any order, delete reverse order",
892       test_insert_any_remove_reverse
893     },
894     {
895       "random-sequence",
896       "insert and delete in random sequence",
897       test_random_sequence
898     },
899     {
900       "replace",
901       "insert and replace in random sequence",
902       test_replace
903     },
904     {
905       "insert-ordered",
906       "insert in ascending order",
907       test_insert_ordered
908     },
909     {
910       "clear",
911       "clear",
912       test_clear
913     },
914     {
915       "clone",
916       "clone",
917       test_clone
918     },
919     {
920       "swap",
921       "swap",
922       test_swap
923     },
924     {
925       "node-swap-value",
926       "node_swap_value",
927       test_node_swap_value
928     },
929     {
930       "insert-map",
931       "insert_map",
932       test_insert_map
933     },
934     {
935       "replace-map",
936       "replace_map",
937       test_replace_map
938     },
939     {
940       "get-keys-and-values",
941       "get keys and values",
942       test_get_keys_and_values
943     },
944     {
945       "destroy-null",
946       "destroying null table",
947       test_destroy_null
948     },
949   };
950
951 enum { N_TESTS = sizeof tests / sizeof *tests };
952
953 int
954 main (int argc, char *argv[])
955 {
956   int i;
957
958   if (argc != 2)
959     {
960       fprintf (stderr, "exactly one argument required; use --help for help\n");
961       return EXIT_FAILURE;
962     }
963   else if (!strcmp (argv[1], "--help"))
964     {
965       printf ("%s: test case-insensitive string map library\n"
966               "usage: %s TEST-NAME\n"
967               "where TEST-NAME is one of the following:\n",
968               argv[0], argv[0]);
969       for (i = 0; i < N_TESTS; i++)
970         printf ("  %s\n    %s\n", tests[i].name, tests[i].description);
971       return 0;
972     }
973   else
974     {
975       for (i = 0; i < N_TESTS; i++)
976         if (!strcmp (argv[1], tests[i].name))
977           {
978             tests[i].function ();
979             free_strings ();
980             return 0;
981           }
982
983       fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);
984       return EXIT_FAILURE;
985     }
986 }