[Testament] Extend and document message testing aids (#19996)

* [Testament] Extend and document message testing aids

* Enable inline msgs when not reject action.
Eliminates the pain of changing the line and column numbers in `nimout`
or `output` while making changes to the test.
* Enable using inline msgs and nimout together.
Allows ease of inline msgs for the test as well as testing msgs from
other modules.
* Add path separator and test filename variable interpolation in
msgs.
Eases handling path separators in the msgs.
* Add some documentation.

* Fixed lots of broken tests

* Fixed more broken tests

* Support multiple inline messages per a line

* Fix a broken test

* Revert variable substitution in `output`

* Remove uneeded params

* Update doc/testament.md

Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com>

* Update testament/specs.nim

Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com>

* Update testament/specs.nim

Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com>

* Fix indentation

Co-authored-by: quantimnot <quantimnot@users.noreply.github.com>
Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com>
This commit is contained in:
quantimnot 2022-09-01 11:52:13 -04:00 • committed by GitHub
commit 6289b002b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 247 additions and 149 deletions

View file

@ -75,6 +75,7 @@ type
# xxx make sure `isJoinableSpec` takes into account each field here.
action*: TTestAction
file*, cmd*: string
filename*: string ## Test filename (without path).
input*: string
outputCheck*: TOutputCheck
sortoutput*: bool
@ -127,19 +128,56 @@ when not declared(parseCfgBool):
of "n", "no", "false", "0", "off": result = false
else: raise newException(ValueError, "cannot interpret as a bool: " & s)
proc addLine*(self: var string; pieces: varargs[string]) =
for piece in pieces:
self.add piece
self.add "\n"
const
inlineErrorMarker = "#[tt."
inlineErrorKindMarker = "tt."
inlineErrorMarker = "#[" & inlineErrorKindMarker
proc extractErrorMsg(s: string; i: int; line: var int; col: var int; spec: var TSpec): int =
## Extract inline error messages.
##
## Can parse a single message for a line:
##
## .. code-block:: nim
##
## proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error
## ^ 'generic_proc' should be: 'genericProc' [Name] ]#
##
## Can parse multiple messages for a line when they are separated by ';':
##
## .. code-block:: nim
##
## proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error
## ^ 'generic_proc' should be: 'genericProc' [Name]; tt.Error
## ^ 'no_destroy' should be: 'nodestroy' [Name]; tt.Error
## ^ 'userPragma' should be: 'user_pragma' [template declared in mstyleCheck.nim(10, 9)] [Name] ]#
##
## .. code-block:: nim
##
## proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error
## ^ 'generic_proc' should be: 'genericProc' [Name];
## tt.Error ^ 'no_destroy' should be: 'nodestroy' [Name];
## tt.Error ^ 'userPragma' should be: 'user_pragma' [template declared in mstyleCheck.nim(10, 9)] [Name] ]#
##
result = i + len(inlineErrorMarker)
inc col, len(inlineErrorMarker)
let msgLine = line
var msgCol = -1
var msg = ""
var kind = ""
while result < s.len and s[result] in IdentChars:
kind.add s[result]
inc result
inc col
var caret = (line, -1)
template parseKind =
while result < s.len and s[result] in IdentChars:
kind.add s[result]
inc result
inc col
if kind notin ["Hint", "Warning", "Error"]:
spec.parseErrors.addLine "expected inline message kind: Hint, Warning, Error"
template skipWhitespace =
while result < s.len and s[result] in Whitespace:
@ -150,34 +188,70 @@ proc extractErrorMsg(s: string; i: int; line: var int; col: var int; spec: var T
inc col
inc result
skipWhitespace()
if result < s.len and s[result] == '^':
caret = (line-1, col)
inc result
inc col
skipWhitespace()
template parseCaret =
if result < s.len and s[result] == '^':
msgCol = col
inc result
inc col
skipWhitespace()
else:
spec.parseErrors.addLine "expected column marker ('^') for inline message"
template isMsgDelimiter: bool =
s[result] == ';' and
(block:
let nextTokenIdx = result + 1 + parseutils.skipWhitespace(s, result + 1)
if s.len > nextTokenIdx + len(inlineErrorKindMarker) and
s[nextTokenIdx..(nextTokenIdx + len(inlineErrorKindMarker) - 1)] == inlineErrorKindMarker:
true
else:
false)
template trimTrailingMsgWhitespace =
while msg.len > 0 and msg[^1] in Whitespace:
setLen msg, msg.len - 1
template addInlineError =
doAssert msg[^1] notin Whitespace
if kind == "Error": spec.action = actionReject
spec.inlineErrors.add InlineError(kind: kind, msg: msg, line: msgLine, col: msgCol)
parseKind()
skipWhitespace()
parseCaret()
var msg = ""
while result < s.len-1:
if s[result] == '\n':
msg.add '\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
elif isMsgDelimiter():
trimTrailingMsgWhitespace()
inc result
skipWhitespace()
addInlineError()
inc result, len(inlineErrorKindMarker)
inc col, 1 + len(inlineErrorKindMarker)
kind.setLen 0
msg.setLen 0
parseKind()
skipWhitespace()
parseCaret()
elif s[result] == ']' and s[result+1] == '#':
trimTrailingMsgWhitespace()
inc result, 2
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])
addInlineError()
break
else:
msg.add s[result]
inc result
inc col
if spec.inlineErrors.len > 0:
spec.unjoinable = true
proc extractSpec(filename: string; spec: var TSpec): string =
const
tripleQuote = "\"\"\""
@ -229,15 +303,6 @@ proc parseTargets*(value: string): set[TTarget] =
of "js": result.incl(targetJS)
else: raise newException(ValueError, "invalid target: '$#'" % v)
proc addLine*(self: var string; a: string) =
self.add a
self.add "\n"
proc addLine*(self: var string; a, b: string) =
self.add a
self.add b
self.add "\n"
proc initSpec*(filename: string): TSpec =
result.file = filename
@ -249,6 +314,7 @@ proc isCurrentBatch*(testamentData: TestamentData; filename: string): bool =
proc parseSpec*(filename: string): TSpec =
result.file = filename
result.filename = extractFilename(filename)
let specStr = extractSpec(filename, result)
var ss = newStringStream(specStr)
var p: CfgParser
@ -439,3 +505,15 @@ proc parseSpec*(filename: string): TSpec =
result.inCurrentBatch = isCurrentBatch(testamentData0, filename) or result.unbatchable
if not result.inCurrentBatch:
result.err = reDisabled
# Interpolate variables in msgs:
template varSub(msg: string): string =
try:
msg % ["/", $DirSep, "file", result.filename]
except ValueError:
result.parseErrors.addLine "invalid variable interpolation (see 'https://nim-lang.github.io/Nim/testament.html#writing-unitests-output-message-variable-interpolation')"
msg
result.nimout = result.nimout.varSub
result.msg = result.msg.varSub
for inlineError in result.inlineErrors.mitems:
inlineError.msg = inlineError.msg.varSub