on master help sign in
Refactor tests in test/Main.hs into separate test suites.
commit79b19d2f8c0a5612d59f91213076f68c44bf5af0 authorpranaysashank <pranaysashank@composewell.com> dateTue 11 Aug 2020, 19:00 (6 years ago) parent25f76d5
15 files changed+2036−2014
streamly.cabal+29−25 test/Main.hs−1105 test/Streamly/Test/Internal/Prelude.hs+33−4 test/Streamly/Test/Prelude.hs+122−859 test/Streamly/Test/Prelude/Ahead.hs+24−5 test/Streamly/Test/Prelude/Async.hs+22−1 test/Streamly/Test/Prelude/Common.hs+1259 test/Streamly/Test/Prelude/Concurrent.hs+184−6 test/Streamly/Test/Prelude/Fold.hs+183 test/Streamly/Test/Prelude/Parallel.hs+28−2 test/Streamly/Test/Prelude/Serial.hs+18−1 test/Streamly/Test/Prelude/WAsync.hs+81−2 test/Streamly/Test/Prelude/WSerial.hs+51−2 test/Streamly/Test/Prelude/ZipAsync.hs+1−1 test/Streamly/Test/Prelude/ZipSerial.hs+1−1
streamly.cabal+29−25
@@ -332,6 +332,7 @@ common prelude-test-options
332332 , base >= 4.9 && < 5
333333 , QuickCheck >= 2.14 && < 2.15
334334 , hspec >= 2.0 && < 3
335+ , exceptions >= 0.8 && < 0.11
335336 default-language: Haskell2010
336337
337338-- Used by maxrate test, benchmarks and executables
@@ -545,22 +546,6 @@ library
545546-- Test suites
546547-------------------------------------------------------------------------------
547548
548−test-suite test
549− import: test-options
550− type: exitcode-stdio-1.0
551− main-is: Main.hs
552− js-sources: jsbits/clock.js
553− hs-source-dirs: test
554− build-depends:
555− streamly
556− , base >= 4.9 && < 5
557− , hspec >= 2.0 && < 3
558− , containers >= 0.5 && < 0.7
559− , transformers >= 0.4 && < 0.6
560− , mtl >= 2.2 && < 3
561− , exceptions >= 0.8 && < 0.11
562− default-language: Haskell2010
563−
564549test-suite internal-prelude-test
565550 import: test-options
566551 type: exitcode-stdio-1.0
@@ -597,13 +582,23 @@ test-suite pure-streams-streamly
597582 , hspec >= 2.0 && < 3
598583 default-language: Haskell2010
599584
585+test-suite Prelude
586+ import: test-options, prelude-test-options
587+ type: exitcode-stdio-1.0
588+ main-is: Streamly/Test/Prelude.hs
589+ ghc-options: -main-is Streamly.Test.Prelude.main
590+ build-depends:
591+ , containers >= 0.5 && < 0.7
592+ , transformers >= 0.4 && < 0.6
593+ , mtl >= 2.2 && < 3
594+
600595test-suite Prelude.Serial
601596 import: test-options, prelude-test-options
602597 type: exitcode-stdio-1.0
603598 main-is: Streamly/Test/Prelude/Serial.hs
604599 ghc-options: -main-is Streamly.Test.Prelude.Serial.main
605600 other-modules: Streamly.Test.Common
606− , Streamly.Test.Prelude
601+ , Streamly.Test.Prelude.Common
607602
608603test-suite Prelude.WSerial
609604 import: test-options, prelude-test-options
@@ -611,7 +606,7 @@ test-suite Prelude.WSerial
611606 main-is: Streamly/Test/Prelude/WSerial.hs
612607 ghc-options: -main-is Streamly.Test.Prelude.WSerial.main
613608 other-modules: Streamly.Test.Common
614− , Streamly.Test.Prelude
609+ , Streamly.Test.Prelude.Common
615610
616611test-suite Prelude.ZipSerial
617612 import: test-options, prelude-test-options
@@ -619,7 +614,7 @@ test-suite Prelude.ZipSerial
619614 main-is: Streamly/Test/Prelude/ZipSerial.hs
620615 ghc-options: -main-is Streamly.Test.Prelude.ZipSerial.main
621616 other-modules: Streamly.Test.Common
622− , Streamly.Test.Prelude
617+ , Streamly.Test.Prelude.Common
623618
624619test-suite Prelude.ZipAsync
625620 import: test-options, prelude-test-options
@@ -627,7 +622,7 @@ test-suite Prelude.ZipAsync
627622 main-is: Streamly/Test/Prelude/ZipAsync.hs
628623 ghc-options: -main-is Streamly.Test.Prelude.ZipAsync.main
629624 other-modules: Streamly.Test.Common
630− , Streamly.Test.Prelude
625+ , Streamly.Test.Prelude.Common
631626
632627test-suite Prelude.Ahead
633628 import: test-options, prelude-test-options
@@ -635,7 +630,7 @@ test-suite Prelude.Ahead
635630 main-is: Streamly/Test/Prelude/Ahead.hs
636631 ghc-options: -main-is Streamly.Test.Prelude.Ahead.main
637632 other-modules: Streamly.Test.Common
638− , Streamly.Test.Prelude
633+ , Streamly.Test.Prelude.Common
639634
640635test-suite Prelude.Async
641636 import: test-options, prelude-test-options
@@ -643,7 +638,7 @@ test-suite Prelude.Async
643638 main-is: Streamly/Test/Prelude/Async.hs
644639 ghc-options: -main-is Streamly.Test.Prelude.Async.main
645640 other-modules: Streamly.Test.Common
646− , Streamly.Test.Prelude
641+ , Streamly.Test.Prelude.Common
647642
648643test-suite Prelude.WAsync
649644 import: test-options, prelude-test-options
@@ -651,7 +646,7 @@ test-suite Prelude.WAsync
651646 main-is: Streamly/Test/Prelude/WAsync.hs
652647 ghc-options: -main-is Streamly.Test.Prelude.WAsync.main
653648 other-modules: Streamly.Test.Common
654− , Streamly.Test.Prelude
649+ , Streamly.Test.Prelude.Common
655650
656651test-suite Prelude.Parallel
657652 import: test-options, prelude-test-options
@@ -659,7 +654,7 @@ test-suite Prelude.Parallel
659654 main-is: Streamly/Test/Prelude/Parallel.hs
660655 ghc-options: -main-is Streamly.Test.Prelude.Parallel.main
661656 other-modules: Streamly.Test.Common
662− , Streamly.Test.Prelude
657+ , Streamly.Test.Prelude.Common
663658
664659test-suite Prelude.Concurrent
665660 import: test-options, prelude-test-options
@@ -667,7 +662,16 @@ test-suite Prelude.Concurrent
667662 main-is: Streamly/Test/Prelude/Concurrent.hs
668663 ghc-options: -main-is Streamly.Test.Prelude.Concurrent.main
669664 other-modules: Streamly.Test.Common
670− , Streamly.Test.Prelude
665+ , Streamly.Test.Prelude.Common
666+ build-depends: mtl >= 2.2 && < 3
667+
668+test-suite Prelude.Fold
669+ import: test-options, prelude-test-options
670+ type: exitcode-stdio-1.0
671+ main-is: Streamly/Test/Prelude/Fold.hs
672+ ghc-options: -main-is Streamly.Test.Prelude.Fold.main
673+ other-modules: Streamly.Test.Common
674+ , Streamly.Test.Prelude.Common
671675
672676test-suite array-test
673677 import: test-options
test/Main.hsdeleted−1105
@@ -1,1105 +0,0 @@
1−module Main (main) where
2−
3−import Control.Concurrent (threadDelay)
4−import Control.Exception (Exception, try, ErrorCall(..), catch)
5−import Control.Monad (void)
6−import Control.Monad.Catch (throwM, MonadThrow)
7−import Control.Monad.Error.Class (throwError, MonadError)
8−import Control.Monad.IO.Class (MonadIO(liftIO))
9−import Control.Monad.State (MonadState, get, modify, runStateT
10− , StateT(..), evalStateT)
11−import Control.Monad.Trans.Except (runExceptT, ExceptT)
12−import Data.Foldable (forM_, fold)
13−import Data.Function ((&))
14−import Data.List (sort)
15−import Data.Maybe (fromJust, isJust)
16−#if !(MIN_VERSION_base(4,11,0))
17−import Data.Semigroup (Semigroup(..))
18−#endif
19−import System.Mem (performMajorGC)
20−
21−import Data.IORef
22−import Test.Hspec as H
23−
24−import Streamly.Prelude
25− ( AsyncT, SerialT, WSerialT, MonadAsync, IsStream, (.:), nil, adapt
26− , ahead, async, aheadly, asyncly, serially, parallely, wAsync, wAsyncly
27− , wSerially, maxBuffer)
28−import qualified Streamly.Prelude as S
29−import qualified Streamly.Internal.Data.Stream.IsStream as IP
30−
31−singleton :: IsStream t => a -> t m a
32−singleton a = a .: nil
33−
34−toListSerial :: SerialT IO a -> IO [a]
35−toListSerial = S.toList . serially
36−
37−toListInterleaved :: WSerialT IO a -> IO [a]
38−toListInterleaved = S.toList . wSerially
39−
40−toListAsync :: AsyncT IO a -> IO [a]
41−toListAsync = S.toList . asyncly
42−
43−main :: IO ()
44−main = hspec $ do
45− parallelTests
46−
47− describe "restricts concurrency and cleans up extra tasks" $ do
48− it "take 1 asyncly" $ checkCleanup 2 asyncly (S.take 1)
49− it "take 1 wAsyncly" $ checkCleanup 2 wAsyncly (S.take 1)
50− it "take 1 aheadly" $ checkCleanup 2 aheadly (S.take 1)
51−
52− it "takeWhile (< 0) asyncly" $ checkCleanup 2 asyncly (S.takeWhile (< 0))
53− it "takeWhile (< 0) wAsyncly" $ checkCleanup 2 wAsyncly (S.takeWhile (< 0))
54− it "takeWhile (< 0) aheadly" $ checkCleanup 2 aheadly (S.takeWhile (< 0))
55−
56−#ifdef DEVBUILD
57− let timed :: (IsStream t, Monad (t IO)) => Int -> t IO Int
58− timed x = S.yieldM (threadDelay (x * 100000)) >> return x
59−
60− -- These are not run parallely because the timing gets affected
61− -- unpredictably when other tests are running on the same machine.
62− --
63− -- Also, they fail intermittently due to scheduling delays, so not run on
64− -- CI machines.
65− describe "Nested parallel and serial compositions" $ do
66− let t = timed
67− p = wAsyncly
68− s = serially
69− {-
70− -- This is not correct, the result can also be [4,4,8,0,8,0,2,2]
71− -- because of parallelism of [8,0] and [8,0].
72− it "Nest <|>, <>, <|> (1)" $
73− let t = timed
74− in toListSerial (
75− ((t 8 <|> t 4) <> (t 2 <|> t 0))
76− <|> ((t 8 <|> t 4) <> (t 2 <|> t 0)))
77− `shouldReturn` ([4,4,8,8,0,0,2,2])
78− -}
79− it "Nest <|>, <>, <|> (2)" $
80− (S.toList . wAsyncly) (
81− s (p (t 4 <> t 8) <> p (t 1 <> t 2))
82− <> s (p (t 4 <> t 8) <> p (t 1 <> t 2)))
83− `shouldReturn` ([4,4,8,8,1,1,2,2])
84− -- FIXME: These two keep failing intermittently on Mac OS X
85− -- Need to examine and fix the tests.
86− {-
87− it "Nest <|>, <=>, <|> (1)" $
88− let t = timed
89− in toListSerial (
90− ((t 8 <|> t 4) <=> (t 2 <|> t 0))
91− <|> ((t 9 <|> t 4) <=> (t 2 <|> t 0)))
92− `shouldReturn` ([4,4,0,0,8,2,9,2])
93− it "Nest <|>, <=>, <|> (2)" $
94− let t = timed
95− in toListSerial (
96− ((t 4 <|> t 8) <=> (t 1 <|> t 2))
97− <|> ((t 4 <|> t 9) <=> (t 1 <|> t 2)))
98− `shouldReturn` ([4,4,1,1,8,2,9,2])
99− -}
100− it "Nest <|>, <|>, <|>" $
101− (S.toList . wAsyncly) (
102− ((t 4 <> t 8) <> (t 0 <> t 2))
103− <> ((t 4 <> t 8) <> (t 0 <> t 2)))
104− `shouldReturn` ([0,0,2,2,4,4,8,8])
105−
106− -- parallely fails on CI machines, may need more difference in times of
107− -- the events, but that would make tests even slower.
108− it "take 1 parallely" $ checkCleanup 3 parallely (S.take 1)
109− it "takeWhile (< 0) parallely" $ checkCleanup 3 parallely (S.takeWhile (< 0))
110−
111− testFoldOpsCleanup "head" S.head
112− testFoldOpsCleanup "null" S.null
113− testFoldOpsCleanup "elem" (S.elem 0)
114− testFoldOpsCleanup "notElem" (S.notElem 0)
115− testFoldOpsCleanup "elemIndex" (S.elemIndex 0)
116− -- S.lookup
117− testFoldOpsCleanup "notElem" (S.notElem 0)
118− testFoldOpsCleanup "find" (S.find (==0))
119− testFoldOpsCleanup "findIndex" (S.findIndex (==0))
120− testFoldOpsCleanup "all" (S.all (==1))
121− testFoldOpsCleanup "any" (S.any (==0))
122− testFoldOpsCleanup "and" (S.and . S.map (==1))
123− testFoldOpsCleanup "or" (S.or . S.map (==0))
124−#endif
125−
126− ---------------------------------------------------------------------------
127− -- Semigroup/Monoidal Composition strict ordering checks
128− ---------------------------------------------------------------------------
129−
130− -- test both (<>) and mappend to make sure we are using correct instance
131− -- for Monoid that is using the right version of semigroup. Instance
132− -- deriving can cause us to pick wrong instances sometimes.
133−
134− describe "WSerial interleaved (<>) ordering check" $ interleaveCheck wSerially (<>)
135− describe "WSerial interleaved mappend ordering check" $ interleaveCheck wSerially mappend
136−
137− -- describe "WAsync interleaved (<>) ordering check" $ interleaveCheck wAsyncly (<>)
138− -- describe "WAsync interleaved mappend ordering check" $ interleaveCheck wAsyncly mappend
139−
140− describe "Async (<>) time order check" $ parallelCheck asyncly (<>)
141− describe "Async mappend time order check" $ parallelCheck asyncly mappend
142−
143− -- XXX this keeps failing intermittently, need to investigate
144− -- describe "WAsync (<>) time order check" $ parallelCheck wAsyncly (<>)
145− -- describe "WAsync mappend time order check" $ parallelCheck wAsyncly mappend
146−
147− describe "Parallel (<>) time order check" $ parallelCheck parallely (<>)
148− describe "Parallel mappend time order check" $ parallelCheck parallely mappend
149− it "fromCallback" $ testFromCallback `shouldReturn` (50*101)
150−
151−checkCleanup :: IsStream t
152− => Int
153− -> (t IO Int -> SerialT IO Int)
154− -> (t IO Int -> t IO Int)
155− -> IO ()
156−checkCleanup d t op = do
157− r <- newIORef (-1 :: Int)
158− S.drain . serially $ do
159− _ <- t $ op $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
160− return ()
161− performMajorGC
162− threadDelay 500000
163− res <- readIORef r
164− res `shouldBe` 0
165− where
166− delay ref i = threadDelay (i*d*100000) >> writeIORef ref i >> return i
167−
168−#ifdef DEVBUILD
169−checkCleanupFold :: IsStream t
170− => (t IO Int -> SerialT IO Int)
171− -> (SerialT IO Int -> IO (Maybe Int))
172− -> IO ()
173−checkCleanupFold t op = do
174− r <- newIORef (-1 :: Int)
175− _ <- op $ t $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
176− performMajorGC
177− threadDelay 500000
178− res <- readIORef r
179− res `shouldBe` 0
180− where
181− delay ref i = threadDelay (i*200000) >> writeIORef ref i >> return i
182−
183−testFoldOpsCleanup :: String -> (SerialT IO Int -> IO a) -> Spec
184−testFoldOpsCleanup name f = do
185− let testOp op x = op x >> return Nothing
186− it (name <> " asyncly") $ checkCleanupFold asyncly (testOp f)
187− it (name <> " wAsyncly") $ checkCleanupFold wAsyncly (testOp f)
188− it (name <> " aheadly") $ checkCleanupFold aheadly (testOp f)
189− it (name <> " parallely") $ checkCleanupFold parallely (testOp f)
190−#endif
191−
192−parallelTests :: SpecWith ()
193−parallelTests = H.parallel $ do
194−
195− ---------------------------------------------------------------------------
196− -- TBD Monoidal composition combinations
197− ---------------------------------------------------------------------------
198−
199− -- TBD need more such combinations to be tested.
200− describe "serial <> and serial <>" $
201− composeAndComposeSimple serially serially (repeat [1 .. 9])
202− describe "ahead <> and ahead <>" $
203− composeAndComposeSimple aheadly aheadly (repeat [1 .. 9])
204− describe "ahead <> and serial <>" $
205− composeAndComposeSimple aheadly serially (repeat [1 .. 9])
206− describe "serial <> and ahead <>" $
207− composeAndComposeSimple serially aheadly (repeat [1 .. 9])
208−
209− describe "<> and <=>" $ composeAndComposeSimple
210− serially
211− wSerially
212− [ [1 .. 9]
213− , [1 .. 9]
214− , [1, 3, 2, 4, 6, 5, 7, 9, 8]
215− , [1, 3, 2, 4, 6, 5, 7, 9, 8]
216− ]
217−
218− describe "<=> and <=>" $ composeAndComposeSimple
219− wSerially
220− wSerially
221− [ [1, 4, 2, 7, 3, 5, 8, 6, 9]
222− , [1, 7, 4, 8, 2, 9, 5, 3, 6]
223− , [1, 4, 3, 7, 2, 6, 9, 5, 8]
224− , [1, 7, 4, 9, 3, 8, 6, 2, 5]
225− ]
226−
227− describe "<=> and <>" $ composeAndComposeSimple
228− wSerially
229− serially
230− [ [1, 4, 2, 7, 3, 5, 8, 6, 9]
231− , [1, 7, 4, 8, 2, 9, 5, 3, 6]
232− , [1, 4, 2, 7, 3, 5, 8, 6, 9]
233− , [1, 7, 4, 8, 2, 9, 5, 3, 6]
234− ]
235−
236− ---------------------------------------------------------------------------
237− -- Monoidal composition recursion loops
238− ---------------------------------------------------------------------------
239−
240− describe "Serial loops" $ loops serially id reverse
241− describe "Ahead loops" $ loops aheadly id reverse
242− describe "Async parallel loops" $ loops asyncly sort sort
243− describe "WAsync loops" $ loops wAsyncly sort sort
244− describe "parallel loops" $ loops parallely sort sort
245−
246− ---------------------------------------------------------------------------
247− -- Bind and monoidal composition combinations
248− ---------------------------------------------------------------------------
249−
250− describe "Bind and compose Stream 1" $ bindAndComposeSimple serially serially
251− describe "Bind and compose Stream 2" $ bindAndComposeSimple serially wSerially
252− describe "Bind and compose Stream 3" $ bindAndComposeSimple serially asyncly
253− describe "Bind and compose Stream 4" $ bindAndComposeSimple serially wAsyncly
254− describe "Bind and compose Stream 5" $ bindAndComposeSimple serially parallely
255− describe "Bind and compose Stream 6" $ bindAndComposeSimple serially aheadly
256−
257− describe "Bind and compose Ahead Stream 0" $ bindAndComposeSimple aheadly aheadly
258− describe "Bind and compose Ahead Stream 1" $ bindAndComposeSimple aheadly serially
259− describe "Bind and compose Ahead Stream 2" $ bindAndComposeSimple aheadly wSerially
260− describe "Bind and compose Ahead Stream 3" $ bindAndComposeSimple aheadly asyncly
261− describe "Bind and compose Ahead Stream 4" $ bindAndComposeSimple aheadly wAsyncly
262− describe "Bind and compose Ahead Stream 5" $ bindAndComposeSimple aheadly parallely
263−
264− describe "Bind and compose Costream 1" $ bindAndComposeSimple wSerially serially
265− describe "Bind and compose Costream 2" $ bindAndComposeSimple wSerially wSerially
266− describe "Bind and compose Costream 3" $ bindAndComposeSimple wSerially asyncly
267− describe "Bind and compose Costream 4" $ bindAndComposeSimple wSerially wAsyncly
268− describe "Bind and compose Costream 5" $ bindAndComposeSimple wSerially parallely
269− describe "Bind and compose Costream 6" $ bindAndComposeSimple wSerially aheadly
270−
271− describe "Bind and compose Async 1" $ bindAndComposeSimple asyncly serially
272− describe "Bind and compose Async 2" $ bindAndComposeSimple asyncly wSerially
273− describe "Bind and compose Async 3" $ bindAndComposeSimple asyncly asyncly
274− describe "Bind and compose Async 4" $ bindAndComposeSimple asyncly wAsyncly
275− describe "Bind and compose Async 5" $ bindAndComposeSimple asyncly parallely
276− describe "Bind and compose Async 6" $ bindAndComposeSimple asyncly aheadly
277−
278− describe "Bind and compose WAsync 1" $ bindAndComposeSimple wAsyncly serially
279− describe "Bind and compose WAsync 2" $ bindAndComposeSimple wAsyncly wSerially
280− describe "Bind and compose WAsync 3" $ bindAndComposeSimple wAsyncly asyncly
281− describe "Bind and compose WAsync 4" $ bindAndComposeSimple wAsyncly wAsyncly
282− describe "Bind and compose WAsync 5" $ bindAndComposeSimple wAsyncly parallely
283− describe "Bind and compose WAsync 6" $ bindAndComposeSimple wAsyncly aheadly
284−
285− describe "Bind and compose Parallel 1" $ bindAndComposeSimple parallely serially
286− describe "Bind and compose Parallel 2" $ bindAndComposeSimple parallely wSerially
287− describe "Bind and compose Parallel 3" $ bindAndComposeSimple parallely asyncly
288− describe "Bind and compose Parallel 4" $ bindAndComposeSimple parallely wAsyncly
289− describe "Bind and compose Parallel 5" $ bindAndComposeSimple parallely parallely
290− describe "Bind and compose Parallel 6" $ bindAndComposeSimple parallely aheadly
291−
292− let fldr, fldl :: (IsStream t, Semigroup (t IO Int)) => [t IO Int] -> t IO Int
293− fldr = foldr (<>) nil
294− fldl = foldl (<>) nil
295−
296− forM_ [fldr, fldl] $ \k ->
297− describe "Bind and compose" $ bindAndComposeHierarchy serially serially k
298− forM_ [fldr, fldl] $ \k ->
299− describe "Bind and compose" $ bindAndComposeHierarchy serially wSerially k
300− forM_ [fldr, fldl] $ \k ->
301− describe "Bind and compose" $ bindAndComposeHierarchy serially asyncly k
302− forM_ [fldr, fldl] $ \k ->
303− describe "Bind and compose" $ bindAndComposeHierarchy serially wAsyncly k
304− forM_ [fldr, fldl] $ \k ->
305− describe "Bind and compose" $ bindAndComposeHierarchy serially parallely k
306− forM_ [fldr, fldl] $ \k ->
307− describe "Bind and compose serially aheadly" $ bindAndComposeHierarchy serially aheadly k
308−
309− forM_ [fldr, fldl] $ \k ->
310− describe "Bind and compose aheadly serially" $ bindAndComposeHierarchy aheadly serially k
311− forM_ [fldr, fldl] $ \k ->
312− describe "Bind and compose aheadly wSerially" $ bindAndComposeHierarchy aheadly wSerially k
313− forM_ [fldr, fldl] $ \k ->
314− describe "Bind and compose aheadly asyncly" $ bindAndComposeHierarchy aheadly asyncly k
315− forM_ [fldr, fldl] $ \k ->
316− describe "Bind and compose aheadly wAsyncly" $ bindAndComposeHierarchy aheadly wAsyncly k
317− forM_ [fldr, fldl] $ \k ->
318− describe "Bind and compose aheadly parallely" $ bindAndComposeHierarchy aheadly parallely k
319− forM_ [fldr, fldl] $ \k ->
320− describe "Bind and compose serially aheadly" $ bindAndComposeHierarchy aheadly aheadly k
321−
322− forM_ [fldr, fldl] $ \k ->
323− describe "Bind and compose" $ bindAndComposeHierarchy wSerially serially k
324− forM_ [fldr, fldl] $ \k ->
325− describe "Bind and compose" $ bindAndComposeHierarchy wSerially wSerially k
326− forM_ [fldr, fldl] $ \k ->
327− describe "Bind and compose" $ bindAndComposeHierarchy wSerially asyncly k
328− forM_ [fldr, fldl] $ \k ->
329− describe "Bind and compose" $ bindAndComposeHierarchy wSerially wAsyncly k
330− forM_ [fldr, fldl] $ \k ->
331− describe "Bind and compose" $ bindAndComposeHierarchy wSerially parallely k
332− forM_ [fldr, fldl] $ \k ->
333− describe "Bind and compose wserially aheadly" $ bindAndComposeHierarchy wSerially aheadly k
334−
335− forM_ [fldr, fldl] $ \k ->
336− describe "Bind and compose" $ bindAndComposeHierarchy asyncly serially k
337− forM_ [fldr, fldl] $ \k ->
338− describe "Bind and compose" $ bindAndComposeHierarchy asyncly wSerially k
339− forM_ [fldr, fldl] $ \k ->
340− describe "Bind and compose" $ bindAndComposeHierarchy asyncly asyncly k
341− forM_ [fldr, fldl] $ \k ->
342− describe "Bind and compose" $ bindAndComposeHierarchy asyncly wAsyncly k
343− forM_ [fldr, fldl] $ \k ->
344− describe "Bind and compose" $ bindAndComposeHierarchy asyncly parallely k
345− forM_ [fldr, fldl] $ \k ->
346− describe "Bind and compose asyncly aheadly" $ bindAndComposeHierarchy asyncly aheadly k
347−
348− forM_ [fldr, fldl] $ \k ->
349− describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly serially k
350− forM_ [fldr, fldl] $ \k ->
351− describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly wSerially k
352− forM_ [fldr, fldl] $ \k ->
353− describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly asyncly k
354− forM_ [fldr, fldl] $ \k ->
355− describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly wAsyncly k
356− forM_ [fldr, fldl] $ \k ->
357− describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly parallely k
358− forM_ [fldr, fldl] $ \k ->
359− describe "Bind and compose wAsyncly aheadly" $ bindAndComposeHierarchy wAsyncly aheadly k
360−
361− forM_ [fldr, fldl] $ \k ->
362− describe "Bind and compose" $ bindAndComposeHierarchy parallely serially k
363− forM_ [fldr, fldl] $ \k ->
364− describe "Bind and compose" $ bindAndComposeHierarchy parallely wSerially k
365− forM_ [fldr, fldl] $ \k ->
366− describe "Bind and compose" $ bindAndComposeHierarchy parallely asyncly k
367− forM_ [fldr, fldl] $ \k ->
368− describe "Bind and compose" $ bindAndComposeHierarchy parallely wAsyncly k
369− forM_ [fldr, fldl] $ \k ->
370− describe "Bind and compose" $ bindAndComposeHierarchy parallely parallely k
371− forM_ [fldr, fldl] $ \k ->
372− describe "Bind and compose parallely aheadly" $ bindAndComposeHierarchy parallely aheadly k
373−
374− -- Nest two lists using different styles of product compositions
375− it "Nests two streams using monadic serial composition" nestTwoSerial
376− it "Nests two streams using monadic ahead composition" nestTwoAhead
377− it "Nests two streams using monadic interleaved composition" nestTwoInterleaved
378− it "Nests two streams using monadic Async composition" nestTwoAsync
379− it "Nests two streams using monadic WAsync composition" nestTwoWAsync
380− it "Nests two streams using monadic parallel composition" nestTwoParallel
381−
382− it "Nests two streams using applicative serial composition" nestTwoSerialApp
383− it "Nests two streams using applicative ahead composition" nestTwoAheadApp
384− it "Nests two streams using applicative interleaved composition" nestTwoInterleavedApp
385− it "Nests two streams using applicative Async composition" nestTwoAsyncApp
386− it "Nests two streams using applicative WAsync composition" nestTwoWAsyncApp
387− it "Nests two streams using applicative parallel composition" nestTwoParallelApp
388−
389− ---------------------------------------------------------------------------
390− -- TBD Bind and Bind combinations
391− ---------------------------------------------------------------------------
392−
393− -- TBD combine all binds and all compose in one example
394− describe "Miscellaneous combined examples" mixedOps
395− describe "Miscellaneous combined examples aheadly" mixedOpsAheadly
396− describe "Simple MonadError and MonadThrow" simpleMonadError
397−
398− {-
399− describe "Composed MonadError serially" $ composeWithMonadError serially
400− describe "Composed MonadError wSerially" $ composeWithMonadError wSerially
401− describe "Composed MonadError asyncly" $ composeWithMonadError asyncly
402− describe "Composed MonadError wAsyncly" $ composeWithMonadError wAsyncly
403− -}
404−
405− describe "Composed MonadThrow serially" $ composeWithMonadThrow serially
406− describe "Composed MonadThrow wSerially" $ composeWithMonadThrow wSerially
407− describe "Composed MonadThrow asyncly" $ composeWithMonadThrow asyncly
408− describe "Composed MonadThrow wAsyncly" $ composeWithMonadThrow wAsyncly
409− describe "Composed MonadThrow parallely" $ composeWithMonadThrow parallely
410− describe "Composed MonadThrow aheadly" $ composeWithMonadThrow aheadly
411−
412− describe "take on infinite concurrent stream" $ takeInfinite asyncly
413− describe "take on infinite concurrent stream" $ takeInfinite wAsyncly
414− describe "take on infinite concurrent stream" $ takeInfinite aheadly
415−
416− ---------------------------------------------------------------------------
417− -- Some ad-hoc tests that failed at times
418− ---------------------------------------------------------------------------
419−
420− it "takes n from stream of streams" (takeCombined 1 aheadly)
421− it "takes n from stream of streams" (takeCombined 2 asyncly)
422− it "takes n from stream of streams" (takeCombined 3 wAsyncly)
423−
424− ---------------------------------------------------------------------------
425− -- Left folds are strict enough
426− ---------------------------------------------------------------------------
427−
428−#ifdef DEVBUILD
429− it "foldx is strict enough" checkFoldxStrictness
430− it "scanx is strict enough" checkScanxStrictness
431− it "foldxM is strict enough" (checkFoldMStrictness foldxMStrictCheck)
432−#endif
433− it "foldl' is strict enough" checkFoldl'Strictness
434− it "scanl' is strict enough" checkScanl'Strictness
435− it "foldlM' is strict enough" (checkFoldMStrictness foldlM'StrictCheck)
436− it "scanlM' is strict enough" (checkScanlMStrictness scanlM'StrictCheck)
437−
438− ---------------------------------------------------------------------------
439− -- Right folds are lazy enough
440− ---------------------------------------------------------------------------
441−
442− it "foldrM is lazy enough" checkFoldrLaziness
443−
444− ---------------------------------------------------------------------------
445− -- Monadic state transfer in concurrent tasks
446− ---------------------------------------------------------------------------
447−
448− -- XXX Can we write better test cases to hit every case?
449−
450− it "async: state is saved and used if the work is partially enqueued"
451− (checkMonadicStateTransfer async)
452−
453− it "wAsync: state is saved and used if the work is partially enqueued"
454− (checkMonadicStateTransfer wAsync)
455−
456− it "ahead: state is saved and used if the work is partially enqueued"
457− (checkMonadicStateTransfer ahead)
458−
459− ---------------------------------------------------------------------------
460− -- Monadic state snapshot in concurrent tasks
461− ---------------------------------------------------------------------------
462−
463− it "asyncly maintains independent states in concurrent tasks"
464− (monadicStateSnapshot asyncly)
465− it "asyncly limited maintains independent states in concurrent tasks"
466− (monadicStateSnapshot (asyncly . S.take 10000))
467− it "wAsyncly maintains independent states in concurrent tasks"
468− (monadicStateSnapshot wAsyncly)
469− it "wAsyncly limited maintains independent states in concurrent tasks"
470− (monadicStateSnapshot (wAsyncly . S.take 10000))
471− it "aheadly maintains independent states in concurrent tasks"
472− (monadicStateSnapshot aheadly)
473− it "aheadly limited maintains independent states in concurrent tasks"
474− (monadicStateSnapshot (aheadly . S.take 10000))
475− it "parallely maintains independent states in concurrent tasks"
476− (monadicStateSnapshot parallely)
477−
478− it "async maintains independent states in concurrent tasks"
479− (monadicStateSnapshotOp async)
480− it "ahead maintains independent states in concurrent tasks"
481− (monadicStateSnapshotOp ahead)
482− it "wAsync maintains independent states in concurrent tasks"
483− (monadicStateSnapshotOp wAsync)
484− it "parallel maintains independent states in concurrent tasks"
485− (monadicStateSnapshotOp S.parallel)
486−
487− ---------------------------------------------------------------------------
488− -- Slower tests are at the end
489− ---------------------------------------------------------------------------
490−
491− ---------------------------------------------------------------------------
492− -- Thread limits
493− ---------------------------------------------------------------------------
494−
495− it "asyncly crosses thread limit (2000 threads)" $
496− S.drain (asyncly $ fold $
497− replicate 2000 $ S.yieldM $ threadDelay 1000000)
498− `shouldReturn` ()
499−
500− it "aheadly crosses thread limit (4000 threads)" $
501− S.drain (aheadly $ fold $
502− replicate 4000 $ S.yieldM $ threadDelay 1000000)
503− `shouldReturn` ()
504−
505−-- Each snapshot carries an independent state. Multiple parallel tasks should
506−-- not affect each other's state. This is especially important when we run
507−-- multiple tasks in a single thread.
508−snapshot :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
509−snapshot =
510− -- We deliberately use a replicate count 1 here, because a lower count
511− -- catches problems that a higher count doesn't.
512− S.replicateM 1 $ do
513− -- Even though we modify the state here it should not reflect in other
514− -- parallel tasks, it is local to each concurrent task.
515− modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==1))
516− modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
517−
518−snapshot1 :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
519−snapshot1 = S.replicateM 1000 $
520− modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
521−
522−snapshot2 :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
523−snapshot2 = S.replicateM 1000 $
524− modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
525−
526−stateComp
527− :: ( IsStream t
528− , MonadAsync m
529− , Semigroup (t m ())
530− , MonadIO (t m)
531− , MonadState Int m
532− , MonadState Int (t m)
533− )
534− => t m ()
535−stateComp = do
536− -- Each task in a concurrent composition inherits the state and maintains
537− -- its own modifications to it, not affecting the parent computation.
538− snapshot <> (modify (+1) >> (snapshot1 <> snapshot2))
539− -- The above modify statement does not affect our state because that is
540− -- used in a parallel composition. In a serial composition it will affect
541− -- our state.
542− get >>= liftIO . (`shouldSatisfy` (== (0 :: Int)))
543−
544−monadicStateSnapshot
545− :: ( IsStream t
546− , Semigroup (t (StateT Int IO) ())
547− , MonadIO (t (StateT Int IO))
548− , MonadState Int (t (StateT Int IO))
549− )
550− => (t (StateT Int IO) () -> SerialT (StateT Int IO) ()) -> IO ()
551−monadicStateSnapshot t = void $ runStateT (S.drain $ t stateComp) 0
552−
553−stateCompOp
554− :: ( AsyncT (StateT Int IO) ()
555− -> AsyncT (StateT Int IO) ()
556− -> AsyncT (StateT Int IO) ()
557− )
558− -> SerialT (StateT Int IO) ()
559−stateCompOp op = do
560− -- Each task in a concurrent composition inherits the state and maintains
561− -- its own modifications to it, not affecting the parent computation.
562− asyncly (snapshot `op` (modify (+1) >> (snapshot1 `op` snapshot2)))
563− -- The above modify statement does not affect our state because that is
564− -- used in a parallel composition. In a serial composition it will affect
565− -- our state.
566− get >>= liftIO . (`shouldSatisfy` (== (0 :: Int)))
567−
568−monadicStateSnapshotOp
569− :: ( AsyncT (StateT Int IO) ()
570− -> AsyncT (StateT Int IO) ()
571− -> AsyncT (StateT Int IO) ()
572− )
573− -> IO ()
574−monadicStateSnapshotOp op = void $ runStateT (S.drain $ stateCompOp op) 0
575−
576−takeCombined :: (Monad m, Semigroup (t m Int), Show a, Eq a, IsStream t)
577− => Int -> (t m Int -> SerialT IO a) -> IO ()
578−takeCombined n t = do
579− let constr = S.fromFoldable
580− r <- (S.toList . t) $
581− S.take n (constr ([] :: [Int]) <> constr ([] :: [Int]))
582− r `shouldBe` []
583−
584−checkMonadicStateTransfer
585− :: (IsStream t1, IsStream t2)
586− => ( t1 (StateT Int IO) ()
587− -> t2 (StateT Int IO) ()
588− -> SerialT (StateT Int IO) a3 )
589− -> IO ()
590−checkMonadicStateTransfer op = evalStateT str (0 :: Int)
591− where
592− str =
593− S.drain $
594− maxBuffer 1 $
595− (serially $ S.mapM snapshoti $ S.fromList [1..10]) `op`
596− (serially $ S.mapM snapshoti $ S.fromList [1..10])
597− snapshoti y = do
598− modify (+ 1)
599− x <- get
600− lift1 $ x `shouldBe` y
601− lift1 m = StateT $ \s -> do
602− a <- m
603− return (a, s)
604−
605−checkFoldrLaziness :: IO ()
606−checkFoldrLaziness = do
607− S.foldrM (\x xs -> if odd x then return True else xs)
608− (return False) (S.fromList (2:4:5:undefined :: [Int]))
609− `shouldReturn` True
610−
611− S.toList (IP.foldrS (\x xs -> if odd x then return True else xs)
612− (return False)
613− $ (S.fromList (2:4:5:undefined) :: SerialT IO Int))
614− `shouldReturn` [True]
615−
616− S.toList (IP.foldrT (\x xs -> if odd x then return True else xs)
617− (return False)
618− $ (S.fromList (2:4:5:undefined) :: SerialT IO Int))
619− `shouldReturn` [True]
620−
621−#ifdef DEVBUILD
622−checkFoldxStrictness :: IO ()
623−checkFoldxStrictness = do
624− let s = return (1 :: Int) `S.consM` error "failure"
625− catch (S.foldx (\_ a -> if a == 1 then error "success" else "done")
626− "begin" id s)
627− (\(ErrorCall err) -> return err)
628− `shouldReturn` "success"
629−#endif
630−
631−checkFoldl'Strictness :: IO ()
632−checkFoldl'Strictness = do
633− let s = return (1 :: Int) `S.consM` error "failure"
634− catch (S.foldl' (\_ a -> if a == 1 then error "success" else "done")
635− "begin" s)
636− (\(ErrorCall err) -> return err)
637− `shouldReturn` "success"
638−
639−#ifdef DEVBUILD
640−checkScanxStrictness :: IO ()
641−checkScanxStrictness = do
642− let s = return (1 :: Int) `S.consM` error "failure"
643− catch
644− (S.drain (
645− S.scanx (\_ a ->
646− if a == 1
647− then error "success"
648− else "done")
649− "begin" id s
650− )
651− >> return "finished"
652− )
653− (\(ErrorCall err) -> return err)
654− `shouldReturn` "success"
655−#endif
656−
657−checkScanl'Strictness :: IO ()
658−checkScanl'Strictness = do
659− let s = return (1 :: Int) `S.consM` error "failure"
660− catch
661− (S.drain
662− (S.scanl'
663− (\_ a ->
664− if a == 1
665− then error "success"
666− else "done")
667− "begin"
668− s)
669− >> return "finished"
670− )
671− (\(ErrorCall err) -> return err)
672− `shouldReturn` "success"
673−
674−foldlM'StrictCheck :: IORef Int -> SerialT IO Int -> IO ()
675−foldlM'StrictCheck ref = S.foldlM' (\_ _ -> writeIORef ref 1) (return ())
676−
677−#ifdef DEVBUILD
678−foldxMStrictCheck :: IORef Int -> SerialT IO Int -> IO ()
679−foldxMStrictCheck ref = S.foldxM (\_ _ -> writeIORef ref 1) (return ()) return
680−#endif
681−
682−checkFoldMStrictness :: (IORef Int -> SerialT IO Int -> IO ()) -> IO ()
683−checkFoldMStrictness f = do
684− ref <- newIORef 0
685− let s = return 1 `S.consM` error "x"
686− catch (f ref s) (\(_ :: ErrorCall) -> return ())
687− readIORef ref `shouldReturn` 1
688−
689−scanlM'StrictCheck :: IORef Int -> SerialT IO Int -> SerialT IO ()
690−scanlM'StrictCheck ref = S.scanlM' (\_ _ -> writeIORef ref 1) ()
691−
692−checkScanlMStrictness :: (IORef Int -> SerialT IO Int -> SerialT IO ()) -> IO ()
693−checkScanlMStrictness f = do
694− ref <- newIORef 0
695− let s = return 1 `S.consM` error "x"
696− catch (S.drain $ f ref s) (\(_ :: ErrorCall) -> return ())
697− readIORef ref `shouldReturn` 1
698−
699−takeInfinite :: IsStream t => (t IO Int -> SerialT IO Int) -> Spec
700−takeInfinite t =
701− it "take 1" $
702− S.drain (t $ S.take 1 $ S.repeatM (print "hello" >> return (1::Int)))
703− `shouldReturn` ()
704−
705−-- XXX need to test that we have promptly cleaned up everything after the error
706−-- XXX We can also check the output that we are expected to get before the
707−-- error occurs.
708−
709−newtype ExampleException = ExampleException String deriving (Eq, Show)
710−
711−instance Exception ExampleException
712−
713−simpleMonadError :: Spec
714−simpleMonadError = do
715−{-
716− it "simple runExceptT" $ do
717− (runExceptT $ S.drain $ return ())
718− `shouldReturn` (Right () :: Either String ())
719− it "simple runExceptT with error" $ do
720− (runExceptT $ S.drain $ throwError "E") `shouldReturn` Left "E"
721− -}
722− it "simple try" $
723− try (S.drain $ return ())
724− `shouldReturn` (Right () :: Either ExampleException ())
725− it "simple try with throw error" $
726− try (S.drain $ throwM $ ExampleException "E")
727− `shouldReturn` (Left (ExampleException "E") :: Either ExampleException ())
728−
729−composeWithMonadThrow
730− :: ( IsStream t
731− , Semigroup (t IO Int)
732− , MonadThrow (t IO)
733− )
734− => (t IO Int -> SerialT IO Int) -> Spec
735−composeWithMonadThrow t = do
736− it "Compose throwM, nil" $
737− try (tl (throwM (ExampleException "E") <> S.nil))
738− `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
739− it "Compose nil, throwM" $
740− try (tl (S.nil <> throwM (ExampleException "E")))
741− `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
742− oneLevelNestedSum "serially" serially
743− oneLevelNestedSum "wSerially" wSerially
744− oneLevelNestedSum "asyncly" asyncly
745− oneLevelNestedSum "wAsyncly" wAsyncly
746− -- XXX add two level nesting
747−
748− oneLevelNestedProduct "serially" serially
749− oneLevelNestedProduct "wSerially" wSerially
750− oneLevelNestedProduct "asyncly" asyncly
751− oneLevelNestedProduct "wAsyncly" wAsyncly
752−
753− where
754− tl = S.toList . t
755− oneLevelNestedSum desc t1 =
756− it ("One level nested sum " <> desc) $ do
757− let nested = S.fromFoldable [1..10] <> throwM (ExampleException "E")
758− <> S.fromFoldable [1..10]
759− try (tl (S.nil <> t1 nested <> S.fromFoldable [1..10]))
760− `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
761−
762− oneLevelNestedProduct desc t1 =
763− it ("One level nested product" <> desc) $ do
764− let s1 = t $ S.concatMapFoldableWith (<>) return [1..4]
765− s2 = t1 $ S.concatMapFoldableWith (<>) return [5..8]
766− try $ tl (do
767− x <- adapt s1
768− y <- s2
769− if x + y > 10
770− then throwM (ExampleException "E")
771− else return (x + y)
772− )
773− `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
774−
775−_composeWithMonadError
776− :: ( IsStream t
777− , Semigroup (t (ExceptT String IO) Int)
778− , MonadError String (t (ExceptT String IO))
779− )
780− => (t (ExceptT String IO) Int -> SerialT (ExceptT String IO) Int) -> Spec
781−_composeWithMonadError t = do
782− let tl = S.toList . t
783− it "Compose throwError, nil" $
784− runExceptT (tl (throwError "E" <> S.nil)) `shouldReturn` Left "E"
785− it "Compose nil, error" $
786− runExceptT (tl (S.nil <> throwError "E")) `shouldReturn` Left "E"
787−
788−nestTwoSerial :: Expectation
789−nestTwoSerial =
790− let s1 = S.concatMapFoldableWith (<>) return [1..4]
791− s2 = S.concatMapFoldableWith (<>) return [5..8]
792− in toListSerial (do
793− x <- s1
794− y <- s2
795− return (x + y)
796− ) `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
797−
798−nestTwoAhead :: Expectation
799−nestTwoAhead =
800− let s1 = S.concatMapFoldableWith (<>) return [1..4]
801− s2 = S.concatMapFoldableWith (<>) return [5..8]
802− in (S.toList . aheadly) (do
803− x <- s1
804− y <- s2
805− return (x + y)
806− ) `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
807−
808−nestTwoSerialApp :: Expectation
809−nestTwoSerialApp =
810− let s1 = S.concatMapFoldableWith (<>) return [1..4]
811− s2 = S.concatMapFoldableWith (<>) return [5..8]
812− in toListSerial ((+) <$> s1 <*> s2)
813− `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
814−
815−nestTwoAheadApp :: Expectation
816−nestTwoAheadApp =
817− let s1 = S.concatMapFoldableWith (<>) return [1..4]
818− s2 = S.concatMapFoldableWith (<>) return [5..8]
819− in (S.toList . aheadly) ((+) <$> s1 <*> s2)
820− `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
821−
822−nestTwoInterleaved :: Expectation
823−nestTwoInterleaved =
824− let s1 = S.concatMapFoldableWith (<>) return [1..4]
825− s2 = S.concatMapFoldableWith (<>) return [5..8]
826− in toListInterleaved (do
827− x <- s1
828− y <- s2
829− return (x + y)
830− ) `shouldReturn` ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
831−
832−nestTwoInterleavedApp :: Expectation
833−nestTwoInterleavedApp =
834− let s1 = S.concatMapFoldableWith (<>) return [1..4]
835− s2 = S.concatMapFoldableWith (<>) return [5..8]
836− in toListInterleaved ((+) <$> s1 <*> s2)
837− `shouldReturn` ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
838−
839−nestTwoAsync :: Expectation
840−nestTwoAsync =
841− let s1 = S.concatMapFoldableWith (<>) return [1..4]
842− s2 = S.concatMapFoldableWith (<>) return [5..8]
843− in sort <$> toListAsync (do
844− x <- s1
845− y <- s2
846− return (x + y))
847− `shouldReturn` sort ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
848−
849−nestTwoAsyncApp :: Expectation
850−nestTwoAsyncApp =
851− let s1 = S.concatMapFoldableWith (<>) return [1..4]
852− s2 = S.concatMapFoldableWith (<>) return [5..8]
853− in sort <$> toListAsync ((+) <$> s1 <*> s2)
854− `shouldReturn` sort ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
855−
856−nestTwoWAsync :: Expectation
857−nestTwoWAsync =
858− let s1 = S.concatMapFoldableWith (<>) return [1..4]
859− s2 = S.concatMapFoldableWith (<>) return [5..8]
860− in sort <$> (S.toList . wAsyncly) (do
861− x <- s1
862− y <- s2
863− return (x + y))
864− `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
865−
866−nestTwoParallel :: Expectation
867−nestTwoParallel =
868− let s1 = S.concatMapFoldableWith (<>) return [1..4]
869− s2 = S.concatMapFoldableWith (<>) return [5..8]
870− in sort <$> (S.toList . parallely) (do
871− x <- s1
872− y <- s2
873− return (x + y))
874− `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
875−
876−nestTwoWAsyncApp :: Expectation
877−nestTwoWAsyncApp =
878− let s1 = S.concatMapFoldableWith (<>) return [1..4]
879− s2 = S.concatMapFoldableWith (<>) return [5..8]
880− in sort <$> (S.toList . wAsyncly) ((+) <$> s1 <*> s2)
881− `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
882−
883−nestTwoParallelApp :: Expectation
884−nestTwoParallelApp =
885− let s1 = S.concatMapFoldableWith (<>) return [1..4]
886− s2 = S.concatMapFoldableWith (<>) return [5..8]
887− in sort <$> (S.toList . parallely) ((+) <$> s1 <*> s2)
888− `shouldReturn` sort ([6,7,7,8,8,8,9,9,9,9,10,10,10,11,11,12] :: [Int])
889−
890−interleaveCheck :: IsStream t
891− => (t IO Int -> SerialT IO Int)
892− -> (t IO Int -> t IO Int -> t IO Int)
893− -> Spec
894−interleaveCheck t f =
895− it "Interleave four" $
896− (S.toList . t) ((singleton 0 `f` singleton 1) `f` (singleton 100 `f` singleton 101))
897− `shouldReturn` [0, 100, 1, 101]
898−
899−parallelCheck :: (IsStream t, Monad (t IO))
900− => (t IO Int -> SerialT IO Int)
901− -> (t IO Int -> t IO Int -> t IO Int)
902− -> Spec
903−parallelCheck t f = do
904− it "Parallel ordering left associated" $
905− (S.toList . t) (((event 4 `f` event 3) `f` event 2) `f` event 1)
906− `shouldReturn` [1..4]
907−
908− it "Parallel ordering right associated" $
909− (S.toList . t) (event 4 `f` (event 3 `f` (event 2 `f` event 1)))
910− `shouldReturn` [1..4]
911−
912− where event n = S.yieldM (threadDelay (n * 200000)) >> return n
913−
914−composeAndComposeSimple
915− :: ( IsStream t1, Semigroup (t1 IO Int)
916− , IsStream t2, Monoid (t2 IO Int), Monad (t2 IO)
917−#if __GLASGOW_HASKELL__ < 804
918− , Semigroup (t2 IO Int)
919−#endif
920− )
921− => (t1 IO Int -> SerialT IO Int)
922− -> (t2 IO Int -> t2 IO Int)
923− -> [[Int]] -> Spec
924−composeAndComposeSimple t1 t2 answer = do
925− let rfold = adapt . t2 . S.concatMapFoldableWith (<>) return
926− it "Compose right associated outer expr, right folded inner" $
927− (S.toList . t1) (rfold [1,2,3] <> (rfold [4,5,6] <> rfold [7,8,9]))
928− `shouldReturn` head answer
929−
930− it "Compose left associated outer expr, right folded inner" $
931− (S.toList . t1) ((rfold [1,2,3] <> rfold [4,5,6]) <> rfold [7,8,9])
932− `shouldReturn` (answer !! 1)
933−
934− let lfold xs = adapt $ t2 $ foldl (<>) mempty $ fmap return xs
935− it "Compose right 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 !! 2)
938−
939− it "Compose left associated outer expr, left folded inner" $
940− (S.toList . t1) ((lfold [1,2,3] <> lfold [4,5,6]) <> lfold [7,8,9])
941− `shouldReturn` (answer !! 3)
942−
943−loops
944− :: (IsStream t, Semigroup (t IO Int), Monad (t IO))
945− => (t IO Int -> t IO Int)
946− -> ([Int] -> [Int])
947− -> ([Int] -> [Int])
948− -> Spec
949−loops t tsrt hsrt = do
950− it "Tail recursive loop" $ (tsrt <$> (S.toList . adapt) (loopTail 0))
951− `shouldReturn` [0..3]
952−
953− it "Head recursive loop" $ (hsrt <$> (S.toList . adapt) (loopHead 0))
954− `shouldReturn` [0..3]
955−
956− where
957− loopHead x = do
958− -- this print line is important for the test (causes a bind)
959− S.yieldM $ putStrLn "LoopHead..."
960− t $ (if x < 3 then loopHead (x + 1) else nil) <> return x
961−
962− loopTail x = do
963− -- this print line is important for the test (causes a bind)
964− S.yieldM $ putStrLn "LoopTail..."
965− t $ return x <> (if x < 3 then loopTail (x + 1) else nil)
966−
967−bindAndComposeSimple
968− :: ( IsStream t1, IsStream t2, Semigroup (t2 IO Int), Monad (t2 IO))
969− => (t1 IO Int -> SerialT IO Int)
970− -> (t2 IO Int -> t2 IO Int)
971− -> Spec
972−bindAndComposeSimple t1 t2 = do
973− -- XXX need a bind in the body of concatForFoldableWith instead of a simple return
974− it "Compose many (right fold) with bind" $
975− (sort <$> (S.toList . t1)
976− (adapt . t2 $ S.concatForFoldableWith (<>) [1..10 :: Int] return))
977− `shouldReturn` [1..10]
978−
979− it "Compose many (left fold) with bind" $
980− let forL xs k = foldl (<>) nil $ fmap k xs
981− in (sort <$> (S.toList . t1) (adapt . t2 $ forL [1..10 :: Int] return))
982− `shouldReturn` [1..10]
983−
984−bindAndComposeHierarchy
985− :: ( IsStream t1, Monad (t1 IO)
986− , IsStream t2, Monad (t2 IO))
987− => (t1 IO Int -> SerialT IO Int)
988− -> (t2 IO Int -> t2 IO Int)
989− -> ([t2 IO Int] -> t2 IO Int)
990− -> Spec
991−bindAndComposeHierarchy t1 t2 g =
992− it "Bind and compose nested" $
993− (sort <$> (S.toList . t1) bindComposeNested)
994− `shouldReturn` (sort (
995− [12, 18]
996− <> replicate 3 13
997− <> replicate 3 17
998− <> replicate 6 14
999− <> replicate 6 16
1000− <> replicate 7 15) :: [Int])
1001−
1002− where
1003−
1004− -- bindComposeNested :: WAsyncT IO Int
1005− bindComposeNested =
1006− let c1 = tripleCompose (return 1) (return 2) (return 3)
1007− c2 = tripleCompose (return 4) (return 5) (return 6)
1008− c3 = tripleCompose (return 7) (return 8) (return 9)
1009− b = tripleBind c1 c2 c3
1010−-- it seems to be causing a huge space leak in hspec so disabling this for now
1011−-- c = tripleCompose b b b
1012−-- m = tripleBind c c c
1013−-- in m
1014− in b
1015−
1016− tripleCompose a b c = adapt . t2 $ g [a, b, c]
1017− tripleBind mx my mz =
1018− mx >>= \x -> my
1019− >>= \y -> mz
1020− >>= \z -> return (x + y + z)
1021−
1022−mixedOps :: Spec
1023−mixedOps =
1024− it "Compose many ops" $
1025− (sort <$> toListSerial composeMixed)
1026− `shouldReturn` ([8,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,11,11
1027− ,11,11,11,11,11,11,11,11,12,12,12,12,12,13
1028− ] :: [Int])
1029− where
1030−
1031− composeMixed :: SerialT IO Int
1032− composeMixed = do
1033− S.yieldM $ return ()
1034− S.yieldM $ putStr ""
1035− let x = 1
1036− let y = 2
1037− z <- do
1038− x1 <- wAsyncly $ return 1 <> return 2
1039− S.yieldM $ return ()
1040− S.yieldM $ putStr ""
1041− y1 <- asyncly $ return 1 <> return 2
1042− z1 <- do
1043− x11 <- return 1 <> return 2
1044− y11 <- asyncly $ return 1 <> return 2
1045− z11 <- wSerially $ return 1 <> return 2
1046− S.yieldM $ return ()
1047− S.yieldM $ putStr ""
1048− return (x11 + y11 + z11)
1049− return (x1 + y1 + z1)
1050− return (x + y + z)
1051−
1052−mixedOpsAheadly :: Spec
1053−mixedOpsAheadly =
1054− it "Compose many ops" $
1055− (sort <$> toListSerial composeMixed)
1056− `shouldReturn` ([8,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,11,11
1057− ,11,11,11,11,11,11,11,11,12,12,12,12,12,13
1058− ] :: [Int])
1059− where
1060−
1061− composeMixed :: SerialT IO Int
1062− composeMixed = do
1063− S.yieldM $ return ()
1064− S.yieldM $ putStr ""
1065− let x = 1
1066− let y = 2
1067− z <- do
1068− x1 <- wAsyncly $ return 1 <> return 2
1069− S.yieldM $ return ()
1070− S.yieldM $ putStr ""
1071− y1 <- aheadly $ return 1 <> return 2
1072− z1 <- do
1073− x11 <- return 1 <> return 2
1074− y11 <- aheadly $ return 1 <> return 2
1075− z11 <- parallely $ return 1 <> return 2
1076− S.yieldM $ return ()
1077− S.yieldM $ putStr ""
1078− return (x11 + y11 + z11)
1079− return (x1 + y1 + z1)
1080− return (x + y + z)
1081−
1082−testFromCallback :: IO Int
1083−testFromCallback = do
1084− ref <- newIORef Nothing
1085− let stream = S.map Just (IP.fromCallback (setCallback ref))
1086− `S.parallel` runCallback ref
1087− S.sum $ S.map fromJust $ S.takeWhile isJust stream
1088−
1089− where
1090−
1091− setCallback ref cb = do
1092− writeIORef ref (Just cb)
1093−
1094− runCallback ref = S.yieldM $ do
1095− cb <-
1096− S.repeatM (readIORef ref)
1097− & IP.delayPost 0.1
1098− & S.mapMaybe id
1099− & S.head
1100−
1101− S.fromList [1..100]
1102− & IP.delayPost 0.001
1103− & S.mapM_ (fromJust cb)
1104− threadDelay 100000
1105− return Nothing
test/Streamly/Test/Internal/Prelude.hs+33−4
@@ -11,6 +11,9 @@ module Main (main) where
1111
1212import Control.Concurrent (threadDelay)
1313import Control.Monad (when)
14+import Data.Function ((&))
15+import Data.IORef (newIORef, writeIORef, readIORef)
16+import Data.Maybe (fromJust, isJust)
1417
1518import Test.Hspec as H
1619import Test.Hspec.QuickCheck (prop)
@@ -20,7 +23,7 @@ import Test.QuickCheck.Monadic (monadicIO, pick)
2023import qualified Streamly.Prelude as S
2124import qualified Streamly.Internal.Data.Fold as FL
2225import qualified Streamly.Internal.Data.Unfold as UF
23−import qualified Streamly.Internal.Data.Stream.IsStream as SI
26+import qualified Streamly.Internal.Data.Stream.IsStream as IS
2427
2528import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
2629import Streamly.Internal.Data.Time.Units
@@ -62,7 +65,7 @@ testTakeByTime :: IO Bool
6265testTakeByTime = do
6366 r <-
6467 S.fold ((,) <$> FL.head <*> FL.last)
65− $ SI.takeByTime takeDropTime
68+ $ IS.takeByTime takeDropTime
6669 $ S.repeatM (threadDelay 1000 >> getTime Monotonic)
6770 checkTakeDropTime r
6871
@@ -71,7 +74,7 @@ testDropByTime = do
7174 t0 <- getTime Monotonic
7275 mt1 <-
7376 S.head
74− $ SI.dropByTime takeDropTime
77+ $ IS.dropByTime takeDropTime
7578 $ S.repeatM (threadDelay 1000 >> getTime Monotonic)
7679 checkTakeDropTime (Just t0, mt1)
7780
@@ -88,9 +91,34 @@ unfold0 = monadicIO $ do
8891 a <- pick $ choose (0, max_length `div` 2)
8992 b <- pick $ choose (0, max_length)
9093 let unf = UF.supply (UF.enumerateFromToIntegral b) a
91− ls <- S.toList $ SI.unfold0 unf
94+ ls <- S.toList $ IS.unfold0 unf
9295 return $ ls == [a..b]
9396
97+testFromCallback :: IO Int
98+testFromCallback = do
99+ ref <- newIORef Nothing
100+ let stream = S.map Just (IS.fromCallback (setCallback ref))
101+ `S.parallel` runCallback ref
102+ S.sum $ S.map fromJust $ S.takeWhile isJust stream
103+
104+ where
105+
106+ setCallback ref cb = do
107+ writeIORef ref (Just cb)
108+
109+ runCallback ref = S.yieldM $ do
110+ cb <-
111+ S.repeatM (readIORef ref)
112+ & IS.delayPost 0.1
113+ & S.mapMaybe id
114+ & S.head
115+
116+ S.fromList [1..100]
117+ & IS.delayPost 0.001
118+ & S.mapM_ (fromJust cb)
119+ threadDelay 100000
120+ return Nothing
121+
94122main :: IO ()
95123main =
96124 hspec $ do
@@ -100,3 +128,4 @@ main =
100128 describe "From Generators" $ do
101129 prop "unfold" unfold
102130 prop "unfold0" unfold0
131+ it "fromCallback" $ testFromCallback `shouldReturn` (50*101)
test/Streamly/Test/Prelude.hs+122−859
@@ -7,871 +7,134 @@
77-- Stability : experimental
88-- Portability : GHC
99
10−module Streamly.Test.Prelude
11− (
12− -- * Construction operations
13− constructWithReplicate
14− , constructWithReplicateM
15− , constructWithIntFromThenTo
16−#if __GLASGOW_HASKELL__ >= 806
17− , constructWithDoubleFromThenTo
10+module Streamly.Test.Prelude (main) where
11+
12+import Control.Exception (Exception, try)
13+import Control.Monad.Catch (throwM)
14+import Control.Monad.Error.Class (throwError, MonadError)
15+import Control.Monad.Trans.Except (runExceptT, ExceptT)
16+import Data.List (sort)
17+#if !(MIN_VERSION_base(4,11,0))
18+import Data.Semigroup (Semigroup(..))
19+import Data.IORef
1820#endif
19− , constructWithIterate
20− , constructWithIterateM
21− , constructWithFromIndices
22− , constructWithFromIndicesM
23− -- * Applicative operations
24− , applicativeOps
25− , applicativeOps1
26− -- * Elimination operations
27− , eliminationOpsOrdered
28− , eliminationOpsWord8
29− , eliminationOps
30− -- * Functor operations
31− , functorOps
32− -- * Monoid operations
33− , monoidOps
34− -- * Semigroup operations
35− , semigroupOps
36− -- * Transformation operations
37− , transformCombineOpsOrdered
38− , transformCombineOpsCommon
39− , toListFL
40− -- * Monad operations
41− , monadBind
42− , monadThen
43− -- * ZipOperations
44− , zipApplicative
45− , zipMonadic
46− , zipAsyncMonadic
47− -- * Default values
48− , maxTestCount
49− , maxStreamLen
50− -- * Helper operations
51− , folded
52− , makeCommonOps
53− , makeOps
54− , mapOps
55− , sortEq
56− ) where
5721
58−import Control.Applicative (ZipList(..), liftA2)
59−import Control.Monad (replicateM)
60−import Data.Function ((&))
61−import Data.IORef (IORef, modifyIORef', newIORef, readIORef)
62−import Data.List
63− ( deleteBy
64− , elemIndex
65− , elemIndices
66− , find
67− , findIndex
68− , findIndices
69− , foldl'
70− , foldl1'
71− , insertBy
72− , intersperse
73− , isPrefixOf
74− , isSubsequenceOf
75− , maximumBy
76− , minimumBy
77− , scanl'
78− , sort
79− , stripPrefix
80− )
81−import Data.Maybe (mapMaybe)
82−#if __GLASGOW_HASKELL__ < 808
83−import Data.Semigroup (Semigroup, (<>))
84−#endif
85−import GHC.Word (Word8)
86−import Test.Hspec.QuickCheck
87−import Test.Hspec
88−import Test.QuickCheck (Property, choose, forAll, withMaxSuccess)
89−import Test.QuickCheck.Monadic (assert, monadicIO, run)
22+import Test.Hspec as H
9023
9124import Streamly.Prelude
92− ( SerialT, IsStream, (.:), nil, (|&), serially, avgRate, rate, maxBuffer
93− , maxThreads, maxBuffer)
25+ ( SerialT, IsStream)
9426import qualified Streamly.Prelude as S
95−import qualified Streamly.Data.Fold as FL
96−import qualified Streamly.Internal.Data.Fold as FL
97−
98−import Streamly.Test.Common
99−
100−maxStreamLen :: Int
101−maxStreamLen = 1000
102−
103−-- Coverage build takes too long with default number of tests
104−maxTestCount :: Int
105−#ifdef DEVBUILD
106−maxTestCount = 100
107−#else
108−maxTestCount = 10
109−#endif
110−
111−singleton :: IsStream t => a -> t m a
112−singleton a = a .: nil
113−
114−sortEq :: Ord a => [a] -> [a] -> Bool
115−sortEq a b = sort a == sort b
116−
117−-------------------------------------------------------------------------------
118−-- Construction operations
119−-------------------------------------------------------------------------------
120−
121−constructWithLen
122− :: (Show a, Eq a)
123− => (Int -> t IO a)
124− -> (Int -> [a])
125− -> (t IO a -> SerialT IO a)
126− -> Word8
127− -> Property
128−constructWithLen mkStream mkList op len = withMaxSuccess maxTestCount $
129− monadicIO $ do
130− stream <- run $ (S.toList . op) (mkStream (fromIntegral len))
131− let list = mkList (fromIntegral len)
132− listEquals (==) stream list
133−
134−constructWithLenM
135− :: (Int -> t IO Int)
136− -> (Int -> IO [Int])
137− -> (t IO Int -> SerialT IO Int)
138− -> Word8
139− -> Property
140−constructWithLenM mkStream mkList op len = withMaxSuccess maxTestCount $
141− monadicIO $ do
142− stream <- run $ (S.toList . op) (mkStream (fromIntegral len))
143− list <- run $ mkList (fromIntegral len)
144− listEquals (==) stream list
145−
146−constructWithReplicate, constructWithReplicateM, constructWithIntFromThenTo
147− :: IsStream t
148− => (t IO Int -> SerialT IO Int)
149− -> Word8
150− -> Property
151−
152−constructWithReplicateM = constructWithLenM stream list
153− where list = flip replicateM (return 1 :: IO Int)
154− stream = flip S.replicateM (return 1 :: IO Int)
155−
156−constructWithReplicate = constructWithLen stream list
157− where list = flip replicate (1 :: Int)
158− stream = flip S.replicate (1 :: Int)
159−
160−constructWithIntFromThenTo op l =
161− forAll (choose (minBound, maxBound)) $ \from ->
162− forAll (choose (minBound, maxBound)) $ \next ->
163− forAll (choose (minBound, maxBound)) $ \to ->
164− let list len = take len [from,next..to]
165− stream len = S.take len $ S.enumerateFromThenTo from next to
166− in constructWithLen stream list op l
167−
168−#if __GLASGOW_HASKELL__ >= 806
169−-- XXX try very small steps close to 0
170−constructWithDoubleFromThenTo
171− :: IsStream t
172− => (t IO Double -> SerialT IO Double)
173− -> Word8
174− -> Property
175−constructWithDoubleFromThenTo op l =
176− forAll (choose (-9007199254740999,9007199254740999)) $ \from ->
177− forAll (choose (-9007199254740999,9007199254740999)) $ \next ->
178− forAll (choose (-9007199254740999,9007199254740999)) $ \to ->
179− let list len = take len [from,next..to]
180− stream len = S.take len $ S.enumerateFromThenTo from next to
181− in constructWithLen stream list op l
182−#endif
183−
184−constructWithIterate ::
185− IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
186−constructWithIterate op len =
187− withMaxSuccess maxTestCount $
188− monadicIO $ do
189− stream <-
190− run $
191− (S.toList . op . S.take (fromIntegral len))
192− (S.iterate (+ 1) (0 :: Int))
193− let list = take (fromIntegral len) (iterate (+ 1) 0)
194− listEquals (==) stream list
195−
196−constructWithIterateM ::
197− IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
198−constructWithIterateM op len =
199− withMaxSuccess maxTestCount $
200− monadicIO $ do
201− mvl <- run (newIORef [] :: IO (IORef [Int]))
202− let addM mv x y = modifyIORef' mv (++ [y + x]) >> return (y + x)
203− list = take (fromIntegral len) (iterate (+ 1) 0)
204− run $
205− S.drain . op $
206− S.take (fromIntegral len) $
207− S.iterateM (addM mvl 1) (addM mvl 0 0 :: IO Int)
208− streamEffect <- run $ readIORef mvl
209− listEquals (==) streamEffect list
210−
211−constructWithFromIndices ::
212− IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
213−constructWithFromIndices op len =
214− withMaxSuccess maxTestCount $
215− monadicIO $ do
216− stream <-
217− run $ (S.toList . op . S.take (fromIntegral len)) (S.fromIndices id)
218− let list = take (fromIntegral len) (iterate (+ 1) 0)
219− listEquals (==) stream list
220−
221−constructWithFromIndicesM ::
222− IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
223−constructWithFromIndicesM op len =
224− withMaxSuccess maxTestCount $
225− monadicIO $ do
226− mvl <- run (newIORef [] :: IO (IORef [Int]))
227− let addIndex mv i = modifyIORef' mv (++ [i]) >> return i
228− list = take (fromIntegral len) (iterate (+ 1) 0)
229− run $
230− S.drain . op $
231− S.take (fromIntegral len) $ S.fromIndicesM (addIndex mvl)
232− streamEffect <- run $ readIORef mvl
233− listEquals (==) streamEffect list
234−
235−-------------------------------------------------------------------------------
236−-- Applicative operations
237−-------------------------------------------------------------------------------
238−
239−applicativeOps
240− :: (Applicative (t IO), Semigroup (t IO Int))
241− => ([Int] -> t IO Int)
242− -> String
243− -> ([(Int, Int)] -> [(Int, Int)] -> Bool)
244− -> (t IO (Int, Int) -> SerialT IO (Int, Int))
245− -> Spec
246−applicativeOps constr desc eq t = do
247− prop (desc <> " <*>") $
248− transformFromList2
249− constr
250− eq
251− (\a b -> (,) <$> a <*> b)
252− (\a b -> t ((,) <$> a <*> b))
253− prop (desc <> " liftA2") $
254− transformFromList2 constr eq (liftA2 (,)) (\a b -> t $ liftA2 (,) a b)
255− prop (desc <> " Apply - composed first argument") $
256− sort <$>
257− (S.toList . t) ((,) <$> (pure 1 <> pure 2) <*> pure 3) `shouldReturn`
258− [(1, 3), (2, 3)]
259− prop (desc <> " Apply - composed second argument") $
260− sort <$>
261− (S.toList . t) ((,) <$> pure 1 <*> (pure 2 <> pure 3)) `shouldReturn`
262− [(1, 2), (1, 3)]
263−
264−-- XXX we can combine this with applicativeOps by making the type sufficiently
265−-- polymorphic.
266−applicativeOps1
267− :: Applicative (t IO)
268− => ([Int] -> t IO Int)
269− -> String
270− -> ([Int] -> [Int] -> Bool)
271− -> (t IO Int -> SerialT IO Int)
272− -> Spec
273−applicativeOps1 constr desc eq t = do
274− prop (desc <> " *>") $
275− transformFromList2 constr eq (*>) (\a b -> t (a *> b))
276− prop (desc <> " <*") $
277− transformFromList2 constr eq (<*) (\a b -> t (a <* b))
278−
279−transformFromList2
280− :: (Eq c, Show c)
281− => ([a] -> t IO a)
282− -> ([c] -> [c] -> Bool)
283− -> ([a] -> [a] -> [c])
284− -> (t IO a -> t IO a -> SerialT IO c)
285− -> ([a], [a])
286− -> Property
287−transformFromList2 constr eq listOp op (a, b) =
288− withMaxSuccess maxTestCount $
289− monadicIO $ do
290− stream <- run (S.toList $ op (constr a) (constr b))
291− let list = listOp a b
292− listEquals eq stream list
293−
294−-------------------------------------------------------------------------------
295−-- Elimination operations
296−-------------------------------------------------------------------------------
297−
298−eliminateOp
299− :: (Show a, Eq a)
300− => ([s] -> t IO s)
301− -> ([s] -> a)
302− -> (t IO s -> IO a)
303− -> [s]
304− -> Property
305−eliminateOp constr listOp op a =
306− monadicIO $ do
307− stream <- run $ op (constr a)
308− let list = listOp a
309− equals (==) stream list
310−
311−wrapMaybe :: ([a1] -> a2) -> [a1] -> Maybe a2
312−wrapMaybe f x = if null x then Nothing else Just (f x)
313−
314−wrapOutOfBounds :: ([a1] -> Int -> a2) -> Int -> [a1] -> Maybe a2
315−wrapOutOfBounds f i x | null x = Nothing
316− | i >= length x = Nothing
317− | otherwise = Just (f x i)
318−
319−wrapThe :: Eq a => [a] -> Maybe a
320−wrapThe (x:xs)
321− | all (x ==) xs = Just x
322− | otherwise = Nothing
323−wrapThe [] = Nothing
324−
325−-- This is the reference uniq implementation to compare uniq against,
326−-- we can use uniq from vector package, but for now this should
327−-- suffice.
328−referenceUniq :: Eq a => [a] -> [a]
329−referenceUniq = go
330− where
331− go [] = []
332− go (x:[]) = [x]
333− go (x:y:xs)
334− | x == y = go (x : xs)
335− | otherwise = x : go (y : xs)
336−
337−eliminationOps
338− :: ([Int] -> t IO Int)
339− -> String
340− -> (t IO Int -> SerialT IO Int)
341− -> Spec
342−eliminationOps constr desc t = do
343− -- Elimination
344− prop (desc <> " null") $ eliminateOp constr null $ S.null . t
345− prop (desc <> " foldl'") $
346− eliminateOp constr (foldl' (+) 0) $ S.foldl' (+) 0 . t
347− prop (desc <> " foldl1'") $
348− eliminateOp constr (wrapMaybe $ foldl1' (+)) $ S.foldl1' (+) . t
349−#ifdef DEVBUILD
350− prop (desc <> " foldr1") $
351− eliminateOp constr (wrapMaybe $ foldr1 (+)) $ S.foldr1 (+) . t
352−#endif
353− prop (desc <> " all") $ eliminateOp constr (all even) $ S.all even . t
354− prop (desc <> " any") $ eliminateOp constr (any even) $ S.any even . t
355− prop (desc <> " and") $ eliminateOp constr (and . fmap (> 0)) $
356− (S.and . S.map (> 0)) . t
357− prop (desc <> " or") $ eliminateOp constr (or . fmap (> 0)) $
358− (S.or . S.map (> 0)) . t
359− prop (desc <> " length") $ eliminateOp constr length $ S.length . t
360− prop (desc <> " sum") $ eliminateOp constr sum $ S.sum . t
361− prop (desc <> " product") $ eliminateOp constr product $ S.product . t
362−
363− prop (desc <> " maximum") $
364− eliminateOp constr (wrapMaybe maximum) $ S.maximum . t
365− prop (desc <> " minimum") $
366− eliminateOp constr (wrapMaybe minimum) $ S.minimum . t
367−
368− prop (desc <> " maximumBy compare") $
369− eliminateOp constr (wrapMaybe $ maximumBy compare) $
370− S.maximumBy compare . t
371− prop (desc <> " maximumBy flip compare") $
372− eliminateOp constr (wrapMaybe $ maximumBy $ flip compare) $
373− S.maximumBy (flip compare) . t
374− prop (desc <> " minimumBy compare") $
375− eliminateOp constr (wrapMaybe $ minimumBy compare) $
376− S.minimumBy compare . t
377− prop (desc <> " minimumBy flip compare") $
378− eliminateOp constr (wrapMaybe $ minimumBy $ flip compare) $
379− S.minimumBy (flip compare) . t
380−
381− prop (desc <> " findIndex") $
382− eliminateOp constr (findIndex odd) $ S.findIndex odd . t
383− prop (desc <> " elemIndex") $
384− eliminateOp constr (elemIndex 3) $ S.elemIndex 3 . t
385−
386− prop (desc <> " !! 5") $
387− eliminateOp constr (wrapOutOfBounds (!!) 5) $ (S.!! 5) . t
388− prop (desc <> " !! 4") $
389− eliminateOp constr (wrapOutOfBounds (!!) 0) $ (S.!! 0) . t
390−
391− prop (desc <> " find") $ eliminateOp constr (find even) $ S.find even . t
392− prop (desc <> " lookup") $
393− eliminateOp constr (lookup 3 . flip zip [1..]) $
394− S.lookup 3 . S.zipWith (\a b -> (b, a)) (S.fromList [(1::Int)..]) . t
395− prop (desc <> " the") $ eliminateOp constr wrapThe $ S.the . t
396−
397− -- Multi-stream eliminations
398− -- Add eqBy, cmpBy
399− -- XXX Write better tests for substreams.
400− prop (desc <> " isPrefixOf 10") $ eliminateOp constr (isPrefixOf [1..10]) $
401− S.isPrefixOf (S.fromList [(1::Int)..10]) . t
402− prop (desc <> " isSubsequenceOf 10") $
403− eliminateOp constr (isSubsequenceOf $ filter even [1..10]) $
404− S.isSubsequenceOf (S.fromList $ filter even [(1::Int)..10]) . t
405− prop (desc <> " stripPrefix 10") $ eliminateOp constr (stripPrefix [1..10]) $
406− (\s -> s >>= maybe (return Nothing) (fmap Just . S.toList)) .
407− S.stripPrefix (S.fromList [(1::Int)..10]) . t
408−
409−-- head/tail/last may depend on the order in case of parallel streams
410−-- so we test these only for serial streams.
411−eliminationOpsOrdered
412− :: ([Int] -> t IO Int)
413− -> String
414− -> (t IO Int -> SerialT IO Int)
415− -> Spec
416−eliminationOpsOrdered constr desc t = do
417− prop (desc <> " head") $ eliminateOp constr (wrapMaybe head) $ S.head . t
418− prop (desc <> " tail") $ eliminateOp constr (wrapMaybe tail) $ \x -> do
419− r <- S.tail (t x)
420− case r of
421− Nothing -> return Nothing
422− Just s -> Just <$> S.toList s
423− prop (desc <> " last") $ eliminateOp constr (wrapMaybe last) $ S.last . t
424− prop (desc <> " init") $ eliminateOp constr (wrapMaybe init) $ \x -> do
425− r <- S.init (t x)
426− case r of
427− Nothing -> return Nothing
428− Just s -> Just <$> S.toList s
429−
430−elemOp
431− :: ([Word8] -> t IO Word8)
432− -> (t IO Word8 -> SerialT IO Word8)
433− -> (Word8 -> SerialT IO Word8 -> IO Bool)
434− -> (Word8 -> [Word8] -> Bool)
435− -> (Word8, [Word8])
436− -> Property
437−elemOp constr op streamOp listOp (x, xs) =
438− monadicIO $ do
439− stream <- run $ (streamOp x . op) (constr xs)
440− let list = listOp x xs
441− equals (==) stream list
442−
443−eliminationOpsWord8
444− :: ([Word8] -> t IO Word8)
445− -> String
446− -> (t IO Word8 -> SerialT IO Word8)
447− -> Spec
448−eliminationOpsWord8 constr desc t = do
449− prop (desc <> " elem") $ elemOp constr t S.elem elem
450− prop (desc <> " notElem") $ elemOp constr t S.notElem notElem
451−
452−-------------------------------------------------------------------------------
453−-- Functor operations
454−-------------------------------------------------------------------------------
455−
456−functorOps
457− :: (Functor (t IO), Semigroup (t IO Int))
458− => ([Int] -> t IO Int)
459− -> String
460− -> ([Int] -> [Int] -> Bool)
461− -> (t IO Int -> SerialT IO Int)
462− -> Spec
463−functorOps constr desc eq t = do
464− prop (desc <> " id") $ transformFromList constr eq id t
465− prop (desc <> " fmap (+1)") $
466− transformFromList constr eq (fmap (+ 1)) $ t . fmap (+ 1)
467− prop (desc <> " fmap on composed (<>)") $
468− sort <$>
469− (S.toList . t) (fmap (+ 1) (constr [1] <> constr [2])) `shouldReturn`
470− ([2, 3] :: [Int])
471−
472−transformFromList
473− :: (Eq b, Show b) =>
474− ([a] -> t IO a)
475− -> ([b] -> [b] -> Bool)
476− -> ([a] -> [b])
477− -> (t IO a -> SerialT IO b)
478− -> [a]
479− -> Property
480−transformFromList constr eq listOp op a =
481− monadicIO $ do
482− stream <- run ((S.toList . op) (constr a))
483− let list = listOp a
484− listEquals eq stream list
485−
486−
487−------------------------------------------------------------------------------
488−-- Monoid operations
489−------------------------------------------------------------------------------
490−
491−monoidOps
492− :: (IsStream t, Semigroup (t IO Int))
493− => String
494− -> t IO Int
495− -> ([Int] -> [Int] -> Bool)
496− -> (t IO Int -> SerialT IO Int)
497− -> Spec
498−monoidOps desc z eq t = do
499− -- XXX these should get covered by the property tests
500− prop (desc <> " Compose mempty, mempty") $ spec (z <> z) []
501− prop (desc <> " Compose empty at the beginning") $ spec (z <> singleton 1) [1]
502− prop (desc <> " Compose empty at the end") $ spec (singleton 1 <> z) [1]
503− prop (desc <> " Compose two") $ spec (singleton 0 <> singleton 1) [0, 1]
504− prop (desc <> " Compose many") $
505− spec (S.concatForFoldableWith (<>) [1 .. 100] singleton) [1 .. 100]
506−
507− -- These are not covered by the property tests
508− prop (desc <> " Compose three - empty in the middle") $
509− spec (singleton 0 <> z <> singleton 1) [0, 1]
510− prop (desc <> " Compose left associated") $
511− spec
512− (((singleton 0 <> singleton 1) <> singleton 2) <> singleton 3)
513− [0, 1, 2, 3]
514− prop (desc <> " Compose right associated") $
515− spec
516− (singleton 0 <> (singleton 1 <> (singleton 2 <> singleton 3)))
517− [0, 1, 2, 3]
518− prop (desc <> " Compose hierarchical (multiple levels)") $
519− spec
520− (((singleton 0 <> singleton 1) <> (singleton 2 <> singleton 3)) <>
521− ((singleton 4 <> singleton 5) <> (singleton 6 <> singleton 7)))
522− [0 .. 7]
52327
28+toListSerial :: SerialT IO a -> IO [a]
29+toListSerial = S.toList . S.serially
30+
31+main :: IO ()
32+main = hspec $ H.parallel $ do
33+
34+ describe "Miscellaneous combined examples" mixedOps
35+ describe "Miscellaneous combined examples aheadly" mixedOpsAheadly
36+ describe "Simple MonadError and MonadThrow" simpleMonadError
37+
38+ {-
39+ describe "Composed MonadError serially" $ composeWithMonadError serially
40+ describe "Composed MonadError wSerially" $ composeWithMonadError wSerially
41+ describe "Composed MonadError asyncly" $ composeWithMonadError asyncly
42+ describe "Composed MonadError wAsyncly" $ composeWithMonadError wAsyncly
43+ -}
44+
45+-- XXX need to test that we have promptly cleaned up everything after the error
46+-- XXX We can also check the output that we are expected to get before the
47+-- error occurs.
48+
49+newtype ExampleException = ExampleException String deriving (Eq, Show)
50+
51+instance Exception ExampleException
52+
53+simpleMonadError :: Spec
54+simpleMonadError = do
55+{-
56+ it "simple runExceptT" $ do
57+ (runExceptT $ S.drain $ return ())
58+ `shouldReturn` (Right () :: Either String ())
59+ it "simple runExceptT with error" $ do
60+ (runExceptT $ S.drain $ throwError "E") `shouldReturn` Left "E"
61+ -}
62+ it "simple try" $
63+ try (S.drain $ return ())
64+ `shouldReturn` (Right () :: Either ExampleException ())
65+ it "simple try with throw error" $
66+ try (S.drain $ throwM $ ExampleException "E")
67+ `shouldReturn` (Left (ExampleException "E") :: Either ExampleException ())
68+
69+_composeWithMonadError
70+ :: ( IsStream t
71+ , Semigroup (t (ExceptT String IO) Int)
72+ , MonadError String (t (ExceptT String IO))
73+ )
74+ => (t (ExceptT String IO) Int -> SerialT (ExceptT String IO) Int) -> Spec
75+_composeWithMonadError t = do
76+ let tl = S.toList . t
77+ it "Compose throwError, nil" $
78+ runExceptT (tl (throwError "E" <> S.nil)) `shouldReturn` Left "E"
79+ it "Compose nil, error" $
80+ runExceptT (tl (S.nil <> throwError "E")) `shouldReturn` Left "E"
81+
82+mixedOps :: Spec
83+mixedOps =
84+ it "Compose many ops" $
85+ (sort <$> toListSerial composeMixed)
86+ `shouldReturn` ([8,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,11,11
87+ ,11,11,11,11,11,11,11,11,12,12,12,12,12,13
88+ ] :: [Int])
52489 where
52590
526− tl = S.toList . t
527− spec s list =
528− monadicIO $ do
529− stream <- run $ tl s
530− listEquals eq stream list
531−
532−-------------------------------------------------------------------------------
533−-- Semigroup operations
534−-------------------------------------------------------------------------------
535−
536−foldFromList
537− :: ([Int] -> t IO Int)
538− -> (t IO Int -> SerialT IO Int)
539− -> ([Int] -> [Int] -> Bool)
540− -> [Int]
541− -> Property
542−foldFromList constr op eq = transformFromList constr eq id op
543−
544−-- XXX concatenate streams of multiple elements rather than single elements
545−semigroupOps
546− :: (IsStream t
547−#if __GLASGOW_HASKELL__ < 804
548− , Semigroup (t IO Int)
549−#endif
550− , Monoid (t IO Int))
551− => String
552− -> ([Int] -> [Int] -> Bool)
553− -> (t IO Int -> SerialT IO Int)
554− -> Spec
555−semigroupOps desc eq t = do
556− prop (desc <> " <>") $ foldFromList (S.concatMapFoldableWith (<>) singleton) t eq
557− prop (desc <> " mappend") $ foldFromList (S.concatMapFoldableWith mappend singleton) t eq
558−
559−-------------------------------------------------------------------------------
560−-- Transformation operations
561−-------------------------------------------------------------------------------
562−
563−transformCombineFromList
564− :: Semigroup (t IO Int)
565− => ([Int] -> t IO Int)
566− -> ([Int] -> [Int] -> Bool)
567− -> ([Int] -> [Int])
568− -> (t IO Int -> SerialT IO Int)
569− -> (t IO Int -> t IO Int)
570− -> [Int]
571− -> [Int]
572− -> [Int]
573− -> Property
574−transformCombineFromList constr eq listOp t op a b c =
575− withMaxSuccess maxTestCount $
576− monadicIO $ do
577− stream <- run ((S.toList . t) $
578− constr a <> op (constr b <> constr c))
579− let list = a <> listOp (b <> c)
580− listEquals eq stream list
581−
582−-- XXX add tests for MonadReader and MonadError etc. In case an SVar is
583−-- accidentally passed through them.
584−--
585−-- This tests transform ops along with detecting illegal sharing of SVar across
586−-- conurrent streams. These tests work for all stream types whereas
587−-- transformCombineOpsOrdered work only for ordered stream types i.e. excluding
588−-- the Async type.
589−transformCombineOpsCommon
590− :: (IsStream t, Semigroup (t IO Int))
591− => ([Int] -> t IO Int)
592− -> String
593− -> ([Int] -> [Int] -> Bool)
594− -> (t IO Int -> SerialT IO Int)
595− -> Spec
596−transformCombineOpsCommon constr desc eq t = do
597− let transform = transformCombineFromList constr eq
598−
599− -- Filtering
600− prop (desc <> " filter False") $
601− transform (filter (const False)) t (S.filter (const False))
602− prop (desc <> " filter True") $
603− transform (filter (const True)) t (S.filter (const True))
604− prop (desc <> " filter even") $
605− transform (filter even) t (S.filter even)
606−
607− prop (desc <> " filterM False") $
608− transform (filter (const False)) t (S.filterM (const $ return False))
609− prop (desc <> " filterM True") $
610− transform (filter (const True)) t (S.filterM (const $ return True))
611− prop (desc <> " filterM even") $
612− transform (filter even) t (S.filterM (return . even))
613−
614− prop (desc <> " take maxBound") $
615− transform (take maxBound) t (S.take maxBound)
616− prop (desc <> " take 0") $ transform (take 0) t (S.take 0)
617−
618− prop (desc <> " takeWhile True") $
619− transform (takeWhile (const True)) t (S.takeWhile (const True))
620− prop (desc <> " takeWhile False") $
621− transform (takeWhile (const False)) t (S.takeWhile (const False))
622−
623− prop (desc <> " takeWhileM True") $
624− transform (takeWhile (const True)) t (S.takeWhileM (const $ return True))
625− prop (desc <> " takeWhileM False") $
626− transform (takeWhile (const False)) t (S.takeWhileM (const $ return False))
627−
628− prop (desc <> " drop maxBound") $
629− transform (drop maxBound) t (S.drop maxBound)
630− prop (desc <> " drop 0") $ transform (drop 0) t (S.drop 0)
631−
632− prop (desc <> " dropWhile True") $
633− transform (dropWhile (const True)) t (S.dropWhile (const True))
634− prop (desc <> " dropWhile False") $
635− transform (dropWhile (const False)) t (S.dropWhile (const False))
636−
637− prop (desc <> " dropWhileM True") $
638− transform (dropWhile (const True)) t (S.dropWhileM (const $ return True))
639− prop (desc <> " dropWhileM False") $
640− transform (dropWhile (const False)) t (S.dropWhileM (const $ return False))
641−
642− prop (desc <> " deleteBy (<=) maxBound") $
643− transform (deleteBy (<=) maxBound) t (S.deleteBy (<=) maxBound)
644− prop (desc <> " deleteBy (==) 4") $
645− transform (deleteBy (==) 4) t (S.deleteBy (==) 4)
646−
647− -- transformation
648− prop (desc <> " mapM (+1)") $
649− transform (fmap (+1)) t (S.mapM (\x -> return (x + 1)))
650−
651− prop (desc <> " scanl'") $ transform (scanl' (flip const) 0) t
652− (S.scanl' (flip const) 0)
653− prop (desc <> " scanlM'") $ transform (scanl' (flip const) 0) t
654− (S.scanlM' (\_ a -> return a) 0)
655− prop (desc <> " scanl") $ transform (scanl' (flip const) 0) t
656− (S.scanl' (flip const) 0)
657− prop (desc <> " scanl1'") $ transform (scanl1 (flip const)) t
658− (S.scanl1' (flip const))
659− prop (desc <> " scanl1M'") $ transform (scanl1 (flip const)) t
660− (S.scanl1M' (\_ a -> return a))
661−
662− let f x = if odd x then Just (x + 100) else Nothing
663− prop (desc <> " mapMaybe") $ transform (mapMaybe f) t (S.mapMaybe f)
664−
665− -- tap
666− prop (desc <> " tap FL.sum . map (+1)") $ \a b ->
667− withMaxSuccess maxTestCount $
668− monadicIO $ do
669− cref <- run $ newIORef 0
670− let sumfoldinref = FL.Fold (\_ e -> modifyIORef' cref (e+))
671− (return ())
672− (const $ return ())
673− op = S.tap sumfoldinref . S.mapM (\x -> return (x+1))
674− listOp = fmap (+1)
675− stream <- run ((S.toList . t) $ op (constr a <> constr b))
676− let list = listOp (a <> b)
677− ssum <- run $ readIORef cref
678− assert (sum list == ssum)
679− listEquals eq stream list
680−
681− -- reordering
682− prop (desc <> " reverse") $ transform reverse t S.reverse
683− -- prop (desc <> " reverse'") $ transform reverse t S.reverse'
684−
685− -- inserting
686− prop (desc <> " intersperseM") $
687− forAll (choose (minBound, maxBound)) $ \n ->
688− transform (intersperse n) t (S.intersperseM $ return n)
689− prop (desc <> " insertBy 0") $
690− forAll (choose (minBound, maxBound)) $ \n ->
691− transform (insertBy compare n) t (S.insertBy compare n)
692−
693− -- multi-stream
694− prop (desc <> " concatMap") $
695− forAll (choose (0, 100)) $ \n ->
696− transform (concatMap (const [1..n]))
697− t (S.concatMap (const (S.fromList [1..n])))
698−
699−toListFL :: Monad m => FL.Fold m a [a]
700−toListFL = FL.toList
701−
702−-- transformation tests that can only work reliably for ordered streams i.e.
703−-- Serial, Ahead and Zip. For example if we use "take 1" on an async stream, it
704−-- might yield a different result every time.
705−transformCombineOpsOrdered
706− :: (IsStream t, Semigroup (t IO Int))
707− => ([Int] -> t IO Int)
708− -> String
709− -> ([Int] -> [Int] -> Bool)
710− -> (t IO Int -> SerialT IO Int)
711− -> Spec
712−transformCombineOpsOrdered constr desc eq t = do
713− let transform = transformCombineFromList constr eq
714−
715− -- Filtering
716− prop (desc <> " take 1") $ transform (take 1) t (S.take 1)
717−#ifdef DEVBUILD
718− prop (desc <> " take 2") $ transform (take 2) t (S.take 2)
719− prop (desc <> " take 3") $ transform (take 3) t (S.take 3)
720− prop (desc <> " take 4") $ transform (take 4) t (S.take 4)
721− prop (desc <> " take 5") $ transform (take 5) t (S.take 5)
722−#endif
723− prop (desc <> " take 10") $ transform (take 10) t (S.take 10)
724−
725− prop (desc <> " takeWhile > 0") $
726− transform (takeWhile (> 0)) t (S.takeWhile (> 0))
727−
728− prop (desc <> " drop 1") $ transform (drop 1) t (S.drop 1)
729− prop (desc <> " drop 10") $ transform (drop 10) t (S.drop 10)
730−
731− prop (desc <> " dropWhile > 0") $
732− transform (dropWhile (> 0)) t (S.dropWhile (> 0))
733− prop (desc <> " scan") $ transform (scanl' (+) 0) t (S.scanl' (+) 0)
734−
735− prop (desc <> " uniq") $ transform referenceUniq t S.uniq
736−
737− prop (desc <> " deleteBy (<=) 0") $
738− transform (deleteBy (<=) 0) t (S.deleteBy (<=) 0)
739−
740− prop (desc <> " findIndices") $
741− transform (findIndices odd) t (S.findIndices odd)
742− prop (desc <> " findIndices . filter") $
743− transform (findIndices odd . filter odd)
744− t
745− (S.findIndices odd . S.filter odd)
746− prop (desc <> " elemIndices") $
747− transform (elemIndices 0) t (S.elemIndices 0)
748−
749− -- XXX this does not fail when the SVar is shared, need to fix.
750− prop (desc <> " concurrent application") $
751− transform (& fmap (+1)) t (|& S.map (+1))
752−
753−-------------------------------------------------------------------------------
754−-- Monad operations
755−-------------------------------------------------------------------------------
756−
757−monadThen
758− :: Monad (t IO)
759− => ([Int] -> t IO Int)
760− -> ([Int] -> [Int] -> Bool)
761− -> (t IO Int -> SerialT IO Int)
762− -> ([Int], [Int])
763− -> Property
764−monadThen constr eq t (a, b) = withMaxSuccess maxTestCount $ monadicIO $ do
765− stream <- run ((S.toList . t) (constr a >> constr b))
766− let list = a >> b
767− listEquals eq stream list
768−
769−monadBind
770− :: Monad (t IO)
771− => ([Int] -> t IO Int)
772− -> ([Int] -> [Int] -> Bool)
773− -> (t IO Int -> SerialT IO Int)
774− -> ([Int], [Int])
775− -> Property
776−monadBind constr eq t (a, b) = withMaxSuccess maxTestCount $
777− monadicIO $ do
778− stream <-
779− run
780− ((S.toList . t)
781− (constr a >>= \x -> (+ x) <$> constr b))
782− let list = a >>= \x -> (+ x) <$> b
783− listEquals eq stream list
784−
785−-------------------------------------------------------------------------------
786−-- Zip operations
787−-------------------------------------------------------------------------------
788−
789−zipApplicative
790− :: (IsStream t, Applicative (t IO))
791− => ([Int] -> t IO Int)
792− -> ([(Int, Int)] -> [(Int, Int)] -> Bool)
793− -> (t IO (Int, Int) -> SerialT IO (Int, Int))
794− -> ([Int], [Int])
795− -> Property
796−zipApplicative constr eq t (a, b) = withMaxSuccess maxTestCount $
797− monadicIO $ do
798− stream1 <- run ((S.toList . t) ((,) <$> constr a <*> constr b))
799− stream2 <- run ((S.toList . t) (pure (,) <*> constr a <*> constr b))
800− stream3 <- run ((S.toList . t) (S.zipWith (,) (constr a) (constr b)))
801− let list = getZipList $ (,) <$> ZipList a <*> ZipList b
802− listEquals eq stream1 list
803− listEquals eq stream2 list
804− listEquals eq stream3 list
805−
806−zipMonadic
807− :: IsStream t
808− => ([Int] -> t IO Int)
809− -> ([(Int, Int)] -> [(Int, Int)] -> Bool)
810− -> (t IO (Int, Int) -> SerialT IO (Int, Int))
811− -> ([Int], [Int])
812− -> Property
813−zipMonadic constr eq t (a, b) = withMaxSuccess maxTestCount $
814− monadicIO $ do
815− stream1 <-
816− run
817− ((S.toList . t)
818− (S.zipWithM (curry return) (constr a) (constr b)))
819− let list = getZipList $ (,) <$> ZipList a <*> ZipList b
820− listEquals eq stream1 list
821−
822−zipAsyncMonadic
823− :: IsStream t
824− => ([Int] -> t IO Int)
825− -> ([(Int, Int)] -> [(Int, Int)] -> Bool)
826− -> (t IO (Int, Int) -> SerialT IO (Int, Int))
827− -> ([Int], [Int])
828− -> Property
829−zipAsyncMonadic constr eq t (a, b) = withMaxSuccess maxTestCount $
830− monadicIO $ do
831− stream1 <-
832− run
833− ((S.toList . t)
834− (S.zipWithM (curry return) (constr a) (constr b)))
835− stream2 <-
836− run
837− ((S.toList . t)
838− (S.zipAsyncWithM (curry return) (constr a) (constr b)))
839− let list = getZipList $ (,) <$> ZipList a <*> ZipList b
840− listEquals eq stream1 list
841− listEquals eq stream2 list
842−
843−-------------------------------------------------------------------------------
844−-- Helper operations
845−-------------------------------------------------------------------------------
846−
847−folded :: IsStream t => [a] -> t IO a
848−folded =
849− serially .
850− (\xs ->
851− case xs of
852− [x] -> return x -- singleton stream case
853− _ -> S.concatMapFoldableWith (<>) return xs)
854−
855−makeCommonOps :: IsStream t => (t m a -> c) -> [(String, t m a -> c)]
856−makeCommonOps t =
857− [ ("default", t)
858−#ifndef COVERAGE_BUILD
859− , ("rate AvgRate 10000", t . avgRate 10000)
860− , ("rate Nothing", t . rate Nothing)
861− , ("maxBuffer 0", t . maxBuffer 0)
862− , ("maxThreads 0", t . maxThreads 0)
863− , ("maxThreads 1", t . maxThreads 1)
864− , ("maxThreads -1", t . maxThreads (-1))
865−#endif
866− ]
867−
868−makeOps :: IsStream t => (t m a -> c) -> [(String, t m a -> c)]
869−makeOps t = makeCommonOps t ++
870− [
871−#ifndef COVERAGE_BUILD
872− ("maxBuffer 1", t . maxBuffer 1)
873−#endif
874− ]
91+ composeMixed :: SerialT IO Int
92+ composeMixed = do
93+ S.yieldM $ return ()
94+ S.yieldM $ putStr ""
95+ let x = 1
96+ let y = 2
97+ z <- do
98+ x1 <- S.wAsyncly $ return 1 <> return 2
99+ S.yieldM $ return ()
100+ S.yieldM $ putStr ""
101+ y1 <- S.asyncly $ return 1 <> return 2
102+ z1 <- do
103+ x11 <- return 1 <> return 2
104+ y11 <- S.asyncly $ return 1 <> return 2
105+ z11 <- S.wSerially $ return 1 <> return 2
106+ S.yieldM $ return ()
107+ S.yieldM $ putStr ""
108+ return (x11 + y11 + z11)
109+ return (x1 + y1 + z1)
110+ return (x + y + z)
111+
112+mixedOpsAheadly :: Spec
113+mixedOpsAheadly =
114+ it "Compose many ops" $
115+ (sort <$> toListSerial composeMixed)
116+ `shouldReturn` ([8,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,11,11
117+ ,11,11,11,11,11,11,11,11,12,12,12,12,12,13
118+ ] :: [Int])
119+ where
875120
876−mapOps :: (a -> Spec) -> [(String, a)] -> Spec
877−mapOps spec = mapM_ (\(desc, f) -> describe desc $ spec f)
121+ composeMixed :: SerialT IO Int
122+ composeMixed = do
123+ S.yieldM $ return ()
124+ S.yieldM $ putStr ""
125+ let x = 1
126+ let y = 2
127+ z <- do
128+ x1 <- S.wAsyncly $ return 1 <> return 2
129+ S.yieldM $ return ()
130+ S.yieldM $ putStr ""
131+ y1 <- S.aheadly $ return 1 <> return 2
132+ z1 <- do
133+ x11 <- return 1 <> return 2
134+ y11 <- S.aheadly $ return 1 <> return 2
135+ z11 <- S.parallely $ return 1 <> return 2
136+ S.yieldM $ return ()
137+ S.yieldM $ putStr ""
138+ return (x11 + y11 + z11)
139+ return (x1 + y1 + z1)
140+ return (x + y + z)
test/Streamly/Test/Prelude/Ahead.hs+24−5
@@ -17,11 +17,11 @@ import Test.Hspec.QuickCheck
1717import Test.QuickCheck.Monadic (monadicIO, run)
1818import Test.Hspec as H
1919
20−import Streamly.Prelude
20+import Streamly.Prelude (IsStream, SerialT, AheadT)
2121import qualified Streamly.Prelude as S
2222
2323import Streamly.Test.Common
24−import Streamly.Test.Prelude
24+import Streamly.Test.Prelude.Common
2525
2626associativityCheck
2727 :: String
@@ -36,7 +36,7 @@ associativityCheck desc t = prop desc assocCheckProp
3636 yStream = S.fromList ys
3737 zStream = S.fromList zs
3838 infixAssocstream <-
39− run $ S.toList $ t $ xStream `ahead` yStream `ahead` zStream
39+ run $ S.toList $ t $ xStream `S.ahead` yStream `S.ahead` zStream
4040 assocStream <- run $ S.toList $ t $ xStream <> yStream <> zStream
4141 listEquals (==) infixAssocstream assocStream
4242
@@ -48,9 +48,9 @@ main = hspec
4848#endif
4949 $ do
5050 let aheadOps :: IsStream t => ((AheadT IO a -> t IO a) -> Spec) -> Spec
51− aheadOps spec = mapOps spec $ makeOps aheadly
51+ aheadOps spec = mapOps spec $ makeOps S.aheadly
5252#ifndef COVERAGE_BUILD
53− <> [("maxBuffer (-1)", aheadly . maxBuffer (-1))]
53+ <> [("maxBuffer (-1)", S.aheadly . S.maxBuffer (-1))]
5454#endif
5555
5656 describe "Construction" $ do
@@ -63,6 +63,20 @@ main = hspec
6363 describe "Monoid operations" $ do
6464 aheadOps $ monoidOps "aheadly" mempty (==)
6565
66+ describe "Ahead loops" $ loops S.aheadly id reverse
67+
68+ describe "Bind and Monoidal composition combinations" $ do
69+ aheadOps $ bindAndComposeSimpleOps "Ahead" sortEq
70+ aheadOps $ bindAndComposeHierarchyOps "Ahead"
71+ aheadOps $ nestTwoStreams "Ahead" id id
72+ aheadOps $ nestTwoStreamsApp "Ahead" id id
73+ composeAndComposeSimpleSerially "Ahead <> " (repeat [1..9]) S.aheadly
74+ composeAndComposeSimpleAheadly "Ahead <> " (repeat [1 .. 9]) S.aheadly
75+ composeAndComposeSimpleWSerially
76+ "Ahead <> "
77+ [[1..9], [1..9], [1,3,2,4,6,5,7,9,8], [1,3,2,4,6,5,7,9,8]]
78+ S.aheadly
79+
6680 describe "Semigroup operations" $ do
6781 aheadOps $ semigroupOps "aheadly" (==)
6882 aheadOps $ associativityCheck "ahead == <>"
@@ -107,3 +121,8 @@ main = hspec
107121 describe "Stream serial elimination operations" $ do
108122 aheadOps $ eliminationOpsOrdered S.fromFoldable "aheadly"
109123 aheadOps $ eliminationOpsOrdered folded "aheadly folded"
124+
125+ describe "Composed MonadThrow aheadly" $ composeWithMonadThrow S.aheadly
126+
127+ -- Ad-hoc tests
128+ it "takes n from stream of streams" $ takeCombined 1 S.aheadly
test/Streamly/Test/Prelude/Async.hs+22−1
@@ -9,6 +9,7 @@
99
1010module Streamly.Test.Prelude.Async where
1111
12+import Data.List (sort)
1213#if __GLASGOW_HASKELL__ < 808
1314import Data.Semigroup ((<>))
1415#endif
@@ -18,7 +19,7 @@ import Test.Hspec as H
1819import Streamly.Prelude
1920import qualified Streamly.Prelude as S
2021
21−import Streamly.Test.Prelude
22+import Streamly.Test.Prelude.Common
2223
2324main :: IO ()
2425main = hspec
@@ -43,6 +44,14 @@ main = hspec
4344 describe "Monoid operations" $ do
4445 asyncOps $ monoidOps "asyncly" mempty sortEq
4546
47+ describe "Async loops" $ loops asyncly sort sort
48+
49+ describe "Bind and Monoidal composition combinations" $ do
50+ asyncOps $ bindAndComposeSimpleOps "Async" sortEq
51+ asyncOps $ bindAndComposeHierarchyOps "Async"
52+ asyncOps $ nestTwoStreams "Async" sort sort
53+ asyncOps $ nestTwoStreamsApp "Async" sort sort
54+
4655 describe "Semigroup operations" $ do
4756 asyncOps $ semigroupOps "asyncly" sortEq
4857
@@ -78,3 +87,15 @@ main = hspec
7887 asyncOps $ eliminationOps folded "asyncly folded"
7988 asyncOps $ eliminationOpsWord8 S.fromFoldable "asyncly"
8089 asyncOps $ eliminationOpsWord8 folded "asyncly folded"
90+
91+ -- test both (<>) and mappend to make sure we are using correct instance
92+ -- for Monoid that is using the right version of semigroup. Instance
93+ -- deriving can cause us to pick wrong instances sometimes.
94+
95+ describe "Async (<>) time order check" $ parallelCheck asyncly (<>)
96+ describe "Async mappend time order check" $ parallelCheck asyncly mappend
97+
98+ describe "Composed MonadThrow asyncly" $ composeWithMonadThrow asyncly
99+
100+ -- Ad-hoc tests
101+ it "takes n from stream of streams" $ takeCombined 2 asyncly
test/Streamly/Test/Prelude/Common.hsnew file+1259
@@ -0,0 +1,1259 @@
1+-- |
2+-- Module : Streamly.Test.Prelude.Common
3+-- Copyright : (c) 2020 Composewell Technologies
4+--
5+-- License : BSD-3-Clause
6+-- Maintainer : streamly@composewell.com
7+-- Stability : experimental
8+-- Portability : GHC
9+
10+module Streamly.Test.Prelude.Common
11+ (
12+ -- * Construction operations
13+ constructWithReplicate
14+ , constructWithReplicateM
15+ , constructWithIntFromThenTo
16+#if __GLASGOW_HASKELL__ >= 806
17+ , constructWithDoubleFromThenTo
18+#endif
19+ , constructWithIterate
20+ , constructWithIterateM
21+ , constructWithFromIndices
22+ , constructWithFromIndicesM
23+ -- * Applicative operations
24+ , applicativeOps
25+ , applicativeOps1
26+ -- * Elimination operations
27+ , eliminationOpsOrdered
28+ , eliminationOpsWord8
29+ , eliminationOps
30+ -- * Functor operations
31+ , functorOps
32+ -- * Monoid operations
33+ , monoidOps
34+ , loops
35+ , bindAndComposeSimpleOps
36+ , bindAndComposeHierarchyOps
37+ , nestTwoStreams
38+ , nestTwoStreamsApp
39+ , composeAndComposeSimpleSerially
40+ , composeAndComposeSimpleAheadly
41+ , composeAndComposeSimpleWSerially
42+ -- * Semigroup operations
43+ , semigroupOps
44+ , parallelCheck
45+ -- * Transformation operations
46+ , transformCombineOpsOrdered
47+ , transformCombineOpsCommon
48+ , toListFL
49+ -- * Monad operations
50+ , monadBind
51+ , monadThen
52+ -- * Zip operations
53+ , zipApplicative
54+ , zipMonadic
55+ , zipAsyncMonadic
56+ -- * MonadThrow operations
57+ , composeWithMonadThrow
58+ -- * Cleanup tests
59+ , checkCleanup
60+ -- * Adhoc tests
61+ , takeCombined
62+ -- * Default values
63+ , maxTestCount
64+ , maxStreamLen
65+ -- * Helper operations
66+ , folded
67+ , makeCommonOps
68+ , makeOps
69+ , mapOps
70+ , sortEq
71+ ) where
72+
73+import Control.Applicative (ZipList(..), liftA2)
74+import Control.Exception (Exception, try)
75+import Control.Concurrent (threadDelay)
76+import Control.Monad (replicateM)
77+import Control.Monad.Catch (throwM, MonadThrow)
78+import Data.Function ((&))
79+import Data.IORef (IORef, modifyIORef', newIORef, readIORef, writeIORef)
80+import Data.List
81+ ( deleteBy
82+ , elemIndex
83+ , elemIndices
84+ , find
85+ , findIndex
86+ , findIndices
87+ , foldl'
88+ , foldl1'
89+ , insertBy
90+ , intersperse
91+ , isPrefixOf
92+ , isSubsequenceOf
93+ , maximumBy
94+ , minimumBy
95+ , scanl'
96+ , sort
97+ , stripPrefix
98+ )
99+import Data.Maybe (mapMaybe)
100+#if !(MIN_VERSION_base(4,11,0))
101+import Data.Semigroup (Semigroup, (<>))
102+#endif
103+import GHC.Word (Word8)
104+import System.Mem (performMajorGC)
105+import Test.Hspec.QuickCheck
106+import Test.Hspec
107+import Test.QuickCheck (Property, choose, forAll, withMaxSuccess)
108+import Test.QuickCheck.Monadic (assert, monadicIO, run)
109+
110+import Streamly.Prelude
111+ ( SerialT, IsStream, (.:), nil, (|&), serially, avgRate, rate, maxBuffer
112+ , maxThreads, maxBuffer)
113+import qualified Streamly.Prelude as S
114+import qualified Streamly.Data.Fold as FL
115+import qualified Streamly.Internal.Data.Fold as FL
116+
117+import Streamly.Test.Common
118+
119+maxStreamLen :: Int
120+maxStreamLen = 1000
121+
122+-- Coverage build takes too long with default number of tests
123+maxTestCount :: Int
124+#ifdef DEVBUILD
125+maxTestCount = 100
126+#else
127+maxTestCount = 10
128+#endif
129+
130+singleton :: IsStream t => a -> t m a
131+singleton a = a .: nil
132+
133+sortEq :: Ord a => [a] -> [a] -> Bool
134+sortEq a b = sort a == sort b
135+
136+-------------------------------------------------------------------------------
137+-- Construction operations
138+-------------------------------------------------------------------------------
139+
140+constructWithLen
141+ :: (Show a, Eq a)
142+ => (Int -> t IO a)
143+ -> (Int -> [a])
144+ -> (t IO a -> SerialT IO a)
145+ -> Word8
146+ -> Property
147+constructWithLen mkStream mkList op len = withMaxSuccess maxTestCount $
148+ monadicIO $ do
149+ stream <- run $ (S.toList . op) (mkStream (fromIntegral len))
150+ let list = mkList (fromIntegral len)
151+ listEquals (==) stream list
152+
153+constructWithLenM
154+ :: (Int -> t IO Int)
155+ -> (Int -> IO [Int])
156+ -> (t IO Int -> SerialT IO Int)
157+ -> Word8
158+ -> Property
159+constructWithLenM mkStream mkList op len = withMaxSuccess maxTestCount $
160+ monadicIO $ do
161+ stream <- run $ (S.toList . op) (mkStream (fromIntegral len))
162+ list <- run $ mkList (fromIntegral len)
163+ listEquals (==) stream list
164+
165+constructWithReplicate, constructWithReplicateM, constructWithIntFromThenTo
166+ :: IsStream t
167+ => (t IO Int -> SerialT IO Int)
168+ -> Word8
169+ -> Property
170+
171+constructWithReplicateM = constructWithLenM stream list
172+ where list = flip replicateM (return 1 :: IO Int)
173+ stream = flip S.replicateM (return 1 :: IO Int)
174+
175+constructWithReplicate = constructWithLen stream list
176+ where list = flip replicate (1 :: Int)
177+ stream = flip S.replicate (1 :: Int)
178+
179+constructWithIntFromThenTo op l =
180+ forAll (choose (minBound, maxBound)) $ \from ->
181+ forAll (choose (minBound, maxBound)) $ \next ->
182+ forAll (choose (minBound, maxBound)) $ \to ->
183+ let list len = take len [from,next..to]
184+ stream len = S.take len $ S.enumerateFromThenTo from next to
185+ in constructWithLen stream list op l
186+
187+#if __GLASGOW_HASKELL__ >= 806
188+-- XXX try very small steps close to 0
189+constructWithDoubleFromThenTo
190+ :: IsStream t
191+ => (t IO Double -> SerialT IO Double)
192+ -> Word8
193+ -> Property
194+constructWithDoubleFromThenTo op l =
195+ forAll (choose (-9007199254740999,9007199254740999)) $ \from ->
196+ forAll (choose (-9007199254740999,9007199254740999)) $ \next ->
197+ forAll (choose (-9007199254740999,9007199254740999)) $ \to ->
198+ let list len = take len [from,next..to]
199+ stream len = S.take len $ S.enumerateFromThenTo from next to
200+ in constructWithLen stream list op l
201+#endif
202+
203+constructWithIterate ::
204+ IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
205+constructWithIterate op len =
206+ withMaxSuccess maxTestCount $
207+ monadicIO $ do
208+ stream <-
209+ run $
210+ (S.toList . op . S.take (fromIntegral len))
211+ (S.iterate (+ 1) (0 :: Int))
212+ let list = take (fromIntegral len) (iterate (+ 1) 0)
213+ listEquals (==) stream list
214+
215+constructWithIterateM ::
216+ IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
217+constructWithIterateM op len =
218+ withMaxSuccess maxTestCount $
219+ monadicIO $ do
220+ mvl <- run (newIORef [] :: IO (IORef [Int]))
221+ let addM mv x y = modifyIORef' mv (++ [y + x]) >> return (y + x)
222+ list = take (fromIntegral len) (iterate (+ 1) 0)
223+ run $
224+ S.drain . op $
225+ S.take (fromIntegral len) $
226+ S.iterateM (addM mvl 1) (addM mvl 0 0 :: IO Int)
227+ streamEffect <- run $ readIORef mvl
228+ listEquals (==) streamEffect list
229+
230+constructWithFromIndices ::
231+ IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
232+constructWithFromIndices op len =
233+ withMaxSuccess maxTestCount $
234+ monadicIO $ do
235+ stream <-
236+ run $ (S.toList . op . S.take (fromIntegral len)) (S.fromIndices id)
237+ let list = take (fromIntegral len) (iterate (+ 1) 0)
238+ listEquals (==) stream list
239+
240+constructWithFromIndicesM ::
241+ IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
242+constructWithFromIndicesM op len =
243+ withMaxSuccess maxTestCount $
244+ monadicIO $ do
245+ mvl <- run (newIORef [] :: IO (IORef [Int]))
246+ let addIndex mv i = modifyIORef' mv (++ [i]) >> return i
247+ list = take (fromIntegral len) (iterate (+ 1) 0)
248+ run $
249+ S.drain . op $
250+ S.take (fromIntegral len) $ S.fromIndicesM (addIndex mvl)
251+ streamEffect <- run $ readIORef mvl
252+ listEquals (==) streamEffect list
253+
254+-------------------------------------------------------------------------------
255+-- Applicative operations
256+-------------------------------------------------------------------------------
257+
258+applicativeOps
259+ :: (Applicative (t IO), Semigroup (t IO Int))
260+ => ([Int] -> t IO Int)
261+ -> String
262+ -> ([(Int, Int)] -> [(Int, Int)] -> Bool)
263+ -> (t IO (Int, Int) -> SerialT IO (Int, Int))
264+ -> Spec
265+applicativeOps constr desc eq t = do
266+ prop (desc <> " <*>") $
267+ transformFromList2
268+ constr
269+ eq
270+ (\a b -> (,) <$> a <*> b)
271+ (\a b -> t ((,) <$> a <*> b))
272+ prop (desc <> " liftA2") $
273+ transformFromList2 constr eq (liftA2 (,)) (\a b -> t $ liftA2 (,) a b)
274+ prop (desc <> " Apply - composed first argument") $
275+ sort <$>
276+ (S.toList . t) ((,) <$> (pure 1 <> pure 2) <*> pure 3) `shouldReturn`
277+ [(1, 3), (2, 3)]
278+ prop (desc <> " Apply - composed second argument") $
279+ sort <$>
280+ (S.toList . t) ((,) <$> pure 1 <*> (pure 2 <> pure 3)) `shouldReturn`
281+ [(1, 2), (1, 3)]
282+
283+-- XXX we can combine this with applicativeOps by making the type sufficiently
284+-- polymorphic.
285+applicativeOps1
286+ :: Applicative (t IO)
287+ => ([Int] -> t IO Int)
288+ -> String
289+ -> ([Int] -> [Int] -> Bool)
290+ -> (t IO Int -> SerialT IO Int)
291+ -> Spec
292+applicativeOps1 constr desc eq t = do
293+ prop (desc <> " *>") $
294+ transformFromList2 constr eq (*>) (\a b -> t (a *> b))
295+ prop (desc <> " <*") $
296+ transformFromList2 constr eq (<*) (\a b -> t (a <* b))
297+
298+transformFromList2
299+ :: (Eq c, Show c)
300+ => ([a] -> t IO a)
301+ -> ([c] -> [c] -> Bool)
302+ -> ([a] -> [a] -> [c])
303+ -> (t IO a -> t IO a -> SerialT IO c)
304+ -> ([a], [a])
305+ -> Property
306+transformFromList2 constr eq listOp op (a, b) =
307+ withMaxSuccess maxTestCount $
308+ monadicIO $ do
309+ stream <- run (S.toList $ op (constr a) (constr b))
310+ let list = listOp a b
311+ listEquals eq stream list
312+
313+-------------------------------------------------------------------------------
314+-- Elimination operations
315+-------------------------------------------------------------------------------
316+
317+eliminateOp
318+ :: (Show a, Eq a)
319+ => ([s] -> t IO s)
320+ -> ([s] -> a)
321+ -> (t IO s -> IO a)
322+ -> [s]
323+ -> Property
324+eliminateOp constr listOp op a =
325+ monadicIO $ do
326+ stream <- run $ op (constr a)
327+ let list = listOp a
328+ equals (==) stream list
329+
330+wrapMaybe :: ([a1] -> a2) -> [a1] -> Maybe a2
331+wrapMaybe f x = if null x then Nothing else Just (f x)
332+
333+wrapOutOfBounds :: ([a1] -> Int -> a2) -> Int -> [a1] -> Maybe a2
334+wrapOutOfBounds f i x | null x = Nothing
335+ | i >= length x = Nothing
336+ | otherwise = Just (f x i)
337+
338+wrapThe :: Eq a => [a] -> Maybe a
339+wrapThe (x:xs)
340+ | all (x ==) xs = Just x
341+ | otherwise = Nothing
342+wrapThe [] = Nothing
343+
344+-- This is the reference uniq implementation to compare uniq against,
345+-- we can use uniq from vector package, but for now this should
346+-- suffice.
347+referenceUniq :: Eq a => [a] -> [a]
348+referenceUniq = go
349+ where
350+ go [] = []
351+ go (x:[]) = [x]
352+ go (x:y:xs)
353+ | x == y = go (x : xs)
354+ | otherwise = x : go (y : xs)
355+
356+eliminationOps
357+ :: ([Int] -> t IO Int)
358+ -> String
359+ -> (t IO Int -> SerialT IO Int)
360+ -> Spec
361+eliminationOps constr desc t = do
362+ -- Elimination
363+ prop (desc <> " null") $ eliminateOp constr null $ S.null . t
364+ prop (desc <> " foldl'") $
365+ eliminateOp constr (foldl' (+) 0) $ S.foldl' (+) 0 . t
366+ prop (desc <> " foldl1'") $
367+ eliminateOp constr (wrapMaybe $ foldl1' (+)) $ S.foldl1' (+) . t
368+#ifdef DEVBUILD
369+ prop (desc <> " foldr1") $
370+ eliminateOp constr (wrapMaybe $ foldr1 (+)) $ S.foldr1 (+) . t
371+#endif
372+ prop (desc <> " all") $ eliminateOp constr (all even) $ S.all even . t
373+ prop (desc <> " any") $ eliminateOp constr (any even) $ S.any even . t
374+ prop (desc <> " and") $ eliminateOp constr (and . fmap (> 0)) $
375+ (S.and . S.map (> 0)) . t
376+ prop (desc <> " or") $ eliminateOp constr (or . fmap (> 0)) $
377+ (S.or . S.map (> 0)) . t
378+ prop (desc <> " length") $ eliminateOp constr length $ S.length . t
379+ prop (desc <> " sum") $ eliminateOp constr sum $ S.sum . t
380+ prop (desc <> " product") $ eliminateOp constr product $ S.product . t
381+
382+ prop (desc <> " maximum") $
383+ eliminateOp constr (wrapMaybe maximum) $ S.maximum . t
384+ prop (desc <> " minimum") $
385+ eliminateOp constr (wrapMaybe minimum) $ S.minimum . t
386+
387+ prop (desc <> " maximumBy compare") $
388+ eliminateOp constr (wrapMaybe $ maximumBy compare) $
389+ S.maximumBy compare . t
390+ prop (desc <> " maximumBy flip compare") $
391+ eliminateOp constr (wrapMaybe $ maximumBy $ flip compare) $
392+ S.maximumBy (flip compare) . t
393+ prop (desc <> " minimumBy compare") $
394+ eliminateOp constr (wrapMaybe $ minimumBy compare) $
395+ S.minimumBy compare . t
396+ prop (desc <> " minimumBy flip compare") $
397+ eliminateOp constr (wrapMaybe $ minimumBy $ flip compare) $
398+ S.minimumBy (flip compare) . t
399+
400+ prop (desc <> " findIndex") $
401+ eliminateOp constr (findIndex odd) $ S.findIndex odd . t
402+ prop (desc <> " elemIndex") $
403+ eliminateOp constr (elemIndex 3) $ S.elemIndex 3 . t
404+
405+ prop (desc <> " !! 5") $
406+ eliminateOp constr (wrapOutOfBounds (!!) 5) $ (S.!! 5) . t
407+ prop (desc <> " !! 4") $
408+ eliminateOp constr (wrapOutOfBounds (!!) 0) $ (S.!! 0) . t
409+
410+ prop (desc <> " find") $ eliminateOp constr (find even) $ S.find even . t
411+ prop (desc <> " lookup") $
412+ eliminateOp constr (lookup 3 . flip zip [1..]) $
413+ S.lookup 3 . S.zipWith (\a b -> (b, a)) (S.fromList [(1::Int)..]) . t
414+ prop (desc <> " the") $ eliminateOp constr wrapThe $ S.the . t
415+
416+ -- Multi-stream eliminations
417+ -- Add eqBy, cmpBy
418+ -- XXX Write better tests for substreams.
419+ prop (desc <> " isPrefixOf 10") $ eliminateOp constr (isPrefixOf [1..10]) $
420+ S.isPrefixOf (S.fromList [(1::Int)..10]) . t
421+ prop (desc <> " isSubsequenceOf 10") $
422+ eliminateOp constr (isSubsequenceOf $ filter even [1..10]) $
423+ S.isSubsequenceOf (S.fromList $ filter even [(1::Int)..10]) . t
424+ prop (desc <> " stripPrefix 10") $ eliminateOp constr (stripPrefix [1..10]) $
425+ (\s -> s >>= maybe (return Nothing) (fmap Just . S.toList)) .
426+ S.stripPrefix (S.fromList [(1::Int)..10]) . t
427+
428+-- head/tail/last may depend on the order in case of parallel streams
429+-- so we test these only for serial streams.
430+eliminationOpsOrdered
431+ :: ([Int] -> t IO Int)
432+ -> String
433+ -> (t IO Int -> SerialT IO Int)
434+ -> Spec
435+eliminationOpsOrdered constr desc t = do
436+ prop (desc <> " head") $ eliminateOp constr (wrapMaybe head) $ S.head . t
437+ prop (desc <> " tail") $ eliminateOp constr (wrapMaybe tail) $ \x -> do
438+ r <- S.tail (t x)
439+ case r of
440+ Nothing -> return Nothing
441+ Just s -> Just <$> S.toList s
442+ prop (desc <> " last") $ eliminateOp constr (wrapMaybe last) $ S.last . t
443+ prop (desc <> " init") $ eliminateOp constr (wrapMaybe init) $ \x -> do
444+ r <- S.init (t x)
445+ case r of
446+ Nothing -> return Nothing
447+ Just s -> Just <$> S.toList s
448+
449+elemOp
450+ :: ([Word8] -> t IO Word8)
451+ -> (t IO Word8 -> SerialT IO Word8)
452+ -> (Word8 -> SerialT IO Word8 -> IO Bool)
453+ -> (Word8 -> [Word8] -> Bool)
454+ -> (Word8, [Word8])
455+ -> Property
456+elemOp constr op streamOp listOp (x, xs) =
457+ monadicIO $ do
458+ stream <- run $ (streamOp x . op) (constr xs)
459+ let list = listOp x xs
460+ equals (==) stream list
461+
462+eliminationOpsWord8
463+ :: ([Word8] -> t IO Word8)
464+ -> String
465+ -> (t IO Word8 -> SerialT IO Word8)
466+ -> Spec
467+eliminationOpsWord8 constr desc t = do
468+ prop (desc <> " elem") $ elemOp constr t S.elem elem
469+ prop (desc <> " notElem") $ elemOp constr t S.notElem notElem
470+
471+-------------------------------------------------------------------------------
472+-- Functor operations
473+-------------------------------------------------------------------------------
474+
475+functorOps
476+ :: (Functor (t IO), Semigroup (t IO Int))
477+ => ([Int] -> t IO Int)
478+ -> String
479+ -> ([Int] -> [Int] -> Bool)
480+ -> (t IO Int -> SerialT IO Int)
481+ -> Spec
482+functorOps constr desc eq t = do
483+ prop (desc <> " id") $ transformFromList constr eq id t
484+ prop (desc <> " fmap (+1)") $
485+ transformFromList constr eq (fmap (+ 1)) $ t . fmap (+ 1)
486+ prop (desc <> " fmap on composed (<>)") $
487+ sort <$>
488+ (S.toList . t) (fmap (+ 1) (constr [1] <> constr [2])) `shouldReturn`
489+ ([2, 3] :: [Int])
490+
491+transformFromList
492+ :: (Eq b, Show b) =>
493+ ([a] -> t IO a)
494+ -> ([b] -> [b] -> Bool)
495+ -> ([a] -> [b])
496+ -> (t IO a -> SerialT IO b)
497+ -> [a]
498+ -> Property
499+transformFromList constr eq listOp op a =
500+ monadicIO $ do
501+ stream <- run ((S.toList . op) (constr a))
502+ let list = listOp a
503+ listEquals eq stream list
504+
505+
506+------------------------------------------------------------------------------
507+-- Monoid operations
508+------------------------------------------------------------------------------
509+
510+monoidOps
511+ :: (IsStream t, Semigroup (t IO Int))
512+ => String
513+ -> t IO Int
514+ -> ([Int] -> [Int] -> Bool)
515+ -> (t IO Int -> SerialT IO Int)
516+ -> Spec
517+monoidOps desc z eq t = do
518+ -- XXX these should get covered by the property tests
519+ prop (desc <> " Compose mempty, mempty") $ spec (z <> z) []
520+ prop (desc <> " Compose empty at the beginning") $ spec (z <> singleton 1) [1]
521+ prop (desc <> " Compose empty at the end") $ spec (singleton 1 <> z) [1]
522+ prop (desc <> " Compose two") $ spec (singleton 0 <> singleton 1) [0, 1]
523+ prop (desc <> " Compose many") $
524+ spec (S.concatForFoldableWith (<>) [1 .. 100] singleton) [1 .. 100]
525+
526+ -- These are not covered by the property tests
527+ prop (desc <> " Compose three - empty in the middle") $
528+ spec (singleton 0 <> z <> singleton 1) [0, 1]
529+ prop (desc <> " Compose left associated") $
530+ spec
531+ (((singleton 0 <> singleton 1) <> singleton 2) <> singleton 3)
532+ [0, 1, 2, 3]
533+ prop (desc <> " Compose right associated") $
534+ spec
535+ (singleton 0 <> (singleton 1 <> (singleton 2 <> singleton 3)))
536+ [0, 1, 2, 3]
537+ prop (desc <> " Compose hierarchical (multiple levels)") $
538+ spec
539+ (((singleton 0 <> singleton 1) <> (singleton 2 <> singleton 3)) <>
540+ ((singleton 4 <> singleton 5) <> (singleton 6 <> singleton 7)))
541+ [0 .. 7]
542+
543+ where
544+
545+ tl = S.toList . t
546+ spec s list =
547+ monadicIO $ do
548+ stream <- run $ tl s
549+ listEquals eq stream list
550+
551+---------------------------------------------------------------------------
552+-- Monoidal composition recursion loops
553+---------------------------------------------------------------------------
554+
555+loops
556+ :: (IsStream t, Semigroup (t IO Int), Monad (t IO))
557+ => (t IO Int -> t IO Int)
558+ -> ([Int] -> [Int])
559+ -> ([Int] -> [Int])
560+ -> Spec
561+loops t tsrt hsrt = do
562+ it "Tail recursive loop" $ (tsrt <$> (S.toList . S.adapt) (loopTail 0))
563+ `shouldReturn` [0..3]
564+
565+ it "Head recursive loop" $ (hsrt <$> (S.toList . S.adapt) (loopHead 0))
566+ `shouldReturn` [0..3]
567+
568+ where
569+ loopHead x = do
570+ -- this print line is important for the test (causes a bind)
571+ S.yieldM $ putStrLn "LoopHead..."
572+ t $ (if x < 3 then loopHead (x + 1) else nil) <> return x
573+
574+ loopTail x = do
575+ -- this print line is important for the test (causes a bind)
576+ S.yieldM $ putStrLn "LoopTail..."
577+ t $ return x <> (if x < 3 then loopTail (x + 1) else nil)
578+
579+---------------------------------------------------------------------------
580+-- Bind and monoidal composition combinations
581+---------------------------------------------------------------------------
582+
583+bindAndComposeSimpleOps
584+ :: IsStream t
585+ => String
586+ -> ([Int] -> [Int] -> Bool)
587+ -> (t IO Int -> SerialT IO Int)
588+ -> Spec
589+bindAndComposeSimpleOps desc eq t = do
590+ bindAndComposeSimple
591+ ("Bind and compose " <> desc <> " Stream serially/")
592+ S.serially
593+ bindAndComposeSimple
594+ ("Bind and compose " <> desc <> " Stream wSerially/")
595+ S.wSerially
596+ bindAndComposeSimple
597+ ("Bind and compose " <> desc <> " Stream aheadly/")
598+ S.aheadly
599+ bindAndComposeSimple
600+ ("Bind and compose " <> desc <> " Stream asyncly/")
601+ S.asyncly
602+ bindAndComposeSimple
603+ ("Bind and compose " <> desc <> " Stream wAsyncly/")
604+ S.wAsyncly
605+ bindAndComposeSimple
606+ ("Bind and compose " <> desc <> " Stream parallely/")
607+ S.parallely
608+
609+ where
610+
611+ bindAndComposeSimple
612+ :: (IsStream t2, Semigroup (t2 IO Int), Monad (t2 IO))
613+ => String
614+ -> (t2 IO Int -> t2 IO Int)
615+ -> Spec
616+ bindAndComposeSimple idesc t2 = do
617+ -- XXX need a bind in the body of forEachWith instead of a simple return
618+ prop (idesc <> " Compose many (right fold) with bind") $ \list ->
619+ monadicIO $ do
620+ stream <-
621+ run $
622+ (S.toList . t)
623+ (S.adapt . t2 $ S.concatForFoldableWith (<>) list return)
624+ listEquals eq stream list
625+
626+ prop (idesc <> " Compose many (left fold) with bind") $ \list ->
627+ monadicIO $ do
628+ let forL xs k = foldl (<>) nil $ fmap k xs
629+ stream <-
630+ run $ (S.toList . t) (S.adapt . t2 $ forL list return)
631+ listEquals eq stream list
632+
633+---------------------------------------------------------------------------
634+-- Bind and monoidal composition combinations
635+---------------------------------------------------------------------------
636+
637+bindAndComposeHierarchyOps ::
638+ (IsStream t, Monad (t IO))
639+ => String
640+ -> (t IO Int -> SerialT IO Int)
641+ -> Spec
642+bindAndComposeHierarchyOps desc t1 = do
643+ let fldldesc = "Bind and compose foldl, " <> desc <> " Stream "
644+ fldrdesc = "Bind and compose foldr, " <> desc <> " Stream "
645+
646+ bindAndComposeHierarchy
647+ (fldldesc <> "serially") S.serially fldl
648+ bindAndComposeHierarchy
649+ (fldrdesc <> "serially") S.serially fldr
650+ bindAndComposeHierarchy
651+ (fldldesc <> "wSerially") S.wSerially fldl
652+ bindAndComposeHierarchy
653+ (fldrdesc <> "wSerially") S.wSerially fldr
654+ bindAndComposeHierarchy
655+ (fldldesc <> "aheadly") S.aheadly fldl
656+ bindAndComposeHierarchy
657+ (fldrdesc <> "aheadly") S.aheadly fldr
658+ bindAndComposeHierarchy
659+ (fldldesc <> "asyncly") S.asyncly fldl
660+ bindAndComposeHierarchy
661+ (fldrdesc <> "asyncly") S.asyncly fldr
662+ bindAndComposeHierarchy
663+ (fldldesc <> "wAsyncly") S.wAsyncly fldl
664+ bindAndComposeHierarchy
665+ (fldrdesc <> "wAsyncly") S.wAsyncly fldr
666+ bindAndComposeHierarchy
667+ (fldldesc <> "parallely") S.parallely fldl
668+ bindAndComposeHierarchy
669+ (fldrdesc <> "parallely") S.parallely fldr
670+
671+ where
672+
673+ bindAndComposeHierarchy
674+ :: (IsStream t2, Monad (t2 IO))
675+ => String
676+ -> (t2 IO Int -> t2 IO Int)
677+ -> ([t2 IO Int] -> t2 IO Int)
678+ -> Spec
679+ bindAndComposeHierarchy specdesc t2 g =
680+ describe specdesc $
681+ it "Bind and compose nested" $
682+ (sort <$> (S.toList . t1) bindComposeNested)
683+ `shouldReturn` (sort (
684+ [12, 18]
685+ <> replicate 3 13
686+ <> replicate 3 17
687+ <> replicate 6 14
688+ <> replicate 6 16
689+ <> replicate 7 15) :: [Int])
690+
691+ where
692+
693+ -- bindComposeNested :: WAsyncT IO Int
694+ bindComposeNested =
695+ let c1 = tripleCompose (return 1) (return 2) (return 3)
696+ c2 = tripleCompose (return 4) (return 5) (return 6)
697+ c3 = tripleCompose (return 7) (return 8) (return 9)
698+ b = tripleBind c1 c2 c3
699+ -- it seems to be causing a huge space leak in hspec so disabling this for now
700+ -- c = tripleCompose b b b
701+ -- m = tripleBind c c c
702+ -- in m
703+ in b
704+
705+ tripleCompose a b c = S.adapt . t2 $ g [a, b, c]
706+ tripleBind mx my mz =
707+ mx >>= \x -> my
708+ >>= \y -> mz
709+ >>= \z -> return (x + y + z)
710+
711+ fldr, fldl :: (IsStream t, Semigroup (t IO Int))
712+ => [t IO Int] -> t IO Int
713+ fldr = foldr (<>) nil
714+ fldl = foldl (<>) nil
715+
716+-- Nest two lists using different styles of product compositions
717+nestTwoStreams
718+ :: (IsStream t, Semigroup (t IO Int), Monad (t IO))
719+ => String
720+ -> ([Int] -> [Int])
721+ -> ([Int] -> [Int])
722+ -> (t IO Int -> SerialT IO Int)
723+ -> Spec
724+nestTwoStreams desc streamListT listT t =
725+ it ("Nests two streams using monadic " <> desc <> " composition") $ do
726+ let s1 = S.concatMapFoldableWith (<>) return [1..4]
727+ s2 = S.concatMapFoldableWith (<>) return [5..8]
728+ r <- (S.toList . t) $ do
729+ x <- s1
730+ y <- s2
731+ return $ x + y
732+ streamListT r `shouldBe` listT [6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12]
733+
734+nestTwoStreamsApp
735+ :: (IsStream t, Semigroup (t IO Int), Monad (t IO))
736+ => String
737+ -> ([Int] -> [Int])
738+ -> ([Int] -> [Int])
739+ -> (t IO Int -> SerialT IO Int)
740+ -> Spec
741+nestTwoStreamsApp desc streamListT listT t =
742+ it ("Nests two streams using applicative " <> desc <> " composition") $ do
743+ let s1 = S.concatMapFoldableWith (<>) return [1..4]
744+ s2 = S.concatMapFoldableWith (<>) return [5..8]
745+ r = (S.toList . t) $ ((+) <$> s1 <*> s2)
746+ streamListT <$> r
747+ `shouldReturn` listT [6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12]
748+
749+
750+-- TBD need more such combinations to be tested.
751+composeAndComposeSimple
752+ :: ( IsStream t1, Semigroup (t1 IO Int)
753+ , IsStream t2, Monoid (t2 IO Int), Monad (t2 IO)
754+#if !(MIN_VERSION_base(4,11,0))
755+ , Semigroup (t2 IO Int)
756+#endif
757+ )
758+ => (t1 IO Int -> SerialT IO Int)
759+ -> (t2 IO Int -> t2 IO Int)
760+ -> [[Int]] -> Spec
761+composeAndComposeSimple t1 t2 answer = do
762+ let rfold = S.adapt . t2 . S.concatMapFoldableWith (<>) return
763+ it "Compose right associated outer expr, right folded inner" $
764+ (S.toList . t1) (rfold [1,2,3] <> (rfold [4,5,6] <> rfold [7,8,9]))
765+ `shouldReturn` head answer
766+
767+ it "Compose left associated outer expr, right folded inner" $
768+ (S.toList . t1) ((rfold [1,2,3] <> rfold [4,5,6]) <> rfold [7,8,9])
769+ `shouldReturn` (answer !! 1)
770+
771+ let lfold xs = S.adapt $ t2 $ foldl (<>) mempty $ fmap return xs
772+ it "Compose right associated outer expr, left folded inner" $
773+ (S.toList . t1) (lfold [1,2,3] <> (lfold [4,5,6] <> lfold [7,8,9]))
774+ `shouldReturn` (answer !! 2)
775+
776+ it "Compose left associated outer expr, left folded inner" $
777+ (S.toList . t1) ((lfold [1,2,3] <> lfold [4,5,6]) <> lfold [7,8,9])
778+ `shouldReturn` (answer !! 3)
779+
780+composeAndComposeSimpleSerially
781+ :: (IsStream t, Semigroup (t IO Int))
782+ => String
783+ -> [[Int]]
784+ -> (t IO Int -> SerialT IO Int)
785+ -> Spec
786+composeAndComposeSimpleSerially desc answer t = do
787+ describe (desc <> " and Serial <>") $ composeAndComposeSimple t S.serially answer
788+
789+composeAndComposeSimpleAheadly
790+ :: (IsStream t, Semigroup (t IO Int))
791+ => String
792+ -> [[Int]]
793+ -> (t IO Int -> SerialT IO Int)
794+ -> Spec
795+composeAndComposeSimpleAheadly desc answer t = do
796+ describe (desc <> " and Ahead <>") $ composeAndComposeSimple t S.aheadly answer
797+
798+composeAndComposeSimpleWSerially
799+ :: (IsStream t, Semigroup (t IO Int))
800+ => String
801+ -> [[Int]]
802+ -> (t IO Int -> SerialT IO Int)
803+ -> Spec
804+composeAndComposeSimpleWSerially desc answer t = do
805+ describe (desc <> " and WSerial <>") $ composeAndComposeSimple t S.wSerially answer
806+
807+-------------------------------------------------------------------------------
808+-- Semigroup operations
809+-------------------------------------------------------------------------------
810+
811+foldFromList
812+ :: ([Int] -> t IO Int)
813+ -> (t IO Int -> SerialT IO Int)
814+ -> ([Int] -> [Int] -> Bool)
815+ -> [Int]
816+ -> Property
817+foldFromList constr op eq = transformFromList constr eq id op
818+
819+-- XXX concatenate streams of multiple elements rather than single elements
820+semigroupOps
821+ :: (IsStream t
822+#if __GLASGOW_HASKELL__ < 804
823+ , Semigroup (t IO Int)
824+#endif
825+ , Monoid (t IO Int))
826+ => String
827+ -> ([Int] -> [Int] -> Bool)
828+ -> (t IO Int -> SerialT IO Int)
829+ -> Spec
830+semigroupOps desc eq t = do
831+ prop (desc <> " <>") $ foldFromList (S.concatMapFoldableWith (<>) singleton) t eq
832+ prop (desc <> " mappend") $ foldFromList (S.concatMapFoldableWith mappend singleton) t eq
833+
834+-------------------------------------------------------------------------------
835+-- Transformation operations
836+-------------------------------------------------------------------------------
837+
838+transformCombineFromList
839+ :: Semigroup (t IO Int)
840+ => ([Int] -> t IO Int)
841+ -> ([Int] -> [Int] -> Bool)
842+ -> ([Int] -> [Int])
843+ -> (t IO Int -> SerialT IO Int)
844+ -> (t IO Int -> t IO Int)
845+ -> [Int]
846+ -> [Int]
847+ -> [Int]
848+ -> Property
849+transformCombineFromList constr eq listOp t op a b c =
850+ withMaxSuccess maxTestCount $
851+ monadicIO $ do
852+ stream <- run ((S.toList . t) $
853+ constr a <> op (constr b <> constr c))
854+ let list = a <> listOp (b <> c)
855+ listEquals eq stream list
856+
857+-- XXX add tests for MonadReader and MonadError etc. In case an SVar is
858+-- accidentally passed through them.
859+--
860+-- This tests transform ops along with detecting illegal sharing of SVar across
861+-- conurrent streams. These tests work for all stream types whereas
862+-- transformCombineOpsOrdered work only for ordered stream types i.e. excluding
863+-- the Async type.
864+transformCombineOpsCommon
865+ :: (IsStream t, Semigroup (t IO Int))
866+ => ([Int] -> t IO Int)
867+ -> String
868+ -> ([Int] -> [Int] -> Bool)
869+ -> (t IO Int -> SerialT IO Int)
870+ -> Spec
871+transformCombineOpsCommon constr desc eq t = do
872+ let transform = transformCombineFromList constr eq
873+
874+ -- Filtering
875+ prop (desc <> " filter False") $
876+ transform (filter (const False)) t (S.filter (const False))
877+ prop (desc <> " filter True") $
878+ transform (filter (const True)) t (S.filter (const True))
879+ prop (desc <> " filter even") $
880+ transform (filter even) t (S.filter even)
881+
882+ prop (desc <> " filterM False") $
883+ transform (filter (const False)) t (S.filterM (const $ return False))
884+ prop (desc <> " filterM True") $
885+ transform (filter (const True)) t (S.filterM (const $ return True))
886+ prop (desc <> " filterM even") $
887+ transform (filter even) t (S.filterM (return . even))
888+
889+ prop (desc <> " take maxBound") $
890+ transform (take maxBound) t (S.take maxBound)
891+ prop (desc <> " take 0") $ transform (take 0) t (S.take 0)
892+
893+ prop (desc <> " takeWhile True") $
894+ transform (takeWhile (const True)) t (S.takeWhile (const True))
895+ prop (desc <> " takeWhile False") $
896+ transform (takeWhile (const False)) t (S.takeWhile (const False))
897+
898+ prop (desc <> " takeWhileM True") $
899+ transform (takeWhile (const True)) t (S.takeWhileM (const $ return True))
900+ prop (desc <> " takeWhileM False") $
901+ transform (takeWhile (const False)) t (S.takeWhileM (const $ return False))
902+
903+ prop (desc <> " drop maxBound") $
904+ transform (drop maxBound) t (S.drop maxBound)
905+ prop (desc <> " drop 0") $ transform (drop 0) t (S.drop 0)
906+
907+ prop (desc <> " dropWhile True") $
908+ transform (dropWhile (const True)) t (S.dropWhile (const True))
909+ prop (desc <> " dropWhile False") $
910+ transform (dropWhile (const False)) t (S.dropWhile (const False))
911+
912+ prop (desc <> " dropWhileM True") $
913+ transform (dropWhile (const True)) t (S.dropWhileM (const $ return True))
914+ prop (desc <> " dropWhileM False") $
915+ transform (dropWhile (const False)) t (S.dropWhileM (const $ return False))
916+
917+ prop (desc <> " deleteBy (<=) maxBound") $
918+ transform (deleteBy (<=) maxBound) t (S.deleteBy (<=) maxBound)
919+ prop (desc <> " deleteBy (==) 4") $
920+ transform (deleteBy (==) 4) t (S.deleteBy (==) 4)
921+
922+ -- transformation
923+ prop (desc <> " mapM (+1)") $
924+ transform (fmap (+1)) t (S.mapM (\x -> return (x + 1)))
925+
926+ prop (desc <> " scanl'") $ transform (scanl' (flip const) 0) t
927+ (S.scanl' (flip const) 0)
928+ prop (desc <> " scanlM'") $ transform (scanl' (flip const) 0) t
929+ (S.scanlM' (\_ a -> return a) 0)
930+ prop (desc <> " scanl") $ transform (scanl' (flip const) 0) t
931+ (S.scanl' (flip const) 0)
932+ prop (desc <> " scanl1'") $ transform (scanl1 (flip const)) t
933+ (S.scanl1' (flip const))
934+ prop (desc <> " scanl1M'") $ transform (scanl1 (flip const)) t
935+ (S.scanl1M' (\_ a -> return a))
936+
937+ let f x = if odd x then Just (x + 100) else Nothing
938+ prop (desc <> " mapMaybe") $ transform (mapMaybe f) t (S.mapMaybe f)
939+
940+ -- tap
941+ prop (desc <> " tap FL.sum . map (+1)") $ \a b ->
942+ withMaxSuccess maxTestCount $
943+ monadicIO $ do
944+ cref <- run $ newIORef 0
945+ let sumfoldinref = FL.Fold (\_ e -> modifyIORef' cref (e+))
946+ (return ())
947+ (const $ return ())
948+ op = S.tap sumfoldinref . S.mapM (\x -> return (x+1))
949+ listOp = fmap (+1)
950+ stream <- run ((S.toList . t) $ op (constr a <> constr b))
951+ let list = listOp (a <> b)
952+ ssum <- run $ readIORef cref
953+ assert (sum list == ssum)
954+ listEquals eq stream list
955+
956+ -- reordering
957+ prop (desc <> " reverse") $ transform reverse t S.reverse
958+ -- prop (desc <> " reverse'") $ transform reverse t S.reverse'
959+
960+ -- inserting
961+ prop (desc <> " intersperseM") $
962+ forAll (choose (minBound, maxBound)) $ \n ->
963+ transform (intersperse n) t (S.intersperseM $ return n)
964+ prop (desc <> " insertBy 0") $
965+ forAll (choose (minBound, maxBound)) $ \n ->
966+ transform (insertBy compare n) t (S.insertBy compare n)
967+
968+ -- multi-stream
969+ prop (desc <> " concatMap") $
970+ forAll (choose (0, 100)) $ \n ->
971+ transform (concatMap (const [1..n]))
972+ t (S.concatMap (const (S.fromList [1..n])))
973+
974+toListFL :: Monad m => FL.Fold m a [a]
975+toListFL = FL.toList
976+
977+-- transformation tests that can only work reliably for ordered streams i.e.
978+-- Serial, Ahead and Zip. For example if we use "take 1" on an async stream, it
979+-- might yield a different result every time.
980+transformCombineOpsOrdered
981+ :: (IsStream t, Semigroup (t IO Int))
982+ => ([Int] -> t IO Int)
983+ -> String
984+ -> ([Int] -> [Int] -> Bool)
985+ -> (t IO Int -> SerialT IO Int)
986+ -> Spec
987+transformCombineOpsOrdered constr desc eq t = do
988+ let transform = transformCombineFromList constr eq
989+
990+ -- Filtering
991+ prop (desc <> " take 1") $ transform (take 1) t (S.take 1)
992+#ifdef DEVBUILD
993+ prop (desc <> " take 2") $ transform (take 2) t (S.take 2)
994+ prop (desc <> " take 3") $ transform (take 3) t (S.take 3)
995+ prop (desc <> " take 4") $ transform (take 4) t (S.take 4)
996+ prop (desc <> " take 5") $ transform (take 5) t (S.take 5)
997+#endif
998+ prop (desc <> " take 10") $ transform (take 10) t (S.take 10)
999+
1000+ prop (desc <> " takeWhile > 0") $
1001+ transform (takeWhile (> 0)) t (S.takeWhile (> 0))
1002+
1003+ prop (desc <> " drop 1") $ transform (drop 1) t (S.drop 1)
1004+ prop (desc <> " drop 10") $ transform (drop 10) t (S.drop 10)
1005+
1006+ prop (desc <> " dropWhile > 0") $
1007+ transform (dropWhile (> 0)) t (S.dropWhile (> 0))
1008+ prop (desc <> " scan") $ transform (scanl' (+) 0) t (S.scanl' (+) 0)
1009+
1010+ prop (desc <> " uniq") $ transform referenceUniq t S.uniq
1011+
1012+ prop (desc <> " deleteBy (<=) 0") $
1013+ transform (deleteBy (<=) 0) t (S.deleteBy (<=) 0)
1014+
1015+ prop (desc <> " findIndices") $
1016+ transform (findIndices odd) t (S.findIndices odd)
1017+ prop (desc <> " findIndices . filter") $
1018+ transform (findIndices odd . filter odd)
1019+ t
1020+ (S.findIndices odd . S.filter odd)
1021+ prop (desc <> " elemIndices") $
1022+ transform (elemIndices 0) t (S.elemIndices 0)
1023+
1024+ -- XXX this does not fail when the SVar is shared, need to fix.
1025+ prop (desc <> " concurrent application") $
1026+ transform (& fmap (+1)) t (|& S.map (+1))
1027+
1028+-------------------------------------------------------------------------------
1029+-- Monad operations
1030+-------------------------------------------------------------------------------
1031+
1032+monadThen
1033+ :: Monad (t IO)
1034+ => ([Int] -> t IO Int)
1035+ -> ([Int] -> [Int] -> Bool)
1036+ -> (t IO Int -> SerialT IO Int)
1037+ -> ([Int], [Int])
1038+ -> Property
1039+monadThen constr eq t (a, b) = withMaxSuccess maxTestCount $ monadicIO $ do
1040+ stream <- run ((S.toList . t) (constr a >> constr b))
1041+ let list = a >> b
1042+ listEquals eq stream list
1043+
1044+monadBind
1045+ :: Monad (t IO)
1046+ => ([Int] -> t IO Int)
1047+ -> ([Int] -> [Int] -> Bool)
1048+ -> (t IO Int -> SerialT IO Int)
1049+ -> ([Int], [Int])
1050+ -> Property
1051+monadBind constr eq t (a, b) = withMaxSuccess maxTestCount $
1052+ monadicIO $ do
1053+ stream <-
1054+ run
1055+ ((S.toList . t)
1056+ (constr a >>= \x -> (+ x) <$> constr b))
1057+ let list = a >>= \x -> (+ x) <$> b
1058+ listEquals eq stream list
1059+
1060+-------------------------------------------------------------------------------
1061+-- Zip operations
1062+-------------------------------------------------------------------------------
1063+
1064+zipApplicative
1065+ :: (IsStream t, Applicative (t IO))
1066+ => ([Int] -> t IO Int)
1067+ -> ([(Int, Int)] -> [(Int, Int)] -> Bool)
1068+ -> (t IO (Int, Int) -> SerialT IO (Int, Int))
1069+ -> ([Int], [Int])
1070+ -> Property
1071+zipApplicative constr eq t (a, b) = withMaxSuccess maxTestCount $
1072+ monadicIO $ do
1073+ stream1 <- run ((S.toList . t) ((,) <$> constr a <*> constr b))
1074+ stream2 <- run ((S.toList . t) (pure (,) <*> constr a <*> constr b))
1075+ stream3 <- run ((S.toList . t) (S.zipWith (,) (constr a) (constr b)))
1076+ let list = getZipList $ (,) <$> ZipList a <*> ZipList b
1077+ listEquals eq stream1 list
1078+ listEquals eq stream2 list
1079+ listEquals eq stream3 list
1080+
1081+zipMonadic
1082+ :: IsStream t
1083+ => ([Int] -> t IO Int)
1084+ -> ([(Int, Int)] -> [(Int, Int)] -> Bool)
1085+ -> (t IO (Int, Int) -> SerialT IO (Int, Int))
1086+ -> ([Int], [Int])
1087+ -> Property
1088+zipMonadic constr eq t (a, b) = withMaxSuccess maxTestCount $
1089+ monadicIO $ do
1090+ stream1 <-
1091+ run
1092+ ((S.toList . t)
1093+ (S.zipWithM (curry return) (constr a) (constr b)))
1094+ let list = getZipList $ (,) <$> ZipList a <*> ZipList b
1095+ listEquals eq stream1 list
1096+
1097+zipAsyncMonadic
1098+ :: IsStream t
1099+ => ([Int] -> t IO Int)
1100+ -> ([(Int, Int)] -> [(Int, Int)] -> Bool)
1101+ -> (t IO (Int, Int) -> SerialT IO (Int, Int))
1102+ -> ([Int], [Int])
1103+ -> Property
1104+zipAsyncMonadic constr eq t (a, b) = withMaxSuccess maxTestCount $
1105+ monadicIO $ do
1106+ stream1 <-
1107+ run
1108+ ((S.toList . t)
1109+ (S.zipWithM (curry return) (constr a) (constr b)))
1110+ stream2 <-
1111+ run
1112+ ((S.toList . t)
1113+ (S.zipAsyncWithM (curry return) (constr a) (constr b)))
1114+ let list = getZipList $ (,) <$> ZipList a <*> ZipList b
1115+ listEquals eq stream1 list
1116+ listEquals eq stream2 list
1117+
1118+---------------------------------------------------------------------------
1119+-- Semigroup/Monoidal Composition strict ordering checks
1120+---------------------------------------------------------------------------
1121+
1122+parallelCheck :: (IsStream t, Monad (t IO))
1123+ => (t IO Int -> SerialT IO Int)
1124+ -> (t IO Int -> t IO Int -> t IO Int)
1125+ -> Spec
1126+parallelCheck t f = do
1127+ it "Parallel ordering left associated" $
1128+ (S.toList . t) (((event 4 `f` event 3) `f` event 2) `f` event 1)
1129+ `shouldReturn` [1..4]
1130+
1131+ it "Parallel ordering right associated" $
1132+ (S.toList . t) (event 4 `f` (event 3 `f` (event 2 `f` event 1)))
1133+ `shouldReturn` [1..4]
1134+
1135+ where event n = S.yieldM (threadDelay (n * 200000)) >> return n
1136+
1137+-------------------------------------------------------------------------------
1138+-- Compose with MonadThrow
1139+-------------------------------------------------------------------------------
1140+
1141+newtype ExampleException = ExampleException String deriving (Eq, Show)
1142+
1143+instance Exception ExampleException
1144+
1145+composeWithMonadThrow
1146+ :: ( IsStream t
1147+ , Semigroup (t IO Int)
1148+ , MonadThrow (t IO)
1149+ )
1150+ => (t IO Int -> SerialT IO Int)
1151+ -> Spec
1152+composeWithMonadThrow t = do
1153+ it "Compose throwM, nil" $
1154+ try (tl (throwM (ExampleException "E") <> S.nil))
1155+ `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
1156+ it "Compose nil, throwM" $
1157+ try (tl (S.nil <> throwM (ExampleException "E")))
1158+ `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
1159+ oneLevelNestedSum "serially" S.serially
1160+ oneLevelNestedSum "wSerially" S.wSerially
1161+ oneLevelNestedSum "asyncly" S.asyncly
1162+ oneLevelNestedSum "wAsyncly" S.wAsyncly
1163+ -- XXX add two level nesting
1164+
1165+ oneLevelNestedProduct "serially" S.serially
1166+ oneLevelNestedProduct "wSerially" S.wSerially
1167+ oneLevelNestedProduct "asyncly" S.asyncly
1168+ oneLevelNestedProduct "wAsyncly" S.wAsyncly
1169+
1170+ where
1171+ tl = S.toList . t
1172+ oneLevelNestedSum desc t1 =
1173+ it ("One level nested sum " <> desc) $ do
1174+ let nested = S.fromFoldable [1..10] <> throwM (ExampleException "E")
1175+ <> S.fromFoldable [1..10]
1176+ try (tl (S.nil <> t1 nested <> S.fromFoldable [1..10]))
1177+ `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
1178+
1179+ oneLevelNestedProduct desc t1 =
1180+ it ("One level nested product" <> desc) $ do
1181+ let s1 = t $ S.concatMapFoldableWith (<>) return [1..4]
1182+ s2 = t1 $ S.concatMapFoldableWith (<>) return [5..8]
1183+ try $ tl (do
1184+ x <- S.adapt s1
1185+ y <- s2
1186+ if x + y > 10
1187+ then throwM (ExampleException "E")
1188+ else return (x + y)
1189+ )
1190+ `shouldReturn` (Left (ExampleException "E") :: Either ExampleException [Int])
1191+
1192+-------------------------------------------------------------------------------
1193+-- Cleanup tests
1194+-------------------------------------------------------------------------------
1195+
1196+checkCleanup :: IsStream t
1197+ => Int
1198+ -> (t IO Int -> SerialT IO Int)
1199+ -> (t IO Int -> t IO Int)
1200+ -> IO ()
1201+checkCleanup d t op = do
1202+ r <- newIORef (-1 :: Int)
1203+ S.drain . serially $ do
1204+ _ <- t $ op $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
1205+ return ()
1206+ performMajorGC
1207+ threadDelay 500000
1208+ res <- readIORef r
1209+ res `shouldBe` 0
1210+ where
1211+ delay ref i = threadDelay (i*d*100000) >> writeIORef ref i >> return i
1212+
1213+-------------------------------------------------------------------------------
1214+-- Some ad-hoc tests that failed at times
1215+-------------------------------------------------------------------------------
1216+
1217+takeCombined :: (Monad m, Semigroup (t m Int), Show a, Eq a, IsStream t)
1218+ => Int -> (t m Int -> SerialT IO a) -> IO ()
1219+takeCombined n t = do
1220+ let constr = S.fromFoldable
1221+ r <- (S.toList . t) $
1222+ S.take n (constr ([] :: [Int]) <> constr ([] :: [Int]))
1223+ r `shouldBe` []
1224+
1225+-------------------------------------------------------------------------------
1226+-- Helper operations
1227+-------------------------------------------------------------------------------
1228+
1229+folded :: IsStream t => [a] -> t IO a
1230+folded =
1231+ serially .
1232+ (\xs ->
1233+ case xs of
1234+ [x] -> return x -- singleton stream case
1235+ _ -> S.concatMapFoldableWith (<>) return xs)
1236+
1237+makeCommonOps :: IsStream t => (t m a -> c) -> [(String, t m a -> c)]
1238+makeCommonOps t =
1239+ [ ("default", t)
1240+#ifndef COVERAGE_BUILD
1241+ , ("rate AvgRate 10000", t . avgRate 10000)
1242+ , ("rate Nothing", t . rate Nothing)
1243+ , ("maxBuffer 0", t . maxBuffer 0)
1244+ , ("maxThreads 0", t . maxThreads 0)
1245+ , ("maxThreads 1", t . maxThreads 1)
1246+ , ("maxThreads -1", t . maxThreads (-1))
1247+#endif
1248+ ]
1249+
1250+makeOps :: IsStream t => (t m a -> c) -> [(String, t m a -> c)]
1251+makeOps t = makeCommonOps t ++
1252+ [
1253+#ifndef COVERAGE_BUILD
1254+ ("maxBuffer 1", t . maxBuffer 1)
1255+#endif
1256+ ]
1257+
1258+mapOps :: (a -> Spec) -> [(String, a)] -> Spec
1259+mapOps spec = mapM_ (\(desc, f) -> describe desc $ spec f)
test/Streamly/Test/Prelude/Concurrent.hs+184−6
@@ -11,14 +11,18 @@
1111
1212module Streamly.Test.Prelude.Concurrent where
1313
14−import Control.Concurrent (MVar, takeMVar, putMVar, newEmptyMVar)
14+import Control.Concurrent (MVar, takeMVar, threadDelay, putMVar, newEmptyMVar)
1515import Control.Exception
1616 (BlockedIndefinitelyOnMVar(..), catches,
1717 BlockedIndefinitelyOnSTM(..), Handler(..))
18−import Control.Monad (when, forM_, replicateM_)
18+import Control.Monad (void, when, forM_, replicateM_)
19+import Control.Monad.IO.Class (MonadIO(liftIO))
20+import Control.Monad.State (MonadState, get, modify, runStateT
21+ , StateT(..), evalStateT)
22+import Data.Foldable (fold)
1923import Data.IORef (readIORef, modifyIORef, newIORef)
20−#if __GLASGOW_HASKELL__ < 808
21−import Data.Semigroup ((<>))
24+#if !(MIN_VERSION_base(4,11,0))
25+import Data.Semigroup (Semigroup, (<>))
2226#endif
2327import GHC.Word (Word8)
2428import Test.Hspec.QuickCheck
@@ -27,11 +31,11 @@ import Test.QuickCheck
2731 (Property, withMaxSuccess)
2832import Test.QuickCheck.Monadic (monadicIO, run)
2933
30−import Streamly.Prelude hiding (replicateM, reverse)
34+import Streamly.Prelude hiding (fold, replicate, replicateM, reverse)
3135import qualified Streamly.Prelude as S
3236
3337import Streamly.Test.Common
34−import Streamly.Test.Prelude
38+import Streamly.Test.Prelude.Common
3539
3640-------------------------------------------------------------------------------
3741-- Concurrent generation
@@ -221,6 +225,104 @@ concurrentFoldrApplication n =
221225 (return [])
222226 listEquals (==) stream list
223227
228+-- Each snapshot carries an independent state. Multiple parallel tasks should
229+-- not affect each other's state. This is especially important when we run
230+-- multiple tasks in a single thread.
231+snapshot :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
232+snapshot =
233+ -- We deliberately use a replicate count 1 here, because a lower count
234+ -- catches problems that a higher count doesn't.
235+ S.replicateM 1 $ do
236+ -- Even though we modify the state here it should not reflect in other
237+ -- parallel tasks, it is local to each concurrent task.
238+ modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==1))
239+ modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
240+
241+snapshot1 :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
242+snapshot1 = S.replicateM 1000 $
243+ modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
244+
245+snapshot2 :: (IsStream t, MonadAsync m, MonadState Int m) => t m ()
246+snapshot2 = S.replicateM 1000 $
247+ modify (+1) >> get >>= liftIO . (`shouldSatisfy` (==2))
248+
249+stateComp
250+ :: ( IsStream t
251+ , MonadAsync m
252+ , Semigroup (t m ())
253+ , MonadIO (t m)
254+ , MonadState Int m
255+ , MonadState Int (t m)
256+ )
257+ => t m ()
258+stateComp = do
259+ -- Each task in a concurrent composition inherits the state and maintains
260+ -- its own modifications to it, not affecting the parent computation.
261+ snapshot <> (modify (+1) >> (snapshot1 <> snapshot2))
262+ -- The above modify statement does not affect our state because that is
263+ -- used in a parallel composition. In a serial composition it will affect
264+ -- our state.
265+ get >>= liftIO . (`shouldSatisfy` (== (0 :: Int)))
266+
267+monadicStateSnapshot
268+ :: ( IsStream t
269+ , Semigroup (t (StateT Int IO) ())
270+ , MonadIO (t (StateT Int IO))
271+ , MonadState Int (t (StateT Int IO))
272+ )
273+ => (t (StateT Int IO) () -> SerialT (StateT Int IO) ()) -> IO ()
274+monadicStateSnapshot t = void $ runStateT (S.drain $ t stateComp) 0
275+
276+stateCompOp
277+ :: ( AsyncT (StateT Int IO) ()
278+ -> AsyncT (StateT Int IO) ()
279+ -> AsyncT (StateT Int IO) ()
280+ )
281+ -> SerialT (StateT Int IO) ()
282+stateCompOp op = do
283+ -- Each task in a concurrent composition inherits the state and maintains
284+ -- its own modifications to it, not affecting the parent computation.
285+ asyncly (snapshot `op` (modify (+1) >> (snapshot1 `op` snapshot2)))
286+ -- The above modify statement does not affect our state because that is
287+ -- used in a parallel composition. In a serial composition it will affect
288+ -- our state.
289+ get >>= liftIO . (`shouldSatisfy` (== (0 :: Int)))
290+
291+checkMonadicStateTransfer
292+ :: (IsStream t1, IsStream t2)
293+ => ( t1 (StateT Int IO) ()
294+ -> t2 (StateT Int IO) ()
295+ -> SerialT (StateT Int IO) a3 )
296+ -> IO ()
297+checkMonadicStateTransfer op = evalStateT str (0 :: Int)
298+ where
299+ str =
300+ S.drain $
301+ maxBuffer 1 $
302+ (serially $ S.mapM snapshoti $ S.fromList [1..10]) `op`
303+ (serially $ S.mapM snapshoti $ S.fromList [1..10])
304+ snapshoti y = do
305+ modify (+ 1)
306+ x <- get
307+ lift1 $ x `shouldBe` y
308+ lift1 m = StateT $ \s -> do
309+ a <- m
310+ return (a, s)
311+
312+monadicStateSnapshotOp
313+ :: ( AsyncT (StateT Int IO) ()
314+ -> AsyncT (StateT Int IO) ()
315+ -> AsyncT (StateT Int IO) ()
316+ )
317+ -> IO ()
318+monadicStateSnapshotOp op = void $ runStateT (S.drain $ stateCompOp op) 0
319+
320+takeInfinite :: IsStream t => (t IO Int -> SerialT IO Int) -> Spec
321+takeInfinite t =
322+ it "take 1" $
323+ S.drain (t $ S.take 1 $ S.repeatM (print "hello" >> return (1::Int)))
324+ `shouldReturn` ()
325+
224326main :: IO ()
225327main = hspec
226328 $ H.parallel
@@ -308,3 +410,79 @@ main = hspec
308410 concurrentFoldrApplication
309411 prop "concurrent foldl application" $ withMaxSuccess maxTestCount
310412 concurrentFoldlApplication
413+
414+ describe "take on infinite concurrent stream" $ takeInfinite asyncly
415+ describe "take on infinite concurrent stream" $ takeInfinite wAsyncly
416+ describe "take on infinite concurrent stream" $ takeInfinite aheadly
417+
418+ ---------------------------------------------------------------------------
419+ -- Monadic state transfer in concurrent tasks
420+ ---------------------------------------------------------------------------
421+
422+ describe "Monadic state transfer in concurrent tasks" $ do
423+ -- XXX Can we write better test cases to hit every case?
424+ it "async: state is saved and used if the work is partially enqueued"
425+ (checkMonadicStateTransfer async)
426+ it "wAsync: state is saved and used if the work is partially enqueued"
427+ (checkMonadicStateTransfer wAsync)
428+ it "ahead: state is saved and used if the work is partially enqueued"
429+ (checkMonadicStateTransfer ahead)
430+
431+ ---------------------------------------------------------------------------
432+ -- Monadic state snapshot in concurrent tasks
433+ ---------------------------------------------------------------------------
434+
435+ describe "Monadic state snapshot in concurrent tasks" $ do
436+ it "asyncly maintains independent states in concurrent tasks"
437+ (monadicStateSnapshot asyncly)
438+ it "asyncly limited maintains independent states in concurrent tasks"
439+ (monadicStateSnapshot (asyncly . S.take 10000))
440+
441+ it "wAsyncly maintains independent states in concurrent tasks"
442+ (monadicStateSnapshot wAsyncly)
443+ it "wAsyncly limited maintains independent states in concurrent tasks"
444+ (monadicStateSnapshot (wAsyncly . S.take 10000))
445+
446+ it "aheadly maintains independent states in concurrent tasks"
447+ (monadicStateSnapshot aheadly)
448+ it "aheadly limited maintains independent states in concurrent tasks"
449+ (monadicStateSnapshot (aheadly . S.take 10000))
450+ it "parallely maintains independent states in concurrent tasks"
451+ (monadicStateSnapshot parallely)
452+
453+
454+ it "async maintains independent states in concurrent tasks"
455+ (monadicStateSnapshotOp async)
456+ it "ahead maintains independent states in concurrent tasks"
457+ (monadicStateSnapshotOp ahead)
458+ it "wAsync maintains independent states in concurrent tasks"
459+ (monadicStateSnapshotOp wAsync)
460+ it "parallel maintains independent states in concurrent tasks"
461+ (monadicStateSnapshotOp S.parallel)
462+
463+ ---------------------------------------------------------------------------
464+ -- Slower tests are at the end
465+ ---------------------------------------------------------------------------
466+
467+ ---------------------------------------------------------------------------
468+ -- Thread limits
469+ ---------------------------------------------------------------------------
470+
471+ it "asyncly crosses thread limit (2000 threads)" $
472+ S.drain (asyncly $ fold $
473+ replicate 2000 $ S.yieldM $ threadDelay 1000000)
474+ `shouldReturn` ()
475+
476+ it "aheadly crosses thread limit (4000 threads)" $
477+ S.drain (aheadly $ fold $
478+ replicate 4000 $ S.yieldM $ threadDelay 1000000)
479+ `shouldReturn` ()
480+
481+ describe "restricts concurrency and cleans up extra tasks" $ do
482+ it "take 1 asyncly" $ checkCleanup 2 asyncly (S.take 1)
483+ it "take 1 wAsyncly" $ checkCleanup 2 wAsyncly (S.take 1)
484+ it "take 1 aheadly" $ checkCleanup 2 aheadly (S.take 1)
485+
486+ it "takeWhile (< 0) asyncly" $ checkCleanup 2 asyncly (S.takeWhile (< 0))
487+ it "takeWhile (< 0) wAsyncly" $ checkCleanup 2 wAsyncly (S.takeWhile (< 0))
488+ it "takeWhile (< 0) aheadly" $ checkCleanup 2 aheadly (S.takeWhile (< 0))
test/Streamly/Test/Prelude/Fold.hsnew file+183
@@ -0,0 +1,183 @@
1+-- |
2+-- Module : Streamly.Test.Prelude.Fold
3+-- Copyright : (c) 2020 Composewell Technologies
4+--
5+-- License : BSD-3-Clause
6+-- Maintainer : streamly@composewell.com
7+-- Stability : experimental
8+-- Portability : GHC
9+
10+module Streamly.Test.Prelude.Fold where
11+
12+#ifdef DEVBUILD
13+import Control.Concurrent (threadDelay)
14+#endif
15+import Control.Exception (ErrorCall(..), catch)
16+import Data.IORef ( newIORef, readIORef, writeIORef, IORef )
17+import Test.Hspec as H
18+#ifdef DEVBUILD
19+import System.Mem (performMajorGC)
20+#endif
21+
22+import qualified Streamly.Internal.Data.Stream.IsStream as IS
23+import qualified Streamly.Prelude as S
24+
25+import Streamly.Prelude (SerialT)
26+#ifdef DEVBUILD
27+import Streamly.Prelude (IsStream)
28+#endif
29+
30+#ifdef DEVBUILD
31+checkFoldxStrictness :: IO ()
32+checkFoldxStrictness = do
33+ let s = return (1 :: Int) `S.consM` error "failure"
34+ catch (S.foldx (\_ a -> if a == 1 then error "success" else "done")
35+ "begin" id s)
36+ (\(ErrorCall err) -> return err)
37+ `shouldReturn` "success"
38+
39+checkScanxStrictness :: IO ()
40+checkScanxStrictness = do
41+ let s = return (1 :: Int) `S.consM` error "failure"
42+ catch
43+ (S.drain (
44+ S.scanx (\_ a ->
45+ if a == 1
46+ then error "success"
47+ else "done")
48+ "begin" id s
49+ )
50+ >> return "finished"
51+ )
52+ (\(ErrorCall err) -> return err)
53+ `shouldReturn` "success"
54+
55+foldxMStrictCheck :: IORef Int -> SerialT IO Int -> IO ()
56+foldxMStrictCheck ref = S.foldxM (\_ _ -> writeIORef ref 1) (return ()) return
57+
58+checkCleanupFold :: IsStream t
59+ => (t IO Int -> SerialT IO Int)
60+ -> (SerialT IO Int -> IO (Maybe Int))
61+ -> IO ()
62+checkCleanupFold t op = do
63+ r <- newIORef (-1 :: Int)
64+ _ <- op $ t $ delay r 0 S.|: delay r 2 S.|: delay r 3 S.|: S.nil
65+ performMajorGC
66+ threadDelay 700000
67+ res <- readIORef r
68+ res `shouldBe` 0
69+ where
70+ delay ref i = threadDelay (i*200000) >> writeIORef ref i >> return i
71+
72+testFoldOpsCleanup :: String -> (SerialT IO Int -> IO a) -> Spec
73+testFoldOpsCleanup name f = do
74+ let testOp op x = op x >> return Nothing
75+ it (name <> " asyncly") $ checkCleanupFold S.asyncly (testOp f)
76+ it (name <> " wAsyncly") $ checkCleanupFold S.wAsyncly (testOp f)
77+ it (name <> " aheadly") $ checkCleanupFold S.aheadly (testOp f)
78+ it (name <> " parallely") $ checkCleanupFold S.parallely (testOp f)
79+#endif
80+
81+checkFoldMStrictness :: (IORef Int -> SerialT IO Int -> IO ()) -> IO ()
82+checkFoldMStrictness f = do
83+ ref <- newIORef 0
84+ let s = return 1 `S.consM` error "x"
85+ catch (f ref s) (\(_ :: ErrorCall) -> return ())
86+ readIORef ref `shouldReturn` 1
87+
88+checkFoldl'Strictness :: IO ()
89+checkFoldl'Strictness = do
90+ let s = return (1 :: Int) `S.consM` error "failure"
91+ catch (S.foldl' (\_ a -> if a == 1 then error "success" else "done")
92+ "begin" s)
93+ (\(ErrorCall err) -> return err)
94+ `shouldReturn` "success"
95+
96+checkScanl'Strictness :: IO ()
97+checkScanl'Strictness = do
98+ let s = return (1 :: Int) `S.consM` error "failure"
99+ catch
100+ (S.drain
101+ (S.scanl'
102+ (\_ a ->
103+ if a == 1
104+ then error "success"
105+ else "done")
106+ "begin"
107+ s)
108+ >> return "finished"
109+ )
110+ (\(ErrorCall err) -> return err)
111+ `shouldReturn` "success"
112+
113+foldlM'StrictCheck :: IORef Int -> SerialT IO Int -> IO ()
114+foldlM'StrictCheck ref = S.foldlM' (\_ _ -> writeIORef ref 1) (return ())
115+
116+scanlM'StrictCheck :: IORef Int -> SerialT IO Int -> SerialT IO ()
117+scanlM'StrictCheck ref = S.scanlM' (\_ _ -> writeIORef ref 1) ()
118+
119+checkScanlMStrictness :: (IORef Int -> SerialT IO Int -> SerialT IO ()) -> IO ()
120+checkScanlMStrictness f = do
121+ ref <- newIORef 0
122+ let s = return 1 `S.consM` error "x"
123+ catch (S.drain $ f ref s) (\(_ :: ErrorCall) -> return ())
124+ readIORef ref `shouldReturn` 1
125+
126+checkFoldrLaziness :: IO ()
127+checkFoldrLaziness = do
128+ S.foldrM (\x xs -> if odd x then return True else xs)
129+ (return False) (S.fromList (2:4:5:undefined :: [Int]))
130+ `shouldReturn` True
131+
132+ S.toList (IS.foldrS (\x xs -> if odd x then return True else xs)
133+ (return False)
134+ $ (S.fromList (2:4:5:undefined) :: SerialT IO Int))
135+ `shouldReturn` [True]
136+
137+ S.toList (IS.foldrT (\x xs -> if odd x then return True else xs)
138+ (return False)
139+ $ (S.fromList (2:4:5:undefined) :: SerialT IO Int))
140+ `shouldReturn` [True]
141+
142+main :: IO ()
143+main = hspec
144+ $ H.parallel
145+#ifdef COVERAGE_BUILD
146+ $ modifyMaxSuccess (const 10)
147+#endif
148+ $ do
149+ ---------------------------------------------------------------------------
150+ -- Left folds are strict enough
151+ ---------------------------------------------------------------------------
152+
153+#ifdef DEVBUILD
154+ it "foldx is strict enough" checkFoldxStrictness
155+ it "scanx is strict enough" checkScanxStrictness
156+ it "foldxM is strict enough" (checkFoldMStrictness foldxMStrictCheck)
157+#endif
158+ it "foldl' is strict enough" checkFoldl'Strictness
159+ it "scanl' is strict enough" checkScanl'Strictness
160+ it "foldlM' is strict enough" (checkFoldMStrictness foldlM'StrictCheck)
161+ it "scanlM' is strict enough" (checkScanlMStrictness scanlM'StrictCheck)
162+
163+ ---------------------------------------------------------------------------
164+ -- Right folds are lazy enough
165+ ---------------------------------------------------------------------------
166+
167+ it "foldrM is lazy enough" checkFoldrLaziness
168+
169+#ifdef DEVBUILD
170+ testFoldOpsCleanup "head" S.head
171+ testFoldOpsCleanup "null" S.null
172+ testFoldOpsCleanup "elem" (S.elem 0)
173+ testFoldOpsCleanup "notElem" (S.notElem 0)
174+ testFoldOpsCleanup "elemIndex" (S.elemIndex 0)
175+ -- S.lookup
176+ testFoldOpsCleanup "notElem" (S.notElem 0)
177+ testFoldOpsCleanup "find" (S.find (==0))
178+ testFoldOpsCleanup "findIndex" (S.findIndex (==0))
179+ testFoldOpsCleanup "all" (S.all (==1))
180+ testFoldOpsCleanup "any" (S.any (==0))
181+ testFoldOpsCleanup "and" (S.and . S.map (==1))
182+ testFoldOpsCleanup "or" (S.or . S.map (==0))
183+#endif
test/Streamly/Test/Prelude/Parallel.hs+28−2
@@ -9,7 +9,8 @@
99
1010module Streamly.Test.Prelude.Parallel where
1111
12−#if __GLASGOW_HASKELL__ < 808
12+import Data.List (sort)
13+#if !(MIN_VERSION_base(4,11,0))
1314import Data.Semigroup ((<>))
1415#endif
1516import Test.Hspec.QuickCheck
@@ -18,7 +19,7 @@ import Test.Hspec as H
1819import Streamly.Prelude
1920import qualified Streamly.Prelude as S
2021
21−import Streamly.Test.Prelude
22+import Streamly.Test.Prelude.Common
2223
2324main :: IO ()
2425main = hspec
@@ -47,6 +48,15 @@ main = hspec
4748 describe "Monoid operations" $ do
4849 parallelOps $ monoidOps "parallely" mempty sortEq
4950
51+ describe "Parallel loops" $ loops parallely sort sort
52+
53+ describe "Bind and Monoidal composition combinations" $ do
54+ -- XXX Taking a long time when parallelOps is used.
55+ bindAndComposeSimpleOps "Parallel" sortEq parallely
56+ bindAndComposeHierarchyOps "Parallel" parallely
57+ parallelOps $ nestTwoStreams "Parallel" sort sort
58+ parallelOps $ nestTwoStreamsApp "Parallel" sort sort
59+
5060 describe "Semigroup operations" $ do
5161 parallelOps $ semigroupOps "parallely" sortEq
5262
@@ -81,3 +91,19 @@ main = hspec
8191 parallelOps $ eliminationOps folded "parallely folded"
8292 parallelOps $ eliminationOpsWord8 S.fromFoldable "parallely"
8393 parallelOps $ eliminationOpsWord8 folded "parallely folded"
94+
95+ -- test both (<>) and mappend to make sure we are using correct instance
96+ -- for Monoid that is using the right version of semigroup. Instance
97+ -- deriving can cause us to pick wrong instances sometimes.
98+
99+ describe "Parallel (<>) time order check" $ parallelCheck parallely (<>)
100+ describe "Parallel mappend time order check" $ parallelCheck parallely mappend
101+
102+ describe "Composed MonadThrow parallely" $ composeWithMonadThrow parallely
103+
104+#ifdef DEVBUILD
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))
109+#endif
test/Streamly/Test/Prelude/Serial.hs+18−1
@@ -35,7 +35,7 @@ import qualified Streamly.Prelude as S
3535import qualified Streamly.Data.Fold as FL
3636
3737import Streamly.Test.Common
38−import Streamly.Test.Prelude
38+import Streamly.Test.Prelude.Common
3939
4040groupSplitOps :: String -> Spec
4141groupSplitOps desc = do
@@ -216,6 +216,21 @@ main = hspec
216216 describe "Monoid operations" $ do
217217 serialOps $ monoidOps "serially" mempty (==)
218218
219+ describe "Serial loops" $ loops serially id reverse
220+
221+ describe "Bind and Monoidal composition combinations" $ do
222+ -- XXX Taking a long time when serialOps is used.
223+ bindAndComposeSimpleOps "Serial" sortEq serially
224+ bindAndComposeHierarchyOps "Serial" serially
225+ serialOps $ nestTwoStreams "Serial" id id
226+ serialOps $ nestTwoStreamsApp "Serial" id id
227+ composeAndComposeSimpleSerially "Serial <> " (repeat [1..9]) serially
228+ composeAndComposeSimpleAheadly "Serial <> " (repeat [1 .. 9]) serially
229+ composeAndComposeSimpleWSerially
230+ "Serial <> "
231+ [[1..9], [1..9], [1,3,2,4,6,5,7,9,8], [1,3,2,4,6,5,7,9,8]]
232+ serially
233+
219234 describe "Semigroup operations" $ do
220235 serialOps $ semigroupOps "serially" (==)
221236 serialOps $ associativityCheck "serial == <>"
@@ -272,3 +287,5 @@ main = hspec
272287 describe "Tests for S.groupsBy" $ do
273288 prop "testGroupsBy" testGroupsBy
274289 prop "testGroupsBySep" testGroupsBySep
290+
291+ describe "Composed MonadThrow serially" $ composeWithMonadThrow serially
test/Streamly/Test/Prelude/WAsync.hs+81−2
@@ -9,7 +9,11 @@
99
1010module Streamly.Test.Prelude.WAsync where
1111
12−#if __GLASGOW_HASKELL__ < 808
12+#ifdef DEVBUILD
13+import Control.Concurrent ( threadDelay )
14+#endif
15+import Data.List (sort)
16+#if !(MIN_VERSION_base(4,11,0))
1317import Data.Semigroup ((<>))
1418#endif
1519import Test.Hspec.QuickCheck
@@ -18,7 +22,7 @@ import Test.Hspec as H
1822import Streamly.Prelude
1923import qualified Streamly.Prelude as S
2024
21−import Streamly.Test.Prelude
25+import Streamly.Test.Prelude.Common
2226
2327main :: IO ()
2428main = hspec
@@ -44,6 +48,14 @@ main = hspec
4448 describe "Monoid operations" $ do
4549 wAsyncOps $ monoidOps "wAsyncly" mempty sortEq
4650
51+ describe "WAsync loops" $ loops wAsyncly sort sort
52+
53+ describe "Bind and Monoidal composition combinations" $ do
54+ wAsyncOps $ bindAndComposeSimpleOps "WAsync" sortEq
55+ wAsyncOps $ bindAndComposeHierarchyOps "WAsync"
56+ wAsyncOps $ nestTwoStreams "WAsync" sort sort
57+ wAsyncOps $ nestTwoStreamsApp "WAsync" sort sort
58+
4759 describe "Semigroup operations" $ do
4860 wAsyncOps $ semigroupOps "wAsyncly" sortEq
4961
@@ -81,3 +93,70 @@ main = hspec
8193 wAsyncOps $ eliminationOps folded "wAsyncly folded"
8294 wAsyncOps $ eliminationOpsWord8 S.fromFoldable "wAsyncly"
8395 wAsyncOps $ eliminationOpsWord8 folded "wAsyncly folded"
96+
97+ -- describe "WAsync interleaved (<>) ordering check" $
98+ -- interleaveCheck wAsyncly (<>)
99+ -- describe "WAsync interleaved mappend ordering check" $
100+ -- interleaveCheck wAsyncly mappend
101+
102+ -- XXX this keeps failing intermittently, need to investigate
103+ -- describe "WAsync (<>) time order check" $
104+ -- parallelCheck wAsyncly (<>)
105+ -- describe "WAsync mappend time order check" $
106+ -- parallelCheck wAsyncly mappend
107+
108+ describe "Composed MonadThrow wAsyncly" $ composeWithMonadThrow wAsyncly
109+
110+ -- Ad-hoc tests
111+ it "takes n from stream of streams" (takeCombined 3 wAsyncly)
112+
113+#ifdef DEVBUILD
114+ let timed :: (IsStream t, Monad (t IO)) => Int -> t IO Int
115+ timed x = S.yieldM (threadDelay (x * 100000)) >> return x
116+
117+ -- These are not run parallely because the timing gets affected
118+ -- unpredictably when other tests are running on the same machine.
119+ --
120+ -- Also, they fail intermittently due to scheduling delays, so not run on
121+ -- CI machines.
122+ describe "Nested parallel and serial compositions" $ do
123+ let t = timed
124+ p = wAsyncly
125+ s = serially
126+ {-
127+ -- This is not correct, the result can also be [4,4,8,0,8,0,2,2]
128+ -- because of parallelism of [8,0] and [8,0].
129+ it "Nest <|>, <>, <|> (1)" $
130+ let t = timed
131+ in toListSerial (
132+ ((t 8 <|> t 4) <> (t 2 <|> t 0))
133+ <|> ((t 8 <|> t 4) <> (t 2 <|> t 0)))
134+ `shouldReturn` ([4,4,8,8,0,0,2,2])
135+ -}
136+ it "Nest <|>, <>, <|> (2)" $
137+ (S.toList . wAsyncly) (
138+ s (p (t 4 <> t 8) <> p (t 1 <> t 2))
139+ <> s (p (t 4 <> t 8) <> p (t 1 <> t 2)))
140+ `shouldReturn` ([4,4,8,8,1,1,2,2])
141+ -- FIXME: These two keep failing intermittently on Mac OS X
142+ -- Need to examine and fix the tests.
143+ {-
144+ it "Nest <|>, <=>, <|> (1)" $
145+ let t = timed
146+ in toListSerial (
147+ ((t 8 <|> t 4) <=> (t 2 <|> t 0))
148+ <|> ((t 9 <|> t 4) <=> (t 2 <|> t 0)))
149+ `shouldReturn` ([4,4,0,0,8,2,9,2])
150+ it "Nest <|>, <=>, <|> (2)" $
151+ let t = timed
152+ in toListSerial (
153+ ((t 4 <|> t 8) <=> (t 1 <|> t 2))
154+ <|> ((t 4 <|> t 9) <=> (t 1 <|> t 2)))
155+ `shouldReturn` ([4,4,1,1,8,2,9,2])
156+ -}
157+ it "Nest <|>, <|>, <|>" $
158+ (S.toList . wAsyncly) (
159+ ((t 4 <> t 8) <> (t 0 <> t 2))
160+ <> ((t 4 <> t 8) <> (t 0 <> t 2)))
161+ `shouldReturn` ([0,0,2,2,4,4,8,8])
162+#endif
test/Streamly/Test/Prelude/WSerial.hs+51−2
@@ -9,6 +9,7 @@
99
1010module Streamly.Test.Prelude.WSerial where
1111
12+import Data.List (sort)
1213#if __GLASGOW_HASKELL__ < 808
1314import Data.Semigroup ((<>))
1415#endif
@@ -17,11 +18,11 @@ import Test.Hspec.QuickCheck
1718import Test.QuickCheck.Monadic (monadicIO, run)
1819import Test.Hspec as H
1920
20−import Streamly.Prelude
21+import Streamly.Prelude hiding (repeat)
2122import qualified Streamly.Prelude as S
2223
2324import Streamly.Test.Common
24−import Streamly.Test.Prelude
25+import Streamly.Test.Prelude.Common
2526
2627associativityCheck
2728 :: String
@@ -40,6 +41,20 @@ associativityCheck desc t = prop desc assocCheckProp
4041 assocStream <- run $ S.toList $ t $ xStream <> yStream <> zStream
4142 listEquals (==) infixAssocstream assocStream
4243
44+interleaveCheck :: IsStream t
45+ => (t IO Int -> SerialT IO Int)
46+ -> (t IO Int -> t IO Int -> t IO Int)
47+ -> Spec
48+interleaveCheck t f =
49+ it "Interleave four" $
50+ (S.toList . t) ((singleton 0 `f` singleton 1) `f` (singleton 100 `f` singleton 101))
51+ `shouldReturn` [0, 100, 1, 101]
52+
53+ where
54+
55+ singleton :: IsStream t => a -> t m a
56+ singleton a = a .: nil
57+
4358main :: IO ()
4459main = hspec
4560 $ H.parallel
@@ -64,6 +79,29 @@ main = hspec
6479 describe "Monoid operations" $ do
6580 wSerialOps $ monoidOps "wSerially" mempty sortEq
6681
82+ describe "Bind and Monoidal composition combinations" $ do
83+ -- XXX Taking a long time when wSerialOps is used.
84+ bindAndComposeSimpleOps "WSerial" sortEq wSerially
85+ bindAndComposeHierarchyOps "WSerial" wSerially
86+ wSerialOps $ nestTwoStreams "WSerial" id sort
87+ wSerialOps $ nestTwoStreamsApp "WSerial" id sort
88+ composeAndComposeSimpleSerially
89+ "WSerial <> "
90+ [ [1, 4, 2, 7, 3, 5, 8, 6, 9]
91+ , [1, 7, 4, 8, 2, 9, 5, 3, 6]
92+ , [1, 4, 2, 7, 3, 5, 8, 6, 9]
93+ , [1, 7, 4, 8, 2, 9, 5, 3, 6]
94+ ]
95+ wSerially
96+ composeAndComposeSimpleWSerially
97+ "WSerial <> "
98+ [ [1, 4, 2, 7, 3, 5, 8, 6, 9]
99+ , [1, 7, 4, 8, 2, 9, 5, 3, 6]
100+ , [1, 4, 3, 7, 2, 6, 9, 5, 8]
101+ , [1, 7, 4, 9, 3, 8, 6, 2, 5]
102+ ]
103+ wSerially
104+
67105 describe "Semigroup operations" $ do
68106 wSerialOps $ semigroupOps "wSerially" (==)
69107 wSerialOps $ associativityCheck "wSerial == <>"
@@ -101,3 +139,14 @@ main = hspec
101139 describe "Stream serial elimination operations" $ do
102140 wSerialOps $ eliminationOpsOrdered S.fromFoldable "wSerially"
103141 wSerialOps $ eliminationOpsOrdered folded "wSerially folded"
142+
143+ ---------------------------------------------------------------------------
144+ -- Semigroup/Monoidal Composition strict ordering checks
145+ ---------------------------------------------------------------------------
146+
147+ describe "WSerial interleaved (<>) ordering check" $
148+ interleaveCheck wSerially (<>)
149+ describe "WSerial interleaved mappend ordering check" $
150+ interleaveCheck wSerially mappend
151+
152+ describe "Composed MonadThrow wSerially" $ composeWithMonadThrow wSerially
test/Streamly/Test/Prelude/ZipAsync.hs+1−1
@@ -15,7 +15,7 @@ import Test.Hspec as H
1515import Streamly.Prelude
1616import qualified Streamly.Prelude as S
1717
18−import Streamly.Test.Prelude
18+import Streamly.Test.Prelude.Common
1919
2020main :: IO ()
2121main = hspec
test/Streamly/Test/Prelude/ZipSerial.hs+1−1
@@ -18,7 +18,7 @@ import Test.Hspec as H
1818import Streamly.Prelude
1919import qualified Streamly.Prelude as S
2020
21−import Streamly.Test.Prelude
21+import Streamly.Test.Prelude.Common
2222
2323main :: IO ()
2424main = hspec