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
27import qualified Streamly.Data.Stream as Stream
28import qualified Streamly.Internal.Data.Stream.Concurrent as Async
30import Streamly.Test.Common (listEquals)
33moduleName = "Data.Stream.Concurrent"
35-------------------------------------------------------------------------------
37-------------------------------------------------------------------------------
39sortEq :: Ord a => [a] -> [a] -> Bool
40sortEq a b = sort a == sort b
42cmp :: Stream IO Int -> ([Int] -> [Int] -> Bool) -> [Int] -> Property
45 stream <- run $ Stream.fold Fold.toList s
46 listEquals eq stream list
48prop1 :: Testable prop => String -> prop -> SpecWith ()
49prop1 x y = modifyMaxSuccess (const 1) $ prop x y
51-- Coverage build takes too long with default number of tests
59transformCombineFromList ::
60 ([Int] -> Stream IO Int)
61 -> ([Int] -> [Int] -> Bool)
63 -> (Stream IO Int -> Stream IO Int)
68transformCombineFromList constr eq listOp op a b c =
69 withMaxSuccess maxTestCount $
71 let s1 = op (Async.parList id [constr b, constr c])
72 let s2 = Async.parList id [constr a, s1]
73 stream <- run (Stream.fold Fold.toList s2)
74 let list = a <> listOp (b <> c)
75 listEquals eq stream list
77commonOpConfigs :: [(String, Async.Config -> Async.Config)]
81 , ("rate AvgRate 10000", Async.avgRate 10000)
82 , ("rate Nothing", Async.rate Nothing)
83 , ("maxBuffer 0", Async.maxBuffer 0)
84 , ("maxThreads 0", Async.maxThreads 0)
85 , ("maxThreads 1", Async.maxThreads 1)
86 , ("eager", Async.eager True)
87 -- XXX Need to use an unsorted eq operation for ahead
88 , ("ordered", Async.ordered True)
89#ifdef USE_LARGE_MEMORY
90 , ("maxThreads -1", Async.maxThreads (-1))
95opConfigs :: [(String, Async.Config -> Async.Config)]
96opConfigs = commonOpConfigs
99 ("maxBuffer 1", Async.maxBuffer 1)
103makeSpec :: [(String, a)] -> (a -> Spec) -> Spec
104makeSpec cfg spec = mapM_ (\(desc, arg) -> describe desc $ spec arg) cfg
106asyncSpec :: ((Async.Config -> Async.Config) -> Spec) -> Spec
109#ifndef COVERAGE_BUILD
110 <> [("maxBuffer (-1)", Async.maxBuffer (-1))]
113-------------------------------------------------------------------------------
114-- Compose with MonadThrow
115-------------------------------------------------------------------------------
117newtype ExampleException = ExampleException String deriving (Eq, Show, Ord)
119instance Exception ExampleException
121exceptionPropagation ::
122 (Stream IO Int -> Stream IO Int -> Stream IO Int) -> Spec
123exceptionPropagation f = do
124 it "append throwM, nil" $
125 try (tl (Stream.fromEffect (throwM (ExampleException "E")) `f` Stream.nil))
127 (Left (ExampleException "E") :: Either ExampleException [Int])
128 it "append nil, throwM" $
129 try (tl (Stream.nil `f` Stream.fromEffect (throwM (ExampleException "E"))))
131 (Left (ExampleException "E") :: Either ExampleException [Int])
132 it "append nested throwM" $ do
134 (Stream.fromList [1..10])
135 `f` Stream.fromEffect (throwM (ExampleException "E"))
136 `f` (Stream.fromList [1..10])
137 try (tl (Stream.nil `f` nested `f` (Stream.fromList [1..10])))
139 (Left (ExampleException "E")
140 :: Either ExampleException [Int])
141 it "sequence throwM" $
142 let stream = Stream.fromList [throwM (ExampleException "E")]
143 in try (tl (Stream.nil `f` Async.parSequence id stream))
145 (Left (ExampleException "E") :: Either ExampleException [Int])
147 it "concatMap throwM" $ do
148 let s1 = Async.parList id $ fmap Stream.fromPure [1..4]
149 s2 = Async.parList id $ fmap Stream.fromPure [5..8]
151 let bind = flip (Async.parConcatMap id)
155 then Stream.fromEffect (throwM (ExampleException "E"))
156 else Stream.fromPure (x + y)
159 (Left (ExampleException "E") :: Either ExampleException [Int])
163 tl = Stream.fold Fold.toList
165---------------------------------------------------------------------------
167---------------------------------------------------------------------------
170timeOrdering :: ([Stream IO Int] -> Stream IO Int) -> Spec
172 it "Parallel event ordering check" $
173 Stream.fold Fold.toList (f [event 4, event 3, event 2, event 1])
174 `shouldReturn` [1..4]
176 where event n = Stream.fromEffect (threadDelay (n * 200000) >> return n)
179-------------------------------------------------------------------------------
180-- Some ad-hoc tests that failed at times
181-------------------------------------------------------------------------------
183takeCombined :: Int -> IO ()
185 let constr = Stream.fromList
186 let s = Async.parList id [constr ([] :: [Int]), constr ([] :: [Int])]
187 r <- Stream.fold Fold.toList $ Stream.take n s
190---------------------------------------------------------------------------
192---------------------------------------------------------------------------
195 :: (Int -> Stream IO Int)
199constructWithLenM mkStream mkList len =
200 withMaxSuccess maxTestCount
204 $ Stream.fold Fold.toList
205 $ mkStream (fromIntegral len)
206 list <- run $ mkList (fromIntegral len)
207 listEquals (==) stream list
210 :: (Async.Config -> Async.Config)
213sequenceReplicate cfg = constructWithLenM stream list
217 list = flip replicateM (return 1 :: IO Int)
218 stream = Async.parSequence cfg . flip Stream.replicate (return 1 :: IO Int)
224 $ modifyMaxSuccess (const 10)
226 $ describe moduleName $ do
227 let transform = transformCombineFromList Stream.fromList sortEq
232 (fmap (+1) . Async.parEval id . fmap (+1))
234 asyncSpec $ prop "parSequence" . sequenceReplicate
235 -- XXX Need to use asyncSpec in all tests
237 transform (fmap (+1)) (Async.parMapM id (\x -> return (x + 1)))
239 -- XXX Need to use eq instead of sortEq for ahead oeprations
242 $ cmp (Async.parList id [Stream.nil, Stream.nil]) sortEq []
243 prop1 "append [] [1]"
244 $ cmp (Async.parList id [Stream.nil, Stream.fromPure 1]) sortEq [1]
245 prop1 "append [1] []"
246 $ cmp (Async.parList id [Stream.fromPure 1, Stream.nil]) sortEq [1]
247 prop1 "append [0] [1]"
248 $ let stream = Async.parList id [Stream.fromPure 0, Stream.fromPure 1]
249 in cmp stream sortEq [0, 1]
251 prop1 "append [0] [] [1]"
254 [Stream.fromPure 0, Stream.nil, Stream.fromPure 1]
255 in cmp stream sortEq [0, 1]
257 let async = Async.parTwo id
258 prop1 "append2 left associated"
261 `async` Stream.fromPure 1
262 `async` Stream.fromPure 2
263 `async` Stream.fromPure 3
264 in cmp stream sortEq [0, 1, 2, 3]
266 prop1 "append right associated"
269 `async` (Stream.fromPure 1
270 `async` (Stream.fromPure 2
271 `async` Stream.fromPure 3))
272 in cmp stream sortEq [0, 1, 2, 3]
274 prop1 "append balanced"
275 $ let leaf x y = Stream.fromPure x `async` Stream.fromPure y
276 leaf11 = leaf 0 1 `async` leaf 2 (3 :: Int)
277 leaf12 = leaf 4 5 `async` leaf 6 7
278 stream = leaf11 `async` leaf12
279 in cmp stream sortEq [0, 1, 2, 3, 4, 5, 6,7]
281 prop1 "combineWith (maxThreads 1)"
283 Async.parTwo (Async.maxThreads 1)
284 (Stream.fromList [1,2,3,4,5])
285 (Stream.fromList [6,7,8,9,10])
286 in cmp stream (==) [1,2,3,4,5,6,7,8,9,10]
288 prop1 "apply (async arg1)"
289 $ let s1 = Async.parApply id (Stream.fromPure (,)) (Stream.fromPure 1 `async` Stream.fromPure 2)
290 s2 = Async.parApply id s1 (Stream.fromPure 3) :: Stream IO (Int, Int)
291 xs = Stream.fold Fold.toList s2
292 in sort <$> xs `shouldReturn` [(1, 3), (2, 3)]
294 prop1 "apply (async arg2)"
295 $ let s1 = Stream.fromPure (1,)
296 s2 = Async.parApply id s1 (Stream.fromPure 2 `async` Stream.fromPure 3)
297 xs = Stream.fold Fold.toList s2 :: IO [(Int, Int)]
298 in sort <$> xs `shouldReturn` [(1, 2), (1, 3)]
304 $ fmap Stream.fromPure
305 $ Stream.fromList [1..100]
306 in cmp stream sortEq [1..100]
309 forAll (choose (0, 100)) $ \n ->
311 (concatMap (const [1..n]))
312 (Async.parConcatMap id (const (Stream.fromList [1..n])))
315 describe "Time ordering" $ timeOrdering Async.parList id
317 describe "Exception propagation" $ exceptionPropagation async
319 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