streamly-0.8.2: test/Streamly/Test/Prelude/Serial.hs
-- |
-- Module : Streamly.Test.Prelude.Serial
-- Copyright : (c) 2020 Composewell Technologies
--
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
module Streamly.Test.Prelude.Serial (checkTakeDropTime, main) where
import Control.Concurrent ( threadDelay )
import Control.Monad ( when, forM_ )
import Data.Function ( (&) )
import Data.IORef ( newIORef, readIORef, writeIORef, modifyIORef' )
import Data.Int (Int64)
import Data.List (sort, group, intercalate)
import Data.Maybe ( isJust, fromJust )
import Foreign.Storable (Storable)
#if __GLASGOW_HASKELL__ < 808
import Data.Semigroup ((<>))
#endif
import Data.Semigroup (Sum(..), getSum)
import Test.Hspec.QuickCheck
import Test.QuickCheck
( Gen
, Property
, Arbitrary(..)
, choose
, elements
, forAll
, frequency
, listOf
, listOf1
, suchThat
, vectorOf
, withMaxSuccess
)
import Test.QuickCheck.Monadic (assert, monadicIO, pick, run)
import Test.Hspec as H
import Streamly.Prelude (SerialT, IsStream, serial, fromSerial)
#ifndef COVERAGE_BUILD
import Streamly.Prelude (avgRate, maxBuffer)
#endif
import qualified Streamly.Prelude as S
import qualified Streamly.Internal.Data.Fold as FL
import qualified Streamly.Internal.Data.Unfold as UF
import qualified Streamly.Internal.Data.Stream.IsStream as IS
import qualified Streamly.Data.Array.Foreign as A
import Streamly.Internal.Data.Time.Units
(AbsTime, NanoSecond64(..), toRelTime64, diffAbsTime64)
#ifdef DEVBUILD
import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
#endif
import Streamly.Test.Common
import Streamly.Test.Prelude.Common
splitOnSeq :: Spec
splitOnSeq = do
describe "Tests for splitOnSeq" $ do
it "splitOnSeq' \"hello\" \"\" = [\"\"]"
$ splitOnSeq' "hello" "" `shouldReturn` [""]
it "splitOnSeq' \"hello\" \"hello\" = [\"\", \"\"]"
$ splitOnSeq' "hello" "hello" `shouldReturn` ["", ""]
it "splitOnSeq' \"x\" \"hello\" = [\"hello\"]"
$ splitOnSeq' "x" "hello" `shouldReturn` ["hello"]
it "splitOnSeq' \"h\" \"hello\" = [\"\", \"ello\"]"
$ splitOnSeq' "h" "hello" `shouldReturn` ["", "ello"]
it "splitOnSeq' \"o\" \"hello\" = [\"hell\", \"\"]"
$ splitOnSeq' "o" "hello" `shouldReturn` ["hell", ""]
it "splitOnSeq' \"e\" \"hello\" = [\"h\", \"llo\"]"
$ splitOnSeq' "e" "hello" `shouldReturn` ["h", "llo"]
it "splitOnSeq' \"l\" \"hello\" = [\"he\", \"\", \"o\"]"
$ splitOnSeq' "l" "hello" `shouldReturn` ["he", "", "o"]
it "splitOnSeq' \"ll\" \"hello\" = [\"he\", \"o\"]"
$ splitOnSeq' "ll" "hello" `shouldReturn` ["he", "o"]
where
splitOnSeq' pat xs =
S.toList $ IS.splitOnSeq (A.fromList pat) FL.toList (S.fromList xs)
splitOnSuffixSeq :: Spec
splitOnSuffixSeq = do
describe "Tests for splitOnSuffixSeq" $ do
it "splitSuffixOn_ \".\" \"\" []"
$ splitSuffixOn_ "." "" `shouldReturn` []
it "splitSuffixOn_ \".\" \".\" [\"\"]"
$ splitSuffixOn_ "." "." `shouldReturn` [""]
it "splitSuffixOn_ \".\" \"a\" [\"a\"]"
$ splitSuffixOn_ "." "a" `shouldReturn` ["a"]
it "splitSuffixOn_ \".\" \".a\" [\"\",\"a\"]"
$ splitSuffixOn_ "." ".a" `shouldReturn` ["", "a"]
it "splitSuffixOn_ \".\" \"a.\" [\"a\"]"
$ splitSuffixOn_ "." "a." `shouldReturn` ["a"]
it "splitSuffixOn_ \".\" \"a.b\" [\"a\",\"b\"]"
$ splitSuffixOn_ "." "a.b" `shouldReturn` ["a", "b"]
it "splitSuffixOn_ \".\" \"a.b.\" [\"a\",\"b\"]"
$ splitSuffixOn_ "." "a.b." `shouldReturn` ["a", "b"]
it "splitSuffixOn_ \".\" \"a..b..\" [\"a\",\"\",\"b\",\"\"]"
$ splitSuffixOn_ "." "a..b.." `shouldReturn` ["a", "", "b", ""]
where
splitSuffixOn_ pat xs =
S.toList
$ IS.splitOnSuffixSeq (A.fromList pat) FL.toList (S.fromList xs)
splitterProperties ::
forall a. (Arbitrary a, Eq a, Show a, Storable a, Enum a)
=> a
-> String
-> Spec
splitterProperties sep desc = do
describe (desc <> " splitOnSeq")
$ do
forM_ [0, 1, 2, 4]
$ intercalateSplitEqId splitOnSeq_ intercalate IS.intercalate
forM_ [0, 1, 2, 4]
$ concatSplitIntercalateEqConcat
splitOnSeq_
intercalate
IS.intercalate
-- Exclusive case
splitIntercalateEqId splitOnSeq_ intercalate IS.intercalate
describe (desc <> " splitOn")
$ do
intercalateSplitEqId splitOn_ intercalate IS.intercalate 1
concatSplitIntercalateEqConcat
splitOn_ intercalate IS.intercalate 1
-- Exclusive case
splitIntercalateEqId splitOn_ intercalate IS.intercalate
describe (desc <> " splitOnSuffixSeq")
$ do
forM_ [0, 1, 2, 4]
$ intercalateSplitEqIdNoSepEnd
splitOnSuffixSeq_
intercalate
IS.intercalate
forM_ [0, 1, 2, 4]
$ concatSplitIntercalateEqConcat
splitOnSuffixSeq_
intercalateSuffix
IS.intercalateSuffix
-- Exclusive case
splitIntercalateEqId
splitOnSuffixSeq_
intercalateSuffix
IS.intercalateSuffix
describe (desc <> " splitOnSuffix")
$ do
intercalateSplitEqIdNoSepEnd
splitOnSuffix_ intercalate IS.intercalate 1
concatSplitIntercalateEqConcat
splitOnSuffix_ intercalateSuffix IS.intercalateSuffix 1
-- Exclusive case
splitIntercalateEqId
splitOnSuffix_ intercalateSuffix IS.intercalateSuffix
where
splitOnSeq_ xs ys =
S.toList $ IS.splitOnSeq (A.fromList ys) FL.toList (S.fromList xs)
splitOnSuffixSeq_ xs ys =
S.toList $ IS.splitOnSuffixSeq (A.fromList ys) FL.toList (S.fromList xs)
splitOn_ xs ys =
S.toList $ IS.splitOn (== (head ys)) FL.toList (S.fromList xs)
splitOnSuffix_ xs ys =
S.toList $ IS.splitOnSuffix (== (head ys)) FL.toList (S.fromList xs)
intercalateSuffix xs yss = intercalate xs yss ++ xs
nonSepElem :: Gen a
nonSepElem = suchThat arbitrary (/= sep)
listWithSep :: Gen [a]
listWithSep = listOf $ frequency [(3, arbitrary), (1, elements [sep])]
listWithoutSep :: Gen [a]
listWithoutSep = vectorOf 4 nonSepElem
listsWithoutSep :: Gen [[a]]
listsWithoutSep = listOf listWithoutSep
listsWithoutSep1 :: Gen [[a]]
listsWithoutSep1 = listOf1 listWithoutSep
intercalateSplitEqId splitter lIntercalater sIntercalater i =
let name =
"intercalater . splitter == id ("
<> show i <> " element separator)"
in prop name
$ forAll listWithSep
$ \xs -> withMaxSuccess maxTestCount $ monadicIO $ testCase xs
where
testCase xs = do
ys <- splitter xs (replicate i sep)
szs <-
IS.toList
$ sIntercalater UF.fromList (replicate i sep)
$ IS.fromList ys
let lzs = lIntercalater (replicate i sep) ys
listEquals (==) szs xs
listEquals (==) lzs xs
intercalateSplitEqIdNoSepEnd splitter lIntercalater sIntercalater i =
let name =
"intercalater . splitter . (++ [x \\= sep]) == id ("
<> show i <> " element separator)"
in prop name
$ forAll ((,) <$> listWithSep <*> nonSepElem)
$ \(xs_, nonSep) -> do
let xs = xs_ ++ [nonSep]
withMaxSuccess maxTestCount $ monadicIO $ testCase xs
where
testCase xs = do
ys <- splitter xs (replicate i sep)
szs <-
IS.toList
$ sIntercalater UF.fromList (replicate i sep)
$ IS.fromList ys
let lzs = lIntercalater (replicate i sep) ys
listEquals (==) szs xs
listEquals (==) lzs xs
concatSplitIntercalateEqConcat splitter lIntercalater sIntercalater i =
let name =
"concat . splitter . S.intercalater == "
<> "concat ("
<> show i <> " element separator/possibly empty list)"
in prop name
$ forAll listsWithoutSep
$ \xss -> withMaxSuccess maxTestCount $ monadicIO $ testCase xss
where
testCase xss = do
let lxs = lIntercalater (replicate i sep) xss
lys <- splitter lxs (replicate i sep)
sxs <-
S.toList
$ sIntercalater UF.fromList (replicate i sep)
$ S.fromList xss
sys <- splitter sxs (replicate i sep)
listEquals (==) (concat lys) (concat xss)
listEquals (==) (concat sys) (concat xss)
splitIntercalateEqId splitter lIntercalater sIntercalater =
let name =
"splitter . intercalater == id"
<> " (exclusive separator/non-empty list)"
in prop name
$ forAll listsWithoutSep1
$ \xss -> do
withMaxSuccess maxTestCount $ monadicIO $ testCase xss
where
testCase xss = do
let lxs = lIntercalater [sep] xss
lys <- splitter lxs [sep]
sxs <- S.toList $ sIntercalater UF.fromList [sep] $ S.fromList xss
sys <- splitter sxs [sep]
listEquals (==) lys xss
listEquals (==) sys xss
groupSplitOps :: String -> Spec
groupSplitOps desc = do
-- splitting
splitOnSeq
splitOnSuffixSeq
-- splitting properties
splitterProperties (0 :: Int) desc
-- XXX This will fail
-- splitterProperties (0 :: Word8) desc
prop (desc <> " intercalate [x] . splitOn (== x) == id") $
forAll listWithZeroes $ \xs -> do
withMaxSuccess maxTestCount $
monadicIO $ do
ys <- S.toList $ S.splitOn (== 0) toListFL (S.fromList xs)
listEquals (==) (intercalate [0] ys) xs
where
listWithZeroes :: Gen [Int]
listWithZeroes = listOf $ frequency [(3, arbitrary), (1, elements [0])]
-- |
-- After grouping (and folding) Int stream using @>@ operation,
-- the first @Int@ of every @[Int]@ in the @[Int]@ stream should be the minimum.
testGroupsBy :: Property
testGroupsBy =
forAll (choose (0, maxStreamLen)) $ \len ->
forAll (vectorOf len (arbitrary :: Gen Int)) $ \vec -> monadicIO $ do
r <- run $ S.all (\ls ->
case ls of
[] -> True
(x:_) -> x == minimum ls)
$ S.groupsBy (>) FL.toList
$ S.fromList vec
assert r
testGroups :: Property
testGroups =
forAll (choose (0, maxStreamLen)) $ \len ->
forAll (vectorOf len (arbitrary :: Gen Int)) $ \vec -> monadicIO $ do
r <- run $ S.toList $ S.groups FL.toList $ S.fromList vec
assert $ r == group vec
testGroupsByRolling :: Property
testGroupsByRolling =
forAll (choose (0, maxStreamLen)) $ \len ->
forAll (vectorOf len (arbitrary :: Gen Int)) $ \vec -> monadicIO $ do
r <- run $ S.toList $ S.groupsByRolling (==) FL.toList $ S.fromList vec
assert $ r == group vec
-- |
-- If the list is empty, returns Nothing,
-- else wraps the minimum value of the list in Just.
maybeMinimum :: [Int] -> Maybe Int
maybeMinimum [] = Nothing
maybeMinimum ls = Just $ minimum ls
-- |
-- Checks if the @[Int]@ is non-increasing.
decreasing :: [Maybe Int] -> Bool
decreasing [] = True
decreasing xs = all (== True) $ zipWith (<=) (tail xs) xs
-- |
-- To check if the minimum elements (after grouping on @>@)
-- are non-increasing (either decrease or remain the same).
-- Had an element been strictly greater, it would have been grouped
-- with that element only.
testGroupsBySep :: Property
testGroupsBySep =
forAll (choose (0, maxStreamLen)) $ \len ->
forAll (vectorOf len (arbitrary :: Gen Int)) $ \vec -> monadicIO $ do
a <- run $ S.toList
$ S.map maybeMinimum
$ S.groupsBy (>) FL.toList
$ S.fromList vec
assert $ decreasing a
groupingOps :: Spec
groupingOps = do
prop "groupsBy" testGroupsBy
prop "S.groups = groups" testGroups
prop "S.groupsByRolling = groups" testGroupsByRolling
prop "testGroupsBySep" testGroupsBySep
associativityCheck
:: String
-> (SerialT IO Int -> SerialT IO Int)
-> Spec
associativityCheck desc t = prop desc assocCheckProp
where
assocCheckProp :: [Int] -> [Int] -> [Int] -> Property
assocCheckProp xs ys zs =
monadicIO $ do
let xStream = S.fromList xs
yStream = S.fromList ys
zStream = S.fromList zs
infixAssocstream <-
run $ S.toList $ t $ xStream `serial` yStream `serial` zStream
assocStream <- run $ S.toList $ t $ xStream <> yStream <> zStream
listEquals (==) infixAssocstream assocStream
max_length :: Int
max_length = 1000
tenPow8 :: Int64
tenPow8 = 10^(8 :: Int)
tenPow7 :: Int64
tenPow7 = 10^(7 :: Int)
takeDropTime :: NanoSecond64
takeDropTime = NanoSecond64 $ 5 * tenPow8
checkTakeDropTime :: (Maybe AbsTime, Maybe AbsTime) -> IO Bool
checkTakeDropTime (mt0, mt1) = do
let graceTime = NanoSecond64 $ 8 * tenPow7
case mt0 of
Nothing -> return True
Just t0 ->
case mt1 of
Nothing -> return True
Just t1 -> do
let tMax = toRelTime64 (takeDropTime + graceTime)
let tMin = toRelTime64 (takeDropTime - graceTime)
let t = diffAbsTime64 t1 t0
let r = t >= tMin && t <= tMax
when (not r) $ putStrLn $
"t = " ++ show t ++
" tMin = " ++ show tMin ++
" tMax = " ++ show tMax
return r
#ifdef DEVBUILD
testTakeInterval :: IO Bool
testTakeInterval = do
r <-
S.fold (FL.tee FL.head FL.last)
$ IS.takeInterval takeDropTime
$ S.repeatM (threadDelay 1000 >> getTime Monotonic)
checkTakeDropTime r
testDropInterval :: IO Bool
testDropInterval = do
t0 <- getTime Monotonic
mt1 <-
S.head
$ IS.dropInterval takeDropTime
$ S.repeatM (threadDelay 1000 >> getTime Monotonic)
checkTakeDropTime (Just t0, mt1)
#endif
unfold :: Property
unfold = monadicIO $ do
a <- pick $ choose (0, max_length `div` 2)
b <- pick $ choose (0, max_length)
let unf = UF.supplySecond b UF.enumerateFromToIntegral
ls <- S.toList $ S.unfold unf a
return $ ls == [a..b]
unfold0 :: Property
unfold0 = monadicIO $ do
a <- pick $ choose (0, max_length `div` 2)
b <- pick $ choose (0, max_length)
let unf = UF.supply a (UF.supplySecond b UF.enumerateFromToIntegral)
ls <- S.toList $ IS.unfold0 unf
return $ ls == [a..b]
testFromCallback :: IO Int
testFromCallback = do
ref <- newIORef Nothing
let stream = S.map Just (IS.fromCallback (setCallback ref))
`S.parallel` runCallback ref
S.sum $ S.map fromJust $ S.takeWhile isJust stream
where
setCallback ref cb = do
writeIORef ref (Just cb)
runCallback ref = S.fromEffect $ do
cb <-
S.repeatM (readIORef ref)
& IS.delayPost 0.1
& S.mapMaybe id
& S.head
S.fromList [1..100]
& IS.delayPost 0.001
& S.mapM_ (fromJust cb)
threadDelay 100000
return Nothing
foldIterateM :: Property
foldIterateM =
forAll (listOf (chooseInt (0, max_length))) $ \lst -> monadicIO $ do
let s1 = Prelude.sum lst
strm = S.fromList lst
ms2 <-
S.last
$ S.map getSum
$ IS.foldIterateM
(return . FL.take 1 . FL.sconcat)
(return (Sum 0))
$ S.map Sum strm
case ms2 of
Nothing -> assert $ s1 == 0
Just s2 -> assert $ s1 == s2
sortBy :: Property
sortBy = forAll (listOf (chooseInt (0, max_length))) $ \lst -> monadicIO $ do
let s1 = sort lst
s2 <- run $ S.toList $ IS.sortBy compare $ S.fromList lst
assert $ s1 == s2
moduleName :: String
moduleName = "Prelude.Serial"
main :: IO ()
main = hspec
$ H.parallel
#ifdef COVERAGE_BUILD
$ modifyMaxSuccess (const 10)
#endif
$ describe moduleName $ do
let serialOps :: IsStream t => ((SerialT IO a -> t IO a) -> Spec) -> Spec
serialOps spec = mapOps spec $ makeOps fromSerial
#ifndef COVERAGE_BUILD
<> [("rate AvgRate 0.00000001", fromSerial . avgRate 0.00000001)]
<> [("maxBuffer -1", fromSerial . maxBuffer (-1))]
#endif
let toListSerial :: SerialT IO a -> IO [a]
toListSerial = S.toList . fromSerial
describe "Runners" $ do
-- XXX use an IORef to store and check the side effects
it "simple serially" $
(S.drain . fromSerial) (return (0 :: Int)) `shouldReturn` ()
it "simple serially with IO" $
(S.drain . fromSerial) (S.fromEffect $ putStrLn "hello") `shouldReturn` ()
describe "Empty" $
it "Monoid - mempty" $
toListSerial mempty `shouldReturn` ([] :: [Int])
-- it "Alternative - empty" $
-- (toListSerial empty) `shouldReturn` ([] :: [Int])
-- it "MonadPlus - mzero" $
-- (toListSerial mzero) `shouldReturn` ([] :: [Int])
describe "Construction" $ do
-- Add all the construction tests for all stream types.
serialOps $ prop "serially repeat" . constructWithRepeat
serialOps $ prop "serially repeatM" . constructWithRepeatM
serialOps $ prop "serially replicate" . constructWithReplicate
serialOps $ prop "serially replicateM" . constructWithReplicateM
serialOps $ prop "serially intFromThenTo" .
constructWithIntFromThenTo
#if __GLASGOW_HASKELL__ >= 806
serialOps $ prop "serially DoubleFromThenTo" .
constructWithDoubleFromThenTo
#endif
serialOps $ prop "serially iterate" . constructWithIterate
-- XXX test for all types of streams
serialOps $ prop "serially iterateM" . constructWithIterateM
serialOps $ prop "serially enumerate" . constructWithEnumerate id
serialOps $ prop "serially enumerateTo" . constructWithEnumerateTo id
serialOps $ prop "serially fromIndices" . constructWithFromIndices
serialOps $ prop "serially fromIndicesM" . constructWithFromIndicesM
serialOps $ prop "serially fromList" . constructWithFromList id
serialOps $ prop "serially fromListM" . constructWithFromListM id
serialOps $ prop "serially unfoldr" . constructWithUnfoldr id
serialOps $ prop "serially fromPure" . constructWithFromPure id
serialOps $ prop "serially fromEffect" . constructWithFromEffect id
serialOps $ prop "serially cons" . constructWithCons S.cons
serialOps $ prop "serially consM" . constructWithConsM S.consM id
serialOps $ prop "serially (.:)" . constructWithCons (S..:)
serialOps $ prop "serially (|:)" . constructWithConsM (S.|:) id
describe "From Generators" $ do
prop "unfold" unfold
prop "unfold0" unfold0
describe "Simple Operations" $ serialOps simpleOps
describe "Functor operations" $ do
serialOps $ functorOps S.fromFoldable "serially" (==)
serialOps $ functorOps folded "serially folded" (==)
describe "Monoid operations" $ do
serialOps $ monoidOps "serially" mempty (==)
describe "Serial loops" $ loops fromSerial id reverse
describe "Bind and Monoidal composition combinations" $ do
-- XXX Taking a long time when serialOps is used.
bindAndComposeSimpleOps "Serial" sortEq fromSerial
bindAndComposeHierarchyOps "Serial" fromSerial
serialOps $ nestTwoStreams "Serial" id id
serialOps $ nestTwoStreamsApp "Serial" id id
composeAndComposeSimpleSerially "Serial <> " (repeat [1..9]) fromSerial
composeAndComposeSimpleAheadly "Serial <> " (repeat [1 .. 9]) fromSerial
composeAndComposeSimpleWSerially
"Serial <> "
[[1..9], [1..9], [1,3,2,4,6,5,7,9,8], [1,3,2,4,6,5,7,9,8]]
fromSerial
describe "Semigroup operations" $ do
serialOps $ semigroupOps "serially" (==)
serialOps $ associativityCheck "serial == <>"
describe "Applicative operations" $ do
-- The tests using sorted equality are weaker tests
-- We need to have stronger unit tests for all those
-- XXX applicative with three arguments
serialOps $ applicativeOps S.fromFoldable "serially" (==)
serialOps $ applicativeOps folded "serially folded" (==)
serialOps $ applicativeOps1 S.fromFoldable "serially" (==)
serialOps $ applicativeOps1 S.fromFoldable "serially folded" (==)
-- XXX add tests for indexed/indexedR
describe "Zip operations" $ do
-- We test only the serial zip with serial streams and the parallel
-- stream, because the rate setting in these streams can slow down
-- zipAsync.
serialOps $ prop "zip monadic serially" . zipMonadic S.fromFoldable (==)
serialOps $ prop "zip monadic serially folded" . zipMonadic folded (==)
-- XXX add merge tests like zip tests
-- for mergeBy, we can split a list randomly into two lists and
-- then merge them, it should result in original list
-- describe "Merge operations" $ do
describe "Monad operations" $ do
serialOps $ prop "serially monad then" . monadThen S.fromFoldable (==)
serialOps $ prop "serially monad then folded" . monadThen folded (==)
serialOps $ prop "serially monad bind" . monadBind S.fromFoldable (==)
serialOps $ prop "serially monad bind folded" . monadBind folded (==)
describe "Stream transform and combine operations" $ do
serialOps $ transformCombineOpsCommon S.fromFoldable "serially" (==)
serialOps $ transformCombineOpsCommon folded "serially" (==)
serialOps $ transformCombineOpsOrdered S.fromFoldable "serially" (==)
serialOps $ transformCombineOpsOrdered folded "serially" (==)
#ifdef DEVBUILD
describe "Filtering" $ do
it "takeInterval" (testTakeInterval `shouldReturn` True)
it "dropInterval" (testDropInterval `shouldReturn` True)
#endif
-- Just some basic sanity tests for now
let input = [[1,1] :: [Int],[2,2],[3,3],[4,4],[5,5],[6,6],[7,7],[8,8]]
mustBe g inp out =
S.toList (IS.concatPairsWith g S.fromList (S.fromList inp))
`shouldReturn` out
in do
it "concatPairsWith serial"
$ mustBe S.serial input [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8]
it "concatPairsWith wSerial"
$ mustBe S.wSerial input [1,5,3,7,2,6,4,8,1,5,3,7,2,6,4,8]
it "concatPairsWith mergeBy sorted"
$ mustBe
(S.mergeBy compare) input [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8]
it "concatPairsWith mergeBy reversed"
$ mustBe
(S.mergeBy compare)
(reverse input)
[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8]
prop "sortBy" sortBy
describe "Stream group and split operations" $ do
groupSplitOps "serially"
describe "Stream elimination operations" $ do
serialOps $ eliminationOps S.fromFoldable "serially"
serialOps $ eliminationOps folded "serially folded"
serialOps $ eliminationOpsWord8 S.fromFoldable "serially"
serialOps $ eliminationOpsWord8 folded "serially folded"
serialOps $ \t ->
prop "drainWhile (> 0)" $ \n ->
withMaxSuccess maxTestCount $
monadicIO $ do
let xs = [1..n]
ioRef <- run $ newIORef ([] :: [Int])
run $
S.drainWhile (> 0) . t $
S.mapM (\a -> modifyIORef' ioRef (a :) >> return a) $
S.fromList xs
strm <- run $ readIORef ioRef
listEquals (==) (reverse strm) (takeWhile (> 0) xs)
-- XXX Add a test where we chain all transformation APIs and make sure that
-- the state is being passed through all of them.
describe "Stream serial elimination operations" $ do
serialOps $ eliminationOpsOrdered S.fromFoldable "serially"
serialOps $ eliminationOpsOrdered folded "serially folded"
describe "Tests for S.groupsBy" groupingOps
describe "Tests for exceptions" $ serialOps $ exceptionOps "serially"
describe "Composed MonadThrow serially" $ composeWithMonadThrow fromSerial
it "fromCallback" $ testFromCallback `shouldReturn` (50*101)
describe "Nesting" $ do
prop "foldIterateM" foldIterateM