From 960584c0f3e8d0e9909b5b98a057dc0faa3f22ff Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 29 Nov 2016 20:59:43 +0100 Subject: [PATCH] Replace the hacky and broken pipeline in testdriver.sh with a custom-written tool in C; much more robust and easier to understand, as well as avoiding the dependency on timeout (which isn't Posix). --- tests/plat/build.lua | 5 +- tests/plat/testdriver.sh | 79 +++++++++++++++-------------- util/build/build.lua | 7 +++ util/build/testrunner.c | 104 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 42 deletions(-) create mode 100644 util/build/build.lua create mode 100644 util/build/testrunner.c diff --git a/tests/plat/build.lua b/tests/plat/build.lua index 3873ed9a5..be65668fd 100644 --- a/tests/plat/build.lua +++ b/tests/plat/build.lua @@ -45,10 +45,11 @@ definerule("plat_testsuite", outleaves = { "stamp" }, ins = { bin, - "tests/plat/testdriver.sh" + "tests/plat/testdriver.sh", + "util/build+testrunner" }, commands = { - "%{ins[2]} "..e.method.." %{ins[1]} 5", + "%{ins[2]} "..e.method.." %{ins[1]} 5 %{ins[3]}", "touch %{outs}" } } diff --git a/tests/plat/testdriver.sh b/tests/plat/testdriver.sh index 272c6b2b7..6af14da0f 100755 --- a/tests/plat/testdriver.sh +++ b/tests/plat/testdriver.sh @@ -2,50 +2,49 @@ method=$1 img=$2 timeout=$3 +timeoutprog=$4 -pipe=/tmp/$$.testdriver.pipe -mknod $pipe p -trap "rm -f $pipe" EXIT +set -e result=/tmp/$$.testdriver.result trap "rm -f $result" EXIT -pidfile=/tmp/$$.testdriver.pid -trap "rm -f $pidfile" EXIT - -case $method in - qemu-system-*) - if ! command -v $method >/dev/null 2>&1 ; then - echo "Warning: $method not installed, skipping test" - exit 0 - fi - - case $method in - qemu-system-i386) img="-drive file=$img,if=floppy,format=raw" ;; - qemu-system-ppc) img="-kernel $img" ;; - esac - - ( $method -nographic $img 2>&1 & echo $! > $pidfile ) \ - | tee $result \ - | ( timeout $timeout grep -l -q @@FINISHED ; echo ) \ - | ( read dummy && kill $(cat $pidfile) ) - - ;; - - qemu-*) - if ! command -v $method >/dev/null 2>&1 ; then - echo "Warning: $method not installed, skipping test" - exit 0 - fi - - $method $img > $result - ;; - - *) - echo "Error: $method not known by testdriver" - exit 1 - ;; -esac - +errcho() { + >&2 echo "$*" +} + +get_test_output() { + case $method in + qemu-system-*) + if ! command -v $method >/dev/null 2>&1 ; then + errcho "Warning: $method not installed, skipping test" + exit 0 + fi + + case $method in + qemu-system-i386) img="-drive file=$img,if=floppy,format=raw" ;; + qemu-system-ppc) img="-kernel $img" ;; + esac + + $timeoutprog -t $timeout -- $method -nographic $img > $result + ;; + + qemu-*) + if ! command -v $method >/dev/null 2>&1 ; then + errcho "Warning: $method not installed, skipping test" + exit 0 + fi + + $method $img > $result + ;; + + *) + errcho "Error: $method not known by testdriver" + exit 1 + ;; + esac +} + +get_test_output > $result ( grep -q @@FAIL $result || ! grep -q @@FINISHED $result ) && cat $result && exit 1 exit 0 diff --git a/util/build/build.lua b/util/build/build.lua new file mode 100644 index 000000000..76623fe91 --- /dev/null +++ b/util/build/build.lua @@ -0,0 +1,7 @@ +cprogram { + name = "testrunner", + srcs = { "./testrunner.c" }, + deps = { + "modules/src/data+lib" + } +} diff --git a/util/build/testrunner.c b/util/build/testrunner.c new file mode 100644 index 000000000..16d526d9a --- /dev/null +++ b/util/build/testrunner.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "diagnostics.h" + +static bool timed_out = false; +static bool child_exited = false; +static char** command = NULL; +static int timeout = 0; +static jmp_buf exit_jmp; + +static void parse_arguments(int argc, char* const argv[]) +{ + program_name = argv[0]; + for (;;) + { + int c = getopt(argc, argv, "t:"); + if (c == -1) + break; + + switch (c) + { + case 't': + timeout = atoi(optarg); + break; + + default: + fatal("syntax: testrunner -- \n"); + } + } + + command = &argv[optind]; + if (!command[0]) + fatal("you must supply a command"); + if (timeout <= 0) + fatal("timeout missing or invalid"); +} + +static void sigalrm_cb(int sigraised) +{ + timed_out = true; + longjmp(exit_jmp, 1); +} + +int main(int argc, char* const argv[]) +{ + const int READ = 0; + const int WRITE = 1; + + int fds[2]; + int pid; + FILE* childin; + int wstatus; + char buffer[4096]; + + parse_arguments(argc, argv); + + if (setjmp(exit_jmp) == 0) + { + /* First time through. */ + + signal(SIGALRM, sigalrm_cb); + alarm(timeout); + + pipe(fds); + pid = fork(); + if (pid == 0) + { + /* Child */ + close(fds[READ]); + close(0); + dup2(fds[WRITE], 1); + dup2(fds[WRITE], 2); + execvp(command[0], command); + _exit(1); + } + + childin = fdopen(fds[READ], "r"); + while (!timed_out) + { + if (!fgets(buffer, sizeof(buffer), childin)) + break; + fputs(buffer, stdout); + + if (strcmp(buffer, "@@FINISHED\n") == 0) + break; + if (strcmp(buffer, "@@FINISHED\r\n") == 0) + break; + } + } + + /* Reached via longjmp, EOF, or seeing a @@FINISHED. */ + + kill(pid, SIGKILL); + waitpid(pid, &wstatus, 0); + if (timed_out) + exit(1); + exit(WEXITSTATUS(wstatus)); +} -- 2.34.1