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).
This commit is contained in:
David Given 2016-11-29 20:59:43 +01:00
parent 4f446467c8
commit 960584c0f3
4 changed files with 146 additions and 35 deletions

View file

@ -45,10 +45,11 @@ definerule("plat_testsuite",
outleaves = { "stamp" }, outleaves = { "stamp" },
ins = { ins = {
bin, bin,
"tests/plat/testdriver.sh" "tests/plat/testdriver.sh",
"util/build+testrunner"
}, },
commands = { commands = {
"%{ins[2]} "..e.method.." %{ins[1]} 5", "%{ins[2]} "..e.method.." %{ins[1]} 5 %{ins[3]}",
"touch %{outs}" "touch %{outs}"
} }
} }

View file

@ -2,50 +2,49 @@
method=$1 method=$1
img=$2 img=$2
timeout=$3 timeout=$3
timeoutprog=$4
pipe=/tmp/$$.testdriver.pipe set -e
mknod $pipe p
trap "rm -f $pipe" EXIT
result=/tmp/$$.testdriver.result result=/tmp/$$.testdriver.result
trap "rm -f $result" EXIT trap "rm -f $result" EXIT
pidfile=/tmp/$$.testdriver.pid errcho() {
trap "rm -f $pidfile" EXIT >&2 echo "$*"
}
case $method in get_test_output() {
qemu-system-*) case $method in
if ! command -v $method >/dev/null 2>&1 ; then qemu-system-*)
echo "Warning: $method not installed, skipping test" if ! command -v $method >/dev/null 2>&1 ; then
exit 0 errcho "Warning: $method not installed, skipping test"
fi exit 0
fi
case $method in case $method in
qemu-system-i386) img="-drive file=$img,if=floppy,format=raw" ;; qemu-system-i386) img="-drive file=$img,if=floppy,format=raw" ;;
qemu-system-ppc) img="-kernel $img" ;; qemu-system-ppc) img="-kernel $img" ;;
esac esac
( $method -nographic $img 2>&1 & echo $! > $pidfile ) \ $timeoutprog -t $timeout -- $method -nographic $img > $result
| tee $result \ ;;
| ( timeout $timeout grep -l -q @@FINISHED ; echo ) \
| ( read dummy && kill $(cat $pidfile) )
;;
qemu-*) qemu-*)
if ! command -v $method >/dev/null 2>&1 ; then if ! command -v $method >/dev/null 2>&1 ; then
echo "Warning: $method not installed, skipping test" errcho "Warning: $method not installed, skipping test"
exit 0 exit 0
fi fi
$method $img > $result $method $img > $result
;; ;;
*) *)
echo "Error: $method not known by testdriver" errcho "Error: $method not known by testdriver"
exit 1 exit 1
;; ;;
esac esac
}
get_test_output > $result
( grep -q @@FAIL $result || ! grep -q @@FINISHED $result ) && cat $result && exit 1 ( grep -q @@FAIL $result || ! grep -q @@FINISHED $result ) && cat $result && exit 1
exit 0 exit 0

7
util/build/build.lua Normal file
View file

@ -0,0 +1,7 @@
cprogram {
name = "testrunner",
srcs = { "./testrunner.c" },
deps = {
"modules/src/data+lib"
}
}

104
util/build/testrunner.c Normal file
View file

@ -0,0 +1,104 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <setjmp.h>
#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 <timeout in secs> -- <command>\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));
}