1 /*
2  * Copyright (c) 2018 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <zephyr/ztest.h>
7 #include <zephyr/sys/rb.h>
8 
9 #include "../../../lib/utils/rb.c"
10 
11 #define _CHECK(n) \
12 	zassert_true(!!(n), "Tree check failed: [ " #n " ] @%d", __LINE__)
13 
14 #define MAX_NODES 256
15 
16 static struct rbtree test_rbtree;
17 
18 static struct rbnode nodes[MAX_NODES];
19 
20 /* Bit is set if node is in the tree */
21 static unsigned int node_mask[(MAX_NODES + 31)/32];
22 
23 /* Array of nodes dumped via rb_walk */
24 static struct rbnode *walked_nodes[MAX_NODES];
25 
26 /* Node currently being inserted, for testing lessthan() argument order */
27 static struct rbnode *current_insertee;
28 
set_node_mask(int node,int val)29 void set_node_mask(int node, int val)
30 {
31 	unsigned int *p = &node_mask[node / 32];
32 	unsigned int bit = 1u << (node % 32);
33 
34 	*p &= ~bit;
35 	*p |= val ? bit : 0;
36 }
37 
get_node_mask(int node)38 int get_node_mask(int node)
39 {
40 	unsigned int *p = &node_mask[node / 32];
41 	unsigned int bit = 1u << (node % 32);
42 
43 	return !!(*p & bit);
44 }
45 
node_index(struct rbnode * n)46 int node_index(struct rbnode *n)
47 {
48 	return (int)(n - &nodes[0]);
49 }
50 
51 /* Our "lessthan" is just the location of the struct */
node_lessthan(struct rbnode * a,struct rbnode * b)52 bool node_lessthan(struct rbnode *a, struct rbnode *b)
53 {
54 	if (current_insertee) {
55 		_CHECK(a == current_insertee);
56 		_CHECK(b != current_insertee);
57 	}
58 
59 	return a < b;
60 }
61 
62 /* Simple LCRNG (modulus is 2^64!) cribbed from:
63  * https://nuclear.llnl.gov/CNP/rng/rngman/node4.html
64  *
65  * Don't need much in the way of quality, do need repeatability across
66  * platforms.
67  */
next_rand_mod(unsigned int mod)68 static unsigned int next_rand_mod(unsigned int mod)
69 {
70 	static unsigned long long state = 123456789; /* seed */
71 
72 	state = state * 2862933555777941757ul + 3037000493ul;
73 
74 	return ((unsigned int)(state >> 32)) % mod;
75 }
76 
visit_node(struct rbnode * node,void * cookie)77 void visit_node(struct rbnode *node, void *cookie)
78 {
79 	int *nwalked = cookie;
80 
81 	_CHECK(*nwalked < MAX_NODES);
82 
83 	walked_nodes[*nwalked] = node;
84 	*nwalked += 1;
85 }
86 
87 /* Stores the last-seen black height at a leaf during check_rb(), or
88  * zero if no leaves have been seen yet
89  */
90 static int last_black_height;
91 
check_rbnode(struct rbnode * node,int blacks_above)92 void check_rbnode(struct rbnode *node, int blacks_above)
93 {
94 	int side, bheight = blacks_above + z_rb_is_black(node);
95 
96 	for (side = 0; side < 2; side++) {
97 		struct rbnode *ch = z_rb_child(node, side);
98 
99 		if (ch) {
100 			/* Basic tree requirement */
101 			if (side == 0) {
102 				_CHECK(node_lessthan(ch, node));
103 			} else {
104 				_CHECK(node_lessthan(node, ch));
105 			}
106 
107 			/* Can't have adjacent red nodes */
108 			_CHECK(z_rb_is_black(node) || z_rb_is_black(ch));
109 
110 			/* Recurse */
111 			check_rbnode(ch, bheight);
112 		} else {
113 			/* All leaf nodes must be at the same black height */
114 			if (last_black_height) {
115 				_CHECK(last_black_height == bheight);
116 			}
117 			last_black_height = bheight;
118 		}
119 	}
120 }
121 
check_rb(void)122 void check_rb(void)
123 {
124 	last_black_height = 0;
125 
126 	_CHECK(test_rbtree.root);
127 	_CHECK(z_rb_is_black(test_rbtree.root));
128 
129 	check_rbnode(test_rbtree.root, 0);
130 }
131 
132 /* First validates the external API behavior via a walk, then checks
133  * interior tree and red/black state via internal APIs.
134  */
_check_tree(int size,int use_foreach)135 void _check_tree(int size, int use_foreach)
136 {
137 	int nwalked = 0, i, ni;
138 	struct rbnode *n, *last = NULL;
139 
140 	(void)memset(walked_nodes, 0, sizeof(walked_nodes));
141 
142 	if (use_foreach) {
143 		RB_FOR_EACH(&test_rbtree, n) {
144 			visit_node(n, &nwalked);
145 		}
146 	} else {
147 		rb_walk(&test_rbtree, visit_node, &nwalked);
148 	}
149 
150 	/* Make sure all found nodes are in-order and marked in the tree */
151 	for (i = 0; i < nwalked; i++) {
152 		n = walked_nodes[i];
153 		ni = node_index(n);
154 
155 		if (last) {
156 			_CHECK(node_lessthan(last, n));
157 		}
158 
159 		_CHECK(get_node_mask(ni));
160 
161 		last = n;
162 	}
163 
164 	/* Make sure all tree bits properly reflect the set of nodes we found */
165 	ni = 0;
166 	for (i = 0; i < MAX_NODES; i++) {
167 		_CHECK(get_node_mask(i) == rb_contains(&test_rbtree, &nodes[i]));
168 
169 		if (get_node_mask(i)) {
170 			_CHECK(node_index(walked_nodes[ni]) == i);
171 			ni++;
172 		}
173 	}
174 
175 	_CHECK(ni == nwalked);
176 
177 	if (test_rbtree.root) {
178 		check_rb();
179 	}
180 }
181 
check_tree(int size)182 void check_tree(int size)
183 {
184 	/* Do it with both enumeration mechanisms */
185 	_check_tree(size, 0);
186 	_check_tree(size, 1);
187 }
188 
checked_insert(struct rbtree * tree,struct rbnode * node)189 void checked_insert(struct rbtree *tree, struct rbnode *node)
190 {
191 	current_insertee = node;
192 	rb_insert(tree, node);
193 	current_insertee = NULL;
194 }
195 
test_tree(int size)196 void test_tree(int size)
197 {
198 	int i, j;
199 
200 	/* Small trees get checked after every op, big trees less often */
201 	int small_tree = size <= 32;
202 
203 	(void)memset(&test_rbtree, 0, sizeof(test_rbtree));
204 	test_rbtree.lessthan_fn = node_lessthan;
205 	(void)memset(nodes, 0, sizeof(nodes));
206 	(void)memset(node_mask, 0, sizeof(node_mask));
207 
208 	for (j = 0; j < 10; j++) {
209 		for (i = 0; i < size; i++) {
210 			int node = next_rand_mod(size);
211 
212 			if (!get_node_mask(node)) {
213 				rb_insert(&test_rbtree, &nodes[node]);
214 				set_node_mask(node, 1);
215 			} else {
216 				rb_remove(&test_rbtree, &nodes[node]);
217 				set_node_mask(node, 0);
218 			}
219 
220 			if (small_tree) {
221 				check_tree(size);
222 			}
223 		}
224 
225 		if (!small_tree) {
226 			check_tree(size);
227 		}
228 	}
229 }
230 
ZTEST(rbtree_api,test_rbtree_spam)231 ZTEST(rbtree_api, test_rbtree_spam)
232 {
233 	int size = 1;
234 
235 	do {
236 		size += next_rand_mod(size) + 1;
237 
238 		if (size > MAX_NODES) {
239 			size = MAX_NODES;
240 		}
241 
242 		TC_PRINT("Checking trees built from %d nodes...\n", size);
243 
244 		test_tree(size);
245 	} while (size < MAX_NODES);
246 }
247 
248 /**
249  * @brief Test removing a node with abnormal color.
250  *
251  * @details Initialize a tree and insert it,
252  * and test APIs rb_get_min(), rb_get_max().
253  *
254  * @ingroup lib_rbtree_tests
255  *
256  * @see rb_get_min(), rb_get_max()
257  */
ZTEST(rbtree_api,test_rb_get_minmax)258 ZTEST(rbtree_api, test_rb_get_minmax)
259 {
260 	struct rbnode temp = {0};
261 
262 	/* Initialize a tree and insert it */
263 	(void)memset(&test_rbtree, 0, sizeof(test_rbtree));
264 	test_rbtree.lessthan_fn = node_lessthan;
265 	(void)memset(nodes, 0, sizeof(nodes));
266 
267 	zassert_true(rb_get_min(&test_rbtree) == NULL, "the tree is invalid");
268 
269 	for (int i = 0; i < 8; i++) {
270 		rb_insert(&test_rbtree, &nodes[i]);
271 	}
272 
273 	rb_remove(&test_rbtree, &temp);
274 
275 	/* Check if tree's max and min node are expected */
276 	zassert_true(rb_get_min(&test_rbtree) == &nodes[0], "the tree is invalid");
277 	zassert_true(rb_get_max(&test_rbtree) == &nodes[7], "the tree is invalid");
278 }
279 
280 ZTEST_SUITE(rbtree_api, NULL, NULL, NULL, NULL, NULL);
281