[Haskell-cafe] [ANN] dejafu-188.8.131.52 - a library for unit-testing concurrent programs
mike at barrucadu.co.uk
Wed Feb 13 00:13:13 UTC 2019
I'm pleased to announce a new super-major release of [dejafu], a
library for testing concurrent Haskell programs.
While there are breaking changes, common use-cases shouldn't be
affected too significantly (or not at all). There is a brief guide to
the changes, and how to migrate if necessary, [on the website].
dejafu is a unit-testing library for concurrent Haskell programs.
Tests are deterministic, and work by systematically exploring the
possible schedules of your concurrency-using test case, allowing you
to confidently check your threaded code.
[HUnit] and [Tasty] bindings are available.
dejafu requires your test case to be written against the `MonadConc`
typeclass from the [concurrency] package. This is a necessity,
dejafu cannot peek inside your `IO` or `STM` actions, so it needs to
be able to plug in an alternative implementation of the concurrency
primitives for testing. There is some guidance for how to switch from
`IO` code to `MonadConc` code [on the website].
If you really need `IO`, you can use `MonadIO` - but make sure it's
deterministic enough to not invalidate your tests!
Here's a small example reproducing a deadlock found in an earlier
version of the [auto-update] library:
autocheck $ do
auto <- mkAutoUpdate defaultUpdateSettings
dejafu finds the deadlock, and gives a simplified execution trace for
each distinct result. More in-depth traces showing exactly what each
thread did are also available. This is using a version of auto-update
modified to use the `MonadConc` typeclass. The source is in the
The highlights for this release are setup actions, teardown actions,
- **Setup actions** are for things which are not really a part of your
test case, but which are needed for it (for example, setting up a
test distributed system). As dejafu can run a single test case many
times, repeating this work can be a significant overhead. By
defining this as a setup action, dejafu can "snapshot" the state at
the end of the action, and efficiently reload it in subsequent
executions of the same test.
- **Teardown actions** are for things you want to run after your test
case completes, in all cases, even if the test deadlocks (for
example). As dejafu controls the concurrent execution of the test
case, inspecting shared state is possible even if the test case
fails to complete.
- **Invariants** are effect-free atomically-checked conditions over
shared state which must always hold. If an invariant throws an
exception, the test case is aborted, and any teardown action run.
Here is an example of a setup action with an invariant:
let setup = do
var <- newEmptyMVar
registerInvariant $ do
value <- inspectMVar var
when (value == Just 1) $
in withSetup setup $ \var -> do
fork $ putMVar var 0
fork $ putMVar var 1
[invariant failure] S0--P2-
[invariant failure] S0--P2-
Just 0 S0--P1--S0--
In the `[invariant failure]` case, thread 2 is scheduled, writing the
forbidden value "1" to the MVar, which terminates the test.
Here is an example of a setup action with a teardown action:
let setup = newMVar ()
teardown var (Right _) = show <$> tryReadMVar var
teardown _ (Left e) = pure (show e)
in withSetupAndTeardown setup teardown $ \var -> do
fork $ takeMVar var
The teardown action can perform arbitrary concurrency effects,
including inspecting any mutable state returned by the setup action.
Setup and teardown actions were previously available in a slightly
different form as the `dontCheck` and `subconcurrency` functions,
which have been removed (see the migration guide if you used these).
Michael Walker (http://www.barrucadu.co.uk)
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Haskell-Cafe