Merge pull request #17 from davidgiven/dtrg-tests

Refactor the tests so they run for several plats.
This commit is contained in:
David Given 2016-11-26 12:59:13 +01:00 committed by GitHub
commit 9641801455
41 changed files with 312 additions and 151 deletions

View file

@ -1,13 +1,13 @@
matrix: matrix:
include: include:
- os: linux - os: linux
- os: osx
addons: addons:
apt: apt:
packages: packages:
- ed - ed
- openbios-ppc - openbios-ppc
- qemu-user
git: git:
depth: 10 depth: 10

View file

@ -14,7 +14,10 @@ vars.plats = {
"rpi", "rpi",
} }
vars.plats_with_tests = { vars.plats_with_tests = {
"linux386",
"linuxppc",
"qemuppc", "qemuppc",
"pc86",
} }
local plat_packages = {} local plat_packages = {}

View file

@ -26,7 +26,6 @@ void* sbrk(int increment)
{ {
char* old; char* old;
char* new; char* new;
char* actual;
if (!current) if (!current)
current = (char*) _syscall(__NR_brk, 0, 0, 0); current = (char*) _syscall(__NR_brk, 0, 0, 0);
@ -35,15 +34,21 @@ void* sbrk(int increment)
return current; return current;
old = current; old = current;
new = old + increment; new = old + increment;
actual = (char*) _syscall(__NR_brk, (quad) new, 0, 0); if ((increment > 0) && (new <= old))
if (actual < new) goto out_of_memory;
{ else if ((increment < 0) && (new >= old))
errno = ENOMEM; goto out_of_memory;
return OUT_OF_MEMORY;
} if (brk(new) < 0)
goto out_of_memory;
current = actual;
return old; return old;
out_of_memory:
errno = ENOMEM;
return OUT_OF_MEMORY;
} }

View file

@ -16,6 +16,7 @@ return installable {
["$(PLATDEP)/linux386/as"] = "+as", ["$(PLATDEP)/linux386/as"] = "+as",
["$(PLATDEP)/linux386/ncg"] = "+ncg", ["$(PLATDEP)/linux386/ncg"] = "+ncg",
["$(PLATIND)/descr/linux386"] = "./descr", ["$(PLATIND)/descr/linux386"] = "./descr",
"util/amisc+aelflod-pkg",
"util/opt+pkg", "util/opt+pkg",
} }
} }

View file

@ -0,0 +1,7 @@
include("tests/plat/build.lua")
plat_testsuite {
name = "tests",
plat = "linux386",
method = "qemu-i386"
}

View file

@ -16,6 +16,7 @@ return installable {
["$(PLATDEP)/linux68k/as"] = "+as", ["$(PLATDEP)/linux68k/as"] = "+as",
["$(PLATDEP)/linux68k/ncg"] = "+ncg", ["$(PLATDEP)/linux68k/ncg"] = "+ncg",
["$(PLATIND)/descr/linux68k"] = "./descr", ["$(PLATIND)/descr/linux68k"] = "./descr",
"util/amisc+aelflod-pkg",
"util/opt+pkg", "util/opt+pkg",
} }
} }

View file

@ -28,6 +28,7 @@ return installable {
["$(PLATDEP)/linuxppc/mcg"] = "+mcg", ["$(PLATDEP)/linuxppc/mcg"] = "+mcg",
["$(PLATDEP)/linuxppc/top"] = "+top", ["$(PLATDEP)/linuxppc/top"] = "+top",
["$(PLATIND)/descr/linuxppc"] = "./descr", ["$(PLATIND)/descr/linuxppc"] = "./descr",
"util/amisc+aelflod-pkg",
"util/opt+pkg", "util/opt+pkg",
} }
} }

View file

@ -0,0 +1,7 @@
include("tests/plat/build.lua")
plat_testsuite {
name = "tests",
plat = "linuxppc",
method = "qemu-ppc"
}

View file

@ -23,6 +23,9 @@ are stubs required to make the demo apps link. File descriptors 0, 1 and 2
represent the console. All reads block. There's enough TTY emulation to allow represent the console. All reads block. There's enough TTY emulation to allow
\n conversion and local echo (but it can't be turned off). \n conversion and local echo (but it can't be turned off).
Console output is echoed to the serial port (without any setup). This is used
by qemu for running tests.
Example command line Example command line
==================== ====================

