constrained-generators-0.2.0.0: examples/Constrained/Examples/Basic.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ViewPatterns #-}
module Constrained.Examples.Basic where
import Constrained.API
import GHC.Generics
import Test.QuickCheck qualified as QC
leqPair :: Specification (Int, Int)
leqPair = constrained $ \ [var| p |] ->
match p $ \ [var| x |] [var| y |] ->
x <=. y
simplePairSpec :: Specification (Int, Int)
simplePairSpec = constrained $ \(name "p" -> p) ->
match p $ \(name "x" -> x) y ->
[ assert $ x /=. 0
, assert $ name "y" y /=. 0
, -- You can use `monitor` to add QuickCheck property modifiers for
-- monitoring distribution, like classify, label, and cover, to your
-- specification
monitor $ \eval ->
QC.classify (eval y > 0) "positive y"
. QC.classify (eval x > 0) "positive x"
]
sizeAddOrSub1 :: Specification Integer
sizeAddOrSub1 = constrained $ \s ->
4 ==. s + 2
sizeAddOrSub2 :: Specification Integer
sizeAddOrSub2 = constrained $ \s ->
4 ==. 2 + s
sizeAddOrSub3 :: Specification Integer
sizeAddOrSub3 = constrained $ \s ->
4 ==. s - 2
-- | We expect a negative Integer, so ltSpec tests for that.
sizeAddOrSub4 :: Specification Integer
sizeAddOrSub4 = ltSpec 0 <> (constrained $ \s -> 4 ==. 2 - s)
sizeAddOrSub5 :: Specification Integer
sizeAddOrSub5 = constrained $ \s ->
2 ==. 12 - s
listSubSize :: Specification [Int]
listSubSize = constrained $ \s ->
2 ==. 12 - (sizeOf_ s)
orPair :: Specification (Int, Int)
orPair = constrained' $ \x y ->
x <=. 5 ||. y <=. 5
trickyCompositional :: Specification (Int, Int)
trickyCompositional = constrained $ \p ->
satisfies p simplePairSpec <> assert (fst_ p ==. 1000)
data Foo = Foo Int | Bar Int Int
deriving (Show, Eq, Ord, Generic)
instance HasSimpleRep Foo
instance HasSpec Foo
fooSpec :: Specification Foo
fooSpec = constrained $ \foo ->
(caseOn foo)
( branch $ \i ->
[ assert $ 0 <=. i
, monitor $ \_ -> QC.cover 40 True "Foo"
]
)
( branch $ \i j ->
[ assert $ i <=. j
, monitor $ \_ -> QC.cover 40 True "Bar"
]
)
intSpec :: Specification (Int, Int)
intSpec = constrained' $ \a b ->
reify a (`mod` 10) $ \a' -> b ==. a'
mapElemKeySpec :: Specification Int
mapElemKeySpec = constrained $ \n ->
letBind (pair_ n $ lit (False, 4)) $ \(p :: Term (Int, (Bool, Int))) ->
letBind (snd_ (snd_ p)) $ \x ->
[x <. 10, 0 <. x, not_ $ elem_ n $ lit []]
intRangeSpec :: Int -> Specification Int
intRangeSpec a = constrained $ \n -> n <. lit a
testRewriteSpec :: Specification ((Int, Int), (Int, Int))
testRewriteSpec = constrained' $ \x y ->
x ==. fromGeneric_ (toGeneric_ y)
pairSingletonSpec :: Specification (Int, Int)
pairSingletonSpec = constrained $ \q ->
forAll (singleton_ q) $ \p ->
letBind (fst_ p) $ \x ->
letBind (snd_ p) $ \y ->
x <=. y
parallelLet :: Specification (Int, Int)
parallelLet = constrained $ \p ->
[ letBind (fst_ p) $ \x -> 0 <. x
, letBind (snd_ p) $ \x -> x <. 0
]
letExists :: Specification (Int, Int)
letExists = constrained $ \p ->
[ letBind (fst_ p) $ \x -> 0 <. x
, exists (\eval -> pure $ snd (eval p)) $
\x ->
[ x <. 0
, snd_ p ==. x
]
]
letExistsLet :: Specification (Int, Int)
letExistsLet = constrained $ \p ->
[ letBind (fst_ p) $ \x -> 0 <. x
, exists (\eval -> pure $ snd (eval p)) $
\x ->
[ assert $ x <. 0
, letBind (snd_ p) $ \y ->
[ x ==. y
, y <. -1
]
]
]
dependencyWeirdness :: Specification (Int, Int, Int)
dependencyWeirdness = constrained' $ \x y z ->
reify (x + y) id $ \zv -> z ==. zv
parallelLetPair :: Specification (Int, Int)
parallelLetPair = constrained $ \p ->
[ match p $ \x y ->
[ assert $ x <=. y
, y `dependsOn` x
]
, match p $ \x y -> y <=. x
]
existsUnfree :: Specification Int
existsUnfree = constrained $ \_ -> exists (\_ -> pure 1) $ \y -> y `elem_` lit [1, 2 :: Int]
reifyYucky :: Specification (Int, Int, Int)
reifyYucky = constrained' $ \x y z ->
[ reify x id $ \w ->
[ y ==. w
, z ==. w
]
, z `dependsOn` y
]
basicSpec :: Specification Int
basicSpec = constrained $ \x ->
exists (\eval -> pure $ eval x) $ \y ->
satisfies x $ constrained $ \x' ->
x' <=. 1 + y
canFollowLike :: Specification ((Int, Int), (Int, Int))
canFollowLike = constrained' $ \p q ->
match p $ \ma mi ->
match q $ \ma' mi' ->
[ ifElse
(ma' ==. ma)
(mi' ==. mi + 1)
(mi' ==. 0)
, assert $ ma' <=. ma + 1
, assert $ ma <=. ma'
, ma' `dependsOn` ma
]
ifElseBackwards :: Specification (Int, Int)
ifElseBackwards = constrained' $ \p q ->
[ ifElse
(p ==. 1)
(q <=. 0)
(0 <. q)
, p `dependsOn` q
]
assertReal :: Specification Int
assertReal = constrained $ \x ->
[ assert $ x <=. 10
, assertReified x (<= 10)
]
assertRealMultiple :: Specification (Int, Int)
assertRealMultiple = constrained' $ \x y ->
[ assert $ x <=. 10
, assert $ 11 <=. y
, assertReified (pair_ x y) $ uncurry (/=)
]
reifiesMultiple :: Specification (Int, Int, Int)
reifiesMultiple = constrained' $ \x y z ->
[ reifies (x + y) z id
, x `dependsOn` y
]
data Three = One | Two | Three deriving (Ord, Eq, Show, Generic)
instance HasSimpleRep Three
instance HasSpec Three
trueSpecUniform :: Specification Three
trueSpecUniform = constrained $ \o -> monitor $ \eval -> QC.cover 30 True (show $ eval o)
three :: Specification Three
three = constrained $ \o ->
[ caseOn
o
(branchW 1 $ \_ -> True)
(branchW 1 $ \_ -> True)
(branchW 1 $ \_ -> True)
, monitor $ \eval -> QC.cover 30 True (show $ eval o)
]
three' :: Specification Three
three' = three <> three
threeSpecific :: Specification Three
threeSpecific = constrained $ \o ->
[ caseOn
o
(branchW 1 $ \_ -> True)
(branchW 1 $ \_ -> True)
(branchW 2 $ \_ -> True)
, monitor $ \eval ->
QC.coverTable "TheValue" [("One", 22), ("Two", 22), ("Three", 47)]
. QC.tabulate "TheValue" [show $ eval o]
]
threeSpecific' :: Specification Three
threeSpecific' = threeSpecific <> threeSpecific
posNegDistr :: Specification Int
posNegDistr =
constrained $ \x ->
[ monitor $ \eval -> QC.cover 60 (0 < eval x) "x positive"
, x `satisfies` chooseSpec (1, constrained (<. 0)) (2, constrained (0 <.))
]
ifElseMany :: Specification (Bool, Int, Int)
ifElseMany = constrained' $ \b x y ->
ifElse
b
[ x <. 0
, y <. 10
]
[ 0 <. x
, 10 <. y
]
propBack :: Specification (Int, Int)
propBack = constrained' $ \x y ->
[ x ==. y + 10
, x <. 20
, 8 <. y
]
propBack' :: Specification (Int, Int)
propBack' = constrained' $ \x y ->
[ y ==. x - 10
, 20 >. x
, 8 >. y
, y >. x - 20
]
propBack'' :: Specification (Int, Int)
propBack'' = constrained' $ \x y ->
[ assert $ y + 10 ==. x
, x `dependsOn` y
, assert $ x <. 20
, assert $ 8 <. y
]
chooseBackwards :: Specification (Int, [Int])
chooseBackwards = constrained $ \xy ->
[ assert $ xy `elem_` lit [(1, [1001 .. 1005]), (2, [1006 .. 1010])]
, match xy $ \_ ys ->
forAll ys $ \y -> 0 <. y
]
chooseBackwards' :: Specification ([(Int, [Int])], (Int, [Int]))
chooseBackwards' = constrained' $ \ [var| xys |] [var| xy |] ->
[ forAll' xys $ \_ [var| ys |] ->
forAll ys $ \ [var| y |] -> 1000 <. y
, assert $ 0 <. length_ xys
, assert $ xy `elem_` xys
, match xy $ \_ [var| ys |] ->
forAll ys $ \ [var| y |] -> 0 <. y
]
whenTrueExists :: Specification Int
whenTrueExists = constrained $ \x ->
whenTrue ([var| x |] ==. 0) $
exists (\_ -> pure False) $ \b ->
[ not_ [var| b |]
, not_ (not_ b)
]
wtfSpec :: Specification ([Int], Maybe ((), [Int]))
wtfSpec = constrained' $ \ [var| options |] [var| mpair |] ->
caseOn
mpair
(branch $ \_ -> False)
( branch $ \pair -> match pair $ \unit ints ->
[ forAll ints $ \int -> reify options id $ \xs -> int `elem_` xs
, assert $ unit ==. lit ()
]
)
manyInconsistent :: Specification (Int, Int, Int, Int, Int, Int)
manyInconsistent = constrained' $ \ [var| a |] b c d e [var| f |] ->
[ assert $ a <. 10
, assert $ b >. a
, assert $ c >. b
, assert $ d >. c
, assert $ e >. d
, f `dependsOn` e
, assert $ f >. 10
, assert $ f <. a
]
manyInconsistentTrans :: Specification (Int, Int, Int, Int, Int, Int)
manyInconsistentTrans = constrained' $ \ [var| a |] [var| b |] c d e [var| f |] ->
[ assert $ a <. 10
, assert $ b <. a
, assert $ c >. b
, assert $ d >. c
, assert $ e >. d
, f `dependsOn` e
, assert $ f >. 10
, assert $ f <. b
]
complicatedEither :: Specification (Either Int Int, (Either Int Int, Int, Int))
complicatedEither = constrained' $ \ [var| i |] [var| t |] ->
[ caseOn
i
(branch $ \a -> a `elem_` lit [1 .. 10])
(branch $ \b -> b `elem_` lit [1 .. 10])
, match t $ \ [var| k |] _ _ ->
[ k ==. i
, not_ $ k `elem_` lit [Left j | j <- [1 .. 9]]
]
]
pairCant :: Specification (Int, (Int, Int))
pairCant = constrained' $ \ [var| i |] [var| p |] ->
[ assert $ i `elem_` lit [1 .. 10]
, match p $ \ [var| k |] _ ->
[ k ==. i
, not_ $ k `elem_` lit [1 .. 9]
]
]
signumPositive :: Specification Rational
signumPositive = constrained $ \x -> signum (x * 30) >=. 1
twiceChooseSpec :: Specification Bool
twiceChooseSpec =
chooseSpec (1, notEqualSpec True) (3, notEqualSpec False)
<> chooseSpec (1, notEqualSpec True) (3, notEqualSpec False)
twiceChooseSpecInt :: Specification Int
twiceChooseSpecInt =
chooseSpec (1, leqSpec 1) (3, gtSpec 1)
<> chooseSpec (1, leqSpec 1) (3, gtSpec 1)