Skip to content

Commit 1f2f19e

Browse files
committed
Some simple Set tests
1 parent 72eee55 commit 1f2f19e

File tree

3 files changed

+116
-1
lines changed

3 files changed

+116
-1
lines changed

stm-containers.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ test-suite test
132132
main-is: Main.hs
133133
other-modules:
134134
Suites.Bimap
135+
Suites.Set
135136
Suites.Map
136137
Suites.Map.Update
137138

test/Main.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import qualified Suites.Bimap
22
import qualified Suites.Map
3+
import qualified Suites.Set
34
import Test.Tasty
45
import Prelude
56

@@ -8,5 +9,6 @@ main =
89
defaultMain
910
. testGroup ""
1011
$ [ testGroup "Bimap" Suites.Bimap.tests,
11-
testGroup "Map" Suites.Map.tests
12+
testGroup "Map" Suites.Map.tests,
13+
testGroup "Set" Suites.Set.tests
1214
]

test/Suites/Set.hs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
module Suites.Set (tests) where
2+
3+
import qualified Control.Foldl as Foldl
4+
import Control.Monad.Free
5+
import qualified DeferredFolds.UnfoldlM as UnfoldlM
6+
import qualified Focus
7+
import qualified StmContainers.Set as StmSet
8+
import Test.QuickCheck.Instances ()
9+
import Test.Tasty
10+
import Test.Tasty.HUnit
11+
import Test.Tasty.QuickCheck
12+
import Prelude hiding (null, choose)
13+
import System.IO.Unsafe (unsafePerformIO)
14+
import Control.Monad (forM_)
15+
import Control.Concurrent.STM
16+
import Data.Hashable
17+
import Data.List (nub, sort, splitAt)
18+
import Data.Word (Word8)
19+
20+
-- helpers
21+
22+
stmSetFromList :: (Hashable a, Eq a) => [a] -> STM (StmSet.Set a)
23+
stmSetFromList xs = do
24+
s <- StmSet.new
25+
forM_ xs $ \x -> StmSet.insert x s
26+
return s
27+
28+
stmSetToList :: StmSet.Set a -> STM [a]
29+
stmSetToList = UnfoldlM.foldM (Foldl.generalize Foldl.list) . StmSet.unfoldlM
30+
31+
-- * Intentional hash collision simulation
32+
33+
newtype TestKey = TestKey Word8
34+
deriving (Eq, Ord, Show)
35+
36+
instance Arbitrary TestKey where
37+
arbitrary = TestKey <$> choose (0, 63)
38+
39+
instance Hashable TestKey where
40+
hashWithSalt salt (TestKey w) =
41+
if odd w
42+
then hashWithSalt salt (pred w)
43+
else hashWithSalt salt w
44+
45+
-- * Tests
46+
47+
tests :: [TestTree]
48+
tests =
49+
[ testProperty "sizeAndList" $
50+
let gen = nub <$> listOf (choose ('a', 'z'))
51+
prop xs =
52+
length xs == stmSetSize
53+
where
54+
stmSetSize =
55+
unsafePerformIO $ atomically $ do
56+
s <- stmSetFromList xs
57+
StmSet.size s
58+
in forAll gen prop,
59+
testProperty "fromListToListSetIsomorphism" $ \(xs :: [Int]) ->
60+
let setList =
61+
unsafePerformIO $ atomically $
62+
stmSetFromList xs >>= stmSetToList
63+
in sort (nub xs) === sort setList,
64+
testProperty "insertDeleteWithCollisions" $ \(ks :: [TestKey]) ->
65+
let dropped = take (length ks `div` 2) ks
66+
(finalSize, finalList) =
67+
unsafePerformIO $ atomically $ do
68+
s <- StmSet.new
69+
-- insert all
70+
forM_ ks $ \k -> StmSet.insert k s
71+
-- delete ~the first half of them
72+
forM_ dropped $ \k -> StmSet.delete k s
73+
sz <- StmSet.size s
74+
ls <- stmSetToList s
75+
return (sz, sort ls)
76+
expected =
77+
let remaining = nub (filter (`notElem` dropped) ks)
78+
in (length remaining, sort remaining)
79+
in (finalSize, finalList) === expected,
80+
testCase "insert" $
81+
assertEqual "" (sort ['a','b','c']) =<< do
82+
atomically $ do
83+
s <- StmSet.new
84+
StmSet.insert 'a' s
85+
StmSet.insert 'c' s
86+
StmSet.insert 'b' s
87+
sort <$> stmSetToList s,
88+
testCase "focusInsert" $
89+
assertEqual "" (sort ['a','b']) =<< do
90+
atomically $ do
91+
s <- StmSet.new
92+
StmSet.focus (Focus.insert ()) 'a' s
93+
StmSet.focus (Focus.insert ()) 'b' s
94+
sort <$> stmSetToList s,
95+
testCase "insertAndDelete" $
96+
assertEqual "" ['b'] =<< do
97+
atomically $ do
98+
s <- StmSet.new
99+
StmSet.focus (Focus.insert ()) 'a' s
100+
StmSet.focus (Focus.insert ()) 'b' s
101+
StmSet.focus Focus.delete 'a' s
102+
sort <$> stmSetToList s,
103+
testCase "nullAndNotNull" $ do
104+
assertEqual "" True =<< atomically (StmSet.null =<< StmSet.new)
105+
assertEqual "" False =<< atomically (StmSet.null =<< stmSetFromList ['a']),
106+
testCase "nullAfterDeletingTheLastElement" $
107+
assertEqual "" True =<< do
108+
atomically $ do
109+
s <- stmSetFromList ['a']
110+
StmSet.delete 'a' s
111+
StmSet.null s
112+
]

0 commit comments

Comments
 (0)