/* * testrunner.c * * Created on: Jun 18, 2013 * Author: petera */ #include #include #include #include #include #include #include #include #include "testrunner.h" static struct { test *tests; test *_last_test; int test_count; void (*on_stop)(test *t); test_res *failed; test_res *failed_last; test_res *stopped; test_res *stopped_last; FILE *spec; } test_main; void test_init(void (*on_stop)(test *t)) { test_main.on_stop = on_stop; } static char check_spec(char *name) { if (test_main.spec) { fseek(test_main.spec, 0, SEEK_SET); char *line = NULL; size_t sz; ssize_t read; while ((read = getline(&line, &sz, test_main.spec)) != -1) { if (strncmp(line, name, strlen(name)) == 0) { free(line); return 1; } } free(line); return 0; } else { return 1; } } void add_test(test_f f, char *name, void (*setup)(test *t), void (*teardown)(test *t)) { if (f == 0) return; if (!check_spec(name)) return; DBGT("adding test %s\n", name); test *t = malloc(sizeof(test)); memset(t, 0, sizeof(test)); t->f = f; strcpy(t->name, name); t->setup = setup; t->teardown = teardown; if (test_main.tests == 0) { test_main.tests = t; } else { test_main._last_test->_next = t; } test_main._last_test = t; test_main.test_count++; } static void add_res(test *t, test_res **head, test_res **last) { test_res *tr = malloc(sizeof(test_res)); memset(tr,0,sizeof(test_res)); strcpy(tr->name, t->name); if (*head == 0) { *head = tr; } else { (*last)->_next = tr; } *last = tr; } static void dump_res(test_res **head) { test_res *tr = (*head); while (tr) { test_res *next_tr = tr->_next; printf(" %s\n", tr->name); free(tr); tr = next_tr; } } void run_tests(int argc, char **args) { memset(&test_main, 0, sizeof(test_main)); if (argc > 1) { printf("running tests from %s\n", args[1]); FILE *fd = fopen(args[1], "r"); if (fd == NULL) { printf("%s not found\n", args[1]); exit(EXIT_FAILURE); } test_main.spec = fd; } DBGT("adding suites...\n"); add_suites(); DBGT("%i tests added\n", test_main.test_count); if (test_main.spec) { fclose(test_main.spec); } if (test_main.test_count == 0) { printf("No tests to run\n"); return; } int fd_success = open("_tests_ok", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); int fd_bad = open("_tests_fail", O_APPEND | O_TRUNC | O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); DBGT("running tests...\n"); int ok = 0; int failed = 0; int stopped = 0; test *cur_t = test_main.tests; int i = 1; while (cur_t) { cur_t->setup(cur_t); test *next_test = cur_t->_next; DBGT("TEST %i/%i : running test %s\n", i, test_main.test_count, cur_t->name); i++; int res = cur_t->f(cur_t); cur_t->teardown(cur_t); int fd = res == TEST_RES_OK ? fd_success : fd_bad; write(fd, cur_t->name, strlen(cur_t->name)); write(fd, "\n", 1); switch (res) { case TEST_RES_OK: ok++; printf(" .. ok\n"); break; case TEST_RES_FAIL: failed++; printf(" .. FAILED\n"); if (test_main.on_stop) test_main.on_stop(cur_t); add_res(cur_t, &test_main.failed, &test_main.failed_last); break; case TEST_RES_ASSERT: stopped++; printf(" .. ABORTED\n"); if (test_main.on_stop) test_main.on_stop(cur_t); add_res(cur_t, &test_main.stopped, &test_main.stopped_last); break; } free(cur_t); cur_t = next_test; } close(fd_success); close(fd_bad); DBGT("ran %i tests\n", test_main.test_count); printf("Test report, %i tests\n", test_main.test_count); printf("%i succeeded\n", ok); printf("%i failed\n", failed); dump_res(&test_main.failed); printf("%i stopped\n", stopped); dump_res(&test_main.stopped); if (ok < test_main.test_count) { printf("\nFAILED\n"); } else { printf("\nALL TESTS OK\n"); } }