diff options
Diffstat (limited to 'libmisc/tests')
-rw-r--r-- | libmisc/tests/test_assert.c | 70 | ||||
-rw-r--r-- | libmisc/tests/test_fmt.c | 170 | ||||
-rw-r--r-- | libmisc/tests/test_log.c | 79 | ||||
-rw-r--r-- | libmisc/tests/test_macro.c | 128 |
4 files changed, 382 insertions, 65 deletions
diff --git a/libmisc/tests/test_assert.c b/libmisc/tests/test_assert.c index c6d2dc1..cdbc567 100644 --- a/libmisc/tests/test_assert.c +++ b/libmisc/tests/test_assert.c @@ -9,23 +9,22 @@ #include <stdlib.h> #include <string.h> -#include <libmisc/macro.h> -#include <libmisc/assert.h> #include <libmisc/_intercept.h> +#include <libmisc/assert.h> +#include <libmisc/fmt.h> +#include <libmisc/macro.h> #include "test.h" /* Intercept failures and logging *********************************************/ -bool global_failed; -char *global_log; -jmp_buf global_env; +static bool global_failed; +static struct fmt_buf global_log; +static jmp_buf global_env; #define with_intercept() ({ \ global_failed = false; \ - if (global_log) \ - free(global_log); \ - global_log = NULL; \ + global_log_clear(); \ setjmp(global_env) == 0; \ }) @@ -34,12 +33,19 @@ void __lm_abort(void) { longjmp(global_env, 1); } -int __lm_light_printf(const char *format, ...) { - va_list va; - va_start(va, format); - int ret = vasprintf(&global_log, format, va); - va_end(va); - return ret; +void __lm_putchar(unsigned char c) { + if (global_log.len+1 >= global_log.cap) { + global_log.cap += 16; + global_log.dat = realloc(global_log.dat, global_log.cap); + memset(global_log.dat + global_log.len, 0, global_log.cap - global_log.len); + } + ((uint8_t *)global_log.dat)[global_log.len++] = (uint8_t)c; +} + +static void global_log_clear(void) { + if (global_log.dat) + memset(global_log.dat, 0, global_log.cap); + global_log.len = 0; } #define __builtin_unreachable() test_assert(0) @@ -51,21 +57,21 @@ int __lm_light_printf(const char *format, ...) { test; \ } \ test_assert(global_failed == false); \ - test_assert(global_log == NULL); \ + test_assert(global_log.len == 0); \ } while (0) -#define test_should_fail(test, exp_log) do { \ - if (with_intercept()) { \ - test; \ - } \ - test_assert(global_failed == true); \ - if (!(global_log != NULL && \ - strcmp(global_log, exp_log) == 0)) { \ - printf("exp = \"%s\"\n" \ - "act = \"%s\"\n", \ - exp_log, global_log); \ - test_assert(0); \ - } \ +#define test_should_fail(test, exp_log) do { \ + if (with_intercept()) { \ + test; \ + } \ + test_assert(global_failed == true); \ + if (!(global_log.len != 0 && \ + strcmp(global_log.dat, exp_log) == 0)) { \ + printf("exp = \"%s\"\n" \ + "act = \"%s\"\n", \ + exp_log, (char *)global_log.dat); \ + test_assert(0); \ + } \ } while (0) /* Actual tests ***************************************************************/ @@ -83,11 +89,13 @@ int main() { test_should_fail(assert_msg(false, NULL), "error: ASSERT: "__FILE__":"LM_STR_(__LINE__)":main(): assertion \"false\" failed\n"); test_should_fail(assert_notreached("xxx"), "error: ASSERT: "__FILE__":"LM_STR_(__LINE__)":main(): assertion \"notreached\" failed: xxx\n"); +#endif - if (global_log) { - free(global_log); - global_log = NULL; + if (global_log.dat) { + global_log_clear(); + free(global_log.dat); + global_log.dat = NULL; + global_log.cap = 0; } -#endif return 0; } diff --git a/libmisc/tests/test_fmt.c b/libmisc/tests/test_fmt.c new file mode 100644 index 0000000..6a6eb7c --- /dev/null +++ b/libmisc/tests/test_fmt.c @@ -0,0 +1,170 @@ +/* libmisc/tests/test_fmt.c - Tests for <libmisc/fmt.h> + * + * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <string.h> /* for strcmp(), memcmp(), memset() */ + +#include <libmisc/fmt.h> + +#include "test.h" + +int main() { + char str[128] = {}; +#define do_print(...) fmt_snprint(str, sizeof(str), __VA_ARGS__) + + do_print("hello ", 9, " world!\n"); + test_assert(strcmp(str, "hello 9 world!\n") == 0); + memset(str, 0, sizeof(str)); + + do_print("hello ", (base8, 9), " world!\n"); + test_assert(strcmp(str, "hello 11 world!\n") == 0); + memset(str, 0, sizeof(str)); + + do_print("hello ", (base2, 9), (qstr, " world!\n")); + test_assert(strcmp(str, "hello 1001\" world!\\n\"") == 0); + memset(str, 0, sizeof(str)); + + do_print("hello ", (base16, 17), " world!\n"); + test_assert(strcmp(str, "hello 11 world!\n") == 0); + memset(str, 0, sizeof(str)); + + do_print((strn, "hello ", 4)); + test_assert(strcmp(str, "hell") == 0); + memset(str, 0, sizeof(str)); + + do_print((strn, "h\0ello ", 4)); + test_assert(memcmp(str, "h\0\0", 3) == 0); + memset(str, 0, sizeof(str)); + + do_print((mem, "hello ", 4)); + test_assert(strcmp(str, "hell") == 0); + memset(str, 0, sizeof(str)); + + do_print((mem, "hello\0world", strlen("hello world")+1)); + test_assert(memcmp(str, "hello\0world", strlen("hello world")+1) == 0); + memset(str, 0, sizeof(str)); + + do_print((qmem, "hello\0world", strlen("hello world")+1)); + test_assert(strcmp(str, "\"hello\\0world\\0\"") == 0); + memset(str, 0, sizeof(str)); + + do_print((qstr, "hello\0world")); + test_assert(strcmp(str, "\"hello\"") == 0); + memset(str, 0, sizeof(str)); + + do_print((qstrn, "hello\0world", strlen("hello world")+1)); + test_assert(strcmp(str, "\"hello\"") == 0); + memset(str, 0, sizeof(str)); + + do_print((qstrn, "hello\0world", 4)); + test_assert(strcmp(str, "\"hell\"") == 0); + memset(str, 0, sizeof(str)); + + do_print((byte, 'h'), (byte, 'w')); + test_assert(strcmp(str, "hw") == 0); + memset(str, 0, sizeof(str)); + + do_print((qbyte, 'h'), (qbyte, 'w')); + test_assert(strcmp(str, "'h''w'") == 0); + memset(str, 0, sizeof(str)); + + do_print("zero ", 0); + test_assert(strcmp(str, "zero 0") == 0); + memset(str, 0, sizeof(str)); + + const char *const_str = "hello"; + do_print(const_str); + test_assert(strcmp(str, "hello") == 0); + memset(str, 0, sizeof(str)); + + bool t = true; + do_print(t); + test_assert(strcmp(str, "true") == 0); + memset(str, 0, sizeof(str)); + + bool f = false; + do_print(f); + test_assert(strcmp(str, "false") == 0); + memset(str, 0, sizeof(str)); + + /* Check that it accepts all primitive types of integer, not + * just all sizes of integer (e.g., on x86-64, + * sizeof(long)==sizeof(int), but they're different primitive + * types). */ + { + signed char x = 42; + do_print("schar ", x); + test_assert(strcmp(str, "schar 42") == 0); + memset(str, 0, sizeof(str)); + } + { + unsigned char x = 43; + do_print("uchar ", x); + test_assert(strcmp(str, "uchar 43") == 0); + memset(str, 0, sizeof(str)); + } + + { + short x = 44; + do_print("short ", x); + test_assert(strcmp(str, "short 44") == 0); + memset(str, 0, sizeof(str)); + } + { + unsigned short x = 45; + do_print("ushort ", x); + test_assert(strcmp(str, "ushort 45") == 0); + memset(str, 0, sizeof(str)); + } + + { + int x = 46; + do_print("int ", x); + test_assert(strcmp(str, "int 46") == 0); + memset(str, 0, sizeof(str)); + } + { + unsigned int x = 47; + do_print("uint ", x); + test_assert(strcmp(str, "uint 47") == 0); + memset(str, 0, sizeof(str)); + } + + { + long x = 48; + do_print("long ", x); + test_assert(strcmp(str, "long 48") == 0); + memset(str, 0, sizeof(str)); + } + { + unsigned long x = 49; + do_print("ulong ", x); + test_assert(strcmp(str, "ulong 49") == 0); + memset(str, 0, sizeof(str)); + } + + { + long long x = 50; + do_print("long long ", x); + test_assert(strcmp(str, "long long 50") == 0); + memset(str, 0, sizeof(str)); + } + { + unsigned long long x = 51; + do_print("ulong long ", x); + test_assert(strcmp(str, "ulong long 51") == 0); + memset(str, 0, sizeof(str)); + } + + do_print((ljust, 10, ' ', (base10, 1), "x")); + test_assert(strcmp(str, "1x ") == 0); + memset(str, 0, sizeof(str)); + + do_print((rjust, 10, ' ', (base10, 1), "x")); + test_assert(strcmp(str, " 1x") == 0); + memset(str, 0, sizeof(str)); + + return 0; +} diff --git a/libmisc/tests/test_log.c b/libmisc/tests/test_log.c index 02b95d6..ee762e2 100644 --- a/libmisc/tests/test_log.c +++ b/libmisc/tests/test_log.c @@ -4,11 +4,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#define _GNU_SOURCE /* for vasprintf() */ #include <stdarg.h> /* for va_list */ -#include <stdio.h> /* for vasprintf() */ -#include <stdlib.h> /* for free() */ -#include <string.h> /* for strcmp() */ +#include <stdio.h> /* for vsnprintf() */ +#include <stdlib.h> /* for realloc(), free() */ +#include <string.h> /* for strlen(), strcmp() */ #define LOG_NAME FROBNICATE #include <libmisc/log.h> @@ -19,52 +18,64 @@ /* Intercept output ***********************************************************/ -static char *log_output = NULL; +static struct fmt_buf log_output = {}; -int __lm_printf(const char *format, ...) { - va_list va; - va_start(va, format); - int ret = vasprintf(&log_output, format, va); - va_end(va); - return ret; +void __lm_putchar(unsigned char c) { + if (log_output.len+1 >= log_output.cap) { + log_output.cap += 16; + log_output.dat = realloc(log_output.dat, log_output.cap); + memset(log_output.dat + log_output.len, 0, log_output.cap - log_output.len); + } + ((uint8_t *)log_output.dat)[log_output.len++] = (uint8_t)c; +} + +static void log_output_clear(void) { + if (log_output.dat) + memset(log_output.dat, 0, log_output.cap); + log_output.len = 0; } /* Actual tests ***************************************************************/ -#define should_print(_exp, cmd) do { \ - char *exp = _exp; \ - test_assert(!log_output); \ - cmd; \ - if (!exp) \ - test_assert(!log_output); \ - else { \ - test_assert(log_output); \ - if (strcmp(log_output, exp)) { \ - printf("exp = \"%s\"\n" \ - "act = \"%s\"\n", \ - exp, log_output); \ - test_assert(0); \ - } \ - } \ - if (log_output) { \ - free(log_output); \ - log_output = NULL; \ - } \ +#define should_print(_exp, cmd) do { \ + char *exp = _exp; \ + test_assert(log_output.len == 0); \ + cmd; \ + if (!exp) \ + test_assert(log_output.len == 0); \ + else { \ + test_assert(log_output.dat); \ + test_assert(strlen(log_output.dat) == log_output.len); \ + if (strcmp(log_output.dat, exp)) { \ + printf("exp = \"%s\"\n" \ + "act = \"%s\"\n", \ + exp, (char *)log_output.dat); \ + test_assert(0); \ + } \ + } \ + log_output_clear(); \ } while (0) int main() { should_print("error: FROBNICATE: val=42\n", - log_errorf("val=%d", 42)); + log_errorln("val=", 42)); should_print("info : FROBNICATE: val=0\n", - log_infof("val=%d", 0)); + log_infoln("val=", 0)); #ifndef NDEBUG #define CONFIG_FROBNICATE_DEBUG 1 should_print("debug: FROBNICATE: val=-2\n", - log_debugf("val=%d", -2)); + log_debugln("val=", -2)); #undef CONFIG_FROBNICATE_DEBUG #define CONFIG_FROBNICATE_DEBUG 0 should_print(NULL, - log_debugf("val=%d", -2)); + log_debugln("val=", -2)); +#undef CONFIG_FROBNICATE_DEBUG #endif + + if (log_output.dat) { + free(log_output.dat); + log_output.dat = NULL; + log_output.cap = 0; + } return 0; } diff --git a/libmisc/tests/test_macro.c b/libmisc/tests/test_macro.c index 1320eb3..5157820 100644 --- a/libmisc/tests/test_macro.c +++ b/libmisc/tests/test_macro.c @@ -4,10 +4,34 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +#include <stdlib.h> /* for free() */ +#include <string.h> /* for strcmp(), strlen(), memcmp(), strdup() */ + #include <libmisc/macro.h> #include "test.h" +/** Given `N` from `#define _LM_EVAL _LM_EVAL__{N}`, UNDER is `(N*2)-2`. (16*2)-2=30. */ +#define UNDER 30 +/** Given `N` from `#define _LM_EVAL _LM_EVAL__{N}`, OVER is `(N*2)-1`. (16*2)-1=31. */ +#define OVER 31 + +/** XUNDER is 0 through `UNDER` inclusive. */ +#define XUNDER \ + X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) \ + X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) +/** XUNDER is 0 through `OVER` inclusive. */ +#define XOVER XUNDER X(OVER) + +static char *without_spaces(const char *in) { + char *out = strdup(in); + for (size_t i = 0; out[i]; i++) + while (out[i] == ' ') + for (size_t j = i; out[j]; j++) + out[j] = out[j+1]; + return out; +} + int main() { printf("== LM_NEXT_POWER_OF_2 =====================================\n"); /* valid down to 0. */ @@ -50,5 +74,109 @@ int main() { /* ... */ test_assert(LM_FLOORLOG2(0xFFFFFFFFFFFFFFFF) == 63); + printf("== LM_TUPLE ===============================================\n"); + test_assert(LM_IF(LM_IS_TUPLE( 9 ))(0)(1)); + test_assert(LM_IF(LM_IS_TUPLE( a ))(0)(1)); + test_assert(LM_IF(LM_IS_TUPLE( () ))(1)(0)); + test_assert(LM_IF(LM_IS_TUPLE( (9) ))(1)(0)); + test_assert(LM_IF(LM_IS_TUPLE( (a) ))(1)(0)); + test_assert(LM_IF(LM_IS_TUPLE( (a, b) ))(1)(0)); + + test_assert(LM_IF(LM_IS_EMPTY_TUPLE( () ))(1)(0)); + test_assert(LM_IF(LM_IS_EMPTY_TUPLE( 9 ))(0)(1)); + test_assert(LM_IF(LM_IS_EMPTY_TUPLE( a ))(0)(1)); + test_assert(LM_IF(LM_IS_EMPTY_TUPLE( (9) ))(0)(1)); + test_assert(LM_IF(LM_IS_EMPTY_TUPLE( (a) ))(0)(1)); + test_assert(LM_IF(LM_IS_EMPTY_TUPLE( (a, b) ))(0)(1)); + + printf("== LM_TUPLES ==============================================\n"); + test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( ))(0)(1)); + test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( () ))(1)(0)); + test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a) ))(1)(0)); + test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a)(b) ))(1)(0)); + test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a)(b)(c) ))(1)(0)); + + printf("== LM_FOREACH_PARAM =======================================\n"); + /* Basic test. */ + { + #define FN(A, B) A "-" #B + const char *str = LM_FOREACH_PARAM(FN, (" "), a, (b), c); + #undef FN + test_assert(strcmp(str, " -a -(b) -c") == 0); + } + + /* Test that it works with the documented limit of params. */ + { + #define X(n) , n + #define FN(n) #n "\n" + const char *str = LM_FOREACH_PARAM_(FN, () XUNDER); + #undef FN + #undef X + #define X(n) #n "\n" + test_assert(strcmp(str, XUNDER) == 0); + #undef X + } + + /* Test that it breaks at documented_limit+1 tuples. */ + { + #define X(n) , n + #define FN(n) n + const char *str = LM_STR_(LM_FOREACH_PARAM_(FN, () XOVER)); + #undef FN + #undef X + /* This comparison is a little extra complicated in + * order to not be sensitive to whitespace in the + * suffix. */ + #define X(n) #n " " + const char *exp_prefix = XUNDER; + #undef X + const char *exp_suffix = "FN(" LM_STR_(OVER) ")_LM_FOREACH_PARAM_ITEM_indirect()(FN,(),())"; + test_assert(strlen(exp_prefix) < strlen(str) && memcmp(exp_prefix, str, strlen(exp_prefix)) == 0); + char *act_suffix = without_spaces(&str[strlen(exp_prefix)]); + test_assert(strcmp(act_suffix, exp_suffix) == 0); + free(act_suffix); + } + + printf("== LM_FOREACH_TUPLE =======================================\n"); + /* Basic test. */ + { + #define FN(a, b) a "-" b + const char *str = LM_FOREACH_TUPLE( ("a") ("b") ("c"), FN, " "); + #undef FN + test_assert(strcmp(str, " -a -b -c") == 0); + } + + /* Test that it works with the documented limit of tuples. */ + { + #define X(n) (n) + #define FN(n) #n "\n" + const char *str = LM_FOREACH_TUPLE(XUNDER, FN); + #undef FN + #undef X + #define X(n) #n "\n" + test_assert(strcmp(str, XUNDER) == 0); + #undef X + } + + /* Test that it breaks at documented_limit+1 tuples. */ + { + #define X(n) (n) + #define FN(n) n + const char *str = LM_STR_(LM_FOREACH_TUPLE(XOVER, FN)); + #undef FN + #undef X + /* This comparison is a little extra complicated in + * order to not be sensitive to whitespace in the + * suffix. */ + #define X(n) #n " " + const char *exp_prefix = XUNDER; + #undef X + const char *exp_suffix = "FN(" LM_STR_(OVER) ")_LM_FOREACH_TUPLE_indirect()(,FN,)"; + test_assert(strlen(exp_prefix) < strlen(str) && memcmp(exp_prefix, str, strlen(exp_prefix)) == 0); + char *act_suffix = without_spaces(&str[strlen(exp_prefix)]); + test_assert(strcmp(act_suffix, exp_suffix) == 0); + free(act_suffix); + } + return 0; } |