shithub: mc

ref: 75658ff682f476ca448dc4734a1b6da0c1c4132a
dir: /mbld/subtest.myr/

View raw version
use std
use bio
use regex

use "types"

pkg bld =
	const showsub	: (b : build#, cmd : byte[:], \
		f : std.fd, logfd : std.fd, \
		failed : byte[:][:]# -> 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+(.*)<<{!\\s*"))
	footpat = std.try(regex.compile("!}>>\\s*(ok|fail\\s*(.*))\\s*"))
}

const showsub = {b, cmd, fd, logfd, failed
	var f, log
	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 testplan(ln)
		| `std.None:
			bio.write(log, ln)
			showraw(fd, logfd)
		| `std.Some ntests:
			res = `std.Some showtests(b, cmd, failed, \
				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.Err e:	std.fatal("error writing log: {}\n", e)
		;;
	;;
	std.slfree(buf)
}

const showtests = {b, cmd, failed, f, log, ntests
	var curtest
	var nresults
	var ok

	if ntests == 0
		std.put("FAIL: missing tests\n")
		-> false
	;;
	ok = true
	curtest = ""
	nresults = 0
	std.put("\n")
	for ln in bio.byline(f)
		ln = std.strstrip(ln)
		match testhead(ln)
		| `std.None:
		| `std.Some t:	
			starttest(&curtest, t)
			bio.put(log, "RUN {}\n", t)
			continue
		;;

		match testfoot(ln)
		| `std.None:
		| `std.Some `std.Ok _:
			endtest(b, cmd, &curtest, failed, &nresults, true, "")
			continue
		| `std.Some `std.Err m:
			endtest(b, cmd, &curtest, failed, &nresults, false, m)
			ok = false
			continue
		;;

		match testplan(ln)
		| `std.None:
		| `std.Some n:
			if curtest.len == 0
				if !checktests(ntests, nresults)
					ok = false
				;;
				ntests = n
				nresults = 0
				continue
			;;
		;;

		bio.put(log, "\t{}\n", ln)
	;;
	if !checktests(ntests, nresults)
		ok = false
	;;
	-> ok
}

const checktests = {ntests, nresults
	if ntests > 0 && ntests != nresults
		std.put("mismatched test count: expected {}, got {}\n", ntests, nresults)
		-> false
	;;
	-> true
}

const starttest = {curtest, t
	if curtest#.len != 0
		std.fatal("malformed input: test {} nested in {}\n", t, curtest)
	;;
	std.put("\trun {}:\t", std.strstrip(t))
	curtest# = t
}

const endtest = {b, cmd, curtest, failed, nresults, pass, msg
	var p

	if curtest#.len == 0
		std.fatal("malformed input: test ended without start\n")
	;;

	if pass
		std.put("PASS\n")
	else
		if msg.len > 0
			std.put("FAIL {}\n", msg)
		else
			std.put("FAIL\n")
		;;
		p = std.pathjoin([b.curdir, cmd, curtest#][:])
		std.slpush(failed, p)
	;;

	std.slfree(curtest#)
	curtest# = ""
	nresults#++
}

const testplan = {ln
	var ntests

	match regex.exec(planpat, ln)
	| `std.None:
		-> `std.None
	| `std.Some m:
		ntests = std.get(std.intparse(m[1]))
		regex.matchfree(m)
		-> `std.Some ntests
	;;
}

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.Err std.sldup(m[2])
		;;
	| `std.None:
		-> `std.None
	;;
}