diff --git a/common/testing.h b/common/testing.h index 9d8c8006..10403c33 100644 --- a/common/testing.h +++ b/common/testing.h @@ -25,23 +25,43 @@ extern "C" { #endif -#if defined(__GNUC__) +#ifdef __cplusplus +#define testingprivMkScaffold(name) \ + static inline void testingprivScaffold ## name(testingT *t) \ + { \ + bool failedNow = false; \ + try { name(t); } \ + catch (testingprivFailNowException e) { failedNow = true; } \ + /* TODO see if we should catch other exceptions too */ \ + /* don't call this in the catch block as it calls longjmp() */ \ + if (failedNow) testingprivTDoFailNow(t); \ + } +#else +#define testingprivMkScaffold(name) \ + static inline void testingprivScaffold ## name(testingT *t) { name(t); } +#endif + +#if defined(__cplusplus) #define testingprivMkCtor(name, reg) \ - __attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, name); } + static reg ## Class testingprivCtor ## name(#name, testingprivScaffold ## name); +#elif 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, name); return 0; } \ + 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, name); } + _Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); } #else -#error unknown compiler; cannot continue +#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) @@ -50,14 +70,27 @@ extern int testingMain(void); typedef struct testingT testingT; #define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) #define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) +#ifdef __cplusplus +#define testingTFailNow(t) (throw testingprivFailNowException()) +#else +#define testingTFailNow(t) testingprivTDoFailNow(t) +#endif // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); +extern void testingprivTDoFailNow(testingT *); #ifdef __cplusplus } +namespace { + class testingprivFailNowException {}; + class testingprivRegisterTestClass { + public: + testingprivRegisterTestClass(const char *name, void (*f)(testingT *)) { testingprivRegisterTest(name, f); } + }; +} #endif #endif diff --git a/common/testing_testing.c b/common/testing_testing.c index c8d0d986..33e19bbe 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -1,6 +1,7 @@ // 27 february 2018 #include #include +#include #include "testing.h" #define testingprivNew(T) ((T *) malloc(sizeof (T))) @@ -9,6 +10,7 @@ struct testingT { const char *name; void (*f)(testingT *); int failed; + jmp_buf failNowBuf; testingT *next; }; @@ -41,7 +43,8 @@ int testingMain(void) anyFailed = 0; for (t = tests; t != NULL; t = t->next) { printf("=== RUN %s\n", t->name); - (*(t->f))(t); + if (setjmp(t->failNowBuf) == 0) + (*(t->f))(t); status = "PASS"; if (t->failed) { status = "FAIL"; @@ -81,3 +84,9 @@ void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char testingprivTDoLog(t, file, line, format, ap); t->failed = 1; } + +void testingprivTDoFailNow(testingT *t) +{ + t->failed = 1; + longjmp(t->failNowBuf, 1); +}