packages feed

method-0.3.0.0: test/Test/Method/DynamicSpec.hs

{-# LANGUAGE TypeApplications #-}

module Test.Method.DynamicSpec where

import Control.Exception (evaluate)
import Control.Exception.Base (ErrorCall (ErrorCall))
import Data.Proxy (Proxy (Proxy), asProxyTypeOf)
import Test.Hspec
  ( Expectation,
    Spec,
    anyErrorCall,
    describe,
    it,
    shouldBe,
    shouldReturn,
    shouldThrow,
  )
import Test.Method.Dynamic
  ( Dynamic,
    DynamicShow,
    FromDyn (fromDyn),
    ToDyn (toDyn),
    Typeable,
    castMethod,
    dynArg,
  )
import Test.Method.Matcher
  ( ArgsMatcher (args),
    Matcher,
    anything,
    when,
  )
import Test.Method.Mock (mockup, thenReturn, throwNoStub)

spec :: Spec
spec = do
  describe "fromDyn" $ do
    let fid :: (FromDyn b a, ToDyn b a, Show a, Eq a) => Proxy b -> a -> Expectation
        fid proxy x = fromDyn (toDyn x `asProxyTypeOf` proxy) `shouldBe` x
    it "Int -> Int" $ fid (Proxy @Int) (0 :: Int)
    it "Dynamic -> Int" $ fid (Proxy @Dynamic) (0 :: Int)
    it "DynamicShow ->Int" $ fid (Proxy @DynamicShow) (0 :: Int)
    it "[Dynamic] -> [Int]" $ fid (Proxy @[Dynamic]) [0 :: Int]
    it "Maybe Dynamic -> Maybe ()" $ fid (Proxy @(Maybe Dynamic)) (Nothing :: Maybe ())
    it "Either Dynamic Bool -> Either Int Bool" $ fid (Proxy @(Either Dynamic Bool)) (Left 0 :: Either Int Bool)
    it "(Int, Dynamic) -> (Int, Int)" $ fid (Proxy @(Int, Dynamic)) (0 :: Int, 0 :: Int)
    it "Dynamic^7 -> Char^7" $
      fid
        (Proxy @(Dynamic, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic, Dynamic))
        ('a', 'b', 'c', 'd', 'e', 'f', 'g')
    it "((Int, Dynamic) -> (Dynamic, Int)) -> ((Int, Int) -> (Int, Int))" $ do
      let f :: (Int, Int) -> (Int, Int)
          f = fromDyn (toDyn (id :: (Int, Int) -> (Int, Int)) :: ((Int, Dynamic) -> (Dynamic, Int)))
      f (42, 57) `shouldBe` (42, 57)

    it "throw type error" $
      evaluate (fromDyn (toDyn True :: Dynamic) :: Char)
        `shouldThrow` ( \(ErrorCall msg) ->
                          msg == "cannot cast <<Bool>> to Char"
                      )

  describe "Show DynamicShow" $ do
    it "show (DynamicShow (0 :: Int)) == \"<<0 :: Int>>\"" $ do
      show (toDyn (0 :: Int) :: DynamicShow) `shouldBe` "<<0 :: Int>>"
  describe "castMethod" $ do
    let f :: (Typeable a, Show a) => String -> a -> IO a
        f = castMethod f'
        f' :: String -> DynamicShow -> IO DynamicShow
        f' = mockup $ do
          when (args ((== "int"), dynArg (== (10 :: Int)))) `thenReturn` toDyn (20 :: Int)
          when (args ((== "bool"), dynArg (== True))) `thenReturn` toDyn False
          when (args ((== "invalid"), dynArg (anything :: Matcher Bool))) `thenReturn` toDyn 'A'
          throwNoStub (when anything)
    it "polymorphic" $ do
      f "int" (10 :: Int) `shouldReturn` 20
      f "bool" True `shouldReturn` False
      (f "invalid" True >>= evaluate) `shouldThrow` anyErrorCall
      f "invalid" 'a' `shouldThrow` anyErrorCall