@@ -1,6 +1,7 @@
1−{-# LANGUAGE DataKinds #-}
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2−{-# LANGUAGE KindSignatures #-}
3+{-# LANGUAGE KindSignatures #-}
3−{-# LANGUAGE PolyKinds #-}
4+{-# LANGUAGE PolyKinds #-}
5{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
6{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
@@ -9,22 +10,31 @@ module Test.TypeRep.MapProperty where
9import Prelude hiding (lookup)
10import Prelude hiding (lookup)
11import Data.Proxy (Proxy (..))
12import Data.Proxy (Proxy (..))
13+import Data.Semigroup (Semigroup (..))
12import GHC.Stack (HasCallStack)
14import GHC.Stack (HasCallStack)
13import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
15import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
14import Hedgehog (MonadGen, PropertyT, forAll, property, (===))
16import Hedgehog (MonadGen, PropertyT, forAll, property, (===))
15import Test.Tasty (TestName, TestTree)
17import Test.Tasty (TestName, TestTree)
16import Test.Tasty.Hedgehog (testProperty)
18import Test.Tasty.Hedgehog (testProperty)
18−import Data.TypeRep.Map (TF (..), TypeRepMap, delete, fromList, insert, lookup, member)
20+import Data.TypeRep.Map (TF (..), TypeRepMap (..), delete, fromList, insert, lookup, member)
20import qualified Hedgehog.Gen as Gen
22import qualified Hedgehog.Gen as Gen
21import qualified Hedgehog.Range as Range
23import qualified Hedgehog.Range as Range
25+----------------------------------------------------------------------------
27+----------------------------------------------------------------------------
23type PropertyTest = [TestTree]
29type PropertyTest = [TestTree]
25prop :: HasCallStack => TestName -> PropertyT IO () -> PropertyTest
31prop :: HasCallStack => TestName -> PropertyT IO () -> PropertyTest
26prop testName = pure . testProperty testName . property
32prop testName = pure . testProperty testName . property
34+----------------------------------------------------------------------------
35+-- Map modification properties
36+----------------------------------------------------------------------------
28test_InsertLookup :: PropertyTest
38test_InsertLookup :: PropertyTest
29test_InsertLookup = prop "lookup k (insert k v m) == Just v" $ do
39test_InsertLookup = prop "lookup k (insert k v m) == Just v" $ do
30 m <- forAll genMap
40 m <- forAll genMap
@@ -50,6 +60,36 @@ test_DeleteMember = prop "member k . delete k == False" $ do
51 member @n (delete @n m) === False
61 member @n (delete @n m) === False
63+----------------------------------------------------------------------------
64+-- Semigroup and Monoid laws
65+----------------------------------------------------------------------------
67+-- This newtype is used to compare 'TypeRepMap's using only 'Fingerprint's. It's
68+-- not a good idea to write such `Eq` instance for `TypeRepMap` itself because
69+-- it doesn't compare values so it's not true equality. But this should be
71+newtype FpMap f = FpMap (TypeRepMap f)
72+ deriving (Show, Semigroup, Monoid)
74+instance Eq (FpMap f) where
75+ FpMap (TypeRepMap as1 bs1 _) == FpMap (TypeRepMap as2 bs2 _) =
76+ as1 == as2 && bs1 == bs2
78+test_SemigroupAssoc :: PropertyTest
79+test_SemigroupAssoc = prop "x <> (y <> z) == (x <> y) <> z" $ do
80+ x <- FpMap <$> forAll genMap
81+ y <- FpMap <$> forAll genMap
82+ z <- FpMap <$> forAll genMap
84+ (x <> (y <> z)) === ((x <> y) <> z)
86+test_MonoidIdentity :: PropertyTest
87+test_MonoidIdentity = prop "x <> mempty == mempty <> x == x" $ do
88+ x <- FpMap <$> forAll genMap
53----------------------------------------------------------------------------
93----------------------------------------------------------------------------
54-- Generators
94-- Generators
55----------------------------------------------------------------------------
95----------------------------------------------------------------------------