aboutsummaryrefslogtreecommitdiffstats
path: root/testhelp.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--testhelp.c366
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();
+ }
+}