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