packages feed

HMock-0.5.1.0: test/TH.hs

{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TemplateHaskell #-}

module TH where

import Control.Monad.Trans (liftIO)
import Language.Haskell.TH
import QuasiMock
import Test.HMock
import Test.HMock.Internal.TH (resolveInstance, unifyTypes)
import Test.Hspec
import Test.Predicates (anything, eq)

data NotShowable

class NoInstances a

$(pure [])

thUtilSpec :: SpecWith ()
thUtilSpec = do
  describe "unifyTypes" $ do
    it "accepts identical types" $
      example $
        runMockT $ do
          result <- runQ $ unifyTypes (ConT ''Bool) (ConT ''Bool)
          liftIO $ result `shouldBe` Just []

    it "rejects different types" $
      example $
        runMockT $ do
          result <- runQ $ unifyTypes (ConT ''Bool) (ConT ''Int)
          liftIO $ result `shouldBe` Nothing

    it "unifies patterns with variables" $
      example $
        runMockT $ do
          v <- runQ $ newName "a"
          t1 <- runQ [t|Maybe ($(varT v), Int)|]
          t2 <- runQ [t|Maybe (Bool, Int)|]
          result <- runQ $ unifyTypes t1 t2
          liftIO $ result `shouldBe` Just [(v, ConT ''Bool)]

    it "substitutes synonyms" $
      example $
        runMockT $ do
          v <- runQ $ newName "a"
          t1 <- runQ [t|[$(varT v)]|]
          t2 <- runQ [t|String|]
          result <- runQ $ unifyTypes t1 t2
          liftIO $ result `shouldBe` Just [(v, ConT ''Char)]

  describe "resolveInstance" $ do
    it "fails for missing instances" $
      example $
        runMockT $ do
          allowUnexpected $ QReifyInstances_ (eq ''NoInstances) anything |-> []
          result <- runQ $ resolveInstance ''NoInstances [ConT ''String]
          liftIO $ result `shouldBe` Nothing

    it "finds unrestricted instances" $
      example $
        runMockT $ do
          result <- runQ $ resolveInstance ''Show [ConT ''String]
          liftIO $ result `shouldBe` Just []

    it "finds context on nested vars" $
      example $
        runMockT $ do
          v <- runQ $ newName "a"
          result <- runQ $ resolveInstance ''Show [AppT ListT (VarT v)]
          liftIO $ result `shouldBe` Just [AppT (ConT ''Show) (VarT v)]

    it "recognizes when a nested type lacks instance" $
      example $
        runMockT $ do
          $(onReify [|expectAny|] ''NotShowable)
          $(onReifyInstances [|expectAny|] ''Show [[t|NotShowable|]])
          $(onReifyInstances [|expectAny|] ''Show [[t|(Int, NotShowable)|]])

          t <- runQ [t|(Int, NotShowable)|]
          result <- runQ $ resolveInstance ''Show [t]
          liftIO $ result `shouldBe` Nothing