hspec-hashable (empty) → 0.1.0.0
raw patch · 5 files changed
+192/−0 lines, 5 filesdep +QuickCheckdep +basedep +hashablesetup-changed
Dependencies added: QuickCheck, base, hashable, hspec, hspec-core, hspec-hashable, silently
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- hspec-hashable.cabal +41/−0
- src/Test/Hspec/Hashable.hs +118/−0
- test/Spec.hs +1/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Plow Technologies (c) 2016++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Plow Technologies, James M.C. Haver II, nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hspec-hashable.cabal view
@@ -0,0 +1,41 @@+name: hspec-hashable+version: 0.1.0.0+synopsis: Initial project template from stack+description: Please see README.md+homepage: https://github.com/plow-technologies/hspec-hashable#readme+license: BSD3+license-file: LICENSE+author: James M.C. Haver II+maintainer: mchaver@gmail.com+copyright: 2016 Plow Technologies+category: Web+build-type: Simple+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Test.Hspec.Hashable+ build-depends: base >= 4.7 && < 5+ , hashable+ , hspec+ , QuickCheck+ default-language: Haskell2010+++test-suite test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: base+ , hashable+ , hspec+ , hspec-core+ , hspec-hashable+ , silently+ , QuickCheck+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/plow-technologies/hspec-hashable
+ src/Test/Hspec/Hashable.hs view
@@ -0,0 +1,118 @@+{-|+Module : Test.Hspec.Hashable+Description : Hashable testing functions+Copyright : (c) Plow Technologies, 2016+License : BSD3+Maintainer : mchaver@gmail.com+Stability : Beta+-}++{-# LANGUAGE ScopedTypeVariables #-}++module Test.Hspec.Hashable (+ -- * Introduction+ -- $introduction++ -- * Main functions+ -- $main+ testHashableUniqueness+ , testHashableUniquenessWithoutTypeable+ , testSelfEquality+ , testHashableCollision++ -- * Internal help functions+ -- $helperfunctions+ , dupsByMatchingSnd+ ) where++import Control.Arrow ((&&&))++import Data.Hashable+import Data.List+import Data.Proxy+import Data.Typeable++import Test.Hspec+import Test.QuickCheck++-- $main++-- | the main testing function, give it a sampleSize larger than zero (or it will fail) and it+-- will produce arbitrary elements to test the uniqueness of the created hash+-- for a particular type. Should use a large sample size to help find hash collisions.+testHashableUniqueness :: forall a. (Arbitrary a, Eq a, Hashable a, Show a, Typeable a)+ => Int -> Proxy a -> Spec+testHashableUniqueness sampleSize proxy = do+ case sampleSize <= 0 of+ True -> fail ("The sample size must be greater than zero. The sample size you provided is: " ++ show sampleSize ++ ".")+ False -> do+ testSelfEquality sampleSize typeName proxy+ testHashableCollision sampleSize typeName proxy+ where+ typeName = show . typeRep $ proxy++-- | same as 'testHashableUniqueness' but it does not require an instance+-- of typeable and you should pass the type name as a string so it appears+-- in the error message.+testHashableUniquenessWithoutTypeable :: forall a. (Arbitrary a, Eq a, Hashable a, Show a)+ => Int -> String -> Proxy a -> Spec+testHashableUniquenessWithoutTypeable sampleSize typeName proxy = do+ case sampleSize <= 0 of+ True -> fail ("The sample size must be greater than zero. The sample size you provided is: " ++ show sampleSize ++ ".")+ False -> do+ testSelfEquality sampleSize typeName proxy+ testHashableCollision sampleSize typeName proxy++-- | test whether or not the Eq instances is defined such that any value+-- equals itself. If it does not, then the testHashableCollision+-- testing function might not work as expected.+testSelfEquality :: forall a. (Arbitrary a, Eq a, Hashable a, Show a)+ => Int -> String -> Proxy a -> Spec+testSelfEquality sampleSize typeName Proxy =+ describe ("Values of " ++ typeName ++ " derive Eq.") $+ it "all values should be equal to themself. " $ do+ xs <- generate (vectorOf sampleSize (arbitrary :: Gen a))+ (and $ (\x -> x == x) <$> xs) `shouldBe` True++-- | test whether or not there is are hash collisions between unique values.+-- if there are you need to fix your definition of Hashable.+testHashableCollision :: forall a. (Arbitrary a, Eq a, Hashable a, Show a)+ => Int -> String -> Proxy a -> Spec+testHashableCollision sampleSize typeName Proxy =+ describe ("Hashed values of " ++ typeName) $+ it "all non-equivalent values should have unique hashes" $ do+ xs <- generate (vectorOf sampleSize (arbitrary :: Gen a))+ -- nub : remove duplicates in xs+ -- (id &&& hash): put x and hash of x in a tuple+ -- dupsByMatchingSnd: get any tuples that have the same hash value but+ -- have unique (non-equivalent) x values.+ let matchingHashesForUniqueXs = dupsByMatchingSnd [] $ (id &&& hash) <$> nub xs+ -- if the Eq and Hashable instances are well defined, the list should be empty+ matchingHashesForUniqueXs `shouldBe` []+++-- $helperfunctions++-- | filter a list by collecting all duplications of the second item of+-- the tuple and return both elements of the tuple.+dupsByMatchingSnd :: (Eq b) => [(a,b)] -> [(a,b)] -> [(a,b)]+dupsByMatchingSnd ys (x:xs) = newX ++ dupsByMatchingSnd (ys ++ [x]) xs+ where+ xDups = filter (\y -> (snd x) == (snd y)) (xs ++ ys)+ newX = if length xDups > 0+ then [x]+ else []+dupsByMatchingSnd _ [] = []+++-- $introduction+--+-- For every 'Hashable' instance of a type, each unique value of that type+-- should have a unique hash. Generally, a 'Generic' 'Hashable' instance of a type should+-- create a unique hash, and ideally these match the rules of type's 'Eq'+-- instance. Any values for that type that are equal should have the same hash+-- and any values that are not equal should have unique hashes.+-- There might still be cases where a 'Generic' 'Hashable' instance+-- breaks those expectations. There are also cases where you might implement+-- 'Hashable' by hand. This testing library assumes that you expect the+-- uniqueness of a type matches in `Eq` and `Hashable`.
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}