mirror of
https://github.com/barrucadu/dejafu.git
synced 2024-12-19 19:41:31 +03:00
Merge branch 'docs'
This commit is contained in:
commit
91fa969fc2
@ -18,7 +18,7 @@ or email (mike@barrucadu.co.uk).
|
|||||||
[next-major]: https://github.com/barrucadu/dejafu/tree/next-major
|
[next-major]: https://github.com/barrucadu/dejafu/tree/next-major
|
||||||
|
|
||||||
|
|
||||||
Test Coverage
|
Test coverage
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
[`hpc`][hpc] can generate a coverage report from the execution of
|
[`hpc`][hpc] can generate a coverage report from the execution of
|
||||||
@ -121,7 +121,7 @@ use the "lint.sh" and "style.sh" scripts to run the tools for you.
|
|||||||
[hlint]: https://github.com/ndmitchell/hlint
|
[hlint]: https://github.com/ndmitchell/hlint
|
||||||
|
|
||||||
|
|
||||||
Documenting your Changes
|
Documenting your changes
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
**If a top-level definition is introduced or significantly changed**,
|
**If a top-level definition is introduced or significantly changed**,
|
||||||
|
@ -7,10 +7,12 @@ This project is versioned according to the [Package Versioning Policy](https://p
|
|||||||
*de facto* standard Haskell versioning scheme.
|
*de facto* standard Haskell versioning scheme.
|
||||||
|
|
||||||
|
|
||||||
1.1.2.1 [2017-06-07] (tag: [concurrency-1.1.2.1][])
|
1.1.2.1
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/concurrency-1.1.2.1
|
- **Date** 2017-06-07
|
||||||
|
- **Git tag** [concurrency-1.1.2.1][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/concurrency-1.1.2.1
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@ -28,10 +30,12 @@ https://hackage.haskell.org/package/concurrency-1.1.2.1
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
1.1.2.0 [2017-04-05] (tag: [concurrency-1.1.2.0][])
|
1.1.2.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/concurrency-1.1.2.0
|
- **Date** 2017-04-05
|
||||||
|
- **Git tag** [concurrency-1.1.2.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/concurrency-1.1.2.0
|
||||||
|
|
||||||
### Control.Concurrent.Classy.Async
|
### Control.Concurrent.Classy.Async
|
||||||
|
|
||||||
@ -66,10 +70,12 @@ https://hackage.haskell.org/package/concurrency-1.1.2.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
1.1.1.0 [2017-03-04] (git tag: [concurrency-1.1.1.0][])
|
1.1.1.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/concurrency-1.1.1.0
|
- **Date** 2017-03-04
|
||||||
|
- **Git tag** [concurrency-1.1.1.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/concurrency-1.1.1.0
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -82,10 +88,12 @@ https://hackage.haskell.org/package/concurrency-1.1.1.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
1.1.0.0 [2017-02-21] (git tag: [concurrency-1.1.0.0][])
|
1.1.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/concurrency-1.1.0.0
|
- **Date** 2017-02-21
|
||||||
|
- **Git tag** [concurrency-1.1.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/concurrency-1.1.0.0
|
||||||
|
|
||||||
### Control.Monad.Conc.Class
|
### Control.Monad.Conc.Class
|
||||||
|
|
||||||
@ -100,10 +108,12 @@ https://hackage.haskell.org/package/concurrency-1.1.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
1.0.0.0 [2016-09-10] (git tag: [concurrency-1.0.0.0][])
|
1.0.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/concurrency-1.0.0.0
|
- **Date** 2016-09-10
|
||||||
|
- **Git tag** [concurrency-1.0.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/concurrency-1.0.0.0
|
||||||
|
|
||||||
Initial release. Go read the API docs.
|
Initial release. Go read the API docs.
|
||||||
|
|
||||||
|
@ -7,10 +7,12 @@ This project is versioned according to the [Package Versioning Policy](https://p
|
|||||||
*de facto* standard Haskell versioning scheme.
|
*de facto* standard Haskell versioning scheme.
|
||||||
|
|
||||||
|
|
||||||
0.7.1.1 [2017-08-16] (git tag: [dejafu-0.7.1.1][])
|
0.7.1.1
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.7.1.1
|
- **Date** 2017-08-16
|
||||||
|
- **Git tag** [dejafu-0.7.1.1][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.7.1.1
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -26,10 +28,12 @@ https://hackage.haskell.org/package/dejafu-0.7.1.1
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.7.1.0 [2017-08-10] (git tag: [dejafu-0.7.1.0][])
|
0.7.1.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.7.1.0
|
- **Date** 2017-08-10
|
||||||
|
- **Git tag** [dejafu-0.7.1.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.7.1.0
|
||||||
|
|
||||||
### Test.DejaFu
|
### Test.DejaFu
|
||||||
|
|
||||||
@ -60,10 +64,12 @@ https://hackage.haskell.org/package/dejafu-0.7.1.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.7.0.2 [2017-06-12] (git tag: [dejafu-0.7.0.2][])
|
0.7.0.2
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.7.0.2
|
- **Date** 2017-06-12
|
||||||
|
- **Git tag** [dejafu-0.7.0.2][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.7.0.2
|
||||||
|
|
||||||
### Test.DejaFu.Refinement
|
### Test.DejaFu.Refinement
|
||||||
|
|
||||||
@ -81,10 +87,12 @@ https://hackage.haskell.org/package/dejafu-0.7.0.2
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.7.0.1 [2017-06-09] (git tag: [dejafu-0.7.0.1][])
|
0.7.0.1
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.7.0.1
|
- **Date** 2017-06-09
|
||||||
|
- **Git tag** [dejafu-0.7.0.1][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.7.0.1
|
||||||
|
|
||||||
### Test.DejaFu.Refinement
|
### Test.DejaFu.Refinement
|
||||||
|
|
||||||
@ -98,10 +106,12 @@ https://hackage.haskell.org/package/dejafu-0.7.0.1
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.7.0.0 [2017-06-07] (git tag: [dejafu-0.7.0.0][])
|
0.7.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.7.0.0
|
- **Date** 2017-06-07
|
||||||
|
- **Git tag** [dejafu-0.7.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.7.0.0
|
||||||
|
|
||||||
### Test.DejaFu
|
### Test.DejaFu
|
||||||
|
|
||||||
@ -145,10 +155,12 @@ https://hackage.haskell.org/package/dejafu-0.7.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.6.0.0 [2017-04-08] (git tag: [dejafu-0.6.0.0][])
|
0.6.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.6.0.0
|
- **Date** 2017-04-08
|
||||||
|
- **Git tag** [dejafu-0.6.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.6.0.0
|
||||||
|
|
||||||
### Test.DejaFu.Conc
|
### Test.DejaFu.Conc
|
||||||
|
|
||||||
@ -176,10 +188,12 @@ https://hackage.haskell.org/package/dejafu-0.6.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.5.1.3 [2017-04-05] (git tag: [dejafu-0.5.1.3][])
|
0.5.1.3
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.5.1.3
|
- **Date** 2017-04-05
|
||||||
|
- **Git tag** [dejafu-0.5.1.3][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.5.1.3
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -191,10 +205,12 @@ https://hackage.haskell.org/package/dejafu-0.5.1.3
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.5.1.2 [2017-03-04] (git tag: [dejafu-0.5.1.2][])
|
0.5.1.2
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.5.1.2
|
- **Date** 2017-03-04
|
||||||
|
- **Git tag** [dejafu-0.5.1.2][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.5.1.2
|
||||||
|
|
||||||
**This version was misnumbered! It should have caused a minor version bump!**
|
**This version was misnumbered! It should have caused a minor version bump!**
|
||||||
|
|
||||||
@ -217,10 +233,12 @@ https://hackage.haskell.org/package/dejafu-0.5.1.2
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.5.1.1 [2017-02-25] (git tag: [dejafu-0.5.1.1][])
|
0.5.1.1
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.5.1.1
|
- **Date** 2017-02-25
|
||||||
|
- **Git tag** [dejafu-0.5.1.1][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.5.1.1
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@ -234,10 +252,12 @@ https://hackage.haskell.org/package/dejafu-0.5.1.1
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.5.1.0 [2017-02-25] (git tag: [dejafu-0.5.1.0][])
|
0.5.1.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.5.1.0
|
- **Date** 2017-02-25
|
||||||
|
- **Git tag** [dejafu-0.5.1.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.5.1.0
|
||||||
|
|
||||||
### Test.DejaFu
|
### Test.DejaFu
|
||||||
|
|
||||||
@ -265,10 +285,12 @@ https://hackage.haskell.org/package/dejafu-0.5.1.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.5.0.2 [2017-02-22] (git tag: [dejafu-0.5.0.2][])
|
0.5.0.2
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.5.0.2
|
- **Date** 2017-02-22
|
||||||
|
- **Git tag** [dejafu-0.5.0.2][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.5.0.2
|
||||||
|
|
||||||
**This version was misnumbered! It should have caused a major version bump!**
|
**This version was misnumbered! It should have caused a major version bump!**
|
||||||
|
|
||||||
@ -295,10 +317,12 @@ https://hackage.haskell.org/package/dejafu-0.5.0.2
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.5.0.1 [2017-02-21] (git tag: [dejafu-0.5.0.1][])
|
0.5.0.1
|
||||||
-------
|
-------
|
||||||
|
|
||||||
**This version was never pushed to hackage, whoops!**
|
- **Date** 2017-02-21
|
||||||
|
- **Git tag** [dejafu-0.5.0.1][]
|
||||||
|
- **This version was never pushed to hackage, whoops!**
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@ -310,10 +334,12 @@ https://hackage.haskell.org/package/dejafu-0.5.0.2
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.5.0.0 [2017-02-21] (git tag: [dejafu-0.5.0.0][])
|
0.5.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.5.0.0
|
- **Date** 2017-02-21
|
||||||
|
- **Git tag** [dejafu-0.5.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.5.0.0
|
||||||
|
|
||||||
### Test.DejaFu
|
### Test.DejaFu
|
||||||
|
|
||||||
@ -356,10 +382,12 @@ https://hackage.haskell.org/package/dejafu-0.5.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.4.0.0 [2016-09-10] (git tag: [dejafu-0.4.0.0][])
|
0.4.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.4.0.0
|
- **Date** 2016-09-10
|
||||||
|
- **Git tag** [dejafu-0.4.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.4.0.0
|
||||||
|
|
||||||
### Test.DejaFu
|
### Test.DejaFu
|
||||||
|
|
||||||
@ -403,10 +431,12 @@ https://hackage.haskell.org/package/dejafu-0.4.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.2.1 [2016-07-21] (git tag: [dejafu-0.3.2.1][])
|
0.3.2.1
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.3.2.1
|
- **Date** 2016-07-21
|
||||||
|
- **Git tag** [dejafu-0.3.2.1][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.3.2.1
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@ -419,10 +449,12 @@ https://hackage.haskell.org/package/dejafu-0.3.2.1
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.2.0 [2016-06-06] (git tag: [dejafu-0.3.2.0][])
|
0.3.2.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.3.2.0
|
- **Date** 2016-06-06
|
||||||
|
- **Git tag** [dejafu-0.3.2.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.3.2.0
|
||||||
|
|
||||||
**Builds with both dpor-0.1 and dpor-0.2, however some improvements require dpor-0.2.**
|
**Builds with both dpor-0.1 and dpor-0.2, however some improvements require dpor-0.2.**
|
||||||
|
|
||||||
@ -444,10 +476,12 @@ https://hackage.haskell.org/package/dejafu-0.3.2.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.1.1 [2016-05-26] (git tag: [dejafu-0.3.1.1][])
|
0.3.1.1
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.3.1.1
|
- **Date** 2016-05-26
|
||||||
|
- **Git tag** [dejafu-0.3.1.1][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.3.1.1
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -459,10 +493,12 @@ https://hackage.haskell.org/package/dejafu-0.3.1.1
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.1.0 [2016-05-02] (git tag: [dejafu-0.3.1.0][])
|
0.3.1.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.3.1.0
|
- **Date** 2016-05-02
|
||||||
|
- **Git tag** [dejafu-0.3.1.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.3.1.0
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@ -475,10 +511,12 @@ https://hackage.haskell.org/package/dejafu-0.3.1.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.0.0 [2016-04-03] (git tag: [dejafu-0.3.0.0][])
|
0.3.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.3.0.0
|
- **Date** 2016-04-03
|
||||||
|
- **Git tag** [dejafu-0.3.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.3.0.0
|
||||||
|
|
||||||
**The minimum supported version of GHC is now 7.10.**
|
**The minimum supported version of GHC is now 7.10.**
|
||||||
|
|
||||||
@ -491,10 +529,12 @@ logs.
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.2.0.0 [2015-12-01] (git tag: [0.2.0.0][])
|
0.2.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.2.0.0
|
- **Date** 2015-12-01
|
||||||
|
- **Git tag** [0.2.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.2.0.0
|
||||||
|
|
||||||
I didn't write proper release notes, and this is so far back I don't really care to dig through the
|
I didn't write proper release notes, and this is so far back I don't really care to dig through the
|
||||||
logs.
|
logs.
|
||||||
@ -505,10 +545,12 @@ logs.
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.1.0.0 [2015-08-27] (git tag: [0.1.0.0][])
|
0.1.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/dejafu-0.1.0.0
|
- **Date** 2015-08-27
|
||||||
|
- **Git tag** [0.1.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/dejafu-0.1.0.0
|
||||||
|
|
||||||
Initial release. Go read the API docs.
|
Initial release. Go read the API docs.
|
||||||
|
|
||||||
|
1
doc/.gitignore
vendored
Normal file
1
doc/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
_build
|
20
doc/Makefile
Normal file
20
doc/Makefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line.
|
||||||
|
SPHINXOPTS =
|
||||||
|
SPHINXBUILD = sphinx-build
|
||||||
|
SPHINXPROJ = DjFu
|
||||||
|
SOURCEDIR = .
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
207
doc/advanced.rst
Normal file
207
doc/advanced.rst
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
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 ``autocheck``, ``dejafu``, and ``dejafus`` functions from
|
||||||
|
``Test.DejaFu`` all have a variant which lets you specify the **memory
|
||||||
|
model** used for ``CRef`` operations and the **way** in which
|
||||||
|
schedules are explored. These are ``autocheckWay``, ``dejafuWay``,
|
||||||
|
and ``dejafusWay`` (plus ``IO`` variants).
|
||||||
|
|
||||||
|
Memory model
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Threads running under modern multicore processors do not behave as a
|
||||||
|
simple interleaving of the individual thread actions. Processors do
|
||||||
|
all sorts of complex things to increase speed, such as buffering
|
||||||
|
writes. For concurrent programs which make use of non-synchronised
|
||||||
|
functions (such as ``readCRef`` coupled with ``writeCRef``) different
|
||||||
|
memory models may yield different results.
|
||||||
|
|
||||||
|
As an example, consider this program from the ``Data.IORef``
|
||||||
|
documentation. Two ``CRef`` variables are created, and two threads
|
||||||
|
spawned to write to and read from both. Each thread returns the value
|
||||||
|
it observes.
|
||||||
|
|
||||||
|
.. code-block:: haskell
|
||||||
|
|
||||||
|
example :: MonadConc m => m (Bool, Bool)
|
||||||
|
example = do
|
||||||
|
r1 <- newCRef False
|
||||||
|
r2 <- newCRef False
|
||||||
|
x <- spawn $ writeCRef r1 True >> readCRef r2
|
||||||
|
y <- spawn $ writeCRef r2 True >> readCRef r1
|
||||||
|
(,) <$> readMVar x <*> readMVar y
|
||||||
|
|
||||||
|
Under a sequentially consistent memory model the possible results are
|
||||||
|
``(True, True)``, ``(True, False)``, and ``(False, True)``. Under
|
||||||
|
total or partial store order, ``(False, False)`` is also a possible
|
||||||
|
result, even though there is no interleaving of the threads which can
|
||||||
|
lead to this.
|
||||||
|
|
||||||
|
We can see this by testing with different memory models:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
> autocheckWay defaultWay SequentialConsistency example
|
||||||
|
[pass] Never Deadlocks (checked: 6)
|
||||||
|
[pass] No Exceptions (checked: 6)
|
||||||
|
[fail] Consistent Result (checked: 5)
|
||||||
|
(False,True) S0---------S1----S0--S2----S0--
|
||||||
|
|
||||||
|
(True,True) S0---------S1-P2----S1---S0---
|
||||||
|
|
||||||
|
(True,False) S0---------S2----S1----S0---
|
||||||
|
False
|
||||||
|
|
||||||
|
> autocheckWay defaultWay TotalStoreOrder example
|
||||||
|
[pass] Never Deadlocks (checked: 28)
|
||||||
|
[pass] No Exceptions (checked: 28)
|
||||||
|
[fail] Consistent Result (checked: 27)
|
||||||
|
(False,True) S0---------S1----S0--S2----S0--
|
||||||
|
|
||||||
|
(False,False) S0---------S1--P2----S1--S0---
|
||||||
|
|
||||||
|
(True,False) S0---------S2----S1----S0---
|
||||||
|
|
||||||
|
(True,True) S0---------S1-C-S2----S1---S0---
|
||||||
|
False
|
||||||
|
|
||||||
|
Traces for non-sequentially-consistent memory models show where
|
||||||
|
``CRef`` writes are committed, which makes a write visible to all
|
||||||
|
threads rather than just the one which performed the write.
|
||||||
|
|
||||||
|
The default memory model is total store order, as that is how x86
|
||||||
|
processors behave.
|
||||||
|
|
||||||
|
Schedule bounds
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Schedule bounding is an optimisation which only considers schedules
|
||||||
|
within some bound. This sacrifices completeness outside of the bound,
|
||||||
|
but can drastically reduce the number of schedules to test, and is in
|
||||||
|
fact necessary for non-terminating programs.
|
||||||
|
|
||||||
|
There are three supported types of bounds:
|
||||||
|
|
||||||
|
Pre-emption bounding
|
||||||
|
Restricts the number of pre-emptive context switches. A context
|
||||||
|
switch is pre-emptive if the previously executing thread is still
|
||||||
|
runnable and did not explicitly yield.
|
||||||
|
|
||||||
|
Fair bounding
|
||||||
|
Restricts how many times each thread can yield, by bounding the
|
||||||
|
maximum difference between the thread which has yielded the most,
|
||||||
|
and the thread which has yielded the least.
|
||||||
|
|
||||||
|
Length bounding
|
||||||
|
Restricts how long an execution can be, in terms of Déjà Fu's
|
||||||
|
"primitive actions".
|
||||||
|
|
||||||
|
The standard testing mechanism uses all three bounds. Pre-emption +
|
||||||
|
fair bounding is useful for programs which use spinlocks or yield for
|
||||||
|
control flow, but which are otherwise terminating. Length bounding
|
||||||
|
makes it possible to test potentially non-terminating programs.
|
||||||
|
|
||||||
|
If you wanted to disable pre-emption bounding, for example, you can do
|
||||||
|
so like so:
|
||||||
|
|
||||||
|
.. code-block:: haskell
|
||||||
|
|
||||||
|
dejafuWay (systematically defaultBounds { boundPreemp = Nothing })
|
||||||
|
defaultMemType
|
||||||
|
myAction
|
||||||
|
("Assert the thing holds", myPredicate)
|
||||||
|
|
||||||
|
|
||||||
|
Random scheduling
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If you don't want to find all executions within the schedule bounds,
|
||||||
|
and instead want to test a fixed number of executions, you can use
|
||||||
|
random scheduling.
|
||||||
|
|
||||||
|
There are three variants:
|
||||||
|
|
||||||
|
``randomly randomGen numExecutions``
|
||||||
|
Perform the given number of executions using weighted random
|
||||||
|
scheduling. On creation, a thread is given a random weight, which
|
||||||
|
is used to perform a nonuniform random selection amongst the
|
||||||
|
runnable threads at every scheduling point.
|
||||||
|
|
||||||
|
``uniformly randomGen numExecutions``
|
||||||
|
Like ``randomly``, but rather than a weighted selection, it's a
|
||||||
|
uniform selection.
|
||||||
|
|
||||||
|
``swarmy randomGen numExecutions numUses``
|
||||||
|
Like ``randomly``, but each set of thread weights is used for
|
||||||
|
``numUses`` executions.
|
||||||
|
|
||||||
|
These are all given as the first argument to ``dejafuWay`` (and its
|
||||||
|
ilk), like ``systematically``. So for example you could do this:
|
||||||
|
|
||||||
|
.. code-block:: haskell
|
||||||
|
|
||||||
|
dejafuWay (randomly (mkStdGen 42) 1000)
|
||||||
|
defaultMemType
|
||||||
|
myAction
|
||||||
|
("Assert the thing holds", myPredicate)
|
||||||
|
|
||||||
|
|
||||||
|
.. _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 ``*Way``
|
||||||
|
functions and ``Test.DejaFu.SCT.sct{Uniform,Weighted}Random``.
|
||||||
|
|
||||||
|
* 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
|
||||||
|
``*Discard`` 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,
|
||||||
|
and are going to sacrifice completeness because your possible
|
||||||
|
state-space is huge. You could do it like this:
|
||||||
|
|
||||||
|
.. code-block:: haskell
|
||||||
|
|
||||||
|
dejafuDiscard
|
||||||
|
-- "efa" == "either failure a", discard everything but deadlocks
|
||||||
|
(\efa -> if efa == Left Deadlock then Nothing else Just DiscardResultAndTrace)
|
||||||
|
-- try 10000 executions with random scheduling
|
||||||
|
(randomly (mkStdGen 42) 10000)
|
||||||
|
-- use the default memory model
|
||||||
|
defaultMemType
|
||||||
|
-- your test case
|
||||||
|
testCase
|
||||||
|
-- the predicate to check (which is a bit redundant in this case)
|
||||||
|
("Never Deadlocks", deadlocksNever)
|
1
doc/changelog_concurrency.md
Symbolic link
1
doc/changelog_concurrency.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../concurrency/CHANGELOG.markdown
|
1
doc/changelog_dejafu.md
Symbolic link
1
doc/changelog_dejafu.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../dejafu/CHANGELOG.markdown
|
1
doc/changelog_hunit-dejafu.md
Symbolic link
1
doc/changelog_hunit-dejafu.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../hunit-dejafu/CHANGELOG.markdown
|
1
doc/changelog_tasty-dejafu.md
Symbolic link
1
doc/changelog_tasty-dejafu.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../tasty-dejafu/CHANGELOG.markdown
|
160
doc/conf.py
Normal file
160
doc/conf.py
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
# -*- 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
|
||||||
|
from recommonmark.parser import CommonMarkParser
|
||||||
|
|
||||||
|
# -- 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 = []
|
||||||
|
|
||||||
|
# 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 = {
|
||||||
|
'.md': CommonMarkParser,
|
||||||
|
}
|
||||||
|
|
||||||
|
source_suffix = ['.rst', '.md']
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = u'Déjà Fu'
|
||||||
|
copyright = u'2017, Michael Walker'
|
||||||
|
author = u'Michael Walker'
|
||||||
|
|
||||||
|
# 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'0.7.1.0'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = u'0.7.1.0'
|
||||||
|
|
||||||
|
# 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
doc/contributing.md
Symbolic link
1
doc/contributing.md
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../CONTRIBUTING.markdown
|
181
doc/getting_started.rst
Normal file
181
doc/getting_started.rst
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
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_, Hackage_, and
|
||||||
|
Stackage_.
|
||||||
|
|
||||||
|
.. _GitHub: https://github.com/barrucadu/dejafu
|
||||||
|
.. _Hackage: https://hackage.haskell.org/package/dejafu
|
||||||
|
.. _Stackage: https://www.stackage.org/package/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 HUnit_ and tasty_
|
||||||
|
|
||||||
|
.. _HUnit: https://hackage.haskell.org/package/HUnit
|
||||||
|
.. _Tasty: https://hackage.haskell.org/package/tasty
|
||||||
|
|
||||||
|
|
||||||
|
There are a few different packages under the Déjà Fu umbrella:
|
||||||
|
|
||||||
|
.. csv-table::
|
||||||
|
:header: "Package", "Version", "Summary"
|
||||||
|
|
||||||
|
"concurrency_", "1.1.2.1", "Typeclasses, functions, and data types for concurrency and STM"
|
||||||
|
"dejafu_", "0.7.1.0", "Systematic testing for Haskell concurrency"
|
||||||
|
"hunit-dejafu_", "0.7.0.0", "Déjà Fu support for the HUnit test framework"
|
||||||
|
"tasty-dejafu_", "0.7.0.0", "Déjà Fu support for the tasty test framework"
|
||||||
|
|
||||||
|
.. _concurrency: https://hackage.haskell.org/package/concurrency
|
||||||
|
.. _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
|
||||||
|
|
||||||
|
Everything is on Hackage and Stackage, and the last three major GHC
|
||||||
|
versions (currently 8.2, 8.0, and 7.10) are supported.
|
||||||
|
|
||||||
|
|
||||||
|
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] Never Deadlocks (checked: 12)
|
||||||
|
[pass] No Exceptions (checked: 12)
|
||||||
|
[fail] Consistent Result (checked: 11)
|
||||||
|
"hello" S0----S1-P2-S0--
|
||||||
|
|
||||||
|
"world" S0----S2--S0-P1-S0-
|
||||||
|
False
|
||||||
|
|
||||||
|
There are no deadlocks or uncaught exceptions, 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 schedule variety than just
|
||||||
|
running your test case in ``IO`` the same number 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 simply following
|
||||||
|
the trace by eye.
|
||||||
|
|
||||||
|
**If you'd like to get involved with Déjà Fu**, check out `the
|
||||||
|
"beginner friendly" label on the issue tracker`__.
|
||||||
|
|
||||||
|
.. __: https://github.com/barrucadu/dejafu/issues?q=is%3Aissue+is%3Aopen+label%3A%22beginner+friendly%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 `file an issue`__.
|
||||||
|
|
||||||
|
.. __: https://github.com/barrucadu/dejafu/issues
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
33
doc/index.rst
Normal file
33
doc/index.rst
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
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: Release Notes
|
||||||
|
|
||||||
|
concurrency <changelog_concurrency>
|
||||||
|
dejafu <changelog_dejafu>
|
||||||
|
hunit-dejafu <changelog_hunit-dejafu>
|
||||||
|
tasty-dejafu <changelog_tasty-dejafu>
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Developer Documentation
|
||||||
|
|
||||||
|
contributing
|
159
doc/refinement_testing.rst
Normal file
159
doc/refinement_testing.rst
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
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..."
|
||||||
|
|
||||||
|
"``refines``", "``=>=``", "...the left is equivalent to, or strictly refines, the right"
|
||||||
|
"``strictlyRefines``", "``->-``", "...the left has strictly fewer behaviours than the right"
|
||||||
|
"``equivalentTo``", "``===``", "...the left and right have exactly the same behaviours"
|
||||||
|
|
||||||
|
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
|
||||||
|
LeanCheck_ package, an enumerative property-based testing library.
|
||||||
|
This means that any types you use will need to have a ``Listable``
|
||||||
|
instance.
|
||||||
|
|
||||||
|
.. _LeanCheck: https://hackage.haskell.org/package/leancheck
|
||||||
|
|
||||||
|
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 `a collection of refinement properties`__,
|
||||||
|
which may help you get a feel for this sort of testing.
|
||||||
|
|
||||||
|
.. __: 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.
|
||||||
|
|
||||||
|
.. _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
|
||||||
|
|
||||||
|
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
|
74
doc/typeclass.rst
Normal file
74
doc/typeclass.rst
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
Typeclasses
|
||||||
|
===========
|
||||||
|
|
||||||
|
We don't use the regular ``Control.Concurrent`` and
|
||||||
|
``Control.Exception`` modules, we use typeclass-generalised ones
|
||||||
|
instead from the `concurrency`_ and `exceptions`_ packages.
|
||||||
|
|
||||||
|
.. _concurrency: https://hackage.haskell.org/package/concurrency
|
||||||
|
.. _exceptions: 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 ``CRef m`` [#]_
|
||||||
|
|
||||||
|
5. Some functions are renamed:
|
||||||
|
|
||||||
|
* ``*IORef*`` becomes ``*CRef*``
|
||||||
|
* ``forkIO*`` becomes ``fork*``
|
||||||
|
* ``atomicModifyIORefCAS*`` becomes ``modifyCRefCAS*``
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. [#] I felt that calling it ``IORef`` when there was no I/O involved
|
||||||
|
would be confusing, but this was perhaps a mistake.
|
||||||
|
|
||||||
|
|
||||||
|
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 ``IO`` values can't be broken up into smaller chunks, 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.
|
170
doc/unit_testing.rst
Normal file
170
doc/unit_testing.rst
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
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 myAction ("Assert the thing holds", myPredicate)
|
||||||
|
|
||||||
|
The ``dejafu`` function comes from ``Test.DejaFu``. It can't deal
|
||||||
|
with testcases which need ``MonadIO``, use ``dejafuIO`` for that.
|
||||||
|
|
||||||
|
|
||||||
|
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_ 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 <- newCRef 0
|
||||||
|
let settings = (defaultUpdateSettings :: UpdateSettings m ())
|
||||||
|
{ updateAction = atomicModifyCRef var (\x -> (x+1, x)) }
|
||||||
|
auto <- mkAutoUpdate settings
|
||||||
|
auto
|
||||||
|
auto
|
||||||
|
|
||||||
|
.. _auto-update: https://hackage.haskell.org/package/auto-update
|
||||||
|
.. __: 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 `in the Déjà Fu testsuite`__, if you
|
||||||
|
want to see the full code. [#]_
|
||||||
|
|
||||||
|
.. __: https://github.com/barrucadu/dejafu/blob/2a15549d97c2fa12f5e8b92ab918fdb34da78281/dejafu-tests/Examples/AutoUpdate.hs
|
||||||
|
|
||||||
|
.. [#] 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
.. 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"
|
||||||
|
``notAlwaysSame``,"checks that the computation is nondeterministic"
|
||||||
|
|
||||||
|
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"
|
||||||
|
``alwaysTrue2 p``,"checks that ``p`` is true for every pair of results"
|
||||||
|
``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 failures)"
|
||||||
|
``gives' xs``,"checks that the set of results is exactly ``xs`` (which may not include failures)"
|
||||||
|
|
||||||
|
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 ``dejafusIO`` functions. These
|
||||||
|
avoid recomputing the results, and so may be faster than multiple
|
||||||
|
``dejafu`` / ``dejafuIO`` 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. HUnit_
|
||||||
|
and tasty_ integration is provided to get more of the features you'd
|
||||||
|
expect from a testing framework.
|
||||||
|
|
||||||
|
.. _HUnit: https://hackage.haskell.org/package/HUnit
|
||||||
|
.. _Tasty: https://hackage.haskell.org/package/tasty
|
||||||
|
|
||||||
|
The integration is provided by the hunit-dejafu_ and tasty-dejafu_ packages.
|
||||||
|
|
||||||
|
.. _hunit-dejafu: https://hackage.haskell.org/package/hunit-dejafu
|
||||||
|
.. _tasty-dejafu: https://hackage.haskell.org/package/tasty-dejafu
|
||||||
|
|
||||||
|
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 myAction "Assert the thing holds" myPredicate
|
||||||
|
|
||||||
|
The ``autocheck`` and ``autocheckIO`` functions are exposed as
|
||||||
|
``testAuto`` and ``testAutoIO``.
|
@ -7,10 +7,12 @@ This project is versioned according to the [Package Versioning Policy](https://p
|
|||||||
*de facto* standard Haskell versioning scheme.
|
*de facto* standard Haskell versioning scheme.
|
||||||
|
|
||||||
|
|
||||||
0.7.0.0 [2017-08-10] (git tag: [hunit-dejafu-0.7.0.0][])
|
0.7.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/hunit-dejafu-0.7.0.0
|
- **Date** 2017-08-10
|
||||||
|
- **Git tag** [hunit-dejafu-0.7.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/hunit-dejafu-0.7.0.0
|
||||||
|
|
||||||
### Test.HUnit.DejaFu
|
### Test.HUnit.DejaFu
|
||||||
|
|
||||||
@ -28,10 +30,12 @@ https://hackage.haskell.org/package/hunit-dejafu-0.7.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.6.0.0 [2017-06-07] (git tag: [hunit-dejafu-0.6.0.0][])
|
0.6.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/hunit-dejafu-0.6.0.0
|
- **Date** 2017-06-07
|
||||||
|
- **Git tag** [hunit-dejafu-0.6.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/hunit-dejafu-0.6.0.0
|
||||||
|
|
||||||
### Test.HUnit.DejaFu
|
### Test.HUnit.DejaFu
|
||||||
|
|
||||||
@ -55,10 +59,12 @@ https://hackage.haskell.org/package/hunit-dejafu-0.6.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.5.0.0 [2017-04-08] (git tag: [hunit-dejafu-0.5.0.0][])
|
0.5.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/hunit-dejafu-0.5.0.0
|
- **Date** 2017-04-08
|
||||||
|
- **Git tag** [hunit-dejafu-0.5.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/hunit-dejafu-0.5.0.0
|
||||||
|
|
||||||
### Test.HUnit.DejaFu
|
### Test.HUnit.DejaFu
|
||||||
|
|
||||||
@ -76,10 +82,12 @@ https://hackage.haskell.org/package/hunit-dejafu-0.5.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.4.0.1 [2017-03-20] (git tag: [hunit-dejafu-0.4.0.1][])
|
0.4.0.1
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/hunit-dejafu-0.4.0.1
|
- **Date** 2017-03-20
|
||||||
|
- **Git tag** [hunit-dejafu-0.4.0.1][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/hunit-dejafu-0.4.0.1
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -91,10 +99,12 @@ https://hackage.haskell.org/package/hunit-dejafu-0.4.0.1
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.4.0.0 [2017-02-21] (git tag: [hunit-dejafu-0.4.0.0][])
|
0.4.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/hunit-dejafu-0.4.0.0
|
- **Date** 2017-02-21
|
||||||
|
- **Git tag** [hunit-dejafu-0.4.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/hunit-dejafu-0.4.0.0
|
||||||
|
|
||||||
### Test.HUnit.DejaFu
|
### Test.HUnit.DejaFu
|
||||||
|
|
||||||
@ -112,10 +122,12 @@ https://hackage.haskell.org/package/hunit-dejafu-0.4.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.0.3 [2016-10-22] (git tag: [hunit-dejafu-0.3.0.3][])
|
0.3.0.3
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/hunit-dejafu-0.3.0.3
|
- **Date** 2016-10-22
|
||||||
|
- **Git tag** [hunit-dejafu-0.3.0.3][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/hunit-dejafu-0.3.0.3
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -127,10 +139,12 @@ https://hackage.haskell.org/package/hunit-dejafu-0.3.0.3
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.0.2 [2016-09-10] (git tag: [hunit-dejafu-0.3.0.2][])
|
0.3.0.2
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/hunit-dejafu-0.3.0.2
|
- **Date** 2016-09-10
|
||||||
|
- **Git tag** [hunit-dejafu-0.3.0.2][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/hunit-dejafu-0.3.0.2
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -142,10 +156,12 @@ https://hackage.haskell.org/package/hunit-dejafu-0.3.0.2
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.0.1 [2016-05-26] (git tag: [hunit-dejafu-0.3.0.1][])
|
0.3.0.1
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/hunit-dejafu-0.3.0.1
|
- **Date** 2016-05-26
|
||||||
|
- **Git tag** [hunit-dejafu-0.3.0.1][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/hunit-dejafu-0.3.0.1
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -157,10 +173,12 @@ https://hackage.haskell.org/package/hunit-dejafu-0.3.0.1
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.0.0 [2016-04-28] (git tag: [hunit-dejafu-0.3.0.0][])
|
0.3.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/hunit-dejafu-0.3.0.0
|
- **Date** 2016-04-28
|
||||||
|
- **Git tag** [hunit-dejafu-0.3.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/hunit-dejafu-0.3.0.0
|
||||||
|
|
||||||
### Test.HUnit.DejaFu
|
### Test.HUnit.DejaFu
|
||||||
|
|
||||||
@ -177,10 +195,12 @@ https://hackage.haskell.org/package/hunit-dejafu-0.3.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.2.1.0 [2016-04-03] (git tag: [hunit-dejafu-0.2.1.0][])
|
0.2.1.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
**This version was never pushed to hackage, whoops!**
|
- **Date** 2016-04-03
|
||||||
|
- **Git tag** [hunit-dejafu-0.2.1.0][]
|
||||||
|
- **This version was never pushed to hackage, whoops!**
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -192,10 +212,12 @@ https://hackage.haskell.org/package/hunit-dejafu-0.3.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.2.0.0 [2015-12-01] (git tag: [0.2.0.0][])
|
0.2.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/hunit-dejafu-0.2.0.0
|
- **Date** 2015-12-01
|
||||||
|
- **Git tag** [0.2.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/hunit-dejafu-0.2.0.0
|
||||||
|
|
||||||
Initial release. Go read the API docs.
|
Initial release. Go read the API docs.
|
||||||
|
|
||||||
|
@ -7,10 +7,12 @@ This project is versioned according to the [Package Versioning Policy](https://p
|
|||||||
*de facto* standard Haskell versioning scheme.
|
*de facto* standard Haskell versioning scheme.
|
||||||
|
|
||||||
|
|
||||||
0.7.0.0 [2017-08-10] (git tag: [tasty-dejafu-0.7.0.0][])
|
0.7.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/tasty-dejafu-0.6.0.0
|
- **Date** 2017-08-10
|
||||||
|
- **Git tag** [tasty-dejafu-0.7.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/tasty-dejafu-0.6.0.0
|
||||||
|
|
||||||
### Test.Tasty.DejaFu
|
### Test.Tasty.DejaFu
|
||||||
|
|
||||||
@ -28,10 +30,12 @@ https://hackage.haskell.org/package/tasty-dejafu-0.6.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.6.0.0 [2017-04-08] (git tag: [tasty-dejafu-0.6.0.0][])
|
0.6.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/tasty-dejafu-0.6.0.0
|
- **Date** 2017-04-08
|
||||||
|
- **Git tag** [tasty-dejafu-0.6.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/tasty-dejafu-0.6.0.0
|
||||||
|
|
||||||
### Test.Tasty.DejaFu
|
### Test.Tasty.DejaFu
|
||||||
|
|
||||||
@ -55,10 +59,12 @@ https://hackage.haskell.org/package/tasty-dejafu-0.6.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.5.0.0 [2017-04-08] (git tag: [tasty-dejafu-0.5.0.0][])
|
0.5.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/tasty-dejafu-0.5.0.0
|
- **Date** 2017-04-08
|
||||||
|
- **Git tag** [tasty-dejafu-0.5.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/tasty-dejafu-0.5.0.0
|
||||||
|
|
||||||
### Test.Tasty.DejaFu
|
### Test.Tasty.DejaFu
|
||||||
|
|
||||||
@ -76,10 +82,12 @@ https://hackage.haskell.org/package/tasty-dejafu-0.5.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.4.0.0 [2017-02-21] (git tag: [tasty-dejafu-0.4.0.0][])
|
0.4.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/tasty-dejafu-0.4.0.0
|
- **Date** 2017-02-21
|
||||||
|
- **Git tag** [tasty-dejafu-0.4.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/tasty-dejafu-0.4.0.0
|
||||||
|
|
||||||
### Test.Tasty.DejaFu
|
### Test.Tasty.DejaFu
|
||||||
|
|
||||||
@ -101,10 +109,12 @@ https://hackage.haskell.org/package/tasty-dejafu-0.4.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.0.2 [2016-09-10] (git tag: [tasty-dejafu-0.3.0.2][])
|
0.3.0.2
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/tasty-dejafu-0.3.0.2
|
- **Date** 2016-09-10
|
||||||
|
- **Git tag** [tasty-dejafu-0.3.0.2][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/tasty-dejafu-0.3.0.2
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -116,10 +126,12 @@ https://hackage.haskell.org/package/tasty-dejafu-0.3.0.2
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.0.1 [2016-05-26] (git tag: [tasty-dejafu-0.3.0.1][])
|
0.3.0.1
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/tasty-dejafu-0.3.0.1
|
- **Date** 2016-05-26
|
||||||
|
- **Git tag** [tasty-dejafu-0.3.0.1][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/tasty-dejafu-0.3.0.1
|
||||||
|
|
||||||
### Miscellaneous
|
### Miscellaneous
|
||||||
|
|
||||||
@ -131,10 +143,12 @@ https://hackage.haskell.org/package/tasty-dejafu-0.3.0.1
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.3.0.0 [2016-04-28] (git tag: [tasty-dejafu-0.3.0.0][])
|
0.3.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/tasty-dejafu-0.3.0.0
|
- **Date** 2016-04-28
|
||||||
|
- **Git tag** [tasty-dejafu-0.3.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/tasty-dejafu-0.3.0.0
|
||||||
|
|
||||||
### Test.Tasty.DejaFu
|
### Test.Tasty.DejaFu
|
||||||
|
|
||||||
@ -157,10 +171,12 @@ https://hackage.haskell.org/package/tasty-dejafu-0.3.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.1.1.0 [2016-04-03] (git tag: [tasty-dejafu-0.1.1.0][])
|
0.1.1.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
**This version was never pushed to hackage, whoops!**
|
- **Date** 2016-04-03
|
||||||
|
- **Git tag** [tasty-dejafu-0.1.1.0][]
|
||||||
|
- **This version was never pushed to hackage, whoops!**
|
||||||
|
|
||||||
**This version was misnumbered! It should have been 0.2.1.0!**
|
**This version was misnumbered! It should have been 0.2.1.0!**
|
||||||
|
|
||||||
@ -174,10 +190,12 @@ https://hackage.haskell.org/package/tasty-dejafu-0.3.0.0
|
|||||||
---------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
0.2.0.0 [2015-12-01] (git tag: [0.2.0.0][])
|
0.2.0.0
|
||||||
-------
|
-------
|
||||||
|
|
||||||
https://hackage.haskell.org/package/tasty-dejafu-0.2.0.0
|
- **Date** 2015-12-01
|
||||||
|
- **Git tag** [0.2.0.0][]
|
||||||
|
- **Hackage** https://hackage.haskell.org/package/tasty-dejafu-0.2.0.0
|
||||||
|
|
||||||
Initial release. Go read the API docs.
|
Initial release. Go read the API docs.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user