1/*	$NetBSD: harness.c,v 1.1.1.1 2009/12/02 00:25:58 haad Exp $	*/
2
3#include <fcntl.h>
4#include <string.h>
5#include <stdio.h>
6#include <sys/socket.h>
7#include <sys/wait.h>
8#include <unistd.h>
9#include <stdlib.h>
10
11pid_t pid;
12int fds[2];
13int *status;
14int nfailed = 0;
15int nskipped = 0;
16int npassed = 0;
17
18char *readbuf = NULL;
19int readbuf_sz = 0, readbuf_used = 0;
20
21int die = 0;
22
23#define PASSED 0
24#define SKIPPED 1
25#define FAILED 2
26
27void handler( int s ) {
28	signal( s, SIG_DFL );
29	kill( pid, s );
30	die = s;
31}
32
33void dump() {
34	write(1, readbuf, readbuf_used);
35}
36
37void clear() {
38	readbuf_used = 0;
39}
40
41void drain() {
42	int sz;
43	char buf[2048];
44	while (1) {
45		sz = read(fds[1], buf, 2048);
46		if (sz <= 0)
47			return;
48		if (readbuf_used + sz >= readbuf_sz) {
49			readbuf_sz = readbuf_sz ? 2 * readbuf_sz : 4096;
50			readbuf = realloc(readbuf, readbuf_sz);
51		}
52		if (!readbuf)
53			exit(205);
54		memcpy(readbuf + readbuf_used, buf, sz);
55		readbuf_used += sz;
56	}
57}
58
59void passed(int i, char *f) {
60	++ npassed;
61	status[i] = PASSED;
62	printf("passed.\n");
63}
64
65void skipped(int i, char *f) {
66	++ nskipped;
67	status[i] = SKIPPED;
68	printf("skipped.\n");
69}
70
71void failed(int i, char *f, int st) {
72	++ nfailed;
73	status[i] = FAILED;
74	if(die == 2) {
75		printf("interrupted.\n");
76		return;
77	}
78	printf("FAILED.\n");
79	printf("-- FAILED %s ------------------------------------\n", f);
80	dump();
81	printf("-- FAILED %s (end) ------------------------------\n", f);
82}
83
84void run(int i, char *f) {
85	pid = fork();
86	if (pid < 0) {
87		perror("Fork failed.");
88		exit(201);
89	} else if (pid == 0) {
90		close(0);
91		dup2(fds[0], 1);
92		dup2(fds[0], 2);
93		execlp("bash", "bash", f, NULL);
94		perror("execlp");
95		fflush(stderr);
96		_exit(202);
97	} else {
98		char buf[128];
99		snprintf(buf, 128, "%s ...", f);
100		buf[127] = 0;
101		printf("Running %-40s ", buf);
102		fflush(stdout);
103		int st, w;
104		while ((w = waitpid(pid, &st, WNOHANG)) == 0) {
105			drain();
106			usleep(20000);
107		}
108		if (w != pid) {
109			perror("waitpid");
110			exit(206);
111		}
112		drain();
113		if (WIFEXITED(st)) {
114			if (WEXITSTATUS(st) == 0) {
115				passed(i, f);
116			} else if (WEXITSTATUS(st) == 200) {
117				skipped(i, f);
118			} else {
119				failed(i, f, st);
120			}
121		} else {
122			failed(i, f, st);
123		}
124		clear();
125	}
126}
127
128int main(int argc, char **argv) {
129	int i;
130	status = alloca(sizeof(int)*argc);
131
132	if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds)) {
133		perror("socketpair");
134		return 201;
135	}
136
137        if ( fcntl( fds[1], F_SETFL, O_NONBLOCK ) == -1 ) {
138		perror("fcntl on socket");
139		return 202;
140	}
141
142	/* set up signal handlers */
143        for (i = 0; i <= 32; ++i) {
144            if (i == SIGCHLD || i == SIGWINCH || i == SIGURG)
145                continue;
146            signal(i, handler);
147        }
148
149	/* run the tests */
150	for (i = 1; i < argc; ++ i) {
151		run(i, argv[i]);
152		if (die)
153			break;
154	}
155
156	printf("\n## %d tests: %d OK, %d failed, %d skipped\n",
157	       npassed + nfailed + nskipped, npassed, nfailed, nskipped);
158
159	/* print out a summary */
160	if (nfailed || nskipped) {
161		for (i = 1; i < argc; ++ i) {
162			switch (status[i]) {
163			case FAILED:
164				printf("FAILED: %s\n", argv[i]);
165				break;
166			case SKIPPED:
167				printf("skipped: %s\n", argv[i]);
168				break;
169			}
170		}
171		printf("\n");
172		return nfailed > 0 || die;
173	}
174	return !die;
175}
176