@@ -17,6 +17,9 @@ import Test.Hspec
17import Streamly
17import Streamly
18import qualified Streamly.Prelude as A
18import qualified Streamly.Prelude as A
20+sortEq :: Ord a => [a] -> [a] -> Bool
21+sortEq a b = sort a == sort b
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)
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
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)
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
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) =
88transformOp
93transformOp
89 :: (Streaming t)
94 :: (Streaming t)
95+ => ([Int] -> [Int] -> Bool)
90− => ([Int] -> [Int]) -> (t IO Int -> t IO Int) -> [Int] -> Property
97+ -> (t IO Int -> t IO Int)
91transformOp = transformFromList (A.each)
100transformOp = transformFromList (A.each)
93functorOps
102functorOps
94 :: (Streaming t, Functor (t IO))
103 :: (Streaming t, Functor (t IO))
95− => String -> (t IO Int -> t IO Int) -> Spec
105+ -> (t IO Int -> t IO Int)
106+ -> ([Int] -> [Int] -> Bool)
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))
100transformOps
112transformOps
101 :: Streaming t
113 :: Streaming t
102− => String -> (t IO Int -> t IO Int) -> Spec
115+ -> (t IO Int -> t IO Int)
116+ -> ([Int] -> [Int] -> Bool)
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)
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)
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))
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)
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
156+wrapMaybe :: Eq a1 => ([a1] -> a2) -> [a1] -> Maybe a2
166+ -> (t IO Int -> t IO Int)
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
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
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.
187+ -> (t IO Int -> t IO Int)
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
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
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)
@@ -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
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)
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 (==)
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
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
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
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
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
367+ describe "Stream elimination operations" $ do
368+ serialEliminationOps "serially" serially
369+ serialEliminationOps "interleaving" interleaving
370+ serialEliminationOps "zipping" zipping
371+ serialEliminationOps "zippingAsync" zippingAsync