crucible-0.9: test/helpers/Main.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
module Main (main) where
import Control.Lens ((^.))
import Data.List (isInfixOf)
import Data.Maybe (fromMaybe)
import Test.Hspec
import Test.Tasty
import Test.Tasty.Hspec (testSpec)
import qualified Test.Tasty.HUnit as TTH
import Data.Parameterized.Nonce (newIONonceGenerator)
import Data.Parameterized.Some (Some(Some))
import Lang.Crucible.Backend (SomeBackend(..))
import qualified Lang.Crucible.Backend as LCB
import Lang.Crucible.Backend.Simple (newSimpleBackend)
import Lang.Crucible.Panic
import Lang.Crucible.Simulator.SimError (SimErrorReason(GenericSimError))
import What4.Expr (EmptyExprBuilderState(..))
import What4.Expr.Builder (newExprBuilder)
import qualified What4.Interface as W4I
import What4.FloatMode (FloatModeRepr(FloatIEEERepr))
import qualified What4.LabeledPred as W4L
import qualified What4.ProgramLoc as W4P
import qualified Panic as P
import qualified SymSequence as S
main :: IO ()
main = do
p <- panicTests
defaultMain (testGroup "crucible" [p, backendTests, S.tests])
mkBackend :: IO (Some SomeBackend)
mkBackend = do
Some nonceGen <- newIONonceGenerator
sym <- newExprBuilder FloatIEEERepr EmptyExprBuilderState nonceGen
Some . SomeBackend <$> newSimpleBackend sym
assumePred ::
LCB.IsSymBackend sym bak =>
bak ->
String ->
W4I.Pred sym ->
IO ()
assumePred bak msg p =
LCB.addAssumption bak (LCB.GenericAssumption W4P.initializationLoc msg p)
backendTests :: TestTree
backendTests =
testGroup
"Backend"
[ -- When popping an empty frame, the returned assumptions are just @True@
TTH.testCase "push/pop nothing" $ do
Some (SomeBackend bak) <- mkBackend
asmps <- LCB.popAssumptionFrame bak =<< LCB.pushAssumptionFrame bak
p <- LCB.assumptionsPred (LCB.backendGetSym bak) asmps
Just True TTH.@=? W4I.asConstantPred p
-- When popping a frame, the returned assumptions are the ones that were
-- assumed in that frame
, TTH.testCase "push/pop assumptions" $ do
Some (SomeBackend bak) <- mkBackend
let sym = LCB.backendGetSym bak
a <- W4I.freshConstant sym (W4I.safeSymbol "a") W4I.BaseBoolRepr
assumePred bak "assuming a" a
frm <- LCB.pushAssumptionFrame bak
b <- W4I.freshConstant sym (W4I.safeSymbol "b") W4I.BaseBoolRepr
assumePred bak "assuming b" b
p <- LCB.assumptionsPred sym =<< LCB.popAssumptionFrame bak frm
pEqB <- W4I.eqPred sym p b
Just True TTH.@=? W4I.asConstantPred pEqB
-- When popping a frame, the returned obligations are the ones that were
-- asserted in that frame.
, TTH.testCase "push/pop obligations" $ do
Some (SomeBackend bak) <- mkBackend
let sym = LCB.backendGetSym bak
a <- W4I.freshConstant sym (W4I.safeSymbol "a") W4I.BaseBoolRepr
assumePred bak "assuming a" a
b <- W4I.freshConstant sym (W4I.safeSymbol "b") W4I.BaseBoolRepr
LCB.assert bak b (GenericSimError "asserting b")
frm <- LCB.pushAssumptionFrame bak
c <- W4I.freshConstant sym (W4I.safeSymbol "c") W4I.BaseBoolRepr
assumePred bak "assuming c" c
d <- W4I.freshConstant sym (W4I.safeSymbol "d") W4I.BaseBoolRepr
LCB.assert bak d (GenericSimError "asserting d")
(_asmps, mbGoals) <- LCB.popAssumptionFrameAndObligations bak frm
[LCB.ProofGoal asmps gl] <- pure (fromMaybe [] (LCB.goalsToList <$> mbGoals))
asmpsPred <- LCB.assumptionsPred sym asmps
asmpsEqC <- W4I.eqPred sym c asmpsPred
Just True TTH.@=? W4I.asConstantPred asmpsEqC
goalEqD <- W4I.eqPred sym (gl ^. W4L.labeledPred) d
Just True TTH.@=? W4I.asConstantPred goalEqD
]
panicTests :: IO TestTree
panicTests =
do t <- testSpec "Panicking throws an exception" $
describe "panic" $
it "should throw an exception with the right details" $
shouldThrow (panic "Oh no!" ["line 1", "line 2"]) acceptableExn
pure $ testGroup "panic" [ t ]
where
acceptableExn :: P.Panic Crucible -> Bool
acceptableExn e =
let exnMessage = show e
in isInfixOf "Crucible" exnMessage &&
isInfixOf "github.com/GaloisInc/crucible/issues" exnMessage