Skip to content

Instantly share code, notes, and snippets.

@pervognsen
Created November 20, 2010 20:33
Show Gist options
  • Save pervognsen/708129 to your computer and use it in GitHub Desktop.
Save pervognsen/708129 to your computer and use it in GitHub Desktop.
minimalistic unit testing
#ifndef __TEST_H__
#define __TEST_H__
#ifndef NO_TESTING
#define TESTING 1
// Plumbing
void test_main(int argc, char const **argv);
void test_register(const char *name, void (*run)(), const char *file, int phase, int line);
void test_run();
void test_is(int b, const char *expr, const char *file, int line);
#ifdef TEST_IMPL
#include <stdlib.h>
#include <setjmp.h>
#include <string.h>
typedef struct test_t
{
const char *name; void (*run)();
const char *file; int phase; int line;
struct test_t *link;
} test_t;
static test_t *test_link = NULL;
void test_register(const char *name, void (*run)(), const char *file, int phase, int line)
{
test_t *t = (test_t*) malloc(sizeof(test_t)); // blatantly ignores clean-up
t->name = name; t->run = run; t->file = file; t->phase = phase; t->line = line;
t->link = test_link; test_link = t;
}
static jmp_buf test_fail_jmp;
void test_fail(const char *file, int line)
{
printf(" (line %d)\n", line);
longjmp(test_fail_jmp, 1);
}
#define TEST_FAIL_BANNER " FAIL: "
static int test_compare(const void *p1, const void *p2)
{
test_t *x = *(test_t**) p1, *y = *(test_t**) p2;
int file = x->file - y->file, phase = x->phase - y->phase, line = x->line - y->line;
return (file != 0) ? file : (phase != 0) ? phase : line;
}
void test_run()
{
// sort tests
test_t **tests; int n;
{
n = 0;
for (test_t *t = test_link; t; t = t->link) { n++; }
tests = (test_t**) calloc(n, sizeof(test_t*));
int i = 0;
for (test_t *t = test_link; t; t = t->link) { tests[i++] = t; }
qsort(tests, n, sizeof(test_t*), test_compare);
}
// run tests in order
{
int total = 0, passed = 0;
const char *file = NULL;
for (int i = 0; i < n; i++) {
test_t *t = tests[i];
if (t->file != file) {
file = t->file;
printf("%sFILE: %s\n\n", i != 0 ? "\n" : "", file);
}
if (t->phase == 0) {
printf(" TEST: %s\n", t->name);
total++;
if (setjmp(test_fail_jmp) == 0) {
t->run();
passed++;
}
} else {
t->run();
}
}
printf("\nDONE: %d tests (%d passed, %d failed)\n", total, passed, total - passed);
}
free(tests);
}
void test_main(int argc, char const **argv)
{
for (int i = 0; i < argc; i++) {
if (strcmp(argv[i], "--tests") == 0) {
test_run();
getchar();
exit(0);
}
}
}
void test_is(int b, const char *expr, const char *file, int line)
{
if (!b) {
printf("%s%s", TEST_FAIL_BANNER, expr);
test_fail(file, line);
}
}
#endif // TEST_IMPL
#endif // NO_TESTING
#define TEST_TOKENPASTE(x, y) x ## y
#define TEST_TOKENPASTE2(x, y) TEST_TOKENPASTE(x, y)
// Porcelain
#if TESTING
#define PRE_TEST DO_TEST(pre, -1)
#define TEST(name) DO_TEST(name, 0)
#define POST_TEST DO_TEST(post, +1)
#define TEST_MAIN test_main(argc, argv)
#define DO_TEST(name, phase) \
static void test_##name##(); \
struct TEST_TOKENPASTE2(test, __LINE__) \
{ \
TEST_TOKENPASTE2(test, __LINE__)() \
{ \
test_register(#name, test_##name, __FILE__, phase, __LINE__); \
} \
} TEST_TOKENPASTE2(test_, __LINE__); \
void test_##name##()
#define IS(b) test_is((b), #b, __FILE__, __LINE__)
#else
#define TEST_MAIN
#define DO_TEST(name, phase) static void test_##name##()
#define IS(b) do {} while(0)
#endif // TESTING
#endif // __TEST_H__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment