Fix rbtree foreach traversal#9
Conversation
Reviewer's GuideAdds targeted test coverage for om_rbtree_foreach traversal and fixes the internal recursive implementation to correctly traverse all nodes and honor early-termination semantics. Flow diagram for updated _om_rbtree_foreach traversal and early terminationflowchart TD
A_start["_om_rbtree_foreach(node, fun, arg)"] --> B_isNull{"node == NULL?"}
B_isNull -- "yes" --> B_retTrue["return true"]
B_isNull -- "no" --> C_callLeft["call _om_rbtree_foreach(node.left, fun, arg)"]
C_callLeft --> C_leftResult{"left returned true?"}
C_leftResult -- "no" --> C_retFalse["return false"]
C_leftResult -- "yes" --> D_callFun["call fun(node, arg)"]
D_callFun --> D_funResult{"fun returned true?"}
D_funResult -- "no" --> D_retFalse["return false"]
D_funResult -- "yes" --> E_callRight["call _om_rbtree_foreach(node.right, fun, arg)"]
E_callRight --> E_retRight["return value from right subtree"]
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 3 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="test/om_test_base.c" line_range="138-136" />
<code_context>
}
END_TEST
+START_TEST(_RBT_FOREACH) {
+ RBT_ROOT(rbt);
+ om_rbt_node_t node = {.key = "B"};
+ int count = 0;
+
+ om_rbtree_insert(&rbt, &node);
+ om_rbtree_foreach(&rbt, _rbt_foreach_once, &count);
+
+ ck_assert_msg(count == 1, "单节点遍历应执行1次回调,实际执行%d次", count);
+}
+END_TEST
+
Suite* make_om_base_suite(void) {
</code_context>
<issue_to_address>
**suggestion (testing):** Add tests to cover multi-node traversals and verify full in-order behavior, not just the single-node case.
Given the traversal bug fixed in this PR, this test should exercise more than the single-node case. Please build a small multi-node tree (e.g., three nodes that force visiting both left and right branches) and assert that all nodes are visited and that the visit sequence is strictly in-order by key (for example, inserting keys "A", "B", "C" and collecting the visited keys in the callback to check the exact order).
</issue_to_address>
### Comment 2
<location path="test/om_test_base.c" line_range="144" />
<code_context>
+ int count = 0;
+
+ om_rbtree_insert(&rbt, &node);
+ om_rbtree_foreach(&rbt, _rbt_foreach_once, &count);
+
+ ck_assert_msg(count == 1, "单节点遍历应执行1次回调,实际执行%d次", count);
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test that verifies early termination when the callback returns false.
Since the tests only use callbacks that return `true`, they don't verify the new short-circuit behavior. Please add a test where the callback returns `false` after visiting the first (or second) node, and assert both that traversal stops invoking the callback after that node (e.g., via a call counter or collected keys) and that the `false` result is propagated (or at least that no further nodes are visited).
Suggested implementation:
```c
static bool _rbt_foreach_early_stop(om_rbt_node_t* node, void* data) {
int* count = (int*)data;
(*count)++;
/* 在访问到第2个节点后中止遍历 */
if (*count >= 2) {
return false;
}
return true;
}
START_TEST(_RBT_FOREACH) {
```
```c
END_TEST
START_TEST(_RBT_FOREACH_EARLY_STOP) {
RBT_ROOT(rbt);
om_rbt_node_t node_a = {.key = "A"};
om_rbt_node_t node_b = {.key = "B"};
om_rbt_node_t node_c = {.key = "C"};
int count = 0;
om_rbtree_insert(&rbt, &node_b);
om_rbtree_insert(&rbt, &node_a);
om_rbtree_insert(&rbt, &node_c);
om_rbtree_foreach(&rbt, _rbt_foreach_early_stop, &count);
ck_assert_msg(count == 2, "回调返回false后应只访问前2个节点,实际访问%d个", count);
}
END_TEST
Suite* make_om_base_suite(void) {
```
```c
tcase_add_test(tc_rbt, _RBT);
tcase_add_test(tc_rbt, _RBT_FOREACH);
tcase_add_test(tc_rbt, _RBT_FOREACH_EARLY_STOP);
```
1. This patch assumes `bool` is already available (e.g., via `#include <stdbool.h>` in this file or a common header). If not, add that include near the top of `om_test_base.c`.
2. The exact callback signature for `om_rbtree_foreach` was inferred from `_rbt_foreach_once`; if it differs (e.g., const qualifiers or different parameter order), adjust `_rbt_foreach_early_stop` to match it.
3. If your traversal guarantees a different visit order or if the tree may contain fewer than 3 nodes in some configurations, you may want to relax the assertion to `ck_assert_int_le(count, 2)` and separately assert `count >= 1`.
</issue_to_address>
### Comment 3
<location path="test/om_test_base.c" line_range="139" />
<code_context>
END_TEST
+START_TEST(_RBT_FOREACH) {
+ RBT_ROOT(rbt);
+ om_rbt_node_t node = {.key = "B"};
+ int count = 0;
</code_context>
<issue_to_address>
**suggestion (testing):** Consider adding a dedicated test for traversing an empty tree.
Since the fix now returns `true` when `node == NULL`, please add a test that calls `om_rbtree_foreach` on an empty `RBT_ROOT` and verifies that the callback is never invoked (e.g., a counter stays at 0). This will protect the `node == NULL` base case from regressions.
Suggested implementation:
```c
START_TEST(_RBT_FOREACH) {
RBT_ROOT(rbt);
om_rbt_node_t node = {.key = "B"};
int count = 0;
om_rbtree_insert(&rbt, &node);
om_rbtree_foreach(&rbt, _rbt_foreach_once, &count);
ck_assert_msg(count == 1, "单节点遍历应执行1次回调,实际执行%d次", count);
}
END_TEST
START_TEST(_RBT_FOREACH_EMPTY) {
RBT_ROOT(rbt);
int count = 0;
om_rbtree_foreach(&rbt, _rbt_foreach_once, &count);
ck_assert_msg(count == 0, "空树遍历不应执行回调,实际执行%d次", count);
}
END_TEST
```
To actually run the new `_RBT_FOREACH_EMPTY` test, you will also need to register it in the suite definition (likely in `make_om_base_suite` or wherever other `START_TEST` cases are added with `tcase_add_test`). Add a corresponding `tcase_add_test(tc, _RBT_FOREACH_EMPTY);` next to the existing `_RBT_FOREACH` registration.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| @@ -127,6 +135,18 @@ START_TEST(_RBT) { | |||
| } | |||
| END_TEST | |||
There was a problem hiding this comment.
suggestion (testing): Add tests to cover multi-node traversals and verify full in-order behavior, not just the single-node case.
Given the traversal bug fixed in this PR, this test should exercise more than the single-node case. Please build a small multi-node tree (e.g., three nodes that force visiting both left and right branches) and assert that all nodes are visited and that the visit sequence is strictly in-order by key (for example, inserting keys "A", "B", "C" and collecting the visited keys in the callback to check the exact order).
| int count = 0; | ||
|
|
||
| om_rbtree_insert(&rbt, &node); | ||
| om_rbtree_foreach(&rbt, _rbt_foreach_once, &count); |
There was a problem hiding this comment.
suggestion (testing): Add a test that verifies early termination when the callback returns false.
Since the tests only use callbacks that return true, they don't verify the new short-circuit behavior. Please add a test where the callback returns false after visiting the first (or second) node, and assert both that traversal stops invoking the callback after that node (e.g., via a call counter or collected keys) and that the false result is propagated (or at least that no further nodes are visited).
Suggested implementation:
static bool _rbt_foreach_early_stop(om_rbt_node_t* node, void* data) {
int* count = (int*)data;
(*count)++;
/* 在访问到第2个节点后中止遍历 */
if (*count >= 2) {
return false;
}
return true;
}
START_TEST(_RBT_FOREACH) {END_TEST
START_TEST(_RBT_FOREACH_EARLY_STOP) {
RBT_ROOT(rbt);
om_rbt_node_t node_a = {.key = "A"};
om_rbt_node_t node_b = {.key = "B"};
om_rbt_node_t node_c = {.key = "C"};
int count = 0;
om_rbtree_insert(&rbt, &node_b);
om_rbtree_insert(&rbt, &node_a);
om_rbtree_insert(&rbt, &node_c);
om_rbtree_foreach(&rbt, _rbt_foreach_early_stop, &count);
ck_assert_msg(count == 2, "回调返回false后应只访问前2个节点,实际访问%d个", count);
}
END_TEST
Suite* make_om_base_suite(void) { tcase_add_test(tc_rbt, _RBT);
tcase_add_test(tc_rbt, _RBT_FOREACH);
tcase_add_test(tc_rbt, _RBT_FOREACH_EARLY_STOP);- This patch assumes
boolis already available (e.g., via#include <stdbool.h>in this file or a common header). If not, add that include near the top ofom_test_base.c. - The exact callback signature for
om_rbtree_foreachwas inferred from_rbt_foreach_once; if it differs (e.g., const qualifiers or different parameter order), adjust_rbt_foreach_early_stopto match it. - If your traversal guarantees a different visit order or if the tree may contain fewer than 3 nodes in some configurations, you may want to relax the assertion to
ck_assert_int_le(count, 2)and separately assertcount >= 1.
| END_TEST | ||
|
|
||
| START_TEST(_RBT_FOREACH) { | ||
| RBT_ROOT(rbt); |
There was a problem hiding this comment.
suggestion (testing): Consider adding a dedicated test for traversing an empty tree.
Since the fix now returns true when node == NULL, please add a test that calls om_rbtree_foreach on an empty RBT_ROOT and verifies that the callback is never invoked (e.g., a counter stays at 0). This will protect the node == NULL base case from regressions.
Suggested implementation:
START_TEST(_RBT_FOREACH) {
RBT_ROOT(rbt);
om_rbt_node_t node = {.key = "B"};
int count = 0;
om_rbtree_insert(&rbt, &node);
om_rbtree_foreach(&rbt, _rbt_foreach_once, &count);
ck_assert_msg(count == 1, "单节点遍历应执行1次回调,实际执行%d次", count);
}
END_TEST
START_TEST(_RBT_FOREACH_EMPTY) {
RBT_ROOT(rbt);
int count = 0;
om_rbtree_foreach(&rbt, _rbt_foreach_once, &count);
ck_assert_msg(count == 0, "空树遍历不应执行回调,实际执行%d次", count);
}
END_TESTTo actually run the new _RBT_FOREACH_EMPTY test, you will also need to register it in the suite definition (likely in make_om_base_suite or wherever other START_TEST cases are added with tcase_add_test). Add a corresponding tcase_add_test(tc, _RBT_FOREACH_EMPTY); next to the existing _RBT_FOREACH registration.
Summary by Sourcery
Fix red-black tree foreach traversal behavior and add a regression test for single-node iteration.
Bug Fixes:
Tests:
Fixes #8