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 e74c089..33b7481 100644
--- a/lib/cpp/test/Makefile.am
+++ b/lib/cpp/test/Makefile.am
@@ -50,6 +50,7 @@
SpecializationTest \
AllProtocolsTest \
TransportTest \
+ ZlibTest \
UnitTests
TESTS = \
@@ -67,6 +68,11 @@
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 @@
$(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 920f06c..9788eee 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 <stdint.h>
#include <inttypes.h>
+#include <getopt.h>
#include <cstddef>
-#include <cassert>
#include <fstream>
#include <iostream>
+#include <tr1/functional>
#include <boost/random.hpp>
+#include <boost/shared_array.hpp>
+#include <boost/test/unit_test.hpp>
#include <transport/TBufferTransports.h>
#include <transport/TZlibTransport.h>
+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 @@
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<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf, false));
+ zlib_trans->write(buf, buf_len);
+ zlib_trans->flush();
- uint32_t seed = time(NULL);
- printf("seed: %"PRIu32"\n", seed);
- rng.seed(time(NULL));
+ boost::shared_array<uint8_t> 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;
+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<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> 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));
- for (uint8_t** bufp = &buffers[0]; *bufp != NULL; ++bufp) {
- vector<uint8_t> content(*bufp, (*bufp) + buf_len);
- vector<uint8_t> mirror;
+ boost::shared_array<uint8_t> 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();
+}
- // Let's just start with the big dog!
- {
- mirror.clear();
- shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
- shared_ptr<TZlibTransport> 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<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> 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<uint8_t*>(
+ reinterpret_cast<const uint8_t*>(tmp_buf.data())),
+ tmp_buf.length());
+
+ boost::shared_array<uint8_t> 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<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> 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<TMemoryBuffer> membuf(new TMemoryBuffer());
- shared_ptr<TZlibTransport> 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<uint8_t> 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<TMemoryBuffer> membuf(new TMemoryBuffer());
- shared_ptr<TZlibTransport> 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<uint8_t*>(
- reinterpret_cast<const uint8_t*>(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();
+}
- // 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<TMemoryBuffer> membuf(new TMemoryBuffer());
- shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf, false));
- int idx;
- unsigned int tot;
+void test_invalid_checksum(const uint8_t* buf, uint32_t buf_len) {
+ // Verify checksum checking.
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> 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<uint8_t*>(
+ reinterpret_cast<const uint8_t*>(tmp_buf.data())),
+ tmp_buf.length());
- 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++;
- }
+ boost::shared_array<uint8_t> 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);
+ }
+}
- zlib_trans->flush();
- mirror.resize(content.size());
+#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)
- 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 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);
- assert(mirror == content);
- zlib_trans->verifyChecksum();
- }
- }
-
- // Verify checksum checking.
- {
- mirror.clear();
- shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
- shared_ptr<TZlibTransport> 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<uint8_t*>(
- reinterpret_cast<const uint8_t*>(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);
- }
+ 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]);
}
}
- return 0;
+ ADD_TEST_CASE(suite, name, test_invalid_checksum, buf, buf_len);
+}
+
+void print_usage(FILE* f, const char* argv0) {
+ fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
+ fprintf(f, "Options:\n");
+ fprintf(f, " --seed=<N>, -s <N>\n");
+ fprintf(f, " --help\n");
+}
+
+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;
+ }
+
+ 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);
+ }
+ }
+
+ 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;
}