covenant-1.1.0: test/bb/Main.hs
{-# LANGUAGE ViewPatterns #-}
module Main (main) where
-- import Data.Either (isRight)
import Control.Exception (throwIO)
import Control.Monad ((<=<))
import Covenant.Data
( mkBBF,
)
import Covenant.DeBruijn (DeBruijn (S, Z))
import Covenant.Index (ix0, ix1)
import Covenant.Test
( DataDeclFlavor (Poly1PolyThunks),
DataDeclSet (DataDeclSet),
failLeft,
list,
prettyDeclSet,
renameCompT,
renameValT,
runRenameM,
tree,
tyAppTestDatatypes,
unsafeTyCon,
weirderList,
)
import Covenant.Type
( AbstractTy (BoundAt),
CompT (Comp0, Comp1, Comp2),
CompTBody (ReturnT, (:--:>)),
ValT (Abstraction, ThunkT),
tyvar,
)
import Data.Map qualified as M
import Data.Maybe (catMaybes, fromJust)
import Optics.Core (view)
import Test.QuickCheck
( Arbitrary (arbitrary, shrink),
Property,
)
import Test.Tasty (TestTree, adjustOption, defaultMain, testGroup)
import Test.Tasty.HUnit (assertEqual, assertFailure, testCase)
import Test.Tasty.QuickCheck (QuickCheckTests, forAllShrinkShow, testProperty)
main :: IO ()
main =
defaultMain . adjustOption moreTests . testGroup "BB" $
[ testProperty "All BBF transformations rename properly" bbFormRenames,
testMonotypeBB,
bbfList,
bbfTree,
bbfWeirderList
]
where
-- These tests are suuuuppeeerr inefficient, it'd be ideal to run more but it'll take too long
moreTests :: QuickCheckTests -> QuickCheckTests
moreTests = max 500
{- This is the only reasonable property I can think of, and is ultimately more of a test of the
"ensure there aren't any phantom type variables" than it is of the bb transform.
Fortunately the transform itself is fairly straightforward and isn't likely to contain major errors.
-}
bbFormRenames :: Property
bbFormRenames = forAllShrinkShow (arbitrary @(DataDeclSet 'Poly1PolyThunks)) shrink prettyDeclSet $ \(DataDeclSet decls) ->
case traverse mkBBF decls of
Left _ -> False
Right (catMaybes -> bbfDecls) ->
let results =
mapM
( \valT -> case runRenameM . renameValT $ valT of
Left err -> Left (err, valT)
Right res -> Right res
)
bbfDecls
in case results of -- all (isRight . runRenameM . renameValT) bbfDecls
Right {} -> True
Left err -> error (show err)
bbfList :: TestTree
bbfList = testCase "bbfList" $ do
let bbf = mkBBF list
bbf' <- either (assertFailure . show) (maybe (assertFailure "no bbf for list") pure) bbf
assertEqual "bbfList" expectedListBBF bbf'
where
expectedListBBF =
ThunkT
( Comp2
( tyvar Z ix1
:--:> ThunkT
( Comp0
( tyvar (S Z) ix0
:--:> tyvar (S Z) ix1
:--:> ReturnT (tyvar (S Z) ix1)
)
)
:--:> ReturnT (tyvar Z ix1)
)
)
bbfTree :: TestTree
bbfTree = testCase "bbfTree" $ do
let bbf = mkBBF tree
bbf' <- either (assertFailure . show) (maybe (assertFailure "no bbf for tree") pure) bbf
assertEqual "bbfList" expectedTreeBBF bbf'
where
expectedTreeBBF =
ThunkT
( Comp2
( ThunkT
( Comp0
( tyvar (S Z) ix1
:--:> tyvar (S Z) ix1
:--:> ReturnT (tyvar (S Z) ix1)
)
)
:--:> ThunkT
( Comp0
( tyvar (S Z) ix0
:--:> ReturnT (tyvar (S Z) ix1)
)
)
:--:> ReturnT (tyvar Z ix1)
)
)
bbfWeirderList :: TestTree
bbfWeirderList = testCase "bbfWeirderList" $ do
let bbf = mkBBF weirderList
bbf' <- either (assertFailure . show) (maybe (assertFailure "no bbf for tree") pure) bbf
assertEqual "bbfWeirderTree" expectedWeirdBBF bbf'
where
-- forall a r. (Maybe (a,r) -> r) -> r
expectedWeirdBBF =
ThunkT
( Comp2
( ThunkT
( Comp0
( unsafeTyCon "Maybe" [unsafeTyCon "Pair" [tyvar (S Z) ix0, tyvar (S Z) ix1]]
:--:> ReturnT (tyvar (S Z) ix1)
)
)
:--:> ReturnT (tyvar Z ix1)
)
)
-- Simple datatype unification unit test. Checks whether `data Unit = Unit` has the expected BB form
testMonotypeBB :: TestTree
testMonotypeBB = testCase "unitBbf" $ do
let expected = Comp1 $ Abstraction (BoundAt Z ix0) :--:> ReturnT (Abstraction $ BoundAt Z ix0)
expected' <- failLeft . runRenameM . renameCompT $ expected
actual <- case fromJust . (view #bbForm <=< M.lookup "Unit") $ tyAppTestDatatypes of
ThunkT inner -> either (throwIO . userError . show) pure . runRenameM $ renameCompT inner
_ -> assertFailure "BB form not a thunk!"
assertEqual "unit bbf" expected' actual