Charles.Forsyth20080116-1340f4bb33e18 years agoHistory
478 lines · 10.6 KB · Brainfuck
RawDownload
implement Itest; include "sys.m";	sys: Sys;include "string.m";	str: String;include "draw.m";include "daytime.m";	daytime: Daytime;include "bufio.m";	bufio: Bufio;	Iobuf: import bufio;include "readdir.m";	readdir: Readdir;include "arg.m";include "itslib.m";	S_INFO, S_WARN, S_ERROR, S_FATAL, S_STIME, S_ETIME: import Itslib;include "env.m";	env: Env;include "sh.m"; SUMFILE: con "summary";MSGFILE: con "msgs";README: con "README"; configfile := "";cflag := -1;verbosity := 3;repcount := 1;recroot := "";display_stderr := 0;display_stdout := 0;now := 0; stdout: ref Sys->FD;stderr: ref Sys->FD;context: ref Draw->Context; Test: adt {	spec: string;	fullspec: string;	cmd: Command;	recdir: string;	stdout: string;	stderr: string;	nruns: int;	nwarns: int;	nerrors: int;	nfatals: int;	failed: int;};  Itest: module{	init: fn(ctxt: ref Draw->Context, argv: list of string);};   init(ctxt: ref Draw->Context, args: list of string){	sys = load Sys Sys->PATH;	stdout = sys->fildes(1);	stderr = sys->fildes(2);	context = ctxt;	arg := load Arg Arg->PATH;	if(arg == nil)		nomod(Arg->PATH);	daytime = load Daytime Daytime->PATH;	if(daytime == nil)		nomod(Daytime->PATH);	str = load String String->PATH;	bufio = load Bufio Bufio->PATH;	if(bufio == nil)		nomod(Bufio->PATH);	if(str == nil)		nomod(String->PATH);	readdir = load Readdir Readdir->PATH;	if(readdir == nil)		nomod(Readdir->PATH);	env = load Env Env->PATH;	if(env == nil)		nomod(Env->PATH);	arg->init(args);	while((o := arg->opt()) != 0)		case o {		'c' =>	cflag = toint("c", arg->arg(), 0, 9);		'e' =>	display_stderr++;		'o' =>	display_stdout++;		'r' =>		repcount = toint("r", arg->arg(), 0, -1);		'v' =>	verbosity = toint("v", arg->arg(), 0, 9);		'C' =>	configfile = arg->arg();		'R' =>	recroot = arg->arg();		* =>		usage();		}	args = arg->argv();	arg = nil;	testlist : array of ref Test;	if (args != nil)		testlist = arg_tests(args);	else if (configfile != "")		testlist = config_tests(configfile);	if (testlist == nil)		fatal("No tests to run");	sys->pctl(Sys->FORKENV, nil);	if (env->setenv(Itslib->ENV_VERBOSITY, string verbosity))		fatal("Failed to set environment variable " + Itslib->ENV_VERBOSITY);	if (repcount) 		reps := string repcount;	else		reps = "infinite";	if (len testlist == 1) ts := "";	else ts = "s";	if (repcount == 1) rs := "";	else rs = "s";	mreport(0, S_INFO, 2, sys->sprint("Starting tests - %s run%s of %d test%s", reps, rs, len testlist, ts));	run := big 1; 	if (recroot != nil) 		recn := highest(recroot) + 1;	while (repcount == 0 || run <= big repcount) {		mreport(1, S_INFO, 3, sys->sprint("Starting run %bd", run));		for (i:=0; i<len testlist; i++) {			t := testlist[i];			if (recroot != nil) {				t.recdir = sys->sprint("%s/%d", recroot, recn++);				mreport(2, S_INFO, 3, sys->sprint("Recording in %s", t.recdir));				rfd := sys->create(t.recdir, Sys->OREAD, Sys->DMDIR | 8r770);				if (rfd == nil)					fatal(sys->sprint("Failed to create directory %s: %r\n", t.recdir));				rfd = nil;			}			runtest(t);		}		mreport(1, S_INFO, 3, sys->sprint("Finished run %bd", run));		run++;	}	mreport(0, S_INFO, 2, "Finished tests");} usage(){	sys->fprint(stderr, "Usage itest [-eo] [-c cflag] [-r count] [-v vlevel] [-C cfile] [-R recroot] [testdir ...]\n");	raise "fail: usage";} fatal(s: string){	sys->fprint(stderr, "%s\n", s);	raise "fail: error";} nomod(mod: string){	sys->fprint(stderr, "Failed to load %s\n", mod);	raise "fail: module";} toint(opt, s: string, min, max: int): int{	if (len s == 0 || str->take(s, "[0-9]+-") != s)		fatal(sys->sprint("no value specified for option %s", opt));	v := int s;	if (v < min)		fatal(sys->sprint("option %s value is less than minimum of %d: %d", opt, v, min));	if (max != -1 && v > max)		fatal(sys->sprint("option %s value is greater than maximum of %d: %d", opt, v, max));	return v;} arg_tests(args: list of string): array of ref Test{	al := len args;	ta := array[al] of ref Test;	for (i:=0; i<al; i++) {		tspec := hd args;		args = tl args;		ta[i] = ref Test(tspec, "", nil, "", "", "", 0, 0, 0, 0, 0);		tcheck(ta[i]);	}	return ta;} config_tests(cf: string): array of ref Test{	cl := linelist(cf);	if (cl == nil)		fatal("No tests in config file");	al := len cl;	ta := array[al] of ref Test;	for (i:=0; i<al; i++) {		tspec := hd cl;		cl = tl cl;		ta[i] = ref Test(tspec, "", nil, "", "", "", 0, 0, 0, 0, 0);		tcheck(ta[i]);	}	return ta; } highest(path: string): int{	(da, nd) := readdir->init(path, Readdir->NAME);	high := 0;	for (i:=0; i<nd; i++) {		n := int da[i].name;		if (n > high)			high = n;		}	return high;} tcheck(t: ref Test): int{	td := t.spec;	if (!checkdir(td)) {		fatal(sys->sprint("Failed to find test %s\n", td));		return 0;	}	tf1 := t.spec + "/t.sh";	tf2 := t.spec + "/t.dis";	if (checkexec(tf1)) {		t.fullspec = tf1;		return 1;	}	if (checkexec(tf2)) {		t.fullspec = tf2;		return 1;	}	fatal(sys->sprint("Could not find executable files %s or %s\n", tf1, tf2));	return 0;} checkdir(d: string): int{	(ok, dir) := sys->stat(d);	if (ok != 0 || ! dir.qid.qtype & Sys->QTDIR)		return 0;	return 1;} checkexec(d: string): int{	(ok, dir) := sys->stat(d);	if (ok != 0 || ! dir.mode & 8r100)		return 0;	return 1;}  set_cflag(f: int){	wfile("/dev/jit", string f, 0); } runtest(t: ref Test){	if (t.failed)		return; 	if (cflag != -1) {		mreport(0, S_INFO, 7, sys->sprint("Setting cflag to %d", cflag));		set_cflag(cflag);	}	readme := t.spec + "/" + README;	mreport(2, S_INFO, 3, sys->sprint("Starting test %s cflag=%s", t.spec, rfile("/dev/jit")));	if (verbosity > 8)		display_file(readme);	sync := chan of int;	spawn monitor(t, sync);	<-sync;} monitor(t: ref Test, sync: chan of int){	pid := sys->pctl(Sys->FORKFD|Sys->FORKNS|Sys->FORKENV|Sys->NEWPGRP, nil);	pa := array[2] of ref Sys->FD;	if (sys->pipe(pa))		fatal("Failed to set up pipe");	if (env->setenv(Itslib->ENV_MFD, string pa[0].fd))		fatal("Failed to set environment variable " + Itslib->ENV_MFD);	mlfd: ref Sys->FD;	if (t.recdir != nil) {		mfile := t.recdir+"/"+MSGFILE;		mlfd = sys->create(mfile, Sys->OWRITE, 8r660);		if (mlfd == nil)			fatal(sys->sprint("Failed to create %s: %r'\n", mfile));		t.stdout = t.recdir+"/stdout";		t.stderr = t.recdir+"/stderr";	} else {		t.stdout = "/tmp/itest.stdout";		t.stderr = "/tmp/itest.stderr";	}	cf := int rfile("/dev/jit");	stime := sys->millisec();	swhen := daytime->now();	etime := -1;	rsync := chan of int;	spawn runit(t.fullspec, t.stdout, t.stderr, t.spec, pa[0], rsync);	<-rsync;	pa[0] = nil;	(nwarns, nerrors, nfatals) := (0, 0, 0);	while (1) {		mbuf := array[Sys->ATOMICIO] of byte;		n := sys->read(pa[1], mbuf, len mbuf);		if (n <= 0) break;		msg := string mbuf[:n];		sev := int msg[0:1];		verb := int msg[1:2];		body := msg[2:];		if (sev == S_STIME)			stime = int body;		else if (sev == S_ETIME)			etime = int body;		else {			if (sev == S_WARN) {				nwarns++;				t.nwarns++;			}			else if (sev == S_ERROR) {				nerrors++;				t.nerrors++;			}			else if (sev == S_FATAL) {				nfatals++;				t.nfatals++;			}			mreport(3, sev, verb, sys->sprint("%s: %s", severs(sev), body));		}		if (mlfd != nil)			sys->fprint(mlfd, "%d:%s", now, msg);	}	if (etime < 0) {		etime = sys->millisec();		if (mlfd != nil)			sys->fprint(mlfd, "%d:%s", now, sys->sprint("%d0%d\n", S_ETIME, etime));	}	elapsed := etime-stime;	errsum := sys->sprint("WRN:%d ERR:%d FTL:%d", nwarns, nerrors, nfatals);	mreport(2, S_INFO, 3, sys->sprint("Finished test %s after %dms - %s", t.spec, elapsed, errsum));	if (t.recdir != "") {		wfile(t.recdir+"/"+SUMFILE, sys->sprint("%d %d %d %s\n", swhen, elapsed, cf, t.fullspec), 1);	}	if (display_stdout) {		mreport(2, 0, 0, "Stdout from test:");		display_file(t.stdout);	}	if (display_stderr) {		mreport(2, 0, 0, "Stderr from test:");		display_file(t.stderr);	}	sync <-= pid;} runit(fullspec, sofile, sefile, tpath: string, mfd: ref Sys->FD, sync: chan of int){	pid := sys->pctl(Sys->NEWFD|Sys->FORKNS, mfd.fd::nil);	o, e: ref Sys->FD;	o = sys->create(sofile, Sys->OWRITE, 8r660);	if (o == nil)		treport(mfd, S_ERROR, 0, "Failed to open stdout: %r\n");	else		sys->dup(o.fd, 1);	o = nil;	e = sys->create(sefile, Sys->OWRITE, 8r660);	if (e == nil)		treport(mfd, S_ERROR, 0, "Failed to open stderr: %r\n");	else		sys->dup(e.fd, 2);	e = nil;	sync <-= pid;	args := list of {fullspec};	if (fullspec[len fullspec-1] == 's')		cmd := load Command fullspec;	else {		cmd = load Command "/dis/sh.dis";		args = fullspec :: args;	}	if (cmd == nil) {		treport(mfd, S_FATAL, 0, sys->sprint("Failed to load Command from %s", "/dis/sh.dis"));		return;	}	if (sys->chdir(tpath))		treport(mfd, S_FATAL, 0, "Failed to cd to " + tpath);	{		cmd->init(context, args);	} exception ex {		"*" =>		treport(mfd, S_FATAL, 0, sys->sprint("Exception %s in test %s", ex, fullspec));	}			} severs(sevs: int): string{	SEVMAP :=  array[] of {"INF", "WRN", "ERR", "FTL"};	if (sevs >= len SEVMAP)		sstr := "UNK";	else		sstr = SEVMAP[sevs];	return sstr;}  rfile(file: string): string{	fd := sys->open(file, Sys->OREAD);	if (fd == nil) return nil;	buf := array[Sys->ATOMICIO] of byte;	n := sys->read(fd, buf, len buf);	return string buf[:n];}  wfile(file: string, text: string, create: int): int{	if (create)		fd := sys->create(file, Sys->OWRITE, 8r660);	else 		fd = sys->open(file, Sys->OWRITE);	if (fd == nil) {		sys->fprint(stderr, "Failed to open %s: %r\n", file);		return 0;	}	a := array of byte text;	al := len a;	if (sys->write(fd, a, al) != al) {		sys->fprint(stderr, "Failed to write to %s: %r\n", file);		return 0;	}	fd = nil;	return 1;} linelist(file: string): list of string{	bf := bufio->open(file, Bufio->OREAD);	if (bf == nil)		return nil;	cl : list of string;	while ((line := bf.gets('\n')) != nil) {		if (line[len line -1] == '\n')			line = line[:len line - 1];		cl = line :: cl;	}	bf = nil;	return cl;} display_file(file: string){	bf := bufio->open(file, Bufio->OREAD);	if (bf == nil)		return;	while ((line := bf.gets('\n')) != nil) {		sys->print("                    %s", line);	}} mreport(indent: int, sev: int, verb: int, msg: string){	now = daytime->now();	tm := daytime->local(now);	time := sys->sprint("%4d%02d%02d %02d:%02d:%02d", tm.year+1900, tm.mon-1, tm.mday, tm.hour, tm.min, tm.sec);	pad := "---"[:indent];	term := "";	if (len msg && msg[len msg-1] != '\n')		term = "\n";	if (sev || verb <= verbosity)		sys->print("%s %s%s%s", time, pad, msg, term);}  treport(mfd: ref Sys->FD, sev: int, verb: int, msg: string){	sys->fprint(mfd, "%d%d%s\n", sev, verb, msg);}