diff --git a/.github/scripts/build-documentation.sh b/.github/scripts/build-documentation.sh new file mode 100755 index 0000000..26d16b0 --- /dev/null +++ b/.github/scripts/build-documentation.sh @@ -0,0 +1,92 @@ +#! /usr/bin/env nix-shell +#! nix-shell -I nixpkgs=channel:nixos-23.05 -i bash --packages coreutils mdbook mdbook-admonish python3 virtualenv ghc stack + +set -ex + +OUTPUT_DIR="_site" + +pushd docs +mdbook-admonish install +popd + +python3 <<'EOF' > docs/src/index.md +import sys + +with open("README.markdown") as f: + mode = "title" + for line in f: + line = line.rstrip() + if mode == "title": + print("Getting Started") + mode = "after-title" + elif mode == "after-title": + if line.startswith("- "): + mode = "skip-links" + else: + print(line) + elif mode == "skip-links": + if line.startswith("- "): + continue + else: + mode = "pre-version-table" + print(line) + elif mode == "pre-version-table": + print(line) + if line.startswith("|"): + mode = "version-table" + elif mode == "version-table": + print(line) + if line.startswith("See [the latest package documentation]"): + mode = "after-version-table" + elif mode == "after-version-table": + if line.startswith("["): + mode = "pre-contributing" + print("") + print(line) + elif mode == "pre-contributing": + if line == "Contributing": + mode = "skip-to-bibliography" + continue + print(line) + elif mode == "skip-to-bibliography": + if line == "Bibliography": + mode = "rest" + print(line) + else: + print(line) + +if mode != "rest": + print(f"unexpected mode: {mode}", file=sys.stderr) + sys.exit(1) +EOF + +bash <<'EOF' +virtualenv venv +source venv/bin/activate +pip install "rst-to-myst" + +mkdir -p docs/src/release-notes +for package in concurrency dejafu hunit-dejafu tasty-dejafu; do + rst2myst convert --no-sphinx "${package}/CHANGELOG.rst" + cat "${package}/CHANGELOG.md" | \ + sed 'sZ{issue}`\([^`]*\)`Z[issue #\1](https://github.com/barrucadu/dejafu/issues/\1)Zg' | \ + sed 'sZ{pull}`\([^`]*\)`Z[pull request #\1](https://github.com/barrucadu/dejafu/pull/\1)Zg' | \ + sed 'sZ{tag}`\([^`]*\)`Z[\1](https://github.com/barrucadu/dejafu/releases/tag/\1)Zg' | \ + sed 'sZ{u}`\([^`]*\)`Z[\1](https://github.com/\1)Zg' | \ + sed 'sZ{hackage}`\([^`]*\)`Z[\1](https://hackage.haskell.org/package/\1)Zg' > "docs/src/release-notes/${package}.md" + rm "${package}/CHANGELOG.md" +done + +rm -rf venv +EOF + +mdbook build docs +mv docs/book "$OUTPUT_DIR" + +stack --no-install-ghc --no-nix --skip-ghc-check --system-ghc haddock concurrency dejafu hunit-dejafu tasty-dejafu +rm -rf .stack-work/install/*/*/*/doc/all/ +mv .stack-work/install/*/*/*/doc/ "$OUTPUT_DIR/packages" + +chmod -c -R +rX "$OUTPUT_DIR" | while read -r line; do + echo "::warning title=Invalid file permissions automatically fixed::$line" +done diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f53c750..28bddfd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,6 +3,23 @@ name: Run tests on: pull_request jobs: + check-docs-site: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check mdbook-admonish changes are not committed + run: | + if grep -q "do not edit: managed by \`mdbook-admonish install\`" docs/book.toml; then + echo "remove generated mdbook-admonish lines from docs/books.toml" >&2 + exit 1 + fi + - name: Install nix + uses: cachix/install-nix-action@v23 + with: + nix_path: nixpkgs=channel:nixos-23.05 + - name: Check documentation site builds + run: nix-shell ./.github/scripts/build-documentation.sh + lint: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/deploy-documentation.yml b/.github/workflows/deploy-documentation.yml new file mode 100644 index 0000000..a0f838e --- /dev/null +++ b/.github/workflows/deploy-documentation.yml @@ -0,0 +1,51 @@ +name: Deploy documentation site + +on: + # Runs on pushes targeting the default branch + push: + branches: ["master"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v3 + - name: Install nix + uses: cachix/install-nix-action@v23 + with: + nix_path: nixpkgs=channel:nixos-23.05 + - name: Build + run: nix-shell ./.github/scripts/build-documentation.sh + - name: Upload artifact + uses: actions/upload-pages-artifact@v2 + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 diff --git a/.gitignore b/.gitignore index 3f0a296..41192a1 100755 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ dist .stack-work *.tix *.prof +_site diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..3e1e245 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,13 @@ +version: "2" + +build: + os: "ubuntu-22.04" + tools: + python: "3.10" + +python: + install: + - requirements: docs/readthedocs/requirements.txt + +sphinx: + configuration: docs/readthedocs/source/conf.py diff --git a/README.markdown b/README.markdown index d11a2e6..3d79abc 100644 --- a/README.markdown +++ b/README.markdown @@ -14,12 +14,13 @@ dejafu - [Release notes](#release-notes) - [Questions, feedback, discussion](#questions-feedback-discussion) - [Bibliography](#bibliography) -- **[The website!](http://dejafu.readthedocs.io/)** +- **[The website!](https://dejafu.docs.barrucadu.co.uk/)** Déjà Fu is a unit-testing library for concurrent Haskell programs. Tests are deterministic and expressive, making it easy and convenient -to test your threaded code. Available on [GitHub][], [Hackage][], and -[Stackage][]. +to test your threaded code. + +Available on [GitHub][], [Hackage][], and [Stackage][]. [GitHub]: https://github.com/barrucadu/dejafu [Hackage]: https://hackage.haskell.org/package/dejafu @@ -47,6 +48,8 @@ There are a few different packages under the Déjà Fu umbrella: | [hunit-dejafu][h:hunit] | 2.0.0.6 | Deja Fu support for the HUnit test framework. | | [tasty-dejafu][h:tasty] | 2.1.0.1 | Deja Fu support for the Tasty test framework. | +See [the latest package documentation](https://dejafu.docs.barrucadu.co.uk/packages/). + Each package has its own README and CHANGELOG in its subdirectory. There is also dejafu-tests, the test suite for dejafu. This is in a @@ -162,7 +165,7 @@ the trace by eye. Contributing ------------ -See the "contributing" page on [the website](http://dejafu.readthedocs.io/en/latest/contributing.html). +See the "contributing" page on [the website](https://dejafu.docs.barrucadu.co.uk/dev-docs/contributing.html). **If you'd like to get involved with Déjà Fu**, check out [the "good first issue" label on the issue tracker][beginners]. @@ -193,34 +196,33 @@ These libraries wouldn't be possible without prior research, which I mention in the documentation. Haddock comments get the full citation, whereas in-line comments just get the shortened name: -- [BPOR] *Bounded partial-order reduction*, K. Coons, M. Musuvathi, - and K. McKinley (2013) - http://research.microsoft.com/pubs/202164/bpor-oopsla-2013.pdf +- [BPOR] [Bounded partial-order reduction](http://research.microsoft.com/pubs/202164/bpor-oopsla-2013.pdf), + K. Coons, M. Musuvathi, and K. McKinley (2013) -- [RDPOR] *Dynamic Partial Order Reduction for Relaxed Memory Models*, +- [RDPOR] [Dynamic Partial Order Reduction for Relaxed Memory Models](http://www.faculty.ece.vt.edu/chaowang/pubDOC/ZhangKW15.pdf), N. Zhang, M. Kusano, and C. Wang (2015) - http://www.faculty.ece.vt.edu/chaowang/pubDOC/ZhangKW15.pdf -- [Empirical] *Concurrency Testing Using Schedule Bounding: an - Empirical Study*, P. Thompson, A. Donaldson, and A. Betts (2014) - http://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2014/PPoPP.pdf +- [Empirical] [Concurrency Testing Using Schedule Bounding: an Empirical Study](http://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2014/PPoPP.pdf), + P. Thompson, A. Donaldson, and A. Betts (2014) -- [RMMVerification] *On the Verification of Programs on Relaxed Memory - Models*, A. Linden (2014) - https://orbi.ulg.ac.be/bitstream/2268/158670/1/thesis.pdf +- [RMMVerification] [On the Verification of Programs on Relaxed Memory Models](https://orbi.ulg.ac.be/bitstream/2268/158670/1/thesis.pdf), + A. Linden (2014) -There are also a couple of papers on dejafu itself: +There are also a few papers on dejafu itself: -- *Déjà Fu: A Concurrency Testing Library for Haskell*, M. Walker and - C. Runciman (2015) - https://www.barrucadu.co.uk/publications/dejafu-hs15.pdf +- [Déjà Fu: A Concurrency Testing Library for Haskell](https://www.barrucadu.co.uk/publications/dejafu-hs15.pdf), + M. Walker and C. Runciman (2015) This details dejafu-0.1, and was presented at the 2015 Haskell Symposium. -- *Déjà Fu: A Concurrency Testing Library for Haskell*, M. Walker and - C. Runciman (2016) - https://www.barrucadu.co.uk/publications/YCS-2016-503.pdf +- [Déjà Fu: A Concurrency Testing Library for Haskell](https://www.barrucadu.co.uk/publications/YCS-2016-503.pdf), + M. Walker and C. Runciman (2016) This is a more in-depth technical report, written between the dejafu-0.2 and dejafu-0.3 releases. + +- [Revealing Behaviours of Concurrent Functional Programs by Systematic Testing](https://www.barrucadu.co.uk/publications/thesis.pdf), + M. Walker (2018) + + This is my Ph.D thesis, which discusses dejafu and my other research projects. diff --git a/concurrency/CHANGELOG.rst b/concurrency/CHANGELOG.rst index 724eb25..48df708 100644 --- a/concurrency/CHANGELOG.rst +++ b/concurrency/CHANGELOG.rst @@ -46,7 +46,7 @@ Fixed 1.11.0.0 (2020-05-14) --------------------- +--------------------- * Git: :tag:`concurrency-1.11.0.0` * Hackage: :hackage:`concurrency-1.11.0.0` @@ -171,10 +171,10 @@ Added * (:issue:`286`) Copy across additions from the :hackage:`stm` package: - * ``Control.Concurrent.Classy.STM.TQueue.flushTQueue`` - * ``Control.Concurrent.Classy.STM.TBQueue.flushTBQueue`` - * ``Control.Concurrent.Classy.STM.TBQueue.lengthTBQueue`` - * ``Control.Concurrent.Classy.STM.TVar.stateTVar`` + * ``Control.Concurrent.Classy.STM.TQueue.flushTQueue`` + * ``Control.Concurrent.Classy.STM.TBQueue.flushTBQueue`` + * ``Control.Concurrent.Classy.STM.TBQueue.lengthTBQueue`` + * ``Control.Concurrent.Classy.STM.TVar.stateTVar`` * (:issue:`287`) The ``Control.Concurrent.Classy.STM.TSem`` module. @@ -183,10 +183,8 @@ Changed * (:issue:`286`) Copy across changes from the :hackage:`stm` package: - * Make definition of ``readTQueue`` consistent with - ``readTBQueue`` - - * Performance improvements to ``peekTQueue`` and ``peekTBQueue``. + * Make definition of ``readTQueue`` consistent with ``readTBQueue`` + * Performance improvements to ``peekTQueue`` and ``peekTBQueue``. Miscellaneous ~~~~~~~~~~~~~ @@ -235,9 +233,9 @@ Miscellaneous * GHC 7.10 support is dropped. Dependency lower bounds are: - * :hackage:`base`: 4.9 - * :hackage:`array`: 0.5.1 - * :hackage:`transformers`: 0.5 + * :hackage:`base`: 4.9 + * :hackage:`array`: 0.5.1 + * :hackage:`transformers`: 0.5 1.4.0.2 (2018-03-11) @@ -276,8 +274,8 @@ Changed * ``Control.Monad.Conc.Class.peekTicket'`` has a more concrete type, to make deriving newtype instances of ``MonadConc`` possible: - * Old: ``MonadConc m => proxy m -> Ticket m a -> a`` - * New: ``MonadConc m => Proxy m -> Ticket m a -> a`` + * Old: ``MonadConc m => proxy m -> Ticket m a -> a`` + * New: ``MonadConc m => Proxy m -> Ticket m a -> a`` 1.3.0.0 - The Bound Thread Release (2017-12-23) @@ -294,21 +292,21 @@ Added * (:pull:`145`) Bound thread variants of the ``withAsync`` functions: - * ``Control.Concurrent.Classy.Async.asyncBound`` - * ``Control.Concurrent.Classy.Async.asyncBoundN`` - * ``Control.Concurrent.Classy.Async.withAsyncBound`` - * ``Control.Concurrent.Classy.Async.withAsyncBoundN`` + * ``Control.Concurrent.Classy.Async.asyncBound`` + * ``Control.Concurrent.Classy.Async.asyncBoundN`` + * ``Control.Concurrent.Classy.Async.withAsyncBound`` + * ``Control.Concurrent.Classy.Async.withAsyncBoundN`` * (:pull:`145`) Bound thread functions in ``MonadConc``: - * ``Control.Monad.Conc.Class.forkOS`` - * ``Control.Monad.Conc.Class.forkOSN`` - * ``Control.Monad.Conc.Class.isCurrentThreadBound`` + * ``Control.Monad.Conc.Class.forkOS`` + * ``Control.Monad.Conc.Class.forkOSN`` + * ``Control.Monad.Conc.Class.isCurrentThreadBound`` * (:pull:`145`) Helper functions for bound threads: - * ``Control.Monad.Conc.Class.runInBoundThread`` - * ``Control.Monad.Conc.Class.runInUnboundThread`` + * ``Control.Monad.Conc.Class.runInBoundThread`` + * ``Control.Monad.Conc.Class.runInUnboundThread`` Changed ~~~~~~~ @@ -328,10 +326,10 @@ Added * (:issue:`148`) Named thread variants of the ``withAsync`` functions: - * ``Control.Concurrent.Classy.Async.withAsyncN`` - * ``Control.Concurrent.Classy.Async.withAsyncOnN`` - * ``Control.Concurrent.Classy.Async.withAsyncWithUnmaskN`` - * ``Control.Concurrent.Classy.Async.withAsyncOnWithUnmaskN`` + * ``Control.Concurrent.Classy.Async.withAsyncN`` + * ``Control.Concurrent.Classy.Async.withAsyncOnN`` + * ``Control.Concurrent.Classy.Async.withAsyncWithUnmaskN`` + * ``Control.Concurrent.Classy.Async.withAsyncOnWithUnmaskN`` 1.2.2.0 (2017-11-05) @@ -345,12 +343,12 @@ Added * (:issue:`144`) ``IsConc`` and ``IsSTM`` wrapper types: - * ``Control.Monad.Conc.Class.IsConc`` (constructor unexported) - * ``Control.Monad.Conc.Class.toIsConc`` - * ``Control.Monad.Conc.Class.fromIsConc`` - * ``Control.Monad.STM.Class.IsSTM`` (constructor unexported) - * ``Control.Monad.STM.Class.toIsSTM`` - * ``Control.Monad.STM.Class.fromIsSTM`` + * ``Control.Monad.Conc.Class.IsConc`` (constructor unexported) + * ``Control.Monad.Conc.Class.toIsConc`` + * ``Control.Monad.Conc.Class.fromIsConc`` + * ``Control.Monad.STM.Class.IsSTM`` (constructor unexported) + * ``Control.Monad.STM.Class.toIsSTM`` + * ``Control.Monad.STM.Class.fromIsSTM`` Changed ~~~~~~~ @@ -398,10 +396,10 @@ Added * (:pull:`125`) Named thread variants of the ``async`` functions: - * ``Control.Concurrent.Classy.Async.asyncN`` - * ``Control.Concurrent.Classy.Async.asyncOnN`` - * ``Control.Concurrent.Classy.Async.asyncWithUnmaskN`` - * ``Control.Concurrent.Classy.Async.asyncOnWithUnmaskN`` + * ``Control.Concurrent.Classy.Async.asyncN`` + * ``Control.Concurrent.Classy.Async.asyncOnN`` + * ``Control.Concurrent.Classy.Async.asyncWithUnmaskN`` + * ``Control.Concurrent.Classy.Async.asyncOnWithUnmaskN`` 1.2.0.0 (2017-09-16) @@ -446,12 +444,12 @@ Added * Missing functions copied from :hackage:`async`: - * ``Control.Concurrent.Classy.Async.uninterruptibleCancel`` - * ``Control.Concurrent.Classy.Async.replicateConcurrently`` - * ``Control.Concurrent.Classy.Async.concurrently_`` - * ``Control.Concurrent.Classy.Async.mapConcurrently_`` - * ``Control.Concurrent.Classy.Async.forConcurrently_`` - * ``Control.Concurrent.Classy.Async.replicateConcurrently_`` + * ``Control.Concurrent.Classy.Async.uninterruptibleCancel`` + * ``Control.Concurrent.Classy.Async.replicateConcurrently`` + * ``Control.Concurrent.Classy.Async.concurrently_`` + * ``Control.Concurrent.Classy.Async.mapConcurrently_`` + * ``Control.Concurrent.Classy.Async.forConcurrently_`` + * ``Control.Concurrent.Classy.Async.replicateConcurrently_`` * ``Control.Concurrent.Classy.Async.Concurrently`` has a ``Semigroup`` instance when built with :hackage:`base` >= 4.9. diff --git a/dejafu/CHANGELOG.rst b/dejafu/CHANGELOG.rst index bfa4e63..c0dcb3e 100644 --- a/dejafu/CHANGELOG.rst +++ b/dejafu/CHANGELOG.rst @@ -13,6 +13,7 @@ unreleased Miscellaneous ~~~~~~~~~~~~~ +* Update documentation link in ``Test.DejaFu``. * Fix GHC compatibility warning. @@ -139,8 +140,9 @@ Added * Thread action constructors for the ``MonadConc`` ``getMaskingState`` function: - * ``Test.DejaFu.Types.ThreadAction``, ``GetMaskingState`` - * ``Test.DejaFu.Types.Lookahead``, ``WillGetMaskingState`` + + * ``Test.DejaFu.Types.ThreadAction``, ``GetMaskingState`` + * ``Test.DejaFu.Types.Lookahead``, ``WillGetMaskingState`` Miscellaneous ~~~~~~~~~~~~~ @@ -194,50 +196,57 @@ Miscellaneous Added ~~~~~ -* The ``Test.DejaFu.Types.MonadDejaFu`` typeclass, containing the - primitives needed to run a concurrent program. There are instances - for: - * ``IO``, which is probably the ``MonadConc`` instance people used - previously, so there is no breaking change there. - * ``CatchT (ST t)``, meaning that concurrent programs can be run - without ``IO`` once more. +* The ``Test.DejaFu.Types.MonadDejaFu`` typeclass, containing the primitives + needed to run a concurrent program. There are instances for: -* Thread action constructors for ``MonadConc`` - ``supportsBoundThreads`` function: - * ``Test.DejaFu.Types.ThreadAction``, ``SupportsBoundThreads`` - * ``Test.DejaFu.Types.Lookahead``, ``WillSupportsBoundThreads`` + * ``IO``, which is probably the ``MonadConc`` instance people used previously, + so there is no breaking change there. + * ``CatchT (ST t)``, meaning that concurrent programs can be run without + ``IO`` once more. + +* Thread action constructors for ``MonadConc`` ``supportsBoundThreads`` + function: + + * ``Test.DejaFu.Types.ThreadAction``, ``SupportsBoundThreads`` + * ``Test.DejaFu.Types.Lookahead``, ``WillSupportsBoundThreads`` Changed ~~~~~~~ -* Many functions which had a ``MonadConc`` constraint now have a - ``MonadDejaFu`` constraint: - * In ``Test.DejaFu`` - * ``autocheck`` - * ``autocheckWay`` - * ``autocheckWithSettings`` - * ``dejafu`` - * ``dejafuWay`` - * ``dejafuWithSettings`` - * ``dejafus`` - * ``dejafusWay`` - * ``dejafusWithSettings`` - * ``runTest`` - * ``runTestWay`` - * ``runTestWithSettings`` - * In ``Test.DejaFu.Conc`` - * ``runConcurrent`` - * ``recordSnapshot`` - * ``runSnapshot`` - * In ``Test.DejaFu.SCT`` - * ``runSCT`` - * ``resultsSet`` - * ``runSCT'`` - * ``resultsSet'`` - * ``runSCTWithSettings`` - * ``resultsSetWithSettings`` - * ``runSCTWithSettings'`` - * ``resultsSetWithSettings'`` +* Many functions which had a ``MonadConc`` constraint now have a ``MonadDejaFu`` + constraint: + + * In ``Test.DejaFu`` + + * ``autocheck`` + * ``autocheckWay`` + * ``autocheckWithSettings`` + * ``dejafu`` + * ``dejafuWay`` + * ``dejafuWithSettings`` + * ``dejafus`` + * ``dejafusWay`` + * ``dejafusWithSettings`` + * ``runTest`` + * ``runTestWay`` + * ``runTestWithSettings`` + + * In ``Test.DejaFu.Conc`` + + * ``runConcurrent`` + * ``recordSnapshot`` + * ``runSnapshot`` + + * In ``Test.DejaFu.SCT`` + + * ``runSCT`` + * ``resultsSet`` + * ``runSCT'`` + * ``resultsSet'`` + * ``runSCTWithSettings`` + * ``resultsSetWithSettings`` + * ``runSCTWithSettings'`` + * ``resultsSetWithSettings'`` Miscellaneous ~~~~~~~~~~~~~ @@ -269,26 +278,29 @@ Added * The ``Program`` types and their constructors (re-exported from ``Test.DejaFu``): - * ``Test.DejaFu.Conc.Program`` - * ``Test.DejaFu.Conc.Basic`` - * ``Test.DejaFu.Conc.WithSetup`` - * ``Test.DejaFu.Conc.WithSetupAndTeardown`` - * ``Test.DejaFu.Conc.withSetup`` - * ``Test.DejaFu.Conc.withTeardown`` - * ``Test.DejaFu.Conc.withSetupAndTeardown`` + + * ``Test.DejaFu.Conc.Program`` + * ``Test.DejaFu.Conc.Basic`` + * ``Test.DejaFu.Conc.WithSetup`` + * ``Test.DejaFu.Conc.WithSetupAndTeardown`` + * ``Test.DejaFu.Conc.withSetup`` + * ``Test.DejaFu.Conc.withTeardown`` + * ``Test.DejaFu.Conc.withSetupAndTeardown`` * The ``Invariant`` type and associated functions (re-exported from ``Test.DejaFu``): - * ``Test.DejaFu.Conc.Invariant`` - * ``Test.DejaFu.Conc.registerInvariant`` - * ``Test.DejaFu.Conc.inspectIORef`` - * ``Test.DejaFu.Conc.inspectMVar`` - * ``Test.DejaFu.Conc.inspectTVar`` + + * ``Test.DejaFu.Conc.Invariant`` + * ``Test.DejaFu.Conc.registerInvariant`` + * ``Test.DejaFu.Conc.inspectIORef`` + * ``Test.DejaFu.Conc.inspectMVar`` + * ``Test.DejaFu.Conc.inspectTVar`` * New snapshotting functions: - * ``Test.DejaFu.Conc.Snapshot`` - * ``Test.DejaFu.Conc.recordSnapshot`` - * ``Test.DejaFu.Conc.runSnapshot`` + + * ``Test.DejaFu.Conc.Snapshot`` + * ``Test.DejaFu.Conc.recordSnapshot`` + * ``Test.DejaFu.Conc.runSnapshot`` * ``Test.DejaFu.Settings.llengthBound``, which now applies to all ways of testing. @@ -299,14 +311,15 @@ Added * ``Test.DejaFu.runTestWithSettings`` function. * A simplified form of the concurrency state: - * ``Test.DejaFu.Types.ConcurrencyState`` - * ``Test.DejaFu.Types.isBuffered`` - * ``Test.DejaFu.Types.numBuffered`` - * ``Test.DejaFu.Types.isFull`` - * ``Test.DejaFu.Types.canInterrupt`` - * ``Test.DejaFu.Types.canInterruptL`` - * ``Test.DejaFu.Types.isMaskedInterruptible`` - * ``Test.DejaFu.Types.isMaskedUninterruptible`` + + * ``Test.DejaFu.Types.ConcurrencyState`` + * ``Test.DejaFu.Types.isBuffered`` + * ``Test.DejaFu.Types.numBuffered`` + * ``Test.DejaFu.Types.isFull`` + * ``Test.DejaFu.Types.canInterrupt`` + * ``Test.DejaFu.Types.canInterruptL`` + * ``Test.DejaFu.Types.isMaskedInterruptible`` + * ``Test.DejaFu.Types.isMaskedUninterruptible`` Changed ~~~~~~~ @@ -318,75 +331,83 @@ Changed return a representative trace for each unique condition. * Functions which took a ``ConcT`` now take a ``Program pty``: - * ``Test.DejaFu.autocheck`` - * ``Test.DejaFu.autocheckWay`` - * ``Test.DejaFu.autocheckWithSettings`` - * ``Test.DejaFu.dejafu`` - * ``Test.DejaFu.dejafuWay`` - * ``Test.DejaFu.dejafuWithSettings`` - * ``Test.DejaFu.dejafus`` - * ``Test.DejaFu.dejafusWay`` - * ``Test.DejaFu.dejafusWithSettings`` - * ``Test.DejaFu.runTest`` - * ``Test.DejaFu.runTestWay`` - * ``Test.DejaFu.runTestWithSettings`` - * ``Test.DejaFu.Conc.runConcurrent`` - * ``Test.DejaFu.SCT.runSCT`` - * ``Test.DejaFu.SCT.resultsSet`` - * ``Test.DejaFu.SCT.runSCT'`` - * ``Test.DejaFu.SCT.resultsSet'`` - * ``Test.DejaFu.SCT.runSCTWithSettings`` - * ``Test.DejaFu.SCT.resultsSetWithSettings`` - * ``Test.DejaFu.SCT.runSCTWithSettings'`` - * ``Test.DejaFu.SCT.resultsSetWithSettings'`` + + * ``Test.DejaFu.autocheck`` + * ``Test.DejaFu.autocheckWay`` + * ``Test.DejaFu.autocheckWithSettings`` + * ``Test.DejaFu.dejafu`` + * ``Test.DejaFu.dejafuWay`` + * ``Test.DejaFu.dejafuWithSettings`` + * ``Test.DejaFu.dejafus`` + * ``Test.DejaFu.dejafusWay`` + * ``Test.DejaFu.dejafusWithSettings`` + * ``Test.DejaFu.runTest`` + * ``Test.DejaFu.runTestWay`` + * ``Test.DejaFu.runTestWithSettings`` + * ``Test.DejaFu.Conc.runConcurrent`` + * ``Test.DejaFu.SCT.runSCT`` + * ``Test.DejaFu.SCT.resultsSet`` + * ``Test.DejaFu.SCT.runSCT'`` + * ``Test.DejaFu.SCT.resultsSet'`` + * ``Test.DejaFu.SCT.runSCTWithSettings`` + * ``Test.DejaFu.SCT.resultsSetWithSettings`` + * ``Test.DejaFu.SCT.runSCTWithSettings'`` + * ``Test.DejaFu.SCT.resultsSetWithSettings'`` * ``Test.DejaFu.Conc.ConcT`` is an alias for ``Program Basic``. * ``Test.DejaFu.Types.Bounds``: - * Removed ``boundLength`` field. + + * Removed ``boundLength`` field. * ``Test.DejaFu.Types.Condition``: - * Added ``InvariantFailure`` constructor - * Removed ``STMDeadlock`` constructor + + * Added ``InvariantFailure`` constructor + * Removed ``STMDeadlock`` constructor * ``Test.DejaFu.Types.Error``: - * Removed ``NestedSubconcurrency``, ``MultithreadedSubconcurrency``, - and ``LateDontCheck`` constructors. + + * Removed ``NestedSubconcurrency``, ``MultithreadedSubconcurrency``, and + ``LateDontCheck`` constructors. * ``Test.DejaFu.Types.Lookahead``: - * Added ``WillRegisterInvariant`` constructor - * Removed ``WillSubconcurrency``, ``WillStopSubconcurrency``, and - ``WillDontCheck`` constructors + + * Added ``WillRegisterInvariant`` constructor + * Removed ``WillSubconcurrency``, ``WillStopSubconcurrency``, and + ``WillDontCheck`` constructors * ``Test.DejaFu.Types.ThreadAction``: - * Added ``RegisterInvariant`` constructor - * Removed ``Subconcurrency``, ``StopSubconcurrency``, and - ``DontCheck`` constructors + + * Added ``RegisterInvariant`` constructor + * Removed ``Subconcurrency``, ``StopSubconcurrency``, and + ``DontCheck`` constructors Removed ~~~~~~~ * The deprecated functions: - * ``Test.DejaFu.dejafuDiscard`` - * ``Test.DejaFu.SCT.runSCTDiscard`` - * ``Test.DejaFu.SCT.runSCTDiscard'`` - * ``Test.DejaFu.SCT.resultsSetDiscard`` - * ``Test.DejaFu.SCT.resultsSetDiscard'`` - * ``Test.DejaFu.SCT.sctBound`` - * ``Test.DejaFu.SCT.sctBoundDiscard`` - * ``Test.DejaFu.SCT.sctUniformRandom`` - * ``Test.DejaFu.SCT.sctUniformRandomDiscard`` - * ``Test.DejaFu.SCT.sctWeightedRandom`` - * ``Test.DejaFu.SCT.sctWeightedRandomDiscard`` + + * ``Test.DejaFu.dejafuDiscard`` + * ``Test.DejaFu.SCT.runSCTDiscard`` + * ``Test.DejaFu.SCT.runSCTDiscard'`` + * ``Test.DejaFu.SCT.resultsSetDiscard`` + * ``Test.DejaFu.SCT.resultsSetDiscard'`` + * ``Test.DejaFu.SCT.sctBound`` + * ``Test.DejaFu.SCT.sctBoundDiscard`` + * ``Test.DejaFu.SCT.sctUniformRandom`` + * ``Test.DejaFu.SCT.sctUniformRandomDiscard`` + * ``Test.DejaFu.SCT.sctWeightedRandom`` + * ``Test.DejaFu.SCT.sctWeightedRandomDiscard`` * The deprecated type ``Test.DejaFu.Types.Failure`` * Old snapshotting functions: - * ``Test.DejaFu.Conc.DCSnapshot`` - * ``Test.DejaFu.Conc.runForDCSnapshot`` - * ``Test.DejaFu.Conc.runWithDCSnapshot`` - * ``Test.DejaFu.Conc.canDCSnapshot`` - * ``Test.DejaFu.Conc.threadsFromDCSnapshot`` + + * ``Test.DejaFu.Conc.DCSnapshot`` + * ``Test.DejaFu.Conc.runForDCSnapshot`` + * ``Test.DejaFu.Conc.runWithDCSnapshot`` + * ``Test.DejaFu.Conc.canDCSnapshot`` + * ``Test.DejaFu.Conc.threadsFromDCSnapshot`` * ``Test.DejaFu.Conc.dontCheck`` @@ -408,8 +429,9 @@ Added * ``Test.DejaFu.Types.Error`` for internal errors and misuses, with predicates: - * ``Test.DejaFu.Types.isSchedulerError`` - * ``Test.DejaFu.Types.isIncorrectUsage`` + + * ``Test.DejaFu.Types.isSchedulerError`` + * ``Test.DejaFu.Types.isIncorrectUsage`` * Deprecated ``Test.DejaFu.Types.Failure`` type synonym for ``Condition``. @@ -652,9 +674,9 @@ Miscellaneous * GHC 7.10 support is dropped. Dependency lower bounds are: - * :hackage:`base`: 4.9 - * :hackage:`concurrency`: 1.5 - * :hackage:`transformers`: 0.5 + * :hackage:`base`: 4.9 + * :hackage:`concurrency`: 1.5 + * :hackage:`transformers`: 0.5 * The upper bound on :hackage:`concurrency` is 1.6. @@ -699,8 +721,8 @@ Added * (:issue:`183`) SCT settings for trace simplification: - * ``Test.DejaFu.Settings.lequality`` - * ``Test.DejaFu.Settings.lsimplify`` + * ``Test.DejaFu.Settings.lequality`` + * ``Test.DejaFu.Settings.lsimplify`` * (:pull:`248`) ``Test.DejaFu.Utils.toTIdTrace`` to extract thread IDs from a trace. @@ -726,23 +748,23 @@ Added * (:pull:`246`) ``Generic`` instances for: - * ``Test.DejaFu.Types.ThreadId`` - * ``Test.DejaFu.Types.CRefId`` - * ``Test.DejaFu.Types.MVarId`` - * ``Test.DejaFu.Types.TVarId`` - * ``Test.DejaFu.Types.Id`` - * ``Test.DejaFu.Types.ThreadAction`` - * ``Test.DejaFu.Types.Lookahead`` - * ``Test.DejaFu.Types.TAction`` - * ``Test.DejaFu.Types.Decision`` - * ``Test.DejaFu.Types.Failure`` - * ``Test.DejaFu.Types.Bounds`` - * ``Test.DejaFu.Types.PreemptionBound`` - * ``Test.DejaFu.Types.FairBound`` - * ``Test.DejaFu.Types.LengthBound`` - * ``Test.DejaFu.Types.Discard`` - * ``Test.DejaFu.Types.MemType`` - * ``Test.DejaFu.Types.MonadFailException`` + * ``Test.DejaFu.Types.ThreadId`` + * ``Test.DejaFu.Types.CRefId`` + * ``Test.DejaFu.Types.MVarId`` + * ``Test.DejaFu.Types.TVarId`` + * ``Test.DejaFu.Types.Id`` + * ``Test.DejaFu.Types.ThreadAction`` + * ``Test.DejaFu.Types.Lookahead`` + * ``Test.DejaFu.Types.TAction`` + * ``Test.DejaFu.Types.Decision`` + * ``Test.DejaFu.Types.Failure`` + * ``Test.DejaFu.Types.Bounds`` + * ``Test.DejaFu.Types.PreemptionBound`` + * ``Test.DejaFu.Types.FairBound`` + * ``Test.DejaFu.Types.LengthBound`` + * ``Test.DejaFu.Types.Discard`` + * ``Test.DejaFu.Types.MemType`` + * ``Test.DejaFu.Types.MonadFailException`` * (:pull:`246`) ``NFData`` instance for ``Test.DejaFu.Types.MonadFailException`` @@ -817,49 +839,55 @@ Added * (:pull:`238`) A record-based approach to SCT configuration: - * ``Test.DejaFu.Settings`` - (re-exported from ``Test.Dejafu`` and ``Test.DejaFu.SCT``) - * ``Test.DejaFu.Settings.Settings`` - * ``Test.DejaFu.Settings.defaultSettings`` - * ``Test.DejaFu.Settings.fromWayAndMemType`` - * Lenses: - * ``Test.DejaFu.Settings.lway`` - * ``Test.DejaFu.Settings.lmemtype`` - * ``Test.DejaFu.Settings.ldiscard`` - * ``Test.DejaFu.Settings.learlyExit`` - * ``Test.DejaFu.Settings.ldebugShow`` - * ``Test.DejaFu.Settings.ldebugPrint`` - * Lens helpers: - * ``Test.DejaFu.Settings.get`` - * ``Test.DejaFu.Settings.set`` - * Runners: - * ``Test.DejaFu.SCT.runSCTWithSettings`` - * ``Test.DejaFu.SCT.runSCTWithSettings'`` - * ``Test.DejaFu.SCT.resultsSetWithSettings`` - * ``Test.DejaFu.SCT.resultsSetWithSettings'`` + * ``Test.DejaFu.Settings`` (re-exported from ``Test.Dejafu`` and + ``Test.DejaFu.SCT``) + * ``Test.DejaFu.Settings.Settings`` + * ``Test.DejaFu.Settings.defaultSettings`` + * ``Test.DejaFu.Settings.fromWayAndMemType`` + + * Lenses: + + * ``Test.DejaFu.Settings.lway`` + * ``Test.DejaFu.Settings.lmemtype`` + * ``Test.DejaFu.Settings.ldiscard`` + * ``Test.DejaFu.Settings.learlyExit`` + * ``Test.DejaFu.Settings.ldebugShow`` + * ``Test.DejaFu.Settings.ldebugPrint`` + + * Lens helpers: + + * ``Test.DejaFu.Settings.get`` + * ``Test.DejaFu.Settings.set`` + + * Runners: + + * ``Test.DejaFu.SCT.runSCTWithSettings`` + * ``Test.DejaFu.SCT.runSCTWithSettings'`` + * ``Test.DejaFu.SCT.resultsSetWithSettings`` + * ``Test.DejaFu.SCT.resultsSetWithSettings'`` * (:pull:`238`) Settings-based test functions: - * ``Test.DejaFu.autocheckWithSettings`` - * ``Test.DejaFu.dejafuWithSettings`` - * ``Test.DejaFu.dejafusWithSettings`` - * ``Test.DejaFu.runTestWithSettings`` + * ``Test.DejaFu.autocheckWithSettings`` + * ``Test.DejaFu.dejafuWithSettings`` + * ``Test.DejaFu.dejafusWithSettings`` + * ``Test.DejaFu.runTestWithSettings`` Deprecated ~~~~~~~~~~ * (:pull:`238`) SCT function variants: - * ``Test.DejaFu.SCT.runSCTDiscard`` - * ``Test.DejaFu.SCT.resultSetDiscard`` - * ``Test.DejaFu.SCT.runSCTDiscard'`` - * ``Test.DejaFu.SCT.resultSetDiscard'`` - * ``Test.DejaFu.SCT.sctBound`` - * ``Test.DejaFu.SCT.sctBoundDiscard`` - * ``Test.DejaFu.SCT.sctUniformRandom`` - * ``Test.DejaFu.SCT.sctUniformRandomDiscard`` - * ``Test.DejaFu.SCT.sctWeightedRandom`` - * ``Test.DejaFu.SCT.sctWeightedRandomDiscard`` + * ``Test.DejaFu.SCT.runSCTDiscard`` + * ``Test.DejaFu.SCT.resultSetDiscard`` + * ``Test.DejaFu.SCT.runSCTDiscard'`` + * ``Test.DejaFu.SCT.resultSetDiscard'`` + * ``Test.DejaFu.SCT.sctBound`` + * ``Test.DejaFu.SCT.sctBoundDiscard`` + * ``Test.DejaFu.SCT.sctUniformRandom`` + * ``Test.DejaFu.SCT.sctUniformRandomDiscard`` + * ``Test.DejaFu.SCT.sctWeightedRandom`` + * ``Test.DejaFu.SCT.sctWeightedRandomDiscard`` * (:pull:`238`) The ``Test.DejaFu.Defaults`` module. Import ``Test.DejaFu.Settings`` instead. @@ -915,18 +943,18 @@ Added * (:pull:`219`) The testing-only ``Test.DejaFu.Conc.dontCheck`` function, and associated definitions: - * ``Test.DejaFu.Types.DontCheck`` - * ``Test.DejaFu.Types.WillDontCheck`` - * ``Test.DejaFu.Types.IllegalDontCheck`` - * ``Test.DejaFu.Types.isIllegalDontCheck`` + * ``Test.DejaFu.Types.DontCheck`` + * ``Test.DejaFu.Types.WillDontCheck`` + * ``Test.DejaFu.Types.IllegalDontCheck`` + * ``Test.DejaFu.Types.isIllegalDontCheck`` * (:pull:`219`) A snapshotting approach based on ``Test.DejaFu.Conc.dontCheck``: - * ``Test.DejaFu.Conc.runForDCSnapshot`` - * ``Test.DejaFu.Conc.runWithDCSnapshot`` - * ``Test.DejaFu.Conc.canDCSnapshot`` - * ``Test.DejaFu.Conc.threadsFromDCSnapshot`` + * ``Test.DejaFu.Conc.runForDCSnapshot`` + * ``Test.DejaFu.Conc.runWithDCSnapshot`` + * ``Test.DejaFu.Conc.canDCSnapshot`` + * ``Test.DejaFu.Conc.threadsFromDCSnapshot`` Changed ~~~~~~~ @@ -1011,10 +1039,10 @@ Added * (:pull:`145`) Thread action and lookahead values for bound threads: - * ``Test.DejaFu.Types.ForkOS`` - * ``Test.DejaFu.Types.IsCurrentThreadBound`` - * ``Test.DejaFu.Types.WillForkOS`` - * ``Test.DejaFu.Types.WillIsCurrentThreadBound`` + * ``Test.DejaFu.Types.ForkOS`` + * ``Test.DejaFu.Types.IsCurrentThreadBound`` + * ``Test.DejaFu.Types.WillForkOS`` + * ``Test.DejaFu.Types.WillIsCurrentThreadBound`` * (:issue:`155`) ``Test.DejaFu.Types`` and ``Test.DejaFu.Utils`` modules, each containing some of what was in ``Test.DejaFu.Common``. @@ -1064,15 +1092,15 @@ Removed * The ``IO`` specific testing functions: - * ``Test.DejaFu.autocheckIO`` - * ``Test.DejaFu.dejafuIO`` - * ``Test.DejaFu.dejafusIO`` - * ``Test.DejaFu.autocheckWayIO`` - * ``Test.DejaFu.dejafuWayIO`` - * ``Test.DejaFu.dejafusWayIO`` - * ``Test.DejaFu.dejafuDiscardIO`` - * ``Test.DejaFu.runTestM`` - * ``Test.DejaFu.runTestWayM`` + * ``Test.DejaFu.autocheckIO`` + * ``Test.DejaFu.dejafuIO`` + * ``Test.DejaFu.dejafusIO`` + * ``Test.DejaFu.autocheckWayIO`` + * ``Test.DejaFu.dejafuWayIO`` + * ``Test.DejaFu.dejafusWayIO`` + * ``Test.DejaFu.dejafuDiscardIO`` + * ``Test.DejaFu.runTestM`` + * ``Test.DejaFu.runTestWayM`` * The ``Test.DejaFu.Conc.ConcST`` type alias. @@ -1214,16 +1242,16 @@ Added * Failure predicates (also exported from ``Test.DejaFu``): - * ``Test.DejaFu.Common.isAbort`` - * ``Test.DejaFu.Common.isDeadlock`` - * ``Test.DejaFu.Common.isIllegalSubconcurrency`` - * ``Test.DejaFu.Common.isInternalError`` - * ``Test.DejaFu.Common.isUncaughtException`` + * ``Test.DejaFu.Common.isAbort`` + * ``Test.DejaFu.Common.isDeadlock`` + * ``Test.DejaFu.Common.isIllegalSubconcurrency`` + * ``Test.DejaFu.Common.isInternalError`` + * ``Test.DejaFu.Common.isUncaughtException`` * Thread action and lookahead values for ``threadDelay``: - * ``Test.DejaFu.Common.ThreadDelay`` - * ``Test.DejaFu.Common.WillThreadDelay`` + * ``Test.DejaFu.Common.ThreadDelay`` + * ``Test.DejaFu.Common.WillThreadDelay`` Changed ~~~~~~~ @@ -1350,8 +1378,8 @@ Performance * (:issue:`64`) Greatly reduce memory usage in systematic testing when discarding traces by using an alternative data structure. - * Old: ``O(max trace length * number of executions)`` - * New: ``O(max trace length * number of traces kept)`` + * Old: ``O(max trace length * number of executions)`` + * New: ``O(max trace length * number of traces kept)`` 0.7.1.0 - The Discard Release (2017-08-10) @@ -1365,15 +1393,15 @@ Added * (:issue:`90`) A way to selectively discard results or traces: - * Type: ``Test.DejaFu.SCT.Discard`` - * Functions: ``Test.DejaFu.SCT.runSCTDiscard``, - ``resultsSetDiscard``, ``sctBoundDiscard``, - ``sctUniformRandomDiscard``, and ``sctWeightedRandomDiscard``. + * Type: ``Test.DejaFu.SCT.Discard`` + * Functions: ``Test.DejaFu.SCT.runSCTDiscard``, ``resultsSetDiscard``, + ``sctBoundDiscard``, ``sctUniformRandomDiscard``, and + ``sctWeightedRandomDiscard``. * (:issue:`90`) Discarding variants of the testing functions: - * ``Test.DejaFu.dejafuDiscard`` - * ``Test.DejaFu.dejafuDiscardIO`` + * ``Test.DejaFu.dejafuDiscard`` + * ``Test.DejaFu.dejafuDiscardIO`` * (:issue:`90`) ``Test.DejaFu.Defaults.defaultDiscarder``. @@ -1439,13 +1467,12 @@ Added * Smart constructors for ``Test.DejaFu.SCT.Way`` (also re-exported from ``Test.DejaFu``): - * ``Test.DejaFu.SCT.systematically``, like the old - ``Systematically``. - * ``Test.DejaFu.SCT.randomly``, like the old ``Randomly``. - * ``Test.DejaFu.SCT.uniformly``, a new uniform (as opposed to - weighted) random scheduler. - * ``Test.DejaFu.SCT.swarmy``, like the old ``Randomly`` but which - can use the same weights for multiple executions. + * ``Test.DejaFu.SCT.systematically``, like the old ``Systematically``. + * ``Test.DejaFu.SCT.randomly``, like the old ``Randomly``. + * ``Test.DejaFu.SCT.uniformly``, a new uniform (as opposed to weighted) random + scheduler. + * ``Test.DejaFu.SCT.swarmy``, like the old ``Randomly`` but which can use the + same weights for multiple executions. Changed ~~~~~~~ @@ -1463,8 +1490,8 @@ Removed * The ``Test.DejaFu.SCT.Way`` type is now abstract, so its constructors are no longer exported: - * ``Test.DejaFu.SCT.Systematically`` - * ``Test.DejaFu.SCT.Randomly`` + * ``Test.DejaFu.SCT.Systematically`` + * ``Test.DejaFu.SCT.Randomly`` * The ``Test.DejaFu.SCT.sctPreBound``, ``sctFairBound``, and ``sctLengthBound`` functions. @@ -1571,25 +1598,25 @@ Added * ``NFData`` instances for: - * ``Test.DejaFu.Result`` - * ``Test.DejaFu.Common.ThreadId`` - * ``Test.DejaFu.Common.CRefId`` - * ``Test.DejaFu.Common.MVarId`` - * ``Test.DejaFu.Common.TVarId`` - * ``Test.DejaFu.Common.IdSource`` - * ``Test.DejaFu.Common.ThreadAction`` - * ``Test.DejaFu.Common.Lookahead`` - * ``Test.DejaFu.Common.ActionType`` - * ``Test.DejaFu.Common.TAction`` - * ``Test.DejaFu.Common.Decision`` - * ``Test.DejaFu.Common.Failure`` - * ``Test.DejaFu.Common.MemType`` - * ``Test.DejaFu.SCT.Bounds`` - * ``Test.DejaFu.SCT.PreemptionBound`` - * ``Test.DejaFu.SCT.FairBound`` - * ``Test.DejaFu.SCT.LengthBound`` - * ``Test.DejaFu.SCT.Way`` - * ``Test.DejaFu.STM.Result`` + * ``Test.DejaFu.Result`` + * ``Test.DejaFu.Common.ThreadId`` + * ``Test.DejaFu.Common.CRefId`` + * ``Test.DejaFu.Common.MVarId`` + * ``Test.DejaFu.Common.TVarId`` + * ``Test.DejaFu.Common.IdSource`` + * ``Test.DejaFu.Common.ThreadAction`` + * ``Test.DejaFu.Common.Lookahead`` + * ``Test.DejaFu.Common.ActionType`` + * ``Test.DejaFu.Common.TAction`` + * ``Test.DejaFu.Common.Decision`` + * ``Test.DejaFu.Common.Failure`` + * ``Test.DejaFu.Common.MemType`` + * ``Test.DejaFu.SCT.Bounds`` + * ``Test.DejaFu.SCT.PreemptionBound`` + * ``Test.DejaFu.SCT.FairBound`` + * ``Test.DejaFu.SCT.LengthBound`` + * ``Test.DejaFu.SCT.Way`` + * ``Test.DejaFu.STM.Result`` * ``Eq``, ``Ord``, and ``Show`` instances for ``Test.DejaFu.Common.IdSource``. @@ -1661,8 +1688,8 @@ Added * Thread action and lookahead values for ``tryReadMVar``: - * ``Test.DejaFu.Common.TryReadMVar`` - * ``Test.DejaFu.Common.WillTryReadMVar`` + * ``Test.DejaFu.Common.TryReadMVar`` + * ``Test.DejaFu.Common.WillTryReadMVar`` * The testing-only ``Test.DejaFu.Conc.subconcurrency`` function. diff --git a/dejafu/README.markdown b/dejafu/README.markdown index c1f6111..70c569e 100644 --- a/dejafu/README.markdown +++ b/dejafu/README.markdown @@ -14,7 +14,7 @@ dejafu - [Release notes](#release-notes) - [Questions, feedback, discussion](#questions-feedback-discussion) - [Bibliography](#bibliography) -- **[The website!](http://dejafu.readthedocs.io/)** +- **[The website!](https://dejafu.docs.barrucadu.co.uk/)** Déjà Fu is a unit-testing library for concurrent Haskell programs. Tests are deterministic and expressive, making it easy and convenient diff --git a/dejafu/Test/DejaFu.hs b/dejafu/Test/DejaFu.hs index 2c8f5a1..58e9c24 100644 --- a/dejafu/Test/DejaFu.hs +++ b/dejafu/Test/DejaFu.hs @@ -13,7 +13,7 @@ are written using the package's 'MonadConc' typeclass. For more in-depth documentation, including migration guides from -earlier versions of dejafu, see the . __A first test:__ This is a simple concurrent program which forks two diff --git a/dejafu/dejafu.cabal b/dejafu/dejafu.cabal index db73720..7a25644 100755 --- a/dejafu/dejafu.cabal +++ b/dejafu/dejafu.cabal @@ -13,7 +13,7 @@ description: package by enabling you to deterministically test your concurrent programs. . - See the or README for more. + See the or README for more. homepage: https://github.com/barrucadu/dejafu license: MIT diff --git a/doc/.gitignore b/doc/.gitignore deleted file mode 100644 index e35d885..0000000 --- a/doc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_build diff --git a/doc/advanced.rst b/doc/advanced.rst deleted file mode 100644 index f2e7ac9..0000000 --- a/doc/advanced.rst +++ /dev/null @@ -1,102 +0,0 @@ -Advanced Usage -============== - -Déjà Fu tries to have a sensible set of defaults, but there are some -times when the defaults are not suitable. There are a lot of knobs -provided to tweak how things work. - - -.. _settings: - -Execution settings ------------------- - -The ``autocheckWithSettings``, ``dejafuWithSettings``, and -``dejafusWithSettings`` let you provide a ``Settings`` value, which -controls some of Déjà Fu's behaviour: - -.. code-block:: haskell - - dejafuWithSettings mySettings "Assert the thing holds" myPredicate myAction - -The available settings are: - -* **"Way"**, how to explore the behaviours of the program under test. - -* **Length bound**, a cut-off point to terminate an execution even if - it's not done yet. - -* **Memory model**, which affects how non-synchronised operations, - such as ``readIORef`` and ``writeIORef`` behave. - -* **Discarding**, which allows throwing away uninteresting results, - rather than keeping them around in memory. - -* **Early exit**, which allows exiting as soon as a result matching a - predicate is found. - -* **Representative traces**, keeping only one execution trace for each - distinct result. - -* **Trace simplification**, rewriting execution traces into a simpler - form (particularly effective with the random testing). - -* **Safe IO**, pruning needless schedules when your IO is only used to - manage thread-local state. - -See the ``Test.DejaFu.Settings`` module for more information. - - -.. _performance: - -Performance tuning ------------------- - -* Are you happy to trade space for time? - - Consider computing the results once and running multiple - predicates over the output: this is what ``dejafus`` / - ``testDejafus`` / etc does. - -* Can you sacrifice completeness? - - Consider using the random testing functionality. See the ``*WithSettings`` - functions. - -* Would strictness help? - - Consider using the strict functions in ``Test.DejaFu.SCT`` (the - ones ending with a ``'``). - -* Do you just want the set of results, and don't care about traces? - - Consider using ``Test.DejaFu.SCT.resultsSet``. - -* Do you know something about the sort of results you care about? - - Consider discarding results you *don't* care about. See the - ``*WithSettings`` functions in ``Test.DejaFu``, ``Test.DejaFu.SCT``, - and ``Test.{HUnit,Tasty}.DejaFu``. - -For example, let's say you want to know if your test case deadlocks, -but you don't care about the execution trace, and you are going to -sacrifice completeness because your possible state-space is huge. You -could do it like this: - -.. code-block:: haskell - - dejafuWithSettings - ( set ldiscard - -- "efa" == "either failure a", discard everything but deadlocks - (Just $ \efa -> Just (if either isDeadlock (const False) efa then DiscardTrace else DiscardResultAndTrace)) - . set lway - -- try 10000 executions with random scheduling - (randomly (mkStdGen 42) 10000) - $ defaultSettings - ) - -- the name of the test - "Never Deadlocks" - -- the predicate to check - deadlocksNever - -- your test case - testCase diff --git a/doc/changelog_concurrency.rst b/doc/changelog_concurrency.rst deleted file mode 120000 index 2afadef..0000000 --- a/doc/changelog_concurrency.rst +++ /dev/null @@ -1 +0,0 @@ -../concurrency/CHANGELOG.rst \ No newline at end of file diff --git a/doc/changelog_dejafu.rst b/doc/changelog_dejafu.rst deleted file mode 120000 index 8837616..0000000 --- a/doc/changelog_dejafu.rst +++ /dev/null @@ -1 +0,0 @@ -../dejafu/CHANGELOG.rst \ No newline at end of file diff --git a/doc/changelog_hunit-dejafu.rst b/doc/changelog_hunit-dejafu.rst deleted file mode 120000 index 8758380..0000000 --- a/doc/changelog_hunit-dejafu.rst +++ /dev/null @@ -1 +0,0 @@ -../hunit-dejafu/CHANGELOG.rst \ No newline at end of file diff --git a/doc/changelog_tasty-dejafu.rst b/doc/changelog_tasty-dejafu.rst deleted file mode 120000 index 242ffb7..0000000 --- a/doc/changelog_tasty-dejafu.rst +++ /dev/null @@ -1 +0,0 @@ -../tasty-dejafu/CHANGELOG.rst \ No newline at end of file diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index 927963c..0000000 --- a/doc/conf.py +++ /dev/null @@ -1,171 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Déjà Fu documentation build configuration file, created by -# sphinx-quickstart on Tue Aug 15 19:55:19 2017. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - -import os - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = ['sphinx.ext.extlinks'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -source_parsers = { -} - -source_suffix = ['.rst'] - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Déjà Fu' -copyright = u'2017--2018, Michael Walker' -author = u'Michael Walker' - -# External link destinations -_repo = 'https://github.com/barrucadu/dejafu/' -extlinks = { - 'commit': (_repo + 'commit/%s', 'commit '), - 'issue': (_repo + 'issues/%s', 'issue #'), - 'pull': (_repo + 'pull/%s', 'pull request #'), - 'tag': (_repo + 'releases/tag/%s', 'tag '), - 'github': (_repo + '%s', ''), - 'u': ('https://github.com/%s', ''), - 'hackage': ('https://hackage.haskell.org/package/%s', ''), - 'stackage': ('https://www.stackage.org/package/%s', ''), -} - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = u'HEAD' -# The full version, including alpha/beta/rc tags. -release = u'HEAD' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = os.environ['SPHINX_THEME'] if 'SPHINX_THEME' in os.environ else 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - - -# -- Options for HTMLHelp output ------------------------------------------ - -# Output file base name for HTML help builder. -htmlhelp_basename = 'DejaFudoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'DejaFu.tex', u'Déjà Fu Documentation', - u'Michael Walker', 'manual'), -] - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'dejafu', u'Déjà Fu Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'DejaFu', u'Déjà Fu Documentation', - author, 'DejaFu', 'Concurrency testing for Haskell.', - 'Miscellaneous'), -] - - - diff --git a/doc/contributing.rst b/doc/contributing.rst deleted file mode 100644 index a86e977..0000000 --- a/doc/contributing.rst +++ /dev/null @@ -1,219 +0,0 @@ -Contributing -============ - -Thanks for caring about Déjà Fu! - - -Ways to contribute ------------------- - -Déjà Fu is a project under active development, there's always -something to do. Here's a list of ideas to get you started: - -* Submit bug reports. -* Submit feature requests. -* Got a particularly slow test case which you think should be faster? - Open an issue for that too. -* Blog about how and why you use Déjà Fu. -* Check if any bugs which have been open for a while are still bugs. - -If you want to contribute code, you could: - -* Tackle one of the issues tagged :github:`"good first issue" `. -* Tackle a bigger issue, perhaps one of the :github:`roadmap issues `! -* Run code coverage and try to fix a gap in the tests. -* Profile the test suite and try to improve a slow function. - -:github:`Roadmap issues ` are priority issues (in my -opinion), so help with those is especially appreciated. - -If you have a support question, you can talk to me on IRC (#haskell on -freenode) or send an email (mike@barrucadu.co.uk) rather than opening -an issue. But maybe your question is a bug report about poor -documentation in disguise! - - -Making the change ------------------ - -1. Talk to me! - - I don't bite, and chances are I can quickly tell you where you - should start. It's better to ask what seems like a stupid question - than to waste a lot of time on the wrong approach. - -2. Make the change. - - Figure out what needs to be changed, how to change it, and do it. - If you're fixing a bug, make sure to add a minimal reproduction to - Cases.Regressions in dejafu-tests. - -3. Document the change. - - All top-level definitions should have a `Haddock`__ comment - explaining what it does. If you've added or changed a top-level - function, consider commenting its arguments too. - - If you've added a top-level definition, or changed one in a - backwards-incompatible way, add an ``@since unreleased`` Haddock - comment to it. This is to help prevent me from missing things when - I update the changelog ahead of a release. - -4. Submit a PR. - - GitHub Actions will run some checks, which might prompt further - action. You should expect a response from me in a day or two. - -Don't worry about your PR being perfect the first time. We'll work -through any issues together, to ensure that Déjà Fu gets the best code -it can. - -.. __: https://github.com/aisamanra/haddock-cheatsheet - - -Coding style ------------- - -There isn't really a prescribed style. It's not quite the wild west -though; keep these three rules in mind: - -1. Be consistent. -2. Run :hackage:`stylish-haskell` to format import lists. -3. Use :hackage:`hlint` and :hackage:`weeder` and fix lints unless you - have a good reason not to. - -GitHub Actions runs stylish-haskell and hlint on all PRs. - - -Coverage --------- - -`hpc`__ can generate a coverage report from the execution of -dejafu-tests: - -.. code-block:: none - - $ stack build --coverage - $ stack exec dejafu-tests - $ stack hpc report --all dejafu-tests.tix - -This will print some stats and generate an HTML coverage report: - -.. code-block:: none - - Generating combined report - 52% expressions used (4052/7693) - 48% boolean coverage (63/129) - 43% guards (46/106), 31 always True, 9 always False, 20 unevaluated - 68% 'if' conditions (11/16), 2 always True, 3 unevaluated - 85% qualifiers (6/7), 1 unevaluated - 61% alternatives used (392/635) - 80% local declarations used (210/261) - 26% top-level declarations used (280/1063) - The combined report is available at /home/barrucadu/projects/dejafu/.stack-work/install/x86_64-linux/nightly-2016-06-20/8.0.1/hpc/combined/custom/hpc_index.html - -The highlighted code in the HTML report emphasises branch coverage: - -* Red means a branch was evaluated as always false. -* Green means a branch was evaluated as always true. -* Yellow means an expression was never evaluated. - -See also the `stack coverage documentation`__. - -.. __: https://wiki.haskell.org/Haskell_program_coverage -.. __: https://docs.haskellstack.org/en/latest/coverage/ - - -Performance ------------ - -GHC can generate performance statistics from the execution of -dejafu-tests: - -.. code-block:: none - - $ stack build --profile - $ stack exec -- dejafu-tests +RTS -p - $ less dejafu-tests.prof - -This prints a detailed breakdown of where memory and time are being -spent: - -.. code-block:: none - - Mon Mar 20 19:26 2017 Time and Allocation Profiling Report (Final) - - dejafu-tests +RTS -p -RTS - - total time = 105.94 secs (105938 ticks @ 1000 us, 1 processor) - total alloc = 46,641,766,952 bytes (excludes profiling overheads) - - COST CENTRE MODULE %time %alloc - - findBacktrackSteps.doBacktrack.idxs' Test.DejaFu.SCT.Internal 21.9 12.0 - == Test.DejaFu.Common 12.4 0.0 - yieldCount.go Test.DejaFu.SCT 12.1 0.0 - dependent' Test.DejaFu.SCT 5.1 0.0 - runThreads.go Test.DejaFu.Conc.Internal 2.7 4.1 - [...] - -Be careful, however! Compiling with profiling can significantly -affect the behaviour of a program! Use profiling to get an idea of -where the hot spots are, but make sure to confirm with a non-profiled -build that things are actually getting faster. - -If you compile with ``-rtsopts`` you can get some basic stats from a -non-profiled build: - -.. code-block:: none - - $ stack exec -- dejafu-tests +RTS -s - - [...] - 86,659,658,504 bytes allocated in the heap - 13,057,037,448 bytes copied during GC - 13,346,952 bytes maximum residency (4743 sample(s)) - 127,824 bytes maximum slop - 37 MB total memory in use (0 MB lost due to fragmentation) - - Tot time (elapsed) Avg pause Max pause - Gen 0 78860 colls, 0 par 32.659s 32.970s 0.0004s 0.0669s - Gen 1 4743 colls, 0 par 3.043s 3.052s 0.0006s 0.0086s - - TASKS: 174069 (174065 bound, 4 peak workers (4 total), using -N1) - - SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled) - - INIT time 0.001s ( 0.001s elapsed) - MUT time 98.685s (101.611s elapsed) - GC time 35.702s ( 36.022s elapsed) - EXIT time 0.001s ( 0.007s elapsed) - Total time 134.388s (137.640s elapsed) - - Alloc rate 878,145,635 bytes per MUT second - - Productivity 73.4% of total user, 73.8% of total elapsed - - -Heap profiling --------------- - -GHC can tell you where the memory is going: - -.. code-block:: none - - $ stack build --profile - $ stack exec -- dejafu-tests +RTS -hc - $ hp2ps -c dejafu-tests.hp - -This will produce a graph of memory usage over time, as a postscript -file, broken down by cost-centre which produced the data. There are a -few different views: - -- ``-hm`` breaks down the graph by module -- ``-hd`` breaks down the graph by closure description -- ``-hy`` breaks down the graph by type - -I typically find ``-hd`` and ``-hy`` most useful. If you're feeling -particularly brave, you can try ``-hr``, which is intended to help -track down memory leaks caused by unevaluated thunks. diff --git a/doc/getting_started.rst b/doc/getting_started.rst deleted file mode 100644 index 335bc7f..0000000 --- a/doc/getting_started.rst +++ /dev/null @@ -1,161 +0,0 @@ -Getting Started -=============== - - [Déjà Fu is] A martial art in which the user's limbs move in time - as well as space, […] It is best described as "the feeling that - you have been kicked in the head this way before" - - **Terry Pratchett, Thief of Time** - -Déjà Fu is a unit-testing library for concurrent Haskell programs. -Tests are deterministic and expressive, making it easy and convenient -to test your threaded code. Available on :github:`GitHub <>`, -:hackage:`Hackage `, and :stackage:`Stackage `. - -Features: - -* An abstraction over the concurrency functionality in ``IO`` -* Deterministic testing of nondeterministic code -* Both complete (slower) and incomplete (faster) modes -* A unit-testing-like approach to writing test cases -* A property-testing-like approach to comparing stateful operations -* Testing of potentially nonterminating programs -* Integration with :hackage:`HUnit` and :hackage:`tasty` - -There are a few different packages under the Déjà Fu umbrella: - -.. csv-table:: - :header: "Package", "Version", "Summary" - - ":hackage:`concurrency`", "1.11.0.3", "Typeclasses, functions, and data types for concurrency and STM" - ":hackage:`dejafu`", "2.4.0.5", "Systematic testing for Haskell concurrency" - ":hackage:`hunit-dejafu`", "2.0.0.6", "Déjà Fu support for the HUnit test framework" - ":hackage:`tasty-dejafu`", "2.1.0.1", "Déjà Fu support for the tasty test framework" - - -Installation ------------- - -Install from Hackage globally: - -.. code-block:: none - - $ cabal install dejafu - -Or add it to your cabal file: - -.. code-block:: none - - build-depends: ... - , dejafu - -Or to your package.yaml: - -.. code-block:: none - - dependencies: - ... - - dejafu - - -Quick start guide ------------------ - -Déjà Fu supports unit testing, and comes with a helper function -called ``autocheck`` to look for some common issues. Let's see it in -action: - -.. code-block:: haskell - - import Control.Concurrent.Classy - - myFunction :: MonadConc m => m String - myFunction = do - var <- newEmptyMVar - fork (putMVar var "hello") - fork (putMVar var "world") - readMVar var - -That ``MonadConc`` is a typeclass abstraction over concurrency, but -we'll get onto that shortly. First, the result of testing: - -.. code-block:: none - - > autocheck myFunction - [pass] Successful - [fail] Deterministic - "hello" S0----S1--S0-- - - "world" S0----S2--S0-- - False - -There are no concurrency errors, which is good; but the program is (as -you probably spotted) nondeterministic! - -Along with each result, Déjà Fu gives us a representative execution -trace in an abbreviated form. ``Sn`` means that thread ``n`` started -executing, and ``Pn`` means that thread ``n`` pre-empted the -previously running thread. - - -Why Déjà Fu? ------------- - -Testing concurrent programs is difficult, because in general they are -nondeterministic. This leads to people using work-arounds like -running their testsuite many thousands of times; or running their -testsuite while putting their machine under heavy load. - -These approaches are inadequate for a few reasons: - -* **How many runs is enough?** When you are just hopping to spot a bug - by coincidence, how do you know to stop? -* **How do you know if you've fixed a bug you saw previously?** - Because the scheduler is a black box, you don't know if the - previously buggy schedule has been re-run. -* **You won't actually get that much scheduling variety!** Operating - systems and language runtimes like to run threads for long periods - of time, which reduces the variety you get (and so drives up the - number of runs you need). - -Déjà Fu addresses these points by offering *complete* testing. You -can run a test case and be guaranteed to find all results with some -bounds. These bounds can be configured, or even disabled! The -underlying approach used is smarter than merely trying all possible -executions, and will in general explore the state-space quickly. - -If your test case is just too big for complete testing, there is also -a random scheduling mode, which is necessarily *incomplete*. However, -Déjà Fu will tend to produce much more scheduling variety than just -running your test case in ``IO`` a bunch of times, and so bugs will -tend to crop up sooner. Furthermore, as you get execution traces out, -you can be certain that a bug has been fixed by following the trace by -eye. - -**If you'd like to get involved with Déjà Fu**, check out :github:`the -"good first issue" label on the issue tracker -`. - - -Questions, feedback, discussion -------------------------------- - -* For general help talk to me in IRC (barrucadu in #haskell) or shoot - me an email (mike@barrucadu.co.uk) -* For bugs, issues, or requests, please :issue:`file an issue <>`. - - -Bibliography ------------- - -Déjà Fu has been produced as part of my Ph.D work, and wouldn't be -possible without prior research. Here are the core papers: - -* Bounded partial-order reduction, K. Coons, M. Musuvathi, - and K. McKinley (2013) -* Dynamic Partial Order Reduction for Relaxed Memory - Models, N. Zhang, M. Kusano, and C. Wang (2015) -* Concurrency Testing Using Schedule Bounding: an Empirical - Study, P. Thompson, A. Donaldson, and A. Betts (2014) -* On the Verification of Programs on Relaxed Memory - Models, A. Linden (2014) diff --git a/doc/ghc.rst b/doc/ghc.rst deleted file mode 100644 index 9f07ca5..0000000 --- a/doc/ghc.rst +++ /dev/null @@ -1,70 +0,0 @@ -Supported GHC Versions -====================== - -Déjà Fu supports the latest four GHC releases, at least. For testing -purposes, we use Stackage snapshots as a proxy for GHC versions. The -currently supported versions are: - -.. csv-table:: - :header: "GHC", "Stackage", "base" - - "9.6", "Nightly 2021-07-01", "4.18.0.0" - "9.4", "LTS 21.0", "4.17.0.0" - "9.2", "LTS 20.0", "4.16.0.0" - "9.0", "LTS 19.0", "4.15.0.0" - "8.10", "LTS 17.0", "4.14.1.0" - "8.8", "LTS 15.0", "4.13.0.0" - "8.6", "LTS 14.0", "4.12.0.0" - "8.4", "LTS 12.0", "4.11.0.0" - "8.2", "LTS 10.0", "4.10.1.0" - -In practice, we may *compile with* older versions of GHC, but keeping -them working is not a priority. - - -Adding new GHC releases ------------------------ - -When a new version of GHC is released, we need to make some changes -for everything to go smoothly. In general, these changes should only -cause a **patch level version bump**. - -1. Bump the upper bound of :hackage:`base` and set up any needed - conditional compilation -2. Add the GHC and base versions to the table. -3. Remove any unsupported versions from the table. -4. Make a patch release. - -A new GHC release won't get a Stackage snapshot for little while. When it -does: - -1. Add the snapshot to the GitHub Actions configuration. -2. Update the resolver in the stack.yaml. -3. Put the snapshot in the table. - - -Dropping old GHC releases -------------------------- - -When we want to drop an unsupported version of GHC, we need to bump -the version bound on :hackage:`base` to preclude it. This is a -backwards-incompatible change which causes a **major version bump**. - -1. Remove the dropped GHC version from the GitHub Actions - configuration. -2. Bump the lower bound of :hackage:`base`. -3. Look through the other dependencies. Some may not work with our - new lower bound on :hackage:`base`, so we should bump those too. -4. Remove any now-irrelevant conditional compilation (mostly CPP, but - there may also be some cabal file bits). -5. Make whatever change required the bump. -6. Make a major release. - -GHC versions shouldn't be dropped just because we can, but here are -some good reasons to do it: - -* We want to bump the lower bounds of a dependency to a version which - doesn't support that GHC. -* We want to add a new dependency which doesn't support that GHC. -* The conditional compilation needed to keep that GHC working is - getting confusing. diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 9a78885..0000000 --- a/doc/index.rst +++ /dev/null @@ -1,42 +0,0 @@ -This is Déjà Fu -=============== - - [Déjà Fu is] A martial art in which the user's limbs move in time - as well as space, […] It is best described as "the feeling that - you have been kicked in the head this way before" - - **Terry Pratchett, Thief of Time** - -.. toctree:: - :maxdepth: 2 - :caption: User Documentation - - getting_started - typeclass - unit_testing - refinement_testing - advanced - -.. toctree:: - :maxdepth: 2 - :caption: Migration Guides - - migration_1x_2x - migration_0x_1x - -.. toctree:: - :maxdepth: 2 - :caption: Developer Documentation - - contributing - ghc - release_process - -.. toctree:: - :maxdepth: 2 - :caption: Release Notes - - concurrency - dejafu - hunit-dejafu - tasty-dejafu diff --git a/doc/migration_0x_1x.rst b/doc/migration_0x_1x.rst deleted file mode 100644 index 6a7cace..0000000 --- a/doc/migration_0x_1x.rst +++ /dev/null @@ -1,117 +0,0 @@ -0.x to 1.x -========== - -:hackage:`dejafu-1.0.0.0` is a super-major release which breaks -compatibility with :hackage:`dejafu-0.x ` quite -significantly, but brings with it support for bound threads, and -significantly improves memory usage in the general case. - -Highlights reel: - -* Most predicates now only need to keep around the failures, rather - than all results. -* Support for bound threads (with :hackage:`concurrency-1.3.0.0`). -* The ``ST`` / ``IO`` interface duplication is gone, everything is now - monadic. -* Function parameter order is closer to other testing libraries. -* Much improved API documentation. - -See the changelogs for the full details. - - -``ST`` and ``IO`` functions ---------------------------- - -There is only one set of functions now. Testing bound threads -requires being able to fork actual threads, so testing with ``ST`` is -no longer possible. The ``ConcST`` type is gone, there is only -``ConcIO``. - -For :hackage:`dejafu` change: - -* ``autocheckIO`` to ``autocheck`` -* ``dejafuIO`` to ``dejafu`` -* ``dejafusIO`` to ``dejafus`` -* ``autocheckWayIO`` to ``autocheckWay`` -* ``dejafuWayIO`` to ``dejafuWay`` -* ``dejafusWayIO`` to ``dejafusWay`` -* ``dejafuDiscardIO`` to ``dejafuDiscard`` -* ``runTestM`` to ``runTest`` -* ``runTestWayM`` to ``runTestWay`` - -If you relied on being able to get a pure result from the ``ConcST`` -functions, you can no longer do this. - -For :hackage:`hunit-dejafu` and :hackage:`tasty-dejafu` change: - -* ``testAutoIO`` to ``testAuto`` -* ``testDejafuIO`` to ``testDejafu`` -* ``testDejafusIO`` to ``testDejafus`` -* ``testAutoWayIO`` to ``testAutoWay`` -* ``testDejafuWayIO`` to ``testDejafuWay`` -* ``testDejafusWayIO`` to ``testDejafusWay`` -* ``testDejafuDiscardIO`` to ``testDejafuDiscard`` - - -Function parameter order ------------------------- - -Like :hackage:`HUnit`, the monadic action to test is now the last -parameter of the testing functions. This makes it convenient to write -tests without needing to define the action elsewhere. - -For :hackage:`dejafu` change: - -* ``dejafu ma (s, p)`` to ``dejafu s p ma`` -* ``dejafus ma ps`` to ``dejafus ps ma`` -* ``dejafuWay way mem ma (s, p)`` to ``dejafuWay way mem s p ma`` -* ``dejafusWay way mem ma ps`` to ``dejafuWay way mem ps ma`` -* ``dejafuDiscard d way mem ma (s, p)`` to ``dejafuDiscard d way mem s p ma`` - -For :hackage:`hunit-dejafu` and :hackage:`tasty-dejafu` change: - -* ``testDejafu ma s p`` to ``testDejafu s p ma`` -* ``testDejafus ma ps`` to ``testDejafus ps ma`` -* ``testDejafuWay way mem ma s p`` to ``testDejafuWay way mem s p ma`` -* ``testDejafusWay way mem ma ps`` to ``testDejafusWay way mem ps ma`` -* ``testDejafuDiscard d way mem ma s p`` to ``testDejafuDiscard d way mem s p ma`` - - -Predicates ----------- - -The ``Predicate a`` type is now an alias for ``ProPredicate a a``, -defined like so: - -.. code-block:: haskell - - data ProPredicate a b = ProPredicate - { pdiscard :: Either Failure a -> Maybe Discard - -- ^ Selectively discard results before computing the result. - , peval :: [(Either Failure a, Trace)] -> Result b - -- ^ Compute the result with the un-discarded results. - } - -If you use the predicate helper functions to construct a predicate, -you do not need to change anything (and should get a nice reduction in -your resident memory usage). If you supply a function directly, you -can recover the old behaviour like so: - -.. code-block:: haskell - - old :: ([(Either Failure a, Trace)] -> Result a) -> ProPredicate a a - old p = ProPredicate - { pdiscard = const Nothing - , peval = p - } - -The ``alwaysTrue2`` helper function is gone. If you use it, use -``alwaysSameOn`` or ``alwaysSameBy`` instead. - - -Need help? ----------- - -* For general help talk to me in IRC (barrucadu in #haskell) or shoot - me an email (mike@barrucadu.co.uk) -* For bugs, issues, or requests, please :issue:`file an issue <>`. diff --git a/doc/migration_1x_2x.rst b/doc/migration_1x_2x.rst deleted file mode 100644 index 207fa4b..0000000 --- a/doc/migration_1x_2x.rst +++ /dev/null @@ -1,116 +0,0 @@ -1.x to 2.x -========== - -:hackage:`dejafu-2.0.0.0` is a super-major release which breaks -compatibility with :hackage:`dejafu-1.x `. - -Highlights reel: - -* Test cases are written in terms of a new ``Program`` type. -* The ``Failure`` type has been replaced with a ``Condition`` type - (actually in 1.12). -* Random testing takes an optional length bound. -* Atomically-checked invariants over shared mutable state. - -See the changelogs for the full details. - - -The ``Program`` type --------------------- - -The ``ConcT`` type is now an alias for ``Program Basic``. - -A ``Program Basic`` has all the instances ``ConcT`` did, defined using -the ``~`` instance trick, so this shouldn't be a breaking change: - -.. code-block:: haskell - - instance (pty ~ Basic) => MonadTrans (Program pty) - instance (pty ~ Basic) => MonadCatch (Program pty n) - instance (pty ~ Basic) => MonadThrow (Program pty n) - instance (pty ~ Basic) => MonadMask (Program pty n) - instance (pty ~ Basic, Monad n) => MonadConc (Program pty n) - instance (pty ~ Basic, MonadIO n) => MonadIO (Program pty n) - -The ``dontCheck`` function has been removed in favour of -``withSetup``: - -.. code-block:: haskell - - do x <- dontCheck setup - action x - - -- becomes - - withSetup setup action - -The ``subconcurrency`` function has been removed in favour of -``withSetupAndTeardown``: - -.. code-block:: haskell - - do x <- setup - y <- subconcurrency (action x) - teardown x y - - -- becomes - - withSetupAndTeardown setup teardown action - -The ``dontCheck`` and ``subconcurrency`` functions used to throw -runtime errors if nested. This is not possible with ``withSetup`` and -``withSetupAndTeardown`` due to their types: - -.. code-block:: haskell - - withSetup - :: Program Basic n x - -- ^ Setup action - -> (x -> Program Basic n a) - -- ^ Main program - -> Program (WithSetup x) n a - - withSetupAndTeardown - :: Program Basic n x - -- ^ Setup action - -> (x -> Either Condition y -> Program Basic n a) - -- ^ Teardown action - -> (x -> Program Basic n y) - -- ^ Main program - -> Program (WithSetupAndTeardown x y) n a - -Previously, multiple calls to ``subconcurrency`` could be sequenced in -the same test case. This is not possible using -``withSetupAndTeardown``. If you rely on this behaviour, please -:issue:`file an issue <>`. - - -The ``Condition`` type ----------------------- - -This is a change in :hackage:`dejafu-1.12.0.0`, but the alias -``Failure = Condition`` is removed in :hackage:`dejafu-2.0.0.0`. - -* The ``STMDeadlock`` and ``Deadlock`` constructors have been merged. -* Internal errors have been split into the ``Error`` type and are - raised as exceptions, instead of being returned as conditions. - -The name "failure" has been a recurring source of confusion, because -an individual execution can "fail" without the predicate as a whole -failing. My hope is that the more neutral "condition" will prevent -this confusion. - - -Deprecated functions --------------------- - -All the deprecated special-purpose functions have been removed. Use -more general ``*WithSettings`` functions instead. - - -Need help? ----------- - -* For general help talk to me in IRC (barrucadu in #haskell) or shoot - me an email (mike@barrucadu.co.uk) -* For bugs, issues, or requests, please :issue:`file an issue <>`. diff --git a/doc/refinement_testing.rst b/doc/refinement_testing.rst deleted file mode 100644 index c355a64..0000000 --- a/doc/refinement_testing.rst +++ /dev/null @@ -1,152 +0,0 @@ -Refinement Testing -================== - -Déjà Fu also supports a form of property-testing where you can check -things about the side-effects of stateful operations. For example, we -can assert that ``readMVar`` is equivalent to sequencing ``takeMVar`` -and ``putMVar`` like so: - -.. code-block:: haskell - - prop_mvar_read_take_put = - sig readMVar `equivalentTo` sig (\v -> takeMVar v >>= putMVar v) - -Given the signature function, ``sig``, defined in the next section. -If we check this, our property fails! - -.. code-block:: none - - > check prop_mvar_read_take_put - *** Failure: (seed Just 0) - left: [(Nothing,Just 0)] - right: [(Nothing,Just 0),(Just Deadlock,Just 0)] - False - -This is because ``readMVar`` is atomic, whereas sequencing -``takeMVar`` with ``putMVar`` is not, and so another thread can -interfere with the ``MVar`` in the middle. The ``check`` and -``equivalentTo`` functions come from ``Test.DejaFu.Refinement`` (also -re-exported from ``Test.DejaFu``). - - -Signatures ----------- - -A signature tells the property-tester something about the state your -operation acts upon, it has a few components: - -.. code-block:: haskell - - data Sig s o x = Sig - { initialise :: x -> ConcIO s - , observe :: s -> x -> ConcIO o - , interfere :: s -> x -> ConcIO () - , expression :: s -> ConcIO () - } - -* ``s`` is the **state type**, it's the thing which your operations - mutate. For ``readMVar``, the state is some ``MVar a``. - -* ``o`` is the **observation type**, it's some pure (and comparable) - proxy for a snapshot of your mutable state. For ``MVar a``, the - observation is probably a ``Maybe a``. - -* ``x`` is the **seed type**, it's some pure value used to construct - the initial mutable state. For ``MVar a``, the seed is probably a - ``Maybe a``. - -* ``ConcIO`` is just one of the instances of ``MonadConc`` that Déjà - Fu defines for testing purposes. Just write code polymorphic in the - monad as usual, and all will work. - -The ``initialise``, ``observe``, and ``expression`` functions should -be self-explanatory, but the ``interfere`` one may not be. It's the -job of the ``interfere`` function to change the state in some way; -it's run concurrently with the expression, to simulate the -nondeterministic action of other threads. - -Here's a concrete example for our ``MVar`` example: - -.. code-block:: haskell - - sig :: (MVar ConcIO Int -> ConcIO a) -> Sig (MVar ConcIO Int) (Maybe Int) (Maybe Int) - sig e = Sig - { initialise = maybe newEmptyMVar newMVar - , observe = \v _ -> tryTakeMVar v - , interfere = \v s -> tryTakeMVar v >> maybe (pure ()) (\x -> void $ tryPutMVar v (x * 1000)) s - , expression = void . e - } - -The ``observe`` function should be deterministic, but as it is run -after the normal execution ends, it may have side-effects on the -state. The ``interfere`` function can do just about anything [#]_, -but a poor one may result in the property-checker being unable to -distinguish between atomic and nonatomic expressions. - -.. [#] There are probably some concrete rules for a good function, but - I haven't figured them out yet. - - -Properties ----------- - -A property is a pair of signatures linked by one of three provided -functions. These functions are: - -.. csv-table:: - :header: "Function", "Operator", "Checks that..." - - "``equivalentTo``", "``===``", "... the left and right have exactly the same behaviours" - "``refines``", "``=>=``", "... every behaviour of the left is also a behaviour of the right" - "``strictlyRefines``", "``->-``", "... ``left =>= right`` holds but ``left === right`` does not" - -The signatures can have different state types, as long as the seed and -observation types are the same. This lets you compare different -implementations of the same idea: for example, comparing a concurrent -stack implemented using ``MVar`` with one implemented using ``IORef``. - -Properties can have parameters, given in the obvious way: - -.. code-block:: haskell - - check $ \a b c -> sig1 ... `op` sig2 ... - -Under the hood, seed and parameter values are generated using the -:hackage:`leancheck` package, an enumerative property-based testing -library. This means that any types you use will need to have a -``Listable`` instance. - -You can also think about the three functions in terms of sets of -results, where a result is a ``(Maybe Failure, o)`` value. A -``Failure`` is something like deadlocking, or being killed by an -exception; ``o`` is the observation type. An observation is always -made, even if execution of the expression fails. - -.. csv-table:: - :header: "Function", "Result-set operation" - - "``refines``", "For all seed and parameter assignments, subset-or-equal" - "``strictlyRefines``", "For at least one seed and parameter assignment, proper subset; for all others, subset-or-equal" - "``equivalentTo``", "For all seed and parameter assignments, equality" - -Finally, there is an ``expectFailure`` function, which inverts the -expected result of a property. - -The Déjà Fu testsuite has :github:`a collection of refinement -properties -`, -which may help you get a feel for this sort of testing. - - -Using HUnit and Tasty ---------------------- - -As for unit testing, :hackage:`HUnit` and :hackage:`tasty` integration -is provided for refinement testing in the :hackage:`hunit-dejafu` and -:hackage:`tasty-dejafu` packages. - -The ``testProperty`` function is used to check properties. Our example from the start becomes: - -.. code-block:: haskell - - testProperty "Read is equivalent to Take then Put" prop_mvar_read_take_put diff --git a/doc/release_process.rst b/doc/release_process.rst deleted file mode 100644 index 7b7f6bc..0000000 --- a/doc/release_process.rst +++ /dev/null @@ -1,64 +0,0 @@ -Release Process -=============== - -1. Figure out what the next version number is. See the PVP_ page if - unsure. - -2. Update version numbers in the relevant cabal files: - - * Update the ``version`` field - * Update the ``tag`` in the ``source-repository`` block - -3. Fill in all ``@since unreleased`` Haddock comments with the - relevant version number. - -4. Update version numbers in the tables in the README and the Getting - Started page. - -5. Ensure the relevant CHANGELOG files have all the entries they - should. - -6. Add the release information to the relevant CHANGELOG files: - - * Change the ``unreleased`` title to the version number - * Add the current date - - **If it's early in the year (like January or February), make sure - you don't put down the wrong year.** - - * Add the git tag name - * Add the Hackage URL - * Add the contributors list - -7. Commit. - -8. Push to GitHub and wait for GitHub Actions to confirm everything is - OK. If it's not OK, fix what is broken before continuing. - -9. Merge the PR. - -10. Tag the merge commit. Tags are in the form - ``-``, and the message is the changelog entry. - -11. Push tags to GitHub. - -When the merge commit successfully builds on ``master`` the updated -packages will be pushed to Hackage by Concourse. - - -Pro tips --------- - -* If a release would have a combination of breaking and non-breaking - changes, if possible make two releases: the non-breaking ones first, - and then a major release with the breaking ones. - - This makes it possible for users who don't want the breaking changes - to still benefit from the non-breaking improvements. - -* Before uploading to Hackage, check you have no changes to the files - (for example, temporarily changing the GHC options, or adding - ``trace`` calls, for debugging reasons). - - ``stack upload`` will upload the files on the disk, not the files in - version control, so your unwanted changes will be published! diff --git a/doc/typeclass.rst b/doc/typeclass.rst deleted file mode 100644 index 5f91d9b..0000000 --- a/doc/typeclass.rst +++ /dev/null @@ -1,115 +0,0 @@ -Typeclasses -=========== - -We don't use the regular ``Control.Concurrent`` and -``Control.Exception`` modules, we use typeclass-generalised ones -instead from the :hackage:`concurrency` and :hackage:`exceptions` -packages. - - -Porting guide -------------- - -If you want to test some existing code, you'll need to port it to the -appropriate typeclass. The typeclass is necessary, because we can't -peek inside ``IO`` and ``STM`` values, so we need to able to plug in -an alternative implementation when testing. - -Fortunately, this tends to be a fairly mechanical and type-driven -process: - -1. Import ``Control.Concurrent.Classy.*`` instead of - ``Control.Concurrent.*`` - -2. Import ``Control.Monad.Catch`` instead of ``Control.Exception`` - -3. Change your monad type: - - * ``IO a`` becomes ``MonadConc m => m a`` - * ``STM a`` becomes ``MonadSTM stm => stm a`` - -4. Parameterise your state types by the monad: - - * ``TVar`` becomes ``TVar stm`` - * ``MVar`` becomes ``MVar m`` - * ``IORef`` becomes ``IORef m`` - -5. Some functions are renamed: - - * ``forkIO*`` becomes ``fork*`` - * ``atomicModifyIORefCAS*`` becomes ``modifyIORefCAS*`` - -6. Fix the type errors - -If you're lucky enough to be starting a new concurrent Haskell -project, you can just program against the ``MonadConc`` interface. - - -What if I really need I/O? --------------------------- - -You can use ``MonadIO`` and ``liftIO`` with ``MonadConc``, for -instance if you need to talk to a database (or just use some existing -library which needs real I/O). - -To test ``IO``-using code, there are some rules you need to follow: - -1. Given the same set of scheduling decisions, your ``IO`` code must - be deterministic [#]_ - -2. As dejafu can't inspect ``IO`` values, they should be kept small; - otherwise dejafu may miss buggy interleavings - -3. You absolutely cannot block on the action of another thread inside - ``IO``, or the test execution will just deadlock. - -.. [#] This is only essential if you're using the systematic testing - (the default). Nondeterministic ``IO`` won't break the random - testing, it'll just make things more confusing. - - -Deriving your own instances ---------------------------- - -There are ``MonadConc`` and ``MonadSTM`` instances for many common -monad transformers. In the simple case, where you want an instance -for a newtype wrapper around a type that has an instance, you may be -able to derive it. For example: - -.. code-block:: haskell - - {-# LANGUAGE GeneralizedNewtypeDeriving #-} - {-# LANGUAGE StandaloneDeriving #-} - {-# LANGUAGE UndecidableInstances #-} - - data Env = Env - - newtype MyMonad m a = MyMonad { runMyMonad :: ReaderT Env m a } - deriving (Functor, Applicative, Monad) - - deriving instance MonadThrow m => MonadThrow (MyMonad m) - deriving instance MonadCatch m => MonadCatch (MyMonad m) - deriving instance MonadMask m => MonadMask (MyMonad m) - - deriving instance MonadConc m => MonadConc (MyMonad m) - -``MonadSTM`` needs a slightly different set of classes: - -.. code-block:: haskell - - {-# LANGUAGE GeneralizedNewtypeDeriving #-} - {-# LANGUAGE StandaloneDeriving #-} - {-# LANGUAGE UndecidableInstances #-} - - data Env = Env - - newtype MyMonad m a = MyMonad { runMyMonad :: ReaderT Env m a } - deriving (Functor, Applicative, Monad, Alternative, MonadPlus) - - deriving instance MonadThrow m => MonadThrow (MyMonad m) - deriving instance MonadCatch m => MonadCatch (MyMonad m) - - deriving instance MonadSTM m => MonadSTM (MyMonad m) - -Don't be put off by the use of ``UndecidableInstances``, it's safe -here. diff --git a/doc/unit_testing.rst b/doc/unit_testing.rst deleted file mode 100644 index c5c55cb..0000000 --- a/doc/unit_testing.rst +++ /dev/null @@ -1,250 +0,0 @@ -Unit Testing -============ - -Writing tests with Déjà Fu is a little different to traditional unit -testing, as your test case may have multiple results. A "test" is a -combination of your code, and a predicate which says something about -the set of allowed results. - -Most tests will look something like this: - -.. code-block:: haskell - - dejafu "Assert the thing holds" myPredicate myAction - -The ``dejafu`` function comes from ``Test.DejaFu``. Another useful -function is ``dejafuWithSettings``; see :ref:`settings`. - - -Actions -------- - -An action is just something with the type ``MonadConc m => m a``, or -``(MonadConc m, MonadIO m) => m a`` for some ``a`` that your chosen -predicate can deal with. - -For example, some users on Reddit found a couple of apparent bugs in -the :hackage:`auto-update` package a while ago (`thread here`__). As -the package is simple and self-contained, I translated it to the -``MonadConc`` abstraction and wrote a couple of tests to replicate the -bugs. Here they are: - -.. code-block:: haskell - - deadlocks :: MonadConc m => m () - deadlocks = do - auto <- mkAutoUpdate defaultUpdateSettings - auto - - nondeterministic :: forall m. MonadConc m => m Int - nondeterministic = do - var <- newIORef 0 - let settings = (defaultUpdateSettings :: UpdateSettings m ()) - { updateAction = atomicModifyIORef var (\x -> (x+1, x)) } - auto <- mkAutoUpdate settings - auto - auto - -.. __: https://www.reddit.com/r/haskell/comments/2i5d7m/updating_autoupdate/ - -These actions action could be tested with ``autocheck``, and the -issues would be revealed. The use of ``ScopedTypeVariables`` in the -second is an unfortunate example of what can happen when everything -becomes more polymorphic. But other than that, note how there is no -special mention of Déjà Fu in the actions: it's just normal concurrent -Haskell, simply written against a different interface. - -The modified package is included :github:`in the test suite -`, -if you want to see the full code. [#]_ - -.. [#] The predicates in dejafu-tests are a little confusing, as - they're the opposite of what you would normally write! These - predicates are checking that the bug is found, not that the - code is correct. - -If the RTS supports bound threads (the ``-threaded`` flag was passed -to GHC when linking), then the main thread of an action given to Déjà -Fu will be bound, and further bound threads can be forked with the -``forkOS`` functions. If not, then attempting to fork a bound thread -will raise an error. - - -Conditions ----------- - -When a concurrent program of type ``MonadConc m => m a`` is executed, -it may produce a value of type ``a``, or it may experience a -**condition** such as deadlock. - -A condition does not necessarily cause your test to fail. It's -important to be aware of what exactly your test is testing, to avoid -drawing the wrong conclusions from a passing (or failing) test. - - -Setup and Teardown ------------------- - -Because dejafu drives the execution of the program under test, there -are some tricks available to you which are not possible using normal -concurrent Haskell. - -If your test does some set-up work which is required for your test to -work, but which is not the actual thing you are testing, you can -define that as a **setup action**: - -.. code-block:: haskell - - withSetup - :: Program Basic n x - -- ^ Setup action - -> (x -> Program Basic n a) - -- ^ Main program - -> Program (WithSetup x) n a - -dejafu will save the state at the end of the setup action, and -efficiently restore that state in subsequent runs of the same test -with a different schedule. This can be much more efficient than -dejafu running the setup action normally every single time. - -If you want to examine some state you created in your setup action -even if your actual test case deadlocks or something, you can define a -**teardown action**: - -.. code-block:: haskell - - withSetupAndTeardown - :: Program Basic n x - -- ^ Setup action - -> (x -> Either Condition y -> Program Basic n a) - -- ^ Teardown action - -> (x -> Program Basic n y) - -- ^ Main program - -> Program (WithSetupAndTeardown x y) n a - -The teardown action is always executed. - -Finally, if you want to ensure that some invariant holds over some -shared state, you can define invariants in the setup action, which are -checked atomically during the main action: - -.. code-block:: haskell - - -- slightly contrived example - let setup = do - var <- newEmptyMVar - registerInvariant $ do - value <- inspectMVar var - when (x == Just 1) (throwM Overflow) - pure var - in withSetup setup $ \var -> do - fork $ putMVar var 0 - fork $ putMVar var 1 - tryReadMVar var - -If the main action violates the invariant, it is terminated with an -``InvariantFailure`` condition, and any teardown action is run. - - -Predicates ----------- - -There are a few predicates built in, and some helpers to define your -own. - -.. csv-table:: - :widths: 25, 75 - - ``abortsNever``,"checks that the computation never aborts" - ``abortsAlways``,"checks that the computation always aborts" - ``abortsSometimes``,"checks that the computation aborts at least once" - -An **abort** is where the scheduler chooses to terminate execution -early. If you see it, it probably means that a test didn't terminate -before it hit the execution length limit. Aborts are hidden unless -you use explicitly enable them, see :ref:`settings`. - -.. csv-table:: - :widths: 25, 75 - - ``deadlocksNever``,"checks that the computation never deadlocks" - ``deadlocksAlways``,"checks that the computation always deadlocks" - ``deadlocksSometimes``,"checks that the computation deadlocks at least once" - -**Deadlocking** is where every thread becomes blocked. This can be, -for example, if every thread is trying to read from an ``MVar`` that -has been emptied. - -.. csv-table:: - :widths: 25, 75 - - ``exceptionsNever``,"checks that the main thread is never killed by an exception" - ``exceptionsAlways``,"checks that the main thread is always killed by an exception" - ``exceptionsSometimes``,"checks that the main thread is killed by an exception at least once" - -An uncaught **exception** in the main thread kills the process. These -can be synchronous (thrown in the main thread) or asynchronous (thrown -to it from a different thread). - -.. csv-table:: - :widths: 25, 75 - - ``alwaysSame``,"checks that the computation is deterministic and always produces a value" - ``alwaysSameOn f``,"is like ``alwaysSame``, but transforms the results with ``f`` first" - ``alwaysSameBy f``,"is like ``alwaysSame``, but uses ``f`` instead of ``(==)`` to compare" - ``notAlwaysSame``,"checks that the computation is nondeterministic" - ``notAlwaysSameOn f``,"is like ``notAlwaysSame``, but transforms the results with ``f`` first" - ``notAlwaysSameBy f``,"is like ``notAlwaysSame``, but uses ``f`` instead of ``(==)`` to compare" - -Checking for **determinism** will also find nondeterministic failures: -deadlocking (for instance) is still a result of a test! - -.. csv-table:: - :widths: 25, 75 - - ``alwaysTrue p``,"checks that ``p`` is true for every result" - ``somewhereTrue p``,"checks that ``p`` is true for at least one result" - -These can be used to check custom predicates. For example, you might -want all your results to be less than five. - -.. csv-table:: - :widths: 25, 75 - - ``gives xs``,"checks that the set of results is exactly ``xs`` (which may include conditions)" - ``gives' xs``,"checks that the set of results is exactly ``xs`` (which may not include conditions)" - -These let you say exactly what you want the results to be. Your test -will fail if it has any extra results, or misses a result. - -You can check multiple predicates against the same collection of -results using the ``dejafus`` and ``dejafusWithSettings`` functions. -These avoid recomputing the results, and so may be faster than -multiple ``dejafu`` / ``dejafuWithSettings`` calls; see -:ref:`performance`. - - -Using HUnit and Tasty ---------------------- - -By itself, Déjà Fu has no framework in place for named test groups and -parallel execution or anything like that. It does one thing and does -it well, which is running test cases for concurrent programs. -:hackage:`HUnit` and :hackage:`tasty` integration is provided to get -more of the features you'd expect from a testing framework. - -The integration is provided by the :hackage:`hunit-dejafu` and -:hackage:`tasty-dejafu` packages. - -There's a simple naming convention used: the ``Test.DejaFu`` function -``dejafuFoo`` is wrapped in the appropriate way and exposed as -``testDejafuFoo`` from ``Test.HUnit.DejaFu`` and -``Test.Tasty.DejaFu``. - -Our example from the start becomes: - -.. code-block:: haskell - - testDejafu "Assert the thing holds" myPredicate myAction - -The ``autocheck`` function is exposed as ``testAuto``. diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..0cf4b24 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,9 @@ +book + +# generated by the build script +src/index.md +src/release-notes/concurrency.md +src/release-notes/dejafu.md +src/release-notes/hunit-dejafu.md +src/release-notes/tasty-dejafu.md +mdbook-admonish.css diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 0000000..92ab99b --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,17 @@ +[book] +title = "Déjà Fu" +authors = ["Michael Walker (barrucadu)"] +description = "Systematic concurrency testing meets Haskell." +language = "en" +multilingual = false + +[build] +create-missing = false + +[output.html] +git-repository-url = "https://github.com/barrucadu/dejafu" +cname = "dejafu.docs.barrucadu.co.uk" + +[preprocessor.admonish] +on_failure = "bail" +command = "mdbook-admonish" diff --git a/doc/Makefile b/docs/readthedocs/Makefile similarity index 68% rename from doc/Makefile rename to docs/readthedocs/Makefile index b69e402..d0c3cbf 100644 --- a/doc/Makefile +++ b/docs/readthedocs/Makefile @@ -1,12 +1,12 @@ # Minimal makefile for Sphinx documentation # -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXPROJ = DjFu -SOURCEDIR = . -BUILDDIR = _build +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build # Put it first so that "make" without argument is like "make help". help: @@ -17,4 +17,4 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/readthedocs/requirements.txt b/docs/readthedocs/requirements.txt new file mode 100644 index 0000000..53fc1f3 --- /dev/null +++ b/docs/readthedocs/requirements.txt @@ -0,0 +1,2 @@ +sphinx==7.1.2 +sphinx-rtd-theme==1.3.0rc1 diff --git a/docs/readthedocs/source/conf.py b/docs/readthedocs/source/conf.py new file mode 100644 index 0000000..1eea67e --- /dev/null +++ b/docs/readthedocs/source/conf.py @@ -0,0 +1,23 @@ +# Configuration file for the Sphinx documentation builder. + +# -- Project information + +project = 'Déjà Fu' +copyright = 'Michael Walker (barrucadu)' +author = 'Michael Walker (barrucadu)' + +release = 'HEAD' +version = 'HEAD' + +# -- General configuration + +extensions = [] + +templates_path = ['_templates'] + +# -- Options for HTML output + +html_theme = 'sphinx_rtd_theme' + +# -- Options for EPUB output +epub_show_urls = 'footnote' diff --git a/docs/readthedocs/source/index.rst b/docs/readthedocs/source/index.rst new file mode 100644 index 0000000..8985d0c --- /dev/null +++ b/docs/readthedocs/source/index.rst @@ -0,0 +1,10 @@ +The Déjà Fu documentation has moved! +==================================== + + [Déjà Fu is] A martial art in which the user's limbs move in time + as well as space, […] It is best described as "the feeling that + you have been kicked in the head this way before" + + **Terry Pratchett, Thief of Time** + +`Visit the new documentation website `. diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 0000000..8f2bd1d --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,25 @@ +# Summary + +- [Getting Started](./index.md) +- [Typeclasses](./typeclasses.md) +- [Unit Testing](./unit-testing.md) +- [Refinement Testing](./refinement-testing.md) +- [Advanced Usage](./advanced-usage.md) + +# Migration Guides + +- [1.x to 2.x](./migration-guides/1x-2x.md) +- [0.x to 1.x](./migration-guides/0x-1x.md) + +# Developer Documentation + +- [Contributing](./dev-docs/contributing.md) +- [Supported GHC Versions](./dev-docs/supported-ghc-versions.md) +- [Release Process](./dev-docs/release-process.md) + +# Release Notes + +- [concurrency](./release-notes/concurrency.md) +- [dejafu](./release-notes/dejafu.md) +- [hunit-dejafu](./release-notes/hunit-dejafu.md) +- [tasty-dejafu](./release-notes/tasty-dejafu.md) diff --git a/docs/src/advanced-usage.md b/docs/src/advanced-usage.md new file mode 100644 index 0000000..0e1701a --- /dev/null +++ b/docs/src/advanced-usage.md @@ -0,0 +1,96 @@ +Advanced Usage +============== + +Déjà Fu tries to have a sensible set of defaults, but there are some +times when the defaults are not suitable. There are a lot of knobs +provided to tweak how things work. + + +Execution settings +------------------ + +The `autocheckWithSettings`, `dejafuWithSettings`, and `dejafusWithSettings` let +you provide a `Settings` value, which controls some of Déjà Fu's behaviour: + +```haskell +dejafuWithSettings mySettings "Assert the thing holds" myPredicate myAction +``` + +The available settings are: + +- **"Way"**, how to explore the behaviours of the program under test. + +- **Length bound**, a cut-off point to terminate an execution even if it's not + done yet. + +- **Memory model**, which affects how non-synchronised operations, such as + `readIORef` and `writeIORef` behave. + +- **Discarding**, which allows throwing away uninteresting results, rather than + keeping them around in memory. + +- **Early exit**, which allows exiting as soon as a result matching a predicate + is found. + +- **Representative traces**, keeping only one execution trace for each distinct + result. + +- **Trace simplification**, rewriting execution traces into a simpler form + (particularly effective with the random testing). + +- **Safe IO**, pruning needless schedules when your IO is only used to manage + thread-local state. + +See the `Test.DejaFu.Settings` module for more information. + + +Performance tuning +------------------ + +- Are you happy to trade space for time? + + Consider computing the results once and running multiple predicates over the + output: this is what `dejafus` / `testDejafus` / etc does. + +- Can you sacrifice completeness? + + Consider using the random testing functionality. See the `*WithSettings` + functions. + +- Would strictness help? + + Consider using the strict functions in `Test.DejaFu.SCT` (the ones ending + with a `'`). + +- Do you just want the set of results, and don't care about traces? + + Consider using `Test.DejaFu.SCT.resultsSet`. + +- Do you know something about the sort of results you care about? + + Consider discarding results you *don't* care about. See the `*WithSettings` + functions in `Test.DejaFu`, `Test.DejaFu.SCT`, and + `Test.{HUnit,Tasty}.DejaFu`. + +For example, let's say you want to know if your test case deadlocks, but you +don't care about the execution trace, and you are going to sacrifice +completeness because your possible state-space is huge. You could do it like +this: + +```haskell +dejafuWithSettings + ( set ldiscard + -- "efa" == "either failure a", discard everything but deadlocks + (Just $ \efa -> Just (if either isDeadlock (const False) efa then DiscardTrace else DiscardResultAndTrace)) + . set lway + -- try 10000 executions with random scheduling + (randomly (mkStdGen 42) 10000) + $ defaultSettings + ) + -- the name of the test + "Never Deadlocks" + -- the predicate to check + deadlocksNever + -- your test case + testCase +``` diff --git a/docs/src/dev-docs/contributing.md b/docs/src/dev-docs/contributing.md new file mode 100644 index 0000000..46ab0d7 --- /dev/null +++ b/docs/src/dev-docs/contributing.md @@ -0,0 +1,221 @@ +Contributing +============ + +Thanks for caring about Déjà Fu! + + +Ways to contribute +------------------ + +Déjà Fu is a project under active development, there's always something to do. +Here's a list of ideas to get you started: + +- Submit bug reports. +- Submit feature requests. +- Got a particularly slow test case which you think should be faster? Open an + issue for that too. +- Blog about how and why you use Déjà Fu. +- Check if any bugs which have been open for a while are still bugs. + +If you want to contribute code, you could: + +- Tackle one of the issues tagged ["good first issue"][good-first-issue]. +- Tackle a bigger issue, perhaps one of the [roadmap issues][roadmap]! +- Run code coverage and try to fix a gap in the tests. +- Profile the test suite and try to improve a slow function. + +[Roadmap issues][roadmap] are priority issues (in my opinion), so help with +those is especially appreciated. + +If you have a support question, you can talk to me on IRC (#haskell on freenode) +or send an email (mike@barrucadu.co.uk) rather than opening an issue. But maybe +your question is a bug report about poor documentation in disguise! + +[good-first-issue]: https://github.com/barrucadu/dejafu/labels/good%20first%20issue +[roadmap]: https://github.com/barrucadu/dejafu/labels/roadmap + + +Making the change +----------------- + +1. Talk to me! + + I don't bite, and chances are I can quickly tell you where you should start. + It's better to ask what seems like a stupid question than to waste a lot of + time on the wrong approach. + +2. Make the change. + + Figure out what needs to be changed, how to change it, and do it. If you're + fixing a bug, make sure to add a minimal reproduction to Cases.Regressions in + dejafu-tests. + +3. Document the change. + + All top-level definitions should have a [Haddock][] comment explaining what + it does. If you've added or changed a top-level function, consider + commenting its arguments too. + + If you've added a top-level definition, or changed one in a + backwards-incompatible way, add an `@since unreleased` Haddock comment to it. + This is to help prevent me from missing things when I update the changelog + ahead of a release. + +4. Submit a PR. + + GitHub Actions will run some checks, which might prompt further action. You + should expect a response from me in a day or two. + +Don't worry about your PR being perfect the first time. We'll work through any +issues together, to ensure that Déjà Fu gets the best code it can. + +[Haddock]: https://hackage.haskell.org/package/haddock + + +Coding style +------------ + +There isn't really a prescribed style. It's not quite the wild west though; +keep these three rules in mind: + +1. Be consistent. +2. Run [stylish-haskell][] to format import lists. +3. Use [hlint][] and [weeder][] and fix lints unless you + have a good reason not to. + +GitHub Actions runs stylish-haskell and hlint on all PRs. + +[stylish-haskell]: https://hackage.haskell.org/package/stylish-haskell +[hlint]: https://hackage.haskell.org/package/hlint +[weeder]: https://hackage.haskell.org/package/weeder + + +Coverage +-------- + +[hpc][] can generate a coverage report from the execution of dejafu-tests: + +```text +$ stack build --coverage +$ stack exec dejafu-tests +$ stack hpc report --all dejafu-tests.tix +``` + +This will print some stats and generate an HTML coverage report: + +```text +Generating combined report + 52% expressions used (4052/7693) + 48% boolean coverage (63/129) + 43% guards (46/106), 31 always True, 9 always False, 20 unevaluated + 68% 'if' conditions (11/16), 2 always True, 3 unevaluated + 85% qualifiers (6/7), 1 unevaluated + 61% alternatives used (392/635) + 80% local declarations used (210/261) + 26% top-level declarations used (280/1063) +The combined report is available at /home/barrucadu/projects/dejafu/.stack-work/install/x86_64-linux/nightly-2016-06-20/8.0.1/hpc/combined/custom/hpc_index.html +``` + +The highlighted code in the HTML report emphasises branch coverage: + +- Red means a branch was evaluated as always false. +- Green means a branch was evaluated as always true. +- Yellow means an expression was never evaluated. + +See also the [stack coverage documentation][cov]. + +[hpc]: https://wiki.haskell.org/Haskell_program_coverage +[cov]: https://docs.haskellstack.org/en/latest/coverage/ + + +Performance +----------- + +GHC can generate performance statistics from the execution of dejafu-tests: + +```text +$ stack build --profile +$ stack exec -- dejafu-tests +RTS -p +$ less dejafu-tests.prof +``` + +This prints a detailed breakdown of where memory and time are being spent: + +```text + Mon Mar 20 19:26 2017 Time and Allocation Profiling Report (Final) + + dejafu-tests +RTS -p -RTS + + total time = 105.94 secs (105938 ticks @ 1000 us, 1 processor) + total alloc = 46,641,766,952 bytes (excludes profiling overheads) + +COST CENTRE MODULE %time %alloc + +findBacktrackSteps.doBacktrack.idxs' Test.DejaFu.SCT.Internal 21.9 12.0 +== Test.DejaFu.Common 12.4 0.0 +yieldCount.go Test.DejaFu.SCT 12.1 0.0 +dependent' Test.DejaFu.SCT 5.1 0.0 +runThreads.go Test.DejaFu.Conc.Internal 2.7 4.1 +[...] +``` + +Be careful, however! Compiling with profiling can significantly affect the +behaviour of a program! Use profiling to get an idea of where the hot spots +are, but make sure to confirm with a non-profiled build that things are actually +getting faster. + +If you compile with `-rtsopts` you can get some basic stats from a non-profiled +build: + +```text +$ stack exec -- dejafu-tests +RTS -s + +[...] +86,659,658,504 bytes allocated in the heap +13,057,037,448 bytes copied during GC + 13,346,952 bytes maximum residency (4743 sample(s)) + 127,824 bytes maximum slop + 37 MB total memory in use (0 MB lost due to fragmentation) + + Tot time (elapsed) Avg pause Max pause +Gen 0 78860 colls, 0 par 32.659s 32.970s 0.0004s 0.0669s +Gen 1 4743 colls, 0 par 3.043s 3.052s 0.0006s 0.0086s + +TASKS: 174069 (174065 bound, 4 peak workers (4 total), using -N1) + +SPARKS: 0 (0 converted, 0 overflowed, 0 dud, 0 GC'd, 0 fizzled) + +INIT time 0.001s ( 0.001s elapsed) +MUT time 98.685s (101.611s elapsed) +GC time 35.702s ( 36.022s elapsed) +EXIT time 0.001s ( 0.007s elapsed) +Total time 134.388s (137.640s elapsed) + +Alloc rate 878,145,635 bytes per MUT second + +Productivity 73.4% of total user, 73.8% of total elapsed +``` + + +Heap profiling +-------------- + +GHC can tell you where the memory is going: + +```text +$ stack build --profile +$ stack exec -- dejafu-tests +RTS -hc +$ hp2ps -c dejafu-tests.hp +``` + +This will produce a graph of memory usage over time, as a postscript file, +broken down by cost-centre which produced the data. There are a few different +views: + +- `-hm` breaks down the graph by module +- `-hd` breaks down the graph by closure description +- `-hy` breaks down the graph by type + +I typically find `-hd` and `-hy` most useful. If you're feeling particularly +brave, you can try `-hr`, which is intended to help track down memory leaks +caused by unevaluated thunks. diff --git a/docs/src/dev-docs/release-process.md b/docs/src/dev-docs/release-process.md new file mode 100644 index 0000000..629f4ad --- /dev/null +++ b/docs/src/dev-docs/release-process.md @@ -0,0 +1,61 @@ +Release Process +=============== + +```admonish warning +If it's early in the year, make sure you put down the right year in the CHANGELOG! +``` + +1. Figure out what the next version number is. See the PVP_ page if unsure. + +2. Update version numbers in the relevant cabal files: + + * Update the `version` field + * Update the `tag` in the `source-repository` block + +3. Fill in all `@since unreleased` Haddock comments with the relevant version + number. + +4. Update version numbers in the tables in the README page. + +5. Ensure the relevant CHANGELOG files have all the entries they should. + +6. Add the release information to the relevant CHANGELOG files: + + * Change the `unreleased` title to the version number + * Add the current date + * Add the git tag name + * Add the Hackage URL + * Add the contributors list + +7. Commit. + +8. Push to GitHub and wait for GitHub Actions to confirm everything is + OK. If it's not OK, fix what is broken before continuing. + +9. Merge the PR. + +10. Tag the merge commit. Tags are in the form `-`, and the + message is the changelog entry. + +11. Push tags to GitHub. + +When the merge commit successfully builds on `master` the updated packages will +be pushed to Hackage by Concourse. + + +Pro tips +-------- + +* If a release would have a combination of breaking and non-breaking changes, if + possible make two releases: the non-breaking ones first, and then a major + release with the breaking ones. + + This makes it possible for users who don't want the breaking changes to still + benefit from the non-breaking improvements. + +* Before uploading to Hackage, check you have no changes to the files (for + example, temporarily changing the GHC options, or adding `trace` calls, for + debugging reasons). + + `stack upload` will upload the files on the disk, not the files in version + control, so your unwanted changes will be published! diff --git a/docs/src/dev-docs/supported-ghc-versions.md b/docs/src/dev-docs/supported-ghc-versions.md new file mode 100644 index 0000000..50700e4 --- /dev/null +++ b/docs/src/dev-docs/supported-ghc-versions.md @@ -0,0 +1,70 @@ +Supported GHC Versions +====================== + +Déjà Fu supports the latest four GHC releases, at least. For testing purposes, +we use Stackage snapshots as a proxy for GHC versions. The currently supported +versions are: + +| GHC | Stackage | base | +| --- | -------- | ---- | +| 9.6 | Nightly 2021-07-01 | 4.18.0.0 | +| 9.4 | LTS 21.0 | 4.17.0.0 | +| 9.2 | LTS 20.0 | 4.16.0.0 | +| 9.0 | LTS 19.0 | 4.15.0.0 | +| 8.1 |,LTS 17.0 | 4.14.1.0 | +| 8.8 | LTS 15.0 | 4.13.0.0 | +| 8.6 | LTS 14.0 | 4.12.0.0 | +| 8.4 | LTS 12.0 | 4.11.0.0 | +| 8.2 | LTS 10.0 | 4.10.1.0 | + +In practice, we may *compile with* older versions of GHC, but keeping them +working is not a priority. + + +Adding new GHC releases +----------------------- + +When a new version of GHC is released, we need to make some changes for +everything to go smoothly. In general, these changes should only cause a +**patch level version bump**. + +1. Bump the upper bound of [base][] and set up any needed conditional + compilation +2. Add the GHC and base versions to the table. +3. Remove any unsupported versions from the table. +4. Make a patch release. + +A new GHC release won't get a Stackage snapshot for little while. When it +does: + +1. Add the snapshot to the GitHub Actions configuration. +2. Update the resolver in the stack.yaml. +3. Put the snapshot in the table. + + +Dropping old GHC releases +------------------------- + +When we want to drop an unsupported version of GHC, we need to bump the version +bound on [base][] to preclude it. This is a backwards-incompatible change which +causes a **major version bump**. + +1. Remove the dropped GHC version from the GitHub Actions configuration. +2. Bump the lower bound of [base][]. +3. Look through the other dependencies. Some may not work with our new lower + bound on [base][], so we should bump those too. +4. Remove any now-irrelevant conditional compilation (mostly CPP, but there may + also be some cabal file bits). +5. Make whatever change required the bump. +6. Make a major release. + +GHC versions shouldn't be dropped just because we can, but here are some good +reasons to do it: + +- We want to bump the lower bounds of a dependency to a version which doesn't + support that GHC. +- We want to add a new dependency which doesn't support that GHC. +- The conditional compilation needed to keep that GHC working is getting + confusing. + +[base]: https://hackage.haskell.org/package/base diff --git a/docs/src/migration-guides/0x-1x.md b/docs/src/migration-guides/0x-1x.md new file mode 100644 index 0000000..73a01c3 --- /dev/null +++ b/docs/src/migration-guides/0x-1x.md @@ -0,0 +1,122 @@ +0.x to 1.x +========== + +[dejafu-1.0.0.0][] is a super-major release which breaks compatibility with +[dejafu-0.x][] quite significantly, but brings with it support for bound +threads, and significantly improves memory usage in the general case. + +Highlights reel: + +- Most predicates now only need to keep around the failures, rather than all + results. +- Support for bound threads (with [concurrency-1.3.0.0][]). +- The `ST` / `IO` interface duplication is gone, everything is now monadic. +- Function parameter order is closer to other testing libraries. +- Much improved API documentation. + +See the changelogs for the full details. + + +`ST` and `IO` functions +----------------------- + +There is only one set of functions now. Testing bound threads requires being +able to fork actual threads, so testing with `ST` is no longer possible. The +`ConcST` type is gone, there is only `ConcIO`. + +For [dejafu][] change: + +- `autocheckIO` to `autocheck` +- `dejafuIO` to `dejafu` +- `dejafusIO` to `dejafus` +- `autocheckWayIO` to `autocheckWay` +- `dejafuWayIO` to `dejafuWay` +- `dejafusWayIO` to `dejafusWay` +- `dejafuDiscardIO` to `dejafuDiscard` +- `runTestM` to `runTest` +- `runTestWayM` to `runTestWay` + +If you relied on being able to get a pure result from the `ConcST` functions, +you can no longer do this. + +For [hunit-dejafu][] and [tasty-dejafu][] change: + +- `testAutoIO` to `testAuto` +- `testDejafuIO` to `testDejafu` +- `testDejafusIO` to `testDejafus` +- `testAutoWayIO` to `testAutoWay` +- `testDejafuWayIO` to `testDejafuWay` +- `testDejafusWayIO` to `testDejafusWay` +- `testDejafuDiscardIO` to `testDejafuDiscard` + + +Function parameter order +------------------------ + +Like [HUnit][], the monadic action to test is now the last parameter of the +testing functions. This makes it convenient to write tests without needing to +define the action elsewhere. + +For [dejafu][] change: + +- `dejafu ma (s, p)` to `dejafu s p ma` +- `dejafus ma ps` to `dejafus ps ma` +- `dejafuWay way mem ma (s, p)` to `dejafuWay way mem s p ma` +- `dejafusWay way mem ma ps` to `dejafuWay way mem ps ma` +- `dejafuDiscard d way mem ma (s, p)` to `dejafuDiscard d way mem s p ma` + +For [hunit-dejafu][] and [tasty-dejafu][] change: + +- `testDejafu ma s p` to `testDejafu s p ma` +- `testDejafus ma ps` to `testDejafus ps ma` +- `testDejafuWay way mem ma s p` to `testDejafuWay way mem s p ma` +- `testDejafusWay way mem ma ps` to `testDejafusWay way mem ps ma` +- `testDejafuDiscard d way mem ma s p` to `testDejafuDiscard d way mem s p ma` + + +Predicates +---------- + +The `Predicate a` type is now an alias for `ProPredicate a a`, defined like so: + +```haskell +data ProPredicate a b = ProPredicate + { pdiscard :: Either Failure a -> Maybe Discard + -- ^ Selectively discard results before computing the result. + , peval :: [(Either Failure a, Trace)] -> Result b + -- ^ Compute the result with the un-discarded results. + } +``` + +If you use the predicate helper functions to construct a predicate, you do not +need to change anything (and should get a nice reduction in your resident memory +usage). If you supply a function directly, you can recover the old behaviour +like so: + +```haskell +old :: ([(Either Failure a, Trace)] -> Result a) -> ProPredicate a a +old p = ProPredicate + { pdiscard = const Nothing + , peval = p + } +``` + +The `alwaysTrue2` helper function is gone. If you use it, use `alwaysSameOn` or +`alwaysSameBy` instead. + + +Need help? +---------- + +- For general help talk to me in IRC (barrucadu in #haskell) or shoot me an + email (mike@barrucadu.co.uk) +- For bugs, issues, or requests, please [file an issue][]. + +[dejafu-1.0.0.0]: https://hackage.haskell.org/package/dejafu-1.0.0.0 +[dejafu-0.x]: https://hackage.haskell.org/package/dejafu-0.9.1.1 +[concurrency-1.3.0.0]: https://hackage.haskell.org/package/concurrency-1.3.0.0 +[dejafu]: https://hackage.haskell.org/package/dejafu +[hunit-dejafu]: https://hackage.haskell.org/package/hunit-dejafu +[tasty-dejafu]: https://hackage.haskell.org/package/tasty-dejafu +[HUnit]: https://hackage.haskell.org/package/HUnit +[file an issue]: https://github.com/barrucadu/dejafu/issues/ diff --git a/docs/src/migration-guides/1x-2x.md b/docs/src/migration-guides/1x-2x.md new file mode 100644 index 0000000..e62550b --- /dev/null +++ b/docs/src/migration-guides/1x-2x.md @@ -0,0 +1,117 @@ +1.x to 2.x +========== + +[dejafu-2.0.0.0][] is a super-major release which breaks compatibility with +[dejafu-1.x][]. + +Highlights reel: + +- Test cases are written in terms of a new `Program` type. +- The `Failure` type has been replaced with a `Condition` type (actually in + 1.12). +- Random testing takes an optional length bound. +- Atomically-checked invariants over shared mutable state. + +See the changelogs for the full details. + + +The `Program` type +------------------ + +The `ConcT` type is now an alias for `Program Basic`. + +A `Program Basic` has all the instances `ConcT` did, defined using the `~` +instance trick, so this shouldn't be a breaking change: + +```haskell +instance (pty ~ Basic) => MonadTrans (Program pty) +instance (pty ~ Basic) => MonadCatch (Program pty n) +instance (pty ~ Basic) => MonadThrow (Program pty n) +instance (pty ~ Basic) => MonadMask (Program pty n) +instance (pty ~ Basic, Monad n) => MonadConc (Program pty n) +instance (pty ~ Basic, MonadIO n) => MonadIO (Program pty n) +``` + +The `dontCheck` function has been removed in favour of `withSetup`: + +```haskell +do x <- dontCheck setup + action x + +-- becomes + +withSetup setup action +``` + +The `subconcurrency` function has been removed in favour of +`withSetupAndTeardown`: + +```haskell +do x <- setup + y <- subconcurrency (action x) + teardown x y + +-- becomes + +withSetupAndTeardown setup teardown action +``` + +The `dontCheck` and `subconcurrency` functions used to throw runtime errors if +nested. This is not possible with `withSetup` and `withSetupAndTeardown` due to +their types: + +```haskell +withSetup + :: Program Basic n x + -- ^ Setup action + -> (x -> Program Basic n a) + -- ^ Main program + -> Program (WithSetup x) n a + +withSetupAndTeardown + :: Program Basic n x + -- ^ Setup action + -> (x -> Either Condition y -> Program Basic n a) + -- ^ Teardown action + -> (x -> Program Basic n y) + -- ^ Main program + -> Program (WithSetupAndTeardown x y) n a +``` + +Previously, multiple calls to `subconcurrency` could be sequenced in the same +test case. This is not possible using `withSetupAndTeardown`. If you rely on +this behaviour, please [file an issue][]. + + +The `Condition` type +-------------------- + +This is a change in [dejafu-1.12.0.0][dejafu-1.x], but the alias `Failure = +Condition` is removed in [dejafu-2.0.0.0][]. + +- The `STMDeadlock` and `Deadlock` constructors have been merged. +- Internal errors have been split into the `Error` type and are raised as + exceptions, instead of being returned as conditions. + +The name "failure" has been a recurring source of confusion, because an +individual execution can "fail" without the predicate as a whole failing. My +hope is that the more neutral "condition" will prevent this confusion. + + +Deprecated functions +-------------------- + +All the deprecated special-purpose functions have been removed. Use more +general `*WithSettings` functions instead. + + +Need help? +---------- + +- For general help talk to me in IRC (barrucadu in #haskell) or shoot me an + email (mike@barrucadu.co.uk) +- For bugs, issues, or requests, please [file an issue][]. + +[dejafu-2.0.0.0]: https://hackage.haskell.org/package/dejafu-2.0.0.0 +[dejafu-1.x]: https://hackage.haskell.org/package/dejafu-1.12.0.0 +[file an issue]: https://github.com/barrucadu/dejafu/issues/ diff --git a/docs/src/refinement-testing.md b/docs/src/refinement-testing.md new file mode 100644 index 0000000..873b2c4 --- /dev/null +++ b/docs/src/refinement-testing.md @@ -0,0 +1,148 @@ +Refinement Testing +================== + +Déjà Fu also supports a form of property-testing where you can check things +about the side-effects of stateful operations. For example, we can assert that +`readMVar` is equivalent to sequencing `takeMVar` and `putMVar` like so: + +```haskell +prop_mvar_read_take_put = + sig readMVar `equivalentTo` sig (\v -> takeMVar v >>= putMVar v) +``` + +Given the signature function, `sig`, defined in the next section. If we check +this, our property fails! + +```text +> check prop_mvar_read_take_put +*** Failure: (seed Just 0) + left: [(Nothing,Just 0)] + right: [(Nothing,Just 0),(Just Deadlock,Just 0)] +False +``` + +This is because `readMVar` is atomic, whereas sequencing `takeMVar` with +`putMVar` is not, and so another thread can interfere with the `MVar` in the +middle. The `check` and `equivalentTo` functions come from +`Test.DejaFu.Refinement` (also re-exported from `Test.DejaFu`). + + +Signatures +---------- + +A signature tells the property-tester something about the state your operation +acts upon, it has a few components: + +```haskell +data Sig s o x = Sig + { initialise :: x -> ConcIO s + , observe :: s -> x -> ConcIO o + , interfere :: s -> x -> ConcIO () + , expression :: s -> ConcIO () + } +``` + +- `s` is the **state type**, it's the thing which your operations mutate. For + `readMVar`, the state is some `MVar a`. + +- `o` is the **observation type**, it's some pure (and comparable) proxy for a + snapshot of your mutable state. For `MVar a`, the observation is probably a + `Maybe a`. + +- `x` is the **seed type**, it's some pure value used to construct the initial + mutable state. For `MVar a`, the seed is probably a `Maybe a`. + +- `ConcIO` is just one of the instances of `MonadConc` that Déjà Fu defines for + testing purposes. Just write code polymorphic in the monad as usual, and all + will work. + +The `initialise`, `observe`, and `expression` functions should be +self-explanatory, but the `interfere` one may not be. It's the job of the +`interfere` function to change the state in some way; it's run concurrently with +the expression, to simulate the nondeterministic action of other threads. + +Here's a concrete example for our `MVar` example: + +```haskell +sig :: (MVar ConcIO Int -> ConcIO a) -> Sig (MVar ConcIO Int) (Maybe Int) (Maybe Int) +sig e = Sig +{ initialise = maybe newEmptyMVar newMVar +, observe = \v _ -> tryTakeMVar v +, interfere = \v s -> tryTakeMVar v >> maybe (pure ()) (\x -> void $ tryPutMVar v (x * 1000)) s +, expression = void . e +} +``` + +The `observe` function should be deterministic, but as it is run after the +normal execution ends, it may have side-effects on the state. The `interfere` +function can do just about anything (there are probably some concrete rules for +a good function, but I haven't figured them out yet), but a poor one may result +in the property-checker being unable to distinguish between atomic and nonatomic +expressions. + + +Properties +---------- + +A property is a pair of signatures linked by one of three provided +functions. These functions are: + +| Function | Operator | Checks that... | +| - | - | - | +| `equivalentTo` | `===` | ... the left and right have exactly the same behaviours | +| `refines` | `=>=` | ... every behaviour of the left is also a behaviour of the right | +| `strictlyRefines` | `->-` | ... `left =>= right` holds but `left === right` does not | + +The signatures can have different state types, as long as the seed and +observation types are the same. This lets you compare different implementations +of the same idea: for example, comparing a concurrent stack implemented using +`MVar` with one implemented using `IORef`. + +Properties can have parameters, given in the obvious way: + +```haskell +check $ \a b c -> sig1 ... `op` sig2 ... +``` + +Under the hood, seed and parameter values are generated using the [leancheck][] +package, an enumerative property-based testing library. This means that any +types you use will need to have a `Listable` instance. + +You can also think about the three functions in terms of sets of results, where +a result is a `(Maybe Failure, o)` value. A `Failure` is something like +deadlocking, or being killed by an exception; `o` is the observation type. An +observation is always made, even if execution of the expression fails. + +| Function | Result-set operation | +| - | - | +| `refines` | For all seed and parameter assignments, subset-or-equal | +| `strictlyRefines` | For at least one seed and parameter assignment, proper subset; for all others, subset-or-equal | +| `equivalentTo` | For all seed and parameter assignments, equality | + +Finally, there is an `expectFailure` function, which inverts the expected result +of a property. + +The Déjà Fu testsuite has [a collection of refinement properties][], which may +help you get a feel for this sort of testing. + +[leancheck]: https://hackage.haskell.org/package/leancheck +[a collection of refinement properties]: https://github.com/barrucadu/dejafu/blob/2a15549d97c2fa12f5e8b92ab918fdb34da78281/dejafu-tests/Cases/Refinement.hs + + +Using HUnit and Tasty +--------------------- + +As for unit testing, [HUnit][] and [tasty][] integration is provided for +refinement testing in the [hunit-dejafu][] and [tasty-dejafu][] packages. + +The `testProperty` function is used to check properties. Our example from the +start becomes: + +```haskell +testProperty "Read is equivalent to Take then Put" prop_mvar_read_take_put +``` + +[HUnit]: https://hackage.haskell.org/package/HUnit +[tasty]: https://hackage.haskell.org/package/tasty +[hunit-dejafu]: https://hackage.haskell.org/package/hunit-dejafu +[tasty-dejafu]: https://hackage.haskell.org/package/tasty-dejafu diff --git a/docs/src/typeclasses.md b/docs/src/typeclasses.md new file mode 100644 index 0000000..e3e4e83 --- /dev/null +++ b/docs/src/typeclasses.md @@ -0,0 +1,115 @@ +Typeclasses +=========== + +We don't use the regular `Control.Concurrent` and `Control.Exception` modules, +we use typeclass-generalised ones instead from the [concurrency][h:conc] and +[exceptions][h:exc] packages. + +[h:conc]: https://hackage.haskell.org/package/concurrency +[h:exc]: https://hackage.haskell.org/package/exceptions + + +Porting guide +------------- + +If you want to test some existing code, you'll need to port it to the +appropriate typeclass. The typeclass is necessary, because we can't peek inside +`IO` and `STM` values, so we need to able to plug in an alternative +implementation when testing. + +Fortunately, this tends to be a fairly mechanical and type-driven process: + +1. Import `Control.Concurrent.Classy.*` instead of `Control.Concurrent.*` + +2. Import `Control.Monad.Catch` instead of `Control.Exception` + +3. Change your monad type: + + - `IO a` becomes `MonadConc m => m a` + - `STM a` becomes `MonadSTM stm => stm a` + +4. Parameterise your state types by the monad: + + - `TVar` becomes `TVar stm` + - `MVar` becomes `MVar m` + - `IORef` becomes `IORef m` + +5. Some functions are renamed: + + - `forkIO*` becomes `fork*` + - `atomicModifyIORefCAS` becomes `modifyIORefCAS*` + +6. Fix the type errors + +If you're lucky enough to be starting a new concurrent Haskell project, you can +just program against the `MonadConc` interface. + + +What if I really need I/O? +-------------------------- + +You can use `MonadIO` and `liftIO` with `MonadConc`, for instance if you need to +talk to a database (or just use some existing library which needs real I/O). + +To test `IO`-using code, there are some rules you need to follow: + +1. Given the same set of scheduling decisions, your `IO` code must be + deterministic (see below). + +2. As dejafu can't inspect `IO` values, they should be kept small; otherwise + dejafu may miss buggy interleavings. + +3. You absolutely cannot block on the action of another thread inside `IO`, or + the test execution will just deadlock. + +```admonish tip +Deterministic `IO` is only essential if you're using the systematic testing (the +default). Nondeterministic `IO` won't break the random testing, it'll just make +things more confusing. +``` + + +Deriving your own instances +--------------------------- + +There are `MonadConc` and `MonadSTM` instances for many common monad +transformers. In the simple case, where you want an instance for a newtype +wrapper around a type that has an instance, you may be able to derive it. For +example: + +```haskell +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE UndecidableInstances #-} + +data Env = Env + +newtype MyMonad m a = MyMonad { runMyMonad :: ReaderT Env m a } + deriving (Functor, Applicative, Monad) + +deriving instance MonadThrow m => MonadThrow (MyMonad m) +deriving instance MonadCatch m => MonadCatch (MyMonad m) +deriving instance MonadMask m => MonadMask (MyMonad m) + +deriving instance MonadConc m => MonadConc (MyMonad m) +``` + +`MonadSTM` needs a slightly different set of classes: + +```haskell +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE UndecidableInstances #-} + +data Env = Env + +newtype MyMonad m a = MyMonad { runMyMonad :: ReaderT Env m a } + deriving (Functor, Applicative, Monad, Alternative, MonadPlus) + +deriving instance MonadThrow m => MonadThrow (MyMonad m) +deriving instance MonadCatch m => MonadCatch (MyMonad m) + +deriving instance MonadSTM m => MonadSTM (MyMonad m) +``` + +Don't be put off by the use of `UndecidableInstances`, it's safe here. diff --git a/docs/src/unit-testing.md b/docs/src/unit-testing.md new file mode 100644 index 0000000..239a1ac --- /dev/null +++ b/docs/src/unit-testing.md @@ -0,0 +1,240 @@ +Unit Testing +============ + +Writing tests with Déjà Fu is a little different to traditional unit testing, as +your test case may have multiple results. A "test" is a combination of your +code, and a predicate which says something about the set of allowed results. + +Most tests will look something like this: + +```haskell +dejafu "Assert the thing holds" myPredicate myAction +``` + +The `dejafu` function comes from `Test.DejaFu`. Another useful function is +`dejafuWithSettings` (see [Advanced Usage](./advanced-usage.md)). + + +Actions +------- + +An action is just something with the type `MonadConc m => m a`, or `(MonadConc +m, MonadIO m) => m a` for some `a` that your chosen predicate can deal with. + +For example, some users on Reddit found a couple of apparent bugs in the +[auto-update][h:auto] package a while ago ([thread here][auto-thread]). As the +package is simple and self-contained, I translated it to the `MonadConc` +abstraction and wrote a couple of tests to replicate the bugs. Here they are: + +```haskell +deadlocks :: MonadConc m => m () +deadlocks = do + auto <- mkAutoUpdate defaultUpdateSettings + auto + +nondeterministic :: forall m. MonadConc m => m Int +nondeterministic = do + var <- newIORef 0 + let settings = (defaultUpdateSettings :: UpdateSettings m ()) + { updateAction = atomicModifyIORef var (\x -> (x+1, x)) } + auto <- mkAutoUpdate settings + auto + auto +``` + +These actions action could be tested with `autocheck`, and the issues would be +revealed. The use of `ScopedTypeVariables` in the second is an unfortunate +example of what can happen when everything becomes more polymorphic. But other +than that, note how there is no special mention of Déjà Fu in the actions: it's +just normal concurrent Haskell, simply written against a different interface. + +The modified package is included [in the test suite][], if you want to see the +full code. + +```admonish note +The predicates in dejafu-tests are a little confusing, as they're the opposite +of what you would normally write! These predicates are checking that the bug is +found, not that the code is correct. +``` + +If the RTS supports bound threads (the `-threaded` flag was passed to GHC when +linking), then the main thread of an action given to Déjà Fu will be bound, and +further bound threads can be forked with the `forkOS` functions. If not, then +attempting to fork a bound thread will raise an error. + +[h:auto]: https://hackage.haskell.org/package/auto-update +[auto-thread]: https://www.reddit.com/r/haskell/comments/2i5d7m/updating_autoupdate/ +[in the test suite]: https://github.com/barrucadu/dejafu/blob/2a15549d97c2fa12f5e8b92ab918fdb34da78281/dejafu-tests/Examples/AutoUpdate.hs + + +Conditions +---------- + +When a concurrent program of type `MonadConc m => m a` is executed, it may +produce a value of type `a`, or it may experience a **condition** such as +deadlock. + +A condition does not necessarily cause your test to fail. It's important to be +aware of what exactly your test is testing, to avoid drawing the wrong +conclusions from a passing (or failing) test. + + +Setup and Teardown +------------------ + +Because dejafu drives the execution of the program under test, there are some +tricks available to you which are not possible using normal concurrent Haskell. + +If your test does some set-up work which is required for your test to work, but +which is not the actual thing you are testing, you can define that as a **setup +action**: + +```haskell +withSetup + :: Program Basic n x + -- ^ Setup action + -> (x -> Program Basic n a) + -- ^ Main program + -> Program (WithSetup x) n a +``` + +dejafu will save the state at the end of the setup action, and efficiently +restore that state in subsequent runs of the same test with a different +schedule. This can be much more efficient than dejafu running the setup action +normally every single time. + +If you want to examine some state you created in your setup action even if your +actual test case deadlocks or something, you can define a **teardown action**: + +```haskell +withSetupAndTeardown + :: Program Basic n x + -- ^ Setup action + -> (x -> Either Condition y -> Program Basic n a) + -- ^ Teardown action + -> (x -> Program Basic n y) + -- ^ Main program + -> Program (WithSetupAndTeardown x y) n a +``` + +The teardown action is always executed. + +Finally, if you want to ensure that some invariant holds over some shared state, +you can define invariants in the setup action, which are checked atomically +during the main action: + +```haskell +-- slightly contrived example +let setup = do + var <- newEmptyMVar + registerInvariant $ do + value <- inspectMVar var + when (x == Just 1) (throwM Overflow) + pure var +in withSetup setup $ \var -> do + fork $ putMVar var 0 + fork $ putMVar var 1 + tryReadMVar var +``` + +If the main action violates the invariant, it is terminated with an +`InvariantFailure` condition, and any teardown action is run. + + +Predicates +---------- + +There are a few predicates built in, and some helpers to define your own. + + +| | | +| - | - | +| `abortsNever` | checks that the computation never aborts | +| `abortsAlways` | checks that the computation always aborts | +| `abortsSometimes` | checks that the computation aborts at least once | + +An **abort** is where the scheduler chooses to terminate execution early. If +you see it, it probably means that a test didn't terminate before it hit the +execution length limit. Aborts are hidden unless you use explicitly enable +them, see (see [Advanced Usage](./advanced-usage.md)). + +| | | +| - | - | +| `deadlocksNever` | checks that the computation never deadlocks | +| `deadlocksAlways` | checks that the computation always deadlocks | +| `deadlocksSometimes` | checks that the computation deadlocks at least once | + +**Deadlocking** is where every thread becomes blocked. This can be, for +example, if every thread is trying to read from an `MVar` that has been emptied. + +| | | +| - | - | +| `exceptionsNever` | checks that the main thread is never killed by an exception | +| `exceptionsAlways` | checks that the main thread is always killed by an exception | +| `exceptionsSometimes` | checks that the main thread is killed by an exception at least once | + +An uncaught **exception** in the main thread kills the process. These can be +synchronous (thrown in the main thread) or asynchronous (thrown to it from a +different thread). + +| | | +| - | - | +| `alwaysSame` | checks that the computation is deterministic and always produces a value | +| `alwaysSameOn f` | is like `alwaysSame`, but transforms the results with `f` first | +| `alwaysSameBy f` | is like `alwaysSame`, but uses `f` instead of `(==)` to compare | +| `notAlwaysSame` | checks that the computation is nondeterministic | +| `notAlwaysSameOn f` | is like `notAlwaysSame`, but transforms the results with `f` first | +| `notAlwaysSameBy f` | is like `notAlwaysSame`, but uses `f` instead of `(==)` to compare | + +Checking for **determinism** will also find nondeterministic failures: +deadlocking (for instance) is still a result of a test! + +| | | +| - | - | +| `alwaysTrue p` | checks that `p` is true for every result | +| `somewhereTrue p` | checks that `p` is true for at least one result | + +These can be used to check custom predicates. For example, you might want all +your results to be less than five. + +| | | +| - | - | +| `gives xs` |checks that the set of results is exactly `xs` (which may include conditions) | +| `gives' xs` |checks that the set of results is exactly `xs` (which may not include conditions) | + +These let you say exactly what you want the results to be. Your test will fail +if it has any extra results, or misses a result. + +You can check multiple predicates against the same collection of results using +the `dejafus` and `dejafusWithSettings` functions. These avoid recomputing the +results, and so may be faster than multiple `dejafu` / `dejafuWithSettings` +calls. + + +Using HUnit and Tasty +--------------------- + +By itself, Déjà Fu has no framework in place for named test groups and parallel +execution or anything like that. It does one thing and does it well, which is +running test cases for concurrent programs. [HUnit][] and [tasty][] integration +is provided to get more of the features you'd expect from a testing framework. + +The integration is provided by the [hunit-dejafu][] and [tasty-dejafu][] +packages. + +There's a simple naming convention used: the `Test.DejaFu` function `dejafuFoo` +is wrapped in the appropriate way and exposed as `testDejafuFoo` from +`Test.HUnit.DejaFu` and `Test.Tasty.DejaFu`. + +Our example from the start becomes: + +```haskell +testDejafu "Assert the thing holds" myPredicate myAction +``` + +The `autocheck` function is exposed as `testAuto`. + +[HUnit]: https://hackage.haskell.org/package/HUnit +[tasty]: https://hackage.haskell.org/package/tasty +[hunit-dejafu]: https://hackage.haskell.org/package/hunit-dejafu +[tasty-dejafu]: https://hackage.haskell.org/package/tasty-dejafu diff --git a/hunit-dejafu/CHANGELOG.rst b/hunit-dejafu/CHANGELOG.rst index eefd3bb..cae2d05 100644 --- a/hunit-dejafu/CHANGELOG.rst +++ b/hunit-dejafu/CHANGELOG.rst @@ -90,43 +90,47 @@ Added ~~~~~ * Re-exports for the ``Program`` types and their constructors: - * ``Test.HUnit.DejaFu.Program`` - * ``Test.HUnit.DejaFu.Basic`` - * ``Test.HUnit.DejaFu.ConcT`` - * ``Test.HUnit.DejaFu.ConcIO`` - * ``Test.HUnit.DejaFu.WithSetup`` - * ``Test.HUnit.DejaFu.WithSetupAndTeardown`` - * ``Test.HUnit.DejaFu.withSetup`` - * ``Test.HUnit.DejaFu.withTeardown`` - * ``Test.HUnit.DejaFu.withSetupAndTeardown`` + + * ``Test.HUnit.DejaFu.Program`` + * ``Test.HUnit.DejaFu.Basic`` + * ``Test.HUnit.DejaFu.ConcT`` + * ``Test.HUnit.DejaFu.ConcIO`` + * ``Test.HUnit.DejaFu.WithSetup`` + * ``Test.HUnit.DejaFu.WithSetupAndTeardown`` + * ``Test.HUnit.DejaFu.withSetup`` + * ``Test.HUnit.DejaFu.withTeardown`` + * ``Test.HUnit.DejaFu.withSetupAndTeardown`` * Re-exports for the ``Invariant`` type and its functions: - * ``Test.HUnit.DejaFu.Invariant`` - * ``Test.HUnit.DejaFu.registerInvariant`` - * ``Test.HUnit.DejaFu.inspectIORef`` - * ``Test.HUnit.DejaFu.inspectMVar`` - * ``Test.HUnit.DejaFu.inspectTVar`` + + * ``Test.HUnit.DejaFu.Invariant`` + * ``Test.HUnit.DejaFu.registerInvariant`` + * ``Test.HUnit.DejaFu.inspectIORef`` + * ``Test.HUnit.DejaFu.inspectMVar`` + * ``Test.HUnit.DejaFu.inspectTVar`` Changed ~~~~~~~ * Functions which took a ``ConcIO`` now take a ``Program pty IO``: - * ``Test.HUnit.DejaFu.testAuto`` - * ``Test.HUnit.DejaFu.testAutoWay`` - * ``Test.HUnit.DejaFu.testAutoWithSettings`` - * ``Test.HUnit.DejaFu.testDejafu`` - * ``Test.HUnit.DejaFu.testDejafuWay`` - * ``Test.HUnit.DejaFu.testDejafuWithSettings`` - * ``Test.HUnit.DejaFu.testDejafus`` - * ``Test.HUnit.DejaFu.testDejafusWay`` - * ``Test.HUnit.DejaFu.testDejafusWithSettings`` + + * ``Test.HUnit.DejaFu.testAuto`` + * ``Test.HUnit.DejaFu.testAutoWay`` + * ``Test.HUnit.DejaFu.testAutoWithSettings`` + * ``Test.HUnit.DejaFu.testDejafu`` + * ``Test.HUnit.DejaFu.testDejafuWay`` + * ``Test.HUnit.DejaFu.testDejafuWithSettings`` + * ``Test.HUnit.DejaFu.testDejafus`` + * ``Test.HUnit.DejaFu.testDejafusWay`` + * ``Test.HUnit.DejaFu.testDejafusWithSettings`` Removed ~~~~~~~ * The deprecated functions: - * ``Test.HUnit.DejaFu.testDejafuDiscard`` - * ``Test.HUnit.DejaFu.testDejafusDiscard`` + + * ``Test.HUnit.DejaFu.testDejafuDiscard`` + * ``Test.HUnit.DejaFu.testDejafusDiscard`` Miscellaneous ~~~~~~~~~~~~~ @@ -235,9 +239,9 @@ Miscellaneous * GHC 7.10 support is dropped. Dependency lower bounds are: - * :hackage:`base`: 4.9 - * :hackage:`dejafu`: 1.5 - * :hackage:`HUnit`: 1.3.1 + * :hackage:`base`: 4.9 + * :hackage:`dejafu`: 1.5 + * :hackage:`HUnit`: 1.3.1 * The upper bound on :hackage:`dejafu` is 1.6. @@ -289,9 +293,9 @@ Added * (:pull:`238`) Settings-based test functions: - * ``Test.HUnit.DejaFu.testAutoWithSettings`` - * ``Test.HUnit.DejaFu.testDejafuWithSettings`` - * ``Test.HUnit.DejaFu.testDejafusWithSettings`` + * ``Test.HUnit.DejaFu.testAutoWithSettings`` + * ``Test.HUnit.DejaFu.testDejafuWithSettings`` + * ``Test.HUnit.DejaFu.testDejafusWithSettings`` * (:pull:`238`) Re-export of ``Test.DejaFu.Settings``. diff --git a/tasty-dejafu/CHANGELOG.rst b/tasty-dejafu/CHANGELOG.rst index 2a76914..7317329 100644 --- a/tasty-dejafu/CHANGELOG.rst +++ b/tasty-dejafu/CHANGELOG.rst @@ -158,43 +158,47 @@ Added ~~~~~ * Re-exports for the ``Program`` types and their constructors: - * ``Test.Tasty.DejaFu.Program`` - * ``Test.Tasty.DejaFu.Basic`` - * ``Test.Tasty.DejaFu.ConcT`` - * ``Test.Tasty.DejaFu.ConcIO`` - * ``Test.Tasty.DejaFu.WithSetup`` - * ``Test.Tasty.DejaFu.WithSetupAndTeardown`` - * ``Test.Tasty.DejaFu.withSetup`` - * ``Test.Tasty.DejaFu.withTeardown`` - * ``Test.Tasty.DejaFu.withSetupAndTeardown`` + + * ``Test.Tasty.DejaFu.Program`` + * ``Test.Tasty.DejaFu.Basic`` + * ``Test.Tasty.DejaFu.ConcT`` + * ``Test.Tasty.DejaFu.ConcIO`` + * ``Test.Tasty.DejaFu.WithSetup`` + * ``Test.Tasty.DejaFu.WithSetupAndTeardown`` + * ``Test.Tasty.DejaFu.withSetup`` + * ``Test.Tasty.DejaFu.withTeardown`` + * ``Test.Tasty.DejaFu.withSetupAndTeardown`` * Re-exports for the ``Invariant`` type and its functions: - * ``Test.Tasty.DejaFu.Invariant`` - * ``Test.Tasty.DejaFu.registerInvariant`` - * ``Test.Tasty.DejaFu.inspectIORef`` - * ``Test.Tasty.DejaFu.inspectMVar`` - * ``Test.Tasty.DejaFu.inspectTVar`` + + * ``Test.Tasty.DejaFu.Invariant`` + * ``Test.Tasty.DejaFu.registerInvariant`` + * ``Test.Tasty.DejaFu.inspectIORef`` + * ``Test.Tasty.DejaFu.inspectMVar`` + * ``Test.Tasty.DejaFu.inspectTVar`` Changed ~~~~~~~ * Functions which took a ``ConcIO`` now take a ``Program pty IO``: - * ``Test.Tasty.DejaFu.testAuto`` - * ``Test.Tasty.DejaFu.testAutoWay`` - * ``Test.Tasty.DejaFu.testAutoWithSettings`` - * ``Test.Tasty.DejaFu.testDejafu`` - * ``Test.Tasty.DejaFu.testDejafuWay`` - * ``Test.Tasty.DejaFu.testDejafuWithSettings`` - * ``Test.Tasty.DejaFu.testDejafus`` - * ``Test.Tasty.DejaFu.testDejafusWay`` - * ``Test.Tasty.DejaFu.testDejafusWithSettings`` + + * ``Test.Tasty.DejaFu.testAuto`` + * ``Test.Tasty.DejaFu.testAutoWay`` + * ``Test.Tasty.DejaFu.testAutoWithSettings`` + * ``Test.Tasty.DejaFu.testDejafu`` + * ``Test.Tasty.DejaFu.testDejafuWay`` + * ``Test.Tasty.DejaFu.testDejafuWithSettings`` + * ``Test.Tasty.DejaFu.testDejafus`` + * ``Test.Tasty.DejaFu.testDejafusWay`` + * ``Test.Tasty.DejaFu.testDejafusWithSettings`` Removed ~~~~~~~ * The deprecated functions: - * ``Test.Tasty.DejaFu.testDejafuDiscard`` - * ``Test.Tasty.DejaFu.testDejafusDiscard`` + + * ``Test.Tasty.DejaFu.testDejafuDiscard`` + * ``Test.Tasty.DejaFu.testDejafusDiscard`` Miscellaneous ~~~~~~~~~~~~~ @@ -327,8 +331,8 @@ Miscellaneous * GHC 7.10 support is dropped. Dependency lower bounds are: - * :hackage:`base`: 4.9 - * :hackage:`dejafu`: 1.5 + * :hackage:`base`: 4.9 + * :hackage:`dejafu`: 1.5 * The upper bound on :hackage:`dejafu` is 1.6. @@ -368,9 +372,9 @@ Added * (:pull:`238`) Settings-based test functions: - * ``Test.Tasty.DejaFu.testAutoWithSettings`` - * ``Test.Tasty.DejaFu.testDejafuWithSettings`` - * ``Test.Tasty.DejaFu.testDejafusWithSettings`` + * ``Test.Tasty.DejaFu.testAutoWithSettings`` + * ``Test.Tasty.DejaFu.testDejafuWithSettings`` + * ``Test.Tasty.DejaFu.testDejafusWithSettings`` * (:pull:`238`) Re-export of ``Test.DejaFu.Settings``. @@ -611,8 +615,8 @@ Added * Orphan ``IsOption`` instance for ``Test.DejaFu.SCT.Way``. Command-line parameters are: - * "systematically": systematic testing with the default bounds - * "randomly": 100 executions with a fixed random seed + * "systematically": systematic testing with the default bounds + * "randomly": 100 executions with a fixed random seed Changed ~~~~~~~ @@ -670,9 +674,9 @@ Added * Orphan ``IsOption`` instances for ``Test.DejaFu.SCT.Bounds`` and ``MemType``. Command-line parameters are: - * "sc": sequential consistency - * "tso": total store order - * "pso": partial store order + * "sc": sequential consistency + * "tso": total store order + * "pso": partial store order * Re-export ``Test.DejaFu.SCT.Bounds``. @@ -687,8 +691,8 @@ Miscellaneous * Git: :tag:`tasty-dejafu-0.1.1.0` -**Note:** this was misnumbered (it should have been 0.2.1.0) *and* was - never pushed to Hackage, whoops! +**Note:** this was misnumbered (it should have been 0.2.1.0) *and* was never +pushed to Hackage, whoops! Miscellaneous ~~~~~~~~~~~~~