// // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/beast // // Test that header file is self-contained. #include #include #include #include #include #include "zlib-1.2.11/zlib.h" namespace boost { namespace beast { namespace zlib { class inflate_stream_test : public beast::unit_test::suite { public: // Lots of repeats, limited char range static std::string corpus1(std::size_t n) { static std::string const alphabet{ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" }; std::string s; s.reserve(n + 5); std::mt19937 g; std::uniform_int_distribution d0{ 0, alphabet.size() - 1}; std::uniform_int_distribution d1{ 1, 5}; while(s.size() < n) { auto const rep = d1(g); auto const ch = alphabet[d0(g)]; s.insert(s.end(), rep, ch); } s.resize(n); return s; } // Random data static std::string corpus2(std::size_t n) { std::string s; s.reserve(n); std::mt19937 g; std::uniform_int_distribution d0{0, 255}; while(n--) s.push_back(static_cast(d0(g))); return s; } static std::string compress( string_view const& in, int level, // 0=none, 1..9, -1=default int windowBits, // 9..15 int memLevel, // 1..9 (8=default) int strategy) // e.g. Z_DEFAULT_STRATEGY { int result; z_stream zs; memset(&zs, 0, sizeof(zs)); result = deflateInit2( &zs, level, Z_DEFLATED, -windowBits, memLevel, strategy); if(result != Z_OK) throw std::logic_error{"deflateInit2 failed"}; zs.next_in = (Bytef*)in.data(); zs.avail_in = static_cast(in.size()); std::string out; out.resize(deflateBound(&zs, static_cast(in.size()))); zs.next_in = (Bytef*)in.data(); zs.avail_in = static_cast(in.size()); zs.next_out = (Bytef*)&out[0]; zs.avail_out = static_cast(out.size()); result = deflate(&zs, Z_FULL_FLUSH); if(result != Z_OK) throw std::logic_error("deflate failed"); out.resize(zs.total_out); deflateEnd(&zs); return out; } //-------------------------------------------------------------------------- enum Split { once, half, full }; class Beast { Split in_; Split check_; Flush flush_; public: Beast(Split in, Split check, Flush flush = Flush::sync) : in_(in) , check_(check) , flush_(flush) { } void operator()( int window, std::string const& in, std::string const& check, unit_test::suite& suite) const { auto const f = [&](std::size_t i, std::size_t j) { std::string out(check.size(), 0); z_params zs; zs.next_in = in.data(); zs.next_out = &out[0]; zs.avail_in = i; zs.avail_out = j; inflate_stream is; is.reset(window); bool bi = ! (i < in.size()); bool bo = ! (j < check.size()); for(;;) { error_code ec; is.write(zs, flush_, ec); if( ec == error::need_buffers || ec == error::end_of_stream) { out.resize(zs.total_out); suite.expect(out == check, __FILE__, __LINE__); break; } if(ec) { suite.fail(ec.message(), __FILE__, __LINE__); break; } if(zs.avail_in == 0 && ! bi) { bi = true; zs.avail_in = in.size() - i; } if(zs.avail_out == 0 && ! bo) { bo = true; zs.avail_out = check.size() - j; } } }; std::size_t i0, i1; std::size_t j0, j1; switch(in_) { default: case once: i0 = in.size(); i1 = i0; break; case half: i0 = in.size() / 2; i1 = i0; break; case full: i0 = 1; i1 = in.size(); break; } switch(check_) { default: case once: j0 = check.size(); j1 = j0; break; case half: j0 = check.size() / 2; j1 = j0; break; case full: j0 = 1; j1 = check.size(); break; } for(std::size_t i = i0; i <= i1; ++i) for(std::size_t j = j0; j <= j1; ++j) f(i, j); } }; class Matrix { unit_test::suite& suite_; int level_[2]; int window_[2]; int strategy_[2]; public: explicit Matrix(unit_test::suite& suite) : suite_(suite) { level_[0] = 0; level_[1] = 9; window_[0] = 9; window_[1] = 15; strategy_[0] = 0; strategy_[1] = 4; } void level(int from, int to) { level_[0] = from; level_[1] = to; } void level(int what) { level(what, what); } void window(int from, int to) { window_[0] = from; window_[1] = to; } void window(int what) { window(what, what); } void strategy(int from, int to) { strategy_[0] = from; strategy_[1] = to; } void strategy(int what) { strategy(what, what); } template void operator()( F const& f, std::string const& check) const { for(auto level = level_[0]; level <= level_[1]; ++level) { for(auto window = window_[0]; window <= window_[1]; ++window) { for(auto strategy = strategy_[0]; strategy <= strategy_[1]; ++strategy) f( window, compress(check, level, window, 4, strategy), check, suite_); } } } }; void testInflate() { { Matrix m{*this}; std::string check = "{\n \"AutobahnPython/0.6.0\": {\n" " \"1.1.1\": {\n" " \"behavior\": \"OK\",\n" " \"behaviorClose\": \"OK\",\n" " \"duration\": 2,\n" " \"remoteCloseCode\": 1000,\n" " \"reportfile\": \"autobahnpython_0_6_0_case_1_1_1.json\"\n" ; m(Beast{half, half}, check); } { Matrix m{*this}; auto const check = corpus1(5000); m(Beast{half, half}, check); } { Matrix m{*this}; auto const check = corpus2(5000); m(Beast{half, half}, check); } { Matrix m{*this}; auto const check = corpus1(1000); m.level(6); m.window(9); m.strategy(Z_DEFAULT_STRATEGY); m(Beast{once, full}, check); } { Matrix m{*this}; auto const check = corpus2(1000); m.level(6); m.window(9); m.strategy(Z_DEFAULT_STRATEGY); m(Beast{once, full}, check); } { Matrix m{*this}; m.level(6); m.window(9); auto const check = corpus1(200); m(Beast{full, full}, check); } { Matrix m{*this}; m.level(6); m.window(9); auto const check = corpus2(500); m(Beast{full, full}, check); } { Matrix m{*this}; auto const check = corpus2(1000); m.level(6); m.window(9); m.strategy(Z_DEFAULT_STRATEGY); m(Beast{full, once, Flush::block}, check); } // VFALCO Fails, but I'm unsure of what the correct // behavior of Z_TREES/Flush::trees is. #if 0 { Matrix m{*this}; auto const check = corpus2(10000); m.level(6); m.window(9); m.strategy(Z_DEFAULT_STRATEGY); m(Beast{full, once, Flush::trees}, check); } #endif } void run() override { log << "sizeof(inflate_stream) == " << sizeof(inflate_stream) << std::endl; testInflate(); } }; BEAST_DEFINE_TESTSUITE(beast,zlib,inflate_stream); } // zlib } // beast } // boost