sensei-0.7.0: test/ReadHandleSpec.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns #-}
module ReadHandleSpec (spec) where
import Helper
import Test.QuickCheck
import qualified Data.ByteString as B
import ReadHandle
chunkByteString :: (Int, Int) -> ByteString -> Gen [ByteString]
chunkByteString size = go
where
go "" = return []
go xs = do
n <- chooseInt size
let (chunk, rest) = B.splitAt n xs
(chunk :) <$> go rest
fakeHandle :: [ByteString] -> IO ReadHandle
fakeHandle chunks = ReadHandle <$> stubAction chunks <*> newEmptyBuffer
data ChunkSizes = SmallChunks | BigChunks
withRandomChunkSizes :: [ByteString] -> (ReadHandle -> Expectation) -> Property
withRandomChunkSizes (mconcat -> input) action = property $ do
chunkSizes <- elements [SmallChunks, BigChunks]
let
maxChunkSize = case chunkSizes of
SmallChunks -> 4
BigChunks -> B.length input
chunks <- chunkByteString (1, maxChunkSize) input
return $ fakeHandle chunks >>= action
partialMarker :: ByteString
partialMarker = B.take 5 marker
spec :: Spec
spec = do
describe "drain" $ do
it "drains all remaining input" $ do
h <- fakeHandle ["foo", marker, "bar", marker, "baz", marker, ""]
withSpy (drain h) `shouldReturn` ["foo", "bar", "baz"]
describe "getResult" $ do
context "with a single result" $ do
let input = ["foo", "bar", "baz", marker]
it "returns result" $ do
withSpy $ \ echo -> do
h <- fakeHandle input
getResult h echo `shouldReturn` "foobarbaz"
`shouldReturn` ["foo", "bar", "baz"]
context "with chunks of arbitrary size" $ do
it "returns result" $ do
withRandomChunkSizes input $ \ h -> do
fmap mconcat . withSpy $ \ echo -> do
getResult h echo `shouldReturn` "foobarbaz"
`shouldReturn` "foobarbaz"
context "with multiple results" $ do
let input = ["foo", marker, "bar", marker, "baz", marker]
it "returns one result at a time" $ do
withSpy $ \ echo -> do
h <- fakeHandle input
getResult h echo `shouldReturn` "foo"
getResult h echo `shouldReturn` "bar"
getResult h echo `shouldReturn` "baz"
`shouldReturn` ["foo", "bar", "baz"]
context "with chunks of arbitrary size" $ do
it "returns one result at a time" $ do
withRandomChunkSizes input $ \ h -> do
fmap mconcat . withSpy $ \ echo -> do
getResult h echo `shouldReturn` "foo"
getResult h echo `shouldReturn` "bar"
getResult h echo `shouldReturn` "baz"
`shouldReturn` "foobarbaz"
context "when a chunk that contains a marker ends with a partial marker" $ do
it "correctly gives the marker precedence over the partial marker" $ do
withSpy $ \ echo -> do
h <- fakeHandle ["foo" <> marker <> "bar" <> partialMarker, ""]
getResult h echo `shouldReturn` "foo"
getResult h echo `shouldReturn` ("bar" <> partialMarker)
`shouldReturn` ["foo", "bar", partialMarker]
context "on EOF" $ do
it "returns all remaining input" $ do
withSpy $ \ echo -> do
h <- fakeHandle ["foo", "bar", "baz", ""]
getResult h echo `shouldReturn` "foobarbaz"
`shouldReturn` ["foo", "bar", "baz"]
context "with a partialMarker at the end" $ do
it "includes the partial marker in the output" $ do
withSpy $ \ echo -> do
h <- fakeHandle ["foo", "bar", "baz", partialMarker, ""]
getResult h echo `shouldReturn` ("foobarbaz" <> partialMarker)
`shouldReturn` ["foo", "bar", "baz", partialMarker]
context "after a marker" $ do
it "returns all remaining input" $ do
withSpy $ \ echo -> do
h <- fakeHandle ["foo", "bar", "baz", marker, "qux", ""]
getResult h echo `shouldReturn` "foobarbaz"
getResult h echo `shouldReturn` "qux"
`shouldReturn` ["foo", "bar", "baz", "qux"]