/**************************************************************** * * ======== lily-test ======== * * a midsize C unit testing library * copyright (c) 2022 kate swanson (sanine) * * This is anti-capitalist software, released for free use by individuals and * organizations that do not operate by capitalist principles. * * Permission is hereby granted, free of charge, to any person or * organization (the "User") obtaining a copy of this software and associated * documentation files (the "Software"), to use, copy, modify, merge, * distribute, and/or sell copies of the Software, subject to the following conditions: * * 1. The above copyright notice and this permission notice shall be included * in all copies or modified versions of the Software. * * 2. The User is one of the following: * a. An individual person, laboring for themselves * b. A non-profit organization * c. An educational institution * d. An organization that seeks shared profit for all of its members, and * allows non-members to set the cost of their labor * * 3. If the User is an organization with owners, then all owners are workers * and all workers are owners with equal equity and/or equal vote. * * 4. If the User is an organization, then the User is not law enforcement or * military, or working for or under either. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT EXPRESS OR IMPLIED WARRANTY OF * ANY KIND, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS 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. * * https://anticapitalist.software/ * https://sanine.net * ****************************************************************/ #ifndef LILY_TEST_H #define LILY_TEST_H #define LILY_VERSION_MAJOR 2 #define LILY_VERSION_MINOR 0 #define LILY_VERSION_PATCH 0 #include #include #include #include #include #include #define STR_IMP(x) #x #define STR(x) STR_IMP(x) /* define SOURCE_PATH_SIZE to strip away the leading parts of the full compilation path */ #ifndef SOURCE_PATH_SIZE #define LILY_LOCATION (__FILE__ ":" STR(__LINE__)) #else #define LILY_LOCATION ((__FILE__ ":" STR(__LINE__)) + SOURCE_PATH_SIZE) #endif /* self-location macro */ #ifndef LILY_TEST_H_LOCATION #define LILY_TEST_H_LOCATION "lily-test.h" #endif /* counter macros */ #define LILY_HEX_(x) 0x##x #define LILY_HEX(x) LILY_HEX_(x) #define LILY_COUNTER_POS_0 0 #define LILY_COUNTER_POS_1 0 #define LILY_COUNTER_POS_2 0 #define LILY_COUNTER__(a, b, c) 0x##a##b##c #define LILY_COUNTER_(a, b, c) LILY_COUNTER__(a, b, c) #define LILY_COUNTER LILY_COUNTER_(LILY_COUNTER_POS_2, LILY_COUNTER_POS_1, LILY_COUNTER_POS_0) /* automatic registration macros */ typedef struct lily_ll_node_t { struct lily_ll_node_t *next; void (*f)(); } lily_ll_node_t; #define LILY_FILE_BEGIN(unique_id) \ static lily_ll_node_t lily_ll_last = { NULL, NULL }; #define LILY_LIST_HEAD lily_ll_last #define LILY_LIST_NEXT lily_ll_last #define LILY_PUSH_TEST() LILY_TEST_H_LOCATION #define LILY_REGISTER_TESTS() LILY_TEST_H_LOCATION #define LILY_CAT_(x, y) x##y #define LILY_CAT(x, y) LILY_CAT_(x, y) #define LILY_ANON(x) LILY_CAT(x, LILY_COUNTER) #define LILY_ANON_FUNC LILY_ANON(LILY_FUNCTION_) #define LILY_ANON_NODE LILY_ANON(LILY_NODE_) #define LILY_ANON_DESC LILY_ANON(LILY_DESCRIPTION_) #define LILY_TEST_(f, desc_str, description) \ static const char * const desc_str = description; \ static void f() #define LILY_TEST(description) LILY_TEST_(LILY_ANON_FUNC, LILY_ANON_DESC, description) /* assertions */ typedef struct lily_test_msg_t { struct lily_test_msg_t *next; char *msg; const char *location; } lily_test_msg_t; void lily_msg_destroy(lily_test_msg_t *m); void lily_info(const char *location, const char *fmt, ...); #define LILY_INFO_(location, ...) \ lily_info(location, __VA_ARGS__) #define LILY_INFO(...) LILY_INFO_(LILY_LOCATION, __VA_ARGS__) #define INFO(...) LILY_INFO(__VA_ARGS__) void lily_check(int x, const char *location, const char *fmt, ...); #define LILY_CHECK_(str, x, location) \ lily_check(x, location, "%s", str) #define LILY_CHECK(x) LILY_CHECK_(STR(x), x, LILY_LOCATION) #define CHECK(x) LILY_CHECK(x) void lily_require(int x, const char *location, const char *fmt, ...); #define LILY_REQUIRE_(str, x, location) \ lily_require(x, location, "%s", str) #define LILY_REQUIRE(x) LILY_REQUIRE_(STR(x), x, LILY_LOCATION) #define REQUIRE(x) LILY_REQUIRE(x) struct lily_global_t { int failed; jmp_buf env; lily_test_msg_t HEAD; lily_test_msg_t *TAIL; } extern lily_g; #ifdef LILY_IMPLEMENTATION struct lily_global_t lily_g; lily_test_msg_t *lily_msg_create(const char *location, const char *fmt, va_list args) { lily_test_msg_t *m = malloc(sizeof(lily_test_msg_t)); if (m == NULL) { return NULL; } m->next = NULL; m->location = location; va_list args_len; va_copy(args_len, args); size_t len = vsnprintf(NULL, 0, fmt, args_len) + 1; va_end(args_len); m->msg = malloc(len * sizeof(char)); if (m->msg == NULL) { free(m); return NULL; } vsnprintf(m->msg, len, fmt, args); return m; } void lily_msg_destroy(lily_test_msg_t *m) { if (m->next != NULL) { // destroy the whole chain lily_msg_destroy(m->next); } free(m->msg); free(m); } void lily_info(const char *location, const char *fmt, ...) { va_list args; va_start(args, fmt); lily_test_msg_t *m = lily_msg_create(location, fmt, args); va_end(args); lily_g.TAIL->next = m; lily_g.TAIL = m; } void lily_check(int x, const char *location, const char *fmt, ...) { if (!x) { // assertion failed va_list args; va_start(args, fmt); lily_test_msg_t *m = lily_msg_create(location, fmt, args); va_end(args); lily_g.TAIL->next = m; lily_g.TAIL = m; lily_g.failed = 1; } } void lily_require(int x, const char *location, const char *fmt, ...) { if (!x) { // assertion failed va_list args; va_start(args, fmt); lily_test_msg_t *m = lily_msg_create(location, fmt, args); va_end(args); lily_g.TAIL->next = m; lily_g.TAIL = m; lily_g.failed = 1; /* longjump out of test */ longjmp(lily_g.env, 1); } } #endif /* ifdef LILY_TEST_H */ #else /* counter incrementing */ /* this counter can count up to 4096, but it should be pretty trivial to extend it more or less arbitrarily far */ /* to increment the counter, just #define LILY_COUNTER_INCREMENT and then include this file again */ #ifdef LILY_COUNTER_INCREMENT #undef LILY_COUNTER_INCREMENT /* ones position */ # if LILY_HEX(LILY_COUNTER_POS_0) == 0 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 1 # elif LILY_HEX(LILY_COUNTER_POS_0) == 1 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 2 # elif LILY_HEX(LILY_COUNTER_POS_0) == 2 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 3 # elif LILY_HEX(LILY_COUNTER_POS_0) == 3 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 4 # elif LILY_HEX(LILY_COUNTER_POS_0) == 4 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 5 # elif LILY_HEX(LILY_COUNTER_POS_0) == 5 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 6 # elif LILY_HEX(LILY_COUNTER_POS_0) == 6 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 7 # elif LILY_HEX(LILY_COUNTER_POS_0) == 7 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 8 # elif LILY_HEX(LILY_COUNTER_POS_0) == 8 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 9 # elif LILY_HEX(LILY_COUNTER_POS_0) == 9 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 a # elif LILY_HEX(LILY_COUNTER_POS_0) == 10 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 b # elif LILY_HEX(LILY_COUNTER_POS_0) == 11 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 c # elif LILY_HEX(LILY_COUNTER_POS_0) == 12 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 d # elif LILY_HEX(LILY_COUNTER_POS_0) == 13 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 e # elif LILY_HEX(LILY_COUNTER_POS_0) == 14 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 f # elif LILY_HEX(LILY_COUNTER_POS_0) == 15 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 0 /* sixteens position */ # if LILY_HEX(LILY_COUNTER_POS_1) == 0 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 1 # elif LILY_HEX(LILY_COUNTER_POS_1) == 1 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 2 # elif LILY_HEX(LILY_COUNTER_POS_1) == 2 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 3 # elif LILY_HEX(LILY_COUNTER_POS_1) == 3 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 4 # elif LILY_HEX(LILY_COUNTER_POS_1) == 4 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 5 # elif LILY_HEX(LILY_COUNTER_POS_1) == 5 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 6 # elif LILY_HEX(LILY_COUNTER_POS_1) == 6 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 7 # elif LILY_HEX(LILY_COUNTER_POS_1) == 7 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 8 # elif LILY_HEX(LILY_COUNTER_POS_1) == 8 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 9 # elif LILY_HEX(LILY_COUNTER_POS_1) == 9 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 a # elif LILY_HEX(LILY_COUNTER_POS_1) == 10 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 b # elif LILY_HEX(LILY_COUNTER_POS_1) == 11 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 c # elif LILY_HEX(LILY_COUNTER_POS_1) == 12 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 d # elif LILY_HEX(LILY_COUNTER_POS_1) == 13 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 e # elif LILY_HEX(LILY_COUNTER_POS_1) == 14 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 f # elif LILY_HEX(LILY_COUNTER_POS_1) == 15 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 0 /* 256s position */ # if LILY_HEX(LILY_COUNTER_POS_2) == 0 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 1 # elif LILY_HEX(LILY_COUNTER_POS_2) == 1 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 2 # elif LILY_HEX(LILY_COUNTER_POS_2) == 2 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 3 # elif LILY_HEX(LILY_COUNTER_POS_2) == 3 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 4 # elif LILY_HEX(LILY_COUNTER_POS_2) == 4 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 5 # elif LILY_HEX(LILY_COUNTER_POS_2) == 5 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 6 # elif LILY_HEX(LILY_COUNTER_POS_2) == 6 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 7 # elif LILY_HEX(LILY_COUNTER_POS_2) == 7 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 8 # elif LILY_HEX(LILY_COUNTER_POS_2) == 8 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 9 # elif LILY_HEX(LILY_COUNTER_POS_2) == 9 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 a # elif LILY_HEX(LILY_COUNTER_POS_2) == 10 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 b # elif LILY_HEX(LILY_COUNTER_POS_2) == 11 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 c # elif LILY_HEX(LILY_COUNTER_POS_2) == 12 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 d # elif LILY_HEX(LILY_COUNTER_POS_2) == 13 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 e # elif LILY_HEX(LILY_COUNTER_POS_2) == 14 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 f # elif LILY_HEX(LILY_COUNTER_POS_2) == 15 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 0 # endif # endif # endif #elif defined(LILY_COUNTER_DECREMENT) #undef LILY_COUNTER_DECREMENT /* ones position */ # if LILY_HEX(LILY_COUNTER_POS_0) == 15 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 e # elif LILY_HEX(LILY_COUNTER_POS_0) == 14 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 d # elif LILY_HEX(LILY_COUNTER_POS_0) == 13 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 c # elif LILY_HEX(LILY_COUNTER_POS_0) == 12 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 b # elif LILY_HEX(LILY_COUNTER_POS_0) == 11 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 a # elif LILY_HEX(LILY_COUNTER_POS_0) == 10 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 9 # elif LILY_HEX(LILY_COUNTER_POS_0) == 9 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 8 # elif LILY_HEX(LILY_COUNTER_POS_0) == 8 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 7 # elif LILY_HEX(LILY_COUNTER_POS_0) == 7 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 6 # elif LILY_HEX(LILY_COUNTER_POS_0) == 6 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 5 # elif LILY_HEX(LILY_COUNTER_POS_0) == 5 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 4 # elif LILY_HEX(LILY_COUNTER_POS_0) == 4 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 3 # elif LILY_HEX(LILY_COUNTER_POS_0) == 3 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 2 # elif LILY_HEX(LILY_COUNTER_POS_0) == 2 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 1 # elif LILY_HEX(LILY_COUNTER_POS_0) == 1 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 0 # elif LILY_HEX(LILY_COUNTER_POS_0) == 0 # undef LILY_COUNTER_POS_0 # define LILY_COUNTER_POS_0 f /* sixteens position */ # if LILY_HEX(LILY_COUNTER_POS_1) == 15 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 e # elif LILY_HEX(LILY_COUNTER_POS_1) == 14 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 d # elif LILY_HEX(LILY_COUNTER_POS_1) == 13 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 c # elif LILY_HEX(LILY_COUNTER_POS_1) == 12 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 b # elif LILY_HEX(LILY_COUNTER_POS_1) == 11 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 a # elif LILY_HEX(LILY_COUNTER_POS_1) == 10 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 9 # elif LILY_HEX(LILY_COUNTER_POS_1) == 9 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 8 # elif LILY_HEX(LILY_COUNTER_POS_1) == 8 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 7 # elif LILY_HEX(LILY_COUNTER_POS_1) == 7 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 6 # elif LILY_HEX(LILY_COUNTER_POS_1) == 6 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 5 # elif LILY_HEX(LILY_COUNTER_POS_1) == 5 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 4 # elif LILY_HEX(LILY_COUNTER_POS_1) == 4 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 3 # elif LILY_HEX(LILY_COUNTER_POS_1) == 3 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 2 # elif LILY_HEX(LILY_COUNTER_POS_1) == 2 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 1 # elif LILY_HEX(LILY_COUNTER_POS_1) == 1 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 0 # elif LILY_HEX(LILY_COUNTER_POS_1) == 0 # undef LILY_COUNTER_POS_1 # define LILY_COUNTER_POS_1 f /* 256s position */ # if LILY_HEX(LILY_COUNTER_POS_2) == 15 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 e # elif LILY_HEX(LILY_COUNTER_POS_2) == 14 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 d # elif LILY_HEX(LILY_COUNTER_POS_2) == 13 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 c # elif LILY_HEX(LILY_COUNTER_POS_2) == 12 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 b # elif LILY_HEX(LILY_COUNTER_POS_2) == 11 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 a # elif LILY_HEX(LILY_COUNTER_POS_2) == 10 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 9 # elif LILY_HEX(LILY_COUNTER_POS_2) == 9 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 8 # elif LILY_HEX(LILY_COUNTER_POS_2) == 8 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 7 # elif LILY_HEX(LILY_COUNTER_POS_2) == 7 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 6 # elif LILY_HEX(LILY_COUNTER_POS_2) == 6 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 5 # elif LILY_HEX(LILY_COUNTER_POS_2) == 5 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 4 # elif LILY_HEX(LILY_COUNTER_POS_2) == 4 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 3 # elif LILY_HEX(LILY_COUNTER_POS_2) == 3 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 2 # elif LILY_HEX(LILY_COUNTER_POS_2) == 2 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 1 # elif LILY_HEX(LILY_COUNTER_POS_2) == 1 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 0 # elif LILY_HEX(LILY_COUNTER_POS_2) == 0 # undef LILY_COUNTER_POS_2 # define LILY_COUNTER_POS_2 f # endif # endif # endif #elif defined(LILY_FILE_END) /* ending a file */ /* LILY_LIST_HEAD is one too high, so we decrement */ #define LILY_COUNTER_DECREMENT #include LILY_TEST_H_LOCATION #else /* register new test */ static lily_ll_node_t LILY_ANON_NODE = { #define LILY_COUNTER_DECREMENT #include LILY_TEST_H_LOCATION &LILY_LIST_HEAD, #define LILY_COUNTER_INCREMENT #include LILY_TEST_H_LOCATION LILY_ANON_FUNC, }; #undef LILY_LIST_HEAD #define LILY_LIST_HEAD LILY_ANON_NODE #define LILY_COUNTER_INCREMENT #include LILY_TEST_H_LOCATION #endif #endif