Windows build: Adapted icon names to org.fsf.pspp naming
[pspp.git] / tests / libpspp / stringi-map-test.c
blob499e8b33b6d8385772d6421fedad57ff95d7d6c7
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. */
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include "libpspp/stringi-map.h"
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>
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.) */
49 static void
50 check_die (void)
52 exit (EXIT_FAILURE);
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)
60 if (!ok)
62 fprintf (stderr, "%s:%d: check failed\n", __FILE__, line);
63 check_die ();
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__)
73 /* Support routines. */
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
84 static char *string_table[MAX_IDX];
86 static const char *
87 get_string (int idx)
89 char **s;
91 assert (idx >= 0 && idx < MAX_IDX);
92 s = &string_table[idx];
93 if (*s == NULL)
95 *s = xmalloc (16);
96 str_format_26adic (idx + 1, true, *s, 16);
98 return *s;
101 static void
102 free_strings (void)
104 int i;
106 for (i = 0; i < MAX_IDX; i++)
107 free (string_table[i]);
110 static const char *
111 make_key (int value)
113 return get_string ((value & KEY_MASK) >> KEY_SHIFT);
116 static const char *
117 make_value (int value)
119 return get_string ((value & VALUE_MASK) >> VALUE_SHIFT);
122 static int
123 random_value (unsigned int seed, int basis)
125 return hash_int (seed, basis) & VALUE_MASK;
128 /* Swaps *A and *B. */
129 static void
130 swap (int *a, int *b)
132 int t = *a;
133 *a = *b;
134 *b = t;
137 /* Reverses the order of the N integers starting at VALUES. */
138 static void
139 reverse (int *values, size_t n)
141 size_t i = 0;
142 size_t j = n;
144 while (j > i)
145 swap (&values[i++], &values[--j]);
148 /* Arranges the N 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. */
155 static bool
156 next_permutation (int *values, size_t n)
158 if (n > 0)
160 size_t i = n - 1;
161 while (i != 0)
163 i--;
164 if ((values[i] & KEY_MASK) < (values[i + 1] & KEY_MASK))
166 size_t j;
167 for (j = n - 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), n - (i + 1));
173 return true;
177 reverse (values, n);
180 return false;
183 /* Returns N!. */
184 static unsigned int
185 factorial (unsigned int n)
187 unsigned int value = 1;
188 while (n > 1)
189 value *= n--;
190 return value;
193 /* Randomly shuffles the N elements in ARRAY, each of which is
194 SIZE bytes in size. */
195 static void
196 random_shuffle (void *array_, size_t n, size_t size)
198 char *array = array_;
199 char *tmp = xmalloc (size);
200 size_t i;
202 for (i = 0; i < n; i++)
204 size_t j = rand () % (n - i) + i;
205 if (i != j)
207 memcpy (tmp, array + j * size, size);
208 memcpy (array + j * size, array + i * size, size);
209 memcpy (array + i * size, tmp, size);
213 free (tmp);
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)
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, 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)));
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),
235 xstrdup ("345")));
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 N 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 n)
249 size_t i;
251 check (stringi_map_is_empty (map) == (n == 0));
252 check (stringi_map_count (map) == n);
254 for (i = 0; i < n; i++)
256 const char *key = make_key (data[i]);
257 const char *value = make_value (data[i]);
258 char copy[16];
259 char *p;
261 check_map_contains (map, key, value);
263 strcpy (copy, key);
264 for (p = copy; *p != '\0'; p++)
266 assert (isupper (*p));
267 *p = tolower (*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, "", 0) == NULL);
275 check (!stringi_map_delete (map, "xyz"));
277 if (n == 0)
278 check (stringi_map_first (map) == NULL);
279 else
281 const struct stringi_map_node *node;
282 int *data_copy;
283 int left;
285 data_copy = xmemdup (data, n * sizeof *data);
286 left = n;
287 for (node = stringi_map_first (map), i = 0; i < n;
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);
292 size_t j;
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];
299 goto next;
301 check_die ();
303 next: ;
305 check (node == NULL);
306 free (data_copy);
310 /* Inserts the N strings from 0 to N - 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 n)
319 struct stringi_map map;
320 size_t i;
322 stringi_map_init (&map);
323 check_stringi_map (&map, NULL, 0);
324 for (i = 0; i < n; 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 < n; i++)
332 check (stringi_map_delete (&map, make_key (deletions[i])));
333 check_stringi_map (&map, deletions + i + 1, n - 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. */
340 static void
341 test_insert_any_remove_any (void)
343 const int basis = 0;
344 const int max_elems = 5;
345 int n;
347 for (n = 0; n <= max_elems; n++)
349 int *insertions, *deletions;
350 unsigned int ins_n_perms;
351 int i;
353 insertions = xnmalloc (n, sizeof *insertions);
354 deletions = xnmalloc (n, sizeof *deletions);
355 for (i = 0; i < n; i++)
356 insertions[i] = i | random_value (i, basis);
358 for (ins_n_perms = 0;
359 ins_n_perms == 0 || next_permutation (insertions, n);
360 ins_n_perms++)
362 unsigned int del_n_perms;
363 int i;
365 for (i = 0; i < n; i++)
366 deletions[i] = i | random_value (i, basis);
368 for (del_n_perms = 0;
369 del_n_perms == 0 || next_permutation (deletions, n);
370 del_n_perms++)
371 test_insert_delete (insertions, deletions, n);
373 check (del_n_perms == factorial (n));
375 check (ins_n_perms == factorial (n));
377 free (insertions);
378 free (deletions);
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)
387 const int max_elems = 7;
388 int n;
390 for (n = 0; n <= max_elems; n++)
392 int *values;
393 unsigned int n_permutations;
394 int i;
396 values = xnmalloc (n, sizeof *values);
397 for (i = 0; i < n; i++)
398 values[i] = i | random_value (i, 1);
400 for (n_permutations = 0;
401 n_permutations == 0 || next_permutation (values, n);
402 n_permutations++)
403 test_insert_delete (values, values, n);
404 check (n_permutations == factorial (n));
406 free (values);
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)
416 const int max_elems = 7;
417 int n;
419 for (n = 0; n <= max_elems; n++)
421 int *insertions, *deletions;
422 unsigned int n_permutations;
423 int i;
425 insertions = xnmalloc (n, sizeof *insertions);
426 deletions = xnmalloc (n, sizeof *deletions);
427 for (i = 0; i < n; i++)
428 insertions[i] = i | random_value (i, 2);
430 for (n_permutations = 0;
431 n_permutations == 0 || next_permutation (insertions, n);
432 n_permutations++)
434 memcpy (deletions, insertions, sizeof *insertions * n);
435 reverse (deletions, n);
437 test_insert_delete (insertions, deletions, n);
439 check (n_permutations == factorial (n));
441 free (insertions);
442 free (deletions);
446 /* Inserts and removes strings in a map, in random order. */
447 static void
448 test_random_sequence (void)
450 const int basis = 3;
451 const int max_elems = 64;
452 const int max_trials = 8;
453 int n;
455 for (n = 0; n <= max_elems; n += 2)
457 int *insertions, *deletions;
458 int trial;
459 int i;
461 insertions = xnmalloc (n, sizeof *insertions);
462 deletions = xnmalloc (n, sizeof *deletions);
463 for (i = 0; i < n; i++)
464 insertions[i] = i | random_value (i, basis);
465 for (i = 0; i < n; i++)
466 deletions[i] = i | random_value (i, basis);
468 for (trial = 0; trial < max_trials; trial++)
470 random_shuffle (insertions, n, sizeof *insertions);
471 random_shuffle (deletions, n, sizeof *deletions);
473 test_insert_delete (insertions, deletions, n);
476 free (insertions);
477 free (deletions);
481 /* Inserts strings into a map in ascending order, then delete in ascending
482 order. */
483 static void
484 test_insert_ordered (void)
486 const int max_elems = 64;
487 int *values;
488 struct stringi_map map;
489 int i;
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);
506 free (values);
509 /* Inserts and replaces strings in a map, in random order. */
510 static void
511 test_replace (void)
513 const int basis = 15;
514 enum { MAX_ELEMS = 16 };
515 const int max_trials = 8;
516 int n;
518 for (n = 0; n <= MAX_ELEMS; n++)
520 int insertions[MAX_ELEMS];
521 int trial;
522 int i;
524 for (i = 0; i < n; i++)
525 insertions[i] = (i / 2) | random_value (i, basis);
527 for (trial = 0; trial < max_trials; trial++)
529 struct stringi_map map;
530 int data[MAX_ELEMS];
531 int n_data;
533 /* Insert with replacement in random order. */
534 n_data = 0;
535 stringi_map_init (&map);
536 random_shuffle (insertions, n, sizeof *insertions);
537 for (i = 0; i < n; i++)
539 const char *key = make_key (insertions[i]);
540 const char *value = make_value (insertions[i]);
541 int j;
543 for (j = 0; j < n_data; j++)
544 if ((data[j] & KEY_MASK) == (insertions[i] & KEY_MASK))
546 data[j] = insertions[i];
547 goto found;
549 data[n_data++] = insertions[i];
550 found:
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);
560 /* Delete in original order. */
561 for (i = 0; i < n; i++)
563 const char *expected_value;
564 char *value;
565 int j;
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];
573 break;
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);
582 assert (stringi_map_is_empty (&map));
584 stringi_map_destroy (&map);
589 static void
590 make_patterned_map (struct stringi_map *map, unsigned int pattern, int basis,
591 int insertions[], int *np)
593 int n;
594 int i;
596 stringi_map_init (map);
598 n = 0;
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])));
606 n++;
608 check_stringi_map (map, insertions, n);
610 *np = n;
613 static void
614 for_each_map (void (*cb)(struct stringi_map *, int data[], int n),
615 int basis)
617 enum { MAX_ELEMS = 5 };
618 unsigned int pattern;
620 for (pattern = 0; pattern < (1u << MAX_ELEMS); pattern++)
622 int data[MAX_ELEMS];
623 struct stringi_map map;
624 int n;
626 make_patterned_map (&map, pattern, basis, data, &n);
627 (*cb) (&map, data, n);
628 stringi_map_destroy (&map);
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)
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;
646 int n_a, n_b;
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);
656 static void
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);
663 static void
664 test_clear (void)
666 for_each_map (clear_cb, 5);
669 static void
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);
679 static void
680 test_clone (void)
682 for_each_map (clone_cb, 6);
685 static void
686 node_swap_value_cb (struct stringi_map *map, int data[], int n)
688 int i;
690 for (i = 0; i < n; i++)
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;
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);
708 static void
709 test_node_swap_value (void)
711 for_each_map (node_swap_value_cb, 14);
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)
718 stringi_map_swap (a, b);
719 check_stringi_map (a, b_data, n_b);
720 check_stringi_map (b, a_data, n_a);
723 static void
724 test_swap (void)
726 for_each_pair_of_maps (swap_cb, 7, 8);
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)
733 int i, j;
735 stringi_map_insert_map (a, b);
737 for (i = 0; i < n_b; i++)
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:;
745 check_stringi_map (a, a_data, n_a);
746 check_stringi_map (b, b_data, n_b);
749 static void
750 test_insert_map (void)
752 for_each_pair_of_maps (insert_map_cb, 91, 10);
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)
759 int i, j;
761 stringi_map_replace_map (a, b);
763 for (i = 0; i < n_b; i++)
765 for (j = 0; j < n_a; j++)
766 if ((b_data[i] & KEY_MASK) == (a_data[j] & KEY_MASK))
768 a_data[j] = (a_data[j] & KEY_MASK) | (b_data[i] & VALUE_MASK);
769 goto found;
771 a_data[n_a++] = b_data[i];
772 found:;
774 check_stringi_map (a, a_data, n_a);
775 check_stringi_map (b, b_data, n_b);
778 static void
779 test_replace_map (void)
781 for_each_pair_of_maps (replace_map_cb, 11, 12);
784 static void
785 check_iset (struct stringi_set *set, const int *data, int n_data,
786 int mask, int shift)
788 int *unique;
789 int n_unique;
790 int i;
792 n_unique = 0;
793 unique = xmalloc (n_data * sizeof *unique);
794 for (i = 0; i < n_data; i++)
796 int idx = (data[i] & mask) >> shift;
797 int j;
799 for (j = 0; j < n_unique; j++)
800 if (unique[j] == idx)
801 goto found;
802 unique[n_unique++] = idx;
803 found:;
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);
813 static void
814 check_set (struct string_set *set, const int *data, int n_data,
815 int mask, int shift)
817 int *unique;
818 int n_unique;
819 int i;
821 n_unique = 0;
822 unique = xmalloc (n_data * sizeof *unique);
823 for (i = 0; i < n_data; i++)
825 int idx = (data[i] & mask) >> shift;
826 int j;
828 for (j = 0; j < n_unique; j++)
829 if (unique[j] == idx)
830 goto found;
831 unique[n_unique++] = idx;
832 found:;
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);
842 static void
843 get_keys_and_values_cb (struct stringi_map *map, int data[], int n)
845 struct stringi_set keys;
846 struct string_set values;
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);
856 static void
857 test_get_keys_and_values (void)
859 for_each_map (get_keys_and_values_cb, 13);
862 static void
863 test_destroy_null (void)
865 stringi_map_destroy (NULL);
868 /* Main program. */
870 struct test
872 const char *name;
873 const char *description;
874 void (*function) (void);
877 static const struct test tests[] =
880 "insert-any-remove-any",
881 "insert any order, delete any order",
882 test_insert_any_remove_any
885 "insert-any-remove-same",
886 "insert any order, delete same order",
887 test_insert_any_remove_same
890 "insert-any-remove-reverse",
891 "insert any order, delete reverse order",
892 test_insert_any_remove_reverse
895 "random-sequence",
896 "insert and delete in random sequence",
897 test_random_sequence
900 "replace",
901 "insert and replace in random sequence",
902 test_replace
905 "insert-ordered",
906 "insert in ascending order",
907 test_insert_ordered
910 "clear",
911 "clear",
912 test_clear
915 "clone",
916 "clone",
917 test_clone
920 "swap",
921 "swap",
922 test_swap
925 "node-swap-value",
926 "node_swap_value",
927 test_node_swap_value
930 "insert-map",
931 "insert_map",
932 test_insert_map
935 "replace-map",
936 "replace_map",
937 test_replace_map
940 "get-keys-and-values",
941 "get keys and values",
942 test_get_keys_and_values
945 "destroy-null",
946 "destroying null table",
947 test_destroy_null
951 enum { N_TESTS = sizeof tests / sizeof *tests };
954 main (int argc, char *argv[])
956 int i;
958 if (argc != 2)
960 fprintf (stderr, "exactly one argument required; use --help for help\n");
961 return EXIT_FAILURE;
963 else if (!strcmp (argv[1], "--help"))
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;
973 else
975 for (i = 0; i < N_TESTS; i++)
976 if (!strcmp (argv[1], tests[i].name))
978 tests[i].function ();
979 free_strings ();
980 return 0;
983 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);
984 return EXIT_FAILURE;