packages feed

mockcat-1.0.0.0: test/Test/MockCat/TH/TypeUtilsSpec.hs

{-# LANGUAGE TemplateHaskellQuotes #-}

module Test.MockCat.TH.TypeUtilsSpec (spec) where

import qualified Data.Map.Strict as Map
import Language.Haskell.TH
import Test.Hspec
import Test.MockCat.TH.TypeUtils

spec :: Spec
spec = do
  describe "splitApps" $ do
    it "expands function type arguments in order" $ do
      let a = mkName "a"
          ty = AppT (AppT ArrowT (ConT ''Int)) (VarT a)
      splitApps ty `shouldBe` (ArrowT, [ConT ''Int, VarT a])

    it "decomposes nested type applications into top-level and argument list" $ do
      let a = mkName "a"
          b = mkName "b"
          ty =
            AppT
              (ConT ''Maybe)
              (AppT (AppT (ConT ''Either) (VarT a)) (VarT b))
      splitApps ty
        `shouldBe` ( ConT ''Maybe
                   , [AppT (AppT (ConT ''Either) (VarT a)) (VarT b)]
                   )

    it "returns types without application as they are" $ do
      splitApps (ConT ''Bool) `shouldBe` (ConT ''Bool, [])

  describe "substituteType" $ do
    it "replaces VarT with types from the map" $ do
      let a = mkName "a"
          b = mkName "b"
          ty = AppT (VarT a) (VarT b)
          subMap =
            Map.fromList
              [ (a, ConT ''Int)
              , (b, ConT ''Bool)
              ]
      substituteType subMap ty `shouldBe` AppT (ConT ''Int) (ConT ''Bool)

    it "recursively applies to constraints and body within ForallT" $ do
      let a = mkName "a"
          b = mkName "b"
          ty =
            ForallT
              [PlainTV a SpecifiedSpec]
              [AppT (ConT ''Eq) (VarT a)]
              (AppT (VarT b) (VarT a))
          subMap =
            Map.fromList
              [ (a, ConT ''Char)
              , (b, ConT ''Maybe)
              ]
          expected =
            ForallT
              [PlainTV a SpecifiedSpec]
              [AppT (ConT ''Eq) (ConT ''Char)]
              (AppT (ConT ''Maybe) (ConT ''Char))
      substituteType subMap ty `shouldBe` expected

    it "leaves type variables not in the map as they are" $ do
      let a = mkName "a"
          ty = VarT a
      substituteType Map.empty ty `shouldBe` VarT a

  describe "isNotConstantFunctionType" $ do
    it "is True for types containing arrows" $ do
      let ty = AppT (AppT ArrowT (ConT ''Int)) (ConT ''Bool)
      isNotConstantFunctionType ty `shouldBe` True

    it "is False for tuples" $ do
      isNotConstantFunctionType (TupleT 0) `shouldBe` False

    it "can judge even when wrapped in forall" $ do
      let a = mkName "a"
          body = AppT (AppT ArrowT (VarT a)) (ConT ''Int)
          ty = ForallT [PlainTV a SpecifiedSpec] [] body
      isNotConstantFunctionType ty `shouldBe` True