Refactor zlib tests and fix typo:

close #1744

* Fixed typo in enum name
* Added missing zlib error enumerator
* Marked caveat in zlib impl to avoid future confusion
* Created Compressor/Decompressor abstractions in Beast.zlib tests
* Applied abstractions on Beast.zlib tests
* Workaround Travis-CI' 10min silence timeout
* Add test for Beast.zlib's need_dict error
* Avoid breaking user-code dependant on deprecated enumerator interface

Signed-off-by: AeroStun <24841307+AeroStun@users.noreply.github.com>
This commit is contained in:
AeroStun authored and Vinnie Falco committed 2019-11-02 08:29:30 -07:00
1 parent 26a156e300
commit d895906bcc
7 files changed
+443 -194

No files matched your search

+248 -136
View File
@@ -25,6 +25,138 @@ namespace zlib {
class deflate_stream_test : public beast::unit_test::suite
{
struct ICompressor {
virtual void init() = 0;
virtual void init(
int level,
int windowBits,
int memLevel,
int strategy) = 0;
virtual std::size_t avail_in() const noexcept = 0;
virtual void avail_in(std::size_t) noexcept = 0;
virtual void const* next_in() const noexcept = 0;
virtual void next_in(const void*) noexcept = 0;
virtual std::size_t avail_out() const noexcept = 0;
virtual void avail_out(std::size_t) noexcept = 0;
virtual void* next_out() const noexcept = 0;
virtual void next_out(void*) noexcept = 0;
virtual std::size_t total_out() const noexcept = 0;
virtual std::size_t bound(std::size_t) = 0;
virtual error_code write(Flush) = 0;
virtual ~ICompressor() = default;
};
class ZlibCompressor : public ICompressor {
z_stream zs{};
public:
ZlibCompressor() = default;
void init() override {
deflateEnd(&zs);
zs = {};
const auto res = deflateInit2(&zs, -1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
if(res != Z_OK)
throw std::invalid_argument{"zlib compressor: failure"};
}
void init(
int level,
int windowBits,
int memLevel,
int strategy) override
{
deflateEnd(&zs);
zs = {};
const auto res = deflateInit2(&zs, level, Z_DEFLATED, -windowBits, memLevel, strategy);
if(res != Z_OK)
BOOST_THROW_EXCEPTION(std::invalid_argument{"zlib compressor: bad arg"});
}
virtual std::size_t avail_in() const noexcept override { return zs.avail_in; }
virtual void avail_in(std::size_t n) noexcept override { zs.avail_in = n; }
virtual void const* next_in() const noexcept override { return zs.next_in; }
virtual void next_in(const void* ptr) noexcept override { zs.next_in = const_cast<Bytef*>(static_cast<const Bytef*>(ptr)); }
virtual std::size_t avail_out() const noexcept override { return zs.avail_out; }
virtual void avail_out(std::size_t n_out) noexcept override { zs.avail_out = n_out; }
virtual void* next_out() const noexcept override { return zs.next_out; }
virtual void next_out(void* ptr) noexcept override { zs.next_out = (Bytef*)ptr; }
virtual std::size_t total_out() const noexcept override { return zs.total_out; }
std::size_t bound(std::size_t src_size) override {
return deflateBound(&zs, static_cast<uLong>(src_size));
}
error_code write(Flush flush) override {
constexpr static int zlib_flushes[] = {0, Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH, Z_TREES};
const auto zlib_flush = zlib_flushes[static_cast<int>(flush)];
if(zs.next_in == nullptr && zs.avail_in != 0)
BOOST_THROW_EXCEPTION(std::invalid_argument{"zlib compressor: invalid input"});
const auto res = deflate(&zs, zlib_flush);
switch(res){
case Z_OK:
return {};
case Z_STREAM_END:
return error::end_of_stream;
case Z_STREAM_ERROR:
return error::stream_error;
case Z_BUF_ERROR:
return error::need_buffers;
default:
throw;
}
}
~ZlibCompressor() override {
deflateEnd(&zs);
}
} zlib_compressor;
class BeastCompressor : public ICompressor {
z_params zp;
deflate_stream ds;
public:
BeastCompressor() = default;
void init() override {
zp = {};
ds.clear();
ds.reset();
}
void init(
int level,
int windowBits,
int memLevel,
int strategy) override
{
zp = {};
ds.clear();
ds.reset(
level,
windowBits,
memLevel,
toStrategy(strategy));
}
virtual std::size_t avail_in() const noexcept override { return zp.avail_in; }
virtual void avail_in(std::size_t n) noexcept override { zp.avail_in = n; }
virtual void const* next_in() const noexcept override { return zp.next_in; }
virtual void next_in(const void* ptr) noexcept override { zp.next_in = ptr; }
virtual std::size_t avail_out() const noexcept override { return zp.avail_out; }
virtual void avail_out(std::size_t n_out) noexcept override { zp.avail_out = n_out; }
virtual void* next_out() const noexcept override { return zp.next_out; }
virtual void next_out(void* ptr) noexcept override { zp.next_out = (Bytef*)ptr; }
virtual std::size_t total_out() const noexcept override { return zp.total_out; }
std::size_t bound(std::size_t src_size) override {
return ds.upper_bound(src_size);
}
error_code write(Flush flush) override {
error_code ec{};
ds.write(zp, flush, ec);
return ec;
}
~BeastCompressor() override = default;
} beast_compressor;
public:
// Lots of repeats, limited char range
static
@@ -151,6 +283,7 @@ public:
using self = deflate_stream_test;
typedef void(self::*pmf_t)(
ICompressor& c,
int level, int windowBits, int memLevel,
int strategy, std::string const&);
@@ -171,29 +304,26 @@ public:
void
doDeflate1_beast(
ICompressor& c,
int level, int windowBits, int memLevel,
int strategy, std::string const& check)
{
std::string out;
z_params zs;
deflate_stream ds;
ds.reset(
c.init(
level,
windowBits,
memLevel,
toStrategy(strategy));
out.resize(ds.upper_bound(
static_cast<uLong>(check.size())));
zs.next_in = (Bytef*)check.data();
zs.avail_in = static_cast<uInt>(check.size());
zs.next_out = (Bytef*)out.data();
zs.avail_out = static_cast<uInt>(out.size());
strategy);
out.resize(c.bound(check.size()));
c.next_in(check.data());
c.avail_in(check.size());
c.next_out((void*)out.data());
c.avail_out(out.size());
{
bool progress = true;
for(;;)
{
error_code ec;
ds.write(zs, Flush::full, ec);
error_code ec = c.write(Flush::full);
if( ec == error::need_buffers ||
ec == error::end_of_stream) // per zlib FAQ
goto fin;
@@ -206,7 +336,7 @@ public:
}
fin:
out.resize(zs.total_out);
out.resize(c.total_out());
BEAST_EXPECT(decompress(out) == check);
err:
@@ -217,6 +347,7 @@ public:
void
doDeflate2_beast(
ICompressor& c,
int level, int windowBits, int memLevel,
int strategy, std::string const& check)
{
@@ -224,50 +355,44 @@ public:
{
for(std::size_t j = 1;; ++j)
{
z_params zs;
deflate_stream ds;
ds.reset(
c.init(
level,
windowBits,
memLevel,
toStrategy(strategy));
strategy);
std::string out;
out.resize(ds.upper_bound(
static_cast<uLong>(check.size())));
out.resize(c.bound(check.size()));
if(j >= out.size())
break;
zs.next_in = (Bytef*)check.data();
zs.avail_in = static_cast<uInt>(i);
zs.next_out = (Bytef*)out.data();
zs.avail_out = static_cast<uInt>(j);
c.next_in((void*)check.data());
c.avail_in(i);
c.next_out((void*)out.data());
c.avail_out(j);
bool bi = false;
bool bo = false;
for(;;)
{
error_code ec;
ds.write(zs,
bi ? Flush::full : Flush::none, ec);
error_code ec = c.write(
bi ? Flush::full : Flush::none);
if( ec == error::need_buffers ||
ec == error::end_of_stream) // per zlib FAQ
goto fin;
if(! BEAST_EXPECTS(! ec, ec.message()))
goto err;
if(zs.avail_in == 0 && ! bi)
if(c.avail_in() == 0 && ! bi)
{
bi = true;
zs.avail_in =
static_cast<uInt>(check.size() - i);
c.avail_in(check.size() - i);
}
if(zs.avail_out == 0 && ! bo)
if(c.avail_out() == 0 && ! bo)
{
bo = true;
zs.avail_out =
static_cast<uInt>(out.size() - j);
c.avail_out(out.size() - j);
}
}
fin:
out.resize(zs.total_out);
out.resize(c.total_out());
BEAST_EXPECT(decompress(out) == check);
err:
@@ -279,7 +404,7 @@ public:
//--------------------------------------------------------------------------
void
doMatrix(std::string const& check, pmf_t pmf)
doMatrix(ICompressor& c, std::string const& check, pmf_t pmf)
{
for(int level = 0; level <= 9; ++level)
{
@@ -293,127 +418,115 @@ public:
for (int memLevel = 8; memLevel <= 9; ++memLevel)
{
(this->*pmf)(
level, windowBits, memLevel, strategy, check);
c, level, windowBits, memLevel, strategy, check);
}
}
}
}
// Check default settings
(this->*pmf)(compression::default_size, 15, 8, 0, check);
(this->*pmf)(c, compression::default_size, 15, 8, 0, check);
}
void
testDeflate()
testDeflate(ICompressor& c)
{
doMatrix("Hello, world!", &self::doDeflate1_beast);
doMatrix("Hello, world!", &self::doDeflate2_beast);
doMatrix(corpus1(56), &self::doDeflate2_beast);
doMatrix(corpus1(1024), &self::doDeflate1_beast);
doMatrix(c, "Hello, world!", &self::doDeflate1_beast);
doMatrix(c, "Hello, world!", &self::doDeflate2_beast);
log << "no-silence keepalive" << std::endl;
doMatrix(c, corpus1(56), &self::doDeflate2_beast);
doMatrix(c, corpus1(1024), &self::doDeflate1_beast);
}
void testInvalidSettings()
void testInvalidSettings(ICompressor& c)
{
except<std::invalid_argument>(
[]()
[&]()
{
deflate_stream ds;
ds.reset(-42, 15, 8, Strategy::normal);
c.init(-42, 15, 8, static_cast<int>(Strategy::normal));
});
except<std::invalid_argument>(
[]()
[&]()
{
deflate_stream ds;
ds.reset(compression::default_size, -1, 8, Strategy::normal);
c.init(compression::default_size, -1, 8, static_cast<int>(Strategy::normal));
});
except<std::invalid_argument>(
[]()
[&]()
{
deflate_stream ds;
ds.reset(compression::default_size, 15, -1, Strategy::normal);
c.init(compression::default_size, 15, -1, static_cast<int>(Strategy::normal));
});
except<std::invalid_argument>(
[]()
[&]()
{
deflate_stream ds;
ds.reset();
z_params zp{};
zp.avail_in = 1;
zp.next_in = nullptr;
error_code ec;
ds.write(zp, Flush::full, ec);
c.init();
c.avail_in(1);
c.next_in(nullptr);
c.write(Flush::full);
});
}
void
testWriteAfterFinish()
testWriteAfterFinish(ICompressor& c)
{
z_params zp;
deflate_stream ds;
ds.reset();
c.init();
std::string out;
out.resize(1024);
string_view s = "Hello";
zp.next_in = s.data();
zp.avail_in = s.size();
zp.next_out = &out.front();
zp.avail_out = out.size();
error_code ec;
ds.write(zp, Flush::sync, ec);
c.next_in(s.data());
c.avail_in(s.size());
c.next_out(&out.front());
c.avail_out(out.size());
error_code ec = c.write(Flush::sync);
BEAST_EXPECT(!ec);
zp.next_in = nullptr;
zp.avail_in = 0;
ds.write(zp, Flush::finish, ec);
c.next_in(nullptr);
c.avail_in(0);
ec = c.write(Flush::finish);
BEAST_EXPECT(ec == error::end_of_stream);
zp.next_in = s.data();
zp.avail_in = s.size();
zp.next_out = &out.front();
zp.avail_out = out.size();
ds.write(zp, Flush::sync, ec);
c.next_in(s.data());
c.avail_in(s.size());
c.next_out(&out.front());
c.avail_out(out.size());
ec = c.write(Flush::sync);
BEAST_EXPECT(ec == error::stream_error);
ds.write(zp, Flush::finish, ec);
ec = c.write(Flush::finish);
BEAST_EXPECT(ec == error::need_buffers);
}
void
testFlushPartial()
testFlushPartial(ICompressor& c)
{
z_params zp;
deflate_stream ds;
ds.reset();
c.init();
std::string out;
out.resize(1024);
string_view s = "Hello";
zp.next_in = s.data();
zp.avail_in = s.size();
zp.next_out = &out.front();
zp.avail_out = out.size();
c.next_in(s.data());
c.avail_in(s.size());
c.next_out(&out.front());
c.avail_out(out.size());
error_code ec;
ds.write(zp, Flush::none, ec);
ec = c.write(Flush::none);
BEAST_EXPECT(!ec);
ds.write(zp, Flush::partial, ec);
ec = c.write(Flush::partial);
BEAST_EXPECT(!ec);
}
void
testFlushAtLiteralBufferFull()
testFlushAtLiteralBufferFull(ICompressor& c)
{
struct fixture
{
explicit fixture(std::size_t n, Strategy s)
ICompressor& c;
explicit fixture(ICompressor&c, std::size_t n, Strategy s) : c(c)
{
ds.reset(8, 15, 1, s);
std::iota(in.begin(), in.end(),
static_cast<std::uint8_t>(0));
c.init(8, 15, 1, static_cast<int>(s));
std::iota(in.begin(), in.end(), std::uint8_t{0});
out.resize(n);
zp.next_in = in.data();
zp.avail_in = in.size();
zp.next_out = &out.front();
zp.avail_out = out.size();
c.next_in(in.data());
c.avail_in(in.size());
c.next_out(&out.front());
c.avail_out(out.size());
}
z_params zp;
deflate_stream ds;
std::array<std::uint8_t, 255> in;
std::string out;
};
@@ -421,25 +534,22 @@ public:
for (auto s : {Strategy::huffman, Strategy::rle, Strategy::normal})
{
{
fixture f{264, s};
error_code ec;
f.ds.write(f.zp, Flush::finish, ec);
fixture f{c, 264, s};
error_code ec = c.write(Flush::finish);
BEAST_EXPECT(ec == error::end_of_stream);
BEAST_EXPECT(f.zp.avail_out == 1);
BEAST_EXPECT(c.avail_out() == 1);
}
{
fixture f{263, s};
error_code ec;
f.ds.write(f.zp, Flush::finish, ec);
fixture f{c,263, s};
error_code ec = c.write(Flush::finish);
BEAST_EXPECT(!ec);
BEAST_EXPECT(f.zp.avail_out == 0);
BEAST_EXPECT(c.avail_out() == 0);
}
{
fixture f{20, s};
error_code ec;
f.ds.write(f.zp, Flush::sync, ec);
fixture f{c, 20, s};
error_code ec = c.write(Flush::sync);
BEAST_EXPECT(!ec);
}
@@ -447,35 +557,30 @@ public:
}
void
testRLEMatchLengthExceedLookahead()
testRLEMatchLengthExceedLookahead(ICompressor& c)
{
z_params zp;
deflate_stream ds;
std::vector<std::uint8_t> in;
in.resize(300);
ds.reset(8, 15, 1, Strategy::rle);
c.init(8, 15, 1, static_cast<int>(Strategy::rle));
std::fill_n(in.begin(), 4, 'a');
std::string out;
out.resize(in.size() * 2);
zp.next_in = in.data();
zp.avail_in = in.size();
zp.next_out = &out.front();
zp.avail_out = out.size();
c.next_in(in.data());
c.avail_in(in.size());
c.next_out(&out.front());
c.avail_out(out.size());
error_code ec;
ds.write(zp, Flush::sync, ec);
ec = c.write(Flush::sync);
BEAST_EXPECT(!ec);
}
void
testFlushAfterDistMatch()
testFlushAfterDistMatch(ICompressor& c)
{
for (auto out_size : {144, 129})
{
z_params zp;
deflate_stream ds;
std::array<std::uint8_t, 256> in{};
// 125 will mostly fill the lit buffer, so emitting a distance code
// results in a flush.
@@ -485,16 +590,16 @@ public:
std::iota(in.begin() + n, in.end(),
static_cast<std::uint8_t>(0));
ds.reset(8, 15, 1, Strategy::normal);
c.init(8, 15, 1, static_cast<int>(Strategy::normal));
std::string out;
out.resize(out_size);
zp.next_in = in.data();
zp.avail_in = in.size();
zp.next_out = &out.front();
zp.avail_out = out.size();
c.next_in(in.data());
c.avail_in(in.size());
c.next_out(&out.front());
c.avail_out(out.size());
error_code ec;
ds.write(zp, Flush::sync, ec);
ec = c.write(Flush::sync);
BEAST_EXPECT(!ec);
}
}
@@ -506,13 +611,20 @@ public:
"sizeof(deflate_stream) == " <<
sizeof(deflate_stream) << std::endl;
testDeflate();
testInvalidSettings();
testWriteAfterFinish();
testFlushPartial();
testFlushAtLiteralBufferFull();
testRLEMatchLengthExceedLookahead();
testFlushAfterDistMatch();
testDeflate(zlib_compressor);
testDeflate(beast_compressor);
testInvalidSettings(zlib_compressor);
testInvalidSettings(beast_compressor);
testWriteAfterFinish(zlib_compressor);
testWriteAfterFinish(beast_compressor);
testFlushPartial(zlib_compressor);
testFlushPartial(beast_compressor);
testFlushAtLiteralBufferFull(zlib_compressor);
testFlushAtLiteralBufferFull(beast_compressor);
testRLEMatchLengthExceedLookahead(zlib_compressor);
testRLEMatchLengthExceedLookahead(beast_compressor);
testFlushAfterDistMatch(zlib_compressor);
testFlushAfterDistMatch(beast_compressor);
}
};