1 /*
2  * Copyright (c) 2017 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/sys/rb.h>
9 
10 #define TREE_SIZE 512
11 /* zephyr can't do floating-point arithmetic,
12  * so manual: dlog_N = 2log(TREE_SIZE) = 18
13  */
14 const static uint32_t dlog_N = 18;
15 
16 /* rbnode structure is embeddable in user structure */
17 struct container_node {
18 	struct rbnode node;
19 	int value;
20 };
21 static struct rbnode nodes[TREE_SIZE];
22 static struct rbtree test_rbtree;
23 
24 /* Our "lessthan" is just the location of the struct */
node_lessthan(struct rbnode * a,struct rbnode * b)25 bool node_lessthan(struct rbnode *a, struct rbnode *b)
26 {
27 	return a < b;
28 }
29 
30 /**
31  * @brief Test whether rbtree node struct is embedded
32  * in any user struct
33  *
34  * @details
35  * Test Objective:
36  * - Define and initialize a rbtree, and test two features:
37  * first, rbtree node struct can be embedded in any user struct.
38  * last, rbtree can be walked though by some macro APIs.
39  *
40  * Testing techniques:
41  * - Interface testing
42  * - Dynamic analysis and testing
43  * - Structural test coverage(entry points,statements,branches)
44  *
45  * Prerequisite Conditions:
46  * - Design a predicate function node_lessthan
47  * - Define a rbtree by using struct rbtree
48  *
49  * Input Specifications:
50  * - N/A
51  *
52  * Test Procedure:
53  * -# Define some arrays of rbtree nodes.And initialize
54  * the rbtree.
55  * -# Then inserting some nodes into the rbtree.
56  * -# Check if the inserted nodes are contained in the
57  * rbtree by using the iteration APIs.
58  *
59  * Expected Test Result:
60  * - The inserted nodes are contained in the
61  * rbtree by using the iteration APIs.
62  *
63  * Pass/Fail Criteria:
64  * - Successful if check points in test procedure are all pass,
65  * otherwise failure.
66  *
67  * Assumptions and Constraints:
68  * - N/A
69  *
70  * @ingroup lib_rbtree_tests
71  *
72  * @see RB_FOR_EACH(),RB_FOR_EACH_CONTAINER()
73  */
ZTEST(rbtree_perf,test_rbtree_container)74 ZTEST(rbtree_perf, test_rbtree_container)
75 {
76 	int count = 0;
77 	struct rbtree test_tree_l;
78 	struct container_node *c_foreach_node;
79 	struct rbnode *foreach_node;
80 	struct container_node tree_node[10];
81 
82 	(void)memset(&test_tree_l, 0, sizeof(test_tree_l));
83 	(void)memset(tree_node, 0, sizeof(tree_node));
84 
85 	test_tree_l.lessthan_fn = node_lessthan;
86 	for (uint32_t i = 0; i < ARRAY_SIZE(tree_node); i++) {
87 		tree_node[i].value = i;
88 		rb_insert(&test_tree_l, &tree_node[i].node);
89 	}
90 
91 	RB_FOR_EACH(&test_tree_l, foreach_node) {
92 		zassert_true(CONTAINER_OF(foreach_node, struct container_node,
93 		node)->value == count, "RB_FOR_EACH failed");
94 		count++;
95 	}
96 
97 	count = 0;
98 
99 	RB_FOR_EACH_CONTAINER(&test_tree_l, c_foreach_node, node) {
100 		zassert_true(c_foreach_node->value == count,
101 		"RB_FOR_EACH_CONTAINER failed");
102 		count++;
103 	}
104 }
105 
106 /* initialize and insert a tree */
init_tree(struct rbtree * tree,int size)107 static void init_tree(struct rbtree *tree, int size)
108 {
109 	tree->lessthan_fn = node_lessthan;
110 
111 	for (uint32_t i = 0; i < size; i++) {
112 		rb_insert(tree, &nodes[i]);
113 	}
114 }
115 
search_height_recurse(struct rbnode * node,struct rbnode * final_node,uint32_t current_height)116 static int search_height_recurse(struct rbnode *node, struct rbnode
117 			*final_node, uint32_t current_height)
118 {
119 	if (node == NULL) {
120 		return -1;
121 	}
122 
123 	if (node == final_node) {
124 		return current_height;
125 	}
126 
127 	current_height++;
128 	struct rbnode *ch = z_rb_child(node,
129 			!test_rbtree.lessthan_fn(final_node, node));
130 
131 	return search_height_recurse(ch, final_node, current_height);
132 }
133 
verify_rbtree_perf(struct rbnode * root,struct rbnode * test)134 static void verify_rbtree_perf(struct rbnode *root, struct rbnode *test)
135 {
136 	uint32_t node_height = 0;
137 
138 	node_height = search_height_recurse(root, test, node_height);
139 	zassert_true(node_height <= dlog_N);
140 }
141 
142 /**
143  * @brief Test some operations of rbtree are running in
144  * logarithmic time
145  *
146  * @details
147  * Test Objective:
148  * - The inserted, removed, get minimum and get maximum operations
149  * of rbtree are in logarithmic time by comparing a node's operation
150  * height with the worst-case's height.
151  *
152  * Testing techniques:
153  * - Interface testing
154  * - Dynamic analysis and testing
155  * - Structural test coverage(entry points,statements,branches)
156  *
157  * Prerequisite Conditions:
158  * - Design a predicate function node_lessthan
159  * - Define a rbtree by using struct rbtree
160  *
161  * Input Specifications:
162  * - N/A
163  *
164  * Test Procedure:
165  * -# Initialize the rbtree and insert some nodes on it.
166  * -# Search the far left/right node by recursion and
167  * record its height.
168  * -# Check if the recorded heights are less than the worst
169  * case height(log2(N)).
170  * -# Select out a node at the rbtree arbitrarily as remove
171  * and insert node. And record the height.
172  * -# repeat the check point above.
173  *
174  * Expected Test Result:
175  * - Some operations of rbtree are running in
176  * logarithmic time
177  *
178  * Pass/Fail Criteria:
179  * - Successful if check points in test procedure are all pass,
180  * otherwise failure.
181  *
182  * Assumptions and Constraints:
183  * - N/A
184  *
185  * @ingroup lib_rbtree_tests
186  *
187  * @see rb_get_min(), rb_get_max()
188  */
ZTEST(rbtree_perf,test_rbtree_perf)189 ZTEST(rbtree_perf, test_rbtree_perf)
190 {
191 	init_tree(&test_rbtree, TREE_SIZE);
192 	struct rbnode *root = test_rbtree.root;
193 	struct rbnode *test = NULL;
194 
195 	test = rb_get_min(&test_rbtree);
196 	verify_rbtree_perf(root, test);
197 
198 	test = rb_get_max(&test_rbtree);
199 	verify_rbtree_perf(root, test);
200 
201 	/* insert and remove a same node with same height.Assume that
202 	 * the node nodes[TREE_SIZE/2] will be removed and inserted,
203 	 * verify that searching times is less than 2logN
204 	 * using the height of this node.
205 	 */
206 	test = &nodes[TREE_SIZE/2];
207 	verify_rbtree_perf(root, test);
208 }
209 
210 ZTEST_SUITE(rbtree_perf, NULL, NULL, NULL, NULL, NULL);
211