mirror of
https://github.com/logos-messaging/nim-sds.git
synced 2026-06-12 04:09:33 +00:00
71 lines
2.3 KiB
Nim
71 lines
2.3 KiB
Nim
|
|
## Shared async-aware wrappers around `unittest` so tests in this repo can
|
||
|
|
## `await` directly in setup/test/teardown blocks instead of sprinkling
|
||
|
|
## `waitFor` at each call site.
|
||
|
|
##
|
||
|
|
## Usage:
|
||
|
|
##
|
||
|
|
## ```nim
|
||
|
|
## import ./async_unittest
|
||
|
|
##
|
||
|
|
## suite "X":
|
||
|
|
## var rm: ReliabilityManager
|
||
|
|
##
|
||
|
|
## asyncSetup:
|
||
|
|
## rm = newReliabilityManager(...).get()
|
||
|
|
## check (await rm.ensureChannel("ch")).isOk()
|
||
|
|
##
|
||
|
|
## asyncTeardown:
|
||
|
|
## if not rm.isNil:
|
||
|
|
## await rm.cleanup()
|
||
|
|
##
|
||
|
|
## asyncTest "Y":
|
||
|
|
## await rm.wrapOutgoingMessage(...)
|
||
|
|
## ```
|
||
|
|
##
|
||
|
|
## All three blocks run inside the same async proc (per test). unittest's
|
||
|
|
## own `setup:`/`teardown:` still work for purely synchronous fixtures.
|
||
|
|
|
||
|
|
import unittest, chronos
|
||
|
|
export unittest, chronos
|
||
|
|
|
||
|
|
template asyncSetup*(body: untyped) {.dirty.} =
|
||
|
|
## Async counterpart to unittest's `setup:`. Runs inside each asyncTest's
|
||
|
|
## async proc, so `await` works.
|
||
|
|
template asyncTestSetupIMPL(): untyped {.dirty.} =
|
||
|
|
body
|
||
|
|
|
||
|
|
template asyncTeardown*(body: untyped) {.dirty.} =
|
||
|
|
## Async counterpart to unittest's `teardown:`. Runs in a `finally` so it
|
||
|
|
## executes even when the test body (or setup) raises.
|
||
|
|
template asyncTestTeardownIMPL(): untyped {.dirty.} =
|
||
|
|
body
|
||
|
|
|
||
|
|
template asyncTest*(name: string, body: untyped) =
|
||
|
|
## Wraps a unittest `test` body in an async proc so `await` works on the
|
||
|
|
## now-async ReliabilityManager API. unittest's `check` raises Exception,
|
||
|
|
## which is wider than chronos's default CatchableError; the exception is
|
||
|
|
## caught inside the async body, stashed, and re-raised after waitFor so
|
||
|
|
## unittest's normal failure handling sees it.
|
||
|
|
##
|
||
|
|
## `cast(gcsafe)` is needed because suite-level vars (e.g. `var rm`) look
|
||
|
|
## like globals to the async closure, but the FFI runtime is single-thread
|
||
|
|
## so the "not gcsafe" warning isn't a real hazard here.
|
||
|
|
test name:
|
||
|
|
var asyncTestErr {.inject.}: ref Exception = nil
|
||
|
|
proc inner() {.async.} =
|
||
|
|
{.cast(gcsafe).}:
|
||
|
|
try:
|
||
|
|
when declared(asyncTestSetupIMPL):
|
||
|
|
asyncTestSetupIMPL()
|
||
|
|
try:
|
||
|
|
body
|
||
|
|
finally:
|
||
|
|
when declared(asyncTestTeardownIMPL):
|
||
|
|
asyncTestTeardownIMPL()
|
||
|
|
except Exception as e:
|
||
|
|
asyncTestErr = e
|
||
|
|
|
||
|
|
waitFor inner()
|
||
|
|
if asyncTestErr != nil:
|
||
|
|
raise asyncTestErr
|