View file

@ -23,7 +23,7 @@
! If you ever need to change the boot code, this needs adjusting. I recommend ! If you ever need to change the boot code, this needs adjusting. I recommend
! a hex editor. ! a hex editor.
PADDING = 0xB7 PADDING = 0xB3
! Some definitions. ! Some definitions.
@ -271,9 +271,12 @@ finished:
! Push standard parameters onto the stack and go. ! Push standard parameters onto the stack and go.
push envp ! envp mov ax, envp
push argv ! argv push ax
push 1 ! argc mov ax, argv
push ax
mov ax, 1
push ax
call __m_a_i_n call __m_a_i_n
! fall through into the exit routine. ! fall through into the exit routine.

View file

@ -37,6 +37,7 @@ extern char** environ;
extern void _exit(int); extern void _exit(int);
extern pid_t getpid(void); extern pid_t getpid(void);
extern int brk(void* addr);
extern void* sbrk(int increment); extern void* sbrk(int increment);
extern int isatty(int d); extern int isatty(int d);
extern off_t lseek(int fildes, off_t offset, int whence); extern off_t lseek(int fildes, off_t offset, int whence);

View file

@ -21,9 +21,18 @@ __sys_rawwrite:
push bp push bp
mov bp, sp mov bp, sp
! Write to the BIOS console.
movb al, 4(bp) movb al, 4(bp)
movb ah, 0x0E movb ah, 0x0E
mov bx, 0x0007 mov bx, 0x0007
int 0x10 int 0x10
! Also write to the serial port (used by the test suite).
movb ah, 0x01
xor dx, dx
int 0x14
jmp .cret jmp .cret

View file

@ -22,7 +22,10 @@ int brk(void* newend)
if ((p > (&dummy - STACK_BUFFER)) || if ((p > (&dummy - STACK_BUFFER)) ||
(p < _end)) (p < _end))
{
errno = ENOMEM;
return -1; return -1;
}
current = p; current = p;
return 0; return 0;
@ -31,13 +34,26 @@ int brk(void* newend)
void* sbrk(int increment) void* sbrk(int increment)
{ {
char* old; char* old;
char* new;
if (increment == 0) if (increment == 0)
return current; return current;
old = current; old = current;
if (brk(old + increment) < 0)
return OUT_OF_MEMORY; new = old + increment;
if ((increment > 0) && (new <= old))
goto out_of_memory;
else if ((increment < 0) && (new >= old))
goto out_of_memory;
if (brk(new) < 0)
goto out_of_memory;
return old; return old;
out_of_memory:
errno = ENOMEM;
return OUT_OF_MEMORY;
} }

View file

@ -0,0 +1,7 @@
include("tests/plat/build.lua")
plat_testsuite {
name = "tests",
plat = "pc86",
method = "qemu-system-i386"
}

View file

@ -34,13 +34,26 @@ int brk(void* newend)
void* sbrk(int increment) void* sbrk(int increment)
{ {
char* old; char* old;
char* new;
if (increment == 0) if (increment == 0)
return current; return current;
old = current; old = current;
if (brk(old + increment) < 0)
return OUT_OF_MEMORY; new = old + increment;
if ((increment > 0) && (new <= old))
goto out_of_memory;
else if ((increment < 0) && (new >= old))
goto out_of_memory;
if (brk(new) < 0)
goto out_of_memory;
return old; return old;
out_of_memory:
errno = ENOMEM;
return OUT_OF_MEMORY;
} }

View file

@ -1,28 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include "test.h"
int main(int argc, const char* argv[])
{
void* p;
ASSERT(-1 == (intptr_t)brk((void*)0xffffffff));
ASSERT(ENOMEM == errno);
p = sbrk(0);
ASSERT(p == sbrk(0));
ASSERT(p == sbrk(8));
ASSERT(p != sbrk(0));
ASSERT(p != sbrk(-8));
ASSERT(p == sbrk(0));
/* We assume the test environment has less than 2GB of RAM. */
ASSERT(-1 == (intptr_t)sbrk(INT_MAX));
ASSERT(-1 == (intptr_t)sbrk(INT_MIN));
finished();
}

