Files
libakstdlib/tests/test_tree.c
Tachikoma 55d986c631
All checks were successful
libakstdlib CI Build / cmake_build (push) Successful in 5m53s
libakstdlib CI Build / sanitizers (push) Successful in 2m55s
libakstdlib CI Build / coverage (push) Successful in 2m48s
libakstdlib CI Build / mutation_test (push) Successful in 12m39s
Drop the TODO.md section numbers from 26 files
Eighty-five comments cited section numbers -- 1.1, 2.2.6, 3.6 -- from a
numbering the file had already abandoned before the move to the tracker. They
label completed work, so the pointer was the only wrong part.

The citation is removed and the sentence kept, which is what issue #27
recommended: these are labels, not references, and a label carrying a
version-dependent pointer goes stale again at the next reorganisation. Where a
pointer earns its place it names what actually holds the content now --
UPGRADING.md for the confirmed defects, libakerror #15 for the target
namespacing, issue #7 for the mutation survivors.

README.md and akstdlib.h sent readers to TODO.md for 'what is still open';
they name the tracker.

Verified: cmake --build build && ctest --test-dir build, 19/19.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
2026-08-02 22:01:27 -04:00

639 lines
21 KiB
C

/*
* Tree traversal.
*
* The old version of this file counted steps, which cannot tell the three
* depth-first orders apart because all three visit all seven nodes -- and could
* not prove that AKERR_ITERATOR_BREAK stopped anything either, because it hid
* its target in tree[6], the last node visited in every depth-first order. Every
* traversal here records the node pointers it was handed, in order, and compares
* against the expected sequence.
*
* The 7-node tree used throughout:
*
* TREE[0]
* +--------^^---------+
* | |
* TREE[1] TREE[2]
* +---^^---+ +---^^---+
* | | | |
* TREE[3] TREE[4] TREE[5] TREE[6]
*
* pre-order 0 1 3 4 2 5 6
* in-order 3 1 4 0 5 2 6
* post-order 3 4 1 5 6 2 0
* BFS 0 1 2 3 4 5 6
* BFS_RIGHT 0 2 1 6 5 4 3
*/
#include "aksl_capture.h"
#define MAX_LEAVES 7
#define MAX_VISITS 32
typedef struct VisitLog
{
int count;
aksl_TreeNode *seen[MAX_VISITS];
aksl_TreeNode *break_at; /* node to raise ITERATOR_BREAK on, or NULL */
aksl_TreeNode *fail_at; /* node to raise AKERR_VALUE on, or NULL */
} VisitLog;
static void visitlog_init(VisitLog *log)
{
memset((void *)log, 0x00, sizeof(VisitLog));
}
static akerr_ErrorContext AKERR_NOIGNORE *record_visit(aksl_TreeNode *node, void *data)
{
VisitLog *log = NULL;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, node, AKERR_NULLPOINTER, "node");
FAIL_ZERO_RETURN(e, data, AKERR_NULLPOINTER, "data");
log = (VisitLog *)data;
if ( log->count < MAX_VISITS ) {
log->seen[log->count] = node;
}
log->count += 1;
if ( log->fail_at == node ) {
FAIL_RETURN(e, AKERR_VALUE, "iterator failed at node %p", (void *)node);
}
if ( log->break_at == node ) {
FAIL_RETURN(e, AKERR_ITERATOR_BREAK, "stop at node %p", (void *)node);
}
SUCCEED_RETURN(e);
}
static void build_tree(aksl_TreeNode *tree)
{
int i = 0;
for ( i = 0; i < MAX_LEAVES; i++ ) {
memset((void *)&tree[i], 0x00, sizeof(aksl_TreeNode));
}
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];
}
/* Assert that the visit log matches `expected`, which is a list of tree indices. */
static int check_order(VisitLog *log, aksl_TreeNode *tree,
const int *expected, int n)
{
int i = 0;
if ( log->count != n ) {
fprintf(stderr, " CHECK FAILED: visited %d nodes, expected %d\n",
log->count, n);
return 1;
}
for ( i = 0; i < n; i++ ) {
if ( log->seen[i] != &tree[expected[i]] ) {
fprintf(stderr, " CHECK FAILED: visit %d was node %ld, expected %d\n",
i, (long)(log->seen[i] - tree), expected[i]);
return 1;
}
}
return 0;
}
/* ---------------------------------------------------------------------- */
/* aksl_tree_node_init */
/* ---------------------------------------------------------------------- */
static int test_node_init_zeroes_the_links(void)
{
aksl_TreeNode node;
int payload = 3;
memset((void *)&node, 0xff, sizeof(node));
AKSL_CHECK_OK(aksl_tree_node_init(&node, &payload));
AKSL_CHECK(node.parent == NULL);
AKSL_CHECK(node.left == NULL);
AKSL_CHECK(node.right == NULL);
AKSL_CHECK(node.leaf == (void *)&payload);
AKSL_CHECK_STATUS(aksl_tree_node_init(NULL, NULL), AKERR_NULLPOINTER);
return 0;
}
/* ---------------------------------------------------------------------- */
/* Traversal orders */
/* ---------------------------------------------------------------------- */
static int test_preorder_visits_root_left_right(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
static const int expected[MAX_LEAVES] = { 0, 1, 3, 4, 2, 5, 6 };
build_tree(tree);
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_PREORDER, &log));
AKSL_CHECK(check_order(&log, tree, expected, MAX_LEAVES) == 0);
return 0;
}
static int test_inorder_visits_left_root_right(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
static const int expected[MAX_LEAVES] = { 3, 1, 4, 0, 5, 2, 6 };
build_tree(tree);
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_INORDER, &log));
AKSL_CHECK(check_order(&log, tree, expected, MAX_LEAVES) == 0);
return 0;
}
static int test_postorder_visits_left_right_root(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
static const int expected[MAX_LEAVES] = { 3, 4, 1, 5, 6, 2, 0 };
build_tree(tree);
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_POSTORDER, &log));
AKSL_CHECK(check_order(&log, tree, expected, MAX_LEAVES) == 0);
return 0;
}
/* AKSL_TREE_SEARCH_DFS is documented as an alias for the pre-order mode. */
static int test_dfs_is_an_alias_for_preorder(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
static const int expected[MAX_LEAVES] = { 0, 1, 3, 4, 2, 5, 6 };
build_tree(tree);
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS, &log));
AKSL_CHECK(check_order(&log, tree, expected, MAX_LEAVES) == 0);
return 0;
}
/*
* BFS was AKERR_NOT_IMPLEMENTED, and the lalloc/lfree parameters that existed to
* serve it were defaulted and then never called. See UPGRADING.md.
* Both modes work now, and the allocator test below proves the queue is real.
*/
static int test_bfs_visits_level_by_level(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
static const int expected[MAX_LEAVES] = { 0, 1, 2, 3, 4, 5, 6 };
build_tree(tree);
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_BFS, &log));
AKSL_CHECK(check_order(&log, tree, expected, MAX_LEAVES) == 0);
return 0;
}
static int test_bfs_right_visits_right_child_first(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
static const int expected[MAX_LEAVES] = { 0, 2, 1, 6, 5, 4, 3 };
build_tree(tree);
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_BFS_RIGHT, &log));
AKSL_CHECK(check_order(&log, tree, expected, MAX_LEAVES) == 0);
return 0;
}
/* AKSL_TREE_SEARCH_VISIT: this node and no further. */
static int test_visit_mode_does_not_descend(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
static const int expected[1] = { 0 };
build_tree(tree);
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_VISIT, &log));
AKSL_CHECK(check_order(&log, tree, expected, 1) == 0);
return 0;
}
/* ---------------------------------------------------------------------- */
/* Custom allocator */
/* ---------------------------------------------------------------------- */
static int counting_alloc_calls = 0;
static int counting_free_calls = 0;
static akerr_ErrorContext AKERR_NOIGNORE *counting_alloc(size_t size, void **dest)
{
counting_alloc_calls += 1;
return aksl_malloc(size, dest);
}
static akerr_ErrorContext AKERR_NOIGNORE *counting_free(void *ptr)
{
counting_free_calls += 1;
return aksl_free(ptr);
}
/*
* The wrapper plan asked that "custom lalloc/lfree are actually invoked -- currently they are
* stored and never called". They are called now, once per node enqueued, and
* every allocation is released. The depth-first modes allocate nothing at all,
* which is the other half of the contract.
*/
static int test_custom_allocator_is_used_and_balanced(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
build_tree(tree);
visitlog_init(&log);
counting_alloc_calls = 0;
counting_free_calls = 0;
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit,
&counting_alloc, &counting_free,
AKSL_TREE_SEARCH_BFS, &log));
AKSL_CHECK(log.count == MAX_LEAVES);
/* One queue entry per node visited, and every one of them released. */
AKSL_CHECK(counting_alloc_calls == MAX_LEAVES);
AKSL_CHECK(counting_free_calls == MAX_LEAVES);
/* Depth-first needs no queue, so it must not touch the allocator. */
counting_alloc_calls = 0;
counting_free_calls = 0;
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit,
&counting_alloc, &counting_free,
AKSL_TREE_SEARCH_DFS_PREORDER, &log));
AKSL_CHECK(counting_alloc_calls == 0);
AKSL_CHECK(counting_free_calls == 0);
return 0;
}
/* A break part-way through a BFS still drains the queue it had already built. */
static int test_break_during_bfs_releases_the_queue(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
build_tree(tree);
visitlog_init(&log);
log.break_at = &tree[1];
counting_alloc_calls = 0;
counting_free_calls = 0;
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit,
&counting_alloc, &counting_free,
AKSL_TREE_SEARCH_BFS, &log));
/* 0 and 1 visited; the walk stops at 1 with 2 still sitting in the queue. */
AKSL_CHECK(log.count == 2);
AKSL_CHECK(counting_alloc_calls > 0);
AKSL_CHECK(counting_alloc_calls == counting_free_calls);
return 0;
}
/* ---------------------------------------------------------------------- */
/* Degenerate shapes */
/* ---------------------------------------------------------------------- */
static int test_single_node_tree_visits_once_in_every_order(void)
{
aksl_TreeNode node;
VisitLog log;
static const uint8_t modes[] = {
AKSL_TREE_SEARCH_DFS_PREORDER,
AKSL_TREE_SEARCH_DFS_INORDER,
AKSL_TREE_SEARCH_DFS_POSTORDER,
AKSL_TREE_SEARCH_BFS,
AKSL_TREE_SEARCH_BFS_RIGHT,
AKSL_TREE_SEARCH_VISIT,
};
size_t i = 0;
for ( i = 0; i < sizeof(modes) / sizeof(modes[0]); i++ ) {
AKSL_CHECK_OK(aksl_tree_node_init(&node, NULL));
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&node, &record_visit, NULL, NULL,
modes[i], &log));
AKSL_CHECK(log.count == 1);
AKSL_CHECK(log.seen[0] == &node);
}
return 0;
}
/* Left-only and right-only chains of three nodes, no branching anywhere. */
static int test_degenerate_chains(void)
{
aksl_TreeNode chain[3];
VisitLog log;
static const int forward[3] = { 0, 1, 2 };
static const int backward[3] = { 2, 1, 0 };
int i = 0;
/* Left-only: 0 -> 1 -> 2 */
for ( i = 0; i < 3; i++ ) {
AKSL_CHECK_OK(aksl_tree_node_init(&chain[i], NULL));
}
chain[0].left = &chain[1];
chain[1].left = &chain[2];
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&chain[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_PREORDER, &log));
AKSL_CHECK(check_order(&log, chain, forward, 3) == 0);
/* In-order down a left chain arrives at the deepest node first. */
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&chain[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_INORDER, &log));
AKSL_CHECK(check_order(&log, chain, backward, 3) == 0);
/* Right-only: the same shape, mirrored. */
for ( i = 0; i < 3; i++ ) {
AKSL_CHECK_OK(aksl_tree_node_init(&chain[i], NULL));
}
chain[0].right = &chain[1];
chain[1].right = &chain[2];
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&chain[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_INORDER, &log));
AKSL_CHECK(check_order(&log, chain, forward, 3) == 0);
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&chain[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_BFS, &log));
AKSL_CHECK(check_order(&log, chain, forward, 3) == 0);
return 0;
}
/*
* A chain deeper than the recursion can take. It used to
* overflow the stack; it is AKERR_OUTOFBOUNDS now, and the message names the
* documented limit. Built one node past the cap so the failure is the cap itself
* and not some incidental shortfall. `static` because AKSL_TREE_MAX_DEPTH nodes
* on the stack of a test function is not the point of the test.
*/
static int test_tree_deeper_than_the_cap_is_out_of_bounds(void)
{
static aksl_TreeNode chain[AKSL_TREE_MAX_DEPTH + 2];
VisitLog log;
int i = 0;
for ( i = 0; i < AKSL_TREE_MAX_DEPTH + 2; i++ ) {
AKSL_CHECK_OK(aksl_tree_node_init(&chain[i], NULL));
}
for ( i = 0; i < AKSL_TREE_MAX_DEPTH + 1; i++ ) {
chain[i].left = &chain[i + 1];
}
visitlog_init(&log);
AKSL_CHECK_STATUS_MSG_CONTAINS(
aksl_tree_iterate(&chain[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_PREORDER, &log),
AKERR_OUTOFBOUNDS, "AKSL_TREE_MAX_DEPTH");
/* Exactly at the cap is fine -- a limit, not an off-by-one. */
chain[AKSL_TREE_MAX_DEPTH - 1].left = NULL;
visitlog_init(&log);
AKSL_CHECK_OK(aksl_tree_iterate(&chain[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_PREORDER, &log));
AKSL_CHECK(log.count == AKSL_TREE_MAX_DEPTH);
/*
* And again leaning right. The recursion counts depth separately for each
* child, so a chain that only ever goes left says nothing about whether the
* right-hand descent counts at all -- mutation testing found exactly that:
* changing the right child's `depth + 1` to `depth + 0` survived the whole
* suite, because nothing here had ever recursed right more than three deep.
*/
for ( i = 0; i < AKSL_TREE_MAX_DEPTH + 2; i++ ) {
AKSL_CHECK_OK(aksl_tree_node_init(&chain[i], NULL));
}
for ( i = 0; i < AKSL_TREE_MAX_DEPTH + 1; i++ ) {
chain[i].right = &chain[i + 1];
}
visitlog_init(&log);
AKSL_CHECK_STATUS(aksl_tree_iterate(&chain[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_PREORDER, &log),
AKERR_OUTOFBOUNDS);
/* Both orders that descend right, so in-order and post-order count too. */
visitlog_init(&log);
AKSL_CHECK_STATUS(aksl_tree_iterate(&chain[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_INORDER, &log),
AKERR_OUTOFBOUNDS);
visitlog_init(&log);
AKSL_CHECK_STATUS(aksl_tree_iterate(&chain[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_POSTORDER, &log),
AKERR_OUTOFBOUNDS);
/* And breadth-first, whose depth is carried on the queue entry instead. */
visitlog_init(&log);
AKSL_CHECK_STATUS(aksl_tree_iterate(&chain[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_BFS, &log),
AKERR_OUTOFBOUNDS);
return 0;
}
/*
* A child pointing back at an ancestor. This used to recurse until the process
* died. The depth-first walk carries the ancestor chain and recognises the node
* as its own forebear; the breadth-first walk has no ancestor chain to compare
* against, so the same tree comes back as AKERR_OUTOFBOUNDS through the depth
* cap instead -- a different status for the same shape, which the header says
* out loud rather than leaving to be discovered.
*/
static int test_cyclic_tree_is_caught(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
build_tree(tree);
tree[3].left = &tree[0]; /* back to the root */
visitlog_init(&log);
AKSL_CHECK_STATUS_MSG_CONTAINS(
aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_PREORDER, &log),
AKERR_CIRCULAR_REFERENCE, "own ancestor");
visitlog_init(&log);
AKSL_CHECK_STATUS(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_BFS, &log),
AKERR_OUTOFBOUNDS);
return 0;
}
/* ---------------------------------------------------------------------- */
/* Callback control flow */
/* ---------------------------------------------------------------------- */
/*
* The defect that made ITERATOR_BREAK useless on a tree: the recursive frame
* that raised the break handled it in its own PROCESS/HANDLE block and returned
* success, so the parent frame's PASS saw nothing wrong and carried straight on
* into the sibling subtree. All seven nodes were visited no matter where the
* break was raised.
*
* One case per order, each breaking on a node that is *not* last in that order --
* which is precisely what the old test could not do, because it hid its target
* in tree[6], the final node in all three depth-first walks.
*/
static int test_break_aborts_the_whole_traversal(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
build_tree(tree);
/* Pre-order 0 1 3 ... : breaking at 3 is the third visit. */
visitlog_init(&log);
log.break_at = &tree[3];
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_PREORDER, &log));
AKSL_CHECK(log.count == 3);
/* In-order 3 1 4 ... : breaking at 4 is the third visit. */
visitlog_init(&log);
log.break_at = &tree[4];
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_INORDER, &log));
AKSL_CHECK(log.count == 3);
/* Post-order 3 4 1 5 ... : breaking at 5 is the fourth visit. */
visitlog_init(&log);
log.break_at = &tree[5];
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_POSTORDER, &log));
AKSL_CHECK(log.count == 4);
/* BFS 0 1 2 3 ... : breaking at 2 is the third visit. */
visitlog_init(&log);
log.break_at = &tree[2];
AKSL_CHECK_OK(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_BFS, &log));
AKSL_CHECK(log.count == 3);
return 0;
}
/* Any other status propagates out with its message and its own status intact. */
static int test_callback_error_propagates(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
build_tree(tree);
visitlog_init(&log);
log.fail_at = &tree[3];
AKSL_CHECK_STATUS_MSG_CONTAINS(
aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_PREORDER, &log),
AKERR_VALUE, "iterator failed at node");
AKSL_CHECK(log.count == 3);
/* And out of a BFS, where the queue has to be drained on the way past. */
visitlog_init(&log);
log.fail_at = &tree[2];
AKSL_CHECK_STATUS(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_BFS, &log),
AKERR_VALUE);
return 0;
}
/* ---------------------------------------------------------------------- */
/* Argument validation */
/* ---------------------------------------------------------------------- */
static int test_null_arguments(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
build_tree(tree);
visitlog_init(&log);
AKSL_CHECK_STATUS(aksl_tree_iterate(NULL, &record_visit, NULL, NULL,
AKSL_TREE_SEARCH_DFS_PREORDER, &log),
AKERR_NULLPOINTER);
AKSL_CHECK_STATUS(aksl_tree_iterate(&tree[0], NULL, NULL, NULL,
AKSL_TREE_SEARCH_DFS_PREORDER, &log),
AKERR_NULLPOINTER);
AKSL_CHECK(log.count == 0);
return 0;
}
/*
* the switch had no default, so an unrecognised mode -- and
* AKSL_TREE_SEARCH_VISIT, which the header documented but nothing implemented --
* fell straight through to SUCCEED_RETURN having visited nothing at all. A
* traversal that silently did not happen, reported as success.
*/
static int test_unknown_searchmode_is_a_value_error(void)
{
aksl_TreeNode tree[MAX_LEAVES];
VisitLog log;
build_tree(tree);
visitlog_init(&log);
AKSL_CHECK_STATUS_MSG_CONTAINS(
aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL, 99, &log),
AKERR_VALUE, "unknown searchmode");
AKSL_CHECK(log.count == 0);
/* 6 is one past the last defined mode, and just as unacceptable. */
AKSL_CHECK_STATUS(aksl_tree_iterate(&tree[0], &record_visit, NULL, NULL, 6, &log),
AKERR_VALUE);
AKSL_CHECK(log.count == 0);
return 0;
}
int main(void)
{
int failures = 0;
akerr_init();
AKSL_RUN(failures, test_node_init_zeroes_the_links);
AKSL_RUN(failures, test_preorder_visits_root_left_right);
AKSL_RUN(failures, test_inorder_visits_left_root_right);
AKSL_RUN(failures, test_postorder_visits_left_right_root);
AKSL_RUN(failures, test_dfs_is_an_alias_for_preorder);
AKSL_RUN(failures, test_bfs_visits_level_by_level);
AKSL_RUN(failures, test_bfs_right_visits_right_child_first);
AKSL_RUN(failures, test_visit_mode_does_not_descend);
AKSL_RUN(failures, test_custom_allocator_is_used_and_balanced);
AKSL_RUN(failures, test_break_during_bfs_releases_the_queue);
AKSL_RUN(failures, test_single_node_tree_visits_once_in_every_order);
AKSL_RUN(failures, test_degenerate_chains);
AKSL_RUN(failures, test_tree_deeper_than_the_cap_is_out_of_bounds);
AKSL_RUN(failures, test_cyclic_tree_is_caught);
AKSL_RUN(failures, test_break_aborts_the_whole_traversal);
AKSL_RUN(failures, test_callback_error_propagates);
AKSL_RUN(failures, test_null_arguments);
AKSL_RUN(failures, test_unknown_searchmode_is_a_value_error);
AKSL_REPORT(failures);
}