2015-03-10 16:07:47 +01:00
|
|
|
/*
|
|
|
|
* testrunner.c
|
|
|
|
*
|
|
|
|
* Created on: Jun 18, 2013
|
|
|
|
* Author: petera
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#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) {
|
2015-05-29 08:54:07 +02:00
|
|
|
if (strncmp(line, name, strlen(name)) == 0) {
|
2015-03-10 16:07:47 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-29 08:54:07 +02:00
|
|
|
void run_tests(int argc, char **args) {
|
2015-03-10 16:07:47 +01:00
|
|
|
memset(&test_main, 0, sizeof(test_main));
|
2015-05-29 08:54:07 +02:00
|
|
|
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);
|
2015-03-10 16:07:47 +01:00
|
|
|
}
|
2015-05-29 08:54:07 +02:00
|
|
|
test_main.spec = fd;
|
2015-03-10 16:07:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
2015-05-29 08:54:07 +02:00
|
|
|
return;
|
2015-03-10 16:07:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|