mockcat-1.0.0.0: test/Test/MockCat/TH/FunctionBuilderSpec.hs
{-# LANGUAGE TemplateHaskellQuotes #-}
module Test.MockCat.TH.FunctionBuilderSpec (spec) where
import qualified Data.Text as T
import Language.Haskell.TH
import Test.Hspec
import Test.MockCat.Cons ((:>))
import Test.MockCat.Param (Param)
import Test.MockCat.TH.FunctionBuilder
import Test.MockCat.SharedSpecDefs (UserInput(..))
spec :: Spec
spec = do
describe "createMockBuilderVerifyParams" $ do
it "returns a single Param for a function with a single argument" $ do
let m = mkName "m"
funType = AppT (AppT ArrowT (ConT ''String)) (AppT (VarT m) (TupleT 0))
createMockBuilderVerifyParams funType
`shouldBe` AppT (ConT ''Param) (ConT ''String)
it "connects Params with :> for functions with multiple arguments" $ do
let m = mkName "m"
rest = AppT (AppT ArrowT (ConT ''Int)) (AppT (VarT m) (TupleT 0))
funType = AppT (AppT ArrowT (ConT ''String)) rest
expected =
AppT
(AppT (ConT ''(:>)) (AppT (ConT ''Param) (ConT ''String)))
(AppT (ConT ''Param) (ConT ''Int))
createMockBuilderVerifyParams funType `shouldBe` expected
it "returns () when there are no arguments" $ do
let m = mkName "m"
createMockBuilderVerifyParams (AppT (VarT m) (TupleT 0))
`shouldBe` TupleT 0
describe "createMockBuilderFnType" $ do
it "removes monad arguments to make a pure function type" $ do
let m = mkName "m"
funType = AppT (AppT ArrowT (ConT ''String)) (AppT (VarT m) (ConT ''Int))
expected = AppT (AppT ArrowT (ConT ''String)) (ConT ''Int)
createMockBuilderFnType m funType `shouldBe` expected
it "does not change return values other than the monad variable" $ do
let m = mkName "m"
funType = AppT (AppT ArrowT (ConT ''String)) (AppT (ConT ''IO) (ConT ''Int))
createMockBuilderFnType m funType `shouldBe` funType
it "removes the monad variable even in types containing forall" $ do
let m = mkName "m"
a = mkName "a"
body = AppT (VarT m) (VarT a)
funType = ForallT [PlainTV a SpecifiedSpec] [] body
createMockBuilderFnType m funType `shouldBe` VarT a
describe "partialAdditionalPredicates (migrated from THMockFnContextSpec)" $ do
it "adds Typeable and equality constraints for verification parameters in polymorphic functions" $ do
let a = mkName "a"
b = mkName "b"
funType = AppT (AppT ArrowT (VarT a)) (VarT b)
verifyParams = AppT (ConT ''Param) (VarT a)
preds = partialAdditionalPredicates funType verifyParams
normalize preds `shouldMatchList`
[ "ResolvableParamsOf (a -> b) ~ Param a"
]
it "does not add Typeable for verification parameters when type variables only appear in the return value" $ do
let a = mkName "a"
funType = AppT (AppT ArrowT (ConT ''String)) (VarT a)
verifyParams = AppT (ConT ''Param) (ConT ''String)
preds = partialAdditionalPredicates funType verifyParams
normalize preds `shouldMatchList`
[ "ResolvableParamsOf (String -> a) ~ Param String"
]
it "does not duplicate Typeable even if the same type variable appears multiple times" $ do
let a = mkName "a"
funType = AppT (AppT ArrowT (VarT a)) (VarT a)
verifyParams = AppT (ConT ''Param) (VarT a)
preds = partialAdditionalPredicates funType verifyParams
normalize preds `shouldMatchList`
[ "ResolvableParamsOf (a -> a) ~ Param a"
]
it "does not add redundant constraints for functions with concrete types" $ do
let funType =
AppT
(AppT ArrowT (ConT ''String))
(AppT (ConT ''Maybe) (ConT ''UserInput))
verifyParams = AppT (ConT ''Param) (ConT ''String)
preds = partialAdditionalPredicates funType verifyParams
normalize preds `shouldBe` []
describe "createTypeablePreds" $ do
it "decomposes and extracts types containing type variables and removes duplicates" $ do
let a = mkName "a"
b = mkName "b"
funType = AppT (AppT ArrowT (VarT a)) (VarT b)
verifyParams = AppT (ConT ''Param) (VarT a)
preds = createTypeablePreds [funType, verifyParams]
normalize preds `shouldMatchList`
[ "Typeable a"
, "Typeable b"
]
it "extracts type families without decomposing them" $ do
let m = mkName "m"
assoc = AppT (ConT (mkName "ResultType")) (VarT m)
preds = createTypeablePreds [assoc]
normalize preds `shouldMatchList`
[ "Typeable (ResultType m)"
]
it "generates nothing for concrete types only" $ do
let ty = AppT (ConT ''Maybe) (ConT ''Int)
preds = createTypeablePreds [ty]
normalize preds `shouldBe` []
normalize :: [Pred] -> [String]
normalize = fmap (cleanup . pprint)
where
cleanup =
T.unpack
. T.unwords
. T.words
. T.replace (T.pack "\n") (T.pack " ")
. T.replace (T.pack "Data.Typeable.Internal.") (T.pack "")
. T.replace (T.pack "GHC.Internal.") (T.pack "")
. T.replace (T.pack "Base.") (T.pack "")
. T.replace (T.pack "GHC.Base.") (T.pack "")
. T.replace (T.pack "GHC.Types.") (T.pack "")
. T.replace (T.pack "Test.MockCat.Param.") (T.pack "")
. T.replace (T.pack "Test.MockCat.Verify.") (T.pack "")
. T.pack