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