The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
heap_tests.c
Go to the documentation of this file.
1#include "acutest.h"
2#include <freeradius-devel/util/time.h>
3#include <freeradius-devel/util/rand.h>
4
5#include "../heap.c"
6
7static 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
22typedef struct {
23 int data;
24 unsigned int heap; /* for the heap */
26
27
28/*
29 * cc -g -DTESTING -I .. heap.c -o heap
30 *
31 * ./heap
32 */
33static fr_cmp_ret_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
42static void heap_test(int skip)
43{
44 fr_heap_t *hp;
45 int i;
46 heap_thing *array;
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++) {
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 int entry;
84
85 for (i = 0; i < HEAP_TEST_SIZE / skip; i++) {
86 entry = i * skip;
87
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
122static void heap_test_skip_0(void)
123{
124 heap_test(1);
125}
126
127static void heap_test_skip_2(void)
128{
129 heap_test(2);
130}
131
132static void heap_test_skip_10(void)
133{
134 heap_test(10);
135}
136
137#define HEAP_CYCLE_SIZE (1600000)
138
139static 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 ((fr_heap_pop((void **)&thing, &hp) == 0) && thing) {
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
191static 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
215 free(array);
216}
217
218static 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
305static bool poisoned;
306
307static fr_cmp_ret_t heap_poison_cmp(void const *one, void const *two)
308{
309 if (poisoned) {
310 fr_strerror_const("Poisoned comparator");
311 return CMP_ERR;
312 }
313
314 return heap_cmp(one, two);
315}
316
317/*
318 * A comparator returning CMP_ERR must complete the operation
319 * structurally (no lost or duplicated elements), report the error
320 * through the int return, and leave the ordering undefined
321 * until the offending element is removed.
322 */
323static void heap_test_cmp_err(void)
324{
325 fr_heap_t *hp;
326 heap_thing values[8] = {};
327 void *found;
328 size_t i;
329 unsigned int popped;
330
331 TEST_CASE("comparator error reported, heap structurally intact");
332 hp = fr_heap_alloc(NULL, heap_poison_cmp, heap_thing, heap, 0);
333 TEST_ASSERT(hp != NULL);
334
335 poisoned = false;
336 for (i = 0; i < NUM_ELEMENTS(values); i++) {
337 values[i].data = (int)(NUM_ELEMENTS(values) - i);
338 TEST_CHECK(fr_heap_insert(&hp, &values[i]) == 0);
339 }
340
341 poisoned = true;
342
343 TEST_CHECK(fr_heap_pop(&found, &hp) == -1);
344 TEST_CHECK(found == NULL);
345 TEST_CHECK(fr_heap_num_elements(hp) == NUM_ELEMENTS(values) - 1);
346
347 /*
348 * Once the comparator recovers, every remaining element pops
349 * back out exactly once.
350 */
351 poisoned = false;
352 popped = 0;
353 while ((fr_heap_pop(&found, &hp) == 0) && found) {
354 popped++;
355 }
356 TEST_CHECK(popped == NUM_ELEMENTS(values) - 1);
358
359 talloc_free(hp);
360}
361
363 /*
364 * Basic tests
365 */
366 { "heap_test_skip_0", heap_test_skip_0 },
367 { "heap_test_skip_2", heap_test_skip_2 },
368 { "heap_test_skip_10", heap_test_skip_10 },
369 { "heap_test_order", heap_test_order },
370 { "heap_iter", heap_iter },
371 { "heap_cycle", heap_cycle },
372 { "heap_test_cmp_err", heap_test_cmp_err },
374};
375
#define TEST_MSG_ALWAYS(...)
Definition acutest.h:224
#define TEST_CHECK(cond)
Definition acutest.h:87
#define TEST_CASE(name)
Definition acutest.h:186
#define TEST_ASSERT(cond)
Definition acutest.h:110
#define TEST_TERMINATOR
Definition acutest.h:64
#define TEST_MSG(...)
Definition acutest.h:217
#define CMP_PREFER_SMALLER(_a, _b)
Evaluates to +1 for a > b, and -1 for a < b.
Definition build.h:105
#define NUM_ELEMENTS(_t)
Definition build.h:406
int fr_heap_insert(fr_heap_t **hp, void *data)
Insert a new element into the heap.
Definition heap.c:149
int fr_heap_pop(void **out, fr_heap_t **hp)
Remove a node from the heap.
Definition heap.c:359
int fr_heap_extract(fr_heap_t **hp, void *data)
Remove a node from the heap.
Definition heap.c:259
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:138
#define FR_HEAP_VERIFY(_heap)
Definition heap.h:214
#define fr_heap_alloc(_ctx, _cmp, _type, _field, _init)
Creates a heap that can be used with non-talloced elements.
Definition heap.h:102
unsigned int _CONST num_elements
Number of nodes used.
Definition heap.h:74
static bool fr_heap_entry_inserted(fr_heap_index_t heap_idx)
Check if an entry is inserted into a heap.
Definition heap.h:126
static unsigned int fr_heap_num_elements(fr_heap_t *h)
Return the number of elements in the heap.
Definition heap.h:181
void *_CONST p[]
Array of nodes.
Definition heap.h:79
#define fr_heap_foreach(_heap, _type, _data)
Iterate over the contents of a heap.
Definition heap.h:207
The main heap structure.
Definition heap.h:68
TEST_LIST
Definition heap_tests.c:362
static void heap_test_order(void)
Definition heap_tests.c:139
static fr_cmp_ret_t heap_poison_cmp(void const *one, void const *two)
Definition heap_tests.c:307
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
static void heap_test_cmp_err(void)
Definition heap_tests.c:323
free(array)
static void heap_iter(void)
Definition heap_tests.c:191
static bool poisoned
Definition heap_tests.c:305
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 fr_cmp_ret_t heap_cmp(void const *one, void const *two)
Definition heap_tests.c:33
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)
#define HEAP_ITER_SIZE
Definition heap_tests.c:189
static void heap_cycle(void)
Definition heap_tests.c:218
#define fr_time()
Definition event.c:60
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:121
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
@ CMP_ERR
comparison failed
Definition misc.h:51
uint32_t fr_fast_rand(fr_fast_rand_t *ctx)
Definition rand.c:278
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:104
uint32_t b
Definition rand.h:55
uint32_t a
Definition rand.h:55
Smaller fast random number generator.
Definition rand.h:54
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
static unsigned count
Definition unittest.c:47
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1340