1module Main (main) where
3import Control.Concurrent (threadDelay)
4import Control.Exception (Exception, try, ErrorCall(..), catch)
5import Control.Monad (void)
6import Control.Monad.Catch (throwM, MonadThrow)
7import Control.Monad.Error.Class (throwError, MonadError)
8import Control.Monad.IO.Class (MonadIO(liftIO))
9import Control.Monad.State (MonadState, get, modify, runStateT
10 , StateT(..), evalStateT)
11import Control.Monad.Trans.Except (runExceptT, ExceptT)
12import Data.Foldable (forM_, fold)
13import Data.Function ((&))
14import Data.List (sort)
15import Data.Maybe (fromJust, isJust)
16import System.Mem (performMajorGC)
22import Streamly.Prelude ((.:), nil)
23import qualified Streamly as S
24import qualified Streamly.Prelude as S
25import qualified Streamly.Internal.Data.Stream.IsStream as IP
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
43 describe "restricts concurrency and cleans up extra tasks" $ do
44 it "take 1 asyncly" $ checkCleanup 2 asyncly (S.take 1)
45 it "take 1 wAsyncly" $ checkCleanup 2 wAsyncly (S.take 1)
46 it "take 1 aheadly" $ checkCleanup 2 aheadly (S.take 1)
48 it "takeWhile (< 0) asyncly" $ checkCleanup 2 asyncly (S.takeWhile (< 0))
49 it "takeWhile (< 0) wAsyncly" $ checkCleanup 2 wAsyncly (S.takeWhile (< 0))
50 it "takeWhile (< 0) aheadly" $ checkCleanup 2 aheadly (S.takeWhile (< 0))
53 let timed :: (IsStream t, Monad (t IO)) => Int -> t IO Int
54 timed x = S.yieldM (threadDelay (x * 100000)) >> return x
56 -- These are not run parallely because the timing gets affected
57 -- unpredictably when other tests are running on the same machine.
59 -- Also, they fail intermittently due to scheduling delays, so not run on
61 describe "Nested parallel and serial compositions" $ do
66 -- This is not correct, the result can also be [4,4,8,0,8,0,2,2]
67 -- because of parallelism of [8,0] and [8,0].
68 it "Nest <|>, <>, <|> (1)" $
71 ((t 8 <|> t 4) <> (t 2 <|> t 0))
72 <|> ((t 8 <|> t 4) <> (t 2 <|> t 0)))
73 `shouldReturn` ([4,4,8,8,0,0,2,2])
75 it "Nest <|>, <>, <|> (2)" $
76 (S.toList . wAsyncly) (
77 s (p (t 4 <> t 8) <> p (t 1 <> t 2))
78 <> s (p (t 4 <> t 8) <> p (t 1 <> t 2)))
79 `shouldReturn` ([4,4,8,8,1,1,2,2])
80 -- FIXME: These two keep failing intermittently on Mac OS X
81 -- Need to examine and fix the tests.
83 it "Nest <|>, <=>, <|> (1)" $
86 ((t 8 <|> t 4) <=> (t 2 <|> t 0))
87 <|> ((t 9 <|> t 4) <=> (t 2 <|> t 0)))
88 `shouldReturn` ([4,4,0,0,8,2,9,2])
89 it "Nest <|>, <=>, <|> (2)" $
92 ((t 4 <|> t 8) <=> (t 1 <|> t 2))
93 <|> ((t 4 <|> t 9) <=> (t 1 <|> t 2)))
94 `shouldReturn` ([4,4,1,1,8,2,9,2])
96 it "Nest <|>, <|>, <|>" $
97 (S.toList . wAsyncly) (
98 ((t 4 <> t 8) <> (t 0 <> t 2))
99 <> ((t 4 <> t 8) <> (t 0 <> t 2)))
100 `shouldReturn` ([0,0,2,2,4,4,8,8])
102 -- parallely fails on CI machines, may need more difference in times of
103 -- the events, but that would make tests even slower.
104 it "take 1 parallely" $ checkCleanup 3 parallely (S.take 1)
105 it "takeWhile (< 0) parallely" $ checkCleanup 3 parallely (S.takeWhile (< 0))
107 testFoldOpsCleanup "head" S.head
108 testFoldOpsCleanup "null" S.null
109 testFoldOpsCleanup "elem" (S.elem 0)
110 testFoldOpsCleanup "notElem" (S.notElem 0)
111 testFoldOpsCleanup "elemIndex" (S.elemIndex 0)
113 testFoldOpsCleanup "notElem" (S.notElem 0)
114 testFoldOpsCleanup "find" (S.find (==0))
115 testFoldOpsCleanup "findIndex" (S.findIndex (==0))
116 testFoldOpsCleanup "all" (S.all (==1))
117 testFoldOpsCleanup "any" (S.any (==0))
118 testFoldOpsCleanup "and" (S.and . S.map (==1))
119 testFoldOpsCleanup "or" (S.or . S.map (==0))
122 ---------------------------------------------------------------------------
123 -- Semigroup/Monoidal Composition strict ordering checks
124 ---------------------------------------------------------------------------
126 -- test both (<>) and mappend to make sure we are using correct instance
127 -- for Monoid that is using the right version of semigroup. Instance
128 -- deriving can cause us to pick wrong instances sometimes.
130 describe "WSerial interleaved (<>) ordering check" $ interleaveCheck wSerially (<>)
131 describe "WSerial interleaved mappend ordering check" $ interleaveCheck wSerially mappend
133 -- describe "WAsync interleaved (<>) ordering check" $ interleaveCheck wAsyncly (<>)
134 -- describe "WAsync interleaved mappend ordering check" $ interleaveCheck wAsyncly mappend
136 describe "Async (<>) time order check" $ parallelCheck asyncly (<>)
137 describe "Async mappend time order check" $ parallelCheck asyncly mappend
139 -- XXX this keeps failing intermittently, need to investigate
140 -- describe "WAsync (<>) time order check" $ parallelCheck wAsyncly (<>)
141 -- describe "WAsync mappend time order check" $ parallelCheck wAsyncly mappend
143 describe "Parallel (<>) time order check" $ parallelCheck parallely (<>)
144 describe "Parallel mappend time order check" $ parallelCheck parallely mappend
145 it "fromCallback" $ testFromCallback `shouldReturn` (50*101)
147checkCleanup :: IsStream t
149 -> (t IO Int -> SerialT IO Int)
150 -> (t IO Int -> t IO Int)
152checkCleanup d t op = do
153 r <- newIORef (-1 :: Int)
154 S.drain . serially $ do
155 _ <- t $ op $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
162 delay ref i = threadDelay (i*d*100000) >> writeIORef ref i >> return i
165checkCleanupFold :: IsStream t
166 => (t IO Int -> SerialT IO Int)
167 -> (SerialT IO Int -> IO (Maybe Int))
169checkCleanupFold t op = do
170 r <- newIORef (-1 :: Int)
171 _ <- op $ t $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
177 delay ref i = threadDelay (i*200000) >> writeIORef ref i >> return i
179testFoldOpsCleanup :: String -> (SerialT IO Int -> IO a) -> Spec
180testFoldOpsCleanup name f = do
181 let testOp op x = op x >> return Nothing
182 it (name <> " asyncly") $ checkCleanupFold asyncly (testOp f)
183 it (name <> " wAsyncly") $ checkCleanupFold wAsyncly (testOp f)
184 it (name <> " aheadly") $ checkCleanupFold aheadly (testOp f)
185 it (name <> " parallely") $ checkCleanupFold parallely (testOp f)
188parallelTests :: SpecWith ()
189parallelTests = H.parallel $ do
191 ---------------------------------------------------------------------------
192 -- TBD Monoidal composition combinations
193 ---------------------------------------------------------------------------
195 -- TBD need more such combinations to be tested.
196 describe "serial <> and serial <>" $
197 composeAndComposeSimple serially serially (repeat [1 .. 9])
198 describe "ahead <> and ahead <>" $
199 composeAndComposeSimple aheadly aheadly (repeat [1 .. 9])
200 describe "ahead <> and serial <>" $
201 composeAndComposeSimple aheadly serially (repeat [1 .. 9])
202 describe "serial <> and ahead <>" $
203 composeAndComposeSimple serially aheadly (repeat [1 .. 9])
205 describe "<> and <=>" $ composeAndComposeSimple
210 , [1, 3, 2, 4, 6, 5, 7, 9, 8]
211 , [1, 3, 2, 4, 6, 5, 7, 9, 8]
214 describe "<=> and <=>" $ composeAndComposeSimple
217 [ [1, 4, 2, 7, 3, 5, 8, 6, 9]
218 , [1, 7, 4, 8, 2, 9, 5, 3, 6]
219 , [1, 4, 3, 7, 2, 6, 9, 5, 8]
220 , [1, 7, 4, 9, 3, 8, 6, 2, 5]
223 describe "<=> and <>" $ composeAndComposeSimple
226 [ [1, 4, 2, 7, 3, 5, 8, 6, 9]
227 , [1, 7, 4, 8, 2, 9, 5, 3, 6]
228 , [1, 4, 2, 7, 3, 5, 8, 6, 9]
229 , [1, 7, 4, 8, 2, 9, 5, 3, 6]
232 ---------------------------------------------------------------------------
233 -- Monoidal composition recursion loops
234 ---------------------------------------------------------------------------
236 describe "Serial loops" $ loops serially id reverse
237 describe "Ahead loops" $ loops aheadly id reverse
238 describe "Async parallel loops" $ loops asyncly sort sort
239 describe "WAsync loops" $ loops wAsyncly sort sort
240 describe "parallel loops" $ loops parallely sort sort
242 ---------------------------------------------------------------------------
243 -- Bind and monoidal composition combinations
244 ---------------------------------------------------------------------------
246 describe "Bind and compose Stream 1" $ bindAndComposeSimple serially serially
247 describe "Bind and compose Stream 2" $ bindAndComposeSimple serially wSerially
248 describe "Bind and compose Stream 3" $ bindAndComposeSimple serially asyncly
249 describe "Bind and compose Stream 4" $ bindAndComposeSimple serially wAsyncly
250 describe "Bind and compose Stream 5" $ bindAndComposeSimple serially parallely
251 describe "Bind and compose Stream 6" $ bindAndComposeSimple serially aheadly
253 describe "Bind and compose Ahead Stream 0" $ bindAndComposeSimple aheadly aheadly
254 describe "Bind and compose Ahead Stream 1" $ bindAndComposeSimple aheadly serially
255 describe "Bind and compose Ahead Stream 2" $ bindAndComposeSimple aheadly wSerially
256 describe "Bind and compose Ahead Stream 3" $ bindAndComposeSimple aheadly asyncly
257 describe "Bind and compose Ahead Stream 4" $ bindAndComposeSimple aheadly wAsyncly
258 describe "Bind and compose Ahead Stream 5" $ bindAndComposeSimple aheadly parallely
260 describe "Bind and compose Costream 1" $ bindAndComposeSimple wSerially serially
261 describe "Bind and compose Costream 2" $ bindAndComposeSimple wSerially wSerially
262 describe "Bind and compose Costream 3" $ bindAndComposeSimple wSerially asyncly
263 describe "Bind and compose Costream 4" $ bindAndComposeSimple wSerially wAsyncly
264 describe "Bind and compose Costream 5" $ bindAndComposeSimple wSerially parallely
265 describe "Bind and compose Costream 6" $ bindAndComposeSimple wSerially aheadly
267 describe "Bind and compose Async 1" $ bindAndComposeSimple asyncly serially
268 describe "Bind and compose Async 2" $ bindAndComposeSimple asyncly wSerially
269 describe "Bind and compose Async 3" $ bindAndComposeSimple asyncly asyncly
270 describe "Bind and compose Async 4" $ bindAndComposeSimple asyncly wAsyncly
271 describe "Bind and compose Async 5" $ bindAndComposeSimple asyncly parallely
272 describe "Bind and compose Async 6" $ bindAndComposeSimple asyncly aheadly
274 describe "Bind and compose WAsync 1" $ bindAndComposeSimple wAsyncly serially
275 describe "Bind and compose WAsync 2" $ bindAndComposeSimple wAsyncly wSerially
276 describe "Bind and compose WAsync 3" $ bindAndComposeSimple wAsyncly asyncly
277 describe "Bind and compose WAsync 4" $ bindAndComposeSimple wAsyncly wAsyncly
278 describe "Bind and compose WAsync 5" $ bindAndComposeSimple wAsyncly parallely
279 describe "Bind and compose WAsync 6" $ bindAndComposeSimple wAsyncly aheadly
281 describe "Bind and compose Parallel 1" $ bindAndComposeSimple parallely serially
282 describe "Bind and compose Parallel 2" $ bindAndComposeSimple parallely wSerially
283 describe "Bind and compose Parallel 3" $ bindAndComposeSimple parallely asyncly
284 describe "Bind and compose Parallel 4" $ bindAndComposeSimple parallely wAsyncly
285 describe "Bind and compose Parallel 5" $ bindAndComposeSimple parallely parallely
286 describe "Bind and compose Parallel 6" $ bindAndComposeSimple parallely aheadly
288 let fldr, fldl :: (IsStream t, Semigroup (t IO Int)) => [t IO Int] -> t IO Int
289 fldr = foldr (<>) nil
290 fldl = foldl (<>) nil
292 forM_ [fldr, fldl] $ \k ->
293 describe "Bind and compose" $ bindAndComposeHierarchy serially serially k
294 forM_ [fldr, fldl] $ \k ->
295 describe "Bind and compose" $ bindAndComposeHierarchy serially wSerially k
296 forM_ [fldr, fldl] $ \k ->
297 describe "Bind and compose" $ bindAndComposeHierarchy serially asyncly k
298 forM_ [fldr, fldl] $ \k ->
299 describe "Bind and compose" $ bindAndComposeHierarchy serially wAsyncly k
300 forM_ [fldr, fldl] $ \k ->
301 describe "Bind and compose" $ bindAndComposeHierarchy serially parallely k
302 forM_ [fldr, fldl] $ \k ->
303 describe "Bind and compose serially aheadly" $ bindAndComposeHierarchy serially aheadly k
305 forM_ [fldr, fldl] $ \k ->
306 describe "Bind and compose aheadly serially" $ bindAndComposeHierarchy aheadly serially k
307 forM_ [fldr, fldl] $ \k ->
308 describe "Bind and compose aheadly wSerially" $ bindAndComposeHierarchy aheadly wSerially k
309 forM_ [fldr, fldl] $ \k ->
310 describe "Bind and compose aheadly asyncly" $ bindAndComposeHierarchy aheadly asyncly k
311 forM_ [fldr, fldl] $ \k ->
312 describe "Bind and compose aheadly wAsyncly" $ bindAndComposeHierarchy aheadly wAsyncly k
313 forM_ [fldr, fldl] $ \k ->
314 describe "Bind and compose aheadly parallely" $ bindAndComposeHierarchy aheadly parallely k
315 forM_ [fldr, fldl] $ \k ->
316 describe "Bind and compose serially aheadly" $ bindAndComposeHierarchy aheadly aheadly k
318 forM_ [fldr, fldl] $ \k ->
319 describe "Bind and compose" $ bindAndComposeHierarchy wSerially serially k
320 forM_ [fldr, fldl] $ \k ->
321 describe "Bind and compose" $ bindAndComposeHierarchy wSerially wSerially k
322 forM_ [fldr, fldl] $ \k ->
323 describe "Bind and compose" $ bindAndComposeHierarchy wSerially asyncly k
324 forM_ [fldr, fldl] $ \k ->
325 describe "Bind and compose" $ bindAndComposeHierarchy wSerially wAsyncly k
326 forM_ [fldr, fldl] $ \k ->
327 describe "Bind and compose" $ bindAndComposeHierarchy wSerially parallely k
328 forM_ [fldr, fldl] $ \k ->
329 describe "Bind and compose wserially aheadly" $ bindAndComposeHierarchy wSerially aheadly k
331 forM_ [fldr, fldl] $ \k ->
332 describe "Bind and compose" $ bindAndComposeHierarchy asyncly serially k
333 forM_ [fldr, fldl] $ \k ->
334 describe "Bind and compose" $ bindAndComposeHierarchy asyncly wSerially k
335 forM_ [fldr, fldl] $ \k ->
336 describe "Bind and compose" $ bindAndComposeHierarchy asyncly asyncly k
337 forM_ [fldr, fldl] $ \k ->
338 describe "Bind and compose" $ bindAndComposeHierarchy asyncly wAsyncly k
339 forM_ [fldr, fldl] $ \k ->
340 describe "Bind and compose" $ bindAndComposeHierarchy asyncly parallely k
341 forM_ [fldr, fldl] $ \k ->
342 describe "Bind and compose asyncly aheadly" $ bindAndComposeHierarchy asyncly aheadly k
344 forM_ [fldr, fldl] $ \k ->
345 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly serially k
346 forM_ [fldr, fldl] $ \k ->
347 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly wSerially k
348 forM_ [fldr, fldl] $ \k ->
349 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly asyncly k
350 forM_ [fldr, fldl] $ \k ->
351 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly wAsyncly k
352 forM_ [fldr, fldl] $ \k ->
353 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly parallely k
354 forM_ [fldr, fldl] $ \k ->
355 describe "Bind and compose wAsyncly aheadly" $ bindAndComposeHierarchy wAsyncly aheadly k
357 forM_ [fldr, fldl] $ \k ->
358 describe "Bind and compose" $ bindAndComposeHierarchy parallely serially k
359 forM_ [fldr, fldl] $ \k ->
360 describe "Bind and compose" $ bindAndComposeHierarchy parallely wSerially k
361 forM_ [fldr, fldl] $ \k ->
362 describe "Bind and compose" $ bindAndComposeHierarchy parallely asyncly k
363 forM_ [fldr, fldl] $ \k ->
364 describe "Bind and compose" $ bindAndComposeHierarchy parallely wAsyncly k
365 forM_ [fldr, fldl] $ \k ->
366 describe "Bind and compose" $ bindAndComposeHierarchy parallely parallely k
367 forM_ [fldr, fldl] $ \k ->
368 describe "Bind and compose parallely aheadly" $ bindAndComposeHierarchy parallely aheadly k
370 -- Nest two lists using different styles of product compositions
371 it "Nests two streams using monadic serial composition" nestTwoSerial
372 it "Nests two streams using monadic ahead composition" nestTwoAhead
373 it "Nests two streams using monadic interleaved composition" nestTwoInterleaved
374 it "Nests two streams using monadic Async composition" nestTwoAsync
375 it "Nests two streams using monadic WAsync composition" nestTwoWAsync
376 it "Nests two streams using monadic parallel composition" nestTwoParallel
378 it "Nests two streams using applicative serial composition" nestTwoSerialApp
379 it "Nests two streams using applicative ahead composition" nestTwoAheadApp
380 it "Nests two streams using applicative interleaved composition" nestTwoInterleavedApp
381 it "Nests two streams using applicative Async composition" nestTwoAsyncApp
382 it "Nests two streams using applicative WAsync composition" nestTwoWAsyncApp
383 it "Nests two streams using applicative parallel composition" nestTwoParallelApp
385 ---------------------------------------------------------------------------
386 -- TBD Bind and Bind combinations
387 ---------------------------------------------------------------------------
389 -- TBD combine all binds and all compose in one example
390 describe "Miscellaneous combined examples" mixedOps
391 describe "Miscellaneous combined examples aheadly" mixedOpsAheadly
392 describe "Simple MonadError and MonadThrow" simpleMonadError
395 describe "Composed MonadError serially" $ composeWithMonadError serially
396 describe "Composed MonadError wSerially" $ composeWithMonadError wSerially
397 describe "Composed MonadError asyncly" $ composeWithMonadError asyncly
398 describe "Composed MonadError wAsyncly" $ composeWithMonadError wAsyncly
401 describe "Composed MonadThrow serially" $ composeWithMonadThrow serially
402 describe "Composed MonadThrow wSerially" $ composeWithMonadThrow wSerially
403 describe "Composed MonadThrow asyncly" $ composeWithMonadThrow asyncly
404 describe "Composed MonadThrow wAsyncly" $ composeWithMonadThrow wAsyncly
405 describe "Composed MonadThrow parallely" $ composeWithMonadThrow parallely
406 describe "Composed MonadThrow aheadly" $ composeWithMonadThrow aheadly
408 describe "take on infinite concurrent stream" $ takeInfinite asyncly
409 describe "take on infinite concurrent stream" $ takeInfinite wAsyncly
410 describe "take on infinite concurrent stream" $ takeInfinite aheadly
412 ---------------------------------------------------------------------------
413 -- Some ad-hoc tests that failed at times
414 ---------------------------------------------------------------------------
416 it "takes n from stream of streams" (takeCombined 1 aheadly)
417 it "takes n from stream of streams" (takeCombined 2 asyncly)
418 it "takes n from stream of streams" (takeCombined 3 wAsyncly)
420 ---------------------------------------------------------------------------
421 -- Left folds are strict enough
422 ---------------------------------------------------------------------------
425 it "foldx is strict enough" checkFoldxStrictness
426 it "scanx is strict enough" checkScanxStrictness
427 it "foldxM is strict enough" (checkFoldMStrictness foldxMStrictCheck)
429 it "foldl' is strict enough" checkFoldl'Strictness
430 it "scanl' is strict enough" checkScanl'Strictness
431 it "foldlM' is strict enough" (checkFoldMStrictness foldlM'StrictCheck)
432 it "scanlM' is strict enough" (checkScanlMStrictness scanlM'StrictCheck)
434 ---------------------------------------------------------------------------
435 -- Right folds are lazy enough
436 ---------------------------------------------------------------------------
438 it "foldrM is lazy enough" checkFoldrLaziness
440 ---------------------------------------------------------------------------
441 -- Monadic state transfer in concurrent tasks
442 ---------------------------------------------------------------------------
444 -- XXX Can we write better test cases to hit every case?
446 it "async: state is saved and used if the work is partially enqueued"
447 (checkMonadicStateTransfer async)
449 it "wAsync: state is saved and used if the work is partially enqueued"
450 (checkMonadicStateTransfer wAsync)
452 it "ahead: state is saved and used if the work is partially enqueued"
453 (checkMonadicStateTransfer ahead)
455 ---------------------------------------------------------------------------
456 -- Monadic state snapshot in concurrent tasks
457 ---------------------------------------------------------------------------
459 it "asyncly maintains independent states in concurrent tasks"
460 (monadicStateSnapshot asyncly)
461 it "asyncly limited maintains independent states in concurrent tasks"
462 (monadicStateSnapshot (asyncly . S.take 10000))
463 it "wAsyncly maintains independent states in concurrent tasks"
464 (monadicStateSnapshot wAsyncly)
465 it "wAsyncly limited maintains independent states in concurrent tasks"
466 (monadicStateSnapshot (wAsyncly . S.take 10000))
467 it "aheadly maintains independent states in concurrent tasks"
468 (monadicStateSnapshot aheadly)
469 it "aheadly limited maintains independent states in concurrent tasks"
470 (monadicStateSnapshot (aheadly . S.take 10000))
471 it "parallely maintains independent states in concurrent tasks"
472 (monadicStateSnapshot parallely)
474 it "async maintains independent states in concurrent tasks"
475 (monadicStateSnapshotOp async)
476 it "ahead maintains independent states in concurrent tasks"
477 (monadicStateSnapshotOp ahead)
478 it "wAsync maintains independent states in concurrent tasks"
479 (monadicStateSnapshotOp wAsync)
480 it "parallel maintains independent states in concurrent tasks"
481 (monadicStateSnapshotOp Streamly.parallel)
483 ---------------------------------------------------------------------------
484 -- Slower tests are at the end
485 ---------------------------------------------------------------------------
487 ---------------------------------------------------------------------------
489 ---------------------------------------------------------------------------
491 it "asyncly crosses thread limit (2000 threads)" $
492 S.drain (asyncly $ fold $
493 replicate 2000 $ S.yieldM $ threadDelay 1000000)
496 it "aheadly crosses thread limit (4000 threads)" $
497 S.drain (aheadly $ fold $
498 replicate 4000 $ S.yieldM $ threadDelay 1000000)
501-- Each snapshot carries an independent state. Multiple parallel tasks should
502-- not affect each other's state. This is especially important when we run
503-- multiple tasks in a single thread.
504snapshot :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
506 -- We deliberately use a replicate count 1 here, because a lower count
507 -- catches problems that a higher count doesn't.
509 -- Even though we modify the state here it should not reflect in other
510 -- parallel tasks, it is local to each concurrent task.
511 modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==1))
512 modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
514snapshot1 :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
515snapshot1 = S.replicateM 1000 $
516 modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
518snapshot2 :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
519snapshot2 = S.replicateM 1000 $
520 modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
528 , MonadState Int (t m)
532 -- Each task in a concurrent composition inherits the state and maintains
533 -- its own modifications to it, not affecting the parent computation.
534 snapshot <> (modify (+1) >> (snapshot1 <> snapshot2))
535 -- The above modify statement does not affect our state because that is
536 -- used in a parallel composition. In a serial composition it will affect
538 get >>= liftIO . (`shouldSatisfy` (== (0 :: Int)))
542 , Semigroup (t (StateT Int IO) ())
543 , MonadIO (t (StateT Int IO))
544 , MonadState Int (t (StateT Int IO))
546 => (t (StateT Int IO) () -> SerialT (StateT Int IO) ()) -> IO ()
547monadicStateSnapshot t = void $ runStateT (S.drain $ t stateComp) 0
550 :: ( AsyncT (StateT Int IO) ()
551 -> AsyncT (StateT Int IO) ()
552 -> AsyncT (StateT Int IO) ()
554 -> SerialT (StateT Int IO) ()
556 -- Each task in a concurrent composition inherits the state and maintains
557 -- its own modifications to it, not affecting the parent computation.
558 asyncly (snapshot `op` (modify (+1) >> (snapshot1 `op` snapshot2)))
559 -- The above modify statement does not affect our state because that is
560 -- used in a parallel composition. In a serial composition it will affect
562 get >>= liftIO . (`shouldSatisfy` (== (0 :: Int)))
564monadicStateSnapshotOp
565 :: ( AsyncT (StateT Int IO) ()
566 -> AsyncT (StateT Int IO) ()
567 -> AsyncT (StateT Int IO) ()
570monadicStateSnapshotOp op = void $ runStateT (S.drain $ stateCompOp op) 0
572takeCombined :: (Monad m, Semigroup (t m Int), Show a, Eq a, IsStream t)
573 => Int -> (t m Int -> SerialT IO a) -> IO ()
575 let constr = S.fromFoldable
576 r <- (S.toList . t) $
577 S.take n (constr ([] :: [Int]) <> constr ([] :: [Int]))
580checkMonadicStateTransfer
581 :: (IsStream t1, IsStream t2)
582 => ( t1 (StateT Int IO) ()
583 -> t2 (StateT Int IO) ()
584 -> SerialT (StateT Int IO) a3 )
586checkMonadicStateTransfer op = evalStateT str (0 :: Int)
591 (serially $ S.mapM snapshoti $ S.fromList [1..10]) `op`
592 (serially $ S.mapM snapshoti $ S.fromList [1..10])
596 lift1 $ x `shouldBe` y
597 lift1 m = StateT $ \s -> do
601checkFoldrLaziness :: IO ()
602checkFoldrLaziness = do
603 S.foldrM (\x xs -> if odd x then return True else xs)
604 (return False) (S.fromList (2:4:5:undefined :: [Int]))
607 S.toList (IP.foldrS (\x xs -> if odd x then return True else xs)
609 $ (S.fromList (2:4:5:undefined) :: SerialT IO Int))
610 `shouldReturn` [True]
612 S.toList (IP.foldrT (\x xs -> if odd x then return True else xs)
614 $ (S.fromList (2:4:5:undefined) :: SerialT IO Int))
615 `shouldReturn` [True]
618checkFoldxStrictness :: IO ()
619checkFoldxStrictness = do
620 let s = return (1 :: Int) `S.consM` error "failure"
621 catch (S.foldx (\_ a -> if a == 1 then error "success" else "done")
623 (\(ErrorCall err) -> return err)
624 `shouldReturn` "success"
627checkFoldl'Strictness :: IO ()
628checkFoldl'Strictness = do
629 let s = return (1 :: Int) `S.consM` error "failure"
630 catch (S.foldl' (\_ a -> if a == 1 then error "success" else "done")
632 (\(ErrorCall err) -> return err)
633 `shouldReturn` "success"
636checkScanxStrictness :: IO ()
637checkScanxStrictness = do
638 let s = return (1 :: Int) `S.consM` error "failure"
649 (\(ErrorCall err) -> return err)
650 `shouldReturn` "success"
653checkScanl'Strictness :: IO ()
654checkScanl'Strictness = do
655 let s = return (1 :: Int) `S.consM` error "failure"
667 (\(ErrorCall err) -> return err)
668 `shouldReturn` "success"
670foldlM'StrictCheck :: IORef Int -> SerialT IO Int -> IO ()
671foldlM'StrictCheck ref = S.foldlM' (\_ _ -> writeIORef ref 1) (return ())
674foldxMStrictCheck :: IORef Int -> SerialT IO Int -> IO ()
675foldxMStrictCheck ref = S.foldxM (\_ _ -> writeIORef ref 1) (return ()) return
678checkFoldMStrictness :: (IORef Int -> SerialT IO Int -> IO ()) -> IO ()
679checkFoldMStrictness f = do
681 let s = return 1 `S.consM` error "x"
682 catch (f ref s) (\(_ :: ErrorCall) -> return ())
683 readIORef ref `shouldReturn` 1
685scanlM'StrictCheck :: IORef Int -> SerialT IO Int -> SerialT IO ()
686scanlM'StrictCheck ref = S.scanlM' (\_ _ -> writeIORef ref 1) ()
688checkScanlMStrictness :: (IORef Int -> SerialT IO Int -> SerialT IO ()) -> IO ()
689checkScanlMStrictness f = do
691 let s = return 1 `S.consM` error "x"
692 catch (S.drain $ f ref s) (\(_ :: ErrorCall) -> return ())
693 readIORef ref `shouldReturn` 1
695takeInfinite :: IsStream t => (t IO Int -> SerialT IO Int) -> Spec
698 S.drain (t $ S.take 1 $ S.repeatM (print "hello" >> return (1::Int)))
701-- XXX need to test that we have promptly cleaned up everything after the error
702-- XXX We can also check the output that we are expected to get before the
705newtype ExampleException = ExampleException String deriving (Eq, Show)
707instance Exception ExampleException
709simpleMonadError :: Spec
712 it "simple runExceptT" $ do
713 (runExceptT $ S.drain $ return ())
714 `shouldReturn` (Right () :: Either String ())
715 it "simple runExceptT with error" $ do
716 (runExceptT $ S.drain $ throwError "E") `shouldReturn` Left "E"
719 try (S.drain $ return ())
720 `shouldReturn` (Right () :: Either ExampleException ())
721 it "simple try with throw error" $
722 try (S.drain $ throwM $ ExampleException "E")
723 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException ())
727 , Semigroup (t IO Int)
730 => (t IO Int -> SerialT IO Int) -> Spec
731composeWithMonadThrow t = do
732 it "Compose throwM, nil" $
733 try (tl (throwM (ExampleException "E") <> S.nil))
734 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
735 it "Compose nil, throwM" $
736 try (tl (S.nil <> throwM (ExampleException "E")))
737 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
738 oneLevelNestedSum "serially" serially
739 oneLevelNestedSum "wSerially" wSerially
740 oneLevelNestedSum "asyncly" asyncly
741 oneLevelNestedSum "wAsyncly" wAsyncly
742 -- XXX add two level nesting
744 oneLevelNestedProduct "serially" serially
745 oneLevelNestedProduct "wSerially" wSerially
746 oneLevelNestedProduct "asyncly" asyncly
747 oneLevelNestedProduct "wAsyncly" wAsyncly
751 oneLevelNestedSum desc t1 =
752 it ("One level nested sum " <> desc) $ do
753 let nested = S.fromFoldable [1..10] <> throwM (ExampleException "E")
754 <> S.fromFoldable [1..10]
755 try (tl (S.nil <> t1 nested <> S.fromFoldable [1..10]))
756 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
758 oneLevelNestedProduct desc t1 =
759 it ("One level nested product" <> desc) $ do
760 let s1 = t $ S.foldMapWith (<>) return [1..4]
761 s2 = t1 $ S.foldMapWith (<>) return [5..8]
766 then throwM (ExampleException "E")
769 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
771_composeWithMonadError
773 , Semigroup (t (ExceptT String IO) Int)
774 , MonadError String (t (ExceptT String IO))
776 => (t (ExceptT String IO) Int -> SerialT (ExceptT String IO) Int) -> Spec
777_composeWithMonadError t = do
778 let tl = S.toList . t
779 it "Compose throwError, nil" $
780 runExceptT (tl (throwError "E" <> S.nil)) `shouldReturn` Left "E"
781 it "Compose nil, error" $
782 runExceptT (tl (S.nil <> throwError "E")) `shouldReturn` Left "E"
784nestTwoSerial :: Expectation
786 let s1 = S.foldMapWith (<>) return [1..4]
787 s2 = S.foldMapWith (<>) return [5..8]
792 ) `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
794nestTwoAhead :: Expectation
796 let s1 = S.foldMapWith (<>) return [1..4]
797 s2 = S.foldMapWith (<>) return [5..8]
798 in (S.toList . aheadly) (do
802 ) `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
804nestTwoSerialApp :: Expectation
806 let s1 = S.foldMapWith (<>) return [1..4]
807 s2 = S.foldMapWith (<>) return [5..8]
808 in toListSerial ((+) <$> s1 <*> s2)
809 `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
811nestTwoAheadApp :: Expectation
813 let s1 = S.foldMapWith (<>) return [1..4]
814 s2 = S.foldMapWith (<>) return [5..8]
815 in (S.toList . aheadly) ((+) <$> s1 <*> s2)
816 `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
818nestTwoInterleaved :: Expectation
820 let s1 = S.foldMapWith (<>) return [1..4]
821 s2 = S.foldMapWith (<>) return [5..8]
822 in toListInterleaved (do
826 ) `shouldReturn` ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
828nestTwoInterleavedApp :: Expectation
829nestTwoInterleavedApp =
830 let s1 = S.foldMapWith (<>) return [1..4]
831 s2 = S.foldMapWith (<>) return [5..8]
832 in toListInterleaved ((+) <$> s1 <*> s2)
833 `shouldReturn` ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
835nestTwoAsync :: Expectation
837 let s1 = S.foldMapWith (<>) return [1..4]
838 s2 = S.foldMapWith (<>) return [5..8]
839 in sort <$> toListAsync (do
843 `shouldReturn` sort ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
845nestTwoAsyncApp :: Expectation
847 let s1 = S.foldMapWith (<>) return [1..4]
848 s2 = S.foldMapWith (<>) return [5..8]
849 in sort <$> toListAsync ((+) <$> s1 <*> s2)
850 `shouldReturn` sort ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
852nestTwoWAsync :: Expectation
854 let s1 = S.foldMapWith (<>) return [1..4]
855 s2 = S.foldMapWith (<>) return [5..8]
856 in sort <$> (S.toList . wAsyncly) (do
860 `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
862nestTwoParallel :: Expectation
864 let s1 = S.foldMapWith (<>) return [1..4]
865 s2 = S.foldMapWith (<>) return [5..8]
866 in sort <$> (S.toList . parallely) (do
870 `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
872nestTwoWAsyncApp :: Expectation
874 let s1 = S.foldMapWith (<>) return [1..4]
875 s2 = S.foldMapWith (<>) return [5..8]
876 in sort <$> (S.toList . wAsyncly) ((+) <$> s1 <*> s2)
877 `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
879nestTwoParallelApp :: Expectation
881 let s1 = S.foldMapWith (<>) return [1..4]
882 s2 = S.foldMapWith (<>) return [5..8]
883 in sort <$> (S.toList . parallely) ((+) <$> s1 <*> s2)
884 `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
886interleaveCheck :: IsStream t
887 => (t IO Int -> SerialT IO Int)
888 -> (t IO Int -> t IO Int -> t IO Int)
891 it "Interleave four" $
892 (S.toList . t) ((singleton 0 `f` singleton 1) `f` (singleton 100 `f` singleton 101))
893 `shouldReturn` [0, 100, 1, 101]
895parallelCheck :: (IsStream t, Monad (t IO))
896 => (t IO Int -> SerialT IO Int)
897 -> (t IO Int -> t IO Int -> t IO Int)
899parallelCheck t f = do
900 it "Parallel ordering left associated" $
901 (S.toList . t) (((event 4 `f` event 3) `f` event 2) `f` event 1)
902 `shouldReturn` [1..4]
904 it "Parallel ordering right associated" $
905 (S.toList . t) (event 4 `f` (event 3 `f` (event 2 `f` event 1)))
906 `shouldReturn` [1..4]
908 where event n = S.yieldM (threadDelay (n * 200000)) >> return n
910composeAndComposeSimple
911 :: ( IsStream t1, Semigroup (t1 IO Int)
912 , IsStream t2, Monoid (t2 IO Int), Monad (t2 IO)
913#if __GLASGOW_HASKELL__ < 804
914 , Semigroup (t2 IO Int)
917 => (t1 IO Int -> SerialT IO Int)
918 -> (t2 IO Int -> t2 IO Int)
920composeAndComposeSimple t1 t2 answer = do
921 let rfold = adapt . t2 . S.foldMapWith (<>) return
922 it "Compose right associated outer expr, right folded inner" $
923 (S.toList . t1) (rfold [1,2,3] <> (rfold [4,5,6] <> rfold [7,8,9]))
924 `shouldReturn` head answer
926 it "Compose left associated outer expr, right folded inner" $
927 (S.toList . t1) ((rfold [1,2,3] <> rfold [4,5,6]) <> rfold [7,8,9])
928 `shouldReturn` (answer !! 1)
930 let lfold xs = adapt $ t2 $ foldl (<>) mempty $ fmap return xs
931 it "Compose right associated outer expr, left folded inner" $
932 (S.toList . t1) (lfold [1,2,3] <> (lfold [4,5,6] <> lfold [7,8,9]))
933 `shouldReturn` (answer !! 2)
935 it "Compose left associated outer expr, left folded inner" $
936 (S.toList . t1) ((lfold [1,2,3] <> lfold [4,5,6]) <> lfold [7,8,9])
937 `shouldReturn` (answer !! 3)
940 :: (IsStream t, Semigroup (t IO Int), Monad (t IO))
941 => (t IO Int -> t IO Int)
945loops t tsrt hsrt = do
946 it "Tail recursive loop" $ (tsrt <$> (S.toList . adapt) (loopTail 0))
947 `shouldReturn` [0..3]
949 it "Head recursive loop" $ (hsrt <$> (S.toList . adapt) (loopHead 0))
950 `shouldReturn` [0..3]
954 -- this print line is important for the test (causes a bind)
955 S.yieldM $ putStrLn "LoopHead..."
956 t $ (if x < 3 then loopHead (x + 1) else nil) <> return x
959 -- this print line is important for the test (causes a bind)
960 S.yieldM $ putStrLn "LoopTail..."
961 t $ return x <> (if x < 3 then loopTail (x + 1) else nil)
964 :: ( IsStream t1, IsStream t2, Semigroup (t2 IO Int), Monad (t2 IO))
965 => (t1 IO Int -> SerialT IO Int)
966 -> (t2 IO Int -> t2 IO Int)
968bindAndComposeSimple t1 t2 = do
969 -- XXX need a bind in the body of forEachWith instead of a simple return
970 it "Compose many (right fold) with bind" $
971 (sort <$> (S.toList . t1)
972 (adapt . t2 $ S.forEachWith (<>) [1..10 :: Int] return))
973 `shouldReturn` [1..10]
975 it "Compose many (left fold) with bind" $
976 let forL xs k = foldl (<>) nil $ fmap k xs
977 in (sort <$> (S.toList . t1) (adapt . t2 $ forL [1..10 :: Int] return))
978 `shouldReturn` [1..10]
980bindAndComposeHierarchy
981 :: ( IsStream t1, Monad (t1 IO)
982 , IsStream t2, Monad (t2 IO))
983 => (t1 IO Int -> SerialT IO Int)
984 -> (t2 IO Int -> t2 IO Int)
985 -> ([t2 IO Int] -> t2 IO Int)
987bindAndComposeHierarchy t1 t2 g =
988 it "Bind and compose nested" $
989 (sort <$> (S.toList . t1) bindComposeNested)
990 `shouldReturn` (sort (
996 <> replicate 7 15) :: [Int])
1000 -- bindComposeNested :: WAsyncT IO Int
1002 let c1 = tripleCompose (return 1) (return 2) (return 3)
1003 c2 = tripleCompose (return 4) (return 5) (return 6)
1004 c3 = tripleCompose (return 7) (return 8) (return 9)
1005 b = tripleBind c1 c2 c3
1006-- it seems to be causing a huge space leak in hspec so disabling this for now
1007-- c = tripleCompose b b b
1008-- m = tripleBind c c c
1012 tripleCompose a b c = adapt . t2 $ g [a, b, c]
1013 tripleBind mx my mz =
1016 >>= \z -> return (x + y + z)
1020 it "Compose many ops" $
1021 (sort <$> toListSerial composeMixed)
1022 `shouldReturn` ([8,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,11,11
1023 ,11,11,11,11,11,11,11,11,12,12,12,12,12,13
1027 composeMixed :: SerialT IO Int
1029 S.yieldM $ return ()
1030 S.yieldM $ putStr ""
1034 x1 <- wAsyncly $ return 1 <> return 2
1035 S.yieldM $ return ()
1036 S.yieldM $ putStr ""
1037 y1 <- asyncly $ return 1 <> return 2
1039 x11 <- return 1 <> return 2
1040 y11 <- asyncly $ return 1 <> return 2
1041 z11 <- wSerially $ return 1 <> return 2
1042 S.yieldM $ return ()
1043 S.yieldM $ putStr ""
1044 return (x11 + y11 + z11)
1045 return (x1 + y1 + z1)
1048mixedOpsAheadly :: Spec
1050 it "Compose many ops" $
1051 (sort <$> toListSerial composeMixed)
1052 `shouldReturn` ([8,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,11,11
1053 ,11,11,11,11,11,11,11,11,12,12,12,12,12,13
1057 composeMixed :: SerialT IO Int
1059 S.yieldM $ return ()
1060 S.yieldM $ putStr ""
1064 x1 <- wAsyncly $ return 1 <> return 2
1065 S.yieldM $ return ()
1066 S.yieldM $ putStr ""
1067 y1 <- aheadly $ return 1 <> return 2
1069 x11 <- return 1 <> return 2
1070 y11 <- aheadly $ return 1 <> return 2
1071 z11 <- parallely $ return 1 <> return 2
1072 S.yieldM $ return ()
1073 S.yieldM $ putStr ""
1074 return (x11 + y11 + z11)
1075 return (x1 + y1 + z1)
1078testFromCallback :: IO Int
1079testFromCallback = do
1080 ref <- newIORef Nothing
1081 let stream = S.map Just (IP.fromCallback (setCallback ref))
1082 `Streamly.parallel` runCallback ref
1083 S.sum $ S.map fromJust $ S.takeWhile isJust stream
1087 setCallback ref cb = do
1088 writeIORef ref (Just cb)
1090 runCallback ref = S.yieldM $ do
1092 S.repeatM (readIORef ref)
1098 & IP.delayPost 0.001
1099 & S.mapM_ (fromJust cb)
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