Compare commits

..

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

39 changed files with 121 additions and 334 deletions

View File

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

1
.gitignore vendored
View File

@ -3,4 +3,3 @@
!*.* !*.*
nimble.develop nimble.develop
nimble.paths nimble.paths
nimbledeps

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: Add the following to its .nimble file:
```nim ```nim
requires "asynctest >= 0.5.4 & < 0.6.0" requires "asynctest >= 0.3.2 & < 0.4.0"
``` ```
Usage 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. 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 Example
------- -------
```nim ```nim
import asynctest/asyncdispatch/unittest import asynctest
import asyncdispatch # alternatively: import chronos
proc someAsyncProc {.async.} = proc someAsyncProc {.async.} =
# perform some async operations using await # perform some async operations using await
@ -60,26 +47,6 @@ suite "test async proc":
``` ```
check eventually
----------------
When you find yourself adding calls to `sleepAsync` to your tests, you might
want to consider using `check eventually` instead. It will repeatedly check
an expression until it becomes true. It has a built-in timeout of 5 seconds that
you can override.
```nim
var x: int
proc slowProcedure {.async.} =
# perform a slow operation
x = 42
let future = slowProcedure()
check eventually x == 42
await future
```
setupAll and teardownAll setupAll and teardownAll
------------------------ ------------------------
@ -91,9 +58,12 @@ 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 the tests do not modify the environment that you set up, lest you introduce
dependencies between tests. 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 [1]: https://nim-lang.org/docs/unittest.html
[2]: https://github.com/nim-lang/nimble [2]: https://github.com/nim-lang/nimble
[3]: https://github.com/status-im/nim-unittest2 [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: import ./asynctest/unittest
"As of version 0.5.0 you need to import either " &
"asynctest/asyncdispatch/unittest or " & export unittest
"asynctest/chronos/unittest depending on your choice of async framework".}

View File

@ -1,13 +1,12 @@
version = "0.5.4" version = "0.3.2"
author = "asynctest Authors" author = "asynctest Authors"
description = "Test asynchronous code" description = "Test asynchronous code"
license = "MIT" license = "MIT"
skipDirs = @["testmodules"] requires "nim >= 1.2.0 & < 2.0.0"
task test, "Runs the test suite": task test, "Runs the test suite":
for module in ["stdlib", "chronosv3", "chronosv4", "unittest2"]: for module in ["stdlib", "chronos", "unittest2"]:
withDir "testmodules/" & module: withDir "testmodules/" & module:
delEnv "NIMBLE_DIR" # use nimbledeps dir
exec "nimble install -d -y" exec "nimble install -d -y"
exec "nimble test -y" exec "nimble test -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

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()

4
asynctest/unittest.nim Normal file
View File

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

View File

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

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

View File

@ -1,2 +0,0 @@
--path:"../.."
--hint:"XCannotRaiseY:off"

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,90 +0,0 @@
proc someAsyncProc {.async.} =
# perform some async operations using await
discard
suite "test async proc":
setup:
# invoke await in the test setup:
await someAsyncProc()
teardown:
# invoke await in the test teardown:
await someAsyncProc()
test "async test":
# invoke await in tests:
await someAsyncProc()
suite "setupAll and teardownAll":
setupAll:
# invoke await in the test setup:
await someAsyncProc()
teardownAll:
# invoke await in the test teardown:
await someAsyncProc()
test "async test":
# invoke await in tests:
await someAsyncProc()
from std/os import sleep
suite "eventually":
test "becomes true":
var tries = 0
proc becomesTrue: bool =
inc tries
tries == 3
check eventually(becomesTrue(), pollInterval=10)
test "becomes false after timeout":
proc remainsFalse: bool = false
check not eventually(remainsFalse(), timeout=100, pollInterval=10)
test "becomes true during timeout":
proc slowTrue: bool =
sleep(100)
true
check eventually(slowTrue(), timeout=50, pollInterval=10)
test "works with async procedures":
var x: int
proc slowProcedure {.async.} =
when compiles(await sleepAsync(100.milliseconds)):
await sleepAsync(100.milliseconds) # chronos
else:
await sleepAsync(100) # asyncdispatch
x = 42
let future = slowProcedure()
check eventually(x == 42, pollInterval=10)
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

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

View File

@ -4,4 +4,4 @@ description = "Asynctest tests for std/unittest and std/asyncdispatch"
license = "MIT" license = "MIT"
task test, "Runs the test suite": task test, "Runs the test suite":
exec "nim c -f -r --skipParentCfg test.nim" exec "nim c -f -r test.nim"

View File

@ -0,0 +1,31 @@
proc someAsyncProc {.async.} =
# perform some async operations using await
discard
suite "test async proc":
setup:
# invoke await in the test setup:
await someAsyncProc()
teardown:
# invoke await in the test teardown:
await someAsyncProc()
test "async test":
# invoke await in tests:
await someAsyncProc()
suite "test async setupAll and teardownAll, allow multiple suites":
setupAll:
# invoke await in the test setup:
await someAsyncProc()
teardownAll:
# invoke await in the test teardown:
await someAsyncProc()
test "async test":
# invoke await in tests:
await someAsyncProc()

View File

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

View File

@ -1,2 +1 @@
--path:"../.." --path:"../.."
--hint:"XCannotRaiseY:off"

View File

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

View File

@ -7,4 +7,4 @@ requires "unittest2"
requires "chronos" requires "chronos"
task test, "Runs the test suite": task test, "Runs the test suite":
exec "nim c -f -r --skipParentCfg test.nim" exec "nim c -f -r test.nim"