Add unittest suite/test name filters
Support simple globbing
This commit is contained in:
parent
e758b9408e
commit
25831a83d7
2 changed files with 118 additions and 11 deletions
|
|
@ -21,13 +21,41 @@
|
||||||
## ``nim c -r <testfile.nim>`` exits with 0 or 1
|
## ``nim c -r <testfile.nim>`` exits with 0 or 1
|
||||||
##
|
##
|
||||||
## Running a single test
|
## Running a single test
|
||||||
## ---------------------
|
## =====================
|
||||||
##
|
##
|
||||||
## Simply specify the test name as a command line argument.
|
## Specify the test name as a command line argument.
|
||||||
##
|
##
|
||||||
## .. code::
|
## .. code::
|
||||||
##
|
##
|
||||||
## nim c -r test "my super awesome test name"
|
## nim c -r test "my test name" "another test"
|
||||||
|
##
|
||||||
|
## Multiple arguments can be used.
|
||||||
|
##
|
||||||
|
## Running a single test suite
|
||||||
|
## ===========================
|
||||||
|
##
|
||||||
|
## Specify the suite name delimited by ``"::"``.
|
||||||
|
##
|
||||||
|
## .. code::
|
||||||
|
##
|
||||||
|
## nim c -r test "my test name::"
|
||||||
|
##
|
||||||
|
## Selecting tests by pattern
|
||||||
|
## ==========================
|
||||||
|
##
|
||||||
|
## A single ``"*"`` can be used for globbing.
|
||||||
|
##
|
||||||
|
## Delimit the end of a suite name with ``"::"``.
|
||||||
|
##
|
||||||
|
## Tests matching **any** of the arguments are executed.
|
||||||
|
##
|
||||||
|
## .. code::
|
||||||
|
##
|
||||||
|
## nim c -r test fast_suite::mytest1 fast_suite::mytest2
|
||||||
|
## nim c -r test "fast_suite::mytest*"
|
||||||
|
## nim c -r test "auth*::" "crypto::hashing*"
|
||||||
|
## # Run suites starting with 'bug #' and standalone tests starting with '#'
|
||||||
|
## nim c -r test 'bug #*::' '::#*'
|
||||||
##
|
##
|
||||||
## Example
|
## Example
|
||||||
## -------
|
## -------
|
||||||
|
|
@ -121,7 +149,7 @@ var
|
||||||
|
|
||||||
checkpoints {.threadvar.}: seq[string]
|
checkpoints {.threadvar.}: seq[string]
|
||||||
formatters {.threadvar.}: seq[OutputFormatter]
|
formatters {.threadvar.}: seq[OutputFormatter]
|
||||||
testsToRun {.threadvar.}: HashSet[string]
|
testsFilters {.threadvar.}: HashSet[string]
|
||||||
|
|
||||||
when declared(stdout):
|
when declared(stdout):
|
||||||
abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
||||||
|
|
@ -300,22 +328,63 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
|
||||||
method suiteEnded*(formatter: JUnitOutputFormatter) =
|
method suiteEnded*(formatter: JUnitOutputFormatter) =
|
||||||
formatter.stream.writeLine("\t</testsuite>")
|
formatter.stream.writeLine("\t</testsuite>")
|
||||||
|
|
||||||
proc shouldRun(testName: string): bool =
|
proc glob(matcher, filter: string): bool =
|
||||||
if testsToRun.len == 0:
|
## Globbing using a single `*`. Empty `filter` matches everything.
|
||||||
|
if filter.len == 0:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
result = testName in testsToRun
|
if not filter.contains('*'):
|
||||||
|
return matcher == filter
|
||||||
|
|
||||||
|
let beforeAndAfter = filter.split('*', maxsplit=1)
|
||||||
|
if beforeAndAfter.len == 1:
|
||||||
|
# "foo*"
|
||||||
|
return matcher.startswith(beforeAndAfter[0])
|
||||||
|
|
||||||
|
if matcher.len < filter.len - 1:
|
||||||
|
return false # "12345" should not match "123*345"
|
||||||
|
|
||||||
|
return matcher.startsWith(beforeAndAfter[0]) and matcher.endsWith(beforeAndAfter[1])
|
||||||
|
|
||||||
|
proc matchFilter(suiteName, testName, filter: string): bool =
|
||||||
|
if filter == "":
|
||||||
|
return true
|
||||||
|
if testName == filter:
|
||||||
|
# corner case for tests containing "::" in their name
|
||||||
|
return true
|
||||||
|
let suiteAndTestFilters = filter.split("::", maxsplit=1)
|
||||||
|
|
||||||
|
if suiteAndTestFilters.len == 1:
|
||||||
|
# no suite specified
|
||||||
|
let test_f = suiteAndTestFilters[0]
|
||||||
|
return glob(testName, test_f)
|
||||||
|
|
||||||
|
return glob(suiteName, suiteAndTestFilters[0]) and glob(testName, suiteAndTestFilters[1])
|
||||||
|
|
||||||
|
when defined(testing): export matchFilter
|
||||||
|
|
||||||
|
proc shouldRun(currentSuiteName, testName: string): bool =
|
||||||
|
## Check if a test should be run by matching suiteName and testName against
|
||||||
|
## test filters.
|
||||||
|
if testsFilters.len == 0:
|
||||||
|
return true
|
||||||
|
|
||||||
|
for f in testsFilters:
|
||||||
|
if matchFilter(currentSuiteName, testName, f):
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
proc ensureInitialized() =
|
proc ensureInitialized() =
|
||||||
if formatters == nil:
|
if formatters == nil:
|
||||||
formatters = @[OutputFormatter(defaultConsoleFormatter())]
|
formatters = @[OutputFormatter(defaultConsoleFormatter())]
|
||||||
|
|
||||||
if not testsToRun.isValid:
|
if not testsFilters.isValid:
|
||||||
testsToRun.init()
|
testsFilters.init()
|
||||||
when declared(paramCount):
|
when declared(paramCount):
|
||||||
# Read tests to run from the command line.
|
# Read tests to run from the command line.
|
||||||
for i in 1 .. paramCount():
|
for i in 1 .. paramCount():
|
||||||
testsToRun.incl(paramStr(i))
|
testsFilters.incl(paramStr(i))
|
||||||
|
|
||||||
# These two procs are added as workarounds for
|
# These two procs are added as workarounds for
|
||||||
# https://github.com/nim-lang/Nim/issues/5549
|
# https://github.com/nim-lang/Nim/issues/5549
|
||||||
|
|
@ -395,7 +464,7 @@ template test*(name, body) {.dirty.} =
|
||||||
|
|
||||||
ensureInitialized()
|
ensureInitialized()
|
||||||
|
|
||||||
if shouldRun(name):
|
if shouldRun(when declared(testSuiteName): testSuiteName else: "", name):
|
||||||
checkpoints = @[]
|
checkpoints = @[]
|
||||||
var testStatusIMPL {.inject.} = OK
|
var testStatusIMPL {.inject.} = OK
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ discard """
|
||||||
|
|
||||||
[Suite] bug #5784
|
[Suite] bug #5784
|
||||||
|
|
||||||
|
[Suite] test name filtering
|
||||||
|
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -120,3 +122,39 @@ suite "bug #5784":
|
||||||
field: int
|
field: int
|
||||||
var obj: Obj
|
var obj: Obj
|
||||||
check obj.isNil or obj.field == 0
|
check obj.isNil or obj.field == 0
|
||||||
|
|
||||||
|
when defined(testing):
|
||||||
|
suite "test name filtering":
|
||||||
|
test "test name":
|
||||||
|
check matchFilter("suite1", "foo", "")
|
||||||
|
check matchFilter("suite1", "foo", "foo")
|
||||||
|
check matchFilter("suite1", "foo", "::")
|
||||||
|
check matchFilter("suite1", "foo", "*")
|
||||||
|
check matchFilter("suite1", "foo", "::foo")
|
||||||
|
check matchFilter("suite1", "::foo", "::foo")
|
||||||
|
|
||||||
|
test "test name - glob":
|
||||||
|
check matchFilter("suite1", "foo", "f*")
|
||||||
|
check matchFilter("suite1", "foo", "*oo")
|
||||||
|
check matchFilter("suite1", "12345", "12*345")
|
||||||
|
check matchFilter("suite1", "q*wefoo", "q*wefoo")
|
||||||
|
check false == matchFilter("suite1", "foo", "::x")
|
||||||
|
check false == matchFilter("suite1", "foo", "::x*")
|
||||||
|
check false == matchFilter("suite1", "foo", "::*x")
|
||||||
|
# overlap
|
||||||
|
check false == matchFilter("suite1", "12345", "123*345")
|
||||||
|
check matchFilter("suite1", "ab*c::d*e::f", "ab*c::d*e::f")
|
||||||
|
|
||||||
|
test "suite name":
|
||||||
|
check matchFilter("suite1", "foo", "suite1::")
|
||||||
|
check false == matchFilter("suite1", "foo", "suite2::")
|
||||||
|
check matchFilter("suite1", "qwe::foo", "qwe::foo")
|
||||||
|
check matchFilter("suite1", "qwe::foo", "suite1::qwe::foo")
|
||||||
|
|
||||||
|
test "suite name - glob":
|
||||||
|
check matchFilter("suite1", "foo", "::*")
|
||||||
|
check matchFilter("suite1", "foo", "*::*")
|
||||||
|
check matchFilter("suite1", "foo", "*::foo")
|
||||||
|
check false == matchFilter("suite1", "foo", "*ite2::")
|
||||||
|
check matchFilter("suite1", "q**we::foo", "q**we::foo")
|
||||||
|
check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue