shithub: mc

Download patch

ref: 6d7c80f8d5efcba00ebd44d34fd7b613f4672829
parent: ec5f2d23637dadf6d2893bf6f2f829b070ec3b34
author: Ori Bernstein <ori@eigenstate.org>
date: Mon May 23 19:23:09 EDT 2016

Add support for running subtests.

--- a/mbld/bld.sub
+++ b/mbld/bld.sub
@@ -9,6 +9,7 @@
 	main.myr
 	opts.myr
 	parse.myr
+	subtest.myr
 	syssel.myr
 	test.myr
         types.myr
--- /dev/null
+++ b/mbld/subtest.myr
@@ -1,0 +1,128 @@
+use std
+use bio
+use regex
+
+pkg bld =
+	const showsub	: (f : std.fd, logfd : std.fd -> std.option(bool))
+;;
+
+var planpat
+var headpat
+var footpat
+
+const __init__ = {
+	planpat = std.try(regex.compile("MTEST\\s+(-?\\d+)\\s*"))
+	headpat = std.try(regex.compile("test\\s+(.*)<<{!"))
+	footpat = std.try(regex.compile("!}>>\\s*(ok|fail\\s*(.*))"))
+}
+
+const showsub = {fd, logfd
+	var f, log
+	var ntests
+	var res
+
+	f = bio.mkfile(fd, bio.Rd)
+	log = bio.mkfile(logfd, bio.Wr)
+	res = `std.None
+	match bio.readln(f)
+	| `bio.Err e:	std.fatal("error reading subfile: {}\n", e)
+	| `bio.Eof:	-> `std.None
+	| `bio.Ok ln:
+		match regex.exec(planpat, ln)
+		| `std.None:
+			bio.write(log, ln)
+			showraw(fd, logfd)
+		| `std.Some m:
+			ntests = std.get(std.intparse(m[1]))
+			regex.matchfree(m)
+			res = `std.Some showtests(f, log, ntests)
+		;;
+		std.slfree(ln)
+	;;
+	bio.close(f)
+	bio.close(log)
+	-> res
+}
+
+const showraw = {f, log
+	var buf : byte[:]
+
+	buf = std.slalloc(64*std.KiB)
+	while true
+		match std.read(f, buf[:])
+		| `std.Ok 0:	break
+		| `std.Ok n:	std.write(log, buf[:n])
+		| `std.Fail e:	std.fatal("error writing log: {}\n", e)
+		;;
+	;;
+	std.slfree(buf)
+}
+
+const showtests = {f, log, ntests
+	var nresults
+	var ok
+
+	if ntests == 0
+		std.put("FAIL: missing tests\n")
+		-> false
+	;;
+	ok = true
+	nresults = 0
+	for ln in bio.byline(f)
+		ln = std.strstrip(ln)
+		match testhead(ln)
+		| `std.None:
+		| `std.Some t:	
+			std.put("\trun {}:\t", std.strstrip(t))
+			bio.put(log, "RUN {}\n", t)
+			continue
+		;;
+
+		match testfoot(ln)
+		| `std.None:
+		| `std.Some `std.Ok _:
+			std.put("PASS\n")
+			nresults++
+			continue
+		| `std.Some `std.Fail m:
+			std.put("FAIL\n")
+			ok = false
+			nresults++
+			continue
+		;;
+
+		bio.put(log, "\t{}\n", ln)
+	;;
+	if ntests > 0 && ntests != nresults
+		std.put("mismatched test count: expected {}, got {}\n", ntests, nresults)
+		-> false
+	;;
+	-> ok
+}
+
+const testhead = {ln
+	var t
+
+	match regex.exec(headpat, ln)
+	| `std.Some m:
+		t = std.sldup(m[1])
+		regex.matchfree(m)
+		-> `std.Some t
+	| `std.None:
+		-> `std.None
+	;;
+}
+
+const testfoot : (ln : byte[:] -> std.option(std.result(void, byte[:]))) = {ln
+	match regex.exec(footpat, ln)
+	| `std.Some m:
+		if std.sleq(m[1], "ok")
+			-> `std.Some `std.Ok void
+		else
+			-> `std.Some `std.Fail std.sldup(m[2])
+		;;
+	| `std.None:
+		-> `std.None
+	;;
+}
+
--- a/mbld/test.myr
+++ b/mbld/test.myr
@@ -8,6 +8,7 @@
 use "types"
 use "util"
 use "syssel"
+use "subtest"
 
 use "config"
 
@@ -149,7 +150,8 @@
 }
 
 const runtest = {b, cmd
-	var r, log, p
+	var res, log, logfd, p
+	var sub
 
 	std.put("run")
 	for c in cmd
@@ -163,27 +165,27 @@
 		std.fatal("\nunable to run test: {}\n", m)
 	| `std.Ok (pid, infd, outfd):
 		log = std.strcat(cmd[0], ".log")
-		match std.fslurp(outfd)
-		| `std.Ok "":	/* empty output; nothing to log */
-		| `std.Ok buf:
-			std.blat(log, buf, 0o644)
-		| `std.Fail m:
-		;;
+		logfd = std.try(std.open(log, std.Owronly | std.Ocreat))
+		sub = showsub(outfd, logfd)
 		std.slfree(log)
 		std.close(infd)
 		std.close(outfd)
 
-		r = false
+		res = false
 		match std.wait(pid)
 		| `std.Wfailure:	std.put("FAIL\n")
 		| `std.Wsignalled:	std.put("CRASH\n")
 		| `std.Wsuccess:
-			std.put("PASS\n")
-			r = true
+			res = true
+			/* if we have subtests, we've already printed the output */
+			match sub
+			| `std.Some r:	res = r
+			| `std.None:	std.put("PASS\n")
+			;;
 		| `std.Waiterror:	std.put("failed waiting for pid {}\n", pid)
 		;;
 	;;
-	-> r
+	-> res
 }
 
 const testpath = {s