Make test macro work in compile only mode
This commit is contained in:
parent
8ca9a17681
commit
7d44349f2b
1 changed files with 292 additions and 181 deletions
315
src/tani.nim
315
src/tani.nim
|
|
@ -1,24 +1,32 @@
|
||||||
import macros, tables, strutils
|
import macros
|
||||||
|
import tables, strutils, os
|
||||||
|
|
||||||
|
export tables, strutils, os
|
||||||
|
|
||||||
when defined(ECMAScript):
|
when defined(ECMAScript):
|
||||||
const noColors = true
|
const noColors = true
|
||||||
else:
|
else:
|
||||||
const noColors = defined(noColors)
|
const noColors = defined(noColors)
|
||||||
import terminal
|
import terminal
|
||||||
|
export terminal
|
||||||
|
|
||||||
import private/utils
|
import private/utils
|
||||||
|
|
||||||
type
|
type
|
||||||
Test = ref object
|
Test = ref object
|
||||||
procDef: proc(test: Test)
|
procDef: NimNode
|
||||||
name: string
|
name: NimNode
|
||||||
|
|
||||||
TestsInfo = ref object
|
TestsModule = ref object
|
||||||
## The base TestSuite
|
## The base TestSuite
|
||||||
fileName: string
|
fileName: string
|
||||||
currentTestName: string
|
tests: seq[Test]
|
||||||
|
|
||||||
|
TestsInfo = ref object
|
||||||
|
fileName: string
|
||||||
|
numTests: int
|
||||||
testsPassed: int
|
testsPassed: int
|
||||||
lastTestFailed: bool
|
lastTestFailed: bool
|
||||||
tests: seq[Test]
|
|
||||||
|
|
||||||
TestAssertError = object of Exception
|
TestAssertError = object of Exception
|
||||||
## check and other check* statements will raise
|
## check and other check* statements will raise
|
||||||
|
|
@ -31,7 +39,11 @@ type
|
||||||
checkFuncName: string
|
checkFuncName: string
|
||||||
valTable: Table[string, string]
|
valTable: Table[string, string]
|
||||||
|
|
||||||
var testsInfoMap = newTable[string, TestsInfo]()
|
PrivateTestError = object of TestAssertError
|
||||||
|
## Raised when a test tries to access something
|
||||||
|
## that is private to a module
|
||||||
|
|
||||||
|
var testsModuleMap {.compileTime.} = newTable[string, TestsModule]()
|
||||||
|
|
||||||
proc `==`*[T](ar: openarray[T], ar2: openarray[T]): bool =
|
proc `==`*[T](ar: openarray[T], ar2: openarray[T]): bool =
|
||||||
## helper proc to compare arrays
|
## helper proc to compare arrays
|
||||||
|
|
@ -51,8 +63,9 @@ template returnException(name, testName, snip, vals, pos, posRel) =
|
||||||
|
|
||||||
var message = "\l"
|
var message = "\l"
|
||||||
message &= " Condition: $2($1)\l".format(snip, name)
|
message &= " Condition: $2($1)\l".format(snip, name)
|
||||||
message &= " Where:\l"
|
|
||||||
|
|
||||||
|
if vals.len() > 0:
|
||||||
|
message &= " Where:\l"
|
||||||
for k, v in vals.pairs:
|
for k, v in vals.pairs:
|
||||||
message &= " $1 -> $2\l".format(k, v)
|
message &= " $1 -> $2\l".format(k, v)
|
||||||
|
|
||||||
|
|
@ -69,7 +82,7 @@ template returnException(name, testName, snip, vals, pos, posRel) =
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
proc `$`(test: Test): string =
|
proc `$`(test: Test): string =
|
||||||
return "proc `"&test.name&"`()"
|
return "proc `" & $test.name.toStrLit & "`()"
|
||||||
|
|
||||||
proc `$`*[T](ar: openarray[T]): string =
|
proc `$`*[T](ar: openarray[T]): string =
|
||||||
## Converts an array into a string
|
## Converts an array into a string
|
||||||
|
|
@ -139,36 +152,13 @@ macro toString*(obj: typed): untyped =
|
||||||
$(obj)
|
$(obj)
|
||||||
result = getAst(toStrAst(obj))
|
result = getAst(toStrAst(obj))
|
||||||
|
|
||||||
proc getTestsInfo(name: string): TestsInfo =
|
proc getTestsModule(name: string): TestsModule {.compileTime.} =
|
||||||
if not testsInfoMap.hasKey(name):
|
if not testsModuleMap.hasKey(name):
|
||||||
testsInfoMap[name] = TestsInfo(fileName: name)
|
testsModuleMap[name] = TestsModule(fileName: name)
|
||||||
return testsInfoMap[name]
|
return testsModuleMap[name]
|
||||||
|
|
||||||
proc addTest*(testsInfo: TestsInfo, procDef: proc(test: Test), name: string) =
|
proc addTest(testsModule: TestsModule, procDef: NimNode, name: NimNode) =
|
||||||
testsInfo.tests.add(Test(procDef: procDef, name: name))
|
testsModule.tests.add(Test(procDef: procDef, name: name))
|
||||||
|
|
||||||
template addToTests(body, name, sym) =
|
|
||||||
let
|
|
||||||
posRel = instantiationInfo()
|
|
||||||
testsInfo = getTestsInfo(posRel.filename)
|
|
||||||
|
|
||||||
testsInfo.addTest(proc(sym: Test) = body, name)
|
|
||||||
|
|
||||||
macro test*(name: string, body: untyped): untyped =
|
|
||||||
|
|
||||||
let sym = genSym(nskParam, "t")
|
|
||||||
|
|
||||||
body.insert(0,
|
|
||||||
nnkLetSection.newTree(
|
|
||||||
nnkIdentDefs.newTree(
|
|
||||||
ident("self"),
|
|
||||||
newEmptyNode(),
|
|
||||||
sym
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
result = getAst(addToTests(body, name, sym))
|
|
||||||
|
|
||||||
template strRep(n: NimNode): untyped =
|
template strRep(n: NimNode): untyped =
|
||||||
toString(n)
|
toString(n)
|
||||||
|
|
@ -233,68 +223,52 @@ macro getSyms(code:untyped): untyped =
|
||||||
template check*(code: untyped) =
|
template check*(code: untyped) =
|
||||||
## Assertions for tests
|
## Assertions for tests
|
||||||
if not code:
|
if not code:
|
||||||
# These need to be here to capture the actual info
|
|
||||||
let
|
let
|
||||||
pos = instantiationInfo(fullpaths=true)
|
pos = instantiationInfo(fullpaths=true)
|
||||||
posRel = instantiationInfo()
|
posRel = instantiationInfo()
|
||||||
|
vals = getSyms(code)
|
||||||
var snip = ""
|
|
||||||
let testName = $self.name
|
|
||||||
|
|
||||||
var vals = getSyms(code)
|
|
||||||
# get ast string with extra spaces ignored
|
# get ast string with extra spaces ignored
|
||||||
snip = astToStr(code).strip().split({'\t', '\v', '\c', '\n', '\f'}).join("; ")
|
snip = astToStr(code).strip().split({'\t', '\v', '\c', '\n', '\f'}).join("; ")
|
||||||
|
|
||||||
returnException("check", testName, snip, vals, pos, posRel)
|
returnException("check", testName, snip, vals, pos, posRel)
|
||||||
|
|
||||||
template checkRaises*(error: untyped,
|
template wrapCode(code): untyped =
|
||||||
code: untyped): untyped =
|
# This is needed to prevent an "unreachable code" error
|
||||||
|
# if the code block raises an exception
|
||||||
|
(proc () = code)()
|
||||||
|
|
||||||
|
template checkRaises*(error: untyped, code: untyped): untyped =
|
||||||
## Raises a TestAssertError when the exception "error" is
|
## Raises a TestAssertError when the exception "error" is
|
||||||
## not thrown in the code
|
## not thrown in the code
|
||||||
let
|
let
|
||||||
pos = instantiationInfo(fullpaths=true)
|
pos = instantiationInfo(fullpaths=true)
|
||||||
posRel = instantiationInfo()
|
posRel = instantiationInfo()
|
||||||
|
|
||||||
when error isnot Exception:
|
|
||||||
try:
|
try:
|
||||||
code
|
wrapCode(code)
|
||||||
let
|
let
|
||||||
codeStr = astToStr(code).split().join(" ")
|
codeStr = astToStr(code).strip().split().join(" ")
|
||||||
snip = "$1, $2".format(astToStr(error), codeStr)
|
snip = "$1, $2".format(astToStr(error), codeStr)
|
||||||
vals = {codeStr: "No Exception Raised"}.toTable()
|
vals = {codeStr: "No Exception Raised"}.toTable()
|
||||||
testName = $self.name
|
testName = testName
|
||||||
returnException("checkRaises", testName, snip, vals, pos, posRel)
|
returnException("checkRaises", testName, snip, vals, pos, posRel)
|
||||||
|
|
||||||
except error:
|
except error:
|
||||||
discard
|
discard
|
||||||
except TestAssertError:
|
except TestAssertError:
|
||||||
raise
|
raise
|
||||||
except Exception:
|
except:
|
||||||
let
|
let
|
||||||
e = getCurrentException()
|
e = getCurrentException()
|
||||||
codeStr = astToStr(code).split().join(" ")
|
codeStr = astToStr(code).strip().split().join(" ")
|
||||||
snip = "$1, $2".format(astToStr(error), codeStr)
|
snip = "$1, $2".format(astToStr(error), codeStr)
|
||||||
vals = {codeStr: $e.name}.toTable()
|
vals = {codeStr: $e.name}.toTable()
|
||||||
testName = $self.name
|
testName = testName
|
||||||
|
|
||||||
returnException("checkRaises", testName, snip, vals, pos, posRel)
|
returnException("checkRaises", testName, snip, vals, pos, posRel)
|
||||||
else:
|
|
||||||
try:
|
|
||||||
code
|
|
||||||
let
|
|
||||||
codeStr = astToStr(code).split().join(" ")
|
|
||||||
snip = "$1, $2".format(astToStr(error), codeStr)
|
|
||||||
vals = {codeStr: "No Exception Raised"}.toTable()
|
|
||||||
testName = $self.name
|
|
||||||
returnException("checkRaises", testName, snip, vals, pos, posRel)
|
|
||||||
|
|
||||||
except error:
|
|
||||||
discard
|
|
||||||
except TestAssertError:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
proc printRunning(info: TestsInfo) =
|
proc printRunning*(testsInfo: TestsInfo) =
|
||||||
let termSize = getTermSize()
|
let termSize = getTermSize()
|
||||||
var
|
var
|
||||||
numTicks = termSize.width
|
numTicks = termSize.width
|
||||||
|
|
@ -307,23 +281,27 @@ proc printRunning(info: TestsInfo) =
|
||||||
when not noColors:
|
when not noColors:
|
||||||
styledEcho(
|
styledEcho(
|
||||||
styleBright,
|
styleBright,
|
||||||
fgYellow, "\l"&ticks,
|
fgYellow, "\l" & ticks,
|
||||||
fgYellow, "\l\l[Running]",
|
fgYellow, "\l\l[Running]",
|
||||||
fgWhite, " tests in $1 ".format(info.fileName)
|
fgWhite, " tests in $1 ".format(testsInfo.fileName)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
echo "\l$1\l".format(ticks)
|
echo "\l$1\l".format(ticks)
|
||||||
echo "[Running] tests in $1".format(info.name)
|
echo "[Running] tests in $1".format(testsInfo.fileName)
|
||||||
|
|
||||||
|
proc printPassedTests*(info: TestsInfo) =
|
||||||
|
|
||||||
|
if info.testsPassed == 0 and info.numTests == 0:
|
||||||
|
return
|
||||||
|
|
||||||
proc printPassedTests(info: TestsInfo) =
|
|
||||||
when not noColors:
|
when not noColors:
|
||||||
# Output red if tests didn't pass, green otherwise
|
# Output red if tests didn't pass, green otherwise
|
||||||
var color = fgGreen
|
var color = fgGreen
|
||||||
|
|
||||||
if info.testsPassed != info.tests.len():
|
if info.testsPassed != info.numTests:
|
||||||
color = fgRed
|
color = fgRed
|
||||||
|
|
||||||
var passedStr = "[" & $info.testsPassed & "/" & $info.tests.len() & "]"
|
var passedStr = "[" & $info.testsPassed & "/" & $info.numTests & "]"
|
||||||
|
|
||||||
when not defined(quiet):
|
when not defined(quiet):
|
||||||
when not noColors:
|
when not noColors:
|
||||||
|
|
@ -335,15 +313,10 @@ proc printPassedTests(info: TestsInfo) =
|
||||||
echo "\l$1 tests passed for $2.".format(passedStr, info.fileName)
|
echo "\l$1 tests passed for $2.".format(passedStr, info.fileName)
|
||||||
|
|
||||||
|
|
||||||
proc runTests(info: TestsInfo) =
|
template runTest(procCall, info, testName) =
|
||||||
when noColors:
|
|
||||||
stdout.write(info.fileName & " ")
|
|
||||||
else:
|
|
||||||
setForegroundColor(fgWhite)
|
|
||||||
writeStyled(info.fileName & " ", {styleBright})
|
|
||||||
for t in info.tests:
|
|
||||||
try:
|
try:
|
||||||
t.procDef(t)
|
procCall
|
||||||
when defined(quiet):
|
when defined(quiet):
|
||||||
when noColors:
|
when noColors:
|
||||||
stdout.write(".")
|
stdout.write(".")
|
||||||
|
|
@ -358,15 +331,53 @@ proc runTests(info: TestsInfo) =
|
||||||
|
|
||||||
when not noColors:
|
when not noColors:
|
||||||
styledEcho(styleBright, fgGreen, okStr,
|
styledEcho(styleBright, fgGreen, okStr,
|
||||||
fgWhite, " ", t.name)
|
fgWhite, " ", testName)
|
||||||
else:
|
else:
|
||||||
echo "$1 $2".format(okStr, t.name)
|
echo "$1 $2".format(okStr, t.name)
|
||||||
|
|
||||||
info.testsPassed += 1
|
info.testsPassed += 1
|
||||||
info.lastTestFailed = false
|
info.lastTestFailed = false
|
||||||
except TestAssertError:
|
except PrivateTestError as e:
|
||||||
let e = (ref TestAssertError)(getCurrentException())
|
info.numTests -= 1
|
||||||
|
when defined(quiet):
|
||||||
|
when noColors:
|
||||||
|
stdout.write("N")
|
||||||
|
else:
|
||||||
|
setForegroundColor(fgBlue)
|
||||||
|
writeStyled("N", {styleBright})
|
||||||
|
setForegroundColor(fgWhite)
|
||||||
|
else:
|
||||||
|
when not noColors:
|
||||||
|
styledEcho(styleBright,
|
||||||
|
fgBlue, "\l[Not run]",
|
||||||
|
fgWhite, " ", testName)
|
||||||
|
else:
|
||||||
|
echo "\l[Not run] $1".format(t.name)
|
||||||
|
|
||||||
|
let
|
||||||
|
name = e.checkFuncName
|
||||||
|
snip = e.codeSnip
|
||||||
|
line = e.lineNumber
|
||||||
|
col = e.column
|
||||||
|
filename = e.fileName
|
||||||
|
vals = e.valTable
|
||||||
|
|
||||||
|
when not noColors:
|
||||||
|
styledEcho(styleDim, fgWhite,
|
||||||
|
" Test code contains private or non accessible symbols:")
|
||||||
|
|
||||||
|
styledEcho(styleDim, fgGreen, " " & snip)
|
||||||
|
|
||||||
|
styledEcho(
|
||||||
|
styleDim, fgWhite,
|
||||||
|
" Location: $1; line $2; col $3".format(filename, line, col))
|
||||||
|
else:
|
||||||
|
echo " Test code contains private or non accessible symbols:\l $1".format(snip)
|
||||||
|
echo " Location: $1; line $2; col: $3".format(filename, line, col)
|
||||||
|
|
||||||
|
info.lastTestFailed = true
|
||||||
|
|
||||||
|
except TestAssertError as e:
|
||||||
when defined(quiet):
|
when defined(quiet):
|
||||||
when noColors:
|
when noColors:
|
||||||
stdout.write("F")
|
stdout.write("F")
|
||||||
|
|
@ -378,7 +389,7 @@ proc runTests(info: TestsInfo) =
|
||||||
when not noColors:
|
when not noColors:
|
||||||
styledEcho(styleBright,
|
styledEcho(styleBright,
|
||||||
fgRed, "\l[Failed]",
|
fgRed, "\l[Failed]",
|
||||||
fgWhite, " ", t.name)
|
fgWhite, " ", testName)
|
||||||
else:
|
else:
|
||||||
echo "\l[Failed] $1".format(t.name)
|
echo "\l[Failed] $1".format(t.name)
|
||||||
|
|
||||||
|
|
@ -391,7 +402,10 @@ proc runTests(info: TestsInfo) =
|
||||||
vals = e.valTable
|
vals = e.valTable
|
||||||
|
|
||||||
when not noColors:
|
when not noColors:
|
||||||
styledEcho(styleDim, fgWhite, " Condition: $2($1)\l".format(snip, name), " Where:")
|
styledEcho(styleDim, fgWhite, " Condition: $2($1)".format(snip, name))
|
||||||
|
|
||||||
|
if vals.len > 0:
|
||||||
|
styledEcho(styleDim, fgWhite, " Where:")
|
||||||
for k, v in vals.pairs:
|
for k, v in vals.pairs:
|
||||||
styledEcho(styleDim, fgCyan, " ", k,
|
styledEcho(styleDim, fgCyan, " ", k,
|
||||||
fgWhite, " -> ",
|
fgWhite, " -> ",
|
||||||
|
|
@ -401,14 +415,15 @@ proc runTests(info: TestsInfo) =
|
||||||
" Location: $1; line $2; col $3".format(filename, line, col))
|
" Location: $1; line $2; col $3".format(filename, line, col))
|
||||||
else:
|
else:
|
||||||
echo " Condition: $2($1)".format(snip, name)
|
echo " Condition: $2($1)".format(snip, name)
|
||||||
|
if vals.len > 0:
|
||||||
echo " Where:"
|
echo " Where:"
|
||||||
for k, v in vals.pairs:
|
for k, v in vals.pairs:
|
||||||
echo " ", k, " -> ", v
|
echo " ", k, " -> ", v
|
||||||
|
|
||||||
echo " Location: $1; line $2; col: $3".format(filename, line, col)
|
echo " Location: $1; line $2; col: $3".format(filename, line, col)
|
||||||
|
|
||||||
info.lastTestFailed = true
|
info.lastTestFailed = true
|
||||||
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
proc printSummary(totalTestsPassed: int, totalTests: int) =
|
proc printSummary(totalTestsPassed: int, totalTests: int) =
|
||||||
when not noColors:
|
when not noColors:
|
||||||
|
|
@ -450,23 +465,119 @@ proc printSummary(totalTestsPassed: int, totalTests: int) =
|
||||||
echo "\l[Summary]"
|
echo "\l[Summary]"
|
||||||
echo "\l $1 tests passed.".format(passedStr)
|
echo "\l $1 tests passed.".format(passedStr)
|
||||||
|
|
||||||
proc runTests*() =
|
template createRunTests(tests, testsInfo, totalTests, totalTestsPassed) =
|
||||||
|
testsInfo.printRunning()
|
||||||
|
|
||||||
|
when defined(quiet):
|
||||||
|
echo ""
|
||||||
|
when noColors:
|
||||||
|
stdout.write(testsInfo.fileName & " ")
|
||||||
|
else:
|
||||||
|
setForegroundColor(fgWhite)
|
||||||
|
writeStyled(testsInfo.fileName & " ", {styleBright})
|
||||||
|
|
||||||
|
tests
|
||||||
|
testsInfo.printPassedTests()
|
||||||
|
|
||||||
|
totalTests += testsInfo.numTests
|
||||||
|
totalTestsPassed += testsInfo.testsPassed
|
||||||
|
|
||||||
|
template makeProc(body, tnameSym, tName, lineInfo, currentDir) =
|
||||||
|
when not compiles((proc (tnameSym: string) = body)(tName)):
|
||||||
|
(proc (tnameSym: string) =
|
||||||
|
let
|
||||||
|
#vals = getSyms(body)
|
||||||
|
# get ast string with extra spaces ignored
|
||||||
|
astBody = astToStr(body).strip().split({'\t', '\v', '\c', '\n', '\f'})
|
||||||
|
snip = astBody[1..<astBody.len].join("\l ")
|
||||||
|
|
||||||
|
var
|
||||||
|
filename = lineInfo.fileName.relativePath(currentDir)
|
||||||
|
line = lineInfo.line
|
||||||
|
col = lineInfo.column
|
||||||
|
|
||||||
|
var message = "\lCode contains private or non accessible symbols."
|
||||||
|
message &= " Location: $1; line $2; col: $3".format(filename, line, col)
|
||||||
|
|
||||||
|
var exc = newException(PrivateTestError, message)
|
||||||
|
exc.fileName = filename
|
||||||
|
exc.lineNumber = line
|
||||||
|
exc.column = col
|
||||||
|
exc.codeSnip = snip
|
||||||
|
exc.testName = tName
|
||||||
|
#exc.valTable = vals
|
||||||
|
exc.checkFuncName = tnameSym
|
||||||
|
raise exc
|
||||||
|
)(tName)
|
||||||
|
else:
|
||||||
|
(proc (tnameSym: string) = body)(tName)
|
||||||
|
|
||||||
|
template createInfo(infoSym, name, ntests, currentDir) =
|
||||||
|
let infoSym = TestsInfo(fileName: name.relativePath(currentDir), numTests: ntests)
|
||||||
|
|
||||||
|
template createTotalVars(totalTestsPassed, totalTests) =
|
||||||
var
|
var
|
||||||
totalTests = 0
|
totalTests = 0
|
||||||
totalTestsPassed = 0
|
totalTestsPassed = 0
|
||||||
|
|
||||||
for name in testsInfoMap.keys():
|
template printFinalSummary(totalTestsPassed, totalTests) =
|
||||||
let testsInfo = getTestsInfo(name)
|
|
||||||
testsInfo.printRunning()
|
|
||||||
|
|
||||||
testsInfo.runTests()
|
|
||||||
|
|
||||||
testsInfo.printPassedTests()
|
|
||||||
|
|
||||||
totalTests += testsInfo.tests.len()
|
|
||||||
totalTestsPassed += testsInfo.testsPassed
|
|
||||||
|
|
||||||
printSummary(totalTestsPassed, totalTests)
|
printSummary(totalTestsPassed, totalTests)
|
||||||
|
|
||||||
when isMainModule:
|
proc expandTests(currentDir: string): NimNode =
|
||||||
runTests()
|
|
||||||
|
result = newNimNode(nnkStmtList)
|
||||||
|
|
||||||
|
let
|
||||||
|
totalTestsPassed = genSym(nskVar, "totalPassed")
|
||||||
|
totalTests = genSym(nskVar, "totalTests")
|
||||||
|
|
||||||
|
result.add(getAst(createTotalVars(totalTestsPassed, totalTests)))
|
||||||
|
|
||||||
|
for name in testsModuleMap.keys():
|
||||||
|
let
|
||||||
|
testsModule = testsModuleMap[name]
|
||||||
|
infoSym = genSym(nskLet, "testsInfo")
|
||||||
|
allTests = newNimNode(nnkStmtList)
|
||||||
|
numTests = newLit(testsModule.tests.len())
|
||||||
|
|
||||||
|
result.add(getAst(createInfo(infoSym, name, numTests, currentDir)))
|
||||||
|
|
||||||
|
for test in testsModule.tests:
|
||||||
|
let
|
||||||
|
body = test.procDef
|
||||||
|
tname = genSym(nskParam, "tname")
|
||||||
|
|
||||||
|
body.insert(0,
|
||||||
|
nnkLetSection.newTree(
|
||||||
|
nnkIdentDefs.newTree(
|
||||||
|
ident("testName"),
|
||||||
|
newEmptyNode(),
|
||||||
|
tname
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let
|
||||||
|
lineInfo = body.lineInfoObj
|
||||||
|
testCall = getAst(makeProc(body, tname, test.name, lineInfo, currentDir))
|
||||||
|
testRun = getAst(runTest(testCall, infoSym, test.name))
|
||||||
|
|
||||||
|
allTests.add(testRun)
|
||||||
|
|
||||||
|
result.add(getAst(createRunTests(allTests, infoSym, totalTests, totalTestsPassed)))
|
||||||
|
|
||||||
|
result.add(getAst(printFinalSummary(totalTestsPassed, totalTests)))
|
||||||
|
|
||||||
|
macro test*(name: string, body: untyped): untyped =
|
||||||
|
## The test macro. No runtime overhead is introduced using it and
|
||||||
|
## it can be run from anywhere, including in the proc being tested.
|
||||||
|
let
|
||||||
|
fileName = body.lineInfoObj.fileName
|
||||||
|
testsInfo = getTestsModule(fileName)
|
||||||
|
testsInfo.addTest(body, name)
|
||||||
|
|
||||||
|
macro runTests*(callsite: varargs[untyped]): untyped =
|
||||||
|
## This macro must be run in the main testing module that imports
|
||||||
|
## all of the other tests
|
||||||
|
let currentDir = getProjectPath()
|
||||||
|
return expandTests(currentDir)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue