Skip to content

Commit a845c2f

Browse files
committed
Add a basic ShouldFail test
This adds a basic test that is expected to fail due to not asserting a necessary precondition. We check that such tests fail in the test suite by way of the `tasty-expected-failure` package. Fixes #13.
1 parent 39b9e2b commit a845c2f

5 files changed

Lines changed: 65 additions & 10 deletions

File tree

copilot-verifier/copilot-verifier.cabal

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ library copilot-verifier-examples
6767
copilot-verifier
6868
exposed-modules:
6969
Copilot.Verifier.Examples
70+
71+
Copilot.Verifier.Examples.ShouldFail.UnderConstrained
72+
7073
Copilot.Verifier.Examples.ShouldPass.Array
7174
Copilot.Verifier.Examples.ShouldPass.Arith
7275
Copilot.Verifier.Examples.ShouldPass.Clock
@@ -99,6 +102,8 @@ test-suite copilot-verifier-test
99102
case-insensitive,
100103
copilot-verifier,
101104
copilot-verifier-examples,
105+
silently >= 1.2,
102106
tasty >= 0.10,
107+
tasty-expected-failure >= 0.12,
103108
tasty-hunit >= 0.10
104109
main-is: Test.hs

copilot-verifier/examples/Copilot/Verifier/Examples.hs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{-# LANGUAGE OverloadedStrings #-}
2-
module Copilot.Verifier.Examples (shouldPassExamples) where
2+
module Copilot.Verifier.Examples
3+
( shouldFailExamples
4+
, shouldPassExamples
5+
) where
36

47
import qualified Data.CaseInsensitive as CI
58
import Data.CaseInsensitive (CI)
@@ -8,6 +11,7 @@ import Data.Map (Map)
811
import Data.Text (Text)
912

1013
import Copilot.Verifier (Verbosity)
14+
import qualified Copilot.Verifier.Examples.ShouldFail.UnderConstrained as UnderConstrained
1115
import qualified Copilot.Verifier.Examples.ShouldPass.Array as Array
1216
import qualified Copilot.Verifier.Examples.ShouldPass.Arith as Arith
1317
import qualified Copilot.Verifier.Examples.ShouldPass.Clock as Clock
@@ -20,6 +24,11 @@ import qualified Copilot.Verifier.Examples.ShouldPass.Structs as Structs
2024
import qualified Copilot.Verifier.Examples.ShouldPass.Voting as Voting
2125
import qualified Copilot.Verifier.Examples.ShouldPass.WCV as WCV
2226

27+
shouldFailExamples :: Verbosity -> Map (CI Text) (IO ())
28+
shouldFailExamples verb = Map.fromList
29+
[ example "UnderConstrained" (UnderConstrained.verifySpec verb)
30+
]
31+
2332
shouldPassExamples :: Verbosity -> Map (CI Text) (IO ())
2433
shouldPassExamples verb = Map.fromList
2534
[ example "Array" (Array.verifySpec verb)
@@ -34,6 +43,6 @@ shouldPassExamples verb = Map.fromList
3443
, example "Voting" (Voting.verifySpec verb)
3544
, example "WCV" (WCV.verifySpec verb)
3645
]
37-
where
38-
example :: Text -> IO () -> (CI Text, IO ())
39-
example name action = (CI.mk name, action)
46+
47+
example :: Text -> IO () -> (CI Text, IO ())
48+
example name action = (CI.mk name, action)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{-# LANGUAGE NoImplicitPrelude #-}
2+
3+
-- | This will fail to verify since the verification does not assume the
4+
-- @nonzero@ property, which is needed to prevent a division-by-zero error.
5+
module Copilot.Verifier.Examples.ShouldFail.UnderConstrained where
6+
7+
import Language.Copilot
8+
import Copilot.Compile.C99
9+
import Copilot.Verifier (Verbosity, verifyWithVerbosity)
10+
11+
spec :: Spec
12+
spec = do
13+
let stream :: Stream Int16
14+
stream = extern "stream" Nothing
15+
16+
_ <- prop "nonzero" (forall (stream /= 0))
17+
trigger "streamDiv" ((stream `div` stream) == 1) [arg stream]
18+
19+
verifySpec :: Verbosity -> IO ()
20+
verifySpec verb = do
21+
spec' <- reify spec
22+
verifyWithVerbosity verb mkDefaultCSettings
23+
-- ["nonzero"]
24+
[]
25+
"underConstrained" spec'

copilot-verifier/exe/VerifyExamples.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Data.Traversable (for)
1313
import Options.Applicative
1414

1515
import Copilot.Verifier (Verbosity(..))
16-
import Copilot.Verifier.Examples (shouldPassExamples)
16+
import Copilot.Verifier.Examples (shouldFailExamples, shouldPassExamples)
1717

1818
newtype Options = Options
1919
{ examples :: [CI Text]
@@ -39,7 +39,7 @@ verifyExamples :: Options -> IO ()
3939
verifyExamples Options{examples} = do
4040
-- Check that all requested examples exist
4141
examplesWithMain <- for examples $ \example ->
42-
case Map.lookup example (shouldPassExamples Noisy) of
42+
case Map.lookup example (shouldFailExamples Noisy `Map.union` shouldPassExamples Noisy) of
4343
Just m -> pure (example, m)
4444
Nothing -> fail $ "No example named " ++ Text.unpack (CI.original example)
4545

copilot-verifier/test/Test.hs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,30 @@ module Main (main) where
33
import qualified Data.CaseInsensitive as CI
44
import qualified Data.Map as Map
55
import qualified Data.Text as Text
6+
import System.IO (stderr, stdout)
7+
import System.IO.Silently (hSilence)
68
import Test.Tasty
9+
import Test.Tasty.ExpectedFailure
710
import Test.Tasty.HUnit
811

912
import Copilot.Verifier (Verbosity(..))
10-
import Copilot.Verifier.Examples (shouldPassExamples)
13+
import Copilot.Verifier.Examples (shouldFailExamples, shouldPassExamples)
1114

1215
main :: IO ()
1316
main = defaultMain $
14-
testGroup "copilot-verifier-examples tests" $
15-
map (\(name, action) -> testCase (Text.unpack (CI.original name)) action)
16-
(Map.toAscList (shouldPassExamples Quiet))
17+
testGroup "copilot-verifier-examples tests"
18+
[ testGroup "should-fail tests" $
19+
-- Why use hSilence for the should-fail tests when we are passing
20+
-- Quiet? It's because crux-llvm errors are logged at the highest
21+
-- severity possible, and even Crux's quietMode isn't enough to
22+
-- suppress those messages. We could try messing with things on the
23+
-- Crux side to avoid this, but it's simpler just to use hSilence here.
24+
-- After all, we don't really care about the output of failing tests
25+
-- anyway, just their exit codes.
26+
map (\(name, action) -> expectFail (testCase (Text.unpack (CI.original name))
27+
(hSilence [stderr, stdout] action)))
28+
(Map.toAscList (shouldFailExamples Quiet))
29+
, testGroup "should-pass tests" $
30+
map (\(name, action) -> testCase (Text.unpack (CI.original name)) action)
31+
(Map.toAscList (shouldPassExamples Quiet))
32+
]

0 commit comments

Comments
 (0)