lz4-bytes-0.2.0.0: test/Main.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Bytes (Bytes)
import Data.Primitive (ByteArray,sizeofByteArray)
import Data.Word (Word8)
import Lz4.Block (compress, compressHighly, decompress)
import Test.Tasty (TestTree, defaultMain, testGroup)
import Test.Tasty.HUnit (testCase,(@=?))
import Test.Tasty.QuickCheck (Gen, choose, forAll, testProperty, vectorOf, (===))
import qualified Data.Bytes as Bytes
import qualified GHC.Exts as Exts
import qualified Lz4.Frame as Frame
main :: IO ()
main = defaultMain tests
tests :: TestTree
tests = testGroup "lz4"
[ testGroup "block"
[ testProperty "roundtrip" $ forAll genBytes $ \bs ->
let cs = compress 1 bs in
Just bs
===
decompress (Bytes.length bs) cs
, testProperty "roundtrip-HC" $ forAll genBytes $ \bs ->
let cs = compressHighly 3 bs in
Just bs
===
decompress (Bytes.length bs) cs
]
, testGroup "frame"
[ testProperty "roundtrip-HC" $ forAll genByteArray $ \bs ->
let cs = Frame.compressHighlyU 3 (Bytes.fromByteArray bs) in
Right bs
===
Frame.decompressU (sizeofByteArray bs) (Bytes.fromByteArray cs)
, testCase "roundtrip-HC-80k" $ testRoundtrip 80_000
, testCase "roundtrip-HC-800k" $ testRoundtrip 800_000
, testCase "roundtrip-HC-2M" $ testRoundtrip 2_000_000
, testCase "roundtrip-HC-5M" $ testRoundtrip 5_000_000
, testCase "roundtrip-HC-9M" $ testRoundtrip 9_000_000
, testCase "packing-HC-80k" $ testPacking 80_000
, testCase "packing-HC-800k" $ testPacking 800_000
, testCase "packing-HC-2M" $ testPacking 2_000_000
, testCase "packing-HC-5M" $ testPacking 5_000_000
, testCase "packing-HC-9M" $ testPacking 9_000_000
, testCase "example-a" $ case Frame.decompressU 20 (Bytes.fromByteArray exampleA) of
Left{} -> fail "decompression failed"
Right _ -> pure ()
, testCase "example-b" $ case Frame.decompressU 10 (Bytes.fromByteArray exampleB) of
Left{} -> fail "decompression failed"
Right x -> x @=? Exts.fromList [0xbb :: Word8, 0x01, 0xbb, 0x01, 0xbb, 0x01, 0xbb, 0x01, 0xbb, 0x01 ]
, testCase "example-c" $ case Frame.decompressU 20 (Bytes.fromByteArray exampleC) of
Left e -> fail ("decompression failed, error code: " ++ show e)
Right _ -> pure ()
]
]
testRoundtrip :: Int -> IO ()
testRoundtrip sz =
let uncompressed = Bytes.replicate sz 0x01 in
let compressed = Frame.compressHighlyU 3 uncompressed in
case Frame.decompressU sz (Bytes.fromByteArray compressed) of
Left{} -> fail "decompression failed"
Right result -> if uncompressed == Bytes.fromByteArray result
then pure ()
else fail "compression and decompression did not round trip"
testPacking :: Int -> IO ()
testPacking sz =
let uncompressed = Bytes.replicate sz 0x01 in
let compressed = Frame.compressHighlyU 3 uncompressed in
let lenCompressed = sizeofByteArray compressed in
if lenCompressed * 100 > sz
then fail "Repetition of same byte has compression factor less than 100"
else pure ()
genBytes :: Gen Bytes
genBytes = do
n <- choose (0, 200)
bs <- vectorOf n (choose (0, 2 :: Word8))
pure (Exts.fromList bs)
genByteArray :: Gen ByteArray
genByteArray = fmap Bytes.toByteArray genBytes
-- Example from Clickhouse arrow output
-- Compressed Length: 28
-- Decompressed Length: 20
exampleA :: ByteArray
exampleA = Exts.fromList
[ 0x04, 0x22, 0x4d, (0x18 :: Word8)
, 0x60, 0x40, 0x82
, 0x0d, 0x00, 0x00, 0x00 -- little-endian encoding of the number 13
, 0x47, 0x15, 0x08, 0x01, 0x0a, 0x04 , 0x00, 0x50, 0x0a, 0x15, 0x08, 0x01, 0x0a
, 0x00, 0x00, 0x00, 0x00
]
-- Same as exampleA, but the flag indicates that linked blocks are used.
-- There is only one block, so there aren't actually linked blocks, but
-- this lets us force the decompress function to take the slow path
-- where it sets up a decompression context.
exampleC :: ByteArray
exampleC = Exts.fromList
[ 0x04, 0x22, 0x4d, (0x18 :: Word8)
, 0x40, 0x40, 0xc0
, 0x0d, 0x00, 0x00, 0x00 -- little-endian encoding of the number 13
, 0x47, 0x15, 0x08, 0x01, 0x0a, 0x04 , 0x00, 0x50, 0x0a, 0x15, 0x08, 0x01, 0x0a
, 0x00, 0x00, 0x00, 0x00
]
-- Example that tests a frame that does not use compression
exampleB :: ByteArray
exampleB = Exts.fromList
[ 0x04, 0x22, 0x4d, (0x18 :: Word8)
, 0x60, 0x40, 0x82
, 0x0a, 0x00, 0x00, 0x80 -- little-endian encoding of 10 but with the high bit set to disable compression
, 0xbb, 0x01
, 0xbb, 0x01
, 0xbb, 0x01
, 0xbb, 0x01
, 0xbb, 0x01
, 0x00, 0x00, 0x00, 0x00
]