Add "safety compilation". Improved, fixed messages

- node ext searcher also looks for "iojs" if no node(js?) found.
- koch ensures the local work dir compiler is freshly compiled before executing the tests.
- the tester can only run in the repo dir - now the compiler and libs used are also explicitly the local dirs - so no confusion from system wide pathing ("sufficiently sandboxed").
- fixed expectations in tmitems.nim test to match changes in json mod.
- re-layouted the columns / row printing slightly, making test fails pop out more, and everything "normal" layed back.
This commit is contained in:
Oscar Campbell 2015-06-04 07:33:27 +02:00
commit 071ccf3d02
4 changed files with 100 additions and 58 deletions

View file

@ -4,3 +4,5 @@ proc findNodeJs*(): string =
result = findExe("nodejs") result = findExe("nodejs")
if result == "": if result == "":
result = findExe("node") result = findExe("node")
if result == "":
result = findExe("iojs")

View file

@ -330,12 +330,12 @@ proc winRelease() =
template `|`(a, b): expr = (if a.len > 0: a else: b) template `|`(a, b): expr = (if a.len > 0: a else: b)
proc tests(args: string) = proc tests(args: string) =
# Since tests take a long time (on my machine) - lets make sure a stupid
# mistake - like forgetting to compile the compiler - isn't made...
exec "nim c --lib:lib -d:release compiler/nim.nim"
# we compile the tester with taintMode:on to have a basic # we compile the tester with taintMode:on to have a basic
# taint mode test :-) # taint mode test :-)
exec "nim cc --taintMode:on tests/testament/tester" exec "nim cc --taintMode:on tests/testament/tester"
# Since tests take a long time (on my machine), and we want to defy Murhpys
# law - lets make sure the compiler really is freshly compiled!
exec "nim c --lib:lib -d:release --opt:speed compiler/nim.nim"
let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe) let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe)
let success = tryExec tester & " " & (args|"all") let success = tryExec tester & " " & (args|"all")
exec tester & " html" exec tester & " html"

View file

@ -11,8 +11,8 @@ fpqeew
[11, 12, 13] [11, 12, 13]
[11, 12, 13] [11, 12, 13]
[11, 12, 13] [11, 12, 13]
{"key1": 11, "key2": 12, "key3": 13} {"key1":11,"key2":12,"key3":13}
[11, 12, 13] [11,12,13]
<Students> <Students>
<Student Name="Aprilfoo" /> <Student Name="Aprilfoo" />
<Student Name="bar" /> <Student Name="bar" />

View file

