diff options
| author | Lion Kortlepel <[email protected]> | 2026-01-14 22:08:55 +0100 |
|---|---|---|
| committer | Lion Kortlepel <[email protected]> | 2026-01-14 22:08:55 +0100 |
| commit | f93efff02151e4a3affa4b2d0031e957fe7553aa (patch) | |
| tree | 806c714926848a98447d4fbc190c76ba891c0fd3 /tests | |
| download | queue-f93efff02151e4a3affa4b2d0031e957fe7553aa.tar.zst queue-f93efff02151e4a3affa4b2d0031e957fe7553aa.zip | |
initial commit
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/ls_test.h | 288 | ||||
| -rw-r--r-- | tests/tests.c | 16 |
2 files changed, 304 insertions, 0 deletions
diff --git a/tests/ls_test.h b/tests/ls_test.h new file mode 100644 index 0000000..a2a6f2a --- /dev/null +++ b/tests/ls_test.h @@ -0,0 +1,288 @@ +#pragma once + +/* Lion's Standard (LS) test harness. + * + * Version: 1.0 + * Repo: https://github.com/lionkor/ls_test + * SPDX-License-Identifier: MIT + * + * ==== TABLE OF CONTENTS ==== + * + * 1. DESCRIPTION + * 2. HOW TO USE + * 3. LICENSE + * + * ==== 1. DESCRIPTION ==== + * + * This is a super simple, minimal unit-test harness. It has auto-registering + * tests and some macros for easy usage. + * + * Compiles under ANSI C, the only special part is the extension __typeof__ if + * you use asserts other than `ASSERT` (e.g. ASSERT_EQ), and the constructor + * attribute __attribute__((destructor)) for automatic test registration. + * + * ==== 2. HOW TO USE ==== + * + * 1. Copy this file into your project and include it: + * + * #include "ls_test.h" + * + * 2. In ONE C file, define LS_TEST_IMPLEMENTATION before including: + * + * #define LS_TEST_IMPLEMENTATION + * #include "ls_test.h" + * + * 3. Write tests as functions with no arguments/returns: + * + * TEST_CASE(test_add) { + * ASSERT_EQ(add(1, 2), 3, "%d"); + * return 0; // needed for --failfast + * } + * + * Use unique names, avoid starting with ls_ or lst_. + * + * 4. Add a main: + * + * TEST_MAIN + * + * 5. Compile and run. Use --help for options. + * + * ==== 3. LICENSE ==== + * + * This file is provided under the MIT license. For commercial support and + * maintenance, feel free to use the e-mail below to contact the author(s). + * + * The MIT License (MIT) + * + * Copyright (c) 2026 Lion Kortlepel <[email protected]> + * + * 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. + */ + +#include <stdio.h> + +#define TEST_MAIN \ + int main(int argc, char** argv) { return ls_test_main(argc, argv); } + +#define TEST_CASE(name) \ + static int LS_CAT(lst_t_, name)(void); \ + static void LS_CAT(lst_init_, name)(void) LS_CONSTRUCTOR; \ + static void LS_CAT(lst_init_, name)(void) { \ + lst_reg(LS_CAT(lst_t_, name)); \ + } \ + static int LS_CAT(lst_t_, name)(void) + +#define ASSERT(cond) \ + do { \ + if (!(cond)) { \ + const char* _func = __func__; \ + if (strncmp(_func, "lst_t_", 6) == 0) \ + _func += 6; \ + fprintf(stderr, "%s: FAILED: %s (%s:%d)\n", _func, #cond, \ + __FILE__, __LINE__); \ + } \ + } while (0) + +/* the following macros require __typeof__ */ + +#define ASSERT_EQ(a, b, fmt) \ + do { \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + if (_a != _b) { \ + const char* _func = __func__; \ + if (strncmp(_func, "lst_t_", 6) == 0) \ + _func += 6; \ + fprintf(stderr, \ + "%s: FAILED: %s == %s (actual: " fmt " != " fmt ") (%s:%d)\n", \ + _func, #a, #b, _a, _b, __FILE__, __LINE__); \ + ++lst_fail; \ + return 1; \ + } \ + ++lst_ok; \ + } while (0) + +#define ASSERT_NEQ(a, b, fmt) \ + do { \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + if (_a == _b) { \ + const char* _func = __func__; \ + if (strncmp(_func, "lst_t_", 6) == 0) \ + _func += 6; \ + fprintf(stderr, \ + "%s: FAILED: %s != %s (actual: " fmt " == " fmt ") (%s:%d)\n", \ + _func, #a, #b, _a, _b, __FILE__, __LINE__); \ + ++lst_fail; \ + return 1; \ + } \ + ++lst_ok; \ + } while (0) + +#define ASSERT_LT(a, b, fmt) \ + do { \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + if (!(_a < _b)) { \ + const char* _func = __func__; \ + if (strncmp(_func, "lst_t_", 6) == 0) \ + _func += 6; \ + fprintf(stderr, \ + "%s: FAILED: %s < %s (actual: " fmt " >= " fmt ") (%s:%d)\n", \ + _func, #a, #b, _a, _b, __FILE__, __LINE__); \ + ++lst_fail; \ + return 1; \ + } \ + ++lst_ok; \ + } while (0) + +#define ASSERT_LE(a, b, fmt) \ + do { \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + if (!(_a <= _b)) { \ + const char* _func = __func__; \ + if (strncmp(_func, "lst_t_", 6) == 0) \ + _func += 6; \ + fprintf(stderr, \ + "%s: FAILED: %s <= %s (actual: " fmt " > " fmt ") (%s:%d)\n", \ + _func, #a, #b, _a, _b, __FILE__, __LINE__); \ + ++lst_fail; \ + return 1; \ + } \ + ++lst_ok; \ + } while (0) + +#define ASSERT_GT(a, b, fmt) \ + do { \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + if (!(_a > _b)) { \ + const char* _func = __func__; \ + if (strncmp(_func, "lst_t_", 6) == 0) \ + _func += 6; \ + fprintf(stderr, \ + "%s: FAILED: %s > %s (actual: " fmt " <= " fmt ") (%s:%d)\n", \ + _func, #a, #b, _a, _b, __FILE__, __LINE__); \ + ++lst_fail; \ + return 1; \ + } \ + ++lst_ok; \ + } while (0) + +#define ASSERT_GE(a, b, fmt) \ + do { \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + if (!(_a >= _b)) { \ + const char* _func = __func__; \ + if (strncmp(_func, "lst_t_", 6) == 0) \ + _func += 6; \ + fprintf(stderr, \ + "%s: FAILED: %s >= %s (actual: " fmt " < " fmt ") (%s:%d)\n", \ + _func, #a, #b, _a, _b, __FILE__, __LINE__); \ + ++lst_fail; \ + return 1; \ + } \ + ++lst_ok; \ + } while (0) +#define LS_CAT2(a, b) a##b +#define LS_CAT(a, b) LS_CAT2(a, b) + +#if defined(__GNUC__) || defined(__clang__) +#define LS_CONSTRUCTOR __attribute__((constructor)) +#else +#error "Requires __attribute__((constructor)) support" +#endif + +typedef int (*lst_func)(void); + +extern lst_func* lst_funcs; +extern int lst_n; +extern int lst_cap; +extern int lst_fail; +extern int lst_ok; + +void lst_reg(lst_func f); + +#ifdef LS_TEST_IMPLEMENTATION +#include <stdlib.h> +#include <string.h> + +lst_func* lst_funcs; +int lst_n; +int lst_cap; +int lst_fail = 0; +int lst_ok = 0; + +void lst_reg(lst_func f) { + if (lst_n == lst_cap) { + if (lst_cap == 0) { + lst_cap = 8; + } else { + lst_cap *= 2; + } + lst_funcs = (lst_func*)realloc(lst_funcs, lst_cap * sizeof(*lst_funcs)); + } + lst_funcs[lst_n++] = f; +} + +#define HELP_STR \ + "Usage: %s [options]\n" \ + "Options:\n" \ + " --failfast Stop after the first failed test\n" \ + " --help Show this help message\n" + +static int ls_test_main(int argc, char** argv) { + (void)argc; + (void)argv; + + int failfast = 0; + int i; + + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--failfast") == 0) { + failfast = 1; + } else if (strcmp(argv[i], "--help") == 0) { + fprintf(stderr, HELP_STR, argv[0]); + return 0; + } else { + fprintf( + stderr, "unknown argument: %s\n\n" HELP_STR, argv[i], argv[0]); + return 1; + } + } + + for (i = 0; i < lst_n; ++i) { + if (lst_funcs[i]() != 0 && failfast) { + goto end; + } + } + +end: + fprintf(stderr, "%d succeeded, %d failed, %d total\n", lst_ok, lst_fail, + lst_ok + lst_fail); + + if (lst_fail > 0) { + return 1; + } + + return 0; +} +#endif diff --git a/tests/tests.c b/tests/tests.c new file mode 100644 index 0000000..0d57df4 --- /dev/null +++ b/tests/tests.c @@ -0,0 +1,16 @@ +#define LS_TEST_IMPLEMENTATION +#include "ls_test.h" + +int add(int a, int b) { + return a + b; +} + +TEST_CASE(add) { + ASSERT_EQ(add(1, 2), 3, "%d"); + ASSERT_EQ(add(2, 3), 5, "%d"); + ASSERT_EQ(add(0, 0), 100000, "%d"); + + return 0; +} + +TEST_MAIN |
