{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import Codec.Compression.LZ4.Conduit (compress, decompress, bsChunksOf)
import Control.Monad (when)
import Control.Monad.IO.Unlift (MonadUnliftIO)
import Control.Monad.Trans.Resource (ResourceT, runResourceT)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Lazy.Char8 as BSL8
import Data.Conduit
import Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import qualified Data.Conduit.Process as CP
import Data.List (intersperse)
import Test.Hspec
import Test.Hspec.QuickCheck (modifyMaxSize)
import qualified Test.QuickCheck as QC
import qualified Test.QuickCheck.Monadic as QCM
runCompressToLZ4 :: (MonadUnliftIO m) => ConduitT () ByteString (ResourceT m) () -> m ByteString
runCompressToLZ4 source = runResourceT $ do
(_, result, _) <- CP.sourceCmdWithStreams "lz4 -d" (source .| compress) CL.consume CL.consume
return $ BS.concat result
runLZ4ToDecompress :: (MonadUnliftIO m) => ConduitT () ByteString (ResourceT m) () -> m ByteString
runLZ4ToDecompress source = runResourceT $ do
(_, result, _) <- CP.sourceCmdWithStreams "lz4 -c" source (decompress .| CL.consume) CL.consume
return $ BS.concat result
main :: IO ()
main = do
-- Big memory tests are disabled by default to be kind to packagers and CI.
let skipBigmemTests = True
skipBigmemTest = when skipBigmemTests $ pendingWith "skipped by default due to big RAM requirement"
let prepare :: [BSL.ByteString] -> [ByteString]
prepare strings = BSL.toChunks $ BSL.concat $ intersperse " " $ ["BEGIN"] ++ strings ++ ["END"]
hspec $ do
describe "bsChunksOf" $ do
it "chunks up a string" $ do
bsChunksOf 3 "abc123def4567" `shouldBe` ["abc", "123", "def", "456", "7"]
describe "Compression" $ do
it "compresses simple string" $ do
let string = "hellohellohellohello"
actual <- runCompressToLZ4 (yield string)
actual `shouldBe` string
it "compresses 100000 integers" $ do
let strings = prepare $ map (BSL8.pack . show) [1..100000 :: Int]
actual <- runCompressToLZ4 (CL.sourceList strings)
actual `shouldBe` (BS.concat strings)
it "compresses 100000 strings" $ do
let strings = prepare $ replicate 100000 "hello"
actual <- runCompressToLZ4 (CL.sourceList strings)
actual `shouldBe` (BS.concat strings)
it "compresses 1MB ByteString" $ do
let bs = BS.replicate 100000 42
actual <- runCompressToLZ4 (CB.sourceLbs $ BSL.fromStrict bs)
actual `shouldBe` bs
it "compresses 5GiB ByteString" $ do -- more than 32-bit many Bytes
skipBigmemTest
let bs = BS.replicate (5 * 1024*1024*1024) 42
actual <- runCompressToLZ4 (CB.sourceLbs $ BSL.fromStrict bs)
actual `shouldBe` bs
describe "Decompression" $ do
it "decompresses simple string" $ do
let string = "hellohellohellohello"
actual <- runLZ4ToDecompress (yield string)
actual `shouldBe` string
it "decompresses 100000 integers" $ do
let strings = prepare $ map (BSL8.pack . show) [1..100000 :: Int]
actual <- runLZ4ToDecompress (CL.sourceList strings)
actual `shouldBe` (BS.concat strings)
it "decompresses 100000 strings" $ do
let strings = prepare $ replicate 100000 "hello"
actual <- runLZ4ToDecompress (CL.sourceList strings)
actual `shouldBe` (BS.concat strings)
it "decompresses 1MB ByteString" $ do
let bs = BS.replicate 100000 42
actual <- runLZ4ToDecompress (CB.sourceLbs $ BSL.fromStrict bs)
actual `shouldBe` bs
it "decompresses 5GiB ByteString" $ do -- more than 32-bit many Bytes
skipBigmemTest
let bs = BS.replicate (5 * 1024*1024*1024) 42
actual <- runLZ4ToDecompress (CB.sourceLbs $ BSL.fromStrict bs)
actual `shouldBe` bs
describe "Identity" $ do
modifyMaxSize (const 10000) $ it "compress and decompress arbitrary strings"$
QC.property $ \string -> QCM.monadicIO $ do
let bs = BSL.toChunks $ BSL8.pack string
actual <- QCM.run (runConduitRes $ CL.sourceList bs .| compress .| decompress .| CL.consume)
QCM.assert(BS.concat bs == BS.concat actual)