diff options
-rw-r--r-- | testament/specs.nim | 95 | ||||
-rw-r--r-- | testament/testament.nim | 49 | ||||
-rw-r--r-- | tests/effects/teffects1.nim | 19 |
3 files changed, 142 insertions, 21 deletions
diff --git a/testament/specs.nim b/testament/specs.nim index acffbf8be..e0b9cbeec 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -7,8 +7,8 @@ # distribution, for details about the copyright. # -import sequtils, parseutils, strutils, os, streams, parsecfg -from hashes import hash +import sequtils, parseutils, strutils, os, streams, parsecfg, + tables, hashes type TestamentData* = ref object # better to group globals under 1 object; could group the other ones here too @@ -62,6 +62,11 @@ type targetObjC = "ObjC" targetJS = "JS" + InlineError* = object + kind*: string + msg*: string + line*, col*: int + TSpec* = object action*: TTestAction file*, cmd*: string @@ -90,6 +95,7 @@ type useValgrind*: bool timeout*: float # in seconds, fractions possible, # but don't rely on much precision + inlineErrors*: seq[InlineError] # line information to error message proc getCmd*(s: TSpec): string = if s.cmd.len == 0: @@ -116,14 +122,85 @@ when not declared(parseCfgBool): of "n", "no", "false", "0", "off": result = false else: raise newException(ValueError, "cannot interpret as a bool: " & s) -proc extractSpec(filename: string): string = - const tripleQuote = "\"\"\"" - var x = readFile(filename).string - var a = x.find(tripleQuote) - var b = x.find(tripleQuote, a+3) +const + inlineErrorMarker = "#[tt." + +proc extractErrorMsg(s: string; i: int; line: var int; col: var int; spec: var TSpec): int = + result = i + len(inlineErrorMarker) + inc col, len(inlineErrorMarker) + var kind = "" + while result < s.len and s[result] in IdentChars: + kind.add s[result] + inc result + inc col + + var caret = (line, -1) + + template skipWhitespace = + while result < s.len and s[result] in Whitespace: + if s[result] == '\n': + col = 1 + inc line + else: + inc col + inc result + + skipWhitespace() + if result < s.len and s[result] == '^': + caret = (line-1, col) + inc result + inc col + skipWhitespace() + + var msg = "" + while result < s.len-1: + if s[result] == '\n': + inc result + inc line + col = 1 + elif s[result] == ']' and s[result+1] == '#': + while msg.len > 0 and msg[^1] in Whitespace: + setLen msg, msg.len - 1 + + inc result + inc col, 2 + if kind == "Error": spec.action = actionReject + spec.unjoinable = true + spec.inlineErrors.add InlineError(kind: kind, msg: msg, line: caret[0], col: caret[1]) + break + else: + msg.add s[result] + inc result + inc col + +proc extractSpec(filename: string; spec: var TSpec): string = + const + tripleQuote = "\"\"\"" + var s = readFile(filename).string + + var i = 0 + var a = -1 + var b = -1 + var line = 1 + var col = 1 + while i < s.len: + if s.continuesWith(tripleQuote, i): + if a < 0: a = i + elif b < 0: b = i + inc i, 2 + inc col + elif s[i] == '\n': + inc line + col = 1 + elif s.continuesWith(inlineErrorMarker, i): + i = extractErrorMsg(s, i, line, col, spec) + else: + inc col + inc i + # look for """ only in the first section if a >= 0 and b > a and a < 40: - result = x.substr(a+3, b-1).replace("'''", tripleQuote) + result = s.substr(a+3, b-1).replace("'''", tripleQuote) else: #echo "warning: file does not contain spec: " & filename result = "" @@ -160,7 +237,7 @@ proc isCurrentBatch(testamentData: TestamentData, filename: string): bool = proc parseSpec*(filename: string): TSpec = result.file = filename - let specStr = extractSpec(filename) + let specStr = extractSpec(filename, result) var ss = newStringStream(specStr) var p: CfgParser open(p, ss, filename, 1) diff --git a/testament/testament.nim b/testament/testament.nim index baa340139..4afafb645 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -12,7 +12,7 @@ import strutils, pegs, os, osproc, streams, json, backend, parseopt, specs, htmlgen, browsers, terminal, - algorithm, times, md5, sequtils, azure + algorithm, times, md5, sequtils, azure, intsets from std/sugar import dup import compiler/nodejs @@ -71,6 +71,7 @@ type let pegLineError = peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' ('Error') ':' \s* {.*}" + pegLineTemplate = peg""" {[^(]*} '(' {\d+} ', ' {\d+} ') ' @@ -317,8 +318,50 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, discard waitForExit(p) close(p) +proc checkForInlineErrors(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = + let pegLine = peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' {[^:]*} ':' \s* {.*}" + var covered = initIntSet() + for line in splitLines(given.nimout): + + if line =~ pegLine: + let file = extractFilename(matches[0]) + let line = try: parseInt(matches[1]) except: -1 + let col = try: parseInt(matches[2]) except: -1 + let kind = matches[3] + let msg = matches[4] + + if file == extractFilename test.name: + var i = 0 + for x in expected.inlineErrors: + if x.line == line and (x.col == col or x.col < 0) and + x.kind == kind and x.msg in msg: + covered.incl i + inc i + + block coverCheck: + for j in 0..high(expected.inlineErrors): + if j notin covered: + var e = test.name + e.add "(" + e.addInt expected.inlineErrors[j].line + if expected.inlineErrors[j].col > 0: + e.add ", " + e.addInt expected.inlineErrors[j].col + e.add ") " + e.add expected.inlineErrors[j].kind + e.add ": " + e.add expected.inlineErrors[j].msg + + r.addResult(test, target, e, given.nimout, reMsgsDiffer) + break coverCheck + + r.addResult(test, target, "", given.msg, reSuccess) + inc(r.passed) + proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = - if strip(expected.msg) notin strip(given.msg): + if expected.inlineErrors.len > 0: + checkForInlineErrors(r, expected, given, test, target) + elif strip(expected.msg) notin strip(given.msg): r.addResult(test, target, expected.msg, given.msg, reMsgsDiffer) elif expected.nimout.len > 0 and expected.nimout.normalizeMsg notin given.nimout.normalizeMsg: r.addResult(test, target, expected.nimout, given.nimout, reMsgsDiffer) @@ -389,6 +432,8 @@ proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) = given.err = reMsgsDiffer break + + proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, expected: TSpec; r: var TResults) = var expectedmsg: string = "" diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim index 3bd19f4ab..3551ff1a1 100644 --- a/tests/effects/teffects1.nim +++ b/tests/effects/teffects1.nim @@ -1,13 +1,5 @@ discard """ - errormsg: "type mismatch: got <proc (x: int): string{.noSideEffect, gcsafe, locks: 0.}> but expected 'MyProcType = proc (x: int): string{.closure.}'" - file: "teffects1.nim" - line: 38 cmd: "nim check $file" - nimout: '''teffects1.nim(22, 28) template/generic instantiation from here -teffects1.nim(23, 13) Error: can raise an unlisted exception: ref IOError -teffects1.nim(22, 29) Hint: 'IO2Error' is declared but not used [XDeclaredButNotUsed] -teffects1.nim(38, 21) Error: type mismatch: got <proc (x: int): string{.noSideEffect, gcsafe, locks: 0.}> but expected 'MyProcType = proc (x: int): string{.closure.}' -.raise effects differ''' """ type @@ -20,7 +12,10 @@ type proc forw: int {. .} proc lier(): int {.raises: [IO2Error].} = - writeLine stdout, "arg" + #[tt.Hint ^ 'IO2Error' is declared but not used [XDeclaredButNotUsed] ]# + writeLine stdout, "arg" #[tt.Error + ^ can raise an unlisted exception: ref IOError + ]# proc forw: int = raise newException(IOError, "arg") @@ -35,5 +30,9 @@ proc foo(x: int): string {.raises: [ValueError].} = raise newException(ValueError, "Use single digit") $x -var p: MyProcType = foo +var p: MyProcType = foo #[tt.Error + ^ +type mismatch: got <proc (x: int): string{.noSideEffect, gcsafe, locks: 0.}> but expected 'MyProcType = proc (x: int): string{.closure.}' + +]# {.pop.} |