/* 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(); } }