/* * Depth-first tree search. * * Previously this test shared one TreeSearchParams across all three searches * without resetting it, so `steps` accumulated (7, then 14, then 21) and the * second assertion failed -- `tree` was a red test on every run. Each search * now gets its own params. * * Caveat worth knowing when reading the step counts below: the value is hidden * in tree[6], which is the last node visited in pre-, in- and post-order alike, * so "7 steps" holds for all three orders and does not actually distinguish * them -- nor does it prove that AKERR_ITERATOR_BREAK stopped anything (it does * not; see tests/test_tree_iterate_break.c and TODO.md 2.1.3). Visit-order * assertions that tell the three traversals apart are TODO.md section 1.8. */ #include "aksl_capture.h" #define MAX_LEAVES 7 #define HIDDEN_VALUE ((void *)17336) typedef struct TreeSearchParams { void *value; int steps; aksl_TreeNode *node; } TreeSearchParams; static akerr_ErrorContext AKERR_NOIGNORE *find_value(aksl_TreeNode *node, void *data) { TreeSearchParams *parms = NULL; PREPARE_ERROR(e); FAIL_ZERO_RETURN(e, node, AKERR_NULLPOINTER, "node"); FAIL_ZERO_RETURN(e, data, AKERR_NULLPOINTER, "data"); parms = (TreeSearchParams *)data; parms->steps += 1; if ( node->leaf == parms->value ) { parms->node = node; FAIL_RETURN(e, AKERR_ITERATOR_BREAK, "stop"); } SUCCEED_RETURN(e); } /* * Build the 3-level tree used by every case here, with the search value hidden * in the bottom-right leaf. * * LEFT RIGHT * TREE[0] * +--------^^---------+ * | | * TREE[1] TREE[2] * +---^^---+ +---^^---+ * | | | | *TREE[3] TREE[4] TREE[5] TREE[6] */ static void build_tree(aksl_TreeNode *tree, TreeSearchParams *parms) { memset((void *)tree, 0x00, sizeof(aksl_TreeNode) * MAX_LEAVES); tree[0].left = &tree[1]; tree[0].right = &tree[2]; tree[1].left = &tree[3]; tree[1].right = &tree[4]; tree[2].left = &tree[5]; tree[2].right = &tree[6]; tree[6].leaf = HIDDEN_VALUE; memset((void *)parms, 0x00, sizeof(TreeSearchParams)); parms->value = HIDDEN_VALUE; } static int search_finds_hidden_value(uint8_t searchmode) { aksl_TreeNode tree[MAX_LEAVES]; TreeSearchParams parms; build_tree(tree, &parms); AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &find_value, NULL, NULL, searchmode, &parms, NULL)); AKSL_CHECK(parms.node == &tree[6]); AKSL_CHECK(parms.steps == MAX_LEAVES); return 0; } static int test_dfs_preorder(void) { return search_finds_hidden_value(AKSL_TREE_SEARCH_DFS_PREORDER); } static int test_dfs_inorder(void) { return search_finds_hidden_value(AKSL_TREE_SEARCH_DFS_INORDER); } static int test_dfs_postorder(void) { return search_finds_hidden_value(AKSL_TREE_SEARCH_DFS_POSTORDER); } /* * Both breadth-first modes are declared in the header but not implemented, and * say so through AKERR_NOT_IMPLEMENTED rather than by silently visiting nothing. * TODO.md 2.2.10 tracks implementing them; until then this is the contract. */ static int bfs_reports_not_implemented(uint8_t searchmode) { aksl_TreeNode tree[MAX_LEAVES]; TreeSearchParams parms; build_tree(tree, &parms); AKSL_CHECK_STATUS_MSG_CONTAINS( aksl_tree_iterate(&tree[0], &find_value, NULL, NULL, searchmode, &parms, NULL), AKERR_NOT_IMPLEMENTED, "Searchmode"); AKSL_CHECK(parms.steps == 0); AKSL_CHECK(parms.node == NULL); return 0; } static int test_bfs_is_not_implemented(void) { return bfs_reports_not_implemented(AKSL_TREE_SEARCH_BFS); } static int test_bfs_right_is_not_implemented(void) { return bfs_reports_not_implemented(AKSL_TREE_SEARCH_BFS_RIGHT); } static int test_iterate_null_arguments(void) { aksl_TreeNode tree[MAX_LEAVES]; TreeSearchParams parms; build_tree(tree, &parms); AKSL_CHECK_STATUS_MSG_CONTAINS( aksl_tree_iterate(NULL, &find_value, NULL, NULL, AKSL_TREE_SEARCH_DFS_PREORDER, &parms, NULL), AKERR_NULLPOINTER, "root"); AKSL_CHECK_STATUS_MSG_CONTAINS( aksl_tree_iterate(&tree[0], NULL, NULL, NULL, AKSL_TREE_SEARCH_DFS_PREORDER, &parms, NULL), AKERR_NULLPOINTER, "iter"); return 0; } /* A callback error that is not AKERR_ITERATOR_BREAK reaches the caller. */ static int test_iterate_propagates_callback_error(void) { aksl_TreeNode tree[MAX_LEAVES]; TreeSearchParams parms; build_tree(tree, &parms); /* find_value raises AKERR_NULLPOINTER when it is handed no data. */ AKSL_CHECK_STATUS_MSG_CONTAINS( aksl_tree_iterate(&tree[0], &find_value, NULL, NULL, AKSL_TREE_SEARCH_DFS_PREORDER, NULL, NULL), AKERR_NULLPOINTER, "data"); return 0; } int main(void) { int failures = 0; akerr_init(); AKSL_RUN(failures, test_dfs_preorder); AKSL_RUN(failures, test_dfs_inorder); AKSL_RUN(failures, test_dfs_postorder); AKSL_RUN(failures, test_bfs_is_not_implemented); AKSL_RUN(failures, test_bfs_right_is_not_implemented); AKSL_RUN(failures, test_iterate_null_arguments); AKSL_RUN(failures, test_iterate_propagates_callback_error); AKSL_REPORT(failures); }