ZCodec::Compress read a fresh chunk over whatever deflate had left unconsumed. deflate stops as soon as its output buffer is full, and the write-back that frees that buffer only ran at the top of the next iteration, by which time the tail of the previous chunk was gone. Nothing said so. EndCompression answers with the number of bytes taken and no caller compared it, so the result was a shorter document than the one handed in, compressed perfectly well. Passing 100000 bytes through a 64 byte output buffer took 49152 of them. Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com> Change-Id: I1044a46aa1d1f424e7d699a927e01a8427768919 Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13208 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins CPCI <releng@collaboraoffice.com> (cherry picked from commit 23e2e9c2136d8b5c9fd6d9604bccd35e9b657571) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211555 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
157 lines
6.7 KiB
C++
157 lines
6.7 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include <tools/zcodec.hxx>
|
|
#include <tools/stream.hxx>
|
|
#include <rtl/crc.h>
|
|
#include <cppunit/TestAssert.h>
|
|
#include <cppunit/TestFixture.h>
|
|
#include <cppunit/extensions/HelperMacros.h>
|
|
#include <cppunit/plugin/TestPlugIn.h>
|
|
|
|
namespace
|
|
{
|
|
// Sample text for compression
|
|
constexpr const char* DUMMY_TEXT
|
|
= "Lorem ipsum dolor sit amet, consectetur adipiscing elit,\n"
|
|
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n"
|
|
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
|
|
"commodo consequat.\n";
|
|
constexpr auto DUMMY_SIZE = std::char_traits<char>::length(DUMMY_TEXT);
|
|
|
|
class ZCodecTest : public CppUnit::TestFixture
|
|
{
|
|
private:
|
|
void testGzCompressDecompress();
|
|
void testMakeDummyFile();
|
|
void testZlibCompressDecompress();
|
|
void testSmallOutputBuffer();
|
|
|
|
CPPUNIT_TEST_SUITE(ZCodecTest);
|
|
CPPUNIT_TEST(testMakeDummyFile);
|
|
CPPUNIT_TEST(testGzCompressDecompress);
|
|
CPPUNIT_TEST(testZlibCompressDecompress);
|
|
CPPUNIT_TEST(testSmallOutputBuffer);
|
|
CPPUNIT_TEST_SUITE_END();
|
|
};
|
|
|
|
// Creates a stream from DUMMY_TEXT to compress and decompress
|
|
std::unique_ptr<SvMemoryStream> makeDummyFile()
|
|
{
|
|
SvMemoryStream* pRet = new SvMemoryStream();
|
|
pRet->WriteBytes(DUMMY_TEXT, DUMMY_SIZE);
|
|
return std::unique_ptr<SvMemoryStream>(pRet);
|
|
}
|
|
|
|
// Test that the stream generated from makeDummyFile is valid
|
|
void ZCodecTest::testMakeDummyFile()
|
|
{
|
|
auto pStream = makeDummyFile();
|
|
// Check for null
|
|
CPPUNIT_ASSERT_MESSAGE("pStream is null", pStream);
|
|
decltype(DUMMY_SIZE) size = pStream->GetSize();
|
|
// Check size
|
|
CPPUNIT_ASSERT_EQUAL(DUMMY_SIZE, size);
|
|
}
|
|
|
|
// Test that ZCodec::Compress and ZCodec::Decompress work for gzlib = true
|
|
// Compares the CRC32 checksum of the initial stream and the compressed->decompressed stream
|
|
void ZCodecTest::testGzCompressDecompress()
|
|
{
|
|
auto pInitialStream = makeDummyFile();
|
|
SvMemoryStream pCompressedStream;
|
|
SvMemoryStream pDecompressedStream;
|
|
auto nInitialStreamCRC32 = rtl_crc32(0, pInitialStream->GetData(), pInitialStream->GetSize());
|
|
ZCodec aCodec;
|
|
aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, true);
|
|
// Set compression metadata for compressing a GZ file
|
|
aCodec.SetCompressionMetadata("dummy.txt"_ostr, 0, nInitialStreamCRC32);
|
|
aCodec.Compress(*pInitialStream, pCompressedStream);
|
|
auto nCompressedSize
|
|
= aCodec.EndCompression(); // returns compressed size or -1 if compression failed
|
|
// Check that the compression succeeded
|
|
CPPUNIT_ASSERT_GREATER(static_cast<tools::Long>(0), nCompressedSize);
|
|
// No need to Seek(0) here because ZCodec::InitDecompress does that already for gz
|
|
aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, true);
|
|
aCodec.Decompress(pCompressedStream, pDecompressedStream);
|
|
auto nDecompressedSize
|
|
= aCodec.EndCompression(); // returns decompressed size or -1 if decompression failed
|
|
// Check that the decompressed text size is equal to the original
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long>(DUMMY_SIZE), nDecompressedSize);
|
|
auto nCompressedDecompressedStreamCRC32
|
|
= rtl_crc32(0, pDecompressedStream.GetData(), pDecompressedStream.GetSize());
|
|
// Check that the initial and decompressed CRC32 checksums are the same -> gz (de)compression works
|
|
CPPUNIT_ASSERT_EQUAL(nInitialStreamCRC32, nCompressedDecompressedStreamCRC32);
|
|
}
|
|
|
|
// Test that ZCodec::Compress and ZCodec::Decompress work for gzlib = false
|
|
// Compares the CRC32 checksum of the initial stream and the compressed->decompressed stream
|
|
void ZCodecTest::testZlibCompressDecompress()
|
|
{
|
|
auto pInitialStream = makeDummyFile();
|
|
SvMemoryStream pCompressedStream;
|
|
SvMemoryStream pDecompressedStream;
|
|
auto nInitialStreamCRC32 = rtl_crc32(0, pInitialStream->GetData(), pInitialStream->GetSize());
|
|
ZCodec aCodec;
|
|
aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, false);
|
|
aCodec.Compress(*pInitialStream, pCompressedStream);
|
|
auto nCompressedSize
|
|
= aCodec.EndCompression(); // returns compressed size or -1 if compression failed
|
|
// Check that the compression succeeded
|
|
CPPUNIT_ASSERT_GREATER(static_cast<tools::Long>(0), nCompressedSize);
|
|
pCompressedStream.Seek(0);
|
|
aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, false);
|
|
aCodec.Decompress(pCompressedStream, pDecompressedStream);
|
|
auto nDecompressedSize
|
|
= aCodec.EndCompression(); // returns decompressed size or -1 if decompression failed
|
|
// Check that the decompressed text size is equal to the original
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long>(DUMMY_SIZE), nDecompressedSize);
|
|
auto nCompressedDecompressedStreamCRC32
|
|
= rtl_crc32(0, pDecompressedStream.GetData(), pDecompressedStream.GetSize());
|
|
// Check that the initial and decompressed CRC32 checksums are the same -> zlib (de)compression works
|
|
CPPUNIT_ASSERT_EQUAL(nInitialStreamCRC32, nCompressedDecompressedStreamCRC32);
|
|
}
|
|
|
|
// Test that nothing is dropped when deflate runs out of output part way through a chunk of
|
|
// input. A small output buffer makes that happen on almost every call.
|
|
void ZCodecTest::testSmallOutputBuffer()
|
|
{
|
|
SvMemoryStream aInitialStream;
|
|
sal_uInt32 nSeed = 1;
|
|
for (sal_uInt32 i = 0; i < 100000; ++i)
|
|
{
|
|
nSeed = nSeed * 1103515245 + 12345;
|
|
const sal_uInt8 nByte(nSeed >> 24);
|
|
aInitialStream.WriteBytes(&nByte, 1);
|
|
}
|
|
const sal_uInt64 nInitialSize = aInitialStream.GetSize();
|
|
const auto nInitialCRC32 = rtl_crc32(0, aInitialStream.GetData(), nInitialSize);
|
|
|
|
SvMemoryStream aCompressedStream;
|
|
SvMemoryStream aDecompressedStream;
|
|
ZCodec aCodec(0x4000, 64);
|
|
aCodec.BeginCompression();
|
|
aCodec.Compress(aInitialStream, aCompressedStream);
|
|
// EndCompression answers with the bytes deflate took, which has to be all of them
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long>(nInitialSize), aCodec.EndCompression());
|
|
|
|
aCompressedStream.Seek(0);
|
|
aCodec.BeginCompression();
|
|
aCodec.Decompress(aCompressedStream, aDecompressedStream);
|
|
CPPUNIT_ASSERT_EQUAL(static_cast<tools::Long>(nInitialSize), aCodec.EndCompression());
|
|
CPPUNIT_ASSERT_EQUAL(
|
|
nInitialCRC32, rtl_crc32(0, aDecompressedStream.GetData(), aDecompressedStream.GetSize()));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
CPPUNIT_TEST_SUITE_REGISTRATION(ZCodecTest);
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|