summaryrefslogtreecommitdiff
path: root/libmisc/tests
diff options
context:
space:
mode:
Diffstat (limited to 'libmisc/tests')
-rw-r--r--libmisc/tests/test_assert.c70
-rw-r--r--libmisc/tests/test_fmt.c170
-rw-r--r--libmisc/tests/test_log.c79
-rw-r--r--libmisc/tests/test_macro.c128
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;
}