From 182494a5b7c10191bd9a4d25474add69908a9ae4 Mon Sep 17 00:00:00 2001 From: Petr Rockai Date: Thu, 12 Feb 2009 19:54:45 +0000 Subject: [PATCH] Re-implement the test harness in C. This lets us pass through signals and trigger proper test teardown upon harness interrupt or termination. I also tweaked the output somewhat while I was at it... --- test/Makefile.in | 15 +++-- test/harness.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 test/harness.c diff --git a/test/Makefile.in b/test/Makefile.in index e21f9f6bd..ed66cb79a 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -25,13 +25,16 @@ abs_builddir = @abs_builddir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ -all: bin/not init.sh - sh harness.sh +all: init.sh + ./bin/harness $(T) -bin/not: .bin-dir-stamp +bin/not: .bin-dir-stamp not.c $(CC) -o bin/not not.c -init.sh: Makefile.in .bin-dir-stamp +bin/harness: .bin-dir-stamp harness.c + $(CC) -o bin/harness harness.c + +init.sh: Makefile.in .bin-dir-stamp bin/not bin/harness rm -f $@-t $@ echo 'top_srcdir=$(top_srcdir)' >> $@-t echo 'abs_top_builddir=$(abs_top_builddir)' >> $@-t @@ -52,8 +55,8 @@ T = $(wildcard t-*.sh) Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -$(T): bin/not init.sh - sh harness.sh $@ +$(T): init.sh + ./bin/harness $@ .bin-dir-stamp: lvm-wrapper rm -rf bin diff --git a/test/harness.c b/test/harness.c new file mode 100644 index 000000000..31b3922b4 --- /dev/null +++ b/test/harness.c @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include + +pid_t pid; +int fds[2]; +int *status; +int nfailed = 0; +int nskipped = 0; +int npassed = 0; + +char *readbuf = NULL; +int readbuf_sz = 0, readbuf_used = 0; + +int die = 0; + +#define PASSED 0 +#define SKIPPED 1 +#define FAILED 2 + +void handler( int s ) { + signal( s, SIG_DFL ); + kill( pid, s ); + die = s; +} + +void dump() { + write(1, readbuf, readbuf_used); +} + +void clear() { + readbuf_used = 0; +} + +void drain() { + int sz; + char buf[2048]; + while (1) { + sz = read(fds[1], buf, 2048); + if (sz <= 0) + return; + if (readbuf_used + sz >= readbuf_sz) { + readbuf_sz = readbuf_sz ? 2 * readbuf_sz : 4096; + readbuf = realloc(readbuf, readbuf_sz); + } + if (!readbuf) + exit(205); + memcpy(readbuf + readbuf_used, buf, sz); + readbuf_used += sz; + } +} + +void passed(int i, char *f) { + ++ npassed; + status[i] = PASSED; + printf("passed.\n"); +} + +void skipped(int i, char *f) { + ++ nskipped; + status[i] = SKIPPED; + printf("skipped.\n"); +} + +void failed(int i, char *f, int st) { + ++ nfailed; + status[i] = FAILED; + if(die == 2) { + printf("interrupted.\n"); + return; + } + printf("FAILED.\n"); + printf("-- FAILED %s ------------------------------------\n", f); + dump(); + printf("-- FAILED %s (end) ------------------------------\n", f); +} + +void run(int i, char *f) { + pid = fork(); + if (pid < 0) { + perror("Fork failed."); + exit(201); + } else if (pid == 0) { + close(0); + dup2(fds[0], 1); + dup2(fds[0], 2); + execlp("bash", "bash", f, NULL); + perror("execlp"); + exit(202); + } else { + char buf[128]; + snprintf(buf, 128, "%s ...", f); + buf[127] = 0; + printf("Running %-40s ", buf); + fflush(stdout); + int st, w; + while ((w = waitpid(pid, &st, WNOHANG)) == 0) { + drain(); + usleep(20000); + } + if (w != pid) { + perror("waitpid"); + exit(206); + } + if (WIFEXITED(st)) { + if (WEXITSTATUS(st) == 0) { + passed(i, f); + } else if (WEXITSTATUS(st) == 200) { + skipped(i, f); + } else { + failed(i, f, st); + } + } else { + failed(i, f, st); + } + clear(); + } +} + +int main(int argc, char **argv) { + int i; + status = alloca(sizeof(int)*argc); + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds)) { + perror("socketpair"); + return 201; + } + + if ( fcntl( fds[1], F_SETFL, O_NONBLOCK ) == -1 ) { + perror("fcntl on socket"); + return 202; + } + + /* set up signal handlers */ + for (i = 0; i <= 32; ++i) { + if (i == SIGCHLD || i == SIGWINCH || i == SIGURG) + continue; + signal(i, handler); + } + + /* run the tests */ + for (i = 1; i < argc; ++ i) { + run(i, argv[i]); + if (die) + break; + } + + printf("\n## %d tests: %d OK, %d failed, %d skipped\n", + npassed + nfailed + nskipped, npassed, nfailed, nskipped); + + /* print out a summary */ + if (nfailed || nskipped ) { + for (i = 1; i < argc; ++ i) { + switch (status[i]) { + case FAILED: + printf("FAILED: %s\n", argv[i]); + break; + case SKIPPED: + printf("skipped: %s\n", argv[i]); + break; + } + } + printf("\n"); + return 1; + } + return !die; +} -- 2.43.5