View file

@ -1,49 +1,7 @@
include("plat/build.lua") include("tests/plat/build.lua")
local qemu = "qemu-system-ppc" plat_testsuite {
local tests = {} name = "tests",
plat = "qemuppc",
if os.execute("which "..qemu.." > /dev/null") ~= 0 then method = "qemu-system-ppc"
print("warning: skipping tests which require ", qemu)
else
local testcases = filenamesof("./*.c", "./*.s", "./*.e", "./*.p")
for _, f in ipairs(testcases) do
local fs = replace(basename(f), "%..$", "")
local _, _, lang = fs:find("_(.)$")
if not lang then
lang = "e"
end
local bin = ackprogram {
name = fs.."_bin",
srcs = { f },
deps = { "plat/qemuppc/tests/lib+lib" },
vars = {
plat = "qemuppc",
lang = lang,
ackcflags = "-O0"
}
}
tests[#tests+1] = normalrule {
name = fs,
outleaves = { "stamp" },
ins = {
bin,
"./testdriver.sh"
},
commands = {
"%{ins[2]} "..qemu.." %{ins[1]} 5",
"touch %{outs}"
}
}
end
end
normalrule {
name = "tests",
outleaves = { "stamp" },
ins = tests,
commands = { "touch %{outs}" }
} }

View file

@ -1,22 +0,0 @@
#!/bin/sh
qemu=$1
img=$2
timeout=$3
pipe=/tmp/$$.testdriver.pipe
mknod $pipe p
trap "rm -f $pipe" EXIT
result=/tmp/$$.testdriver.result
trap "rm -f $result" EXIT
pidfile=/tmp/$$.testdriver.pid
trap "rm -f $pidfile" EXIT
( $qemu -nographic -kernel $img 2>&1 & echo $! > $pidfile ) \
| tee $result \
| ( timeout $timeout grep -l -q @@FINISHED ; echo ) \
| ( read dummy && kill $(cat $pidfile) )
( grep -q @@FAIL $result || ! grep -q @@FINISHED $result ) && cat $result && exit 1
exit 0

View file

@ -6,3 +6,4 @@ void _m_a_i_n(void)
ASSERT(0 == 0); ASSERT(0 == 0);
finished(); finished();
} }

46
tests/plat/brk_c.c Normal file
View file

@ -0,0 +1,46 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include "test.h"
int main(int argc, const char* argv[])
{
char* o;
char* p;
p = sbrk(0);
ASSERT(p == sbrk(0));
ASSERT(p == sbrk(8));
ASSERT(p != sbrk(0));
ASSERT(p != sbrk(-8));
ASSERT(p == sbrk(0));
errno = 0;
o = sbrk(INT_MAX);
if (o == (char*)-1)
ASSERT(ENOMEM == errno);
else
{
ASSERT(0 == errno);
p = sbrk(0);
ASSERT(p > o);
brk(o);
}
errno = 0;
o = sbrk(INT_MIN);
if (o == (char*)-1)
ASSERT(ENOMEM == errno);
else
{
ASSERT(0 == errno);
p = sbrk(0);
ASSERT(p < o);
brk(o);
}
finished();
}

64
tests/plat/build.lua Normal file
View file

@ -0,0 +1,64 @@
include("plat/build.lua")
definerule("plat_testsuite",
{
plat = { type="string" },
method = { type="string" },
},
function(e)
-- Remember this is executed from the caller's directory; local
-- target names will resolve there.
local testfiles = filenamesof(
"tests/plat/*.c",
"tests/plat/*.e",
"tests/plat/*.p"
)
acklibrary {
name = "lib",
srcs = { "tests/plat/lib/test.c" },
hdrs = { "tests/plat/lib/test.h" },
vars = { plat = e.plat },
}
local tests = {}
for _, f in ipairs(testfiles) do
local fs = replace(basename(f), "%..$", "")
local _, _, lang = fs:find("_(.)$")
if not lang then
lang = "e"
end
local bin = ackprogram {
name = fs.."_bin",
srcs = { f },
deps = { "+lib" },
vars = {
plat = e.plat,
lang = lang,
ackcflags = "-O0"
}
}
tests[#tests+1] = normalrule {
name = fs,
outleaves = { "stamp" },
ins = {
bin,
"tests/plat/testdriver.sh"
},
commands = {
"%{ins[2]} "..e.method.." %{ins[1]} 5",
"touch %{outs}"
}
}
end
return normalrule {
name = e.name,
outleaves = { "stamp" },
ins = tests,
commands = { "touch %{outs}" }
}
end
)

