#include "test_harness.h" #include #include #include #include #include #include #include #include #include "fake_env.h" static jmp_buf jmpbuf; volatile test_t dummy __attribute((__section__("tests"))) __attribute((__used__)) = {"dummy", "dummy", NULL}; extern unsigned char __data_start; extern unsigned char _end; extern test_t __start_tests; extern test_t __stop_tests; test_t* iter = &__start_tests; static int execute_test(test_t* test); void test_printll(size_t sz, long long v1, long long v2) { fprintf(stderr, "%lld == %lld\n", v1, v2); } void test_printul(size_t sz, unsigned long v1, unsigned long v2) { fprintf(stderr, "%lu == %lu\n", v1, v2); } void test_printd(size_t sz, int v1, int v2) { fprintf(stderr, "%d == %d\n", v1, v2); } void test_printl(size_t sz, long v1, long v2) { fprintf(stderr, "%lu == %lu\n", v1, v2); } void test_printui(size_t sz, unsigned int v1, unsigned int v2) { fprintf(stderr, "%u == %u\n", v1, v2); } void test_prints(size_t sz, short v1, short v2) { fprintf(stderr, "%hu == %hu\n", v1, v2); } void test_printus(size_t sz, unsigned short v1, unsigned short v2) { fprintf(stderr, "%hu == %hu\n", v1, v2); } void test_printc(size_t sz, char v1, char v2) { fprintf(stderr, "'%c' == '%c'\n", v1, v2); } void test_printf(size_t sz, double v1, double v2) { fprintf(stderr, "%f == %f\n", v1, v2); } void test_printp(size_t sz, void* v1, void* v2) { fprintf(stderr, "%p == %p\n", v1, v2); } void test_printuc(size_t sz, unsigned char v1, unsigned char v2) { fprintf(stderr, "%02x == %02x\n", (int)v1, (int)v2); } static int do_fork = 1; static size_t saved_data_size; static unsigned char* saved_data = NULL; int main(int argc, char** argv) { /* Save all initialized data. */ saved_data_size = &_end - &__data_start; saved_data = malloc(saved_data_size); memcpy(saved_data, &__data_start, saved_data_size); if (argc > 1 && strcmp(argv[1], "--nofork") == 0) { do_fork = 0; } for (; iter < &__stop_tests; ++iter) { if (iter->fn_ptr != NULL) { execute_test(iter); } } } void test_harness_abort(int ec) { longjmp(jmpbuf, ec); assert("Long jump failed.\n"); } /* * When nofork is true, this function will be called after each * test to try and make each test hermetic. * * It does this by reseting the data segment to what it was when * the program was first initialized. * * Of course, without forking, there's no way to guarantee hermetic * testing in C. In fact a simple segementation fault will break * the hermetic testing, but this does a pretty good job of at least * reseting the environment so tests don't directly depend on eachother. */ static void nofork_reset() { wipeout_fake_env(); /* Reset the data segment to what it was before. */ memcpy(&__data_start, saved_data, saved_data_size); } void panic(const char* fmt, ...) { va_list l; va_start(l, fmt); fprintf(stderr, "Kernel panic detected."); vfprintf(stderr, fmt, l); ASSERT_TRUE(0); } static int execute_test(test_t* test) { char fullname[512]; int status; int ec = 0; pid_t pid; snprintf( fullname, sizeof(fullname), "%s::%s", iter->test_suite, iter->test_name); if (!do_fork) { if ((ec = setjmp(jmpbuf)) == 0) { test->fn_ptr(); printf(GREEN "[PASS]" RESET " %s\n", fullname); nofork_reset(); return 0; } else { printf(RED "[FAIL] (%d)" RESET " %s\n", ec, fullname); nofork_reset(); return ec; } } if (!(pid = fork())) { // child if ((ec = setjmp(jmpbuf))) { exit(ec); } else { test->fn_ptr(); exit(0); } } else { if (waitpid(pid, &status, 0) == -1) { fprintf(stderr, "waitpid() failed\n"); return 1; } if (WIFEXITED(status)) { switch ((ec = WEXITSTATUS(status))) { case 0: printf(GREEN "[PASS]" RESET " %s\n", fullname); return 0; default: printf(RED "[FAIL] (%d)" RESET " %s\n", ec, fullname); return ec; } } else if (WIFSIGNALED(status)) { int ec = WTERMSIG(status); printf("%s " RED "[FAIL] signaled %d" RESET "\n", fullname, ec); return ec; } return ec; } }