aboutsummaryrefslogtreecommitdiff
path: root/test_harness/test_harness.c
diff options
context:
space:
mode:
Diffstat (limited to 'test_harness/test_harness.c')
-rw-r--r--test_harness/test_harness.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/test_harness/test_harness.c b/test_harness/test_harness.c
new file mode 100644
index 0000000..bf9249c
--- /dev/null
+++ b/test_harness/test_harness.c
@@ -0,0 +1,181 @@
+#include "test_harness.h"
+#include "fake_env.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <setjmp.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);
+}
+
+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;
+ }
+}