Skip to content

Commit fbfbbd3

Browse files
Merge branch 'develop-test-what4'. Close #474.
**Description** As part of the requirements for Class D (NPR7150.2C), we need unit tests for all of copilot. A natural next step is to introduce the testing infrastructure for `copilot-theorem`. **Type** - Management: conformance with new requirement. **Additional context** None. **Requester** - Ivan Perez **Method to check presence of bug** The following Dockerfile checks that copilot-theorem's tests can be executed, in which case it prints the message Success: ``` FROM ubuntu:focal RUN apt-get update RUN apt-get install --yes libz-dev RUN apt-get install --yes git RUN apt-get install --yes wget RUN mkdir -p $HOME/.ghcup/bin RUN wget https://downloads.haskell.org/~ghcup/0.1.19.2/x86_64-linux-ghcup-0.1.19.2 -O $HOME/.ghcup/bin/ghcup RUN chmod a+x $HOME/.ghcup/bin/ghcup ENV PATH=$PATH:/root/.ghcup/bin/ ENV PATH=$PATH:/root/.cabal/bin/ RUN apt-get install --yes curl RUN apt-get install --yes gcc make libgmp3-dev g++ SHELL ["/bin/bash", "-c"] RUN ghcup install ghc 9.4.7 RUN ghcup install cabal 3.8 RUN ghcup set ghc 9.4.7 RUN cabal update RUN apt-get install --yes z3 CMD git clone $REPO && \ cd $NAME && \ git checkout $COMMIT && \ cd copilot-theorem && \ cabal test copilot-theorem:unit-tests && \ echo Success ``` Command (substitute variables based on new path after merge): ``` $ docker run -e "REPO=https://github.com/Copilot-Language/copilot" -e "NAME=copilot" -e "COMMIT=<HASH>" -it copilot-verify-474 ``` **Expected result** The library `copilot-theorem` includes tests for `what4`. Introducing (some classes of) errors in the implementation of `copilot-theorem` makes the tests detect those errors. Running the docker file above prints the message success, indicating that copilot-theorem now declares a unit-tests component. **Proposed solution** Add tests for `copilot-theorem` that test basic properties whose result is known with one of the existing solver. The tests focus on What4, the Z3 solver, and only 3 properties. The goal is to introduce enough infrastructure that testing the What4 connection more thoroughly should be reduced to adding more properties to the now-existing test module. **Further notes** Due to the magnitude of this change, we decide to simplify the issue in three ways: - Focus only on `what4`, which is likely going to replace other ways of connecting to SMT solvers provided by `copilot-theorem`. - Focus only on one of the SMT solvers. - Focus only on the core of the infrastructure, leaving it to a future issue to have a more comprehensive list of properties.
2 parents 4620006 + e717ae2 commit fbfbbd3

File tree

6 files changed

+169
-1
lines changed

6 files changed

+169
-1
lines changed

.travis.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ before_install:
1515
- travis_retry sudo add-apt-repository -y ppa:hvr/ghc
1616
- travis_retry sudo apt-get update
1717
- travis_retry sudo apt-get install cabal-install-$CABALVER ghc-$GHCVER # see note about happy/alex
18+
19+
# We install z3 only for the tests, since it is not needed for normal
20+
# compilation.
21+
- if [ "${GHCVER}" == "8.10.4" ]; then travis_retry sudo apt-get install --yes z3; fi
22+
1823
- export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
1924
- cabal --version
2025
- echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
@@ -27,4 +32,7 @@ script:
2732
# Run tests only on GHC 8.10.4
2833
#
2934
# Only libraries with tests are listed below or the v2-test command fails.
30-
- if [ "${GHCVER}" == "8.10.4" ]; then cabal v2-test -j1 copilot-core copilot-language copilot-interpreter copilot-c99; fi
35+
#
36+
# Testing copilot-theorem requires z3. See above conditional installation,
37+
# and keep GHC version numbers in both places in sync.
38+
- if [ "${GHCVER}" == "8.10.4" ]; then cabal v2-test -j1 copilot-core copilot-language copilot-interpreter copilot-c99 copilot-theorem; fi

