range-set: New functions range_set_last and range_set_prev.
[pspp-builds.git] / tests / libpspp / range-set-test.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 /* This is a test program for the routines defined in
18    range-set.c.  This test program aims to be as comprehensive as
19    possible.  With -DNDEBUG, "gcov -b" should report 100%
20    coverage of lines and branches in range-set.c routines.
21    (Without -DNDEBUG, branches caused by failed assertions will
22    not be taken.)  "valgrind --leak-check=yes
23    --show-reachable=yes" should give a clean report, both with
24    and without -DNDEBUG. */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <libpspp/range-set.h>
31
32 #include <assert.h>
33 #include <limits.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include <libpspp/compiler.h>
39 #include <libpspp/pool.h>
40
41 #include "xalloc.h"
42 \f
43 /* Currently running test. */
44 static const char *test_name;
45
46 /* Exit with a failure code.
47    (Place a breakpoint on this function while debugging.) */
48 static void
49 check_die (void)
50 {
51   exit (EXIT_FAILURE);
52 }
53
54 /* If OK is not true, prints a message about failure on the
55    current source file and the given LINE and terminates. */
56 static void
57 check_func (bool ok, int line)
58 {
59   if (!ok)
60     {
61       printf ("Check failed in %s test at %s, line %d\n",
62               test_name, __FILE__, line);
63       check_die ();
64     }
65 }
66
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__)
71 \f
72 /* A contiguous region. */
73 struct region
74   {
75     unsigned long int start;    /* Start of region. */
76     unsigned long int end;      /* One past the end. */
77   };
78
79 /* Number of bits in an unsigned int. */
80 #define UINT_BIT (CHAR_BIT * sizeof (unsigned int))
81
82 /* Returns the number of contiguous 1-bits in X starting from bit
83    0.
84    This implementation is designed to be obviously correct, not
85    to be efficient. */
86 static int
87 count_one_bits (unsigned long int x)
88 {
89   int count = 0;
90   while (x & 1)
91     {
92       count++;
93       x >>= 1;
94     }
95   return count;
96 }
97
98 /* Searches the bits in PATTERN from right to left starting from
99    bit OFFSET for one or more 1-bits.  If any are found, sets
100    *START to the bit index of the first and *WIDTH to the number
101    of contiguous 1-bits and returns true.  Otherwise, returns
102    false.
103    This implementation is designed to be obviously correct, not
104    to be efficient. */
105 static bool
106 next_region (unsigned int pattern, unsigned int offset,
107              unsigned long int *start, unsigned long int *width)
108 {
109   unsigned int i;
110
111   assert (offset <= UINT_BIT);
112   for (i = offset; i < UINT_BIT; i++)
113     if (pattern & (1u << i))
114       {
115         *start = i;
116         *width = count_one_bits (pattern >> i);
117         return true;
118       }
119   return false;
120 }
121
122 /* Searches the bits in PATTERN from left to right starting from
123    just beyond bit OFFSET for one or more 1-bits.  If any are
124    found, sets *START to the bit index of the first and *WIDTH to
125    the number of contiguous 1-bits and returns true.  Otherwise,
126    returns false.
127    This implementation is designed to be obviously correct, not
128    to be efficient. */
129 static bool
130 prev_region (unsigned int pattern, unsigned int offset,
131              unsigned long int *start, unsigned long int *width)
132 {
133   unsigned int i;
134
135   assert (offset <= UINT_BIT);
136   for (i = offset; i-- > 0; )
137     if (pattern & (1u << i))
138       {
139         *start = i;
140         *width = 1;
141         while (i-- > 0 && pattern & (1u << i))
142           {
143             ++*width;
144             --*start;
145           }
146         return true;
147       }
148   return false;
149 }
150
151 /* Searches the bits in PATTERN from right to left starting from
152    bit OFFSET.  Returns the bit index of the first 1-bit found,
153    or ULONG_MAX if none is found. */
154 static unsigned long int
155 next_1bit (unsigned int pattern, unsigned int offset)
156 {
157   for (; offset < UINT_BIT; offset++)
158     if (pattern & (1u << offset))
159       return offset;
160   return ULONG_MAX;
161 }
162
163 /* Prints the regions in RS to stdout. */
164 static void UNUSED
165 print_regions (const struct range_set *rs)
166 {
167   const struct range_set_node *node;
168
169   printf ("result:");
170   for (node = range_set_first (rs); node != NULL;
171        node = range_set_next (rs, node))
172     printf (" (%lu,%lu)",
173             range_set_node_get_start (node), range_set_node_get_end (node));
174   printf ("\n");
175 }
176
177 /* Checks that the regions in RS match the bits in PATTERN. */
178 static void
179 check_pattern (const struct range_set *rs, unsigned int pattern)
180 {
181   const struct range_set_node *node;
182   unsigned long int start, width;
183   unsigned long int s1, s2;
184   int i;
185
186   for (node = rand () % 2 ? range_set_first (rs) : range_set_next (rs, NULL),
187          start = width = 0;
188        next_region (pattern, start + width, &start, &width);
189        node = range_set_next (rs, node))
190     {
191       check (node != NULL);
192       check (range_set_node_get_start (node) == start);
193       check (range_set_node_get_end (node) == start + width);
194       check (range_set_node_get_width (node) == width);
195     }
196   check (node == NULL);
197
198   for (node = rand () % 2 ? range_set_last (rs) : range_set_prev (rs, NULL),
199          start = UINT_BIT;
200        prev_region (pattern, start, &start, &width);
201        node = range_set_prev (rs, node))
202     {
203       check (node != NULL);
204       check (range_set_node_get_start (node) == start);
205       check (range_set_node_get_end (node) == start + width);
206       check (range_set_node_get_width (node) == width);
207     }
208   check (node == NULL);
209
210   /* Scan from all possible positions, resetting the cache each
211      time, to ensure that we get the correct answers without
212      caching. */
213   for (start = 0; start <= 32; start++)
214     {
215       struct range_set *nonconst_rs = (struct range_set *) rs;
216       nonconst_rs->cache_end = 0;
217       s1 = range_set_scan (rs, start);
218       s2 = next_1bit (pattern, start);
219       check (s1 == s2);
220     }
221
222   /* Scan in forward order to exercise expected cache behavior. */
223   for (s1 = range_set_scan (rs, 0), s2 = next_1bit (pattern, 0); ;
224        s1 = range_set_scan (rs, s1 + 1), s2 = next_1bit (pattern, s2 + 1))
225     {
226       check (s1 == s2);
227       if (s1 == ULONG_MAX)
228         break;
229     }
230
231   /* Scan in random order to frustrate cache. */
232   for (i = 0; i < 32; i++)
233     {
234       start = rand () % 32;
235       s1 = range_set_scan (rs, start);
236       s2 = next_1bit (pattern, start);
237       check (s1 == s2);
238     }
239
240   /* Test range_set_scan() with negative cache. */
241   check (!range_set_contains (rs, 999));
242   check (range_set_scan (rs, 1111) == ULONG_MAX);
243
244   for (i = 0; i < UINT_BIT; i++)
245     check (range_set_contains (rs, i) == ((pattern & (1u << i)) != 0));
246   check (!range_set_contains (rs,
247                               UINT_BIT + rand () % (ULONG_MAX - UINT_BIT)));
248
249   check (range_set_is_empty (rs) == (pattern == 0));
250 }
251
252 /* Creates and returns a range set that contains regions for the
253    bits set in PATTERN. */
254 static struct range_set *
255 make_pattern (unsigned int pattern)
256 {
257   unsigned long int start = 0;
258   unsigned long int width = 0;
259   struct range_set *rs = range_set_create_pool (NULL);
260   while (next_region (pattern, start + width, &start, &width))
261     range_set_insert (rs, start, width);
262   check_pattern (rs, pattern);
263   return rs;
264 }
265
266 /* Returns an unsigned int with bits OFS...OFS+CNT (exclusive)
267    set to 1, other bits set to 0. */
268 static unsigned int
269 bit_range (unsigned int ofs, unsigned int cnt)
270 {
271   assert (ofs < UINT_BIT);
272   assert (cnt <= UINT_BIT);
273   assert (ofs + cnt <= UINT_BIT);
274
275   return cnt < UINT_BIT ? ((1u << cnt) - 1) << ofs : UINT_MAX;
276 }
277 \f
278 /* Tests inserting all possible patterns into all possible range
279    sets (up to a small maximum number of bits). */
280 static void
281 test_insert (void)
282 {
283   const int positions = 9;
284   unsigned int init_pat;
285   int i, j;
286
287   for (init_pat = 0; init_pat < (1u << positions); init_pat++)
288     for (i = 0; i < positions + 1; i++)
289       for (j = i; j <= positions + 1; j++)
290         {
291           struct range_set *rs, *rs2;
292           unsigned int final_pat;
293
294           rs = make_pattern (init_pat);
295           range_set_insert (rs, i, j - i);
296           final_pat = init_pat | bit_range (i, j - i);
297           check_pattern (rs, final_pat);
298           rs2 = range_set_clone (rs, NULL);
299           check_pattern (rs2, final_pat);
300           range_set_destroy (rs);
301           range_set_destroy (rs2);
302         }
303 }
304
305 /* Tests deleting all possible patterns from all possible range
306    sets (up to a small maximum number of bits). */
307 static void
308 test_delete (void)
309 {
310   const int positions = 9;
311   unsigned int init_pat;
312   int i, j;
313
314   for (init_pat = 0; init_pat < (1u << positions); init_pat++)
315     for (i = 0; i < positions + 1; i++)
316       for (j = i; j <= positions + 1; j++)
317         {
318           struct range_set *rs;
319           unsigned int final_pat;
320
321           rs = make_pattern (init_pat);
322           range_set_delete (rs, i, j - i);
323           final_pat = init_pat & ~bit_range (i, j - i);
324           check_pattern (rs, final_pat);
325           range_set_destroy (rs);
326         }
327 }
328
329 /* Tests all possible allocation in all possible range sets (up
330    to a small maximum number of bits). */
331 static void
332 test_allocate (void)
333 {
334   const int positions = 9;
335   unsigned int init_pat;
336   int request;
337
338   for (init_pat = 0; init_pat < (1u << positions); init_pat++)
339     for (request = 1; request <= positions + 1; request++)
340       {
341         struct range_set *rs;
342         unsigned long int start, width, expect_start, expect_width;
343         bool success, expect_success;
344         unsigned int final_pat;
345         int i;
346
347         /* Figure out expected results. */
348         expect_success = false;
349         expect_start = expect_width = 0;
350         final_pat = init_pat;
351         for (i = 0; i < positions; i++)
352           if (init_pat & (1u << i))
353             {
354               expect_success = true;
355               expect_start = i;
356               expect_width = count_one_bits (init_pat >> i);
357               if (expect_width > request)
358                 expect_width = request;
359               final_pat &= ~bit_range (expect_start, expect_width);
360               break;
361             }
362
363         /* Test. */
364         rs = make_pattern (init_pat);
365         success = range_set_allocate (rs, request, &start, &width);
366         check_pattern (rs, final_pat);
367         range_set_destroy (rs);
368
369         /* Check results. */
370         check (success == expect_success);
371         if (expect_success)
372           {
373             check (start == expect_start);
374             check (width == expect_width);
375           }
376       }
377 }
378
379 /* Tests all possible full allocations in all possible range sets
380    (up to a small maximum number of bits). */
381 static void
382 test_allocate_fully (void)
383 {
384   const int positions = 9;
385   unsigned int init_pat;
386   int request;
387
388   for (init_pat = 0; init_pat < (1u << positions); init_pat++)
389     for (request = 1; request <= positions + 1; request++)
390       {
391         struct range_set *rs;
392         unsigned long int start, expect_start;
393         bool success, expect_success;
394         unsigned int final_pat;
395         int i;
396
397         /* Figure out expected results. */
398         expect_success = false;
399         expect_start = 0;
400         final_pat = init_pat;
401         for (i = 0; i < positions - request + 1; i++)
402           {
403             int j;
404
405             final_pat = init_pat;
406             for (j = i; j < i + request; j++)
407               {
408                 if (!(init_pat & (1u << j)))
409                   goto next;
410                 final_pat &= ~(1u << j);
411               }
412
413             expect_success = true;
414             expect_start = i;
415             break;
416           next:
417             final_pat = init_pat;
418           }
419
420         /* Test. */
421         rs = make_pattern (init_pat);
422         success = range_set_allocate_fully (rs, request, &start);
423         check_pattern (rs, final_pat);
424         range_set_destroy (rs);
425
426         /* Check results. */
427         check (success == expect_success);
428         if (expect_success)
429           check (start == expect_start);
430       }
431 }
432
433 /* Tests freeing a range set through a pool. */
434 static void
435 test_pool (void)
436 {
437   struct pool *pool;
438   struct range_set *rs;
439
440   /* Destroy the range set, then the pool.
441      Makes sure that this doesn't cause a double-free. */
442   pool = pool_create ();
443   rs = range_set_create_pool (pool);
444   range_set_insert (rs, 1, 10);
445   range_set_destroy (rs);
446   pool_destroy (pool);
447
448   /* Just destroy the pool.
449      Makes sure that this doesn't cause a leak. */
450   pool = pool_create ();
451   rs = range_set_create_pool (pool);
452   range_set_insert (rs, 1, 10);
453   pool_destroy (pool);
454 }
455
456 /* Tests range_set_destroy(NULL). */
457 static void
458 test_destroy_null (void)
459 {
460   range_set_destroy (NULL);
461 }
462 \f
463 /* Main program. */
464
465 /* Runs TEST_FUNCTION and prints a message about NAME. */
466 static void
467 run_test (void (*test_function) (void), const char *name)
468 {
469   test_name = name;
470   putchar ('.');
471   fflush (stdout);
472   test_function ();
473 }
474
475 int
476 main (void)
477 {
478   run_test (test_insert, "insert");
479   run_test (test_delete, "delete");
480   run_test (test_allocate, "allocate");
481   run_test (test_allocate_fully, "allocate_fully");
482   run_test (test_pool, "pool");
483   run_test (test_destroy_null, "destroy null");
484   putchar ('\n');
485
486   return 0;
487 }