diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/core/macros.nim | 18 | ||||
-rw-r--r-- | lib/pure/asyncdispatch.nim | 20 | ||||
-rw-r--r-- | lib/pure/asyncnet.nim | 2 | ||||
-rw-r--r-- | lib/pure/httpclient.nim | 2 | ||||
-rw-r--r-- | lib/pure/marshal.nim | 2 | ||||
-rw-r--r-- | lib/pure/unittest.nim | 144 | ||||
-rw-r--r-- | lib/system.nim | 19 | ||||
-rw-r--r-- | lib/system/platforms.nim | 6 | ||||
-rw-r--r-- | lib/system/repr.nim | 5 | ||||
-rw-r--r-- | lib/system/sets.nim | 6 |
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 |