View file

@ -1,11 +1,12 @@
#include <limits.h>
#include "test.h" #include "test.h"
/* Constants in globals to defeat constant folding. */ /* Constants in globals to defeat constant folding. */
double one = 1.0; double one = 1.0;
double zero = 0.0; double zero = 0.0;
double minusone = -1.0; double minusone = -1.0;
double big = 2147483647.0; double big = (double)INT_MAX;
double minusbig = -2147483648.0; double minusbig = (double)INT_MIN;
/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ /* Bypasses the CRT, so there's no stdio or BSS initialisation. */
void _m_a_i_n(void) void _m_a_i_n(void)
@ -13,8 +14,8 @@ void _m_a_i_n(void)
ASSERT((int)zero == 0); ASSERT((int)zero == 0);
ASSERT((int)one == 1); ASSERT((int)one == 1);
ASSERT((int)minusone == -1); ASSERT((int)minusone == -1);
ASSERT((int)big == 2147483647); ASSERT((int)big == INT_MAX);
ASSERT((int)minusbig == -2147483648); ASSERT((int)minusbig == INT_MIN);
finished(); finished();
} }

View file

@ -1,16 +1,17 @@
#include <limits.h>
#include "test.h" #include "test.h"
/* Constants in globals to defeat constant folding. */ /* Constants in globals to defeat constant folding. */
double one = 1.0; double one = 1.0;
double zero = 0.0; double zero = 0.0;
double big = 4294967295.0; double big = (double)UINT_MAX;
/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ /* Bypasses the CRT, so there's no stdio or BSS initialisation. */
void _m_a_i_n(void) void _m_a_i_n(void)
{ {
ASSERT((unsigned int)zero == 0); ASSERT((unsigned int)zero == 0);
ASSERT((unsigned int)one == 1); ASSERT((unsigned int)one == 1);
ASSERT((unsigned int)big == 4294967295); ASSERT((unsigned int)big == UINT_MAX);
finished(); finished();
} }

View file

@ -1,11 +1,12 @@
#include <limits.h>
#include "test.h" #include "test.h"
/* Constants in globals to defeat constant folding. */ /* Constants in globals to defeat constant folding. */
int one = 1; int one = 1;
int zero = 0; int zero = 0;
int minusone = -1; int minusone = -1;
int big = 0x7fffffff; int big = INT_MAX;
int minusbig = -0x8000000; int minusbig = INT_MIN;
/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ /* Bypasses the CRT, so there's no stdio or BSS initialisation. */
void _m_a_i_n(void) void _m_a_i_n(void)
@ -13,8 +14,8 @@ void _m_a_i_n(void)
ASSERT((double)zero == 0.0); ASSERT((double)zero == 0.0);
ASSERT((double)one == 1.0); ASSERT((double)one == 1.0);
ASSERT((double)minusone == -1.0); ASSERT((double)minusone == -1.0);
ASSERT((double)big == 2147483647.0); ASSERT((double)big == (double)INT_MAX);
/* ASSERT((double)minusbig == -2147483648.0); FIXME: fails for now */ /* ASSERT((double)minusbig == (double)INT_MIN); FIXME: fails for now */
finished(); finished();
} }

View file

@ -1,16 +1,17 @@
#include <limits.h>
#include "test.h" #include "test.h"
/* Constants in globals to defeat constant folding. */ /* Constants in globals to defeat constant folding. */
unsigned int one_u = 1; unsigned int one_u = 1;
unsigned int zero_u = 0; unsigned int zero_u = 0;
unsigned int big_u = 0xffffffff; unsigned int big_u = UINT_MAX;
/* Bypasses the CRT, so there's no stdio or BSS initialisation. */ /* Bypasses the CRT, so there's no stdio or BSS initialisation. */
void _m_a_i_n(void) void _m_a_i_n(void)
{ {
ASSERT((double)zero_u == 0.0); ASSERT((double)zero_u == 0.0);
ASSERT((double)one_u == 1.0); ASSERT((double)one_u == 1.0);
ASSERT((double)big_u == 4294967295.0); ASSERT((double)big_u == (double)UINT_MAX);
finished(); finished();
} }

