From fdf921c0adff5317e426e798f8a716ca97046383 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Fri, 12 Jan 2024 16:15:10 +0100 Subject: Refactor; defer with fblocks --- build_and_test.sh | 7 + source/kit/defer.h | 39 ++ source/kit/kit_test.h | 1113 ------------------------------- source/kit/test.h | 1113 +++++++++++++++++++++++++++++++ source/tests/_exe.c | 1 + source/tests/array_ref.test.c | 2 +- source/tests/async_function.test.c | 2 +- source/tests/atomic.test.c | 2 +- source/tests/bench.test.c | 2 +- source/tests/bigint.test.c | 2 +- source/tests/condition_variable.test.c | 2 +- source/tests/defer.test.c | 37 + source/tests/duration.test.c | 2 +- source/tests/dynamic_array.test.c | 2 +- source/tests/file.test.c | 2 +- source/tests/http1.test.c | 2 +- source/tests/input_buffer.test.c | 2 +- source/tests/input_stream.test.c | 2 +- source/tests/lower_bound.test.c | 2 +- source/tests/main.test.c | 2 +- source/tests/mersenne_twister_64.test.c | 2 +- source/tests/move_back.test.c | 2 +- source/tests/mutex.test.c | 2 +- source/tests/secure_random.test.c | 2 +- source/tests/sha256.test.c | 2 +- source/tests/string_ref.test.c | 2 +- source/tests/test_cpp.cpp | 2 +- source/tests/test_signals.cpp | 2 +- source/tests/test_too_many_assertions.c | 2 +- source/tests/test_too_many_tests.c | 2 +- source/tests/thread.test.c | 2 +- source/tests/xml.test.c | 2 +- 32 files changed, 1223 insertions(+), 1139 deletions(-) create mode 100644 source/kit/defer.h delete mode 100644 source/kit/kit_test.h create mode 100644 source/kit/test.h create mode 100644 source/tests/defer.test.c diff --git a/build_and_test.sh b/build_and_test.sh index f677961..b3a3f2d 100644 --- a/build_and_test.sh +++ b/build_and_test.sh @@ -132,10 +132,17 @@ else fi fi +if [ "$COMPILE" = "clang" ] || [ "$OS" = "macOS" ]; then + FLAGS="-fblocks ${FLAGS}" +fi + if [ ! -d "$FOLDER" ]; then mkdir "$FOLDER" fi +echo "" +echo "Compiler options: ${FLAGS}" +echo "Link options: ${LINK_FLAGS}" echo "" # diff --git a/source/kit/defer.h b/source/kit/defer.h new file mode 100644 index 0000000..bb78b69 --- /dev/null +++ b/source/kit/defer.h @@ -0,0 +1,39 @@ +// Requres GCC or Clang with `-fblocks` option available. +// + +#ifndef KIT_DEFER_H +#define KIT_DEFER_H + +#if !defined(__clang__) && !defined(__APPLE__) +# error C blocks support required +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wunknown-pragmas" +#endif + +static void kit_defer_cleanup_(void (^*b)(void)) { (*b)(); } + +#define kit_defer_merge_(a,b) a##b +#define kit_defer_varname_(a) \ + kit_defer_merge_(kit_defer_scopevar_,a) +#define defer \ + __attribute__((unused, cleanup(kit_defer_cleanup_))) \ + void (^kit_defer_varname_(__COUNTER__))(void) = ^ +#define defer_ref __block + +#if defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic pop +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/kit/kit_test.h b/source/kit/kit_test.h deleted file mode 100644 index 176d40c..0000000 --- a/source/kit/kit_test.h +++ /dev/null @@ -1,1113 +0,0 @@ -// ================================================================ -// -// kit_test.h -// https://guattari.tech/git/kit -// -// Header-only unit-testing and microbenchmarks framework for C. -// -// -// - Define a unique KIT_TEST_FILE for each file to avoid name -// collisions. -// -// - Define KIT_TEST_IMPLEMENTATION to include the implementation. -// -// -// Optional settings -// -// - KIT_TESTS_SIZE_LIMIT -// - KIT_TEST_ASSERTIONS_LIMIT -// - KIT_BENCHS_SIZE_LIMIT -// - KIT_BENCH_MAX_REPEATS -// - KIT_BENCH_MAX_CYCLES -// - KIT_BENCH_REPEATS_DEFAULT_1 -// - KIT_BENCH_REPEATS_DEFAULT_2 -// -// -// Usage example -// -// // foo.test.c -// #define KIT_TEST_FILE foo // This is required if you want to use -// // multiple test files. -// #include "kit_test.h" -// TEST("foo") { -// REQUIRE(1); -// REQUIRE_EQ(2 + 2, 4); -// } -// -// // main.c -// #include "kit_test.h" -// int main(int argc, char **argv) { -// return run_tests(argc, argv); -// } -// -// ================================================================ -// -// The MIT License -// -// Copyright (c) 2022-2023 Mitya Selivanov -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, copy, -// modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// -// ================================================================ - -#ifndef KIT_TEST_H -#define KIT_TEST_H - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif - -#include -#include - -#ifndef KIT_TEST_FILE -# define KIT_TEST_FILE kit_test -#endif - -#ifndef KIT_TESTS_SIZE_LIMIT -# define KIT_TESTS_SIZE_LIMIT 0x1000 -#endif - -#ifndef KIT_TEST_ASSERTIONS_LIMIT -# define KIT_TEST_ASSERTIONS_LIMIT 0x50 -#endif - -#ifndef KIT_BENCHS_SIZE_LIMIT -# define KIT_BENCHS_SIZE_LIMIT 200 -#endif - -#ifndef KIT_BENCH_MAX_REPEATS -# define KIT_BENCH_MAX_REPEATS 4000 -#endif - -#ifndef KIT_BENCH_MAX_CYCLES -# define KIT_BENCH_MAX_CYCLES 40 -#endif - -#ifndef KIT_BENCH_REPEATS_DEFAULT_1 -# define KIT_BENCH_REPEATS_DEFAULT_1 40 -#endif - -#ifndef KIT_BENCH_REPEATS_DEFAULT_2 -# define KIT_BENCH_REPEATS_DEFAULT_2 400 -#endif - -typedef void (*kit_test_report_fn)(int test_index, int line, - int64_t value, int64_t expected); -typedef void (*kit_test_run_fn)( - int kit_test_index_, kit_test_report_fn kit_test_report_fn_); - -typedef struct { - char const *test_name; - char const *test_file; - kit_test_run_fn test_fn; - int assertions; - int line[KIT_TEST_ASSERTIONS_LIMIT]; - int status[KIT_TEST_ASSERTIONS_LIMIT]; - int64_t value[KIT_TEST_ASSERTIONS_LIMIT]; - int64_t expected[KIT_TEST_ASSERTIONS_LIMIT]; - int signal; -} kit_test_case_t; - -typedef struct { - int size; - kit_test_case_t v[KIT_TESTS_SIZE_LIMIT]; -} kit_tests_list_t; - -extern kit_tests_list_t kit_tests_list; - -#define KIT_TEST_CONCAT4_(a, b, c, d) a##b##c##d -#define KIT_TEST_CONCAT3_(a, b, c) KIT_TEST_CONCAT4_(a, b, _, c) - -#ifdef __cplusplus -# define KIT_TEST_ON_START_(f) \ - static void f(void); \ - static int KIT_TEST_CONCAT3_(_kit_test_init_, __LINE__, \ - f) = (f(), 0); \ - static void f(void) -#else -# ifdef _MSC_VER -# pragma section(".CRT$XCU", read) -# define KIT_TEST_ON_START_2_(f, p) \ - static void f(void); \ - __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ - __pragma(comment(linker, "/include:" p #f "_")) static void f( \ - void) -# ifdef _WIN64 -# define KIT_TEST_ON_START_(f) KIT_TEST_ON_START_2_(f, "") -# else -# define KIT_TEST_ON_START_(f) KIT_TEST_ON_START_2_(f, "_") -# endif -# else -# define KIT_TEST_ON_START_(f) \ - static void f(void) __attribute__((constructor)); \ - static void f(void) -# endif -#endif - -void kit_test_register(char const *name, char const *file, - kit_test_run_fn fn); - -#define KIT_TEST(name) \ - static void KIT_TEST_CONCAT3_(kit_test_run_, __LINE__, \ - KIT_TEST_FILE)(int, \ - kit_test_report_fn); \ - KIT_TEST_ON_START_( \ - KIT_TEST_CONCAT3_(kit_test_case_, __LINE__, KIT_TEST_FILE)) { \ - kit_test_register( \ - name, __FILE__, \ - KIT_TEST_CONCAT3_(kit_test_run_, __LINE__, KIT_TEST_FILE)); \ - } \ - static void KIT_TEST_CONCAT3_(kit_test_run_, __LINE__, \ - KIT_TEST_FILE)( \ - int kit_test_index_, kit_test_report_fn kit_test_report_fn_) - -#define KIT_REQUIRE(...) \ - kit_test_report_fn_(kit_test_index_, __LINE__, (__VA_ARGS__), 1) - -#define KIT_REQUIRE_EQ(...) \ - kit_test_report_fn_(kit_test_index_, __LINE__, __VA_ARGS__) - -int kit_run_tests(int argc, char **argv); - -typedef void (*kit_bench_set_repeats_limit_fn)(int bench_index, - int repeats_limit); -typedef int (*kit_bench_loop_fn)(int bench_index); -typedef void (*kit_bench_begin_fn)(int bench_index); -typedef void (*kit_bench_end_fn)(int bench_index); - -typedef void (*kit_bench_run_fn)( - int kit_bench_index_, - kit_bench_set_repeats_limit_fn kit_bench_set_repeats_limit_, - kit_bench_loop_fn kit_bench_loop_, - kit_bench_begin_fn kit_bench_begin_, - kit_bench_end_fn kit_bench_end_); - -typedef struct { - char const *bench_name; - char const *bench_file; - kit_bench_run_fn bench_fn; - int64_t sec[KIT_BENCH_MAX_REPEATS]; - int32_t nsec[KIT_BENCH_MAX_REPEATS]; - int64_t duration_nsec[KIT_BENCH_MAX_REPEATS]; - int64_t duration_sorted_nsec[KIT_BENCH_MAX_REPEATS]; - int repeats; - int cycles_size; - int cycles[KIT_BENCH_MAX_CYCLES]; - int cycle; - int signal; - int ready; -} kit_benchmark_t; - -typedef struct { - int size; - kit_benchmark_t v[KIT_BENCHS_SIZE_LIMIT]; -} kit_benchs_list_t; - -extern kit_benchs_list_t kit_benchs_list; - -void kit_bench_register(char const *name, char const *file, - kit_bench_run_fn fn); - -#define KIT_BENCHMARK(name) \ - static void KIT_TEST_CONCAT3_(kit_bench_run_, __LINE__, \ - KIT_TEST_FILE)( \ - int, kit_bench_set_repeats_limit_fn, kit_bench_loop_fn, \ - kit_bench_begin_fn, kit_bench_end_fn); \ - KIT_TEST_ON_START_( \ - KIT_TEST_CONCAT3_(kit_benchmark_, __LINE__, KIT_TEST_FILE)) { \ - kit_bench_register( \ - name, __FILE__, \ - KIT_TEST_CONCAT3_(kit_bench_run_, __LINE__, KIT_TEST_FILE)); \ - } \ - static void KIT_TEST_CONCAT3_(kit_bench_run_, __LINE__, \ - KIT_TEST_FILE)( \ - int kit_bench_index_, \ - kit_bench_set_repeats_limit_fn kit_bench_set_repeats_limit_, \ - kit_bench_loop_fn kit_bench_loop_, \ - kit_bench_begin_fn kit_bench_begin_, \ - kit_bench_end_fn kit_bench_end_) - -#define KIT_BENCHMARK_REPEAT(repeats_limit_) \ - kit_bench_set_repeats_limit_(kit_bench_index_, repeats_limit_) - -#define KIT_BENCHMARK_BEGIN \ - while (kit_bench_loop_(kit_bench_index_)) { \ - kit_bench_begin_(kit_bench_index_); \ - { - -#define KIT_BENCHMARK_END \ - } \ - kit_bench_end_(kit_bench_index_); \ - } - -/* FIXME - */ -#define KIT_DO_NOT_OPTIMIZE(x) \ - do { \ - volatile void *bench_ptr_ = &(x); \ - (void) bench_ptr_; \ - } while (0) - -int kit_run_benchmarks(int argc, char **argv); - -#ifndef KIT_DISABLE_SHORT_NAMES -# define TEST KIT_TEST -# define REQUIRE KIT_REQUIRE -# define REQUIRE_EQ KIT_REQUIRE_EQ -# define BENCHMARK KIT_BENCHMARK -# define BENCHMARK_REPEAT KIT_BENCHMARK_REPEAT -# define BENCHMARK_BEGIN KIT_BENCHMARK_BEGIN -# define BENCHMARK_END KIT_BENCHMARK_END -# define DO_NOT_OPTIMIZE KIT_DO_NOT_OPTIMIZE - -# define test_register kit_test_register -# define run_tests kit_run_tests -# define bench_register kit_bench_register -# define run_benchmarks kit_run_benchmarks -#endif - -#ifdef __cplusplus -} -#endif - -#endif - -#if defined(KIT_TEST_IMPLEMENTATION) && !defined(KIT_TEST_H_IMPL) -#define KIT_TEST_H_IMPL - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef KIT_TIME_H -# define KIT_TIME_H - -# include - -# ifndef TIME_UTC -# define TIME_UTC 1 -# endif - -# ifdef KIT_REQUIRE_TIMESPEC_GET -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN 1 -# endif -# include - -# define KIT_TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS \ - 116444736000000000ull -# define KIT_TIMESPEC_IMPL_TICKS_PER_SECONDS 10000000ull - -static int timespec_get(struct timespec *ts, int base) { - if (ts == NULL || base != TIME_UTC) - return 0; - - FILETIME ft; - ULARGE_INTEGER date; - LONGLONG ticks; - - GetSystemTimeAsFileTime(&ft); - date.HighPart = ft.dwHighDateTime; - date.LowPart = ft.dwLowDateTime; - ticks = (LONGLONG) (date.QuadPart - - KIT_TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS); - ts->tv_sec = ticks / KIT_TIMESPEC_IMPL_TICKS_PER_SECONDS; - ts->tv_nsec = (ticks % KIT_TIMESPEC_IMPL_TICKS_PER_SECONDS) * 100; - - return base; -} -# endif - -#endif - -#include -#include -#include -#include -#include - -enum { - kit_white_, - kit_blue_, - kit_light_, - kit_yellow_, - kit_red_, - kit_green_ -}; - -static char const *kit_color_codes_[] = { "\x1b[38m", "\x1b[34m", - "\x1b[37m", "\x1b[33m", - "\x1b[31m", "\x1b[32m" }; - -static int kit_print_color_(int c) { - return printf("%s", kit_color_codes_[c]); -} - -static int kit_signums_[] = { SIGINT, SIGILL, SIGABRT, - SIGFPE, SIGSEGV, SIGTERM }; - -static char const *kit_signames_[64]; - -static void kit_signames_init_(void) { - memset(kit_signames_, 0, sizeof kit_signames_); - kit_signames_[SIGINT] = "Interactive attention signal"; - kit_signames_[SIGILL] = "Illegal instruction"; - kit_signames_[SIGABRT] = "Abnormal termination"; - kit_signames_[SIGFPE] = "Erroneous arithmetic operation"; - kit_signames_[SIGSEGV] = "Invalid access to storage"; - kit_signames_[SIGTERM] = "Termination request"; -} - -kit_tests_list_t kit_tests_list = { 0 }; - -static void kit_report_(int i, int line, int64_t value, - int64_t expected) { - int n = kit_tests_list.v[i].assertions++; - - if (n >= KIT_TEST_ASSERTIONS_LIMIT) - return; - - kit_tests_list.v[i].line[n] = line; - kit_tests_list.v[i].status[n] = value == expected; - kit_tests_list.v[i].value[n] = value; - kit_tests_list.v[i].expected[n] = expected; -} - -static int64_t ns_to_ms(int64_t ns) { - return (ns + 500000) / 1000000; -} - -static int64_t sec_to_ms(int64_t sec) { - return 1000 * sec; -} - -void kit_test_register(char const *name, char const *file, - kit_test_run_fn fn) { - int n = kit_tests_list.size++; - if (n < KIT_TESTS_SIZE_LIMIT) { - kit_tests_list.v[n].test_fn = fn; - kit_tests_list.v[n].test_name = name; - kit_tests_list.v[n].test_file = file; - kit_tests_list.v[n].assertions = 0; - } -} - -static jmp_buf kit_test_restore_execution; - -static void kit_test_handle_signal(int signum) { - longjmp(kit_test_restore_execution, signum); -} - -static void kit_test_setup_signals() { - for (int i = 0; i < sizeof kit_signums_ / sizeof *kit_signums_; i++) - signal(kit_signums_[i], kit_test_handle_signal); -} - -static int kit_run_test_(volatile int i) { - int signum = setjmp(kit_test_restore_execution); - - if (signum != 0) { - kit_tests_list.v[i].signal = signum; - return 0; - } - - kit_tests_list.v[i].test_fn(i, kit_report_); - return 1; -} - -int kit_run_tests(int argc, char **argv) { - kit_signames_init_(); - - int success_count = 0; - int fail_assertion_count = 0; - int total_assertion_count = 0; - int status = 0; - int quiet = 0; - int no_color = 0; - int line_width = 20; - int carriage_return = 1; - - int i, j; - - char const *specific_test = NULL; - - kit_test_setup_signals(); - - for (i = 0; i < argc; i++) - if (strcmp("--no-term-color", argv[i]) == 0) - no_color = 1; - else if (strcmp("--no-carriage-return", argv[i]) == 0) - carriage_return = 0; - else if (strcmp("--quiet", argv[i]) == 0) - quiet = 1; - else if (strcmp("--match", argv[i]) == 0) - specific_test = argv[++i]; - - quiet && (no_color = 1); - - if (specific_test != NULL) { - no_color || kit_print_color_(kit_light_); - quiet || printf("Run tests matching "); - no_color || kit_print_color_(kit_white_); - quiet || printf("*%s*", specific_test); - no_color || kit_print_color_(kit_light_); - quiet || printf("\n\n"); - } - - char const *file = NULL; - ptrdiff_t file_root = -1; - int tests_total = 0; - - for (i = 0; i < kit_tests_list.size && i < KIT_TESTS_SIZE_LIMIT; - i++) { - if (specific_test != NULL && - strstr(kit_tests_list.v[i].test_name, specific_test) == NULL) - continue; - tests_total++; - int l = 2 + (int) strlen(kit_tests_list.v[i].test_name); - if (line_width < l) - line_width = l; - } - - if (tests_total > 0) { - char const *s = kit_tests_list.v[0].test_file; - - for (j = 1; j < kit_tests_list.size && j < KIT_TESTS_SIZE_LIMIT; - j++) { - if (specific_test != NULL && - strstr(kit_tests_list.v[j].test_name, specific_test) == - NULL) - continue; - if (strcmp(s, kit_tests_list.v[j].test_file) == 0) - continue; - int k = 0; - for (; - s[k] != '\0' && kit_tests_list.v[j].test_file[k] != '\0' && - s[k] == kit_tests_list.v[j].test_file[k]; - k++) { } - if (file_root == -1 || file_root > k) - file_root = k; - } - - if (file_root == -1) { - for (i = 0; s[i] != '\0'; i++) - if (s[i] == '/' || s[i] == '\\') - file_root = i + 1; - } - } - - for (i = 0; i < kit_tests_list.size && i < KIT_TESTS_SIZE_LIMIT; - i++) { - if (specific_test != NULL && - strstr(kit_tests_list.v[i].test_name, specific_test) == NULL) - continue; - if (file == NULL || - strcmp(file, kit_tests_list.v[i].test_file) != 0) { - if (file != NULL) - quiet || printf("\n"); - file = kit_tests_list.v[i].test_file; - no_color || kit_print_color_(kit_blue_); - quiet || printf("* "); - no_color || kit_print_color_(kit_white_); - quiet || printf("%s\n", file + file_root); - } - - !carriage_return || no_color || kit_print_color_(kit_yellow_); - carriage_return || no_color || kit_print_color_(kit_light_); - quiet || printf("` %s ", kit_tests_list.v[i].test_name); - !carriage_return || quiet || printf("\r"); - quiet || fflush(stdout); - - struct timespec begin, end; - timespec_get(&begin, TIME_UTC); - - int test_status = kit_run_test_(i); - - timespec_get(&end, TIME_UTC); - int duration = (int) (ns_to_ms(end.tv_nsec - begin.tv_nsec) + - sec_to_ms(end.tv_sec - begin.tv_sec)); - - for (j = 0; j < kit_tests_list.v[i].assertions && - j < KIT_TEST_ASSERTIONS_LIMIT; - j++) - if (kit_tests_list.v[i].status[j] == 0) { - fail_assertion_count++; - test_status = 0; - } - - if (kit_tests_list.v[i].assertions > KIT_TEST_ASSERTIONS_LIMIT) - test_status = 0; - - total_assertion_count += kit_tests_list.v[i].assertions; - - !carriage_return || no_color || kit_print_color_(kit_light_); - !carriage_return || quiet || - printf("` %s ", kit_tests_list.v[i].test_name); - - int l = (int) strlen(kit_tests_list.v[i].test_name); - quiet || printf("%*c", line_width - l, ' '); - - if (test_status == 0) { - no_color || kit_print_color_(kit_red_); - quiet || printf("FAIL"); - no_color || kit_print_color_(kit_light_); - duration == 0 || quiet || printf(" %d ms", duration); - quiet || printf("\n"); - status = 1; - } else { - no_color || kit_print_color_(kit_green_); - quiet || printf("OK"); - no_color || kit_print_color_(kit_light_); - duration == 0 || quiet || printf(" %d ms", duration); - quiet || printf("\n"); - success_count++; - } - - quiet || fflush(stdout); - } - - no_color || kit_print_color_(kit_white_); - quiet || printf("\n%d of %d tests passed.\n", success_count, - tests_total); - quiet || printf("%d of %d assertions passed.\n\n", - total_assertion_count - fail_assertion_count, - total_assertion_count); - - no_color || kit_print_color_(kit_light_); - - if (!quiet && status != 0) { - int have_kit_report_s = 0; - - for (i = 0; i < kit_tests_list.size && i < KIT_TESTS_SIZE_LIMIT; - i++) { - if (specific_test != NULL && - strstr(kit_tests_list.v[i].test_name, specific_test) == - NULL) - continue; - if (kit_tests_list.v[i].signal != 0) { - int signum = kit_tests_list.v[i].signal; - if (signum >= 0 && - signum < sizeof kit_signames_ / sizeof *kit_signames_ && - kit_signames_[signum] != NULL) { - no_color || kit_print_color_(kit_light_); - printf("Signal \"%s\" (%d) for \"", kit_signames_[signum], - signum); - no_color || kit_print_color_(kit_white_); - printf("%s", kit_tests_list.v[i].test_name); - no_color || kit_print_color_(kit_light_); - printf("\" in \""); - no_color || kit_print_color_(kit_white_); - - printf("%s", kit_tests_list.v[i].test_file + file_root); - no_color || kit_print_color_(kit_light_); - printf("\"!\n"); - } else { - no_color || kit_print_color_(kit_light_); - printf("Unknown signal (%d) for \"", signum); - no_color || kit_print_color_(kit_white_); - printf("%s", kit_tests_list.v[i].test_name); - no_color || kit_print_color_(kit_light_); - printf("\" in \""); - no_color || kit_print_color_(kit_white_); - - printf("%s", kit_tests_list.v[i].test_file + file_root); - no_color || kit_print_color_(kit_light_); - printf("\"!\n"); - } - have_kit_report_s = 1; - } - if (kit_tests_list.v[i].assertions > - KIT_TEST_ASSERTIONS_LIMIT) { - no_color || kit_print_color_(kit_light_); - printf("Too many assertions for \""); - no_color || kit_print_color_(kit_white_); - printf("%s", kit_tests_list.v[i].test_name); - no_color || kit_print_color_(kit_light_); - printf("\" in \""); - no_color || kit_print_color_(kit_white_); - - printf("%s", kit_tests_list.v[i].test_file + file_root); - no_color || kit_print_color_(kit_light_); - printf("\"!\n"); - have_kit_report_s = 1; - } - } - - have_kit_report_s &&printf("\n"); - } - - if (!quiet && status != 0) { - for (i = 0; i < kit_tests_list.size && i < KIT_TESTS_SIZE_LIMIT; - i++) { - if (specific_test != NULL && - strstr(kit_tests_list.v[i].test_name, specific_test) == - NULL) - continue; - - if (kit_tests_list.v[i].assertions <= KIT_TEST_ASSERTIONS_LIMIT) - for (j = 0; j < kit_tests_list.v[i].assertions; j++) - if (!kit_tests_list.v[i].status[j]) { - no_color || kit_print_color_(kit_light_); - printf("Assertion on line "); - no_color || kit_print_color_(kit_white_); - printf("%d", kit_tests_list.v[i].line[j]); - no_color || kit_print_color_(kit_light_); - printf(" in \""); - no_color || kit_print_color_(kit_white_); - printf("%s", kit_tests_list.v[i].test_file + file_root); - no_color || kit_print_color_(kit_light_); - printf("\" failed.\n"); - no_color || kit_print_color_(kit_red_); - printf(" -> "); - no_color || kit_print_color_(kit_light_); - printf("Got wrong value "); - no_color || kit_print_color_(kit_white_); - printf("%10lld", - (long long) kit_tests_list.v[i].value[j]); - no_color || kit_print_color_(kit_light_); - printf(" ("); - no_color || kit_print_color_(kit_white_); - printf("0x%08llx", - (unsigned long long) kit_tests_list.v[i].value[j]); - no_color || kit_print_color_(kit_light_); - printf(")\n"); - no_color || kit_print_color_(kit_green_); - printf(" -> "); - no_color || kit_print_color_(kit_light_); - printf("Expected value "); - no_color || kit_print_color_(kit_white_); - printf("%10lld", - (long long) kit_tests_list.v[i].expected[j]); - no_color || kit_print_color_(kit_light_); - printf(" ("); - no_color || kit_print_color_(kit_white_); - printf( - "0x%08llx", - (unsigned long long) kit_tests_list.v[i].expected[j]); - no_color || kit_print_color_(kit_light_); - printf(")\n\n"); - } - } - } - - if (kit_tests_list.size > KIT_TESTS_SIZE_LIMIT) { - no_color || kit_print_color_(kit_light_); - quiet || printf("Too many tests!\n\n"); - status = 1; - } - - if (status == 0) { - no_color || kit_print_color_(kit_green_); - quiet || printf("OK\n"); - } else { - no_color || kit_print_color_(kit_red_); - quiet || printf("FAILED\n"); - } - - no_color || kit_print_color_(kit_white_); - quiet || printf("\n"); - return status; -} - -kit_benchs_list_t kit_benchs_list = { 0 }; - -static void bench_set_repeats_limit(int i, int repeats_limit) { - if (kit_benchs_list.v[i].ready) - return; - if (kit_benchs_list.v[i].cycles_size >= KIT_BENCH_MAX_CYCLES) - return; - kit_benchs_list.v[i].cycles[kit_benchs_list.v[i].cycles_size] = - repeats_limit; - kit_benchs_list.v[i].cycles_size++; -} - -static int bench_loop(int i) { - if (!kit_benchs_list.v[i].ready) - return 0; - return kit_benchs_list.v[i].repeats < - kit_benchs_list.v[i].cycles[kit_benchs_list.v[i].cycle]; -} - -static void bench_begin(int i) { - int n = kit_benchs_list.v[i].repeats++; - - if (n >= KIT_BENCH_MAX_REPEATS) - return; - - struct timespec tv; - timespec_get(&tv, TIME_UTC); - - kit_benchs_list.v[i].sec[n] = (int64_t) tv.tv_sec; - kit_benchs_list.v[i].nsec[n] = (int64_t) tv.tv_nsec; -} - -static void bench_end(int i) { - int n = kit_benchs_list.v[i].repeats - 1; - - if (n < 0 || n >= KIT_BENCH_MAX_REPEATS) - return; - - struct timespec tv; - timespec_get(&tv, TIME_UTC); - - int64_t sec = ((int64_t) tv.tv_sec) - kit_benchs_list.v[i].sec[n]; - int64_t nsec = ((int64_t) tv.tv_nsec) - - kit_benchs_list.v[i].nsec[n]; - - kit_benchs_list.v[i].duration_nsec[n] = sec * 1000000000 + nsec; -} - -void kit_bench_register(char const *name, char const *file, - kit_bench_run_fn fn) { - int n = kit_benchs_list.size++; - if (n < KIT_BENCHS_SIZE_LIMIT) { - kit_benchmark_t *bench = kit_benchs_list.v + n; - - bench->bench_fn = fn; - bench->bench_name = name; - bench->bench_file = file; - bench->cycles_size = 0; - bench->ready = 0; - } -} - -static void kit_bench_setup_signals() { - for (int i = 0; i < sizeof kit_signums_ / sizeof *kit_signums_; i++) - signal(kit_signums_[i], kit_test_handle_signal); -} - -static int kit_run_bench_(volatile int i) { - int signum = setjmp(kit_test_restore_execution); - - if (signum != 0) { - kit_benchs_list.v[i].signal = signum; - return 0; - } - - kit_benchs_list.v[i].bench_fn(i, bench_set_repeats_limit, - bench_loop, bench_begin, bench_end); - return 1; -} - -static int kit_compare_64_(void const *x_, void const *y_) { - int64_t const *x = (int64_t const *) x_; - int64_t const *y = (int64_t const *) y_; - return *x - *y; -} - -static int kit_compare_32_(void const *x_, void const *y_) { - int const *x = (int const *) x_; - int const *y = (int const *) y_; - return *x - *y; -} - -int kit_run_benchmarks(int argc, char **argv) { - int success_count = 0; - int status = 0; - int no_color = 0; - int line_width = 20; - int carriage_return = 1; - - char const *specific_bench = NULL; - - kit_bench_setup_signals(); - - for (int i = 0; i < argc; i++) - if (strcmp("--no-term-color", argv[i]) == 0) - no_color = 1; - else if (strcmp("--no-carriage-return", argv[i]) == 0) - carriage_return = 0; - else if (strcmp("--match", argv[i]) == 0) - specific_bench = argv[++i]; - - if (specific_bench != NULL) { - no_color || kit_print_color_(kit_light_); - printf("Run benchmarks matching "); - no_color || kit_print_color_(kit_white_); - printf("*%s*", specific_bench); - no_color || kit_print_color_(kit_light_); - printf("\n\n"); - } - - char const *file = NULL; - ptrdiff_t file_root = -1; - int benchs_total = 0; - - for (int i = 0; - i < kit_benchs_list.size && i < KIT_BENCHS_SIZE_LIMIT; i++) { - if (specific_bench != NULL && - strstr(kit_benchs_list.v[i].bench_name, specific_bench) == - NULL) - continue; - benchs_total++; - int l = 2 + (int) strlen(kit_benchs_list.v[i].bench_name); - if (line_width < l) - line_width = l; - } - - if (benchs_total > 0) { - char const *s = kit_benchs_list.v[0].bench_file; - - for (int j = 1; - j < kit_benchs_list.size && j < KIT_BENCHS_SIZE_LIMIT; j++) { - kit_benchmark_t *bench = kit_benchs_list.v + j; - - if (specific_bench != NULL && - strstr(bench->bench_name, specific_bench) == NULL) - continue; - if (strcmp(s, bench->bench_file) == 0) - continue; - int k = 0; - for (; s[k] != '\0' && bench->bench_file[k] != '\0' && - s[k] == bench->bench_file[k]; - k++) { } - if (file_root == -1 || file_root > k) - file_root = k; - } - - if (file_root == -1) { - for (int i = 0; s[i] != '\0'; i++) - if (s[i] == '/' || s[i] == '\\') - file_root = i + 1; - } - } - - no_color || kit_print_color_(kit_blue_); - printf("# "); - no_color || kit_print_color_(kit_light_); - printf("BENCHMARK"); - printf("%*c", line_width - 9, ' '); - no_color || kit_print_color_(kit_green_); - printf(" LOW "); - no_color || kit_print_color_(kit_light_); - printf("|"); - no_color || kit_print_color_(kit_blue_); - printf(" MEDIAN "); - no_color || kit_print_color_(kit_light_); - printf("|"); - no_color || kit_print_color_(kit_yellow_); - printf(" HIGH\n"); - no_color || kit_print_color_(kit_light_); - printf(" (in microseconds)\n\n"); - - /* Prepare cycles. - */ - - for (int i = 0; - i < kit_benchs_list.size && i < KIT_BENCHS_SIZE_LIMIT; i++) { - kit_benchmark_t *bench = kit_benchs_list.v + i; - - if (specific_bench != NULL && - strstr(bench->bench_name, specific_bench) == NULL) - continue; - - kit_run_bench_(i); - - if (bench->cycles_size == 0) { - bench->cycles_size = 2; - bench->cycles[0] = KIT_BENCH_REPEATS_DEFAULT_1; - bench->cycles[1] = KIT_BENCH_REPEATS_DEFAULT_2; - } - - qsort(bench->cycles, bench->cycles_size, sizeof *bench->cycles, - kit_compare_32_); - - kit_benchs_list.v[i].ready = 1; - } - - /* Run cycles. - */ - - for (int cycle = 0; cycle < KIT_BENCH_MAX_CYCLES; cycle++) { - /* Prepare cycle. - */ - - int cycles_done = 1; - - for (int i = 0; - i < kit_benchs_list.size && i < KIT_BENCHS_SIZE_LIMIT; i++) { - kit_benchmark_t *bench = kit_benchs_list.v + i; - - if (specific_bench != NULL && - strstr(bench->bench_name, specific_bench) == NULL) - continue; - if (cycle >= bench->cycles_size) - continue; - - bench->repeats = 0; - bench->cycle = cycle; - cycles_done = 0; - } - - if (cycles_done) - break; - - /* Run benchmarks. - */ - - for (int i = 0; - i < kit_benchs_list.size && i < KIT_BENCHS_SIZE_LIMIT; i++) { - kit_benchmark_t *bench = kit_benchs_list.v + i; - - if (specific_bench != NULL && - strstr(bench->bench_name, specific_bench) == NULL) - continue; - if (cycle >= bench->cycles_size) - continue; - - if (file == NULL || strcmp(file, bench->bench_file) != 0) { - if (file != NULL) - printf("\n"); - file = bench->bench_file; - no_color || kit_print_color_(kit_blue_); - printf("* "); - no_color || kit_print_color_(kit_white_); - printf("%s\n", file + file_root); - } - - !carriage_return || no_color || kit_print_color_(kit_yellow_); - carriage_return || no_color || kit_print_color_(kit_light_); - printf("` %s ", bench->bench_name); - !carriage_return || printf("\r"); - fflush(stdout); - - int bench_status = kit_run_bench_(i); - - if (bench->repeats > KIT_BENCH_MAX_REPEATS) - bench_status = 0; - - !carriage_return || no_color || kit_print_color_(kit_light_); - !carriage_return || printf("` %s ", bench->bench_name); - - int l = (int) strlen(bench->bench_name); - printf("%*c", line_width - l, ' '); - - if (bench->repeats <= 0) { - no_color || kit_print_color_(kit_yellow_); - printf(" 0 runs\n"); - success_count++; - } else if (bench_status == 0) { - no_color || kit_print_color_(kit_red_); - printf(" FAIL\n"); - status = 1; - } else { - int repeats = bench->repeats; - - memcpy(bench->duration_sorted_nsec, bench->duration_nsec, - repeats * sizeof *bench->duration_sorted_nsec); - qsort(bench->duration_sorted_nsec, repeats, - sizeof *bench->duration_sorted_nsec, kit_compare_64_); - - int64_t average = bench->duration_sorted_nsec[repeats / 2]; - int64_t floor = bench->duration_sorted_nsec[repeats / 20]; - int64_t roof = - bench->duration_sorted_nsec[repeats - repeats / 20 - 1]; - - no_color || kit_print_color_(kit_white_); - printf("%-9g", (double) floor * 0.001); - no_color || kit_print_color_(kit_light_); - printf("| "); - no_color || kit_print_color_(kit_white_); - printf("%-9g", (double) average * 0.001); - no_color || kit_print_color_(kit_light_); - printf("| "); - no_color || kit_print_color_(kit_white_); - printf("%-9g", (double) roof * 0.001); - no_color || kit_print_color_(kit_light_); - printf(" %d runs\n", repeats); - success_count++; - } - } - } - - printf("\n"); - - if (status != 0) { - for (int i = 0; - i < kit_benchs_list.size && i < KIT_BENCHS_SIZE_LIMIT; i++) { - kit_benchmark_t *bench = kit_benchs_list.v + i; - - if (specific_bench != NULL && - strstr(bench->bench_name, specific_bench) == NULL) - continue; - if (bench->signal != 0) { - int signum = bench->signal; - if (signum >= 0 && - signum < sizeof kit_signames_ / sizeof *kit_signames_ && - kit_signames_[signum] != NULL) { - no_color || kit_print_color_(kit_light_); - printf("Signal \"%s\" (%d) for \"", kit_signames_[signum], - signum); - no_color || kit_print_color_(kit_white_); - printf("%s", bench->bench_name); - no_color || kit_print_color_(kit_light_); - printf("\" in \""); - no_color || kit_print_color_(kit_white_); - printf("%s", bench->bench_file + file_root); - no_color || kit_print_color_(kit_light_); - printf("\"!.\n"); - } else { - no_color || kit_print_color_(kit_light_); - printf("Unknown signal (%d) for \"", signum); - no_color || kit_print_color_(kit_white_); - printf("%s", bench->bench_name); - no_color || kit_print_color_(kit_light_); - printf("\" in \""); - no_color || kit_print_color_(kit_white_); - printf("%s", bench->bench_file + file_root); - no_color || kit_print_color_(kit_light_); - printf("\"!.\n"); - } - } - } - - printf("\n"); - } - - if (kit_benchs_list.size > KIT_BENCHS_SIZE_LIMIT) { - no_color || kit_print_color_(kit_light_); - printf("Too many benchmarks!\n\n"); - status = 1; - } - - if (status == 0) { - no_color || kit_print_color_(kit_green_); - printf("DONE\n"); - } else { - no_color || kit_print_color_(kit_red_); - printf("DONE WITH ERRORS\n"); - } - - no_color || kit_print_color_(kit_white_); - printf("\n"); - return status; -} - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/kit/test.h b/source/kit/test.h new file mode 100644 index 0000000..b6be132 --- /dev/null +++ b/source/kit/test.h @@ -0,0 +1,1113 @@ +// ================================================================ +// +// test.h +// https://guattari.tech/git/kit +// +// Header-only unit-testing and microbenchmarks framework for C. +// +// +// - Define a unique KIT_TEST_FILE for each file to avoid name +// collisions. +// +// - Define KIT_TEST_IMPLEMENTATION to include the implementation. +// +// +// Optional settings +// +// - KIT_TESTS_SIZE_LIMIT +// - KIT_TEST_ASSERTIONS_LIMIT +// - KIT_BENCHS_SIZE_LIMIT +// - KIT_BENCH_MAX_REPEATS +// - KIT_BENCH_MAX_CYCLES +// - KIT_BENCH_REPEATS_DEFAULT_1 +// - KIT_BENCH_REPEATS_DEFAULT_2 +// +// +// Usage example +// +// // foo.test.c +// #define KIT_TEST_FILE foo // This is required if you want to +// // use multiple test files. +// #include "test.h" +// TEST("foo") { +// REQUIRE(1); +// REQUIRE_EQ(2 + 2, 4); +// } +// +// // main.c +// #include "test.h" +// int main(int argc, char **argv) { +// return run_tests(argc, argv); +// } +// +// ================================================================ +// +// The MIT License +// +// Copyright (c) 2022-2023 Mitya Selivanov +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// ================================================================ + +#ifndef KIT_TEST_H +#define KIT_TEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include + +#ifndef KIT_TEST_FILE +# define KIT_TEST_FILE kit_test +#endif + +#ifndef KIT_TESTS_SIZE_LIMIT +# define KIT_TESTS_SIZE_LIMIT 0x1000 +#endif + +#ifndef KIT_TEST_ASSERTIONS_LIMIT +# define KIT_TEST_ASSERTIONS_LIMIT 0x50 +#endif + +#ifndef KIT_BENCHS_SIZE_LIMIT +# define KIT_BENCHS_SIZE_LIMIT 200 +#endif + +#ifndef KIT_BENCH_MAX_REPEATS +# define KIT_BENCH_MAX_REPEATS 4000 +#endif + +#ifndef KIT_BENCH_MAX_CYCLES +# define KIT_BENCH_MAX_CYCLES 40 +#endif + +#ifndef KIT_BENCH_REPEATS_DEFAULT_1 +# define KIT_BENCH_REPEATS_DEFAULT_1 40 +#endif + +#ifndef KIT_BENCH_REPEATS_DEFAULT_2 +# define KIT_BENCH_REPEATS_DEFAULT_2 400 +#endif + +typedef void (*kit_test_report_fn)(int test_index, int line, + int64_t value, int64_t expected); +typedef void (*kit_test_run_fn)( + int kit_test_index_, kit_test_report_fn kit_test_report_fn_); + +typedef struct { + char const *test_name; + char const *test_file; + kit_test_run_fn test_fn; + int assertions; + int line[KIT_TEST_ASSERTIONS_LIMIT]; + int status[KIT_TEST_ASSERTIONS_LIMIT]; + int64_t value[KIT_TEST_ASSERTIONS_LIMIT]; + int64_t expected[KIT_TEST_ASSERTIONS_LIMIT]; + int signal; +} kit_test_case_t; + +typedef struct { + int size; + kit_test_case_t v[KIT_TESTS_SIZE_LIMIT]; +} kit_tests_list_t; + +extern kit_tests_list_t kit_tests_list; + +#define KIT_TEST_CONCAT4_(a, b, c, d) a##b##c##d +#define KIT_TEST_CONCAT3_(a, b, c) KIT_TEST_CONCAT4_(a, b, _, c) + +#ifdef __cplusplus +# define KIT_TEST_ON_START_(f) \ + static void f(void); \ + static int KIT_TEST_CONCAT3_(_kit_test_init_, __LINE__, \ + f) = (f(), 0); \ + static void f(void) +#else +# ifdef _MSC_VER +# pragma section(".CRT$XCU", read) +# define KIT_TEST_ON_START_2_(f, p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ + __pragma(comment(linker, "/include:" p #f "_")) static void f( \ + void) +# ifdef _WIN64 +# define KIT_TEST_ON_START_(f) KIT_TEST_ON_START_2_(f, "") +# else +# define KIT_TEST_ON_START_(f) KIT_TEST_ON_START_2_(f, "_") +# endif +# else +# define KIT_TEST_ON_START_(f) \ + static void f(void) __attribute__((constructor)); \ + static void f(void) +# endif +#endif + +void kit_test_register(char const *name, char const *file, + kit_test_run_fn fn); + +#define KIT_TEST(name) \ + static void KIT_TEST_CONCAT3_(kit_test_run_, __LINE__, \ + KIT_TEST_FILE)(int, \ + kit_test_report_fn); \ + KIT_TEST_ON_START_( \ + KIT_TEST_CONCAT3_(kit_test_case_, __LINE__, KIT_TEST_FILE)) { \ + kit_test_register( \ + name, __FILE__, \ + KIT_TEST_CONCAT3_(kit_test_run_, __LINE__, KIT_TEST_FILE)); \ + } \ + static void KIT_TEST_CONCAT3_(kit_test_run_, __LINE__, \ + KIT_TEST_FILE)( \ + int kit_test_index_, kit_test_report_fn kit_test_report_fn_) + +#define KIT_REQUIRE(...) \ + kit_test_report_fn_(kit_test_index_, __LINE__, (__VA_ARGS__), 1) + +#define KIT_REQUIRE_EQ(...) \ + kit_test_report_fn_(kit_test_index_, __LINE__, __VA_ARGS__) + +int kit_run_tests(int argc, char **argv); + +typedef void (*kit_bench_set_repeats_limit_fn)(int bench_index, + int repeats_limit); +typedef int (*kit_bench_loop_fn)(int bench_index); +typedef void (*kit_bench_begin_fn)(int bench_index); +typedef void (*kit_bench_end_fn)(int bench_index); + +typedef void (*kit_bench_run_fn)( + int kit_bench_index_, + kit_bench_set_repeats_limit_fn kit_bench_set_repeats_limit_, + kit_bench_loop_fn kit_bench_loop_, + kit_bench_begin_fn kit_bench_begin_, + kit_bench_end_fn kit_bench_end_); + +typedef struct { + char const *bench_name; + char const *bench_file; + kit_bench_run_fn bench_fn; + int64_t sec[KIT_BENCH_MAX_REPEATS]; + int32_t nsec[KIT_BENCH_MAX_REPEATS]; + int64_t duration_nsec[KIT_BENCH_MAX_REPEATS]; + int64_t duration_sorted_nsec[KIT_BENCH_MAX_REPEATS]; + int repeats; + int cycles_size; + int cycles[KIT_BENCH_MAX_CYCLES]; + int cycle; + int signal; + int ready; +} kit_benchmark_t; + +typedef struct { + int size; + kit_benchmark_t v[KIT_BENCHS_SIZE_LIMIT]; +} kit_benchs_list_t; + +extern kit_benchs_list_t kit_benchs_list; + +void kit_bench_register(char const *name, char const *file, + kit_bench_run_fn fn); + +#define KIT_BENCHMARK(name) \ + static void KIT_TEST_CONCAT3_(kit_bench_run_, __LINE__, \ + KIT_TEST_FILE)( \ + int, kit_bench_set_repeats_limit_fn, kit_bench_loop_fn, \ + kit_bench_begin_fn, kit_bench_end_fn); \ + KIT_TEST_ON_START_( \ + KIT_TEST_CONCAT3_(kit_benchmark_, __LINE__, KIT_TEST_FILE)) { \ + kit_bench_register( \ + name, __FILE__, \ + KIT_TEST_CONCAT3_(kit_bench_run_, __LINE__, KIT_TEST_FILE)); \ + } \ + static void KIT_TEST_CONCAT3_(kit_bench_run_, __LINE__, \ + KIT_TEST_FILE)( \ + int kit_bench_index_, \ + kit_bench_set_repeats_limit_fn kit_bench_set_repeats_limit_, \ + kit_bench_loop_fn kit_bench_loop_, \ + kit_bench_begin_fn kit_bench_begin_, \ + kit_bench_end_fn kit_bench_end_) + +#define KIT_BENCHMARK_REPEAT(repeats_limit_) \ + kit_bench_set_repeats_limit_(kit_bench_index_, repeats_limit_) + +#define KIT_BENCHMARK_BEGIN \ + while (kit_bench_loop_(kit_bench_index_)) { \ + kit_bench_begin_(kit_bench_index_); \ + { + +#define KIT_BENCHMARK_END \ + } \ + kit_bench_end_(kit_bench_index_); \ + } + +// FIXME +// +#define KIT_DO_NOT_OPTIMIZE(x) \ + do { \ + volatile void *bench_ptr_ = &(x); \ + (void) bench_ptr_; \ + } while (0) + +int kit_run_benchmarks(int argc, char **argv); + +#ifndef KIT_DISABLE_SHORT_NAMES +# define TEST KIT_TEST +# define REQUIRE KIT_REQUIRE +# define REQUIRE_EQ KIT_REQUIRE_EQ +# define BENCHMARK KIT_BENCHMARK +# define BENCHMARK_REPEAT KIT_BENCHMARK_REPEAT +# define BENCHMARK_BEGIN KIT_BENCHMARK_BEGIN +# define BENCHMARK_END KIT_BENCHMARK_END +# define DO_NOT_OPTIMIZE KIT_DO_NOT_OPTIMIZE + +# define test_register kit_test_register +# define run_tests kit_run_tests +# define bench_register kit_bench_register +# define run_benchmarks kit_run_benchmarks +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +#if defined(KIT_TEST_IMPLEMENTATION) && !defined(KIT_TEST_H_IMPL) +#define KIT_TEST_H_IMPL + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef KIT_TIME_H +# define KIT_TIME_H + +# include + +# ifndef TIME_UTC +# define TIME_UTC 1 +# endif + +# ifdef KIT_REQUIRE_TIMESPEC_GET +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# include + +# define KIT_TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS \ + 116444736000000000ull +# define KIT_TIMESPEC_IMPL_TICKS_PER_SECONDS 10000000ull + +static int timespec_get(struct timespec *ts, int base) { + if (ts == NULL || base != TIME_UTC) + return 0; + + FILETIME ft; + ULARGE_INTEGER date; + LONGLONG ticks; + + GetSystemTimeAsFileTime(&ft); + date.HighPart = ft.dwHighDateTime; + date.LowPart = ft.dwLowDateTime; + ticks = (LONGLONG) (date.QuadPart - + KIT_TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS); + ts->tv_sec = ticks / KIT_TIMESPEC_IMPL_TICKS_PER_SECONDS; + ts->tv_nsec = (ticks % KIT_TIMESPEC_IMPL_TICKS_PER_SECONDS) * 100; + + return base; +} +# endif + +#endif + +#include +#include +#include +#include +#include + +enum { + kit_white_, + kit_blue_, + kit_light_, + kit_yellow_, + kit_red_, + kit_green_ +}; + +static char const *kit_color_codes_[] = { "\x1b[38m", "\x1b[34m", + "\x1b[37m", "\x1b[33m", + "\x1b[31m", "\x1b[32m" }; + +static int kit_print_color_(int c) { + return printf("%s", kit_color_codes_[c]); +} + +static int kit_signums_[] = { SIGINT, SIGILL, SIGABRT, + SIGFPE, SIGSEGV, SIGTERM }; + +static char const *kit_signames_[64]; + +static void kit_signames_init_(void) { + memset(kit_signames_, 0, sizeof kit_signames_); + kit_signames_[SIGINT] = "Interactive attention signal"; + kit_signames_[SIGILL] = "Illegal instruction"; + kit_signames_[SIGABRT] = "Abnormal termination"; + kit_signames_[SIGFPE] = "Erroneous arithmetic operation"; + kit_signames_[SIGSEGV] = "Invalid access to storage"; + kit_signames_[SIGTERM] = "Termination request"; +} + +kit_tests_list_t kit_tests_list = { 0 }; + +static void kit_report_(int i, int line, int64_t value, + int64_t expected) { + int n = kit_tests_list.v[i].assertions++; + + if (n >= KIT_TEST_ASSERTIONS_LIMIT) + return; + + kit_tests_list.v[i].line[n] = line; + kit_tests_list.v[i].status[n] = value == expected; + kit_tests_list.v[i].value[n] = value; + kit_tests_list.v[i].expected[n] = expected; +} + +static int64_t ns_to_ms(int64_t ns) { + return (ns + 500000) / 1000000; +} + +static int64_t sec_to_ms(int64_t sec) { + return 1000 * sec; +} + +void kit_test_register(char const *name, char const *file, + kit_test_run_fn fn) { + int n = kit_tests_list.size++; + if (n < KIT_TESTS_SIZE_LIMIT) { + kit_tests_list.v[n].test_fn = fn; + kit_tests_list.v[n].test_name = name; + kit_tests_list.v[n].test_file = file; + kit_tests_list.v[n].assertions = 0; + } +} + +static jmp_buf kit_test_restore_execution; + +static void kit_test_handle_signal(int signum) { + longjmp(kit_test_restore_execution, signum); +} + +static void kit_test_setup_signals() { + for (int i = 0; i < sizeof kit_signums_ / sizeof *kit_signums_; i++) + signal(kit_signums_[i], kit_test_handle_signal); +} + +static int kit_run_test_(volatile int i) { + int signum = setjmp(kit_test_restore_execution); + + if (signum != 0) { + kit_tests_list.v[i].signal = signum; + return 0; + } + + kit_tests_list.v[i].test_fn(i, kit_report_); + return 1; +} + +int kit_run_tests(int argc, char **argv) { + kit_signames_init_(); + + int success_count = 0; + int fail_assertion_count = 0; + int total_assertion_count = 0; + int status = 0; + int quiet = 0; + int no_color = 0; + int line_width = 20; + int carriage_return = 1; + + int i, j; + + char const *specific_test = NULL; + + kit_test_setup_signals(); + + for (i = 0; i < argc; i++) + if (strcmp("--no-term-color", argv[i]) == 0) + no_color = 1; + else if (strcmp("--no-carriage-return", argv[i]) == 0) + carriage_return = 0; + else if (strcmp("--quiet", argv[i]) == 0) + quiet = 1; + else if (strcmp("--match", argv[i]) == 0) + specific_test = argv[++i]; + + quiet && (no_color = 1); + + if (specific_test != NULL) { + no_color || kit_print_color_(kit_light_); + quiet || printf("Run tests matching "); + no_color || kit_print_color_(kit_white_); + quiet || printf("*%s*", specific_test); + no_color || kit_print_color_(kit_light_); + quiet || printf("\n\n"); + } + + char const *file = NULL; + ptrdiff_t file_root = -1; + int tests_total = 0; + + for (i = 0; i < kit_tests_list.size && i < KIT_TESTS_SIZE_LIMIT; + i++) { + if (specific_test != NULL && + strstr(kit_tests_list.v[i].test_name, specific_test) == NULL) + continue; + tests_total++; + int l = 2 + (int) strlen(kit_tests_list.v[i].test_name); + if (line_width < l) + line_width = l; + } + + if (tests_total > 0) { + char const *s = kit_tests_list.v[0].test_file; + + for (j = 1; j < kit_tests_list.size && j < KIT_TESTS_SIZE_LIMIT; + j++) { + if (specific_test != NULL && + strstr(kit_tests_list.v[j].test_name, specific_test) == + NULL) + continue; + if (strcmp(s, kit_tests_list.v[j].test_file) == 0) + continue; + int k = 0; + for (; + s[k] != '\0' && kit_tests_list.v[j].test_file[k] != '\0' && + s[k] == kit_tests_list.v[j].test_file[k]; + k++) { } + if (file_root == -1 || file_root > k) + file_root = k; + } + + if (file_root == -1) { + for (i = 0; s[i] != '\0'; i++) + if (s[i] == '/' || s[i] == '\\') + file_root = i + 1; + } + } + + for (i = 0; i < kit_tests_list.size && i < KIT_TESTS_SIZE_LIMIT; + i++) { + if (specific_test != NULL && + strstr(kit_tests_list.v[i].test_name, specific_test) == NULL) + continue; + if (file == NULL || + strcmp(file, kit_tests_list.v[i].test_file) != 0) { + if (file != NULL) + quiet || printf("\n"); + file = kit_tests_list.v[i].test_file; + no_color || kit_print_color_(kit_blue_); + quiet || printf("* "); + no_color || kit_print_color_(kit_white_); + quiet || printf("%s\n", file + file_root); + } + + !carriage_return || no_color || kit_print_color_(kit_yellow_); + carriage_return || no_color || kit_print_color_(kit_light_); + quiet || printf("` %s ", kit_tests_list.v[i].test_name); + !carriage_return || quiet || printf("\r"); + quiet || fflush(stdout); + + struct timespec begin, end; + timespec_get(&begin, TIME_UTC); + + int test_status = kit_run_test_(i); + + timespec_get(&end, TIME_UTC); + int duration = (int) (ns_to_ms(end.tv_nsec - begin.tv_nsec) + + sec_to_ms(end.tv_sec - begin.tv_sec)); + + for (j = 0; j < kit_tests_list.v[i].assertions && + j < KIT_TEST_ASSERTIONS_LIMIT; + j++) + if (kit_tests_list.v[i].status[j] == 0) { + fail_assertion_count++; + test_status = 0; + } + + if (kit_tests_list.v[i].assertions > KIT_TEST_ASSERTIONS_LIMIT) + test_status = 0; + + total_assertion_count += kit_tests_list.v[i].assertions; + + !carriage_return || no_color || kit_print_color_(kit_light_); + !carriage_return || quiet || + printf("` %s ", kit_tests_list.v[i].test_name); + + int l = (int) strlen(kit_tests_list.v[i].test_name); + quiet || printf("%*c", line_width - l, ' '); + + if (test_status == 0) { + no_color || kit_print_color_(kit_red_); + quiet || printf("FAIL"); + no_color || kit_print_color_(kit_light_); + duration == 0 || quiet || printf(" %d ms", duration); + quiet || printf("\n"); + status = 1; + } else { + no_color || kit_print_color_(kit_green_); + quiet || printf("OK"); + no_color || kit_print_color_(kit_light_); + duration == 0 || quiet || printf(" %d ms", duration); + quiet || printf("\n"); + success_count++; + } + + quiet || fflush(stdout); + } + + no_color || kit_print_color_(kit_white_); + quiet || printf("\n%d of %d tests passed.\n", success_count, + tests_total); + quiet || printf("%d of %d assertions passed.\n\n", + total_assertion_count - fail_assertion_count, + total_assertion_count); + + no_color || kit_print_color_(kit_light_); + + if (!quiet && status != 0) { + int have_kit_report_s = 0; + + for (i = 0; i < kit_tests_list.size && i < KIT_TESTS_SIZE_LIMIT; + i++) { + if (specific_test != NULL && + strstr(kit_tests_list.v[i].test_name, specific_test) == + NULL) + continue; + if (kit_tests_list.v[i].signal != 0) { + int signum = kit_tests_list.v[i].signal; + if (signum >= 0 && + signum < sizeof kit_signames_ / sizeof *kit_signames_ && + kit_signames_[signum] != NULL) { + no_color || kit_print_color_(kit_light_); + printf("Signal \"%s\" (%d) for \"", kit_signames_[signum], + signum); + no_color || kit_print_color_(kit_white_); + printf("%s", kit_tests_list.v[i].test_name); + no_color || kit_print_color_(kit_light_); + printf("\" in \""); + no_color || kit_print_color_(kit_white_); + + printf("%s", kit_tests_list.v[i].test_file + file_root); + no_color || kit_print_color_(kit_light_); + printf("\"!\n"); + } else { + no_color || kit_print_color_(kit_light_); + printf("Unknown signal (%d) for \"", signum); + no_color || kit_print_color_(kit_white_); + printf("%s", kit_tests_list.v[i].test_name); + no_color || kit_print_color_(kit_light_); + printf("\" in \""); + no_color || kit_print_color_(kit_white_); + + printf("%s", kit_tests_list.v[i].test_file + file_root); + no_color || kit_print_color_(kit_light_); + printf("\"!\n"); + } + have_kit_report_s = 1; + } + if (kit_tests_list.v[i].assertions > + KIT_TEST_ASSERTIONS_LIMIT) { + no_color || kit_print_color_(kit_light_); + printf("Too many assertions for \""); + no_color || kit_print_color_(kit_white_); + printf("%s", kit_tests_list.v[i].test_name); + no_color || kit_print_color_(kit_light_); + printf("\" in \""); + no_color || kit_print_color_(kit_white_); + + printf("%s", kit_tests_list.v[i].test_file + file_root); + no_color || kit_print_color_(kit_light_); + printf("\"!\n"); + have_kit_report_s = 1; + } + } + + have_kit_report_s &&printf("\n"); + } + + if (!quiet && status != 0) { + for (i = 0; i < kit_tests_list.size && i < KIT_TESTS_SIZE_LIMIT; + i++) { + if (specific_test != NULL && + strstr(kit_tests_list.v[i].test_name, specific_test) == + NULL) + continue; + + if (kit_tests_list.v[i].assertions <= KIT_TEST_ASSERTIONS_LIMIT) + for (j = 0; j < kit_tests_list.v[i].assertions; j++) + if (!kit_tests_list.v[i].status[j]) { + no_color || kit_print_color_(kit_light_); + printf("Assertion on line "); + no_color || kit_print_color_(kit_white_); + printf("%d", kit_tests_list.v[i].line[j]); + no_color || kit_print_color_(kit_light_); + printf(" in \""); + no_color || kit_print_color_(kit_white_); + printf("%s", kit_tests_list.v[i].test_file + file_root); + no_color || kit_print_color_(kit_light_); + printf("\" failed.\n"); + no_color || kit_print_color_(kit_red_); + printf(" -> "); + no_color || kit_print_color_(kit_light_); + printf("Got wrong value "); + no_color || kit_print_color_(kit_white_); + printf("%10lld", + (long long) kit_tests_list.v[i].value[j]); + no_color || kit_print_color_(kit_light_); + printf(" ("); + no_color || kit_print_color_(kit_white_); + printf("0x%08llx", + (unsigned long long) kit_tests_list.v[i].value[j]); + no_color || kit_print_color_(kit_light_); + printf(")\n"); + no_color || kit_print_color_(kit_green_); + printf(" -> "); + no_color || kit_print_color_(kit_light_); + printf("Expected value "); + no_color || kit_print_color_(kit_white_); + printf("%10lld", + (long long) kit_tests_list.v[i].expected[j]); + no_color || kit_print_color_(kit_light_); + printf(" ("); + no_color || kit_print_color_(kit_white_); + printf( + "0x%08llx", + (unsigned long long) kit_tests_list.v[i].expected[j]); + no_color || kit_print_color_(kit_light_); + printf(")\n\n"); + } + } + } + + if (kit_tests_list.size > KIT_TESTS_SIZE_LIMIT) { + no_color || kit_print_color_(kit_light_); + quiet || printf("Too many tests!\n\n"); + status = 1; + } + + if (status == 0) { + no_color || kit_print_color_(kit_green_); + quiet || printf("OK\n"); + } else { + no_color || kit_print_color_(kit_red_); + quiet || printf("FAILED\n"); + } + + no_color || kit_print_color_(kit_white_); + quiet || printf("\n"); + return status; +} + +kit_benchs_list_t kit_benchs_list = { 0 }; + +static void bench_set_repeats_limit(int i, int repeats_limit) { + if (kit_benchs_list.v[i].ready) + return; + if (kit_benchs_list.v[i].cycles_size >= KIT_BENCH_MAX_CYCLES) + return; + kit_benchs_list.v[i].cycles[kit_benchs_list.v[i].cycles_size] = + repeats_limit; + kit_benchs_list.v[i].cycles_size++; +} + +static int bench_loop(int i) { + if (!kit_benchs_list.v[i].ready) + return 0; + return kit_benchs_list.v[i].repeats < + kit_benchs_list.v[i].cycles[kit_benchs_list.v[i].cycle]; +} + +static void bench_begin(int i) { + int n = kit_benchs_list.v[i].repeats++; + + if (n >= KIT_BENCH_MAX_REPEATS) + return; + + struct timespec tv; + timespec_get(&tv, TIME_UTC); + + kit_benchs_list.v[i].sec[n] = (int64_t) tv.tv_sec; + kit_benchs_list.v[i].nsec[n] = (int64_t) tv.tv_nsec; +} + +static void bench_end(int i) { + int n = kit_benchs_list.v[i].repeats - 1; + + if (n < 0 || n >= KIT_BENCH_MAX_REPEATS) + return; + + struct timespec tv; + timespec_get(&tv, TIME_UTC); + + int64_t sec = ((int64_t) tv.tv_sec) - kit_benchs_list.v[i].sec[n]; + int64_t nsec = ((int64_t) tv.tv_nsec) - + kit_benchs_list.v[i].nsec[n]; + + kit_benchs_list.v[i].duration_nsec[n] = sec * 1000000000 + nsec; +} + +void kit_bench_register(char const *name, char const *file, + kit_bench_run_fn fn) { + int n = kit_benchs_list.size++; + if (n < KIT_BENCHS_SIZE_LIMIT) { + kit_benchmark_t *bench = kit_benchs_list.v + n; + + bench->bench_fn = fn; + bench->bench_name = name; + bench->bench_file = file; + bench->cycles_size = 0; + bench->ready = 0; + } +} + +static void kit_bench_setup_signals() { + for (int i = 0; i < sizeof kit_signums_ / sizeof *kit_signums_; i++) + signal(kit_signums_[i], kit_test_handle_signal); +} + +static int kit_run_bench_(volatile int i) { + int signum = setjmp(kit_test_restore_execution); + + if (signum != 0) { + kit_benchs_list.v[i].signal = signum; + return 0; + } + + kit_benchs_list.v[i].bench_fn(i, bench_set_repeats_limit, + bench_loop, bench_begin, bench_end); + return 1; +} + +static int kit_compare_64_(void const *x_, void const *y_) { + int64_t const *x = (int64_t const *) x_; + int64_t const *y = (int64_t const *) y_; + return *x - *y; +} + +static int kit_compare_32_(void const *x_, void const *y_) { + int const *x = (int const *) x_; + int const *y = (int const *) y_; + return *x - *y; +} + +int kit_run_benchmarks(int argc, char **argv) { + int success_count = 0; + int status = 0; + int no_color = 0; + int line_width = 20; + int carriage_return = 1; + + char const *specific_bench = NULL; + + kit_bench_setup_signals(); + + for (int i = 0; i < argc; i++) + if (strcmp("--no-term-color", argv[i]) == 0) + no_color = 1; + else if (strcmp("--no-carriage-return", argv[i]) == 0) + carriage_return = 0; + else if (strcmp("--match", argv[i]) == 0) + specific_bench = argv[++i]; + + if (specific_bench != NULL) { + no_color || kit_print_color_(kit_light_); + printf("Run benchmarks matching "); + no_color || kit_print_color_(kit_white_); + printf("*%s*", specific_bench); + no_color || kit_print_color_(kit_light_); + printf("\n\n"); + } + + char const *file = NULL; + ptrdiff_t file_root = -1; + int benchs_total = 0; + + for (int i = 0; + i < kit_benchs_list.size && i < KIT_BENCHS_SIZE_LIMIT; i++) { + if (specific_bench != NULL && + strstr(kit_benchs_list.v[i].bench_name, specific_bench) == + NULL) + continue; + benchs_total++; + int l = 2 + (int) strlen(kit_benchs_list.v[i].bench_name); + if (line_width < l) + line_width = l; + } + + if (benchs_total > 0) { + char const *s = kit_benchs_list.v[0].bench_file; + + for (int j = 1; + j < kit_benchs_list.size && j < KIT_BENCHS_SIZE_LIMIT; j++) { + kit_benchmark_t *bench = kit_benchs_list.v + j; + + if (specific_bench != NULL && + strstr(bench->bench_name, specific_bench) == NULL) + continue; + if (strcmp(s, bench->bench_file) == 0) + continue; + int k = 0; + for (; s[k] != '\0' && bench->bench_file[k] != '\0' && + s[k] == bench->bench_file[k]; + k++) { } + if (file_root == -1 || file_root > k) + file_root = k; + } + + if (file_root == -1) { + for (int i = 0; s[i] != '\0'; i++) + if (s[i] == '/' || s[i] == '\\') + file_root = i + 1; + } + } + + no_color || kit_print_color_(kit_blue_); + printf("# "); + no_color || kit_print_color_(kit_light_); + printf("BENCHMARK"); + printf("%*c", line_width - 9, ' '); + no_color || kit_print_color_(kit_green_); + printf(" LOW "); + no_color || kit_print_color_(kit_light_); + printf("|"); + no_color || kit_print_color_(kit_blue_); + printf(" MEDIAN "); + no_color || kit_print_color_(kit_light_); + printf("|"); + no_color || kit_print_color_(kit_yellow_); + printf(" HIGH\n"); + no_color || kit_print_color_(kit_light_); + printf(" (in microseconds)\n\n"); + + /* Prepare cycles. + */ + + for (int i = 0; + i < kit_benchs_list.size && i < KIT_BENCHS_SIZE_LIMIT; i++) { + kit_benchmark_t *bench = kit_benchs_list.v + i; + + if (specific_bench != NULL && + strstr(bench->bench_name, specific_bench) == NULL) + continue; + + kit_run_bench_(i); + + if (bench->cycles_size == 0) { + bench->cycles_size = 2; + bench->cycles[0] = KIT_BENCH_REPEATS_DEFAULT_1; + bench->cycles[1] = KIT_BENCH_REPEATS_DEFAULT_2; + } + + qsort(bench->cycles, bench->cycles_size, sizeof *bench->cycles, + kit_compare_32_); + + kit_benchs_list.v[i].ready = 1; + } + + /* Run cycles. + */ + + for (int cycle = 0; cycle < KIT_BENCH_MAX_CYCLES; cycle++) { + /* Prepare cycle. + */ + + int cycles_done = 1; + + for (int i = 0; + i < kit_benchs_list.size && i < KIT_BENCHS_SIZE_LIMIT; i++) { + kit_benchmark_t *bench = kit_benchs_list.v + i; + + if (specific_bench != NULL && + strstr(bench->bench_name, specific_bench) == NULL) + continue; + if (cycle >= bench->cycles_size) + continue; + + bench->repeats = 0; + bench->cycle = cycle; + cycles_done = 0; + } + + if (cycles_done) + break; + + /* Run benchmarks. + */ + + for (int i = 0; + i < kit_benchs_list.size && i < KIT_BENCHS_SIZE_LIMIT; i++) { + kit_benchmark_t *bench = kit_benchs_list.v + i; + + if (specific_bench != NULL && + strstr(bench->bench_name, specific_bench) == NULL) + continue; + if (cycle >= bench->cycles_size) + continue; + + if (file == NULL || strcmp(file, bench->bench_file) != 0) { + if (file != NULL) + printf("\n"); + file = bench->bench_file; + no_color || kit_print_color_(kit_blue_); + printf("* "); + no_color || kit_print_color_(kit_white_); + printf("%s\n", file + file_root); + } + + !carriage_return || no_color || kit_print_color_(kit_yellow_); + carriage_return || no_color || kit_print_color_(kit_light_); + printf("` %s ", bench->bench_name); + !carriage_return || printf("\r"); + fflush(stdout); + + int bench_status = kit_run_bench_(i); + + if (bench->repeats > KIT_BENCH_MAX_REPEATS) + bench_status = 0; + + !carriage_return || no_color || kit_print_color_(kit_light_); + !carriage_return || printf("` %s ", bench->bench_name); + + int l = (int) strlen(bench->bench_name); + printf("%*c", line_width - l, ' '); + + if (bench->repeats <= 0) { + no_color || kit_print_color_(kit_yellow_); + printf(" 0 runs\n"); + success_count++; + } else if (bench_status == 0) { + no_color || kit_print_color_(kit_red_); + printf(" FAIL\n"); + status = 1; + } else { + int repeats = bench->repeats; + + memcpy(bench->duration_sorted_nsec, bench->duration_nsec, + repeats * sizeof *bench->duration_sorted_nsec); + qsort(bench->duration_sorted_nsec, repeats, + sizeof *bench->duration_sorted_nsec, kit_compare_64_); + + int64_t average = bench->duration_sorted_nsec[repeats / 2]; + int64_t floor = bench->duration_sorted_nsec[repeats / 20]; + int64_t roof = + bench->duration_sorted_nsec[repeats - repeats / 20 - 1]; + + no_color || kit_print_color_(kit_white_); + printf("%-9g", (double) floor * 0.001); + no_color || kit_print_color_(kit_light_); + printf("| "); + no_color || kit_print_color_(kit_white_); + printf("%-9g", (double) average * 0.001); + no_color || kit_print_color_(kit_light_); + printf("| "); + no_color || kit_print_color_(kit_white_); + printf("%-9g", (double) roof * 0.001); + no_color || kit_print_color_(kit_light_); + printf(" %d runs\n", repeats); + success_count++; + } + } + } + + printf("\n"); + + if (status != 0) { + for (int i = 0; + i < kit_benchs_list.size && i < KIT_BENCHS_SIZE_LIMIT; i++) { + kit_benchmark_t *bench = kit_benchs_list.v + i; + + if (specific_bench != NULL && + strstr(bench->bench_name, specific_bench) == NULL) + continue; + if (bench->signal != 0) { + int signum = bench->signal; + if (signum >= 0 && + signum < sizeof kit_signames_ / sizeof *kit_signames_ && + kit_signames_[signum] != NULL) { + no_color || kit_print_color_(kit_light_); + printf("Signal \"%s\" (%d) for \"", kit_signames_[signum], + signum); + no_color || kit_print_color_(kit_white_); + printf("%s", bench->bench_name); + no_color || kit_print_color_(kit_light_); + printf("\" in \""); + no_color || kit_print_color_(kit_white_); + printf("%s", bench->bench_file + file_root); + no_color || kit_print_color_(kit_light_); + printf("\"!.\n"); + } else { + no_color || kit_print_color_(kit_light_); + printf("Unknown signal (%d) for \"", signum); + no_color || kit_print_color_(kit_white_); + printf("%s", bench->bench_name); + no_color || kit_print_color_(kit_light_); + printf("\" in \""); + no_color || kit_print_color_(kit_white_); + printf("%s", bench->bench_file + file_root); + no_color || kit_print_color_(kit_light_); + printf("\"!.\n"); + } + } + } + + printf("\n"); + } + + if (kit_benchs_list.size > KIT_BENCHS_SIZE_LIMIT) { + no_color || kit_print_color_(kit_light_); + printf("Too many benchmarks!\n\n"); + status = 1; + } + + if (status == 0) { + no_color || kit_print_color_(kit_green_); + printf("DONE\n"); + } else { + no_color || kit_print_color_(kit_red_); + printf("DONE WITH ERRORS\n"); + } + + no_color || kit_print_color_(kit_white_); + printf("\n"); + return status; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/tests/_exe.c b/source/tests/_exe.c index f14e620..99a34b8 100644 --- a/source/tests/_exe.c +++ b/source/tests/_exe.c @@ -18,6 +18,7 @@ #include "string_ref.test.c" #include "duration.test.c" #include "thread.test.c" +#include "defer.test.c" #include "xml.test.c" #include "http1.test.c" #include "bench.test.c" diff --git a/source/tests/array_ref.test.c b/source/tests/array_ref.test.c index 2037bed..921e9fd 100644 --- a/source/tests/array_ref.test.c +++ b/source/tests/array_ref.test.c @@ -1,7 +1,7 @@ #include "../kit/array_ref.h" #define KIT_TEST_FILE array_ref -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("array ref wrap") { int foo[] = { 1, 2, 3 }; diff --git a/source/tests/async_function.test.c b/source/tests/async_function.test.c index 9f90474..34cc7d1 100644 --- a/source/tests/async_function.test.c +++ b/source/tests/async_function.test.c @@ -1,7 +1,7 @@ #include "../kit/async_function.h" #define KIT_TEST_FILE async_function -#include "../kit/kit_test.h" +#include "../kit/test.h" AF_STATE(int, test_foo, ); static AF_DECL(test_foo); diff --git a/source/tests/atomic.test.c b/source/tests/atomic.test.c index 061f5d6..d5c172b 100644 --- a/source/tests/atomic.test.c +++ b/source/tests/atomic.test.c @@ -2,7 +2,7 @@ #include "../kit/threads.h" #define KIT_TEST_FILE atomic -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("atomic store and load") { ATOMIC(int) value; diff --git a/source/tests/bench.test.c b/source/tests/bench.test.c index f5d28aa..011817d 100644 --- a/source/tests/bench.test.c +++ b/source/tests/bench.test.c @@ -1,5 +1,5 @@ #define KIT_TEST_FILE bench -#include "../kit/kit_test.h" +#include "../kit/test.h" struct test_foo_ { double f; diff --git a/source/tests/bigint.test.c b/source/tests/bigint.test.c index 3e55c39..d7375f5 100644 --- a/source/tests/bigint.test.c +++ b/source/tests/bigint.test.c @@ -2,7 +2,7 @@ #include "../kit/bigint.h" #define KIT_TEST_FILE bigint -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("bigint size check") { REQUIRE_EQ(sizeof(u8), 1); diff --git a/source/tests/condition_variable.test.c b/source/tests/condition_variable.test.c index 9deb9ae..83bf2b3 100644 --- a/source/tests/condition_variable.test.c +++ b/source/tests/condition_variable.test.c @@ -1,7 +1,7 @@ #include "../kit/threads.h" #define KIT_TEST_FILE condition_variable -#include "../kit/kit_test.h" +#include "../kit/test.h" typedef struct { mtx_t m; diff --git a/source/tests/defer.test.c b/source/tests/defer.test.c new file mode 100644 index 0000000..0eb8557 --- /dev/null +++ b/source/tests/defer.test.c @@ -0,0 +1,37 @@ +#if defined(__clang__) || defined(__APPLE__) +# include "../kit/defer.h" + +# define KIT_TEST_FILE defer_block +# include "../kit/test.h" + +TEST("defer") { + int defer_ref x = 1; + + { + defer { + x = 2; + }; + + x = 3; + } + + REQUIRE_EQ(x, 2); +} + +TEST("defer capture") { + int defer_ref x = 1; + + { + defer { + if (x == 3) + x = 2; + }; + + x = 3; + } + + REQUIRE_EQ(x, 2); +} + +# undef KIT_TEST_FILE +#endif diff --git a/source/tests/duration.test.c b/source/tests/duration.test.c index 1bdb68b..32865c7 100644 --- a/source/tests/duration.test.c +++ b/source/tests/duration.test.c @@ -1,5 +1,5 @@ #define KIT_TEST_FILE duration -#include "../kit/kit_test.h" +#include "../kit/test.h" #if defined(_WIN32) && !defined(__CYGWIN__) __declspec(dllimport) void __stdcall Sleep(unsigned long timeout); diff --git a/source/tests/dynamic_array.test.c b/source/tests/dynamic_array.test.c index a1c0ebc..0d07233 100644 --- a/source/tests/dynamic_array.test.c +++ b/source/tests/dynamic_array.test.c @@ -1,7 +1,7 @@ #include "../kit/dynamic_array.h" #define KIT_TEST_FILE dynamic_array -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("dynamic array empty") { DA_CREATE(v, char, 0); diff --git a/source/tests/file.test.c b/source/tests/file.test.c index 646d0ef..8edfcca 100644 --- a/source/tests/file.test.c +++ b/source/tests/file.test.c @@ -2,7 +2,7 @@ #include "../kit/string_ref.h" #define KIT_TEST_FILE file -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("path cache") { str_builder_t user = path_user(NULL); diff --git a/source/tests/http1.test.c b/source/tests/http1.test.c index 09da5f4..20c1ea7 100644 --- a/source/tests/http1.test.c +++ b/source/tests/http1.test.c @@ -1,7 +1,7 @@ #include "../kit/http1.h" #define KIT_TEST_FILE http1 -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("http1") { // TODO diff --git a/source/tests/input_buffer.test.c b/source/tests/input_buffer.test.c index 89717d1..b8dc19b 100644 --- a/source/tests/input_buffer.test.c +++ b/source/tests/input_buffer.test.c @@ -1,7 +1,7 @@ #include "../kit/input_buffer.h" #define KIT_TEST_FILE input_buffer -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("input buffer read once") { str_t text = { .size = 3, .values = "foo" }; diff --git a/source/tests/input_stream.test.c b/source/tests/input_stream.test.c index 72e43d5..25ef721 100644 --- a/source/tests/input_stream.test.c +++ b/source/tests/input_stream.test.c @@ -2,7 +2,7 @@ #include "../kit/file.h" #define KIT_TEST_FILE input_stream -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("input stream wrap string") { char foo[] = "test"; diff --git a/source/tests/lower_bound.test.c b/source/tests/lower_bound.test.c index d709446..ae7ed72 100644 --- a/source/tests/lower_bound.test.c +++ b/source/tests/lower_bound.test.c @@ -2,7 +2,7 @@ #include "../kit/array_ref.h" #define KIT_TEST_FILE lower_bound -#include "../kit/kit_test.h" +#include "../kit/test.h" static int kit_less_int(int left, int right) { return left < right; diff --git a/source/tests/main.test.c b/source/tests/main.test.c index 6d439b6..d6fabc3 100644 --- a/source/tests/main.test.c +++ b/source/tests/main.test.c @@ -1,5 +1,5 @@ #define KIT_TEST_IMPLEMENTATION -#include "../kit/kit_test.h" +#include "../kit/test.h" int main(int argc, char **argv) { int status = run_tests(argc, argv); diff --git a/source/tests/mersenne_twister_64.test.c b/source/tests/mersenne_twister_64.test.c index 4d0d97d..df9e508 100644 --- a/source/tests/mersenne_twister_64.test.c +++ b/source/tests/mersenne_twister_64.test.c @@ -2,7 +2,7 @@ #include "../kit/secure_random.h" #define KIT_TEST_FILE mersenne_twister_64 -#include "../kit/kit_test.h" +#include "../kit/test.h" enum { MT64_TEST_SIZE = 1000 }; diff --git a/source/tests/move_back.test.c b/source/tests/move_back.test.c index 626850a..a97a8b9 100644 --- a/source/tests/move_back.test.c +++ b/source/tests/move_back.test.c @@ -1,7 +1,7 @@ #include "../kit/move_back.h" #define KIT_TEST_FILE move_back -#include "../kit/kit_test.h" +#include "../kit/test.h" static int is_equal(int x, int y) { return x == y; diff --git a/source/tests/mutex.test.c b/source/tests/mutex.test.c index 578e48b..61c1caa 100644 --- a/source/tests/mutex.test.c +++ b/source/tests/mutex.test.c @@ -1,7 +1,7 @@ #include "../kit/threads.h" #define KIT_TEST_FILE mutex -#include "../kit/kit_test.h" +#include "../kit/test.h" enum { SLEEP = 400000000, TICK_COUNT = 200, THREAD_COUNT = 100 }; diff --git a/source/tests/secure_random.test.c b/source/tests/secure_random.test.c index 62021b1..3452521 100644 --- a/source/tests/secure_random.test.c +++ b/source/tests/secure_random.test.c @@ -2,7 +2,7 @@ #include #define KIT_TEST_FILE secure_random -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("secure random") { int v[20]; diff --git a/source/tests/sha256.test.c b/source/tests/sha256.test.c index cf67c03..dff3ca6 100644 --- a/source/tests/sha256.test.c +++ b/source/tests/sha256.test.c @@ -2,7 +2,7 @@ #include "../kit/array_ref.h" #define KIT_TEST_FILE sha256_64 -#include "../kit/kit_test.h" +#include "../kit/test.h" #include diff --git a/source/tests/string_ref.test.c b/source/tests/string_ref.test.c index a5863df..8ce9368 100644 --- a/source/tests/string_ref.test.c +++ b/source/tests/string_ref.test.c @@ -1,7 +1,7 @@ #include "../kit/string_ref.h" #define KIT_TEST_FILE string_ref -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("static string wrap") { str_t ref = SZ("foo bar"); diff --git a/source/tests/test_cpp.cpp b/source/tests/test_cpp.cpp index 3e88ede..d1eab09 100644 --- a/source/tests/test_cpp.cpp +++ b/source/tests/test_cpp.cpp @@ -1,5 +1,5 @@ #define KIT_TEST_IMPLEMENTATION -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("foo") { REQUIRE(20 + 22 == 42); diff --git a/source/tests/test_signals.cpp b/source/tests/test_signals.cpp index 2a9c009..e0298c4 100644 --- a/source/tests/test_signals.cpp +++ b/source/tests/test_signals.cpp @@ -1,5 +1,5 @@ #define KIT_TEST_IMPLEMENTATION -#include "../kit/kit_test.h" +#include "../kit/test.h" #include #include diff --git a/source/tests/test_too_many_assertions.c b/source/tests/test_too_many_assertions.c index ffff093..f54bc36 100644 --- a/source/tests/test_too_many_assertions.c +++ b/source/tests/test_too_many_assertions.c @@ -1,6 +1,6 @@ #define KIT_TEST_IMPLEMENTATION #define KIT_TEST_ASSERTIONS_LIMIT 10 -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("foo") { int i; diff --git a/source/tests/test_too_many_tests.c b/source/tests/test_too_many_tests.c index b03a3b2..8d1aec0 100644 --- a/source/tests/test_too_many_tests.c +++ b/source/tests/test_too_many_tests.c @@ -1,6 +1,6 @@ #define KIT_TEST_IMPLEMENTATION #define KIT_TESTS_SIZE_LIMIT 40 -#include "../kit/kit_test.h" +#include "../kit/test.h" void bar(int index, kit_test_report_fn report) { } diff --git a/source/tests/thread.test.c b/source/tests/thread.test.c index 8f849cd..dca2fe0 100644 --- a/source/tests/thread.test.c +++ b/source/tests/thread.test.c @@ -1,7 +1,7 @@ #include "../kit/threads.h" #define KIT_TEST_FILE thread -#include "../kit/kit_test.h" +#include "../kit/test.h" static int test_nothing(void *_) { return 0; diff --git a/source/tests/xml.test.c b/source/tests/xml.test.c index 2a0b114..a3e931d 100644 --- a/source/tests/xml.test.c +++ b/source/tests/xml.test.c @@ -1,7 +1,7 @@ #include "../kit/xml.h" #define KIT_TEST_FILE xml -#include "../kit/kit_test.h" +#include "../kit/test.h" TEST("xml parse tag") { is_handle_t is = IS_WRAP_STRING(SZ(" ")); -- cgit v1.2.3