/* * Copyright (c) 2021, Tim Schumacher * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #define NODE(node) static_cast(node) #define ROOTP(root) reinterpret_cast(root) #define COMP(func) reinterpret_cast(func) #define U8(value) static_cast(value) struct twalk_test_entry { void const* node; VISIT order; int depth; }; #define TWALK_SET_DATA (-2) #define TWALK_CHECK_END (-3) #define TWALK_END_MARKER (-4) TEST_CASE(tsearch) { struct search_tree_node* root = nullptr; void* ret; char const* key; char* search; // Try a nullptr rootp. ret = tsearch("buggie", nullptr, COMP(strcmp)); EXPECT_EQ(ret, nullptr); // Try creating a new tree. key = "5"; ret = tsearch(key, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root); EXPECT_EQ(NODE(ret)->key, key); // Insert an element on the left side. key = "3"; ret = tsearch(key, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->left); EXPECT_EQ(NODE(ret)->key, key); // Insert an element on the right side. key = "7"; ret = tsearch(key, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->right); EXPECT_EQ(NODE(ret)->key, key); // Add another layer for testing. ret = tsearch("2", ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->left->left); ret = tsearch("4", ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->left->right); ret = tsearch("6", ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->right->left); ret = tsearch("8", ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->right->right); // Find the root element. // strdup ensures that we are using the comparator. search = strdup("5"); ret = tsearch(search, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root); free(search); // Find the lowest-level elements. search = strdup("2"); ret = tsearch(search, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->left->left); free(search); search = strdup("4"); ret = tsearch(search, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->left->right); free(search); search = strdup("6"); ret = tsearch(search, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->right->left); free(search); search = strdup("8"); ret = tsearch(search, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->right->right); free(search); delete_node_recursive(root); } TEST_CASE(tfind) { struct search_tree_node* root = nullptr; void* ret; char* search; // Try a nullptr rootp. ret = tfind("buggie", nullptr, COMP(strcmp)); EXPECT_EQ(ret, nullptr); // Search for something that doesn't exist. ret = tfind("buggie", ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, nullptr); // Construct a tree for testing. root = new_tree_node("5"); root->left = new_tree_node("3"); root->right = new_tree_node("7"); root->left->left = new_tree_node("2"); root->left->right = new_tree_node("4"); root->right->left = new_tree_node("6"); root->right->right = new_tree_node("8"); // Find the root element. // strdup ensures that we are using the comparator. search = strdup("5"); ret = tfind(search, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root); free(search); // Find the lowest-level elements. search = strdup("2"); ret = tfind(search, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->left->left); free(search); search = strdup("4"); ret = tfind(search, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->left->right); free(search); search = strdup("6"); ret = tfind(search, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->right->left); free(search); search = strdup("8"); ret = tfind(search, ROOTP(&root), COMP(strcmp)); EXPECT_EQ(ret, root->right->right); free(search); delete_node_recursive(root); } void twalk_action(void const* node, VISIT order, int depth); void twalk_action(void const* node, VISIT order, int depth) { static int count = 0; static const struct twalk_test_entry* tests = nullptr; // Special case: Set test data. if (depth == TWALK_SET_DATA) { count = 0; tests = static_cast(node); return; } // Special case: End signaled by tester. if (depth == TWALK_CHECK_END) { if (tests[count].depth != TWALK_END_MARKER) { FAIL(DeprecatedString::formatted("Expected action (node={:#x}, order={}, depth={}), but twalk ended early.", tests[count].node, U8(tests[count].order), tests[count].depth)); } return; } // Special case: End marker reached. if (tests[count].depth == TWALK_END_MARKER) { FAIL(DeprecatedString::formatted("Expected end, but twalk sent another action (node={:#x}, order={}, depth={}).", node, U8(order), depth)); return; } EXPECT_EQ(node, tests[count].node); EXPECT_EQ(U8(order), U8(tests[count].order)); EXPECT_EQ(depth, tests[count].depth); count++; } TEST_CASE(twalk) { struct search_tree_node* root = nullptr; // Try an empty tree. struct twalk_test_entry tests1[] = { { nullptr, leaf, TWALK_END_MARKER }, }; twalk_action(tests1, leaf, TWALK_SET_DATA); twalk(nullptr, twalk_action); twalk_action(nullptr, leaf, TWALK_CHECK_END); // Try a single node. root = new_tree_node("5"); struct twalk_test_entry tests2[] = { { root, leaf, 0 }, { nullptr, leaf, TWALK_END_MARKER }, }; twalk_action(tests2, leaf, TWALK_SET_DATA); twalk(root, twalk_action); twalk_action(nullptr, leaf, TWALK_CHECK_END); // Try two layers of nodes. root->left = new_tree_node("3"); root->right = new_tree_node("7"); struct twalk_test_entry tests3[] = { { root, preorder, 0 }, { root->left, leaf, 1 }, { root, postorder, 0 }, { root->right, leaf, 1 }, { root, endorder, 0 }, { nullptr, leaf, TWALK_END_MARKER }, }; twalk_action(tests3, leaf, TWALK_SET_DATA); twalk(root, twalk_action); twalk_action(nullptr, leaf, TWALK_CHECK_END); // Try three layers of nodes. root->left->left = new_tree_node("2"); root->left->right = new_tree_node("4"); root->right->left = new_tree_node("6"); root->right->right = new_tree_node("8"); struct twalk_test_entry tests4[] = { { root, preorder, 0 }, { root->left, preorder, 1 }, { root->left->left, leaf, 2 }, { root->left, postorder, 1 }, { root->left->right, leaf, 2 }, { root->left, endorder, 1 }, { root, postorder, 0 }, { root->right, preorder, 1 }, { root->right->left, leaf, 2 }, { root->right, postorder, 1 }, { root->right->right, leaf, 2 }, { root->right, endorder, 1 }, { root, endorder, 0 }, { nullptr, leaf, TWALK_END_MARKER }, }; twalk_action(tests4, leaf, TWALK_SET_DATA); twalk(root, twalk_action); twalk_action(nullptr, leaf, TWALK_CHECK_END); delete_node_recursive(root); }