[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:
parent
c2cdc752c8
commit
6289b002b6
15 changed files with 247 additions and 149 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue