From b26e0677af9ae00b8037b90aa236753314edcc09 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 10 Apr 2019 13:23:25 -0400 Subject: [PATCH] Okay, I'm not satisfied with the other testing frameworks, so strip testing.h down to just what libui needs. --- test/testing.h | 67 +++++++++++++++++++ test/testing_testing.c | 144 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 test/testing.h create mode 100644 test/testing_testing.c diff --git a/test/testing.h b/test/testing.h new file mode 100644 index 00000000..c3a28316 --- /dev/null +++ b/test/testing.h @@ -0,0 +1,67 @@ +// 27 february 2018 + +// TODO +// - https://blogs.msdn.microsoft.com/oldnewthing/20181107-00/?p=100155 https://blogs.msdn.microsoft.com/oldnewthing/20181108-00/?p=100165 https://blogs.msdn.microsoft.com/oldnewthing/20181109-00/?p=100175 +// - also in the above: note the unspecified order of data in the sub-segments... + +#include + +#define testingprivMkScaffold(name) \ + static inline void testingprivScaffold ## name(testingT *t) { name(t); } + +// references: +// - https://gitlab.gnome.org/GNOME/glib/blob/master/glib/gconstructor.h +// - https://gitlab.gnome.org/GNOME/glib/blob/master/gio/glib-compile-resources.c +// - https://msdn.microsoft.com/en-us/library/bb918180.aspx +#if defined(__GNUC__) +#define testingprivMkCtor(name, reg) \ + __attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); } +#elif defined(_MSC_VER) +#define testingprivMkCtorPrototype(name, reg) \ + static int name(void) testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); return 0; } \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) static int (*testingprivCtorPtr ## name)(void) = testingprivCtor ## name; +#elif defined(__SUNPRO_C) +#define testingprivMkCtor(name, reg) \ + _Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); } +#else +#error unknown compiler for making constructors in C; cannot continue +#endif + +#define testingTest(Name) \ + void Test ## Name(testingT *t); \ + testingprivMkScaffold(Test ## Name) \ + testingprivMkCtor(Test ## Name, testingprivRegisterTest) \ + void Test ## Name(testingT *t) + +extern int testingMain(void); + +typedef struct testingT testingT; +#define testingTLogf(t, ...) \ + testingprivExpand(testingprivTLogfThen((void), t, __VA_ARGS__)) +#define testingTLogvf(t, format, ap) \ + testingprivTLogvfThen((void), t, format, ap) +#define testingTErrorf(t, ...) \ + testingprivExpand(testingprivTLogfThen(testingTFail, t, __VA_ARGS__)) +#define testingTErrorvf(t, format, ap) \ + testingprivTLogvfThen(testingTFail, t, format, ap) +#define testingTFatalf(t, ...) \ + testingprivExpand(testingprivTLogfThen(testingTFailNow, t, __VA_ARGS__)) +#define testingTFatalvf(t, format, ap) \ + testingprivTLogvfThen(testingTFailNow, t, format, ap) +#define testingTSkipf(t, ...) \ + testingprivExpand(testingprivTLogfThen(testingTSkipNow, t, __VA_ARGS__)) +#define testingTSkipvf(t, format, ap) \ + testingprivTLogvfThen(testingTSkipNow, t, format, ap) +extern void testingTFail(testingT *t); +extern void testingTFailNow(testingT *t); +extern void testingTSkipNow(testingT *t); + +// TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? +extern void testingprivRegisterTest(const char *, void (*)(testingT *)); +// see https://stackoverflow.com/questions/32399191/va-args-expansion-using-msvc +#define testingprivExpand(x) x +#define testingprivTLogfThen(then, t, ...) ((testingprivTLogfFull(t, __FILE__, __LINE__, __VA_ARGS__)), (then(t))) +#define testingprivTLogvfThen(then, t, format, ap) ((testingprivTLogvfFull(t, __FILE__, __LINE__, format, ap)), (then(t))) +extern void testingprivTLogfFull(testingT *, const char *, int, const char *, ...); +extern void testingprivTLogvfFull(testingT *, const char *, int, const char *, va_list); diff --git a/test/testing_testing.c b/test/testing_testing.c new file mode 100644 index 00000000..eb334648 --- /dev/null +++ b/test/testing_testing.c @@ -0,0 +1,144 @@ +// 27 february 2018 +#include +#include +#include +#include "testing.h" + +#define testingprivNew(T) ((T *) malloc(sizeof (T))) + +struct defer { + void (*f)(void *); + void *data; + struct defer *next; +}; + +struct testingT { + const char *name; + void (*f)(testingT *); + int failed; + int skipped; + jmp_buf returnNowBuf; + struct defer *defers; + int defersRun; + testingT *next; +}; + +static testingT *tests = NULL; + +void testingprivRegisterTest(const char *name, void (*f)(testingT *)) +{ + testingT *t; + + t = testingprivNew(testingT); + t->name = name; + t->f = f; + t->failed = 0; + t->skipped = 0; + t->defers = NULL; + t->defersRun = 0; + // TODO add in the order called + t->next = tests; + tests = t; +} + +static void runDefers(testingT *t) +{ + struct defer *d; + + if (t->defersRun) + return; + t->defersRun = 1; + for (d = t->defers; d != NULL; d = d->next) + (*(d->f))(d->data); +} + +int testingMain(void) +{ + testingT *t; + int anyFailed; + const char *status; + + // TODO see if this should run if all tests are skipped + if (tests == NULL) { + fprintf(stderr, "warning: no tests to run\n"); + // imitate Go here (TODO confirm this) + return 0; + } + + anyFailed = 0; + for (t = tests; t != NULL; t = t->next) { + printf("=== RUN %s\n", t->name); + if (setjmp(t->returnNowBuf) == 0) + (*(t->f))(t); + runDefers(t); + status = "PASS"; + if (t->failed) { + status = "FAIL"; + anyFailed = 1; + } else if (t->skipped) + // note that failed overrides skipped + status = "SKIP"; + printf("--- %s: %s (%s)\n", status, t->name, "TODO"); + } + + if (anyFailed) { + printf("FAIL\n"); + return 1; + } + printf("PASS\n"); + return 0; +} + +void testingprivTLogfFull(testingT *t, const char *file, int line, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + testingprivTLogvfFull(t, file, line, format, ap); + va_end(ap); +} + +void testingprivTLogvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) +{ + // TODO extract filename from file + printf("\t%s:%d: ", file, line); + // TODO split into lines separated by \n\t\t and trimming trailing empty lines + vprintf(format, ap); + printf("\n"); +} + +void testingTFail(testingT *t) +{ + t->failed = 1; +} + +static void returnNow(testingT *t) +{ + // run defers before calling longjmp() just to be safe + runDefers(t); + longjmp(t->returnNowBuf, 1); +} + +void testingprivTDoFailNow(testingT *t) +{ + testingTFail(t); + returnNow(t); +} + +void testingprivTDoSkipNow(testingT *t) +{ + t->skipped = 1; + returnNow(t); +} + +void testingTDefer(testingT *t, void (*f)(void *data), void *data) +{ + struct defer *d; + + d = testingprivNew(struct defer); + d->f = f; + d->data = data; + // add to the head of the list so defers are run in reverse order of how they were added + d->next = t->defers; + t->defers = d; +}