diff options
Diffstat (limited to '')
-rw-r--r-- | testhelp.c | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/testhelp.c b/testhelp.c new file mode 100644 index 0000000..74997eb --- /dev/null +++ b/testhelp.c @@ -0,0 +1,366 @@ +/* See LICENSE file for copyright and license details. */ +#define TEST +#include "common.h" + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wswitch" +# pragma GCC diagnostic ignored "-Wswitch-enum" +#endif + + +static char * +quote(const char *s, char **out) +{ + char *ret, *p; + int safe = 1; + *out = p = ret = malloc(strlen(s) * 4 + 3); + if (!ret) { + fprintf(stderr, "ran out of memory during printing of assertion failure\n"); + exit(1); + } + *p++ = '"'; + for (; *s; s++) { + if (*s == '\r') + p = stpcpy(p, "\\r"), safe = 1; + else if (*s == '\t') + p = stpcpy(p, "\\t"), safe = 1; + else if (*s == '\a') + p = stpcpy(p, "\\a"), safe = 1; + else if (*s == '\f') + p = stpcpy(p, "\\f"), safe = 1; + else if (*s == '\v') + p = stpcpy(p, "\\v"), safe = 1; + else if (*s == '\b') + p = stpcpy(p, "\\b"), safe = 1; + else if (*s == '\n') + p = stpcpy(p, "\\n"), safe = 1; + else if (*s == '\"') + p = stpcpy(p, "\\\""), safe = 1; + else if (*s == '\\') + p = stpcpy(p, "\\\\"), safe = 1; + else if (*s < ' ' || *s >= 127) + p += sprintf(p, "\\x%02X", (unsigned char)*s), safe = 0; + else if (*s == '?' && p[-1] == '?') + p += sprintf(p, "\"\"?"), safe = 1; + else if (isxdigit(*s) && !safe) + p += sprintf(p, "\"\"%c", *s), safe = 1; + else + *p++ = *s, safe = 1; + } + *p++ = '"'; + *p++ = '\0'; + return ret; +} + + +#define CASE(HOW, OP)\ + case HOW:\ + if ((*have) OP (*expect))\ + return;\ + symbol = #OP;\ + break + + +#define CASE_LOGIC(HOW, OP1, OP2, OPERAND3_IS_EXPECT)\ + case HOW:\ + if (((*have) OP1 (*expect)) OP2 (OPERAND3_IS_EXPECT ? (*expect) : 0))\ + return;\ + symbol = #OP1;\ + symbol2 = #OP2;\ + operand3_is_expect = (OPERAND3_IS_EXPECT);\ + break + + +static void +test_assert_uint(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const uintmax_t *have, const uintmax_t *expect) +{ + const char *symbol; + const char *symbol2 = NULL; + int operand3_is_expect; + switch (how) { + case ASSERT_NOT_LT: how = ASSERT_NOT_GE; break; + case ASSERT_NOT_LE: how = ASSERT_NOT_GT; break; + case ASSERT_NOT_GT: how = ASSERT_NOT_LE; break; + case ASSERT_NOT_GE: how = ASSERT_NOT_LT; break; + } + switch (how) { + CASE(ASSERT_EQ, ==); + CASE(ASSERT_NE, !=); + CASE(ASSERT_LT, <); + CASE(ASSERT_LE, <=); + CASE(ASSERT_GT, >); + CASE(ASSERT_GE, >=); + CASE_LOGIC(ASSERT_CONTAINS_ALL, &, ==, 1); + CASE_LOGIC(ASSERT_CONTAINS_ANY, &, !=, 0); + CASE_LOGIC(ASSERT_CONTAINS_NOT_ALL, &, !=, 1); + CASE_LOGIC(ASSERT_CONTAINS_NOT_ANY, &, ==, 0); + CASE_LOGIC(ASSERT_IS_IN, &~, ==, 0); + CASE_LOGIC(ASSERT_NOT_IN, &~, !=, 0); + default: + abort(); + } + if (!symbol2) + fprintf(stderr, "TEST FAILED at %s:%i: asserting (%s) %s (%s), actualised as %ju %s %ju\n", + file, line, have_string, symbol, expect_string, *have, symbol, *expect); + else if (!operand3_is_expect) + fprintf(stderr, "TEST FAILED at %s:%i: asserting ((%s) %s (%s)) %s 0, actualised as (%#jx %s %#jx) %s 0\n", + file, line, have_string, symbol, expect_string, symbol2, *have, symbol, *expect, symbol2); + else + fprintf(stderr, "TEST FAILED at %s:%i: asserting ((%s) %s (%s)) %s (%s), actualised as (%#jx %s %#jx) %s %#jx\n", + file, line, have_string, symbol, expect_string, symbol2, expect_string, + *have, symbol, *expect, symbol2, *expect); + exit(1); +} + + +static void +test_assert_int(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const intmax_t *have, const intmax_t *expect) +{ + const char *symbol; + switch (how) { + case ASSERT_NOT_LT: how = ASSERT_NOT_GE; break; + case ASSERT_NOT_LE: how = ASSERT_NOT_GT; break; + case ASSERT_NOT_GT: how = ASSERT_NOT_LE; break; + case ASSERT_NOT_GE: how = ASSERT_NOT_LT; break; + } + switch (how) { + CASE(ASSERT_EQ, ==); + CASE(ASSERT_NE, !=); + CASE(ASSERT_LT, <); + CASE(ASSERT_LE, <=); + CASE(ASSERT_GT, >); + CASE(ASSERT_GE, >=); + default: + test_assert_uint(file, line, how, have_string, expect_string, + (const uintmax_t *)have, (const uintmax_t *)expect); + return; + } + fprintf(stderr, "TEST FAILED at %s:%i: asserting (%s) %s (%s), actualised as %ji %s %ji\n", + file, line, have_string, symbol, expect_string, *have, symbol, *expect); + exit(1); +} + + +#define PTR_CASE(HOW, OP)\ + case HOW:\ + if ((have) OP (expect))\ + return;\ + symbol = #OP;\ + break + + +static void +test_assert_ptr(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const char *have, const char *expect) +{ + const char *symbol; + switch (how) { + case ASSERT_NOT_LT: how = ASSERT_NOT_GE; break; + case ASSERT_NOT_LE: how = ASSERT_NOT_GT; break; + case ASSERT_NOT_GT: how = ASSERT_NOT_LE; break; + case ASSERT_NOT_GE: how = ASSERT_NOT_LT; break; + } + switch (how) { + PTR_CASE(ASSERT_EQ, ==); + PTR_CASE(ASSERT_NE, !=); + PTR_CASE(ASSERT_LT, <); + PTR_CASE(ASSERT_LE, <=); + PTR_CASE(ASSERT_GT, >); + PTR_CASE(ASSERT_GE, >=); + default: + abort(); + } + fprintf(stderr, "TEST FAILED at %s:%i: asserting (%s) %s (%s), actualised as %p %s %p\n", + file, line, have_string, symbol, expect_string, have, symbol, expect); + exit(1); +} + + +#define STR_CASE(HOW, FUNC)\ + case HOW:\ + if (FUNC(have, expect))\ + return;\ + function = #FUNC;\ + break + +#define SWAP_STR_CASE(HOW, NEW_HOW)\ + case HOW:\ + test_assert_str(file, line, how, expect_string, have_string, expect, have, rev);\ + return + + +static void +test_assert_str(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const char *have, const char *expect, int rev) +{ + const char *function; + size_t have_len; + size_t expect_len; + char *have_quote = NULL; + char *expect_quote = NULL; + + if (!have && !expect) + fprintf(stderr, "TEST FAILED at %s:%i: asserting failed because (%s) and (%s) are NULL\n", + file, line, have_string, expect_string); + else if (!have) + fprintf(stderr, "TEST FAILED at %s:%i: asserting failed because (%s) was NULL\n", + file, line, have_string); + else if (!expect) + fprintf(stderr, "TEST FAILED at %s:%i: asserting failed because (%s) was NULL\n", + file, line, expect_string); + if (!have || !expect) + exit(1); + + switch (how) { + STR_CASE(ASSERT_EQ, !strcmp); + STR_CASE(ASSERT_NE, strcmp); + STR_CASE(ASSERT_IS_IN, strstr); + STR_CASE(ASSERT_NOT_IN, !strstr); + SWAP_STR_CASE(ASSERT_CONTAINS_ALL, ASSERT_IS_IN); + SWAP_STR_CASE(ASSERT_CONTAINS_NOT_ALL, ASSERT_NOT_INT); + SWAP_STR_CASE(ASSERT_LE, ASSERT_GE); + SWAP_STR_CASE(ASSERT_LT, ASSERT_GT); + SWAP_STR_CASE(ASSERT_NOT_LE, ASSERT_NOT_GE); + SWAP_STR_CASE(ASSERT_NOT_LT, ASSERT_NOT_GT); + default: + have_len = strlen(have); + expect_len = strlen(expect); + if (rev) + goto assert_end; + else + goto assert_start; + } + fprintf(stderr, "TEST FAILED at %s:%i: asserting %s(%s, %s), actualised as %s(%s, %s)\n", + file, line, function, have_string, expect_string, + function, quote(have, &have_quote), quote(expect, &expect_quote)); +free_and_exit: + free(have_quote); + free(expect_quote); + exit(1); + +assert_start: + switch (how) { + case ASSERT_GE: + if (have_len >= expect_len && !strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) >= strlen(%s) && !strncmp(%s, %s, strlen(%s)), " + "actualised as %zu >= %zu && !strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + case ASSERT_NOT_GE: + if (have_len < expect_len || strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) < strlen(%s) || strncmp(%s, %s, strlen(%s)), " + "actualised as %zu < %zu && !strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + case ASSERT_GT: + if (have_len > expect_len && !strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) > strlen(%s) && !strncmp(%s, %s, strlen(%s)), " + "actualised as %zu > %zu && !strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + case ASSERT_NOT_GT: + if (have_len <= expect_len || strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) <= strlen(%s) || strncmp(%s, %s, strlen(%s)), " + "actualised as %zu <= %zu || strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + default: + abort(); + } + goto free_and_exit; + +assert_end: + switch (how) { + case ASSERT_GE: + if (have_len >= expect_len && !strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) >= strlen(%s) && !strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu >= %zu && !strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + case ASSERT_NOT_GE: + if (have_len > expect_len || strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) < strlen(%s) || strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu < %zu && !strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + case ASSERT_GT: + if (have_len > expect_len && !strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) > strlen(%s) && !strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu > %zu && !strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + case ASSERT_NOT_GT: + if (have_len <= expect_len || strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) <= strlen(%s) || strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu <= %zu || strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + default: + abort(); + } + goto free_and_exit; +} + + +void +test_assert(const char *file, int line, enum assert_type type, enum assert_how how, + const char *have_string, const char *expect_string, const void *have, const void *expect) +{ + switch (type) { + case ASSERT_ENUM: + case ASSERT_UINT: + test_assert_uint(file, line, how, have_string, expect_string, have, expect); + break; + + case ASSERT_INT: + test_assert_int(file, line, how, have_string, expect_string, have, expect); + break; + + case ASSERT_NULL: + case ASSERT_PTR: + test_assert_ptr(file, line, how, have_string, expect_string, have, expect); + break; + + case ASSERT_STR: + test_assert_str(file, line, how, have_string, expect_string, have, expect, 0); + break; + + case ASSERT_STR_REV: + test_assert_str(file, line, how, have_string, expect_string, have, expect, 1); + break; + + default: + abort(); + } +} |