# # # Nimrod's Runtime Library # (c) Copyright 2012 Nimrod Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## :Author: Zahary Karadjov ## ## This module implements the standard unit testing facilities such as ## suites, fixtures and test cases as well as facilities for combinatorial ## and randomzied test case generation (not yet available) ## and object mocking (not yet available) ## ## It is loosely based on C++'s boost.test and Haskell's QuickTest import macros when defined(stdout): import os when not defined(ECMAScript): import terminal type TTestStatus* = enum OK, FAILED TOutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE var # XXX: These better be thread-local AbortOnError*: bool OutputLevel*: TOutputLevel ColorOutput*: bool checkpoints: seq[string] = @[] template TestSetupIMPL*: stmt {.immediate, dirty.} = discard template TestTeardownIMPL*: stmt {.immediate, dirty.} = discard proc shouldRun(testName: string): bool = result = true template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} = block: template setup*(setupBody: stmt): stmt {.immediate, dirty.} = template TestSetupIMPL: stmt {.immediate, dirty.} = setupBody template teardown*(teardownBody: stmt): stmt {.immediate, dirty.} = template TestTeardownIMPL: stmt {.immediate, dirty.} = teardownBody body proc testDone(name: string, s: TTestStatus) = if s == FAILED: program_result += 1 if OutputLevel != PRINT_NONE and (OutputLevel == PRINT_ALL or s == FAILED): template rawPrint() = echo("[", $s, "] ", name, "\n") when not defined(ECMAScript): if ColorOutput and not defined(ECMAScript): var color = (if s == OK: fgGreen else: fgRed) styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n" else: rawPrint() else: rawPrint() template test*(name: expr, body: stmt): stmt {.immediate, dirty.} = bind shouldRun, checkpoints, testDone if shouldRun(name): checkpoints = @[] var TestStatusIMPL {.inject.} = OK try: TestSetupIMPL() body except: checkpoint("Unhandled exception: " & getCurrentExceptionMsg()) fail() finally: TestTeardownIMPL() testDone name, TestStatusIMPL proc checkpoint*(msg: string) = checkpoints.add(msg) # TODO: add support for something like SCOPED_TRACE from Google Test template fail* = bind checkpoints for msg in items(checkpoints): echo msg when not defined(ECMAScript): if AbortOnError: quit(1) when defined(TestStatusIMPL): TestStatusIMPL = FAILED else: program_result += 1 checkpoints = @[] macro check*(conditions: stmt): stmt {.immediate.} = let checked = callsite()[1] var argsAsgns = newNimNode(nnkStmtList) argsPrintOuts = newNimNode(nnkStmtList) counter = 0 template asgn(a, value: expr): stmt = var a = value # XXX: we need "var: var" here in order to # preserve the semantics of var params template print(name, value: expr): stmt = when compiles(string($value)): checkpoint(name & " was " & $value) proc inspectArgs(exp: PNimrodNode) = for i in 1 .. 0: for opt in countup(low(TOutputLevel), high(TOutputLevel)): if $opt == envOutLvl: OutputLevel = opt break