summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorCharles Blake <cblake@csail.mit.edu>2015-07-30 18:42:20 -0400
committerCharles Blake <cblake@csail.mit.edu>2015-07-30 18:42:20 -0400
commit970cfec6c2d9a952bcee83c862c1f142988e7e58 (patch)
tree3b280dc9beaa2c173917b0a97dc5a8c55e39b1c8 /lib
parent0b4c816cba8f11c28e983818e89ee8cbf20dff9e (diff)
parentc9084585dc507267d48c5f79674847da7fc4a6bb (diff)
downloadNim-970cfec6c2d9a952bcee83c862c1f142988e7e58.tar.gz
Merge ../Nim into devel
Diffstat (limited to 'lib')
-rw-r--r--lib/core/macros.nim18
-rw-r--r--lib/pure/asyncdispatch.nim20
-rw-r--r--lib/pure/asyncnet.nim2
-rw-r--r--lib/pure/marshal.nim2
-rw-r--r--lib/pure/times.nim25
-rw-r--r--lib/pure/unittest.nim144
-rw-r--r--lib/system.nim6
7 files changed, 177 insertions, 40 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 7d1c15610..c89fa354a 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -592,10 +592,8 @@ proc newNilLit*(): NimNode {.compileTime.} =
   ## New nil literal shortcut
   result = newNimNode(nnkNilLit)
 
-proc high*(node: NimNode): int {.compileTime.} = len(node) - 1
-  ## Return the highest index available for a node
-proc last*(node: NimNode): NimNode {.compileTime.} = node[node.high]
-  ## Return the last item in nodes children. Same as `node[node.high()]`
+proc last*(node: NimNode): NimNode {.compileTime.} = node[<node.len]
+  ## Return the last item in nodes children. Same as `node[^1]`
 
 
 const
@@ -695,7 +693,7 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
   of nnkBlockStmt, nnkWhileStmt:
     someProc[1] = val
   of nnkForStmt:
-    someProc[high(someProc)] = val
+    someProc[len(someProc)-1] = val
   else:
     badNodeKind someProc.kind, "body="
 
@@ -722,7 +720,7 @@ proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name)
   ## Create a new ident node from a string
 
 iterator children*(n: NimNode): NimNode {.inline.}=
