g2-0.2.0.0: tests/UFMapTests.hs
module UFMapTests where
import G2.Data.UFMap
import Data.Hashable
import qualified Data.List as L
import Data.Maybe as M
import Test.Tasty
import Test.Tasty.QuickCheck
import qualified Data.HashMap.Lazy as HM
import qualified Data.HashSet as S
import Prelude hiding (lookup, map, null)
import qualified Prelude as P
ufMapQuickcheck :: TestTree
ufMapQuickcheck =
testGroup "UFMap" [
testProperty "from hashset to hashset" prop_from_hs_to_hs
, testProperty "insert forces in list" prop_insert_forces_in_list
, testProperty "after joining two keys, they will return the same result from lookup" join_makes_lookup_match
, testProperty "lookup j after joining nonpresent j with some k in the map" prop_lookup_after_joining
, testProperty "if two keys are joined in the first map, they are joined after merging that map" prop_merge_preserves_joins1
, testProperty "if two keys are joined in the second map, they are joined after merging that map" prop_merge_preserves_joins2
, testProperty "if a key is present in two maps being merged, in will be present in the merge" prop_merge_preserves_values
, testProperty "if a key is present in two maps being merged, in will be present in the merge (less thorough, but simpler)" prop_merge_preserves_values_simple
, testProperty "merge function is applied to all values from original map" prop_merge_merges_values
, testProperty "toList . fromList preserves joins" prop_toList_fromList_joins
, testProperty "toList . fromList preserves stored values" prop_toList_fromList_values
, testProperty "the toList function returns a list with at least as many elements as in the simple map" prop_toList_leq_simple_map
, testProperty "the unionWith function correctly preserves elements in the first UFMaps" prop_unionWith_preserves_values1
, testProperty "the unionWith function correctly preserves elements in the second UFMaps" prop_unionWith_preserves_values2
]
prop_from_hs_to_hs :: [([Integer], Maybe Integer)] -> Property
prop_from_hs_to_hs xs =
let
xs_tails = L.init $ L.tails xs
xs' = [ (ks', v) | ((ks, v):xs_tls') <- xs_tails
, let later_ks = concat (P.map fst xs_tls')
, let ks' = P.filter (\k -> k `notElem` later_ks) ks ]
hs = toHS xs'
in
all (not . P.null . fst) xs'
==>
property (hs == toSet (fromSet hs))
toHS :: (Hashable k, Eq k, Hashable v, Eq v) => [([k], Maybe v)] -> S.HashSet (S.HashSet k, Maybe v)
toHS = S.fromList . mapMaybe (\(ks, v) -> if isJust v
then Just (S.fromList ks, v)
else Nothing )
prop_insert_forces_in_list :: Integer -> Integer -> UFMap Integer Integer -> Property
prop_insert_forces_in_list k v ufm =
let
ufm' = insert k v ufm
lst = toList ufm'
in
property (any (\(ks, v') -> k `elem` ks
&& case v' of
Just v'' -> v == v''
Nothing -> False) lst)
join_makes_lookup_match :: Fun (Integer, Integer) Integer -> Integer -> Integer -> UFMap Integer Integer -> Property
join_makes_lookup_match (Fn2 f) x y ufm =
let
ufm' = join f x y ufm
in
property (lookup x ufm' == lookup y ufm')
prop_lookup_after_joining :: Integer -> UFMap Integer Integer -> Property
prop_lookup_after_joining j ufm =
let
ks = keys ufm
k = head ks
in
not (L.null ks) && j `notElem` ks
==>
property $ lookup j (join undefined j k ufm) == lookup k ufm
prop_merge_preserves_joins1 :: Integer
-> Integer
-> Fun (Integer, Integer) Integer
-> Fun (Integer, Integer, Integer) (Integer, [(Integer, Integer)])
-> Fun (Integer, Integer) (Integer, [(Integer, Integer)])
-> Fun (Integer, Integer) (Integer, [(Integer, Integer)])
-> Fun (Integer, Integer) Integer
-> Fun (Integer, Integer) Integer
-> Fun (Integer, Integer) Integer
-> UFMap Integer Integer
-> UFMap Integer Integer
-> Property
prop_merge_preserves_joins1 x y (Fn2 jf) (Fn3 fb) (Fn2 f1) (Fn2 f2) (Fn2 fj1) (Fn2 fj2) (Fn2 j) ufm1 ufm2 =
let
ufm1' = join jf x y ufm1
m_ufm = mergeJoiningWithKey fb f1 f2 fj1 fj2 j ufm1' ufm2
in
property (lookup x m_ufm == lookup y m_ufm)
prop_merge_preserves_joins2 :: Integer
-> Integer
-> Fun (Integer, Integer) Integer
-> Fun (Integer, Integer, Integer) (Integer, [(Integer, Integer)])
-> Fun (Integer, Integer) (Integer, [(Integer, Integer)])
-> Fun (Integer, Integer) (Integer, [(Integer, Integer)])
-> Fun (Integer, Integer) Integer
-> Fun (Integer, Integer) Integer
-> Fun (Integer, Integer) Integer
-> UFMap Integer Integer
-> UFMap Integer Integer
-> Property
prop_merge_preserves_joins2 x y (Fn2 jf) (Fn3 fb) (Fn2 f1) (Fn2 f2) (Fn2 fj1) (Fn2 fj2) (Fn2 j) ufm1 ufm2 =
let
ufm2' = join jf x y ufm2
m_ufm = mergeJoiningWithKey fb f1 f2 fj1 fj2 j ufm1 ufm2'
in
property (lookup x m_ufm == lookup y m_ufm)
prop_merge_preserves_values :: Integer
-> Fun (Integer, Integer, Integer) (Integer, [(Integer, Integer)])
-> Fun (Integer, Integer) (Integer, [(Integer, Integer)])
-> Fun (Integer, Integer) (Integer, [(Integer, Integer)])
-> Fun (Integer, Integer) Integer
-> Fun (Integer, Integer) Integer
-> Fun (Integer, Integer) Integer
-> UFMap Integer Integer
-> UFMap Integer Integer
-> Property
prop_merge_preserves_values x (Fn3 fb) (Fn2 f1) (Fn2 f2) (Fn2 fj1) (Fn2 fj2) (Fn2 j) ufm1 ufm2 =
let
m_ufm = mergeJoiningWithKey fb f1 f2 fj1 fj2 j ufm1 ufm2
in
isJust (lookup x ufm1) || isJust (lookup x ufm2)
==>
property (isJust (lookup x m_ufm))
prop_merge_preserves_values_simple :: Integer
-> Fun (Integer, Integer) (Integer, [(Integer, Integer)])
-> UFMap Integer Integer
-> UFMap Integer Integer
-> Property
prop_merge_preserves_values_simple x (Fn2 f2) ufm1 ufm2 =
let
m_ufm = mergeJoiningWithKey (\_ x y -> (x + y, [])) (\_ x -> (x, [])) f2 (+) (+) (+) ufm1 ufm2
in
isJust (lookup x ufm1) || isJust (lookup x ufm2)
==>
property (isJust (lookup x m_ufm))
prop_merge_merges_values :: Integer
-> UFMap Integer (S.HashSet Integer)
-> UFMap Integer (S.HashSet Integer)
-> Property
prop_merge_merges_values x ufm1 ufm2 =
let
m_ufm = mergeJoiningWithKey (\_ x y -> (S.union x y, []))
(\_ x -> (x, []))
(\_ x -> (x, []))
S.union
S.union
S.union
ufm1 ufm2
s1 = case lookup x ufm1 of
Just s -> s
Nothing -> S.empty
s2 = case lookup x ufm2 of
Just s -> s
Nothing -> S.empty
in
not (S.null s1) || not (S.null s2)
==>
property (s1 `isSubsetOf` (m_ufm ! x) && s2 `isSubsetOf` (m_ufm ! x))
isSubsetOf :: (Eq a, Hashable a) => S.HashSet a -> S.HashSet a -> Bool
isSubsetOf xs ys = all (\x -> x `S.member` ys) $ S.toList xs
prop_toList_fromList_joins :: Integer
-> Integer
-> UFMap Integer (S.HashSet Integer)
-> Property
prop_toList_fromList_joins i1 i2 ufm =
let
ufm' = fromList . toList $ join S.union i1 i2 ufm
in
property $ find i1 ufm' == find i2 ufm'
prop_toList_fromList_values :: Integer
-> S.HashSet Integer
-> UFMap Integer (S.HashSet Integer)
-> Property
prop_toList_fromList_values i hs ufm =
let
ufm' = fromList . toList $ insert i hs ufm
in
property $ lookup i ufm' == Just hs
prop_toList_leq_simple_map :: [(Integer, S.HashSet Integer)] -> UFMap Integer (S.HashSet Integer) -> Property
prop_toList_leq_simple_map els ufm =
let
ufm' = foldr (uncurry insert) ufm els
in
property $ length (toList ufm') >= HM.size (toSimpleMap ufm')
prop_unionWith_preserves_values1 :: Integer
-> UFMap Integer (S.HashSet Integer)
-> UFMap Integer (S.HashSet Integer)
-> Property
prop_unionWith_preserves_values1 x ufm1 ufm2 =
let
union = unionWith S.union ufm1 ufm2
s1 = case lookup x ufm1 of
Just s -> s
Nothing -> S.empty
s2 = case lookup x union of
Just s -> s
Nothing -> S.empty
in
not (S.null s1) || not (S.null s2) ==> property $ s1 `isSubsetOf` s2
prop_unionWith_preserves_values2 :: Integer
-> UFMap Integer (S.HashSet Integer)
-> UFMap Integer (S.HashSet Integer)
-> Property
prop_unionWith_preserves_values2 x ufm1 ufm2 =
let
union = unionWith S.union ufm1 ufm2
s1 = case lookup x ufm2 of
Just s -> s
Nothing -> S.empty
s2 = case lookup x union of
Just s -> s
Nothing -> S.empty
in
not (S.null s1) || not (S.null s2) ==> property $ s1 `isSubsetOf` s2
instance (Hashable a, Eq a, Arbitrary a) => Arbitrary (S.HashSet a) where
arbitrary = return . S.fromList =<< arbitrary
shrink = P.map S.fromList . shrink . S.toList