on master help sign in
use multiset equality for async streams
203fce4Harendra Kumar
test/Prop.hs+111−63
@@ -17,6 +17,9 @@ import Test.Hspec
17import Streamly
17import Streamly
18import qualified Streamly.Prelude as A
18import qualified Streamly.Prelude as A
19
19
20+sortEq :: Ord a => [a] -> [a] -> Bool
21+sortEq a b = sort a == sort b
22+
20equals
23equals
21 :: (Show a, Monad m)
24 :: (Show a, Monad m)
22 => (a -> a -> Bool) -> a -> a -> PropertyM m ()
25 => (a -> a -> Bool) -> a -> a -> PropertyM m ()
@@ -42,23 +45,25 @@ constructWithReplicateM op len =
42transformFromList
45transformFromList
43 :: (Streaming t)
46 :: (Streaming t)
44 => ([Int] -> t IO Int)
47 => ([Int] -> t IO Int)
48+ -> ([Int] -> [Int] -> Bool)
45 -> ([Int] -> [Int])
49 -> ([Int] -> [Int])
46 -> (t IO Int -> t IO Int)
50 -> (t IO Int -> t IO Int)
47 -> [Int]
51 -> [Int]
48 -> Property
52 -> Property
49−transformFromList constr listOp op a =
53+transformFromList constr eq listOp op a =
50 monadicIO $ do
54 monadicIO $ do
51 stream <- run ((A.toList . op) (constr a))
55 stream <- run ((A.toList . op) (constr a))
52 let list = listOp a
56 let list = listOp a
53− equals (==) stream list
57+ equals eq stream list
54
58
55foldFromList
59foldFromList
56 :: (Streaming t)
60 :: (Streaming t)
57 => ([Int] -> t IO Int)
61 => ([Int] -> t IO Int)
58 -> (t IO Int -> t IO Int)
62 -> (t IO Int -> t IO Int)
63+ -> ([Int] -> [Int] -> Bool)
59 -> [Int]
64 -> [Int]
60 -> Property
65 -> Property
61−foldFromList constr op a = transformFromList constr id op a
66+foldFromList constr op eq a = transformFromList constr eq id op a
62
67
63eliminateOp
68eliminateOp
64 :: (Streaming t, Show a, Eq a)
69 :: (Streaming t, Show a, Eq a)
@@ -87,57 +92,80 @@ elemOp op streamOp listOp (x, xs) =
87
92
88transformOp
93transformOp
89 :: (Streaming t)
94 :: (Streaming t)
95+ => ([Int] -> [Int] -> Bool)
96+ -> ([Int] -> [Int])
90− => ([Int] -> [Int]) -> (t IO Int -> t IO Int) -> [Int] -> Property
97+ -> (t IO Int -> t IO Int)
98+ -> [Int]
99+ -> Property
91transformOp = transformFromList (A.each)
100transformOp = transformFromList (A.each)
92
101
93functorOps
102functorOps
94 :: (Streaming t, Functor (t IO))
103 :: (Streaming t, Functor (t IO))
104+ => String
95− => String -> (t IO Int -> t IO Int) -> Spec
105+ -> (t IO Int -> t IO Int)
106+ -> ([Int] -> [Int] -> Bool)
107+ -> Spec
96−functorOps desc t = do
108+functorOps desc t eq = do
97− prop (desc ++ " id") $ transformOp id $ t
109+ prop (desc ++ " id") $ transformOp eq id $ t
98− prop (desc ++ " fmap (+1)") $ transformOp (fmap (+1)) $ t . (fmap (+1))
110+ prop (desc ++ " fmap (+1)") $ transformOp eq (fmap (+1)) $ t . (fmap (+1))
99
111
100transformOps
112transformOps
101 :: Streaming t
113 :: Streaming t
114+ => String
102− => String -> (t IO Int -> t IO Int) -> Spec
115+ -> (t IO Int -> t IO Int)
116+ -> ([Int] -> [Int] -> Bool)
117+ -> Spec
103−transformOps desc t
118+transformOps desc t eq = do
104− = do
119+ let transform = transformOp eq
105 -- Filtering
120 -- Filtering
106 prop (desc ++ " filter False") $
121 prop (desc ++ " filter False") $
107− transformOp (filter (const False)) $ t . (A.filter (const False))
122+ transform (filter (const False)) $ t . (A.filter (const False))
108 prop (desc ++ " filter True") $
123 prop (desc ++ " filter True") $
109− transformOp (filter (const True)) $ t . (A.filter (const True))
124+ transform (filter (const True)) $ t . (A.filter (const True))
110 prop (desc ++ " filter even") $
125 prop (desc ++ " filter even") $
111− transformOp (filter even) $ t . (A.filter even)
126+ transform (filter even) $ t . (A.filter even)
112
127
113 prop (desc ++ " take maxBound") $
128 prop (desc ++ " take maxBound") $
114− transformOp (take maxBound) $ t . (A.take maxBound)
129+ transform (take maxBound) $ t . (A.take maxBound)
115− prop (desc ++ " take 0") $ transformOp (take 0) $ t . (A.take 0)
130+ prop (desc ++ " take 0") $ transform (take 0) $ t . (A.take 0)
116− prop (desc ++ " take 1") $ transformOp (take 1) $ t . (A.take 1)
131+ prop (desc ++ " take 1") $ transform (take 1) $ t . (A.take 1)
117− prop (desc ++ " take 10") $ transformOp (take 10) $ t . (A.take 10)
132+ prop (desc ++ " take 10") $ transform (take 10) $ t . (A.take 10)
118
133
119 prop (desc ++ " takeWhile True") $
134 prop (desc ++ " takeWhile True") $
120− transformOp (takeWhile (const True)) $ t . (A.takeWhile (const True))
135+ transform (takeWhile (const True)) $ t . (A.takeWhile (const True))
121 prop (desc ++ " takeWhile False") $
136 prop (desc ++ " takeWhile False") $
122− transformOp (takeWhile (const False)) $ t . (A.takeWhile (const False))
137+ transform (takeWhile (const False)) $ t . (A.takeWhile (const False))
123 prop (desc ++ " takeWhile > 0") $
138 prop (desc ++ " takeWhile > 0") $
124− transformOp (takeWhile (> 0)) $ t . (A.takeWhile (> 0))
139+ transform (takeWhile (> 0)) $ t . (A.takeWhile (> 0))
125
140
126 prop (desc ++ " drop maxBound") $
141 prop (desc ++ " drop maxBound") $
127− transformOp (drop maxBound) $ t . (A.drop maxBound)
142+ transform (drop maxBound) $ t . (A.drop maxBound)
128− prop (desc ++ " drop 0") $ transformOp (drop 0) $ t . (A.drop 0)
143+ prop (desc ++ " drop 0") $ transform (drop 0) $ t . (A.drop 0)
129− prop (desc ++ " drop 1") $ transformOp (drop 1) $ t . (A.drop 1)
144+ prop (desc ++ " drop 1") $ transform (drop 1) $ t . (A.drop 1)
130− prop (desc ++ " drop 10") $ transformOp (drop 10) $ t . (A.drop 10)
145+ prop (desc ++ " drop 10") $ transform (drop 10) $ t . (A.drop 10)
131
146
132 prop (desc ++ " dropWhile True") $
147 prop (desc ++ " dropWhile True") $
133− transformOp (dropWhile (const True)) $ t . (A.dropWhile (const True))
148+ transform (dropWhile (const True)) $ t . (A.dropWhile (const True))
134 prop (desc ++ " dropWhile False") $
149 prop (desc ++ " dropWhile False") $
135− transformOp (dropWhile (const False)) $ t . (A.dropWhile (const False))
150+ transform (dropWhile (const False)) $ t . (A.dropWhile (const False))
136 prop (desc ++ " dropWhile > 0") $
151 prop (desc ++ " dropWhile > 0") $
137− transformOp (dropWhile (> 0)) $ t . (A.dropWhile (> 0))
152+ transform (dropWhile (> 0)) $ t . (A.dropWhile (> 0))
138− prop (desc ++ " scan") $ transformOp (scanl (+) 0) $ t . (A.scan (+) 0 id)
153+ prop (desc ++ " scan") $ transform (scanl (+) 0) $ t . (A.scan (+) 0 id)
139− prop (desc ++ "reverse") $ transformOp reverse $ t . A.reverse
154+ prop (desc ++ "reverse") $ transform reverse $ t . A.reverse
140−
155+
156+wrapMaybe :: Eq a1 => ([a1] -> a2) -> [a1] -> Maybe a2
157+wrapMaybe f =
158+ \x ->
159+ if x == []
160+ then Nothing
161+ else Just (f x)
162+
163+eliminationOps
164+ :: Streaming t
165+ => String
166+ -> (t IO Int -> t IO Int)
167+ -> Spec
168+eliminationOps desc t = do
141 -- Elimination
169 -- Elimination
142 prop (desc ++ " null") $ eliminateOp null $ A.null . t
170 prop (desc ++ " null") $ eliminateOp null $ A.null . t
143 prop (desc ++ " foldl") $
171 prop (desc ++ " foldl") $
@@ -146,22 +174,26 @@ transformOps desc t
146 prop (desc ++ " any") $ eliminateOp (any even) $ (A.any even) . t
174 prop (desc ++ " any") $ eliminateOp (any even) $ (A.any even) . t
147 prop (desc ++ " length") $ eliminateOp length $ A.length . t
175 prop (desc ++ " length") $ eliminateOp length $ A.length . t
148 prop (desc ++ " sum") $ eliminateOp sum $ A.sum . t
176 prop (desc ++ " sum") $ eliminateOp sum $ A.sum . t
149− prop (desc ++ " sum") $ eliminateOp product $ A.product . t
177+ prop (desc ++ " product") $ eliminateOp product $ A.product . t
150−
178+
151− let wrap f =
179+ prop (desc ++ " maximum") $ eliminateOp (wrapMaybe maximum) $ A.maximum . t
152− \x ->
180+ prop (desc ++ " minimum") $ eliminateOp (wrapMaybe minimum) $ A.minimum . t
153− if x == []
181+
154− then Nothing
182+-- head/tail/last may depend on the order in case of parallel streams
155− else Just (f x)
183+-- so we test these only for serial streams.
184+serialEliminationOps
185+ :: Streaming t
186+ => String
187+ -> (t IO Int -> t IO Int)
188+ -> Spec
189+serialEliminationOps desc t = do
156− prop (desc ++ " head") $ eliminateOp (wrap head) $ A.head . t
190+ prop (desc ++ " head") $ eliminateOp (wrapMaybe head) $ A.head . t
157− prop (desc ++ " tail") $ eliminateOp (wrap tail) $ \x -> do
191+ prop (desc ++ " tail") $ eliminateOp (wrapMaybe tail) $ \x -> do
158 r <- A.tail (t x)
192 r <- A.tail (t x)
159 case r of
193 case r of
160 Nothing -> return Nothing
194 Nothing -> return Nothing
161 Just s -> A.toList s >>= return . Just
195 Just s -> A.toList s >>= return . Just
162− prop (desc ++ " last") $ eliminateOp (wrap last) $ A.last . t
196+ prop (desc ++ " last") $ eliminateOp (wrapMaybe last) $ A.last . t
163− prop (desc ++ " maximum") $ eliminateOp (wrap maximum) $ A.maximum . t
164− prop (desc ++ " minimum") $ eliminateOp (wrap minimum) $ A.minimum . t
165
197
166transformOpsWord8
198transformOpsWord8
167 :: Streaming t
199 :: Streaming t
@@ -170,6 +202,7 @@ transformOpsWord8 desc t = do
170 prop (desc ++ " elem") $ elemOp t A.elem elem
202 prop (desc ++ " elem") $ elemOp t A.elem elem
171 prop (desc ++ " elem") $ elemOp t A.notElem notElem
203 prop (desc ++ " elem") $ elemOp t A.notElem notElem
172
204
205+-- XXX concatenate streams of multiple elements rather than single elements
173semigroupOps
206semigroupOps
174 :: ( Streaming t, MonadPlus (t IO)
207 :: ( Streaming t, MonadPlus (t IO)
175
208
@@ -179,13 +212,12 @@ semigroupOps
179 , Monoid (t IO Int))
212 , Monoid (t IO Int))
180 => String -> (t IO Int -> t IO Int) -> Spec
213 => String -> (t IO Int -> t IO Int) -> Spec
181semigroupOps desc t = do
214semigroupOps desc t = do
182− prop (desc ++ " <>") $ foldFromList (foldMapWith (<>) return) t
215+ prop (desc ++ " <>") $ foldFromList (foldMapWith (<>) return) t (==)
183− prop (desc ++ " mappend") $ foldFromList (foldMapWith mappend return) t
216+ prop (desc ++ " mappend") $ foldFromList (foldMapWith mappend return) t (==)
184− prop (desc ++ " <=>") $ foldFromList (foldMapWith (<=>) return) t
217+ prop (desc ++ " <=>") $ foldFromList (foldMapWith (<=>) return) t (==)
185− -- XXX equality should be multiset equality
186− prop (desc ++ " <|>") $ foldFromList (foldMapWith (<|>) return) t
218+ prop (desc ++ " <|>") $ foldFromList (foldMapWith (<|>) return) t sortEq
187− prop (desc ++ " mplus") $ foldFromList (foldMapWith mplus return) t
219+ prop (desc ++ " mplus") $ foldFromList (foldMapWith mplus return) t sortEq
188− prop (desc ++ " <|") $ foldFromList (foldMapWith (<|) return) t
220+ prop (desc ++ " <|") $ foldFromList (foldMapWith (<|) return) t sortEq
189
221
190applicativeOps
222applicativeOps
191 :: (Streaming t, Applicative (t IO))
223 :: (Streaming t, Applicative (t IO))
@@ -257,6 +289,7 @@ monadBind t eq (a, b) =
257main :: IO ()
289main :: IO ()
258main = hspec $ do
290main = hspec $ do
259 describe "Construction" $ do
291 describe "Construction" $ do
292+ -- XXX test for all types of streams
260 prop "serially replicateM" $ constructWithReplicateM serially
293 prop "serially replicateM" $ constructWithReplicateM serially
261 it "iterate" $
294 it "iterate" $
262 (A.toList . serially . (A.take 100) $ (A.iterate (+ 1) (0 :: Int)))
295 (A.toList . serially . (A.take 100) $ (A.iterate (+ 1) (0 :: Int)))
@@ -268,18 +301,17 @@ main = hspec $ do
268 `shouldReturn` (take 100 $ iterate (+ 1) 0)
301 `shouldReturn` (take 100 $ iterate (+ 1) 0)
269
302
270 describe "Functor operations" $ do
303 describe "Functor operations" $ do
271− functorOps "serially" serially
304+ functorOps "serially" serially (==)
272− functorOps "interleaving" interleaving
305+ functorOps "interleaving" interleaving (==)
273− functorOps "zipping" zipping
306+ functorOps "zipping" zipping (==)
274− functorOps "asyncly" asyncly
307+ functorOps "asyncly" asyncly sortEq
275− functorOps "parallely" parallely
308+ functorOps "parallely" parallely sortEq
276− functorOps "zippingAsync" zippingAsync
309+ functorOps "zippingAsync" zippingAsync (==)
277
310
278 describe "Semigroup operations" $ do
311 describe "Semigroup operations" $ do
279 semigroupOps "serially" serially
312 semigroupOps "serially" serially
280 semigroupOps "interleaving" interleaving
313 semigroupOps "interleaving" interleaving
281
314
282− let sortEq a b = sort a == sort b
283 describe "Applicative operations" $ do
315 describe "Applicative operations" $ do
284 -- The tests using sorted equality are weaker tests
316 -- The tests using sorted equality are weaker tests
285 -- We need to have stronger unit tests for all those
317 -- We need to have stronger unit tests for all those
@@ -309,15 +341,31 @@ main = hspec $ do
309 prop "asyncly monad bind" $ monadBind asyncly sortEq
341 prop "asyncly monad bind" $ monadBind asyncly sortEq
310 prop "parallely monad bind" $ monadBind parallely sortEq
342 prop "parallely monad bind" $ monadBind parallely sortEq
311
343
312− describe "Stream operations" $ do
344+ describe "Stream transform operations" $ do
313− transformOps "serially" serially
345+ transformOps "serially" serially (==)
314− transformOps "interleaving" interleaving
346+ transformOps "interleaving" interleaving (==)
315− transformOps "zipping" zipping
347+ transformOps "zipping" zipping (==)
348+ transformOps "zippingAsync" zippingAsync (==)
316− transformOps "asyncly" asyncly
349+ transformOps "asyncly" asyncly sortEq
317− transformOps "parallely" parallely
350+ transformOps "parallely" parallely sortEq
318
351
319 transformOpsWord8 "serially" serially
352 transformOpsWord8 "serially" serially
320 transformOpsWord8 "interleaving" interleaving
353 transformOpsWord8 "interleaving" interleaving
321 transformOpsWord8 "zipping" zipping
354 transformOpsWord8 "zipping" zipping
355+ transformOpsWord8 "zippingAsync" zippingAsync
322 transformOpsWord8 "asyncly" asyncly
356 transformOpsWord8 "asyncly" asyncly
323 transformOpsWord8 "parallely" parallely
357 transformOpsWord8 "parallely" parallely
358+
359+ describe "Stream elimination operations" $ do
360+ eliminationOps "serially" serially
361+ eliminationOps "interleaving" interleaving
362+ eliminationOps "zipping" zipping
363+ eliminationOps "zippingAsync" zippingAsync
364+ eliminationOps "asyncly" asyncly
365+ eliminationOps "parallely" parallely
366+
367+ describe "Stream elimination operations" $ do
368+ serialEliminationOps "serially" serially
369+ serialEliminationOps "interleaving" interleaving
370+ serialEliminationOps "zipping" zipping
371+ serialEliminationOps "zippingAsync" zippingAsync