{-# 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