From 748fa400b49179b34dfb5e666835fe7becb8076b Mon Sep 17 00:00:00 2001 From: Max Ulidtko Date: Sun, 25 May 2025 13:31:26 +0200 Subject: [PATCH] tests: add repro for empty conduit bug --- Codec/Archive/Zip/Conduit/Zip.hs | 1 + tests/Main.hs | 47 ++++++++++++++++++++++++++++++-- zip-stream.cabal | 1 + 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Codec/Archive/Zip/Conduit/Zip.hs b/Codec/Archive/Zip/Conduit/Zip.hs index 89c37ff..f30c187 100644 --- a/Codec/Archive/Zip/Conduit/Zip.hs +++ b/Codec/Archive/Zip/Conduit/Zip.hs @@ -221,6 +221,7 @@ zipStream ZipOptions{..} = execStateC 0 $ do P.putWord64le $ fromMaybe 0 usiz P.putWord64le $ fromMaybe 0 csiz let outsz c = stateC $ \(!o) -> (id &&& (o +) . snd) <$> c + -- let outsz c = stateC $ \(!o) -> (id &&& (o +) . snd) <$> (C.yield mempty >> c) ((cdiUsz, cdiCrc), cdiCsz) <- either (\cd -> do r@((usz, crc), csz) <- outsz cd -- write compressed data diff --git a/tests/Main.hs b/tests/Main.hs index 3453b8a..4396612 100644 --- a/tests/Main.hs +++ b/tests/Main.hs @@ -1,6 +1,7 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} module Main (main) where @@ -8,17 +9,20 @@ import Control.Monad (when, void) import Control.Monad.IO.Class (liftIO) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BSL +import Data.Conduit ((.|)) import qualified Data.Conduit as C -import Data.Conduit.Combinators (sinkNull) +import qualified Data.Conduit.Binary as C (sinkLbs, sourceLbs) +import Data.Conduit.Combinators as C -- (sinkFile, sinkNull) import Data.Foldable (for_) import qualified Data.Text as T import Data.Time.LocalTime (utc, utcToLocalTime) import Data.Time.Clock.POSIX (posixSecondsToUTCTime) import GHC.Stats (getRTSStats, RTSStats(..), GCDetails(..)) import System.Mem (performMajorGC) -import Test.Hspec (hspec, describe, it) +import Test.Hspec (describe, hspec, it, shouldBe) import Codec.Archive.Zip.Conduit.Zip +import Codec.Archive.Zip.Conduit.UnZip main :: IO () @@ -57,3 +61,42 @@ main = hspec $ do C..| sinkNull :: IO () + it "ZipDataSource behaves correctly with empty conduits" $ do + zipbytes <- C.runConduitRes + $ entries + .| void (zipStream defaultZipOptions) + .| C.sinkLbs + C.runConduitRes $ C.sourceLbs zipbytes .| C.sinkFile "/tmp/test.zip" + ZipInfo{..} <- C.runConduitRes + $ C.sourceLbs zipbytes + .| C.fuseUpstream unZipStream (C.awaitForever assertItem) + zipComment `shouldBe` "" + where + entries = do + C.yield ( simpleZipEntry "roses.txt" + , ZipDataSource (C.yield "Roses are red\n") + ) + C.yield (simpleZipEntry "empty_OK_1.txt", ZipDataByteString "") + C.yield (simpleZipEntry "empty_OK_2.txt", ZipDataSource emptySingleChunk) + C.yield (simpleZipEntry "empty_BUG.txt", ZipDataSource emptyNoYield) + C.yield (simpleZipEntry "trailer.txt", ZipDataByteString "FIN") + + emptySingleChunk = C.yield "" + emptyNoYield = mempty -- return () + + posixEpoch = utcToLocalTime utc (posixSecondsToUTCTime 0) + simpleZipEntry fname = ZipEntry{..} where + zipEntryName = Left fname + zipEntryTime = posixEpoch + zipEntrySize = Nothing + zipEntryExternalAttributes = Nothing + + assertItem (Right _) = fail "Unexpected leading or directory data contents" + assertItem (Left ZipEntry{..}) = liftIO $ do + zipEntryTime `shouldBe` posixEpoch + when (zipEntryName == Left "roses.txt") $ zipEntrySize `shouldBe` Just 14 + when (zipEntryName == Left "empty_OK_1.txt") $ zipEntrySize `shouldBe` Just 0 + when (zipEntryName == Left "empty_OK_2.txt") $ zipEntrySize `shouldBe` Just 0 + when (zipEntryName == Left "empty_BUG.txt") $ zipEntrySize `shouldBe` Just 0 + when (zipEntryName == Left "trailer.txt") $ zipEntrySize `shouldBe` Just 3 + diff --git a/zip-stream.cabal b/zip-stream.cabal index 1e4e779..7451cf3 100644 --- a/zip-stream.cabal +++ b/zip-stream.cabal @@ -89,6 +89,7 @@ test-suite tests , zip-stream , bytestring , conduit + , conduit-extra , hspec , text , time