shithub: mc

ref: 186c4e4026e1d9db729153e185bd717377060926
dir: /mbld/test.myr/

View raw version
use std

use "build"
use "clean"
use "deps"
use "opts"
use "parse"
use "types"
use "util"
use "syssel"
use "subtest"

use "config"

pkg bld =
	const test	: (b : build# -> bool)
;;

const test = {b
	var tests : (byte[:][:], byte[:])[:]
	var failed, ok
	var bincmd

	/* build with the test tag */
	addsysattrs(b, ["test"][:])
	/* no implicit tests to run */
	tests = [][:]
	buildall(b)
	setdir(b, "")
        for tn in b.all
		match gettarg(b.targs, tn)
		| `Bin bt:      std.sljoin(&tests, buildtests(b, bt))
		| `Lib lt:	std.sljoin(&tests, buildtests(b, lt))
		| _:	/* nothing */
		;;
        ;;
	for tn in b.all
		match gettarg(b.targs, tn)
		| `Bin t:
			if !t.istest
				continue
			;;
			if t.incpath.len == 0 || !std.sleq(t.incpath[0], ".")
				std.slput(&t.incpath, 0, std.sldup("."))
			;;
			buildbin(b, t, false)
			bincmd = std.sldup([std.strcat("./", t.name)][:])
			std.slpush(&tests, (bincmd, std.sldup(t.dir)))
		| `Cmd t:
			if !t.istest
				continue
			;;
			std.slpush(&tests, (dupcmd(t.cmd), std.sldup(t.dir)))
		| _:
			/* skip */
		;;
	;;

	ok = true
	failed = [][:]
	for (c, dir) in tests
		setdir(b, dir)
		if !runtest(b, c, &failed)
			ok = false
		;;

	;;
	for (bin, dir) in tests
		freecmd(bin)
		std.slfree(dir)
	;;
	if tests.len == 0
		-> true
	;;

	printfailed(failed)
	for f in failed
		std.slfree(f)
	;;
	if ok
		std.put("TESTS PASSED\n")
	else
		std.put("TESTS FAILED\n")
	;;
	std.slfree(failed)
	std.slfree(tests)
	-> ok
}

const printfailed = {failed
	if failed.len > 0
		std.put("FAILURES: {}\n", failed.len)
		for t in failed
			std.put("\t{}\n", t)
		;;
	;;
}

const dupcmd = {cmd
	var ret

	ret = [][:]
	for c in cmd
		std.slpush(&ret, std.sldup(c))
	;;
	-> ret
}

const freecmd = {cmd
	for c in cmd
		std.slfree(c)
	;;
	std.slfree(cmd)
}

const buildtests = {b, targ
	var tt, bin, tests, incpath, libdeps
	var cmd

	tests = [][:]
	setdir(b, targ.dir)
	for s in targ.inputs
		match testpath(s)
		| `std.None: /* nothing to do */
		| `std.Some path:
			bin = srcswapsuffix(path, "")
			incpath = std.sldup(targ.incpath)
			libdeps = std.sldup(targ.libdeps)
			std.sljoin(&libdeps, targ.tstdeps)
			tt = [
				.name = bin,
				.dir = targ.dir,
				.inputs = [path][:],
				.install = false,
				.libdeps = libdeps,
				.incpath = std.slput(&incpath, 0, "."),
			]

			buildbin(b, &tt, true)
			cmd = std.sldup([std.strcat("./", bin)][:])
			std.slpush(&tests, (cmd, std.sldup(targ.dir)))
			std.slfree(tt.libdeps)
			std.slfree(tt.incpath)
			std.slfree(path)
		;;
	;;
	-> tests
}

const runtest = {b, cmd, failed
	var res, log, logfd, p
	var sub

	std.put("run")
	for c in cmd
		p = std.pathcat(b.curdir, c)
		std.put(" {}", p)
		std.slfree(p)
	;;
	std.put(":\t")
	match std.spork(cmd)
	| `std.Err m:
		std.fatal("\nunable to run test: {}\n", m)
	| `std.Ok (pid, infd, outfd):
		log = std.strcat(cmd[0], ".log")
		logfd = std.try(std.openmode(log, std.Owronly | std.Ocreat, 0o644))
		sub = showsub(b, cmd[0], outfd, logfd, failed)
		std.slfree(log)
		std.close(infd)
		std.close(outfd)

		res = false
		match std.wait(pid)
		| `std.Wfailure:	std.put("FAIL\n")
		| `std.Wsignalled:	std.put("CRASH\n")
		| `std.Wsuccess:
			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)
		;;
		if !res
			p = std.pathcat(b.curdir, cmd[0])
			std.slpush(failed, p)
		;;
	;;
	-> res
}

const testpath = {s
	var path, file

	path = std.pathcat("./test", s)
	if std.fexists(path)
		-> `std.Some path
	;;
	match std.strfind(s, "+")
	| `std.None:
	| `std.Some idx:
		file = std.strcat(s[:idx], ".myr")
		path = std.pathcat("./test", file)
		std.slfree(file)
		if std.fexists(path)
			-> `std.Some path
		;;
	;;
	-> `std.None
}