2-- Module : Streamly.Test.Data.Stream.Concurrent
3-- Copyright : (c) 2020 Composewell Technologies
5-- License : BSD-3-Clause
6-- Maintainer : streamly@composewell.com
7-- Stability : experimental
10module Streamly.Test.Data.Stream.Concurrent (main) where
13import Control.Concurrent (threadDelay)
15import Control.Exception (Exception, try)
16import Control.Monad (replicateM)
17import Control.Monad.Catch (throwM)
18import Data.List (sort)
19import Data.Word (Word8)
20import Streamly.Data.Stream (Stream)
21import Test.Hspec.QuickCheck
22import Test.QuickCheck (Testable, Property, choose, forAll, withMaxSuccess)
23import Test.QuickCheck.Monadic (monadicIO, run)
26import qualified Streamly.Data.Fold as Fold ( toList )
27import qualified Streamly.Data.Stream as Stream
28 ( replicate, fromEffect, fromPure, fromList, fold, take, nil )
29import qualified Streamly.Internal.Data.Stream.Concurrent as Async
31import Streamly.Test.Common (listEquals)
34moduleName = "Data.Stream.Concurrent"
36-------------------------------------------------------------------------------
38-------------------------------------------------------------------------------
40sortEq :: Ord a => [a] -> [a] -> Bool
41sortEq a b = sort a == sort b
43cmp :: (Show a, Ord a) => ([a] -> [a] -> Bool) -> [a] -> Stream IO a -> Property
46 stream <- run $ sort <$> Stream.fold Fold.toList s
47 listEquals eq stream list
49prop1 :: Testable prop => String -> prop -> SpecWith ()
50prop1 x y = modifyMaxSuccess (const 1) $ prop x y
52-- Coverage build takes too long with default number of tests
60transformCombineFromList ::
61 ([Int] -> Stream IO Int)
62 -> ([Int] -> [Int] -> Bool)
64 -> (Stream IO Int -> Stream IO Int)
69transformCombineFromList constr eq listOp op a b c =
70 withMaxSuccess maxTestCount $
72 let s1 = op (Async.parList id [constr b, constr c])
73 let s2 = Async.parList id [constr a, s1]
74 stream <- run (Stream.fold Fold.toList s2)
75 let list = a <> listOp (b <> c)
76 listEquals eq stream list
78commonOpConfigs :: [(String, Async.Config -> Async.Config)]
82 , ("rate AvgRate 10000", Async.avgRate 10000)
83 , ("rate Nothing", Async.rate Nothing)
84 , ("maxBuffer 0", Async.maxBuffer 0)
85 , ("maxThreads 0", Async.maxThreads 0)
86 , ("maxThreads 1", Async.maxThreads 1)
87 , ("eager", Async.eager True)
88 -- XXX Need to use an unsorted eq operation for ahead
89 , ("ordered", Async.ordered True)
90#ifdef USE_LARGE_MEMORY
91 , ("maxThreads -1", Async.maxThreads (-1))
96opConfigs :: [(String, Async.Config -> Async.Config)]
97opConfigs = commonOpConfigs
100 ("maxBuffer 1", Async.maxBuffer 1)
104makeSpec :: [(String, a)] -> (a -> Spec) -> Spec
105makeSpec cfg spec = mapM_ (\(desc, arg) -> describe desc $ spec arg) cfg
107asyncSpec :: ((Async.Config -> Async.Config) -> Spec) -> Spec
110#ifndef COVERAGE_BUILD
111 <> [("maxBuffer (-1)", Async.maxBuffer (-1))]
114-------------------------------------------------------------------------------
115-- Compose with MonadThrow
116-------------------------------------------------------------------------------
118newtype ExampleException = ExampleException String deriving (Eq, Show, Ord)
120instance Exception ExampleException
122exceptionPropagation ::
123 (Stream IO Int -> Stream IO Int -> Stream IO Int) -> Spec
124exceptionPropagation f = do
125 it "append throwM, nil" $
126 try (tl (Stream.fromEffect (throwM (ExampleException "E")) `f` Stream.nil))
128 (Left (ExampleException "E") :: Either ExampleException [Int])
129 it "append nil, throwM" $
130 try (tl (Stream.nil `f` Stream.fromEffect (throwM (ExampleException "E"))))
132 (Left (ExampleException "E") :: Either ExampleException [Int])
133 it "append nested throwM" $ do
135 (Stream.fromList [1..10])
136 `f` Stream.fromEffect (throwM (ExampleException "E"))
137 `f` (Stream.fromList [1..10])
138 try (tl (Stream.nil `f` nested `f` (Stream.fromList [1..10])))
140 (Left (ExampleException "E")
141 :: Either ExampleException [Int])
142 it "sequence throwM" $
143 let stream = Stream.fromList [throwM (ExampleException "E")]
144 in try (tl (Stream.nil `f` Async.parSequence id stream))
146 (Left (ExampleException "E") :: Either ExampleException [Int])
148 it "concatMap throwM" $ do
149 let s1 = Async.parList id $ fmap Stream.fromPure [1..4]
150 s2 = Async.parList id $ fmap Stream.fromPure [5..8]
152 let bind = flip (Async.parConcatMap id)
156 then Stream.fromEffect (throwM (ExampleException "E"))
157 else Stream.fromPure (x + y)
160 (Left (ExampleException "E") :: Either ExampleException [Int])
164 tl = Stream.fold Fold.toList
166---------------------------------------------------------------------------
168---------------------------------------------------------------------------
171timeOrdering :: ([Stream IO Int] -> Stream IO Int) -> Spec
173 it "Parallel event ordering check" $
174 Stream.fold Fold.toList (f [event 4, event 3, event 2, event 1])
175 `shouldReturn` [1..4]
177 where event n = Stream.fromEffect (threadDelay (n * 200000) >> return n)
180-------------------------------------------------------------------------------
181-- Some ad-hoc tests that failed at times
182-------------------------------------------------------------------------------
184takeCombined :: Int -> IO ()
186 let constr = Stream.fromList
187 let s = Async.parList id [constr ([] :: [Int]), constr ([] :: [Int])]
188 r <- Stream.fold Fold.toList $ Stream.take n s
191---------------------------------------------------------------------------
193---------------------------------------------------------------------------
196 :: (Int -> Stream IO Int)
200constructWithLenM mkStream mkList len =
201 withMaxSuccess maxTestCount
205 $ Stream.fold Fold.toList
206 $ mkStream (fromIntegral len)
207 list <- run $ mkList (fromIntegral len)
208 listEquals (==) stream list
211 :: (Async.Config -> Async.Config)
214sequenceReplicate cfg = constructWithLenM stream list
218 list = flip replicateM (return 1 :: IO Int)
219 stream = Async.parSequence cfg . flip Stream.replicate (return 1 :: IO Int)
225 $ modifyMaxSuccess (const 10)
227 $ describe moduleName $ do
228 let transform = transformCombineFromList Stream.fromList sortEq
233 (fmap (+1) . Async.parEval id . fmap (+1))
235 asyncSpec $ prop "parSequence" . sequenceReplicate
239 . transform (fmap (+1))
240 . (`Async.parMapM` (\x -> return (x + 1)))
242 -- XXX Need to use eq instead of sortEq for ahead oeprations
245 let appWith cfg = Async.parList cfg [Stream.nil, Stream.nil]
246 in prop1 "parList [] []" . cmp sortEq ([] :: [Int]) . appWith
249 let appWith cfg = Async.parList cfg [Stream.nil, Stream.fromPure 1]
250 in prop1 "parList [] [1]" . cmp sortEq [1 :: Int] . appWith
253 let appWith cfg = Async.parList cfg [Stream.fromPure 1, Stream.nil]
254 in prop1 "parList [1] []" . cmp sortEq [1 :: Int] . appWith
258 Async.parList cfg [Stream.fromPure 0, Stream.fromPure 1]
259 in prop1 "parList [0] [1]" . cmp sortEq [0, 1 :: Int] . appWith
264 cfg [Stream.fromPure 0, Stream.nil, Stream.fromPure 1]
265 in prop1 "parList [0] [] [1]" . cmp sortEq [0, 1 :: Int] . appWith
272 (Stream.fromPure 0) (Stream.fromPure 1))
275 in prop1 "parTwo left associated"
276 . cmp sortEq [0, 1, 2, 3 :: Int] . appWith
285 (Stream.fromPure 2) (Stream.fromPure 3))
287 in prop1 "parTwo right associated"
288 . cmp sortEq [0, 1, 2, 3 :: Int] . appWith
292 Async.parTwo cfg (Stream.fromPure x)
295 Async.parTwo cfg (leaf 0 1 cfg) $ leaf 2 (3 :: Int) cfg
297 Async.parTwo cfg (leaf 4 5 cfg) $ leaf 6 7 cfg
299 Async.parTwo cfg (leaf11 cfg) (leaf12 cfg)
300 in prop1 "parTwo balanced"
301 . cmp sortEq [0, 1, 2, 3, 4, 5, 6,7] . appWith
306 (Stream.fromList [1,2,3,4,5 :: Int])
307 (Stream.fromList [6,7,8,9,10])
308 in prop1 "parTwo" . cmp (==) [1,2,3,4,5,6,7,8,9,10] . appWith
319 (Stream.fromPure (,))
325 (Stream.fromPure 3) :: Stream IO (Int, Int)
327 "parApply (async arg1)" . cmp (==) ( [(1, 3), (2, 3)]) . s2
333 (Stream.fromPure (2 :: Int))
335 s1 = Stream.fromPure (1 :: Int,)
336 s2 cfg = Async.parApply cfg s1 (par2 cfg)
337 in prop1 "apply (async arg2)" . cmp (==) ([(1, 2), (1, 3)]) . s2
343 $ fmap Stream.fromPure
344 $ Stream.fromList [1..100 :: Int]
345 in prop1 "parConcat" . cmp sortEq [1..100] . stream
349 forAll (choose (0, 100)) $ \n ->
351 (concatMap (const [1..n]))
353 cfg (const (Stream.fromList [1..n]))
355 in prop "parConcatMap" . f
358 describe "Time ordering" $ timeOrdering (Async.parList id)
360 let async = Async.parTwo id
361 describe "Exception propagation" $ exceptionPropagation async
363 it "takes n from stream of streams" $ takeCombined 2
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