From: David Reiss Date: Wed, 6 Oct 2010 17:10:23 +0000 (+0000) Subject: THRIFT-929. cpp: Convert ZlibTest to use the boost unit test framework X-Git-Tag: 0.6.0~111 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=9a961e7750bd9d0393802df4d7a3bb6996a3339b;p=common%2Fthrift.git THRIFT-929. cpp: Convert ZlibTest to use the boost unit test framework git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@1005149 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/lib/cpp/test/Makefile.am b/lib/cpp/test/Makefile.am index e74c0895..33b74816 100644 --- a/lib/cpp/test/Makefile.am +++ b/lib/cpp/test/Makefile.am @@ -50,6 +50,7 @@ check_PROGRAMS = \ SpecializationTest \ AllProtocolsTest \ TransportTest \ + ZlibTest \ UnitTests TESTS = \ @@ -67,6 +68,11 @@ TransportTest_SOURCES = \ TransportTest_LDADD = libtestgencpp.la $(top_builddir)/lib/cpp/libthriftz.la -l:libboost_unit_test_framework.a -lz +ZlibTest_SOURCES = \ + ZlibTest.cpp + +ZlibTest_LDADD = libtestgencpp.la $(top_builddir)/lib/cpp/libthriftz.la -l:libboost_unit_test_framework.a -lz + # # TFDTransportTest # @@ -156,7 +162,6 @@ clean-local: $(RM) -r gen-cpp EXTRA_DIST = \ - ZlibTest.cpp \ DenseProtoTest.cpp \ ThriftTest_extras.cpp \ DebugProtoTest_extras.cpp diff --git a/lib/cpp/test/ZlibTest.cpp b/lib/cpp/test/ZlibTest.cpp index 920f06cb..9788eeee 100644 --- a/lib/cpp/test/ZlibTest.cpp +++ b/lib/cpp/test/ZlibTest.cpp @@ -17,30 +17,32 @@ * under the License. */ -/* -thrift --gen cpp DebugProtoTest.thrift -g++ -Wall -g -I../lib/cpp/src -I/usr/local/include/boost-1_33_1 \ - ZlibTest.cpp \ - ../lib/cpp/.libs/libthriftz.a ../lib/cpp/.libs/libthrift.a \ - -lz -o ZlibTest -./ZlibTest -*/ - #define __STDC_LIMIT_MACROS #define __STDC_FORMAT_MACROS +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // needed for getopt_long +#endif + #include #include +#include #include -#include #include #include +#include #include +#include +#include #include #include +using namespace std; +using namespace boost; +using namespace apache::thrift::transport; + // Distributions of reads and writes meant to approximate a real load, // mixing up small and large while also hitting various boundary conditions. @@ -224,171 +226,242 @@ uint8_t* gen_random_buffer(uint32_t buf_len) { return buf; } -int main() { - using namespace std; - using namespace boost; - using namespace apache::thrift::transport; +void test_write_then_read(const uint8_t* buf, uint32_t buf_len) { + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf, false)); + zlib_trans->write(buf, buf_len); + zlib_trans->flush(); + + boost::shared_array mirror(new uint8_t[buf_len]); + uint32_t got = zlib_trans->read(mirror.get(), buf_len); + BOOST_REQUIRE_EQUAL(got, buf_len); + BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf, buf_len), 0); + zlib_trans->verifyChecksum(); +} - uint32_t seed = time(NULL); - printf("seed: %"PRIu32"\n", seed); - rng.seed(time(NULL)); +void test_separate_checksum(const uint8_t* buf, uint32_t buf_len) { + // This one is tricky. I separate the last byte of the stream out + // into a separate crbuf_. The last byte is part of the checksum, + // so the entire read goes fine, but when I go to verify the checksum + // it isn't there. The original implementation complained that + // the stream was not complete. I'm about to go fix that. + // It worked. Awesome. + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf, false)); + zlib_trans->write(buf, buf_len); + zlib_trans->flush(); + string tmp_buf; + membuf->appendBufferToString(tmp_buf); + zlib_trans.reset(new TZlibTransport(membuf, false, + TZlibTransport::DEFAULT_URBUF_SIZE, + tmp_buf.length()-1)); + + boost::shared_array mirror(new uint8_t[buf_len]); + uint32_t got = zlib_trans->read(mirror.get(), buf_len); + BOOST_REQUIRE_EQUAL(got, buf_len); + BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf, buf_len), 0); + zlib_trans->verifyChecksum(); +} - uint32_t buf_len = 1024*32; - uint8_t* buffers[4]; - buffers[0] = gen_uniform_buffer(buf_len, 'a'); - buffers[1] = gen_compressible_buffer(buf_len); - buffers[2] = gen_random_buffer(buf_len); - buffers[3] = NULL; - - for (uint8_t** bufp = &buffers[0]; *bufp != NULL; ++bufp) { - vector content(*bufp, (*bufp) + buf_len); - vector mirror; - - // Let's just start with the big dog! - { - mirror.clear(); - shared_ptr membuf(new TMemoryBuffer()); - shared_ptr zlib_trans(new TZlibTransport(membuf, false)); - zlib_trans->write(&content[0], content.size()); - zlib_trans->flush(); - mirror.resize(content.size()); - uint32_t got = zlib_trans->read(&mirror[0], mirror.size()); - assert(got == content.size()); - assert(mirror == content); - zlib_trans->verifyChecksum(); +void test_incomplete_checksum(const uint8_t* buf, uint32_t buf_len) { + // Make sure we still get that "not complete" error if + // it really isn't complete. + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf, false)); + zlib_trans->write(buf, buf_len); + zlib_trans->flush(); + string tmp_buf; + membuf->appendBufferToString(tmp_buf); + tmp_buf.erase(tmp_buf.length() - 1); + membuf->resetBuffer(const_cast( + reinterpret_cast(tmp_buf.data())), + tmp_buf.length()); + + boost::shared_array mirror(new uint8_t[buf_len]); + uint32_t got = zlib_trans->read(mirror.get(), buf_len); + BOOST_REQUIRE_EQUAL(got, buf_len); + BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf, buf_len), 0); + try { + zlib_trans->verifyChecksum(); + BOOST_ERROR("verifyChecksum() did not report an error"); + } catch (TTransportException& ex) { + BOOST_CHECK_EQUAL(ex.getType(), TTransportException::CORRUPTED_DATA); + } +} + +void test_read_write_mix(const uint8_t* buf, uint32_t buf_len, + unsigned int* write_dist, unsigned int* read_dist) { + // Try it with a mix of read/write sizes. + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf, false)); + int idx; + unsigned int tot; + + idx = 0; + tot = 0; + while (tot < buf_len) { + uint32_t write_len = write_dist[idx]; + if (tot + write_len > buf_len) { + write_len = buf_len - tot; } + zlib_trans->write(buf + tot, write_len); + tot += write_len; + idx++; + } - // This one is tricky. I separate the last byte of the stream out - // into a separate crbuf_. The last byte is part of the checksum, - // so the entire read goes fine, but when I go to verify the checksum - // it isn't there. The original implementation complained that - // the stream was not complete. I'm about to go fix that. - // It worked. Awesome. - { - mirror.clear(); - shared_ptr membuf(new TMemoryBuffer()); - shared_ptr zlib_trans(new TZlibTransport(membuf, false)); - zlib_trans->write(&content[0], content.size()); - zlib_trans->flush(); - string tmp_buf; - membuf->appendBufferToString(tmp_buf); - zlib_trans.reset(new TZlibTransport(membuf, false, - TZlibTransport::DEFAULT_URBUF_SIZE, - tmp_buf.length()-1)); - mirror.resize(content.size()); - uint32_t got = zlib_trans->read(&mirror[0], mirror.size()); - assert(got == content.size()); - assert(mirror == content); - zlib_trans->verifyChecksum(); + zlib_trans->flush(); + + idx = 0; + tot = 0; + boost::shared_array mirror(new uint8_t[buf_len]); + while (tot < buf_len) { + uint32_t read_len = read_dist[idx]; + uint32_t expected_read_len = read_len; + if (tot + read_len > buf_len) { + expected_read_len = buf_len - tot; } + uint32_t got = zlib_trans->read(mirror.get() + tot, read_len); + BOOST_REQUIRE_EQUAL(got, expected_read_len); + tot += got; + idx++; + } - // Make sure we still get that "not complete" error if - // it really isn't complete. - { - mirror.clear(); - shared_ptr membuf(new TMemoryBuffer()); - shared_ptr zlib_trans(new TZlibTransport(membuf, false)); - zlib_trans->write(&content[0], content.size()); - zlib_trans->flush(); - string tmp_buf; - membuf->appendBufferToString(tmp_buf); - tmp_buf.erase(tmp_buf.length() - 1); - membuf->resetBuffer(const_cast( - reinterpret_cast(tmp_buf.data())), - tmp_buf.length()); - mirror.resize(content.size()); - uint32_t got = zlib_trans->read(&mirror[0], mirror.size()); - assert(got == content.size()); - assert(mirror == content); - try { - zlib_trans->verifyChecksum(); - assert(false); - } catch (TTransportException& ex) { - assert(ex.getType() == TTransportException::CORRUPTED_DATA); - } + BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf, buf_len), 0); + zlib_trans->verifyChecksum(); +} + +void test_invalid_checksum(const uint8_t* buf, uint32_t buf_len) { + // Verify checksum checking. + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf, false)); + zlib_trans->write(buf, buf_len); + zlib_trans->flush(); + string tmp_buf; + membuf->appendBufferToString(tmp_buf); + // Modify a byte at the end of the buffer (part of the checksum). + // On rare occasions, modifying a byte in the middle of the buffer + // isn't caught by the checksum. + // + // (This happens especially often for the uniform buffer. The + // re-inflated data is correct, however. I suspect in this case that + // we're more likely to modify bytes that are part of zlib metadata + // instead of the actual compressed data.) + // + // I've also seen some failure scenarios where a checksum failure isn't + // reported, but zlib keeps trying to decode past the end of the data. + // (When this occurs, verifyChecksum() throws an exception indicating + // that the end of the data hasn't been reached.) I haven't seen this + // error when only modifying checksum bytes. + int index = tmp_buf.size() - 1; + tmp_buf[index]++; + membuf->resetBuffer(const_cast( + reinterpret_cast(tmp_buf.data())), + tmp_buf.length()); + + boost::shared_array mirror(new uint8_t[buf_len]); + try { + zlib_trans->read(mirror.get(), buf_len); + zlib_trans->verifyChecksum(); + BOOST_ERROR("verifyChecksum() did not report an error"); + } catch (TZlibTransportException& ex) { + BOOST_CHECK_EQUAL(ex.getType(), TTransportException::INTERNAL_ERROR); + } +} + +#define ADD_TEST_CASE(suite, name, function, ...) \ + do { \ + ::std::ostringstream name_ss; \ + name_ss << name << "-" << BOOST_STRINGIZE(function); \ + ::boost::unit_test::test_case* tc = ::boost::unit_test::make_test_case( \ + ::std::tr1::bind(function, ## __VA_ARGS__), \ + name_ss.str()); \ + (suite)->add(tc); \ + } while (0) + +void add_tests(unit_test::test_suite* suite, + const uint8_t* buf, + uint32_t buf_len, + const char* name) { + ADD_TEST_CASE(suite, name, test_write_then_read, buf, buf_len); + ADD_TEST_CASE(suite, name, test_separate_checksum, buf, buf_len); + ADD_TEST_CASE(suite, name, test_incomplete_checksum, buf, buf_len); + + for (int d1 = 0; d1 < 3; d1++) { + for (int d2 = 0; d2 < 3; d2++) { + ADD_TEST_CASE(suite, name << "_w" << d1 << "_r" << d2, + test_read_write_mix, buf, buf_len, dist[d1], dist[d2]); } + } - // Try it with a mix of read/write sizes. - for (int d1 = 0; d1 < 3; d1++) { - for (int d2 = 0; d2 < 3; d2++) { - mirror.clear(); - shared_ptr membuf(new TMemoryBuffer()); - shared_ptr zlib_trans(new TZlibTransport(membuf, false)); - int idx; - unsigned int tot; - - idx = 0; - tot = 0; - while (tot < content.size()) { - uint32_t write_len = dist[d1][idx]; - if (tot + write_len > content.size()) { - write_len = content.size() - tot; - } - zlib_trans->write(&content[tot], write_len); - tot += write_len; - idx++; - } + ADD_TEST_CASE(suite, name, test_invalid_checksum, buf, buf_len); +} - zlib_trans->flush(); - mirror.resize(content.size()); - - idx = 0; - tot = 0; - while (tot < mirror.size()) { - uint32_t read_len = dist[d2][idx]; - uint32_t expected_read_len = read_len; - if (tot + read_len > content.size()) { - expected_read_len = content.size() - tot; - } - uint32_t got = zlib_trans->read(&mirror[tot], read_len); - assert(got == expected_read_len); - tot += got; - idx++; - } +void print_usage(FILE* f, const char* argv0) { + fprintf(f, "Usage: %s [boost_options] [options]\n", argv0); + fprintf(f, "Options:\n"); + fprintf(f, " --seed=, -s \n"); + fprintf(f, " --help\n"); +} - assert(mirror == content); - zlib_trans->verifyChecksum(); - } +void parse_args(int argc, char* argv[]) { + uint32_t seed = 0; + bool has_seed = false; + + struct option long_opts[] = { + { "help", false, NULL, 'h' }, + { "seed", true, NULL, 's' }, + { NULL, 0, NULL, 0 } + }; + + while (true) { + optopt = 1; + int optchar = getopt_long(argc, argv, "hs:", long_opts, NULL); + if (optchar == -1) { + break; } - // Verify checksum checking. - { - mirror.clear(); - shared_ptr membuf(new TMemoryBuffer()); - shared_ptr zlib_trans(new TZlibTransport(membuf, false)); - zlib_trans->write(&content[0], content.size()); - zlib_trans->flush(); - string tmp_buf; - membuf->appendBufferToString(tmp_buf); - // Modify a byte at the end of the buffer (part of the checksum). - // On rare occasions, modifying a byte in the middle of the buffer - // isn't caught by the checksum. - // - // (This happens especially often for the uniform buffer. The - // re-inflated data is correct, however. I suspect in this case that - // we're more likely to modify bytes that are part of zlib metadata - // instead of the actual compressed data.) - // - // I've also seen some failure scenarios where a checksum failure isn't - // reported, but zlib keeps trying to decode past the end of the data. - // (When this occurs, verifyChecksum() throws an exception indicating - // that the end of the data hasn't been reached.) I haven't seen this - // error when only modifying checksum bytes. - int index = tmp_buf.size() - 1; - tmp_buf[index]++; - membuf->resetBuffer(const_cast( - reinterpret_cast(tmp_buf.data())), - tmp_buf.length()); - mirror.resize(content.size()); - try { - zlib_trans->read(&mirror[0], mirror.size()); - zlib_trans->verifyChecksum(); - assert(false); - } catch (TZlibTransportException& ex) { - assert(ex.getType() == TTransportException::INTERNAL_ERROR); + switch (optchar) { + case 's': { + char *endptr; + seed = strtol(optarg, &endptr, 0); + if (endptr == optarg || *endptr != '\0') { + fprintf(stderr, "invalid seed value \"%s\": must be a positive " + "integer\n", optarg); + exit(1); + } + has_seed = true; + break; } + case 'h': + print_usage(stdout, argv[0]); + exit(0); + case '?': + exit(1); + default: + // Only happens if someone adds another option to the optarg string, + // but doesn't update the switch statement to handle it. + fprintf(stderr, "unknown option \"-%c\"\n", optchar); + exit(1); } } - return 0; + if (!has_seed) { + seed = time(NULL); + } + + printf("seed: %" PRIu32 "\n", seed); + rng.seed(seed); +} + +unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { + parse_args(argc, argv); + + unit_test::test_suite* suite = BOOST_TEST_SUITE("ZlibTests"); + + uint32_t buf_len = 1024*32; + add_tests(suite, gen_uniform_buffer(buf_len, 'a'), buf_len, "uniform"); + add_tests(suite, gen_compressible_buffer(buf_len), buf_len, "compressible"); + add_tests(suite, gen_random_buffer(buf_len), buf_len, "random"); + + return suite; }