summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.comy>2011-10-05 16:03:24 +0300
committerZahary Karadjov <zahary@gmail.comy>2011-10-07 17:07:18 +0300
commit22546c44d1a8c34077c7c24e0b565bc8900061d1 (patch)
treeaadb268297dab6d9be096521effb023ee41b11e7 /lib
parenteaeed1f8468d5e88ec6f447d68556eeb86ea22f6 (diff)
downloadNim-22546c44d1a8c34077c7c24e0b565bc8900061d1.tar.gz
Basic unit testing facilities (suites, fixtures, cases)
Added: PNimrodNode.lineinfo for better error messages from macros
Added: seq.splice

For easier use from templates and macros, except stament now supports
the list of exception types to be supplied in nkBraket node (array literal).
Diffstat (limited to 'lib')
-rwxr-xr-xlib/core/macros.nim16
-rw-r--r--lib/pure/unittest.nim157
-rwxr-xr-xlib/system.nim21
3 files changed, 187 insertions, 7 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index c5afcdf17..e61575f3e 100755
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -36,8 +36,8 @@ type
     nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, 

     nnkElifBranch, nnkExceptBranch, nnkElse, nnkMacroStmt, 

     nnkAsmStmt, nnkPragma, nnkIfStmt, nnkWhenStmt, 

-    nnkForStmt, nnkWhileStmt, nnkCaseStmt, 
-    nnkVarSection, nnkLetSection, nnkConstSection, 
+    nnkForStmt, nnkWhileStmt, nnkCaseStmt, 

+    nnkVarSection, nnkLetSection, nnkConstSection, 

     nnkConstDef, nnkTypeSection, nnkTypeDef, 

     nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, 

     nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, 

@@ -45,8 +45,8 @@ type
     nnkIncludeStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, 

     nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, 

     nnkTupleTy, nnkRecList, nnkRecCase, nnkRecWhen, 

-    nnkRefTy, nnkPtrTy, nnkVarTy, 
-    nnkConstTy, nnkMutableTy,
+    nnkRefTy, nnkPtrTy, nnkVarTy, 

+    nnkConstTy, nnkMutableTy,

     nnkDistinctTy, 

     nnkProcTy, nnkEnumTy, nnkEnumFieldDef, nnkReturnToken

   TNimNodeKinds* = set[TNimrodNodeKind]

@@ -187,6 +187,8 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
   ## in a string literal node

   return newStrLitNode(repr(n))

 

+proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}

+

 proc toLisp*(n: PNimrodNode): string {.compileTime.} =

   ## Convert the AST `n` to a human-readable string

   ##

@@ -230,9 +232,9 @@ proc parseStmt*(s: string): stmt {.magic: "ParseStmtToAst".}
 

 proc getAst*(macroOrTemplate: expr): expr {.magic: "ExpandMacroToAst".}

   ## Obtains the AST nodes returned from a macro or template invocation.

-  ## Example:
-  ## 
-  ## .. code-block:: nimrod
+  ## Example:

+  ## 

+  ## .. code-block:: nimrod

   ##

   ##   macro FooMacro() = 

   ##     var ast = getAst(BarTemplate())

diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
new file mode 100644
index 000000000..a5c97ee9b
--- /dev/null
+++ b/lib/pure/unittest.nim
@@ -0,0 +1,157 @@
+#

+#

+#            Nimrod's Runtime Library

+#        (c) Copyright 2011 Nimrod Contributors

+#

+#    See the file "copying.txt", included in this

+#    distribution, for details about the copyright.

+#

+

+## 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

+##

+## Maintainer: Zahary Karadjov (zah@github)

+##

+

+import

+  macros

+

+type

+  TestStatus* = enum OK, FAILED

+  # ETestFailed* = object of ESynch

+

+var 

+  # XXX: These better be thread-local

+  AbortOnError* = false

+  checkpoints: seq[string] = @[]

+

+template TestSetupIMPL*: stmt = nil

+template TestTeardownIMPL*: stmt = nil

