quickcheck-classes-0.6.0.0: src/Test/QuickCheck/Classes/Generic.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
#if HAVE_QUANTIFIED_CONSTRAINTS
{-# LANGUAGE QuantifiedConstraints #-}
#endif
{-# OPTIONS_GHC -Wall #-}
module Test.QuickCheck.Classes.Generic
(
#if MIN_VERSION_base(4,5,0)
genericLaws
#if HAVE_UNARY_LAWS
, generic1Laws
#endif
#endif
) where
#if MIN_VERSION_base(4,5,0)
import Control.Applicative
import Data.Semigroup as SG
import Data.Monoid as MD
import GHC.Generics
#if HAVE_UNARY_LAWS
import Data.Functor.Classes
#endif
import Data.Proxy (Proxy(Proxy))
import Test.QuickCheck
import Test.QuickCheck.Property (Property)
import Test.QuickCheck.Classes.Common (Laws(..), Apply(..))
-- | Tests the following properties:
--
-- [/From-To Inverse/]
-- @'from' '.' 'to' ≡ 'id'@
-- [/To-From Inverse/]
-- @'to' '.' 'from' ≡ 'id'@
--
-- /Note:/ This property test is only available when
-- using @base-4.5@ or newer.
--
-- /Note:/ 'from' and 'to' don't actually care about
-- the type variable @x@ in @'Rep' a x@, so here we instantiate
-- it to @'()'@ by default. If you would like to instantiate @x@
-- as something else, please file a bug report.
genericLaws :: (Generic a, Eq a, Arbitrary a, Show a, Show (Rep a ()), Arbitrary (Rep a ()), Eq (Rep a ())) => Proxy a -> Laws
genericLaws pa = Laws "Generic"
[ ("From-To inverse", fromToInverse pa (Proxy :: Proxy ()))
, ("To-From inverse", toFromInverse pa)
]
toFromInverse :: forall proxy a. (Generic a, Eq a, Arbitrary a, Show a) => proxy a -> Property
toFromInverse _ = property $ \(v :: a) -> (to . from $ v) == v
fromToInverse ::
forall proxy a x.
(Generic a, Show (Rep a x), Arbitrary (Rep a x), Eq (Rep a x))
=> proxy a
-> proxy x
-> Property
fromToInverse _ _ = property $ \(r :: Rep a x) -> r == (from (to r :: a))
#if HAVE_UNARY_LAWS
-- | Tests the following properties:
--
-- [/From-To Inverse/]
-- @'from1' '.' 'to1' ≡ 'id'@
-- [/To-From Inverse/]
-- @'to1' '.' 'from1' ≡ 'id'@
--
-- /Note:/ This property test is only available when
-- using @base-4.9@ or newer.
generic1Laws :: (Generic1 f, Eq1 f, Arbitrary1 f, Show1 f, Eq1 (Rep1 f), Show1 (Rep1 f), Arbitrary1 (Rep1 f))
=> proxy f -> Laws
generic1Laws p = Laws "Generic1"
[ ("From1-To1 inverse", fromToInverse1 p)
, ("To1-From1 inverse", toFromInverse1 p)
]
-- hack for quantified constraints: under base >= 4.12,
-- our usual 'Apply' wrapper has Eq, Show, and Arbitrary
-- instances that are incompatible.
newtype GApply f a = GApply { getGApply :: f a }
instance (Applicative f, Semigroup a) => Semigroup (GApply f a) where
GApply x <> GApply y = GApply $ liftA2 (SG.<>) x y
instance (Applicative f, Monoid a) => Monoid (GApply f a) where
mempty = GApply $ pure mempty
mappend (GApply x) (GApply y) = GApply $ liftA2 (MD.<>) x y
instance (Eq1 f, Eq a) => Eq (GApply f a) where
GApply a == GApply b = eq1 a b
instance (Show1 f, Show a) => Show (GApply f a) where
showsPrec p = showsPrec1 p . getGApply
instance (Arbitrary1 f, Arbitrary a) => Arbitrary (GApply f a) where
arbitrary = fmap GApply arbitrary1
shrink = map GApply . shrink1 . getGApply
toFromInverse1 :: forall proxy f. (Generic1 f, Eq1 f, Arbitrary1 f, Show1 f) => proxy f -> Property
toFromInverse1 _ = property $ \(GApply (v :: f Integer)) -> eq1 v (to1 . from1 $ v)
fromToInverse1 :: forall proxy f. (Generic1 f, Eq1 (Rep1 f), Arbitrary1 (Rep1 f), Show1 (Rep1 f)) => proxy f -> Property
fromToInverse1 _ = property $ \(GApply (r :: Rep1 f Integer)) -> eq1 r (from1 ((to1 $ r) :: f Integer))
#endif
#endif