From 47f09225b07c33c7657ded5bbe4c7e4f98eb9e30 Mon Sep 17 00:00:00 2001 From: JP Appel Date: Fri, 26 Jul 2024 22:23:17 -0400 Subject: FEAT: test can now time out Tests now have an extra field `max_time_ms` which is the max duration before it's process gets killed. A time of 0 ms is used to disable eztester managed timeouts, however a test can still return a timeout status which will be handled similarily. --- README.md | 10 ++++++---- examples/basics.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ eztester.c | 39 +++++++++++++++++++++++++++++++++++---- eztester.h | 1 + 4 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 examples/basics.c diff --git a/README.md b/README.md index bf88697..f89c277 100644 --- a/README.md +++ b/README.md @@ -77,12 +77,12 @@ int main(int argc, char* argv[]){ eztester_list *tests = ezterster_create_list(2); // runners that always return the same status are provided - eztester_register(tests, (eztester_test){eztester_always_pass, "Always Pass"}); - eztester_register(tests, (eztester_test){sample_test, "Sample Test"}); // our test, can be defined in a different translation unit + eztester_register(tests, (eztester_test){eztester_always_pass, "Always Pass", 0}); + eztester_register(tests, (eztester_test){sample_test, "Sample Test", 0}); // our test, can be defined in a different translation unit // a list will resize on register when it doesn't have capacity - eztester_register(tests, (eztester_test){eztester_always_fail, "Always Fail"}); - eztester_register(tests, (eztester_test){eztester_always_warn, "Always Warn"}); + eztester_register(tests, (eztester_test){eztester_always_fail, "Always Fail", 0}); + eztester_register(tests, (eztester_test){eztester_always_warn, "Always Warn", 0}); eztester_register(tests, (eztester_test){sample_shell_test, "Check a non existent url"); @@ -95,6 +95,8 @@ int main(int argc, char* argv[]){ +More programs are provided in [examples](examples/). + ### Static After building, copy the static libraries into your project diff --git a/examples/basics.c b/examples/basics.c new file mode 100644 index 0000000..6aab9d2 --- /dev/null +++ b/examples/basics.c @@ -0,0 +1,51 @@ +#include "../eztester.h" +#include +#include + +#define EZTESTER_IMPLEMENTATION +#include "../build/header/eztester.h" +#undef EZTESTER_IMPLEMENTATION + +eztester_status shell_exists() { + int status = eztester_shell(NULL); + if (status) { + return TEST_FAIL; + } else { + return TEST_PASS; + } +} + +eztester_status python3_exists() { + int status = eztester_shell("/usr/bin/env python3 --version"); + if (status) { + return TEST_FAIL; + } else { + return TEST_PASS; + } +} + +eztester_status sleepy() { + eztester_log("Im feeling sleepy"); + sleep(2); + eztester_log("zzzzzzz"); + sleep(1); + eztester_log("ZZZZZZZZ"); + sleep(2); + eztester_log("oh, hello there."); + return TEST_PASS; +} + +int main(int argc, char *argv[]) { + eztester_list *list = eztester_create_list(5); + + eztester_register( + list, (eztester_test){eztester_always_pass_test, "Always pass", 0}); + eztester_register( + list, (eztester_test){eztester_always_warn_test, "Always warn", 0}); + eztester_register(list, (eztester_test){sleepy, "Timeout test", 4e3}); + eztester_register(list, (eztester_test){python3_exists, "Python3 Exists", 0}); + + eztester_run(list, EXIT_ON_FAIL); + + return 0; +} diff --git a/eztester.c b/eztester.c index 578d50a..b0a2322 100644 --- a/eztester.c +++ b/eztester.c @@ -150,14 +150,16 @@ void eztester_run(eztester_list *test_list, eztester_behavior behavior) { mem->work_in_queue = false; mem->behavior = behavior; - pid_t pid = fork(); + pid_t pid, child_pgid; + pid = fork(); if (pid < 0) { perror("fork"); exit(1); - } - if (pid == 0) { + } else if (pid == 0) { + setpgrp(); _ez_worker(mem, test_list); } + child_pgid = pid; eztester_status status; eztester_test test; @@ -180,12 +182,41 @@ void eztester_run(eztester_list *test_list, eztester_behavior behavior) { kill(pid, SIGCONT); + unsigned int elapsed_ms = 0; while (mem->work_in_queue) { - usleep(50e3); + usleep(1e3); + elapsed_ms++; if (_ez_child_premature_exit) { _ez_premature_exit("Worker Process ended prematurely!", pid, mem, results); } + if (test.max_time_ms > 0 && elapsed_ms > test.max_time_ms) { + + int status; + signal(SIGCHLD, SIG_DFL); + + // ask nicely + killpg(child_pgid, SIGTERM); + usleep(10e3); + if (waitpid(pid, &status, WNOHANG) == 0) { + // no longer ask nicely + killpg(child_pgid, SIGKILL); + } + + pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } else if (pid == 0) { + setpgrp(); + _ez_worker(mem, test_list); + } + child_pgid = pid; + signal(SIGCHLD, _ez_chld_handler); + + mem->work_in_queue = false; + mem->status = TEST_TIMEOUT; + } } status = mem->status; diff --git a/eztester.h b/eztester.h index 1f7e385..9a68651 100644 --- a/eztester.h +++ b/eztester.h @@ -29,6 +29,7 @@ typedef eztester_status(eztester_runner)(); typedef struct { eztester_runner *runner; const char *name; + unsigned int max_time_ms; } eztester_test; typedef struct { -- cgit v1.2.3