copilot-theorem-3.18: tests/Test/Copilot/Theorem/What4.hs
-- The following warning is disabled due to a necessary instance of SatResult
-- defined in this module.
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- | Test copilot-theorem:Copilot.Theorem.What4.
module Test.Copilot.Theorem.What4 where
-- External imports
import Data.Int (Int8)
import Test.Framework (Test, testGroup)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Test.QuickCheck (Property, arbitrary, forAll)
import Test.QuickCheck.Monadic (monadicIO, run)
-- External imports: Copilot
import Copilot.Core.Expr (Expr (Const, Op2))
import Copilot.Core.Operators (Op2 (..))
import Copilot.Core.Spec (Spec (..))
import qualified Copilot.Core.Spec as Copilot
import Copilot.Core.Type (Typed (typeOf))
-- Internal imports: Modules being tested
import Copilot.Theorem.What4 (SatResult (..), Solver (..), prove)
-- * Constants
-- | Unit tests for copilot-theorem:Copilot.Theorem.What4.
tests :: Test.Framework.Test
tests =
testGroup "Copilot.Theorem.What4"
[ testProperty "Prove via Z3 that true is valid" testProveZ3True
, testProperty "Prove via Z3 that false is invalid" testProveZ3False
, testProperty "Prove via Z3 that x == x is valid" testProveZ3EqConst
]
-- * Individual tests
-- | Test that Z3 is able to prove the following expression valid:
-- @
-- constant True
-- @
testProveZ3True :: Property
testProveZ3True =
monadicIO $ run $ checkResult Z3 propName spec Valid
where
propName :: String
propName = "prop"
spec :: Spec
spec = propSpec propName $ Const typeOf True
-- | Test that Z3 is able to prove the following expression invalid:
-- @
-- constant False
-- @
testProveZ3False :: Property
testProveZ3False =
monadicIO $ run $ checkResult Z3 propName spec Invalid
where
propName :: String
propName = "prop"
spec :: Spec
spec = propSpec propName $ Const typeOf False
-- | Test that Z3 is able to prove the following expresion valid:
-- @
-- for all (x :: Int8), constant x == constant x
-- @
testProveZ3EqConst :: Property
testProveZ3EqConst = forAll arbitrary $ \x ->
monadicIO $ run $ checkResult Z3 propName (spec x) Valid
where
propName :: String
propName = "prop"
spec :: Int8 -> Spec
spec x = propSpec propName $
Op2 (Eq typeOf) (Const typeOf x) (Const typeOf x)
-- | Check that the solver's satisfiability result for the given property in
-- the given spec matches the expectation.
checkResult :: Solver -> String -> Spec -> SatResult -> IO Bool
checkResult solver propName spec expectation = do
results <- prove solver spec
-- Find the satisfiability result for propName.
let propResult = lookup propName results
-- The following check also works for the case in which the property name
-- does not exist in the results, in which case the lookup returns 'Nothing'.
return $ propResult == Just expectation
-- * Auxiliary
-- | Build a 'Spec' that contains one property with the given name and defined
-- by the given boolean expression.
propSpec :: String -> Expr Bool -> Spec
propSpec propName propExpr = Spec [] [] [] [Copilot.Property propName propExpr]
-- | Equality for 'SatResult'.
--
-- This is an orphan instance, so we suppress the warning that GHC would
-- normally produce with a GHC option at the top.
instance Eq SatResult where
Valid == Valid = True
Invalid == Invalid = True
Unknown == Unknown = True
_ == _ = False