The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
rb_tests.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Tests for rbtrees
18 *
19 * @file src/lib/util/test//rb_tests.c
20 *
21 * @copyright 2021 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
22 */
23#include "acutest_common_init.h"
24#include "acutest_helpers.h"
25#include <freeradius-devel/util/rand.h>
26#include <freeradius-devel/util/rb.h>
27
28#define MAXSIZE 128
29
34
35static fr_cmp_ret_t fr_rb_tree_test_cmp(void const *one, void const *two)
36{
37 fr_rb_tree_test_node_t const *a = one, *b = two;
38 return CMP(a->num, b->num);
39}
40
41static int fr_rb_qsort_cmp(void const *one, void const *two)
42{
43 fr_rb_tree_test_node_t const *a = one, *b = two;
44 return CMP(a->num, b->num);
45}
46
47static void test_fr_rb_iter_inorder(void)
48{
49 fr_rb_tree_t *t;
52 size_t n, i;
54
55 TEST_CASE("in-order iterator");
57 TEST_CHECK(t != NULL);
58
59 n = (fr_rand() % MAXSIZE) + 1;
60
61 /*
62 * Initialise the test nodes
63 * with random numbers.
64 */
65 for (i = 0; i < n; i++) {
66 p = talloc(t, fr_rb_tree_test_node_t);
67 p->num = fr_rand();
68 sorted[i].num = p->num;
69 fr_rb_insert(t, p);
70 }
71
72 qsort(sorted, n, sizeof(fr_rb_tree_test_node_t), fr_rb_qsort_cmp);
73
74 for (p = fr_rb_iter_init_inorder(t, &iter), i = 0;
75 p;
76 p = fr_rb_iter_next_inorder(t, &iter), i++) {
77 TEST_MSG("Checking sorted[%zu] s = %u vs n = %u", i, sorted[i].num, p->num);
78 TEST_CHECK(sorted[i].num == p->num);
79 }
80
81 talloc_free(t);
82}
83
84/*
85 * There's no natural test for pre- and post-order traversal
86 * as there is for in-order, so we must content ourselves
87 * with static test data.
88 */
89static uint32_t pre_post_input[] = {0, 15, 256, 49, 3, 8192, 144, 4, 4096, 25194};
90static uint32_t pre_output[] = {15, 3, 0, 4, 256, 49, 144, 8192, 4096, 25194};
91static uint32_t post_output[] = {0, 4, 3, 144, 49, 4096, 25194, 8192, 256, 15};
92
93static void test_fr_rb_iter_preorder(void)
94{
95 fr_rb_tree_t *t;
97 size_t i;
99
100 TEST_CASE("pre-order iterator");
101 /*
102 * Build a tree from pre_post_input.
103 */
105 TEST_CHECK(t != NULL);
106
107 for (i = 0; i < sizeof(pre_post_input) / sizeof(uint32_t); i++) {
108 p = talloc(t, fr_rb_tree_test_node_t);
109 p->num = pre_post_input[i];
110 fr_rb_insert(t, p);
111 }
112
113 for (p = fr_rb_iter_init_preorder(t, &iter), i = 0;
114 p;
115 p = fr_rb_iter_next_preorder(t, &iter), i++) {
116 TEST_MSG("Checking pre_output[%zu] = %u vs n = %u", i, pre_output[i], p->num);
117 TEST_CHECK(pre_output[i] == p->num);
118 }
119
120 talloc_free(t);
121}
122
124{
125 fr_rb_tree_t *t;
127 size_t i;
129
130 TEST_CASE("post-order iterator");
131 /*
132 * Build a tree from pre_post_input.
133 */
135 TEST_CHECK(t != NULL);
136
137 for (i = 0; i < sizeof(pre_post_input) / sizeof(uint32_t); i++) {
138 p = talloc(t, fr_rb_tree_test_node_t);
139 p->num = pre_post_input[i];
140 fr_rb_insert(t, p);
141 }
142
143 for (p = fr_rb_iter_init_postorder(t, &iter), i = 0;
144 p;
145 p = fr_rb_iter_next_postorder(t, &iter), i++) {
146 TEST_MSG("Checking post_output[%zu] s = %u vs n = %u", i, post_output[i], p->num);
147 TEST_CHECK(post_output[i] == p->num);
148 }
149
150 talloc_free(t);
151}
152
153/*
154 * primality test used in fr_rb_delete_iter() test.
155 */
156static bool is_prime(uint32_t n)
157{
158 uint32_t i, q;
159
160 if (n < 2) return false;
161
162 for (i = 2; (q = n / i) >= i; i++) {
163 if (i * q == n) return false;
164 }
165 return true;
166}
167
168static uint32_t non_primes[] = { 1, 4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25, 26, 27, 28,
169 30, 32, 33, 34, 35, 36, 38, 39, 40, 42, 44, 45, 46, 48, 49, 50};
170
171static void test_fr_rb_iter_delete(void)
172{
173 fr_rb_tree_t *t;
174 size_t i;
177
179 TEST_CHECK(t != NULL);
180
181 /*
182 * Initialise the test nodes
183 * with integers from 1 to 50.
184 */
185 for (i = 1; i <= 50; i++) {
186 p = talloc(t, fr_rb_tree_test_node_t);
187 p->num = i;
188 fr_rb_insert(t, p);
189 }
190
191 /*
192 * Remove the primes.
193 */
194 for (p = fr_rb_iter_init_inorder(t, &iter);
195 p;
196 p = fr_rb_iter_next_inorder(t, &iter)) {
197 if (is_prime(p->num)) fr_rb_iter_delete_inorder(t, &iter);
198 }
199
200 /*
201 * Check that all the non-primes are still there.
202 */
203 for (p = fr_rb_iter_init_inorder(t, &iter), i = 0;
204 p;
205 p = fr_rb_iter_next_inorder(t, &iter), i++) {
206 TEST_MSG("Checking non_primes[%zu] = %u vs p->num = %u", i, non_primes[i], p->num);
207 TEST_CHECK(non_primes[i] == p->num);
208 }
209
210 talloc_free(t);
211}
212
213static bool poisoned;
214
215static fr_cmp_ret_t fr_rb_tree_test_poison_cmp(void const *one, void const *two)
216{
217 if (poisoned) {
218 fr_strerror_const("Poisoned comparator");
219 return CMP_ERR;
220 }
221
222 return fr_rb_tree_test_cmp(one, two);
223}
224
225/*
226 * A comparator returning CMP_ERR must fail the operation closed,
227 * report the error through the int return, and leave the
228 * tree structurally intact.
229 */
230static void test_fr_rb_cmp_err(void)
231{
232 fr_rb_tree_t *t;
233 fr_rb_tree_test_node_t nodes[8];
235 size_t i;
236 void *found;
237
238 TEST_CASE("comparator error fails closed");
240 TEST_CHECK(t != NULL);
241
242 poisoned = false;
243 for (i = 0; i < NUM_ELEMENTS(nodes); i++) {
244 nodes[i].num = i * 16;
245 TEST_CHECK(fr_rb_insert(t, &nodes[i]) == 0);
246 }
247
248 poisoned = true;
249 needle.num = 3 * 16;
250
251 TEST_CHECK(fr_rb_find(&found, t, &needle) == -1);
252 TEST_CHECK(found == NULL);
253
254 needle.num = 9 * 16;
255 TEST_CHECK(fr_rb_insert(t, &needle) == -1);
257
258 found = &needle;
259 TEST_CHECK(fr_rb_find_or_insert(&found, t, &needle) == -1);
260 TEST_CHECK(found == NULL);
261
262 needle.num = 3 * 16;
263 TEST_CHECK(fr_rb_remove(&found, t, &needle) == -1);
264 TEST_CHECK(found == NULL);
265
266 TEST_CHECK(fr_rb_delete(t, &needle) == -1);
268
269 /*
270 * Once the comparator recovers every element is still
271 * present and findable.
272 */
273 poisoned = false;
274 for (i = 0; i < NUM_ELEMENTS(nodes); i++) {
275 needle.num = i * 16;
276 TEST_CHECK(fr_rb_find(&found, t, &needle) == 0);
277 TEST_CHECK(found == &nodes[i]);
278 }
279
280 talloc_free(t);
281}
282
284 { "fr_rb_iter_inorder", test_fr_rb_iter_inorder },
285 { "fr_rb_iter_preorder", test_fr_rb_iter_preorder },
286 { "fr_rb_iter_postorder", test_fr_rb_iter_postorder },
287 { "fr_rb_iter_delete", test_fr_rb_iter_delete },
288 { "fr_rb_cmp_err", test_fr_rb_cmp_err },
289
291};
#define TEST_CHECK(cond)
Definition acutest.h:87
int n
Definition acutest.h:577
#define TEST_CASE(name)
Definition acutest.h:186
#define TEST_TERMINATOR
Definition acutest.h:64
#define TEST_MSG(...)
Definition acutest.h:217
Common initialisation for acutest binaries.
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:113
#define NUM_ELEMENTS(_t)
Definition build.h:406
talloc_free(hp)
unsigned int uint32_t
fr_cmp_ret_t
Result of an ordering comparison.
Definition misc.h:50
@ CMP_ERR
comparison failed
Definition misc.h:51
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:104
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:807
int fr_rb_remove(void **removed, fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition rb.c:718
int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:586
void * fr_rb_iter_init_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Initialise an in-order iterator.
Definition rb.c:850
void fr_rb_iter_delete_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Remove the current node from the tree.
Definition rb.c:925
int fr_rb_find_or_insert(void **found, fr_rb_tree_t *tree, void const *data)
Attempt to find current data in the tree, if it does not exist insert it.
Definition rb.c:608
void * fr_rb_iter_init_preorder(fr_rb_tree_t *tree, fr_rb_iter_preorder_t *iter)
Initialise a pre-order iterator.
Definition rb.c:944
void * fr_rb_iter_next_postorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_postorder_t *iter)
Return the next node.
Definition rb.c:1052
void * fr_rb_iter_next_preorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_preorder_t *iter)
Return the next node.
Definition rb.c:968
int fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition rb.c:767
void * fr_rb_iter_init_postorder(fr_rb_tree_t *tree, fr_rb_iter_postorder_t *iter)
Initialise a post-order iterator.
Definition rb.c:1021
int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:637
void * fr_rb_iter_next_inorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:876
#define fr_rb_inline_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black tree.
Definition rb.h:269
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:319
Iterator structure for post-order traversal of an rbtree.
Definition rb.h:352
Iterator structure for pre-order traversal of an rbtree.
Definition rb.h:337
The main red black tree structure.
Definition rb.h:71
TEST_LIST
Definition rb_tests.c:283
static void test_fr_rb_iter_postorder(void)
Definition rb_tests.c:123
static uint32_t non_primes[]
Definition rb_tests.c:168
static fr_cmp_ret_t fr_rb_tree_test_cmp(void const *one, void const *two)
Definition rb_tests.c:35
static void test_fr_rb_cmp_err(void)
Definition rb_tests.c:230
static uint32_t pre_output[]
Definition rb_tests.c:90
static void test_fr_rb_iter_inorder(void)
Definition rb_tests.c:47
#define MAXSIZE
Definition rb_tests.c:28
static int fr_rb_qsort_cmp(void const *one, void const *two)
Definition rb_tests.c:41
static uint32_t pre_post_input[]
Definition rb_tests.c:89
static bool is_prime(uint32_t n)
Definition rb_tests.c:156
static void test_fr_rb_iter_delete(void)
Definition rb_tests.c:171
static void test_fr_rb_iter_preorder(void)
Definition rb_tests.c:93
static bool poisoned
Definition rb_tests.c:213
static uint32_t post_output[]
Definition rb_tests.c:91
static fr_cmp_ret_t fr_rb_tree_test_poison_cmp(void const *one, void const *two)
Definition rb_tests.c:215
fr_rb_node_t node
Definition rb_tests.c:32
#define fr_strerror_const(_msg)
Definition strerror.h:223