Skip to content

Commit 744ffdb

Browse files
committed
Add Expr to make better generator for tests
1 parent 450bf5e commit 744ffdb

File tree

5 files changed

+127
-23
lines changed

5 files changed

+127
-23
lines changed

tests/HashMapProperties.hs

+1-6
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,7 @@ import Test.QuickCheck (Arbitrary, Property, (==>), (===))
2121
import Test.Framework (Test, defaultMain, testGroup)
2222
import Test.Framework.Providers.QuickCheck2 (testProperty)
2323

24-
-- Key type that generates more hash collisions.
25-
newtype Key = K { unK :: Int }
26-
deriving (Arbitrary, Eq, Ord, Read, Show)
27-
28-
instance Hashable Key where
29-
hashWithSalt salt k = hashWithSalt salt (unK k) `mod` 20
24+
import Utils
3025

3126
------------------------------------------------------------------------
3227
-- * Properties

tests/HashSetProperties.hs

+14-9
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,20 @@ import qualified Data.List as L
1111
import qualified Data.HashSet as S
1212
import qualified Data.Set as Set
1313
import Data.Ord (comparing)
14-
import Test.QuickCheck (Arbitrary, Property, (==>), (===))
14+
import Test.QuickCheck (Property, (==>), (===))
1515
import Test.Framework (Test, defaultMain, testGroup)
1616
import Test.Framework.Providers.QuickCheck2 (testProperty)
1717

18-
-- Key type that generates more hash collisions.
19-
newtype Key = K { unK :: Int }
20-
deriving (Arbitrary, Enum, Eq, Integral, Num, Ord, Read, Show, Real)
21-
22-
instance Hashable Key where
23-
hashWithSalt salt k = hashWithSalt salt (unK k) `mod` 20
18+
import Utils
2419

2520
------------------------------------------------------------------------
2621
-- * Properties
2722

2823
------------------------------------------------------------------------
2924
-- ** Instances
3025

31-
pEq :: [Key] -> [Key] -> Bool
32-
pEq xs = (Set.fromList xs ==) `eq` (S.fromList xs ==)
26+
pEq :: Expr Key () -> Expr Key () -> Bool
27+
pEq xs = (evalExprOrdSet xs ==) `eqExpr` (evalExprSet xs ==)
3328

3429
pNeq :: [Key] -> [Key] -> Bool
3530
pNeq xs = (Set.fromList xs /=) `eq` (S.fromList xs /=)
@@ -225,6 +220,16 @@ eq :: (Eq a, Hashable a, Ord a, Eq b)
225220
-> Bool -- ^ True if the functions are equivalent
226221
eq f g xs = g (S.fromList xs) == f (Set.fromList xs)
227222

223+
-- | Check that a function operating on a 'HashMap' is equivalent to
224+
-- one operating on a 'Model'.
225+
eqExpr :: (Eq a, Hashable a, Ord a, Eq b, Show b)
226+
=> (Model a -> b) -- ^ Function that modifies a 'Model' in the same
227+
-- way
228+
-> (S.HashSet a -> b) -- ^ Function that modified a 'HashSet'
229+
-> Expr a () -- ^ Initial content of the 'HashSet' and 'Model'
230+
-> Property -- ^ True if the functions are equivalent
231+
eqExpr f g xs = g (evalExprSet xs) === f (evalExprOrdSet xs)
232+
228233
eq_ :: (Eq a, Hashable a, Ord a)
229234
=> (Model a -> Model a) -- ^ Function that modifies a 'Model'
230235
-> (S.HashSet a -> S.HashSet a) -- ^ Function that modified a

tests/Strictness.hs

+3-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
module Main (main) where
55

6-
import Data.Hashable (Hashable(hashWithSalt))
6+
import Data.Hashable (Hashable)
77
import Test.ChasingBottoms.IsBottom
88
import Test.Framework (Test, defaultMain, testGroup)
99
import Test.Framework.Providers.QuickCheck2 (testProperty)
@@ -23,16 +23,11 @@ import Prelude hiding (all)
2323
import Data.HashMap.Strict (HashMap)
2424
import qualified Data.HashMap.Strict as HM
2525

26-
-- Key type that generates more hash collisions.
27-
newtype Key = K { unK :: Int }
28-
deriving (Arbitrary, Eq, Ord, Show)
29-
30-
instance Hashable Key where
31-
hashWithSalt salt k = hashWithSalt salt (unK k) `mod` 20
26+
import Utils
3227

3328
instance (Arbitrary k, Arbitrary v, Eq k, Hashable k) =>
3429
Arbitrary (HashMap k v) where
35-
arbitrary = HM.fromList `fmap` arbitrary
30+
arbitrary = evalExprStrict `fmap` arbitrary
3631

3732
instance Show (Int -> Int) where
3833
show _ = "<function>"