-  for i in 0 .. high(n):
+  for i in 0 ..< n.len:
     yield n[i]
 
 template findChild*(n: NimNode; cond: expr): NimNode {.
@@ -742,16 +740,16 @@ template findChild*(n: NimNode; cond: expr): NimNode {.
 
 proc insert*(a: NimNode; pos: int; b: NimNode) {.compileTime.} =
   ## Insert node B into A at pos
-  if high(a) < pos:
+  if len(a)-1 < pos:
     ## add some empty nodes first
-    for i in high(a)..pos-2:
+    for i in len(a)-1..pos-2:
       a.add newEmptyNode()
     a.add b
   else:
     ## push the last item onto the list again
     ## and shift each item down to pos up one
-    a.add(a[a.high])
-    for i in countdown(high(a) - 2, pos):
+    a.add(a[a.len-1])
+    for i in countdown(len(a) - 3, pos):
       a[i + 1] = a[i]
     a[pos] = b
 
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 2ce31b4e8..7523b29d5 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -972,9 +972,9 @@ else:
       let data = PData(info.key.data)
       assert data.fd == info.key.fd.AsyncFD
       #echo("In poll ", data.fd.cint)
-      if EvError in info.events:
-        closeSocket(data.fd)
-        continue
+      # There may be EvError here, but we handle them in callbacks,
+      # so that exceptions can be raised from `send(...)` and
+      # `recv(...)` routines.
 
       if EvRead in info.events:
         # Callback may add items to ``data.readCBs`` which causes issues if
@@ -1013,9 +1013,17 @@ else:
     var retFuture = newFuture[void]("connect")
 
     proc cb(fd: AsyncFD): bool =
-      # We have connected.
-      retFuture.complete()
-      return true
+      var ret = SocketHandle(fd).getSockOptInt(cint(SOL_SOCKET), cint(SO_ERROR))
+      if ret == 0:
+          # We have connected.
+          retFuture.complete()
+          return true
+      elif ret == EINTR:
+          # interrupted, keep waiting
+          return false
+      else:
+          retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
+          return true
 
     assert getSockDomain(socket.SocketHandle) == domain
     var aiList = getAddrInfo(address, port, domain)
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index cfd3d7666..9139200f3 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -453,6 +453,7 @@ proc close*(socket: AsyncSocket) =
   when defined(ssl):
     if socket.isSSL:
       let res = SslShutdown(socket.sslHandle)
+      SSLFree(socket.sslHandle)
       if res == 0:
         discard
       elif res != 1:
@@ -567,4 +568,3 @@ when not defined(testing) and isMainModule:
     var f = accept(sock)
     f.callback = onAccept
   runForever()
-
diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim
index 49f049e46..173cd1e81 100644
--- a/lib/pure/marshal.nim
+++ b/lib/pure/marshal.nim
@@ -17,7 +17,7 @@
 ## .. code-block:: nim
 ##
 ##   type
-##     A = object
+##     A = object of RootObj
 ##     B = object of A
 ##       f: int
 ##
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index e4d3f7494..f1315a9fd 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -789,7 +789,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
     of "sat":
       info.weekday = dSat
     else:
-      raise newException(ValueError, "invalid day of week ")
+      raise newException(ValueError,
+        "Couldn't parse day of week (ddd), got: " & value[j..j+2])
     j += 3
   of "dddd":
     if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0:
@@ -814,7 +815,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
       info.weekday = dSat
       j += 8
     else:
-      raise newException(ValueError, "invalid day of week ")
+      raise newException(ValueError,
+        "Couldn't parse day of week (dddd), got: " & value)
   of "h", "H":
     var pd = parseInt(value[j..j+1], sv)
     info.hour = sv
@@ -865,7 +867,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
     of "dec":
       info.month =  mDec
     else:
-      raise newException(ValueError, "invalid month")
+      raise newException(ValueError,
+        "Couldn't parse month (MMM), got: " & value)
     j += 3
   of "MMMM":
     if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0:
@@ -905,7 +908,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
       info.month =  mDec
       j += 8
     else:
-      raise newException(ValueError, "invalid month")
+      raise newException(ValueError,
+        "Couldn't parse month (MMMM), got: " & value)
   of "s":
     var pd = parseInt(value[j..j+1], sv)
     info.second = sv
@@ -936,7 +940,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
     elif value[j] == '-':
       info.timezone = 0-parseInt($value[j+1])
     else:
-      raise newException(ValueError, "Sign for timezone " & value[j])
+      raise newException(ValueError,
+        "Couldn't parse timezone offset (z), got: " & value[j])
     j += 2
   of "zz":
     if value[j] == '+':
@@ -944,7 +949,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
     elif value[j] == '-':
       info.timezone = 0-value[j+1..j+2].parseInt()
     else:
-      raise newException(ValueError, "Sign for timezone " & value[j])
+      raise newException(ValueError,
+        "Couldn't parse timezone offset (zz), got: " & value[j])
     j += 3
   of "zzz":
     if value[j] == '+':
@@ -952,7 +958,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
     elif value[j] == '-':
       info.timezone = 0-value[j+1..j+2].parseInt()
     else:
-      raise newException(ValueError, "Sign for timezone " & value[j])
+      raise newException(ValueError,
+        "Couldn't parse timezone offset (zzz), got: " & value[j])
     j += 6
   of "ZZZ":
     info.tzname = value[j..j+2].toUpper()
@@ -1020,10 +1027,10 @@ proc parse*(value, layout: string): TimeInfo =
       # These are literals in both the layout and the value string
       if layout[i] == '\'':
         inc(i)
-        inc(j)
         while layout[i] != '\'' and layout.len-1 > i:
           inc(i)
           inc(j)
+        inc(i)
       else:
         inc(i)
         inc(j)
@@ -1112,6 +1119,8 @@ when isMainModule:
   s = "2006-01-12T15:04:05Z-07:00"
   f = "yyyy-MM-ddTHH:mm:ssZzzz"
   assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
+  f = "yyyy-MM-dd'T'HH:mm:ss'Z'zzz"
+  assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
   # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
   s = "2006-01-12T15:04:05.999999999Z-07:00"
   f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index de52cbbd1..c459023a9 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -9,7 +9,7 @@
 
 ## :Author: Zahary Karadjov
 ##
-## This module implements boilerplate to make testing easy.
+## This module implements boilerplate to make unit testing easy.
 ##
 ## Example:
 ##
@@ -41,27 +41,69 @@ when not defined(ECMAScript):
   import terminal
 
 type
-  TestStatus* = enum OK, FAILED
-  OutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
+  TestStatus* = enum OK, FAILED ## The status of a test when it is done.
+  OutputLevel* = enum  ## The output verbosity of the tests.
+    PRINT_ALL,         ## Print as much as possible.
+    PRINT_FAILURES,    ## Print only the failed tests.
+    PRINT_NONE         ## Print nothing.
 
 {.deprecated: [TTestStatus: TestStatus, TOutputLevel: OutputLevel]}
 
-var
-  abortOnError* {.threadvar.}: bool
-  outputLevel* {.threadvar.}: OutputLevel
-  colorOutput* {.threadvar.}: bool
+var ## Global unittest settings!
+
+  abortOnError* {.threadvar.}: bool ## Set to true in order to quit
+                                    ## immediately on fail. Default is false,
+                                    ## unless the ``NIMTEST_ABORT_ON_ERROR``
+                                    ## environment variable is set for
+                                    ## the non-js target.
+  outputLevel* {.threadvar.}: OutputLevel ## Set the verbosity of test results.
+                                          ## Default is ``PRINT_ALL``, unless
+                                          ## the ``NIMTEST_OUTPUT_LVL`` environment
+                                          ## variable is set for the non-js target.
+
+  colorOutput* {.threadvar.}: bool ## Have test results printed in color.
+                                   ## Default is true for the non-js target
+                                   ## unless, the environment variable
+                                   ## ``NIMTEST_NO_COLOR`` is set.
 
   checkpoints {.threadvar.}: seq[string]
 
 checkpoints = @[]
 
-template testSetupIMPL*: stmt {.immediate, dirty.} = discard
+template testSetupIMPL*: stmt {.immediate, dirty.} = discard      #Should this be public or even exist?
 template testTeardownIMPL*: stmt {.immediate, dirty.} = discard
 
 proc shouldRun(testName: string): bool =
   result = true
 
 template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} =
+  ## Declare a test suite identified by `name` with optional ``setup``
+  ## and/or ``teardown`` section.
+  ##
+  ## A test suite is a series of one or more related tests sharing a
+  ## common fixture (``setup``, ``teardown``). The fixture is executed
+  ## for EACH test.
+  ##
+  ## .. code-block:: nim
+  ##  suite "test suite for addition":
+  ##    setup:
+  ##      let result = 4
+  ##
+  ##    test "2 + 2 = 4":
+  ##      check(2+2 == result)
+  ##
+  ##    test "2 + -2 != 4":
+  ##      check(2+2 != result)
+  ##
+  ##    # No teardown needed
+  ##
+  ## The suite will run the individual test cases in the order in which
+  ## they were listed. With default global settings the above code prints:
+  ##
+  ## .. code-block::
+  ##
+  ##  [OK] 2 + 2 = 4
+  ##  [OK] (2 + -2) != 4
   block:
     template setup(setupBody: stmt): stmt {.immediate, dirty.} =
       template testSetupIMPL: stmt {.immediate, dirty.} = setupBody
@@ -87,6 +129,19 @@ proc testDone(name: string, s: TestStatus) =
       rawPrint()
 
 template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
+  ## Define a single test case identified by `name`.
+  ##
+  ## .. code-block:: nim
+  ##
+  ##  test "roses are red":
+  ##    let roses = "red"
+  ##    check(roses == "red")
+  ##
+  ## The above code outputs:
+  ##
+  ## .. code-block::
+  ##
+  ##  [OK] roses are red
   bind shouldRun, checkpoints, testDone
 
   if shouldRun(name):
@@ -108,10 +163,32 @@ template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
       testDone name, testStatusIMPL
 
 proc checkpoint*(msg: string) =
+  ## Set a checkpoint identified by `msg`. Upon test failure all
+  ## checkpoints encountered so far are printed out. Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##  checkpoint("Checkpoint A")
+  ##  check((42, "the Answer to life and everything") == (1, "a"))
+  ##  checkpoint("Checkpoint B")
+  ##
+  ## outputs "Checkpoint A" once it fails.
   checkpoints.add(msg)
   # TODO: add support for something like SCOPED_TRACE from Google Test
 
 template fail* =
+  ## Print out the checkpoints encountered so far and quit if ``abortOnError``
+  ## is true. Otherwise, erase the checkpoints and indicate the test has
+  ## failed (change exit code and test status). This template is useful
+  ## for debugging, but is otherwise mostly used internally. Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##  checkpoint("Checkpoint A")
+  ##  complicatedProcInThread()
+  ##  fail()
+  ##
+  ## outputs "Checkpoint A" before quitting.
   bind checkpoints
   for msg in items(checkpoints):
     echo msg
@@ -127,8 +204,23 @@ template fail* =
   checkpoints = @[]
 
 macro check*(conditions: stmt): stmt {.immediate.} =
+  ## Verify if a statement or a list of statements is true.
+  ## A helpful error message and set checkpoints are printed out on
+  ## failure (if ``outputLevel`` is not ``PRINT_NONE``).
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##  import strutils
+  ##
+  ##  check("AKB48".toLower() == "akb48")
+  ##
+  ##  let teams = {'A', 'K', 'B', '4', '8'}
+  ##
+  ##  check:
+  ##    "AKB48".toLower() == "akb48"
+  ##    'C' in teams
   let checked = callsite()[1]
-
   var
     argsAsgns = newNimNode(nnkStmtList)
     argsPrintOuts = newNimNode(nnkStmtList)
@@ -143,7 +235,7 @@ macro check*(conditions: stmt): stmt {.immediate.} =
       checkpoint(name & " was " & $value)
 
   proc inspectArgs(exp: NimNode) =
-    for i in 1 .. <exp.len:
+    for i in countup(1, exp.len - 1):
       if exp[i].kind notin nnkLiterals:
         inc counter
         var arg = newIdentNode(":p" & $counter)
@@ -194,11 +286,34 @@ macro check*(conditions: stmt): stmt {.immediate.} =
     result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit))
 
 template require*(conditions: stmt): stmt {.immediate, dirty.} =
