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