packages feed

toysolver-0.7.0: test/Test/Smtlib.hs

{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module Test.Smtlib (smtlibTestGroup) where

import Control.DeepSeq
import Control.Monad
import qualified Data.Set as Set

import Test.Tasty
import Test.Tasty.QuickCheck hiding (Success)
import Test.Tasty.HUnit
import Test.Tasty.TH

import Smtlib.Syntax.Syntax
import Smtlib.Syntax.ShowSL
import Smtlib.Parsers.CommonParsers
import Smtlib.Parsers.CommandsParsers
import Smtlib.Parsers.ResponseParsers
import Text.Parsec (parse)


prop_parseTerm :: Property
prop_parseTerm = forAll arbitrary $ \(t :: Term) ->
  parse parseTerm "" (showSL t) == Right t

prop_parseAttribute :: Property
prop_parseAttribute = forAll arbitrary $ \a ->
  parse parseAttribute "" (showSL a) == Right a

prop_parseSort :: Property
prop_parseSort = forAll arbitrary $ \s ->
  parse parseSort "" (showSL s) == Right s

prop_parseIdentifier :: Property
prop_parseIdentifier = forAll arbitrary $ \a ->
  parse parseIdentifier "" (showSL a) == Right a

prop_parseSexpr :: Property
prop_parseSexpr = forAll arbitrary $ \a ->
  parse parseSexpr "" (showSL a) == Right a

prop_parseSpecConstant :: Property
prop_parseSpecConstant = forAll arbitrary $ \a ->
  parse parseSpecConstant "" (showSL a) == Right a

prop_parseSource :: Property
prop_parseSource = forAll arbitrary $ \a ->
  parse parseSource "" (joinA a) == Right a

prop_parseCommand :: Property
prop_parseCommand = forAll arbitrary $ \a ->
  parse parseCommand "" (showSL a) == Right a

prop_parseOption :: Property
prop_parseOption = forAll arbitrary $ \a ->
  parse parseOption "" (showSL a) == Right a

prop_parseInfoFlags :: Property
prop_parseInfoFlags = forAll arbitrary $ \a ->
  parse parseOption "" (showSL a) == Right a

prop_parseCmdResult :: Property
prop_parseCmdResult = forAll arbitrary $ \(a :: CmdResponse) ->
  case parse parseCmdResult "" (showSL a) of
    Left err -> error (show err)
    Right a' -> parse parseCmdResult "" (showSL a') == Right a'

prop_parseGenResponse :: Property
prop_parseGenResponse = forAll arbitrary $ \a ->
  parse parseGenResponse "" (showSL a) == Right a

prop_parseGetInfoResponse :: Property
prop_parseGetInfoResponse = forAll arbitrary $ \a ->
  parse parseGetInfoResponse "" ("(" ++ joinA a ++ ")") == Right a

prop_parseCheckSatResponse :: Property
prop_parseCheckSatResponse = forAll arbitrary $ \a ->
  parse parseCheckSatResponse "" (showSL a) == Right a

prop_parseGetAssertionsResponse :: Property
prop_parseGetAssertionsResponse = forAll genGetAssertionsResponse $ \a ->
  parse parseGetAssertionsResponse "" ("(" ++ joinA a ++ ")") == Right a

prop_parseGetAssignmentResp :: Property
prop_parseGetAssignmentResp = forAll arbitrary $ \a ->
  parse parseGetAssignmentResp "" ("(" ++ joinA a ++ ")") == Right a

prop_parseGetProofResponse :: Property
prop_parseGetProofResponse = forAll arbitrary $ \a ->
  parse parseGetProofResponse "" (showSL a) == Right a

prop_parseGetUnsatCoreResp :: Property
prop_parseGetUnsatCoreResp = forAll genGetUnsatCoreResponse $ \a ->
  parse parseGetUnsatCoreResp "" ("(" ++ unwords (fmap showSymbol a) ++ ")") == Right a

