packages feed

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"]