packages feed

FailT-0.1.0.0: tests/Test/Control/Monad/Trans/FailSpec.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-orphans #-}

module Test.Control.Monad.Trans.FailSpec (spec) where

import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Trans.Fail
import Data.Proxy
import Data.Semigroup
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck
import Test.QuickCheck.Classes

laws :: Laws -> Spec
laws Laws{lawsTypeclass, lawsProperties} =
  describe lawsTypeclass $ mapM_ (uncurry prop) lawsProperties

instance (Applicative m, Arbitrary e, Arbitrary a) => Arbitrary (FailT e m a) where
  arbitrary = FailT . pure <$> arbitrary

instance (Arbitrary e, Arbitrary1 m, Applicative m) => Arbitrary1 (FailT e m) where
  liftArbitrary gen =
    oneof
      [ FailT . pure . Left <$> arbitrary
      , FailT . pure . Right <$> gen
      ]
  {-# INLINE liftArbitrary #-}

spec :: Spec
spec = do
  describe "FailT" $ do
    prop "runFail" $ \err -> runFail (fail err :: Fail String ()) `shouldBe` Left err
    describe "Instance Laws" $ do
      let px = Proxy :: Proxy (FailT String Maybe (Sum Int))
      let px1 = Proxy :: Proxy (FailT String Maybe)
      mapM_ laws $
        [ eqLaws px
        , ordLaws px
        , showLaws px
        , showReadLaws px
        , monoidLaws px
        , semigroupLaws px
        , alternativeLaws px1
        , applicativeLaws px1
        , foldableLaws px1
        , functorLaws px1
        , monadLaws px1
        , monadZipLaws px1
        , traversableLaws px1
        ]
          ++ extraLaws px
    describe "mtl" $ do
      prop "MonadReader" $ \msg (x :: Int) -> do
        let t :: FailT String (ReaderT Int IO) Int
            t = do
              x' <- ask
              pure $ succ x'
        runReaderT (runFailT t) x `shouldReturn` Right (x + 1)
        runReaderT (runFailT (fail msg >> pure x)) x `shouldReturn` Left msg
      prop "MonadState" $ \msg (x :: Int) -> do
        let t :: FailT String (StateT Int IO) ()
            t = do
              x' <- get
              liftIO (x' `shouldBe` x)
              put (x' + 1)
              modify' succ
        execStateT (runFailT t) x `shouldReturn` (x + 2)
        runStateT (runFailT (fail msg >> modify' succ)) x `shouldReturn` (Left msg, x)

extraLaws :: (Semigroup a, Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> [Laws]
#if MIN_VERSION_quickcheck_classes(0,6,2)
extraLaws px = [semigroupMonoidLaws px]
#else
extraLaws _px = []
#endif