View file

@ -1,5 +1,5 @@
# #
mes 2, 4, 4 mes 2, EM_WSIZE, EM_PSIZE
exp $_m_a_i_n exp $_m_a_i_n
pro $_m_a_i_n, 0 pro $_m_a_i_n, 0
@ -10,12 +10,12 @@
rom 0I1, 0I1, 0I1, 0I1 rom 0I1, 0I1, 0I1, 0I1
loe .1 loe .1
loc 1 /* bit number */ loc 1 /* bit number */
inn 4 inn EM_WSIZE
zeq *1 zeq *1
loc __LINE__ loc __LINE__
cal $fail cal $fail
ass 4 ass EM_WSIZE
1 1
/* Test existent bit */ /* Test existent bit */
@ -24,12 +24,12 @@
rom 2I1, 0I1, 0I1, 0I1 rom 2I1, 0I1, 0I1, 0I1
loe .2 loe .2
loc 1 /* bit number */ loc 1 /* bit number */
inn 4 inn EM_WSIZE
zne *2 zne *2
loc __LINE__ loc __LINE__
cal $fail cal $fail
ass 4 ass EM_WSIZE
2 2
/* Test non-existent high bit */ /* Test non-existent high bit */
@ -38,36 +38,44 @@
rom 0I1, 0I1, 0I1, 0I1 rom 0I1, 0I1, 0I1, 0I1
rom 0I1, 0I1, 0I1, 0I1 rom 0I1, 0I1, 0I1, 0I1
.31 .31
rom 33 /* to defeat constant folding */ rom (EM_WSIZE*8)+1 /* to defeat constant folding */
lae .3 lae .3
loi 8 loi EM_WSIZE*2
loe .31 /* bit number */ loe .31 /* bit number */
inn 8 inn EM_WSIZE*2
zeq *3 zeq *3
loc __LINE__ loc __LINE__
cal $fail cal $fail
ass 4 ass EM_WSIZE
3 3
/* Test existent high bit */ /* Test existent high bit */
.4 .4
#if EM_WSIZE == 2
rom 0I1, 0I1
rom 2I1, 0I1
#elif EM_WSIZE == 4
rom 0I1, 0I1, 0I1, 0I1 rom 0I1, 0I1, 0I1, 0I1
rom 2I1, 0I1, 0I1, 0I1 rom 2I1, 0I1, 0I1, 0I1
#else
#error Unknown word size
#endif
.41 .41
rom 33 /* to defeat constant folding */ rom (EM_WSIZE*8)+1 /* to defeat constant folding */
lae .4 lae .4
loi 8 loi EM_WSIZE*2
loe .41 /* bit number */ loe .41 /* bit number */
inn 8 inn EM_WSIZE*2
zne *4 zne *4
loc __LINE__ loc __LINE__
cal $fail cal $fail
ass 4 ass EM_WSIZE
4 4
cal $finished cal $finished

View file

