|
| 1 | +{-# LANGUAGE QuasiQuotes #-} |
| 2 | + |
| 3 | +{- | |
| 4 | +Copyright : (c) Runtime Verification, 2024 |
| 5 | +License : BSD-3-Clause |
| 6 | +-} |
| 7 | +module Test.Booster.Builtin ( |
| 8 | + test_builtins, |
| 9 | +) where |
| 10 | + |
| 11 | +import Control.Monad.Trans.Except |
| 12 | +import Data.Either (isLeft) |
| 13 | +import Data.Map qualified as Map |
| 14 | +import Data.Maybe (fromMaybe) |
| 15 | +import Data.Text qualified as Text |
| 16 | + |
| 17 | +import Hedgehog |
| 18 | +import Hedgehog.Gen qualified as Gen |
| 19 | +import Hedgehog.Range qualified as Range |
| 20 | +import Test.Tasty (TestTree, testGroup) |
| 21 | +import Test.Tasty.HUnit hiding (assert) |
| 22 | +import Test.Tasty.Hedgehog |
| 23 | + |
| 24 | +import Booster.Builtin qualified as Builtin (hooks) |
| 25 | +import Booster.Builtin.BOOL qualified as Builtin |
| 26 | +import Booster.Builtin.INT qualified as Builtin |
| 27 | +import Booster.Pattern.Base |
| 28 | +import Booster.Syntax.Json.Internalise (trm) |
| 29 | +import Booster.Syntax.ParsedKore.Internalise (symb) |
| 30 | +import Test.Booster.Fixture as Fixture |
| 31 | + |
| 32 | +test_builtins :: [TestTree] |
| 33 | +test_builtins = |
| 34 | + [ testIntHooks |
| 35 | + , testListSizeHooks |
| 36 | + ] |
| 37 | + |
| 38 | +testIntHooks :: TestTree |
| 39 | +testIntHooks = |
| 40 | + testGroup |
| 41 | + "Integer hooks" |
| 42 | + [ testIntHook2 "INT.add" (+) Builtin.intTerm |
| 43 | + , testIntHook2 "INT.mul" (*) Builtin.intTerm |
| 44 | + , testIntHook2 "INT.sub" (-) Builtin.intTerm |
| 45 | + , -- comparisons |
| 46 | + testIntHook2 "INT.gt" (>) Builtin.boolTerm |
| 47 | + , testIntHook2 "INT.ge" (>=) Builtin.boolTerm |
| 48 | + , testIntHook2 "INT.lt" (<) Builtin.boolTerm |
| 49 | + , testIntHook2 "INT.le" (<=) Builtin.boolTerm |
| 50 | + , testIntHook2 "INT.eq" (==) Builtin.boolTerm |
| 51 | + , testIntHook2 "INT.ne" (/=) Builtin.boolTerm |
| 52 | + ] |
| 53 | + where |
| 54 | + -- testIntHook2 :: ByteString -> (Int -> Int -> a) -> (a -> Term) -> TestTree |
| 55 | + testIntHook2 name op result = |
| 56 | + testProperty ("Hook " <> show name) . property $ do |
| 57 | + let f = |
| 58 | + fromMaybe |
| 59 | + (error $ "Hook " <> show name <> " missing") |
| 60 | + (Map.lookup name Builtin.hooks) |
| 61 | + runHook h args = either (error . Text.unpack) id . runExcept $ h args |
| 62 | + a <- fmap fromIntegral $ forAll $ Gen.int64 Range.linearBounded |
| 63 | + b <- fmap fromIntegral $ forAll $ Gen.int64 Range.linearBounded |
| 64 | + let dv_a = Builtin.intTerm a |
| 65 | + dv_b = Builtin.intTerm b |
| 66 | + -- regular computation |
| 67 | + Just (result $ op a b) === runHook f [dv_a, dv_b] |
| 68 | + -- Nothing for unevaluated arguments |
| 69 | + let fct = [symb| symbol f{}(SortInt) : SortInt [function{}()] |] |
| 70 | + Nothing === runHook f [app fct [dv_a], dv_b] |
| 71 | + Nothing === runHook f [dv_a, app fct [dv_b]] |
| 72 | + Nothing === runHook f [app fct [dv_a], app fct [dv_b]] |
| 73 | + -- arity error on wrong argument count |
| 74 | + let assertException = assert . isLeft . runExcept |
| 75 | + assertException $ f [] |
| 76 | + assertException $ f [dv_a] |
| 77 | + assertException $ f [dv_a, dv_a, dv_a] |
| 78 | + |
| 79 | +testListSizeHooks :: TestTree |
| 80 | +testListSizeHooks = |
| 81 | + testGroup |
| 82 | + "LIST.size hooks" |
| 83 | + [ testProperty "LIST.size on concrete lists works" . property $ do |
| 84 | + l <- forAll $ Gen.int (Range.constant 0 12) |
| 85 | + let aList = |
| 86 | + KList Fixture.testKListDef (replicate l [trm| \dv{SomeSort{}}("thing")|]) Nothing |
| 87 | + hook <- maybe failure pure $ Map.lookup "LIST.size" Builtin.hooks |
| 88 | + result <- evalEither $ runExcept $ hook [aList] |
| 89 | + Just (Builtin.intTerm (fromIntegral l)) === result |
| 90 | + , testProperty "LIST.size on symbolic lists has no result" . property $ do |
| 91 | + l <- forAll $ Gen.int (Range.constant 1 5) |
| 92 | + let aList = |
| 93 | + KList Fixture.testKListDef [] $ |
| 94 | + Just ([trm| INIT:SortList|], replicate l [trm| \dv{SomeSort{}}("thing")|]) |
| 95 | + hook <- maybe failure pure $ Map.lookup "LIST.size" Builtin.hooks |
| 96 | + result <- evalEither $ runExcept $ hook [aList] |
| 97 | + Nothing === result |
| 98 | + , testCase "LIST.size arity is checked" $ do |
| 99 | + hook <- maybe (error "LIST.size hook not found") pure $ Map.lookup "LIST.size" Builtin.hooks |
| 100 | + let assertException = assertBool "Unexpected success" . isLeft . runExcept |
| 101 | + assertException $ hook [] |
| 102 | + assertException $ hook $ replicate 2 [trm| X:SortList{} |] |
| 103 | + ] |
0 commit comments