summaryrefslogtreecommitdiff
path: root/source/tests
diff options
context:
space:
mode:
Diffstat (limited to 'source/tests')
-rw-r--r--source/tests/_static.c55
-rw-r--r--source/tests/array_ref.test.c65
-rw-r--r--source/tests/async_function.test.c132
-rw-r--r--source/tests/atomic.test.c218
-rw-r--r--source/tests/bench.test.c41
-rw-r--r--source/tests/bigint.test.c108
-rw-r--r--source/tests/condition_variable.test.c84
-rw-r--r--source/tests/cpp.cpp13
-rw-r--r--source/tests/dynamic_array.test.c174
-rw-r--r--source/tests/file.test.c220
-rw-r--r--source/tests/input_buffer.test.c105
-rw-r--r--source/tests/input_stream.test.c23
-rw-r--r--source/tests/lower_bound.test.c178
-rw-r--r--source/tests/main.test.c9
-rw-r--r--source/tests/mersenne_twister_64.test.c38
-rw-r--r--source/tests/move_back.test.c148
-rw-r--r--source/tests/mutex.test.c79
-rw-r--r--source/tests/secure_random.test.c24
-rw-r--r--source/tests/sha256.test.c42
-rw-r--r--source/tests/signals.cpp29
-rw-r--r--source/tests/string_ref.test.c23
-rw-r--r--source/tests/test_duration.test.c19
-rw-r--r--source/tests/thread.test.c91
-rw-r--r--source/tests/too_many_assertions.c13
-rw-r--r--source/tests/too_many_tests.c14
25 files changed, 1945 insertions, 0 deletions
diff --git a/source/tests/_static.c b/source/tests/_static.c
new file mode 100644
index 0000000..4b7543d
--- /dev/null
+++ b/source/tests/_static.c
@@ -0,0 +1,55 @@
+#include "array_ref.test.c"
+#undef KIT_TEST_FILE
+
+#include "async_function.test.c"
+#undef KIT_TEST_FILE
+
+#include "bigint.test.c"
+#undef KIT_TEST_FILE
+
+#include "condition_variable.test.c"
+#undef KIT_TEST_FILE
+
+#include "dynamic_array.test.c"
+#undef KIT_TEST_FILE
+
+#include "file.test.c"
+#undef KIT_TEST_FILE
+
+#include "input_buffer.test.c"
+#undef KIT_TEST_FILE
+
+#include "input_stream.test.c"
+#undef KIT_TEST_FILE
+
+#include "lower_bound.test.c"
+#undef KIT_TEST_FILE
+
+#include "mersenne_twister_64.test.c"
+#undef KIT_TEST_FILE
+
+#include "move_back.test.c"
+#undef KIT_TEST_FILE
+
+#include "mutex.test.c"
+#undef KIT_TEST_FILE
+
+#include "secure_random.test.c"
+#undef KIT_TEST_FILE
+
+#include "sha256.test.c"
+#undef KIT_TEST_FILE
+
+#include "string_ref.test.c"
+#undef KIT_TEST_FILE
+
+#include "test_duration.test.c"
+#undef KIT_TEST_FILE
+
+#include "thread.test.c"
+#undef KIT_TEST_FILE
+
+#include "bench.test.c"
+#undef KIT_TEST_FILE
+
+#include "main.test.c"
diff --git a/source/tests/array_ref.test.c b/source/tests/array_ref.test.c
new file mode 100644
index 0000000..da20aa0
--- /dev/null
+++ b/source/tests/array_ref.test.c
@@ -0,0 +1,65 @@
+#include "../kit/array_ref.h"
+
+#define KIT_TEST_FILE array_ref
+#include "../kit_test/test.h"
+
+TEST("array ref const wrap") {
+ int foo[] = { 1, 2, 3 };
+ AR_WRAP(ref, int, foo);
+
+ REQUIRE(ref.size == 3);
+ REQUIRE(ref.values[0] == 1);
+ REQUIRE(ref.values[1] == 2);
+ REQUIRE(ref.values[2] == 3);
+}
+
+TEST("array ref wrap") {
+ int foo[] = { 1, 2, 3 };
+ AR_MUT_WRAP(ref, int, foo);
+
+ REQUIRE(ref.size == 3);
+ REQUIRE(ref.values[0] == 1);
+ REQUIRE(ref.values[1] == 2);
+ REQUIRE(ref.values[2] == 3);
+
+ ref.values[1] = 42;
+ REQUIRE(ref.values[1] == 42);
+}
+
+TEST("array ref equal") {
+ int foo[] = { 1, 2, 3, 4, 5, 6, 7 };
+ int bar[] = { 3, 4, 5 };
+
+ AR(int) foo_ref = { .size = 3, .values = foo + 2 };
+ AR(int) bar_ref = { .size = 3, .values = bar };
+
+ REQUIRE(AR_EQUAL(foo_ref, bar_ref));
+}
+
+static int compare(int const *left, int const *right) {
+ return *left - *right;
+}
+
+TEST("array ref compare") {
+ int foo[] = { 1, 2, 3, 5 };
+ int bar[] = { 1, 2, 4, 5 };
+
+ AR(int) foo_ref = { .size = 3, .values = foo };
+ AR(int) bar_ref = { .size = 3, .values = bar };
+
+ REQUIRE(AR_COMPARE(foo_ref, bar_ref, compare) < 0);
+ REQUIRE(AR_COMPARE(bar_ref, foo_ref, compare) > 0);
+ REQUIRE(AR_COMPARE(foo_ref, foo_ref, compare) == 0);
+}
+
+TEST("array ref different element sizes") {
+ int foo[] = { 1, 2, 3 };
+ char bar[] = { 1, 2, 3 };
+
+ AR(int) foo_ref = { .size = 3, .values = foo };
+ AR(char) bar_ref = { .size = 3, .values = bar };
+
+ REQUIRE(!AR_EQUAL(foo_ref, bar_ref));
+ REQUIRE(AR_COMPARE(foo_ref, bar_ref, compare) > 0);
+ REQUIRE(AR_COMPARE(bar_ref, foo_ref, compare) < 0);
+}
diff --git a/source/tests/async_function.test.c b/source/tests/async_function.test.c
new file mode 100644
index 0000000..35ce0ce
--- /dev/null
+++ b/source/tests/async_function.test.c
@@ -0,0 +1,132 @@
+#include "../kit/async_function.h"
+
+#define KIT_TEST_FILE async_function
+#include "../kit_test/test.h"
+
+AF_STATE(int, test_foo, );
+static AF_DECL(test_foo);
+
+CORO_IMPL(test_foo) {
+ AF_RETURN(42);
+}
+CORO_END
+
+AF_STATE(int, test_bar, );
+static AF_DECL(test_bar);
+
+CORO_IMPL(test_bar) {
+ AF_YIELD_VOID;
+ AF_RETURN(42);
+}
+CORO_END
+
+STATIC_CORO(int, test_gen, int i; int min; int max;) {
+ for (self->i = self->min; self->i < self->max; self->i++)
+ AF_YIELD(self->i);
+ AF_RETURN(self->max);
+}
+CORO_END
+
+STATIC_CORO_VOID(test_task, ) {
+ AF_YIELD_VOID;
+ AF_YIELD_VOID;
+ AF_RETURN_VOID;
+}
+CORO_END
+
+STATIC_CORO_VOID(test_nest_task, AF_TYPE(test_task) promise;) {
+ AF_INIT(self->promise, test_task, );
+ AF_AWAIT(self->promise);
+ AF_AWAIT(self->promise);
+ AF_AWAIT(self->promise);
+}
+CORO_END
+
+STATIC_CORO(int, test_nest_generator, AF_TYPE(test_gen) promise;) {
+ AF_INIT(self->promise, test_gen, .min = 1, .max = 3);
+ AF_YIELD_AWAIT(self->promise);
+}
+CORO_END
+
+TEST("coroutine create") {
+ AF_CREATE(promise, test_foo, );
+ REQUIRE(!AF_FINISHED(promise));
+}
+
+TEST("coroutine init") {
+ AF_TYPE(test_foo) promise;
+ AF_INIT(promise, test_foo, );
+ REQUIRE(!AF_FINISHED(promise));
+}
+
+TEST("coroutine init with value") {
+ AF_TYPE(test_foo) promise;
+ AF_INIT(promise, test_foo, .return_value = 42);
+ REQUIRE(promise.return_value == 42);
+ REQUIRE(!AF_FINISHED(promise));
+}
+
+TEST("coroutine create with value") {
+ AF_CREATE(promise, test_foo, .return_value = -1);
+ REQUIRE(promise.return_value == -1);
+ REQUIRE(!AF_FINISHED(promise));
+}
+
+TEST("coroutine execute and return") {
+ AF_CREATE(promise, test_foo, );
+ REQUIRE(AF_NEXT(promise) == 42);
+ REQUIRE(AF_FINISHED(promise));
+}
+
+TEST("coroutine execute two steps") {
+ AF_CREATE(promise, test_bar, .return_value = 0);
+ AF_EXECUTE(promise);
+ REQUIRE(promise.return_value == 0);
+ AF_EXECUTE(promise);
+ REQUIRE(promise.return_value == 42);
+}
+
+TEST("coroutine generator") {
+ int i;
+ AF_CREATE(promise, test_gen, .min = 10, .max = 15);
+ for (i = 0; i <= 5; i++) REQUIRE(AF_NEXT(promise) == 10 + i);
+}
+
+TEST("coroutine status finished") {
+ AF_CREATE(promise, test_bar, );
+ REQUIRE(!AF_FINISHED(promise));
+ AF_EXECUTE(promise);
+ REQUIRE(!AF_FINISHED(promise));
+ AF_EXECUTE(promise);
+ REQUIRE(AF_FINISHED(promise));
+}
+
+TEST("coroutine task") {
+ AF_CREATE(promise, test_task, );
+ AF_EXECUTE(promise);
+ REQUIRE(!AF_FINISHED(promise));
+ AF_EXECUTE(promise);
+ REQUIRE(!AF_FINISHED(promise));
+ AF_EXECUTE(promise);
+ REQUIRE(AF_FINISHED(promise));
+}
+
+TEST("coroutine nested task") {
+ AF_CREATE(promise, test_nest_task, );
+ AF_EXECUTE(promise);
+ REQUIRE(!AF_FINISHED(promise));
+ AF_EXECUTE(promise);
+ REQUIRE(!AF_FINISHED(promise));
+ AF_EXECUTE(promise);
+ REQUIRE(AF_FINISHED(promise));
+}
+
+TEST("coroutine nested generator") {
+ AF_CREATE(promise, test_nest_generator, );
+ REQUIRE(AF_NEXT(promise) == 1);
+ REQUIRE(AF_NEXT(promise) == 2);
+ REQUIRE(AF_NEXT(promise) == 3);
+ REQUIRE(!AF_FINISHED(promise));
+ AF_EXECUTE(promise);
+ REQUIRE(AF_FINISHED(promise));
+}
diff --git a/source/tests/atomic.test.c b/source/tests/atomic.test.c
new file mode 100644
index 0000000..c1831a3
--- /dev/null
+++ b/source/tests/atomic.test.c
@@ -0,0 +1,218 @@
+#include "../kit/atomic.h"
+#include "../kit/thread.h"
+
+#define KIT_TEST_FILE atomic
+#include "../kit_test/test.h"
+
+TEST("atomic store and load") {
+ ATOMIC(int) value;
+ atomic_store_explicit(&value, 20, memory_order_relaxed);
+ REQUIRE(atomic_load_explicit(&value, memory_order_relaxed) == 20);
+}
+
+TEST("atomic exchange") {
+ ATOMIC(int) value;
+ atomic_store_explicit(&value, 20, memory_order_relaxed);
+ REQUIRE(atomic_exchange_explicit(&value, 42,
+ memory_order_relaxed) == 20);
+ REQUIRE(atomic_load_explicit(&value, memory_order_relaxed) == 42);
+}
+
+TEST("atomic fetch add") {
+ ATOMIC(int) value;
+ atomic_store_explicit(&value, 20, memory_order_relaxed);
+ REQUIRE(atomic_fetch_add_explicit(&value, 22,
+ memory_order_relaxed) == 20);
+ REQUIRE(atomic_load_explicit(&value, memory_order_relaxed) == 42);
+}
+
+enum { THREAD_COUNT = 20, TICK_COUNT = 10000 };
+
+static int test_8_(void *p) {
+ ptrdiff_t i;
+ ATOMIC(int8_t) *x = (ATOMIC(int8_t) *) p;
+
+ for (i = 0; i < TICK_COUNT; i++) {
+ atomic_fetch_add_explicit(x, 20, memory_order_relaxed);
+ thrd_yield();
+ atomic_fetch_add_explicit(x, 22, memory_order_relaxed);
+ thrd_yield();
+ atomic_fetch_add_explicit(x, -42, memory_order_relaxed);
+ thrd_yield();
+ }
+
+ return 0;
+}
+
+TEST("atomic types") {
+ ATOMIC(int8_t) b_1;
+ ATOMIC(int8_t) b_2;
+ ATOMIC(int16_t) i16;
+ ATOMIC(int32_t) i32;
+ ATOMIC(int64_t) i64;
+
+ atomic_store_explicit(&b_1, 42, memory_order_relaxed);
+ atomic_store_explicit(&b_2, 43, memory_order_relaxed);
+ atomic_store_explicit(&i16, 4242, memory_order_relaxed);
+ atomic_store_explicit(&i32, 42424242, memory_order_relaxed);
+ atomic_store_explicit(&i64, 4242424242424242ll,
+ memory_order_relaxed);
+
+ atomic_fetch_add_explicit(&b_1, -20, memory_order_relaxed);
+ atomic_fetch_add_explicit(&b_2, -20, memory_order_relaxed);
+ atomic_fetch_add_explicit(&i16, -2020, memory_order_relaxed);
+ atomic_fetch_add_explicit(&i32, -20202020, memory_order_relaxed);
+ atomic_fetch_add_explicit(&i64, -2020202020202020ll,
+ memory_order_relaxed);
+
+ REQUIRE(atomic_exchange_explicit(&b_1, 0, memory_order_relaxed) ==
+ 22);
+ REQUIRE(atomic_exchange_explicit(&b_2, 0, memory_order_relaxed) ==
+ 23);
+ REQUIRE(atomic_exchange_explicit(&i16, 0, memory_order_relaxed) ==
+ 2222);
+ REQUIRE(atomic_exchange_explicit(&i32, 0, memory_order_relaxed) ==
+ 22222222);
+ REQUIRE(atomic_exchange_explicit(&i64, 0, memory_order_relaxed) ==
+ 2222222222222222ll);
+
+ REQUIRE(atomic_load_explicit(&b_1, memory_order_relaxed) == 0);
+ REQUIRE(atomic_load_explicit(&b_2, memory_order_relaxed) == 0);
+ REQUIRE(atomic_load_explicit(&i16, memory_order_relaxed) == 0);
+ REQUIRE(atomic_load_explicit(&i32, memory_order_relaxed) == 0);
+ REQUIRE(atomic_load_explicit(&i64, memory_order_relaxed) == 0ll);
+}
+
+TEST("atomic byte concurrency") {
+ ptrdiff_t i;
+
+ ATOMIC(int8_t) foo;
+ ATOMIC(int8_t) bar;
+
+ atomic_store_explicit(&foo, 42, memory_order_relaxed);
+ atomic_store_explicit(&bar, 43, memory_order_relaxed);
+
+ thrd_t threads[THREAD_COUNT];
+ for (i = 0; i < THREAD_COUNT; i++)
+ REQUIRE_EQ(thrd_create(threads + i, test_8_,
+ (void *) ((i % 2) ? &foo : &bar)),
+ thrd_success);
+ for (i = 0; i < THREAD_COUNT; i++) thrd_join(threads[i], NULL);
+
+ REQUIRE(atomic_load_explicit(&foo, memory_order_relaxed) == 42);
+ REQUIRE(atomic_load_explicit(&bar, memory_order_relaxed) == 43);
+}
+
+static int test_16_(void *p) {
+ ptrdiff_t i;
+
+ ATOMIC(int16_t) *x = (ATOMIC(int16_t) *) p;
+
+ for (i = 0; i < TICK_COUNT; i++) {
+ atomic_fetch_add_explicit(x, 2020, memory_order_relaxed);
+ thrd_yield();
+ atomic_fetch_add_explicit(x, 2222, memory_order_relaxed);
+ thrd_yield();
+ atomic_fetch_add_explicit(x, -4242, memory_order_relaxed);
+ thrd_yield();
+ }
+
+ return 0;
+}
+
+TEST("atomic int16 concurrency") {
+ ptrdiff_t i;
+
+ ATOMIC(int16_t) foo;
+ ATOMIC(int16_t) bar;
+
+ atomic_store_explicit(&foo, 42, memory_order_relaxed);
+ atomic_store_explicit(&bar, 43, memory_order_relaxed);
+
+ thrd_t threads[THREAD_COUNT];
+ for (i = 0; i < THREAD_COUNT; i++)
+ REQUIRE_EQ(thrd_create(threads + i, test_16_,
+ (void *) ((i % 2) ? &foo : &bar)),
+ thrd_success);
+ for (i = 0; i < THREAD_COUNT; i++) thrd_join(threads[i], NULL);
+
+ REQUIRE(atomic_load_explicit(&foo, memory_order_relaxed) == 42);
+ REQUIRE(atomic_load_explicit(&bar, memory_order_relaxed) == 43);
+}
+
+static int test_32_(void *p) {
+ ptrdiff_t i;
+
+ ATOMIC(int32_t) *x = (ATOMIC(int32_t) *) p;
+
+ for (i = 0; i < TICK_COUNT; i++) {
+ atomic_fetch_add_explicit(x, 202020, memory_order_relaxed);
+ thrd_yield();
+ atomic_fetch_add_explicit(x, 222222, memory_order_relaxed);
+ thrd_yield();
+ atomic_fetch_add_explicit(x, -424242, memory_order_relaxed);
+ thrd_yield();
+ }
+
+ return 0;
+}
+
+TEST("atomic int32 concurrency") {
+ ptrdiff_t i;
+
+ ATOMIC(int32_t) foo;
+ ATOMIC(int32_t) bar;
+
+ atomic_store_explicit(&foo, 42, memory_order_relaxed);
+ atomic_store_explicit(&bar, 43, memory_order_relaxed);
+
+ thrd_t threads[THREAD_COUNT];
+ for (i = 0; i < THREAD_COUNT; i++)
+ REQUIRE_EQ(thrd_create(threads + i, test_32_,
+ (void *) ((i % 2) ? &foo : &bar)),
+ thrd_success);
+ for (i = 0; i < THREAD_COUNT; i++) thrd_join(threads[i], NULL);
+
+ REQUIRE(atomic_load_explicit(&foo, memory_order_relaxed) == 42);
+ REQUIRE(atomic_load_explicit(&bar, memory_order_relaxed) == 43);
+}
+
+static int test_64_(void *p) {
+ ptrdiff_t i;
+
+ ATOMIC(int64_t) *x = (ATOMIC(int64_t) *) p;
+
+ for (i = 0; i < TICK_COUNT; i++) {
+ atomic_fetch_add_explicit(x, 20202020202020ll,
+ memory_order_relaxed);
+ thrd_yield();
+ atomic_fetch_add_explicit(x, 22222222222222ll,
+ memory_order_relaxed);
+ thrd_yield();
+ atomic_fetch_add_explicit(x, -42424242424242ll,
+ memory_order_relaxed);
+ thrd_yield();
+ }
+
+ return 0;
+}
+
+TEST("atomic int64 concurrency") {
+ ptrdiff_t i;
+
+ ATOMIC(int64_t) foo;
+ ATOMIC(int64_t) bar;
+
+ atomic_store_explicit(&foo, 42, memory_order_relaxed);
+ atomic_store_explicit(&bar, 43, memory_order_relaxed);
+
+ thrd_t threads[THREAD_COUNT];
+ for (i = 0; i < THREAD_COUNT; i++)
+ REQUIRE_EQ(thrd_create(threads + i, test_64_,
+ (void *) ((i % 2) ? &foo : &bar)),
+ thrd_success);
+ for (i = 0; i < THREAD_COUNT; i++) thrd_join(threads[i], NULL);
+
+ REQUIRE(atomic_load_explicit(&foo, memory_order_relaxed) == 42);
+ REQUIRE(atomic_load_explicit(&bar, memory_order_relaxed) == 43);
+}
diff --git a/source/tests/bench.test.c b/source/tests/bench.test.c
new file mode 100644
index 0000000..92e216d
--- /dev/null
+++ b/source/tests/bench.test.c
@@ -0,0 +1,41 @@
+#define KIT_TEST_FILE bench
+#include "../kit_test/bench.h"
+
+struct test_foo_ {
+ double f;
+};
+
+BENCHMARK("foo") {
+
+ /* Benchmark setup.
+ */
+
+ BENCHMARK_BEGIN;
+ {
+ /* Measured code snippet.
+ */
+
+ int x = 0;
+ struct test_foo_ f = { 0. };
+
+ for (int i = 0; i < 100000; i++) {
+ x += (1 << 1);
+ x ^= i;
+ f.f += 0.1;
+ }
+
+ DO_NOT_OPTIMIZE(x);
+ DO_NOT_OPTIMIZE(f);
+ }
+ BENCHMARK_END;
+}
+
+BENCHMARK("bar") {
+ BENCHMARK_REPEAT(10);
+ BENCHMARK_REPEAT(100);
+
+ BENCHMARK_BEGIN;
+ BENCHMARK_END;
+}
+
+BENCHMARK("empty") { }
diff --git a/source/tests/bigint.test.c b/source/tests/bigint.test.c
new file mode 100644
index 0000000..c8ac44f
--- /dev/null
+++ b/source/tests/bigint.test.c
@@ -0,0 +1,108 @@
+#define KIT_BIGINT_SIZE 256
+#include "../kit/bigint.h"
+
+#define KIT_TEST_FILE bigint
+#include "../kit_test/test.h"
+
+#if __STDC_VERSION__ >= 199901L
+static_assert(sizeof(bigint_t) == 256, "KIT_BIGINT_SIZE check");
+#endif
+
+TEST("bigint bin hex") {
+ REQUIRE(bi_equal(HEX("10"), BIN("10000")));
+ REQUIRE(bi_equal(HEX("20"), BIN("100000")));
+ REQUIRE(bi_equal(HEX("40"), BIN("1000000")));
+ REQUIRE(bi_equal(HEX("80"), BIN("10000000")));
+ REQUIRE(bi_equal(HEX("310"), BIN("1100010000")));
+ REQUIRE(bi_equal(HEX("420"), BIN("10000100000")));
+ REQUIRE(bi_equal(HEX("540"), BIN("10101000000")));
+ REQUIRE(bi_equal(HEX("780"), BIN("11110000000")));
+}
+
+TEST("bigint hex add") {
+ REQUIRE(bi_equal(
+ bi_add(HEX("4242424242424242424242424242424242424242"),
+ HEX("1111111111111111111111111111111111111111")),
+ HEX("5353535353535353535353535353535353535353")));
+}
+
+TEST("bigint hex sub") {
+ REQUIRE(bi_equal(
+ bi_sub(HEX("4242424242424242424242424242424242424242"),
+ HEX("1111111111111111111111111111111111111111")),
+ HEX("3131313131313131313131313131313131313131")));
+}
+
+TEST("bigint base58") {
+ REQUIRE(bi_equal(BASE58("31"), bi_uint32(58 * 2)));
+}
+
+TEST("bigint base58 add") {
+ REQUIRE(bi_equal(
+ bi_add(BASE58("4242424242424242424242424242424242424242"),
+ BASE58("2222222222222222222222222222222222222222")),
+ BASE58("5353535353535353535353535353535353535353")));
+}
+
+TEST("bigint base58 sub") {
+ REQUIRE(bi_equal(
+ bi_sub(BASE58("4242424242424242424242424242424242424242"),
+ BASE58("2222222222222222222222222222222222222222")),
+ BASE58("3131313131313131313131313131313131313131")));
+}
+
+TEST("bigint base58 mul") {
+ REQUIRE(bi_equal(bi_mul(BASE58("2111111111111111111111"),
+ BASE58("foofoofoofoofoo")),
+ BASE58("foofoofoofoofoo111111111111111111111")));
+}
+
+TEST("bigint div") {
+ REQUIRE(bi_equal(bi_div(bi_int32(-1), bi_int32(-1)).quotient,
+ bi_int32(1)));
+ REQUIRE(bi_equal(bi_div(bi_int32(-1), bi_int32(-1)).remainder,
+ bi_int32(0)));
+ REQUIRE(bi_equal(bi_div(bi_int32(-3), bi_int32(2)).quotient,
+ bi_int32(-2)));
+ REQUIRE(bi_equal(bi_div(bi_int32(-3), bi_int32(2)).remainder,
+ bi_int32(1)));
+ REQUIRE(bi_equal(bi_div(bi_int32(3), bi_int32(-2)).quotient,
+ bi_int32(-1)));
+ REQUIRE(bi_equal(bi_div(bi_int32(3), bi_int32(-2)).remainder,
+ bi_int32(1)));
+ REQUIRE(bi_equal(bi_div(bi_int32(-3), bi_int32(4)).quotient,
+ bi_int32(-1)));
+ REQUIRE(bi_equal(bi_div(bi_int32(-3), bi_int32(4)).remainder,
+ bi_int32(3)));
+ REQUIRE(bi_equal(bi_div(bi_int32(3), bi_int32(-4)).quotient,
+ bi_int32(0)));
+ REQUIRE(bi_equal(bi_div(bi_int32(3), bi_int32(-4)).remainder,
+ bi_int32(3)));
+
+ REQUIRE(
+ bi_equal(bi_div(HEX("100"), HEX("10")).quotient, HEX("10")));
+
+ REQUIRE(bi_equal(bi_div(bi_mul(BASE58("foofoofoofoofoofoo"),
+ BASE58("barbarbarbarbarbar")),
+ BASE58("barbarbarbarbarbar"))
+ .quotient,
+ BASE58("foofoofoofoofoofoo")));
+
+ REQUIRE(bi_equal(bi_div(bi_mul(BASE58("foofoofoofoofoofoofoofoo"),
+ BASE58("barbarbarbarbarbar")),
+ BASE58("barbarbarbarbarbar"))
+ .quotient,
+ BASE58("foofoofoofoofoofoofoofoo")));
+
+ REQUIRE(bi_equal(
+ bi_div(
+ bi_mul(BASE58("foofoofoofoofoofoofoofoofoofoofoofoofoofoofo"
+ "ofoofoofoo"),
+ BASE58("barbarbarbarbarbarbarbarbarbarbarbarbarbarba"
+ "rbar")),
+ BASE58("barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar"))
+ .quotient,
+ BASE58(
+ "foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo")));
+}
+
diff --git a/source/tests/condition_variable.test.c b/source/tests/condition_variable.test.c
new file mode 100644
index 0000000..dfbe4a6
--- /dev/null
+++ b/source/tests/condition_variable.test.c
@@ -0,0 +1,84 @@
+#include "../kit/condition_variable.h"
+#include "../kit/thread.h"
+
+#define KIT_TEST_FILE condition_variable
+#include "../kit_test/test.h"
+
+typedef struct {
+ mtx_t m;
+ int in;
+ int out;
+ cnd_t send;
+ cnd_t receive;
+ int value;
+} cnd_test_data_t;
+
+static int cnd_test_run(void *p) {
+ cnd_test_data_t *data = (cnd_test_data_t *) p;
+
+ mtx_lock(&data->m);
+ data->value = 20;
+ data->out = 1;
+ mtx_unlock(&data->m);
+
+ cnd_broadcast(&data->send);
+
+ mtx_lock(&data->m);
+ if (data->in == 0)
+ cnd_wait(&data->receive, &data->m);
+ data->in = 0;
+ data->value = 22;
+ data->out = 1;
+ mtx_unlock(&data->m);
+
+ cnd_broadcast(&data->send);
+
+ return 0;
+}
+
+TEST("condition variable") {
+ int i;
+ int ok = 1;
+
+ for (i = 0; i < 10; i++) {
+ cnd_test_data_t data;
+
+ data.in = 0;
+ data.out = 0;
+ data.value = 0;
+
+ ok = ok && (mtx_init(&data.m, mtx_plain) == thrd_success);
+ ok = ok && (cnd_init(&data.send) == thrd_success);
+ ok = ok && (cnd_init(&data.receive) == thrd_success);
+
+ thrd_t t;
+ ok = ok && (thrd_create(&t, cnd_test_run, &data) == thrd_success);
+
+ ok = ok && (mtx_lock(&data.m) == thrd_success);
+ if (data.out == 0)
+ ok = ok && (cnd_wait(&data.send, &data.m) == thrd_success);
+ data.out = 0;
+ int x = data.value;
+ data.in = 1;
+ ok = ok && (mtx_unlock(&data.m) == thrd_success);
+
+ ok = ok && (cnd_broadcast(&data.receive) == thrd_success);
+
+ ok = ok && (mtx_lock(&data.m) == thrd_success);
+ if (data.out == 0)
+ ok = ok && (cnd_wait(&data.send, &data.m) == thrd_success);
+ data.out = 0;
+ x += data.value;
+ ok = ok && (mtx_unlock(&data.m) == thrd_success);
+
+ ok = ok && (thrd_join(t, NULL) == thrd_success);
+
+ mtx_destroy(&data.m);
+ cnd_destroy(&data.send);
+ cnd_destroy(&data.receive);
+
+ ok = ok && (x == 42);
+ }
+
+ REQUIRE(ok);
+}
diff --git a/source/tests/cpp.cpp b/source/tests/cpp.cpp
new file mode 100644
index 0000000..8b762e4
--- /dev/null
+++ b/source/tests/cpp.cpp
@@ -0,0 +1,13 @@
+#include "../kit_test/test.h"
+
+TEST("foo") {
+ REQUIRE(20 + 22 == 42);
+}
+
+TEST("bar") {
+ REQUIRE(true);
+}
+
+int main(int argc, char **argv) {
+ return run_tests(argc, argv);
+}
diff --git a/source/tests/dynamic_array.test.c b/source/tests/dynamic_array.test.c
new file mode 100644
index 0000000..8701e6c
--- /dev/null
+++ b/source/tests/dynamic_array.test.c
@@ -0,0 +1,174 @@
+#include "../kit/dynamic_array.h"
+
+#define KIT_TEST_FILE dynamic_array
+#include "../kit_test/test.h"
+
+TEST("dynamic array empty") {
+ DA_CREATE(v, char, 0);
+
+ REQUIRE(v.size == 0);
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array resize") {
+ DA_CREATE(v, char, 0);
+ DA_RESIZE(v, 10);
+
+ REQUIRE(v.size == 10);
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array grow") {
+ DA_CREATE(v, char, 2);
+ DA_RESIZE(v, 10);
+
+ REQUIRE(v.size == 10);
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array diminish") {
+ DA_CREATE(v, char, 10);
+ DA_RESIZE(v, 9);
+
+ REQUIRE(v.size == 9);
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array of chars") {
+ DA_CREATE(v, char, 100);
+
+ REQUIRE(v.size == 100);
+ REQUIRE(v.capacity >= 100);
+ REQUIRE(v.values != NULL);
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array push") {
+ DA_CREATE(v, char, 0);
+ DA_APPEND(v, 'x');
+
+ REQUIRE(v.size == 1);
+ REQUIRE(v.values[0] == 'x');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array insert front") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_INSERT(v, 0, 'x');
+
+ REQUIRE(v.size == 4);
+ REQUIRE(v.values[0] == 'x');
+ REQUIRE(v.values[1] == 'a');
+ REQUIRE(v.values[2] == 'b');
+ REQUIRE(v.values[3] == 'c');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array insert back") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_INSERT(v, 3, 'x');
+
+ REQUIRE(v.size == 4);
+ REQUIRE(v.values[0] == 'a');
+ REQUIRE(v.values[1] == 'b');
+ REQUIRE(v.values[2] == 'c');
+ REQUIRE(v.values[3] == 'x');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array insert middle") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_INSERT(v, 2, 'x');
+
+ REQUIRE(v.size == 4);
+ REQUIRE(v.values[0] == 'a');
+ REQUIRE(v.values[1] == 'b');
+ REQUIRE(v.values[2] == 'x');
+ REQUIRE(v.values[3] == 'c');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array erase front") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_ERASE(v, 0);
+
+ REQUIRE(v.size == 2);
+ REQUIRE(v.values[0] == 'b');
+ REQUIRE(v.values[1] == 'c');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array erase back") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_ERASE(v, 2);
+
+ REQUIRE(v.size == 2);
+ REQUIRE(v.values[0] == 'a');
+ REQUIRE(v.values[1] == 'b');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array erase middle") {
+ DA_CREATE(v, char, 3);
+
+ v.values[0] = 'a';
+ v.values[1] = 'b';
+ v.values[2] = 'c';
+
+ DA_ERASE(v, 1);
+
+ REQUIRE(v.size == 2);
+ REQUIRE(v.values[0] == 'a');
+ REQUIRE(v.values[1] == 'c');
+
+ DA_DESTROY(v);
+}
+
+TEST("dynamic array of ints") {
+ DA_CREATE(v, int, 10);
+ DA_RESIZE(v, 5);
+ v.values[4] = 42;
+ DA_APPEND(v, 43);
+
+ REQUIRE(v.size == 6);
+ REQUIRE(v.values[4] == 42);
+ REQUIRE(v.values[5] == 43);
+
+ DA_DESTROY(v);
+}
diff --git a/source/tests/file.test.c b/source/tests/file.test.c
new file mode 100644
index 0000000..e72df83
--- /dev/null
+++ b/source/tests/file.test.c
@@ -0,0 +1,220 @@
+#include "../kit/file.h"
+#include "../kit/string_ref.h"
+#include <string.h>
+
+#define KIT_TEST_FILE file
+#include "../kit_test/test.h"
+
+TEST("file path cache") {
+ kit_allocator_t alloc = kit_alloc_default();
+
+ string_t user = path_user(alloc);
+ string_t cache = path_cache(alloc);
+
+ DA_RESIZE(cache, cache.size + 1);
+ cache.values[cache.size - 1] = '\0';
+ DA_RESIZE(cache, cache.size - 1);
+
+ string_t expected =
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ path_join(WRAP_STR(user), SZ("AppData" PATH_DELIM "Local"),
+ alloc);
+#elif defined(__APPLE__)
+ path_join(WRAP_STR(user), SZ("Library" PATH_DELIM "Caches"),
+ alloc);
+#else
+ path_join(WRAP_STR(user), SZ(".cache"), alloc);
+#endif
+
+ REQUIRE(AR_EQUAL(cache, expected));
+
+ DA_DESTROY(user);
+ DA_DESTROY(cache);
+ DA_DESTROY(expected);
+}
+
+TEST("file path normalize one") {
+ str_t foo = SZ("foo/bar/../baz");
+ str_t foo_norm = SZ("foo" PATH_DELIM "baz");
+
+ string_t bar = path_norm(foo, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(foo_norm, bar));
+
+ DA_DESTROY(bar);
+}
+
+TEST("file path normalize two") {
+ str_t foo = SZ("foo/bar/../../baz");
+ str_t foo_norm = SZ("baz");
+
+ string_t bar = path_norm(foo, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(foo_norm, bar));
+
+ DA_DESTROY(bar);
+}
+
+TEST("file path normalize parent") {
+ str_t foo = SZ("../baz");
+ str_t foo_norm = SZ(".." PATH_DELIM "baz");
+
+ string_t bar = path_norm(foo, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(foo_norm, bar));
+
+ DA_DESTROY(bar);
+}
+
+TEST("file path normalize double parent") {
+ str_t foo = SZ("foo/../../baz");
+ str_t foo_norm = SZ(".." PATH_DELIM "baz");
+
+ string_t bar = path_norm(foo, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(foo_norm, bar));
+
+ DA_DESTROY(bar);
+}
+
+TEST("file path normalize windows delim") {
+ str_t foo = SZ("foo\\bar\\..\\baz");
+ str_t foo_norm = SZ("foo" PATH_DELIM "baz");
+
+ string_t bar = path_norm(foo, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(foo_norm, bar));
+
+ DA_DESTROY(bar);
+}
+
+TEST("file path join no delim") {
+ str_t foo = SZ("foo");
+ str_t bar = SZ("bar");
+ str_t joined = SZ("foo" PATH_DELIM "bar");
+
+ string_t foobar = path_join(foo, bar, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(joined, foobar));
+
+ DA_DESTROY(foobar);
+}
+
+TEST("file path join delim left") {
+ str_t foo = SZ("foo/");
+ str_t bar = SZ("bar");
+ str_t joined = SZ("foo" PATH_DELIM "bar");
+
+ string_t foobar = path_join(foo, bar, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(joined, foobar));
+
+ DA_DESTROY(foobar);
+}
+
+TEST("file path join delim right") {
+ str_t foo = SZ("foo");
+ str_t bar = SZ("/bar");
+ str_t joined = SZ("foo" PATH_DELIM "bar");
+
+ string_t foobar = path_join(foo, bar, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(joined, foobar));
+
+ DA_DESTROY(foobar);
+}
+
+TEST("file path join delim both") {
+ str_t foo = SZ("foo/");
+ str_t bar = SZ("/bar");
+ str_t joined = SZ("foo" PATH_DELIM "bar");
+
+ string_t foobar = path_join(foo, bar, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(joined, foobar));
+
+ DA_DESTROY(foobar);
+}
+
+TEST("file path user") {
+ string_t user = path_user(kit_alloc_default());
+
+ REQUIRE(user.size > 0);
+
+ DA_DESTROY(user);
+}
+
+TEST("file path index relative") {
+ str_t foobar = SZ("foo/bar");
+ str_t foo = SZ("foo");
+ str_t bar = SZ("bar");
+
+ REQUIRE(AR_EQUAL(path_index(foobar, 0), foo));
+ REQUIRE(AR_EQUAL(path_index(foobar, 1), bar));
+ REQUIRE(path_index(foobar, 2).size == 0);
+}
+
+TEST("file path index absolute") {
+ str_t foobar = SZ("/foo/bar");
+ str_t foo = SZ("foo");
+ str_t bar = SZ("bar");
+
+ REQUIRE(AR_EQUAL(path_index(foobar, 0), foo));
+ REQUIRE(AR_EQUAL(path_index(foobar, 1), bar));
+ REQUIRE(path_index(foobar, 2).size == 0);
+}
+
+TEST("file path index windows disk name") {
+ str_t foobar = SZ("c:\\foo\\bar");
+ str_t disk = SZ("c:");
+ str_t foo = SZ("foo");
+ str_t bar = SZ("bar");
+
+ REQUIRE(AR_EQUAL(path_index(foobar, 0), disk));
+ REQUIRE(AR_EQUAL(path_index(foobar, 1), foo));
+ REQUIRE(AR_EQUAL(path_index(foobar, 2), bar));
+ REQUIRE(path_index(foobar, 3).size == 0);
+}
+
+TEST("file path take relative") {
+ str_t foobar = SZ("foo/bar/");
+ str_t foo = SZ("foo");
+ str_t bar = SZ("foo/bar");
+ str_t bar_end = SZ("foo/bar/");
+
+ REQUIRE(AR_EQUAL(path_take(foobar, 0), foo));
+ REQUIRE(AR_EQUAL(path_take(foobar, 1), bar));
+ REQUIRE(AR_EQUAL(path_take(foobar, 2), bar_end));
+}
+
+TEST("file path take absolute") {
+ str_t foobar = SZ("/foo/bar");
+ str_t foo = SZ("/foo");
+ str_t bar = SZ("/foo/bar");
+
+ REQUIRE(AR_EQUAL(path_take(foobar, 0), foo));
+ REQUIRE(AR_EQUAL(path_take(foobar, 1), bar));
+}
+
+TEST("file path take windows disk name") {
+ str_t foobar = SZ("c:\\foo\\bar");
+ str_t disk = SZ("c:");
+ str_t foo = SZ("c:\\foo");
+ str_t bar = SZ("c:\\foo\\bar");
+
+ REQUIRE(AR_EQUAL(path_take(foobar, 0), disk));
+ REQUIRE(AR_EQUAL(path_take(foobar, 1), foo));
+ REQUIRE(AR_EQUAL(path_take(foobar, 2), bar));
+}
+
+TEST("file create folder") { }
+
+TEST("file create folder recursive") { }
+
+TEST("file remove") { }
+
+TEST("file remove folder") { }
+
+TEST("file remove recursive") { }
+
+TEST("file enum folder") { }
diff --git a/source/tests/input_buffer.test.c b/source/tests/input_buffer.test.c
new file mode 100644
index 0000000..3cd84b6
--- /dev/null
+++ b/source/tests/input_buffer.test.c
@@ -0,0 +1,105 @@
+#include "../kit/input_buffer.h"
+
+#define KIT_TEST_FILE input_buffer
+#include "../kit_test/test.h"
+
+TEST("input buffer read once") {
+ str_t text = { .size = 3, .values = "foo" };
+ is_handle_t in = IS_WRAP_STRING(text);
+ ib_handle_t first = IB_WRAP(in);
+
+ ib_handle_t second = ib_read(first, 3);
+
+ REQUIRE(second.status == KIT_OK);
+ REQUIRE(second.data.size == 3);
+ REQUIRE(AR_EQUAL(text, second.data));
+
+ ib_destroy(second);
+ ib_destroy(first);
+ is_destroy(in);
+}
+
+TEST("input buffer read again") {
+ str_t text = { .size = 6, .values = "foobar" };
+ str_t foo = { .size = 3, .values = "foo" };
+ is_handle_t in = IS_WRAP_STRING(text);
+ ib_handle_t first = IB_WRAP(in);
+
+ ib_handle_t second = ib_read(first, 3);
+ ib_handle_t third = ib_read(first, 3);
+
+ REQUIRE(AR_EQUAL(foo, second.data));
+ REQUIRE(AR_EQUAL(foo, third.data));
+
+ ib_destroy(third);
+ ib_destroy(second);
+ ib_destroy(first);
+ is_destroy(in);
+}
+
+TEST("input buffer read twice") {
+ str_t text = { .size = 6, .values = "foobar" };
+ str_t foo = { .size = 3, .values = "foo" };
+ str_t bar = { .size = 3, .values = "bar" };
+ is_handle_t in = IS_WRAP_STRING(text);
+ ib_handle_t first = IB_WRAP(in);
+
+ ib_handle_t second = ib_read(first, 3);
+ ib_handle_t third = ib_read(second, 3);
+
+ REQUIRE(AR_EQUAL(foo, second.data));
+ REQUIRE(AR_EQUAL(bar, third.data));
+
+ ib_destroy(third);
+ ib_destroy(second);
+ ib_destroy(first);
+ is_destroy(in);
+}
+
+static int is_integer_(str_t const data) {
+ for (ptrdiff_t i = 0; i < data.size; i++)
+ if (data.values[i] < '0' || data.values[i] > '9')
+ return 0;
+ return 1;
+}
+
+TEST("input buffer read integer once") {
+ str_t text = { .size = 9, .values = "31415 foo" };
+ str_t num = { .size = 5, .values = "31415" };
+ is_handle_t in = IS_WRAP_STRING(text);
+ ib_handle_t first = IB_WRAP(in);
+
+ ib_handle_t second = ib_read_while(first, is_integer_);
+
+ REQUIRE(second.status == KIT_OK);
+ REQUIRE(second.data.size == 5);
+ REQUIRE(AR_EQUAL(num, second.data));
+
+ ib_destroy(second);
+ ib_destroy(first);
+ is_destroy(in);
+}
+
+TEST("input buffer read integer twice") {
+ str_t text = { .size = 6, .values = "314 15" };
+ str_t num_0 = { .size = 3, .values = "314" };
+ str_t num_1 = { .size = 2, .values = "15" };
+ is_handle_t in = IS_WRAP_STRING(text);
+ ib_handle_t first = IB_WRAP(in);
+
+ ib_handle_t second = ib_read_while(first, is_integer_);
+ ib_handle_t third = ib_read(second, 1);
+ ib_handle_t fourth = ib_read_while(third, is_integer_);
+
+ REQUIRE(fourth.status == KIT_OK);
+ REQUIRE(second.data.size == 3);
+ REQUIRE(fourth.data.size == 2);
+ REQUIRE(AR_EQUAL(num_0, second.data));
+ REQUIRE(AR_EQUAL(num_1, fourth.data));
+
+ ib_destroy(first);
+ ib_destroy(second);
+ ib_destroy(third);
+ ib_destroy(fourth);
+ is_destroy(in);
+}
diff --git a/source/tests/input_stream.test.c b/source/tests/input_stream.test.c
new file mode 100644
index 0000000..61c2254
--- /dev/null
+++ b/source/tests/input_stream.test.c
@@ -0,0 +1,23 @@
+#include "../kit/input_stream.h"
+
+#define KIT_TEST_FILE input_stream
+#include "../kit_test/test.h"
+
+TEST("input stream wrap string") {
+ char foo[] = "test";
+ char bar[] = "test";
+
+ str_t foo_ref = { .size = sizeof(foo) - 1, .values = foo };
+ str_t bar_ref = { .size = sizeof(bar) - 1, .values = bar };
+
+ is_handle_t in = IS_WRAP_STRING(foo_ref);
+
+ char buf[4];
+ out_str_t buf_ref = { .size = sizeof(buf), .values = buf };
+
+ REQUIRE(IS_READ(in, buf_ref) == buf_ref.size);
+ REQUIRE(AR_EQUAL(foo_ref, bar_ref));
+ REQUIRE(AR_EQUAL(buf_ref, bar_ref));
+
+ is_destroy(in);
+}
diff --git a/source/tests/lower_bound.test.c b/source/tests/lower_bound.test.c
new file mode 100644
index 0000000..3b62325
--- /dev/null
+++ b/source/tests/lower_bound.test.c
@@ -0,0 +1,178 @@
+#include "../kit/lower_bound.h"
+#include "../kit/array_ref.h"
+
+#define KIT_TEST_FILE lower_bound
+#include "../kit_test/test.h"
+
+static int kit_less_int(int left, int right) {
+ return left < right;
+}
+
+static int kit_less_int_ref(int const *left, int const *right) {
+ return *left < *right;
+}
+
+TEST("lower bound empty") {
+ AR(int) ref = { .size = 0, .values = NULL };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 42, kit_less_int);
+ REQUIRE(index == 0);
+}
+
+TEST("lower bound single left") {
+ int const v[1] = { 42 };
+ AR(int) ref = { .size = 1, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 42, kit_less_int);
+ REQUIRE(index == 0);
+}
+
+TEST("lower bound single right") {
+ int const v[1] = { 42 };
+ AR(int) ref = { .size = 1, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 43, kit_less_int);
+ REQUIRE(index == 1);
+}
+
+TEST("lower bound first of four") {
+ int const v[4] = { 1, 2, 3, 4 };
+ AR(int) ref = { .size = 4, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 1, kit_less_int);
+ REQUIRE(index == 0);
+}
+
+TEST("lower bound second of four") {
+ int const v[4] = { 1, 2, 3, 4 };
+ AR(int) ref = { .size = 4, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 2, kit_less_int);
+ REQUIRE(index == 1);
+}
+
+TEST("lower bound third of four") {
+ int const v[4] = { 1, 2, 3, 4 };
+ AR(int) ref = { .size = 4, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 3, kit_less_int);
+ REQUIRE(index == 2);
+}
+
+TEST("lower bound forth of four") {
+ int const v[4] = { 1, 2, 3, 4 };
+ AR(int) ref = { .size = 4, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 4, kit_less_int);
+ REQUIRE(index == 3);
+}
+
+TEST("lower bound fifth of four") {
+ int const v[4] = { 1, 2, 3, 4 };
+ AR(int) ref = { .size = 4, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 5, kit_less_int);
+ REQUIRE(index == 4);
+}
+
+TEST("lower bound first of five") {
+ int const v[5] = { 1, 2, 3, 4, 5 };
+ AR(int) ref = { .size = 5, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 1, kit_less_int);
+ REQUIRE(index == 0);
+}
+
+TEST("lower bound second of five") {
+ int const v[5] = { 1, 2, 3, 4, 5 };
+ AR(int) ref = { .size = 5, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 2, kit_less_int);
+ REQUIRE(index == 1);
+}
+
+TEST("lower bound third of five") {
+ int const v[5] = { 1, 2, 3, 4, 5 };
+ AR(int) ref = { .size = 5, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 3, kit_less_int);
+ REQUIRE(index == 2);
+}
+
+TEST("lower bound forth of five") {
+ int const v[5] = { 1, 2, 3, 4, 5 };
+ AR(int) ref = { .size = 5, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 4, kit_less_int);
+ REQUIRE(index == 3);
+}
+
+TEST("lower bound fifth of five") {
+ int const v[5] = { 1, 2, 3, 4, 5 };
+ AR(int) ref = { .size = 5, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 5, kit_less_int);
+ REQUIRE(index == 4);
+}
+
+TEST("lower bound sixth of five") {
+ int const v[5] = { 1, 2, 3, 4, 5 };
+ AR(int) ref = { .size = 5, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND(index, ref, 6, kit_less_int);
+ REQUIRE(index == 5);
+}
+
+TEST("lower bound ref first of four") {
+ int const v[4] = { 1, 2, 3, 4 };
+ int const value = 1;
+ AR(int) ref = { .size = 4, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND_REF(index, ref, &value, kit_less_int_ref);
+ REQUIRE(index == 0);
+}
+
+TEST("lower bound ref second of four") {
+ int const v[4] = { 1, 2, 3, 4 };
+ int const value = 2;
+ AR(int) ref = { .size = 4, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND_REF(index, ref, &value, kit_less_int_ref);
+ REQUIRE(index == 1);
+}
+
+TEST("lower bound ref fifth of five") {
+ int const v[5] = { 1, 2, 3, 4, 5 };
+ int const value = 5;
+ AR(int) ref = { .size = 5, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND_REF(index, ref, &value, kit_less_int_ref);
+ REQUIRE(index == 4);
+}
+
+TEST("lower bound ref sixth of five") {
+ int const v[5] = { 1, 2, 3, 4, 5 };
+ int const value = 6;
+ AR(int) ref = { .size = 5, .values = v };
+
+ ptrdiff_t index;
+ LOWER_BOUND_REF(index, ref, &value, kit_less_int_ref);
+ REQUIRE(index == 5);
+}
diff --git a/source/tests/main.test.c b/source/tests/main.test.c
new file mode 100644
index 0000000..2a41d4b
--- /dev/null
+++ b/source/tests/main.test.c
@@ -0,0 +1,9 @@
+#include "../kit_test/bench.h"
+#include "../kit_test/test.h"
+
+int main(int argc, char **argv) {
+ int status = run_tests(argc, argv);
+ if (status == 0)
+ status = run_benchmarks(argc, argv);
+ return status;
+}
diff --git a/source/tests/mersenne_twister_64.test.c b/source/tests/mersenne_twister_64.test.c
new file mode 100644
index 0000000..6bf5b9c
--- /dev/null
+++ b/source/tests/mersenne_twister_64.test.c
@@ -0,0 +1,38 @@
+#include "../kit/mersenne_twister_64.h"
+#include "../kit/secure_random.h"
+
+#define KIT_TEST_FILE mersenne_twister_64
+#include "../kit_test/test.h"
+
+enum { SIZE = 1000 };
+
+TEST("mt64 same seeds") {
+ ptrdiff_t i;
+ uint64_t seed;
+ secure_random(sizeof seed, &seed);
+
+ mt64_state_t foo, bar;
+ mt64_init(&foo, seed);
+ mt64_init(&bar, seed);
+
+ int ok = 1;
+ for (i = 0; i < SIZE; i++)
+ ok = ok && mt64_generate(&foo) == mt64_generate(&bar);
+
+ REQUIRE(ok);
+}
+
+TEST("mt64 different seeds") {
+ ptrdiff_t i;
+
+ mt64_state_t foo, bar;
+ mt64_init(&foo, 42);
+ mt64_init(&bar, 4242424242);
+
+ ptrdiff_t difference_count = 0;
+ for (i = 0; i < SIZE; i++)
+ if (mt64_generate(&foo) != mt64_generate(&bar))
+ difference_count++;
+
+ REQUIRE(difference_count > SIZE / 2);
+}
diff --git a/source/tests/move_back.test.c b/source/tests/move_back.test.c
new file mode 100644
index 0000000..f08d190
--- /dev/null
+++ b/source/tests/move_back.test.c
@@ -0,0 +1,148 @@
+#include "../kit/move_back.h"
+
+#define KIT_TEST_FILE move_back
+#include "../kit_test/test.h"
+
+static int is_equal(int const x, int const y) {
+ return x == y;
+}
+
+static int is_equal_ref(int const *const x, int const y) {
+ return *x == y;
+}
+
+static int is_even(int const x, int const _) {
+ return (x % 2) == 0;
+}
+
+static int is_even_ref(int const *const x, int const _) {
+ return (*x % 2) == 0;
+}
+
+TEST("move back val") {
+ int v[] = { 1, 2, 2, 2, 1, 1 };
+
+ struct {
+ int size;
+ int *values;
+ } ref = { .size = sizeof v / sizeof *v, .values = v };
+
+ MOVE_BACK(ref.size, ref, 2, is_equal);
+
+ REQUIRE_EQ(ref.size, 3);
+ REQUIRE_EQ(v[0], 1);
+ REQUIRE_EQ(v[1], 1);
+ REQUIRE_EQ(v[2], 1);
+}
+
+TEST("move back ref val") {
+ int v[] = { 1, 2, 2, 2, 1, 1 };
+
+ struct {
+ int size;
+ int *values;
+ } ref = { .size = sizeof v / sizeof *v, .values = v };
+
+ MOVE_BACK_REF(ref.size, ref, 2, is_equal_ref);
+
+ REQUIRE(ref.size == 3);
+ REQUIRE(v[0] == 1);
+ REQUIRE(v[1] == 1);
+ REQUIRE(v[2] == 1);
+}
+
+TEST("move back 1") {
+ int v[] = { 1, 2, 3, 4, 5, 6 };
+
+ struct {
+ int size;
+ int *values;
+ } ref = { .size = sizeof v / sizeof *v, .values = v };
+
+ MOVE_BACK(ref.size, ref, 0, is_even);
+
+ REQUIRE(ref.size == 3);
+ REQUIRE(v[0] == 1);
+ REQUIRE(v[1] == 5);
+ REQUIRE(v[2] == 3);
+}
+
+TEST("move back 2") {
+ int v[] = { 2, 4, 6, 1, 3, 5 };
+
+ struct {
+ int size;
+ int *values;
+ } ref = { .size = sizeof v / sizeof *v, .values = v };
+
+ MOVE_BACK(ref.size, ref, 0, is_even);
+
+ REQUIRE(ref.size == 3);
+ REQUIRE(v[0] == 5);
+ REQUIRE(v[1] == 3);
+ REQUIRE(v[2] == 1);
+}
+
+TEST("move back 3") {
+ int v[] = { 1, 3, 5, 2, 4, 6 };
+
+ struct {
+ int size;
+ int *values;
+ } ref = { .size = sizeof v / sizeof *v, .values = v };
+
+ MOVE_BACK(ref.size, ref, 0, is_even);
+
+ REQUIRE(ref.size == 3);
+ REQUIRE(v[0] == 1);
+ REQUIRE(v[1] == 3);
+ REQUIRE(v[2] == 5);
+}
+
+TEST("move back ref 1") {
+ int v[] = { 1, 2, 3, 4, 5, 6 };
+
+ struct {
+ int size;
+ int *values;
+ } ref = { .size = sizeof v / sizeof *v, .values = v };
+
+ MOVE_BACK_REF(ref.size, ref, 0, is_even_ref);
+
+ REQUIRE(ref.size == 3);
+ REQUIRE(v[0] == 1);
+ REQUIRE(v[1] == 5);
+ REQUIRE(v[2] == 3);
+}
+
+TEST("move back ref 2") {
+ int v[] = { 2, 4, 6, 1, 3, 5 };
+
+ struct {
+ int size;
+ int *values;
+ } ref = { .size = sizeof v / sizeof *v, .values = v };
+
+ MOVE_BACK_REF(ref.size, ref, 0, is_even_ref);
+
+ REQUIRE(ref.size == 3);
+ REQUIRE(v[0] == 5);
+ REQUIRE(v[1] == 3);
+ REQUIRE(v[2] == 1);
+}
+
+TEST("move back ref 3") {
+ int v[] = { 1, 3, 5, 2, 4, 6 };
+
+ struct {
+ int size;
+ int *values;
+ } ref = { .size = sizeof v / sizeof *v, .values = v };
+
+ MOVE_BACK_REF(ref.size, ref, 0, is_even_ref);
+
+ REQUIRE(ref.size == 3);
+ REQUIRE(v[0] == 1);
+ REQUIRE(v[1] == 3);
+ REQUIRE(v[2] == 5);
+}
diff --git a/source/tests/mutex.test.c b/source/tests/mutex.test.c
new file mode 100644
index 0000000..ce26991
--- /dev/null
+++ b/source/tests/mutex.test.c
@@ -0,0 +1,79 @@
+#include "../kit/mutex.h"
+#include "../kit/thread.h"
+
+#define KIT_TEST_FILE mutex
+#include "../kit_test/test.h"
+
+enum { SLEEP = 400000000, TICK_COUNT = 200, THREAD_COUNT = 100 };
+
+typedef struct {
+ mtx_t lock;
+ int value;
+} mtx_test_data_t;
+
+static int mtx_test_run(void *data) {
+ int i;
+ mtx_test_data_t *x = (mtx_test_data_t *) data;
+ for (i = 0; i < TICK_COUNT; i++) {
+ mtx_lock(&x->lock);
+
+ x->value += i;
+ thrd_yield();
+ x->value -= i + 42;
+ thrd_yield();
+ x->value += i + 20;
+ thrd_yield();
+ x->value += 22 - i;
+
+ mtx_unlock(&x->lock);
+ }
+ return 0;
+}
+
+TEST("mutex lock") {
+ ptrdiff_t i;
+
+ mtx_test_data_t data;
+ thrd_t pool[THREAD_COUNT];
+ data.value = 42;
+ REQUIRE(mtx_init(&data.lock, mtx_plain) == thrd_success);
+
+ for (i = 0; i < THREAD_COUNT; i++)
+ thrd_create(pool + i, mtx_test_run, &data);
+ for (i = 0; i < THREAD_COUNT; i++) thrd_join(pool[i], NULL);
+
+ mtx_destroy(&data.lock);
+ REQUIRE(data.value == 42);
+}
+
+static int test_lock(void *data) {
+ mtx_t *m = (mtx_t *) data;
+ mtx_lock(m);
+
+ struct timespec sec = { .tv_sec = 0, .tv_nsec = SLEEP };
+ thrd_sleep(&sec, NULL);
+
+ mtx_unlock(m);
+
+ return 0;
+}
+
+TEST("mutex try lock") {
+ mtx_t m;
+ REQUIRE(mtx_init(&m, mtx_plain) == thrd_success);
+
+ thrd_t t;
+ REQUIRE(thrd_create(&t, test_lock, &m) == thrd_success);
+
+ struct timespec sec = { .tv_sec = 0, .tv_nsec = SLEEP / 2 };
+ REQUIRE(thrd_sleep(&sec, NULL) == thrd_success);
+
+ REQUIRE(mtx_trylock(&m) == thrd_busy);
+
+ REQUIRE(thrd_join(t, NULL) == thrd_success);
+
+ REQUIRE(mtx_trylock(&m) == thrd_success);
+ REQUIRE(mtx_unlock(&m) == thrd_success);
+
+ mtx_destroy(&m);
+}
diff --git a/source/tests/secure_random.test.c b/source/tests/secure_random.test.c
new file mode 100644
index 0000000..22bdd37
--- /dev/null
+++ b/source/tests/secure_random.test.c
@@ -0,0 +1,24 @@
+#include "../kit/secure_random.h"
+#include <string.h>
+
+#define KIT_TEST_FILE secure_random
+#include "../kit_test/test.h"
+
+TEST("secure random") {
+ int i, j;
+
+ int v[20];
+ memset(v, 0, sizeof v);
+
+ secure_random(40, v);
+ secure_random(40, v + 10);
+
+ int repeats = 0;
+
+ for (i = 1; i < sizeof v / sizeof *v; i++)
+ for (j = 0; j < i; j++)
+ if (v[i] == v[j])
+ repeats++;
+
+ REQUIRE(repeats == 0);
+}
diff --git a/source/tests/sha256.test.c b/source/tests/sha256.test.c
new file mode 100644
index 0000000..0f751df
--- /dev/null
+++ b/source/tests/sha256.test.c
@@ -0,0 +1,42 @@
+#include "../kit/sha256.h"
+#include "../kit/array_ref.h"
+
+#define KIT_TEST_FILE sha256_64
+#include "../kit_test/test.h"
+
+#include <string.h>
+
+TEST("sha256") {
+ uint8_t text1[] = "abc";
+ uint8_t text2[] =
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+
+ uint8_t text3[1000000];
+ memset(text3, 'a', sizeof text3);
+
+ uint8_t hash1[] = {
+ 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40,
+ 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17,
+ 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
+ };
+ uint8_t hash2[] = {
+ 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26,
+ 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff,
+ 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
+ };
+ uint8_t hash3[] = {
+ 0xcd, 0xc7, 0x6e, 0x5c, 0x99, 0x14, 0xfb, 0x92, 0x81, 0xa1, 0xc7,
+ 0xe2, 0x84, 0xd7, 0x3e, 0x67, 0xf1, 0x80, 0x9a, 0x48, 0xa4, 0x97,
+ 0x20, 0x0e, 0x04, 0x6d, 0x39, 0xcc, 0xc7, 0x11, 0x2c, 0xd0
+ };
+
+ REQUIRE(ar_equal_bytes(1, SHA256_BLOCK_SIZE,
+ sha256((sizeof text1) - 1, text1).v, 1,
+ SHA256_BLOCK_SIZE, hash1));
+ REQUIRE(ar_equal_bytes(1, SHA256_BLOCK_SIZE,
+ sha256((sizeof text2) - 1, text2).v, 1,
+ SHA256_BLOCK_SIZE, hash2));
+ REQUIRE(ar_equal_bytes(1, SHA256_BLOCK_SIZE,
+ sha256(sizeof text3, text3).v, 1,
+ SHA256_BLOCK_SIZE, hash3));
+}
diff --git a/source/tests/signals.cpp b/source/tests/signals.cpp
new file mode 100644
index 0000000..0f6d77a
--- /dev/null
+++ b/source/tests/signals.cpp
@@ -0,0 +1,29 @@
+#include "../kit_test/test.h"
+
+#include <cstdlib>
+#include <stdexcept>
+
+/* FIXME
+ * MSVC tests fail in GitHub Actions.
+ */
+
+TEST("c++ exception") {
+ throw std::exception();
+}
+
+TEST("abort") {
+ abort();
+}
+
+TEST("invalid access") {
+ *(volatile int *) nullptr = 42;
+}
+
+int main(int argc, char **argv) {
+#ifndef _MSC_VER
+ if (run_tests(argc, argv) != 1)
+ return 1;
+#endif
+
+ return 0;
+}
diff --git a/source/tests/string_ref.test.c b/source/tests/string_ref.test.c
new file mode 100644
index 0000000..2891f16
--- /dev/null
+++ b/source/tests/string_ref.test.c
@@ -0,0 +1,23 @@
+#include "../kit/string_ref.h"
+
+#define KIT_TEST_FILE string_ref
+#include "../kit_test/test.h"
+
+TEST("static string wrap") {
+ str_t ref = SZ("foo bar");
+
+ REQUIRE(ref.size == 7);
+ REQUIRE(ref.values[0] == 'f');
+ REQUIRE(ref.values[1] == 'o');
+ REQUIRE(ref.values[2] == 'o');
+ REQUIRE(ref.values[3] == ' ');
+ REQUIRE(ref.values[4] == 'b');
+ REQUIRE(ref.values[5] == 'a');
+ REQUIRE(ref.values[6] == 'r');
+}
+
+TEST("string literal") {
+ str_t foo = SZ("foo");
+ str_t bar = foo;
+ (void) bar;
+}
diff --git a/source/tests/test_duration.test.c b/source/tests/test_duration.test.c
new file mode 100644
index 0000000..27384b4
--- /dev/null
+++ b/source/tests/test_duration.test.c
@@ -0,0 +1,19 @@
+#define KIT_TEST_FILE test_duration
+#include "../kit_test/test.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+__declspec(dllimport) void __stdcall Sleep(unsigned long timeout);
+static void kit_sleep(int ms) {
+ Sleep(ms);
+}
+#else
+# include <stdlib.h>
+# include <unistd.h>
+static void kit_sleep(int ms) {
+ usleep(ms * 1000);
+}
+#endif
+
+TEST("test duration") {
+ kit_sleep(100);
+}
diff --git a/source/tests/thread.test.c b/source/tests/thread.test.c
new file mode 100644
index 0000000..50f7c04
--- /dev/null
+++ b/source/tests/thread.test.c
@@ -0,0 +1,91 @@
+#include "../kit/thread.h"
+
+#define KIT_TEST_FILE thread
+#include "../kit_test/test.h"
+
+static int test_nothing(void *_) {
+ return 0;
+}
+
+static int test_run(void *data) {
+ int *n = (int *) data;
+ return *n + 20;
+}
+
+static int test_exit(void *data) {
+ int *n = (int *) data;
+
+ *n = 1;
+ thrd_exit(3);
+ *n = 2;
+ return 4;
+}
+
+static int test_yield(void *data) {
+ thrd_yield();
+ return 0;
+}
+
+static int test_sleep(void *data) {
+ struct timespec t = { .tv_sec = 0, .tv_nsec = 10000000 };
+ thrd_sleep(&t, NULL);
+ return 0;
+}
+
+TEST("thread run") {
+ thrd_t t;
+ int data = 22;
+ int result;
+ REQUIRE(thrd_create(&t, test_run, &data) == thrd_success);
+ REQUIRE(thrd_join(t, &result) == thrd_success);
+ REQUIRE(result == 42);
+}
+
+TEST("thread stack size") {
+ thrd_t foo;
+ REQUIRE(thrd_create_with_stack(&foo, test_nothing, NULL, 30000) ==
+ thrd_success);
+ REQUIRE(thrd_join(foo, NULL) == thrd_success);
+}
+
+TEST("thread equal") {
+ thrd_t foo, bar;
+ REQUIRE(thrd_create(&foo, test_nothing, NULL) == thrd_success);
+ REQUIRE(thrd_create(&bar, test_nothing, NULL) == thrd_success);
+ REQUIRE(thrd_equal(foo, foo));
+ REQUIRE(!thrd_equal(foo, bar));
+ REQUIRE(!thrd_equal(foo, thrd_current()));
+ REQUIRE(thrd_join(foo, NULL) == thrd_success);
+ REQUIRE(thrd_join(bar, NULL) == thrd_success);
+}
+
+TEST("thread exit") {
+ thrd_t foo;
+ int data;
+ int result;
+ REQUIRE(thrd_create(&foo, test_exit, &data) == thrd_success);
+ REQUIRE(thrd_join(foo, &result) == thrd_success);
+ REQUIRE(data == 1);
+ REQUIRE(result == 3);
+}
+
+TEST("thread yield") {
+ thrd_t foo;
+ REQUIRE(thrd_create(&foo, test_yield, NULL) == thrd_success);
+ REQUIRE(thrd_join(foo, NULL) == thrd_success);
+}
+
+TEST("thread sleep") {
+ thrd_t foo;
+ REQUIRE(thrd_create(&foo, test_sleep, NULL) == thrd_success);
+ REQUIRE(thrd_join(foo, NULL) == thrd_success);
+}
+
+TEST("thread detach") {
+ thrd_t foo;
+ REQUIRE(thrd_create(&foo, test_nothing, NULL) == thrd_success);
+ REQUIRE(thrd_detach(foo) == thrd_success);
+
+ struct timespec t = { .tv_sec = 0, .tv_nsec = 10000000 };
+ thrd_sleep(&t, NULL);
+}
diff --git a/source/tests/too_many_assertions.c b/source/tests/too_many_assertions.c
new file mode 100644
index 0000000..662207d
--- /dev/null
+++ b/source/tests/too_many_assertions.c
@@ -0,0 +1,13 @@
+#include "../kit_test/test.h"
+
+TEST("foo") {
+ int i;
+ for (i = 0; i <= KIT_TEST_ASSERTIONS_LIMIT; i++) REQUIRE(1);
+}
+
+int main(int argc, char **argv) {
+ if (run_tests(argc, argv) != 1)
+ return 1;
+
+ return 0;
+}
diff --git a/source/tests/too_many_tests.c b/source/tests/too_many_tests.c
new file mode 100644
index 0000000..d4842e4
--- /dev/null
+++ b/source/tests/too_many_tests.c
@@ -0,0 +1,14 @@
+#include "../kit_test/test.h"
+
+void bar(int index, kit_test_report_fn report) { }
+
+int main(int argc, char **argv) {
+ int i;
+ for (i = 0; i <= KIT_TESTS_SIZE_LIMIT; i++)
+ test_register("foo", __FILE__, bar);
+
+ if (run_tests(argc, argv) != 1)
+ return 1;
+
+ return 0;
+}