prop_parseGetUnsatAssumptionsResp :: Property
prop_parseGetUnsatAssumptionsResp = forAll (listOf' genSymbol) $ \a ->
  parse parseGetUnsatCoreResp "" ("(" ++ unwords (fmap showSymbol a) ++ ")") == Right a

prop_parseGetValueResponse :: Property
prop_parseGetValueResponse = forAll genGetValueResponse $ \a ->
  parse parseGetValueResponse "" ("(" ++ joinA a ++ ")") == Right a

prop_parseGetModelResponse :: Property
prop_parseGetModelResponse = forAll genGetModelResponse $ \a ->
  parse parseGetModelResponse "" ("(" ++ joinA a ++ ")") == Right a

prop_parseGetOptionResponse :: Property
prop_parseGetOptionResponse = forAll arbitrary $ \a ->
  parse parseGetOptionResponse "" (showSL a) == Right a

prop_parseEchoResponse :: Property
prop_parseEchoResponse = forAll genEchoResponse $ \a ->
  parse parseEchoResponse "" a == Right a

case_bug_1 :: Assertion
case_bug_1 = do
  parse parseSort "" "(_a b)" @?= Right (SortIdentifiers (ISymbol "_a") [SortId (ISymbol "b")])
  parse parseTerm "" "(_a b)" @?= Right (TermQualIdentifierT (QIdentifier (ISymbol "_a")) [TermQualIdentifier (QIdentifier (ISymbol "b"))])

case_bug_2 :: Assertion
case_bug_2 = do
  parse parseTerm "" "(asX b)" @?= Right (TermQualIdentifierT (QIdentifier (ISymbol "asX")) [TermQualIdentifier (QIdentifier (ISymbol "b"))])

case_parse_string_literal_ascii :: Assertion
case_parse_string_literal_ascii = parse str "" s @?= Right s
  where
    s = showStringLiteral $ ['\t','\n','\r'] ++ [toEnum 32 .. toEnum 126]

prop_parse_string_literal :: Property
prop_parse_string_literal =
  forAll genStringLiteral $  \s ->
    parse str "" s == Right s

case_parseSexprKeyword_bug :: Assertion
case_parseSexprKeyword_bug =
  parse parseSexprKeyword "" ":keyword" @?= Right (SexprKeyword ":keyword")

case_parseHexadecimal :: Assertion
case_parseHexadecimal = parse parseHexadecimal "" (showSL s) @?= Right s
  where
    s = SpecConstantHexadecimal "afbf00"

case_parseBinary :: Assertion
case_parseBinary = parse parseBinary "" (showSL s) @?= Right s
  where
    s = SpecConstantBinary "011001"

case_comment :: Assertion
case_comment = do
  parse parseTerm "" "(f ; hogehoge\na;bbb\n)" @?=
    Right (TermQualIdentifierT (QIdentifier (ISymbol "f")) [TermQualIdentifier (QIdentifier (ISymbol "a"))])

-- ---------------------------------------------------------------------

prop_Command_show :: Property
prop_Command_show = forAll arbitrary $ \(a :: Command) ->
  show a `deepseq` True

prop_Option_show :: Property
prop_Option_show = forAll arbitrary $ \(a :: Option) ->
  show a `deepseq` True

prop_InfoFlags_show :: Property
prop_InfoFlags_show = forAll arbitrary $ \(a :: InfoFlags) ->
  show a `deepseq` True

prop_Term_show :: Property
prop_Term_show = forAll arbitrary $ \(a :: Term) ->
  show a `deepseq` True

prop_VarBinding_show :: Property
prop_VarBinding_show = forAll arbitrary $ \(a :: VarBinding) ->
  show a `deepseq` True

prop_SortedVar_show :: Property
prop_SortedVar_show = forAll arbitrary $ \(a :: VarBinding) ->
  show a `deepseq` True

prop_QualIdentifier_show :: Property
prop_QualIdentifier_show = forAll arbitrary $ \(a :: QualIdentifier) ->
  show a `deepseq` True

prop_FunDec_show :: Property
prop_FunDec_show = forAll arbitrary $ \(a :: FunDec) ->
  show a `deepseq` True

prop_AttrValue_show :: Property
prop_AttrValue_show = forAll arbitrary $ \(a :: AttrValue) ->
  show a `deepseq` True

prop_Attribute_show :: Property
prop_Attribute_show = forAll arbitrary $ \(a :: Attribute) ->
  show a `deepseq` True

prop_Index_show :: Property
prop_Index_show = forAll arbitrary $ \(a :: Index) ->
  show a `deepseq` True

prop_Identifier_show :: Property
prop_Identifier_show = forAll arbitrary $ \(a :: Identifier) ->
  show a `deepseq` True

prop_Sort_show :: Property
prop_Sort_show = forAll arbitrary $ \(a :: Sort) ->
  show a `deepseq` True

prop_SpecConstant_show :: Property
prop_SpecConstant_show = forAll arbitrary $ \(a :: SpecConstant) ->
  show a `deepseq` True

prop_Sexpr_show :: Property
prop_Sexpr_show = forAll arbitrary $ \(a :: Sexpr) ->
  show a `deepseq` True

prop_CmdResponse_show :: Property
prop_CmdResponse_show = forAll arbitrary $ \(a :: CmdResponse) ->
  show a `deepseq` True

prop_GenResponse_show :: Property
prop_GenResponse_show = forAll arbitrary $ \(a :: GenResponse) ->
  show a `deepseq` True

prop_ErrorBehavior_show :: Property
prop_ErrorBehavior_show = forAll arbitrary $ \(a :: ErrorBehavior) ->
  show a `deepseq` True

prop_ReasonUnknown_show :: Property
prop_ReasonUnknown_show = forAll arbitrary $ \(a :: ReasonUnknown) ->
  show a `deepseq` True

prop_CheckSatResponse_show :: Property
prop_CheckSatResponse_show = forAll arbitrary $ \(a :: CheckSatResponse) ->
  show a `deepseq` True

prop_InfoResponse_show :: Property
prop_InfoResponse_show = forAll arbitrary $ \(a :: InfoResponse) ->
  show a `deepseq` True

prop_ValuationPair_show :: Property
prop_ValuationPair_show = forAll arbitrary $ \(a :: ValuationPair) ->
  show a `deepseq` True

prop_TValuationPair_show :: Property
prop_TValuationPair_show = forAll arbitrary $ \(a :: TValuationPair) ->
  show a `deepseq` True

-- ---------------------------------------------------------------------

instance Arbitrary Term where
  arbitrary = sized $ \n -> oneof $
    [ TermSpecConstant <$> arbitrary
    , TermQualIdentifier <$> arbitrary
    ] ++ (if n > 0 then gs else [])
    where
      gs =
        [ liftM2 TermQualIdentifierT arbitrary (listOf1' arbitrary')
        , liftM2 TermLet (listOf1' arbitrary) arbitrary'
        , liftM2 TermForall (listOf1' arbitrary) arbitrary'
        , liftM2 TermExists (listOf1' arbitrary) arbitrary'
        , liftM2 TermAnnot arbitrary' (listOf1' arbitrary)
        ]

instance Arbitrary VarBinding where
  arbitrary = liftM2 VB genSymbol arbitrary'

instance Arbitrary SortedVar where
  arbitrary = liftM2 SV genSymbol arbitrary

instance Arbitrary AttrValue where
  arbitrary = oneof
    [ AttrValueConstant <$> arbitrary
    , AttrValueSymbol <$> genSymbol
    , AttrValueSexpr <$> listOf' arbitrary
    ]

instance Arbitrary Attribute where
  arbitrary = oneof
    [ Attribute <$> genKeyword
    , liftM2 AttributeVal genKeyword arbitrary
    ]

instance Arbitrary QualIdentifier where
  arbitrary = oneof
    [ QIdentifier <$> arbitrary
    , liftM2 QIdentifierAs arbitrary arbitrary
    ]

instance Arbitrary Index where
  arbitrary = oneof
    [ IndexNumeral <$> abs <$> arbitrary
    , IndexSymbol <$> genSymbol
    ]

instance Arbitrary Identifier where
  arbitrary = oneof
    [ ISymbol <$> genSymbol
    , liftM2 I_Symbol genSymbol (listOf1' arbitrary)
    ]

instance Arbitrary Sort where
  arbitrary = oneof
    [ SortId <$> arbitrary
    , liftM2 SortIdentifiers arbitrary (listOf1' arbitrary')
    ]

instance Arbitrary SpecConstant where
  arbitrary = oneof
    [ SpecConstantNumeral <$> abs <$> arbitrary
    , liftM SpecConstantDecimal $ do
        a <- show <$> abs <$> (arbitrary :: Gen Int)
        b <- listOf $ return '0'
        c <- show <$> abs <$> (arbitrary :: Gen Int)
        return $ a ++ "." ++ b ++ c
    , SpecConstantHexadecimal <$> listOf1 (elements (['0'..'9'] ++ ['a'..'f'] ++ ['A'..'F']))
    , SpecConstantBinary <$> listOf1 (elements ['0','1'])
    , liftM SpecConstantString $ do
        let p c = c `elem` ['\t','\n','\r'] || (32 <= fromEnum c && fromEnum c <= 126) || 128 <= fromEnum c
        s <- listOf $ arbitrary `suchThat` p
        return $ "\"" ++ concat [if c == '"' then "\"\"" else [c] | c <- s] ++ "\""
    ]

instance Arbitrary Sexpr where
  arbitrary = sized $ \n -> oneof $
    [ SexprSpecConstant <$> arbitrary
    , SexprSymbol <$> genSymbol
    , SexprKeyword <$> genKeyword
    ] ++
    [ liftM SexprSxp $ listOf' arbitrary' | n > 0 ]

halve :: Gen a -> Gen a
halve g = sized (\n -> resize (n `div` 2) g)

arbitrary' :: Arbitrary a => Gen a
arbitrary' = halve arbitrary

listOf' :: Gen a -> Gen [a]
listOf' g = do
  n <- frequency [(6, return 0), (3, return 1), (1, return 2)]
  replicateM n g

listOf1' :: Gen a -> Gen [a]
listOf1' g = do
  n <- frequency [(10, return 1), (4, return 2), (1, return 3)]
  replicateM n g

-- ---------------------------------------------------------------------

type Symbol = String

genSymbol :: Gen Symbol
genSymbol = oneof [genSimpleSymbol, genQuotedSymbol]

genSimpleSymbol :: Gen Symbol
genSimpleSymbol = g `suchThat` (`Set.notMember` reserved)
  where
    xs = ['a'..'z']++['A'..'Z']++"~!@$%^&*_-+=<>.?/"
    g = liftM2 (:) (elements xs) (listOf1 $ elements $ ['0'..'9'] ++ xs)
    reserved = Set.fromList $
      ["BINARY", "DECIMAL", "HEXADECIMAL", "NUMERAL", "STRING", "_", "!", "as", "let", "exists", "forall", "par"] ++
      ["set-logic", "set-option", "set-info", "declare-sort", "define-sort", "declare-const", "declare-fun", "declare-fun-rec", "declare-funs-rec", "push", "pop", "reset", "reset-assertions", "assert", "check-sat", "check-sat-assuming", "get-assertions", "get-model", "get-proof", "get-unsat-core", "get-unsat-assumptions", "get-value", "get-assignment", "get-option", "get-info", "echo", "exit"]

genQuotedSymbol :: Gen Symbol
genQuotedSymbol = listOf1 g
  where
    g :: Gen Char
    g = oneof [elements (Set.toList xs), choose (toEnum 128, maxBound)]
    xs = Set.fromList (['\t','\n','\r'] ++ [' ' .. toEnum 126]) `Set.difference` Set.fromList ['\\', '|']

genKeyword :: Gen String
genKeyword = oneof
  [ (':':) <$> genSimpleSymbol
  , elements
      [ ":print-success"
      , ":expand-definitions"
      , ":interactive-mode"
      , ":produce-proofs"
      , ":produce-unsat-cores"
      , ":produce-unsat-assumptions"
      , ":produce-models"
      , ":produce-assignments"
      , ":produce-assertions"
      , ":global-declarations"
      , ":regular-output-channel"
      , ":diagnostic-output-channel"
      , ":random-seed"
      , ":verbosity"
      , ":reproducible-resource-limit"

      , ":error-behavior"
      , ":name"
      , ":authors"
      , ":version"
      , ":status"
      , ":reason-unknown"
      , ":all-statistics"
      , ":assertion-stack-levels"
      ]
  ]

genStringLiteral :: Gen String
genStringLiteral = showStringLiteral <$> listOf genStringChar

showStringLiteral :: String -> String
showStringLiteral s = "\"" ++ concat [if c == '"' then "\"\"" else [c] | c <- s] ++ "\""

genStringChar :: Gen Char
genStringChar = arbitrary `suchThat` p
   where
    p c = c `elem` ['\t','\n','\r'] || (32 <= fromEnum c && fromEnum c <= 126) || 128 <= fromEnum c

-- ---------------------------------------------------------------------

instance Arbitrary Command where
  arbitrary = oneof
    [ SetLogic <$> genSymbol
    , SetOption <$> arbitrary
    , SetInfo <$> arbitrary
    , DeclareSort <$> genSymbol <*> (abs <$> arbitrary)
    , DefineSort <$> genSymbol <*> listOf' genSymbol <*> arbitrary
    , DeclareConst <$> genSymbol <*> arbitrary
    , DeclareFun <$> genSymbol <*> listOf' arbitrary <*> arbitrary
    , DefineFun <$> genSymbol <*> listOf' arbitrary <*> arbitrary <*> arbitrary
    , DefineFunRec <$> genSymbol <*> listOf' arbitrary <*> arbitrary <*> arbitrary
    , DefineFunsRec <$> listOf1' arbitrary <*> listOf1' arbitrary
    , Push <$> (abs <$> arbitrary)
    , Pop <$> (abs <$> arbitrary)
    , return Reset
    , return ResetAssertions
    , Assert <$> arbitrary
    , return CheckSat
    , CheckSatAssuming <$> listOf' arbitrary
    , return GetAssertions
    , return GetModel
    , return GetProof
    , return GetUnsatCore
    , return GetUnsatAssumptions
    , GetValue <$> listOf1' arbitrary
    , return GetAssignment
    , GetOption <$> genKeyword
    , GetInfo <$> arbitrary
    , Echo <$> genStringLiteral
    , return Exit
    ]

instance Arbitrary Option where
  arbitrary = oneof
    [ PrintSuccess <$> arbitrary
    , ExpandDefinitions <$> arbitrary
    , InteractiveMode <$> arbitrary
    , ProduceProofs <$> arbitrary
    , ProduceUnsatCores <$> arbitrary
    , ProduceUnsatAssumptions <$> arbitrary
    , ProduceModels <$> arbitrary
    , ProduceAssignments <$> arbitrary
    , ProduceAssertions <$> arbitrary
    , GlobalDeclarations <$> arbitrary
    , RegularOutputChannel <$> genStringLiteral
    , DiagnosticOutputChannel <$> genStringLiteral
    , RandomSeed <$> (abs <$> arbitrary)
    , Verbosity <$> abs <$> arbitrary
    , ReproducibleResourceLimit <$> abs <$> arbitrary
    , OptionAttr <$> (arbitrary `suchThat` p)
    ]
    where
      p (Attribute kw) = kw `Set.notMember` reserved
      p (AttributeVal kw _) = kw `Set.notMember` reserved
      reserved = Set.fromList
        [ ":print-success"
        , ":expand-definitions"
        , ":interactive-mode"
        , ":produce-proofs"
        , ":produce-unsat-cores"
        , ":produce-unsat-assumptions"
        , ":produce-models"
        , ":produce-assignments"
        , ":produce-assertions"
        , ":global-declarations"
        , ":regular-output-channel"
        , ":diagnostic-output-channel"
        , ":random-seed"
        , ":verbosity"
        , ":reproducible-resource-limit"
        ]

instance Arbitrary InfoFlags where
  arbitrary = oneof
    [ return ErrorBehavior
    , return Name
    , return Authors
    , return Version
    , return Status
    , return ReasonUnknown
    , return AllStatistics
    , return AssertionStackLevels
    , InfoFlags <$> (genKeyword `suchThat` (`Set.notMember` reserved))
    ]
    where
      reserved = Set.fromList
        [ ":error-behavior"
        , ":name"
        , ":authors"
        , ":version"
        , ":status"
        , ":reason-unknown"
        , ":all-statistics"
        , ":assertion-stack-levels"
        ]

instance Arbitrary FunDec where
  arbitrary = FunDec <$> genSymbol <*> listOf' arbitrary <*> arbitrary

-- ---------------------------------------------------------------------

instance Arbitrary CmdResponse where
  arbitrary = oneof
    [ CmdGenResponse <$> arbitrary
    , CmdGetInfoResponse <$> genGetInfoResponse
    , CmdCheckSatResponse <$> arbitrary
    , CmdGetAssertionsResponse <$> genGetAssertionsResponse
    , CmdGetAssignmentResponse <$> arbitrary
    , CmdGetProofResponse <$> arbitrary
    , CmdGetUnsatCoreResponse <$> genGetUnsatCoreResponse
    , CmdGetUnsatAssumptionsResponse <$> arbitrary
    , CmdGetValueResponse <$> genGetValueResponse
    , CmdGetModelResponse <$> arbitrary
    , CmdGetOptionResponse <$> arbitrary
    , CmdEchoResponse <$> genEchoResponse
    ]

instance Arbitrary GenResponse where
  arbitrary = oneof
    [ return Unsupported
    , return Success
    , Error <$> genStringLiteral
    ]

instance Arbitrary ErrorBehavior where
  arbitrary = elements [ImmediateExit, ContinuedExecution]

instance Arbitrary ReasonUnknown where
  arbitrary = elements [Memout, Incomplete]

instance Arbitrary CheckSatResponse where
  arbitrary = elements [Sat, Unsat, Unknown]

instance Arbitrary InfoResponse where
  arbitrary = oneof
    [ ResponseErrorBehavior <$> arbitrary
    , ResponseName <$> genStringLiteral
    , ResponseAuthors <$> genStringLiteral
    , ResponseVersion <$> genStringLiteral
    , ResponseReasonUnknown <$> arbitrary
    , ResponseAssertionStackLevels <$> abs <$> arbitrary
    , ResponseAttribute <$> (arbitrary `suchThat` p)
    ]
    where
      p (Attribute kw) = kw `Set.notMember` reserved
      p (AttributeVal kw _) = kw `Set.notMember` reserved
      reserved = Set.fromList
        [ ":error-behavior"
        , ":name"
        , ":authors"
        , ":version"
        , ":status"
        , ":reason-unknown"
        , ":all-statistics"
        , ":assertion-stack-levels"
        ]

genGetInfoResponse :: Gen [InfoResponse]
genGetInfoResponse = listOf' arbitrary

genGetAssertionsResponse :: Gen [Term]
genGetAssertionsResponse = listOf' arbitrary

genGetUnsatCoreResponse :: Gen [Symbol]
genGetUnsatCoreResponse = listOf' genSymbol

genGetValueResponse :: Gen [ValuationPair]
genGetValueResponse = listOf' arbitrary

genGetModelResponse :: Gen [Command]
genGetModelResponse = listOf1' $ oneof
  [ DefineFun <$> genSymbol <*> listOf' arbitrary <*> arbitrary <*> arbitrary
  , DefineFunRec <$> genSymbol <*> listOf' arbitrary <*> arbitrary <*> arbitrary
  , DefineFunsRec <$> listOf1' arbitrary <*> listOf1' arbitrary
  ]

genEchoResponse :: Gen String
genEchoResponse = genStringLiteral

instance Arbitrary ValuationPair where
  arbitrary = ValuationPair <$> arbitrary <*> arbitrary

instance Arbitrary TValuationPair where
  arbitrary = TValuationPair <$> genSymbol <*> arbitrary

-- ---------------------------------------------------------------------
-- Test harness

smtlibTestGroup :: TestTree
smtlibTestGroup = $(testGroupGenerator)