@ -12,7 +12,7 @@
import import
parseutils, strutils, pegs, os, osproc, streams, parsecfg, json, parseutils, strutils, pegs, os, osproc, streams, parsecfg, json,
marshal, backend, parseopt, specs, htmlgen, browsers, terminal, marshal, backend, parseopt, specs, htmlgen, browsers, terminal,
algorithm, compiler/nodejs algorithm, compiler/nodejs, re
const const
resultsFile = "testresults.html" resultsFile = "testresults.html"
@ -126,6 +126,9 @@ proc `$`(x: TResults): string =
"Tests skipped: $2 / $3 <br />\n") % "Tests skipped: $2 / $3 <br />\n") %
[$x.passed, $x.skipped, $x.total] [$x.passed, $x.skipped, $x.total]
proc leftAlign(s: string, count: Natural, padding = ' '): string =
s & repeat(padding, max(0, count - s.len))
proc addResult(r: var TResults, test: TTest, proc addResult(r: var TResults, test: TTest,
expected, given: string, success: TResultEnum) = expected, given: string, success: TResultEnum) =
let name = test.name.extractFilename & test.options let name = test.name.extractFilename & test.options
@ -137,14 +140,19 @@ proc addResult(r: var TResults, test: TTest,
expected = expected, expected = expected,
given = given) given = given)
r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success)
if success == reIgnored: let alignedName = leftAlign(name, 72) # Make the total line length 78 chars
styledEcho styleBright, name, fgYellow, " [", $success, "]" if success == reSuccess:
elif success != reSuccess: styledEcho fgCyan, alignedName, fgGreen, " [PASS]"
styledEcho styleBright, name, fgRed, " [", $success, "]" elif success == reIgnored:
echo"Expected:" styledEcho styleBright, fgCyan, alignedName, styleDim, fgYellow, " [SKIP]"
styledEcho styleBright, expected else:
echo"Given:" styledEcho styleBright, fgCyan, alignedName, fgRed, " [FAIL]"
styledEcho styleBright, given styledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\""
styledEcho styleBright, fgRed, "Failure: ", $success
styledEcho fgYellow, "Expected:"
styledEcho styleBright, expected, "\n"
styledEcho fgYellow, "Gotten:"
styledEcho styleBright, given, "\n"
proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) =
if strip(expected.msg) notin strip(given.msg): if strip(expected.msg) notin strip(given.msg):
@ -211,68 +219,100 @@ proc compilerOutputTests(test: TTest, given: var TSpec, expected: TSpec;
if given.err == reSuccess: inc(r.passed) if given.err == reSuccess: inc(r.passed)
r.addResult(test, expectedmsg, givenmsg, given.err) r.addResult(test, expectedmsg, givenmsg, given.err)
proc analyzeAndConsolidateOutput(s: string): string =
result = ""
let rows = s.splitLines
for i in 0 ..< rows.len:
if (let pos = find(rows[i], "Traceback (most recent call last)"); pos != -1):
result = substr(rows[i], pos) & "\n"
for i in i+1 ..< rows.len:
result.add rows[i] & "\n"
if not (rows[i] =~ re"^[^(]+\(\d+\)\s+"):
return
elif (let pos = find(rows[i], "SIGSEGV: Illegal storage access."); pos != -1):
result = substr(rows[i], pos)
return
proc testSpec(r: var TResults, test: TTest) = proc testSpec(r: var TResults, test: TTest) =
# major entry point for a single test # major entry point for a single test
let tname = test.name.addFileExt(".nim") let tname = test.name.addFileExt(".nim")
inc(r.total) inc(r.total)
styledEcho "Processing ", fgCyan, extractFilename(tname)
var expected: TSpec var expected: TSpec
if test.action != actionRunNoSpec: if test.action != actionRunNoSpec:
expected = parseSpec(tname) expected = parseSpec(tname)
else: else:
specDefaults expected specDefaults expected
expected.action = actionRunNoSpec expected.action = actionRunNoSpec
if expected.err == reIgnored: if expected.err == reIgnored:
r.addResult(test, "", "", reIgnored) r.addResult(test, "", "", reIgnored)
inc(r.skipped) inc(r.skipped)
else: return
case expected.action case expected.action
of actionCompile: of actionCompile:
var given = callCompiler(expected.cmd, test.name, var given = callCompiler(expected.cmd, test.name,
test.options & " --hint[Path]:off --hint[Processing]:off", test.target) test.options & " --hint[Path]:off --hint[Processing]:off", test.target)
compilerOutputTests(test, given, expected, r) compilerOutputTests(test, given, expected, r)
of actionRun, actionRunNoSpec: of actionRun, actionRunNoSpec:
# In this branch of code "early return" pattern is clearer than deep
# nested conditionals - the empty rows in between to clarify the "danger"
var given = callCompiler(expected.cmd, test.name, test.options, var given = callCompiler(expected.cmd, test.name, test.options,
test.target) test.target)
if given.err != reSuccess: if given.err != reSuccess:
r.addResult(test, "", given.msg, given.err) r.addResult(test, "", given.msg, given.err)
else: return
let isJsTarget = test.target == targetJS
var exeFile: string var exeFile: string
if test.target == targetJS: if isJsTarget:
let (dir, file, ext) = splitFile(tname) let (dir, file, ext) = splitFile(tname)
exeFile = dir / "nimcache" / file & ".js" exeFile = dir / "nimcache" / file & ".js" # *TODO* hardcoded "nimcache"
else: else:
exeFile = changeFileExt(tname, ExeExt) exeFile = changeFileExt(tname, ExeExt)
if existsFile(exeFile):
let nodejs = findNodeJs() if not existsFile(exeFile):
if test.target == targetJS and nodejs == "": r.addResult(test, expected.outp, "executable not found", reExeNotFound)
return
let nodejs = if isJsTarget: findNodeJs() else: ""
if isJsTarget and nodejs == "":
r.addResult(test, expected.outp, "nodejs binary not in PATH", r.addResult(test, expected.outp, "nodejs binary not in PATH",
reExeNotFound) reExeNotFound)
return return
var (buf, exitCode) = execCmdEx(
(if test.target == targetJS: nodejs & " " else: "") & exeFile) let exeCmd = (if isJsTarget: nodejs & " " else: "") & exeFile
let (buf, exitCode) = execCmdEx(exeCmd)
let bufB = if expected.sortoutput: makeDeterministic(strip(buf.string))
else: strip(buf.string)
if exitCode != expected.exitCode: if exitCode != expected.exitCode:
r.addResult(test, "exitcode: " & $expected.exitCode, r.addResult(test, "exitcode: " & $expected.exitCode,
"exitcode: " & $exitCode, reExitCodesDiffer) "exitcode: " & $exitCode & "\n\nOutput:\n" &
else: analyzeAndConsolidateOutput(bufB),
var bufB = strip(buf.string) reExitCodesDiffer)
if expected.sortoutput: bufB = makeDeterministic(bufB) return
if bufB != strip(expected.outp): if bufB != strip(expected.outp):
if not (expected.substr and expected.outp in bufB): if not (expected.substr and expected.outp in bufB):
given.err = reOutputsDiffer given.err = reOutputsDiffer
r.addResult(test, expected.outp, bufB, reOutputsDiffer)
return
compilerOutputTests(test, given, expected, r) compilerOutputTests(test, given, expected, r)
else: return
r.addResult(test, expected.outp, "executable not found", reExeNotFound)
of actionReject: of actionReject:
var given = callCompiler(expected.cmd, test.name, test.options, var given = callCompiler(expected.cmd, test.name, test.options,
test.target) test.target)
cmpMsgs(r, expected, given, test) cmpMsgs(r, expected, given, test)
return
proc testNoSpec(r: var TResults, test: TTest) = proc testNoSpec(r: var TResults, test: TTest) =
# does not extract the spec because the file is not supposed to have any # does not extract the spec because the file is not supposed to have any
let tname = test.name.addFileExt(".nim") let tname = test.name.addFileExt(".nim")
inc(r.total) inc(r.total)
styledEcho "Processing ", fgCyan, extractFilename(tname)
let given = callCompiler(cmdTemplate, test.name, test.options, test.target) let given = callCompiler(cmdTemplate, test.name, test.options, test.target)
r.addResult(test, "", given.msg, given.err) r.addResult(test, "", given.msg, given.err)
if given.err == reSuccess: inc(r.passed) if given.err == reSuccess: inc(r.passed)