2016-04-29 01:00:51 +03:00
|
|
|
{-# LANGUAGE CPP #-}
|
2017-06-07 16:02:40 +03:00
|
|
|
{-# LANGUAGE FlexibleContexts #-}
|
2016-04-29 01:00:51 +03:00
|
|
|
{-# LANGUAGE FlexibleInstances #-}
|
|
|
|
{-# LANGUAGE GADTs #-}
|
|
|
|
{-# LANGUAGE RankNTypes #-}
|
|
|
|
{-# LANGUAGE TypeSynonymInstances #-}
|
2015-10-08 23:50:52 +03:00
|
|
|
|
2016-05-26 15:54:13 +03:00
|
|
|
#if __GLASGOW_HASKELL__ >= 800
|
|
|
|
-- Impredicative polymorphism checks got stronger in GHC 8, breaking
|
|
|
|
-- the use of 'unsafeCoerce' below.
|
|
|
|
{-# LANGUAGE ImpredicativeTypes #-}
|
|
|
|
#endif
|
|
|
|
|
2016-08-26 13:16:59 +03:00
|
|
|
-- |
|
|
|
|
-- Module : Test.Tasty.DejaFu
|
|
|
|
-- Copyright : (c) 2016 Michael Walker
|
|
|
|
-- License : MIT
|
|
|
|
-- Maintainer : Michael Walker <mike@barrucadu.co.uk>
|
|
|
|
-- Stability : stable
|
2017-06-07 16:02:40 +03:00
|
|
|
-- Portability : CPP, FlexibleContexts, FlexibleInstances, GADTs, ImpredicativeTypes, RankNTypes, TypeSynonymInstances
|
2016-08-26 13:16:59 +03:00
|
|
|
--
|
|
|
|
-- This module allows using Deja Fu predicates with Tasty to test the
|
|
|
|
-- behaviour of concurrent systems.
|
2015-10-08 23:50:52 +03:00
|
|
|
module Test.Tasty.DejaFu
|
2016-04-29 01:00:51 +03:00
|
|
|
( -- * Unit testing
|
|
|
|
|
|
|
|
-- | This is supported by the 'IsTest' instances for 'ConcST' and
|
|
|
|
-- 'ConcIO'. These instances try all executions, reporting as
|
|
|
|
-- failures the cases which return a 'Just' string.
|
|
|
|
--
|
|
|
|
-- @instance Typeable t => IsTest (ConcST t (Maybe String))@
|
|
|
|
-- @instance IsTest (ConcIO (Maybe String))@
|
|
|
|
-- @instance IsOption Bounds@
|
|
|
|
-- @instance IsOption MemType@
|
|
|
|
|
|
|
|
-- * Property testing
|
2015-10-08 23:50:52 +03:00
|
|
|
testAuto
|
|
|
|
, testDejafu
|
|
|
|
, testDejafus
|
2016-04-29 01:00:51 +03:00
|
|
|
|
2017-02-20 06:17:22 +03:00
|
|
|
, testAutoWay
|
|
|
|
, testDejafuWay
|
|
|
|
, testDejafusWay
|
2016-04-29 01:00:51 +03:00
|
|
|
|
|
|
|
-- ** @IO@
|
2015-10-08 23:50:52 +03:00
|
|
|
, testAutoIO
|
|
|
|
, testDejafuIO
|
|
|
|
, testDejafusIO
|
|
|
|
|
2017-02-20 06:17:22 +03:00
|
|
|
, testAutoWayIO
|
|
|
|
, testDejafuWayIO
|
|
|
|
, testDejafusWayIO
|
2016-04-29 01:00:51 +03:00
|
|
|
|
2017-06-07 16:02:40 +03:00
|
|
|
-- ** Re-exports
|
2017-05-27 18:15:51 +03:00
|
|
|
, Way
|
|
|
|
, defaultWay
|
|
|
|
, systematically
|
|
|
|
, randomly
|
2016-04-29 01:00:51 +03:00
|
|
|
, Bounds(..)
|
2017-05-27 18:15:51 +03:00
|
|
|
, defaultBounds
|
2016-04-29 01:00:51 +03:00
|
|
|
, MemType(..)
|
2017-05-27 18:15:51 +03:00
|
|
|
, defaultMemType
|
2017-06-07 16:02:40 +03:00
|
|
|
|
|
|
|
-- * Refinement property testing
|
|
|
|
, testProperty
|
|
|
|
|
|
|
|
-- ** Re-exports
|
|
|
|
, R.Sig(..)
|
|
|
|
, R.RefinementProperty
|
|
|
|
, R.Testable(..)
|
|
|
|
, R.Listable(..)
|
|
|
|
, R.expectFailure
|
|
|
|
, R.refines, (R.=>=)
|
|
|
|
, R.strictlyRefines, (R.->-)
|
|
|
|
, R.equivalentTo, (R.===)
|
2015-10-08 23:50:52 +03:00
|
|
|
) where
|
|
|
|
|
2017-06-07 16:02:40 +03:00
|
|
|
import Control.Monad.ST (runST)
|
|
|
|
import Data.Char (toUpper)
|
|
|
|
import qualified Data.Foldable as F
|
|
|
|
import Data.List (intercalate, intersperse)
|
|
|
|
import Data.Proxy (Proxy(..))
|
|
|
|
import Data.Tagged (Tagged(..))
|
|
|
|
import Data.Typeable (Typeable)
|
|
|
|
import System.Random (StdGen, mkStdGen)
|
2017-06-07 16:08:34 +03:00
|
|
|
import Test.DejaFu hiding (Testable(..))
|
2017-06-07 16:02:40 +03:00
|
|
|
import qualified Test.DejaFu.Conc as Conc
|
|
|
|
import qualified Test.DejaFu.Refinement as R
|
|
|
|
import qualified Test.DejaFu.SCT as SCT
|
|
|
|
import Test.Tasty (TestName, TestTree, testGroup)
|
|
|
|
import Test.Tasty.Options (IsOption(..), OptionDescription(..),
|
|
|
|
lookupOption)
|
|
|
|
import Test.Tasty.Providers (IsTest(..), singleTest, testFailed,
|
|
|
|
testPassed)
|
2015-10-08 23:50:52 +03:00
|
|
|
|
2016-04-29 01:00:51 +03:00
|
|
|
-- Can't put the necessary forall in the @IsTest ConcST t@
|
|
|
|
-- instance :(
|
2017-06-07 16:02:40 +03:00
|
|
|
import Unsafe.Coerce (unsafeCoerce)
|
2016-04-29 01:00:51 +03:00
|
|
|
|
2017-02-26 05:51:36 +03:00
|
|
|
runSCTst :: Way -> MemType -> (forall t. Conc.ConcST t a) -> [(Either Failure a, Conc.Trace)]
|
2017-02-20 06:17:22 +03:00
|
|
|
runSCTst way memtype conc = runST (SCT.runSCT way memtype conc)
|
2016-05-12 20:21:01 +03:00
|
|
|
|
2017-02-26 05:51:36 +03:00
|
|
|
runSCTio :: Way -> MemType -> Conc.ConcIO a -> IO [(Either Failure a, Conc.Trace)]
|
2017-02-20 06:17:22 +03:00
|
|
|
runSCTio = SCT.runSCT
|
2016-05-12 20:21:01 +03:00
|
|
|
|
2015-10-08 23:50:52 +03:00
|
|
|
--------------------------------------------------------------------------------
|
2017-06-07 16:02:40 +03:00
|
|
|
-- Tasty-style unit testing
|
2016-04-29 01:00:51 +03:00
|
|
|
|
2017-04-08 21:51:09 +03:00
|
|
|
-- | @since 0.3.0.0
|
2016-06-22 13:20:22 +03:00
|
|
|
instance Typeable t => IsTest (Conc.ConcST t (Maybe String)) where
|
2016-04-29 01:00:51 +03:00
|
|
|
testOptions = Tagged concOptions
|
|
|
|
|
|
|
|
run options conc callback = do
|
|
|
|
let memtype = lookupOption options :: MemType
|
2017-02-26 05:51:36 +03:00
|
|
|
let way = lookupOption options :: Way
|
2017-02-20 06:17:22 +03:00
|
|
|
let runSCTst' :: Conc.ConcST t (Maybe String) -> [(Either Failure (Maybe String), Conc.Trace)]
|
|
|
|
runSCTst' = unsafeCoerce $ runSCTst way memtype
|
|
|
|
let traces = runSCTst' conc
|
2016-04-29 01:00:51 +03:00
|
|
|
run options (ConcTest traces assertableP) callback
|
|
|
|
|
2017-04-08 21:51:09 +03:00
|
|
|
-- | @since 0.3.0.0
|
2016-06-22 13:20:22 +03:00
|
|
|
instance IsTest (Conc.ConcIO (Maybe String)) where
|
2016-04-29 01:00:51 +03:00
|
|
|
testOptions = Tagged concOptions
|
|
|
|
|
|
|
|
run options conc callback = do
|
|
|
|
let memtype = lookupOption options
|
2017-02-26 05:51:36 +03:00
|
|
|
let way = lookupOption options
|
2017-02-20 06:17:22 +03:00
|
|
|
let traces = runSCTio way memtype conc
|
2016-04-29 01:00:51 +03:00
|
|
|
run options (ConcIOTest traces assertableP) callback
|
|
|
|
|
|
|
|
concOptions :: [OptionDescription]
|
|
|
|
concOptions =
|
2017-02-20 06:17:22 +03:00
|
|
|
[ Option (Proxy :: Proxy MemType)
|
2017-02-26 05:51:36 +03:00
|
|
|
, Option (Proxy :: Proxy Way)
|
2016-04-29 01:00:51 +03:00
|
|
|
]
|
|
|
|
|
|
|
|
assertableP :: Predicate (Maybe String)
|
|
|
|
assertableP = alwaysTrue $ \r -> case r of
|
|
|
|
Right (Just _) -> False
|
|
|
|
_ -> True
|
|
|
|
|
2017-04-08 21:51:09 +03:00
|
|
|
-- | @since 0.3.0.0
|
2016-04-29 01:00:51 +03:00
|
|
|
instance IsOption MemType where
|
|
|
|
defaultValue = defaultMemType
|
2017-02-20 06:17:22 +03:00
|
|
|
parseValue = shortName . map toUpper where
|
2016-04-29 01:00:51 +03:00
|
|
|
shortName "SC" = Just SequentialConsistency
|
|
|
|
shortName "TSO" = Just TotalStoreOrder
|
|
|
|
shortName "PSO" = Just PartialStoreOrder
|
|
|
|
shortName _ = Nothing
|
|
|
|
optionName = Tagged "memory-model"
|
2017-02-20 06:17:22 +03:00
|
|
|
optionHelp = Tagged "The memory model to use. This should be one of \"sc\", \"tso\", or \"pso\"."
|
|
|
|
|
2017-04-08 23:18:20 +03:00
|
|
|
-- | @since 0.5.0.0
|
2017-02-26 05:51:36 +03:00
|
|
|
instance IsOption Way where
|
2017-02-20 06:17:22 +03:00
|
|
|
defaultValue = defaultWay
|
|
|
|
parseValue = shortName . map toUpper where
|
2017-05-27 18:15:51 +03:00
|
|
|
shortName "SYSTEMATICALLY" = Just (systematically defaultBounds)
|
|
|
|
shortName "RANDOMLY" = Just (randomly (mkStdGen 42) 100 1)
|
2017-02-20 06:17:22 +03:00
|
|
|
shortName _ = Nothing
|
|
|
|
optionName = Tagged "way"
|
|
|
|
optionHelp = Tagged "The execution method to use. This should be one of \"systematically\" or \"randomly\"."
|
2016-04-29 01:00:51 +03:00
|
|
|
|
2017-06-07 16:02:40 +03:00
|
|
|
|
2016-04-29 01:00:51 +03:00
|
|
|
--------------------------------------------------------------------------------
|
2017-06-07 16:02:40 +03:00
|
|
|
-- DejaFu-style unit testing
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
-- | Automatically test a computation. In particular, look for
|
|
|
|
-- deadlocks, uncaught exceptions, and multiple return values.
|
2017-02-20 06:17:22 +03:00
|
|
|
--
|
2015-10-08 23:50:52 +03:00
|
|
|
-- This uses the 'Conc' monad for testing, which is an instance of
|
|
|
|
-- 'MonadConc'. If you need to test something which also uses
|
|
|
|
-- 'MonadIO', use 'testAutoIO'.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
|
|
|
-- @since 0.2.0.0
|
2015-10-08 23:50:52 +03:00
|
|
|
testAuto :: (Eq a, Show a)
|
2016-06-22 13:20:22 +03:00
|
|
|
=> (forall t. Conc.ConcST t a)
|
2015-10-08 23:50:52 +03:00
|
|
|
-- ^ The computation to test
|
|
|
|
-> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testAuto = testAutoWay defaultWay defaultMemType
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
-- | Variant of 'testAuto' which tests a computation under a given
|
2017-02-20 06:17:22 +03:00
|
|
|
-- execution way and memory model.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
2017-04-08 23:18:20 +03:00
|
|
|
-- @since 0.5.0.0
|
2017-02-26 05:51:36 +03:00
|
|
|
testAutoWay :: (Eq a, Show a)
|
|
|
|
=> Way
|
2017-02-20 06:17:22 +03:00
|
|
|
-- ^ How to execute the concurrent program.
|
|
|
|
-> MemType
|
2015-10-08 23:50:52 +03:00
|
|
|
-- ^ The memory model to use for non-synchronised @CRef@ operations.
|
2016-06-22 13:20:22 +03:00
|
|
|
-> (forall t. Conc.ConcST t a)
|
2015-10-08 23:50:52 +03:00
|
|
|
-- ^ The computation to test
|
|
|
|
-> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testAutoWay way memtype conc = testDejafusWay way memtype conc autocheckCases
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
-- | Variant of 'testAuto' for computations which do 'IO'.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
|
|
|
-- @since 0.2.0.0
|
2016-06-22 13:20:22 +03:00
|
|
|
testAutoIO :: (Eq a, Show a) => Conc.ConcIO a -> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testAutoIO = testAutoWayIO defaultWay defaultMemType
|
2015-10-08 23:50:52 +03:00
|
|
|
|
2017-02-20 06:17:22 +03:00
|
|
|
-- | Variant of 'testAutoWay' for computations which do 'IO'.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
2017-04-08 23:18:20 +03:00
|
|
|
-- @since 0.5.0.0
|
2017-02-26 05:51:36 +03:00
|
|
|
testAutoWayIO :: (Eq a, Show a)
|
|
|
|
=> Way -> MemType -> Conc.ConcIO a -> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testAutoWayIO way memtype concio =
|
|
|
|
testDejafusWayIO way memtype concio autocheckCases
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
-- | Predicates for the various autocheck functions.
|
|
|
|
autocheckCases :: Eq a => [(TestName, Predicate a)]
|
|
|
|
autocheckCases =
|
2015-11-12 17:55:54 +03:00
|
|
|
[("Never Deadlocks", representative deadlocksNever)
|
|
|
|
, ("No Exceptions", representative exceptionsNever)
|
2015-10-08 23:50:52 +03:00
|
|
|
, ("Consistent Result", alwaysSame)
|
|
|
|
]
|
|
|
|
|
|
|
|
-- | Check that a predicate holds.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
|
|
|
-- @since 0.2.0.0
|
2015-10-25 20:05:40 +03:00
|
|
|
testDejafu :: Show a
|
2016-06-22 13:20:22 +03:00
|
|
|
=> (forall t. Conc.ConcST t a)
|
2015-10-08 23:50:52 +03:00
|
|
|
-- ^ The computation to test
|
|
|
|
-> TestName
|
|
|
|
-- ^ The name of the test.
|
|
|
|
-> Predicate a
|
|
|
|
-- ^ The predicate to check
|
|
|
|
-> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testDejafu = testDejafuWay defaultWay defaultMemType
|
|
|
|
|
|
|
|
-- | Variant of 'testDejafu' which takes a way to execute the program
|
|
|
|
-- and a memory model.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
2017-04-08 23:18:20 +03:00
|
|
|
-- @since 0.5.0.0
|
2017-02-26 05:51:36 +03:00
|
|
|
testDejafuWay :: Show a
|
|
|
|
=> Way
|
2017-02-20 06:17:22 +03:00
|
|
|
-- ^ How to execute the concurrent program.
|
|
|
|
-> MemType
|
2015-10-08 23:50:52 +03:00
|
|
|
-- ^ The memory model to use for non-synchronised @CRef@ operations.
|
2016-06-22 13:20:22 +03:00
|
|
|
-> (forall t. Conc.ConcST t a)
|
2015-10-08 23:50:52 +03:00
|
|
|
-- ^ The computation to test
|
|
|
|
-> TestName
|
|
|
|
-- ^ The name of the test.
|
|
|
|
-> Predicate a
|
|
|
|
-- ^ The predicate to check
|
|
|
|
-> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testDejafuWay way memtype conc name p =
|
|
|
|
testDejafusWay way memtype conc [(name, p)]
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
-- | Variant of 'testDejafu' which takes a collection of predicates to
|
|
|
|
-- test. This will share work between the predicates, rather than
|
|
|
|
-- running the concurrent computation many times for each predicate.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
|
|
|
-- @since 0.2.0.0
|
2015-10-25 20:05:40 +03:00
|
|
|
testDejafus :: Show a
|
2016-06-22 13:20:22 +03:00
|
|
|
=> (forall t. Conc.ConcST t a)
|
2015-10-08 23:50:52 +03:00
|
|
|
-- ^ The computation to test
|
|
|
|
-> [(TestName, Predicate a)]
|
|
|
|
-- ^ The list of predicates (with names) to check
|
|
|
|
-> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testDejafus = testDejafusWay defaultWay defaultMemType
|
|
|
|
|
|
|
|
-- | Variant of 'testDejafus' which takes a way to execute the program
|
|
|
|
-- and a memory model.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
2017-04-08 23:18:20 +03:00
|
|
|
-- @since 0.5.0.0
|
2017-02-26 05:51:36 +03:00
|
|
|
testDejafusWay :: Show a
|
|
|
|
=> Way
|
2017-02-20 06:17:22 +03:00
|
|
|
-- ^ How to execute the concurrent program.
|
|
|
|
-> MemType
|
2015-10-08 23:50:52 +03:00
|
|
|
-- ^ The memory model to use for non-synchronised @CRef@ operations.
|
2016-06-22 13:20:22 +03:00
|
|
|
-> (forall t. Conc.ConcST t a)
|
2015-10-08 23:50:52 +03:00
|
|
|
-- ^ The computation to test
|
|
|
|
-> [(TestName, Predicate a)]
|
|
|
|
-- ^ The list of predicates (with names) to check
|
|
|
|
-> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testDejafusWay = testst
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
-- | Variant of 'testDejafu' for computations which do 'IO'.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
|
|
|
-- @since 0.2.0.0
|
2016-06-22 13:20:22 +03:00
|
|
|
testDejafuIO :: Show a => Conc.ConcIO a -> TestName -> Predicate a -> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testDejafuIO = testDejafuWayIO defaultWay defaultMemType
|
2015-10-08 23:50:52 +03:00
|
|
|
|
2017-02-20 06:17:22 +03:00
|
|
|
-- | Variant of 'testDejafuWay' for computations which do 'IO'.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
2017-04-08 23:18:20 +03:00
|
|
|
-- @since 0.5.0.0
|
2017-02-26 05:51:36 +03:00
|
|
|
testDejafuWayIO :: Show a
|
|
|
|
=> Way -> MemType -> Conc.ConcIO a -> TestName -> Predicate a -> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testDejafuWayIO way memtype concio name p =
|
|
|
|
testDejafusWayIO way memtype concio [(name, p)]
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
-- | Variant of 'testDejafus' for computations which do 'IO'.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
|
|
|
-- @since 0.2.0.0
|
2016-06-22 13:20:22 +03:00
|
|
|
testDejafusIO :: Show a => Conc.ConcIO a -> [(TestName, Predicate a)] -> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testDejafusIO = testDejafusWayIO defaultWay defaultMemType
|
2015-10-08 23:50:52 +03:00
|
|
|
|
2017-02-20 06:17:22 +03:00
|
|
|
-- | Variant of 'dejafusWay' for computations which do 'IO'.
|
2017-04-08 21:51:09 +03:00
|
|
|
--
|
2017-04-08 23:18:20 +03:00
|
|
|
-- @since 0.5.0.0
|
2017-02-26 05:51:36 +03:00
|
|
|
testDejafusWayIO :: Show a
|
|
|
|
=> Way -> MemType -> Conc.ConcIO a -> [(TestName, Predicate a)] -> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testDejafusWayIO = testio
|
2015-10-08 23:50:52 +03:00
|
|
|
|
2017-06-07 16:02:40 +03:00
|
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
-- Refinement property testing
|
|
|
|
|
|
|
|
-- | Check a refinement property with a variety of seed values and
|
|
|
|
-- variable assignments.
|
|
|
|
--
|
|
|
|
-- @since unreleased
|
|
|
|
testProperty :: (R.Testable p, R.Listable (R.X p), Eq (R.X p), Show (R.X p), Show (R.O p))
|
|
|
|
=> TestName
|
|
|
|
-- ^ The name of the test.
|
|
|
|
-> p
|
|
|
|
-- ^ The property to check.
|
|
|
|
-> TestTree
|
|
|
|
testProperty = testprop
|
|
|
|
|
|
|
|
|
2015-10-08 23:50:52 +03:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- Tasty integration
|
|
|
|
|
|
|
|
data ConcTest where
|
2017-02-20 06:17:22 +03:00
|
|
|
ConcTest :: Show a => [(Either Failure a, Conc.Trace)] -> Predicate a -> ConcTest
|
2015-10-08 23:50:52 +03:00
|
|
|
deriving Typeable
|
|
|
|
|
|
|
|
data ConcIOTest where
|
2017-02-20 06:17:22 +03:00
|
|
|
ConcIOTest :: Show a => IO [(Either Failure a, Conc.Trace)] -> Predicate a -> ConcIOTest
|
2015-10-08 23:50:52 +03:00
|
|
|
deriving Typeable
|
|
|
|
|
2017-06-07 16:02:40 +03:00
|
|
|
data PropTest where
|
|
|
|
PropTest :: (R.Testable p, R.Listable (R.X p), Eq (R.X p), Show (R.X p), Show (R.O p)) => p -> PropTest
|
|
|
|
deriving Typeable
|
|
|
|
|
2015-10-08 23:50:52 +03:00
|
|
|
instance IsTest ConcTest where
|
2017-04-08 00:50:30 +03:00
|
|
|
testOptions = pure []
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
run _ (ConcTest traces p) _ =
|
|
|
|
let err = showErr $ p traces
|
2017-04-08 00:50:30 +03:00
|
|
|
in pure (if null err then testPassed "" else testFailed err)
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
instance IsTest ConcIOTest where
|
2017-04-08 00:50:30 +03:00
|
|
|
testOptions = pure []
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
run _ (ConcIOTest iotraces p) _ = do
|
|
|
|
traces <- iotraces
|
|
|
|
let err = showErr $ p traces
|
2017-04-08 00:50:30 +03:00
|
|
|
pure (if null err then testPassed "" else testFailed err)
|
2015-10-08 23:50:52 +03:00
|
|
|
|
2017-06-07 16:02:40 +03:00
|
|
|
instance IsTest PropTest where
|
|
|
|
testOptions = pure []
|
|
|
|
|
|
|
|
run _ (PropTest p) _ = do
|
|
|
|
ce <- R.check' p
|
|
|
|
pure $ case ce of
|
|
|
|
Just c -> testFailed . init $ unlines
|
|
|
|
[ "*** Failure: " ++
|
|
|
|
(if null (R.failingArgs c) then "" else unwords (R.failingArgs c) ++ " ") ++
|
|
|
|
"(seed " ++ show (R.failingSeed c) ++ ")"
|
|
|
|
, " left: " ++ show (F.toList $ R.leftResults c)
|
|
|
|
, " right: " ++ show (F.toList $ R.rightResults c)
|
|
|
|
]
|
|
|
|
Nothing -> testPassed ""
|
|
|
|
|
2015-10-08 23:50:52 +03:00
|
|
|
-- | Produce a Tasty 'TestTree' from a Deja Fu test.
|
2017-02-26 05:51:36 +03:00
|
|
|
testst :: Show a
|
|
|
|
=> Way -> MemType -> (forall t. Conc.ConcST t a) -> [(TestName, Predicate a)] -> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testst way memtype conc tests = case map toTest tests of
|
2015-10-08 23:50:52 +03:00
|
|
|
[t] -> t
|
|
|
|
ts -> testGroup "Deja Fu Tests" ts
|
|
|
|
|
|
|
|
where
|
|
|
|
toTest (name, p) = singleTest name $ ConcTest traces p
|
|
|
|
|
2017-02-20 06:17:22 +03:00
|
|
|
traces = runSCTst way memtype conc
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
-- | Produce a Tasty 'Test' from an IO-using Deja Fu test.
|
2017-02-26 05:51:36 +03:00
|
|
|
testio :: Show a
|
|
|
|
=> Way -> MemType -> Conc.ConcIO a -> [(TestName, Predicate a)] -> TestTree
|
2017-02-20 06:17:22 +03:00
|
|
|
testio way memtype concio tests = case map toTest tests of
|
2015-10-08 23:50:52 +03:00
|
|
|
[t] -> t
|
|
|
|
ts -> testGroup "Deja Fu Tests" ts
|
|
|
|
|
|
|
|
where
|
|
|
|
toTest (name, p) = singleTest name $ ConcIOTest traces p
|
|
|
|
|
|
|
|
-- As with HUnit, constructing a test is side-effect free, so
|
|
|
|
-- sharing of traces can't happen here.
|
2017-02-20 06:17:22 +03:00
|
|
|
traces = runSCTio way memtype concio
|
2015-10-08 23:50:52 +03:00
|
|
|
|
2017-06-07 16:02:40 +03:00
|
|
|
-- | Produce a Tasty 'TestTree' from a Deja Fu refinement property test.
|
|
|
|
testprop :: (R.Testable p, R.Listable (R.X p), Eq (R.X p), Show (R.X p), Show (R.O p))
|
|
|
|
=> TestName -> p -> TestTree
|
|
|
|
testprop name = singleTest name . PropTest
|
|
|
|
|
2015-10-08 23:50:52 +03:00
|
|
|
-- | Convert a test result into an error message on failure (empty
|
|
|
|
-- string on success).
|
|
|
|
showErr :: Show a => Result a -> String
|
|
|
|
showErr res
|
|
|
|
| _pass res = ""
|
2015-10-27 18:39:52 +03:00
|
|
|
| otherwise = "Failed after " ++ show (_casesChecked res) ++ " cases:\n" ++ msg ++ unlines failures ++ rest where
|
|
|
|
|
|
|
|
msg = if null (_failureMsg res) then "" else _failureMsg res ++ "\n"
|
2015-10-08 23:50:52 +03:00
|
|
|
|
2016-06-22 13:20:22 +03:00
|
|
|
failures = intersperse "" . map (\(r, t) -> indent $ either Conc.showFail show r ++ " " ++ Conc.showTrace t) . take 5 $ _failures res
|
2015-10-08 23:50:52 +03:00
|
|
|
|
|
|
|
rest = if moreThan (_failures res) 5 then "\n\t..." else ""
|
|
|
|
|
|
|
|
-- | Check if a list has more than some number of elements.
|
|
|
|
moreThan :: [a] -> Int -> Bool
|
|
|
|
moreThan [] n = n < 0
|
|
|
|
moreThan _ 0 = True
|
|
|
|
moreThan (_:xs) n = moreThan xs (n-1)
|
|
|
|
|
2016-02-09 21:37:50 +03:00
|
|
|
-- | Indent every line of a string.
|
|
|
|
indent :: String -> String
|
|
|
|
indent = intercalate "\n" . map ('\t':) . lines
|