Skip to content

Commit

Permalink
More test cleanups
Browse files Browse the repository at this point in the history
I still feel there's a touch of coverage missing
  • Loading branch information
dustin committed Aug 24, 2023
1 parent a2159f3 commit f25a9c3
Showing 1 changed file with 24 additions and 46 deletions.
70 changes: 24 additions & 46 deletions test/ExpiringSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,23 @@ data Mutation = Insert SomeKey Int
| Delete SomeKey
| Update SomeKey Int
| UpdateNothing SomeKey
| NewGeneration
| NewGeneration (Positive Int)
deriving Show

instance Arbitrary Mutation where
arbitrary = oneof [Insert <$> arbitrary <*> arbitrary,
Delete <$> arbitrary,
Update <$> arbitrary <*> arbitrary,
UpdateNothing <$> arbitrary,
pure NewGeneration
NewGeneration <$> arbitrary
]

allOpTypes :: [String]
allOpTypes = ["Insert", "Delete", "Update", "UpdateNothing", "NewGeneration"]

-- Verify that after a series of operations, the map and expiring map return the same values for the given keys.
prop_expMapDoesMapStuff :: [Mutation] -> [SomeKey] -> Property
prop_expMapDoesMapStuff ops lookups =
prop_doesMapStuff :: [Mutation] -> [SomeKey] -> Property
prop_doesMapStuff ops lookups =
coverTable "mutation types" ((,5) <$> allOpTypes) $ -- The test paths should hit every mutation type (5% min)
tabulate "mutation types" (takeWhile (/= ' ') . show <$> ops) $ -- We can identify one by the first word in its constructor
checkCoverage $
Expand All @@ -55,68 +55,46 @@ prop_expMapDoesMapStuff ops lookups =
Delete k -> Map.delete k
Update k v -> snd . Map.updateLookupWithKey (\_ _ -> Just v) k
UpdateNothing k -> snd . Map.updateLookupWithKey (\_ _ -> Nothing) k
NewGeneration -> const mempty
NewGeneration _ -> const mempty

applyOpE m = \case
Insert k v -> ExpiringMap.insert gen k v m
Delete k -> ExpiringMap.delete k m
Update k v -> snd $ ExpiringMap.updateLookupWithKey gen (\k' _ -> bool Nothing (Just v) (k == k')) k m
UpdateNothing k -> snd $ ExpiringMap.updateLookupWithKey gen (\_ _ -> Nothing) k m
NewGeneration -> ExpiringMap.newGen (gen + 1) m
NewGeneration n -> ExpiringMap.newGen (gen + getPositive n) m
where gen = ExpiringMap.generation m

prop_expiringMapWorks :: Int -> [Int] -> Property
prop_expiringMapWorks baseGen keys = Just keys === traverse (`ExpiringMap.lookup` m) keys
where
m = foldr (\x -> ExpiringMap.insert futureGen x x) (ExpiringMap.new baseGen) keys
futureGen = succ baseGen

ulength :: (Ord a, Foldable t) => t a -> Int
ulength = Set.size . Set.fromList . toList

prop_expiringMapExpires :: Int -> [Int] -> Property
prop_expiringMapExpires baseGen keys = (ulength keys, futureGen, ulength keys) === ExpiringMap.inspect m1 .&&. (0, lastGen, 0) === ExpiringMap.inspect m2
where
m1 = ExpiringMap.newGen futureGen $ foldr (\x -> ExpiringMap.insert futureGen x x) (ExpiringMap.new baseGen) keys
m2 = ExpiringMap.newGen lastGen m1
futureGen = succ baseGen
lastGen = succ futureGen

prop_expiringMapCannotAcceptExpired :: Positive Int -> Positive Int -> Int -> Property
prop_expiringMapCannotAcceptExpired (Positive lowGen) (Positive offset) k = ExpiringMap.inspect m === ExpiringMap.inspect m'
prop_updateReturn :: Int -> Property
prop_updateReturn x = (Just plus2, Just plus2, Nothing, Just plus2) === (up1, ExpiringMap.lookup x m', up2, up3)
where
m = ExpiringMap.insert 0 x 0 $ ExpiringMap.new 0
plus2 = x + 2
(up1, m') = ExpiringMap.updateLookupWithKey 0 (\_ v -> Just (x + 2)) x m -- New value returns new value
(up2, m'') = ExpiringMap.updateLookupWithKey 0 (\_ v -> Just (x + 3)) (x + 1) m' -- Missing returns nothing
up3 = fst $ ExpiringMap.updateLookupWithKey 0 (\_ v -> Nothing) x m'' -- Nothing returns previous value

prop_cannotAcceptExpired :: Positive Int -> Positive Int -> Int -> Property
prop_cannotAcceptExpired (Positive lowGen) (Positive offset) k = ExpiringMap.inspect m === ExpiringMap.inspect m'
where
highGen = lowGen + offset
m = ExpiringMap.new highGen :: ExpiringMap.Map Int Int Int
m' = ExpiringMap.insert lowGen k k m

prop_expiringMapUpdateMissing :: Int -> Int -> Property
prop_expiringMapUpdateMissing gen k = mv === Nothing .&&. ExpiringMap.inspect m === ExpiringMap.inspect m'
where
m = ExpiringMap.new gen :: ExpiringMap.Map Int Int Bool
(mv, m') = ExpiringMap.updateLookupWithKey gen (\_ _ -> Just True) k m

prop_expiringMapCannotUpdateExpired :: Positive Int -> Positive Int -> Int -> Property
prop_expiringMapCannotUpdateExpired (Positive lowGen) (Positive offset) k = mv === Nothing .&&. ExpiringMap.lookup k m' === Just True
prop_cannotUpdateExpired :: Positive Int -> Positive Int -> Int -> Property
prop_cannotUpdateExpired (Positive lowGen) (Positive offset) k = mv === Nothing .&&. ExpiringMap.lookup k m' === Just True
where
highGen = lowGen + offset
m = ExpiringMap.insert highGen k True $ ExpiringMap.new highGen
(mv, m') = ExpiringMap.updateLookupWithKey lowGen (\_ _ -> Just False) k m

prop_expiringMapDelete :: Int -> [Int] -> Property
prop_expiringMapDelete baseGen keys = (ulength keys, baseGen, ulength keys) === ExpiringMap.inspect m .&&. (0, baseGen, 0) === ExpiringMap.inspect m'
where
m = foldr (\x -> ExpiringMap.insert futureGen x x) (ExpiringMap.new baseGen) keys
m' = foldr ExpiringMap.delete m keys
futureGen = succ baseGen

prop_expiringMapElems :: Int -> Set Int -> Property
prop_expiringMapElems baseGen keys = keys === Set.fromList (toList m)
prop_assocs :: [SomeKey] -> Property
prop_assocs keys = ExpiringMap.assocs m === Map.assocs (Map.fromList $ zip keys keys)
where
m = foldr (\x -> ExpiringMap.insert futureGen x x) (ExpiringMap.new baseGen) keys
futureGen = succ baseGen
m = foldr (\k -> ExpiringMap.insert 0 k k) (ExpiringMap.new 0) keys

prop_expiringMapGen :: Int -> Int -> Property
prop_expiringMapGen g1 g2 = ExpiringMap.inspect m === (0, max g1 g2, 0)
prop_generation :: Int -> Int -> Property
prop_generation g1 g2 = ExpiringMap.inspect m === (0, max g1 g2, 0)
where
m :: ExpiringMap.Map Int Int Int
m = ExpiringMap.newGen g2 $ ExpiringMap.new g1

0 comments on commit f25a9c3

Please sign in to comment.