monoidmap-0.0.3.0: src/test/Examples/RecoveredMapSpec.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use any" #-}
{-# HLINT ignore "Use null" #-}
-- |
-- Copyright: © 2022–2025 Jonathan Knowles
-- License: Apache-2.0
--
module Examples.RecoveredMapSpec
where
import Prelude
import Data.Function
( on, (&) )
import Data.List
( nubBy )
import Data.Monoid
( Sum (..) )
import Data.Proxy
( Proxy (..) )
import Data.Semigroup
( Semigroup (stimes) )
import Data.Set
( Set )
import Data.Text
( Text )
import Data.Typeable
( Typeable, typeRep )
import Numeric.Natural
( Natural )
import Test.Hspec
( Spec, describe, it )
import Test.QuickCheck
( Arbitrary (..)
, CoArbitrary
, Fun
, Function
, NonNegative (..)
, Property
, Testable
, applyFun
, applyFun2
, applyFun3
, checkCoverage
, cover
, listOf
, shrinkMapBy
, (===)
)
import Test.QuickCheck.Classes
( eqLaws, functorLaws, monoidLaws, semigroupLaws, semigroupMonoidLaws )
import Test.QuickCheck.Classes.Hspec
( testLawsMany )
import Test.QuickCheck.Instances.Natural
()
import Test.QuickCheck.Instances.Text
()
import qualified Data.Map.Strict as OMap
import qualified Data.Set as Set
import qualified Examples.RecoveredMap as RMap
import qualified Test.QuickCheck as QC
spec :: Spec
spec = do
specFor (Proxy @Int) (Proxy @(Set Int))
specFor (Proxy @Int) (Proxy @(Set Natural))
specFor (Proxy @Int) (Proxy @(Sum Int))
specFor (Proxy @Int) (Proxy @(Sum Natural))
specFor (Proxy @Int) (Proxy @Text)
specFor
:: forall k v. () =>
( Arbitrary k
, Arbitrary v
, CoArbitrary k
, CoArbitrary v
, Eq v
, Function k
, Function v
, Monoid v
, Ord k
, Show k
, Show v
, Typeable k
, Typeable v
)
=> Proxy k
-> Proxy v
-> Spec
specFor keyType valueType = do
let description = mconcat
[ "RecoveredMap ("
, show (typeRep keyType)
, ") ("
, show (typeRep valueType)
, ")"
]
let property :: Testable t => t -> Property
property = checkCoverage . QC.property
describe description $ do
describe "Class laws" $ do
testLawsMany @(RMap.Map k v)
[ eqLaws
, monoidLaws
, semigroupLaws
, semigroupMonoidLaws
]
testLawsMany @(RMap.Map k)
[ functorLaws
]
describe "Conversion to and from lists" $ do
it "prop_fromList_toList" $
prop_fromList_toList
@k @v & property
describe "Empty" $ do
it "prop_empty_keysSet" $
prop_empty_keysSet
@k & property
it "prop_empty_lookup" $
prop_empty_lookup
@k @v & property
it "prop_empty_show" $
prop_empty_show
@k @v & property
it "prop_empty_toList" $
prop_empty_toList
@k @v & property
describe "Singleton" $ do
it "prop_singleton_keysSet" $
prop_singleton_keysSet
@k @v & property
it "prop_singleton_lookup" $
prop_singleton_lookup
@k @v & property
it "prop_singleton_show" $
prop_singleton_show
@k @v & property
it "prop_singleton_toList" $
prop_singleton_toList
@k @v & property
describe "Append" $ do
it "prop_append_toList" $
prop_append_toList
@k @v & property
describe "Times" $ do
it "prop_stimes_toList" $
prop_stimes_toList
@k @v & property
describe "Delete" $ do
it "prop_delete_lookup" $
prop_delete_lookup
@k @v & property
it "prop_delete_member" $
prop_delete_member
@k @v & property
it "prop_delete_toList" $
prop_delete_toList
@k @v & property
describe "Insert" $ do
it "prop_insert_lookup" $
prop_insert_lookup
@k @v & property
it "prop_insert_member" $
prop_insert_member
@k @v & property
it "prop_insert_toList" $
prop_insert_toList
@k @v & property
describe "Map" $ do
it "prop_map" $
prop_map
@k @v @v & property
it "prop_map_mempty" $
prop_map_mempty
@k @v @v & property
it "prop_mapWithKey" $
prop_mapWithKey
@k @v @v & property
describe "MapAccumL" $ do
it "prop_mapAccumL @Int" $
prop_mapAccumL @Int
@k @v @v & property
it "prop_mapAccumL @String" $
prop_mapAccumL @String
@k @v @v & property
describe "MapAccumR" $ do
it "prop_mapAccumR @Int" $
prop_mapAccumR @Int
@k @v @v & property
it "prop_mapAccumR @String" $
prop_mapAccumR @String
@k @v @v & property
describe "MapAccumWithKeyL" $ do
it "prop_mapAccumLWithKey @Int" $
prop_mapAccumLWithKey @Int
@k @v @v & property
it "prop_mapAccumLWithKey @String" $
prop_mapAccumLWithKey @String
@k @v @v & property
describe "MapAccumWithKeyR" $ do
it "prop_mapAccumRWithKey @Int" $
prop_mapAccumRWithKey @Int
@k @v @v & property
it "prop_mapAccumRWithKey @String" $
prop_mapAccumRWithKey @String
@k @v @v & property
--------------------------------------------------------------------------------
-- Conversion to and from lists
--------------------------------------------------------------------------------
prop_fromList_toList
:: forall k v. (Ord k, Show k, Eq v, Show v)
=> [(k, v)]
-> Property
prop_fromList_toList kvs =
(===)
(RMap.toList (RMap.fromList kvs))
(OMap.toList (OMap.fromList kvs))
& cover 10
(length kvs > 1 && length (nubBy ((==) `on` fst) kvs) /= length kvs)
"length kvs > 1 && length (nubBy ((==) `on` fst) kvs) /= length kvs"
& cover 10
(length kvs > 1 && length (nubBy ((==) `on` fst) kvs) == length kvs)
"length kvs > 1 && length (nubBy ((==) `on` fst) kvs) == length kvs"
--------------------------------------------------------------------------------
-- Empty
--------------------------------------------------------------------------------
prop_empty_keysSet
:: forall k. (Eq k, Show k)
=> Property
prop_empty_keysSet =
(===)
(RMap.keysSet (RMap.empty @k))
(OMap.keysSet (OMap.empty @k))
prop_empty_lookup
:: forall k v. (Ord k, Eq v, Show v)
=> k
-> Property
prop_empty_lookup k =
(===)
(RMap.lookup k (RMap.empty @k @v))
(OMap.lookup k (OMap.empty @k @v))
prop_empty_show
:: forall k v. (Show k, Show v)
=> Property
prop_empty_show =
(===)
(show (RMap.empty @k @v))
(show (OMap.empty @k @v))
prop_empty_toList
:: forall k v. (Eq k, Show k, Eq v, Show v)
=> Property
prop_empty_toList =
(===)
(RMap.toList (RMap.empty @k @v))
(OMap.toList (OMap.empty @k @v))
--------------------------------------------------------------------------------
-- Singleton
--------------------------------------------------------------------------------
prop_singleton_keysSet
:: forall k v. (Ord k, Show k)
=> k
-> v
-> Property
prop_singleton_keysSet k v =
(===)
(RMap.keysSet (RMap.singleton k v))
(OMap.keysSet (OMap.singleton k v))
prop_singleton_lookup
:: forall k v. (Ord k, Eq v, Show v)
=> k
-> v
-> Property
prop_singleton_lookup k v =
(===)
(RMap.lookup k (RMap.singleton k v))
(OMap.lookup k (OMap.singleton k v))
prop_singleton_show
:: forall k v. (Ord k, Show k, Show v)
=> k
-> v
-> Property
prop_singleton_show k v =
(===)
(show (RMap.singleton k v))
(show (OMap.singleton k v))
prop_singleton_toList
:: forall k v. (Ord k, Show k, Eq v, Show v)
=> k
-> v
-> Property
prop_singleton_toList k v =
(===)
(RMap.toList (RMap.singleton k v))
(OMap.toList (OMap.singleton k v))
--------------------------------------------------------------------------------
-- Append
--------------------------------------------------------------------------------
prop_append_toList
:: forall k v. (Ord k, Show k, Eq v, Show v)
=> [(k, v)]
-> [(k, v)]
-> Property
prop_append_toList kvs1 kvs2 =
(===)
(RMap.toList (RMap.fromList kvs1 <> RMap.fromList kvs2))
(OMap.toList (OMap.fromList kvs1 <> OMap.fromList kvs2))
& cover 10
(ks1 `Set.disjoint` ks2)
"ks1 `Set.disjoint` ks2"
& cover 10
(not (ks1 `Set.disjoint` ks2))
"not (ks1 `Set.disjoint` ks2)"
where
ks1 = Set.fromList (fst <$> kvs1)
ks2 = Set.fromList (fst <$> kvs2)
--------------------------------------------------------------------------------
-- Times
--------------------------------------------------------------------------------
prop_stimes_toList
:: forall k v. (Ord k, Show k, Eq v, Show v)
=> [(k, v)]
-> NonNegative Int
-> Property
prop_stimes_toList kvs (NonNegative n) =
(===)
(RMap.toList (stimes n (RMap.fromList kvs)))
(OMap.toList (stimes n (OMap.fromList kvs)))
& cover 1
(n == 0)
"n == 0"
& cover 1
(n == 1)
"n == 1"
& cover 10
(n >= 2)
"n >= 2"
--------------------------------------------------------------------------------
-- Delete
--------------------------------------------------------------------------------
prop_delete_lookup
:: forall k v. (Ord k, Eq v, Show v)
=> [(k, v)]
-> k
-> Property
prop_delete_lookup kvs k =
(===)
(RMap.lookup k (RMap.delete k (RMap.fromList kvs)))
(OMap.lookup k (OMap.delete k (OMap.fromList kvs)))
& cover 10
(filter ((== k) . fst) kvs == [])
"filter ((== k) . fst) kvs == []"
& cover 10
(filter ((== k) . fst) kvs /= [])
"filter ((== k) . fst) kvs /= []"
prop_delete_member
:: forall k v. (Ord k, Eq v)
=> [(k, v)]
-> k
-> Property
prop_delete_member kvs k =
(===)
(RMap.member k (RMap.delete k (RMap.fromList kvs)))
(OMap.member k (OMap.delete k (OMap.fromList kvs)))
& cover 10
(filter ((== k) . fst) kvs == [])
"filter ((== k) . fst) kvs == []"
& cover 10
(filter ((== k) . fst) kvs /= [])
"filter ((== k) . fst) kvs /= []"
prop_delete_toList
:: forall k v. (Ord k, Show k, Eq v, Show v)
=> [(k, v)]
-> k
-> Property
prop_delete_toList kvs k =
(===)
(RMap.toList (RMap.delete k (RMap.fromList kvs)))
(OMap.toList (OMap.delete k (OMap.fromList kvs)))
& cover 10
(filter ((== k) . fst) kvs == [])
"filter ((== k) . fst) kvs == []"
& cover 10
(filter ((== k) . fst) kvs /= [])
"filter ((== k) . fst) kvs /= []"
--------------------------------------------------------------------------------
-- Insert
--------------------------------------------------------------------------------
prop_insert_lookup
:: forall k v. (Ord k, Eq v, Show v)
=> [(k, v)]
-> k
-> v
-> Property
prop_insert_lookup kvs k v =
(===)
(RMap.lookup k (RMap.insert k v (RMap.fromList kvs)))
(OMap.lookup k (OMap.insert k v (OMap.fromList kvs)))
& cover 10
(filter ((== k) . fst) kvs == [])
"filter ((== k) . fst) kvs == []"
& cover 10
(filter ((== k) . fst) kvs /= [])
"filter ((== k) . fst) kvs /= []"
prop_insert_member
:: forall k v. (Ord k, Eq v)
=> [(k, v)]
-> k
-> v
-> Property
prop_insert_member kvs k v =
(===)
(RMap.member k (RMap.insert k v (RMap.fromList kvs)))
(OMap.member k (OMap.insert k v (OMap.fromList kvs)))
& cover 10
(filter ((== k) . fst) kvs == [])
"filter ((== k) . fst) kvs == []"
& cover 10
(filter ((== k) . fst) kvs /= [])
"filter ((== k) . fst) kvs /= []"
prop_insert_toList
:: forall k v. (Ord k, Show k, Eq v, Show v)
=> [(k, v)]
-> k
-> v
-> Property
prop_insert_toList kvs k v =
(===)
(RMap.toList (RMap.insert k v (RMap.fromList kvs)))
(OMap.toList (OMap.insert k v (OMap.fromList kvs)))
& cover 10
(filter ((== k) . fst) kvs == [])
"filter ((== k) . fst) kvs == []"
& cover 10
(filter ((== k) . fst) kvs /= [])
"filter ((== k) . fst) kvs /= []"
--------------------------------------------------------------------------------
-- Map
--------------------------------------------------------------------------------
prop_map
:: (Ord k, Show k, Eq v2, Show v2)
=> [(k, v1)]
-> Fun v1 v2
-> Property
prop_map kvs (applyFun -> f) =
(===)
(RMap.toList (RMap.map f (RMap.fromList kvs)))
(OMap.toList (OMap.map f (OMap.fromList kvs)))
prop_map_mempty
:: forall k v1 v2. (Ord k, Show k, Eq v2, Monoid v2, Show v2)
=> [(k, v1)]
-> Property
prop_map_mempty kvs =
(===)
(RMap.toList (RMap.map (const (mempty @v2)) (RMap.fromList kvs)))
(OMap.toList (OMap.map (const (mempty @v2)) (OMap.fromList kvs)))
prop_mapWithKey
:: (Ord k, Show k, Eq v2, Show v2)
=> [(k, v1)]
-> Fun (k, v1) v2
-> Property
prop_mapWithKey kvs (applyFun2 -> f) =
(===)
(RMap.toList (RMap.mapWithKey f (RMap.fromList kvs)))
(OMap.toList (OMap.mapWithKey f (OMap.fromList kvs)))
--------------------------------------------------------------------------------
-- MapAccum
--------------------------------------------------------------------------------
prop_mapAccumL
:: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)
=> Fun (s, v1) (s, v2)
-> s
-> [(k, v1)]
-> Property
prop_mapAccumL (applyFun2 -> f) s0 kvs =
(===)
(RMap.toList <$> rmapAccumL f s0 (RMap.fromList kvs))
(OMap.toList <$> omapAccumL f s0 (OMap.fromList kvs))
where
rmapAccumL = RMap.mapAccumL
omapAccumL = OMap.mapAccum
prop_mapAccumR
:: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)
=> Fun (s, v1) (s, v2)
-> s
-> [(k, v1)]
-> Property
prop_mapAccumR (applyFun2 -> f) s0 kvs =
(===)
(RMap.toList <$> rmapAccumR f s0 (RMap.fromList kvs))
(OMap.toList <$> omapAccumR f s0 (OMap.fromList kvs))
where
rmapAccumR = RMap.mapAccumR
omapAccumR g = OMap.mapAccumRWithKey (\s _ v -> g s v)
--------------------------------------------------------------------------------
-- MapAccumWithKey
--------------------------------------------------------------------------------
prop_mapAccumLWithKey
:: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)
=> Fun (s, k, v1) (s, v2)
-> s
-> [(k, v1)]
-> Property
prop_mapAccumLWithKey (applyFun3 -> f) s0 kvs =
(===)
(RMap.toList <$> rmapAccumLWithKey f s0 (RMap.fromList kvs))
(OMap.toList <$> omapAccumLWithKey f s0 (OMap.fromList kvs))
where
rmapAccumLWithKey = RMap.mapAccumLWithKey
omapAccumLWithKey = OMap.mapAccumWithKey
prop_mapAccumRWithKey
:: forall s k v1 v2. (Eq s, Eq v2, Ord k, Show k, Show s, Show v2)
=> Fun (s, k, v1) (s, v2)
-> s
-> [(k, v1)]
-> Property
prop_mapAccumRWithKey (applyFun3 -> f) s0 kvs =
(===)
(RMap.toList <$> rmapAccumRWithKey f s0 (RMap.fromList kvs))
(OMap.toList <$> omapAccumRWithKey f s0 (OMap.fromList kvs))
where
rmapAccumRWithKey = RMap.mapAccumRWithKey
omapAccumRWithKey = OMap.mapAccumRWithKey
--------------------------------------------------------------------------------
-- Arbitrary instances
--------------------------------------------------------------------------------
instance (Arbitrary k, Ord k, Arbitrary v) =>
Arbitrary (RMap.Map k v)
where
arbitrary = RMap.fromList <$> listOf ((,) <$> arbitrary <*> arbitrary)
shrink = shrinkMapBy RMap.fromList RMap.toList shrink