The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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
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 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
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 ((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
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
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_CHECK(cond)
Definition acutest.h:85
#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:104
Functions for a basic binary heaps.
int fr_heap_insert(fr_heap_t **hp, void *data)
Insert a new element into the heap.
Definition heap.c:146
void * fr_heap_pop(fr_heap_t **hp)
Remove a node from the heap.
Definition heap.c:322
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)
#define HEAP_ITER_SIZE
Definition heap_tests.c:189
static void heap_cycle(void)
Definition heap_tests.c:218
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:122
uint32_t fr_fast_rand(fr_fast_rand_t *ctx)
Definition rand.c:279
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:105
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