{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} module Decoder.Laws (decoderLaws) where import Control.Applicative (Applicative, pure) import Control.Monad.Except (throwError) import Data.Functor.Alt (Alt (())) import Data.Functor.Identity (Identity) import Test.Tasty (TestTree, testGroup) import Test.Tasty.Hedgehog (testProperty) import Hedgehog import qualified Hedgehog.Gen as Gen import qualified Waargonaut.Attoparsec as WA import qualified Waargonaut.Decode as D import Waargonaut.Decode.Error (DecodeError (ConversionFailure)) import Waargonaut.Decode.Types (Decoder) import qualified Laws runD :: Decoder Identity a -> Either (DecodeError, D.CursorHistory) a runD d = WA.pureDecodeAttoparsecText d "true" newtype ShowDecoder a = SD (Decoder Identity a) deriving (Functor, Monad, Applicative) instance Alt ShowDecoder where (SD a) (SD b) = SD (a b) instance Eq a => Eq (ShowDecoder a) where (SD a) == (SD b) = runD a == runD b instance Show a => Show (ShowDecoder a) where show (SD d) = show $ runD d genShowDecoder :: Gen a -> Gen (ShowDecoder a) genShowDecoder genA = Gen.choice [ SD . pure <$> genA , SD <$> Gen.constant (throwError $ ConversionFailure "Intentional DecodeError (TEST)") ] decoderLaws :: TestTree decoderLaws = testGroup "Decoder Laws" [ testGroup "Applicative" [ testProperty "identity" $ Laws.applicative_id genShowDecoder Gen.bool , testProperty "composition" $ Laws.applicative_composition genShowDecoder Gen.bool Gen.bool Gen.bool , testProperty "homomorphism" $ Laws.applicative_homomorphism sdPure Gen.bool Gen.bool , testProperty "interchange" $ Laws.applicative_interchange sdPure Gen.bool Gen.bool ] , testGroup "Alt" [ testProperty "associativity" $ Laws.alt_associativity genShowDecoder Gen.bool , testProperty "left distributes" $ Laws.alt_left_distributes genShowDecoder Gen.bool Gen.bool ] , testGroup "Monad" [ testProperty "return a >>= k = k a" $ Laws.monad_return_bind genShowDecoder Gen.bool Gen.bool , testProperty "m >>= return = m" $ Laws.monad_bind_return_id genShowDecoder Gen.bool , testProperty "associativity" $ Laws.monad_associativity genShowDecoder Gen.bool Gen.bool Gen.bool ] , testGroup "Functor" [ testProperty "'fmap compose'" $ Laws.fmap_compose genShowDecoder Gen.bool Gen.bool Gen.bool ] ] where sdPure = (pure :: a -> ShowDecoder a)