summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--lib/pure/unittest.nim394
-rwxr-xr-xtests/compile/tseq2.nim2
2 files changed, 212 insertions, 184 deletions
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index be5e19c23..fff84ee29 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -1,187 +1,215 @@
-#

-#

-#            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 (zah@github)

-##

-## 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, terminal, os

-

-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.} = nil

-template TestTeardownIMPL*: stmt {.immediate, dirty.} = nil

-

-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):

-    var color = (if s == OK: fgGreen else: fgRed)

-    

-    if ColorOutput:

-      styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n"

-    else:

-      echo "[", $s, "] ", name, "\n"

-  

-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

-

-  if AbortOnError: quit(1)

-  

-  TestStatusIMPL = FAILED

-  checkpoints = @[]

-

+#
+#
+#            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, terminal, os
+
+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.} = nil
+template TestTeardownIMPL*: stmt {.immediate, dirty.} = nil
+
+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):
+    var color = (if s == OK: fgGreen else: fgRed)
+    
+    if ColorOutput:
+      styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n"
+    else:
+      echo "[", $s, "] ", name, "\n"
+  
+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
+
+  if AbortOnError: quit(1)
+  
+  TestStatusIMPL = FAILED
+  checkpoints = @[]
+
 macro check*(conditions: stmt): stmt {.immediate.} =
-  let conditions = callsite()

+  let conditions = callsite()
   
-  proc standardRewrite(e: PNimrodNode): PNimrodNode =

-    template rewrite(Exp, lineInfoLit: expr, expLit: string): stmt =

-      if not Exp:

-        checkpoint(lineInfoLit & ": Check failed: " & expLit)

-        fail()

- 

-    result = getAst(rewrite(e, e.lineinfo, e.toStrLit))

-  

-  case conditions.kind

-  of nnkCall, nnkCommand, nnkMacroStmt:

-    case conditions[1].kind

-    of nnkInfix:

-      proc rewriteBinaryOp(op: PNimrodNode): PNimrodNode =

-        template rewrite(op, left, right, lineInfoLit: expr, opLit,

-          leftLit, rightLit: string, printLhs, printRhs: bool): stmt =

-          block:

-            var 

-              lhs = left

-              rhs = right

-

-            if not `op`(lhs, rhs):

-              checkpoint(lineInfoLit & ": Check failed: " & opLit)

-              when printLhs: checkpoint("  " & leftLit & " was " & $lhs)

-              when printRhs: checkpoint("  " & rightLit & " was " & $rhs)

-              fail()

-

-        result = getAst(rewrite(

-          op[0], op[1], op[2],

-          op.lineinfo,

-          op.toStrLit,

-          op[1].toStrLit,

-          op[2].toStrLit,

-          op[1].kind notin nnkLiterals,

-          op[2].kind notin nnkLiterals))

-        

-      result = rewriteBinaryOp(conditions[1])

-  

-    of nnkCall, nnkCommand:

-      # TODO: We can print out the call arguments in case of failure

-      result = standardRewrite(conditions[1])

-

-    of nnkStmtList:

-      result = newNimNode(nnkStmtList)

-      for i in countup(0, conditions[1].len - 1):

-        result.add(newCall(!"check", conditions[1][i]))

-

-    else:

-      result = standardRewrite(conditions[1])

-

-  else:

-    var ast = conditions.treeRepr

-    error conditions.lineinfo & ": Malformed check statement:\n" & ast

-

-template require*(conditions: stmt): stmt {.immediate, dirty.} =

-  block:

-    const AbortOnError {.inject.} = true

-    check conditions

-

-macro expect*(exp: stmt): stmt {.immediate.} =

