1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007, 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 sparse array routines defined
18 in sparse-array.c. This test program aims to be as
19 comprehensive as possible. "gcov -b" should report 100%
20 coverage of lines and branches in sparse-array.c when compiled
21 with -DNDEBUG and BITS_PER_LEVEL is greater than the number of
22 bits in a long. "valgrind --leak-check=yes
23 --show-reachable=yes" should give a clean report. */
29 #include <libpspp/sparse-array.h>
36 /* Support preliminaries. */
37 #if __GNUC__ >= 2 && !defined UNUSED
38 #define UNUSED __attribute__ ((unused))
43 /* Exit with a failure code.
44 (Place a breakpoint on this function while debugging.) */
51 /* If OK is not true, prints a message about failure on the
52 current source file and the given LINE and terminates. */
54 check_func (bool ok, int line)
58 fprintf (stderr, "%s:%d: check failed\n", __FILE__, line);
63 /* Verifies that EXPR evaluates to true.
64 If not, prints a message citing the calling line number and
66 #define check(EXPR) check_func ((EXPR), __LINE__)
68 /* Prints a message about memory exhaustion and exits with a
73 printf ("virtual memory exhausted\n");
77 /* Allocates and returns N bytes of memory. */
93 /* Returns a malloc()'d duplicate of the N bytes starting at
96 xmemdup (const void *p, size_t n)
98 void *q = xmalloc (n);
103 /* Allocates and returns N * M bytes of memory. */
105 xnmalloc (size_t n, size_t m)
107 if ((size_t) -1 / m <= n)
109 return xmalloc (n * m);
112 /* Compares A and B and returns a strcmp-type return value. */
114 compare_unsigned_longs_noaux (const void *a_, const void *b_)
116 const unsigned long *a = a_;
117 const unsigned long *b = b_;
119 return *a < *b ? -1 : *a > *b;
122 /* Checks that SPAR contains the CNT ints in DATA, that its
123 structure is correct, and that certain operations on SPAR
124 produce the expected results. */
126 check_sparse_array (struct sparse_array *spar,
127 const unsigned long data[], size_t cnt)
130 unsigned long *order;
134 check (sparse_array_count (spar) == cnt);
136 for (i = 0; i < cnt; i++)
138 p = sparse_array_get (spar, data[i]);
140 check (*p == data[i]);
143 order = xmemdup (data, cnt * sizeof *data);
144 qsort (order, cnt, sizeof *order, compare_unsigned_longs_noaux);
146 for (i = 0; i < cnt; i++)
148 p = sparse_array_get (spar, order[i]);
150 check (*p == order[i]);
153 if (cnt > 0 && order[0] - 1 != order[cnt - 1])
155 check (sparse_array_get (spar, order[0] - 1) == NULL);
156 check (!sparse_array_remove (spar, order[0] - 1));
158 if (cnt > 0 && order[0] != order[cnt - 1] + 1)
160 check (sparse_array_get (spar, order[cnt - 1] + 1) == NULL);
161 check (!sparse_array_remove (spar, order[cnt - 1] + 1));
164 for (i = 0, p = sparse_array_first (spar, &idx); i < cnt;
165 i++, p = sparse_array_next (spar, idx, &idx))
168 check (idx == order[i]);
169 check (*p == order[i]);
173 for (i = 0, p = sparse_array_last (spar, &idx); i < cnt;
174 i++, p = sparse_array_prev (spar, idx, &idx))
177 check (idx == order[cnt - i - 1]);
178 check (*p == order[cnt - i - 1]);
185 /* Inserts the CNT values from 0 to CNT - 1 (inclusive) into a
186 sparse array in the order specified by INSERTIONS, then
187 deletes them in the order specified by DELETIONS, checking the
188 array's contents for correctness after each operation. */
190 test_insert_delete (const unsigned long insertions[],
191 const unsigned long deletions[],
194 struct sparse_array *spar;
197 spar = sparse_array_create (sizeof *insertions);
198 for (i = 0; i < cnt; i++)
200 unsigned long *p = sparse_array_insert (spar, insertions[i]);
202 check_sparse_array (spar, insertions, i + 1);
204 for (i = 0; i < cnt; i++)
206 bool deleted = sparse_array_remove (spar, deletions[i]);
208 check_sparse_array (spar, deletions + i + 1, cnt - (i + 1));
210 check_sparse_array (spar, NULL, 0);
211 sparse_array_destroy (spar);
214 /* Inserts the CNT values from 0 to CNT - 1 (inclusive) into a
215 sparse array in the order specified by INSERTIONS, then
216 destroys the sparse array, to check that sparse_cases_destroy
217 properly frees all the nodes. */
219 test_destroy (const unsigned long insertions[], size_t cnt)
221 struct sparse_array *spar;
224 spar = sparse_array_create (sizeof *insertions);
225 for (i = 0; i < cnt; i++)
227 unsigned long *p = sparse_array_insert (spar, insertions[i]);
229 check_sparse_array (spar, insertions, i + 1);
231 sparse_array_destroy (spar);
234 /* Randomly shuffles the CNT elements in ARRAY, each of which is
235 SIZE bytes in size. */
237 random_shuffle (void *array_, size_t cnt, size_t size)
239 char *array = array_;
240 char *tmp = xmalloc (size);
243 for (i = 0; i < cnt; i++)
245 size_t j = rand () % (cnt - i) + i;
248 memcpy (tmp, array + j * size, size);
249 memcpy (array + j * size, array + i * size, size);
250 memcpy (array + i * size, tmp, size);
257 /* Tests inserting and deleting elements whose values are
258 determined by starting from various offsets and skipping
259 across various strides, and doing so in various orders. */
261 test_insert_delete_strides (void)
263 static const unsigned long strides[] =
265 1, 2, 4, 16, 64, 4096, 262144, 16777216,
266 3, 5, 17, 67, 4099, 262147, 16777259,
268 const size_t stride_cnt = sizeof strides / sizeof *strides;
270 static const unsigned long offsets[] =
274 1024ul * 1024 * 512 + 23,
277 const size_t offset_cnt = sizeof offsets / sizeof *offsets;
280 unsigned long *insertions, *deletions;
281 const unsigned long *stride, *offset;
283 insertions = xnmalloc (cnt, sizeof *insertions);
284 deletions = xnmalloc (cnt, sizeof *deletions);
285 for (stride = strides; stride < strides + stride_cnt; stride++)
287 printf ("%lu\n", *stride);
288 for (offset = offsets; offset < offsets + offset_cnt; offset++)
292 for (k = 0; k < cnt; k++)
293 insertions[k] = *stride * k + *offset;
295 test_insert_delete (insertions, insertions, cnt);
296 test_destroy (insertions, cnt);
298 for (k = 0; k < cnt; k++)
299 deletions[k] = insertions[cnt - k - 1];
300 test_insert_delete (insertions, deletions, cnt);
302 random_shuffle (insertions, cnt, sizeof *insertions);
303 test_insert_delete (insertions, insertions, cnt);
304 test_insert_delete (insertions, deletions, cnt);
311 /* Returns the index in ARRAY of the (CNT+1)th element that has
314 scan_bools (bool target, bool array[], size_t cnt)
319 if (array[i] == target && cnt-- == 0)
323 /* Performs a random sequence of insertions and deletions in a
326 test_random_insert_delete (void)
328 unsigned long int values[] =
330 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096,
331 8192, 16384, 32768, 65536, 131072, 262144, 4194304, 8388608,
332 16777216, 33554432, 67108864, 134217728, 268435456, 536870912,
333 1073741824, 2147483648,
335 3, 7, 15, 31, 63, 127, 257, 511, 1023, 2047, 4095,
336 8191, 16383, 32767, 65535, 131071, 262143, 4194303, 8388607,
337 16777215, 33554431, 67108863, 134217727, 268435455, 536870911,
338 1073741823, 2147483647, 4294967295,
340 const int max_values = sizeof values / sizeof *values;
342 const int num_actions = 250000;
343 struct sparse_array *spar;
349 has_values = xnmalloc (max_values, sizeof *has_values);
350 memset (has_values, 0, max_values * sizeof *has_values);
355 spar = sparse_array_create (sizeof *values);
356 for (i = 0; i < num_actions; i++)
358 enum { INSERT, DELETE } action;
365 if (insert_chance < 9)
368 else if (cnt == max_values)
371 if (insert_chance > 0)
375 action = rand () % 10 < insert_chance ? INSERT : DELETE;
377 if (action == INSERT)
381 ins_index = scan_bools (false, has_values,
382 rand () % (max_values - cnt));
383 assert (has_values[ins_index] == false);
384 has_values[ins_index] = true;
386 p = sparse_array_insert (spar, values[ins_index]);
388 *p = values[ins_index];
392 else if (action == DELETE)
396 del_index = scan_bools (true, has_values, rand () % cnt);
397 assert (has_values[del_index] == true);
398 has_values[del_index] = false;
400 check (sparse_array_remove (spar, values[del_index]));
406 check (sparse_array_count (spar) == cnt);
407 for (j = 0; j < max_values; j++)
409 p = sparse_array_get (spar, values[j]);
413 check (*p == values[j]);
418 if (rand () % 10 == 0)
419 sparse_array_remove (spar, values[j]);
423 sparse_array_destroy (spar);
432 const char *description;
433 void (*function) (void);
436 static const struct test tests[] =
439 "random-insert-delete",
440 "random insertions and deletions",
441 test_random_insert_delete
444 "insert-delete-strides",
445 "insert in ascending order with strides and offset",
446 test_insert_delete_strides
450 enum { N_TESTS = sizeof tests / sizeof *tests };
453 main (int argc, char *argv[])
459 fprintf (stderr, "exactly one argument required; use --help for help\n");
462 else if (!strcmp (argv[1], "--help"))
464 printf ("%s: test sparse array library\n"
465 "usage: %s TEST-NAME\n"
466 "where TEST-NAME is one of the following:\n",
468 for (i = 0; i < N_TESTS; i++)
469 printf (" %s\n %s\n", tests[i].name, tests[i].description);
474 for (i = 0; i < N_TESTS; i++)
475 if (!strcmp (argv[1], tests[i].name))
477 tests[i].function ();
481 fprintf (stderr, "unknown test %s; use --help for help\n", argv[1]);