@ -1,3 +1,4 @@
#include <limits.h>
#include "test.h" #include "test.h"
/* Constants in globals to defeat constant folding. */ /* Constants in globals to defeat constant folding. */
@ -25,8 +26,8 @@ void _m_a_i_n(void)
ASSERT(((unsigned int)one >>(unsigned int)zero) == 1); ASSERT(((unsigned int)one >>(unsigned int)zero) == 1);
ASSERT(((unsigned int)one >>(unsigned int)one) == 0); ASSERT(((unsigned int)one >>(unsigned int)one) == 0);
ASSERT(((unsigned int)minusone>>(unsigned int)zero) == 0xffffffff); ASSERT(((unsigned int)minusone>>(unsigned int)zero) == UINT_MAX);
ASSERT(((unsigned int)minusone>>(unsigned int)one) == 0x7fffffff); ASSERT(((unsigned int)minusone>>(unsigned int)one) == (UINT_MAX>>1));
ASSERT((one <<0) == 1); ASSERT((one <<0) == 1);
ASSERT((one <<1) == 2); ASSERT((one <<1) == 2);
@ -45,8 +46,8 @@ void _m_a_i_n(void)
ASSERT(((unsigned int)one >>(unsigned int)0) == 1); ASSERT(((unsigned int)one >>(unsigned int)0) == 1);
ASSERT(((unsigned int)one >>(unsigned int)1) == 0); ASSERT(((unsigned int)one >>(unsigned int)1) == 0);
ASSERT(((unsigned int)minusone>>(unsigned int)0) == 0xffffffff); ASSERT(((unsigned int)minusone>>(unsigned int)0) == UINT_MAX);
ASSERT(((unsigned int)minusone>>(unsigned int)1) == 0x7fffffff); ASSERT(((unsigned int)minusone>>(unsigned int)1) == (UINT_MAX>>1));
finished(); finished();
} }

View file

@ -1,3 +1,4 @@
#include <limits.h>
#include "test.h" #include "test.h"
/* Constants in globals to defeat constant folding. */ /* Constants in globals to defeat constant folding. */
@ -19,13 +20,13 @@ void _m_a_i_n(void)
ASSERT((1 - two) == -1); ASSERT((1 - two) == -1);
ASSERT(((unsigned int)two - (unsigned int)one) == 1); ASSERT(((unsigned int)two - (unsigned int)one) == 1);
ASSERT(((unsigned int)one - (unsigned int)two) == 0xffffffff); ASSERT(((unsigned int)one - (unsigned int)two) == UINT_MAX);
ASSERT(((unsigned int)two - (unsigned int)1) == 1); ASSERT(((unsigned int)two - (unsigned int)1) == 1);
ASSERT(((unsigned int)one - (unsigned int)2) == 0xffffffff); ASSERT(((unsigned int)one - (unsigned int)2) == UINT_MAX);
ASSERT(((unsigned int)2 - (unsigned int)one) == 1); ASSERT(((unsigned int)2 - (unsigned int)one) == 1);
ASSERT(((unsigned int)1 - (unsigned int)two) == 0xffffffff); ASSERT(((unsigned int)1 - (unsigned int)two) == UINT_MAX);
finished(); finished();
} }

View file

@ -4,5 +4,4 @@ acklibrary {
name = "lib", name = "lib",
srcs = { "./test.c" }, srcs = { "./test.c" },
hdrs = { "./test.h" }, hdrs = { "./test.h" },
vars = { plat = "qemuppc" }
} }

View file

@ -6,6 +6,7 @@ void finished(void)
{ {
static const char s[] = "@@FINISHED\n"; static const char s[] = "@@FINISHED\n";
write(1, s, sizeof(s)); write(1, s, sizeof(s));
_exit(0);
} }
void writehex(uint32_t code) void writehex(uint32_t code)
@ -25,7 +26,7 @@ void writehex(uint32_t code)
void fail(uint32_t code) void fail(uint32_t code)
{ {
write(1, "@@FAIL on line 0x", 7); write(1, "@@FAIL 0x", 10);
writehex(code); writehex(code);
write(1, "\n", 1); write(1, "\n", 1);
} }

View file

@ -9,6 +9,6 @@ extern void writehex(uint32_t code);
extern void fail(uint32_t code); extern void fail(uint32_t code);
#define ASSERT(condition) \ #define ASSERT(condition) \
if (!(condition)) fail(__LINE__) do { if (!(condition)) fail(__LINE__); } while(0)
#endif #endif

51
tests/plat/testdriver.sh Executable file
View file

@ -0,0 +1,51 @@
#!/bin/sh
method=$1
img=$2
timeout=$3
pipe=/tmp/$$.testdriver.pipe
mknod $pipe p
trap "rm -f $pipe" EXIT
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 2>/dev/null; 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 2>/dev/null; then
echo "Warning: $method not installed, skipping test"
exit 0
fi
$method $img > $result
;;
*)
echo "Error: $method not known by testdriver"
exit 1
;;
esac
( grep -q @@FAIL $result || ! grep -q @@FINISHED $result ) && cat $result && exit 1
exit 0