+  case conditions.kind
+  of nnkCall, nnkCommand, nnkMacroStmt:
+    case conditions[1].kind
+    of nnkInfix:
+      proc rewriteBinaryOp(op: PNimrodNode): PNimrodNode =
+        template rewrite(op, left, right, lineInfoLit: expr, opLit,
+          leftLit, rightLit: string, printLhs, printRhs: bool): stmt =
+          block:
+            var 
+              lhs = left
+              rhs = right
+
+            if not `op`(lhs, rhs):
+              checkpoint(lineInfoLit & ": Check failed: " & opLit)
+              when printLhs: checkpoint("  " & leftLit & " was " & $lhs)
+              when printRhs: checkpoint("  " & rightLit & " was " & $rhs)
+              fail()
+
+        result = getAst(rewrite(
+          op[0], op[1], op[2],
+          op.lineinfo,
+          op.toStrLit,
+          op[1].toStrLit,
+          op[2].toStrLit,
+          op[1].kind notin nnkLiterals,
+          op[2].kind notin nnkLiterals))
+        
+      result = rewriteBinaryOp(conditions[1])
+  
+    of nnkCall, nnkCommand:
+      proc rewriteCall(op: PNimrodNode): PNimrodNode =
+        template rewrite(call, lineInfoLit: expr, expLit: string,
+                         argAssgs, argPrintOuts: stmt): stmt =
+          block:
+            argAssgs
+            if not call:
+              checkpoint(lineInfoLit & ": Check failed: " & expLit)
+              argPrintOuts
+              fail()
+
+        template asgn(a, value: expr): stmt =
+          let a = value
+        
+        template print(name, value: expr): stmt =
+          checkpoint(name & " was " & $value)
+
+        var 
+          argsAsgns = newNimNode(nnkStmtList)
+          argsPrintOuts = newNimNode(nnkStmtList)
+          opStr = op.toStrLit
+        
+        for i in 1 .. <op.len:
+          if op[i].kind notin nnkLiterals:
+            # TODO: print only types that are printable
+            var arg = newIdentNode(":param" & ($i))
+            argsAsgns.add getAst(asgn(arg, op[i]))
+            argsPrintOuts.add getAst(print(op[i].toStrLit, arg))
+            op[i] = arg
+
+        result = getAst(rewrite(op, op.lineinfo, opStr, argsAsgns, argsPrintOuts))
+
+      result = rewriteCall(conditions[1])
+
+    of nnkStmtList:
+      result = newNimNode(nnkStmtList)
+      for i in countup(0, conditions[1].len - 1):
+        result.add(newCall(!"check", conditions[1][i]))
+
+    else:
+      template rewrite(Exp, lineInfoLit: expr, expLit: string): stmt =
+        if not Exp:
+          checkpoint(lineInfoLit & ": Check failed: " & expLit)
+          fail()
+
+      let e = conditions[1]
+      result = getAst(rewrite(e, e.lineinfo, e.toStrLit))
+
+  else:
+    var ast = conditions.treeRepr
+    error conditions.lineinfo & ": Malformed check statement:\n" & ast
+
+template require*(conditions: stmt): stmt {.immediate, dirty.} =
+  block:
+    const AbortOnError {.inject.} = true
+    check conditions
+
+macro expect*(exp: stmt): stmt {.immediate.} =
   let exp = callsite()
   template expectBody(errorTypes, lineInfoLit: expr,
-                      body: stmt): PNimrodNode {.dirty.} =

-    try:

-      body

-      checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")

-      fail()

-    except errorTypes:

-      nil

-

-  var expectCall = exp[0]

-  var body = exp[1]

-  

-  var errorTypes = newNimNode(nnkBracket)

-  for i in countup(1, expectCall.len - 1):

-    errorTypes.add(expectCall[i])

-

-  result = getAst(expectBody(errorTypes, exp.lineinfo, body))

-

-

-## Reading settings

-var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string

-

-if envOutLvl.len > 0:

-  for opt in countup(low(TOutputLevel), high(TOutputLevel)):

-    if $opt == envOutLvl:

-      OutputLevel = opt

-      break

-

-AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")

-ColorOutput  = not existsEnv("NIMTEST_NO_COLOR")

+                      body: stmt): PNimrodNode {.dirty.} =
+    try:
+      body
+      checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")
+      fail()
+    except errorTypes:
+      nil
+
+  var expectCall = exp[0]
+  var body = exp[1]
+  
+  var errorTypes = newNimNode(nnkBracket)
+  for i in countup(1, expectCall.len - 1):
+    errorTypes.add(expectCall[i])
+
+  result = getAst(expectBody(errorTypes, exp.lineinfo, body))
+
+
+## Reading settings
+var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
+
+if envOutLvl.len > 0:
+  for opt in countup(low(TOutputLevel), high(TOutputLevel)):
+    if $opt == envOutLvl:
+      OutputLevel = opt
+      break
+
+AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
+ColorOutput  = not existsEnv("NIMTEST_NO_COLOR")
diff --git a/tests/compile/tseq2.nim b/tests/compile/tseq2.nim
index 03bdb3fab..5036ff5f4 100755
--- a/tests/compile/tseq2.nim
+++ b/tests/compile/tseq2.nim
@@ -1,4 +1,4 @@
-
+var s = @[]
   
 proc `*` *(a, b: seq[int]): seq[int] = 
   # allocate a new sequence: