mirror of
https://github.com/barrucadu/dejafu.git
synced 2024-11-22 21:50:51 +03:00
Merge pull request #402 from barrucadu/docs-site
Migrate documentation to mdbook / GitHub Pages
This commit is contained in:
commit
bb15fcf4e4
92
.github/scripts/build-documentation.sh
vendored
Executable file
92
.github/scripts/build-documentation.sh
vendored
Executable file
@ -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
|
17
.github/workflows/ci.yaml
vendored
17
.github/workflows/ci.yaml
vendored
@ -3,6 +3,23 @@ name: Run tests
|
|||||||
on: pull_request
|
on: pull_request
|
||||||
|
|
||||||
jobs:
|
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:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
51
.github/workflows/deploy-documentation.yml
vendored
Normal file
51
.github/workflows/deploy-documentation.yml
vendored
Normal file
@ -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
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ dist
|
|||||||
.stack-work
|
.stack-work
|
||||||
*.tix
|
*.tix
|
||||||
*.prof
|
*.prof
|
||||||
|
_site
|
||||||
|
13
.readthedocs.yaml
Normal file
13
.readthedocs.yaml
Normal file
@ -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
|
@ -14,12 +14,13 @@ dejafu
|
|||||||
- [Release notes](#release-notes)
|
- [Release notes](#release-notes)
|
||||||
- [Questions, feedback, discussion](#questions-feedback-discussion)
|
- [Questions, feedback, discussion](#questions-feedback-discussion)
|
||||||
- [Bibliography](#bibliography)
|
- [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.
|
Déjà Fu is a unit-testing library for concurrent Haskell programs.
|
||||||
Tests are deterministic and expressive, making it easy and convenient
|
Tests are deterministic and expressive, making it easy and convenient
|
||||||
to test your threaded code. Available on [GitHub][], [Hackage][], and
|
to test your threaded code.
|
||||||
[Stackage][].
|
|
||||||
|
Available on [GitHub][], [Hackage][], and [Stackage][].
|
||||||
|
|
||||||
[GitHub]: https://github.com/barrucadu/dejafu
|
[GitHub]: https://github.com/barrucadu/dejafu
|
||||||
[Hackage]: https://hackage.haskell.org/package/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. |
|
| [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. |
|
| [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.
|
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
|
There is also dejafu-tests, the test suite for dejafu. This is in a
|
||||||
@ -162,7 +165,7 @@ the trace by eye.
|
|||||||
Contributing
|
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
|
**If you'd like to get involved with Déjà Fu**, check out [the
|
||||||
"good first issue" label on the issue tracker][beginners].
|
"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,
|
mention in the documentation. Haddock comments get the full citation,
|
||||||
whereas in-line comments just get the shortened name:
|
whereas in-line comments just get the shortened name:
|
||||||
|
|
||||||
- [BPOR] *Bounded partial-order reduction*, K. Coons, M. Musuvathi,
|
- [BPOR] [Bounded partial-order reduction](http://research.microsoft.com/pubs/202164/bpor-oopsla-2013.pdf),
|
||||||
and K. McKinley (2013)
|
K. Coons, M. Musuvathi, and K. McKinley (2013)
|
||||||
http://research.microsoft.com/pubs/202164/bpor-oopsla-2013.pdf
|
|
||||||
|
|
||||||
- [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)
|
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] [Concurrency Testing Using Schedule Bounding: an Empirical Study](http://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2014/PPoPP.pdf),
|
||||||
Empirical Study*, P. Thompson, A. Donaldson, and A. Betts (2014)
|
P. Thompson, A. Donaldson, and A. Betts (2014)
|
||||||
http://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2014/PPoPP.pdf
|
|
||||||
|
|
||||||
- [RMMVerification] *On the Verification of Programs on Relaxed Memory
|
- [RMMVerification] [On the Verification of Programs on Relaxed Memory Models](https://orbi.ulg.ac.be/bitstream/2268/158670/1/thesis.pdf),
|
||||||
Models*, A. Linden (2014)
|
A. Linden (2014)
|
||||||
https://orbi.ulg.ac.be/bitstream/2268/158670/1/thesis.pdf
|
|
||||||
|
|
||||||
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
|
- [Déjà Fu: A Concurrency Testing Library for Haskell](https://www.barrucadu.co.uk/publications/dejafu-hs15.pdf),
|
||||||
C. Runciman (2015)
|
M. Walker and C. Runciman (2015)
|
||||||
https://www.barrucadu.co.uk/publications/dejafu-hs15.pdf
|
|
||||||
|
|
||||||
This details dejafu-0.1, and was presented at the 2015 Haskell
|
This details dejafu-0.1, and was presented at the 2015 Haskell
|
||||||
Symposium.
|
Symposium.
|
||||||
|
|
||||||
- *Déjà Fu: A Concurrency Testing Library for Haskell*, M. Walker and
|
- [Déjà Fu: A Concurrency Testing Library for Haskell](https://www.barrucadu.co.uk/publications/YCS-2016-503.pdf),
|
||||||
C. Runciman (2016)
|
M. Walker and C. Runciman (2016)
|
||||||
https://www.barrucadu.co.uk/publications/YCS-2016-503.pdf
|
|
||||||
|
|
||||||
This is a more in-depth technical report, written between the
|
This is a more in-depth technical report, written between the
|
||||||
dejafu-0.2 and dejafu-0.3 releases.
|
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.
|
||||||
|
@ -46,7 +46,7 @@ Fixed
|
|||||||
|
|
||||||
|
|
||||||
1.11.0.0 (2020-05-14)
|
1.11.0.0 (2020-05-14)
|
||||||
--------------------
|
---------------------
|
||||||
|
|
||||||
* Git: :tag:`concurrency-1.11.0.0`
|
* Git: :tag:`concurrency-1.11.0.0`
|
||||||
* Hackage: :hackage:`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:
|
* (:issue:`286`) Copy across additions from the :hackage:`stm` package:
|
||||||
|
|
||||||
* ``Control.Concurrent.Classy.STM.TQueue.flushTQueue``
|
* ``Control.Concurrent.Classy.STM.TQueue.flushTQueue``
|
||||||
* ``Control.Concurrent.Classy.STM.TBQueue.flushTBQueue``
|
* ``Control.Concurrent.Classy.STM.TBQueue.flushTBQueue``
|
||||||
* ``Control.Concurrent.Classy.STM.TBQueue.lengthTBQueue``
|
* ``Control.Concurrent.Classy.STM.TBQueue.lengthTBQueue``
|
||||||
* ``Control.Concurrent.Classy.STM.TVar.stateTVar``
|
* ``Control.Concurrent.Classy.STM.TVar.stateTVar``
|
||||||
|
|
||||||
* (:issue:`287`) The ``Control.Concurrent.Classy.STM.TSem`` module.
|
* (:issue:`287`) The ``Control.Concurrent.Classy.STM.TSem`` module.
|
||||||
|
|
||||||
@ -183,10 +183,8 @@ Changed
|
|||||||
|
|
||||||
* (:issue:`286`) Copy across changes from the :hackage:`stm` package:
|
* (:issue:`286`) Copy across changes from the :hackage:`stm` package:
|
||||||
|
|
||||||
* Make definition of ``readTQueue`` consistent with
|
* Make definition of ``readTQueue`` consistent with ``readTBQueue``
|
||||||
``readTBQueue``
|
* Performance improvements to ``peekTQueue`` and ``peekTBQueue``.
|
||||||
|
|
||||||
* Performance improvements to ``peekTQueue`` and ``peekTBQueue``.
|
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@ -235,9 +233,9 @@ Miscellaneous
|
|||||||
|
|
||||||
* GHC 7.10 support is dropped. Dependency lower bounds are:
|
* GHC 7.10 support is dropped. Dependency lower bounds are:
|
||||||
|
|
||||||
* :hackage:`base`: 4.9
|
* :hackage:`base`: 4.9
|
||||||
* :hackage:`array`: 0.5.1
|
* :hackage:`array`: 0.5.1
|
||||||
* :hackage:`transformers`: 0.5
|
* :hackage:`transformers`: 0.5
|
||||||
|
|
||||||
|
|
||||||
1.4.0.2 (2018-03-11)
|
1.4.0.2 (2018-03-11)
|
||||||
@ -276,8 +274,8 @@ Changed
|
|||||||
* ``Control.Monad.Conc.Class.peekTicket'`` has a more concrete type,
|
* ``Control.Monad.Conc.Class.peekTicket'`` has a more concrete type,
|
||||||
to make deriving newtype instances of ``MonadConc`` possible:
|
to make deriving newtype instances of ``MonadConc`` possible:
|
||||||
|
|
||||||
* Old: ``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``
|
* New: ``MonadConc m => Proxy m -> Ticket m a -> a``
|
||||||
|
|
||||||
|
|
||||||
1.3.0.0 - The Bound Thread Release (2017-12-23)
|
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:
|
* (:pull:`145`) Bound thread variants of the ``withAsync`` functions:
|
||||||
|
|
||||||
* ``Control.Concurrent.Classy.Async.asyncBound``
|
* ``Control.Concurrent.Classy.Async.asyncBound``
|
||||||
* ``Control.Concurrent.Classy.Async.asyncBoundN``
|
* ``Control.Concurrent.Classy.Async.asyncBoundN``
|
||||||
* ``Control.Concurrent.Classy.Async.withAsyncBound``
|
* ``Control.Concurrent.Classy.Async.withAsyncBound``
|
||||||
* ``Control.Concurrent.Classy.Async.withAsyncBoundN``
|
* ``Control.Concurrent.Classy.Async.withAsyncBoundN``
|
||||||
|
|
||||||
* (:pull:`145`) Bound thread functions in ``MonadConc``:
|
* (:pull:`145`) Bound thread functions in ``MonadConc``:
|
||||||
|
|
||||||
* ``Control.Monad.Conc.Class.forkOS``
|
* ``Control.Monad.Conc.Class.forkOS``
|
||||||
* ``Control.Monad.Conc.Class.forkOSN``
|
* ``Control.Monad.Conc.Class.forkOSN``
|
||||||
* ``Control.Monad.Conc.Class.isCurrentThreadBound``
|
* ``Control.Monad.Conc.Class.isCurrentThreadBound``
|
||||||
|
|
||||||
* (:pull:`145`) Helper functions for bound threads:
|
* (:pull:`145`) Helper functions for bound threads:
|
||||||
|
|
||||||
* ``Control.Monad.Conc.Class.runInBoundThread``
|
* ``Control.Monad.Conc.Class.runInBoundThread``
|
||||||
* ``Control.Monad.Conc.Class.runInUnboundThread``
|
* ``Control.Monad.Conc.Class.runInUnboundThread``
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
@ -328,10 +326,10 @@ Added
|
|||||||
|
|
||||||
* (:issue:`148`) Named thread variants of the ``withAsync`` functions:
|
* (:issue:`148`) Named thread variants of the ``withAsync`` functions:
|
||||||
|
|
||||||
* ``Control.Concurrent.Classy.Async.withAsyncN``
|
* ``Control.Concurrent.Classy.Async.withAsyncN``
|
||||||
* ``Control.Concurrent.Classy.Async.withAsyncOnN``
|
* ``Control.Concurrent.Classy.Async.withAsyncOnN``
|
||||||
* ``Control.Concurrent.Classy.Async.withAsyncWithUnmaskN``
|
* ``Control.Concurrent.Classy.Async.withAsyncWithUnmaskN``
|
||||||
* ``Control.Concurrent.Classy.Async.withAsyncOnWithUnmaskN``
|
* ``Control.Concurrent.Classy.Async.withAsyncOnWithUnmaskN``
|
||||||
|
|
||||||
|
|
||||||
1.2.2.0 (2017-11-05)
|
1.2.2.0 (2017-11-05)
|
||||||
@ -345,12 +343,12 @@ Added
|
|||||||
|
|
||||||
* (:issue:`144`) ``IsConc`` and ``IsSTM`` wrapper types:
|
* (:issue:`144`) ``IsConc`` and ``IsSTM`` wrapper types:
|
||||||
|
|
||||||
* ``Control.Monad.Conc.Class.IsConc`` (constructor unexported)
|
* ``Control.Monad.Conc.Class.IsConc`` (constructor unexported)
|
||||||
* ``Control.Monad.Conc.Class.toIsConc``
|
* ``Control.Monad.Conc.Class.toIsConc``
|
||||||
* ``Control.Monad.Conc.Class.fromIsConc``
|
* ``Control.Monad.Conc.Class.fromIsConc``
|
||||||
* ``Control.Monad.STM.Class.IsSTM`` (constructor unexported)
|
* ``Control.Monad.STM.Class.IsSTM`` (constructor unexported)
|
||||||
* ``Control.Monad.STM.Class.toIsSTM``
|
* ``Control.Monad.STM.Class.toIsSTM``
|
||||||
* ``Control.Monad.STM.Class.fromIsSTM``
|
* ``Control.Monad.STM.Class.fromIsSTM``
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
@ -398,10 +396,10 @@ Added
|
|||||||
|
|
||||||
* (:pull:`125`) Named thread variants of the ``async`` functions:
|
* (:pull:`125`) Named thread variants of the ``async`` functions:
|
||||||
|
|
||||||
* ``Control.Concurrent.Classy.Async.asyncN``
|
* ``Control.Concurrent.Classy.Async.asyncN``
|
||||||
* ``Control.Concurrent.Classy.Async.asyncOnN``
|
* ``Control.Concurrent.Classy.Async.asyncOnN``
|
||||||
* ``Control.Concurrent.Classy.Async.asyncWithUnmaskN``
|
* ``Control.Concurrent.Classy.Async.asyncWithUnmaskN``
|
||||||
* ``Control.Concurrent.Classy.Async.asyncOnWithUnmaskN``
|
* ``Control.Concurrent.Classy.Async.asyncOnWithUnmaskN``
|
||||||
|
|
||||||
|
|
||||||
1.2.0.0 (2017-09-16)
|
1.2.0.0 (2017-09-16)
|
||||||
@ -446,12 +444,12 @@ Added
|
|||||||
|
|
||||||
* Missing functions copied from :hackage:`async`:
|
* Missing functions copied from :hackage:`async`:
|
||||||
|
|
||||||
* ``Control.Concurrent.Classy.Async.uninterruptibleCancel``
|
* ``Control.Concurrent.Classy.Async.uninterruptibleCancel``
|
||||||
* ``Control.Concurrent.Classy.Async.replicateConcurrently``
|
* ``Control.Concurrent.Classy.Async.replicateConcurrently``
|
||||||
* ``Control.Concurrent.Classy.Async.concurrently_``
|
* ``Control.Concurrent.Classy.Async.concurrently_``
|
||||||
* ``Control.Concurrent.Classy.Async.mapConcurrently_``
|
* ``Control.Concurrent.Classy.Async.mapConcurrently_``
|
||||||
* ``Control.Concurrent.Classy.Async.forConcurrently_``
|
* ``Control.Concurrent.Classy.Async.forConcurrently_``
|
||||||
* ``Control.Concurrent.Classy.Async.replicateConcurrently_``
|
* ``Control.Concurrent.Classy.Async.replicateConcurrently_``
|
||||||
|
|
||||||
* ``Control.Concurrent.Classy.Async.Concurrently`` has a ``Semigroup``
|
* ``Control.Concurrent.Classy.Async.Concurrently`` has a ``Semigroup``
|
||||||
instance when built with :hackage:`base` >= 4.9.
|
instance when built with :hackage:`base` >= 4.9.
|
||||||
|
@ -13,6 +13,7 @@ unreleased
|
|||||||
Miscellaneous
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
* Update documentation link in ``Test.DejaFu``.
|
||||||
* Fix GHC compatibility warning.
|
* Fix GHC compatibility warning.
|
||||||
|
|
||||||
|
|
||||||
@ -139,8 +140,9 @@ Added
|
|||||||
|
|
||||||
* Thread action constructors for the ``MonadConc`` ``getMaskingState``
|
* Thread action constructors for the ``MonadConc`` ``getMaskingState``
|
||||||
function:
|
function:
|
||||||
* ``Test.DejaFu.Types.ThreadAction``, ``GetMaskingState``
|
|
||||||
* ``Test.DejaFu.Types.Lookahead``, ``WillGetMaskingState``
|
* ``Test.DejaFu.Types.ThreadAction``, ``GetMaskingState``
|
||||||
|
* ``Test.DejaFu.Types.Lookahead``, ``WillGetMaskingState``
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@ -194,50 +196,57 @@ Miscellaneous
|
|||||||
Added
|
Added
|
||||||
~~~~~
|
~~~~~
|
||||||
|
|
||||||
* The ``Test.DejaFu.Types.MonadDejaFu`` typeclass, containing the
|
* The ``Test.DejaFu.Types.MonadDejaFu`` typeclass, containing the primitives
|
||||||
primitives needed to run a concurrent program. There are instances
|
needed to run a concurrent program. There are instances for:
|
||||||
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.
|
|
||||||
|
|
||||||
* Thread action constructors for ``MonadConc``
|
* ``IO``, which is probably the ``MonadConc`` instance people used previously,
|
||||||
``supportsBoundThreads`` function:
|
so there is no breaking change there.
|
||||||
* ``Test.DejaFu.Types.ThreadAction``, ``SupportsBoundThreads``
|
* ``CatchT (ST t)``, meaning that concurrent programs can be run without
|
||||||
* ``Test.DejaFu.Types.Lookahead``, ``WillSupportsBoundThreads``
|
``IO`` once more.
|
||||||
|
|
||||||
|
* Thread action constructors for ``MonadConc`` ``supportsBoundThreads``
|
||||||
|
function:
|
||||||
|
|
||||||
|
* ``Test.DejaFu.Types.ThreadAction``, ``SupportsBoundThreads``
|
||||||
|
* ``Test.DejaFu.Types.Lookahead``, ``WillSupportsBoundThreads``
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
* Many functions which had a ``MonadConc`` constraint now have a
|
* Many functions which had a ``MonadConc`` constraint now have a ``MonadDejaFu``
|
||||||
``MonadDejaFu`` constraint:
|
constraint:
|
||||||
* In ``Test.DejaFu``
|
|
||||||
* ``autocheck``
|
* In ``Test.DejaFu``
|
||||||
* ``autocheckWay``
|
|
||||||
* ``autocheckWithSettings``
|
* ``autocheck``
|
||||||
* ``dejafu``
|
* ``autocheckWay``
|
||||||
* ``dejafuWay``
|
* ``autocheckWithSettings``
|
||||||
* ``dejafuWithSettings``
|
* ``dejafu``
|
||||||
* ``dejafus``
|
* ``dejafuWay``
|
||||||
* ``dejafusWay``
|
* ``dejafuWithSettings``
|
||||||
* ``dejafusWithSettings``
|
* ``dejafus``
|
||||||
* ``runTest``
|
* ``dejafusWay``
|
||||||
* ``runTestWay``
|
* ``dejafusWithSettings``
|
||||||
* ``runTestWithSettings``
|
* ``runTest``
|
||||||
* In ``Test.DejaFu.Conc``
|
* ``runTestWay``
|
||||||
* ``runConcurrent``
|
* ``runTestWithSettings``
|
||||||
* ``recordSnapshot``
|
|
||||||
* ``runSnapshot``
|
* In ``Test.DejaFu.Conc``
|
||||||
* In ``Test.DejaFu.SCT``
|
|
||||||
* ``runSCT``
|
* ``runConcurrent``
|
||||||
* ``resultsSet``
|
* ``recordSnapshot``
|
||||||
* ``runSCT'``
|
* ``runSnapshot``
|
||||||
* ``resultsSet'``
|
|
||||||
* ``runSCTWithSettings``
|
* In ``Test.DejaFu.SCT``
|
||||||
* ``resultsSetWithSettings``
|
|
||||||
* ``runSCTWithSettings'``
|
* ``runSCT``
|
||||||
* ``resultsSetWithSettings'``
|
* ``resultsSet``
|
||||||
|
* ``runSCT'``
|
||||||
|
* ``resultsSet'``
|
||||||
|
* ``runSCTWithSettings``
|
||||||
|
* ``resultsSetWithSettings``
|
||||||
|
* ``runSCTWithSettings'``
|
||||||
|
* ``resultsSetWithSettings'``
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@ -269,26 +278,29 @@ Added
|
|||||||
|
|
||||||
* The ``Program`` types and their constructors (re-exported from
|
* The ``Program`` types and their constructors (re-exported from
|
||||||
``Test.DejaFu``):
|
``Test.DejaFu``):
|
||||||
* ``Test.DejaFu.Conc.Program``
|
|
||||||
* ``Test.DejaFu.Conc.Basic``
|
* ``Test.DejaFu.Conc.Program``
|
||||||
* ``Test.DejaFu.Conc.WithSetup``
|
* ``Test.DejaFu.Conc.Basic``
|
||||||
* ``Test.DejaFu.Conc.WithSetupAndTeardown``
|
* ``Test.DejaFu.Conc.WithSetup``
|
||||||
* ``Test.DejaFu.Conc.withSetup``
|
* ``Test.DejaFu.Conc.WithSetupAndTeardown``
|
||||||
* ``Test.DejaFu.Conc.withTeardown``
|
* ``Test.DejaFu.Conc.withSetup``
|
||||||
* ``Test.DejaFu.Conc.withSetupAndTeardown``
|
* ``Test.DejaFu.Conc.withTeardown``
|
||||||
|
* ``Test.DejaFu.Conc.withSetupAndTeardown``
|
||||||
|
|
||||||
* The ``Invariant`` type and associated functions (re-exported from
|
* The ``Invariant`` type and associated functions (re-exported from
|
||||||
``Test.DejaFu``):
|
``Test.DejaFu``):
|
||||||
* ``Test.DejaFu.Conc.Invariant``
|
|
||||||
* ``Test.DejaFu.Conc.registerInvariant``
|
* ``Test.DejaFu.Conc.Invariant``
|
||||||
* ``Test.DejaFu.Conc.inspectIORef``
|
* ``Test.DejaFu.Conc.registerInvariant``
|
||||||
* ``Test.DejaFu.Conc.inspectMVar``
|
* ``Test.DejaFu.Conc.inspectIORef``
|
||||||
* ``Test.DejaFu.Conc.inspectTVar``
|
* ``Test.DejaFu.Conc.inspectMVar``
|
||||||
|
* ``Test.DejaFu.Conc.inspectTVar``
|
||||||
|
|
||||||
* New snapshotting functions:
|
* New snapshotting functions:
|
||||||
* ``Test.DejaFu.Conc.Snapshot``
|
|
||||||
* ``Test.DejaFu.Conc.recordSnapshot``
|
* ``Test.DejaFu.Conc.Snapshot``
|
||||||
* ``Test.DejaFu.Conc.runSnapshot``
|
* ``Test.DejaFu.Conc.recordSnapshot``
|
||||||
|
* ``Test.DejaFu.Conc.runSnapshot``
|
||||||
|
|
||||||
* ``Test.DejaFu.Settings.llengthBound``, which now applies to all ways
|
* ``Test.DejaFu.Settings.llengthBound``, which now applies to all ways
|
||||||
of testing.
|
of testing.
|
||||||
@ -299,14 +311,15 @@ Added
|
|||||||
* ``Test.DejaFu.runTestWithSettings`` function.
|
* ``Test.DejaFu.runTestWithSettings`` function.
|
||||||
|
|
||||||
* A simplified form of the concurrency state:
|
* A simplified form of the concurrency state:
|
||||||
* ``Test.DejaFu.Types.ConcurrencyState``
|
|
||||||
* ``Test.DejaFu.Types.isBuffered``
|
* ``Test.DejaFu.Types.ConcurrencyState``
|
||||||
* ``Test.DejaFu.Types.numBuffered``
|
* ``Test.DejaFu.Types.isBuffered``
|
||||||
* ``Test.DejaFu.Types.isFull``
|
* ``Test.DejaFu.Types.numBuffered``
|
||||||
* ``Test.DejaFu.Types.canInterrupt``
|
* ``Test.DejaFu.Types.isFull``
|
||||||
* ``Test.DejaFu.Types.canInterruptL``
|
* ``Test.DejaFu.Types.canInterrupt``
|
||||||
* ``Test.DejaFu.Types.isMaskedInterruptible``
|
* ``Test.DejaFu.Types.canInterruptL``
|
||||||
* ``Test.DejaFu.Types.isMaskedUninterruptible``
|
* ``Test.DejaFu.Types.isMaskedInterruptible``
|
||||||
|
* ``Test.DejaFu.Types.isMaskedUninterruptible``
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
@ -318,75 +331,83 @@ Changed
|
|||||||
return a representative trace for each unique condition.
|
return a representative trace for each unique condition.
|
||||||
|
|
||||||
* Functions which took a ``ConcT`` now take a ``Program pty``:
|
* Functions which took a ``ConcT`` now take a ``Program pty``:
|
||||||
* ``Test.DejaFu.autocheck``
|
|
||||||
* ``Test.DejaFu.autocheckWay``
|
* ``Test.DejaFu.autocheck``
|
||||||
* ``Test.DejaFu.autocheckWithSettings``
|
* ``Test.DejaFu.autocheckWay``
|
||||||
* ``Test.DejaFu.dejafu``
|
* ``Test.DejaFu.autocheckWithSettings``
|
||||||
* ``Test.DejaFu.dejafuWay``
|
* ``Test.DejaFu.dejafu``
|
||||||
* ``Test.DejaFu.dejafuWithSettings``
|
* ``Test.DejaFu.dejafuWay``
|
||||||
* ``Test.DejaFu.dejafus``
|
* ``Test.DejaFu.dejafuWithSettings``
|
||||||
* ``Test.DejaFu.dejafusWay``
|
* ``Test.DejaFu.dejafus``
|
||||||
* ``Test.DejaFu.dejafusWithSettings``
|
* ``Test.DejaFu.dejafusWay``
|
||||||
* ``Test.DejaFu.runTest``
|
* ``Test.DejaFu.dejafusWithSettings``
|
||||||
* ``Test.DejaFu.runTestWay``
|
* ``Test.DejaFu.runTest``
|
||||||
* ``Test.DejaFu.runTestWithSettings``
|
* ``Test.DejaFu.runTestWay``
|
||||||
* ``Test.DejaFu.Conc.runConcurrent``
|
* ``Test.DejaFu.runTestWithSettings``
|
||||||
* ``Test.DejaFu.SCT.runSCT``
|
* ``Test.DejaFu.Conc.runConcurrent``
|
||||||
* ``Test.DejaFu.SCT.resultsSet``
|
* ``Test.DejaFu.SCT.runSCT``
|
||||||
* ``Test.DejaFu.SCT.runSCT'``
|
* ``Test.DejaFu.SCT.resultsSet``
|
||||||
* ``Test.DejaFu.SCT.resultsSet'``
|
* ``Test.DejaFu.SCT.runSCT'``
|
||||||
* ``Test.DejaFu.SCT.runSCTWithSettings``
|
* ``Test.DejaFu.SCT.resultsSet'``
|
||||||
* ``Test.DejaFu.SCT.resultsSetWithSettings``
|
* ``Test.DejaFu.SCT.runSCTWithSettings``
|
||||||
* ``Test.DejaFu.SCT.runSCTWithSettings'``
|
* ``Test.DejaFu.SCT.resultsSetWithSettings``
|
||||||
* ``Test.DejaFu.SCT.resultsSetWithSettings'``
|
* ``Test.DejaFu.SCT.runSCTWithSettings'``
|
||||||
|
* ``Test.DejaFu.SCT.resultsSetWithSettings'``
|
||||||
|
|
||||||
* ``Test.DejaFu.Conc.ConcT`` is an alias for ``Program Basic``.
|
* ``Test.DejaFu.Conc.ConcT`` is an alias for ``Program Basic``.
|
||||||
|
|
||||||
* ``Test.DejaFu.Types.Bounds``:
|
* ``Test.DejaFu.Types.Bounds``:
|
||||||
* Removed ``boundLength`` field.
|
|
||||||
|
* Removed ``boundLength`` field.
|
||||||
|
|
||||||
* ``Test.DejaFu.Types.Condition``:
|
* ``Test.DejaFu.Types.Condition``:
|
||||||
* Added ``InvariantFailure`` constructor
|
|
||||||
* Removed ``STMDeadlock`` constructor
|
* Added ``InvariantFailure`` constructor
|
||||||
|
* Removed ``STMDeadlock`` constructor
|
||||||
|
|
||||||
* ``Test.DejaFu.Types.Error``:
|
* ``Test.DejaFu.Types.Error``:
|
||||||
* Removed ``NestedSubconcurrency``, ``MultithreadedSubconcurrency``,
|
|
||||||
and ``LateDontCheck`` constructors.
|
* Removed ``NestedSubconcurrency``, ``MultithreadedSubconcurrency``, and
|
||||||
|
``LateDontCheck`` constructors.
|
||||||
|
|
||||||
* ``Test.DejaFu.Types.Lookahead``:
|
* ``Test.DejaFu.Types.Lookahead``:
|
||||||
* Added ``WillRegisterInvariant`` constructor
|
|
||||||
* Removed ``WillSubconcurrency``, ``WillStopSubconcurrency``, and
|
* Added ``WillRegisterInvariant`` constructor
|
||||||
``WillDontCheck`` constructors
|
* Removed ``WillSubconcurrency``, ``WillStopSubconcurrency``, and
|
||||||
|
``WillDontCheck`` constructors
|
||||||
|
|
||||||
* ``Test.DejaFu.Types.ThreadAction``:
|
* ``Test.DejaFu.Types.ThreadAction``:
|
||||||
* Added ``RegisterInvariant`` constructor
|
|
||||||
* Removed ``Subconcurrency``, ``StopSubconcurrency``, and
|
* Added ``RegisterInvariant`` constructor
|
||||||
``DontCheck`` constructors
|
* Removed ``Subconcurrency``, ``StopSubconcurrency``, and
|
||||||
|
``DontCheck`` constructors
|
||||||
|
|
||||||
Removed
|
Removed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
* The deprecated functions:
|
* The deprecated functions:
|
||||||
* ``Test.DejaFu.dejafuDiscard``
|
|
||||||
* ``Test.DejaFu.SCT.runSCTDiscard``
|
* ``Test.DejaFu.dejafuDiscard``
|
||||||
* ``Test.DejaFu.SCT.runSCTDiscard'``
|
* ``Test.DejaFu.SCT.runSCTDiscard``
|
||||||
* ``Test.DejaFu.SCT.resultsSetDiscard``
|
* ``Test.DejaFu.SCT.runSCTDiscard'``
|
||||||
* ``Test.DejaFu.SCT.resultsSetDiscard'``
|
* ``Test.DejaFu.SCT.resultsSetDiscard``
|
||||||
* ``Test.DejaFu.SCT.sctBound``
|
* ``Test.DejaFu.SCT.resultsSetDiscard'``
|
||||||
* ``Test.DejaFu.SCT.sctBoundDiscard``
|
* ``Test.DejaFu.SCT.sctBound``
|
||||||
* ``Test.DejaFu.SCT.sctUniformRandom``
|
* ``Test.DejaFu.SCT.sctBoundDiscard``
|
||||||
* ``Test.DejaFu.SCT.sctUniformRandomDiscard``
|
* ``Test.DejaFu.SCT.sctUniformRandom``
|
||||||
* ``Test.DejaFu.SCT.sctWeightedRandom``
|
* ``Test.DejaFu.SCT.sctUniformRandomDiscard``
|
||||||
* ``Test.DejaFu.SCT.sctWeightedRandomDiscard``
|
* ``Test.DejaFu.SCT.sctWeightedRandom``
|
||||||
|
* ``Test.DejaFu.SCT.sctWeightedRandomDiscard``
|
||||||
|
|
||||||
* The deprecated type ``Test.DejaFu.Types.Failure``
|
* The deprecated type ``Test.DejaFu.Types.Failure``
|
||||||
|
|
||||||
* Old snapshotting functions:
|
* Old snapshotting functions:
|
||||||
* ``Test.DejaFu.Conc.DCSnapshot``
|
|
||||||
* ``Test.DejaFu.Conc.runForDCSnapshot``
|
* ``Test.DejaFu.Conc.DCSnapshot``
|
||||||
* ``Test.DejaFu.Conc.runWithDCSnapshot``
|
* ``Test.DejaFu.Conc.runForDCSnapshot``
|
||||||
* ``Test.DejaFu.Conc.canDCSnapshot``
|
* ``Test.DejaFu.Conc.runWithDCSnapshot``
|
||||||
* ``Test.DejaFu.Conc.threadsFromDCSnapshot``
|
* ``Test.DejaFu.Conc.canDCSnapshot``
|
||||||
|
* ``Test.DejaFu.Conc.threadsFromDCSnapshot``
|
||||||
|
|
||||||
* ``Test.DejaFu.Conc.dontCheck``
|
* ``Test.DejaFu.Conc.dontCheck``
|
||||||
|
|
||||||
@ -408,8 +429,9 @@ Added
|
|||||||
|
|
||||||
* ``Test.DejaFu.Types.Error`` for internal errors and misuses, with
|
* ``Test.DejaFu.Types.Error`` for internal errors and misuses, with
|
||||||
predicates:
|
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
|
* Deprecated ``Test.DejaFu.Types.Failure`` type synonym for
|
||||||
``Condition``.
|
``Condition``.
|
||||||
@ -652,9 +674,9 @@ Miscellaneous
|
|||||||
|
|
||||||
* GHC 7.10 support is dropped. Dependency lower bounds are:
|
* GHC 7.10 support is dropped. Dependency lower bounds are:
|
||||||
|
|
||||||
* :hackage:`base`: 4.9
|
* :hackage:`base`: 4.9
|
||||||
* :hackage:`concurrency`: 1.5
|
* :hackage:`concurrency`: 1.5
|
||||||
* :hackage:`transformers`: 0.5
|
* :hackage:`transformers`: 0.5
|
||||||
|
|
||||||
* The upper bound on :hackage:`concurrency` is 1.6.
|
* The upper bound on :hackage:`concurrency` is 1.6.
|
||||||
|
|
||||||
@ -699,8 +721,8 @@ Added
|
|||||||
|
|
||||||
* (:issue:`183`) SCT settings for trace simplification:
|
* (:issue:`183`) SCT settings for trace simplification:
|
||||||
|
|
||||||
* ``Test.DejaFu.Settings.lequality``
|
* ``Test.DejaFu.Settings.lequality``
|
||||||
* ``Test.DejaFu.Settings.lsimplify``
|
* ``Test.DejaFu.Settings.lsimplify``
|
||||||
|
|
||||||
* (:pull:`248`) ``Test.DejaFu.Utils.toTIdTrace`` to extract thread IDs
|
* (:pull:`248`) ``Test.DejaFu.Utils.toTIdTrace`` to extract thread IDs
|
||||||
from a trace.
|
from a trace.
|
||||||
@ -726,23 +748,23 @@ Added
|
|||||||
|
|
||||||
* (:pull:`246`) ``Generic`` instances for:
|
* (:pull:`246`) ``Generic`` instances for:
|
||||||
|
|
||||||
* ``Test.DejaFu.Types.ThreadId``
|
* ``Test.DejaFu.Types.ThreadId``
|
||||||
* ``Test.DejaFu.Types.CRefId``
|
* ``Test.DejaFu.Types.CRefId``
|
||||||
* ``Test.DejaFu.Types.MVarId``
|
* ``Test.DejaFu.Types.MVarId``
|
||||||
* ``Test.DejaFu.Types.TVarId``
|
* ``Test.DejaFu.Types.TVarId``
|
||||||
* ``Test.DejaFu.Types.Id``
|
* ``Test.DejaFu.Types.Id``
|
||||||
* ``Test.DejaFu.Types.ThreadAction``
|
* ``Test.DejaFu.Types.ThreadAction``
|
||||||
* ``Test.DejaFu.Types.Lookahead``
|
* ``Test.DejaFu.Types.Lookahead``
|
||||||
* ``Test.DejaFu.Types.TAction``
|
* ``Test.DejaFu.Types.TAction``
|
||||||
* ``Test.DejaFu.Types.Decision``
|
* ``Test.DejaFu.Types.Decision``
|
||||||
* ``Test.DejaFu.Types.Failure``
|
* ``Test.DejaFu.Types.Failure``
|
||||||
* ``Test.DejaFu.Types.Bounds``
|
* ``Test.DejaFu.Types.Bounds``
|
||||||
* ``Test.DejaFu.Types.PreemptionBound``
|
* ``Test.DejaFu.Types.PreemptionBound``
|
||||||
* ``Test.DejaFu.Types.FairBound``
|
* ``Test.DejaFu.Types.FairBound``
|
||||||
* ``Test.DejaFu.Types.LengthBound``
|
* ``Test.DejaFu.Types.LengthBound``
|
||||||
* ``Test.DejaFu.Types.Discard``
|
* ``Test.DejaFu.Types.Discard``
|
||||||
* ``Test.DejaFu.Types.MemType``
|
* ``Test.DejaFu.Types.MemType``
|
||||||
* ``Test.DejaFu.Types.MonadFailException``
|
* ``Test.DejaFu.Types.MonadFailException``
|
||||||
|
|
||||||
* (:pull:`246`) ``NFData`` instance for
|
* (:pull:`246`) ``NFData`` instance for
|
||||||
``Test.DejaFu.Types.MonadFailException``
|
``Test.DejaFu.Types.MonadFailException``
|
||||||
@ -817,49 +839,55 @@ Added
|
|||||||
|
|
||||||
* (:pull:`238`) A record-based approach to SCT configuration:
|
* (:pull:`238`) A record-based approach to SCT configuration:
|
||||||
|
|
||||||
* ``Test.DejaFu.Settings``
|
* ``Test.DejaFu.Settings`` (re-exported from ``Test.Dejafu`` and
|
||||||
(re-exported from ``Test.Dejafu`` and ``Test.DejaFu.SCT``)
|
``Test.DejaFu.SCT``)
|
||||||
* ``Test.DejaFu.Settings.Settings``
|
* ``Test.DejaFu.Settings.Settings``
|
||||||
* ``Test.DejaFu.Settings.defaultSettings``
|
* ``Test.DejaFu.Settings.defaultSettings``
|
||||||
* ``Test.DejaFu.Settings.fromWayAndMemType``
|
* ``Test.DejaFu.Settings.fromWayAndMemType``
|
||||||
* Lenses:
|
|
||||||
* ``Test.DejaFu.Settings.lway``
|
* Lenses:
|
||||||
* ``Test.DejaFu.Settings.lmemtype``
|
|
||||||
* ``Test.DejaFu.Settings.ldiscard``
|
* ``Test.DejaFu.Settings.lway``
|
||||||
* ``Test.DejaFu.Settings.learlyExit``
|
* ``Test.DejaFu.Settings.lmemtype``
|
||||||
* ``Test.DejaFu.Settings.ldebugShow``
|
* ``Test.DejaFu.Settings.ldiscard``
|
||||||
* ``Test.DejaFu.Settings.ldebugPrint``
|
* ``Test.DejaFu.Settings.learlyExit``
|
||||||
* Lens helpers:
|
* ``Test.DejaFu.Settings.ldebugShow``
|
||||||
* ``Test.DejaFu.Settings.get``
|
* ``Test.DejaFu.Settings.ldebugPrint``
|
||||||
* ``Test.DejaFu.Settings.set``
|
|
||||||
* Runners:
|
* Lens helpers:
|
||||||
* ``Test.DejaFu.SCT.runSCTWithSettings``
|
|
||||||
* ``Test.DejaFu.SCT.runSCTWithSettings'``
|
* ``Test.DejaFu.Settings.get``
|
||||||
* ``Test.DejaFu.SCT.resultsSetWithSettings``
|
* ``Test.DejaFu.Settings.set``
|
||||||
* ``Test.DejaFu.SCT.resultsSetWithSettings'``
|
|
||||||
|
* Runners:
|
||||||
|
|
||||||
|
* ``Test.DejaFu.SCT.runSCTWithSettings``
|
||||||
|
* ``Test.DejaFu.SCT.runSCTWithSettings'``
|
||||||
|
* ``Test.DejaFu.SCT.resultsSetWithSettings``
|
||||||
|
* ``Test.DejaFu.SCT.resultsSetWithSettings'``
|
||||||
|
|
||||||
* (:pull:`238`) Settings-based test functions:
|
* (:pull:`238`) Settings-based test functions:
|
||||||
|
|
||||||
* ``Test.DejaFu.autocheckWithSettings``
|
* ``Test.DejaFu.autocheckWithSettings``
|
||||||
* ``Test.DejaFu.dejafuWithSettings``
|
* ``Test.DejaFu.dejafuWithSettings``
|
||||||
* ``Test.DejaFu.dejafusWithSettings``
|
* ``Test.DejaFu.dejafusWithSettings``
|
||||||
* ``Test.DejaFu.runTestWithSettings``
|
* ``Test.DejaFu.runTestWithSettings``
|
||||||
|
|
||||||
Deprecated
|
Deprecated
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
* (:pull:`238`) SCT function variants:
|
* (:pull:`238`) SCT function variants:
|
||||||
|
|
||||||
* ``Test.DejaFu.SCT.runSCTDiscard``
|
* ``Test.DejaFu.SCT.runSCTDiscard``
|
||||||
* ``Test.DejaFu.SCT.resultSetDiscard``
|
* ``Test.DejaFu.SCT.resultSetDiscard``
|
||||||
* ``Test.DejaFu.SCT.runSCTDiscard'``
|
* ``Test.DejaFu.SCT.runSCTDiscard'``
|
||||||
* ``Test.DejaFu.SCT.resultSetDiscard'``
|
* ``Test.DejaFu.SCT.resultSetDiscard'``
|
||||||
* ``Test.DejaFu.SCT.sctBound``
|
* ``Test.DejaFu.SCT.sctBound``
|
||||||
* ``Test.DejaFu.SCT.sctBoundDiscard``
|
* ``Test.DejaFu.SCT.sctBoundDiscard``
|
||||||
* ``Test.DejaFu.SCT.sctUniformRandom``
|
* ``Test.DejaFu.SCT.sctUniformRandom``
|
||||||
* ``Test.DejaFu.SCT.sctUniformRandomDiscard``
|
* ``Test.DejaFu.SCT.sctUniformRandomDiscard``
|
||||||
* ``Test.DejaFu.SCT.sctWeightedRandom``
|
* ``Test.DejaFu.SCT.sctWeightedRandom``
|
||||||
* ``Test.DejaFu.SCT.sctWeightedRandomDiscard``
|
* ``Test.DejaFu.SCT.sctWeightedRandomDiscard``
|
||||||
|
|
||||||
* (:pull:`238`) The ``Test.DejaFu.Defaults`` module. Import
|
* (:pull:`238`) The ``Test.DejaFu.Defaults`` module. Import
|
||||||
``Test.DejaFu.Settings`` instead.
|
``Test.DejaFu.Settings`` instead.
|
||||||
@ -915,18 +943,18 @@ Added
|
|||||||
* (:pull:`219`) The testing-only ``Test.DejaFu.Conc.dontCheck``
|
* (:pull:`219`) The testing-only ``Test.DejaFu.Conc.dontCheck``
|
||||||
function, and associated definitions:
|
function, and associated definitions:
|
||||||
|
|
||||||
* ``Test.DejaFu.Types.DontCheck``
|
* ``Test.DejaFu.Types.DontCheck``
|
||||||
* ``Test.DejaFu.Types.WillDontCheck``
|
* ``Test.DejaFu.Types.WillDontCheck``
|
||||||
* ``Test.DejaFu.Types.IllegalDontCheck``
|
* ``Test.DejaFu.Types.IllegalDontCheck``
|
||||||
* ``Test.DejaFu.Types.isIllegalDontCheck``
|
* ``Test.DejaFu.Types.isIllegalDontCheck``
|
||||||
|
|
||||||
* (:pull:`219`) A snapshotting approach based on
|
* (:pull:`219`) A snapshotting approach based on
|
||||||
``Test.DejaFu.Conc.dontCheck``:
|
``Test.DejaFu.Conc.dontCheck``:
|
||||||
|
|
||||||
* ``Test.DejaFu.Conc.runForDCSnapshot``
|
* ``Test.DejaFu.Conc.runForDCSnapshot``
|
||||||
* ``Test.DejaFu.Conc.runWithDCSnapshot``
|
* ``Test.DejaFu.Conc.runWithDCSnapshot``
|
||||||
* ``Test.DejaFu.Conc.canDCSnapshot``
|
* ``Test.DejaFu.Conc.canDCSnapshot``
|
||||||
* ``Test.DejaFu.Conc.threadsFromDCSnapshot``
|
* ``Test.DejaFu.Conc.threadsFromDCSnapshot``
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
@ -1011,10 +1039,10 @@ Added
|
|||||||
|
|
||||||
* (:pull:`145`) Thread action and lookahead values for bound threads:
|
* (:pull:`145`) Thread action and lookahead values for bound threads:
|
||||||
|
|
||||||
* ``Test.DejaFu.Types.ForkOS``
|
* ``Test.DejaFu.Types.ForkOS``
|
||||||
* ``Test.DejaFu.Types.IsCurrentThreadBound``
|
* ``Test.DejaFu.Types.IsCurrentThreadBound``
|
||||||
* ``Test.DejaFu.Types.WillForkOS``
|
* ``Test.DejaFu.Types.WillForkOS``
|
||||||
* ``Test.DejaFu.Types.WillIsCurrentThreadBound``
|
* ``Test.DejaFu.Types.WillIsCurrentThreadBound``
|
||||||
|
|
||||||
* (:issue:`155`) ``Test.DejaFu.Types`` and ``Test.DejaFu.Utils``
|
* (:issue:`155`) ``Test.DejaFu.Types`` and ``Test.DejaFu.Utils``
|
||||||
modules, each containing some of what was in ``Test.DejaFu.Common``.
|
modules, each containing some of what was in ``Test.DejaFu.Common``.
|
||||||
@ -1064,15 +1092,15 @@ Removed
|
|||||||
|
|
||||||
* The ``IO`` specific testing functions:
|
* The ``IO`` specific testing functions:
|
||||||
|
|
||||||
* ``Test.DejaFu.autocheckIO``
|
* ``Test.DejaFu.autocheckIO``
|
||||||
* ``Test.DejaFu.dejafuIO``
|
* ``Test.DejaFu.dejafuIO``
|
||||||
* ``Test.DejaFu.dejafusIO``
|
* ``Test.DejaFu.dejafusIO``
|
||||||
* ``Test.DejaFu.autocheckWayIO``
|
* ``Test.DejaFu.autocheckWayIO``
|
||||||
* ``Test.DejaFu.dejafuWayIO``
|
* ``Test.DejaFu.dejafuWayIO``
|
||||||
* ``Test.DejaFu.dejafusWayIO``
|
* ``Test.DejaFu.dejafusWayIO``
|
||||||
* ``Test.DejaFu.dejafuDiscardIO``
|
* ``Test.DejaFu.dejafuDiscardIO``
|
||||||
* ``Test.DejaFu.runTestM``
|
* ``Test.DejaFu.runTestM``
|
||||||
* ``Test.DejaFu.runTestWayM``
|
* ``Test.DejaFu.runTestWayM``
|
||||||
|
|
||||||
* The ``Test.DejaFu.Conc.ConcST`` type alias.
|
* The ``Test.DejaFu.Conc.ConcST`` type alias.
|
||||||
|
|
||||||
@ -1214,16 +1242,16 @@ Added
|
|||||||
|
|
||||||
* Failure predicates (also exported from ``Test.DejaFu``):
|
* Failure predicates (also exported from ``Test.DejaFu``):
|
||||||
|
|
||||||
* ``Test.DejaFu.Common.isAbort``
|
* ``Test.DejaFu.Common.isAbort``
|
||||||
* ``Test.DejaFu.Common.isDeadlock``
|
* ``Test.DejaFu.Common.isDeadlock``
|
||||||
* ``Test.DejaFu.Common.isIllegalSubconcurrency``
|
* ``Test.DejaFu.Common.isIllegalSubconcurrency``
|
||||||
* ``Test.DejaFu.Common.isInternalError``
|
* ``Test.DejaFu.Common.isInternalError``
|
||||||
* ``Test.DejaFu.Common.isUncaughtException``
|
* ``Test.DejaFu.Common.isUncaughtException``
|
||||||
|
|
||||||
* Thread action and lookahead values for ``threadDelay``:
|
* Thread action and lookahead values for ``threadDelay``:
|
||||||
|
|
||||||
* ``Test.DejaFu.Common.ThreadDelay``
|
* ``Test.DejaFu.Common.ThreadDelay``
|
||||||
* ``Test.DejaFu.Common.WillThreadDelay``
|
* ``Test.DejaFu.Common.WillThreadDelay``
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
@ -1350,8 +1378,8 @@ Performance
|
|||||||
* (:issue:`64`) Greatly reduce memory usage in systematic testing when
|
* (:issue:`64`) Greatly reduce memory usage in systematic testing when
|
||||||
discarding traces by using an alternative data structure.
|
discarding traces by using an alternative data structure.
|
||||||
|
|
||||||
* Old: ``O(max trace length * number of executions)``
|
* Old: ``O(max trace length * number of executions)``
|
||||||
* New: ``O(max trace length * number of traces kept)``
|
* New: ``O(max trace length * number of traces kept)``
|
||||||
|
|
||||||
|
|
||||||
0.7.1.0 - The Discard Release (2017-08-10)
|
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:
|
* (:issue:`90`) A way to selectively discard results or traces:
|
||||||
|
|
||||||
* Type: ``Test.DejaFu.SCT.Discard``
|
* Type: ``Test.DejaFu.SCT.Discard``
|
||||||
* Functions: ``Test.DejaFu.SCT.runSCTDiscard``,
|
* Functions: ``Test.DejaFu.SCT.runSCTDiscard``, ``resultsSetDiscard``,
|
||||||
``resultsSetDiscard``, ``sctBoundDiscard``,
|
``sctBoundDiscard``, ``sctUniformRandomDiscard``, and
|
||||||
``sctUniformRandomDiscard``, and ``sctWeightedRandomDiscard``.
|
``sctWeightedRandomDiscard``.
|
||||||
|
|
||||||
* (:issue:`90`) Discarding variants of the testing functions:
|
* (:issue:`90`) Discarding variants of the testing functions:
|
||||||
|
|
||||||
* ``Test.DejaFu.dejafuDiscard``
|
* ``Test.DejaFu.dejafuDiscard``
|
||||||
* ``Test.DejaFu.dejafuDiscardIO``
|
* ``Test.DejaFu.dejafuDiscardIO``
|
||||||
|
|
||||||
* (:issue:`90`) ``Test.DejaFu.Defaults.defaultDiscarder``.
|
* (:issue:`90`) ``Test.DejaFu.Defaults.defaultDiscarder``.
|
||||||
|
|
||||||
@ -1439,13 +1467,12 @@ Added
|
|||||||
* Smart constructors for ``Test.DejaFu.SCT.Way`` (also re-exported
|
* Smart constructors for ``Test.DejaFu.SCT.Way`` (also re-exported
|
||||||
from ``Test.DejaFu``):
|
from ``Test.DejaFu``):
|
||||||
|
|
||||||
* ``Test.DejaFu.SCT.systematically``, like the old
|
* ``Test.DejaFu.SCT.systematically``, like the old ``Systematically``.
|
||||||
``Systematically``.
|
* ``Test.DejaFu.SCT.randomly``, like the old ``Randomly``.
|
||||||
* ``Test.DejaFu.SCT.randomly``, like the old ``Randomly``.
|
* ``Test.DejaFu.SCT.uniformly``, a new uniform (as opposed to weighted) random
|
||||||
* ``Test.DejaFu.SCT.uniformly``, a new uniform (as opposed to
|
scheduler.
|
||||||
weighted) random scheduler.
|
* ``Test.DejaFu.SCT.swarmy``, like the old ``Randomly`` but which can use the
|
||||||
* ``Test.DejaFu.SCT.swarmy``, like the old ``Randomly`` but which
|
same weights for multiple executions.
|
||||||
can use the same weights for multiple executions.
|
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
@ -1463,8 +1490,8 @@ Removed
|
|||||||
* The ``Test.DejaFu.SCT.Way`` type is now abstract, so its
|
* The ``Test.DejaFu.SCT.Way`` type is now abstract, so its
|
||||||
constructors are no longer exported:
|
constructors are no longer exported:
|
||||||
|
|
||||||
* ``Test.DejaFu.SCT.Systematically``
|
* ``Test.DejaFu.SCT.Systematically``
|
||||||
* ``Test.DejaFu.SCT.Randomly``
|
* ``Test.DejaFu.SCT.Randomly``
|
||||||
|
|
||||||
* The ``Test.DejaFu.SCT.sctPreBound``, ``sctFairBound``, and
|
* The ``Test.DejaFu.SCT.sctPreBound``, ``sctFairBound``, and
|
||||||
``sctLengthBound`` functions.
|
``sctLengthBound`` functions.
|
||||||
@ -1571,25 +1598,25 @@ Added
|
|||||||
|
|
||||||
* ``NFData`` instances for:
|
* ``NFData`` instances for:
|
||||||
|
|
||||||
* ``Test.DejaFu.Result``
|
* ``Test.DejaFu.Result``
|
||||||
* ``Test.DejaFu.Common.ThreadId``
|
* ``Test.DejaFu.Common.ThreadId``
|
||||||
* ``Test.DejaFu.Common.CRefId``
|
* ``Test.DejaFu.Common.CRefId``
|
||||||
* ``Test.DejaFu.Common.MVarId``
|
* ``Test.DejaFu.Common.MVarId``
|
||||||
* ``Test.DejaFu.Common.TVarId``
|
* ``Test.DejaFu.Common.TVarId``
|
||||||
* ``Test.DejaFu.Common.IdSource``
|
* ``Test.DejaFu.Common.IdSource``
|
||||||
* ``Test.DejaFu.Common.ThreadAction``
|
* ``Test.DejaFu.Common.ThreadAction``
|
||||||
* ``Test.DejaFu.Common.Lookahead``
|
* ``Test.DejaFu.Common.Lookahead``
|
||||||
* ``Test.DejaFu.Common.ActionType``
|
* ``Test.DejaFu.Common.ActionType``
|
||||||
* ``Test.DejaFu.Common.TAction``
|
* ``Test.DejaFu.Common.TAction``
|
||||||
* ``Test.DejaFu.Common.Decision``
|
* ``Test.DejaFu.Common.Decision``
|
||||||
* ``Test.DejaFu.Common.Failure``
|
* ``Test.DejaFu.Common.Failure``
|
||||||
* ``Test.DejaFu.Common.MemType``
|
* ``Test.DejaFu.Common.MemType``
|
||||||
* ``Test.DejaFu.SCT.Bounds``
|
* ``Test.DejaFu.SCT.Bounds``
|
||||||
* ``Test.DejaFu.SCT.PreemptionBound``
|
* ``Test.DejaFu.SCT.PreemptionBound``
|
||||||
* ``Test.DejaFu.SCT.FairBound``
|
* ``Test.DejaFu.SCT.FairBound``
|
||||||
* ``Test.DejaFu.SCT.LengthBound``
|
* ``Test.DejaFu.SCT.LengthBound``
|
||||||
* ``Test.DejaFu.SCT.Way``
|
* ``Test.DejaFu.SCT.Way``
|
||||||
* ``Test.DejaFu.STM.Result``
|
* ``Test.DejaFu.STM.Result``
|
||||||
|
|
||||||
* ``Eq``, ``Ord``, and ``Show`` instances for
|
* ``Eq``, ``Ord``, and ``Show`` instances for
|
||||||
``Test.DejaFu.Common.IdSource``.
|
``Test.DejaFu.Common.IdSource``.
|
||||||
@ -1661,8 +1688,8 @@ Added
|
|||||||
|
|
||||||
* Thread action and lookahead values for ``tryReadMVar``:
|
* Thread action and lookahead values for ``tryReadMVar``:
|
||||||
|
|
||||||
* ``Test.DejaFu.Common.TryReadMVar``
|
* ``Test.DejaFu.Common.TryReadMVar``
|
||||||
* ``Test.DejaFu.Common.WillTryReadMVar``
|
* ``Test.DejaFu.Common.WillTryReadMVar``
|
||||||
|
|
||||||
* The testing-only ``Test.DejaFu.Conc.subconcurrency`` function.
|
* The testing-only ``Test.DejaFu.Conc.subconcurrency`` function.
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ dejafu
|
|||||||
- [Release notes](#release-notes)
|
- [Release notes](#release-notes)
|
||||||
- [Questions, feedback, discussion](#questions-feedback-discussion)
|
- [Questions, feedback, discussion](#questions-feedback-discussion)
|
||||||
- [Bibliography](#bibliography)
|
- [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.
|
Déjà Fu is a unit-testing library for concurrent Haskell programs.
|
||||||
Tests are deterministic and expressive, making it easy and convenient
|
Tests are deterministic and expressive, making it easy and convenient
|
||||||
|
@ -13,7 +13,7 @@ are written using the <https://hackage.haskell.org/package/concurrency
|
|||||||
concurrency> package's 'MonadConc' typeclass.
|
concurrency> package's 'MonadConc' typeclass.
|
||||||
|
|
||||||
For more in-depth documentation, including migration guides from
|
For more in-depth documentation, including migration guides from
|
||||||
earlier versions of dejafu, see the <https://dejafu.readthedocs.io
|
earlier versions of dejafu, see the <https://dejafu.docs.barrucadu.co.uk/
|
||||||
website>.
|
website>.
|
||||||
|
|
||||||
__A first test:__ This is a simple concurrent program which forks two
|
__A first test:__ This is a simple concurrent program which forks two
|
||||||
|
@ -13,7 +13,7 @@ description:
|
|||||||
package by enabling you to deterministically test your concurrent
|
package by enabling you to deterministically test your concurrent
|
||||||
programs.
|
programs.
|
||||||
.
|
.
|
||||||
See the <https://dejafu.readthedocs.io website> or README for more.
|
See the <https://dejafu.docs.barrucadu.co.uk/ website> or README for more.
|
||||||
|
|
||||||
homepage: https://github.com/barrucadu/dejafu
|
homepage: https://github.com/barrucadu/dejafu
|
||||||
license: MIT
|
license: MIT
|
||||||
|
1
doc/.gitignore
vendored
1
doc/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
_build
|
|
102
doc/advanced.rst
102
doc/advanced.rst
@ -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
|
|
@ -1 +0,0 @@
|
|||||||
../concurrency/CHANGELOG.rst
|
|
@ -1 +0,0 @@
|
|||||||
../dejafu/CHANGELOG.rst
|
|
@ -1 +0,0 @@
|
|||||||
../hunit-dejafu/CHANGELOG.rst
|
|
@ -1 +0,0 @@
|
|||||||
../tasty-dejafu/CHANGELOG.rst
|
|
171
doc/conf.py
171
doc/conf.py
@ -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'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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" <labels/good%20first%20issue>`.
|
|
||||||
* Tackle a bigger issue, perhaps one of the :github:`roadmap issues <labels/roadmap>`!
|
|
||||||
* 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 <labels/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!
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
@ -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 <dejafu>`, and :stackage:`Stackage <dejafu>`.
|
|
||||||
|
|
||||||
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
|
|
||||||
<issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22>`.
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
70
doc/ghc.rst
70
doc/ghc.rst
@ -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.
|
|
@ -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 <changelog_concurrency>
|
|
||||||
dejafu <changelog_dejafu>
|
|
||||||
hunit-dejafu <changelog_hunit-dejafu>
|
|
||||||
tasty-dejafu <changelog_tasty-dejafu>
|
|
@ -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 <dejafu-0.9.1.1>` 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 <>`.
|
|
@ -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 <dejafu-1.12.0.0>`.
|
|
||||||
|
|
||||||
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 <>`.
|
|
@ -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
|
|
||||||
<blob/2a15549d97c2fa12f5e8b92ab918fdb34da78281/dejafu-tests/Cases/Refinement.hs>`,
|
|
||||||
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
|
|
@ -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
|
|
||||||
``<package>-<version>``, 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!
|
|
@ -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.
|
|
@ -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
|
|
||||||
<blob/2a15549d97c2fa12f5e8b92ab918fdb34da78281/dejafu-tests/Examples/AutoUpdate.hs>`,
|
|
||||||
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``.
|
|
9
docs/.gitignore
vendored
Normal file
9
docs/.gitignore
vendored
Normal file
@ -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
|
17
docs/book.toml
Normal file
17
docs/book.toml
Normal file
@ -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"
|
@ -1,12 +1,12 @@
|
|||||||
# Minimal makefile for Sphinx documentation
|
# Minimal makefile for Sphinx documentation
|
||||||
#
|
#
|
||||||
|
|
||||||
# You can set these variables from the command line.
|
# You can set these variables from the command line, and also
|
||||||
SPHINXOPTS =
|
# from the environment for the first two.
|
||||||
SPHINXBUILD = sphinx-build
|
SPHINXOPTS ?=
|
||||||
SPHINXPROJ = DjFu
|
SPHINXBUILD ?= sphinx-build
|
||||||
SOURCEDIR = .
|
SOURCEDIR = source
|
||||||
BUILDDIR = _build
|
BUILDDIR = build
|
||||||
|
|
||||||
# Put it first so that "make" without argument is like "make help".
|
# Put it first so that "make" without argument is like "make help".
|
||||||
help:
|
help:
|
2
docs/readthedocs/requirements.txt
Normal file
2
docs/readthedocs/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
sphinx==7.1.2
|
||||||
|
sphinx-rtd-theme==1.3.0rc1
|
23
docs/readthedocs/source/conf.py
Normal file
23
docs/readthedocs/source/conf.py
Normal file
@ -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'
|
10
docs/readthedocs/source/index.rst
Normal file
10
docs/readthedocs/source/index.rst
Normal file
@ -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 <https://dejafu.docs.barrucadu.co.uk/>`.
|
25
docs/src/SUMMARY.md
Normal file
25
docs/src/SUMMARY.md
Normal file
@ -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)
|
96
docs/src/advanced-usage.md
Normal file
96
docs/src/advanced-usage.md
Normal file
@ -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
|
||||||
|
```
|
221
docs/src/dev-docs/contributing.md
Normal file
221
docs/src/dev-docs/contributing.md
Normal file
@ -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.
|
61
docs/src/dev-docs/release-process.md
Normal file
61
docs/src/dev-docs/release-process.md
Normal file
@ -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 `<package>-<version>`, 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!
|
70
docs/src/dev-docs/supported-ghc-versions.md
Normal file
70
docs/src/dev-docs/supported-ghc-versions.md
Normal file
@ -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
|
122
docs/src/migration-guides/0x-1x.md
Normal file
122
docs/src/migration-guides/0x-1x.md
Normal file
@ -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/
|
117
docs/src/migration-guides/1x-2x.md
Normal file
117
docs/src/migration-guides/1x-2x.md
Normal file
@ -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/
|
148
docs/src/refinement-testing.md
Normal file
148
docs/src/refinement-testing.md
Normal file
@ -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
|
115
docs/src/typeclasses.md
Normal file
115
docs/src/typeclasses.md
Normal file
@ -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.
|
240
docs/src/unit-testing.md
Normal file
240
docs/src/unit-testing.md
Normal file
@ -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
|
@ -90,43 +90,47 @@ Added
|
|||||||
~~~~~
|
~~~~~
|
||||||
|
|
||||||
* Re-exports for the ``Program`` types and their constructors:
|
* Re-exports for the ``Program`` types and their constructors:
|
||||||
* ``Test.HUnit.DejaFu.Program``
|
|
||||||
* ``Test.HUnit.DejaFu.Basic``
|
* ``Test.HUnit.DejaFu.Program``
|
||||||
* ``Test.HUnit.DejaFu.ConcT``
|
* ``Test.HUnit.DejaFu.Basic``
|
||||||
* ``Test.HUnit.DejaFu.ConcIO``
|
* ``Test.HUnit.DejaFu.ConcT``
|
||||||
* ``Test.HUnit.DejaFu.WithSetup``
|
* ``Test.HUnit.DejaFu.ConcIO``
|
||||||
* ``Test.HUnit.DejaFu.WithSetupAndTeardown``
|
* ``Test.HUnit.DejaFu.WithSetup``
|
||||||
* ``Test.HUnit.DejaFu.withSetup``
|
* ``Test.HUnit.DejaFu.WithSetupAndTeardown``
|
||||||
* ``Test.HUnit.DejaFu.withTeardown``
|
* ``Test.HUnit.DejaFu.withSetup``
|
||||||
* ``Test.HUnit.DejaFu.withSetupAndTeardown``
|
* ``Test.HUnit.DejaFu.withTeardown``
|
||||||
|
* ``Test.HUnit.DejaFu.withSetupAndTeardown``
|
||||||
|
|
||||||
* Re-exports for the ``Invariant`` type and its functions:
|
* Re-exports for the ``Invariant`` type and its functions:
|
||||||
* ``Test.HUnit.DejaFu.Invariant``
|
|
||||||
* ``Test.HUnit.DejaFu.registerInvariant``
|
* ``Test.HUnit.DejaFu.Invariant``
|
||||||
* ``Test.HUnit.DejaFu.inspectIORef``
|
* ``Test.HUnit.DejaFu.registerInvariant``
|
||||||
* ``Test.HUnit.DejaFu.inspectMVar``
|
* ``Test.HUnit.DejaFu.inspectIORef``
|
||||||
* ``Test.HUnit.DejaFu.inspectTVar``
|
* ``Test.HUnit.DejaFu.inspectMVar``
|
||||||
|
* ``Test.HUnit.DejaFu.inspectTVar``
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
* Functions which took a ``ConcIO`` now take a ``Program pty IO``:
|
* Functions which took a ``ConcIO`` now take a ``Program pty IO``:
|
||||||
* ``Test.HUnit.DejaFu.testAuto``
|
|
||||||
* ``Test.HUnit.DejaFu.testAutoWay``
|
* ``Test.HUnit.DejaFu.testAuto``
|
||||||
* ``Test.HUnit.DejaFu.testAutoWithSettings``
|
* ``Test.HUnit.DejaFu.testAutoWay``
|
||||||
* ``Test.HUnit.DejaFu.testDejafu``
|
* ``Test.HUnit.DejaFu.testAutoWithSettings``
|
||||||
* ``Test.HUnit.DejaFu.testDejafuWay``
|
* ``Test.HUnit.DejaFu.testDejafu``
|
||||||
* ``Test.HUnit.DejaFu.testDejafuWithSettings``
|
* ``Test.HUnit.DejaFu.testDejafuWay``
|
||||||
* ``Test.HUnit.DejaFu.testDejafus``
|
* ``Test.HUnit.DejaFu.testDejafuWithSettings``
|
||||||
* ``Test.HUnit.DejaFu.testDejafusWay``
|
* ``Test.HUnit.DejaFu.testDejafus``
|
||||||
* ``Test.HUnit.DejaFu.testDejafusWithSettings``
|
* ``Test.HUnit.DejaFu.testDejafusWay``
|
||||||
|
* ``Test.HUnit.DejaFu.testDejafusWithSettings``
|
||||||
|
|
||||||
Removed
|
Removed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
* The deprecated functions:
|
* The deprecated functions:
|
||||||
* ``Test.HUnit.DejaFu.testDejafuDiscard``
|
|
||||||
* ``Test.HUnit.DejaFu.testDejafusDiscard``
|
* ``Test.HUnit.DejaFu.testDejafuDiscard``
|
||||||
|
* ``Test.HUnit.DejaFu.testDejafusDiscard``
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@ -235,9 +239,9 @@ Miscellaneous
|
|||||||
|
|
||||||
* GHC 7.10 support is dropped. Dependency lower bounds are:
|
* GHC 7.10 support is dropped. Dependency lower bounds are:
|
||||||
|
|
||||||
* :hackage:`base`: 4.9
|
* :hackage:`base`: 4.9
|
||||||
* :hackage:`dejafu`: 1.5
|
* :hackage:`dejafu`: 1.5
|
||||||
* :hackage:`HUnit`: 1.3.1
|
* :hackage:`HUnit`: 1.3.1
|
||||||
|
|
||||||
* The upper bound on :hackage:`dejafu` is 1.6.
|
* The upper bound on :hackage:`dejafu` is 1.6.
|
||||||
|
|
||||||
@ -289,9 +293,9 @@ Added
|
|||||||
|
|
||||||
* (:pull:`238`) Settings-based test functions:
|
* (:pull:`238`) Settings-based test functions:
|
||||||
|
|
||||||
* ``Test.HUnit.DejaFu.testAutoWithSettings``
|
* ``Test.HUnit.DejaFu.testAutoWithSettings``
|
||||||
* ``Test.HUnit.DejaFu.testDejafuWithSettings``
|
* ``Test.HUnit.DejaFu.testDejafuWithSettings``
|
||||||
* ``Test.HUnit.DejaFu.testDejafusWithSettings``
|
* ``Test.HUnit.DejaFu.testDejafusWithSettings``
|
||||||
|
|
||||||
* (:pull:`238`) Re-export of ``Test.DejaFu.Settings``.
|
* (:pull:`238`) Re-export of ``Test.DejaFu.Settings``.
|
||||||
|
|
||||||
|
@ -158,43 +158,47 @@ Added
|
|||||||
~~~~~
|
~~~~~
|
||||||
|
|
||||||
* Re-exports for the ``Program`` types and their constructors:
|
* Re-exports for the ``Program`` types and their constructors:
|
||||||
* ``Test.Tasty.DejaFu.Program``
|
|
||||||
* ``Test.Tasty.DejaFu.Basic``
|
* ``Test.Tasty.DejaFu.Program``
|
||||||
* ``Test.Tasty.DejaFu.ConcT``
|
* ``Test.Tasty.DejaFu.Basic``
|
||||||
* ``Test.Tasty.DejaFu.ConcIO``
|
* ``Test.Tasty.DejaFu.ConcT``
|
||||||
* ``Test.Tasty.DejaFu.WithSetup``
|
* ``Test.Tasty.DejaFu.ConcIO``
|
||||||
* ``Test.Tasty.DejaFu.WithSetupAndTeardown``
|
* ``Test.Tasty.DejaFu.WithSetup``
|
||||||
* ``Test.Tasty.DejaFu.withSetup``
|
* ``Test.Tasty.DejaFu.WithSetupAndTeardown``
|
||||||
* ``Test.Tasty.DejaFu.withTeardown``
|
* ``Test.Tasty.DejaFu.withSetup``
|
||||||
* ``Test.Tasty.DejaFu.withSetupAndTeardown``
|
* ``Test.Tasty.DejaFu.withTeardown``
|
||||||
|
* ``Test.Tasty.DejaFu.withSetupAndTeardown``
|
||||||
|
|
||||||
* Re-exports for the ``Invariant`` type and its functions:
|
* Re-exports for the ``Invariant`` type and its functions:
|
||||||
* ``Test.Tasty.DejaFu.Invariant``
|
|
||||||
* ``Test.Tasty.DejaFu.registerInvariant``
|
* ``Test.Tasty.DejaFu.Invariant``
|
||||||
* ``Test.Tasty.DejaFu.inspectIORef``
|
* ``Test.Tasty.DejaFu.registerInvariant``
|
||||||
* ``Test.Tasty.DejaFu.inspectMVar``
|
* ``Test.Tasty.DejaFu.inspectIORef``
|
||||||
* ``Test.Tasty.DejaFu.inspectTVar``
|
* ``Test.Tasty.DejaFu.inspectMVar``
|
||||||
|
* ``Test.Tasty.DejaFu.inspectTVar``
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
* Functions which took a ``ConcIO`` now take a ``Program pty IO``:
|
* Functions which took a ``ConcIO`` now take a ``Program pty IO``:
|
||||||
* ``Test.Tasty.DejaFu.testAuto``
|
|
||||||
* ``Test.Tasty.DejaFu.testAutoWay``
|
* ``Test.Tasty.DejaFu.testAuto``
|
||||||
* ``Test.Tasty.DejaFu.testAutoWithSettings``
|
* ``Test.Tasty.DejaFu.testAutoWay``
|
||||||
* ``Test.Tasty.DejaFu.testDejafu``
|
* ``Test.Tasty.DejaFu.testAutoWithSettings``
|
||||||
* ``Test.Tasty.DejaFu.testDejafuWay``
|
* ``Test.Tasty.DejaFu.testDejafu``
|
||||||
* ``Test.Tasty.DejaFu.testDejafuWithSettings``
|
* ``Test.Tasty.DejaFu.testDejafuWay``
|
||||||
* ``Test.Tasty.DejaFu.testDejafus``
|
* ``Test.Tasty.DejaFu.testDejafuWithSettings``
|
||||||
* ``Test.Tasty.DejaFu.testDejafusWay``
|
* ``Test.Tasty.DejaFu.testDejafus``
|
||||||
* ``Test.Tasty.DejaFu.testDejafusWithSettings``
|
* ``Test.Tasty.DejaFu.testDejafusWay``
|
||||||
|
* ``Test.Tasty.DejaFu.testDejafusWithSettings``
|
||||||
|
|
||||||
Removed
|
Removed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
* The deprecated functions:
|
* The deprecated functions:
|
||||||
* ``Test.Tasty.DejaFu.testDejafuDiscard``
|
|
||||||
* ``Test.Tasty.DejaFu.testDejafusDiscard``
|
* ``Test.Tasty.DejaFu.testDejafuDiscard``
|
||||||
|
* ``Test.Tasty.DejaFu.testDejafusDiscard``
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@ -327,8 +331,8 @@ Miscellaneous
|
|||||||
|
|
||||||
* GHC 7.10 support is dropped. Dependency lower bounds are:
|
* GHC 7.10 support is dropped. Dependency lower bounds are:
|
||||||
|
|
||||||
* :hackage:`base`: 4.9
|
* :hackage:`base`: 4.9
|
||||||
* :hackage:`dejafu`: 1.5
|
* :hackage:`dejafu`: 1.5
|
||||||
|
|
||||||
* The upper bound on :hackage:`dejafu` is 1.6.
|
* The upper bound on :hackage:`dejafu` is 1.6.
|
||||||
|
|
||||||
@ -368,9 +372,9 @@ Added
|
|||||||
|
|
||||||
* (:pull:`238`) Settings-based test functions:
|
* (:pull:`238`) Settings-based test functions:
|
||||||
|
|
||||||
* ``Test.Tasty.DejaFu.testAutoWithSettings``
|
* ``Test.Tasty.DejaFu.testAutoWithSettings``
|
||||||
* ``Test.Tasty.DejaFu.testDejafuWithSettings``
|
* ``Test.Tasty.DejaFu.testDejafuWithSettings``
|
||||||
* ``Test.Tasty.DejaFu.testDejafusWithSettings``
|
* ``Test.Tasty.DejaFu.testDejafusWithSettings``
|
||||||
|
|
||||||
* (:pull:`238`) Re-export of ``Test.DejaFu.Settings``.
|
* (:pull:`238`) Re-export of ``Test.DejaFu.Settings``.
|
||||||
|
|
||||||
@ -611,8 +615,8 @@ Added
|
|||||||
* Orphan ``IsOption`` instance for ``Test.DejaFu.SCT.Way``.
|
* Orphan ``IsOption`` instance for ``Test.DejaFu.SCT.Way``.
|
||||||
Command-line parameters are:
|
Command-line parameters are:
|
||||||
|
|
||||||
* "systematically": systematic testing with the default bounds
|
* "systematically": systematic testing with the default bounds
|
||||||
* "randomly": 100 executions with a fixed random seed
|
* "randomly": 100 executions with a fixed random seed
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
@ -670,9 +674,9 @@ Added
|
|||||||
* Orphan ``IsOption`` instances for ``Test.DejaFu.SCT.Bounds`` and
|
* Orphan ``IsOption`` instances for ``Test.DejaFu.SCT.Bounds`` and
|
||||||
``MemType``. Command-line parameters are:
|
``MemType``. Command-line parameters are:
|
||||||
|
|
||||||
* "sc": sequential consistency
|
* "sc": sequential consistency
|
||||||
* "tso": total store order
|
* "tso": total store order
|
||||||
* "pso": partial store order
|
* "pso": partial store order
|
||||||
|
|
||||||
* Re-export ``Test.DejaFu.SCT.Bounds``.
|
* Re-export ``Test.DejaFu.SCT.Bounds``.
|
||||||
|
|
||||||
@ -687,8 +691,8 @@ Miscellaneous
|
|||||||
|
|
||||||
* Git: :tag:`tasty-dejafu-0.1.1.0`
|
* Git: :tag:`tasty-dejafu-0.1.1.0`
|
||||||
|
|
||||||
**Note:** this was misnumbered (it should have been 0.2.1.0) *and* was
|
**Note:** this was misnumbered (it should have been 0.2.1.0) *and* was never
|
||||||
never pushed to Hackage, whoops!
|
pushed to Hackage, whoops!
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
Loading…
Reference in New Issue
Block a user