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 string_set_* routines defined in
18 string-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 string-set.c, except that one branch caused by hash collision is
21 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/string-set.h>
37 #include <libpspp/compiler.h>
39 /* Exit with a failure code.
40 (Place a breakpoint on this function while debugging.) */
47 /* If OK is not true, prints a message about failure on the
48 current source file and the given LINE and terminates. */
50 check_func (bool ok
, int line
)
54 fprintf (stderr
, "%s:%d: check failed\n", __FILE__
, line
);
59 /* Verifies that EXPR evaluates to true.
60 If not, prints a message citing the calling line number and
62 #define check(EXPR) check_func ((EXPR), __LINE__)
64 /* Prints a message about memory exhaustion and exits with a
69 printf ("virtual memory exhausted\n");
73 static void *xmalloc (size_t n
) MALLOC_LIKE
;
74 static void *xnmalloc (size_t n
, size_t m
) MALLOC_LIKE
;
75 static void *xmemdup (const void *p
, size_t n
) MALLOC_LIKE
;
77 /* Allocates and returns N bytes of memory. */
94 xmemdup (const void *p
, size_t n
)
96 void *q
= xmalloc (n
);
103 xstrdup (const char *string
)
105 return xmemdup (string
, strlen (string
) + 1);
108 /* Allocates and returns N * M bytes of memory. */
110 xnmalloc (size_t n
, size_t m
)
112 if ((size_t) -1 / m
<= n
)
114 return xmalloc (n
* m
);
117 /* Support routines. */
119 enum { MAX_VALUE
= 1024 };
121 static char *string_table
[MAX_VALUE
];
124 make_string (int value
)
128 assert (value
>= 0 && value
< MAX_VALUE
);
129 s
= &string_table
[value
];
133 sprintf (*s
, "%d", value
);
143 for (i
= 0; i
< MAX_VALUE
; i
++)
144 free (string_table
[i
]);
147 /* Swaps *A and *B. */
149 swap (int *a
, int *b
)
156 /* Reverses the order of the CNT integers starting at VALUES. */
158 reverse (int *values
, size_t cnt
)
164 swap (&values
[i
++], &values
[--j
]);
167 /* Arranges the CNT elements in VALUES into the lexicographically
168 next greater permutation. Returns true if successful.
169 If VALUES is already the lexicographically greatest
170 permutation of its elements (i.e. ordered from greatest to
171 smallest), arranges them into the lexicographically least
172 permutation (i.e. ordered from smallest to largest) and
175 next_permutation (int *values
, size_t cnt
)
183 if (values
[i
] < values
[i
+ 1])
186 for (j
= cnt
- 1; values
[i
] >= values
[j
]; j
--)
188 swap (values
+ i
, values
+ j
);
189 reverse (values
+ (i
+ 1), cnt
- (i
+ 1));
194 reverse (values
, cnt
);
202 factorial (unsigned int n
)
204 unsigned int value
= 1;
210 /* Randomly shuffles the CNT elements in ARRAY, each of which is
211 SIZE bytes in size. */
213 random_shuffle (void *array_
, size_t cnt
, size_t size
)
215 char *array
= array_
;
216 char *tmp
= xmalloc (size
);
219 for (i
= 0; i
< cnt
; i
++)
221 size_t j
= rand () % (cnt
- i
) + i
;
224 memcpy (tmp
, array
+ j
* size
, size
);
225 memcpy (array
+ j
* size
, array
+ i
* size
, size
);
226 memcpy (array
+ i
* size
, tmp
, size
);
233 /* Checks that SET contains the CNT strings in DATA, that its structure is
234 correct, and that certain operations on SET produce the expected results. */
236 check_string_set (struct string_set
*set
, const int data
[], size_t cnt
)
240 check (string_set_is_empty (set
) == (cnt
== 0));
241 check (string_set_count (set
) == cnt
);
243 for (i
= 0; i
< cnt
; i
++)
245 struct string_set_node
*node
;
246 const char *s
= make_string (data
[i
]);
248 check (string_set_contains (set
, s
));
249 check (!string_set_insert (set
, s
));
250 check (!string_set_insert_nocopy (set
, xstrdup (s
)));
252 node
= string_set_find_node (set
, s
);
253 check (node
!= NULL
);
254 check (!strcmp (s
, string_set_node_get_string (node
)));
257 check (!string_set_contains (set
, "xxx"));
258 check (string_set_find_node (set
, "") == NULL
);
261 check (string_set_first (set
) == NULL
);
264 const struct string_set_node
*node
;
268 data_copy
= xmemdup (data
, cnt
* sizeof *data
);
270 for (node
= string_set_first (set
), i
= 0; i
< cnt
;
271 node
= string_set_next (set
, node
), i
++)
273 const char *s
= string_set_node_get_string (node
);
276 for (j
= 0; j
< left
; j
++)
277 if (!strcmp (s
, make_string (data_copy
[j
])))
279 data_copy
[j
] = data_copy
[--left
];
286 check (node
== NULL
);
291 /* Inserts the CNT strings from 0 to CNT - 1 (inclusive) into a set in the
292 order specified by INSERTIONS, then deletes them in the order specified by
293 DELETIONS, checking the set's contents for correctness after each
296 test_insert_delete (const int insertions
[],
297 const int deletions
[],
300 struct string_set set
;
303 string_set_init (&set
);
304 check_string_set (&set
, NULL
, 0);
305 for (i
= 0; i
< cnt
; i
++)
307 check (string_set_insert (&set
, make_string (insertions
[i
])));
308 check_string_set (&set
, insertions
, i
+ 1);
310 for (i
= 0; i
< cnt
; i
++)
312 check (string_set_delete (&set
, make_string (deletions
[i
])));
313 check_string_set (&set
, deletions
+ i
+ 1, cnt
- i
- 1);
315 string_set_destroy (&set
);
318 /* Inserts strings into a set in each possible order, then removes them in each
319 possible order, up to a specified maximum size. */
321 test_insert_any_remove_any (void)
323 const int max_elems
= 5;
326 for (cnt
= 0; cnt
<= max_elems
; cnt
++)
328 int *insertions
, *deletions
;
329 unsigned int ins_perm_cnt
;
332 insertions
= xnmalloc (cnt
, sizeof *insertions
);
333 deletions
= xnmalloc (cnt
, sizeof *deletions
);
334 for (i
= 0; i
< cnt
; i
++)
337 for (ins_perm_cnt
= 0;
338 ins_perm_cnt
== 0 || next_permutation (insertions
, cnt
);
341 unsigned int del_perm_cnt
;
344 for (i
= 0; i
< cnt
; i
++)
347 for (del_perm_cnt
= 0;
348 del_perm_cnt
== 0 || next_permutation (deletions
, cnt
);
350 test_insert_delete (insertions
, deletions
, cnt
);
352 check (del_perm_cnt
== factorial (cnt
));
354 check (ins_perm_cnt
== factorial (cnt
));
361 /* Inserts strings into a set in each possible order, then removes them in the
362 same order, up to a specified maximum size. */
364 test_insert_any_remove_same (void)
366 const int max_elems
= 7;
369 for (cnt
= 0; cnt
<= max_elems
; cnt
++)
372 unsigned int permutation_cnt
;
375 values
= xnmalloc (cnt
, sizeof *values
);
376 for (i
= 0; i
< cnt
; i
++)
379 for (permutation_cnt
= 0;
380 permutation_cnt
== 0 || next_permutation (values
, cnt
);
382 test_insert_delete (values
, values
, cnt
);
383 check (permutation_cnt
== factorial (cnt
));
389 /* Inserts strings into a set in each possible order, then
390 removes them in reverse order, up to a specified maximum
393 test_insert_any_remove_reverse (void)
395 const int max_elems
= 7;
398 for (cnt
= 0; cnt
<= max_elems
; cnt
++)
400 int *insertions
, *deletions
;
401 unsigned int permutation_cnt
;
404 insertions
= xnmalloc (cnt
, sizeof *insertions
);
405 deletions
= xnmalloc (cnt
, sizeof *deletions
);
406 for (i
= 0; i
< cnt
; i
++)
409 for (permutation_cnt
= 0;
410 permutation_cnt
== 0 || next_permutation (insertions
, cnt
);
413 memcpy (deletions
, insertions
, sizeof *insertions
* cnt
);
414 reverse (deletions
, cnt
);
416 test_insert_delete (insertions
, deletions
, cnt
);
418 check (permutation_cnt
== factorial (cnt
));
425 /* Inserts and removes strings in a set, in random order. */
427 test_random_sequence (void)
429 const int max_elems
= 64;
430 const int max_trials
= 8;
433 for (cnt
= 0; cnt
<= max_elems
; cnt
+= 2)
435 int *insertions
, *deletions
;
439 insertions
= xnmalloc (cnt
, sizeof *insertions
);
440 deletions
= xnmalloc (cnt
, sizeof *deletions
);
441 for (i
= 0; i
< cnt
; i
++)
443 for (i
= 0; i
< cnt
; i
++)
446 for (trial
= 0; trial
< max_trials
; trial
++)
448 random_shuffle (insertions
, cnt
, sizeof *insertions
);
449 random_shuffle (deletions
, cnt
, sizeof *deletions
);
451 test_insert_delete (insertions
, deletions
, cnt
);
459 /* Inserts strings into a set in ascending order, then delete in ascending
462 test_insert_ordered (void)
464 const int max_elems
= 64;
466 struct string_set set
;
469 string_set_init (&set
);
470 values
= xnmalloc (max_elems
, sizeof *values
);
471 for (i
= 0; i
< max_elems
; i
++)
474 string_set_insert_nocopy (&set
, xstrdup (make_string (i
)));
475 check_string_set (&set
, values
, i
+ 1);
477 for (i
= 0; i
< max_elems
; i
++)
479 string_set_delete (&set
, make_string (i
));
480 check_string_set (&set
, values
+ i
+ 1, max_elems
- i
- 1);
482 string_set_destroy (&set
);
487 test_boolean_ops (void (*function
)(struct string_set
*a
, struct string_set
*b
,
488 unsigned int *a_pat
, unsigned int *b_pat
))
490 enum { MAX_STRINGS
= 7 };
491 unsigned int a_pat
, b_pat
;
493 for (a_pat
= 0; a_pat
< (1u << MAX_STRINGS
); a_pat
++)
494 for (b_pat
= 0; b_pat
< (1u << MAX_STRINGS
); b_pat
++)
496 unsigned int new_a_pat
= a_pat
;
497 unsigned int new_b_pat
= b_pat
;
498 struct string_set a
, b
;
499 int a_strings
[MAX_STRINGS
], b_strings
[MAX_STRINGS
];
502 string_set_init (&a
);
503 string_set_init (&b
);
504 for (i
= 0; i
< MAX_STRINGS
; i
++)
506 if (a_pat
& (1u << i
))
507 string_set_insert (&a
, make_string (i
));
508 if (b_pat
& (1u << i
))
509 string_set_insert (&b
, make_string (i
));
512 function (&a
, &b
, &new_a_pat
, &new_b_pat
);
515 for (i
= 0; i
< MAX_STRINGS
; i
++)
517 if (new_a_pat
& (1u << i
))
518 a_strings
[n_a
++] = i
;
519 if (new_b_pat
& (1u << i
))
520 b_strings
[n_b
++] = i
;
522 check_string_set (&a
, a_strings
, n_a
);
523 check_string_set (&b
, b_strings
, n_b
);
524 string_set_destroy (&a
);
525 string_set_destroy (&b
);
530 union_cb (struct string_set
*a
, struct string_set
*b
,
531 unsigned int *a_pat
, unsigned int *b_pat
)
533 string_set_union (a
, b
);
540 test_boolean_ops (union_cb
);
544 union_and_intersection_cb (struct string_set
*a
, struct string_set
*b
,
545 unsigned int *a_pat
, unsigned int *b_pat
)
547 unsigned int orig_a_pat
= *a_pat
;
548 unsigned int orig_b_pat
= *b_pat
;
550 string_set_union_and_intersection (a
, b
);
551 *a_pat
= orig_a_pat
| orig_b_pat
;
552 *b_pat
= orig_a_pat
& orig_b_pat
;
556 test_union_and_intersection (void)
558 test_boolean_ops (union_and_intersection_cb
);
562 intersect_cb (struct string_set
*a
, struct string_set
*b
,
563 unsigned int *a_pat
, unsigned int *b_pat
)
565 string_set_intersect (a
, b
);
570 test_intersect (void)
572 test_boolean_ops (intersect_cb
);
576 subtract_cb (struct string_set
*a
, struct string_set
*b
,
577 unsigned int *a_pat
, unsigned int *b_pat
)
579 string_set_subtract (a
, b
);
586 test_boolean_ops (subtract_cb
);
590 swap_cb (struct string_set
*a
, struct string_set
*b
,
591 unsigned int *a_pat
, unsigned int *b_pat
)
594 string_set_swap (a
, b
);
603 test_boolean_ops (swap_cb
);
607 clear_cb (struct string_set
*a
, struct string_set
*b UNUSED
,
608 unsigned int *a_pat
, unsigned int *b_pat UNUSED
)
610 string_set_clear (a
);
617 test_boolean_ops (clear_cb
);
621 clone_cb (struct string_set
*a
, struct string_set
*b
,
622 unsigned int *a_pat
, unsigned int *b_pat
)
624 string_set_destroy (a
);
625 string_set_clone (a
, b
);
632 test_boolean_ops (clone_cb
);
636 test_destroy_null (void)
638 string_set_destroy (NULL
);
646 const char *description
;
647 void (*function
) (void);
650 static const struct test tests
[] =
653 "insert-any-remove-any",
654 "insert any order, delete any order",
655 test_insert_any_remove_any
658 "insert-any-remove-same",
659 "insert any order, delete same order",
660 test_insert_any_remove_same
663 "insert-any-remove-reverse",
664 "insert any order, delete reverse order",
665 test_insert_any_remove_reverse
669 "insert and delete in random sequence",
674 "insert in ascending order",
683 "union-and-intersection",
684 "union and intersection",
685 test_union_and_intersection
714 "destroying null table",
719 enum { N_TESTS
= sizeof tests
/ sizeof *tests
};
722 main (int argc
, char *argv
[])
728 fprintf (stderr
, "exactly one argument required; use --help for help\n");
731 else if (!strcmp (argv
[1], "--help"))
733 printf ("%s: test string set library\n"
734 "usage: %s TEST-NAME\n"
735 "where TEST-NAME is one of the following:\n",
737 for (i
= 0; i
< N_TESTS
; i
++)
738 printf (" %s\n %s\n", tests
[i
].name
, tests
[i
].description
);
743 for (i
= 0; i
< N_TESTS
; i
++)
744 if (!strcmp (argv
[1], tests
[i
].name
))
746 tests
[i
].function ();
751 fprintf (stderr
, "unknown test %s; use --help for help\n", argv
[1]);