{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -F -pgmF htfpp #-} module Data.LargeHashable.Tests.Class where -- keep imports in alphabetic order (in Emacs, use "M-x sort-lines") import Data.Fixed import Data.Hashable import Data.Int import Data.LargeHashable import Data.LargeHashable.Tests.Helper import Data.Map (Map ()) import Data.Ratio import Data.Set (Set ()) import Data.Time.Calendar import Data.Time.Clock import Data.Time.Clock.TAI import Data.Time.LocalTime import Data.Word import Test.Framework hiding (Fixed (..)) import qualified Data.Aeson as J import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Short as BS import qualified Data.HashMap.Lazy as HML import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS import qualified Data.IntMap as IM import qualified Data.IntSet as IS import qualified Data.Map as Map import qualified Data.Scientific as Sci import qualified Data.Sequence as Seq import qualified Data.Set as Set import qualified Data.Strict.Tuple as Tuple import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.Vector as V -- of course we can't fully prove uniqueness using -- properties and there is a small chance of collisions generic_uniquenessProp :: (Eq a, LargeHashable a) => a -> a -> Bool generic_uniquenessProp a b = (a == b) == (largeHash md5HashAlgorithm a == largeHash md5HashAlgorithm b) prop_integerUniqueness :: Integer -> Integer -> Bool prop_integerUniqueness = generic_uniquenessProp test_hashInteger :: IO () test_hashInteger = do subAssert $ assertHashes 0 subAssert $ assertHashes (toInteger (maxBound::Int)) subAssert $ assertHashes (toInteger (minBound::Int)) where assertHashes :: Integer -> IO () assertHashes n = do let list = [(n-100)..(n+100)] hashes = map (largeHash md5HashAlgorithm) list hashesSet = Set.fromList hashes assertEqual (length list) (Set.size hashesSet) test_hashBool :: IO () test_hashBool = assertBool (largeHash md5HashAlgorithm True /= largeHash md5HashAlgorithm False) prop_genericUniqueness :: TestA -> TestA -> Bool prop_genericUniqueness = generic_uniquenessProp prop_intUniqueness :: Int -> Int -> Bool prop_intUniqueness = generic_uniquenessProp prop_int8Uniqueness :: Int8 -> Int8 -> Bool prop_int8Uniqueness = generic_uniquenessProp prop_int16Uniqueness :: Int16 -> Int16 -> Bool prop_int16Uniqueness = generic_uniquenessProp prop_int32Uniqueness :: Int32 -> Int32 -> Bool prop_int32Uniqueness = generic_uniquenessProp prop_int64Uniqueness :: Int64 -> Int64 -> Bool prop_int64Uniqueness = generic_uniquenessProp prop_wordUniqueness :: Word -> Word -> Bool prop_wordUniqueness = generic_uniquenessProp prop_word8Uniqueness :: Word8 -> Word8 -> Bool prop_word8Uniqueness = generic_uniquenessProp prop_word16Uniqueness :: Word16 -> Word16 -> Bool prop_word16Uniqueness = generic_uniquenessProp prop_word32Uniqueness :: Word32 -> Word32 -> Bool prop_word32Uniqueness = generic_uniquenessProp prop_word64Uniqueness :: Word64 -> Word64 -> Bool prop_word64Uniqueness = generic_uniquenessProp prop_charUniqueness :: Char -> Char -> Bool prop_charUniqueness = generic_uniquenessProp prop_bytestringUniqueness :: B.ByteString -> B.ByteString -> Bool prop_bytestringUniqueness = generic_uniquenessProp prop_appendByteStringOk :: B.ByteString -> B.ByteString -> Bool prop_appendByteStringOk b1 b2 = runMD5 (updateHash (b1 `B.append` b2)) /= runMD5 (updateHash b1 >> updateHash b2) test_irrelevantByteStringChunking :: IO () test_irrelevantByteStringChunking = do assertEqual (largeHash md5HashAlgorithm (BL.fromChunks ["foo", "ba", "r"])) (largeHash md5HashAlgorithm (BL.fromChunks ["foob", "ar"])) prop_lazyBytestringUniqueness :: BL.ByteString -> BL.ByteString -> Bool prop_lazyBytestringUniqueness = generic_uniquenessProp prop_appendLazyByteStringOk :: BL.ByteString -> BL.ByteString -> Bool prop_appendLazyByteStringOk b1 b2 = runMD5 (updateHash (b1 `BL.append` b2)) /= runMD5 (updateHash b1 >> updateHash b2) prop_shortBytestringUniqueness :: BS.ShortByteString -> BS.ShortByteString -> Bool prop_shortBytestringUniqueness = generic_uniquenessProp prop_textUniqueness :: T.Text -> T.Text -> Bool prop_textUniqueness = generic_uniquenessProp prop_appendTextOk :: T.Text -> T.Text -> Bool prop_appendTextOk t1 t2 = runMD5 (updateHash (t1 `T.append` t2)) /= runMD5 (updateHash t1 >> updateHash t2) test_irrelevantTextChunking :: IO () test_irrelevantTextChunking = do assertEqual (largeHash md5HashAlgorithm (TL.fromChunks ["don't", " pa", "nic"])) (largeHash md5HashAlgorithm (TL.fromChunks ["don", "'t p", "an", "ic"])) prop_lazyTextUniqueness :: TL.Text -> TL.Text -> Bool prop_lazyTextUniqueness = generic_uniquenessProp prop_appendLazyTextOk :: TL.Text -> TL.Text -> Bool prop_appendLazyTextOk t1 t2 = runMD5 (updateHash (t1 `TL.append` t2)) /= runMD5 (updateHash t1 >> updateHash t2) prop_lazyStrictTextSame :: [T.Text] -> Bool prop_lazyStrictTextSame chunks = let lazy = TL.fromChunks chunks strict = mconcat chunks in runMD5 (updateHash lazy) == runMD5 (updateHash strict) prop_listUniqueness :: [Bool] -> [Bool] -> Bool prop_listUniqueness = generic_uniquenessProp prop_appendListOk :: [Int] -> [Int] -> Bool prop_appendListOk l1 l2 = runMD5 (updateHash (l1 ++ l2)) /= runMD5 (updateHash l1 >> updateHash l2) prop_DoubleUniqueness :: Double -> Double -> Bool prop_DoubleUniqueness = generic_uniquenessProp prop_FloatUniqueness :: Float -> Float -> Bool prop_FloatUniqueness = generic_uniquenessProp prop_setUniqueness :: Set Int -> Set Int -> Bool prop_setUniqueness = generic_uniquenessProp test_hashSet :: IO () test_hashSet = do let s1 = Set.fromList [1::Int, 2, 3] s2 = Set.fromList [3::Int, 2, 1] s3 = Set.fromList [2::Int, 1, 3] assertEqual (largeHash md5HashAlgorithm s1) (largeHash md5HashAlgorithm s2) assertEqual (largeHash md5HashAlgorithm s1) (largeHash md5HashAlgorithm s3) assertEqual (largeHash md5HashAlgorithm s2) (largeHash md5HashAlgorithm s3) prop_unionSetOk :: Set Int -> Set Int -> Bool prop_unionSetOk s1 s2 = runMD5 (updateHash (s1 `Set.union` s2)) /= runMD5 (updateHash s1 >> updateHash s2) prop_intSetUniqueness :: IS.IntSet -> IS.IntSet -> Bool prop_intSetUniqueness = generic_uniquenessProp test_hashIntSet :: IO () test_hashIntSet = do let s1 = IS.fromList [1::Int, 2, 3] s2 = IS.fromList [3::Int, 2, 1] s3 = IS.fromList [2::Int, 1, 3] assertEqual (largeHash md5HashAlgorithm s1) (largeHash md5HashAlgorithm s2) assertEqual (largeHash md5HashAlgorithm s1) (largeHash md5HashAlgorithm s3) assertEqual (largeHash md5HashAlgorithm s2) (largeHash md5HashAlgorithm s3) prop_unionIntSetOk :: IS.IntSet -> IS.IntSet -> Bool prop_unionIntSetOk s1 s2 = runMD5 (updateHash (s1 `IS.union` s2)) /= runMD5 (updateHash s1 >> updateHash s2) prop_hashSetUniqueness :: HS.HashSet Int -> HS.HashSet Int -> Bool prop_hashSetUniqueness = generic_uniquenessProp data ConstHash = ConstHash Int deriving (Eq) instance Hashable ConstHash where hashWithSalt _ _ = 0 instance LargeHashable ConstHash where updateHash (ConstHash i) = updateHash i updateHashStable (ConstHash i) = updateHashStable i test_hashHashSet :: IO () test_hashHashSet = do let s1 = HS.fromList [ConstHash 1, ConstHash 2, ConstHash 3] s2 = HS.fromList [ConstHash 3, ConstHash 2, ConstHash 1] s3 = HS.fromList [ConstHash 2, ConstHash 1, ConstHash 3] assertEqual (largeHash md5HashAlgorithm s1) (largeHash md5HashAlgorithm s2) assertEqual (largeHash md5HashAlgorithm s1) (largeHash md5HashAlgorithm s3) assertEqual (largeHash md5HashAlgorithm s2) (largeHash md5HashAlgorithm s3) prop_unionHashSetOk :: HS.HashSet Int -> HS.HashSet Int -> Bool prop_unionHashSetOk s1 s2 = runMD5 (updateHash (s1 `HS.union` s2)) /= runMD5 (updateHash s1 >> updateHash s2) prop_mapUniqueness :: Map Int String -> Map Int String -> Bool prop_mapUniqueness = generic_uniquenessProp test_hashMap :: IO () test_hashMap = do let m1 = Map.fromList [(1::Int, "1"::String), (2, "2"), (3, "3")] m2 = Set.fromList [(3::Int, "3"::String), (2, "2"), (1, "1")] m3 = Set.fromList [(2::Int, "2"::String), (1, "1"), (3, "3")] assertEqual (largeHash md5HashAlgorithm m1) (largeHash md5HashAlgorithm m2) assertEqual (largeHash md5HashAlgorithm m1) (largeHash md5HashAlgorithm m3) assertEqual (largeHash md5HashAlgorithm m2) (largeHash md5HashAlgorithm m3) prop_unionMapOk :: Map Int Bool -> Map Int Bool -> Bool prop_unionMapOk m1 m2 = runMD5 (updateHash (m1 `Map.union` m2)) /= runMD5 (updateHash m1 >> updateHash m2) prop_intMapUniqueness :: IM.IntMap String -> IM.IntMap String -> Bool prop_intMapUniqueness = generic_uniquenessProp test_hashIntMap :: IO () test_hashIntMap = do let m1 = IM.fromList [(1::Int, "1"::String), (2, "2"), (3, "3")] m2 = IM.fromList [(3::Int, "3"::String), (2, "2"), (1, "1")] m3 = IM.fromList [(2::Int, "2"::String), (1, "1"), (3, "3")] assertEqual (largeHash md5HashAlgorithm m1) (largeHash md5HashAlgorithm m2) assertEqual (largeHash md5HashAlgorithm m1) (largeHash md5HashAlgorithm m3) assertEqual (largeHash md5HashAlgorithm m2) (largeHash md5HashAlgorithm m3) prop_unionIntMapOk :: IM.IntMap Bool -> IM.IntMap Bool -> Bool prop_unionIntMapOk m1 m2 = runMD5 (updateHash (m1 `IM.union` m2)) /= runMD5 (updateHash m1 >> updateHash m2) prop_hashMapUniqueness :: HM.HashMap Int Bool -> HM.HashMap Int Bool -> Bool prop_hashMapUniqueness = generic_uniquenessProp test_hashHashMap :: IO () test_hashHashMap = do let val = "foo" :: String m1 = HM.fromList [(ConstHash 1, val), (ConstHash 2, val), (ConstHash 3, val)] m2 = HM.fromList [(ConstHash 3, val), (ConstHash 2, val), (ConstHash 1, val)] m3 = HM.fromList [(ConstHash 2, val), (ConstHash 1, val), (ConstHash 3, val)] assertEqual (largeHash md5HashAlgorithm m1) (largeHash md5HashAlgorithm m2) assertEqual (largeHash md5HashAlgorithm m1) (largeHash md5HashAlgorithm m3) assertEqual (largeHash md5HashAlgorithm m2) (largeHash md5HashAlgorithm m3) prop_unionHashMapOk :: HM.HashMap Int Bool -> HM.HashMap Int Bool -> Bool prop_unionHashMapOk m1 m2 = runMD5 (updateHash (m1 `HM.union` m2)) /= runMD5 (updateHash m1 >> updateHash m2) prop_hashMapLazyUniqueness :: HML.HashMap Int Bool -> HML.HashMap Int Bool -> Bool prop_hashMapLazyUniqueness = generic_uniquenessProp test_hashHashMapLazy :: IO () test_hashHashMapLazy = do let val = "foo" :: String m1 = HML.fromList [(ConstHash 1, val), (ConstHash 2, val), (ConstHash 3, val)] m2 = HML.fromList [(ConstHash 3, val), (ConstHash 2, val), (ConstHash 1, val)] m3 = HML.fromList [(ConstHash 2, val), (ConstHash 1, val), (ConstHash 3, val)] assertEqual (largeHash md5HashAlgorithm m1) (largeHash md5HashAlgorithm m2) assertEqual (largeHash md5HashAlgorithm m1) (largeHash md5HashAlgorithm m3) assertEqual (largeHash md5HashAlgorithm m2) (largeHash md5HashAlgorithm m3) prop_hashPairUniqueness :: (Int, Int) -> (Int, Int) -> Bool prop_hashPairUniqueness = generic_uniquenessProp prop_hashTripleUniqueness :: (Int, Int, Int) -> (Int, Int, Int) -> Bool prop_hashTripleUniqueness = generic_uniquenessProp prop_hashQuadrupleUniqueness :: (Int, Int, Int, Int) -> (Int, Int, Int, Int) -> Bool prop_hashQuadrupleUniqueness = generic_uniquenessProp prop_hashQuintupleUniqueness :: (Int, Int, Int, Int, Int) -> (Int, Int, Int, Int, Int) -> Bool prop_hashQuintupleUniqueness = generic_uniquenessProp prop_hashStrictPairUniqueness :: Tuple.Pair Int Int -> Tuple.Pair Int Int -> Bool prop_hashStrictPairUniqueness = generic_uniquenessProp prop_hashMaybeUniqueness :: Maybe Int -> Maybe Int -> Bool prop_hashMaybeUniqueness = generic_uniquenessProp prop_hashEitherUniqueness :: Either Int Bool -> Either Int Bool -> Bool prop_hashEitherUniqueness = generic_uniquenessProp prop_hashOrderingUniqueness :: Ordering -> Ordering -> Bool prop_hashOrderingUniqueness = generic_uniquenessProp prop_ratioUniqueness :: Ratio Int -> Ratio Int -> Bool prop_ratioUniqueness = generic_uniquenessProp test_ratio :: IO () test_ratio = do let r1 = (1::Int) % 2 r2 = (2::Int) % 4 assertEqual (largeHash md5HashAlgorithm r1) (largeHash md5HashAlgorithm r2) prop_dayUniqueness :: Day -> Day -> Bool prop_dayUniqueness = generic_uniquenessProp prop_diffTimeUniqueness :: DiffTime -> DiffTime -> Bool prop_diffTimeUniqueness = generic_uniquenessProp prop_utcTimeUniqueness :: UTCTime -> UTCTime -> Bool prop_utcTimeUniqueness = generic_uniquenessProp prop_absoluteTimeUniqueness :: AbsoluteTime -> AbsoluteTime -> Bool prop_absoluteTimeUniqueness = generic_uniquenessProp prop_nominalDiffTimeUniqueness :: NominalDiffTime -> NominalDiffTime -> Bool prop_nominalDiffTimeUniqueness = generic_uniquenessProp prop_timeZoneUniqueness :: TimeZone -> TimeZone -> Bool prop_timeZoneUniqueness = generic_uniquenessProp prop_timeOfDayUniqueness :: TimeOfDay -> TimeOfDay -> Bool prop_timeOfDayUniqueness = generic_uniquenessProp prop_localTimeUniqueness :: LocalTime -> LocalTime -> Bool prop_localTimeUniqueness = generic_uniquenessProp prop_fixedUniqueness :: Fixed E1 -> Fixed E1 -> Bool prop_fixedUniqueness = generic_uniquenessProp prop_scientificUniqueness :: Sci.Scientific -> Sci.Scientific -> Bool prop_scientificUniqueness = generic_uniquenessProp prop_aesonValueUniqueness :: J.Value -> J.Value -> Bool prop_aesonValueUniqueness = generic_uniquenessProp prop_vectorUniqueness :: V.Vector Int -> V.Vector Int -> Bool prop_vectorUniqueness = generic_uniquenessProp prop_appendVectorOk :: V.Vector Int -> V.Vector Int -> Bool prop_appendVectorOk v1 v2 = runMD5 (updateHash (v1 V.++ v2)) /= runMD5 (updateHash v1 >> updateHash v2) prop_seqUniqueness :: Seq.Seq Int -> Seq.Seq Int -> Bool prop_seqUniqueness = generic_uniquenessProp prop_appendSeqOk :: Seq.Seq Int -> Seq.Seq Int -> Bool prop_appendSeqOk s1 s2 = runMD5 (updateHash (s1 Seq.>< s2)) /= runMD5 (updateHash s1 >> updateHash s2) -- regression test for #25 test_textHash :: IO () test_textHash = do let t1 = T.pack "abcdefgh" t2 = T.pack "abcdxxxx" assertEqual (largeHash md5HashAlgorithm (T.take 4 t1)) (largeHash md5HashAlgorithm (T.take 4 t2)) assertNotEqual (largeHash md5HashAlgorithm (T.take 5 t1)) (largeHash md5HashAlgorithm (T.take 5 t2))