{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE AllowAmbiguousTypes #-} -- | Hashable properties -- -- You will need @TypeApplications@ to use these. module Test.Validity.Hashable ( hashableSpecOnValid , hashableSpecOnInvalid , hashableSpec , hashableSpecOnArbitrary , hashableSpecOnGen ) where import Data.Data import Data.Hashable import Control.Monad import Test.Validity.Utils import Data.GenValidity import Test.Hspec import Test.QuickCheck -- | Standard test spec for properties of Hashable instances for valid values -- -- Example usage: -- -- > hashableSpecOnValid @Double hashableSpecOnValid :: forall a. (Show a, Eq a, Typeable a, GenValid a, Hashable a) => Spec hashableSpecOnValid = hashableSpecOnGen @a genValid "valid" -- | Standard test spec for properties of Hashable instances for invalid values -- -- Example usage: -- -- > hashableSpecOnInvalid @Double hashableSpecOnInvalid :: forall a. (Show a, Eq a, Typeable a, GenInvalid a, Hashable a) => Spec hashableSpecOnInvalid = hashableSpecOnGen @a genInvalid "invalid" -- | Standard test spec for properties of Hashable instances for unchecked values -- -- Example usage: -- -- > hashableSpec @Int hashableSpec :: forall a. (Show a, Eq a, Typeable a, GenUnchecked a, Hashable a) => Spec hashableSpec = hashableSpecOnGen @a genUnchecked "unchecked" -- | Standard test spec for properties of Hashable instances for arbitrary values -- -- Example usage: -- -- > hashableSpecOnArbitrary @Int hashableSpecOnArbitrary :: forall a. (Show a, Eq a, Typeable a, Arbitrary a, Hashable a) => Spec hashableSpecOnArbitrary = hashableSpecOnGen @a arbitrary "arbitrary" -- | Standard test spec for properties of Hashable instances for values generated by a given generator (and name for that generator). -- -- Example usage: -- -- > hashableSpecOnGen ((* 2) <$> genValid @Int) "even" hashableSpecOnGen :: forall a. (Show a, Eq a, Typeable a, Hashable a) => Gen a -> String -> Spec hashableSpecOnGen gen genname = checkGen gen2 genname where gen2 = (,) <$> gen <*> gen -- | Test spec like hashableSpecOnGen but with a special generator -- | which is documented to generate equal values by (==) most of the time. checkGen :: forall a. (Show a, Eq a, Typeable a, Hashable a) => Gen (a, a) -> String -> Spec checkGen gen genname = parallel $ do let name = nameOf @a hashablestr = (unwords ["hashWithSalt :: Int ->" , name , "-> Int"]) describe ("Hashable " ++ name) $ do describe hashablestr $ do it (unwords [ "satisfies (a == b) => (hashWithSalt n a) ==" ,"(hashWithSalt n b), for every n and for" , genname , name ]) $ forAll gen $ \(a1, a2) -> forAll arbitrary $ \int -> when (a1 == a2) $ let hash = hashWithSalt int in hash a1 `shouldBe` hash a2