mirror of
https://github.com/barrucadu/dejafu.git
synced 2024-11-27 04:36:57 +03:00
Merge pull request #270 from barrucadu/259-failures
Highlight failures in tests
This commit is contained in:
commit
6966c377b6
@ -9,6 +9,12 @@
|
||||
# Record patterns are just ugly.
|
||||
- ignore: {name: Use record patterns}
|
||||
|
||||
# Don't prefer TupleSections
|
||||
- ignore: {name: Use tuple-section}
|
||||
|
||||
# Inapplicable
|
||||
- ignore: {name: Use readTVarIO, within: Control.Monad.Conc.Class}
|
||||
|
||||
# GHC treats infix $ specially wrt type checking, so that things like
|
||||
# "runST $ do ..." work even though they're impredicative.
|
||||
# Unfortunately, this means that HLint's "avoid lambda" warning for
|
||||
|
@ -45,10 +45,10 @@ There are a few different packages under the Déjà Fu umbrella:
|
||||
|
||||
| | Version | Summary |
|
||||
| - | ------- | ------- |
|
||||
| [concurrency][h:conc] | 1.5.0.0 | Typeclasses, functions, and data types for concurrency and STM. |
|
||||
| [dejafu][h:dejafu] | 1.9.1.0 | Systematic testing for Haskell concurrency. |
|
||||
| [hunit-dejafu][h:hunit] | 1.2.0.4 | Deja Fu support for the HUnit test framework. |
|
||||
| [tasty-dejafu][h:tasty] | 1.2.0.5 | Deja Fu support for the Tasty test framework. |
|
||||
| [concurrency][h:conc] | 1.5.0.0 | Typeclasses, functions, and data types for concurrency and STM. |
|
||||
| [dejafu][h:dejafu] | 1.10.0.0 | Systematic testing for Haskell concurrency. |
|
||||
| [hunit-dejafu][h:hunit] | 1.2.0.5 | Deja Fu support for the HUnit test framework. |
|
||||
| [tasty-dejafu][h:tasty] | 1.2.0.6 | Deja Fu support for the Tasty test framework. |
|
||||
|
||||
Each package has its own README and CHANGELOG in its subdirectory.
|
||||
|
||||
|
@ -18,6 +18,7 @@ cabal-version: >=1.10
|
||||
|
||||
library
|
||||
exposed-modules: Unit
|
||||
, Unit.Predicates
|
||||
, Unit.Properties
|
||||
|
||||
, Integration
|
||||
|
@ -42,7 +42,7 @@ concFilter = unsafeRunFind $ [0..5] @! const True
|
||||
|
||||
-- | Check that two lists of results are equal, modulo order.
|
||||
checkResultLists :: Ord a => Predicate [a]
|
||||
checkResultLists = alwaysSameOn (fmap sort)
|
||||
checkResultLists = alwaysSameOn sort
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
@ -178,7 +178,7 @@ stmTests = toTestList
|
||||
[ djfuT "Transactions are atomic" (gives' [0,2]) $ do
|
||||
x <- atomically $ newTVarInt 0
|
||||
_ <- fork . atomically $ writeTVar x 1 >> writeTVar x 2
|
||||
atomically $ readTVar x
|
||||
readTVarConc x
|
||||
|
||||
, djfuT "'retry' is the left identity of 'orElse'" (gives' [()]) $ do
|
||||
x <- atomically $ newTVar Nothing
|
||||
|
@ -45,7 +45,7 @@ tests = toTestList
|
||||
writeTVar v 2
|
||||
writeTVar v 3
|
||||
retry
|
||||
atomically $ readTVar v
|
||||
readTVarConc v
|
||||
|
||||
, djfu "https://github.com/barrucadu/dejafu/issues/118" exceptionsAlways $
|
||||
catchSomeException
|
||||
|
@ -139,7 +139,7 @@ stmTests = toTestList
|
||||
|
||||
, djfu "When a TVar is updated, its new value is visible in a later transaction" (gives' [True]) $ do
|
||||
ctv <- atomically $ newTVarInt 5
|
||||
(5==) <$> atomically (readTVar ctv)
|
||||
(5==) <$> readTVarConc ctv
|
||||
|
||||
, djfu "Aborting a transaction blocks the thread" (gives [Left STMDeadlock])
|
||||
(atomically retry :: MonadConc m => m ()) -- avoid an ambiguous type
|
||||
@ -147,14 +147,14 @@ stmTests = toTestList
|
||||
, djfu "Aborting a transaction can be caught and recovered from" (gives' [True]) $ do
|
||||
ctv <- atomically $ newTVarInt 5
|
||||
atomically $ orElse retry (writeTVar ctv 6)
|
||||
(6==) <$> atomically (readTVar ctv)
|
||||
(6==) <$> readTVarConc ctv
|
||||
|
||||
, djfu "An exception thrown in a transaction can be caught" (gives' [True]) $ do
|
||||
ctv <- atomically $ newTVarInt 5
|
||||
atomically $ catchArithException
|
||||
(throwSTM Overflow)
|
||||
(\_ -> writeTVar ctv 6)
|
||||
(6==) <$> atomically (readTVar ctv)
|
||||
(6==) <$> readTVarConc ctv
|
||||
|
||||
, djfu "Nested exception handlers in transactions work" (gives' [True]) $ do
|
||||
ctv <- atomically $ newTVarInt 5
|
||||
@ -163,7 +163,7 @@ stmTests = toTestList
|
||||
(throwSTM Overflow)
|
||||
(\_ -> writeTVar ctv 0))
|
||||
(\_ -> writeTVar ctv 6)
|
||||
(6==) <$> atomically (readTVar ctv)
|
||||
(6==) <$> readTVarConc ctv
|
||||
|
||||
, djfu "MonadSTM is a MonadFail" (alwaysFailsWith isUncaughtException)
|
||||
(atomically $ fail "hello world" :: MonadConc m => m ()) -- avoid an ambiguous type
|
||||
|
@ -11,14 +11,16 @@ import Test.Tasty.Hedgehog (HedgehogDiscardLimit(..),
|
||||
import Test.Tasty.Options (IsOption(..), OptionDescription(..))
|
||||
import Text.Read (readMaybe)
|
||||
|
||||
import qualified Unit.Properties as P
|
||||
import qualified Unit.Predicates as PE
|
||||
import qualified Unit.Properties as PO
|
||||
|
||||
import Common
|
||||
|
||||
-- | Run all the unit tests.
|
||||
tests :: [TestTree]
|
||||
tests = map applyHedgehogOptions
|
||||
[ testGroup "Properties" P.tests
|
||||
[ testGroup "Predicates" PE.tests
|
||||
, testGroup "Properties" PO.tests
|
||||
]
|
||||
|
||||
-- | Tasty options
|
||||
|
78
dejafu-tests/lib/Unit/Predicates.hs
Normal file
78
dejafu-tests/lib/Unit/Predicates.hs
Normal file
@ -0,0 +1,78 @@
|
||||
module Unit.Predicates where
|
||||
|
||||
import qualified Test.DejaFu as D
|
||||
import Test.Tasty.HUnit
|
||||
|
||||
import Common
|
||||
|
||||
tests :: [TestTree]
|
||||
tests =
|
||||
[ testGroup "alwaysSameBy" alwaysSameBy
|
||||
, testGroup "notAlwaysSameBy" notAlwaysSameBy
|
||||
, testGroup "alwaysNothing" alwaysNothing
|
||||
, testGroup "somewhereNothing" somewhereNothing
|
||||
, testGroup "gives" gives
|
||||
]
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
alwaysSameBy :: [TestTree]
|
||||
alwaysSameBy = toTestList
|
||||
[ passes "Equal successes" (D.alwaysSameBy (==)) [Right 1, Right 1, Right 1]
|
||||
, fails "Unequal successes" (D.alwaysSameBy (==)) [Right 1, Right 2, Right 3]
|
||||
, fails "Equal failures" (D.alwaysSameBy (==)) [Left D.Deadlock, Left D.Deadlock, Left D.Deadlock]
|
||||
, fails "Unequal failures" (D.alwaysSameBy (==)) [Left D.Deadlock, Left D.STMDeadlock, Left D.Abort]
|
||||
, fails "Mixed failures and successes" (D.alwaysSameBy (==)) [Left D.Deadlock, Right 1, Right 1]
|
||||
]
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
notAlwaysSameBy :: [TestTree]
|
||||
notAlwaysSameBy = toTestList
|
||||
[ fails "Equal successes" (D.notAlwaysSameBy (==)) [Right 1, Right 1, Right 1]
|
||||
, passes "Unequal successes" (D.notAlwaysSameBy (==)) [Right 1, Right 2, Right 3]
|
||||
, fails "Equal failures" (D.notAlwaysSameBy (==)) [Left D.Deadlock, Left D.Deadlock, Left D.Deadlock]
|
||||
, fails "Unequal failures" (D.notAlwaysSameBy (==)) [Left D.Deadlock, Left D.STMDeadlock, Left D.Abort]
|
||||
, fails "Mixed failures and successes" (D.notAlwaysSameBy (==)) [Left D.Deadlock, Right 1, Right 1]
|
||||
]
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
alwaysNothing :: [TestTree]
|
||||
alwaysNothing = toTestList
|
||||
[ passes "Always" (D.alwaysNothing (const Nothing)) [Right 1, Right 2, Left D.Deadlock]
|
||||
, fails "Somewhere" (D.alwaysNothing (either (Just . Left) (const Nothing))) [Right 1, Right 2, Left D.Deadlock]
|
||||
, fails "Never" (D.alwaysNothing Just) [Right 1, Right 2, Left D.Deadlock]
|
||||
]
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
somewhereNothing :: [TestTree]
|
||||
somewhereNothing = toTestList
|
||||
[ passes "Always" (D.somewhereNothing (const Nothing)) [Right 1, Right 2, Left D.Deadlock]
|
||||
, passes "Somewhere" (D.somewhereNothing (either (Just . Left) (const Nothing))) [Right 1, Right 2, Left D.Deadlock]
|
||||
, fails "Never" (D.somewhereNothing Just) [Right 1, Right 2, Left D.Deadlock]
|
||||
]
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
gives :: [TestTree]
|
||||
gives = toTestList
|
||||
[ passes "Exact match" (D.gives [Right 1, Right 2]) [Right 1, Right 2]
|
||||
, fails "Extra results" (D.gives [Right 1, Right 2]) [Right 1, Right 2, Right 3]
|
||||
, fails "Missing results" (D.gives [Right 1, Right 2]) [Right 1]
|
||||
]
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- | Check a predicate passes
|
||||
passes :: String -> D.Predicate Int -> [Either D.Failure Int] -> TestTree
|
||||
passes = checkPredicate D._pass
|
||||
|
||||
-- | Check a predicate fails
|
||||
fails :: String -> D.Predicate Int -> [Either D.Failure Int] -> TestTree
|
||||
fails = checkPredicate (not . D._pass)
|
||||
|
||||
-- | Check a predicate
|
||||
checkPredicate :: (D.Result Int -> Bool) -> String -> D.Predicate Int -> [Either D.Failure Int] -> TestTree
|
||||
checkPredicate f msg p = testCase msg . assertBool "" . f . D.peval p . map (\efa -> (efa, []))
|
@ -7,6 +7,30 @@ standard Haskell versioning scheme.
|
||||
.. _PVP: https://pvp.haskell.org/
|
||||
|
||||
|
||||
1.10.0.0 (2018-06-17)
|
||||
---------------------
|
||||
|
||||
* Git: :tag:`dejafu-1.10.0.0`
|
||||
* Hackage: :hackage:`dejafu-1.10.0.0`
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
* The ``Test.DejaFu.notAlwaysSameOn`` and ``notAlwaysSameBy``
|
||||
predicates, generalising ``notAlwaysSame``.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
* ``Test.DejaFu.autocheck`` and related functions use the
|
||||
``successful`` predicate, rather than looking specifically for
|
||||
deadlocks and uncaught exceptions.
|
||||
|
||||
* (:issue:`259`) The ``Test.DejaFu.alwaysSame``, ``alwaysSameOn``,
|
||||
``alwaysSameBy``, and ``notAlwaysSame`` predicates fail if the
|
||||
computation under test fails.
|
||||
|
||||
|
||||
1.9.1.0 (2018-06-10)
|
||||
--------------------
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE TupleSections #-}
|
||||
|
||||
{- |
|
||||
@ -7,7 +6,7 @@ Copyright : (c) 2015--2018 Michael Walker
|
||||
License : MIT
|
||||
Maintainer : Michael Walker <mike@barrucadu.co.uk>
|
||||
Stability : experimental
|
||||
Portability : LambdaCase, MultiParamTypeClasses, TupleSections
|
||||
Portability : TupleSections
|
||||
|
||||
dejafu is a library for unit-testing concurrent Haskell programs,
|
||||
written using the [concurrency](https://hackage.haskell.org/package/concurrency)
|
||||
@ -27,17 +26,15 @@ let example = do
|
||||
We can test it with dejafu like so:
|
||||
|
||||
>>> autocheck example
|
||||
[pass] Never Deadlocks
|
||||
[pass] No Exceptions
|
||||
[fail] Consistent Result
|
||||
[pass] Successful
|
||||
[fail] Deterministic
|
||||
"hello" S0----S1--S0--
|
||||
<BLANKLINE>
|
||||
"world" S0----S2--S0--
|
||||
False
|
||||
|
||||
The 'autocheck' function takes a concurrent program to test and looks
|
||||
for some common unwanted behaviours: deadlocks, uncaught exceptions in
|
||||
the main thread, and nondeterminism. Here we see the program is
|
||||
for concurrency errors and nondeterminism. Here we see the program is
|
||||
nondeterministic, dejafu gives us all the distinct results it found
|
||||
and, for each, a summarised execution trace leading to that result:
|
||||
|
||||
@ -177,6 +174,8 @@ usage.
|
||||
, representative
|
||||
, alwaysSameOn
|
||||
, alwaysSameBy
|
||||
, notAlwaysSameOn
|
||||
, notAlwaysSameBy
|
||||
, alwaysTrue
|
||||
, somewhereTrue
|
||||
, alwaysNothing
|
||||
@ -272,14 +271,16 @@ import Control.DeepSeq (NFData(..))
|
||||
import Control.Monad (unless, when)
|
||||
import Control.Monad.Conc.Class (MonadConc)
|
||||
import Control.Monad.IO.Class (MonadIO(..))
|
||||
import Data.Either (isLeft)
|
||||
import Data.Function (on)
|
||||
import Data.List (intercalate, intersperse)
|
||||
import Data.List (intercalate, intersperse, partition)
|
||||
import Data.Maybe (catMaybes, isJust, isNothing,
|
||||
mapMaybe)
|
||||
import Data.Profunctor (Profunctor(..))
|
||||
import System.Environment (lookupEnv)
|
||||
|
||||
import Test.DejaFu.Conc
|
||||
import Test.DejaFu.Internal
|
||||
import Test.DejaFu.Refinement
|
||||
import Test.DejaFu.SCT
|
||||
import Test.DejaFu.Settings
|
||||
@ -314,13 +315,12 @@ let relaxed = do
|
||||
|
||||
-- | Automatically test a computation.
|
||||
--
|
||||
-- In particular, look for deadlocks, uncaught exceptions, and
|
||||
-- multiple return values. Returns @True@ if all tests pass
|
||||
-- In particular, concurrency errors and nondeterminism. Returns
|
||||
-- @True@ if all tests pass
|
||||
--
|
||||
-- >>> autocheck example
|
||||
-- [pass] Never Deadlocks
|
||||
-- [pass] No Exceptions
|
||||
-- [fail] Consistent Result
|
||||
-- [pass] Successful
|
||||
-- [fail] Deterministic
|
||||
-- "hello" S0----S1--S0--
|
||||
-- <BLANKLINE>
|
||||
-- "world" S0----S2--S0--
|
||||
@ -337,9 +337,8 @@ autocheck = autocheckWithSettings defaultSettings
|
||||
-- memory model.
|
||||
--
|
||||
-- >>> autocheckWay defaultWay defaultMemType relaxed
|
||||
-- [pass] Never Deadlocks
|
||||
-- [pass] No Exceptions
|
||||
-- [fail] Consistent Result
|
||||
-- [pass] Successful
|
||||
-- [fail] Deterministic
|
||||
-- (False,True) S0---------S1----S0--S2----S0--
|
||||
-- <BLANKLINE>
|
||||
-- (False,False) S0---------S1--P2----S1--S0---
|
||||
@ -350,9 +349,8 @@ autocheck = autocheckWithSettings defaultSettings
|
||||
-- False
|
||||
--
|
||||
-- >>> autocheckWay defaultWay SequentialConsistency relaxed
|
||||
-- [pass] Never Deadlocks
|
||||
-- [pass] No Exceptions
|
||||
-- [fail] Consistent Result
|
||||
-- [pass] Successful
|
||||
-- [fail] Deterministic
|
||||
-- (False,True) S0---------S1----S0--S2----S0--
|
||||
-- <BLANKLINE>
|
||||
-- (True,True) S0---------S1-P2----S1---S0---
|
||||
@ -374,9 +372,8 @@ autocheckWay way = autocheckWithSettings . fromWayAndMemType way
|
||||
-- | Variant of 'autocheck' which takes a settings record.
|
||||
--
|
||||
-- >>> autocheckWithSettings (fromWayAndMemType defaultWay defaultMemType) relaxed
|
||||
-- [pass] Never Deadlocks
|
||||
-- [pass] No Exceptions
|
||||
-- [fail] Consistent Result
|
||||
-- [pass] Successful
|
||||
-- [fail] Deterministic
|
||||
-- (False,True) S0---------S1----S0--S2----S0--
|
||||
-- <BLANKLINE>
|
||||
-- (False,False) S0---------S1--P2----S1--S0---
|
||||
@ -387,9 +384,8 @@ autocheckWay way = autocheckWithSettings . fromWayAndMemType way
|
||||
-- False
|
||||
--
|
||||
-- >>> autocheckWithSettings (fromWayAndMemType defaultWay SequentialConsistency) relaxed
|
||||
-- [pass] Never Deadlocks
|
||||
-- [pass] No Exceptions
|
||||
-- [fail] Consistent Result
|
||||
-- [pass] Successful
|
||||
-- [fail] Deterministic
|
||||
-- (False,True) S0---------S1----S0--S2----S0--
|
||||
-- <BLANKLINE>
|
||||
-- (True,True) S0---------S1-P2----S1---S0---
|
||||
@ -405,9 +401,8 @@ autocheckWithSettings :: (MonadConc n, MonadIO n, Eq a, Show a)
|
||||
-- ^ The computation to test.
|
||||
-> n Bool
|
||||
autocheckWithSettings settings = dejafusWithSettings settings
|
||||
[ ("Never Deadlocks", representative deadlocksNever)
|
||||
, ("No Exceptions", representative exceptionsNever)
|
||||
, ("Consistent Result", alwaysSame) -- already representative
|
||||
[ ("Successful", representative successful)
|
||||
, ("Deterministic", alwaysSame) -- already representative
|
||||
]
|
||||
|
||||
-- | Check a predicate and print the result to stdout, return 'True'
|
||||
@ -830,54 +825,86 @@ exceptionsAlways = alwaysTrue $ either isUncaughtException (const False)
|
||||
exceptionsSometimes :: Predicate a
|
||||
exceptionsSometimes = somewhereTrue $ either isUncaughtException (const False)
|
||||
|
||||
-- | Check that the result of a computation is always the same. In
|
||||
-- particular this means either: (a) it always fails in the same way,
|
||||
-- or (b) it never fails and the values returned are all equal.
|
||||
-- | Check that a computation always gives the same, successful,
|
||||
-- result.
|
||||
--
|
||||
-- > alwaysSame = alwaysSameBy (==)
|
||||
--
|
||||
-- @since 1.0.0.0
|
||||
-- @since 1.10.0.0
|
||||
alwaysSame :: Eq a => Predicate a
|
||||
alwaysSame = alwaysSameBy (==)
|
||||
|
||||
-- | Check that the result of a computation is always the same by
|
||||
-- comparing the result of a function on every result.
|
||||
-- | Check that a computation always gives the same (according to the
|
||||
-- provided function), successful, result.
|
||||
--
|
||||
-- > alwaysSameOn = alwaysSameBy ((==) `on` f)
|
||||
--
|
||||
-- @since 1.0.0.0
|
||||
alwaysSameOn :: Eq b => (Either Failure a -> b) -> Predicate a
|
||||
-- @since 1.10.0.0
|
||||
alwaysSameOn :: Eq b => (a -> b) -> Predicate a
|
||||
alwaysSameOn f = alwaysSameBy ((==) `on` f)
|
||||
|
||||
-- | Check that the result of a computation is always the same, using
|
||||
-- some transformation on results.
|
||||
--
|
||||
-- @since 1.0.0.0
|
||||
alwaysSameBy :: (Either Failure a -> Either Failure a -> Bool) -> Predicate a
|
||||
-- @since 1.10.0.0
|
||||
alwaysSameBy :: (a -> a -> Bool) -> Predicate a
|
||||
alwaysSameBy f = ProPredicate
|
||||
{ pdiscard = const Nothing
|
||||
, peval = \xs -> case simplestsBy f xs of
|
||||
[] -> defaultPass
|
||||
[_] -> defaultPass
|
||||
xs' -> defaultFail xs'
|
||||
, peval = \xs ->
|
||||
let (failures, successes) = partition (isLeft . fst) xs
|
||||
simpleSuccesses = simplestsBy (f `on` efromRight) successes
|
||||
in case (failures, simpleSuccesses) of
|
||||
([], []) -> defaultPass
|
||||
([], [_]) -> defaultPass
|
||||
(_, _) -> defaultFail (failures ++ simpleSuccesses)
|
||||
}
|
||||
|
||||
-- | Check that the result of a computation is not always the same.
|
||||
-- | Check that a computation never fails, and gives multiple distinct
|
||||
-- successful results.
|
||||
--
|
||||
-- @since 1.0.0.0
|
||||
-- > notAlwaysSame = notAlwaysSameBy (==)
|
||||
--
|
||||
-- @since 1.10.0.0
|
||||
notAlwaysSame :: Eq a => Predicate a
|
||||
notAlwaysSame = ProPredicate
|
||||
notAlwaysSame = notAlwaysSameBy (==)
|
||||
|
||||
-- | Check that a computation never fails, and gives multiple distinct
|
||||
-- (according to the provided function) successful results.
|
||||
--
|
||||
-- > notAlwaysSameOn = notAlwaysSameBy ((==) `on` f)
|
||||
--
|
||||
-- @since 1.10.0.0
|
||||
notAlwaysSameOn :: Eq b => (a -> b) -> Predicate a
|
||||
notAlwaysSameOn f = notAlwaysSameBy ((==) `on` f)
|
||||
|
||||
-- | Check that a computation never fails, and gives multiple distinct
|
||||
-- successful results, by applying a transformation on results.
|
||||
--
|
||||
-- This inverts the condition, so (eg) @notAlwaysSameBy (==)@ will
|
||||
-- pass if there are unequal results.
|
||||
--
|
||||
-- @since 1.10.0.0
|
||||
notAlwaysSameBy :: (a -> a -> Bool) -> Predicate a
|
||||
notAlwaysSameBy f = ProPredicate
|
||||
{ pdiscard = const Nothing
|
||||
, peval = \case
|
||||
[x] -> defaultFail [x]
|
||||
xs -> go xs (defaultFail [])
|
||||
, peval = \xs ->
|
||||
let (failures, successes) = partition (isLeft . fst) xs
|
||||
in case successes of
|
||||
[x] -> defaultFail (x : failures)
|
||||
_ ->
|
||||
let res = go successes (defaultFail [])
|
||||
in case failures of
|
||||
[] -> res
|
||||
_ -> res { _failures = failures ++ _failures res, _pass = False }
|
||||
}
|
||||
where
|
||||
y1 .*. y2 = not (on f (efromRight . fst) y1 y2)
|
||||
|
||||
go [y1,y2] res
|
||||
| fst y1 /= fst y2 = res { _pass = True }
|
||||
| y1 .*. y2 = res { _pass = True }
|
||||
| otherwise = res { _failures = y1 : y2 : _failures res }
|
||||
go (y1:y2:ys) res
|
||||
| fst y1 /= fst y2 = go (y2:ys) res { _pass = True }
|
||||
| y1 .*. y2 = go (y2:ys) res { _pass = True }
|
||||
| otherwise = go (y2:ys) res { _failures = y1 : y2 : _failures res }
|
||||
go _ res = res
|
||||
|
||||
|
@ -351,6 +351,12 @@ efromList :: HasCallStack => [a] -> NonEmpty a
|
||||
efromList (x:xs) = x:|xs
|
||||
efromList _ = withFrozenCallStack $ fatal "fromList: empty list"
|
||||
|
||||
-- | 'fromRight' but with a better error message if it fails. Use
|
||||
-- this only where it shouldn't fail!
|
||||
efromRight :: HasCallStack => Either a b -> b
|
||||
efromRight (Right b) = b
|
||||
efromRight _ = withFrozenCallStack $ fatal "fromRight: Left"
|
||||
|
||||
-- | 'M.adjust' but which errors if the key is not present. Use this
|
||||
-- only where it shouldn't fail!
|
||||
eadjust :: (Ord k, Show k, HasCallStack) => (v -> v) -> k -> M.Map k v -> M.Map k v
|
||||
|
@ -2,7 +2,7 @@
|
||||
-- documentation, see http://haskell.org/cabal/users-guide/
|
||||
|
||||
name: dejafu
|
||||
version: 1.9.1.0
|
||||
version: 1.10.0.0
|
||||
synopsis: A library for unit-testing concurrent programs.
|
||||
|
||||
description:
|
||||
@ -33,7 +33,7 @@ source-repository head
|
||||
source-repository this
|
||||
type: git
|
||||
location: https://github.com/barrucadu/dejafu.git
|
||||
tag: dejafu-1.9.1.0
|
||||
tag: dejafu-1.10.0.0
|
||||
|
||||
library
|
||||
exposed-modules: Test.DejaFu
|
||||
|
@ -27,10 +27,10 @@ There are a few different packages under the Déjà Fu umbrella:
|
||||
.. csv-table::
|
||||
:header: "Package", "Version", "Summary"
|
||||
|
||||
":hackage:`concurrency`", "1.5.0.0", "Typeclasses, functions, and data types for concurrency and STM"
|
||||
":hackage:`dejafu`", "1.9.1.0", "Systematic testing for Haskell concurrency"
|
||||
":hackage:`hunit-dejafu`", "1.2.0.4", "Déjà Fu support for the HUnit test framework"
|
||||
":hackage:`tasty-dejafu`", "1.2.0.5", "Déjà Fu support for the tasty test framework"
|
||||
":hackage:`concurrency`", "1.5.0.0", "Typeclasses, functions, and data types for concurrency and STM"
|
||||
":hackage:`dejafu`", "1.10.0.0", "Systematic testing for Haskell concurrency"
|
||||
":hackage:`hunit-dejafu`", "1.2.0.5", "Déjà Fu support for the HUnit test framework"
|
||||
":hackage:`tasty-dejafu`", "1.2.0.6", "Déjà Fu support for the tasty test framework"
|
||||
|
||||
|
||||
Installation
|
||||
@ -82,16 +82,15 @@ we'll get onto that shortly. First, the result of testing:
|
||||
.. code-block:: none
|
||||
|
||||
> autocheck myFunction
|
||||
[pass] Never Deadlocks
|
||||
[pass] No Exceptions
|
||||
[fail] Consistent Result
|
||||
[pass] Successful
|
||||
[fail] Deterministic
|
||||
"hello" S0----S1--S0--
|
||||
|
||||
"world" S0----S2--S0--
|
||||
False
|
||||
|
||||
There are no deadlocks or uncaught exceptions, which is good; but the
|
||||
program is (as you probably spotted) nondeterministic!
|
||||
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
|
||||
|
@ -116,6 +116,8 @@ to it from a different thread).
|
||||
``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!
|
||||
|
@ -7,6 +7,18 @@ standard Haskell versioning scheme.
|
||||
.. _PVP: https://pvp.haskell.org/
|
||||
|
||||
|
||||
1.2.0.5 (2018-06-17)
|
||||
--------------------
|
||||
|
||||
* Git: :tag:`hunit-dejafu-1.2.0.5`
|
||||
* Hackage: :hackage:`hunit-dejafu-1.2.0.5`
|
||||
|
||||
Miscellaneous
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* The upper bound on :hackage:`dejafu` is <1.11.
|
||||
|
||||
|
||||
1.2.0.4 (2018-06-10)
|
||||
--------------------
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
-- documentation, see http://haskell.org/cabal/users-guide/
|
||||
|
||||
name: hunit-dejafu
|
||||
version: 1.2.0.4
|
||||
version: 1.2.0.5
|
||||
synopsis: Deja Fu support for the HUnit test framework.
|
||||
|
||||
description:
|
||||
@ -30,7 +30,7 @@ source-repository head
|
||||
source-repository this
|
||||
type: git
|
||||
location: https://github.com/barrucadu/dejafu.git
|
||||
tag: hunit-dejafu-1.2.0.4
|
||||
tag: hunit-dejafu-1.2.0.5
|
||||
|
||||
library
|
||||
exposed-modules: Test.HUnit.DejaFu
|
||||
@ -38,7 +38,7 @@ library
|
||||
-- other-extensions:
|
||||
build-depends: base >=4.9 && <5
|
||||
, exceptions >=0.7 && <0.11
|
||||
, dejafu >=1.5 && <1.10
|
||||
, dejafu >=1.5 && <1.11
|
||||
, HUnit >=1.3.1 && <1.7
|
||||
-- hs-source-dirs:
|
||||
default-language: Haskell2010
|
||||
|
@ -7,6 +7,18 @@ standard Haskell versioning scheme.
|
||||
.. _PVP: https://pvp.haskell.org/
|
||||
|
||||
|
||||
1.2.0.6 (2018-06-17)
|
||||
--------------------
|
||||
|
||||
* Git: :tag:`tasty-dejafu-1.2.0.6`
|
||||
* Hackage: :hackage:`tasty-dejafu-1.2.0.6`
|
||||
|
||||
Miscellaneous
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
* The upper bound on :hackage:`dejafu` is <1.11.
|
||||
|
||||
|
||||
1.2.0.5 (2018-06-10)
|
||||
--------------------
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
-- documentation, see http://haskell.org/cabal/users-guide/
|
||||
|
||||
name: tasty-dejafu
|
||||
version: 1.2.0.5
|
||||
version: 1.2.0.6
|
||||
synopsis: Deja Fu support for the Tasty test framework.
|
||||
|
||||
description:
|
||||
@ -30,14 +30,14 @@ source-repository head
|
||||
source-repository this
|
||||
type: git
|
||||
location: https://github.com/barrucadu/dejafu.git
|
||||
tag: tasty-dejafu-1.2.0.5
|
||||
tag: tasty-dejafu-1.2.0.6
|
||||
|
||||
library
|
||||
exposed-modules: Test.Tasty.DejaFu
|
||||
-- other-modules:
|
||||
-- other-extensions:
|
||||
build-depends: base >=4.9 && <5
|
||||
, dejafu >=1.5 && <1.10
|
||||
, dejafu >=1.5 && <1.11
|
||||
, random >=1.0 && <1.2
|
||||
, tagged >=0.8 && <0.9
|
||||
, tasty >=0.10 && <1.2
|
||||
|
Loading…
Reference in New Issue
Block a user