2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE ScopedTypeVariables #-}
6module Main (main) where
8import Control.Concurrent (threadDelay)
9import Control.Exception (Exception, try, ErrorCall(..), catch, throw)
10import Control.Monad (void)
11import Control.Monad.Catch (throwM, MonadThrow)
12import Control.Monad.Error.Class (throwError, MonadError)
13import Control.Monad.IO.Class (MonadIO(liftIO))
14import Control.Monad.State (MonadState, get, modify, runStateT, StateT)
15import Control.Monad.Trans.Except (runExceptT, ExceptT)
16import Data.Foldable (forM_, fold)
17import Data.List (sort)
18import System.Mem (performMajorGC)
24import Streamly.Prelude ((.:), nil)
25import qualified Streamly.Prelude as S
27singleton :: IsStream t => a -> t m a
30toListSerial :: SerialT IO a -> IO [a]
31toListSerial = S.toList . serially
33toListInterleaved :: WSerialT IO a -> IO [a]
34toListInterleaved = S.toList . wSerially
36toListAsync :: AsyncT IO a -> IO [a]
37toListAsync = S.toList . asyncly
39toListParallel :: WAsyncT IO a -> IO [a]
40toListParallel = S.toList . wAsyncly
46 describe "restricts concurrency and cleans up extra tasks" $ do
47 it "take 1 asyncly" $ checkCleanup 2 asyncly (S.take 1)
48 it "take 1 wAsyncly" $ checkCleanup 2 wAsyncly (S.take 1)
49 it "take 1 aheadly" $ checkCleanup 2 aheadly (S.take 1)
51 it "takeWhile (< 0) asyncly" $ checkCleanup 2 asyncly (S.takeWhile (< 0))
52 it "takeWhile (< 0) wAsyncly" $ checkCleanup 2 wAsyncly (S.takeWhile (< 0))
53 it "takeWhile (< 0) aheadly" $ checkCleanup 2 aheadly (S.takeWhile (< 0))
56 let timed :: (IsStream t, Monad (t IO)) => Int -> t IO Int
57 timed x = S.yieldM (threadDelay (x * 100000)) >> return x
59 -- These are not run parallely because the timing gets affected
60 -- unpredictably when other tests are running on the same machine.
62 -- Also, they fail intermittently due to scheduling delays, so not run on
64 describe "Nested parallel and serial compositions" $ do
69 -- This is not correct, the result can also be [4,4,8,0,8,0,2,2]
70 -- because of parallelism of [8,0] and [8,0].
71 it "Nest <|>, <>, <|> (1)" $
74 ((t 8 <|> t 4) <> (t 2 <|> t 0))
75 <|> ((t 8 <|> t 4) <> (t 2 <|> t 0)))
76 `shouldReturn` ([4,4,8,8,0,0,2,2])
78 it "Nest <|>, <>, <|> (2)" $
79 (S.toList . wAsyncly) (
80 s (p (t 4 <> t 8) <> p (t 1 <> t 2))
81 <> s (p (t 4 <> t 8) <> p (t 1 <> t 2)))
82 `shouldReturn` ([4,4,8,8,1,1,2,2])
83 -- FIXME: These two keep failing intermittently on Mac OS X
84 -- Need to examine and fix the tests.
86 it "Nest <|>, <=>, <|> (1)" $
89 ((t 8 <|> t 4) <=> (t 2 <|> t 0))
90 <|> ((t 9 <|> t 4) <=> (t 2 <|> t 0)))
91 `shouldReturn` ([4,4,0,0,8,2,9,2])
92 it "Nest <|>, <=>, <|> (2)" $
95 ((t 4 <|> t 8) <=> (t 1 <|> t 2))
96 <|> ((t 4 <|> t 9) <=> (t 1 <|> t 2)))
97 `shouldReturn` ([4,4,1,1,8,2,9,2])
99 it "Nest <|>, <|>, <|>" $
100 (S.toList . wAsyncly) (
101 ((t 4 <> t 8) <> (t 0 <> t 2))
102 <> ((t 4 <> t 8) <> (t 0 <> t 2)))
103 `shouldReturn` ([0,0,2,2,4,4,8,8])
105 -- parallely fails on CI machines, may need more difference in times of
106 -- the events, but that would make tests even slower.
107 it "take 1 parallely" $ checkCleanup 3 parallely (S.take 1)
108 it "takeWhile (< 0) parallely" $ checkCleanup 3 parallely (S.takeWhile (< 0))
110 testFoldOpsCleanup "head" S.head
111 testFoldOpsCleanup "null" S.null
112 testFoldOpsCleanup "elem" (S.elem 0)
113 testFoldOpsCleanup "notElem" (S.notElem 0)
114 testFoldOpsCleanup "elemIndex" (S.elemIndex 0)
116 testFoldOpsCleanup "notElem" (S.notElem 0)
117 testFoldOpsCleanup "find" (S.find (==0))
118 testFoldOpsCleanup "findIndex" (S.findIndex (==0))
119 testFoldOpsCleanup "all" (S.all (==1))
120 testFoldOpsCleanup "any" (S.any (==0))
121 testFoldOpsCleanup "and" (S.and . S.map (==1))
122 testFoldOpsCleanup "or" (S.or . S.map (==0))
125 ---------------------------------------------------------------------------
126 -- Semigroup/Monoidal Composition strict ordering checks
127 ---------------------------------------------------------------------------
129 -- test both (<>) and mappend to make sure we are using correct instance
130 -- for Monoid that is using the right version of semigroup. Instance
131 -- deriving can cause us to pick wrong instances sometimes.
133 describe "WSerial interleaved (<>) ordering check" $ interleaveCheck wSerially (<>)
134 describe "WSerial interleaved mappend ordering check" $ interleaveCheck wSerially mappend
136 -- describe "WAsync interleaved (<>) ordering check" $ interleaveCheck wAsyncly (<>)
137 -- describe "WAsync interleaved mappend ordering check" $ interleaveCheck wAsyncly mappend
139 describe "Async (<>) time order check" $ parallelCheck asyncly (<>)
140 describe "Async mappend time order check" $ parallelCheck asyncly mappend
142 -- XXX this keeps failing intermittently, need to investigate
143 -- describe "WAsync (<>) time order check" $ parallelCheck wAsyncly (<>)
144 -- describe "WAsync mappend time order check" $ parallelCheck wAsyncly mappend
146 describe "Parallel (<>) time order check" $ parallelCheck parallely (<>)
147 describe "Parallel mappend time order check" $ parallelCheck parallely mappend
149checkCleanup :: IsStream t
151 -> (t IO Int -> SerialT IO Int)
152 -> (t IO Int -> t IO Int)
154checkCleanup d t op = do
155 r <- newIORef (-1 :: Int)
156 runStream . serially $ do
157 _ <- t $ op $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
164 delay ref i = threadDelay (i*d*100000) >> writeIORef ref i >> return i
167checkCleanupFold :: IsStream t
168 => (t IO Int -> SerialT IO Int)
169 -> (SerialT IO Int -> IO (Maybe Int))
171checkCleanupFold t op = do
172 r <- newIORef (-1 :: Int)
173 _ <- op $ t $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
179 delay ref i = threadDelay (i*200000) >> writeIORef ref i >> return i
181testFoldOpsCleanup :: String -> (SerialT IO Int -> IO a) -> Spec
182testFoldOpsCleanup name f = do
183 let testOp op x = op x >> return Nothing
184 it (name <> " asyncly") $ checkCleanupFold asyncly (testOp f)
185 it (name <> " wAsyncly") $ checkCleanupFold wAsyncly (testOp f)
186 it (name <> " aheadly") $ checkCleanupFold aheadly (testOp f)
187 it (name <> " parallely") $ checkCleanupFold parallely (testOp f)
190parallelTests :: SpecWith ()
191parallelTests = H.parallel $ do
192 describe "Runners" $ do
193 -- XXX move these to property tests
194 -- XXX use an IORef to store and check the side effects
195 it "simple serially" $
196 (runStream . serially) (return (0 :: Int)) `shouldReturn` ()
197 it "simple serially with IO" $
198 (runStream . serially) (S.yieldM $ putStrLn "hello") `shouldReturn` ()
200 describe "Empty" $ -- do
201 it "Monoid - mempty" $
202 toListSerial mempty `shouldReturn` ([] :: [Int])
203 -- it "Alternative - empty" $
204 -- (toListSerial empty) `shouldReturn` ([] :: [Int])
205 -- it "MonadPlus - mzero" $
206 -- (toListSerial mzero) `shouldReturn` ([] :: [Int])
208 ---------------------------------------------------------------------------
210 ---------------------------------------------------------------------------
212 describe "Functor (fmap)" $ do
213 -- XXX we should do these through property tests by using a
214 -- construction via list fold construction method.
215 it "fmap on composed (<>)" $
216 toListSerial (fmap (+1) (return 1 <> return 2))
217 `shouldReturn` ([2,3] :: [Int])
219 it "fmap on composed (<>)" $
220 sort <$> toListParallel (fmap (+1) (return 1 <> return 2))
221 `shouldReturn` ([2,3] :: [Int])
223 ---------------------------------------------------------------------------
225 ---------------------------------------------------------------------------
227 describe "Applicative" $ do
228 -- XXX we should do these through property tests by using a
229 -- construction via list fold construction method.
230 it "Apply - serial composed first argument" $
231 toListSerial ((,) <$> (return 1 <> return 2) <*> return 3)
232 `shouldReturn` ([(1,3),(2,3)] :: [(Int, Int)])
234 it "Apply - serial composed second argument" $
235 toListSerial ((,) <$> return 1 <*> (return 2 <> return 3))
236 `shouldReturn` ([(1,2),(1,3)] :: [(Int, Int)])
238 it "Apply - parallel composed first argument" $
239 sort <$> toListParallel ((,) <$> (return 1 <> return 2) <*> return 3)
240 `shouldReturn` ([(1,3),(2,3)] :: [(Int, Int)])
242 it "Apply - parallel composed second argument" $
243 sort <$> toListParallel ((,) <$> return 1 <*> (return 2 <> return 3))
244 `shouldReturn` ([(1,2),(1,3)] :: [(Int, Int)])
246 ---------------------------------------------------------------------------
247 -- Monoidal Compositions, multiset equality checks
248 ---------------------------------------------------------------------------
250 describe "Serial Composition" $ compose serially mempty id
251 describe "Ahead Composition" $ compose aheadly mempty id
252 describe "WSerial Composition" $ compose wSerially mempty sort
253 describe "Async Composition" $ compose asyncly mempty sort
254 describe "WAsync Composition" $ compose wAsyncly mempty sort
255 describe "Parallel Composition" $ compose parallely mempty sort
256 describe "Semigroup Composition for ZipSerial" $ compose zipSerially mempty id
257 describe "Semigroup Composition for ZipAsync" $ compose zipAsyncly mempty id
258 -- XXX need to check alternative compositions as well
260 ---------------------------------------------------------------------------
261 -- TBD Monoidal composition combinations
262 ---------------------------------------------------------------------------
264 -- TBD need more such combinations to be tested.
265 describe "serial <> and serial <>" $
266 composeAndComposeSimple serially serially (repeat [1 .. 9])
267 describe "ahead <> and ahead <>" $
268 composeAndComposeSimple aheadly aheadly (repeat [1 .. 9])
269 describe "ahead <> and serial <>" $
270 composeAndComposeSimple aheadly serially (repeat [1 .. 9])
271 describe "serial <> and ahead <>" $
272 composeAndComposeSimple serially aheadly (repeat [1 .. 9])
274 describe "<> and <=>" $ composeAndComposeSimple
279 , [1, 3, 2, 4, 6, 5, 7, 9, 8]
280 , [1, 3, 2, 4, 6, 5, 7, 9, 8]
283 describe "<=> and <=>" $ composeAndComposeSimple
286 [ [1, 4, 2, 7, 3, 5, 8, 6, 9]
287 , [1, 7, 4, 8, 2, 9, 5, 3, 6]
288 , [1, 4, 3, 7, 2, 6, 9, 5, 8]
289 , [1, 7, 4, 9, 3, 8, 6, 2, 5]
292 describe "<=> and <>" $ composeAndComposeSimple
295 [ [1, 4, 2, 7, 3, 5, 8, 6, 9]
296 , [1, 7, 4, 8, 2, 9, 5, 3, 6]
297 , [1, 4, 2, 7, 3, 5, 8, 6, 9]
298 , [1, 7, 4, 8, 2, 9, 5, 3, 6]
301 ---------------------------------------------------------------------------
302 -- Monoidal composition recursion loops
303 ---------------------------------------------------------------------------
305 describe "Serial loops" $ loops serially id reverse
306 describe "Ahead loops" $ loops aheadly id reverse
307 describe "Async parallel loops" $ loops asyncly sort sort
308 describe "WAsync loops" $ loops wAsyncly sort sort
309 describe "parallel loops" $ loops parallely sort sort
311 ---------------------------------------------------------------------------
312 -- Bind and monoidal composition combinations
313 ---------------------------------------------------------------------------
315 describe "Bind and compose Stream 1" $ bindAndComposeSimple serially serially
316 describe "Bind and compose Stream 2" $ bindAndComposeSimple serially wSerially
317 describe "Bind and compose Stream 3" $ bindAndComposeSimple serially asyncly
318 describe "Bind and compose Stream 4" $ bindAndComposeSimple serially wAsyncly
319 describe "Bind and compose Stream 5" $ bindAndComposeSimple serially parallely
320 describe "Bind and compose Stream 6" $ bindAndComposeSimple serially aheadly
322 describe "Bind and compose Ahead Stream 0" $ bindAndComposeSimple aheadly aheadly
323 describe "Bind and compose Ahead Stream 1" $ bindAndComposeSimple aheadly serially
324 describe "Bind and compose Ahead Stream 2" $ bindAndComposeSimple aheadly wSerially
325 describe "Bind and compose Ahead Stream 3" $ bindAndComposeSimple aheadly asyncly
326 describe "Bind and compose Ahead Stream 4" $ bindAndComposeSimple aheadly wAsyncly
327 describe "Bind and compose Ahead Stream 5" $ bindAndComposeSimple aheadly parallely
329 describe "Bind and compose Costream 1" $ bindAndComposeSimple wSerially serially
330 describe "Bind and compose Costream 2" $ bindAndComposeSimple wSerially wSerially
331 describe "Bind and compose Costream 3" $ bindAndComposeSimple wSerially asyncly
332 describe "Bind and compose Costream 4" $ bindAndComposeSimple wSerially wAsyncly
333 describe "Bind and compose Costream 5" $ bindAndComposeSimple wSerially parallely
334 describe "Bind and compose Costream 6" $ bindAndComposeSimple wSerially aheadly
336 describe "Bind and compose Async 1" $ bindAndComposeSimple asyncly serially
337 describe "Bind and compose Async 2" $ bindAndComposeSimple asyncly wSerially
338 describe "Bind and compose Async 3" $ bindAndComposeSimple asyncly asyncly
339 describe "Bind and compose Async 4" $ bindAndComposeSimple asyncly wAsyncly
340 describe "Bind and compose Async 5" $ bindAndComposeSimple asyncly parallely
341 describe "Bind and compose Async 6" $ bindAndComposeSimple asyncly aheadly
343 describe "Bind and compose WAsync 1" $ bindAndComposeSimple wAsyncly serially
344 describe "Bind and compose WAsync 2" $ bindAndComposeSimple wAsyncly wSerially
345 describe "Bind and compose WAsync 3" $ bindAndComposeSimple wAsyncly asyncly
346 describe "Bind and compose WAsync 4" $ bindAndComposeSimple wAsyncly wAsyncly
347 describe "Bind and compose WAsync 5" $ bindAndComposeSimple wAsyncly parallely
348 describe "Bind and compose WAsync 6" $ bindAndComposeSimple wAsyncly aheadly
350 describe "Bind and compose Parallel 1" $ bindAndComposeSimple parallely serially
351 describe "Bind and compose Parallel 2" $ bindAndComposeSimple parallely wSerially
352 describe "Bind and compose Parallel 3" $ bindAndComposeSimple parallely asyncly
353 describe "Bind and compose Parallel 4" $ bindAndComposeSimple parallely wAsyncly
354 describe "Bind and compose Parallel 5" $ bindAndComposeSimple parallely parallely
355 describe "Bind and compose Parallel 6" $ bindAndComposeSimple parallely aheadly
357 let fldr, fldl :: (IsStream t, Semigroup (t IO Int)) => [t IO Int] -> t IO Int
358 fldr = foldr (<>) nil
359 fldl = foldl (<>) nil
361 forM_ [fldr, fldl] $ \k ->
362 describe "Bind and compose" $ bindAndComposeHierarchy serially serially k
363 forM_ [fldr, fldl] $ \k ->
364 describe "Bind and compose" $ bindAndComposeHierarchy serially wSerially k
365 forM_ [fldr, fldl] $ \k ->
366 describe "Bind and compose" $ bindAndComposeHierarchy serially asyncly k
367 forM_ [fldr, fldl] $ \k ->
368 describe "Bind and compose" $ bindAndComposeHierarchy serially wAsyncly k
369 forM_ [fldr, fldl] $ \k ->
370 describe "Bind and compose" $ bindAndComposeHierarchy serially parallely k
371 forM_ [fldr, fldl] $ \k ->
372 describe "Bind and compose serially aheadly" $ bindAndComposeHierarchy serially aheadly k
374 forM_ [fldr, fldl] $ \k ->
375 describe "Bind and compose aheadly serially" $ bindAndComposeHierarchy aheadly serially k
376 forM_ [fldr, fldl] $ \k ->
377 describe "Bind and compose aheadly wSerially" $ bindAndComposeHierarchy aheadly wSerially k
378 forM_ [fldr, fldl] $ \k ->
379 describe "Bind and compose aheadly asyncly" $ bindAndComposeHierarchy aheadly asyncly k
380 forM_ [fldr, fldl] $ \k ->
381 describe "Bind and compose aheadly wAsyncly" $ bindAndComposeHierarchy aheadly wAsyncly k
382 forM_ [fldr, fldl] $ \k ->
383 describe "Bind and compose aheadly parallely" $ bindAndComposeHierarchy aheadly parallely k
384 forM_ [fldr, fldl] $ \k ->
385 describe "Bind and compose serially aheadly" $ bindAndComposeHierarchy aheadly aheadly k
387 forM_ [fldr, fldl] $ \k ->
388 describe "Bind and compose" $ bindAndComposeHierarchy wSerially serially k
389 forM_ [fldr, fldl] $ \k ->
390 describe "Bind and compose" $ bindAndComposeHierarchy wSerially wSerially k
391 forM_ [fldr, fldl] $ \k ->
392 describe "Bind and compose" $ bindAndComposeHierarchy wSerially asyncly k
393 forM_ [fldr, fldl] $ \k ->
394 describe "Bind and compose" $ bindAndComposeHierarchy wSerially wAsyncly k
395 forM_ [fldr, fldl] $ \k ->
396 describe "Bind and compose" $ bindAndComposeHierarchy wSerially parallely k
397 forM_ [fldr, fldl] $ \k ->
398 describe "Bind and compose wserially aheadly" $ bindAndComposeHierarchy wSerially aheadly k
400 forM_ [fldr, fldl] $ \k ->
401 describe "Bind and compose" $ bindAndComposeHierarchy asyncly serially k
402 forM_ [fldr, fldl] $ \k ->
403 describe "Bind and compose" $ bindAndComposeHierarchy asyncly wSerially k
404 forM_ [fldr, fldl] $ \k ->
405 describe "Bind and compose" $ bindAndComposeHierarchy asyncly asyncly k
406 forM_ [fldr, fldl] $ \k ->
407 describe "Bind and compose" $ bindAndComposeHierarchy asyncly wAsyncly k
408 forM_ [fldr, fldl] $ \k ->
409 describe "Bind and compose" $ bindAndComposeHierarchy asyncly parallely k
410 forM_ [fldr, fldl] $ \k ->
411 describe "Bind and compose asyncly aheadly" $ bindAndComposeHierarchy asyncly aheadly k
413 forM_ [fldr, fldl] $ \k ->
414 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly serially k
415 forM_ [fldr, fldl] $ \k ->
416 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly wSerially k
417 forM_ [fldr, fldl] $ \k ->
418 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly asyncly k
419 forM_ [fldr, fldl] $ \k ->
420 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly wAsyncly k
421 forM_ [fldr, fldl] $ \k ->
422 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly parallely k
423 forM_ [fldr, fldl] $ \k ->
424 describe "Bind and compose wAsyncly aheadly" $ bindAndComposeHierarchy wAsyncly aheadly k
426 forM_ [fldr, fldl] $ \k ->
427 describe "Bind and compose" $ bindAndComposeHierarchy parallely serially k
428 forM_ [fldr, fldl] $ \k ->
429 describe "Bind and compose" $ bindAndComposeHierarchy parallely wSerially k
430 forM_ [fldr, fldl] $ \k ->
431 describe "Bind and compose" $ bindAndComposeHierarchy parallely asyncly k
432 forM_ [fldr, fldl] $ \k ->
433 describe "Bind and compose" $ bindAndComposeHierarchy parallely wAsyncly k
434 forM_ [fldr, fldl] $ \k ->
435 describe "Bind and compose" $ bindAndComposeHierarchy parallely parallely k
436 forM_ [fldr, fldl] $ \k ->
437 describe "Bind and compose parallely aheadly" $ bindAndComposeHierarchy parallely aheadly k
439 -- Nest two lists using different styles of product compositions
440 it "Nests two streams using monadic serial composition" nestTwoSerial
441 it "Nests two streams using monadic ahead composition" nestTwoAhead
442 it "Nests two streams using monadic interleaved composition" nestTwoInterleaved
443 it "Nests two streams using monadic Async composition" nestTwoAsync
444 it "Nests two streams using monadic WAsync composition" nestTwoWAsync
445 it "Nests two streams using monadic parallel composition" nestTwoParallel
447 it "Nests two streams using applicative serial composition" nestTwoSerialApp
448 it "Nests two streams using applicative ahead composition" nestTwoAheadApp
449 it "Nests two streams using applicative interleaved composition" nestTwoInterleavedApp
450 it "Nests two streams using applicative Async composition" nestTwoAsyncApp
451 it "Nests two streams using applicative WAsync composition" nestTwoWAsyncApp
452 it "Nests two streams using applicative parallel composition" nestTwoParallelApp
454 ---------------------------------------------------------------------------
455 -- TBD Bind and Bind combinations
456 ---------------------------------------------------------------------------
458 -- TBD combine all binds and all compose in one example
459 describe "Miscellaneous combined examples" mixedOps
460 describe "Miscellaneous combined examples aheadly" mixedOpsAheadly
461 describe "Simple MonadError and MonadThrow" simpleMonadError
464 describe "Composed MonadError serially" $ composeWithMonadError serially
465 describe "Composed MonadError wSerially" $ composeWithMonadError wSerially
466 describe "Composed MonadError asyncly" $ composeWithMonadError asyncly
467 describe "Composed MonadError wAsyncly" $ composeWithMonadError wAsyncly
470 describe "Composed MonadThrow serially" $ composeWithMonadThrow serially
471 describe "Composed MonadThrow wSerially" $ composeWithMonadThrow wSerially
472 describe "Composed MonadThrow asyncly" $ composeWithMonadThrow asyncly
473 describe "Composed MonadThrow wAsyncly" $ composeWithMonadThrow wAsyncly
474 describe "Composed MonadThrow parallely" $ composeWithMonadThrow parallely
475 describe "Composed MonadThrow aheadly" $ composeWithMonadThrow aheadly
477 describe "take on infinite concurrent stream" $ takeInfinite asyncly
478 describe "take on infinite concurrent stream" $ takeInfinite wAsyncly
479 describe "take on infinite concurrent stream" $ takeInfinite aheadly
481 ---------------------------------------------------------------------------
482 -- Some ad-hoc tests that failed at times
483 ---------------------------------------------------------------------------
485 it "takes n from stream of streams" (takeCombined 1 aheadly)
486 it "takes n from stream of streams" (takeCombined 2 asyncly)
487 it "takes n from stream of streams" (takeCombined 3 wAsyncly)
489 ---------------------------------------------------------------------------
490 -- Folds are strict enough
491 ---------------------------------------------------------------------------
493 it "foldx is strict enough" checkFoldxStrictness
494 it "foldl' is strict enough" checkFoldl'Strictness
495 it "scanx is strict enough" checkScanxStrictness
496 it "scanl' is strict enough" checkScanl'Strictness
497 it "foldxM is strict enough" (checkFoldMStrictness foldxMStrictCheck)
498 it "foldlM' is strict enough" (checkFoldMStrictness foldlM'StrictCheck)
499 it "scanlM' is strict enough" (checkScanlMStrictness scanlM'StrictCheck)
501 ---------------------------------------------------------------------------
502 -- Monadic state snapshot in concurrent tasks
503 ---------------------------------------------------------------------------
505 it "asyncly maintains independent states in concurrent tasks"
506 (monadicStateSnapshot asyncly)
507 it "asyncly limited maintains independent states in concurrent tasks"
508 (monadicStateSnapshot (asyncly . S.take 10000))
509 it "wAsyncly maintains independent states in concurrent tasks"
510 (monadicStateSnapshot wAsyncly)
511 it "wAsyncly limited maintains independent states in concurrent tasks"
512 (monadicStateSnapshot (wAsyncly . S.take 10000))
513 it "aheadly maintains independent states in concurrent tasks"
514 (monadicStateSnapshot aheadly)
515 it "aheadly limited maintains independent states in concurrent tasks"
516 (monadicStateSnapshot (aheadly . S.take 10000))
517 it "parallely maintains independent states in concurrent tasks"
518 (monadicStateSnapshot parallely)
520 it "async maintains independent states in concurrent tasks"
521 (monadicStateSnapshotOp async)
522 it "ahead maintains independent states in concurrent tasks"
523 (monadicStateSnapshotOp ahead)
524 it "wAsync maintains independent states in concurrent tasks"
525 (monadicStateSnapshotOp wAsync)
526 it "parallel maintains independent states in concurrent tasks"
527 (monadicStateSnapshotOp Streamly.parallel)
529 ---------------------------------------------------------------------------
530 -- Slower tests are at the end
531 ---------------------------------------------------------------------------
533 ---------------------------------------------------------------------------
535 ---------------------------------------------------------------------------
537 it "asyncly crosses thread limit (2000 threads)" $
538 runStream (asyncly $ fold $
539 replicate 2000 $ S.yieldM $ threadDelay 1000000)
542 it "aheadly crosses thread limit (4000 threads)" $
543 runStream (aheadly $ fold $
544 replicate 4000 $ S.yieldM $ threadDelay 1000000)
547-- Each snapshot carries an independent state. Multiple parallel tasks should
548-- not affect each other's state. This is especially important when we run
549-- multiple tasks in a single thread.
550snapshot :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
552 -- We deliberately use a replicate count 1 here, because a lower count
553 -- catches problems that a higher count doesn't.
555 -- Even though we modify the state here it should not reflect in other
556 -- parallel tasks, it is local to each concurrent task.
557 modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==1))
558 modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
560snapshot1 :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
561snapshot1 = S.replicateM 1000 $
562 modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
564snapshot2 :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
565snapshot2 = S.replicateM 1000 $
566 modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
574 , MonadState Int (t m)
578 -- Each task in a concurrent composition inherits the state and maintains
579 -- its own modifications to it, not affecting the parent computation.
580 snapshot <> (modify (+1) >> (snapshot1 <> snapshot2))
581 -- The above modify statement does not affect our state because that is
582 -- used in a parallel composition. In a serial composition it will affect
584 get >>= liftIO . (`shouldSatisfy` (== (0 :: Int)))
588 , Semigroup (t (StateT Int IO) ())
589 , MonadIO (t (StateT Int IO))
590 , MonadState Int (t (StateT Int IO))
592 => (t (StateT Int IO) () -> SerialT (StateT Int IO) ()) -> IO ()
593monadicStateSnapshot t = void $ runStateT (runStream $ t stateComp) 0
596 :: ( AsyncT (StateT Int IO) ()
597 -> AsyncT (StateT Int IO) ()
598 -> AsyncT (StateT Int IO) ()
600 -> SerialT (StateT Int IO) ()
602 -- Each task in a concurrent composition inherits the state and maintains
603 -- its own modifications to it, not affecting the parent computation.
604 asyncly (snapshot `op` (modify (+1) >> (snapshot1 `op` snapshot2)))
605 -- The above modify statement does not affect our state because that is
606 -- used in a parallel composition. In a serial composition it will affect
608 get >>= liftIO . (`shouldSatisfy` (== (0 :: Int)))
610monadicStateSnapshotOp
611 :: ( AsyncT (StateT Int IO) ()
612 -> AsyncT (StateT Int IO) ()
613 -> AsyncT (StateT Int IO) ()
616monadicStateSnapshotOp op = void $ runStateT (runStream $ stateCompOp op) 0
618takeCombined :: (Monad m, Semigroup (t m Int), Show a, Eq a, IsStream t)
619 => Int -> (t m Int -> SerialT IO a) -> IO ()
621 let constr = S.fromFoldable
622 r <- (S.toList . t) $
623 S.take n (constr ([] :: [Int]) <> constr ([] :: [Int]))
626checkFoldxStrictness :: IO ()
627checkFoldxStrictness = do
628 let s = return (1 :: Int) `S.consM` error "failure"
629 catch (S.foldx (\_ a -> if a == 1 then error "success" else "done")
632 ErrorCall err -> return err
634 `shouldReturn` "success"
636checkFoldl'Strictness :: IO ()
637checkFoldl'Strictness = do
638 let s = return (1 :: Int) `S.consM` error "failure"
639 catch (S.foldl' (\_ a -> if a == 1 then error "success" else "done")
642 ErrorCall err -> return err
644 `shouldReturn` "success"
646checkScanxStrictness :: IO ()
647checkScanxStrictness = do
648 let s = return (1 :: Int) `S.consM` error "failure"
660 ErrorCall err -> return err
662 `shouldReturn` "success"
664checkScanl'Strictness :: IO ()
665checkScanl'Strictness = do
666 let s = return (1 :: Int) `S.consM` error "failure"
679 ErrorCall err -> return err
681 `shouldReturn` "success"
683foldlM'StrictCheck :: IORef Int -> SerialT IO Int -> IO ()
684foldlM'StrictCheck ref = S.foldlM' (\_ _ -> writeIORef ref 1) ()
686foldxMStrictCheck :: IORef Int -> SerialT IO Int -> IO ()
687foldxMStrictCheck ref = S.foldxM (\_ _ -> writeIORef ref 1) (return ()) return
689checkFoldMStrictness :: (IORef Int -> SerialT IO Int -> IO ()) -> IO ()
690checkFoldMStrictness f = do
692 let s = return 1 `S.consM` error "x"
693 catch (f ref s) (\(_ :: ErrorCall) -> return ())
694 readIORef ref `shouldReturn` 1
696scanlM'StrictCheck :: IORef Int -> SerialT IO Int -> SerialT IO ()
697scanlM'StrictCheck ref = S.scanlM' (\_ _ -> writeIORef ref 1) ()
699checkScanlMStrictness :: (IORef Int -> SerialT IO Int -> SerialT IO ()) -> IO ()
700checkScanlMStrictness f = do
702 let s = return 1 `S.consM` error "x"
703 catch (runStream $ f ref s) (\(_ :: ErrorCall) -> return ())
704 readIORef ref `shouldReturn` 1
706takeInfinite :: IsStream t => (t IO Int -> SerialT IO Int) -> Spec
709 runStream (t $ S.take 1 $ S.repeatM (print "hello" >> return (1::Int)))
712-- XXX need to test that we have promptly cleaned up everything after the error
713-- XXX We can also check the output that we are expected to get before the
716newtype ExampleException = ExampleException String deriving (Eq, Show)
718instance Exception ExampleException
720simpleMonadError :: Spec
723 it "simple runExceptT" $ do
724 (runExceptT $ runStream $ return ())
725 `shouldReturn` (Right () :: Either String ())
726 it "simple runExceptT with error" $ do
727 (runExceptT $ runStream $ throwError "E") `shouldReturn` Left "E"
730 try (runStream $ return ())
731 `shouldReturn` (Right () :: Either ExampleException ())
732 it "simple try with throw error" $
733 try (runStream $ throwM $ ExampleException "E")
734 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException ())
738 , Semigroup (t IO Int)
741 => (t IO Int -> SerialT IO Int) -> Spec
742composeWithMonadThrow t = do
743 it "Compose throwM, nil" $
744 try (tl (throwM (ExampleException "E") <> S.nil))
745 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
746 it "Compose nil, throwM" $
747 try (tl (S.nil <> throwM (ExampleException "E")))
748 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
749 oneLevelNestedSum "serially" serially
750 oneLevelNestedSum "wSerially" wSerially
751 oneLevelNestedSum "asyncly" asyncly
752 oneLevelNestedSum "wAsyncly" wAsyncly
753 -- XXX add two level nesting
755 oneLevelNestedProduct "serially" serially
756 oneLevelNestedProduct "wSerially" wSerially
757 oneLevelNestedProduct "asyncly" asyncly
758 oneLevelNestedProduct "wAsyncly" wAsyncly
762 oneLevelNestedSum desc t1 =
763 it ("One level nested sum " <> desc) $ do
764 let nested = S.fromFoldable [1..10] <> throwM (ExampleException "E")
765 <> S.fromFoldable [1..10]
766 try (tl (S.nil <> t1 nested <> S.fromFoldable [1..10]))
767 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
769 oneLevelNestedProduct desc t1 =
770 it ("One level nested product" <> desc) $ do
771 let s1 = t $ foldMapWith (<>) return [1..4]
772 s2 = t1 $ foldMapWith (<>) return [5..8]
777 then throwM (ExampleException "E")
780 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
782_composeWithMonadError
784 , Semigroup (t (ExceptT String IO) Int)
785 , MonadError String (t (ExceptT String IO))
787 => (t (ExceptT String IO) Int -> SerialT (ExceptT String IO) Int) -> Spec
788_composeWithMonadError t = do
789 let tl = S.toList . t
790 it "Compose throwError, nil" $
791 runExceptT (tl (throwError "E" <> S.nil)) `shouldReturn` Left "E"
792 it "Compose nil, error" $
793 runExceptT (tl (S.nil <> throwError "E")) `shouldReturn` Left "E"
795nestTwoSerial :: Expectation
797 let s1 = foldMapWith (<>) return [1..4]
798 s2 = foldMapWith (<>) return [5..8]
803 ) `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
805nestTwoAhead :: Expectation
807 let s1 = foldMapWith (<>) return [1..4]
808 s2 = foldMapWith (<>) return [5..8]
809 in (S.toList . aheadly) (do
813 ) `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
815nestTwoSerialApp :: Expectation
817 let s1 = foldMapWith (<>) return [1..4]
818 s2 = foldMapWith (<>) return [5..8]
819 in toListSerial ((+) <$> s1 <*> s2)
820 `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
822nestTwoAheadApp :: Expectation
824 let s1 = foldMapWith (<>) return [1..4]
825 s2 = foldMapWith (<>) return [5..8]
826 in (S.toList . aheadly) ((+) <$> s1 <*> s2)
827 `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
829nestTwoInterleaved :: Expectation
831 let s1 = foldMapWith (<>) return [1..4]
832 s2 = foldMapWith (<>) return [5..8]
833 in toListInterleaved (do
837 ) `shouldReturn` ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
839nestTwoInterleavedApp :: Expectation
840nestTwoInterleavedApp =
841 let s1 = foldMapWith (<>) return [1..4]
842 s2 = foldMapWith (<>) return [5..8]
843 in toListInterleaved ((+) <$> s1 <*> s2)
844 `shouldReturn` ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
846nestTwoAsync :: Expectation
848 let s1 = foldMapWith (<>) return [1..4]
849 s2 = foldMapWith (<>) return [5..8]
850 in sort <$> toListAsync (do
854 `shouldReturn` sort ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
856nestTwoAsyncApp :: Expectation
858 let s1 = foldMapWith (<>) return [1..4]
859 s2 = foldMapWith (<>) return [5..8]
860 in sort <$> toListAsync ((+) <$> s1 <*> s2)
861 `shouldReturn` sort ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
863nestTwoWAsync :: Expectation
865 let s1 = foldMapWith (<>) return [1..4]
866 s2 = foldMapWith (<>) return [5..8]
867 in sort <$> (S.toList . wAsyncly) (do
871 `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
873nestTwoParallel :: Expectation
875 let s1 = foldMapWith (<>) return [1..4]
876 s2 = foldMapWith (<>) return [5..8]
877 in sort <$> (S.toList . parallely) (do
881 `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
883nestTwoWAsyncApp :: Expectation
885 let s1 = foldMapWith (<>) return [1..4]
886 s2 = foldMapWith (<>) return [5..8]
887 in sort <$> (S.toList . wAsyncly) ((+) <$> s1 <*> s2)
888 `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
890nestTwoParallelApp :: Expectation
892 let s1 = foldMapWith (<>) return [1..4]
893 s2 = foldMapWith (<>) return [5..8]
894 in sort <$> (S.toList . parallely) ((+) <$> s1 <*> s2)
895 `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
897interleaveCheck :: IsStream t
898 => (t IO Int -> SerialT IO Int)
899 -> (t IO Int -> t IO Int -> t IO Int)
902 it "Interleave four" $
903 (S.toList . t) ((singleton 0 `f` singleton 1) `f` (singleton 100 `f` singleton 101))
904 `shouldReturn` [0, 100, 1, 101]
906parallelCheck :: (IsStream t, Monad (t IO))
907 => (t IO Int -> SerialT IO Int)
908 -> (t IO Int -> t IO Int -> t IO Int)
910parallelCheck t f = do
911 it "Parallel ordering left associated" $
912 (S.toList . t) (((event 4 `f` event 3) `f` event 2) `f` event 1)
913 `shouldReturn` [1..4]
915 it "Parallel ordering right associated" $
916 (S.toList . t) (event 4 `f` (event 3 `f` (event 2 `f` event 1)))
917 `shouldReturn` [1..4]
919 where event n = S.yieldM (threadDelay (n * 200000)) >> return n
921compose :: (IsStream t, Semigroup (t IO Int))
922 => (t IO Int -> SerialT IO Int) -> t IO Int -> ([Int] -> [Int]) -> Spec
924 -- XXX these should get covered by the property tests
925 it "Compose mempty, mempty" $
926 tl (z <> z) `shouldReturn` ([] :: [Int])
927 it "Compose empty at the beginning" $
928 tl (z <> singleton 1) `shouldReturn` [1]
929 it "Compose empty at the end" $
930 tl (singleton 1 <> z) `shouldReturn` [1]
932 srt <$> tl (singleton 0 <> singleton 1)
933 `shouldReturn` [0, 1]
935 srt <$> tl (forEachWith (<>) [1..100] singleton)
936 `shouldReturn` [1..100]
938 -- These are not covered by the property tests
939 it "Compose three - empty in the middle" $
940 srt <$> tl (singleton 0 <> z <> singleton 1)
941 `shouldReturn` [0, 1]
942 it "Compose left associated" $
943 srt <$> tl (((singleton 0 <> singleton 1) <> singleton 2) <> singleton 3)
944 `shouldReturn` [0, 1, 2, 3]
945 it "Compose right associated" $
946 srt <$> tl (singleton 0 <> (singleton 1 <> (singleton 2 <> singleton 3)))
947 `shouldReturn` [0, 1, 2, 3]
948 it "Compose hierarchical (multiple levels)" $
949 srt <$> tl (((singleton 0 <> singleton 1) <> (singleton 2 <> singleton 3))
950 <> ((singleton 4 <> singleton 5) <> (singleton 6 <> singleton 7)))
951 `shouldReturn` [0..7]
952 where tl = S.toList . t
954composeAndComposeSimple
955 :: ( IsStream t1, Semigroup (t1 IO Int)
956 , IsStream t2, Monoid (t2 IO Int), Monad (t2 IO)
957#if __GLASGOW_HASKELL__ < 804
958 , Semigroup (t2 IO Int)
961 => (t1 IO Int -> SerialT IO Int)
962 -> (t2 IO Int -> t2 IO Int)
964composeAndComposeSimple t1 t2 answer = do
965 let rfold = adapt . t2 . foldMapWith (<>) return
966 it "Compose right associated outer expr, right folded inner" $
967 (S.toList . t1) (rfold [1,2,3] <> (rfold [4,5,6] <> rfold [7,8,9]))
968 `shouldReturn` head answer
970 it "Compose left associated outer expr, right folded inner" $
971 (S.toList . t1) ((rfold [1,2,3] <> rfold [4,5,6]) <> rfold [7,8,9])
972 `shouldReturn` (answer !! 1)
974 let lfold xs = adapt $ t2 $ foldl (<>) mempty $ fmap return xs
975 it "Compose right associated outer expr, left folded inner" $
976 (S.toList . t1) (lfold [1,2,3] <> (lfold [4,5,6] <> lfold [7,8,9]))
977 `shouldReturn` (answer !! 2)
979 it "Compose left associated outer expr, left folded inner" $
980 (S.toList . t1) ((lfold [1,2,3] <> lfold [4,5,6]) <> lfold [7,8,9])
981 `shouldReturn` (answer !! 3)
984 :: (IsStream t, Semigroup (t IO Int), Monad (t IO))
985 => (t IO Int -> t IO Int)
989loops t tsrt hsrt = do
990 it "Tail recursive loop" $ (tsrt <$> (S.toList . adapt) (loopTail 0))
991 `shouldReturn` [0..3]
993 it "Head recursive loop" $ (hsrt <$> (S.toList . adapt) (loopHead 0))
994 `shouldReturn` [0..3]
998 -- this print line is important for the test (causes a bind)
999 S.yieldM $ putStrLn "LoopHead..."
1000 t $ (if x < 3 then loopHead (x + 1) else nil) <> return x
1003 -- this print line is important for the test (causes a bind)
1004 S.yieldM $ putStrLn "LoopTail..."
1005 t $ return x <> (if x < 3 then loopTail (x + 1) else nil)
1008 :: ( IsStream t1, IsStream t2, Semigroup (t2 IO Int), Monad (t2 IO))
1009 => (t1 IO Int -> SerialT IO Int)
1010 -> (t2 IO Int -> t2 IO Int)
1012bindAndComposeSimple t1 t2 = do
1013 -- XXX need a bind in the body of forEachWith instead of a simple return
1014 it "Compose many (right fold) with bind" $
1015 (sort <$> (S.toList . t1)
1016 (adapt . t2 $ forEachWith (<>) [1..10 :: Int] return))
1017 `shouldReturn` [1..10]
1019 it "Compose many (left fold) with bind" $
1020 let forL xs k = foldl (<>) nil $ fmap k xs
1021 in (sort <$> (S.toList . t1) (adapt . t2 $ forL [1..10 :: Int] return))
1022 `shouldReturn` [1..10]
1024bindAndComposeHierarchy
1025 :: ( IsStream t1, Monad (t1 IO)
1026 , IsStream t2, Monad (t2 IO))
1027 => (t1 IO Int -> SerialT IO Int)
1028 -> (t2 IO Int -> t2 IO Int)
1029 -> ([t2 IO Int] -> t2 IO Int)
1031bindAndComposeHierarchy t1 t2 g =
1032 it "Bind and compose nested" $
1033 (sort <$> (S.toList . t1) bindComposeNested)
1034 `shouldReturn` (sort (
1040 <> replicate 7 15) :: [Int])
1044 -- bindComposeNested :: WAsyncT IO Int
1046 let c1 = tripleCompose (return 1) (return 2) (return 3)
1047 c2 = tripleCompose (return 4) (return 5) (return 6)
1048 c3 = tripleCompose (return 7) (return 8) (return 9)
1049 b = tripleBind c1 c2 c3
1050-- it seems to be causing a huge space leak in hspec so disabling this for now
1051-- c = tripleCompose b b b
1052-- m = tripleBind c c c
1056 tripleCompose a b c = adapt . t2 $ g [a, b, c]
1057 tripleBind mx my mz =
1060 >>= \z -> return (x + y + z)
1064 it "Compose many ops" $
1065 (sort <$> toListSerial composeMixed)
1066 `shouldReturn` ([8,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,11,11
1067 ,11,11,11,11,11,11,11,11,12,12,12,12,12,13
1071 composeMixed :: SerialT IO Int
1073 S.yieldM $ return ()
1074 S.yieldM $ putStr ""
1078 x1 <- wAsyncly $ return 1 <> return 2
1079 S.yieldM $ return ()
1080 S.yieldM $ putStr ""
1081 y1 <- asyncly $ return 1 <> return 2
1083 x11 <- return 1 <> return 2
1084 y11 <- asyncly $ return 1 <> return 2
1085 z11 <- wSerially $ return 1 <> return 2
1086 S.yieldM $ return ()
1087 S.yieldM $ putStr ""
1088 return (x11 + y11 + z11)
1089 return (x1 + y1 + z1)
1092mixedOpsAheadly :: Spec
1094 it "Compose many ops" $
1095 (sort <$> toListSerial composeMixed)
1096 `shouldReturn` ([8,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,11,11
1097 ,11,11,11,11,11,11,11,11,12,12,12,12,12,13
1101 composeMixed :: SerialT IO Int
1103 S.yieldM $ return ()
1104 S.yieldM $ putStr ""
1108 x1 <- wAsyncly $ return 1 <> return 2
1109 S.yieldM $ return ()
1110 S.yieldM $ putStr ""
1111 y1 <- aheadly $ return 1 <> return 2
1113 x11 <- return 1 <> return 2
1114 y11 <- aheadly $ return 1 <> return 2
1115 z11 <- parallely $ return 1 <> return 2
1116 S.yieldM $ return ()
1117 S.yieldM $ putStr ""
1118 return (x11 + y11 + z11)
1119 return (x1 + y1 + z1)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list