Add `eventually` to wait for a condition to become true

This commit is contained in:
Mark Spanbroek 2022-05-19 14:35:09 +02:00 committed by markspanbroek
parent a236a5f0f3
commit c8d324cfa6
5 changed files with 89 additions and 1 deletions

View File

@ -58,6 +58,26 @@ 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.
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
```
Unittest2 Unittest2
--------- ---------

19
asynctest/eventually.nim Normal file
View File

@ -0,0 +1,19 @@
import std/times
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,4 +1,7 @@
import std/unittest import std/unittest
import ./eventually
export unittest except suite, test export unittest except suite, test
export eventually
include ./templates include ./templates

View File

@ -1,4 +1,7 @@
import pkg/unittest2 import pkg/unittest2
import ./eventually
export unittest2 except suite, test export unittest2 except suite, test
export eventually
include ./templates include ./templates

View File

@ -16,7 +16,7 @@ suite "test async proc":
# invoke await in tests: # invoke await in tests:
await someAsyncProc() await someAsyncProc()
suite "test async setupAll and teardownAll, allow multiple suites": suite "setupAll and teardownAll":
setupAll: setupAll:
# invoke await in the test setup: # invoke await in the test setup:
@ -29,3 +29,46 @@ suite "test async setupAll and teardownAll, allow multiple suites":
test "async test": test "async test":
# invoke await in tests: # invoke await in tests:
await someAsyncProc() 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()
test "becomes false after timeout":
proc remainsFalse: bool = false
check not eventually(remainsFalse(), timeout=100)
test "becomes true during timeout":
proc slowTrue: bool =
sleep(100)
true
check eventually(slowTrue(), timeout=50)
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
await future