tests/Utils.hs

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+
-- | Test utilities
3+
module Utils
4+
( Key (..)
5+
, Expr (..)
6+
, evalExprStrict
7+
, evalExprLazy
8+
, evalExprSet
9+
, evalExprOrdSet
10+
) where
11+
12+
import Control.Applicative (liftA2, liftA3)
13+
import Data.Hashable (Hashable(hashWithSalt))
14+
import Test.QuickCheck (Arbitrary(arbitrary,shrink), oneof, sized)
15+
16+
import qualified Data.HashMap.Strict as HMS
17+
import qualified Data.HashMap.Lazy as HML
18+
import qualified Data.HashSet as HS
19+
import qualified Data.Set as Set
20+
21+
-- | A key with collision-prone Hashable instance
22+
------------------------------------------------------------------------
23+
24+
-- Key type that generates more hash collisions.
25+
newtype Key = K { unK :: Int }
26+
deriving (Arbitrary, Enum, Eq, Ord, Read, Show, Integral, Num, Real)
27+
28+
instance Hashable Key where
29+
hashWithSalt salt k = hashWithSalt salt (unK k) `mod` 20
30+
31+
-- | A HashMap/HashSet building expression
32+
------------------------------------------------------------------------
33+
34+
data Expr k v
35+
= ExprEmpty
36+
| ExprSingleton k v
37+
| ExprInsert k v (Expr k v)
38+
| ExprDelete k (Expr k v)
39+
| ExprUnion (Expr k v) (Expr k v)
40+
deriving Show
41+
42+
instance (Arbitrary k, Arbitrary v) => Arbitrary (Expr k v) where
43+
arbitrary = sized arb
44+
where
45+
arb n | n <= 0 = oneof leafs
46+
arb n = oneof $ leafs ++
47+
[ liftA3 ExprInsert arbitrary arbitrary (arb (n - 1))
48+
, liftA2 ExprDelete arbitrary (arb (n - 1))
49+
, liftA2 ExprUnion (arb (n `div` 2)) (arb (n - n `div` 2))
50+
]
51+
52+
leafs =
53+
[ return ExprEmpty
54+
, liftA2 ExprSingleton arbitrary arbitrary
55+
]
56+
57+
shrink ExprEmpty
58+
= []
59+
shrink (ExprSingleton k v) =
60+
ExprEmpty : uncurry ExprSingleton `map` shrink (k, v)
61+
shrink (ExprInsert k v e) =
62+
ExprEmpty : e : uncurry3 ExprInsert `map` shrink (k, v, e)
63+
shrink (ExprDelete k e) =
64+
ExprEmpty : e : uncurry ExprDelete `map` shrink (k, e)
65+
shrink (ExprUnion a b) =
66+
ExprEmpty : a : b : uncurry ExprUnion `map` shrink (a, b)
67+
68+
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
69+
uncurry3 f (a, b, c) = f a b c
70+
71+
evalExprStrict :: (Eq k, Hashable k) => Expr k v -> HMS.HashMap k v
72+
evalExprStrict = go
73+
where
74+
go ExprEmpty = HMS.empty
75+
go (ExprSingleton k v) = HMS.singleton k v
76+
go (ExprInsert k v e) = HMS.insert k v (go e)
77+
go (ExprDelete k e) = HMS.delete k (go e)
78+
go (ExprUnion a b) = HMS.union (go a) (go b)
79+
80+
evalExprLazy :: (Eq k, Hashable k) => Expr k v -> HML.HashMap k v
81+
evalExprLazy = go
82+
where
83+
go ExprEmpty = HML.empty
84+
go (ExprSingleton k v) = HML.singleton k v
85+
go (ExprInsert k v e) = HML.insert k v (go e)
86+
go (ExprDelete k e) = HML.delete k (go e)
87+
go (ExprUnion a b) = HML.union (go a) (go b)
88+
89+
evalExprSet :: (Eq k, Hashable k) => Expr k () -> HS.HashSet k
90+
evalExprSet = go
91+
where
92+
go ExprEmpty = HS.empty
93+
go (ExprSingleton k _) = HS.singleton k
94+
go (ExprInsert k _ e) = HS.insert k (go e)
95+
go (ExprDelete k e) = HS.delete k (go e)
96+
go (ExprUnion a b) = HS.union (go a) (go b)
97+
98+
evalExprOrdSet :: (Ord k) => Expr k () -> Set.Set k
99+
evalExprOrdSet = go
100+
where
101+
go ExprEmpty = Set.empty
102+
go (ExprSingleton k _) = Set.singleton k
103+
go (ExprInsert k _ e) = Set.insert k (go e)
104+
go (ExprDelete k e) = Set.delete k (go e)
105+
go (ExprUnion a b) = Set.union (go a) (go b)

unordered-containers.cabal

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ library
6161
test-suite hashmap-lazy-properties
6262
hs-source-dirs: tests
6363
main-is: HashMapProperties.hs
64+
other-modules: Utils
6465
type: exitcode-stdio-1.0
6566

6667
build-depends:
@@ -78,6 +79,7 @@ test-suite hashmap-lazy-properties
7879
test-suite hashmap-strict-properties
7980
hs-source-dirs: tests
8081
main-is: HashMapProperties.hs
82+
other-modules: Utils
8183
type: exitcode-stdio-1.0
8284

8385
build-depends:
@@ -95,6 +97,7 @@ test-suite hashmap-strict-properties
9597
test-suite hashset-properties
9698
hs-source-dirs: tests
9799
main-is: HashSetProperties.hs
100+
other-modules: Utils
98101
type: exitcode-stdio-1.0
99102

100103
build-depends:
@@ -147,6 +150,7 @@ test-suite regressions
147150
test-suite strictness-properties
148151
hs-source-dirs: tests
149152
main-is: Strictness.hs
153+
other-modules: Utils
150154
type: exitcode-stdio-1.0
151155

152156
build-depends:

0 commit comments

Comments
 (0)