conduit-1.3.6.1: test/Spec.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
module Spec (spec) where
import Conduit
import Prelude hiding (FilePath)
import Data.Maybe (listToMaybe)
import Data.Conduit.Combinators (slidingWindow, chunksOfE, chunksOfExactlyE)
import Data.List (intersperse, sort, find, mapAccumL)
import Safe (tailSafe)
import System.FilePath (takeExtension, (</>))
import Test.Hspec
import Test.Hspec.QuickCheck
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL
import Data.IORef
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Storable as VS
import Control.Monad (liftM)
import Control.Monad.ST (runST)
import Control.Monad.Trans.Writer
import qualified System.IO as IO
#if ! MIN_VERSION_base(4,8,0)
import Data.Monoid (Monoid (..))
import Control.Applicative ((<$>), (<*>))
#endif
#if MIN_VERSION_mono_traversable(1,0,0)
import Data.Sequences (LazySequence (..), Utf8 (..))
#else
import Data.Sequences.Lazy
import Data.Textual.Encoding
#endif
import qualified Data.NonNull as NN
import GHC.IO.Handle (hDuplicateTo)
import qualified Data.ByteString as S
import Data.ByteString.Builder (byteString, toLazyByteString)
import qualified Data.ByteString.Char8 as S8
import qualified Data.ByteString.Lazy.Char8 as L8
import qualified StreamSpec
import UnliftIO.Exception (pureTry)
spec :: Spec
spec = do
describe "yieldMany" $ do
it "list" $
runConduitPure (yieldMany [1..10] .| sinkList)
`shouldBe` [1..10]
it "Text" $
runConduitPure (yieldMany ("Hello World" :: T.Text) .| sinkList)
`shouldBe` "Hello World"
it "unfold" $
let f 11 = Nothing
f i = Just (show i, i + 1)
in runConduitPure (unfoldC f 1 .| sinkList)
`shouldBe` map show [1..10]
it "enumFromTo" $
runConduitPure (enumFromToC 1 10 .| sinkList) `shouldBe` [1..10]
it "iterate" $
let f i = i + 1
src = iterateC f seed
seed = 1
count = 10
res = runConduitPure $ src .| takeC count .| sinkList
in res `shouldBe` take count (iterate f seed)
it "repeat" $
let src = repeatC seed
seed = 1
count = 10
res = runConduitPure $ src .| takeC count .| sinkList
in res `shouldBe` take count (repeat seed)
it "replicate" $
let src = replicateC count seed
seed = 1
count = 10
res = runConduitPure $ src .| sinkList
in res `shouldBe` replicate count seed
it "sourceLazy" $
let tss = ["foo", "bar", "baz"]
tl = TL.fromChunks tss
res = runConduitPure $ sourceLazy tl .| sinkList
in res `shouldBe` tss
it "repeatM" $
let src = repeatMC (return seed)
seed = 1
count = 10
res = runConduitPure $ src .| takeC count .| sinkList
in res `shouldBe` take count (repeat seed)
it "repeatWhileM" $ do
ref <- newIORef 0
let f = atomicModifyIORef ref $ \i -> (succ i, succ i)
src = repeatWhileMC f (< 11)
res <- runConduit $ src .| sinkList
res `shouldBe` [1..10]
it "replicateM" $ do
ref <- newIORef 0
let f = atomicModifyIORef ref $ \i -> (succ i, succ i)
src = replicateMC 10 f
res <- runConduit $ src .| sinkList
res `shouldBe` [1..10]
it "sourceFile" $ do
let contents = concat $ replicate 10000 $ "this is some content\n"
fp = "tmp"
writeFile fp contents
res <- runConduitRes $ sourceFile fp .| sinkLazy
nocrBL res `shouldBe` TL.encodeUtf8 (TL.pack contents)
it "sourceHandle" $ do
let contents = concat $ replicate 10000 $ "this is some content\n"
fp = "tmp"
writeFile fp contents
res <- IO.withBinaryFile "tmp" IO.ReadMode $ \h ->
runConduit $ sourceHandle h .| sinkLazy
nocrBL res `shouldBe` TL.encodeUtf8 (TL.pack contents)
it "sourceIOHandle" $ do
let contents = concat $ replicate 10000 $ "this is some content\n"
fp = "tmp"
writeFile fp contents
let open = IO.openBinaryFile "tmp" IO.ReadMode
res <- runConduitRes $ sourceIOHandle open .| sinkLazy
nocrBL res `shouldBe` TL.encodeUtf8 (TL.pack contents)
prop "stdin" $ \(S.pack -> content) -> do
S.writeFile "tmp" content
IO.withBinaryFile "tmp" IO.ReadMode $ \h -> do
hDuplicateTo h IO.stdin
x <- runConduit $ stdinC .| foldC
x `shouldBe` content
let hasExtension' ext fp = takeExtension fp == ext
it "sourceDirectory" $ do
res <- runConduitRes
$ sourceDirectory "test" .| filterC (not . hasExtension' ".swp") .| sinkList
sort res `shouldBe`
[ "test" </> "Data"
, "test" </> "Spec.hs"
, "test" </> "StreamSpec.hs"
, "test" </> "doctests.hs"
, "test" </> "main.hs"
, "test" </> "subdir"
]
it "sourceDirectoryDeep" $ do
res1 <- runConduitRes
$ sourceDirectoryDeep False "test" .| filterC (not . hasExtension' ".swp") .| sinkList
res2 <- runConduitRes
$ sourceDirectoryDeep True "test" .| filterC (not . hasExtension' ".swp") .| sinkList
sort res1 `shouldBe`
[ "test" </> "Data" </> "Conduit" </> "Extra" </> "ZipConduitSpec.hs"
, "test" </> "Data" </> "Conduit" </> "StreamSpec.hs"
, "test" </> "Spec.hs"
, "test" </> "StreamSpec.hs"
, "test" </> "doctests.hs"
, "test" </> "main.hs"
, "test" </> "subdir" </> "dummyfile.txt"
]
sort res1 `shouldBe` sort res2
prop "drop" $ \(T.pack -> input) count ->
runConduitPure (yieldMany input .| (dropC count >>= \() -> sinkList))
`shouldBe` T.unpack (T.drop count input)
prop "dropE" $ \(T.pack -> input) ->
runConduitPure (yield input .| (dropCE 5 >>= \() -> foldC))
`shouldBe` T.drop 5 input
prop "dropWhile" $ \(T.pack -> input) sep ->
runConduitPure (yieldMany input .| (dropWhileC (<= sep) >>= \() -> sinkList))
`shouldBe` T.unpack (T.dropWhile (<= sep) input)
prop "dropWhileE" $ \(T.pack -> input) sep ->
runConduitPure (yield input .| (dropWhileCE (<= sep) >>= \() -> foldC))
`shouldBe` T.dropWhile (<= sep) input
it "fold" $
let list = [[1..10], [11..20]]
src = yieldMany list
res = runConduitPure $ src .| foldC
in res `shouldBe` concat list
it "foldE" $
let list = [[1..10], [11..20]]
src = yieldMany $ Identity list
res = runConduitPure $ src .| foldCE
in res `shouldBe` concat list
it "foldl" $
let res = runConduitPure $ yieldMany [1..10] .| foldlC (+) 0
in res `shouldBe` sum [1..10]
it "foldlE" $
let res = runConduitPure $ yield [1..10] .| foldlCE (+) 0
in res `shouldBe` sum [1..10]
it "foldMap" $
let src = yieldMany [1..10]
res = runConduitPure $ src .| foldMapC return
in res `shouldBe` [1..10]
it "foldMapE" $
let src = yield [1..10]
res = runConduitPure $ src .| foldMapCE return
in res `shouldBe` [1..10]
prop "all" $ \ (input :: [Int]) -> runConduitPure (yieldMany input .| allC even) `shouldBe` all evenInt input
prop "allE" $ \ (input :: [Int]) -> runConduitPure (yield input .| allCE even) `shouldBe` all evenInt input
prop "any" $ \ (input :: [Int]) -> runConduitPure (yieldMany input .| anyC even) `shouldBe` any evenInt input
prop "anyE" $ \ (input :: [Int]) -> runConduitPure (yield input .| anyCE even) `shouldBe` any evenInt input
prop "and" $ \ (input :: [Bool]) -> runConduitPure (yieldMany input .| andC) `shouldBe` and input
prop "andE" $ \ (input :: [Bool]) -> runConduitPure (yield input .| andCE) `shouldBe` and input
prop "or" $ \ (input :: [Bool]) -> runConduitPure (yieldMany input .| orC) `shouldBe` or input
prop "orE" $ \ (input :: [Bool]) -> runConduitPure (yield input .| orCE) `shouldBe` or input
prop "elem" $ \x xs -> runConduitPure (yieldMany xs .| elemC x) `shouldBe` elemInt x xs
prop "elemE" $ \x xs -> runConduitPure (yield xs .| elemCE x) `shouldBe` elemInt x xs
prop "notElem" $ \x xs -> runConduitPure (yieldMany xs .| notElemC x) `shouldBe` notElemInt x xs
prop "notElemE" $ \x xs -> runConduitPure (yield xs .| notElemCE x) `shouldBe` notElemInt x xs
prop "sinkVector regular" $ \xs -> do
res <- runConduit $ yieldMany xs .| sinkVector
res `shouldBe` V.fromList (xs :: [Int])
prop "sinkVector unboxed" $ \xs -> do
res <- runConduit $ yieldMany xs .| sinkVector
res `shouldBe` VU.fromList (xs :: [Int])
prop "sinkVector storable" $ \xs -> do
res <- runConduit $ yieldMany xs .| sinkVector
res `shouldBe` VS.fromList (xs :: [Int])
prop "sinkVectorN regular" $ \xs' -> do
let maxSize = 20
xs = take maxSize xs'
res <- runConduit $ yieldMany xs' .| sinkVectorN maxSize
res `shouldBe` V.fromList (xs :: [Int])
prop "sinkVectorN unboxed" $ \xs' -> do
let maxSize = 20
xs = take maxSize xs'
res <- runConduit $ yieldMany xs' .| sinkVectorN maxSize
res `shouldBe` VU.fromList (xs :: [Int])
prop "sinkVectorN storable" $ \xs' -> do
let maxSize = 20
xs = take maxSize xs'
res <- runConduit $ yieldMany xs' .| sinkVectorN maxSize
res `shouldBe` VS.fromList (xs :: [Int])
prop "sinkBuilder" $ \(map S.pack -> inputs) ->
let builder = runConduitPure $ yieldMany inputs .| foldMapC byteString
ltext = toLazyByteString builder
in ltext `shouldBe` fromChunks inputs
prop "sinkLazyBuilder" $ \(map S.pack -> inputs) ->
let lbs = runConduitPure (yieldMany (map byteString inputs) .| sinkLazyBuilder)
in lbs `shouldBe` fromChunks inputs
prop "sinkNull" $ \xs toSkip -> do
res <- runConduit $ yieldMany xs .| do
takeC toSkip .| sinkNull
sinkList
res `shouldBe` drop toSkip (xs :: [Int])
prop "awaitNonNull" $ \xs ->
fmap NN.toNullable (runConduitPure $ yieldMany xs .| awaitNonNull)
`shouldBe` listToMaybe (filter (not . null) (xs :: [[Int]]))
prop "headE" $ \ (xs :: [[Int]]) ->
runConduitPure (yieldMany xs .| ((,) <$> headCE <*> foldC))
`shouldBe` (listToMaybe $ concat xs, drop 1 $ concat xs)
prop "peek" $ \xs ->
runConduitPure (yieldMany xs .| ((,) <$> peekC <*> sinkList))
`shouldBe` (listToMaybe xs, xs :: [Int])
prop "peekE" $ \ (xs :: [[Int]]) ->
runConduitPure (yieldMany xs .| ((,) <$> peekCE <*> foldC))
`shouldBe` (listToMaybe $ concat xs, concat xs)
prop "last" $ \xs ->
runConduitPure (yieldMany xs .| lastC)
`shouldBe` listToMaybe (reverse (xs :: [Int]))
prop "lastE" $ \ (xs :: [[Int]]) ->
runConduitPure (yieldMany xs .| lastCE)
`shouldBe` listToMaybe (reverse (concat xs))
prop "length" $ \xs ->
runConduitPure (yieldMany xs .| lengthC)
`shouldBe` length (xs :: [Int])
prop "lengthE" $ \ (xs :: [[Int]]) ->
runConduitPure (yieldMany xs .| lengthCE)
`shouldBe` length (concat xs)
prop "lengthIf" $ \x xs ->
runConduitPure (yieldMany xs .| lengthIfC (< x))
`shouldBe` length (filter (< x) xs :: [Int])
prop "lengthIfE" $ \x (xs :: [[Int]]) ->
runConduitPure (yieldMany xs .| lengthIfCE (< x))
`shouldBe` length (filter (< x) (concat xs))
prop "maximum" $ \xs ->
runConduitPure (yieldMany xs .| maximumC)
`shouldBe` (if null (xs :: [Int]) then Nothing else Just (maximum xs))
prop "maximumE" $ \ (xs :: [[Int]]) ->
runConduitPure (yieldMany xs .| maximumCE)
`shouldBe` (if null (concat xs) then Nothing else Just (maximum $ concat xs))
prop "minimum" $ \xs ->
runConduitPure (yieldMany xs .| minimumC)
`shouldBe` (if null (xs :: [Int]) then Nothing else Just (minimum xs))
prop "minimumE" $ \ (xs :: [[Int]]) ->
runConduitPure (yieldMany xs .| minimumCE)
`shouldBe` (if null (concat xs) then Nothing else Just (minimum $ concat xs))
prop "null" $ \xs ->
runConduitPure (yieldMany xs .| nullC)
`shouldBe` null (xs :: [Int])
prop "nullE" $ \ (xs :: [[Int]]) ->
runConduitPure (yieldMany xs .| ((,) <$> nullCE <*> foldC))
`shouldBe` (null (concat xs), concat xs)
prop "sum" $ \xs ->
runConduitPure (yieldMany xs .| sumC)
`shouldBe` sum (xs :: [Int])
prop "sumE" $ \ (xs :: [[Int]]) ->
runConduitPure (yieldMany xs .| sumCE)
`shouldBe` sum (concat xs)
prop "product" $ \xs ->
runConduitPure (yieldMany xs .| productC)
`shouldBe` product (xs :: [Int])
prop "productE" $ \ (xs :: [[Int]]) ->
runConduitPure (yieldMany xs .| productCE)
`shouldBe` product (concat xs)
prop "find" $ \x xs ->
runConduitPure (yieldMany xs .| findC (< x))
`shouldBe` find (< x) (xs :: [Int])
prop "mapM_" $ \xs ->
let res = execWriter $ runConduit $ yieldMany xs .| mapM_C (tell . return)
in res `shouldBe` (xs :: [Int])
prop "mapM_E" $ \xs ->
let res = execWriter $ runConduit $ yield xs .| mapM_CE (tell . return)
in res `shouldBe` (xs :: [Int])
prop "foldM" $ \ (xs :: [Int]) -> do
res <- runConduit $ yieldMany xs .| foldMC addM 0
res `shouldBe` sum xs
prop "foldME" $ \ (xs :: [Int]) -> do
res <- runConduit $ yield xs .| foldMCE addM 0
res `shouldBe` sum xs
it "foldMapM" $
let src = yieldMany [1..10]
res = runConduitPure $ src .| foldMapMC (return . return)
in res `shouldBe` [1..10]
it "foldMapME" $
let src = yield [1..10]
res = runConduitPure $ src .| foldMapMCE (return . return)
in res `shouldBe` [1..10]
it "sinkFile" $ do
let contents = mconcat $ replicate 1000 $ "this is some content\n"
fp = "tmp"
runConduitRes $ yield contents .| sinkFile fp
res <- S.readFile fp
res `shouldBe` contents
it "sinkHandle" $ do
let contents = mconcat $ replicate 1000 $ "this is some content\n"
fp = "tmp"
IO.withBinaryFile "tmp" IO.WriteMode $ \h -> runConduit $ yield contents .| sinkHandle h
res <- S.readFile fp
res `shouldBe` contents
it "sinkIOHandle" $ do
let contents = mconcat $ replicate 1000 $ "this is some content\n"
fp = "tmp"
open = IO.openBinaryFile "tmp" IO.WriteMode
runConduitRes $ yield contents .| sinkIOHandle open
res <- S.readFile fp
res `shouldBe` contents
prop "map" $ \input ->
runConduitPure (yieldMany input .| mapC succChar .| sinkList)
`shouldBe` map succChar input
prop "mapE" $ \(map V.fromList -> inputs) ->
runConduitPure (yieldMany inputs .| mapCE succChar .| foldC)
`shouldBe` V.map succChar (V.concat inputs)
prop "omapE" $ \(map T.pack -> inputs) ->
runConduitPure (yieldMany inputs .| omapCE succChar .| foldC)
`shouldBe` T.map succChar (T.concat inputs)
prop "concatMap" $ \ (input :: [Int]) ->
runConduitPure (yieldMany input .| concatMapC showInt .| sinkList)
`shouldBe` concatMap showInt input
prop "concatMapE" $ \ (input :: [Int]) ->
runConduitPure (yield input .| concatMapCE showInt .| foldC)
`shouldBe` concatMap showInt input
prop "take" $ \(T.pack -> input) count ->
runConduitPure (yieldMany input .| (takeC count >>= \() -> mempty) .| sinkList)
`shouldBe` T.unpack (T.take count input)
prop "takeE" $ \(T.pack -> input) count ->
runConduitPure (yield input .| (takeCE count >>= \() -> mempty) .| foldC)
`shouldBe` T.take count input
prop "takeWhile" $ \(T.pack -> input) sep ->
runConduitPure (yieldMany input .| do
x <- (takeWhileC (<= sep) >>= \() -> mempty) .| sinkList
y <- sinkList
return (x, y))
`shouldBe` span (<= sep) (T.unpack input)
prop "takeWhileE" $ \(T.pack -> input) sep ->
runConduitPure (yield input .| do
x <- (takeWhileCE (<= sep) >>= \() -> mempty) .| foldC
y <- foldC
return (x, y))
`shouldBe` T.span (<= sep) input
it "takeExactly" $
let src = yieldMany [1..10]
sink = do
x <- takeExactlyC 5 $ return 1
y <- sinkList
return (x, y)
res = runConduitPure $ src .| sink
in res `shouldBe` (1, [6..10])
it "takeExactlyE" $
let src = yield ("Hello World" :: T.Text)
sink = do
takeExactlyCE 5 (mempty :: ConduitT T.Text Void Identity ())
y <- sinkLazy
return y
res = runConduitPure $ src .| sink
in res `shouldBe` " World"
it "takeExactlyE Vector" $ do
let src = yield (V.fromList $ T.unpack "Hello World")
sink = do
x <- takeExactlyCE 5 $ return 1
y <- foldC
return (x, y)
res <- runConduit $ src .| sink
res `shouldBe` (1, V.fromList $ T.unpack " World")
it "takeExactlyE 2" $
let src = yield ("Hello World" :: T.Text)
sink = do
x <- takeExactlyCE 5 $ return 1
y <- sinkLazy
return (x, y)
res = runConduitPure $ src .| sink
-- FIXME type signature on next line is necessary in GHC 7.6.3 to
-- avoid a crash:
--
-- test: internal error: ARR_WORDS object entered!
-- (GHC version 7.6.3 for x86_64_unknown_linux)
-- Please report this as a GHC bug: http://www.haskell.org/ghc/reportabug
-- Aborted (core dumped)
--
-- Report upstream when packages are released
in res `shouldBe` (1, " World" :: TL.Text)
prop "concat" $ \input ->
runConduitPure (yield (T.pack input) .| concatC .| sinkList)
`shouldBe` input
prop "filter" $ \input ->
runConduitPure (yieldMany input .| filterC evenInt .| sinkList)
`shouldBe` filter evenInt input
prop "filterE" $ \input ->
runConduitPure (yield input .| filterCE evenInt .| foldC)
`shouldBe` filter evenInt input
prop "mapWhile" $ \input (min 20 -> highest) ->
let f i | i < highest = Just (i + 2 :: Int)
| otherwise = Nothing
res = runConduitPure $ yieldMany input .| do
x <- (mapWhileC f >>= \() -> mempty) .| sinkList
y <- sinkList
return (x, y)
(taken, dropped) = span (< highest) input
in res `shouldBe` (map (+ 2) taken, dropped)
prop "conduitVector" $ \(take 200 -> input) size' -> do
let size = min 30 $ succ $ abs size'
res <- runConduit $ yieldMany input .| conduitVector size .| sinkList
res `shouldSatisfy` all (\v -> V.length v <= size)
drop 1 (reverse res) `shouldSatisfy` all (\v -> V.length v == size)
V.concat res `shouldBe` V.fromList (input :: [Int])
prop "scanl" $ \input seed ->
let f a b = a + b :: Int
res = runConduitPure $ yieldMany input .| scanlC f seed .| sinkList
in res `shouldBe` scanl f seed input
prop "mapAccumWhile" $ \input (min 20 -> highest) ->
let f i accum | i < highest = Right (i + accum, 2 * i :: Int)
| otherwise = Left accum
res = runConduitPure $ yieldMany input .| do
(s, x) <- fuseBoth (mapAccumWhileC f 0) sinkList
y <- sinkList
return (s, x, y)
(taken, dropped) = span (< highest) input
in res `shouldBe` (sum taken, map (* 2) taken, tailSafe dropped)
prop "concatMapAccum" $ \(input :: [Int]) ->
let f a accum = (a + accum, [a, accum])
res = runConduitPure $ yieldMany input .| concatMapAccumC f 0 .| sinkList
expected = concat $ snd $ mapAccumL (flip f) 0 input
in res `shouldBe` expected
prop "intersperse" $ \xs x ->
runConduitPure (yieldMany xs .| intersperseC x .| sinkList)
`shouldBe` intersperse (x :: Int) xs
prop "mapM" $ \input ->
runConduitPure (yieldMany input .| mapMC (return . succChar) .| sinkList)
`shouldBe` map succChar input
prop "mapME" $ \(map V.fromList -> inputs) ->
runConduitPure (yieldMany inputs .| mapMCE (return . succChar) .| foldC)
`shouldBe` V.map succChar (V.concat inputs)
prop "omapME" $ \(map T.pack -> inputs) ->
runConduitPure (yieldMany inputs .| omapMCE (return . succChar) .| foldC)
`shouldBe` T.map succChar (T.concat inputs)
prop "concatMapM" $ \ (input :: [Int]) ->
runConduitPure (yieldMany input .| concatMapMC (return . showInt) .| sinkList)
`shouldBe` concatMap showInt input
prop "filterM" $ \input ->
runConduitPure (yieldMany input .| filterMC (return . evenInt) .| sinkList)
`shouldBe` filter evenInt input
prop "filterME" $ \input ->
runConduitPure (yield input .| filterMCE (return . evenInt) .| foldC)
`shouldBe` filter evenInt input
prop "iterM" $ \input -> do
(x, y) <- runWriterT $ runConduit $ yieldMany input .| iterMC (tell . return) .| sinkList
x `shouldBe` (input :: [Int])
y `shouldBe` input
prop "scanlM" $ \input seed ->
let f a b = a + b :: Int
fm a b = return $ a + b
res = runConduitPure $ yieldMany input .| scanlMC fm seed .| sinkList
in res `shouldBe` scanl f seed input
prop "mapAccumWhileM" $ \input (min 20 -> highest) ->
let f i accum | i < highest = Right (i + accum, 2 * i :: Int)
| otherwise = Left accum
res = runConduitPure $ yieldMany input .| do
(s, x) <- fuseBoth (mapAccumWhileMC ((return.).f) 0) sinkList
y <- sinkList
return (s, x, y)
(taken, dropped) = span (< highest) input
in res `shouldBe` (sum taken, map (* 2) taken, tailSafe dropped)
prop "concatMapAccumM" $ \(input :: [Int]) ->
let f a accum = (a + accum, [a, accum])
res = runConduitPure $ yieldMany input .| concatMapAccumMC ((return.).f) 0 .| sinkList
expected = concat $ snd $ mapAccumL (flip f) 0 input
in res `shouldBe` expected
prop "encode UTF8" $ \(map T.pack -> inputs) -> do
let expected = encodeUtf8 $ fromChunks inputs
actual <- runConduit
$ yieldMany inputs
.| encodeUtf8C
.| sinkLazy
actual `shouldBe` expected
prop "encode/decode UTF8" $ \(map T.pack -> inputs) (min 50 . max 1 . abs -> chunkSize) -> do
let expected = fromChunks inputs
actual <- runConduit
$ yieldMany inputs
.| encodeUtf8C
.| concatC
.| conduitVector chunkSize
.| mapC (S.pack . V.toList)
.| decodeUtf8C
.| sinkLazy
actual `shouldBe` expected
it "invalid UTF8 is an exception" $
case runConduit $ yield "\129" .| decodeUtf8C .| sinkLazy of
Left _ -> return () :: IO ()
Right x -> error $ "this should have failed, got: " ++ show x
prop "encode/decode UTF8 lenient" $ \(map T.pack -> inputs) (min 50 . max 1 . abs -> chunkSize) -> do
let expected = fromChunks inputs
actual <- runConduit
$ yieldMany inputs
.| encodeUtf8C
.| concatC
.| conduitVector chunkSize
.| mapC (S.pack . V.toList)
.| decodeUtf8LenientC
.| sinkLazy
actual `shouldBe` expected
prop "line" $ \(map T.pack -> input) size ->
let src = yieldMany input
sink = do
x <- lineC $ takeCE size .| foldC
y <- foldC
return (x, y)
res = runConduitPure $ src .| sink
expected =
let (x, y) = T.break (== '\n') (T.concat input)
in (T.take size x, T.drop 1 y)
in res `shouldBe` expected
prop "lineAscii" $ \(map S.pack -> input) size ->
let src = yieldMany input
sink = do
x <- lineAsciiC $ takeCE size .| foldC
y <- foldC
return (x, y)
res = runConduitPure $ src .| sink
expected =
let (x, y) = S.break (== 10) (S.concat input)
in (S.take size x, S.drop 1 y)
in res `shouldBe` expected
prop "unlines" $ \(map T.pack -> input) ->
runConduitPure (yieldMany input .| unlinesC .| foldC)
`shouldBe` T.unlines input
prop "unlinesAscii" $ \(map S.pack -> input) ->
runConduitPure (yieldMany input .| unlinesAsciiC .| foldC)
`shouldBe` S8.unlines input
prop "linesUnbounded" $ \(map T.pack -> input) ->
runConduitPure (yieldMany input .| (linesUnboundedC >>= \() -> mempty) .| sinkList)
`shouldBe` T.lines (T.concat input)
prop "linesUnboundedAscii" $ \(map S.pack -> input) ->
runConduitPure (yieldMany input .| (linesUnboundedAsciiC >>= \() -> mempty) .| sinkList)
`shouldBe` S8.lines (S.concat input)
it "slidingWindow 0" $
let res = runConduitPure $ yieldMany [1..5] .| slidingWindow 0 .| sinkList
in res `shouldBe` [[1],[2],[3],[4],[5]]
it "slidingWindow 1" $
let res = runConduitPure $ yieldMany [1..5] .| slidingWindow 1 .| sinkList
in res `shouldBe` [[1],[2],[3],[4],[5]]
it "slidingWindow 2" $
let res = runConduitPure $ yieldMany [1..5] .| slidingWindow 2 .| sinkList
in res `shouldBe` [[1,2],[2,3],[3,4],[4,5]]
it "slidingWindow 3" $
let res = runConduitPure $ yieldMany [1..5] .| slidingWindow 3 .| sinkList
in res `shouldBe` [[1,2,3],[2,3,4],[3,4,5]]
it "slidingWindow 4" $
let res = runConduitPure $ yieldMany [1..5] .| slidingWindow 4 .| sinkList
in res `shouldBe` [[1,2,3,4],[2,3,4,5]]
it "slidingWindow 5" $
let res = runConduitPure $ yieldMany [1..5] .| slidingWindow 5 .| sinkList
in res `shouldBe` [[1,2,3,4,5]]
it "slidingWindow 6" $
let res = runConduitPure $ yieldMany [1..5] .| slidingWindow 6 .| sinkList
in res `shouldBe` [[1,2,3,4,5]]
it "chunksOfE 1" $
let res = runConduitPure $ yieldMany [[1,2], [3,4], [5,6]] .| chunksOfE 3 .| sinkList
in res `shouldBe` [[1,2,3], [4,5,6]]
it "chunksOfE 2 (last smaller)" $
let res = runConduitPure $ yieldMany [[1,2], [3,4], [5,6,7]] .| chunksOfE 3 .| sinkList
in res `shouldBe` [[1,2,3], [4,5,6], [7]]
it "chunksOfE (ByteString)" $
let res = runConduitPure $ yieldMany [S8.pack "01234", "56789ab", "cdef", "h"] .| chunksOfE 4 .| sinkList
in res `shouldBe` ["0123", "4567", "89ab", "cdef", "h"]
it "chunksOfExactlyE 1" $
let res = runConduitPure $ yieldMany [[1,2], [3,4], [5,6]] .| chunksOfExactlyE 3 .| sinkList
in res `shouldBe` [[1,2,3], [4,5,6]]
it "chunksOfExactlyE 2 (last smaller; thus not yielded)" $
let res = runConduitPure $ yieldMany [[1,2], [3,4], [5,6,7]] .| chunksOfExactlyE 3 .| sinkList
in res `shouldBe` [[1,2,3], [4,5,6]]
prop "vectorBuilder" $ \(values :: [[Int]]) ((+1) . (`mod` 30) . abs -> size) -> do
let res = runST $ runConduit
$ yieldMany values
.| vectorBuilderC size mapM_CE
.| sinkList
expected =
loop $ concat values
where
loop [] = []
loop x =
VU.fromList y : loop z
where
(y, z) = splitAt size x
res `shouldBe` expected
prop "mapAccumS" $ \input ->
let ints = [1..]
f a s = liftM (:s) $ mapC (* a) .| takeC a .| sinkList
res = reverse $ runConduitPure $ yieldMany input
.| mapAccumS f [] (yieldMany ints)
expected = loop input ints
where loop [] _ = []
loop (a:as) xs = let (y, ys) = Prelude.splitAt a xs
in map (* a) y : loop as ys
in res `shouldBe` expected
prop "peekForever" $ \(strs' :: [String]) -> do
let strs = filter (not . null) strs'
res1 <- runConduit $ yieldMany strs .| linesUnboundedC .| sinkList
res2 <- runConduit $ yieldMany strs .| peekForever (lineC $ foldC >>= yield) .| sinkList
res2 `shouldBe` res1
prop "peekForeverE" $ \(strs :: [String]) -> do
res1 <- runConduit $ yieldMany strs .| linesUnboundedC .| sinkList
res2 <- runConduit $ yieldMany strs .| peekForeverE (lineC $ foldC >>= yield) .| sinkList
res2 `shouldBe` res1
StreamSpec.spec
evenInt :: Int -> Bool
evenInt = even
elemInt :: Int -> [Int] -> Bool
elemInt = elem
notElemInt :: Int -> [Int] -> Bool
notElemInt = notElem
addM :: Monad m => Int -> Int -> m Int
addM x y = return (x + y)
succChar :: Char -> Char
succChar c =
case pureTry (succ c) of
Left _ -> 'X' -- QuickCheck may generate characters out of range
Right x -> x
showInt :: Int -> String
showInt = Prelude.show
nocrBL :: L8.ByteString -> L8.ByteString
nocrBL = L8.filter (/= '\r')