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. */
28 #include "libpspp/stringi-map.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.) */
55 /* If OK is not true, prints a message about failure on the
56 current source file and the given LINE and terminates. */
58 check_func (bool ok
, int line
)
62 fprintf (stderr
, "%s:%d: check failed\n", __FILE__
, line
);
67 /* Verifies that EXPR evaluates to true.
68 If not, prints a message citing the calling line number and
70 #define check(EXPR) check_func ((EXPR), __LINE__)
73 /* Support routines. */
77 MAX_IDX
= 1 << IDX_BITS
,
78 KEY_MASK
= (MAX_IDX
- 1),
80 VALUE_MASK
= (MAX_IDX
- 1) << IDX_BITS
,
81 VALUE_SHIFT
= IDX_BITS
84 static char *string_table
[MAX_IDX
];
91 assert (idx
>= 0 && idx
< MAX_IDX
);
92 s
= &string_table
[idx
];
96 str_format_26adic (idx
+ 1, true, *s
, 16);
106 for (i
= 0; i
< MAX_IDX
; i
++)
107 free (string_table
[i
]);
113 return get_string ((value
& KEY_MASK
) >> KEY_SHIFT
);
117 make_value (int value
)
119 return get_string ((value
& VALUE_MASK
) >> VALUE_SHIFT
);
123 random_value (unsigned int seed
, int basis
)
125 return hash_int (seed
, basis
) & VALUE_MASK
;
128 /* Swaps *A and *B. */
130 swap (int *a
, int *b
)
137 /* Reverses the order of the N integers starting at VALUES. */
139 reverse (int *values
, size_t n
)
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. */
156 next_permutation (int *values
, size_t n
)
164 if ((values
[i
] & KEY_MASK
) < (values
[i
+ 1] & KEY_MASK
))
168 (values
[i
] & KEY_MASK
) >= (values
[j
] & KEY_MASK
);
171 swap (values
+ i
, values
+ j
);
172 reverse (values
+ (i
+ 1), n
- (i
+ 1));
185 factorial (unsigned int n
)
187 unsigned int value
= 1;
193 /* Randomly shuffles the N elements in ARRAY, each of which is
194 SIZE bytes in size. */
196 random_shuffle (void *array_
, size_t n
, size_t size
)
198 char *array
= array_
;
199 char *tmp
= xmalloc (size
);
202 for (i
= 0; i
< n
; i
++)
204 size_t j
= rand () % (n
- i
) + i
;
207 memcpy (tmp
, array
+ j
* size
, size
);
208 memcpy (array
+ j
* size
, array
+ i
* size
, size
);
209 memcpy (array
+ i
* size
, tmp
, size
);
216 /* Checks that MAP has (KEY, VALUE) as a pair. */
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
),
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. */
247 check_stringi_map (struct stringi_map
*map
, const int data
[], size_t n
)
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
]);
261 check_map_contains (map
, key
, value
);
264 for (p
= copy
; *p
!= '\0'; p
++)
266 assert (isupper (*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"));
278 check (stringi_map_first (map
) == NULL
);
281 const struct stringi_map_node
*node
;
285 data_copy
= xmemdup (data
, n
* sizeof *data
);
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
);
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
];
305 check (node
== NULL
);
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
315 test_insert_delete (const int insertions
[],
316 const int deletions
[],
319 struct stringi_map map
;
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. */
341 test_insert_any_remove_any (void)
344 const int max_elems
= 5;
347 for (n
= 0; n
<= max_elems
; n
++)
349 int *insertions
, *deletions
;
350 unsigned int ins_n_perms
;
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
);
362 unsigned int del_n_perms
;
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
);
371 test_insert_delete (insertions
, deletions
, n
);
373 check (del_n_perms
== factorial (n
));
375 check (ins_n_perms
== factorial (n
));
382 /* Inserts strings into a map in each possible order, then removes them in the
383 same order, up to a specified maximum size. */
385 test_insert_any_remove_same (void)
387 const int max_elems
= 7;
390 for (n
= 0; n
<= max_elems
; n
++)
393 unsigned int n_permutations
;
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
);
403 test_insert_delete (values
, values
, n
);
404 check (n_permutations
== factorial (n
));
410 /* Inserts strings into a map in each possible order, then
411 removes them in reverse order, up to a specified maximum
414 test_insert_any_remove_reverse (void)
416 const int max_elems
= 7;
419 for (n
= 0; n
<= max_elems
; n
++)
421 int *insertions
, *deletions
;
422 unsigned int n_permutations
;
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
);
434 memcpy (deletions
, insertions
, sizeof *insertions
* n
);
435 reverse (deletions
, n
);
437 test_insert_delete (insertions
, deletions
, n
);
439 check (n_permutations
== factorial (n
));
446 /* Inserts and removes strings in a map, in random order. */
448 test_random_sequence (void)
451 const int max_elems
= 64;
452 const int max_trials
= 8;
455 for (n
= 0; n
<= max_elems
; n
+= 2)
457 int *insertions
, *deletions
;
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
);
481 /* Inserts strings into a map in ascending order, then delete in ascending
484 test_insert_ordered (void)
486 const int max_elems
= 64;
488 struct stringi_map map
;
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
);
509 /* Inserts and replaces strings in a map, in random order. */
513 const int basis
= 15;
514 enum { MAX_ELEMS
= 16 };
515 const int max_trials
= 8;
518 for (n
= 0; n
<= MAX_ELEMS
; n
++)
520 int insertions
[MAX_ELEMS
];
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
;
533 /* Insert with replacement in random order. */
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
]);
543 for (j
= 0; j
< n_data
; j
++)
544 if ((data
[j
] & KEY_MASK
) == (insertions
[i
] & KEY_MASK
))
546 data
[j
] = insertions
[i
];
549 data
[n_data
++] = insertions
[i
];
553 stringi_map_replace (&map
, key
, value
);
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
;
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
];
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
));
582 assert (stringi_map_is_empty (&map
));
584 stringi_map_destroy (&map
);
590 make_patterned_map (struct stringi_map
*map
, unsigned int pattern
, int basis
,
591 int insertions
[], int *np
)
596 stringi_map_init (map
);
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
])));
608 check_stringi_map (map
, insertions
, n
);
614 for_each_map (void (*cb
)(struct stringi_map
*, int data
[], int n
),
617 enum { MAX_ELEMS
= 5 };
618 unsigned int pattern
;
620 for (pattern
= 0; pattern
< (1u << MAX_ELEMS
); pattern
++)
623 struct stringi_map map
;
626 make_patterned_map (&map
, pattern
, basis
, data
, &n
);
627 (*cb
) (&map
, data
, n
);
628 stringi_map_destroy (&map
);
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
;
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
);
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);
666 for_each_map (clear_cb
, 5);
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
);
682 for_each_map (clone_cb
, 6);
686 node_swap_value_cb (struct stringi_map
*map
, int data
[], int n
)
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
;
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
));
709 test_node_swap_value (void)
711 for_each_map (node_swap_value_cb
, 14);
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
);
726 for_each_pair_of_maps (swap_cb
, 7, 8);
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
)
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
))
742 a_data
[n_a
++] = b_data
[i
];
745 check_stringi_map (a
, a_data
, n_a
);
746 check_stringi_map (b
, b_data
, n_b
);
750 test_insert_map (void)
752 for_each_pair_of_maps (insert_map_cb
, 91, 10);
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
)
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
);
771 a_data
[n_a
++] = b_data
[i
];
774 check_stringi_map (a
, a_data
, n_a
);
775 check_stringi_map (b
, b_data
, n_b
);
779 test_replace_map (void)
781 for_each_pair_of_maps (replace_map_cb
, 11, 12);
785 check_iset (struct stringi_set
*set
, const int *data
, int n_data
,
793 unique
= xmalloc (n_data
* sizeof *unique
);
794 for (i
= 0; i
< n_data
; i
++)
796 int idx
= (data
[i
] & mask
) >> shift
;
799 for (j
= 0; j
< n_unique
; j
++)
800 if (unique
[j
] == idx
)
802 unique
[n_unique
++] = idx
;
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
);
814 check_set (struct string_set
*set
, const int *data
, int n_data
,
822 unique
= xmalloc (n_data
* sizeof *unique
);
823 for (i
= 0; i
< n_data
; i
++)
825 int idx
= (data
[i
] & mask
) >> shift
;
828 for (j
= 0; j
< n_unique
; j
++)
829 if (unique
[j
] == idx
)
831 unique
[n_unique
++] = idx
;
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
);
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
);
857 test_get_keys_and_values (void)
859 for_each_map (get_keys_and_values_cb
, 13);
863 test_destroy_null (void)
865 stringi_map_destroy (NULL
);
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
896 "insert and delete in random sequence",
901 "insert and replace in random sequence",
906 "insert in ascending order",
940 "get-keys-and-values",
941 "get keys and values",
942 test_get_keys_and_values
946 "destroying null table",
951 enum { N_TESTS
= sizeof tests
/ sizeof *tests
};
954 main (int argc
, char *argv
[])
960 fprintf (stderr
, "exactly one argument required; use --help for help\n");
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",
969 for (i
= 0; i
< N_TESTS
; i
++)
970 printf (" %s\n %s\n", tests
[i
].name
, tests
[i
].description
);
975 for (i
= 0; i
< N_TESTS
; i
++)
976 if (!strcmp (argv
[1], tests
[i
].name
))
978 tests
[i
].function ();
983 fprintf (stderr
, "unknown test %s; use --help for help\n", argv
[1]);