Merge branch 'master' of github.com:Araq/Nimrod
This commit is contained in:
commit
4a435a8fb4
6 changed files with 299 additions and 233 deletions
|
|
@ -370,23 +370,22 @@ proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
of tyVar:
|
of tyVar:
|
||||||
if a.kind == f.kind: result = typeRel(c, base(f), base(a))
|
if a.kind == f.kind: result = typeRel(c, base(f), base(a))
|
||||||
else: result = typeRel(c, base(f), a)
|
else: result = typeRel(c, base(f), a)
|
||||||
of tyArray, tyArrayConstr:
|
of tyArray, tyArrayConstr:
|
||||||
# tyArrayConstr cannot happen really, but
|
# tyArrayConstr cannot happen really, but
|
||||||
# we wanna be safe here
|
# we wanna be safe here
|
||||||
case a.kind
|
case a.kind
|
||||||
of tyArray:
|
of tyArray, tyArrayConstr:
|
||||||
result = minRel(typeRel(c, f.sons[0], a.sons[0]),
|
var fRange = f.sons[0]
|
||||||
typeRel(c, f.sons[1], a.sons[1]))
|
if fRange.kind == tyGenericParam:
|
||||||
if result < isGeneric: result = isNone
|
var prev = PType(idTableGet(c.bindings, fRange))
|
||||||
of tyArrayConstr:
|
if prev == nil:
|
||||||
|
put(c.bindings, fRange, a.sons[0])
|
||||||
|
fRange = a
|
||||||
|
else:
|
||||||
|
fRange = prev
|
||||||
result = typeRel(c, f.sons[1], a.sons[1])
|
result = typeRel(c, f.sons[1], a.sons[1])
|
||||||
if result < isGeneric:
|
if result < isGeneric: result = isNone
|
||||||
result = isNone
|
elif lengthOrd(fRange) != lengthOrd(a): result = isNone
|
||||||
else:
|
|
||||||
if (result != isGeneric) and (lengthOrd(f) != lengthOrd(a)):
|
|
||||||
result = isNone
|
|
||||||
elif f.sons[0].kind in GenericTypes:
|
|
||||||
result = minRel(result, typeRel(c, f.sons[0], a.sons[0]))
|
|
||||||
else: nil
|
else: nil
|
||||||
of tyOpenArray, tyVarargs:
|
of tyOpenArray, tyVarargs:
|
||||||
case a.Kind
|
case a.Kind
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,8 @@ type
|
||||||
|
|
||||||
handleAccept*: proc (s: PAsyncSocket) {.closure.}
|
handleAccept*: proc (s: PAsyncSocket) {.closure.}
|
||||||
|
|
||||||
|
handleTask*: proc (s: PAsyncSocket) {.closure.}
|
||||||
|
|
||||||
lineBuffer: TaintedString ## Temporary storage for ``recvLine``
|
lineBuffer: TaintedString ## Temporary storage for ``recvLine``
|
||||||
sslNeedAccept: bool
|
sslNeedAccept: bool
|
||||||
proto: TProtocol
|
proto: TProtocol
|
||||||
|
|
@ -145,6 +147,7 @@ proc newAsyncSocket(): PAsyncSocket =
|
||||||
result.handleRead = (proc (s: PAsyncSocket) = nil)
|
result.handleRead = (proc (s: PAsyncSocket) = nil)
|
||||||
result.handleConnect = (proc (s: PAsyncSocket) = nil)
|
result.handleConnect = (proc (s: PAsyncSocket) = nil)
|
||||||
result.handleAccept = (proc (s: PAsyncSocket) = nil)
|
result.handleAccept = (proc (s: PAsyncSocket) = nil)
|
||||||
|
result.handleTask = (proc (s: PAsyncSocket) = nil)
|
||||||
|
|
||||||
result.lineBuffer = "".TaintedString
|
result.lineBuffer = "".TaintedString
|
||||||
|
|
||||||
|
|
@ -196,6 +199,13 @@ when defined(ssl):
|
||||||
# handshake will set socket's ``sslNoHandshake`` field.
|
# handshake will set socket's ``sslNoHandshake`` field.
|
||||||
discard PAsyncSocket(h).socket.handshake()
|
discard PAsyncSocket(h).socket.handshake()
|
||||||
|
|
||||||
|
|
||||||
|
proc asyncSockTask(h: PObject) =
|
||||||
|
when defined(ssl):
|
||||||
|
h.asyncSockDoHandshake()
|
||||||
|
|
||||||
|
PAsyncSocket(h).handleTask(PAsyncSocket(h))
|
||||||
|
|
||||||
proc toDelegate(sock: PAsyncSocket): PDelegate =
|
proc toDelegate(sock: PAsyncSocket): PDelegate =
|
||||||
result = newDelegate()
|
result = newDelegate()
|
||||||
result.deleVal = sock
|
result.deleVal = sock
|
||||||
|
|
@ -204,6 +214,7 @@ proc toDelegate(sock: PAsyncSocket): PDelegate =
|
||||||
result.mode = fmReadWrite
|
result.mode = fmReadWrite
|
||||||
result.handleRead = asyncSockHandleRead
|
result.handleRead = asyncSockHandleRead
|
||||||
result.handleWrite = asyncSockHandleWrite
|
result.handleWrite = asyncSockHandleWrite
|
||||||
|
result.task = asyncSockTask
|
||||||
# TODO: Errors?
|
# TODO: Errors?
|
||||||
#result.handleError = (proc (h: PObject) = assert(false))
|
#result.handleError = (proc (h: PObject) = assert(false))
|
||||||
|
|
||||||
|
|
@ -215,10 +226,7 @@ proc toDelegate(sock: PAsyncSocket): PDelegate =
|
||||||
if sock.info notin {SockIdle, SockClosed}:
|
if sock.info notin {SockIdle, SockClosed}:
|
||||||
sock.deleg.open = true
|
sock.deleg.open = true
|
||||||
else:
|
else:
|
||||||
sock.deleg.open = false
|
sock.deleg.open = false
|
||||||
|
|
||||||
when defined(ssl):
|
|
||||||
result.task = asyncSockDoHandshake
|
|
||||||
|
|
||||||
proc connect*(sock: PAsyncSocket, name: string, port = TPort(0),
|
proc connect*(sock: PAsyncSocket, name: string, port = TPort(0),
|
||||||
af: TDomain = AF_INET) =
|
af: TDomain = AF_INET) =
|
||||||
|
|
@ -257,6 +265,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket,
|
||||||
##
|
##
|
||||||
## **Note**: ``client`` needs to be initialised.
|
## **Note**: ``client`` needs to be initialised.
|
||||||
assert(client != nil)
|
assert(client != nil)
|
||||||
|
client = newAsyncSocket()
|
||||||
var c: TSocket
|
var c: TSocket
|
||||||
new(c)
|
new(c)
|
||||||
when defined(ssl):
|
when defined(ssl):
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,8 @@ type
|
||||||
|
|
||||||
PAsyncIRC* = ref TAsyncIRC
|
PAsyncIRC* = ref TAsyncIRC
|
||||||
TAsyncIRC* = object of TIRC
|
TAsyncIRC* = object of TIRC
|
||||||
userArg: PObject
|
handleEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent) {.closure.}
|
||||||
handleEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent,
|
asyncSock: PAsyncSocket
|
||||||
userArg: PObject) {.nimcall.}
|
|
||||||
lineBuffer: TaintedString
|
|
||||||
|
|
||||||
TIRCMType* = enum
|
TIRCMType* = enum
|
||||||
MUnknown,
|
MUnknown,
|
||||||
|
|
@ -320,12 +318,16 @@ proc connect*(irc: PAsyncIRC) =
|
||||||
assert(irc.address != "")
|
assert(irc.address != "")
|
||||||
assert(irc.port != TPort(0))
|
assert(irc.port != TPort(0))
|
||||||
|
|
||||||
irc.sock = socket()
|
irc.asyncSock = AsyncSocket()
|
||||||
irc.sock.setBlocking(false)
|
irc.asyncSock.connect(irc.address, irc.port)
|
||||||
irc.sock.connectAsync(irc.address, irc.port)
|
|
||||||
irc.status = SockConnecting
|
|
||||||
|
|
||||||
proc handleConnect(h: PObject) =
|
proc handleConnect(s: PAsyncSocket, irc: PAsyncIRC) =
|
||||||
|
# Greet the server :)
|
||||||
|
if irc.serverPass != "": irc[].send("PASS " & irc.serverPass, true)
|
||||||
|
irc[].send("NICK " & irc.nick, true)
|
||||||
|
irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
|
||||||
|
|
||||||
|
discard """proc handleConnect(h: PObject) =
|
||||||
var irc = PAsyncIRC(h)
|
var irc = PAsyncIRC(h)
|
||||||
|
|
||||||
# Greet the server :)
|
# Greet the server :)
|
||||||
|
|
@ -334,8 +336,22 @@ proc handleConnect(h: PObject) =
|
||||||
irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
|
irc[].send("USER $1 * 0 :$2" % [irc.user, irc.realname], true)
|
||||||
|
|
||||||
irc.status = SockConnected
|
irc.status = SockConnected
|
||||||
|
"""
|
||||||
|
|
||||||
proc handleRead(h: PObject) =
|
proc handleRead(s: PAsyncSocket, irc: PAsyncIRC) =
|
||||||
|
var line = "".TaintedString
|
||||||
|
var ret = s.recvLine(line)
|
||||||
|
if ret:
|
||||||
|
if line == "":
|
||||||
|
var ev: TIRCEvent
|
||||||
|
irc[].close()
|
||||||
|
ev.typ = EvDisconnected
|
||||||
|
irc.handleEvent(irc[], ev)
|
||||||
|
else:
|
||||||
|
var ev = irc[].processLine(line.string)
|
||||||
|
irc.handleEvent(irc[], ev)
|
||||||
|
|
||||||
|
discard """proc handleRead(h: PObject) =
|
||||||
var irc = PAsyncIRC(h)
|
var irc = PAsyncIRC(h)
|
||||||
var line = "".TaintedString
|
var line = "".TaintedString
|
||||||
var ret = irc.sock.recvLineAsync(line)
|
var ret = irc.sock.recvLineAsync(line)
|
||||||
|
|
@ -352,13 +368,18 @@ proc handleRead(h: PObject) =
|
||||||
irc[].close()
|
irc[].close()
|
||||||
ev.typ = EvDisconnected
|
ev.typ = EvDisconnected
|
||||||
irc.handleEvent(irc[], ev, irc.userArg)
|
irc.handleEvent(irc[], ev, irc.userArg)
|
||||||
of RecvFail: nil
|
of RecvFail: nil"""
|
||||||
|
|
||||||
proc handleTask(h: PObject) =
|
proc handleTask(s: PAsyncSocket, irc: PAsyncIRC) =
|
||||||
|
var ev: TIRCEvent
|
||||||
|
if irc[].processOther(ev):
|
||||||
|
irc.handleEvent(irc[], ev)
|
||||||
|
|
||||||
|
discard """proc handleTask(h: PObject) =
|
||||||
var irc = PAsyncIRC(h)
|
var irc = PAsyncIRC(h)
|
||||||
var ev: TIRCEvent
|
var ev: TIRCEvent
|
||||||
if PAsyncIRC(h)[].processOther(ev):
|
if PAsyncIRC(h)[].processOther(ev):
|
||||||
irc.handleEvent(irc[], ev, irc.userArg)
|
irc.handleEvent(irc[], ev, irc.userArg)"""
|
||||||
|
|
||||||
proc asyncIRC*(address: string, port: TPort = 6667.TPort,
|
proc asyncIRC*(address: string, port: TPort = 6667.TPort,
|
||||||
nick = "NimrodBot",
|
nick = "NimrodBot",
|
||||||
|
|
@ -366,9 +387,8 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort,
|
||||||
realname = "NimrodBot", serverPass = "",
|
realname = "NimrodBot", serverPass = "",
|
||||||
joinChans: seq[string] = @[],
|
joinChans: seq[string] = @[],
|
||||||
msgLimit: bool = true,
|
msgLimit: bool = true,
|
||||||
ircEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent,
|
ircEvent: proc (irc: var TAsyncIRC, ev: TIRCEvent) {.closure.}
|
||||||
userArg: PObject) {.nimcall.},
|
): PAsyncIRC =
|
||||||
userArg: PObject = nil): PAsyncIRC =
|
|
||||||
## Use this function if you want to use asyncio's dispatcher.
|
## Use this function if you want to use asyncio's dispatcher.
|
||||||
##
|
##
|
||||||
## **Note:** Do **NOT** use this if you're writing a simple IRC bot which only
|
## **Note:** Do **NOT** use this if you're writing a simple IRC bot which only
|
||||||
|
|
@ -389,28 +409,25 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort,
|
||||||
result.msgLimit = msgLimit
|
result.msgLimit = msgLimit
|
||||||
result.messageBuffer = @[]
|
result.messageBuffer = @[]
|
||||||
result.handleEvent = ircEvent
|
result.handleEvent = ircEvent
|
||||||
result.userArg = userArg
|
|
||||||
result.lineBuffer = ""
|
|
||||||
|
|
||||||
proc register*(d: PDispatcher, irc: PAsyncIRC) =
|
proc register*(d: PDispatcher, irc: PAsyncIRC) =
|
||||||
## Registers ``irc`` with dispatcher ``d``.
|
## Registers ``irc`` with dispatcher ``d``.
|
||||||
var dele = newDelegate()
|
irc.asyncSock.handleConnect =
|
||||||
dele.deleVal = irc
|
proc (s: PAsyncSocket) =
|
||||||
dele.getSocket = (proc (h: PObject): tuple[info: TInfo, sock: TSocket] =
|
handleConnect(s, irc)
|
||||||
if PAsyncIRC(h).status == SockConnecting or
|
irc.asyncSock.handleRead =
|
||||||
PAsyncIRC(h).status == SockConnected:
|
proc (s: PAsyncSocket) =
|
||||||
return (PAsyncIRC(h).status, PAsyncIRC(h).sock)
|
handleRead(s, irc)
|
||||||
else: return (SockIdle, PAsyncIRC(h).sock))
|
irc.asyncSock.handleTask =
|
||||||
dele.handleConnect = handleConnect
|
proc (s: PAsyncSocket) =
|
||||||
dele.handleRead = handleRead
|
handleTask(s, irc)
|
||||||
dele.task = handleTask
|
d.register(irc.asyncSock)
|
||||||
d.register(dele)
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
#var m = parseMessage("ERROR :Closing Link: dom96.co.cc (Ping timeout: 252 seconds)")
|
#var m = parseMessage("ERROR :Closing Link: dom96.co.cc (Ping timeout: 252 seconds)")
|
||||||
#echo(repr(m))
|
#echo(repr(m))
|
||||||
|
|
||||||
#discard """
|
|
||||||
|
|
||||||
var client = irc("amber.tenthbit.net", nick="TestBot1234",
|
var client = irc("amber.tenthbit.net", nick="TestBot1234",
|
||||||
joinChans = @["#flood"])
|
joinChans = @["#flood"])
|
||||||
|
|
@ -431,5 +448,5 @@ when isMainModule:
|
||||||
|
|
||||||
#echo( repr(event) )
|
#echo( repr(event) )
|
||||||
#echo("Lag: ", formatFloat(client.getLag()))
|
#echo("Lag: ", formatFloat(client.getLag()))
|
||||||
#"""
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,187 +1,215 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2012 Nimrod Contributors
|
# (c) Copyright 2012 Nimrod Contributors
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## :Author: Zahary Karadjov (zah@github)
|
## :Author: Zahary Karadjov
|
||||||
##
|
##
|
||||||
## This module implements the standard unit testing facilities such as
|
## This module implements the standard unit testing facilities such as
|
||||||
## suites, fixtures and test cases as well as facilities for combinatorial
|
## suites, fixtures and test cases as well as facilities for combinatorial
|
||||||
## and randomzied test case generation (not yet available)
|
## and randomzied test case generation (not yet available)
|
||||||
## and object mocking (not yet available)
|
## and object mocking (not yet available)
|
||||||
##
|
##
|
||||||
## It is loosely based on C++'s boost.test and Haskell's QuickTest
|
## It is loosely based on C++'s boost.test and Haskell's QuickTest
|
||||||
|
|
||||||
import
|
import
|
||||||
macros, terminal, os
|
macros, terminal, os
|
||||||
|
|
||||||
type
|
type
|
||||||
TTestStatus* = enum OK, FAILED
|
TTestStatus* = enum OK, FAILED
|
||||||
TOutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
|
TOutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
|
||||||
|
|
||||||
var
|
var
|
||||||
# XXX: These better be thread-local
|
# XXX: These better be thread-local
|
||||||
AbortOnError*: bool
|
AbortOnError*: bool
|
||||||
OutputLevel*: TOutputLevel
|
OutputLevel*: TOutputLevel
|
||||||
ColorOutput*: bool
|
ColorOutput*: bool
|
||||||
|
|
||||||
checkpoints: seq[string] = @[]
|
|
||||||
|
|
||||||
template TestSetupIMPL*: stmt {.immediate, dirty.} = nil
|
|
||||||
template TestTeardownIMPL*: stmt {.immediate, dirty.} = nil
|
|
||||||
|
|
||||||
proc shouldRun(testName: string): bool =
|
|
||||||
result = true
|
|
||||||
|
|
||||||
template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} =
|
|
||||||
block:
|
|
||||||
template setup*(setupBody: stmt): stmt {.immediate, dirty.} =
|
|
||||||
template TestSetupIMPL: stmt {.immediate, dirty.} = setupBody
|
|
||||||
|
|
||||||
template teardown*(teardownBody: stmt): stmt {.immediate, dirty.} =
|
|
||||||
template TestTeardownIMPL: stmt {.immediate, dirty.} = teardownBody
|
|
||||||
|
|
||||||
body
|
|
||||||
|
|
||||||
proc testDone(name: string, s: TTestStatus) =
|
|
||||||
if s == FAILED:
|
|
||||||
program_result += 1
|
|
||||||
|
|
||||||
if OutputLevel != PRINT_NONE and (OutputLevel == PRINT_ALL or s == FAILED):
|
|
||||||
var color = (if s == OK: fgGreen else: fgRed)
|
|
||||||
|
|
||||||
if ColorOutput:
|
|
||||||
styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n"
|
|
||||||
else:
|
|
||||||
echo "[", $s, "] ", name, "\n"
|
|
||||||
|
|
||||||
template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
|
|
||||||
bind shouldRun, checkpoints, testDone
|
|
||||||
|
|
||||||
if shouldRun(name):
|
|
||||||
checkpoints = @[]
|
|
||||||
var TestStatusIMPL {.inject.} = OK
|
|
||||||
|
|
||||||
try:
|
|
||||||
TestSetupIMPL()
|
|
||||||
body
|
|
||||||
|
|
||||||
except:
|
|
||||||
checkpoint("Unhandled exception: " & getCurrentExceptionMsg())
|
|
||||||
fail()
|
|
||||||
|
|
||||||
finally:
|
|
||||||
TestTeardownIMPL()
|
|
||||||
testDone name, TestStatusIMPL
|
|
||||||
|
|
||||||
proc checkpoint*(msg: string) =
|
|
||||||
checkpoints.add(msg)
|
|
||||||
# TODO: add support for something like SCOPED_TRACE from Google Test
|
|
||||||
|
|
||||||
template fail* =
|
|
||||||
bind checkpoints
|
|
||||||
for msg in items(checkpoints):
|
|
||||||
echo msg
|
|
||||||
|
|
||||||
if AbortOnError: quit(1)
|
|
||||||
|
|
||||||
TestStatusIMPL = FAILED
|
|
||||||
checkpoints = @[]
|
|
||||||
|
|
||||||
macro check*(conditions: stmt): stmt {.immediate.} =
|
|
||||||
let conditions = callsite()
|
|
||||||
|
|
||||||
proc standardRewrite(e: PNimrodNode): PNimrodNode =
|
checkpoints: seq[string] = @[]
|
||||||
template rewrite(Exp, lineInfoLit: expr, expLit: string): stmt =
|
|
||||||
if not Exp:
|
template TestSetupIMPL*: stmt {.immediate, dirty.} = nil
|
||||||
checkpoint(lineInfoLit & ": Check failed: " & expLit)
|
template TestTeardownIMPL*: stmt {.immediate, dirty.} = nil
|
||||||
fail()
|
|
||||||
|
proc shouldRun(testName: string): bool =
|
||||||
result = getAst(rewrite(e, e.lineinfo, e.toStrLit))
|
result = true
|
||||||
|
|
||||||
case conditions.kind
|
template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} =
|
||||||
of nnkCall, nnkCommand, nnkMacroStmt:
|
block:
|
||||||
case conditions[1].kind
|
template setup*(setupBody: stmt): stmt {.immediate, dirty.} =
|
||||||
of nnkInfix:
|
template TestSetupIMPL: stmt {.immediate, dirty.} = setupBody
|
||||||
proc rewriteBinaryOp(op: PNimrodNode): PNimrodNode =
|
|
||||||
template rewrite(op, left, right, lineInfoLit: expr, opLit,
|
template teardown*(teardownBody: stmt): stmt {.immediate, dirty.} =
|
||||||
leftLit, rightLit: string, printLhs, printRhs: bool): stmt =
|
template TestTeardownIMPL: stmt {.immediate, dirty.} = teardownBody
|
||||||
block:
|
|
||||||
var
|
body
|
||||||
lhs = left
|
|
||||||
rhs = right
|
proc testDone(name: string, s: TTestStatus) =
|
||||||
|
if s == FAILED:
|
||||||
if not `op`(lhs, rhs):
|
program_result += 1
|
||||||
checkpoint(lineInfoLit & ": Check failed: " & opLit)
|
|
||||||
when printLhs: checkpoint(" " & leftLit & " was " & $lhs)
|
if OutputLevel != PRINT_NONE and (OutputLevel == PRINT_ALL or s == FAILED):
|
||||||
when printRhs: checkpoint(" " & rightLit & " was " & $rhs)
|
var color = (if s == OK: fgGreen else: fgRed)
|
||||||
fail()
|
|
||||||
|
if ColorOutput:
|
||||||
result = getAst(rewrite(
|
styledEcho styleBright, color, "[", $s, "] ", fgWhite, name, "\n"
|
||||||
op[0], op[1], op[2],
|
else:
|
||||||
op.lineinfo,
|
echo "[", $s, "] ", name, "\n"
|
||||||
op.toStrLit,
|
|
||||||
op[1].toStrLit,
|
template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
|
||||||
op[2].toStrLit,
|
bind shouldRun, checkpoints, testDone
|
||||||
op[1].kind notin nnkLiterals,
|
|
||||||
op[2].kind notin nnkLiterals))
|
if shouldRun(name):
|
||||||
|
checkpoints = @[]
|
||||||
result = rewriteBinaryOp(conditions[1])
|
var TestStatusIMPL {.inject.} = OK
|
||||||
|
|
||||||
of nnkCall, nnkCommand:
|
try:
|
||||||
# TODO: We can print out the call arguments in case of failure
|
TestSetupIMPL()
|
||||||
result = standardRewrite(conditions[1])
|
body
|
||||||
|
|
||||||
of nnkStmtList:
|
except:
|
||||||
result = newNimNode(nnkStmtList)
|
checkpoint("Unhandled exception: " & getCurrentExceptionMsg())
|
||||||
for i in countup(0, conditions[1].len - 1):
|
fail()
|
||||||
result.add(newCall(!"check", conditions[1][i]))
|
|
||||||
|
finally:
|
||||||
else:
|
TestTeardownIMPL()
|
||||||
result = standardRewrite(conditions[1])
|
testDone name, TestStatusIMPL
|
||||||
|
|
||||||
else:
|
proc checkpoint*(msg: string) =
|
||||||
var ast = conditions.treeRepr
|
checkpoints.add(msg)
|
||||||
error conditions.lineinfo & ": Malformed check statement:\n" & ast
|
# TODO: add support for something like SCOPED_TRACE from Google Test
|
||||||
|
|
||||||
template require*(conditions: stmt): stmt {.immediate, dirty.} =
|
template fail* =
|
||||||
block:
|
bind checkpoints
|
||||||
const AbortOnError {.inject.} = true
|
for msg in items(checkpoints):
|
||||||
check conditions
|
echo msg
|
||||||
|
|
||||||
macro expect*(exp: stmt): stmt {.immediate.} =
|
if AbortOnError: quit(1)
|
||||||
|
|
||||||
|
TestStatusIMPL = FAILED
|
||||||
|
checkpoints = @[]
|
||||||
|
|
||||||
|
macro check*(conditions: stmt): stmt {.immediate.} =
|
||||||
|
let conditions = callsite()
|
||||||
|
|
||||||
|
case conditions.kind
|
||||||
|
of nnkCall, nnkCommand, nnkMacroStmt:
|
||||||
|
case conditions[1].kind
|
||||||
|
of nnkInfix:
|
||||||
|
proc rewriteBinaryOp(op: PNimrodNode): PNimrodNode =
|
||||||
|
template rewrite(op, left, right, lineInfoLit: expr, opLit,
|
||||||
|
leftLit, rightLit: string, printLhs, printRhs: bool): stmt =
|
||||||
|
block:
|
||||||
|
var
|
||||||
|
lhs = left
|
||||||
|
rhs = right
|
||||||
|
|
||||||
|
if not `op`(lhs, rhs):
|
||||||
|
checkpoint(lineInfoLit & ": Check failed: " & opLit)
|
||||||
|
when printLhs: checkpoint(" " & leftLit & " was " & $lhs)
|
||||||
|
when printRhs: checkpoint(" " & rightLit & " was " & $rhs)
|
||||||
|
fail()
|
||||||
|
|
||||||
|
result = getAst(rewrite(
|
||||||
|
op[0], op[1], op[2],
|
||||||
|
op.lineinfo,
|
||||||
|
op.toStrLit,
|
||||||
|
op[1].toStrLit,
|
||||||
|
op[2].toStrLit,
|
||||||
|
op[1].kind notin nnkLiterals,
|
||||||
|
op[2].kind notin nnkLiterals))
|
||||||
|
|
||||||
|
result = rewriteBinaryOp(conditions[1])
|
||||||
|
|
||||||
|
of nnkCall, nnkCommand:
|
||||||
|
proc rewriteCall(op: PNimrodNode): PNimrodNode =
|
||||||
|
template rewrite(call, lineInfoLit: expr, expLit: string,
|
||||||
|
argAssgs, argPrintOuts: stmt): stmt =
|
||||||
|
block:
|
||||||
|
argAssgs
|
||||||
|
if not call:
|
||||||
|
checkpoint(lineInfoLit & ": Check failed: " & expLit)
|
||||||
|
argPrintOuts
|
||||||
|
fail()
|
||||||
|
|
||||||
|
template asgn(a, value: expr): stmt =
|
||||||
|
let a = value
|
||||||
|
|
||||||
|
template print(name, value: expr): stmt =
|
||||||
|
checkpoint(name & " was " & $value)
|
||||||
|
|
||||||
|
var
|
||||||
|
argsAsgns = newNimNode(nnkStmtList)
|
||||||
|
argsPrintOuts = newNimNode(nnkStmtList)
|
||||||
|
opStr = op.toStrLit
|
||||||
|
|
||||||
|
for i in 1 .. <op.len:
|
||||||
|
if op[i].kind notin nnkLiterals:
|
||||||
|
# TODO: print only types that are printable
|
||||||
|
var arg = newIdentNode(":param" & ($i))
|
||||||
|
argsAsgns.add getAst(asgn(arg, op[i]))
|
||||||
|
argsPrintOuts.add getAst(print(op[i].toStrLit, arg))
|
||||||
|
op[i] = arg
|
||||||
|
|
||||||
|
result = getAst(rewrite(op, op.lineinfo, opStr, argsAsgns, argsPrintOuts))
|
||||||
|
|
||||||
|
result = rewriteCall(conditions[1])
|
||||||
|
|
||||||
|
of nnkStmtList:
|
||||||
|
result = newNimNode(nnkStmtList)
|
||||||
|
for i in countup(0, conditions[1].len - 1):
|
||||||
|
result.add(newCall(!"check", conditions[1][i]))
|
||||||
|
|
||||||
|
else:
|
||||||
|
template rewrite(Exp, lineInfoLit: expr, expLit: string): stmt =
|
||||||
|
if not Exp:
|
||||||
|
checkpoint(lineInfoLit & ": Check failed: " & expLit)
|
||||||
|
fail()
|
||||||
|
|
||||||
|
let e = conditions[1]
|
||||||
|
result = getAst(rewrite(e, e.lineinfo, e.toStrLit))
|
||||||
|
|
||||||
|
else:
|
||||||
|
var ast = conditions.treeRepr
|
||||||
|
error conditions.lineinfo & ": Malformed check statement:\n" & ast
|
||||||
|
|
||||||
|
template require*(conditions: stmt): stmt {.immediate, dirty.} =
|
||||||
|
block:
|
||||||
|
const AbortOnError {.inject.} = true
|
||||||
|
check conditions
|
||||||
|
|
||||||
|
macro expect*(exp: stmt): stmt {.immediate.} =
|
||||||
let exp = callsite()
|
let exp = callsite()
|
||||||
template expectBody(errorTypes, lineInfoLit: expr,
|
template expectBody(errorTypes, lineInfoLit: expr,
|
||||||
body: stmt): PNimrodNode {.dirty.} =
|
body: stmt): PNimrodNode {.dirty.} =
|
||||||
try:
|
try:
|
||||||
body
|
body
|
||||||
checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")
|
checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")
|
||||||
fail()
|
fail()
|
||||||
except errorTypes:
|
except errorTypes:
|
||||||
nil
|
nil
|
||||||
|
|
||||||
var expectCall = exp[0]
|
var expectCall = exp[0]
|
||||||
var body = exp[1]
|
var body = exp[1]
|
||||||
|
|
||||||
var errorTypes = newNimNode(nnkBracket)
|
var errorTypes = newNimNode(nnkBracket)
|
||||||
for i in countup(1, expectCall.len - 1):
|
for i in countup(1, expectCall.len - 1):
|
||||||
errorTypes.add(expectCall[i])
|
errorTypes.add(expectCall[i])
|
||||||
|
|
||||||
result = getAst(expectBody(errorTypes, exp.lineinfo, body))
|
result = getAst(expectBody(errorTypes, exp.lineinfo, body))
|
||||||
|
|
||||||
|
|
||||||
## Reading settings
|
## Reading settings
|
||||||
var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
|
var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
|
||||||
|
|
||||||
if envOutLvl.len > 0:
|
if envOutLvl.len > 0:
|
||||||
for opt in countup(low(TOutputLevel), high(TOutputLevel)):
|
for opt in countup(low(TOutputLevel), high(TOutputLevel)):
|
||||||
if $opt == envOutLvl:
|
if $opt == envOutLvl:
|
||||||
OutputLevel = opt
|
OutputLevel = opt
|
||||||
break
|
break
|
||||||
|
|
||||||
AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
AbortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
|
||||||
ColorOutput = not existsEnv("NIMTEST_NO_COLOR")
|
ColorOutput = not existsEnv("NIMTEST_NO_COLOR")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
|
var s = @[]
|
||||||
|
|
||||||
proc `*` *(a, b: seq[int]): seq[int] =
|
proc `*` *(a, b: seq[int]): seq[int] =
|
||||||
# allocate a new sequence:
|
# allocate a new sequence:
|
||||||
|
|
|
||||||
13
tests/reject/tarrayplus.nim
Normal file
13
tests/reject/tarrayplus.nim
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
discard """
|
||||||
|
msg: "type mismatch: got (array[0..2, float], array[0..1, float])"
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc `+`*[R, T] (v1, v2: array[R, T]): array[R, T] =
|
||||||
|
for i in low(v1)..high(v1):
|
||||||
|
result[i] = v1[i] + v2[i]
|
||||||
|
|
||||||
|
var
|
||||||
|
v1: array[0..2, float] = [3.0, 1.2, 3.0]
|
||||||
|
v2: array[0..1, float] = [2.0, 1.0]
|
||||||
|
v3 = v1 + v2
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue