1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2007 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. "valgrind --leak-check=yes
22 --show-reachable=yes" should give a clean report. */
28 #include <libpspp/sparse-array.h>
35 /* Support preliminaries. */
36 #if __GNUC__ >= 2 && !defined UNUSED
37 #define UNUSED __attribute__ ((unused))
42 /* Currently running test. */
43 static const char *test_name;
45 /* Exit with a failure code.
46 (Place a breakpoint on this function while debugging.) */
53 /* If OK is not true, prints a message about failure on the
54 current source file and the given LINE and terminates. */
56 check_func (bool ok, int line)
60 printf ("%s:%d: Check failed in %s test\n",
61 __FILE__, line, test_name);
66 /* Verifies that EXPR evaluates to true.
67 If not, prints a message citing the calling line number and
69 #define check(EXPR) check_func ((EXPR), __LINE__)
71 /* Prints a message about memory exhaustion and exits with a
76 printf ("virtual memory exhausted\n");
80 /* Allocates and returns N bytes of memory. */
96 /* Returns a malloc()'d duplicate of the N bytes starting at
99 xmemdup (const void *p, size_t n)
101 void *q = xmalloc (n);
106 /* Allocates and returns N * M bytes of memory. */
108 xnmalloc (size_t n, size_t m)
110 if ((size_t) -1 / m <= n)
112 return xmalloc (n * m);
115 /* Compares A and B and returns a strcmp-type return value. */
117 compare_unsigned_longs_noaux (const void *a_, const void *b_)
119 const unsigned long *a = a_;
120 const unsigned long *b = b_;
122 return *a < *b ? -1 : *a > *b;
125 /* Checks that SPAR contains the CNT ints in DATA, that its
126 structure is correct, and that certain operations on SPAR
127 produce the expected results. */
129 check_sparse_array (struct sparse_array *spar,
130 const unsigned long data[], size_t cnt)
133 unsigned long *order;
137 check (sparse_array_count (spar) == cnt);
139 for (i = 0; i < cnt; i++)
141 p = sparse_array_get (spar, data[i]);
143 check (*p == data[i]);
146 order = xmemdup (data, cnt * sizeof *data);
147 qsort (order, cnt, sizeof *order, compare_unsigned_longs_noaux);
149 for (i = 0; i < cnt; i++)
151 p = sparse_array_get (spar, order[i]);
153 check (*p == order[i]);
156 if (cnt > 0 && order[0] - 1 != order[cnt - 1])
158 check (sparse_array_get (spar, order[0] - 1) == NULL);
159 check (!sparse_array_remove (spar, order[0] - 1));
161 if (cnt > 0 && order[0] != order[cnt - 1] + 1)
163 check (sparse_array_get (spar, order[cnt - 1] + 1) == NULL);
164 check (!sparse_array_remove (spar, order[cnt - 1] + 1));
167 for (i = 0, p = sparse_array_scan (spar, NULL, &idx); i < cnt;
168 i++, p = sparse_array_scan (spar, &idx, &idx))
171 check (idx == order[i]);
172 check (*p == order[i]);
179 /* Inserts the CNT values from 0 to CNT - 1 (inclusive) into a
180 sparse array in the order specified by INSERTIONS, then
181 deletes them in the order specified by DELETIONS, checking the
182 array's contents for correctness after each operation. */
184 test_insert_delete (const unsigned long insertions[],
185 const unsigned long deletions[],
188 struct sparse_array *spar;
191 spar = sparse_array_create (sizeof *insertions);
192 for (i = 0; i < cnt; i++)
194 unsigned long *p = sparse_array_insert (spar, insertions[i]);
196 check_sparse_array (spar, insertions, i + 1);
198 for (i = 0; i < cnt; i++)
200 bool deleted = sparse_array_remove (spar, deletions[i]);
202 check_sparse_array (spar, deletions + i + 1, cnt - (i + 1));
204 check_sparse_array (spar, NULL, 0);
205 sparse_array_destroy (spar);
208 /* Inserts the CNT values from 0 to CNT - 1 (inclusive) into a
209 sparse array in the order specified by INSERTIONS, then
210 destroys the sparse array, to check that sparse_cases_destroy
211 properly frees all the nodes. */
213 test_destroy (const unsigned long insertions[], size_t cnt)
215 struct sparse_array *spar;
218 spar = sparse_array_create (sizeof *insertions);
219 for (i = 0; i < cnt; i++)
221 unsigned long *p = sparse_array_insert (spar, insertions[i]);
223 check_sparse_array (spar, insertions, i + 1);
225 sparse_array_destroy (spar);
228 /* Randomly shuffles the CNT elements in ARRAY, each of which is
229 SIZE bytes in size. */
231 random_shuffle (void *array_, size_t cnt, size_t size)
233 char *array = array_;
234 char *tmp = xmalloc (size);
237 for (i = 0; i < cnt; i++)
239 size_t j = rand () % (cnt - i) + i;
242 memcpy (tmp, array + j * size, size);
243 memcpy (array + j * size, array + i * size, size);
244 memcpy (array + i * size, tmp, size);
251 /* Tests inserting and deleting elements whose values are
252 determined by starting from various offsets and skipping
253 across various strides, and doing so in various orders. */
255 test_insert_delete_strides (void)
257 static const unsigned long strides[] =
259 1, 2, 4, 16, 64, 4096, 262144, 16777216,
260 3, 5, 17, 67, 4099, 262147, 16777259,
262 const size_t stride_cnt = sizeof strides / sizeof *strides;
264 static const unsigned long offsets[] =
268 1024ul * 1024 * 512 + 23,
271 const size_t offset_cnt = sizeof offsets / sizeof *offsets;
274 unsigned long *insertions, *deletions;
275 const unsigned long *stride, *offset;
277 insertions = xnmalloc (cnt, sizeof *insertions);
278 deletions = xnmalloc (cnt, sizeof *deletions);
279 for (stride = strides; stride < strides + stride_cnt; stride++)
281 for (offset = offsets; offset < offsets + offset_cnt; offset++)
285 for (k = 0; k < cnt; k++)
286 insertions[k] = *stride * k + *offset;
288 test_insert_delete (insertions, insertions, cnt);
289 test_destroy (insertions, cnt);
291 for (k = 0; k < cnt; k++)
292 deletions[k] = insertions[cnt - k - 1];
293 test_insert_delete (insertions, deletions, cnt);
295 random_shuffle (insertions, cnt, sizeof *insertions);
296 test_insert_delete (insertions, insertions, cnt);
297 test_insert_delete (insertions, deletions, cnt);
306 /* Returns the index in ARRAY of the (CNT+1)th element that has
309 scan_bools (bool target, bool array[], size_t cnt)
314 if (array[i] == target && cnt-- == 0)
318 /* Performs a random sequence of insertions and deletions in a
321 test_random_insert_delete (void)
323 unsigned long int values[] =
325 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096,
326 8192, 16384, 32768, 65536, 131072, 262144, 4194304, 8388608,
327 16777216, 33554432, 67108864, 134217728, 268435456, 536870912,
328 1073741824, 2147483648,
330 3, 7, 15, 31, 63, 127, 257, 511, 1023, 2047, 4095,
331 8191, 16383, 32767, 65535, 131071, 262143, 4194303, 8388607,
332 16777215, 33554431, 67108863, 134217727, 268435455, 536870911,
333 1073741823, 2147483647, 4294967295,
335 const int max_values = sizeof values / sizeof *values;
337 const int num_actions = 250000;
338 struct sparse_array *spar;
344 has_values = xnmalloc (max_values, sizeof *has_values);
345 memset (has_values, 0, max_values * sizeof *has_values);
350 spar = sparse_array_create (sizeof *values);
351 for (i = 0; i < num_actions; i++)
353 enum { INSERT, DELETE } action;
360 if (insert_chance < 9)
363 else if (cnt == max_values)
366 if (insert_chance > 0)
370 action = rand () % 10 < insert_chance ? INSERT : DELETE;
372 if (action == INSERT)
376 ins_index = scan_bools (false, has_values,
377 rand () % (max_values - cnt));
378 assert (has_values[ins_index] == false);
379 has_values[ins_index] = true;
381 p = sparse_array_insert (spar, values[ins_index]);
383 *p = values[ins_index];
387 else if (action == DELETE)
391 del_index = scan_bools (true, has_values, rand () % cnt);
392 assert (has_values[del_index] == true);
393 has_values[del_index] = false;
395 check (sparse_array_remove (spar, values[del_index]));
401 check (sparse_array_count (spar) == cnt);
402 for (j = 0; j < max_values; j++)
404 p = sparse_array_get (spar, values[j]);
408 check (*p == values[j]);
413 if (rand () % 10 == 0)
414 sparse_array_remove (spar, values[j]);
418 sparse_array_destroy (spar);
424 /* Runs TEST_FUNCTION and prints a message about NAME. */
426 run_test (void (*test_function) (void), const char *name)
437 run_test (test_random_insert_delete,
438 "random insertions and deletions");
439 run_test (test_insert_delete_strides,
440 "insert in ascending order with strides and offset");