+  ## Same as `check` except any failed test causes the program to quit
+  ## immediately. Any teardown statements are not executed and the failed
+  ## test output is not generated.
+  let savedAbortOnError = abortOnError
   block:
-    const AbortOnError {.inject.} = true
+    abortOnError = true
     check conditions
+  abortOnError = savedAbortOnError
 
 macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
+  ## Test if `body` raises an exception found in the passed `exceptions`.
+  ## The test passes if the raised exception is part of the acceptable
+  ## exceptions. Otherwise, it fails.
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##  import math
+  ##  proc defectiveRobot() =
+  ##    randomize()
+  ##    case random(1..4)
+  ##    of 1: raise newException(OSError, "CANNOT COMPUTE!")
+  ##    of 2: discard parseInt("Hello World!")
+  ##    of 3: raise newException(IOError, "I can't do that Dave.")
+  ##    else: assert 2 + 2 == 5
+  ##
+  ##  expect IOError, OSError, ValueError, AssertionError:
+  ##    defectiveRobot()
   let exp = callsite()
   template expectBody(errorTypes, lineInfoLit: expr,
                       body: stmt): NimNode {.dirty.} =
@@ -208,6 +323,9 @@ macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
       fail()
     except errorTypes:
       discard
+    except:
+      checkpoint(lineInfoLit & ": Expect Failed, unexpected exception was thrown.")
+      fail()
 
   var body = exp[exp.len - 1]
 
@@ -219,9 +337,9 @@ macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
 
 
 when declared(stdout):
-  ## Reading settings
+  # Reading settings
+  # On a terminal this branch is executed
   var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
-
   abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
   colorOutput  = not existsEnv("NIMTEST_NO_COLOR")
 
diff --git a/lib/system.nim b/lib/system.nim
index c5b0e0cc7..91495f31a 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -2173,7 +2173,11 @@ proc `$`*[T: tuple|object](x: T): string =
     if not firstElement: result.add(", ")
     result.add(name)
     result.add(": ")
-    result.add($value)
+    when compiles(value.isNil):
+      if value.isNil: result.add "nil"
+      else: result.add($value)
+    else:
+      result.add($value)
     firstElement = false
   result.add(")")