{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE AllowAmbiguousTypes #-} -- | Eq properties -- -- You will need @TypeApplications@ to use these. module Test.Validity.Eq ( eqSpecOnValid , eqSpecOnInvalid , eqSpec , eqSpecOnArbitrary , eqSpecOnGen ) where import Data.Data import Data.GenValidity import Test.Hspec import Test.QuickCheck import Test.Validity.Functions import Test.Validity.Relations import Test.Validity.Utils eqTypeStr :: forall a. Typeable a => String eqTypeStr = binRelStr @a "==" neqTypeStr :: forall a. Typeable a => String neqTypeStr = binRelStr @a "/=" -- | Standard test spec for properties of Eq instances for valid values -- -- Example usage: -- -- > eqSpecOnValid @Double eqSpecOnValid :: forall a. (Show a, Eq a, Typeable a, GenValid a) => Spec eqSpecOnValid = eqSpecOnGen @a genValid "valid" shrinkValid -- | Standard test spec for properties of Eq instances for invalid values -- -- Example usage: -- -- > eqSpecOnInvalid @Double eqSpecOnInvalid :: forall a. (Show a, Eq a, Typeable a, GenInvalid a) => Spec eqSpecOnInvalid = eqSpecOnGen @a genInvalid "invalid" shrinkInvalid -- | Standard test spec for properties of Eq instances for unchecked values -- -- Example usage: -- -- > eqSpec @Int eqSpec :: forall a. (Show a, Eq a, Typeable a, GenUnchecked a) => Spec eqSpec = eqSpecOnGen @a genUnchecked "unchecked" shrinkUnchecked -- | Standard test spec for properties of Eq instances for arbitrary values -- -- Example usage: -- -- > eqSpecOnArbitrary @Int eqSpecOnArbitrary :: forall a. (Show a, Eq a, Typeable a, Arbitrary a) => Spec eqSpecOnArbitrary = eqSpecOnGen @a arbitrary "arbitrary" shrink -- | Standard test spec for properties of Eq instances for values generated by a given generator (and name for that generator). -- -- Example usage: -- -- > eqSpecOnGen ((* 2) <$> genValid @Int) "even" eqSpecOnGen :: forall a. (Show a, Eq a, Typeable a) => Gen a -> String -> (a -> [a]) -> Spec eqSpecOnGen gen genname s = parallel $ do let name = nameOf @a funeqstr = eqTypeStr @a funneqstr = neqTypeStr @a gen2 = (,) <$> gen <*> gen gen3 = (,,) <$> gen <*> gen <*> gen s2 = shrinkT2 s describe ("Eq " ++ name) $ do let eq = (==) @a neq = (/=) @a describe funeqstr $ do it (unwords [ "is reflexive for" , "\"" ++ genname , name ++ "\"" ++ "'s" ]) $ reflexivityOnGen eq gen s it (unwords [ "is symmetric for" , "\"" ++ genname , name ++ "\"" ++ "'s" ]) $ symmetryOnGens eq gen2 s it (unwords [ "is transitive for" , "\"" ++ genname , name ++ "\"" ++ "'s" ]) $ transitivityOnGens eq gen3 s it (unwords [ "is equivalent to (\\a b -> not $ a /= b) for" , "\"" ++ genname , name ++ "\"" ++ "'s" ]) $ equivalentOnGens2 eq (\a b -> not $ a `neq` b) gen2 s2 describe funneqstr $ do it (unwords [ "is antireflexive for" , "\"" ++ genname , name ++ "\"" ++ "'s" ]) $ antireflexivityOnGen neq gen s it (unwords [ "is equivalent to (\\a b -> not $ a == b) for" , "\"" ++ genname , name ++ "\"" ++ "'s" ]) $ equivalentOnGens2 neq (\a b -> not $ a `eq` b) gen2 s2