aboutsummaryrefslogtreecommitdiffstats
path: root/eztester.c
diff options
context:
space:
mode:
Diffstat (limited to 'eztester.c')
-rw-r--r--eztester.c199
1 files changed, 141 insertions, 58 deletions
diff --git a/eztester.c b/eztester.c
index fac36d7..0964e7c 100644
--- a/eztester.c
+++ b/eztester.c
@@ -1,48 +1,28 @@
#include "eztester.h"
-#include <assert.h>
+#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-eztester_list *eztester_create_list(const size_t capacity) {
- eztester_list *list = malloc(sizeof(eztester_list));
- if (!list) {
- return NULL;
- }
+#include <string.h>
- list->length = 0;
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
- if (capacity == 0) {
- list->tests = NULL;
- list->capacity = 0;
- return list;
- }
+#include <stdbool.h>
- eztester_test *tests = malloc(capacity * sizeof(eztester_test));
- if (!tests) {
- free(list);
- return NULL;
- }
- list->tests = tests;
+struct shared_mem {
+ int work_in_queue : 1;
+ eztester_status status : 2;
+ eztester_behavior behavior : 2;
+ size_t index;
+};
- list->capacity = capacity;
- return list;
-}
-void eztester_register(eztester_list *test_list, const eztester_test new_test) {
- if (test_list->capacity == 0) {
- test_list->tests = realloc(test_list->tests, 2 * sizeof(eztester_test));
- test_list->capacity = 2;
- }
- if (test_list->capacity <= test_list->length + 1) {
- test_list->capacity *= 2;
- test_list->tests =
- realloc(test_list->tests, test_list->capacity * sizeof(eztester_test));
- assert(test_list->tests);
- }
-
- test_list->tests[test_list->length++] = new_test;
-}
+volatile sig_atomic_t child_premature_exit = 0;
+volatile sig_atomic_t child_premature_exit_signal;
+volatile sig_atomic_t child_premature_exit_status = 0;
void print_test_results(const size_t tests_run, const size_t tests_passed,
const size_t num_tests) {
@@ -56,17 +36,122 @@ void print_test_results(const size_t tests_run, const size_t tests_passed,
}
}
+void premature_exit(const char *message, const pid_t worker,
+ const size_t current_test, const size_t tests_passed,
+ const size_t num_tests) {
+ fprintf(stderr, "%s\n", message);
+ if (child_premature_exit) {
+ if (child_premature_exit_status != 0) {
+ fprintf(stderr, "Worker Process exited with status: %d\n",
+ child_premature_exit_status);
+ }
+ if (child_premature_exit_signal > 0) {
+ fprintf(stderr, "Worker Process exited because of signal: %s",
+ strsignal(child_premature_exit_signal));
+ }
+ }
+ kill(worker, SIGTERM);
+ wait(NULL);
+ print_test_results(current_test, tests_passed, num_tests);
+ exit(1);
+}
+
+void worker(volatile struct shared_mem *mem, const eztester_list *list) {
+ while (mem->index < list->length) {
+ // wait for work
+ while (!mem->work_in_queue) {
+ usleep(1000 * 50);
+ }
+ mem->status = list->tests[mem->index].runner();
+
+ // check if worker should die
+ if (mem->status == TEST_ERROR ||
+ (mem->status == TEST_FAIL && mem->behavior != CONTINUE_ALL) ||
+ (mem->status == TEST_WARNING && mem->behavior == EXIT_ON_FAIL)) {
+ exit(1);
+ }
+
+ mem->work_in_queue = false;
+ }
+ exit(0);
+}
+
+void chld_handler(int signum) {
+ int status;
+ pid_t pid;
+ child_premature_exit = 1;
+
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ if (WIFEXITED(status)) {
+ child_premature_exit_status = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ child_premature_exit_signal = WTERMSIG(status);
+ }
+ }
+}
+
void eztester_run(eztester_list *test_list, eztester_behavior behavior) {
+ const size_t shm_size = sizeof(struct shared_mem);
+ int shm_fd = shm_open("/test_queue", O_RDWR | O_CREAT, 0666);
+ if (shm_fd == -1) {
+ perror("shm_open");
+ exit(1);
+ }
+
+ if (ftruncate(shm_fd, shm_size) == -1) {
+ perror("ftruncate");
+ exit(1);
+ }
+
+ void *shm_ptr =
+ mmap(NULL, shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
+ if (shm_ptr == MAP_FAILED) {
+ perror("mmap");
+ exit(1);
+ }
+
+ struct shared_mem *mem = shm_ptr;
+ mem->index = 0;
+ mem->work_in_queue = false;
+ mem->behavior = behavior;
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ exit(1);
+ }
+ if (pid == 0) {
+ worker(mem, test_list);
+ }
+
eztester_status status;
eztester_test test;
const size_t length = test_list->length;
size_t pass_count = 0;
+ // set child signal handler
+ signal(SIGCHLD, chld_handler);
+
for (size_t i = 0; i < test_list->length; i++) {
test = test_list->tests[i];
+
+ mem->index = i;
+ mem->work_in_queue = true;
+
printf("[%03zu/%03zu] Testing: %s\n", i + 1, length, test.name);
fflush(stdout);
- status = test.runner();
+ if (child_premature_exit) {
+ premature_exit("Worker Process ended prematurely!", pid, i + 1,
+ pass_count, length);
+ }
+ while (mem->work_in_queue) {
+ usleep(1000 * 50);
+ if (child_premature_exit) {
+ premature_exit("Worker Process ended prematurely!", pid, i + 1,
+ pass_count, length);
+ }
+ }
+ status = mem->status;
switch (status) {
case TEST_PASS:
@@ -77,33 +162,42 @@ void eztester_run(eztester_list *test_list, eztester_behavior behavior) {
case TEST_WARNING:
printf("[%03zu/%03zu] %s Result: Warning\n", i + 1, length, test.name);
if (behavior == EXIT_ON_WARNING) {
- printf("Warning occured, Exitting\n");
- print_test_results(i + 1, pass_count, length);
- exit(1);
+ premature_exit("Warning occured, Exitting", pid, i + 1, pass_count,
+ length);
}
break;
case TEST_FAIL:
printf("[%03zu/%03zu] %s Result: Fail\n", i + 1, length, test.name);
if (behavior != CONTINUE_ALL) {
- printf("Failure occured, Exitting\n");
- print_test_results(i + 1, pass_count, length);
- exit(1);
+ premature_exit("Failure occured, Exitting", pid, i + 1, pass_count,
+ length);
}
break;
case TEST_ERROR:
- printf("[%03zu/%03zu] %s Result: Error\nFatal Error occured! Exiting\n",
- i + 1, length, test.name);
- print_test_results(i + 1, pass_count, length);
- exit(1);
+ printf("[%03zu/%03zu] %s Result: Error\n", i + 1, length, test.name);
+ premature_exit("Fatal Error occured! Exiting", pid, i + 1, pass_count,
+ length);
break;
}
}
+ signal(SIGCHLD, SIG_DFL);
print_test_results(length, pass_count, length);
+
+ // unmap memory
+ if (munmap(shm_ptr, shm_size) == -1) {
+ perror("munmap");
+ exit(1);
+ }
+ // unlink shared memory
+ if (shm_unlink("/test_queue") == -1) {
+ perror("shm_unlink");
+ exit(1);
+ }
}
-void eztester_test_print(const char *format, ...) {
+void eztester_test_print(const char *restrict format, ...) {
va_list args;
va_start(args, format);
printf("> ");
@@ -112,17 +206,6 @@ void eztester_test_print(const char *format, ...) {
va_end(args);
}
-void eztester_clear_list(eztester_list *test_list) {
- free(test_list->tests);
- test_list->length = 0;
- test_list->capacity = 0;
-}
-
-void eztester_destroy_list(eztester_list *test_list) {
- eztester_clear_list(test_list);
- free(test_list);
-}
-
eztester_status eztester_always_pass_test() { return TEST_PASS; }
eztester_status eztester_always_warn_test() { return TEST_WARNING; }
eztester_status eztester_always_fail_test() { return TEST_FAIL; }