summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
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/httpclient.nim2
-rw-r--r--lib/pure/marshal.nim2
-rw-r--r--lib/pure/unittest.nim144
-rw-r--r--lib/system.nim19
-rw-r--r--lib/system/platforms.nim6
-rw-r--r--lib/system/repr.nim5
-rw-r--r--lib/system/sets.nim6
10 files changed, 184 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/httpclient.nim b/lib/pure/httpclient.nim
index e6b8088c5..98687359b 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -402,7 +402,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
 
   headers.add(" HTTP/1.1\c\L")
 
-  add(headers, "Host: " & r.hostname & "\c\L")
+  add(headers, "Host: " & parseUri(url).hostname & "\c\L")
   if userAgent != "":
     add(headers, "User-Agent: " & userAgent & "\c\L")
   if proxy != nil and proxy.auth != "":
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/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..dd1a0721a 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -176,10 +176,16 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
   ## creates a new object of type ``T`` and returns a safe (traced)
   ## reference to it in ``a``.
 
-proc new*(T: typedesc): ref T =
+proc new*(T: typedesc): auto =
   ## creates a new object of type ``T`` and returns a safe (traced)
   ## reference to it as result value
-  new(result)
+  when (T is ref):
+      var r: T
+  else:
+      var r: ref T
+  new(r)
+  return r
+
 
 proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
   ## leaked implementation detail. Do not use.
@@ -1150,7 +1156,8 @@ const
 
   hostCPU* {.magic: "HostCPU".}: string = ""
     ## a string that describes the host CPU. Possible values:
-    ## "i386", "alpha", "powerpc", "powerpc64", "sparc", "amd64", "mips", "arm".
+    ## "i386", "alpha", "powerpc", "powerpc64", "powerpc64el", "sparc",
+    ## "amd64", "mips", "mipsel", "arm", "arm64".
 
   seqShallowFlag = low(int)
 
@@ -2173,7 +2180,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(")")
 
diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim
index 47a01d5fe..0a7045fae 100644
--- a/lib/system/platforms.nim
+++ b/lib/system/platforms.nim
@@ -18,11 +18,14 @@ type
     alpha,                     ## Alpha processor
     powerpc,                   ## 32 bit PowerPC
     powerpc64,                 ## 64 bit PowerPC
+    powerpc64el,               ## Little Endian 64 bit PowerPC
     sparc,                     ## Sparc based processor
     ia64,                      ## Intel Itanium
     amd64,                     ## x86_64 (AMD64); 64 bit x86 compatible CPU
     mips,                      ## Mips based processor
+    mipsel,                    ## Little Endian Mips based processor
     arm,                       ## ARM based processor
+    arm64,                     ## ARM64 based processor
     vm,                        ## Some Virtual machine: Nim's VM or JavaScript
     avr                        ## AVR based processor
 
@@ -63,11 +66,14 @@ const
                elif defined(alpha): CpuPlatform.alpha
                elif defined(powerpc): CpuPlatform.powerpc
                elif defined(powerpc64): CpuPlatform.powerpc64
+               elif defined(powerpc64el): CpuPlatform.powerpc64el
                elif defined(sparc): CpuPlatform.sparc
                elif defined(ia64): CpuPlatform.ia64
                elif defined(amd64): CpuPlatform.amd64
                elif defined(mips): CpuPlatform.mips
+               elif defined(mipsel): CpuPlatform.mipsel
                elif defined(arm): CpuPlatform.arm
+               elif defined(arm64): CpuPlatform.arm64
                elif defined(vm): CpuPlatform.vm
                elif defined(avr): CpuPlatform.avr
                else: CpuPlatform.none
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index 74396f424..b4188527f 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -256,7 +256,10 @@ when not defined(useNimRtl):
     of tyBool: add result, reprBool(cast[ptr bool](p)[])
     of tyChar: add result, reprChar(cast[ptr char](p)[])
     of tyString: reprStrAux(result, cast[ptr string](p)[])
-    of tyCString: reprStrAux(result, $(cast[ptr cstring](p)[]))
+    of tyCString:
+      let cs = cast[ptr cstring](p)[]
+      if cs.isNil: add result, "nil"
+      else: reprStrAux(result, $cs)
     of tyRange: reprAux(result, p, typ.base, cl)
     of tyProc, tyPointer:
       if cast[PPointer](p)[] == nil: add result, "nil"
diff --git a/lib/system/sets.nim b/lib/system/sets.nim
index 22d6d57c0..66877de30 100644
--- a/lib/system/sets.nim
+++ b/lib/system/sets.nim
@@ -19,9 +19,9 @@ proc countBits32(n: int32): int {.compilerproc.} =
   v = (v and 0x33333333'i32) +% ((v shr 2'i32) and 0x33333333'i32)
   result = ((v +% (v shr 4'i32) and 0xF0F0F0F'i32) *% 0x1010101'i32) shr 24'i32
 
-proc countBits64(n: int64): int {.compilerproc.} = 
-  result = countBits32(toU32(n and 0xffff'i64)) +
-           countBits32(toU32(n shr 16'i64))
+proc countBits64(n: int64): int {.compilerproc.} =
+  result = countBits32(toU32(n and 0xffffffff'i64)) +
+           countBits32(toU32(n shr 32'i64))
 
 proc cardSet(s: NimSet, len: int): int {.compilerproc.} =
   result = 0