1aa87760083e56706133b74aceb2ed449c723ec4
[pspp-builds.git] / tests / libpspp / range-set-test.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2007 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    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, write to the Free Software
16    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17    02110-1301, USA. */
18
19 /* This is a test program for the routines defined in
20    range-set.c.  This test program aims to be as comprehensive as
21    possible.  With -DNDEBUG, "gcov -b" should report 100%
22    coverage of lines and branches in range-set.c routines.
23    (Without -DNDEBUG, branches caused by failed assertions will
24    not be taken.)  "valgrind --leak-check=yes
25    --show-reachable=yes" should give a clean report, both with
26    and without -DNDEBUG. */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include <libpspp/range-set.h>
33
34 #include <assert.h>
35 #include <limits.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include <libpspp/compiler.h>
41 #include <libpspp/pool.h>
42
43 #include "xalloc.h"
44 \f
45 /* Currently running test. */
46 static const char *test_name;
47
48 /* Exit with a failure code.
49    (Place a breakpoint on this function while debugging.) */
50 static void
51 check_die (void) 
52 {
53   exit (EXIT_FAILURE);   
54 }
55
56 /* If OK is not true, prints a message about failure on the
57    current source file and the given LINE and terminates. */
58 static void
59 check_func (bool ok, int line) 
60 {
61   if (!ok) 
62     {
63       printf ("Check failed in %s test at %s, line %d\n",
64               test_name, __FILE__, line);
65       check_die ();
66     }
67 }
68
69 /* Verifies that EXPR evaluates to true.
70    If not, prints a message citing the calling line number and
71    terminates. */
72 #define check(EXPR) check_func ((EXPR), __LINE__)
73 \f
74 /* A contiguous region. */
75 struct region 
76   {
77     unsigned long int start;    /* Start of region. */
78     unsigned long int end;      /* One past the end. */
79   };
80
81 /* Number of bits in an unsigned int. */
82 #define UINT_BIT (CHAR_BIT * sizeof (unsigned int))
83
84 /* Returns the number of contiguous 1-bits in X starting from bit
85    0.
86    This implementation is designed to be obviously correct, not
87    to be efficient. */
88 static int
89 count_one_bits (unsigned long int x) 
90 {
91   int count = 0;
92   while (x & 1) 
93     {
94       count++;
95       x >>= 1;
96     }
97   return count;
98 }
99
100 /* Searches the bits in PATTERN from right to left starting from
101    bit OFFSET for one or more 1-bits.  If any are found, sets
102    *START to the bit index of the first and *WIDTH to the number
103    of contiguous 1-bits and returns true.  Otherwise, returns
104    false.
105    This implementation is designed to be obviously correct, not
106    to be efficient. */
107 static bool
108 next_region (unsigned int pattern, unsigned int offset,
109              unsigned long int *start, unsigned long int *width) 
110 {
111   unsigned int i;
112
113   assert (offset <= UINT_BIT);
114   for (i = offset; i < UINT_BIT; i++)
115     if (pattern & (1u << i)) 
116       {
117         *start = i;
118         *width = count_one_bits (pattern >> i);
119         return true;
120       }
121   return false;
122 }
123
124 /* Prints the regions in RS to stdout. */
125 static void UNUSED
126 print_regions (const struct range_set *rs)
127 {
128   const struct range_set_node *node;
129
130   printf ("result:");
131   for (node = range_set_first (rs); node != NULL;
132        node = range_set_next (rs, node)) 
133     printf (" (%lu,%lu)",
134             range_set_node_get_start (node), range_set_node_get_end (node));
135   printf ("\n");
136 }
137
138 /* Checks that the regions in RS match the bits in PATTERN. */
139 static void
140 check_pattern (const struct range_set *rs, unsigned int pattern) 
141 {
142   const struct range_set_node *node;
143   unsigned long int start, width;
144   int i;
145   
146   for (node = rand () % 2 ? range_set_first (rs) : range_set_next (rs, NULL),
147          start = width = 0;
148        next_region (pattern, start + width, &start, &width);
149        node = range_set_next (rs, node)) 
150     {
151       check (node != NULL);
152       check (range_set_node_get_start (node) == start);
153       check (range_set_node_get_end (node) == start + width);
154       check (range_set_node_get_width (node) == width);
155     }
156   check (node == NULL);
157
158   for (i = 0; i < UINT_BIT; i++)
159     check (range_set_contains (rs, i) == ((pattern & (1u << i)) != 0));
160   check (!range_set_contains (rs,
161                               UINT_BIT + rand () % (ULONG_MAX - UINT_BIT)));
162
163   check (range_set_is_empty (rs) == (pattern == 0));
164 }
165
166 /* Creates and returns a range set that contains regions for the
167    bits set in PATTERN. */
168 static struct range_set *
169 make_pattern (unsigned int pattern) 
170 {
171   unsigned long int start = 0;
172   unsigned long int width = 0;
173   struct range_set *rs = range_set_create_pool (NULL);
174   while (next_region (pattern, start + width, &start, &width))
175     range_set_insert (rs, start, width);
176   check_pattern (rs, pattern);
177   return rs;
178 }
179
180 /* Returns an unsigned int with bits OFS...OFS+CNT (exclusive)
181    set to 1, other bits set to 0. */
182 static unsigned int
183 bit_range (unsigned int ofs, unsigned int cnt) 
184 {
185   assert (ofs < UINT_BIT);
186   assert (cnt <= UINT_BIT);
187   assert (ofs + cnt <= UINT_BIT);
188
189   return cnt < UINT_BIT ? ((1u << cnt) - 1) << ofs : UINT_MAX;
190 }
191 \f
192 /* Tests inserting all possible patterns into all possible range
193    sets (up to a small maximum number of bits). */
194 static void
195 test_insert (void) 
196 {
197   const int positions = 9;
198   unsigned int init_pat;
199   int i, j;
200   
201   for (init_pat = 0; init_pat < (1u << positions); init_pat++) 
202     for (i = 0; i < positions + 1; i++)
203       for (j = i; j <= positions + 1; j++)
204         {
205           struct range_set *rs, *rs2;
206           unsigned int final_pat;
207
208           rs = make_pattern (init_pat);
209           range_set_insert (rs, i, j - i);
210           final_pat = init_pat | bit_range (i, j - i);
211           check_pattern (rs, final_pat);
212           rs2 = range_set_clone (rs, NULL);
213           check_pattern (rs2, final_pat);
214           range_set_destroy (rs);
215           range_set_destroy (rs2);
216         }
217 }
218
219 /* Tests deleting all possible patterns from all possible range
220    sets (up to a small maximum number of bits). */
221 static void
222 test_delete (void) 
223 {
224   const int positions = 9;
225   unsigned int init_pat;
226   int i, j;
227   
228   for (init_pat = 0; init_pat < (1u << positions); init_pat++) 
229     for (i = 0; i < positions + 1; i++)
230       for (j = i; j <= positions + 1; j++)
231         {
232           struct range_set *rs;
233           unsigned int final_pat;
234
235           rs = make_pattern (init_pat);
236           range_set_delete (rs, i, j - i);
237           final_pat = init_pat & ~bit_range (i, j - i);
238           check_pattern (rs, final_pat);
239           range_set_destroy (rs);
240         }
241 }
242
243 /* Tests all possible allocation in all possible range sets (up
244    to a small maximum number of bits). */
245 static void
246 test_allocate (void)
247 {
248   const int positions = 9;
249   unsigned int init_pat;
250   int request;
251   
252   for (init_pat = 0; init_pat < (1u << positions); init_pat++) 
253     for (request = 1; request <= positions + 1; request++)
254       {
255         struct range_set *rs;
256         unsigned long int start, width, expect_start, expect_width;
257         bool success, expect_success;
258         unsigned int final_pat;
259         int i;
260
261         /* Figure out expected results. */
262         expect_success = false;
263         expect_start = expect_width = 0;
264         final_pat = init_pat;
265         for (i = 0; i < positions; i++)
266           if (init_pat & (1u << i))
267             {
268               expect_success = true;
269               expect_start = i;
270               expect_width = count_one_bits (init_pat >> i);
271               if (expect_width > request)
272                 expect_width = request;
273               final_pat &= ~bit_range (expect_start, expect_width);
274               break;
275             }
276
277         /* Test. */
278         rs = make_pattern (init_pat);
279         success = range_set_allocate (rs, request, &start, &width);
280         check_pattern (rs, final_pat);
281         range_set_destroy (rs);
282
283         /* Check results. */
284         check (success == expect_success);
285         if (expect_success) 
286           {
287             check (start == expect_start);
288             check (width == expect_width);
289           }
290       }
291 }
292
293 /* Tests freeing a range set through a pool. */
294 static void
295 test_pool (void) 
296 {
297   struct pool *pool;
298   struct range_set *rs;
299
300   /* Destroy the range set, then the pool.
301      Makes sure that this doesn't cause a double-free. */
302   pool = pool_create ();
303   rs = range_set_create_pool (pool);
304   range_set_insert (rs, 1, 10);
305   range_set_destroy (rs);
306   pool_destroy (pool);
307   
308   /* Just destroy the pool.
309      Makes sure that this doesn't cause a leak. */
310   pool = pool_create ();
311   rs = range_set_create_pool (pool);
312   range_set_insert (rs, 1, 10);
313   pool_destroy (pool);
314 }
315 \f
316 /* Main program. */
317
318 /* Runs TEST_FUNCTION and prints a message about NAME. */
319 static void
320 run_test (void (*test_function) (void), const char *name) 
321 {
322   test_name = name;
323   putchar ('.');
324   fflush (stdout);
325   test_function ();
326 }
327
328 int
329 main (void) 
330 {
331   run_test (test_insert, "insert");
332   run_test (test_delete, "delete");
333   run_test (test_allocate, "allocate");
334   run_test (test_pool, "pool");
335   putchar ('\n');
336
337   return 0;
338 }