packages feed

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