+

+proc shouldRun(testName: string): bool =

+  result = true

+

+template suite*(name: expr, body: stmt): stmt =

+  block:

+    template setup(setupBody: stmt): stmt =

+      template TestSetupIMPL: stmt = setupBody

+

+    template teardown(teardownBody: stmt): stmt =

+      template TestTeardownIMPL: stmt = teardownBody

+

+    body

+

+template test*(name: expr, body: stmt): stmt =

+  if bind shouldRun(name):

+    bind checkpoints = @[]

+    var TestStatusIMPL = OK

+    

+    try:

+      TestSetupIMPL()

+      body

+

+    finally:

+      TestTeardownIMPL()

+      echo "[" & $TestStatusIMPL & "] " & name

+

+proc checkpoint*(msg: string) =

+  checkpoints.add(msg)

+  # TODO: add support for something like SCOPED_TRACE from Google Test

+

+template fail* =

+  for msg in items(bind checkpoints):

+    echo msg

+

+  if AbortOnError: quit(1)

+  

+  TestStatusIMPL = FAILED

+  checkpoints = @[]

+

+macro check*(conditions: stmt): stmt =

+  proc standardRewrite(e: expr): stmt =

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

+      if not Exp:

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

+        fail()

+

+    # XXX: If we don't create a string literal node below, the compiler

+    # will SEGFAULT in a rather strange fashion:

+    #

+    # rewrite(e, e.toStrLit, e.toStrLit) is ok, but

+    #

+    # rewrite(e, e.lineinfo, e.toStrLit) or

+    # rewrite(e, "anything", e.toStrLit) are not

+    #

+    # It may have something to do with the dummyContext hack in 

+    # evals.nim/evalTemplate

+    #

+    result = getAst(rewrite(e, newStrLitNode(e.lineinfo), e.toStrLit))

+  

+  case conditions.kind

+  of nnkCall, nnkCommand, nnkMacroStmt:

+    case conditions[1].kind

+    of nnkInfix:

+      proc rewriteBinaryOp(op: expr): stmt =

+        template rewrite(op, left, right, lineInfoLit: expr, opLit, leftLit, rightLit: string): stmt =

+          block:

+            var 

+              lhs = left

+              rhs = right

+

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

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

+              checkpoint("  " & leftLit & " was " & $lhs)

+              checkpoint("  " & rightLit & " was " & $rhs)

+              fail()

+

+        result = getAst(rewrite(

+          op[0], op[1], op[2],

+          newStrLitNode(op.lineinfo),

+          op.toStrLit,

+          op[1].toStrLit,

+          op[2].toStrLit))

+        

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

+    error conditions.lineinfo & ": Malformed check statement"

+

+template require*(conditions: stmt): stmt =

+  block:

+    const AbortOnError = true    

+    check conditions

+

+macro expect*(exp: stmt): stmt =

+  template expectBody(errorTypes, lineInfoLit: expr, body: stmt): stmt =

+    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, newStrLitNode(exp.lineinfo), body))

+

diff --git a/lib/system.nim b/lib/system.nim
index 226642771..b6f5243e5 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -857,6 +857,27 @@ proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} =
     dec(j)
   x[i] = item
 
+template spliceImpl(x, start, count, elements: expr): stmt =
+  var 
+    shift = elements.len - count
+    newLen = x.len + shift
+    totalShifted = x.len - (start + count)
+    firstShifted = newLen - totalShifted
+    
+  if shift > 0:
+    setLen(x, newLen)
+
+  for i in countup(firstShifted, newLen - 1):
+    shallowCopy(x[i], x[i-shift])
+
+  for c in countup(0, elements.len - 1):
+    x[start + c] = elements[c]
+
+  if shift < 0:
+    setLen(x, newLen)
+
+proc splice*[T](x: var seq[T], start, count: int, elements: openarray[T] = []) =
+  spliceImpl(x, start, count, elements)
 
 proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.}
   ## takes any Nimrod variable and returns its string representation. It