copilot-theorem/CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2023-12-18
2+
* Introduce testing infrastructure for Copilot.Theorem.What4. (#474)
3+
14
2023-11-07
25
* Version bump (3.17). (#466)
36
* Relax version constraint on what4. (#461)

copilot-theorem/copilot-theorem.cabal

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,31 @@ library
108108
, Copilot.Theorem.TransSys.Type
109109

110110
, Copilot.Theorem.What4.Translate
111+
112+
test-suite unit-tests
113+
type:
114+
exitcode-stdio-1.0
115+
116+
main-is:
117+
Main.hs
118+
119+
other-modules:
120+
Test.Copilot.Theorem.What4
121+
122+
build-depends:
123+
base
124+
, QuickCheck
125+
, test-framework
126+
, test-framework-quickcheck2
127+
128+
, copilot-core
129+
, copilot-theorem
130+
131+
hs-source-dirs:
132+
tests
133+
134+
default-language:
135+
Haskell2010
136+
137+
ghc-options:
138+
-Wall

copilot-theorem/tests/Main.hs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- | Test copilot-theorem.
2+
module Main where
3+
4+
-- External imports
5+
import Test.Framework (Test, defaultMain)
6+
7+
-- Internal imports
8+
import qualified Test.Copilot.Theorem.What4
9+
10+
-- | Run all unit tests on copilot-theorem.
11+
main :: IO ()
12+
main = defaultMain tests
13+
14+
-- | All unit tests in copilot-theorem.
15+
tests :: [Test.Framework.Test]
16+
tests =
17+
[ Test.Copilot.Theorem.What4.tests
18+
]
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
-- The following warning is disabled due to a necessary instance of SatResult
2+
-- defined in this module.
3+
{-# OPTIONS_GHC -fno-warn-orphans #-}
4+
-- | Test copilot-theorem:Copilot.Theorem.What4.
5+
module Test.Copilot.Theorem.What4 where
6+
7+
-- External imports
8+
import Data.Int (Int8)
9+
import Test.Framework (Test, testGroup)
10+
import Test.Framework.Providers.QuickCheck2 (testProperty)
11+
import Test.QuickCheck (Property, arbitrary, forAll)
12+
import Test.QuickCheck.Monadic (monadicIO, run)
13+
14+
-- External imports: Copilot
15+
import Copilot.Core.Expr (Expr (Const, Op2))
16+
import Copilot.Core.Operators (Op2 (..))
17+
import Copilot.Core.Spec (Spec (..))
18+
import qualified Copilot.Core.Spec as Copilot
19+
import Copilot.Core.Type (Typed (typeOf))
20+
21+
-- Internal imports: Modules being tested
22+
import Copilot.Theorem.What4 (SatResult (..), Solver (..), prove)
23+
24+
-- * Constants
25+
26+
-- | Unit tests for copilot-theorem:Copilot.Theorem.What4.
27+
tests :: Test.Framework.Test
28+
tests =
29+
testGroup "Copilot.Theorem.What4"
30+
[ testProperty "Prove via Z3 that true is valid" testProveZ3True
31+
, testProperty "Prove via Z3 that false is invalid" testProveZ3False
32+
, testProperty "Prove via Z3 that x == x is valid" testProveZ3EqConst
33+
]
34+
35+
-- * Individual tests
36+
37+
-- | Test that Z3 is able to prove the following expression valid:
38+
-- @
39+
-- constant True
40+
-- @
41+
testProveZ3True :: Property
42+
testProveZ3True =
43+
monadicIO $ run $ checkResult Z3 propName spec Valid
44+
where
45+
propName :: String
46+
propName = "prop"
47+
48+
spec :: Spec
49+
spec = propSpec propName $ Const typeOf True
50+
51+
-- | Test that Z3 is able to prove the following expression invalid:
52+
-- @
53+
-- constant False
54+
-- @
55+
testProveZ3False :: Property
56+
testProveZ3False =
57+
monadicIO $ run $ checkResult Z3 propName spec Invalid
58+
where
59+
propName :: String
60+
propName = "prop"
61+
62+
spec :: Spec
63+
spec = propSpec propName $ Const typeOf False
64+
65+
-- | Test that Z3 is able to prove the following expresion valid:
66+
-- @
67+
-- for all (x :: Int8), constant x == constant x
68+
-- @
69+
testProveZ3EqConst :: Property
70+
testProveZ3EqConst = forAll arbitrary $ \x ->
71+
monadicIO $ run $ checkResult Z3 propName (spec x) Valid
72+
where
73+
propName :: String
74+
propName = "prop"
75+
76+
spec :: Int8 -> Spec
77+
spec x = propSpec propName $
78+
Op2 (Eq typeOf) (Const typeOf x) (Const typeOf x)
79+
80+
-- | Check that the solver's satisfiability result for the given property in
81+
-- the given spec matches the expectation.
82+
checkResult :: Solver -> String -> Spec -> SatResult -> IO Bool
83+
checkResult solver propName spec expectation = do
84+
results <- prove solver spec
85+
86+
-- Find the satisfiability result for propName.
87+
let propResult = lookup propName results
88+
89+
-- The following check also works for the case in which the property name
90+
-- does not exist in the results, in which case the lookup returns 'Nothing'.
91+
return $ propResult == Just expectation
92+
93+
-- * Auxiliary
94+
95+
-- | Build a 'Spec' that contains one property with the given name and defined
96+
-- by the given boolean expression.
97+
propSpec :: String -> Expr Bool -> Spec
98+
propSpec propName propExpr = Spec [] [] [] [Copilot.Property propName propExpr]
99+
100+
-- | Equality for 'SatResult'.
101+
--
102+
-- This is an orphan instance, so we suppress the warning that GHC would
103+
-- normally produce with a GHC option at the top.
104+
instance Eq SatResult where
105+
Valid == Valid = True
106+
Invalid == Invalid = True
107+
Unknown == Unknown = True
108+
_ == _ = False

copilot/CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2023-12-18
2+
* Enable tests for copilot-theorem in CI script. (#474)
3+
14
2023-11-07
25
* Version bump (3.17). (#466)
36
* Replace uses of deprecated functions. (#457)

0 commit comments

Comments
 (0)