The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
heap_tests.c
Go to the documentation of this file.
1 #include <freeradius-devel/util/acutest.h>
2 #include <freeradius-devel/util/time.h>
3 #include <freeradius-devel/util/rand.h>
4 
5 #include "heap.c"
6 
7 static bool fr_heap_check(fr_heap_t *h, void *data)
8 {
9  unsigned int i;
10 
11  if (!h || (h->num_elements == 0)) return false;
12 
13  for (i = 0; i < h->num_elements; i++) {
14  if (h->p[i + 1] == data) {
15  return true;
16  }
17  }
18 
19  return false;
20 }
21 
22 typedef struct {
23  int data;
24  unsigned int heap; /* for the heap */
25 } heap_thing;
26 
27 
28 /*
29  * cc -g -DTESTING -I .. heap.c -o heap
30  *
31  * ./heap
32  */
33 static int8_t heap_cmp(void const *one, void const *two)
34 {
35  heap_thing const *a = one, *b = two;
36 
37  return CMP_PREFER_SMALLER(a->data, b->data);
38 }
39 
40 #define HEAP_TEST_SIZE (4096)
41 
42 static void heap_test(int skip)
43 {
44  fr_heap_t *hp;
45  int i;
47  int left;
48  int ret;
49  fr_fast_rand_t rand_ctx;
50 
51  rand_ctx.a = fr_rand();
52  rand_ctx.b = fr_rand();
53 
54  hp = fr_heap_alloc(NULL, heap_cmp, heap_thing, heap, 0);
55  TEST_CHECK(hp != NULL);
56 
57  array = calloc(HEAP_TEST_SIZE, sizeof(heap_thing));
58 
59  /*
60  * Initialise random values
61  */
62  for (i = 0; i < HEAP_TEST_SIZE; i++) array[i].data = fr_fast_rand(&rand_ctx) % 65537;
63 
64 #if 0
65  for (i = 0; i < HEAP_TEST_SIZE; i++) {
66  printf("Array %d has value %d at offset %d\n",
67  i, array[i].data, array[i].heap);
68  }
69 #endif
70 
71  TEST_CASE("insertions");
72  for (i = 0; i < HEAP_TEST_SIZE; i++) {
73  FR_HEAP_VERIFY(hp);
74  TEST_CHECK((ret = fr_heap_insert(&hp, &array[i])) >= 0);
75  TEST_MSG("insert failed, returned %i - %s", ret, fr_strerror());
76 
77  TEST_CHECK(fr_heap_check(hp, &array[i]));
78  TEST_MSG("element %i inserted but not in heap", i);
79  }
80 
81  TEST_CASE("deletions");
82  {
83  unsigned int entry;
84 
85  for (i = 0; i < HEAP_TEST_SIZE / skip; i++) {
86  entry = i * skip;
87 
88  FR_HEAP_VERIFY(hp);
89  TEST_CHECK(array[entry].heap != 0);
90  TEST_MSG("element %i removed out of order", entry);
91 
92  TEST_CHECK((ret = fr_heap_extract(&hp, &array[entry])) >= 0);
93  TEST_MSG("element %i removal failed, returned %i - %s", entry, ret, fr_strerror());
94 
95  TEST_CHECK(!fr_heap_check(hp, &array[entry]));
96  TEST_MSG("element %i removed but still in heap", entry);
97 
98  TEST_CHECK(array[entry].heap == 0);
99  TEST_MSG("element %i removed out of order", entry);
100  }
101  }
102 
103  left = fr_heap_num_elements(hp);
104  for (i = 0; i < left; i++) {
105  heap_thing *t;
106 
107  FR_HEAP_VERIFY(hp);
108  TEST_CHECK((t = fr_heap_peek(hp)) != NULL);
109  TEST_MSG("expected %i elements remaining in the heap", left - i);
110 
111  TEST_CHECK(fr_heap_extract(&hp, t) >= 0);
112  TEST_MSG("failed extracting %i", i);
113  }
114 
115  TEST_CHECK((ret = fr_heap_num_elements(hp)) == 0);
116  TEST_MSG("%i elements remaining", ret);
117 
118  talloc_free(hp);
119  free(array);
120 }
121 
122 static void heap_test_skip_0(void)
123 {
124  heap_test(1);
125 }
126 
127 static void heap_test_skip_2(void)
128 {
129  heap_test(2);
130 }
131 
132 static void heap_test_skip_10(void)
133 {
134  heap_test(10);
135 }
136 
137 #define HEAP_CYCLE_SIZE (1600000)
138 
139 static void heap_test_order(void)
140 {
141  fr_heap_t *hp;
142  int i;
143  heap_thing *array;
144  heap_thing *thing, *prev = NULL;
145  int data = 0;
146  unsigned int count = 0;
147  int ret;
148  fr_fast_rand_t rand_ctx;
149 
150  rand_ctx.a = fr_rand();
151  rand_ctx.b = fr_rand();
152 
153  hp = fr_heap_alloc(NULL, heap_cmp, heap_thing, heap, 0);
154  TEST_CHECK(hp != NULL);
155 
156  array = calloc(HEAP_TEST_SIZE, sizeof(heap_thing));
157 
158  /*
159  * Initialise random values
160  */
161  for (i = 0; i < HEAP_TEST_SIZE; i++) array[i].data = fr_fast_rand(&rand_ctx) % 65537;
162 
163  TEST_CASE("insertions");
164  for (i = 0; i < HEAP_TEST_SIZE; i++) {
165  TEST_CHECK((ret = fr_heap_insert(&hp, &array[i])) >= 0);
166  TEST_MSG("insert failed, returned %i - %s", ret, fr_strerror());
167 
168  TEST_CHECK(fr_heap_check(hp, &array[i]));
169  TEST_MSG("element %i inserted but not in heap", i);
170  }
171 
172  TEST_CASE("ordering");
173 
174  while ((thing = fr_heap_pop(&hp))) {
175  TEST_CHECK(thing->data >= data);
176  TEST_MSG("Expected data >= %i, got %i", data, thing->data);
177  if (thing->data >= data) data = thing->data;
178  TEST_CHECK(thing != prev);
179  prev = thing;
180  count++;
181  }
182 
184 
185  talloc_free(hp);
186  free(array);
187 }
188 
189 #define HEAP_ITER_SIZE 20
190 
191 static void heap_iter(void)
192 {
193  fr_heap_t *hp;
194  heap_thing *array;
195  unsigned int data_set;
196 
197  hp = fr_heap_alloc(NULL, heap_cmp, heap_thing, heap, 0);
198  TEST_CHECK(hp != NULL);
199 
200  array = calloc(HEAP_ITER_SIZE, sizeof(heap_thing));
201 
202  for (size_t i = 0; i < HEAP_ITER_SIZE; i++) {
203  array[i].data = i;
204  TEST_CHECK(fr_heap_insert(&hp, &array[i]) >= 0);
205  }
206 
207  data_set = 0;
209  TEST_CHECK((data_set & (1U << item->data)) == 0);
210  data_set |= (1U << item->data);
211  }}
212  TEST_CHECK(data_set == ((1U << HEAP_ITER_SIZE) - 1U));
213 
216 }
217 
218 static void heap_cycle(void)
219 {
220  fr_heap_t *hp;
221  int i;
222  heap_thing *array;
223  int to_remove;
224  int inserted, removed;
225  int ret;
226  fr_time_t start_insert, start_remove, start_swap, end;
227  fr_fast_rand_t rand_ctx;
228 
229  rand_ctx.a = fr_rand();
230  rand_ctx.b = fr_rand();
231 
232  hp = fr_heap_alloc(NULL, heap_cmp, heap_thing, heap, 0);
233  TEST_CHECK(hp != NULL);
234 
235  array = calloc(HEAP_CYCLE_SIZE, sizeof(heap_thing));
236 
237  /*
238  * Initialise random values
239  */
240  for (i = 0; i < HEAP_CYCLE_SIZE; i++) array[i].data = fr_fast_rand(&rand_ctx) % 65537;
241 
242  start_insert = fr_time();
243  TEST_CASE("insertions");
244  for (i = 0; i < HEAP_CYCLE_SIZE; i++) {
245  TEST_CHECK((ret = fr_heap_insert(&hp, &array[i])) >= 0);
246  TEST_MSG("insert failed, returned %i - %s", ret, fr_strerror());
247  }
249 
250  TEST_CASE("pop");
251 
252  /*
253  * Remove a random number of elements from the heap
254  */
255  to_remove = fr_heap_num_elements(hp) / 2;
256  start_remove = fr_time();
257  for (i = 0; i < to_remove; i++) {
258  heap_thing *t;
259 
260  TEST_CHECK((t = fr_heap_peek(hp)) != NULL);
261  TEST_MSG("expected %i elements remaining in the heap", to_remove - i);
262 
263  TEST_CHECK(fr_heap_extract(&hp, t) >= 0);
264  TEST_MSG("failed extracting %i - %s", i, fr_strerror());
265  }
266 
267  /*
268  * Now swap the inserted and removed set creating churn
269  */
270  start_swap = fr_time();
271  inserted = 0;
272  removed = 0;
273 
274  for (i = 0; i < HEAP_CYCLE_SIZE; i++) {
275  if (!fr_heap_entry_inserted(array[i].heap)) {
276  TEST_CHECK((ret = fr_heap_insert(&hp, &array[i])) >= 0);
277  TEST_MSG("insert failed, returned %i - %s", ret, fr_strerror());
278  inserted++;
279  } else {
280  TEST_CHECK((ret = fr_heap_extract(&hp, &array[i])) >= 0);
281  TEST_MSG("element %i removal failed, returned %i - %s", i, ret, fr_strerror());
282  removed++;
283  }
284  }
285 
286  TEST_CHECK(removed == (HEAP_CYCLE_SIZE - to_remove));
287  TEST_MSG("expected %i", HEAP_CYCLE_SIZE - to_remove);
288  TEST_MSG("got %i", removed);
289 
290  TEST_CHECK(inserted == to_remove);
291  TEST_MSG("expected %i", to_remove);
292  TEST_MSG("got %i", inserted);
293 
294  end = fr_time();
295 
296  TEST_MSG_ALWAYS("\ncycle size: %d\n", HEAP_CYCLE_SIZE);
297  TEST_MSG_ALWAYS("insert: %.2fs\n", fr_time_delta_unwrap(fr_time_sub(start_remove, start_insert)) / (double)NSEC);
298  TEST_MSG_ALWAYS("extract: %.2fs\n", fr_time_delta_unwrap(fr_time_sub(start_swap, start_remove))/ (double)NSEC);
299  TEST_MSG_ALWAYS("swap: %.2fs\n", fr_time_delta_unwrap(fr_time_sub(end, start_swap)) / (double)NSEC);
300 
301  talloc_free(hp);
302  free(array);
303 }
304 
306  /*
307  * Basic tests
308  */
309  { "heap_test_skip_0", heap_test_skip_0 },
310  { "heap_test_skip_2", heap_test_skip_2 },
311  { "heap_test_skip_10", heap_test_skip_10 },
312  { "heap_test_order", heap_test_order },
313  { "heap_iter", heap_iter },
314  { "heap_cycle", heap_cycle },
315  { NULL }
316 };
317 
#define TEST_MSG_ALWAYS(...)
Definition: acutest.h:222
#define TEST_CASE(name)
Definition: acutest.h:184
#define TEST_MSG(...)
Definition: acutest.h:215
#define CMP_PREFER_SMALLER(_a, _b)
Evaluates to +1 for a > b, and -1 for a < b.
Definition: build.h:102
return item
Definition: dcursor.h:553
Functions for a basic binary heaps.
void * fr_heap_pop(fr_heap_t **hp)
Remove a node from the heap.
Definition: heap.c:322
int fr_heap_insert(fr_heap_t **hp, void *data)
Insert a new element into the heap.
Definition: heap.c:146
int fr_heap_extract(fr_heap_t **hp, void *data)
Remove a node from the heap.
Definition: heap.c:239
static void * fr_heap_peek(fr_heap_t *h)
Return the item from the top of the heap but don't pop it.
Definition: heap.h:136
#define FR_HEAP_VERIFY(_heap)
Definition: heap.h:212
#define fr_heap_alloc(_ctx, _cmp, _type, _field, _init)
Creates a heap that can be used with non-talloced elements.
Definition: heap.h:100
unsigned int _CONST num_elements
Number of nodes used.
Definition: heap.h:72
static bool fr_heap_entry_inserted(fr_heap_index_t heap_idx)
Check if an entry is inserted into a heap.
Definition: heap.h:124
static unsigned int fr_heap_num_elements(fr_heap_t *h)
Return the number of elements in the heap.
Definition: heap.h:179
void *_CONST p[]
Array of nodes.
Definition: heap.h:77
#define fr_heap_foreach(_heap, _type, _data)
Iterate over the contents of a heap.
Definition: heap.h:205
The main heap structure.
Definition: heap.h:66
TEST_LIST
Definition: heap_tests.c:305
static void heap_test_order(void)
Definition: heap_tests.c:139
static int8_t heap_cmp(void const *one, void const *two)
Definition: heap_tests.c:33
unsigned int heap
Definition: heap_tests.c:24
#define HEAP_CYCLE_SIZE
Definition: heap_tests.c:137
#define HEAP_TEST_SIZE
Definition: heap_tests.c:40
free(array)
static void heap_iter(void)
Definition: heap_tests.c:191
static void heap_test_skip_0(void)
Definition: heap_tests.c:122
static void heap_test_skip_10(void)
Definition: heap_tests.c:132
static void heap_test_skip_2(void)
Definition: heap_tests.c:127
static void heap_test(int skip)
Definition: heap_tests.c:42
static bool fr_heap_check(fr_heap_t *h, void *data)
Definition: heap_tests.c:7
talloc_free(hp)
TEST_CHECK(data_set==((1U<< HEAP_ITER_SIZE) - 1U))
#define HEAP_ITER_SIZE
Definition: heap_tests.c:189
static void heap_cycle(void)
Definition: heap_tests.c:218
static size_t array[MY_ARRAY_SIZE]
uint32_t fr_fast_rand(fr_fast_rand_t *ctx)
Definition: rand.c:280
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition: rand.c:106
uint32_t b
Definition: rand.h:55
uint32_t a
Definition: rand.h:55
Smaller fast random number generator.
Definition: rand.h:54
return count
Definition: module.c:163
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition: state_test.c:8
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition: time.h:154
#define NSEC
Definition: time.h:379
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition: time.h:229
"server local" time.
Definition: time.h:69
char const * fr_strerror(void)
Get the last library error.
Definition: strerror.c:554
static fr_slen_t data
Definition: value.h:1265