packages feed

streaming-bytestring-0.1.5: tests/test.hs

import Test.Tasty
import Test.Tasty.SmallCheck
import Test.SmallCheck.Series

import Control.Applicative
import Data.Functor.Identity
import qualified Data.ByteString.Char8 as BS8
import qualified Data.ByteString.Streaming.Char8 as SBS8
import qualified Data.ByteString.Streaming.Internal as SBSI
import Text.Printf
import qualified Streaming.Prelude as S
import qualified Streaming as SM
import Data.String (fromString)
import Data.Functor.Identity
import qualified Data.List as L

listOf :: Monad m => Series m a -> Series m [a]
listOf a = decDepth $
  pure [] \/ ((:) <$> a <~> listOf a)

strSeries :: Monad m => Series m String
strSeries = listOf (generate $ const ['a', 'b', '\n'])

strSeriesCrlf :: Monad m => Series m String
strSeriesCrlf = fmap L.concat $ listOf (generate $ const ["a", "b", "\r\n"])

chunksSeries :: Monad m => Series m [String]
chunksSeries = listOf strSeries

nats :: Monad m => Series m Int
nats = generate $ \d -> [1..d]

fromChunks :: [String] -> SBS8.ByteString Identity ()
fromChunks = SBS8.fromChunks . S.each .  map BS8.pack

unix2dos :: String -> String
unix2dos = concatMap $ \c -> if c == '\n' then "\r\n" else [c]

unpackToString :: SBS8.ByteString Identity () -> String
unpackToString = runIdentity . S.toList_ . SBS8.unpack

s_lines :: SBS8.ByteString Identity () -> [BS8.ByteString]
s_lines
  = runIdentity
  . S.toList_
  . S.mapped SBS8.toStrict
  . SBS8.lines

noNullChunks :: S.Stream (SBS8.ByteString Identity) Identity () -> Bool
noNullChunks = SM.streamFold (\() -> True) runIdentity go
  where
  go :: SBS8.ByteString Identity Bool -> Bool
  go (SBSI.Empty b) = b
  go (SBSI.Chunk bs sbs) = not (BS8.null bs) && go sbs
  go (SBSI.Go (Identity sbs)) = go sbs

main = defaultMain $ testGroup "Tests"
  [ testGroup "lines" $
    [ testProperty "Data.ByteString.Streaming.Char8.lines is equivalent to Prelude.lines" $ over chunksSeries $ \chunks ->
        -- This only makes sure that the streaming-bytestring lines function
        -- matches the Prelude lines function when no carriage returns
        -- are present. They are not expected to have the same behavior
        -- with dos-style line termination.
        let expected = lines $ concat chunks
            got = (map BS8.unpack . s_lines . fromChunks) chunks
        in
        if expected == got
          then Right ""
          else Left (printf "Expected %s; got %s" (show expected) (show got) :: String)
    , testProperty "lines recognizes DOS line endings" $ over strSeries $ \str ->
        s_lines (SBS8.string $ unix2dos str) == s_lines (SBS8.string str)
    , testProperty "lines recognizes DOS line endings with tiny chunks" $ over strSeries $ \str ->
        s_lines (mapM_ SBS8.singleton $ unix2dos str) == s_lines (mapM_ SBS8.singleton str)
    , testProperty "lineSplit does not create null chunks (LF)" $ over ((,) <$> nats <~> strSeries) $ \(n,str) ->
        noNullChunks (SBS8.lineSplit n (fromString str))
    , testProperty "lineSplit does not create null chunks (CRLF)" $ over ((,) <$> nats <~> strSeriesCrlf) $ \(n,str) ->
        noNullChunks (SBS8.lineSplit n (fromString str))
    , testProperty "concat after lineSplit round trips (LF)" $ over ((,) <$> nats <~> strSeries) $ \(n,str) ->
        unpackToString (SBS8.concat (SBS8.lineSplit n (fromString str))) == str
    , testProperty "concat after lineSplit round trips (CRLF)" $ over ((,) <$> nats <~> strSeriesCrlf) $ \(n,str) ->
        unpackToString (SBS8.concat (SBS8.lineSplit n (fromString str))) == str
    ]
  ]