Merge ../Nim into devel
This commit is contained in:
commit
970cfec6c2
15 changed files with 349 additions and 61 deletions
|
|
@ -159,6 +159,17 @@ proc genArgNoParam(p: BProc, n: PNode): Rope =
|
||||||
initLocExprSingleUse(p, n, a)
|
initLocExprSingleUse(p, n, a)
|
||||||
result = rdLoc(a)
|
result = rdLoc(a)
|
||||||
|
|
||||||
|
template genParamLoop(params) {.dirty.} =
|
||||||
|
if i < sonsLen(typ):
|
||||||
|
assert(typ.n.sons[i].kind == nkSym)
|
||||||
|
let paramType = typ.n.sons[i]
|
||||||
|
if not paramType.typ.isCompileTimeOnly:
|
||||||
|
if params != nil: add(params, ~", ")
|
||||||
|
add(params, genArg(p, ri.sons[i], paramType.sym, ri))
|
||||||
|
else:
|
||||||
|
if params != nil: add(params, ~", ")
|
||||||
|
add(params, genArgNoParam(p, ri.sons[i]))
|
||||||
|
|
||||||
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
var op: TLoc
|
var op: TLoc
|
||||||
# this is a hotspot in the compiler
|
# this is a hotspot in the compiler
|
||||||
|
|
@ -170,13 +181,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||||
var length = sonsLen(ri)
|
var length = sonsLen(ri)
|
||||||
for i in countup(1, length - 1):
|
for i in countup(1, length - 1):
|
||||||
if ri.sons[i].typ.isCompileTimeOnly: continue
|
genParamLoop(params)
|
||||||
if params != nil: add(params, ~", ")
|
|
||||||
if i < sonsLen(typ):
|
|
||||||
assert(typ.n.sons[i].kind == nkSym)
|
|
||||||
add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
|
|
||||||
else:
|
|
||||||
add(params, genArgNoParam(p, ri.sons[i]))
|
|
||||||
fixupCall(p, le, ri, d, op.r, params)
|
fixupCall(p, le, ri, d, op.r, params)
|
||||||
|
|
||||||
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
|
|
@ -198,13 +203,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
var length = sonsLen(ri)
|
var length = sonsLen(ri)
|
||||||
for i in countup(1, length - 1):
|
for i in countup(1, length - 1):
|
||||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||||
if ri.sons[i].typ.isCompileTimeOnly: continue
|
genParamLoop(pl)
|
||||||
if i < sonsLen(typ):
|
|
||||||
assert(typ.n.sons[i].kind == nkSym)
|
|
||||||
add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
|
|
||||||
else:
|
|
||||||
add(pl, genArgNoParam(p, ri.sons[i]))
|
|
||||||
if i < length - 1: add(pl, ~", ")
|
|
||||||
|
|
||||||
template genCallPattern {.dirty.} =
|
template genCallPattern {.dirty.} =
|
||||||
lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc])
|
lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc])
|
||||||
|
|
@ -241,13 +240,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||||
genCallPattern()
|
genCallPattern()
|
||||||
|
|
||||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
||||||
if ri.sons[i].typ.isCompileTimeOnly:
|
if i < sonsLen(typ):
|
||||||
result = nil
|
|
||||||
elif i < sonsLen(typ):
|
|
||||||
# 'var T' is 'T&' in C++. This means we ignore the request of
|
# 'var T' is 'T&' in C++. This means we ignore the request of
|
||||||
# any nkHiddenAddr when it's a 'var T'.
|
# any nkHiddenAddr when it's a 'var T'.
|
||||||
assert(typ.n.sons[i].kind == nkSym)
|
let paramType = typ.n.sons[i]
|
||||||
if typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
|
assert(paramType.kind == nkSym)
|
||||||
|
if paramType.typ.isCompileTimeOnly:
|
||||||
|
result = nil
|
||||||
|
elif typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
|
||||||
result = genArgNoParam(p, ri.sons[i][0])
|
result = genArgNoParam(p, ri.sons[i][0])
|
||||||
else:
|
else:
|
||||||
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
|
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
|
||||||
|
|
|
||||||
|
|
@ -950,7 +950,7 @@ proc genMainProc(m: BModule) =
|
||||||
gBreakpoints.add(m.genFilenames)
|
gBreakpoints.add(m.genFilenames)
|
||||||
|
|
||||||
let initStackBottomCall =
|
let initStackBottomCall =
|
||||||
if platform.targetOS == osStandalone: "".rope
|
if platform.targetOS == osStandalone or gSelectedGC == gcNone: "".rope
|
||||||
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
|
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
|
||||||
inc(m.labels)
|
inc(m.labels)
|
||||||
appcg(m, m.s[cfsProcs], PreMainBody, [
|
appcg(m, m.s[cfsProcs], PreMainBody, [
|
||||||
|
|
|
||||||
|
|
@ -1046,9 +1046,18 @@ proc genArg(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
proc genArgs(p: PProc, n: PNode, r: var TCompRes) =
|
proc genArgs(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
add(r.res, "(")
|
add(r.res, "(")
|
||||||
var hasArgs = false
|
var hasArgs = false
|
||||||
|
|
||||||
|
var typ = skipTypes(n.sons[0].typ, abstractInst)
|
||||||
|
assert(typ.kind == tyProc)
|
||||||
|
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||||
|
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
let it = n.sons[i]
|
let it = n.sons[i]
|
||||||
if it.typ.isCompileTimeOnly: continue
|
if i < sonsLen(typ):
|
||||||
|
assert(typ.n.sons[i].kind == nkSym)
|
||||||
|
let paramType = typ.n.sons[i]
|
||||||
|
if paramType.typ.isCompileTimeOnly: continue
|
||||||
|
|
||||||
if hasArgs: add(r.res, ", ")
|
if hasArgs: add(r.res, ", ")
|
||||||
genArg(p, it, r)
|
genArg(p, it, r)
|
||||||
hasArgs = true
|
hasArgs = true
|
||||||
|
|
|
||||||
|
|
@ -592,10 +592,8 @@ proc newNilLit*(): NimNode {.compileTime.} =
|
||||||
## New nil literal shortcut
|
## New nil literal shortcut
|
||||||
result = newNimNode(nnkNilLit)
|
result = newNimNode(nnkNilLit)
|
||||||
|
|
||||||
proc high*(node: NimNode): int {.compileTime.} = len(node) - 1
|
proc last*(node: NimNode): NimNode {.compileTime.} = node[<node.len]
|
||||||
## Return the highest index available for a node
|
## Return the last item in nodes children. Same as `node[^1]`
|
||||||
proc last*(node: NimNode): NimNode {.compileTime.} = node[node.high]
|
|
||||||
## Return the last item in nodes children. Same as `node[node.high()]`
|
|
||||||
|
|
||||||
|
|
||||||
const
|
const
|
||||||
|
|
@ -695,7 +693,7 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
|
||||||
of nnkBlockStmt, nnkWhileStmt:
|
of nnkBlockStmt, nnkWhileStmt:
|
||||||
someProc[1] = val
|
someProc[1] = val
|
||||||
of nnkForStmt:
|
of nnkForStmt:
|
||||||
someProc[high(someProc)] = val
|
someProc[len(someProc)-1] = val
|
||||||
else:
|
else:
|
||||||
badNodeKind someProc.kind, "body="
|
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
|
## Create a new ident node from a string
|
||||||
|
|
||||||
iterator children*(n: NimNode): NimNode {.inline.}=
|
iterator children*(n: NimNode): NimNode {.inline.}=
|
||||||
for i in 0 .. high(n):
|
for i in 0 ..< n.len:
|
||||||
yield n[i]
|
yield n[i]
|
||||||
|
|
||||||
template findChild*(n: NimNode; cond: expr): NimNode {.
|
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.} =
|
proc insert*(a: NimNode; pos: int; b: NimNode) {.compileTime.} =
|
||||||
## Insert node B into A at pos
|
## Insert node B into A at pos
|
||||||
if high(a) < pos:
|
if len(a)-1 < pos:
|
||||||
## add some empty nodes first
|
## 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 newEmptyNode()
|
||||||
a.add b
|
a.add b
|
||||||
else:
|
else:
|
||||||
## push the last item onto the list again
|
## push the last item onto the list again
|
||||||
## and shift each item down to pos up one
|
## and shift each item down to pos up one
|
||||||
a.add(a[a.high])
|
a.add(a[a.len-1])
|
||||||
for i in countdown(high(a) - 2, pos):
|
for i in countdown(len(a) - 3, pos):
|
||||||
a[i + 1] = a[i]
|
a[i + 1] = a[i]
|
||||||
a[pos] = b
|
a[pos] = b
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -972,9 +972,9 @@ else:
|
||||||
let data = PData(info.key.data)
|
let data = PData(info.key.data)
|
||||||
assert data.fd == info.key.fd.AsyncFD
|
assert data.fd == info.key.fd.AsyncFD
|
||||||
#echo("In poll ", data.fd.cint)
|
#echo("In poll ", data.fd.cint)
|
||||||
if EvError in info.events:
|
# There may be EvError here, but we handle them in callbacks,
|
||||||
closeSocket(data.fd)
|
# so that exceptions can be raised from `send(...)` and
|
||||||
continue
|
# `recv(...)` routines.
|
||||||
|
|
||||||
if EvRead in info.events:
|
if EvRead in info.events:
|
||||||
# Callback may add items to ``data.readCBs`` which causes issues if
|
# Callback may add items to ``data.readCBs`` which causes issues if
|
||||||
|
|
@ -1013,9 +1013,17 @@ else:
|
||||||
var retFuture = newFuture[void]("connect")
|
var retFuture = newFuture[void]("connect")
|
||||||
|
|
||||||
proc cb(fd: AsyncFD): bool =
|
proc cb(fd: AsyncFD): bool =
|
||||||
# We have connected.
|
var ret = SocketHandle(fd).getSockOptInt(cint(SOL_SOCKET), cint(SO_ERROR))
|
||||||
retFuture.complete()
|
if ret == 0:
|
||||||
return true
|
# 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
|
assert getSockDomain(socket.SocketHandle) == domain
|
||||||
var aiList = getAddrInfo(address, port, domain)
|
var aiList = getAddrInfo(address, port, domain)
|
||||||
|
|
|
||||||
|
|
@ -453,6 +453,7 @@ proc close*(socket: AsyncSocket) =
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
if socket.isSSL:
|
if socket.isSSL:
|
||||||
let res = SslShutdown(socket.sslHandle)
|
let res = SslShutdown(socket.sslHandle)
|
||||||
|
SSLFree(socket.sslHandle)
|
||||||
if res == 0:
|
if res == 0:
|
||||||
discard
|
discard
|
||||||
elif res != 1:
|
elif res != 1:
|
||||||
|
|
@ -567,4 +568,3 @@ when not defined(testing) and isMainModule:
|
||||||
var f = accept(sock)
|
var f = accept(sock)
|
||||||
f.callback = onAccept
|
f.callback = onAccept
|
||||||
runForever()
|
runForever()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
## .. code-block:: nim
|
## .. code-block:: nim
|
||||||
##
|
##
|
||||||
## type
|
## type
|
||||||
## A = object
|
## A = object of RootObj
|
||||||
## B = object of A
|
## B = object of A
|
||||||
## f: int
|
## f: int
|
||||||
##
|
##
|
||||||
|
|
|
||||||
|
|
@ -789,7 +789,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
|
||||||
of "sat":
|
of "sat":
|
||||||
info.weekday = dSat
|
info.weekday = dSat
|
||||||
else:
|
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
|
j += 3
|
||||||
of "dddd":
|
of "dddd":
|
||||||
if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0:
|
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
|
info.weekday = dSat
|
||||||
j += 8
|
j += 8
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "invalid day of week ")
|
raise newException(ValueError,
|
||||||
|
"Couldn't parse day of week (dddd), got: " & value)
|
||||||
of "h", "H":
|
of "h", "H":
|
||||||
var pd = parseInt(value[j..j+1], sv)
|
var pd = parseInt(value[j..j+1], sv)
|
||||||
info.hour = sv
|
info.hour = sv
|
||||||
|
|
@ -865,7 +867,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
|
||||||
of "dec":
|
of "dec":
|
||||||
info.month = mDec
|
info.month = mDec
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "invalid month")
|
raise newException(ValueError,
|
||||||
|
"Couldn't parse month (MMM), got: " & value)
|
||||||
j += 3
|
j += 3
|
||||||
of "MMMM":
|
of "MMMM":
|
||||||
if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0:
|
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
|
info.month = mDec
|
||||||
j += 8
|
j += 8
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "invalid month")
|
raise newException(ValueError,
|
||||||
|
"Couldn't parse month (MMMM), got: " & value)
|
||||||
of "s":
|
of "s":
|
||||||
var pd = parseInt(value[j..j+1], sv)
|
var pd = parseInt(value[j..j+1], sv)
|
||||||
info.second = sv
|
info.second = sv
|
||||||
|
|
@ -936,7 +940,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
|
||||||
elif value[j] == '-':
|
elif value[j] == '-':
|
||||||
info.timezone = 0-parseInt($value[j+1])
|
info.timezone = 0-parseInt($value[j+1])
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "Sign for timezone " & value[j])
|
raise newException(ValueError,
|
||||||
|
"Couldn't parse timezone offset (z), got: " & value[j])
|
||||||
j += 2
|
j += 2
|
||||||
of "zz":
|
of "zz":
|
||||||
if value[j] == '+':
|
if value[j] == '+':
|
||||||
|
|
@ -944,7 +949,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
|
||||||
elif value[j] == '-':
|
elif value[j] == '-':
|
||||||
info.timezone = 0-value[j+1..j+2].parseInt()
|
info.timezone = 0-value[j+1..j+2].parseInt()
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "Sign for timezone " & value[j])
|
raise newException(ValueError,
|
||||||
|
"Couldn't parse timezone offset (zz), got: " & value[j])
|
||||||
j += 3
|
j += 3
|
||||||
of "zzz":
|
of "zzz":
|
||||||
if value[j] == '+':
|
if value[j] == '+':
|
||||||
|
|
@ -952,7 +958,8 @@ proc parseToken(info: var TimeInfo; token, value: string; j: var int) =
|
||||||
elif value[j] == '-':
|
elif value[j] == '-':
|
||||||
info.timezone = 0-value[j+1..j+2].parseInt()
|
info.timezone = 0-value[j+1..j+2].parseInt()
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "Sign for timezone " & value[j])
|
raise newException(ValueError,
|
||||||
|
"Couldn't parse timezone offset (zzz), got: " & value[j])
|
||||||
j += 6
|
j += 6
|
||||||
of "ZZZ":
|
of "ZZZ":
|
||||||
info.tzname = value[j..j+2].toUpper()
|
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
|
# These are literals in both the layout and the value string
|
||||||
if layout[i] == '\'':
|
if layout[i] == '\'':
|
||||||
inc(i)
|
inc(i)
|
||||||
inc(j)
|
|
||||||
while layout[i] != '\'' and layout.len-1 > i:
|
while layout[i] != '\'' and layout.len-1 > i:
|
||||||
inc(i)
|
inc(i)
|
||||||
inc(j)
|
inc(j)
|
||||||
|
inc(i)
|
||||||
else:
|
else:
|
||||||
inc(i)
|
inc(i)
|
||||||
inc(j)
|
inc(j)
|
||||||
|
|
@ -1112,6 +1119,8 @@ when isMainModule:
|
||||||
s = "2006-01-12T15:04:05Z-07:00"
|
s = "2006-01-12T15:04:05Z-07:00"
|
||||||
f = "yyyy-MM-ddTHH:mm:ssZzzz"
|
f = "yyyy-MM-ddTHH:mm:ssZzzz"
|
||||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
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"
|
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
|
||||||
s = "2006-01-12T15:04:05.999999999Z-07:00"
|
s = "2006-01-12T15:04:05.999999999Z-07:00"
|
||||||
f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
|
f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
## :Author: Zahary Karadjov
|
## :Author: Zahary Karadjov
|
||||||
##
|
##
|
||||||
## This module implements boilerplate to make testing easy.
|
## This module implements boilerplate to make unit testing easy.
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
|
|
@ -41,27 +41,69 @@ when not defined(ECMAScript):
|
||||||
import terminal
|
import terminal
|
||||||
|
|
||||||
type
|
type
|
||||||
TestStatus* = enum OK, FAILED
|
TestStatus* = enum OK, FAILED ## The status of a test when it is done.
|
||||||
OutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
|
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]}
|
{.deprecated: [TTestStatus: TestStatus, TOutputLevel: OutputLevel]}
|
||||||
|
|
||||||
var
|
var ## Global unittest settings!
|
||||||
abortOnError* {.threadvar.}: bool
|
|
||||||
outputLevel* {.threadvar.}: OutputLevel
|
abortOnError* {.threadvar.}: bool ## Set to true in order to quit
|
||||||
colorOutput* {.threadvar.}: bool
|
## 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 {.threadvar.}: seq[string]
|
||||||
|
|
||||||
checkpoints = @[]
|
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
|
template testTeardownIMPL*: stmt {.immediate, dirty.} = discard
|
||||||
|
|
||||||
proc shouldRun(testName: string): bool =
|
proc shouldRun(testName: string): bool =
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} =
|
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:
|
block:
|
||||||
template setup(setupBody: stmt): stmt {.immediate, dirty.} =
|
template setup(setupBody: stmt): stmt {.immediate, dirty.} =
|
||||||
template testSetupIMPL: stmt {.immediate, dirty.} = setupBody
|
template testSetupIMPL: stmt {.immediate, dirty.} = setupBody
|
||||||
|
|
@ -87,6 +129,19 @@ proc testDone(name: string, s: TestStatus) =
|
||||||
rawPrint()
|
rawPrint()
|
||||||
|
|
||||||
template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
|
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
|
bind shouldRun, checkpoints, testDone
|
||||||
|
|
||||||
if shouldRun(name):
|
if shouldRun(name):
|
||||||
|
|
@ -108,10 +163,32 @@ template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
|
||||||
testDone name, testStatusIMPL
|
testDone name, testStatusIMPL
|
||||||
|
|
||||||
proc checkpoint*(msg: string) =
|
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)
|
checkpoints.add(msg)
|
||||||
# TODO: add support for something like SCOPED_TRACE from Google Test
|
# TODO: add support for something like SCOPED_TRACE from Google Test
|
||||||
|
|
||||||
template fail* =
|
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
|
bind checkpoints
|
||||||
for msg in items(checkpoints):
|
for msg in items(checkpoints):
|
||||||
echo msg
|
echo msg
|
||||||
|
|
@ -127,8 +204,23 @@ template fail* =
|
||||||
checkpoints = @[]
|
checkpoints = @[]
|
||||||
|
|
||||||
macro check*(conditions: stmt): stmt {.immediate.} =
|
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]
|
let checked = callsite()[1]
|
||||||
|
|
||||||
var
|
var
|
||||||
argsAsgns = newNimNode(nnkStmtList)
|
argsAsgns = newNimNode(nnkStmtList)
|
||||||
argsPrintOuts = newNimNode(nnkStmtList)
|
argsPrintOuts = newNimNode(nnkStmtList)
|
||||||
|
|
@ -143,7 +235,7 @@ macro check*(conditions: stmt): stmt {.immediate.} =
|
||||||
checkpoint(name & " was " & $value)
|
checkpoint(name & " was " & $value)
|
||||||
|
|
||||||
proc inspectArgs(exp: NimNode) =
|
proc inspectArgs(exp: NimNode) =
|
||||||
for i in 1 .. <exp.len:
|
for i in countup(1, exp.len - 1):
|
||||||
if exp[i].kind notin nnkLiterals:
|
if exp[i].kind notin nnkLiterals:
|
||||||
inc counter
|
inc counter
|
||||||
var arg = newIdentNode(":p" & $counter)
|
var arg = newIdentNode(":p" & $counter)
|
||||||
|
|
@ -194,11 +286,34 @@ macro check*(conditions: stmt): stmt {.immediate.} =
|
||||||
result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit))
|
result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit))
|
||||||
|
|
||||||
template require*(conditions: stmt): stmt {.immediate, dirty.} =
|
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:
|
block:
|
||||||
const AbortOnError {.inject.} = true
|
abortOnError = true
|
||||||
check conditions
|
check conditions
|
||||||
|
abortOnError = savedAbortOnError
|
||||||
|
|
||||||
macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
|
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()
|
let exp = callsite()
|
||||||
template expectBody(errorTypes, lineInfoLit: expr,
|
template expectBody(errorTypes, lineInfoLit: expr,
|
||||||
body: stmt): NimNode {.dirty.} =
|
body: stmt): NimNode {.dirty.} =
|
||||||
|
|
@ -208,6 +323,9 @@ macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
|
||||||
fail()
|
fail()
|
||||||
except errorTypes:
|
except errorTypes:
|
||||||
discard
|
discard
|
||||||
|
except:
|
||||||
|
checkpoint(lineInfoLit & ": Expect Failed, unexpected exception was thrown.")
|
||||||
|
fail()
|
||||||
|
|
||||||
var body = exp[exp.len - 1]
|
var body = exp[exp.len - 1]
|
||||||
|
|
||||||
|
|
@ -219,9 +337,9 @@ macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
|
||||||
|
|
||||||
|
|
||||||
when declared(stdout):
|
when declared(stdout):
|
||||||
## Reading settings
|
# Reading settings
|
||||||
|
# On a terminal this branch is executed
|
||||||
var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
|
var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
|
||||||
|
|
||||||
abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
||||||
colorOutput = not existsEnv("NIMTEST_NO_COLOR")
|
colorOutput = not existsEnv("NIMTEST_NO_COLOR")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2173,7 +2173,11 @@ proc `$`*[T: tuple|object](x: T): string =
|
||||||
if not firstElement: result.add(", ")
|
if not firstElement: result.add(", ")
|
||||||
result.add(name)
|
result.add(name)
|
||||||
result.add(": ")
|
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
|
firstElement = false
|
||||||
result.add(")")
|
result.add(")")
|
||||||
|
|
||||||
|
|
|
||||||
33
tests/async/tasyncconnect.nim
Normal file
33
tests/async/tasyncconnect.nim
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
discard """
|
||||||
|
file: "tasyncconnect.nim"
|
||||||
|
exitcode: 1
|
||||||
|
outputsub: "Error: unhandled exception: Connection refused [Exception]"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import
|
||||||
|
asyncdispatch,
|
||||||
|
posix
|
||||||
|
|
||||||
|
|
||||||
|
const
|
||||||
|
testHost = "127.0.0.1"
|
||||||
|
testPort = Port(17357)
|
||||||
|
|
||||||
|
|
||||||
|
when defined(windows) or defined(nimdoc):
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
proc testAsyncConnect() {.async.} =
|
||||||
|
var s = newAsyncRawSocket()
|
||||||
|
|
||||||
|
await s.connect(testHost, testPort)
|
||||||
|
|
||||||
|
var peerAddr: SockAddr
|
||||||
|
var addrSize = Socklen(sizeof(peerAddr))
|
||||||
|
var ret = SocketHandle(s).getpeername(addr(peerAddr), addr(addrSize))
|
||||||
|
|
||||||
|
if ret < 0:
|
||||||
|
echo("`connect(...)` failed but no exception was raised.")
|
||||||
|
quit(2)
|
||||||
|
|
||||||
|
waitFor(testAsyncConnect())
|
||||||
65
tests/async/tasynceverror.nim
Normal file
65
tests/async/tasynceverror.nim
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
discard """
|
||||||
|
file: "tasynceverror.nim"
|
||||||
|
exitcode: 1
|
||||||
|
outputsub: "Error: unhandled exception: Connection reset by peer [Exception]"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import
|
||||||
|
asyncdispatch,
|
||||||
|
asyncnet,
|
||||||
|
rawsockets,
|
||||||
|
os
|
||||||
|
|
||||||
|
|
||||||
|
const
|
||||||
|
testHost = "127.0.0.1"
|
||||||
|
testPort = Port(17357)
|
||||||
|
|
||||||
|
|
||||||
|
when defined(windows) or defined(nimdoc):
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
proc createListenSocket(host: string, port: Port): TAsyncFD =
|
||||||
|
result = newAsyncRawSocket()
|
||||||
|
|
||||||
|
SocketHandle(result).setSockOptInt(SOL_SOCKET, SO_REUSEADDR, 1)
|
||||||
|
|
||||||
|
var aiList = getAddrInfo(host, port, AF_INET)
|
||||||
|
if SocketHandle(result).bindAddr(aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
|
||||||
|
dealloc(aiList)
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
dealloc(aiList)
|
||||||
|
|
||||||
|
if SocketHandle(result).listen(1) < 0'i32:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
|
|
||||||
|
proc testAsyncSend() {.async.} =
|
||||||
|
var
|
||||||
|
ls = createListenSocket(testHost, testPort)
|
||||||
|
s = newAsyncSocket()
|
||||||
|
|
||||||
|
await s.connect(testHost, testPort)
|
||||||
|
|
||||||
|
var ps = await ls.accept()
|
||||||
|
SocketHandle(ls).close()
|
||||||
|
|
||||||
|
await ps.send("test 1", flags={})
|
||||||
|
s.close()
|
||||||
|
# This send should raise EPIPE
|
||||||
|
await ps.send("test 2", flags={})
|
||||||
|
SocketHandle(ps).close()
|
||||||
|
|
||||||
|
|
||||||
|
# The bug was, when the poll function handled EvError for us,
|
||||||
|
# our callbacks may never get executed, thus making the event
|
||||||
|
# loop block indefinitely. This is a timer to keep everything
|
||||||
|
# rolling. 400 ms is an arbitrary value, should be enough though.
|
||||||
|
proc timer() {.async.} =
|
||||||
|
await sleepAsync(400)
|
||||||
|
echo("Timer expired.")
|
||||||
|
quit(2)
|
||||||
|
|
||||||
|
|
||||||
|
asyncCheck(testAsyncSend())
|
||||||
|
waitFor(timer())
|
||||||
28
tests/metatype/tstatic_ones.nim
Normal file
28
tests/metatype/tstatic_ones.nim
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
discard """
|
||||||
|
output: "@[2, 2, 2, 2, 2]"
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bug #3144
|
||||||
|
|
||||||
|
type IntArray[N: static[int]] = array[N, int]
|
||||||
|
|
||||||
|
proc `$`(a: IntArray): string = $(@(a))
|
||||||
|
|
||||||
|
proc `+=`[N: static[int]](a: var IntArray[N], b: IntArray[N]) =
|
||||||
|
for i in 0 .. < N:
|
||||||
|
a[i] += b[i]
|
||||||
|
|
||||||
|
proc zeros(N: static[int]): IntArray[N] =
|
||||||
|
for i in 0 .. < N:
|
||||||
|
result[i] = 0
|
||||||
|
|
||||||
|
proc ones(N: static[int]): IntArray[N] =
|
||||||
|
for i in 0 .. < N:
|
||||||
|
result[i] = 1
|
||||||
|
|
||||||
|
proc sum[N: static[int]](vs: seq[IntArray[N]]): IntArray[N] =
|
||||||
|
result = zeros(N)
|
||||||
|
for v in vs:
|
||||||
|
result += v
|
||||||
|
|
||||||
|
echo sum(@[ones(5), ones(5)])
|
||||||
|
|
@ -19,3 +19,17 @@ import options
|
||||||
test "unittest typedescs":
|
test "unittest typedescs":
|
||||||
check(none(int) == none(int))
|
check(none(int) == none(int))
|
||||||
check(none(int) != some(1))
|
check(none(int) != some(1))
|
||||||
|
|
||||||
|
|
||||||
|
import math
|
||||||
|
from strutils import parseInt
|
||||||
|
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
|
||||||
|
test "unittest expect":
|
||||||
|
expect IOError, OSError, ValueError, AssertionError:
|
||||||
|
defectiveRobot()
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ News
|
||||||
- ``sequtils.delete`` doesn't take confusing default arguments anymore.
|
- ``sequtils.delete`` doesn't take confusing default arguments anymore.
|
||||||
- ``system.free`` was an error-prone alias to ``system.dealloc`` and has
|
- ``system.free`` was an error-prone alias to ``system.dealloc`` and has
|
||||||
been removed.
|
been removed.
|
||||||
|
- ``macros.high`` never worked and the manual says ``high`` cannot be
|
||||||
|
overloaded, so we removed it with no deprecation cycle.
|
||||||
|
|
||||||
|
|
||||||
Library additions
|
Library additions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue