@@ -221,6 +221,24 @@ concurrentFoldrApplication n =
221221 sourceUnfoldrM1 n |&. A.foldrM (\x xs -> return (x : xs)) []
222222 equals (==) stream list
223223
224+transformCombineFromList
225+ :: Semigroup (t IO Int)
226+ => ([Int] -> t IO Int)
227+ -> ([Int] -> [Int] -> Bool)
228+ -> ([Int] -> [Int])
229+ -> (t IO Int -> SerialT IO Int)
230+ -> (t IO Int -> t IO Int)
231+ -> [Int]
232+ -> [Int]
233+ -> [Int]
234+ -> Property
235+transformCombineFromList constr eq listOp t op a b c =
236+ monadicIO $ do
237+ stream <- run ((A.toList . t) $
238+ constr a <> op (constr b <> constr c))
239+ let list = a <> listOp (b <> c)
240+ equals eq stream list
241+
224242foldFromList
225243 :: ([Int] -> t IO Int)
226244 -> (t IO Int -> SerialT IO Int)
@@ -336,6 +354,69 @@ concurrentOps constr desc t eq = do
336354 concurrentMapM constr eq $ \n mv stream ->
337355 t $ A.mapM (mvarSequenceOp mv n) stream
338356
357+-- XXX add tests for MonadReader and MonadError etc. In case an SVar is
358+-- accidentally passed through them.
359+transformCombineOpsCommon
360+ :: (IsStream t, Semigroup (t IO Int))
361+ => ([Int] -> t IO Int)
362+ -> String
363+ -> (t IO Int -> SerialT IO Int)
364+ -> ([Int] -> [Int] -> Bool)
365+ -> Spec
366+transformCombineOpsCommon constr desc t eq = do
367+ let transform = transformCombineFromList constr eq
368+ -- Filtering
369+ prop (desc ++ " filter False") $
370+ transform (filter (const False)) t (A.filter (const False))
371+ prop (desc ++ " filter True") $
372+ transform (filter (const True)) t (A.filter (const True))
373+ prop (desc ++ " filter even") $
374+ transform (filter even) t (A.filter even)
375+
376+ prop (desc ++ " take maxBound") $
377+ transform (take maxBound) t (A.take maxBound)
378+ prop (desc ++ " take 0") $ transform (take 0) t (A.take 0)
379+
380+ prop (desc ++ " takeWhile True") $
381+ transform (takeWhile (const True)) t (A.takeWhile (const True))
382+ prop (desc ++ " takeWhile False") $
383+ transform (takeWhile (const False)) t (A.takeWhile (const False))
384+
385+ prop (desc ++ " drop maxBound") $
386+ transform (drop maxBound) t (A.drop maxBound)
387+ prop (desc ++ " drop 0") $ transform (drop 0) t (A.drop 0)
388+
389+ prop (desc ++ " dropWhile True") $
390+ transform (dropWhile (const True)) t (A.dropWhile (const True))
391+ prop (desc ++ " dropWhile False") $
392+ transform (dropWhile (const False)) t (A.dropWhile (const False))
393+ prop (desc ++ " scan") $ transform (scanl' (flip const) 0) t
394+ (A.scanl' (flip const) 0)
395+ prop (desc ++ " reverse") $ transform reverse t A.reverse
396+
397+transformCombineOpsOrdered
398+ :: (IsStream t, Semigroup (t IO Int))
399+ => ([Int] -> t IO Int)
400+ -> String
401+ -> (t IO Int -> SerialT IO Int)
402+ -> ([Int] -> [Int] -> Bool)
403+ -> Spec
404+transformCombineOpsOrdered constr desc t eq = do
405+ let transform = transformCombineFromList constr eq
406+ -- Filtering
407+ prop (desc ++ " take 1") $ transform (take 1) t (A.take 1)
408+ prop (desc ++ " take 10") $ transform (take 10) t (A.take 10)
409+
410+ prop (desc ++ " takeWhile > 0") $
411+ transform (takeWhile (> 0)) t (A.takeWhile (> 0))
412+
413+ prop (desc ++ " drop 1") $ transform (drop 1) t (A.drop 1)
414+ prop (desc ++ " drop 10") $ transform (drop 10) t (A.drop 10)
415+
416+ prop (desc ++ " dropWhile > 0") $
417+ transform (dropWhile (> 0)) t (A.dropWhile (> 0))
418+ prop (desc ++ " scan") $ transform (scanl' (+) 0) t (A.scanl' (+) 0)
419+
339420wrapMaybe :: Eq a1 => ([a1] -> a2) -> [a1] -> Maybe a2
340421wrapMaybe f =
341422 \x ->
@@ -641,6 +722,32 @@ main = hspec $ do
641722 prop "concurrent foldl application" $ withMaxSuccess maxTestCount $
642723 concurrentFoldlApplication
643724
725+ -- These tests are specifically targeted towards detecting illegal sharing
726+ -- of SVar across conurrent streams.
727+ describe "Stream transform and combine operations" $ do
728+ transformCombineOpsCommon A.fromFoldable "serially" serially (==)
729+ transformCombineOpsCommon A.fromFoldable "aheadly" aheadly (==)
730+ transformCombineOpsCommon A.fromFoldable "wSerially" wSerially sortEq
731+ transformCombineOpsCommon A.fromFoldable "zipSerially" zipSerially (==)
732+ transformCombineOpsCommon A.fromFoldable "zipAsyncly" zipAsyncly (==)
733+ transformCombineOpsCommon A.fromFoldable "asyncly" asyncly sortEq
734+ transformCombineOpsCommon A.fromFoldable "wAsyncly" wAsyncly sortEq
735+ transformCombineOpsCommon A.fromFoldable "parallely" parallely sortEq
736+
737+ transformCombineOpsCommon folded "serially" serially (==)
738+ transformCombineOpsCommon folded "aheadly" aheadly (==)
739+ transformCombineOpsCommon folded "wSerially" wSerially sortEq
740+ transformCombineOpsCommon folded "zipSerially" zipSerially (==)
741+ transformCombineOpsCommon folded "zipAsyncly" zipAsyncly (==)
742+ transformCombineOpsCommon folded "asyncly" asyncly sortEq
743+ transformCombineOpsCommon folded "wAsyncly" wAsyncly sortEq
744+ transformCombineOpsCommon folded "parallely" parallely sortEq
745+
746+ transformCombineOpsOrdered A.fromFoldable "serially" serially (==)
747+ transformCombineOpsOrdered A.fromFoldable "serially" aheadly (==)
748+ transformCombineOpsOrdered A.fromFoldable "zipSerially" zipSerially (==)
749+ transformCombineOpsOrdered A.fromFoldable "zipAsyncly" zipAsyncly (==)
750+
644751 describe "Stream elimination operations" $ do
645752 eliminationOps A.fromFoldable "serially" serially
646753 eliminationOps A.fromFoldable "aheadly" aheadly