Compare commits

..

No commits in common. "main" and "0.4.2" have entirely different histories.
main ... 0.4.2

33 changed files with 124 additions and 245 deletions

View File

@ -7,7 +7,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
nim: [stable, 1.6.18]
nim: [stable, 1.6.14]
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
- uses: actions/checkout@v2

View File

@ -11,35 +11,22 @@ Use the [Nimble][2] package manager to add asynctest to an existing project.
Add the following to its .nimble file:
```nim
requires "asynctest >= 0.5.4 & < 0.6.0"
requires "asynctest >= 0.4.2 & < 0.5.0"
```
Usage
-----
Replace `import unittest` with one of the following imports, and you can await
Simply replace `import unittest` with `import asynctest`, and you can await
asynchronous calls in tests, setup and teardown.
When you use Nim's standard library only ([asyncdispatch][4] and [unittest][1]):
```nim
import asynctest/asyncdispatch/unittest
```
When you use [chronos][5] or [unittest2][3], pick the import that matches your
choices:
```nim
import asynctest/asyncdispatch/unittest2 # standard async and unittest2
import asynctest/chronos/unittest # chronos and standard unittest
import asynctest/chronos/unittest2 # chronos and unittest2
```
Example
-------
```nim
import asynctest/asyncdispatch/unittest
import asynctest
import asyncdispatch # alternatively: import chronos
proc someAsyncProc {.async.} =
# perform some async operations using await
@ -60,6 +47,17 @@ suite "test async proc":
```
setupAll and teardownAll
------------------------
The `setup` and `teardown` code runs before and after every test, just like the
standard [unittest][1] module. In addition we provide `setupAll` and
`teardownAll`. The `setupAll` code runs once before all tests in the suite, and
the `teardownAll` runs once after all tests in the suite. Use these only as a
last resort when setting up the test environment is very costly. Be careful that
the tests do not modify the environment that you set up, lest you introduce
dependencies between tests.
check eventually
----------------
@ -80,20 +78,12 @@ check eventually x == 42
await future
```
setupAll and teardownAll
------------------------
The `setup` and `teardown` code runs before and after every test, just like the
standard [unittest][1] module. In addition we provide `setupAll` and
`teardownAll`. The `setupAll` code runs once before all tests in the suite, and
the `teardownAll` runs once after all tests in the suite. Use these only as a
last resort when setting up the test environment is very costly. Be careful that
the tests do not modify the environment that you set up, lest you introduce
dependencies between tests.
Unittest2
---------
The [unittest2][3] package is supported. Make sure that you
`import asynctest/unittest2` instead of the normal import.
[1]: https://nim-lang.org/docs/unittest.html
[2]: https://github.com/nim-lang/nimble
[3]: https://github.com/status-im/nim-unittest2
[4]: https://nim-lang.org/docs/asyncdispatch.html
[5]: https://github.com/status-im/nim-chronos/

View File

@ -1,4 +1,3 @@
{.error:
"As of version 0.5.0 you need to import either " &
"asynctest/asyncdispatch/unittest or " &
"asynctest/chronos/unittest depending on your choice of async framework".}
import ./asynctest/unittest
export unittest

View File

@ -1,4 +1,4 @@
version = "0.5.4"
version = "0.4.2"
author = "asynctest Authors"
description = "Test asynchronous code"
license = "MIT"
@ -6,7 +6,7 @@ license = "MIT"
skipDirs = @["testmodules"]
task test, "Runs the test suite":
for module in ["stdlib", "chronosv3", "chronosv4", "unittest2"]:
for module in ["stdlib", "chronos", "unittest2"]:
withDir "testmodules/" & module:
delEnv "NIMBLE_DIR" # use nimbledeps dir
exec "nimble install -d -y"

View File

@ -1,10 +0,0 @@
import std/asyncdispatch
import std/unittest
import ../private/asyncdispatch/eventually
import ../private/asyncdispatch/runasync
export asyncdispatch
export unittest except suite, test
export eventually
include ../private/suite

View File

@ -1,10 +0,0 @@
import std/asyncdispatch
import pkg/unittest2
import ../private/asyncdispatch/eventually
import ../private/asyncdispatch/runasync
export asyncdispatch
export unittest2 except suite, test
export eventually
include ../private/suite

View File

@ -1,10 +0,0 @@
import pkg/chronos
import std/unittest
import ../private/chronos/eventually
import ../private/chronos/unittest/runasync
export chronos
export unittest except suite, test
export eventually
include ../private/suite

View File

@ -1,10 +0,0 @@
import pkg/chronos
import pkg/unittest2
import ../private/chronos/eventually
import ../private/chronos/unittest2/runasync
export chronos
export unittest2 except suite, test
export eventually
include ../private/suite

19
asynctest/eventually.nim Normal file
View File

@ -0,0 +1,19 @@
import std/times except milliseconds
template eventually*(expression: untyped, timeout=5000): bool =
template sleep(millis: int): auto =
when compiles(await sleepAsync(millis.milliseconds)):
sleepAsync(millis.milliseconds) # chronos
else:
sleepAsync(millis) # asyncdispatch
proc eventually: Future[bool] {.async.} =
let endTime = getTime() + initDuration(milliseconds=timeout)
while not expression:
if endTime < getTime():
return false
await sleep(10)
return true
await eventually()

View File

@ -1,14 +0,0 @@
import std/asyncdispatch
import std/times
template eventually*(expression: untyped, timeout=5000, pollInterval=1000): bool =
proc eventually: Future[bool] {.async.} =
let endTime = getTime() + initDuration(milliseconds=timeout)
while not expression:
if endTime < getTime():
return false
await sleepAsync(pollInterval)
return true
await eventually()

View File

@ -1,5 +0,0 @@
import std/asyncdispatch
template runAsync*(body): untyped =
let asyncProc = proc {.async.} = body
waitFor asyncProc()

View File

@ -1,24 +0,0 @@
import pkg/chronos
import pkg/chronos/config
template eventuallyProcSignature(body: untyped): untyped =
when compiles(config.chronosHandleException):
proc eventually: Future[bool] {.async: (handleException: true,
raises: [AsyncExceptionError, CancelledError]).} =
body
else:
proc eventually: Future[bool] {.async.} =
body
template eventually*(expression: untyped, timeout=5000, pollInterval=1000): bool =
bind Moment, now, milliseconds
eventuallyProcSignature:
let endTime = Moment.now() + timeout.milliseconds
while not expression:
if endTime < Moment.now():
return false
await sleepAsync(pollInterval.milliseconds)
return true
await eventually()

View File

@ -1,21 +0,0 @@
import pkg/chronos
import pkg/chronos/config
when compiles(config.chronosHandleException): # detect chronos v4
template runAsync*(body): untyped =
# The unittest module from stdlib can still raise a bare Exception,
# so we allow chronos to convert it to an AsyncExceptionError, and
# we disable the ensuing BareExcept warning.
{.push warning[BareExcept]:off.}
let asyncProc =
proc {.async: (handleException: true, raises: [AsyncExceptionError]).} =
body
{.pop.}
waitFor asyncProc()
else:
template runAsync*(body): untyped =
let asyncProc = proc {.async.} = body
waitFor asyncProc()

View File

@ -1,5 +0,0 @@
import pkg/chronos
template runAsync*(body): untyped =
let asyncProc = proc {.async.} = body
waitFor asyncProc()

View File

@ -1,34 +0,0 @@
template suite*(name, body) =
suite name:
let suiteproc = proc =
## Runs before all tests in the suite
template setupAll(setupAllBody) {.used.} =
runAsync setupAllBody
## Runs after all tests in the suite
template teardownAll(teardownAllBody) {.used.} =
template teardownAllIMPL: untyped {.inject.} =
runAsync teardownAllBody
template setup(setupBody) {.used.} =
setup:
runAsync setupBody
template teardown(teardownBody) {.used.} =
teardown:
let exception = getCurrentException()
runAsync teardownBody
setCurrentException(exception)
body
when declared(teardownAllIMPL):
teardownAllIMPL()
suiteproc()
template test*(name, body) =
test name:
runAsync body

39
asynctest/templates.nim Normal file
View File

@ -0,0 +1,39 @@
template suite*(name, body) =
suite name:
## Runs before all tests in the suite
template setupAll(setupAllBody) {.used.} =
let b = proc {.async.} = setupAllBody
waitFor b()
## Runs after all tests in the suite
template teardownAll(teardownAllBody) {.used.} =
template teardownAllIMPL: untyped {.inject.} =
let a = proc {.async.} = teardownAllBody
waitFor a()
template setup(setupBody) {.used.} =
setup:
let asyncproc = proc {.async.} = setupBody
waitFor asyncproc()
template teardown(teardownBody) {.used.} =
teardown:
let exception = getCurrentException()
let asyncproc = proc {.async.} = teardownBody
waitFor asyncproc()
setCurrentException(exception)
let suiteproc = proc = # Avoids GcUnsafe2 warnings with chronos
body
when declared(teardownAllIMPL):
teardownAllIMPL()
suiteproc()
template test*(name, body) =
test name:
let asyncproc = proc {.async.} = body
waitFor asyncproc()

7
asynctest/unittest.nim Normal file
View File

@ -0,0 +1,7 @@
import std/unittest
import ./eventually
export unittest except suite, test
export eventually
include ./templates

View File

@ -1,4 +1,7 @@
{.error:
"As of version 0.5.0 you need to import either " &
"asynctest/asyncdispatch/unittest2 or " &
"asynctest/chronos/unittest2 depending on your choice of async framework".}
import pkg/unittest2
import ./eventually
export unittest2 except suite, test
export eventually
include ./templates

4
config.nims Normal file
View File

@ -0,0 +1,4 @@
# begin Nimble config (version 1)
when fileExists("nimble.paths"):
include "nimble.paths"
# end Nimble config

4
nimble.lock Normal file
View File

@ -0,0 +1,4 @@
{
"version": 1,
"packages": {}
}

View File

@ -0,0 +1,5 @@
import pkg/asynctest
import pkg/chronos
include ../stdlib/testbody
include ../stdlib/testfail

View File

@ -3,7 +3,7 @@ author = "Asynctest Authors"
description = "Asynctest tests for std/unittest and pkg/chronos"
license = "MIT"
requires "chronos >= 3.2.0 & < 4.0.0"
requires "chronos"
task test, "Runs the test suite":
exec "nim c -f -r --skipParentCfg test.nim"

View File

@ -1,4 +0,0 @@
import pkg/asynctest/chronos/unittest
include ../common/testbody
include ../common/testfail

View File

@ -1,3 +0,0 @@
--path:"../.."
--hint:"XCannotRaiseY:off"
--define:"chronosPreviewV4" # TODO: remove once chronos v4 is released

View File

@ -1,18 +0,0 @@
import pkg/asynctest/chronos/unittest
include ../common/testbody
include ../common/testfail
import std/times
suite "eventually and std/times":
test "compiles when both chronos and std/times are imported":
check eventually true
test "compiles when the expression handed to eventually can raise Exception":
proc aProc(): bool {.raises: [Exception].} = raise newException(
Exception, "an exception")
check:
compiles:
discard eventually aProc()

View File

@ -1,9 +0,0 @@
version = "0.1.0"
author = "Asynctest Authors"
description = "Asynctest tests for std/unittest and pkg/chronos"
license = "MIT"
requires "chronos#head" # TODO: use "chronos >= 4.0.0 & < 5.0.0" once it's released
task test, "Runs the test suite":
exec "nim c -f -r --skipParentCfg test.nim"

View File

@ -1,4 +1,5 @@
import pkg/asynctest/asyncdispatch/unittest
import std/asyncdispatch
import pkg/asynctest
include ../common/testbody
include ../common/testfail
include ./testbody
include ./testfail

View File

@ -42,13 +42,13 @@ suite "eventually":
inc tries
tries == 3
check eventually(becomesTrue(), pollInterval=10)
check eventually becomesTrue()
test "becomes false after timeout":
proc remainsFalse: bool = false
check not eventually(remainsFalse(), timeout=100, pollInterval=10)
check not eventually(remainsFalse(), timeout=100)
test "becomes true during timeout":
@ -56,7 +56,7 @@ suite "eventually":
sleep(100)
true
check eventually(slowTrue(), timeout=50, pollInterval=10)
check eventually(slowTrue(), timeout=50)
test "works with async procedures":
@ -70,21 +70,5 @@ suite "eventually":
x = 42
let future = slowProcedure()
check eventually(x == 42, pollInterval=10)
check eventually x == 42
await future
test "respects poll interval":
var evaluations: int = 0
# If we try to access this from the closure, it will crash later with
# a segfault, so pass as var.
proc expensivePredicate(counter: var int): bool =
inc counter
return false
check not eventually(evaluations.expensivePredicate(), pollInterval=100, timeout=500)
check 1 <= evaluations and evaluations <= 6
evaluations = 0
check not eventually(evaluations.expensivePredicate(), pollInterval=10, timeout=500)
check 20 <= evaluations and evaluations <= 51

View File

@ -26,7 +26,7 @@ suite "reports unhandled exception when teardown handles exceptions too":
teardown:
try:
await someAsyncProc()
except CatchableError:
except:
discard
test "should fail, but not crash":

View File

@ -1,4 +1,5 @@
import pkg/asynctest/asyncdispatch/unittest2
import pkg/asynctest/unittest2
import pkg/chronos
include ../common/testbody
include ../common/testfail
include ../stdlib/testbody
include ../stdlib/testfail