summaryrefslogtreecommitdiffstats
path: root/test/btree_test.c
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2022-08-18 13:18:52 -0400
committerDavid Robillard <d@drobilla.net>2022-08-18 14:33:37 -0400
commitf65912600fc301cbdbb613f1df0dc29820f1da35 (patch)
tree43db69627433fbb8f5acd13c6cf0ef8a3c3b829d /test/btree_test.c
parentc4b8ca3dc222b06c40ebcb416d653e17e88de858 (diff)
downloadzix-f65912600fc301cbdbb613f1df0dc29820f1da35.tar.gz
zix-f65912600fc301cbdbb613f1df0dc29820f1da35.tar.bz2
zix-f65912600fc301cbdbb613f1df0dc29820f1da35.zip
Use conventional test executable names
Diffstat (limited to 'test/btree_test.c')
-rw-r--r--test/btree_test.c642
1 files changed, 0 insertions, 642 deletions
diff --git a/test/btree_test.c b/test/btree_test.c
deleted file mode 100644
index 878eef9..0000000
--- a/test/btree_test.c
+++ /dev/null
@@ -1,642 +0,0 @@
-// Copyright 2011-2021 David Robillard <d@drobilla.net>
-// SPDX-License-Identifier: ISC
-
-#undef NDEBUG
-
-#include "zix/btree.h"
-
-#include "failing_allocator.h"
-#include "test_data.h"
-
-#include "zix/common.h"
-
-#include <assert.h>
-#include <inttypes.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-static bool expect_failure = false;
-
-ZIX_PURE_FUNC
-static int
-int_cmp(const void* a, const void* b, const void* ZIX_UNUSED(user_data))
-{
- const uintptr_t ia = (uintptr_t)a;
- const uintptr_t ib = (uintptr_t)b;
-
- assert(ia != 0U); // No wildcards
- assert(ib != 0U); // No wildcards
-
- return ia < ib ? -1 : ia > ib ? 1 : 0;
-}
-
-static uintptr_t
-ith_elem(const unsigned test_num, const size_t n_elems, const size_t i)
-{
- switch (test_num % 3) {
- case 0:
- return i + 1; // Increasing
- case 1:
- return n_elems - i; // Decreasing
- default:
- return 1U + unique_rand(i); // Pseudo-random
- }
-}
-
-typedef struct {
- unsigned test_num;
- size_t n_elems;
-} TestContext;
-
-static uintptr_t
-wildcard_cut(unsigned test_num, size_t n_elems)
-{
- return ith_elem(test_num, n_elems, n_elems / 3);
-}
-
-/// Wildcard comparator where 0 matches anything >= wildcard_cut(n_elems)
-static int
-wildcard_cmp(const void* a, const void* b, const void* user_data)
-{
- const TestContext* ctx = (const TestContext*)user_data;
- const size_t n_elems = ctx->n_elems;
- const unsigned test_num = ctx->test_num;
- const uintptr_t ia = (uintptr_t)a;
- const uintptr_t ib = (uintptr_t)b;
-
- if (ia == 0) {
- if (ib >= wildcard_cut(test_num, n_elems)) {
- return 0; // Wildcard match
- }
-
- return 1; // Wildcard a > b
- }
-
- if (ib == 0) {
- if (ia >= wildcard_cut(test_num, n_elems)) {
- return 0; // Wildcard match
- }
-
- return -1; // Wildcard b > a
- }
-
- return int_cmp(a, b, user_data);
-}
-
-ZIX_LOG_FUNC(2, 3)
-static int
-test_fail(ZixBTree* t, const char* fmt, ...)
-{
- zix_btree_free(t, NULL, NULL);
- if (expect_failure) {
- return EXIT_SUCCESS;
- }
-
- va_list args;
- va_start(args, fmt);
- fprintf(stderr, "error: ");
- vfprintf(stderr, fmt, args);
- va_end(args);
- return EXIT_FAILURE;
-}
-
-static const size_t n_clear_insertions = 1024U;
-
-static void
-destroy(void* const ptr, const void* const user_data)
-{
- (void)user_data;
- assert(ptr);
- assert((uintptr_t)ptr <= n_clear_insertions);
-}
-
-static void
-no_destroy(void* const ptr, const void* const user_data)
-{
- (void)user_data;
- assert(!ptr);
-}
-
-static void
-test_clear(void)
-{
- ZixBTree* t = zix_btree_new(NULL, int_cmp, NULL);
-
- for (uintptr_t r = 0U; r < n_clear_insertions; ++r) {
- assert(!zix_btree_insert(t, (void*)(r + 1U)));
- }
-
- zix_btree_clear(t, destroy, NULL);
- assert(zix_btree_size(t) == 0);
-
- zix_btree_free(t, no_destroy, NULL);
-}
-
-static void
-test_free(void)
-{
- ZixBTree* t = zix_btree_new(NULL, int_cmp, NULL);
-
- for (uintptr_t r = 0U; r < n_clear_insertions; ++r) {
- assert(!zix_btree_insert(t, (void*)(r + 1U)));
- }
-
- assert(zix_btree_size(t) == n_clear_insertions);
-
- zix_btree_free(t, destroy, NULL);
-}
-
-static void
-test_iter_comparison(void)
-{
- static const size_t n_elems = 4096U;
-
- ZixBTree* const t = zix_btree_new(NULL, int_cmp, NULL);
-
- // Store increasing numbers from 1 (jammed into the pointers themselves)
- for (uintptr_t r = 1U; r < n_elems; ++r) {
- assert(!zix_btree_insert(t, (void*)r));
- }
-
- // Check that begin and end work sensibly
- const ZixBTreeIter begin = zix_btree_begin(t);
- const ZixBTreeIter end = zix_btree_end(t);
- assert(!zix_btree_iter_is_end(begin));
- assert(zix_btree_iter_is_end(end));
- assert(!zix_btree_iter_equals(begin, end));
- assert(!zix_btree_iter_equals(end, begin));
-
- // Make another begin iterator
- ZixBTreeIter j = zix_btree_begin(t);
- assert(zix_btree_iter_equals(begin, j));
-
- // Advance it and check that they are no longer equal
- for (size_t r = 1U; r < n_elems - 1U; ++r) {
- j = zix_btree_iter_next(j);
- assert(!zix_btree_iter_is_end(j));
- assert(!zix_btree_iter_equals(begin, j));
- assert(!zix_btree_iter_equals(end, j));
- assert(!zix_btree_iter_equals(j, end));
- }
-
- // Advance it to the end
- zix_btree_iter_increment(&j);
- assert(zix_btree_iter_is_end(j));
- assert(!zix_btree_iter_equals(begin, j));
- assert(zix_btree_iter_equals(end, j));
- assert(zix_btree_iter_equals(j, end));
-
- zix_btree_free(t, NULL, NULL);
-}
-
-static void
-test_insert_split_value(void)
-{
- static const size_t n_insertions = 767U; // Number of insertions to split
- static const uintptr_t split_value = 512U; // Value that will be pulled up
-
- ZixBTree* const t = zix_btree_new(NULL, int_cmp, NULL);
-
- // Insert right up until it would cause a split
- for (uintptr_t r = 1U; r < n_insertions; ++r) {
- assert(!zix_btree_insert(t, (void*)r));
- }
-
- // Insert the element that will be chosen as the split pivot
- assert(zix_btree_insert(t, (void*)split_value) == ZIX_STATUS_EXISTS);
-
- zix_btree_free(t, NULL, NULL);
-}
-
-static void
-test_remove_cases(void)
-{
- /* Insert and remove in several "phases" with different strides that are not
- even multiples. This spreads the load around to hit as many cases as
- possible. */
-
- static const uintptr_t s1 = 2U;
- static const uintptr_t s2 = 255U;
- const size_t n_insertions = s1 * s2 * 1000U;
-
- ZixBTree* const t = zix_btree_new(NULL, int_cmp, NULL);
-
- // Insert in s1-sized chunks
- for (uintptr_t phase = 0U; phase < s1; ++phase) {
- for (uintptr_t r = 0U; r < n_insertions / s1; ++r) {
- const uintptr_t value = (s1 * r) + phase + 1U;
-
- assert(!zix_btree_insert(t, (void*)value));
- }
- }
-
- // Remove in s2-sized chunks
- ZixBTreeIter next = zix_btree_end(t);
- for (uintptr_t phase = 0U; phase < s2; ++phase) {
- for (uintptr_t r = 0U; r < n_insertions / s2; ++r) {
- const uintptr_t value = (s2 * r) + phase + 1U;
- void* out = NULL;
-
- assert(!zix_btree_remove(t, (void*)value, &out, &next));
- assert((uintptr_t)out == value);
- }
- }
-
- assert(!zix_btree_size(t));
- zix_btree_free(t, NULL, NULL);
-}
-
-static int
-stress(ZixAllocator* const allocator,
- const unsigned test_num,
- const size_t n_elems)
-{
- if (n_elems == 0) {
- return 0;
- }
-
- uintptr_t r = 0;
- ZixBTree* t = zix_btree_new(allocator, int_cmp, NULL);
- ZixStatus st = ZIX_STATUS_SUCCESS;
-
- if (!t) {
- return test_fail(t, "Failed to allocate tree\n");
- }
-
- // Ensure begin iterator is end on empty tree
- ZixBTreeIter ti = zix_btree_begin(t);
- ZixBTreeIter end = zix_btree_end(t);
-
- if (!zix_btree_iter_is_end(ti)) {
- return test_fail(t, "Begin iterator on empty tree is not end\n");
- }
-
- if (!zix_btree_iter_equals(ti, end)) {
- return test_fail(t, "Begin and end of empty tree are not equal\n");
- }
-
- // Insert n_elems elements
- for (size_t i = 0; i < n_elems; ++i) {
- r = ith_elem(test_num, n_elems, i);
- if (!zix_btree_find(t, (void*)r, &ti)) {
- return test_fail(t, "%" PRIuPTR " already in tree\n", (uintptr_t)r);
- }
-
- if ((st = zix_btree_insert(t, (void*)r))) {
- return test_fail(
- t, "Insert %" PRIuPTR " failed (%s)\n", (uintptr_t)r, zix_strerror(st));
- }
- }
-
- // Ensure tree size is correct
- if (zix_btree_size(t) != n_elems) {
- return test_fail(t,
- "Tree size %" PRIuPTR " != %" PRIuPTR "\n",
- zix_btree_size(t),
- n_elems);
- }
-
- // Ensure begin no longer equals end
- ti = zix_btree_begin(t);
- end = zix_btree_end(t);
- if (zix_btree_iter_equals(ti, end)) {
- return test_fail(t, "Begin and end of non-empty tree are equal\n");
- }
-
- // Search for all elements
- for (size_t i = 0; i < n_elems; ++i) {
- r = ith_elem(test_num, n_elems, i);
- if (zix_btree_find(t, (void*)r, &ti)) {
- return test_fail(
- t, "Find %" PRIuPTR " @ %" PRIuPTR " failed\n", (uintptr_t)r, i);
- }
-
- if ((uintptr_t)zix_btree_get(ti) != r) {
- return test_fail(t,
- "Search data corrupt (%" PRIuPTR " != %" PRIuPTR ")\n",
- (uintptr_t)zix_btree_get(ti),
- r);
- }
- }
-
- // Find the lower bound of all elements and ensure it's exact
- for (size_t i = 0; i < n_elems; ++i) {
- r = ith_elem(test_num, n_elems, i);
- if (zix_btree_lower_bound(t, int_cmp, NULL, (void*)r, &ti)) {
- return test_fail(
- t, "Lower bound %" PRIuPTR " @ %" PRIuPTR " failed\n", (uintptr_t)r, i);
- }
-
- if (zix_btree_iter_is_end(ti)) {
- return test_fail(t,
- "Lower bound %" PRIuPTR " @ %" PRIuPTR " hit end\n",
- (uintptr_t)r,
- i);
- }
-
- if ((uintptr_t)zix_btree_get(ti) != r) {
- return test_fail(t,
- "Lower bound corrupt (%" PRIuPTR " != %" PRIuPTR "\n",
- (uintptr_t)zix_btree_get(ti),
- r);
- }
- }
-
- // Search for elements that don't exist
- for (size_t i = 0; i < n_elems; ++i) {
- r = ith_elem(test_num, n_elems * 3, n_elems + i);
- if (!zix_btree_find(t, (void*)r, &ti)) {
- return test_fail(t, "Unexpectedly found %" PRIuPTR "\n", (uintptr_t)r);
- }
- }
-
- // Iterate over all elements
- size_t i = 0;
- uintptr_t last = 0;
- for (ti = zix_btree_begin(t); !zix_btree_iter_is_end(ti);
- zix_btree_iter_increment(&ti), ++i) {
- const uintptr_t iter_data = (uintptr_t)zix_btree_get(ti);
- if (iter_data < last) {
- return test_fail(t,
- "Iter @ %" PRIuPTR " corrupt (%" PRIuPTR " < %" PRIuPTR
- ")\n",
- i,
- iter_data,
- last);
- }
- last = iter_data;
- }
-
- if (i != n_elems) {
- return test_fail(t,
- "Iteration stopped at %" PRIuPTR "/%" PRIuPTR
- " elements\n",
- i,
- n_elems);
- }
-
- // Insert n_elems elements again, ensuring duplicates fail
- for (i = 0; i < n_elems; ++i) {
- r = ith_elem(test_num, n_elems, i);
- if (!zix_btree_insert(t, (void*)r)) {
- return test_fail(t, "Duplicate insert succeeded\n");
- }
- }
-
- // Search for the middle element then iterate from there
- r = ith_elem(test_num, n_elems, n_elems / 2);
- if (zix_btree_find(t, (void*)r, &ti)) {
- return test_fail(t, "Find %" PRIuPTR " failed\n", (uintptr_t)r);
- }
- last = (uintptr_t)zix_btree_get(ti);
- zix_btree_iter_increment(&ti);
- for (i = 1; !zix_btree_iter_is_end(ti); zix_btree_iter_increment(&ti), ++i) {
- if ((uintptr_t)zix_btree_get(ti) == last) {
- return test_fail(
- t, "Duplicate element @ %" PRIuPTR " %" PRIuPTR "\n", i, last);
- }
- last = (uintptr_t)zix_btree_get(ti);
- }
-
- // Delete all elements
- ZixBTreeIter next = zix_btree_end_iter;
- for (size_t e = 0; e < n_elems; e++) {
- r = ith_elem(test_num, n_elems, e);
-
- uintptr_t removed = 0;
- if (zix_btree_remove(t, (void*)r, (void**)&removed, &next)) {
- return test_fail(t, "Error removing item %" PRIuPTR "\n", (uintptr_t)r);
- }
-
- if (removed != r) {
- return test_fail(t,
- "Removed wrong item %" PRIuPTR " != %" PRIuPTR "\n",
- removed,
- (uintptr_t)r);
- }
-
- if (test_num == 0) {
- const uintptr_t next_value = ith_elem(test_num, n_elems, e + 1);
- if (!((zix_btree_iter_is_end(next) && e == n_elems - 1) ||
- (uintptr_t)zix_btree_get(next) == next_value)) {
- return test_fail(t,
- "Delete all next iterator %" PRIuPTR " != %" PRIuPTR
- "\n",
- (uintptr_t)zix_btree_get(next),
- next_value);
- }
- }
- }
-
- // Ensure the tree is empty
- if (zix_btree_size(t) != 0) {
- return test_fail(t, "Tree size %" PRIuPTR " != 0\n", zix_btree_size(t));
- }
-
- // Insert n_elems elements again (to test non-empty destruction)
- for (size_t e = 0; e < n_elems; ++e) {
- r = ith_elem(test_num, n_elems, e);
- if (zix_btree_insert(t, (void*)r)) {
- return test_fail(t, "Post-deletion insert failed\n");
- }
- }
-
- // Delete elements that don't exist
- for (size_t e = 0; e < n_elems; e++) {
- r = ith_elem(test_num, n_elems * 3, n_elems + e);
- uintptr_t removed = 0;
- if (!zix_btree_remove(t, (void*)r, (void**)&removed, &next)) {
- return test_fail(
- t, "Non-existant deletion of %" PRIuPTR " succeeded\n", (uintptr_t)r);
- }
- }
-
- // Ensure tree size is still correct
- if (zix_btree_size(t) != n_elems) {
- return test_fail(t,
- "Tree size %" PRIuPTR " != %" PRIuPTR "\n",
- zix_btree_size(t),
- n_elems);
- }
-
- // Delete some elements towards the end
- for (size_t e = 0; e < n_elems / 4; e++) {
- r = ith_elem(test_num, n_elems, n_elems - (n_elems / 4) + e);
- uintptr_t removed = 0;
- if (zix_btree_remove(t, (void*)r, (void**)&removed, &next)) {
- return test_fail(t, "Deletion of %" PRIuPTR " failed\n", (uintptr_t)r);
- }
-
- if (removed != r) {
- return test_fail(t,
- "Removed wrong item %" PRIuPTR " != %" PRIuPTR "\n",
- removed,
- (uintptr_t)r);
- }
-
- if (test_num == 0) {
- const uintptr_t next_value = ith_elem(test_num, n_elems, e + 1);
- if (!zix_btree_iter_is_end(next) &&
- (uintptr_t)zix_btree_get(next) == next_value) {
- return test_fail(t,
- "Next iterator %" PRIuPTR " != %" PRIuPTR "\n",
- (uintptr_t)zix_btree_get(next),
- next_value);
- }
- }
- }
-
- // Check tree size
- if (zix_btree_size(t) != n_elems - (n_elems / 4)) {
- return test_fail(t,
- "Tree size %" PRIuPTR " != %" PRIuPTR "\n",
- zix_btree_size(t),
- n_elems);
- }
-
- // Delete some elements in a random order
- for (size_t e = 0; e < zix_btree_size(t) / 2; e++) {
- r = ith_elem(test_num, n_elems, unique_rand(e) % n_elems);
-
- uintptr_t removed = 0;
- ZixStatus rst = zix_btree_remove(t, (void*)r, (void**)&removed, &next);
- if (rst != ZIX_STATUS_SUCCESS && rst != ZIX_STATUS_NOT_FOUND) {
- return test_fail(t, "Error deleting %" PRIuPTR "\n", (uintptr_t)r);
- }
- }
-
- // Delete all remaining elements via next iterator
- next = zix_btree_begin(t);
- uintptr_t last_value = 0;
- while (!zix_btree_iter_is_end(next)) {
- const uintptr_t value = (uintptr_t)zix_btree_get(next);
- uintptr_t removed = 0;
- if (zix_btree_remove(t, zix_btree_get(next), (void**)&removed, &next)) {
- return test_fail(
- t, "Error removing next item %" PRIuPTR "\n", (uintptr_t)r);
- }
-
- if (removed != value) {
- return test_fail(t,
- "Removed wrong next item %" PRIuPTR " != %" PRIuPTR "\n",
- removed,
- (uintptr_t)value);
- }
-
- if (removed < last_value) {
- return test_fail(t,
- "Removed unordered next item %" PRIuPTR " < %" PRIuPTR
- "\n",
- removed,
- (uintptr_t)value);
- }
-
- last_value = removed;
- }
-
- assert(zix_btree_size(t) == 0);
- zix_btree_free(t, NULL, NULL);
-
- // Test lower_bound with wildcard comparator
-
- TestContext ctx = {test_num, n_elems};
- if (!(t = zix_btree_new(NULL, wildcard_cmp, &ctx))) {
- return test_fail(t, "Failed to allocate tree\n");
- }
-
- // Insert n_elems elements
- for (i = 0; i < n_elems; ++i) {
- r = ith_elem(test_num, n_elems, i);
- if ((st = zix_btree_insert(t, (void*)r))) {
- return test_fail(
- t, "Insert %" PRIuPTR " failed (%s)\n", (uintptr_t)r, zix_strerror(st));
- }
- }
-
- // Find lower bound of wildcard
- const uintptr_t wildcard = 0;
- if (zix_btree_lower_bound(t, wildcard_cmp, &ctx, (void*)wildcard, &ti)) {
- return test_fail(t, "Lower bound failed\n");
- }
-
- if (zix_btree_iter_is_end(ti)) {
- return test_fail(t, "Lower bound reached end\n");
- }
-
- // Check value
- const uintptr_t iter_data = (uintptr_t)zix_btree_get(ti);
- const uintptr_t cut = wildcard_cut(test_num, n_elems);
- if (iter_data != cut) {
- return test_fail(
- t, "Lower bound %" PRIuPTR " != %" PRIuPTR "\n", iter_data, cut);
- }
-
- if (wildcard_cmp((void*)wildcard, (void*)iter_data, &ctx)) {
- return test_fail(
- t, "Wildcard lower bound %" PRIuPTR " != %" PRIuPTR "\n", iter_data, cut);
- }
-
- // Find lower bound of value past end
- const uintptr_t max = (uintptr_t)-1;
- if (zix_btree_lower_bound(t, wildcard_cmp, &ctx, (void*)max, &ti)) {
- return test_fail(t, "Lower bound failed\n");
- }
-
- if (!zix_btree_iter_is_end(ti)) {
- return test_fail(t, "Lower bound of maximum value is not end\n");
- }
-
- zix_btree_free(t, NULL, NULL);
-
- return EXIT_SUCCESS;
-}
-
-static void
-test_failed_alloc(void)
-{
- ZixFailingAllocator allocator = zix_failing_allocator();
-
- // Successfully stress test the tree to count the number of allocations
- assert(!stress(&allocator.base, 0, 4096));
-
- // Test that each allocation failing is handled gracefully
- const size_t n_new_allocs = allocator.n_allocations;
- for (size_t i = 0U; i < n_new_allocs; ++i) {
- allocator.n_remaining = i;
- assert(stress(&allocator.base, 0, 4096));
- }
-}
-
-int
-main(int argc, char** argv)
-{
- if (argc > 2) {
- fprintf(stderr, "Usage: %s [N_ELEMS]\n", argv[0]);
- return EXIT_FAILURE;
- }
-
- test_clear();
- test_free();
- test_iter_comparison();
- test_insert_split_value();
- test_remove_cases();
- test_failed_alloc();
-
- const unsigned n_tests = 3U;
- const size_t n_elems = (argc > 1) ? strtoul(argv[1], NULL, 10) : 131072U;
-
- printf("Running %u tests with %" PRIuPTR " elements", n_tests, n_elems);
- for (unsigned i = 0; i < n_tests; ++i) {
- printf(".");
- fflush(stdout);
- if (stress(NULL, i, n_elems)) {
- return EXIT_FAILURE;
- }
- }
- printf("\n");
-
- return EXIT_SUCCESS;
-}