{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
module Test.DejaFu.Refinement
(
Sig(..)
, RefinementProperty
, expectFailure
, refines, (=>=)
, strictlyRefines, (->-)
, equivalentTo, (===)
, FailedProperty(..)
, Testable(O,X)
, check
, check'
, checkFor
, counterExamples
, Listable(..)
) where
import Control.Arrow (first)
import Control.Monad.Conc.Class (fork)
import Data.Kind (Type)
import Data.Maybe (isNothing)
import Data.Set (Set)
import qualified Data.Set as S
import Test.LeanCheck (Listable(..), concatMapT, mapT)
import Test.DejaFu.Conc (ConcIO, Condition,
withSetupAndTeardown)
import Test.DejaFu.SCT (runSCT)
import Test.DejaFu.Settings (defaultMemType, defaultWay)
data How = Weak | Equiv | Strict deriving How -> How -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: How -> How -> Bool
$c/= :: How -> How -> Bool
== :: How -> How -> Bool
$c== :: How -> How -> Bool
Eq
data RefinementProperty o x where
RP :: Ord o => How -> Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
Neg :: RefinementProperty o x -> RefinementProperty o x
data Sig s o x = Sig
{ forall s o x. Sig s o x -> x -> ConcIO s
initialise :: x -> ConcIO s
, forall s o x. Sig s o x -> s -> x -> ConcIO o
observe :: s -> x -> ConcIO o
, forall s o x. Sig s o x -> s -> x -> ConcIO ()
interfere :: s -> x -> ConcIO ()
, forall s o x. Sig s o x -> s -> ConcIO ()
expression :: s -> ConcIO ()
}
expectFailure :: RefinementProperty o x -> RefinementProperty o x
expectFailure :: forall o x. RefinementProperty o x -> RefinementProperty o x
expectFailure = forall o x. RefinementProperty o x -> RefinementProperty o x
Neg
refines :: Ord o => Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
refines :: forall o s1 x s2.
Ord o =>
Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
refines = forall o s1 x s2.
Ord o =>
How -> Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
RP How
Weak
(=>=) :: Ord o => Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
=>= :: forall o s1 x s2.
Ord o =>
Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
(=>=) = forall o s1 x s2.
Ord o =>
Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
refines
equivalentTo :: Ord o => Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
equivalentTo :: forall o s1 x s2.
Ord o =>
Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
equivalentTo = forall o s1 x s2.
Ord o =>
How -> Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
RP How
Equiv
(===) :: Ord o => Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
=== :: forall o s1 x s2.
Ord o =>
Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
(===) = forall o s1 x s2.
Ord o =>
Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
equivalentTo
strictlyRefines :: Ord o => Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
strictlyRefines :: forall o s1 x s2.
Ord o =>
Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
strictlyRefines = forall o s1 x s2.
Ord o =>
How -> Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
RP How
Strict
(->-) :: Ord o => Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
->- :: forall o s1 x s2.
Ord o =>
Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
(->-) = forall o s1 x s2.
Ord o =>
Sig s1 o x -> Sig s2 o x -> RefinementProperty o x
strictlyRefines
class Testable a where
type O a :: Type
type X a :: Type
rpropTiers :: a -> [[([String], RefinementProperty (O a) (X a))]]
instance Testable (RefinementProperty o x) where
type O (RefinementProperty o x) = o
type X (RefinementProperty o x) = x
rpropTiers :: RefinementProperty o x
-> [[([String],
RefinementProperty
(O (RefinementProperty o x)) (X (RefinementProperty o x)))]]
rpropTiers RefinementProperty o x
p = [[([], RefinementProperty o x
p)]]
instance (Listable a, Show a, Testable b) => Testable (a -> b) where
type O (a -> b) = O b
type X (a -> b) = X b
rpropTiers :: (a -> b)
-> [[([String], RefinementProperty (O (a -> b)) (X (a -> b)))]]
rpropTiers a -> b
p = forall a b. (a -> [[b]]) -> [[a]] -> [[b]]
concatMapT a -> [[([String], RefinementProperty (O b) (X b))]]
resultiersFor forall a. Listable a => [[a]]
tiers where
resultiersFor :: a -> [[([String], RefinementProperty (O b) (X b))]]
resultiersFor a
x = forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (b, d) (c, d)
first (forall a. Show a => a -> String
show a
xforall a. a -> [a] -> [a]
:) forall a b. (a -> b) -> [[a]] -> [[b]]
`mapT` forall a.
Testable a =>
a -> [[([String], RefinementProperty (O a) (X a))]]
rpropTiers (a -> b
p a
x)
data FailedProperty o x
= CounterExample
{ forall o x. FailedProperty o x -> x
failingSeed :: x
, forall o x. FailedProperty o x -> [String]
failingArgs :: [String]
, forall o x. FailedProperty o x -> Set (Maybe Condition, o)
leftResults :: Set (Maybe Condition, o)
, forall o x. FailedProperty o x -> Set (Maybe Condition, o)
rightResults :: Set (Maybe Condition, o)
}
| NoExpectedFailure
deriving Int -> FailedProperty o x -> ShowS
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall o x. (Show x, Show o) => Int -> FailedProperty o x -> ShowS
forall o x. (Show x, Show o) => [FailedProperty o x] -> ShowS
forall o x. (Show x, Show o) => FailedProperty o x -> String
showList :: [FailedProperty o x] -> ShowS
$cshowList :: forall o x. (Show x, Show o) => [FailedProperty o x] -> ShowS
show :: FailedProperty o x -> String
$cshow :: forall o x. (Show x, Show o) => FailedProperty o x -> String
showsPrec :: Int -> FailedProperty o x -> ShowS
$cshowsPrec :: forall o x. (Show x, Show o) => Int -> FailedProperty o x -> ShowS
Show
check :: (Testable p, Listable (X p), Show (X p), Show (O p))
=> p
-> IO Bool
check :: forall p.
(Testable p, Listable (X p), Show (X p), Show (O p)) =>
p -> IO Bool
check p
p = do
Maybe (FailedProperty (O p) (X p))
ce <- forall p.
(Testable p, Listable (X p)) =>
p -> IO (Maybe (FailedProperty (O p) (X p)))
check' p
p
String -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ case Maybe (FailedProperty (O p) (X p))
ce of
Just FailedProperty (O p) (X p)
NoExpectedFailure -> String
"*** Failure: passed, but expected failure."
Just FailedProperty (O p) (X p)
c -> forall a. [a] -> [a]
init forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines
[ String
"*** Failure: " forall a. [a] -> [a] -> [a]
++
(if forall (t :: * -> *) a. Foldable t => t a -> Bool
null (forall o x. FailedProperty o x -> [String]
failingArgs FailedProperty (O p) (X p)
c) then String
"" else [String] -> String
unwords (forall o x. FailedProperty o x -> [String]
failingArgs FailedProperty (O p) (X p)
c) forall a. [a] -> [a] -> [a]
++ String
" ") forall a. [a] -> [a] -> [a]
++
String
"(seed " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show (forall o x. FailedProperty o x -> x
failingSeed FailedProperty (O p) (X p)
c) forall a. [a] -> [a] -> [a]
++ String
")"
, String
" left: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show (forall a. Set a -> [a]
S.toList forall a b. (a -> b) -> a -> b
$ forall o x. FailedProperty o x -> Set (Maybe Condition, o)
leftResults FailedProperty (O p) (X p)
c)
, String
" right: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show (forall a. Set a -> [a]
S.toList forall a b. (a -> b) -> a -> b
$ forall o x. FailedProperty o x -> Set (Maybe Condition, o)
rightResults FailedProperty (O p) (X p)
c)
]
Maybe (FailedProperty (O p) (X p))
Nothing -> String
"+++ OK"
forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a. Maybe a -> Bool
isNothing Maybe (FailedProperty (O p) (X p))
ce)
check' :: (Testable p, Listable (X p))
=> p
-> IO (Maybe (FailedProperty (O p) (X p)))
check' :: forall p.
(Testable p, Listable (X p)) =>
p -> IO (Maybe (FailedProperty (O p) (X p)))
check' = forall p.
(Testable p, Listable (X p)) =>
Int -> Int -> p -> IO (Maybe (FailedProperty (O p) (X p)))
checkFor Int
10 Int
100
checkFor :: (Testable p, Listable (X p))
=> Int
-> Int
-> p
-> IO (Maybe (FailedProperty (O p) (X p)))
checkFor :: forall p.
(Testable p, Listable (X p)) =>
Int -> Int -> p -> IO (Maybe (FailedProperty (O p) (X p)))
checkFor Int
sn Int
vn p
p = do
let seeds :: [X p]
seeds = forall a. Int -> [a] -> [a]
take Int
sn forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a. Listable a => [[a]]
tiers
let cases :: [([String], RefinementProperty (O p) (X p))]
cases = forall a. Int -> [a] -> [a]
take Int
vn forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (forall a.
Testable a =>
a -> [[([String], RefinementProperty (O a) (X a))]]
rpropTiers p
p)
forall {x} {o}.
[x]
-> [([String], RefinementProperty o x)]
-> IO (Maybe (FailedProperty o x))
go [X p]
seeds [([String], RefinementProperty (O p) (X p))]
cases
where
go :: [x]
-> [([String], RefinementProperty o x)]
-> IO (Maybe (FailedProperty o x))
go [x]
seeds (([String]
vs, RefinementProperty o x
p'):[([String], RefinementProperty o x)]
rest) = do
Maybe ([String] -> FailedProperty o x)
r <- forall x o.
[x]
-> RefinementProperty o x
-> IO (Maybe ([String] -> FailedProperty o x))
checkWithSeeds [x]
seeds RefinementProperty o x
p'
case Maybe ([String] -> FailedProperty o x)
r of
Just [String] -> FailedProperty o x
cf -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a. a -> Maybe a
Just ([String] -> FailedProperty o x
cf [String]
vs))
Maybe ([String] -> FailedProperty o x)
Nothing -> [x]
-> [([String], RefinementProperty o x)]
-> IO (Maybe (FailedProperty o x))
go [x]
seeds [([String], RefinementProperty o x)]
rest
go [x]
_ [] = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
counterExamples :: (Testable p, Listable (X p))
=> Int
-> Int
-> p
-> IO [FailedProperty (O p) (X p)]
counterExamples :: forall p.
(Testable p, Listable (X p)) =>
Int -> Int -> p -> IO [FailedProperty (O p) (X p)]
counterExamples Int
sn Int
vn p
p = do
let seeds :: [X p]
seeds = forall a. Int -> [a] -> [a]
take Int
sn forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a. Listable a => [[a]]
tiers
let cases :: [([String], RefinementProperty (O p) (X p))]
cases = forall a. Int -> [a] -> [a]
take Int
vn forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (forall a.
Testable a =>
a -> [[([String], RefinementProperty (O a) (X a))]]
rpropTiers p
p)
[([String], Maybe ([String] -> FailedProperty (O p) (X p)))]
rs <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (\([String]
vs, RefinementProperty (O p) (X p)
p') -> ([String]
vs,) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall x o.
[x]
-> RefinementProperty o x
-> IO (Maybe ([String] -> FailedProperty o x))
checkWithSeeds [X p]
seeds RefinementProperty (O p) (X p)
p') [([String], RefinementProperty (O p) (X p))]
cases
forall (f :: * -> *) a. Applicative f => a -> f a
pure [ [String] -> FailedProperty (O p) (X p)
cf [String]
vs | ([String]
vs, Just [String] -> FailedProperty (O p) (X p)
cf) <- [([String], Maybe ([String] -> FailedProperty (O p) (X p)))]
rs ]
data F x = Failing x | Unknown | Refuted
checkWithSeeds
:: [x]
-> RefinementProperty o x
-> IO (Maybe ([String] -> FailedProperty o x))
checkWithSeeds :: forall x o.
[x]
-> RefinementProperty o x
-> IO (Maybe ([String] -> FailedProperty o x))
checkWithSeeds [x]
seeds (RP How
how Sig s1 o x
l Sig s2 o x
r) = case How
how of
How
Weak -> (Set (Maybe Condition, o) -> Set (Maybe Condition, o) -> Bool)
-> [x] -> IO (Maybe ([String] -> FailedProperty o x))
go1 forall a. Ord a => Set a -> Set a -> Bool
S.isSubsetOf [x]
seeds
How
Equiv -> (Set (Maybe Condition, o) -> Set (Maybe Condition, o) -> Bool)
-> [x] -> IO (Maybe ([String] -> FailedProperty o x))
go1 forall a. Eq a => a -> a -> Bool
(==) [x]
seeds
How
Strict -> F ([String] -> FailedProperty o x)
-> [x] -> IO (Maybe ([String] -> FailedProperty o x))
go2 forall x. F x
Unknown [x]
seeds
where
go1 :: (Set (Maybe Condition, o) -> Set (Maybe Condition, o) -> Bool)
-> [x] -> IO (Maybe ([String] -> FailedProperty o x))
go1 Set (Maybe Condition, o) -> Set (Maybe Condition, o) -> Bool
f (x
x:[x]
xs) = do
Set (Maybe Condition, o)
lrs <- forall o s x.
Ord o =>
Sig s o x -> x -> IO (Set (Maybe Condition, o))
evalSigWithSeed Sig s1 o x
l x
x
Set (Maybe Condition, o)
rrs <- forall o s x.
Ord o =>
Sig s o x -> x -> IO (Set (Maybe Condition, o))
evalSigWithSeed Sig s2 o x
r x
x
if Set (Maybe Condition, o)
lrs Set (Maybe Condition, o) -> Set (Maybe Condition, o) -> Bool
`f` Set (Maybe Condition, o)
rrs
then (Set (Maybe Condition, o) -> Set (Maybe Condition, o) -> Bool)
-> [x] -> IO (Maybe ([String] -> FailedProperty o x))
go1 Set (Maybe Condition, o) -> Set (Maybe Condition, o) -> Bool
f [x]
xs
else forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall {x} {o}.
x
-> Set (Maybe Condition, o)
-> Set (Maybe Condition, o)
-> [String]
-> FailedProperty o x
toCE x
x Set (Maybe Condition, o)
lrs Set (Maybe Condition, o)
rrs)
go1 Set (Maybe Condition, o) -> Set (Maybe Condition, o) -> Bool
_ [] = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
go2 :: F ([String] -> FailedProperty o x)
-> [x] -> IO (Maybe ([String] -> FailedProperty o x))
go2 F ([String] -> FailedProperty o x)
eq (x
x:[x]
xs) = do
Set (Maybe Condition, o)
lrs <- forall o s x.
Ord o =>
Sig s o x -> x -> IO (Set (Maybe Condition, o))
evalSigWithSeed Sig s1 o x
l x
x
Set (Maybe Condition, o)
rrs <- forall o s x.
Ord o =>
Sig s o x -> x -> IO (Set (Maybe Condition, o))
evalSigWithSeed Sig s2 o x
r x
x
let ce :: [String] -> FailedProperty o x
ce = forall {x} {o}.
x
-> Set (Maybe Condition, o)
-> Set (Maybe Condition, o)
-> [String]
-> FailedProperty o x
toCE x
x Set (Maybe Condition, o)
lrs Set (Maybe Condition, o)
rrs
if | Set (Maybe Condition, o)
lrs forall a. Eq a => a -> a -> Bool
== Set (Maybe Condition, o)
rrs -> F ([String] -> FailedProperty o x)
-> [x] -> IO (Maybe ([String] -> FailedProperty o x))
go2 (case F ([String] -> FailedProperty o x)
eq of F ([String] -> FailedProperty o x)
Unknown -> forall x. x -> F x
Failing [String] -> FailedProperty o x
ce; F ([String] -> FailedProperty o x)
_ -> F ([String] -> FailedProperty o x)
eq) [x]
xs
| Set (Maybe Condition, o)
lrs forall a. Ord a => Set a -> Set a -> Bool
`S.isSubsetOf` Set (Maybe Condition, o)
rrs -> F ([String] -> FailedProperty o x)
-> [x] -> IO (Maybe ([String] -> FailedProperty o x))
go2 forall x. F x
Refuted [x]
xs
| Bool
otherwise -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a. a -> Maybe a
Just [String] -> FailedProperty o x
ce)
go2 (Failing [String] -> FailedProperty o x
cf) [] = forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a. a -> Maybe a
Just [String] -> FailedProperty o x
cf)
go2 F ([String] -> FailedProperty o x)
_ [] = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
toCE :: x
-> Set (Maybe Condition, o)
-> Set (Maybe Condition, o)
-> [String]
-> FailedProperty o x
toCE x
x Set (Maybe Condition, o)
lrs Set (Maybe Condition, o)
rrs [String]
args = CounterExample
{ failingSeed :: x
failingSeed = x
x
, failingArgs :: [String]
failingArgs = [String]
args
, leftResults :: Set (Maybe Condition, o)
leftResults = Set (Maybe Condition, o)
lrs
, rightResults :: Set (Maybe Condition, o)
rightResults = Set (Maybe Condition, o)
rrs
}
checkWithSeeds [x]
seeds (Neg RefinementProperty o x
rp) = do
Maybe ([String] -> FailedProperty o x)
r <- forall x o.
[x]
-> RefinementProperty o x
-> IO (Maybe ([String] -> FailedProperty o x))
checkWithSeeds [x]
seeds RefinementProperty o x
rp
forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ case Maybe ([String] -> FailedProperty o x)
r of
Just [String] -> FailedProperty o x
_ -> forall a. Maybe a
Nothing
Maybe ([String] -> FailedProperty o x)
Nothing -> forall a. a -> Maybe a
Just (forall a b. a -> b -> a
const forall o x. FailedProperty o x
NoExpectedFailure)
evalSigWithSeed :: Ord o
=> Sig s o x
-> x
-> IO (Set (Maybe Condition, o))
evalSigWithSeed :: forall o s x.
Ord o =>
Sig s o x -> x -> IO (Set (Maybe Condition, o))
evalSigWithSeed Sig s o x
sig x
x = do
[(Either Condition (Maybe Condition, o), Trace)]
results <- forall (n :: * -> *) pty a.
MonadDejaFu n =>
Way
-> MemType -> Program pty n a -> n [(Either Condition a, Trace)]
runSCT Way
defaultWay MemType
defaultMemType forall a b. (a -> b) -> a -> b
$
forall (n :: * -> *) x y a.
Program Basic n x
-> (x -> Either Condition y -> Program Basic n a)
-> (x -> Program Basic n y)
-> Program (WithSetupAndTeardown x y) n a
withSetupAndTeardown
(forall s o x. Sig s o x -> x -> ConcIO s
initialise Sig s o x
sig x
x)
(\s
s Either Condition ()
r -> do
o
o <- forall s o x. Sig s o x -> s -> x -> ConcIO o
observe Sig s o x
sig s
s x
x
forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either forall a. a -> Maybe a
Just (forall a b. a -> b -> a
const forall a. Maybe a
Nothing) Either Condition ()
r, o
o))
(\s
s -> do
ThreadId
_ <- forall (m :: * -> *). MonadConc m => m () -> m (ThreadId m)
fork (forall s o x. Sig s o x -> s -> x -> ConcIO ()
interfere Sig s o x
sig s
s x
x)
()
_ <- forall s o x. Sig s o x -> s -> ConcIO ()
expression Sig s o x
sig s
s
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Ord a => [a] -> Set a
S.fromList forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (\(Right (Maybe Condition, o)
a, Trace
_) -> (Maybe Condition, o)
a) [(Either Condition (Maybe Condition, o), Trace)]
results