summaryrefslogtreecommitdiffstats
path: root/src/jaegertracing/thrift/lib/cpp/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
commit19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch)
tree42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/jaegertracing/thrift/lib/cpp/test
parentInitial commit. (diff)
downloadceph-upstream/16.2.11+ds.tar.xz
ceph-upstream/16.2.11+ds.zip
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.cpp47
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.tcc225
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/AnnotationTest.cpp68
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/Base64Test.cpp74
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/Benchmark.cpp244
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/CMakeLists.txt390
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest.cpp310
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest_extras.cpp35
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/EnumTest.cpp108
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/GenericHelpers.h107
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/JSONProtoTest.cpp343
-rwxr-xr-xsrc/jaegertracing/thrift/lib/cpp/test/Makefile.am425
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/OneWayHTTPTest.cpp242
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/OneWayTest.thrift45
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/OpenSSLManualInitTest.cpp93
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/OptionalRequiredTest.cpp386
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/RecursiveTest.cpp92
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/RenderedDoubleConstantsTest.cpp122
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/SecurityTest.cpp278
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/SpecializationTest.cpp103
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TBufferBaseTest.cpp639
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TFDTransportTest.cpp50
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TFileTransportTest.cpp404
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TMemoryBufferTest.cpp160
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TNonblockingSSLServerTest.cpp286
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TNonblockingServerTest.cpp215
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TPipeInterruptTest.cpp90
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TPipedTransportTest.cpp53
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TSSLSocketInterruptTest.cpp282
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TServerIntegrationTest.cpp536
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TServerSocketTest.cpp69
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TServerTransportTest.cpp58
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TSocketInterruptTest.cpp146
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TTransportCheckThrow.h44
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/ThriftTest_extras.cpp33
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/ToStringTest.cpp137
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TransportTest.cpp1089
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/TypedefTest.cpp28
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/UnitTestMain.cpp21
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/ZlibTest.cpp475
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/concurrency/Tests.cpp224
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadFactoryTests.h308
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadManagerTests.h639
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/concurrency/TimerManagerTests.h273
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/link/LinkTest.cpp22
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService1.cpp26
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService2.cpp26
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.cpp135
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.h95
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/Handlers.h338
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/ProcessorTest.cpp929
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.cpp152
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.h135
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/processor/proc.thrift22
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/qt/CMakeLists.txt32
-rw-r--r--src/jaegertracing/thrift/lib/cpp/test/qt/TQTcpServerTest.cpp113
56 files changed, 12021 insertions, 0 deletions
diff --git a/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.cpp b/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.cpp
new file mode 100644
index 000000000..6b5c7c436
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.cpp
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stdio.h>
+
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/protocol/TCompactProtocol.h>
+#include <thrift/transport/TBufferTransports.h>
+
+#define BOOST_TEST_MODULE AllProtocolTests
+#include <boost/test/unit_test.hpp>
+
+#include "AllProtocolTests.tcc"
+
+using namespace apache::thrift;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+
+char errorMessage[ERR_LEN];
+
+BOOST_AUTO_TEST_CASE(test_binary_protocol) {
+ testProtocol<TBinaryProtocol>("TBinaryProtocol");
+}
+
+BOOST_AUTO_TEST_CASE(test_little_binary_protocol) {
+ testProtocol<TLEBinaryProtocol>("TLEBinaryProtocol");
+}
+
+BOOST_AUTO_TEST_CASE(test_compact_protocol) {
+ testProtocol<TCompactProtocol>("TCompactProtocol");
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.tcc b/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.tcc
new file mode 100644
index 000000000..80a4ea097
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.tcc
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TEST_GENERICPROTOCOLTEST_TCC_
+#define _THRIFT_TEST_GENERICPROTOCOLTEST_TCC_ 1
+
+#include <limits>
+
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/Thrift.h>
+
+#include "GenericHelpers.h"
+
+using std::shared_ptr;
+using namespace apache::thrift;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::transport;
+
+#define ERR_LEN 512
+extern char errorMessage[ERR_LEN];
+
+template <typename TProto, typename Val>
+void testNaked(Val val) {
+ shared_ptr<TTransport> transport(new TMemoryBuffer());
+ shared_ptr<TProtocol> protocol(new TProto(transport));
+
+ GenericIO::write(protocol, val);
+ Val out;
+ GenericIO::read(protocol, out);
+ if (out != val) {
+ THRIFT_SNPRINTF(errorMessage,
+ ERR_LEN,
+ "Invalid naked test (type: %s)",
+ ClassNames::getName<Val>());
+ throw TException(errorMessage);
+ }
+}
+
+template <typename TProto, TType type, typename Val>
+void testField(const Val val) {
+ shared_ptr<TTransport> transport(new TMemoryBuffer());
+ shared_ptr<TProtocol> protocol(new TProto(transport));
+
+ protocol->writeStructBegin("test_struct");
+ protocol->writeFieldBegin("test_field", type, (int16_t)15);
+
+ GenericIO::write(protocol, val);
+
+ protocol->writeFieldEnd();
+ protocol->writeStructEnd();
+
+ std::string name;
+ TType fieldType;
+ int16_t fieldId;
+
+ protocol->readStructBegin(name);
+ protocol->readFieldBegin(name, fieldType, fieldId);
+
+ if (fieldId != 15) {
+ THRIFT_SNPRINTF(errorMessage, ERR_LEN, "Invalid ID (type: %s)", typeid(val).name());
+ throw TException(errorMessage);
+ }
+ if (fieldType != type) {
+ THRIFT_SNPRINTF(errorMessage, ERR_LEN, "Invalid Field Type (type: %s)", typeid(val).name());
+ throw TException(errorMessage);
+ }
+
+ Val out;
+ GenericIO::read(protocol, out);
+
+ if (out != val) {
+ THRIFT_SNPRINTF(errorMessage, ERR_LEN, "Invalid value read (type: %s)", typeid(val).name());
+ throw TException(errorMessage);
+ }
+
+ protocol->readFieldEnd();
+ protocol->readStructEnd();
+}
+
+template <typename TProto>
+void testMessage() {
+ struct TMessage {
+ const char* name;
+ TMessageType type;
+ int32_t seqid;
+ } messages[] = {{"short message name", T_CALL, 0},
+ {"1", T_REPLY, 12345},
+ {"loooooooooooooooooooooooooooooooooong", T_EXCEPTION, 1 << 16},
+ {"one way push", T_ONEWAY, 12},
+ {"Janky", T_CALL, 0}};
+ const int messages_count = sizeof(messages) / sizeof(TMessage);
+
+ for (int i = 0; i < messages_count; i++) {
+ shared_ptr<TTransport> transport(new TMemoryBuffer());
+ shared_ptr<TProtocol> protocol(new TProto(transport));
+
+ protocol->writeMessageBegin(messages[i].name, messages[i].type, messages[i].seqid);
+ protocol->writeMessageEnd();
+
+ std::string name;
+ TMessageType type;
+ int32_t seqid;
+
+ protocol->readMessageBegin(name, type, seqid);
+ if (name != messages[i].name || type != messages[i].type || seqid != messages[i].seqid) {
+ throw TException("readMessageBegin failed.");
+ }
+ }
+}
+
+template <typename TProto>
+void testProtocol(const char* protoname) {
+ try {
+ testNaked<TProto, int8_t>((int8_t)123);
+
+ for (int32_t i = 0; i < 128; i++) {
+ testField<TProto, T_BYTE, int8_t>((int8_t)i);
+ testField<TProto, T_BYTE, int8_t>((int8_t)-i);
+ }
+
+ testNaked<TProto, int16_t>((int16_t)0);
+ testNaked<TProto, int16_t>((int16_t)1);
+ testNaked<TProto, int16_t>((int16_t)15000);
+ testNaked<TProto, int16_t>((int16_t)0x7fff);
+ testNaked<TProto, int16_t>((int16_t)-1);
+ testNaked<TProto, int16_t>((int16_t)-15000);
+ testNaked<TProto, int16_t>((int16_t)-0x7fff);
+ testNaked<TProto, int16_t>((std::numeric_limits<int16_t>::min)());
+ testNaked<TProto, int16_t>((std::numeric_limits<int16_t>::max)());
+
+ testField<TProto, T_I16, int16_t>((int16_t)0);
+ testField<TProto, T_I16, int16_t>((int16_t)1);
+ testField<TProto, T_I16, int16_t>((int16_t)7);
+ testField<TProto, T_I16, int16_t>((int16_t)150);
+ testField<TProto, T_I16, int16_t>((int16_t)15000);
+ testField<TProto, T_I16, int16_t>((int16_t)0x7fff);
+ testField<TProto, T_I16, int16_t>((int16_t)-1);
+ testField<TProto, T_I16, int16_t>((int16_t)-7);
+ testField<TProto, T_I16, int16_t>((int16_t)-150);
+ testField<TProto, T_I16, int16_t>((int16_t)-15000);
+ testField<TProto, T_I16, int16_t>((int16_t)-0x7fff);
+
+ testNaked<TProto, int32_t>(0);
+ testNaked<TProto, int32_t>(1);
+ testNaked<TProto, int32_t>(15000);
+ testNaked<TProto, int32_t>(0xffff);
+ testNaked<TProto, int32_t>(-1);
+ testNaked<TProto, int32_t>(-15000);
+ testNaked<TProto, int32_t>(-0xffff);
+ testNaked<TProto, int32_t>((std::numeric_limits<int32_t>::min)());
+ testNaked<TProto, int32_t>((std::numeric_limits<int32_t>::max)());
+
+ testField<TProto, T_I32, int32_t>(0);
+ testField<TProto, T_I32, int32_t>(1);
+ testField<TProto, T_I32, int32_t>(7);
+ testField<TProto, T_I32, int32_t>(150);
+ testField<TProto, T_I32, int32_t>(15000);
+ testField<TProto, T_I32, int32_t>(31337);
+ testField<TProto, T_I32, int32_t>(0xffff);
+ testField<TProto, T_I32, int32_t>(0xffffff);
+ testField<TProto, T_I32, int32_t>(-1);
+ testField<TProto, T_I32, int32_t>(-7);
+ testField<TProto, T_I32, int32_t>(-150);
+ testField<TProto, T_I32, int32_t>(-15000);
+ testField<TProto, T_I32, int32_t>(-0xffff);
+ testField<TProto, T_I32, int32_t>(-0xffffff);
+ testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::min)());
+ testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::max)());
+ testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::min)() + 10);
+ testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::max)() - 16);
+ testNaked<TProto, int64_t>((std::numeric_limits<int64_t>::min)());
+ testNaked<TProto, int64_t>((std::numeric_limits<int64_t>::max)());
+
+ testNaked<TProto, int64_t>(0);
+ for (int64_t i = 0; i < 62; i++) {
+ testNaked<TProto, int64_t>(1LL << i);
+ testNaked<TProto, int64_t>(-(1LL << i));
+ }
+
+ testField<TProto, T_I64, int64_t>(0);
+ for (int i = 0; i < 62; i++) {
+ testField<TProto, T_I64, int64_t>(1LL << i);
+ testField<TProto, T_I64, int64_t>(-(1LL << i));
+ }
+
+ testNaked<TProto, double>(123.456);
+
+ testNaked<TProto, std::string>("");
+ testNaked<TProto, std::string>("short");
+ testNaked<TProto, std::string>("borderlinetiny");
+ testNaked<TProto, std::string>("a bit longer than the smallest possible");
+ testNaked<TProto, std::string>("\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA"); // kinda binary test
+
+ testField<TProto, T_STRING, std::string>("");
+ testField<TProto, T_STRING, std::string>("short");
+ testField<TProto, T_STRING, std::string>("borderlinetiny");
+ testField<TProto, T_STRING, std::string>("a bit longer than the smallest possible");
+
+ testMessage<TProto>();
+
+ printf("%s => OK\n", protoname);
+ } catch (const TException &e) {
+ THRIFT_SNPRINTF(errorMessage, ERR_LEN, "%s => Test FAILED: %s", protoname, e.what());
+ throw TException(errorMessage);
+ }
+}
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/AnnotationTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/AnnotationTest.cpp
new file mode 100644
index 000000000..2e18840e9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/AnnotationTest.cpp
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#define BOOST_TEST_MODULE AnnotationTest
+#include <boost/test/unit_test.hpp>
+#include "gen-cpp/AnnotationTest_types.h"
+#include <ostream>
+#include <sstream>
+
+// Normally thrift generates ostream operators, however
+// with the annotation "cpp.customostream" one can tell the
+// compiler they are going to provide their own, and not
+// emit operator << or printTo().
+
+std::ostream& operator<<(std::ostream& os, const ostr_custom& osc)
+{
+ os << "{ bar = " << osc.bar << "; }";
+ return os;
+}
+
+BOOST_AUTO_TEST_SUITE(BOOST_TEST_MODULE)
+
+BOOST_AUTO_TEST_CASE(test_cpp_compiler_generated_ostream_operator)
+{
+ ostr_default def;
+ def.__set_bar(10);
+
+ std::stringstream ssd;
+ ssd << def;
+ BOOST_CHECK_EQUAL(ssd.str(), "ostr_default(bar=10)");
+}
+
+BOOST_AUTO_TEST_CASE(test_cpp_customostream_uses_consuming_application_definition)
+{
+ ostr_custom cus;
+ cus.__set_bar(10);
+
+ std::stringstream csd;
+ csd << cus;
+ BOOST_CHECK_EQUAL(csd.str(), "{ bar = 10; }");
+}
+
+/**
+ * Disabled; see THRIFT-1567 - not sure what it is supposed to do
+BOOST_AUTO_TEST_CASE(test_cpp_type) {
+ // Check that the "cpp.type" annotation changes "struct foo" to "DenseFoo"
+ // This won't compile if the annotation is mishandled
+ DenseFoo foo;
+ foo.__set_bar(5);
+}
+ */
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/Base64Test.cpp b/src/jaegertracing/thrift/lib/cpp/test/Base64Test.cpp
new file mode 100644
index 000000000..7686e4e7b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/Base64Test.cpp
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <thrift/protocol/TBase64Utils.h>
+
+using apache::thrift::protocol::base64_encode;
+using apache::thrift::protocol::base64_decode;
+
+BOOST_AUTO_TEST_SUITE(Base64Test)
+
+void setupTestData(int i, uint8_t* data, int& len) {
+ len = 0;
+ do {
+ data[len] = (uint8_t)(i & 0xFF);
+ i >>= 8;
+ len++;
+ } while ((len < 3) && (i != 0));
+
+ BOOST_ASSERT(i == 0);
+}
+
+void checkEncoding(uint8_t* data, int len) {
+#ifdef NDEBUG
+ ((void)data);
+#endif
+
+ for (int i = 0; i < len; i++) {
+ BOOST_ASSERT(isalnum(data[i]) || data[i] == '/' || data[i] == '+');
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_Base64_Encode_Decode) {
+ int len;
+ uint8_t testInput[3];
+ uint8_t testOutput[4];
+
+ // Test all possible encoding / decoding cases given the
+ // three byte limit for base64_encode.
+
+ for (int i = 0xFFFFFF; i >= 0; i--) {
+
+ // fill testInput based on i
+ setupTestData(i, testInput, len);
+
+ // encode the test data, then decode it again
+ base64_encode(testInput, len, testOutput);
+
+ // verify each byte has a valid Base64 value (alphanumeric or either + or /)
+ checkEncoding(testOutput, len);
+
+ // decode output and check that it matches input
+ base64_decode(testOutput, len + 1);
+ BOOST_ASSERT(0 == memcmp(testInput, testOutput, len));
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/Benchmark.cpp b/src/jaegertracing/thrift/lib/cpp/test/Benchmark.cpp
new file mode 100644
index 000000000..56adac0b2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/Benchmark.cpp
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <iostream>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <memory>
+#include "thrift/protocol/TBinaryProtocol.h"
+#include "thrift/transport/TBufferTransports.h"
+#include "gen-cpp/DebugProtoTest_types.h"
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+class Timer {
+public:
+ timeval vStart;
+
+ Timer() { THRIFT_GETTIMEOFDAY(&vStart, nullptr); }
+ void start() { THRIFT_GETTIMEOFDAY(&vStart, nullptr); }
+
+ double frame() {
+ timeval vEnd;
+ THRIFT_GETTIMEOFDAY(&vEnd, nullptr);
+ double dstart = vStart.tv_sec + ((double)vStart.tv_usec / 1000000.0);
+ double dend = vEnd.tv_sec + ((double)vEnd.tv_usec / 1000000.0);
+ return dend - dstart;
+ }
+};
+
+int main() {
+ using namespace thrift::test::debug;
+ using namespace apache::thrift::transport;
+ using namespace apache::thrift::protocol;
+ using std::cout;
+ using std::endl;
+
+ OneOfEach ooe;
+ ooe.im_true = true;
+ ooe.im_false = false;
+ ooe.a_bite = 0x7f;
+ ooe.integer16 = 27000;
+ ooe.integer32 = 1 << 24;
+ ooe.integer64 = (uint64_t)6000 * 1000 * 1000;
+ ooe.double_precision = M_PI;
+ ooe.some_characters = "JSON THIS! \"\1";
+ ooe.zomg_unicode = "\xd7\n\a\t";
+ ooe.base64 = "\1\2\3\255";
+
+ int num = 100000;
+ std::shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer(num*1000));
+
+ uint8_t* data = nullptr;
+ uint32_t datasize = 0;
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe.write(&prot);
+ }
+ elapsed = timer.frame();
+ cout << "Write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ buf->getBuffer(&data, &datasize);
+
+ {
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer> prot(buf2);
+ OneOfEach ooe2;
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe2.read(&prot);
+ }
+ elapsed = timer.frame();
+ cout << " Read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe.write(&prot);
+ }
+ elapsed = timer.frame();
+ cout << "Write little endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ OneOfEach ooe2;
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf2);
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe2.read(&prot);
+ }
+ elapsed = timer.frame();
+ cout << " Read little endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe.write(&prot);
+ }
+ elapsed = timer.frame();
+ cout << "Write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer> prot(buf2);
+ OneOfEach ooe2;
+ double elapsed = 0.0;
+ Timer timer;
+
+ for (int i = 0; i < num; i++) {
+ ooe2.read(&prot);
+ }
+ elapsed = timer.frame();
+ cout << " Read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+
+ data = nullptr;
+ datasize = 0;
+ num = 10000000;
+
+ ListDoublePerf listDoublePerf;
+ listDoublePerf.field.reserve(num);
+ for (int x = 0; x < num; ++x)
+ listDoublePerf.field.push_back(double(x));
+
+ buf.reset(new TMemoryBuffer(num * 100));
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf.write(&prot);
+ elapsed = timer.frame();
+ cout << "Double write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ buf->getBuffer(&data, &datasize);
+
+ {
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer> prot(buf2);
+ ListDoublePerf listDoublePerf2;
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf2.read(&prot);
+ elapsed = timer.frame();
+ cout << " Double read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf.write(&prot);
+ elapsed = timer.frame();
+ cout << "Double write little endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ ListDoublePerf listDoublePerf2;
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf2);
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf2.read(&prot);
+ elapsed = timer.frame();
+ cout << " Double read little endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ buf->resetBuffer();
+ TBinaryProtocolT<TMemoryBuffer> prot(buf);
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf.write(&prot);
+ elapsed = timer.frame();
+ cout << "Double write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+ {
+ std::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
+ TBinaryProtocolT<TMemoryBuffer> prot(buf2);
+ ListDoublePerf listDoublePerf2;
+ double elapsed = 0.0;
+ Timer timer;
+
+ listDoublePerf2.read(&prot);
+ elapsed = timer.frame();
+ cout << " Double read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
+ }
+
+
+ return 0;
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/CMakeLists.txt b/src/jaegertracing/thrift/lib/cpp/test/CMakeLists.txt
new file mode 100644
index 000000000..ef08dbce2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/CMakeLists.txt
@@ -0,0 +1,390 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Unit tests still require boost
+include(BoostMacros)
+REQUIRE_BOOST_HEADERS()
+set(BOOST_COMPONENTS chrono date_time filesystem random thread unit_test_framework)
+REQUIRE_BOOST_LIBRARIES(BOOST_COMPONENTS)
+
+include(ThriftMacros)
+
+# Make sure gen-cpp files can be included
+include_directories("${CMAKE_CURRENT_BINARY_DIR}")
+
+# Create the thrift C++ test library
+set(testgencpp_SOURCES
+ gen-cpp/AnnotationTest_types.cpp
+ gen-cpp/AnnotationTest_types.h
+ gen-cpp/DebugProtoTest_types.cpp
+ gen-cpp/DebugProtoTest_types.h
+ gen-cpp/EnumTest_types.cpp
+ gen-cpp/EnumTest_types.h
+ gen-cpp/OptionalRequiredTest_types.cpp
+ gen-cpp/OptionalRequiredTest_types.h
+ gen-cpp/Recursive_types.cpp
+ gen-cpp/Recursive_types.h
+ gen-cpp/ThriftTest_types.cpp
+ gen-cpp/ThriftTest_types.h
+ gen-cpp/OneWayTest_types.cpp
+ gen-cpp/OneWayTest_types.h
+ gen-cpp/OneWayService.cpp
+ gen-cpp/OneWayService.h
+ gen-cpp/TypedefTest_types.cpp
+ gen-cpp/TypedefTest_types.h
+ ThriftTest_extras.cpp
+ DebugProtoTest_extras.cpp
+)
+
+add_library(testgencpp STATIC ${testgencpp_SOURCES})
+
+set(testgencpp_cob_SOURCES
+ gen-cpp/ChildService.cpp
+ gen-cpp/ChildService.h
+ gen-cpp/EmptyService.cpp
+ gen-cpp/EmptyService.h
+ gen-cpp/ParentService.cpp
+ gen-cpp/ParentService.h
+ gen-cpp/proc_types.cpp
+ gen-cpp/proc_types.h
+)
+add_library(testgencpp_cob STATIC ${testgencpp_cob_SOURCES})
+
+add_executable(Benchmark Benchmark.cpp)
+target_link_libraries(Benchmark testgencpp)
+LINK_AGAINST_THRIFT_LIBRARY(Benchmark thrift)
+add_test(NAME Benchmark COMMAND Benchmark)
+target_link_libraries(Benchmark testgencpp)
+
+set(UnitTest_SOURCES
+ UnitTestMain.cpp
+ OneWayHTTPTest.cpp
+ TMemoryBufferTest.cpp
+ TBufferBaseTest.cpp
+ Base64Test.cpp
+ ToStringTest.cpp
+ TypedefTest.cpp
+ TServerSocketTest.cpp
+ TServerTransportTest.cpp
+)
+
+add_executable(UnitTests ${UnitTest_SOURCES})
+target_link_libraries(UnitTests testgencpp ${Boost_LIBRARIES})
+LINK_AGAINST_THRIFT_LIBRARY(UnitTests thrift)
+add_test(NAME UnitTests COMMAND UnitTests)
+if ( MSVC )
+ # Disable C4503: decorated name length exceeded, name was truncated
+ # 'insanity' results in very long decorated names
+ set_property( TARGET UnitTests APPEND_STRING PROPERTY COMPILE_FLAGS /wd4503 )
+endif ( MSVC )
+
+
+set( TInterruptTest_SOURCES
+ TSocketInterruptTest.cpp
+ TSSLSocketInterruptTest.cpp
+)
+if (WIN32)
+ list(APPEND TInterruptTest_SOURCES
+ TPipeInterruptTest.cpp
+ )
+endif()
+add_executable(TInterruptTest ${TInterruptTest_SOURCES})
+target_link_libraries(TInterruptTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TInterruptTest thrift)
+if (NOT MSVC AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT MINGW)
+target_link_libraries(TInterruptTest -lrt)
+endif ()
+add_test(NAME TInterruptTest COMMAND TInterruptTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys")
+
+add_executable(TServerIntegrationTest TServerIntegrationTest.cpp)
+target_link_libraries(TServerIntegrationTest
+ testgencpp_cob
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TServerIntegrationTest thrift)
+if (NOT MSVC AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT MINGW)
+target_link_libraries(TServerIntegrationTest -lrt)
+endif ()
+add_test(NAME TServerIntegrationTest COMMAND TServerIntegrationTest)
+
+if(WITH_ZLIB)
+include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")
+add_executable(TransportTest TransportTest.cpp)
+target_link_libraries(TransportTest
+ testgencpp
+ ${Boost_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TransportTest thrift)
+LINK_AGAINST_THRIFT_LIBRARY(TransportTest thriftz)
+add_test(NAME TransportTest COMMAND TransportTest)
+
+add_executable(ZlibTest ZlibTest.cpp)
+target_link_libraries(ZlibTest
+ testgencpp
+ ${Boost_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(ZlibTest thrift)
+LINK_AGAINST_THRIFT_LIBRARY(ZlibTest thriftz)
+add_test(NAME ZlibTest COMMAND ZlibTest)
+endif(WITH_ZLIB)
+
+add_executable(AnnotationTest AnnotationTest.cpp)
+target_link_libraries(AnnotationTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(AnnotationTest thrift)
+add_test(NAME AnnotationTest COMMAND AnnotationTest)
+
+add_executable(EnumTest EnumTest.cpp)
+target_link_libraries(EnumTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(EnumTest thrift)
+add_test(NAME EnumTest COMMAND EnumTest)
+
+if(HAVE_GETOPT_H)
+add_executable(TFileTransportTest TFileTransportTest.cpp)
+target_link_libraries(TFileTransportTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TFileTransportTest thrift)
+add_test(NAME TFileTransportTest COMMAND TFileTransportTest)
+endif()
+
+add_executable(TFDTransportTest TFDTransportTest.cpp)
+target_link_libraries(TFDTransportTest
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TFDTransportTest thrift)
+add_test(NAME TFDTransportTest COMMAND TFDTransportTest)
+
+add_executable(TPipedTransportTest TPipedTransportTest.cpp)
+target_link_libraries(TPipedTransportTest
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TPipedTransportTest thrift)
+add_test(NAME TPipedTransportTest COMMAND TPipedTransportTest)
+
+set(AllProtocolsTest_SOURCES
+ AllProtocolTests.cpp
+ AllProtocolTests.tcc
+ GenericHelpers
+ )
+
+add_executable(AllProtocolsTest ${AllProtocolsTest_SOURCES})
+target_link_libraries(AllProtocolsTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(AllProtocolsTest thrift)
+add_test(NAME AllProtocolsTest COMMAND AllProtocolsTest)
+
+# The debug run-time in Windows asserts on isprint() with negative inputs
+if (NOT MSVC OR (MSVC AND CMAKE_BUILD_TYPE EQUAL "DEBUG"))
+add_executable(DebugProtoTest DebugProtoTest.cpp)
+target_link_libraries(DebugProtoTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(DebugProtoTest thrift)
+add_test(NAME DebugProtoTest COMMAND DebugProtoTest)
+endif()
+
+add_executable(JSONProtoTest JSONProtoTest.cpp)
+target_link_libraries(JSONProtoTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(JSONProtoTest thrift)
+add_test(NAME JSONProtoTest COMMAND JSONProtoTest)
+
+add_executable(OptionalRequiredTest OptionalRequiredTest.cpp)
+target_link_libraries(OptionalRequiredTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(OptionalRequiredTest thrift)
+add_test(NAME OptionalRequiredTest COMMAND OptionalRequiredTest)
+
+add_executable(RecursiveTest RecursiveTest.cpp)
+target_link_libraries(RecursiveTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(RecursiveTest thrift)
+add_test(NAME RecursiveTest COMMAND RecursiveTest)
+
+add_executable(SpecializationTest SpecializationTest.cpp)
+target_link_libraries(SpecializationTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(SpecializationTest thrift)
+add_test(NAME SpecializationTest COMMAND SpecializationTest)
+
+set(concurrency_test_SOURCES
+ concurrency/Tests.cpp
+ concurrency/ThreadFactoryTests.h
+ concurrency/ThreadManagerTests.h
+ concurrency/TimerManagerTests.h
+)
+add_executable(concurrency_test ${concurrency_test_SOURCES})
+LINK_AGAINST_THRIFT_LIBRARY(concurrency_test thrift)
+add_test(NAME concurrency_test COMMAND concurrency_test)
+
+set(link_test_SOURCES
+ link/LinkTest.cpp
+ gen-cpp/ParentService.h
+ link/TemplatedService1.cpp
+ link/TemplatedService2.cpp
+)
+
+add_executable(link_test ${link_test_SOURCES})
+target_link_libraries(link_test testgencpp_cob)
+LINK_AGAINST_THRIFT_LIBRARY(link_test thrift)
+target_link_libraries(link_test testgencpp)
+add_test(NAME link_test COMMAND link_test)
+
+if(WITH_LIBEVENT)
+set(processor_test_SOURCES
+ processor/ProcessorTest.cpp
+ processor/EventLog.cpp
+ processor/ServerThread.cpp
+ processor/EventLog.h
+ processor/Handlers.h
+ processor/ServerThread.h
+)
+add_executable(processor_test ${processor_test_SOURCES})
+target_link_libraries(processor_test
+ testgencpp_cob
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(processor_test thrift)
+LINK_AGAINST_THRIFT_LIBRARY(processor_test thriftnb)
+add_test(NAME processor_test COMMAND processor_test)
+
+set(TNonblockingServerTest_SOURCES TNonblockingServerTest.cpp)
+add_executable(TNonblockingServerTest ${TNonblockingServerTest_SOURCES})
+include_directories(${LIBEVENT_INCLUDE_DIRS})
+target_link_libraries(TNonblockingServerTest
+ testgencpp_cob
+ ${LIBEVENT_LIBRARIES}
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(TNonblockingServerTest thrift)
+LINK_AGAINST_THRIFT_LIBRARY(TNonblockingServerTest thriftnb)
+add_test(NAME TNonblockingServerTest COMMAND TNonblockingServerTest)
+
+if(OPENSSL_FOUND AND WITH_OPENSSL)
+ set(TNonblockingSSLServerTest_SOURCES TNonblockingSSLServerTest.cpp)
+ add_executable(TNonblockingSSLServerTest ${TNonblockingSSLServerTest_SOURCES})
+ include_directories(${LIBEVENT_INCLUDE_DIRS})
+ target_link_libraries(TNonblockingSSLServerTest
+ testgencpp_cob
+ ${LIBEVENT_LIBRARIES}
+ ${Boost_LIBRARIES}
+ )
+ LINK_AGAINST_THRIFT_LIBRARY(TNonblockingSSLServerTest thrift)
+ LINK_AGAINST_THRIFT_LIBRARY(TNonblockingSSLServerTest thriftnb)
+ add_test(NAME TNonblockingSSLServerTest COMMAND TNonblockingSSLServerTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys")
+endif(OPENSSL_FOUND AND WITH_OPENSSL)
+endif(WITH_LIBEVENT)
+
+if(OPENSSL_FOUND AND WITH_OPENSSL)
+add_executable(OpenSSLManualInitTest OpenSSLManualInitTest.cpp)
+target_link_libraries(OpenSSLManualInitTest
+ ${OPENSSL_LIBRARIES}
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(OpenSSLManualInitTest thrift)
+add_test(NAME OpenSSLManualInitTest COMMAND OpenSSLManualInitTest)
+
+add_executable(SecurityTest SecurityTest.cpp)
+target_link_libraries(SecurityTest
+ testgencpp
+ ${Boost_LIBRARIES}
+)
+LINK_AGAINST_THRIFT_LIBRARY(SecurityTest thrift)
+if (NOT MSVC AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT MINGW)
+target_link_libraries(SecurityTest -lrt)
+endif ()
+add_test(NAME SecurityTest COMMAND SecurityTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys")
+
+endif()
+
+if(WITH_QT5)
+add_subdirectory(qt)
+endif()
+
+#
+# Common thrift code generation rules
+#
+
+add_custom_command(OUTPUT gen-cpp/AnnotationTest_constants.cpp
+ gen-cpp/AnnotationTest_constants.h
+ gen-cpp/AnnotationTest_types.cpp
+ gen-cpp/AnnotationTest_types.h
+ gen-cpp/foo_service.cpp
+ gen-cpp/foo_service.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/AnnotationTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/DebugProtoTest_types.cpp gen-cpp/DebugProtoTest_types.h gen-cpp/EmptyService.cpp gen-cpp/EmptyService.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/DebugProtoTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/EnumTest_types.cpp gen-cpp/EnumTest_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/EnumTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/TypedefTest_types.cpp gen-cpp/TypedefTest_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/TypedefTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/OptionalRequiredTest_types.cpp gen-cpp/OptionalRequiredTest_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/OptionalRequiredTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/Recursive_types.cpp gen-cpp/Recursive_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/Recursive.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/Service.cpp gen-cpp/StressTest_types.cpp
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/StressTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/SecondService.cpp gen-cpp/ThriftTest_constants.cpp gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${PROJECT_SOURCE_DIR}/test/ThriftTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/OneWayService.cpp gen-cpp/OneWayTest_constants.cpp gen-cpp/OneWayTest_types.h gen-cpp/OneWayService.h gen-cpp/OneWayTest_constants.h gen-cpp/OneWayTest_types.cpp
+ COMMAND ${THRIFT_COMPILER} --gen cpp ${CMAKE_CURRENT_SOURCE_DIR}/OneWayTest.thrift
+)
+
+add_custom_command(OUTPUT gen-cpp/ChildService.cpp gen-cpp/ChildService.h gen-cpp/ParentService.cpp gen-cpp/ParentService.h gen-cpp/proc_types.cpp gen-cpp/proc_types.h
+ COMMAND ${THRIFT_COMPILER} --gen cpp:templates,cob_style ${CMAKE_CURRENT_SOURCE_DIR}/processor/proc.thrift
+)
diff --git a/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest.cpp
new file mode 100644
index 000000000..060f3547d
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest.cpp
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include "gen-cpp/DebugProtoTest_types.h"
+#include <thrift/protocol/TDebugProtocol.h>
+#include <memory>
+
+#define BOOST_TEST_MODULE DebugProtoTest
+#include <boost/test/unit_test.hpp>
+
+using namespace thrift::test::debug;
+
+static ::std::shared_ptr<OneOfEach> ooe;
+
+void testCaseSetup_1() {
+ ooe.reset(new OneOfEach);
+ ooe->im_true = true;
+ ooe->im_false = false;
+ ooe->a_bite = 0x7f;
+ ooe->integer16 = 27000;
+ ooe->integer32 = 1 << 24;
+ ooe->integer64 = (uint64_t)6000 * 1000 * 1000;
+ ooe->double_precision = M_PI;
+ ooe->some_characters = "Debug THIS!";
+ ooe->zomg_unicode = "\xd7\n\a\t";
+}
+
+BOOST_AUTO_TEST_CASE(test_debug_proto_1) {
+ testCaseSetup_1();
+
+ const std::string expected_result(
+ "OneOfEach {\n"
+ " 01: im_true (bool) = true,\n"
+ " 02: im_false (bool) = false,\n"
+ " 03: a_bite (byte) = 0x7f,\n"
+ " 04: integer16 (i16) = 27000,\n"
+ " 05: integer32 (i32) = 16777216,\n"
+ " 06: integer64 (i64) = 6000000000,\n"
+ " 07: double_precision (double) = 3.1415926535897931,\n"
+ " 08: some_characters (string) = \"Debug THIS!\",\n"
+ " 09: zomg_unicode (string) = \"\\xd7\\n\\a\\t\",\n"
+ " 10: what_who (bool) = false,\n"
+ " 11: base64 (string) = \"\",\n"
+ " 12: byte_list (list) = list<byte>[3] {\n"
+ " [0] = 0x01,\n"
+ " [1] = 0x02,\n"
+ " [2] = 0x03,\n"
+ " },\n"
+ " 13: i16_list (list) = list<i16>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " 14: i64_list (list) = list<i64>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(*ooe));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+static ::std::shared_ptr<Nesting> n;
+
+void testCaseSetup_2() {
+ testCaseSetup_1();
+
+ n.reset(new Nesting);
+ n->my_ooe = *ooe;
+ n->my_ooe.integer16 = 16;
+ n->my_ooe.integer32 = 32;
+ n->my_ooe.integer64 = 64;
+ n->my_ooe.double_precision = (std::sqrt(5.0) + 1) / 2;
+ n->my_ooe.some_characters = ":R (me going \"rrrr\")";
+ n->my_ooe.zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20\xd0\x9d\xce"
+ "\xbf\xe2\x85\xbf\xd0\xbe\xc9\xa1\xd0\xb3\xd0"
+ "\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
+ "\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
+ "\xbc";
+ n->my_bonk.type = 31337;
+ n->my_bonk.message = "I am a bonk... xor!";
+}
+
+BOOST_AUTO_TEST_CASE(test_debug_proto_2) {
+ testCaseSetup_2();
+
+ const std::string expected_result(
+ "Nesting {\n"
+ " 01: my_bonk (struct) = Bonk {\n"
+ " 01: type (i32) = 31337,\n"
+ " 02: message (string) = \"I am a bonk... xor!\",\n"
+ " },\n"
+ " 02: my_ooe (struct) = OneOfEach {\n"
+ " 01: im_true (bool) = true,\n"
+ " 02: im_false (bool) = false,\n"
+ " 03: a_bite (byte) = 0x7f,\n"
+ " 04: integer16 (i16) = 16,\n"
+ " 05: integer32 (i32) = 32,\n"
+ " 06: integer64 (i64) = 64,\n"
+ " 07: double_precision (double) = 1.6180339887498949,\n"
+ " 08: some_characters (string) = \":R (me going \\\"rrrr\\\")\",\n"
+ " 09: zomg_unicode (string) = \"\\xd3\\x80\\xe2\\x85\\xae\\xce\\x9d \\xd"
+ "0\\x9d\\xce\\xbf\\xe2\\x85\\xbf\\xd0\\xbe\\xc9\\xa1\\xd0\\xb3\\xd0\\xb0"
+ "\\xcf\\x81\\xe2\\x84\\x8e \\xce\\x91tt\\xce\\xb1\\xe2\\x85\\xbd\\xce\\xb"
+ "a\\xc7\\x83\\xe2\\x80\\xbc\",\n"
+ " 10: what_who (bool) = false,\n"
+ " 11: base64 (string) = \"\",\n"
+ " 12: byte_list (list) = list<byte>[3] {\n"
+ " [0] = 0x01,\n"
+ " [1] = 0x02,\n"
+ " [2] = 0x03,\n"
+ " },\n"
+ " 13: i16_list (list) = list<i16>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " 14: i64_list (list) = list<i64>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(*n));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+static ::std::shared_ptr<HolyMoley> hm;
+
+void testCaseSetup_3() {
+ testCaseSetup_2();
+
+ hm.reset(new HolyMoley);
+
+ hm->big.push_back(*ooe);
+ hm->big.push_back(n->my_ooe);
+ hm->big[0].a_bite = 0x22;
+ hm->big[1].a_bite = 0x33;
+
+ std::vector<std::string> stage1;
+ stage1.push_back("and a one");
+ stage1.push_back("and a two");
+ hm->contain.insert(stage1);
+ stage1.clear();
+ stage1.push_back("then a one, two");
+ stage1.push_back("three!");
+ stage1.push_back("FOUR!!");
+ hm->contain.insert(stage1);
+ stage1.clear();
+ hm->contain.insert(stage1);
+
+ std::vector<Bonk> stage2;
+ hm->bonks["nothing"] = stage2;
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 1;
+ stage2.back().message = "Wait.";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 2;
+ stage2.back().message = "What?";
+ hm->bonks["something"] = stage2;
+ stage2.clear();
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 3;
+ stage2.back().message = "quoth";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 4;
+ stage2.back().message = "the raven";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 5;
+ stage2.back().message = "nevermore";
+ hm->bonks["poe"] = stage2;
+}
+
+BOOST_AUTO_TEST_CASE(test_debug_proto_3) {
+ testCaseSetup_3();
+
+ const std::string expected_result(
+ "HolyMoley {\n"
+ " 01: big (list) = list<struct>[2] {\n"
+ " [0] = OneOfEach {\n"
+ " 01: im_true (bool) = true,\n"
+ " 02: im_false (bool) = false,\n"
+ " 03: a_bite (byte) = 0x22,\n"
+ " 04: integer16 (i16) = 27000,\n"
+ " 05: integer32 (i32) = 16777216,\n"
+ " 06: integer64 (i64) = 6000000000,\n"
+ " 07: double_precision (double) = 3.1415926535897931,\n"
+ " 08: some_characters (string) = \"Debug THIS!\",\n"
+ " 09: zomg_unicode (string) = \"\\xd7\\n\\a\\t\",\n"
+ " 10: what_who (bool) = false,\n"
+ " 11: base64 (string) = \"\",\n"
+ " 12: byte_list (list) = list<byte>[3] {\n"
+ " [0] = 0x01,\n"
+ " [1] = 0x02,\n"
+ " [2] = 0x03,\n"
+ " },\n"
+ " 13: i16_list (list) = list<i16>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " 14: i64_list (list) = list<i64>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " },\n"
+ " [1] = OneOfEach {\n"
+ " 01: im_true (bool) = true,\n"
+ " 02: im_false (bool) = false,\n"
+ " 03: a_bite (byte) = 0x33,\n"
+ " 04: integer16 (i16) = 16,\n"
+ " 05: integer32 (i32) = 32,\n"
+ " 06: integer64 (i64) = 64,\n"
+ " 07: double_precision (double) = 1.6180339887498949,\n"
+ " 08: some_characters (string) = \":R (me going \\\"rrrr\\\")\",\n"
+ " 09: zomg_unicode (string) = \"\\xd3\\x80\\xe2\\x85\\xae\\xce\\x9d \\"
+ "xd0\\x9d\\xce\\xbf\\xe2\\x85\\xbf\\xd0\\xbe\\xc9\\xa1\\xd0\\xb3\\xd0\\xb"
+ "0\\xcf\\x81\\xe2\\x84\\x8e \\xce\\x91tt\\xce\\xb1\\xe2\\x85\\xbd\\xce\\x"
+ "ba\\xc7\\x83\\xe2\\x80\\xbc\",\n"
+ " 10: what_who (bool) = false,\n"
+ " 11: base64 (string) = \"\",\n"
+ " 12: byte_list (list) = list<byte>[3] {\n"
+ " [0] = 0x01,\n"
+ " [1] = 0x02,\n"
+ " [2] = 0x03,\n"
+ " },\n"
+ " 13: i16_list (list) = list<i16>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " 14: i64_list (list) = list<i64>[3] {\n"
+ " [0] = 1,\n"
+ " [1] = 2,\n"
+ " [2] = 3,\n"
+ " },\n"
+ " },\n"
+ " },\n"
+ " 02: contain (set) = set<list>[3] {\n"
+ " list<string>[0] {\n"
+ " },\n"
+ " list<string>[2] {\n"
+ " [0] = \"and a one\",\n"
+ " [1] = \"and a two\",\n"
+ " },\n"
+ " list<string>[3] {\n"
+ " [0] = \"then a one, two\",\n"
+ " [1] = \"three!\",\n"
+ " [2] = \"FOUR!!\",\n"
+ " },\n"
+ " },\n"
+ " 03: bonks (map) = map<string,list>[3] {\n"
+ " \"nothing\" -> list<struct>[0] {\n"
+ " },\n"
+ " \"poe\" -> list<struct>[3] {\n"
+ " [0] = Bonk {\n"
+ " 01: type (i32) = 3,\n"
+ " 02: message (string) = \"quoth\",\n"
+ " },\n"
+ " [1] = Bonk {\n"
+ " 01: type (i32) = 4,\n"
+ " 02: message (string) = \"the raven\",\n"
+ " },\n"
+ " [2] = Bonk {\n"
+ " 01: type (i32) = 5,\n"
+ " 02: message (string) = \"nevermore\",\n"
+ " },\n"
+ " },\n"
+ " \"something\" -> list<struct>[2] {\n"
+ " [0] = Bonk {\n"
+ " 01: type (i32) = 1,\n"
+ " 02: message (string) = \"Wait.\",\n"
+ " },\n"
+ " [1] = Bonk {\n"
+ " 01: type (i32) = 2,\n"
+ " 02: message (string) = \"What?\",\n"
+ " },\n"
+ " },\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(*hm));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest_extras.cpp b/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest_extras.cpp
new file mode 100644
index 000000000..5c4fd35e7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest_extras.cpp
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Extra functions required for DebugProtoTest_types to work
+
+#include "gen-cpp/DebugProtoTest_types.h"
+
+namespace thrift {
+namespace test {
+namespace debug {
+
+bool Empty::operator<(Empty const& other) const {
+ (void)other;
+ // It is empty, so all are equal.
+ return false;
+}
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/EnumTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/EnumTest.cpp
new file mode 100644
index 000000000..388abb7e9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/EnumTest.cpp
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#define BOOST_TEST_MODULE EnumTest
+#include <boost/test/unit_test.hpp>
+#include "gen-cpp/EnumTest_types.h"
+
+std::ostream& operator <<(std::ostream& os, const MyEnumWithCustomOstream::type& val)
+{
+ os << "{" << (int)val << ":CUSTOM!" << "}";
+ return os;
+}
+
+std::string to_string(const MyEnumWithCustomOstream::type& val)
+{
+ std::ostringstream os;
+ os << val;
+ return os.str();
+}
+
+BOOST_AUTO_TEST_SUITE(EnumTest)
+
+BOOST_AUTO_TEST_CASE(test_enum_value) {
+ // Check that all the enum values match what we expect
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_0, 0);
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_1, 1);
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_2, 2);
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_3, 3);
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_5, 5);
+ BOOST_CHECK_EQUAL(MyEnum1::ME1_6, 6);
+
+ BOOST_CHECK_EQUAL(MyEnum2::ME2_0, 0);
+ BOOST_CHECK_EQUAL(MyEnum2::ME2_1, 1);
+ BOOST_CHECK_EQUAL(MyEnum2::ME2_2, 2);
+
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_0, 0);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_1, 1);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_N2, -2);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_N1, -1);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_D0, 0);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_D1, 1);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_9, 9);
+ BOOST_CHECK_EQUAL(MyEnum3::ME3_10, 10);
+
+ BOOST_CHECK_EQUAL(MyEnum4::ME4_A, 0x7ffffffd);
+ BOOST_CHECK_EQUAL(MyEnum4::ME4_B, 0x7ffffffe);
+ BOOST_CHECK_EQUAL(MyEnum4::ME4_C, 0x7fffffff);
+
+ BOOST_CHECK_EQUAL(MyEnum5::e1, 0);
+ BOOST_CHECK_EQUAL(MyEnum5::e2, 42);
+}
+
+template <class _T>
+std::string EnumToString(_T e)
+{
+ std::stringstream ss;
+ ss << e;
+ return ss.str();
+}
+
+BOOST_AUTO_TEST_CASE(test_enum_ostream)
+{
+ BOOST_CHECK_EQUAL(EnumToString(MyEnum1::ME1_0), "ME1_0");
+ BOOST_CHECK_EQUAL(EnumToString(MyEnum5::e2), "e2");
+ BOOST_CHECK_EQUAL(EnumToString(MyEnum3::ME3_N1), "ME3_N1");
+ BOOST_CHECK_EQUAL(EnumToString(MyEnumWithCustomOstream::CustoM2), "{2:CUSTOM!}");
+
+ // some invalid or unknown value
+ auto uut = static_cast<MyEnum5::type>(44);
+ BOOST_CHECK_EQUAL(EnumToString(uut), "44");
+}
+
+BOOST_AUTO_TEST_CASE(test_enum_to_string)
+{
+ BOOST_CHECK_EQUAL(::to_string(MyEnum1::ME1_0), "ME1_0");
+ BOOST_CHECK_EQUAL(::to_string(MyEnum5::e2), "e2");
+ BOOST_CHECK_EQUAL(::to_string(MyEnum3::ME3_N1), "ME3_N1");
+ BOOST_CHECK_EQUAL(::to_string(MyEnumWithCustomOstream::CustoM2), "{2:CUSTOM!}");
+
+ // some invalid or unknown value
+ auto uut = static_cast<MyEnum5::type>(44);
+ BOOST_CHECK_EQUAL(::to_string(uut), "44");
+}
+
+BOOST_AUTO_TEST_CASE(test_enum_constant)
+{
+ MyStruct ms;
+ BOOST_CHECK_EQUAL(ms.me2_2, 2);
+ BOOST_CHECK_EQUAL(ms.me3_n2, -2);
+ BOOST_CHECK_EQUAL(ms.me3_d1, 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/GenericHelpers.h b/src/jaegertracing/thrift/lib/cpp/test/GenericHelpers.h
new file mode 100644
index 000000000..bcef9f242
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/GenericHelpers.h
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _THRIFT_TEST_GENERICHELPERS_H_
+#define _THRIFT_TEST_GENERICHELPERS_H_ 1
+
+#include <thrift/protocol/TProtocol.h>
+#include <memory>
+#include <thrift/Thrift.h>
+
+/* ClassName Helper for cleaner exceptions */
+class ClassNames {
+public:
+ template <typename T>
+ static const char* getName() {
+ return "Unknown type";
+ }
+};
+
+template <>
+const char* ClassNames::getName<int8_t>() {
+ return "byte";
+}
+template <>
+const char* ClassNames::getName<int16_t>() {
+ return "short";
+}
+template <>
+const char* ClassNames::getName<int32_t>() {
+ return "int";
+}
+template <>
+const char* ClassNames::getName<int64_t>() {
+ return "long";
+}
+template <>
+const char* ClassNames::getName<double>() {
+ return "double";
+}
+template <>
+const char* ClassNames::getName<std::string>() {
+ return "string";
+}
+
+/* Generic Protocol I/O function for tests */
+class GenericIO {
+public:
+ /* Write functions */
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const int8_t& val) {
+ return proto->writeByte(val);
+ }
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const int16_t& val) {
+ return proto->writeI16(val);
+ }
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const int32_t& val) {
+ return proto->writeI32(val);
+ }
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const double& val) {
+ return proto->writeDouble(val);
+ }
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const int64_t& val) {
+ return proto->writeI64(val);
+ }
+
+ static uint32_t write(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, const std::string& val) {
+ return proto->writeString(val);
+ }
+
+ /* Read functions */
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, int8_t& val) { return proto->readByte(val); }
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, int16_t& val) { return proto->readI16(val); }
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, int32_t& val) { return proto->readI32(val); }
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, int64_t& val) { return proto->readI64(val); }
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, double& val) { return proto->readDouble(val); }
+
+ static uint32_t read(std::shared_ptr<apache::thrift::protocol::TProtocol> proto, std::string& val) {
+ return proto->readString(val);
+ }
+};
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/JSONProtoTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/JSONProtoTest.cpp
new file mode 100644
index 000000000..c2ad73e71
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/JSONProtoTest.cpp
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include <iomanip>
+#include <sstream>
+#include <thrift/protocol/TJSONProtocol.h>
+#include <memory>
+#include <thrift/transport/TBufferTransports.h>
+#include "gen-cpp/DebugProtoTest_types.h"
+
+#define BOOST_TEST_MODULE JSONProtoTest
+#include <boost/test/unit_test.hpp>
+
+using namespace thrift::test::debug;
+using namespace apache::thrift;
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::protocol::TJSONProtocol;
+
+static std::shared_ptr<OneOfEach> ooe;
+
+void testCaseSetup_1() {
+ ooe.reset(new OneOfEach);
+ ooe->im_true = true;
+ ooe->im_false = false;
+ ooe->a_bite = 0x7f;
+ ooe->integer16 = 27000;
+ ooe->integer32 = 1 << 24;
+ ooe->integer64 = (uint64_t)6000 * 1000 * 1000;
+ ooe->double_precision = M_PI;
+ ooe->some_characters = "JSON THIS! \"\1";
+ ooe->zomg_unicode = "\xd7\n\a\t";
+ ooe->base64 = "\1\2\3\255";
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_1) {
+ testCaseSetup_1();
+
+ const std::string expected_result(
+ "{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
+ "\"5\":{\"i32\":16777216},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
+ "535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001\"},\"9\":{\"str\":\"\xd7\\"
+ "n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":\"AQIDrQ\"},\"12\":{\"lst\""
+ ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
+ "\",3,1,2,3]}}");
+
+ const std::string result(apache::thrift::ThriftJSONString(*ooe));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+static std::shared_ptr<Nesting> n;
+
+void testCaseSetup_2() {
+ testCaseSetup_1();
+
+ n.reset(new Nesting);
+ n->my_ooe = *ooe;
+ n->my_ooe.integer16 = 16;
+ n->my_ooe.integer32 = 32;
+ n->my_ooe.integer64 = 64;
+ n->my_ooe.double_precision = (std::sqrt(5.0) + 1) / 2;
+ n->my_ooe.some_characters = ":R (me going \"rrrr\")";
+ n->my_ooe.zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20\xd0\x9d\xce"
+ "\xbf\xe2\x85\xbf\xd0\xbe\xc9\xa1\xd0\xb3\xd0"
+ "\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
+ "\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
+ "\xbc";
+ n->my_bonk.type = 31337;
+ n->my_bonk.message = "I am a bonk... xor!";
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_2) {
+ testCaseSetup_2();
+
+ const std::string expected_result(
+ "{\"1\":{\"rec\":{\"1\":{\"i32\":31337},\"2\":{\"str\":\"I am a bonk... xor"
+ "!\"}}},\"2\":{\"rec\":{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127"
+ "},\"4\":{\"i16\":16},\"5\":{\"i32\":32},\"6\":{\"i64\":64},\"7\":{\"dbl\":"
+ "1.6180339887498949},\"8\":{\"str\":\":R (me going \\\"rrrr\\\")\"},\"9\":{"
+ "\"str\":\"ӀⅮΝ Нοⅿоɡгаρℎ Αttαⅽκǃ‼\"},\"10\":{\"tf\":0},\"11\":{\"str\":\""
+ "AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2"
+ ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}}}}"
+ );
+
+ const std::string result(apache::thrift::ThriftJSONString(*n));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+static std::shared_ptr<HolyMoley> hm;
+
+void testCaseSetup_3() {
+ testCaseSetup_2();
+
+ hm.reset(new HolyMoley);
+
+ hm->big.push_back(*ooe);
+ hm->big.push_back(n->my_ooe);
+ hm->big[0].a_bite = 0x22;
+ hm->big[1].a_bite = 0x33;
+
+ std::vector<std::string> stage1;
+ stage1.push_back("and a one");
+ stage1.push_back("and a two");
+ hm->contain.insert(stage1);
+ stage1.clear();
+ stage1.push_back("then a one, two");
+ stage1.push_back("three!");
+ stage1.push_back("FOUR!!");
+ hm->contain.insert(stage1);
+ stage1.clear();
+ hm->contain.insert(stage1);
+
+ std::vector<Bonk> stage2;
+ hm->bonks["nothing"] = stage2;
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 1;
+ stage2.back().message = "Wait.";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 2;
+ stage2.back().message = "What?";
+ hm->bonks["something"] = stage2;
+ stage2.clear();
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 3;
+ stage2.back().message = "quoth";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 4;
+ stage2.back().message = "the raven";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 5;
+ stage2.back().message = "nevermore";
+ hm->bonks["poe"] = stage2;
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_3) {
+ testCaseSetup_3();
+
+ const std::string expected_result(
+ "{\"1\":{\"lst\":[\"rec\",2,{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":"
+ "34},\"4\":{\"i16\":27000},\"5\":{\"i32\":16777216},\"6\":{\"i64\":6000000000"
+ "},\"7\":{\"dbl\":3.1415926535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001"
+ "\"},\"9\":{\"str\":\"\xd7\\n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":"
+ "\"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2"
+ ",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}},{\"1\":{\"tf\":1},\"2\":{\"tf\":0},"
+ "\"3\":{\"i8\":51},\"4\":{\"i16\":16},\"5\":{\"i32\":32},\"6\":{\"i64\":64},"
+ "\"7\":{\"dbl\":1.6180339887498949},\"8\":{\"str\":\":R (me going \\\"rrrr\\\""
+ ")\"},\"9\":{\"str\":\"ӀⅮΝ Нοⅿоɡгаρℎ Αttαⅽκǃ‼\"},\"10\":{\"tf\":0},\"11\":{"
+ "\"str\":\"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16"
+ "\",3,1,2,3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}}]},\"2\":{\"set\":[\"lst\",3"
+ ",[\"str\",0],[\"str\",2,\"and a one\",\"and a two\"],[\"str\",3,\"then a one"
+ ", two\",\"three!\",\"FOUR!!\"]]},\"3\":{\"map\":[\"str\",\"lst\",3,{\"nothin"
+ "g\":[\"rec\",0],\"poe\":[\"rec\",3,{\"1\":{\"i32\":3},\"2\":{\"str\":\"quoth"
+ "\"}},{\"1\":{\"i32\":4},\"2\":{\"str\":\"the raven\"}},{\"1\":{\"i32\":5},\""
+ "2\":{\"str\":\"nevermore\"}}],\"something\":[\"rec\",2,{\"1\":{\"i32\":1},\""
+ "2\":{\"str\":\"Wait.\"}},{\"1\":{\"i32\":2},\"2\":{\"str\":\"What?\"}}]}]}}"
+ );
+
+ const std::string result(apache::thrift::ThriftJSONString(*hm));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_4) {
+ testCaseSetup_1();
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ ooe->write(proto.get());
+ OneOfEach ooe2;
+ ooe2.read(proto.get());
+
+ BOOST_CHECK(*ooe == ooe2);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_5) {
+ testCaseSetup_3();
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ hm->write(proto.get());
+ HolyMoley hm2;
+ hm2.read(proto.get());
+
+ BOOST_CHECK(*hm == hm2);
+
+ hm2.big[0].a_bite = 0x00;
+
+ BOOST_CHECK(*hm != hm2);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_6) {
+ Doubles dub;
+ dub.nan = HUGE_VAL / HUGE_VAL;
+ dub.inf = HUGE_VAL;
+ dub.neginf = -HUGE_VAL;
+ dub.repeating = 10.0 / 3.0;
+ dub.big = 1E+305;
+ dub.tiny = 1E-305;
+ dub.zero = 0.0;
+ dub.negzero = -0.0;
+
+ const std::string expected_result(
+ "{\"1\":{\"dbl\":\"NaN\"},\"2\":{\"dbl\":\"Infinity\"},\"3\":{\"dbl\":\"-Infi"
+ "nity\"},\"4\":{\"dbl\":3.3333333333333335},\"5\":{\"dbl\":9.9999999999999994e+"
+ "304},\"6\":{\"dbl\":1e-305},\"7\":{\"dbl\":0},\"8\":{\"dbl\":-0}}"
+ );
+
+ const std::string result(apache::thrift::ThriftJSONString(dub));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_7) {
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ Base64 base;
+ base.a = 123;
+ base.b1 = "1";
+ base.b2 = "12";
+ base.b3 = "123";
+ base.b4 = "1234";
+ base.b5 = "12345";
+ base.b6 = "123456";
+
+ base.write(proto.get());
+ Base64 base2;
+ base2.read(proto.get());
+
+ BOOST_CHECK(base == base2);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_proto_8) {
+ const char* json_string =
+ "{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
+ "\"5\":{\"i32\":16.77216},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
+ "535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001\"},\"9\":{\"str\":\"\xd7\\"
+ "n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":\"AQIDrQ\"},\"12\":{\"lst\""
+ ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
+ "\",3,1,2,3]}}";
+
+ const std::size_t bufSiz = strlen(json_string) * sizeof(char);
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
+ (uint8_t*)(json_string), static_cast<uint32_t>(bufSiz)));
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ OneOfEach ooe2;
+
+ BOOST_CHECK_THROW(ooe2.read(proto.get()),
+ apache::thrift::protocol::TProtocolException);
+}
+
+static std::string toHexSequence(const std::string& str) {
+ std::stringstream ss;
+ ss << std::hex << std::setfill('0');
+ for (std::size_t i = 0; i < str.size(); i++) {
+ ss << "\\x" << int(uint8_t(str[i]));
+ }
+ return ss.str();
+}
+
+BOOST_AUTO_TEST_CASE(test_json_unicode_escaped) {
+ const char json_string[] =
+ "{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
+ "\"5\":{\"i32\":16},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
+ "535897931},\"8\":{\"str\":\"JSON THIS!\"},\"9\":{\"str\":\"\\u0e01 \\ud835\\udd3e\"},"
+ "\"10\":{\"tf\":0},\"11\":{\"str\":\"000000\"},\"12\":{\"lst\""
+ ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
+ "\",3,1,2,3]}}";
+ const char* expected_zomg_unicode = "\xe0\xb8\x81 \xf0\x9d\x94\xbe";
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
+ (uint8_t*)(json_string), sizeof(json_string)));
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ OneOfEach ooe2;
+ ooe2.read(proto.get());
+ BOOST_CHECK_MESSAGE(!ooe2.zomg_unicode.compare(expected_zomg_unicode),
+ "Expected:\n" << toHexSequence(expected_zomg_unicode) << "\nGotten:\n"
+ << toHexSequence(ooe2.zomg_unicode));
+
+}
+
+BOOST_AUTO_TEST_CASE(test_json_unicode_escaped_missing_low_surrogate) {
+ const char json_string[] =
+ "{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
+ "\"5\":{\"i32\":16},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
+ "535897931},\"8\":{\"str\":\"JSON THIS!\"},\"9\":{\"str\":\"\\ud835\"},"
+ "\"10\":{\"tf\":0},\"11\":{\"str\":\"000000\"},\"12\":{\"lst\""
+ ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
+ "\",3,1,2,3]}}";
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
+ (uint8_t*)(json_string), sizeof(json_string)));
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ OneOfEach ooe2;
+ BOOST_CHECK_THROW(ooe2.read(proto.get()),
+ apache::thrift::protocol::TProtocolException);
+}
+
+BOOST_AUTO_TEST_CASE(test_json_unicode_escaped_missing_hi_surrogate) {
+ const char json_string[] =
+ "{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
+ "\"5\":{\"i32\":16},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
+ "535897931},\"8\":{\"str\":\"JSON THIS!\"},\"9\":{\"str\":\"\\udd3e\"},"
+ "\"10\":{\"tf\":0},\"11\":{\"str\":\"000000\"},\"12\":{\"lst\""
+ ":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
+ "\",3,1,2,3]}}";
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
+ (uint8_t*)(json_string), sizeof(json_string)));
+ std::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
+
+ OneOfEach ooe2;
+ BOOST_CHECK_THROW(ooe2.read(proto.get()),
+ apache::thrift::protocol::TProtocolException);
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/Makefile.am b/src/jaegertracing/thrift/lib/cpp/test/Makefile.am
new file mode 100755
index 000000000..2a0b9e693
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/Makefile.am
@@ -0,0 +1,425 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+AUTOMAKE_OPTIONS = subdir-objects serial-tests nostdinc
+
+BUILT_SOURCES = gen-cpp/AnnotationTest_types.h \
+ gen-cpp/DebugProtoTest_types.h \
+ gen-cpp/EnumTest_types.h \
+ gen-cpp/OptionalRequiredTest_types.h \
+ gen-cpp/Recursive_types.h \
+ gen-cpp/ThriftTest_types.h \
+ gen-cpp/TypedefTest_types.h \
+ gen-cpp/ChildService.h \
+ gen-cpp/EmptyService.h \
+ gen-cpp/ParentService.h \
+ gen-cpp/OneWayTest_types.h \
+ gen-cpp/OneWayService.h \
+ gen-cpp/OneWayTest_constants.h \
+ gen-cpp/proc_types.h
+
+noinst_LTLIBRARIES = libtestgencpp.la libprocessortest.la
+nodist_libtestgencpp_la_SOURCES = \
+ gen-cpp/AnnotationTest_types.cpp \
+ gen-cpp/AnnotationTest_types.h \
+ gen-cpp/DebugProtoTest_types.cpp \
+ gen-cpp/DebugProtoTest_types.h \
+ gen-cpp/DoubleConstantsTest_constants.cpp \
+ gen-cpp/DoubleConstantsTest_constants.h \
+ gen-cpp/EnumTest_types.cpp \
+ gen-cpp/EnumTest_types.h \
+ gen-cpp/OptionalRequiredTest_types.cpp \
+ gen-cpp/OptionalRequiredTest_types.h \
+ gen-cpp/Recursive_types.cpp \
+ gen-cpp/Recursive_types.h \
+ gen-cpp/ThriftTest_types.cpp \
+ gen-cpp/ThriftTest_types.h \
+ gen-cpp/ThriftTest_constants.cpp \
+ gen-cpp/ThriftTest_constants.h \
+ gen-cpp/TypedefTest_types.cpp \
+ gen-cpp/TypedefTest_types.h \
+ gen-cpp/OneWayService.cpp \
+ gen-cpp/OneWayTest_constants.cpp \
+ gen-cpp/OneWayTest_types.h \
+ gen-cpp/OneWayService.h \
+ gen-cpp/OneWayTest_constants.h \
+ gen-cpp/OneWayTest_types.cpp \
+ ThriftTest_extras.cpp \
+ DebugProtoTest_extras.cpp
+
+nodist_libprocessortest_la_SOURCES = \
+ gen-cpp/ChildService.cpp \
+ gen-cpp/ChildService.h \
+ gen-cpp/EmptyService.cpp \
+ gen-cpp/EmptyService.h \
+ gen-cpp/ParentService.cpp \
+ gen-cpp/ParentService.h \
+ gen-cpp/proc_types.cpp \
+ gen-cpp/proc_types.h
+
+ThriftTest_extras.o: gen-cpp/ThriftTest_types.h
+DebugProtoTest_extras.o: gen-cpp/DebugProtoTest_types.h
+
+libtestgencpp_la_LIBADD = $(top_builddir)/lib/cpp/libthrift.la
+
+noinst_PROGRAMS = Benchmark \
+ concurrency_test
+
+Benchmark_SOURCES = \
+ Benchmark.cpp
+
+Benchmark_LDADD = libtestgencpp.la
+
+check_PROGRAMS = \
+ UnitTests \
+ TFDTransportTest \
+ TPipedTransportTest \
+ DebugProtoTest \
+ JSONProtoTest \
+ OptionalRequiredTest \
+ RecursiveTest \
+ SpecializationTest \
+ AllProtocolsTest \
+ TransportTest \
+ TInterruptTest \
+ TServerIntegrationTest \
+ SecurityTest \
+ ZlibTest \
+ TFileTransportTest \
+ link_test \
+ OpenSSLManualInitTest \
+ EnumTest \
+ RenderedDoubleConstantsTest \
+ AnnotationTest
+
+if AMX_HAVE_LIBEVENT
+noinst_PROGRAMS += \
+ processor_test
+check_PROGRAMS += \
+ TNonblockingServerTest \
+ TNonblockingSSLServerTest
+endif
+
+TESTS_ENVIRONMENT= \
+ BOOST_TEST_LOG_SINK=tests.xml \
+ BOOST_TEST_LOG_LEVEL=test_suite \
+ BOOST_TEST_LOG_FORMAT=XML
+
+TESTS = \
+ $(check_PROGRAMS)
+
+UnitTests_SOURCES = \
+ UnitTestMain.cpp \
+ OneWayHTTPTest.cpp \
+ TMemoryBufferTest.cpp \
+ TBufferBaseTest.cpp \
+ Base64Test.cpp \
+ ToStringTest.cpp \
+ TypedefTest.cpp \
+ TServerSocketTest.cpp \
+ TServerTransportTest.cpp \
+ TTransportCheckThrow.h
+
+UnitTests_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD)
+
+TInterruptTest_SOURCES = \
+ TSocketInterruptTest.cpp \
+ TSSLSocketInterruptTest.cpp
+
+TInterruptTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_FILESYSTEM_LDADD) \
+ $(BOOST_CHRONO_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD)
+
+TServerIntegrationTest_SOURCES = \
+ TServerIntegrationTest.cpp
+
+TServerIntegrationTest_LDADD = \
+ libtestgencpp.la \
+ libprocessortest.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD)
+
+SecurityTest_SOURCES = \
+ SecurityTest.cpp
+
+SecurityTest_LDADD = \
+ libtestgencpp.la \
+ libprocessortest.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_FILESYSTEM_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD)
+
+TransportTest_SOURCES = \
+ TransportTest.cpp
+
+TransportTest_LDADD = \
+ libtestgencpp.la \
+ $(top_builddir)/lib/cpp/libthriftz.la \
+ $(BOOST_TEST_LDADD) \
+ -lz
+
+ZlibTest_SOURCES = \
+ ZlibTest.cpp
+
+ZlibTest_LDADD = \
+ libtestgencpp.la \
+ $(top_builddir)/lib/cpp/libthriftz.la \
+ $(BOOST_TEST_LDADD) \
+ -lz
+
+EnumTest_SOURCES = \
+ EnumTest.cpp
+
+EnumTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+RenderedDoubleConstantsTest_SOURCES = RenderedDoubleConstantsTest.cpp
+
+RenderedDoubleConstantsTest_LDADD = libtestgencpp.la $(BOOST_TEST_LDADD)
+
+AnnotationTest_SOURCES = \
+ AnnotationTest.cpp
+
+AnnotationTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+TFileTransportTest_SOURCES = \
+ TFileTransportTest.cpp
+
+TFileTransportTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+#
+# TFDTransportTest
+#
+TFDTransportTest_SOURCES = \
+ TFDTransportTest.cpp
+
+TFDTransportTest_LDADD = \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(BOOST_TEST_LDADD)
+
+
+#
+# TPipedTransportTest
+#
+TPipedTransportTest_SOURCES = \
+ TPipedTransportTest.cpp \
+ TPipeInterruptTest.cpp
+
+TPipedTransportTest_LDADD = \
+ libtestgencpp.la \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD)
+
+#
+# AllProtocolsTest
+#
+AllProtocolsTest_SOURCES = \
+ AllProtocolTests.cpp \
+ AllProtocolTests.tcc \
+ GenericHelpers.h
+
+AllProtocolsTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+#
+# DebugProtoTest
+#
+DebugProtoTest_SOURCES = \
+ DebugProtoTest.cpp
+
+DebugProtoTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+
+#
+# JSONProtoTest
+#
+JSONProtoTest_SOURCES = \
+ JSONProtoTest.cpp
+
+JSONProtoTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+#
+# TNonblockingServerTest
+#
+TNonblockingServerTest_SOURCES = TNonblockingServerTest.cpp
+
+TNonblockingServerTest_LDADD = libprocessortest.la \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(top_builddir)/lib/cpp/libthriftnb.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_LDFLAGS) \
+ $(LIBEVENT_LIBS)
+#
+# TNonblockingSSLServerTest
+#
+TNonblockingSSLServerTest_SOURCES = TNonblockingSSLServerTest.cpp
+
+TNonblockingSSLServerTest_LDADD = libprocessortest.la \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(top_builddir)/lib/cpp/libthriftnb.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_LDFLAGS) \
+ $(BOOST_FILESYSTEM_LDADD) \
+ $(BOOST_CHRONO_LDADD) \
+ $(BOOST_SYSTEM_LDADD) \
+ $(BOOST_THREAD_LDADD) \
+ $(LIBEVENT_LIBS)
+
+#
+# OptionalRequiredTest
+#
+OptionalRequiredTest_SOURCES = \
+ OptionalRequiredTest.cpp
+
+OptionalRequiredTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+#
+# OptionalRequiredTest
+#
+RecursiveTest_SOURCES = \
+ RecursiveTest.cpp
+
+RecursiveTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+#
+# SpecializationTest
+#
+SpecializationTest_SOURCES = \
+ SpecializationTest.cpp
+
+SpecializationTest_LDADD = \
+ libtestgencpp.la \
+ $(BOOST_TEST_LDADD)
+
+concurrency_test_SOURCES = \
+ concurrency/Tests.cpp \
+ concurrency/ThreadFactoryTests.h \
+ concurrency/ThreadManagerTests.h \
+ concurrency/TimerManagerTests.h
+
+concurrency_test_LDADD = \
+ $(top_builddir)/lib/cpp/libthrift.la
+
+link_test_SOURCES = \
+ link/LinkTest.cpp \
+ link/TemplatedService1.cpp \
+ link/TemplatedService2.cpp
+
+processor_test_SOURCES = \
+ processor/ProcessorTest.cpp \
+ processor/EventLog.cpp \
+ processor/ServerThread.cpp \
+ processor/EventLog.h \
+ processor/Handlers.h \
+ processor/ServerThread.h
+
+processor_test_LDADD = libprocessortest.la \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(top_builddir)/lib/cpp/libthriftnb.la \
+ $(BOOST_TEST_LDADD) \
+ $(BOOST_LDFLAGS) \
+ $(LIBEVENT_LIBS)
+
+OpenSSLManualInitTest_SOURCES = \
+ OpenSSLManualInitTest.cpp
+
+OpenSSLManualInitTest_LDADD = \
+ $(top_builddir)/lib/cpp/libthrift.la \
+ $(BOOST_TEST_LDADD) \
+ $(OPENSSL_LDFLAGS) \
+ $(OPENSSL_LIBS)
+
+#
+# Common thrift code generation rules
+#
+
+gen-cpp/AnnotationTest_constants.cpp gen-cpp/AnnotationTest_constants.h gen-cpp/AnnotationTest_types.cpp gen-cpp/AnnotationTest_types.h: $(top_srcdir)/test/AnnotationTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/DebugProtoTest_types.cpp gen-cpp/DebugProtoTest_types.h gen-cpp/EmptyService.cpp gen-cpp/EmptyService.h: $(top_srcdir)/test/DebugProtoTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/DoubleConstantsTest_constants.cpp gen-cpp/DoubleConstantsTest_constants.h: $(top_srcdir)/test/DoubleConstantsTest.thrift
+ $(THRIFT) --gen cpp $<
+
+
+gen-cpp/EnumTest_types.cpp gen-cpp/EnumTest_types.h: $(top_srcdir)/test/EnumTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/TypedefTest_types.cpp gen-cpp/TypedefTest_types.h: $(top_srcdir)/test/TypedefTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/OptionalRequiredTest_types.cpp gen-cpp/OptionalRequiredTest_types.h: $(top_srcdir)/test/OptionalRequiredTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/Recursive_types.cpp gen-cpp/Recursive_types.h: $(top_srcdir)/test/Recursive.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/Service.cpp gen-cpp/StressTest_types.cpp: $(top_srcdir)/test/StressTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/SecondService.cpp gen-cpp/ThriftTest_constants.cpp gen-cpp/ThriftTest.cpp gen-cpp/ThriftTest_types.cpp gen-cpp/ThriftTest_types.h: $(top_srcdir)/test/ThriftTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/OneWayService.cpp gen-cpp/OneWayTest_constants.cpp gen-cpp/OneWayTest_types.h gen-cpp/OneWayService.h gen-cpp/OneWayTest_constants.h gen-cpp/OneWayTest_types.cpp: OneWayTest.thrift
+ $(THRIFT) --gen cpp $<
+
+gen-cpp/ChildService.cpp gen-cpp/ChildService.h gen-cpp/ParentService.cpp gen-cpp/ParentService.h gen-cpp/proc_types.cpp gen-cpp/proc_types.h: processor/proc.thrift
+ $(THRIFT) --gen cpp:templates,cob_style $<
+
+AM_CPPFLAGS = $(BOOST_CPPFLAGS) -I$(top_srcdir)/lib/cpp/src -I$(top_srcdir)/lib/cpp/src/thrift -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I.
+AM_LDFLAGS = $(BOOST_LDFLAGS)
+AM_CXXFLAGS = -Wall -Wextra -pedantic
+
+clean-local:
+ $(RM) gen-cpp/*
+
+EXTRA_DIST = \
+ concurrency \
+ processor \
+ qt \
+ CMakeLists.txt \
+ DebugProtoTest_extras.cpp \
+ ThriftTest_extras.cpp \
+ OneWayTest.thrift
diff --git a/src/jaegertracing/thrift/lib/cpp/test/OneWayHTTPTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/OneWayHTTPTest.cpp
new file mode 100644
index 000000000..55d919bba
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/OneWayHTTPTest.cpp
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/thread.hpp>
+#include <iostream>
+#include <climits>
+#include <vector>
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/protocol/TJSONProtocol.h>
+#include <thrift/server/TThreadedServer.h>
+#include <thrift/transport/THttpServer.h>
+#include <thrift/transport/THttpClient.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/TSocket.h>
+#include <memory>
+#include <thrift/transport/TBufferTransports.h>
+#include "gen-cpp/OneWayService.h"
+
+BOOST_AUTO_TEST_SUITE(OneWayHTTPTest)
+
+using namespace apache::thrift;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TBinaryProtocol;
+using apache::thrift::protocol::TBinaryProtocolFactory;
+using apache::thrift::protocol::TJSONProtocol;
+using apache::thrift::protocol::TJSONProtocolFactory;
+using apache::thrift::server::TThreadedServer;
+using apache::thrift::server::TServerEventHandler;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::THttpServer;
+using apache::thrift::transport::THttpServerTransportFactory;
+using apache::thrift::transport::THttpClient;
+using apache::thrift::transport::TBufferedTransport;
+using apache::thrift::transport::TBufferedTransportFactory;
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::transport::TServerSocket;
+using apache::thrift::transport::TSocket;
+using apache::thrift::transport::TTransportException;
+using std::shared_ptr;
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::string;
+namespace utf = boost::unit_test;
+
+// Define this env var to enable some logging (in case you need to debug)
+#undef ENABLE_STDERR_LOGGING
+
+class OneWayServiceHandler : public onewaytest::OneWayServiceIf {
+public:
+ OneWayServiceHandler() = default;
+
+ void roundTripRPC() override {
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "roundTripRPC()" << endl;
+#endif
+ }
+ void oneWayRPC() override {
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "oneWayRPC()" << std::endl ;
+#endif
+ }
+};
+
+class OneWayServiceCloneFactory : virtual public onewaytest::OneWayServiceIfFactory {
+ public:
+ ~OneWayServiceCloneFactory() override = default;
+ onewaytest::OneWayServiceIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) override
+ {
+ (void)connInfo ;
+ return new OneWayServiceHandler;
+ }
+ void releaseHandler( onewaytest::OneWayServiceIf* handler) override {
+ delete handler;
+ }
+};
+
+class RPC0ThreadClass {
+public:
+ RPC0ThreadClass(TThreadedServer& server) : server_(server) { } // Constructor
+~RPC0ThreadClass() = default; // Destructor
+
+void Run() {
+ server_.serve() ;
+}
+ TThreadedServer& server_ ;
+} ;
+
+using apache::thrift::concurrency::Monitor;
+using apache::thrift::concurrency::Mutex;
+using apache::thrift::concurrency::Synchronized;
+
+// copied from IntegrationTest
+class TServerReadyEventHandler : public TServerEventHandler, public Monitor {
+public:
+ TServerReadyEventHandler() : isListening_(false), accepted_(0) {}
+ ~TServerReadyEventHandler() override = default;
+ void preServe() override {
+ Synchronized sync(*this);
+ isListening_ = true;
+ notify();
+ }
+ void* createContext(shared_ptr<TProtocol> input,
+ shared_ptr<TProtocol> output) override {
+ Synchronized sync(*this);
+ ++accepted_;
+ notify();
+
+ (void)input;
+ (void)output;
+ return nullptr;
+ }
+ bool isListening() const { return isListening_; }
+ uint64_t acceptedCount() const { return accepted_; }
+
+private:
+ bool isListening_;
+ uint64_t accepted_;
+};
+
+class TBlockableBufferedTransport : public TBufferedTransport {
+ public:
+ TBlockableBufferedTransport(std::shared_ptr<TTransport> transport)
+ : TBufferedTransport(transport, 10240),
+ blocked_(false) {
+ }
+
+ uint32_t write_buffer_length() {
+ auto have_bytes = static_cast<uint32_t>(wBase_ - wBuf_.get());
+ return have_bytes ;
+ }
+
+ void block() {
+ blocked_ = true ;
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "block flushing\n" ;
+#endif
+ }
+ void unblock() {
+ blocked_ = false ;
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "unblock flushing, buffer is\n<<" << std::string((char *)wBuf_.get(), write_buffer_length()) << ">>\n" ;
+#endif
+ }
+
+ void flush() override {
+ if (blocked_) {
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "flush was blocked\n" ;
+#endif
+ return ;
+ }
+ TBufferedTransport::flush() ;
+ }
+
+ bool blocked_ ;
+} ;
+
+BOOST_AUTO_TEST_CASE( JSON_BufferedHTTP )
+{
+ std::shared_ptr<TServerSocket> ss = std::make_shared<TServerSocket>(0) ;
+ TThreadedServer server(
+ std::make_shared<onewaytest::OneWayServiceProcessorFactory>(std::make_shared<OneWayServiceCloneFactory>()),
+ ss, //port
+ std::make_shared<THttpServerTransportFactory>(),
+ std::make_shared<TJSONProtocolFactory>());
+
+ std::shared_ptr<TServerReadyEventHandler> pEventHandler(new TServerReadyEventHandler) ;
+ server.setServerEventHandler(pEventHandler);
+
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "Starting the server...\n";
+#endif
+ RPC0ThreadClass t(server) ;
+ boost::thread thread(&RPC0ThreadClass::Run, &t);
+
+ {
+ Synchronized sync(*(pEventHandler.get()));
+ while (!pEventHandler->isListening()) {
+ pEventHandler->wait();
+ }
+ }
+
+ int port = ss->getPort() ;
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "port " << port << endl ;
+#endif
+
+ {
+ std::shared_ptr<TSocket> socket(new TSocket("localhost", port));
+ socket->setRecvTimeout(10000) ; // 1000msec should be enough
+ std::shared_ptr<TBlockableBufferedTransport> blockable_transport(new TBlockableBufferedTransport(socket));
+ std::shared_ptr<TTransport> transport(new THttpClient(blockable_transport, "localhost", "/service"));
+ std::shared_ptr<TProtocol> protocol(new TJSONProtocol(transport));
+ onewaytest::OneWayServiceClient client(protocol);
+
+
+ transport->open();
+ client.roundTripRPC();
+ blockable_transport->block() ;
+ uint32_t size0 = blockable_transport->write_buffer_length() ;
+ client.send_oneWayRPC() ;
+ uint32_t size1 = blockable_transport->write_buffer_length() ;
+ client.send_oneWayRPC() ;
+ uint32_t size2 = blockable_transport->write_buffer_length() ;
+ BOOST_CHECK((size1 - size0) == (size2 - size1)) ;
+ blockable_transport->unblock() ;
+ client.send_roundTripRPC() ;
+ blockable_transport->flush() ;
+ try {
+ client.recv_roundTripRPC() ;
+ } catch (const TTransportException &e) {
+ BOOST_ERROR( "we should not get a transport exception -- this means we failed: " + std::string(e.what()) ) ;
+ }
+ transport->close();
+ }
+ server.stop();
+ thread.join() ;
+#ifdef ENABLE_STDERR_LOGGING
+ cerr << "finished.\n";
+#endif
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/OneWayTest.thrift b/src/jaegertracing/thrift/lib/cpp/test/OneWayTest.thrift
new file mode 100644
index 000000000..102cf26e5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/OneWayTest.thrift
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+namespace c_glib OneWayTest
+namespace java onewaytest
+namespace cpp onewaytest
+namespace rb Onewaytest
+namespace perl OneWayTest
+namespace csharp Onewaytest
+namespace js OneWayTest
+namespace st OneWayTest
+namespace py OneWayTest
+namespace py.twisted OneWayTest
+namespace go onewaytest
+namespace php OneWayTest
+namespace delphi Onewaytest
+namespace lua OneWayTest
+namespace xsd test (uri = 'http://thrift.apache.org/ns/OneWayTest')
+namespace netcore ThriftAsync.OneWayTest
+
+// a minimal Thrift service, for use in OneWayHTTPTtest.cpp
+service OneWayService {
+ void roundTripRPC(),
+ oneway void oneWayRPC()
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/OpenSSLManualInitTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/OpenSSLManualInitTest.cpp
new file mode 100644
index 000000000..a7518064e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/OpenSSLManualInitTest.cpp
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+// To show that this test actually tests something, you can change
+// MANUAL_OPENSSL_INIT to 0 to cause automatic OpenSSL init/cleanup,
+// which will cause the test to fail
+#define MANUAL_OPENSSL_INIT 1
+#ifdef _WIN32
+#include <WinSock2.h>
+#endif
+
+#include <boost/test/unit_test.hpp>
+#include <openssl/evp.h>
+#include <thrift/transport/TSSLSocket.h>
+
+using namespace apache::thrift::transport;
+
+void make_isolated_sslsocketfactory() {
+ // Here we create an isolated TSSLSocketFactory to ensure the
+ // constructor and destructor of TSSLSocketFactory get run. Thus
+ // without manual initialization normally OpenSSL would be
+ // uninitialized after this function.
+ TSSLSocketFactory factory;
+}
+
+void openssl_init() {
+#if MANUAL_OPENSSL_INIT
+ TSSLSocketFactory::setManualOpenSSLInitialization(true);
+ initializeOpenSSL();
+#endif
+}
+
+void openssl_cleanup() {
+#if MANUAL_OPENSSL_INIT
+ cleanupOpenSSL();
+#endif
+}
+
+void test_openssl_availability() {
+ // Check whether Thrift leaves OpenSSL functionality available after
+ // the last TSSLSocketFactory is destroyed when manual
+ // initialization is set
+ openssl_init();
+ make_isolated_sslsocketfactory();
+
+ // The following function is one that will fail if OpenSSL is
+ // uninitialized. It might also fail on very old versions of
+ // OpenSSL...
+ const EVP_MD* md = EVP_get_digestbyname("SHA256");
+ BOOST_CHECK(md != nullptr);
+ openssl_cleanup();
+}
+
+#ifdef BOOST_TEST_DYN_LINK
+bool init_unit_test_suite() {
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "OpenSSLManualInit";
+
+ suite->add(BOOST_TEST_CASE(test_openssl_availability));
+
+ return true;
+}
+
+int main( int argc, char* argv[] ) {
+ return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
+}
+#else
+boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+ THRIFT_UNUSED_VARIABLE(argc);
+ THRIFT_UNUSED_VARIABLE(argv);
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "OpenSSLManualInit";
+
+ suite->add(BOOST_TEST_CASE(test_openssl_availability));
+
+ return NULL;
+}
+#endif \ No newline at end of file
diff --git a/src/jaegertracing/thrift/lib/cpp/test/OptionalRequiredTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/OptionalRequiredTest.cpp
new file mode 100644
index 000000000..4c435469e
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/OptionalRequiredTest.cpp
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include <map>
+#include <thrift/protocol/TDebugProtocol.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/transport/TBufferTransports.h>
+#include "gen-cpp/OptionalRequiredTest_types.h"
+
+#define BOOST_TEST_MODULE OptionalRequiredTest
+#include <boost/test/unit_test.hpp>
+
+using namespace thrift::test;
+using namespace apache::thrift;
+using namespace apache::thrift::transport;
+using namespace apache::thrift::protocol;
+
+/*
+template<typename Struct>
+void trywrite(const Struct& s, bool should_work) {
+ bool worked;
+ try {
+ TBinaryProtocol protocol(std::shared_ptr<TTransport>(new TMemoryBuffer));
+ s.write(&protocol);
+ worked = true;
+ } catch (TProtocolException & ex) {
+ worked = false;
+ }
+ BOOST_CHECK(worked == should_work);
+}
+*/
+
+template <typename Struct1, typename Struct2>
+void write_to_read(const Struct1& w, Struct2& r) {
+ TBinaryProtocol protocol(std::shared_ptr<TTransport>(new TMemoryBuffer));
+ w.write(&protocol);
+ r.read(&protocol);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_1) {
+ OldSchool o;
+
+ const std::string expected_result(
+ "OldSchool {\n"
+ " 01: im_int (i16) = 0,\n"
+ " 02: im_str (string) = \"\",\n"
+ " 03: im_big (list) = list<map>[0] {\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(o));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_2_1) {
+ Simple s;
+
+ const std::string expected_result(
+ "Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(s));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_2_2) {
+ Simple s;
+ s.im_optional = 10;
+
+ const std::string expected_result(
+ "Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(s));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_2_3) {
+ Simple s;
+ s.im_optional = 10;
+ s.__isset.im_optional = true;
+
+ const std::string expected_result(
+ "Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ " 03: im_optional (i16) = 10,\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(s));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_2_4) {
+ Simple s;
+ s.__isset.im_optional = true;
+
+ const std::string expected_result(
+ "Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ " 03: im_optional (i16) = 0,\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(s));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_2_5) {
+ Simple s;
+ s.__isset.im_optional = true;
+ s.im_optional = 10;
+
+ const std::string expected_result(
+ "Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ " 03: im_optional (i16) = 10,\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(s));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_3) {
+ // assign/copy-construct with non-required fields
+
+ Simple s1, s2;
+ s1.__isset.im_default = true;
+ s1.__set_im_optional(10);
+ BOOST_CHECK(s1.__isset.im_default);
+ BOOST_CHECK(s1.__isset.im_optional);
+
+ s2 = s1;
+
+ BOOST_CHECK(s2.__isset.im_default);
+ BOOST_CHECK(s2.__isset.im_optional);
+
+ Simple s3(s1);
+
+ BOOST_CHECK(s3.__isset.im_default);
+ BOOST_CHECK(s3.__isset.im_optional);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_4) {
+ // Write-to-read with optional fields.
+
+ Simple s1, s2, s3;
+ s1.im_optional = 10;
+ BOOST_CHECK(!s1.__isset.im_default);
+ // BOOST_CHECK(!s1.__isset.im_required); // Compile error.
+ BOOST_CHECK(!s1.__isset.im_optional);
+
+ write_to_read(s1, s2);
+
+ BOOST_CHECK(s2.__isset.im_default);
+ // BOOST_CHECK( s2.__isset.im_required); // Compile error.
+ BOOST_CHECK(!s2.__isset.im_optional);
+ BOOST_CHECK(s3.im_optional == 0);
+
+ s1.__isset.im_optional = true;
+ write_to_read(s1, s3);
+
+ BOOST_CHECK(s3.__isset.im_default);
+ // BOOST_CHECK( s3.__isset.im_required); // Compile error.
+ BOOST_CHECK(s3.__isset.im_optional);
+ BOOST_CHECK(s3.im_optional == 10);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_5) {
+ // Writing between optional and default.
+
+ Tricky1 t1;
+ Tricky2 t2;
+
+ t2.im_optional = 10;
+ write_to_read(t2, t1);
+ write_to_read(t1, t2);
+ BOOST_CHECK(!t1.__isset.im_default);
+ BOOST_CHECK(t2.__isset.im_optional);
+ BOOST_CHECK(t1.im_default == t2.im_optional);
+ BOOST_CHECK(t1.im_default == 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_6) {
+ // Writing between default and required.
+
+ Tricky1 t1;
+ Tricky3 t3;
+ write_to_read(t1, t3);
+ write_to_read(t3, t1);
+ BOOST_CHECK(t1.__isset.im_default);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_7) {
+ // Writing between optional and required.
+
+ Tricky2 t2;
+ Tricky3 t3;
+ t2.__isset.im_optional = true;
+ write_to_read(t2, t3);
+ write_to_read(t3, t2);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_8) {
+ // Mu-hu-ha-ha-ha!
+
+ Tricky2 t2;
+ Tricky3 t3;
+ try {
+ write_to_read(t2, t3);
+ abort();
+ } catch (const TProtocolException&) {
+ }
+
+ write_to_read(t3, t2);
+ BOOST_CHECK(t2.__isset.im_optional);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_9) {
+ Complex c;
+
+ const std::string expected_result(
+ "Complex {\n"
+ " 01: cp_default (i16) = 0,\n"
+ " 02: cp_required (i16) = 0,\n"
+ " 04: the_map (map) = map<i16,struct>[0] {\n"
+ " },\n"
+ " 05: req_simp (struct) = Simple {\n"
+ " 01: im_default (i16) = 0,\n"
+ " 02: im_required (i16) = 0,\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(c));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_10) {
+ Tricky1 t1;
+ Tricky2 t2;
+ // Compile error.
+ //(void)(t1 == t2);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_11) {
+ OldSchool o1, o2, o3;
+ BOOST_CHECK(o1 == o2);
+ o1.im_int = o2.im_int = 10;
+ BOOST_CHECK(o1 == o2);
+ o1.__isset.im_int = true;
+ o2.__isset.im_int = false;
+ BOOST_CHECK(o1 == o2);
+ o1.im_int = 20;
+ o1.__isset.im_int = false;
+ BOOST_CHECK(o1 != o2);
+ o1.im_int = 10;
+ BOOST_CHECK(o1 == o2);
+ o1.im_str = o2.im_str = "foo";
+ BOOST_CHECK(o1 == o2);
+ o1.__isset.im_str = o2.__isset.im_str = true;
+ BOOST_CHECK(o1 == o2);
+ std::map<int32_t, std::string> mymap;
+ mymap[1] = "bar";
+ mymap[2] = "baz";
+ o1.im_big.push_back(std::map<int32_t, std::string>());
+ BOOST_CHECK(o1 != o2);
+ o2.im_big.push_back(std::map<int32_t, std::string>());
+ BOOST_CHECK(o1 == o2);
+ o2.im_big.push_back(mymap);
+ BOOST_CHECK(o1 != o2);
+ o1.im_big.push_back(mymap);
+ BOOST_CHECK(o1 == o2);
+
+ TBinaryProtocol protocol(std::shared_ptr<TTransport>(new TMemoryBuffer));
+ o1.write(&protocol);
+
+ o1.im_big.push_back(mymap);
+ mymap[3] = "qux";
+ o2.im_big.push_back(mymap);
+ BOOST_CHECK(o1 != o2);
+ o1.im_big.back()[3] = "qux";
+ BOOST_CHECK(o1 == o2);
+
+ o3.read(&protocol);
+ o3.im_big.push_back(mymap);
+ BOOST_CHECK(o1 == o3);
+
+ const std::string expected_result(
+ "OldSchool {\n"
+ " 01: im_int (i16) = 10,\n"
+ " 02: im_str (string) = \"foo\",\n"
+ " 03: im_big (list) = list<map>[3] {\n"
+ " [0] = map<i32,string>[0] {\n"
+ " },\n"
+ " [1] = map<i32,string>[2] {\n"
+ " 1 -> \"bar\",\n"
+ " 2 -> \"baz\",\n"
+ " },\n"
+ " [2] = map<i32,string>[3] {\n"
+ " 1 -> \"bar\",\n"
+ " 2 -> \"baz\",\n"
+ " 3 -> \"qux\",\n"
+ " },\n"
+ " },\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(o3));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_12) {
+ Tricky2 t1, t2;
+ BOOST_CHECK(t1.__isset.im_optional == false);
+ BOOST_CHECK(t2.__isset.im_optional == false);
+ BOOST_CHECK(t1 == t2);
+ t1.im_optional = 5;
+ BOOST_CHECK(t1 == t2);
+ t2.im_optional = 5;
+ BOOST_CHECK(t1 == t2);
+ t1.__isset.im_optional = true;
+ BOOST_CHECK(t1 != t2);
+ t2.__isset.im_optional = true;
+ BOOST_CHECK(t1 == t2);
+ t1.im_optional = 10;
+ BOOST_CHECK(t1 != t2);
+ t2.__isset.im_optional = false;
+ BOOST_CHECK(t1 != t2);
+}
+
+BOOST_AUTO_TEST_CASE(test_optional_required_13) {
+ OptionalDefault t1, t2;
+
+ BOOST_CHECK(t1.__isset.opt_int == true);
+ BOOST_CHECK(t1.__isset.opt_str == true);
+ BOOST_CHECK(t1.opt_int == t2.opt_int);
+ BOOST_CHECK(t1.opt_str == t2.opt_str);
+
+ write_to_read(t1, t2);
+ BOOST_CHECK(t2.__isset.opt_int == true);
+ BOOST_CHECK(t2.__isset.opt_str == true);
+ BOOST_CHECK(t1.opt_int == t2.opt_int);
+ BOOST_CHECK(t1.opt_str == t2.opt_str);
+
+ const std::string expected_result(
+ "OptionalDefault {\n"
+ " 01: opt_int (i16) = 1234,\n"
+ " 02: opt_str (string) = \"default\",\n"
+ "}");
+ const std::string result(apache::thrift::ThriftDebugString(t2));
+
+ BOOST_CHECK_MESSAGE(!expected_result.compare(result),
+ "Expected:\n" << expected_result << "\nGotten:\n" << result);
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/RecursiveTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/RecursiveTest.cpp
new file mode 100644
index 000000000..ab2e46dd7
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/RecursiveTest.cpp
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Contains some contributions under the Thrift Software License.
+ * Please see doc/old-thrift-license.txt in the Thrift distribution for
+ * details.
+ */
+
+#include "gen-cpp/Recursive_types.h"
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <memory>
+#include <thrift/transport/TBufferTransports.h>
+
+#define BOOST_TEST_MODULE RecursiveTest
+#include <boost/test/unit_test.hpp>
+
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::protocol::TBinaryProtocol;
+using std::shared_ptr;
+
+BOOST_AUTO_TEST_CASE(test_recursive_1) {
+ shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
+
+ RecTree tree;
+ RecTree child;
+ tree.children.push_back(child);
+
+ tree.write(prot.get());
+
+ RecTree result;
+ result.read(prot.get());
+ BOOST_CHECK(tree == result);
+}
+
+BOOST_AUTO_TEST_CASE(test_recursive_2) {
+ shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
+
+ RecList l;
+ shared_ptr<RecList> l2(new RecList);
+ l.nextitem = l2;
+
+ l.write(prot.get());
+
+ RecList resultlist;
+ resultlist.read(prot.get());
+ BOOST_CHECK(resultlist.nextitem != nullptr);
+ BOOST_CHECK(resultlist.nextitem->nextitem == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(test_recursive_3) {
+ shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
+
+ CoRec c;
+ shared_ptr<CoRec2> r(new CoRec2);
+ c.other = r;
+
+ c.write(prot.get());
+
+ c.read(prot.get());
+ BOOST_CHECK(c.other != nullptr);
+ BOOST_CHECK(c.other->other.other == nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(test_recursive_4) {
+ shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
+
+ shared_ptr<RecList> depthLimit(new RecList);
+ depthLimit->nextitem = depthLimit;
+ BOOST_CHECK_THROW(depthLimit->write(prot.get()),
+ apache::thrift::protocol::TProtocolException);
+
+ depthLimit->nextitem.reset();
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/RenderedDoubleConstantsTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/RenderedDoubleConstantsTest.cpp
new file mode 100644
index 000000000..0ca042b73
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/RenderedDoubleConstantsTest.cpp
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#define EPSILON 0.0000001
+#include <typeindex>
+#include <typeinfo>
+#include <vector>
+
+#include "gen-cpp/DoubleConstantsTest_constants.h"
+using namespace thrift::test;
+
+#define BOOST_TEST_MODULE RenderedDoubleConstantsTest
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(RenderedDoubleConstantsTest)
+
+BOOST_AUTO_TEST_CASE(test_rendered_double_constants) {
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT = 1.0;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT = -100.0;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT = 9223372036854775807.0;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT = -9223372036854775807.0;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS = 3.14159265359;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE = 1000000.1;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE = -1000000.1;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE = 1.7e+308;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE = 9223372036854775816.43;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE = -1.7e+308;
+ const double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE = -9223372036854775816.43;
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE, EPSILON);
+ BOOST_CHECK_CLOSE(
+ g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST,
+ EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE, EPSILON);
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST).hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE).hash_code());
+ BOOST_CHECK(
+ typeid(g_DoubleConstantsTest_constants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST)
+ .hash_code() ==
+ typeid(EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE).hash_code());
+}
+
+BOOST_AUTO_TEST_CASE(test_rendered_double_list) {
+ const std::vector<double> EXPECTED_DOUBLE_LIST{1.0,-100.0,100.0,9223372036854775807.0,-9223372036854775807.0,
+ 3.14159265359,1000000.1,-1000000.1,1.7e+308,-1.7e+308,9223372036854775816.43,-9223372036854775816.43};
+ BOOST_CHECK_EQUAL(g_DoubleConstantsTest_constants.DOUBLE_LIST_TEST.size(), EXPECTED_DOUBLE_LIST.size());
+ for (unsigned int i = 0; i < EXPECTED_DOUBLE_LIST.size(); ++i) {
+ BOOST_CHECK_CLOSE(g_DoubleConstantsTest_constants.DOUBLE_LIST_TEST[i], EXPECTED_DOUBLE_LIST[i], EPSILON);
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/SecurityTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/SecurityTest.cpp
new file mode 100644
index 000000000..982a4f30c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/SecurityTest.cpp
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE SecurityTest
+#include <boost/test/unit_test.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <memory>
+#include <thrift/transport/TSSLServerSocket.h>
+#include <thrift/transport/TSSLSocket.h>
+#include <thrift/transport/TTransport.h>
+#include <vector>
+#ifdef __linux__
+#include <signal.h>
+#endif
+
+using apache::thrift::transport::TSSLServerSocket;
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TSSLSocket;
+using apache::thrift::transport::TSSLSocketFactory;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TTransportFactory;
+
+using std::bind;
+using std::shared_ptr;
+
+boost::filesystem::path keyDir;
+boost::filesystem::path certFile(const std::string& filename)
+{
+ return keyDir / filename;
+}
+boost::mutex gMutex;
+
+struct GlobalFixture
+{
+ GlobalFixture()
+ {
+ using namespace boost::unit_test::framework;
+ for (int i = 0; i < master_test_suite().argc; ++i)
+ {
+ BOOST_TEST_MESSAGE(boost::format("argv[%1%] = \"%2%\"") % i % master_test_suite().argv[i]);
+ }
+
+ #ifdef __linux__
+ // OpenSSL calls send() without MSG_NOSIGPIPE so writing to a socket that has
+ // disconnected can cause a SIGPIPE signal...
+ signal(SIGPIPE, SIG_IGN);
+ #endif
+
+ TSSLSocketFactory::setManualOpenSSLInitialization(true);
+ apache::thrift::transport::initializeOpenSSL();
+
+ keyDir = boost::filesystem::current_path().parent_path().parent_path().parent_path() / "test" / "keys";
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ keyDir = boost::filesystem::path(master_test_suite().argv[master_test_suite().argc - 1]);
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ throw std::invalid_argument("The last argument to this test must be the directory containing the test certificate(s).");
+ }
+ }
+ }
+
+ virtual ~GlobalFixture()
+ {
+ apache::thrift::transport::cleanupOpenSSL();
+#ifdef __linux__
+ signal(SIGPIPE, SIG_DFL);
+#endif
+ }
+};
+
+#if (BOOST_VERSION >= 105900)
+BOOST_GLOBAL_FIXTURE(GlobalFixture);
+#else
+BOOST_GLOBAL_FIXTURE(GlobalFixture)
+#endif
+
+struct SecurityFixture
+{
+ void server(apache::thrift::transport::SSLProtocol protocol)
+ {
+ try
+ {
+ boost::mutex::scoped_lock lock(mMutex);
+
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory;
+ shared_ptr<TSSLServerSocket> pServerSocket;
+
+ pServerSocketFactory.reset(new TSSLSocketFactory(static_cast<apache::thrift::transport::SSLProtocol>(protocol)));
+ pServerSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+ pServerSocketFactory->loadCertificate(certFile("server.crt").string().c_str());
+ pServerSocketFactory->loadPrivateKey(certFile("server.key").string().c_str());
+ pServerSocketFactory->server(true);
+ pServerSocket.reset(new TSSLServerSocket("localhost", 0, pServerSocketFactory));
+ shared_ptr<TTransport> connectedClient;
+
+ try
+ {
+ pServerSocket->listen();
+ mPort = pServerSocket->getPort();
+ mCVar.notify_one();
+ lock.unlock();
+
+ connectedClient = pServerSocket->accept();
+ uint8_t buf[2];
+ buf[0] = 'O';
+ buf[1] = 'K';
+ connectedClient->write(&buf[0], 2);
+ connectedClient->flush();
+ }
+
+ catch (apache::thrift::transport::TTransportException& ex)
+ {
+ boost::mutex::scoped_lock lock(gMutex);
+ BOOST_TEST_MESSAGE(boost::format("SRV %1% Exception: %2%") % boost::this_thread::get_id() % ex.what());
+ }
+
+ if (connectedClient)
+ {
+ connectedClient->close();
+ connectedClient.reset();
+ }
+
+ pServerSocket->close();
+ pServerSocket.reset();
+ }
+ catch (std::exception& ex)
+ {
+ BOOST_FAIL(boost::format("%1%: %2%") % typeid(ex).name() % ex.what());
+ }
+ }
+
+ void client(apache::thrift::transport::SSLProtocol protocol)
+ {
+ try
+ {
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory;
+ shared_ptr<TSSLSocket> pClientSocket;
+
+ try
+ {
+ pClientSocketFactory.reset(new TSSLSocketFactory(static_cast<apache::thrift::transport::SSLProtocol>(protocol)));
+ pClientSocketFactory->authenticate(true);
+ pClientSocketFactory->loadCertificate(certFile("client.crt").string().c_str());
+ pClientSocketFactory->loadPrivateKey(certFile("client.key").string().c_str());
+ pClientSocketFactory->loadTrustedCertificates(certFile("CA.pem").string().c_str());
+ pClientSocket = pClientSocketFactory->createSocket("localhost", mPort);
+ pClientSocket->open();
+
+ uint8_t buf[3];
+ buf[0] = 0;
+ buf[1] = 0;
+ BOOST_CHECK_EQUAL(2, pClientSocket->read(&buf[0], 2));
+ BOOST_CHECK_EQUAL(0, memcmp(&buf[0], "OK", 2));
+ mConnected = true;
+ }
+ catch (apache::thrift::transport::TTransportException& ex)
+ {
+ boost::mutex::scoped_lock lock(gMutex);
+ BOOST_TEST_MESSAGE(boost::format("CLI %1% Exception: %2%") % boost::this_thread::get_id() % ex.what());
+ }
+
+ if (pClientSocket)
+ {
+ pClientSocket->close();
+ pClientSocket.reset();
+ }
+ }
+ catch (std::exception& ex)
+ {
+ BOOST_FAIL(boost::format("%1%: %2%") % typeid(ex).name() % ex.what());
+ }
+ }
+
+ static const char *protocol2str(size_t protocol)
+ {
+ static const char *strings[apache::thrift::transport::LATEST + 1] =
+ {
+ "SSLTLS",
+ "SSLv2",
+ "SSLv3",
+ "TLSv1_0",
+ "TLSv1_1",
+ "TLSv1_2"
+ };
+ return strings[protocol];
+ }
+
+ boost::mutex mMutex;
+ boost::condition_variable mCVar;
+ int mPort;
+ bool mConnected;
+};
+
+BOOST_FIXTURE_TEST_SUITE(BOOST_TEST_MODULE, SecurityFixture)
+
+BOOST_AUTO_TEST_CASE(ssl_security_matrix)
+{
+ try
+ {
+ // matrix of connection success between client and server with different SSLProtocol selections
+ bool matrix[apache::thrift::transport::LATEST + 1][apache::thrift::transport::LATEST + 1] =
+ {
+ // server = SSLTLS SSLv2 SSLv3 TLSv1_0 TLSv1_1 TLSv1_2
+ // client
+ /* SSLTLS */ { true, false, false, true, true, true },
+ /* SSLv2 */ { false, false, false, false, false, false },
+ /* SSLv3 */ { false, false, true, false, false, false },
+ /* TLSv1_0 */ { true, false, false, true, false, false },
+ /* TLSv1_1 */ { true, false, false, false, true, false },
+ /* TLSv1_2 */ { true, false, false, false, false, true }
+ };
+
+ for (size_t si = 0; si <= apache::thrift::transport::LATEST; ++si)
+ {
+ for (size_t ci = 0; ci <= apache::thrift::transport::LATEST; ++ci)
+ {
+ if (si == 1 || ci == 1)
+ {
+ // Skip all SSLv2 cases - protocol not supported
+ continue;
+ }
+
+#ifdef OPENSSL_NO_SSL3
+ if (si == 2 || ci == 2)
+ {
+ // Skip all SSLv3 cases - protocol not supported
+ continue;
+ }
+#endif
+
+ boost::mutex::scoped_lock lock(mMutex);
+
+ BOOST_TEST_MESSAGE(boost::format("TEST: Server = %1%, Client = %2%")
+ % protocol2str(si) % protocol2str(ci));
+
+ mConnected = false;
+ // thread_group manages the thread lifetime - ignore the return value of create_thread
+ boost::thread_group threads;
+ (void)threads.create_thread(bind(&SecurityFixture::server, this, static_cast<apache::thrift::transport::SSLProtocol>(si)));
+ mCVar.wait(lock); // wait for listen() to succeed
+ lock.unlock();
+ (void)threads.create_thread(bind(&SecurityFixture::client, this, static_cast<apache::thrift::transport::SSLProtocol>(ci)));
+ threads.join_all();
+
+ BOOST_CHECK_MESSAGE(mConnected == matrix[ci][si],
+ boost::format(" Server = %1%, Client = %2% expected mConnected == %3% but was %4%")
+ % protocol2str(si) % protocol2str(ci) % matrix[ci][si] % mConnected);
+ }
+ }
+ }
+ catch (std::exception& ex)
+ {
+ BOOST_FAIL(boost::format("%1%: %2%") % typeid(ex).name() % ex.what());
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/SpecializationTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/SpecializationTest.cpp
new file mode 100644
index 000000000..008837d31
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/SpecializationTest.cpp
@@ -0,0 +1,103 @@
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include <thrift/transport/TTransportUtils.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <gen-cpp/DebugProtoTest_types.h>
+
+using namespace thrift::test::debug;
+using namespace apache::thrift::transport;
+using namespace apache::thrift::protocol;
+
+#define BOOST_TEST_MODULE SpecializationTest
+#include <boost/test/unit_test.hpp>
+
+typedef TBinaryProtocolT<TMemoryBuffer> MyProtocol;
+// typedef TBinaryProtocolT<TTransport> MyProtocol;
+
+BOOST_AUTO_TEST_CASE(test_specialization_1) {
+ OneOfEach ooe;
+ ooe.im_true = true;
+ ooe.im_false = false;
+ ooe.a_bite = 0x7f;
+ ooe.integer16 = 27000;
+ ooe.integer32 = 1 << 24;
+ ooe.integer64 = (uint64_t)6000 * 1000 * 1000;
+ ooe.double_precision = M_PI;
+ ooe.some_characters = "JSON THIS! \"\1";
+ ooe.zomg_unicode = "\xd7\n\a\t";
+ ooe.base64 = "\1\2\3\255";
+
+ Nesting n;
+ n.my_ooe = ooe;
+ n.my_ooe.integer16 = 16;
+ n.my_ooe.integer32 = 32;
+ n.my_ooe.integer64 = 64;
+ n.my_ooe.double_precision = (std::sqrt(5.0) + 1) / 2;
+ n.my_ooe.some_characters = ":R (me going \"rrrr\")";
+ n.my_ooe.zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20\xd0\x9d\xce"
+ "\xbf\xe2\x85\xbf\xd0\xbe\xc9\xa1\xd0\xb3\xd0"
+ "\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
+ "\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
+ "\xbc";
+ n.my_bonk.type = 31337;
+ n.my_bonk.message = "I am a bonk... xor!";
+
+ HolyMoley hm;
+
+ hm.big.push_back(ooe);
+ hm.big.push_back(n.my_ooe);
+ hm.big[0].a_bite = 0x22;
+ hm.big[1].a_bite = 0x33;
+
+ std::vector<std::string> stage1;
+ stage1.push_back("and a one");
+ stage1.push_back("and a two");
+ hm.contain.insert(stage1);
+ stage1.clear();
+ stage1.push_back("then a one, two");
+ stage1.push_back("three!");
+ stage1.push_back("FOUR!!");
+ hm.contain.insert(stage1);
+ stage1.clear();
+ hm.contain.insert(stage1);
+
+ std::vector<Bonk> stage2;
+ hm.bonks["nothing"] = stage2;
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 1;
+ stage2.back().message = "Wait.";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 2;
+ stage2.back().message = "What?";
+ hm.bonks["something"] = stage2;
+ stage2.clear();
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 3;
+ stage2.back().message = "quoth";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 4;
+ stage2.back().message = "the raven";
+ stage2.resize(stage2.size() + 1);
+ stage2.back().type = 5;
+ stage2.back().message = "nevermore";
+ hm.bonks["poe"] = stage2;
+
+ std::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ std::shared_ptr<TProtocol> proto(new MyProtocol(buffer));
+
+ ooe.write(proto.get());
+ OneOfEach ooe2;
+ ooe2.read(proto.get());
+
+ BOOST_CHECK(ooe == ooe2);
+
+ hm.write(proto.get());
+ HolyMoley hm2;
+ hm2.read(proto.get());
+
+ BOOST_CHECK(hm == hm2);
+
+ hm2.big[0].a_bite = 0x00;
+
+ BOOST_CHECK(hm != hm2);
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TBufferBaseTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TBufferBaseTest.cpp
new file mode 100644
index 000000000..7203f829b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TBufferBaseTest.cpp
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <algorithm>
+#include <boost/test/auto_unit_test.hpp>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TShortReadTransport.h>
+#include <memory>
+
+using std::shared_ptr;
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::transport::TBufferedTransport;
+using apache::thrift::transport::TFramedTransport;
+using apache::thrift::transport::test::TShortReadTransport;
+using std::string;
+
+// Shamelessly copied from ZlibTransport. TODO: refactor.
+unsigned int dist[][5000] = {
+ { 1<<15 },
+
+ {
+ 5,13,9,1,8,9,11,13,18,48,24,13,21,13,5,11,35,2,4,20,17,72,27,14,15,4,7,26,
+ 12,1,14,9,2,16,29,41,7,24,4,27,14,4,1,4,25,3,6,34,10,8,50,2,14,13,55,29,3,
+ 43,53,49,14,4,10,32,27,48,1,3,1,11,5,17,16,51,17,30,15,11,9,2,2,11,52,12,2,
+ 13,94,1,19,1,38,2,8,43,8,33,7,30,8,17,22,2,15,14,12,34,2,12,6,37,29,74,3,
+ 165,16,11,17,5,14,3,10,7,37,11,24,7,1,3,12,37,8,9,34,17,12,8,21,13,37,1,4,
+ 30,14,78,4,15,2,40,37,17,12,36,82,14,4,1,4,7,17,11,16,88,77,2,3,15,3,34,11,
+ 5,79,22,34,8,4,4,40,22,24,28,9,13,3,34,27,9,16,39,16,39,13,2,4,3,41,26,10,4,
+ 33,4,7,12,5,6,3,10,30,8,21,16,58,19,9,0,47,7,13,11,19,15,7,53,57,2,13,28,22,
+ 3,16,9,25,33,12,40,7,12,64,7,14,24,44,9,2,14,11,2,58,1,26,30,11,9,5,24,7,9,
+ 94,2,10,21,5,5,4,5,6,179,9,18,2,7,13,31,41,17,4,36,3,21,6,26,8,15,18,44,27,
+ 11,9,25,7,0,14,2,12,20,23,13,2,163,9,5,15,65,2,14,6,8,98,11,15,14,34,2,3,10,
+ 22,9,92,7,10,32,67,13,3,4,35,8,2,1,5,0,26,381,7,27,8,2,16,93,4,19,5,8,25,9,
+ 31,14,4,21,5,3,9,22,56,4,18,3,11,18,6,4,3,40,12,16,110,8,35,14,1,18,40,9,12,
+ 14,3,11,7,57,13,18,116,53,19,22,7,16,11,5,8,21,16,1,75,21,20,1,28,2,6,1,7,
+ 19,38,5,6,9,9,4,1,7,55,36,62,5,4,4,24,15,1,12,35,48,20,5,17,1,5,26,15,4,54,
+ 13,5,5,15,5,19,32,29,31,7,6,40,7,80,11,18,8,128,48,6,12,84,13,4,7,2,13,9,16,
+ 17,3,254,1,4,181,8,44,7,6,24,27,9,23,14,34,16,22,25,10,3,3,4,4,12,2,12,6,7,
+ 13,58,13,6,11,19,53,11,66,18,19,10,4,13,2,5,49,58,1,67,7,21,64,14,11,14,8,3,
+ 26,33,91,31,20,7,9,42,39,4,3,55,11,10,0,7,4,75,8,12,0,27,3,8,9,0,12,12,23,
+ 28,23,20,4,13,30,2,22,20,19,30,6,22,2,6,4,24,7,19,55,86,5,33,2,161,6,7,1,62,
+ 13,3,72,12,12,9,7,12,10,5,10,29,1,5,22,13,13,5,2,12,3,7,14,18,2,3,46,21,17,
+ 15,19,3,27,5,16,45,31,10,8,17,18,18,3,7,24,6,55,9,3,6,12,10,12,8,91,9,4,4,4,
+ 27,29,16,5,7,22,43,28,11,14,8,11,28,109,55,71,40,3,8,22,26,15,44,3,25,29,5,
+ 3,32,17,12,3,29,27,25,15,11,8,40,39,38,17,3,9,11,2,32,11,6,20,48,75,27,3,7,
+ 54,12,95,12,7,24,23,2,13,8,15,16,5,12,4,17,7,19,88,2,6,13,115,45,12,21,2,86,
+ 74,9,7,5,16,32,16,2,21,18,6,34,5,18,260,7,12,16,44,19,92,31,7,8,2,9,0,0,15,
+ 8,38,4,8,20,18,2,83,3,3,4,9,5,3,10,3,5,29,15,7,11,8,48,17,23,2,17,4,11,22,
+ 21,64,8,8,4,19,95,0,17,28,9,11,20,71,5,11,18,12,13,45,49,4,1,33,32,23,13,5,
+ 52,2,2,16,3,4,7,12,2,1,12,6,24,1,22,155,21,3,45,4,12,44,26,5,40,36,9,9,8,20,
+ 35,31,3,2,32,50,10,8,37,2,75,35,22,15,192,8,11,23,1,4,29,6,8,8,5,12,18,32,4,
+ 7,12,2,0,0,9,5,48,11,35,3,1,123,6,29,8,11,8,23,51,16,6,63,12,2,5,4,14,2,15,
+ 7,14,3,2,7,17,32,8,8,10,1,23,62,2,49,6,49,47,23,3,20,7,11,39,10,24,6,15,5,5,
+ 11,8,16,36,8,13,20,3,10,44,7,52,7,10,36,6,15,10,5,11,4,14,19,17,10,12,3,6,
+ 23,4,13,94,70,7,36,7,38,7,28,8,4,15,3,19,4,33,39,21,109,4,80,6,40,4,432,4,4,
+ 7,8,3,31,8,28,37,34,10,2,21,5,22,0,7,36,14,12,6,24,1,21,5,9,2,29,20,54,113,
+ 13,31,39,27,6,0,27,4,5,2,43,7,8,57,8,62,7,9,12,22,90,30,6,19,7,10,20,6,5,58,
+ 32,30,41,4,10,25,13,3,8,7,10,2,9,6,151,44,16,12,16,20,8,3,18,11,17,4,10,45,
+ 15,8,56,38,52,25,40,14,4,17,15,8,2,19,7,8,26,30,2,3,180,8,26,17,38,35,5,16,
+ 28,5,15,56,13,14,18,9,15,83,27,3,9,4,11,8,27,27,44,10,12,8,3,48,14,7,9,4,4,
+ 8,4,5,9,122,8,14,12,19,17,21,4,29,63,21,17,10,12,18,47,10,10,53,4,18,16,4,8,
+ 118,9,5,12,9,11,9,3,12,32,3,23,2,15,3,3,30,3,17,235,15,22,9,299,14,17,1,5,
+ 16,8,3,7,3,13,2,7,6,4,8,66,2,13,6,15,16,47,3,36,5,7,10,24,1,9,9,8,13,16,26,
+ 12,7,24,21,18,49,23,39,10,41,4,13,4,27,11,12,12,19,4,147,8,10,9,40,21,2,83,
+ 10,5,6,11,25,9,50,57,40,12,12,21,1,3,24,23,9,3,9,13,2,3,12,57,8,11,13,15,26,
+ 15,10,47,36,4,25,1,5,8,5,4,0,12,49,5,19,4,6,16,14,6,10,69,10,33,29,7,8,61,
+ 12,4,0,3,7,6,3,16,29,27,38,4,21,0,24,3,2,1,19,16,22,2,8,138,11,7,7,3,12,22,
+ 3,16,5,7,3,53,9,10,32,14,5,7,3,6,22,9,59,26,8,7,58,5,16,11,55,7,4,11,146,91,
+ 8,13,18,14,6,8,8,31,26,22,6,11,30,11,30,15,18,31,3,48,17,7,6,4,9,2,25,3,35,
+ 13,13,7,8,4,31,10,8,10,4,3,45,10,23,2,7,259,17,21,13,14,3,26,3,8,27,4,18,9,
+ 66,7,12,5,8,17,4,23,55,41,51,2,32,26,66,4,21,14,12,65,16,22,17,5,14,2,29,24,
+ 7,3,36,2,43,53,86,5,28,4,58,13,49,121,6,2,73,2,1,47,4,2,27,10,35,28,27,10,
+ 17,10,56,7,10,14,28,20,24,40,7,4,7,3,10,11,32,6,6,3,15,11,54,573,2,3,6,2,3,
+ 14,64,4,16,12,16,42,10,26,4,6,11,69,18,27,2,2,17,22,9,13,22,11,6,1,15,49,3,
+ 14,1
+ },
+
+ {
+ 11,11,11,15,47,1,3,1,23,5,8,18,3,23,15,21,1,7,19,10,26,1,17,11,31,21,41,18,
+ 34,4,9,58,19,3,3,36,5,18,13,3,14,4,9,10,4,19,56,15,3,5,3,11,27,9,4,10,13,4,
+ 11,6,9,2,18,3,10,19,11,4,53,4,2,2,3,4,58,16,3,0,5,30,2,11,93,10,2,14,10,6,2,
+ 115,2,25,16,22,38,101,4,18,13,2,145,51,45,15,14,15,13,20,7,24,5,13,14,30,40,
+ 10,4,107,12,24,14,39,12,6,13,20,7,7,11,5,18,18,45,22,6,39,3,2,1,51,9,11,4,
+ 13,9,38,44,8,11,9,15,19,9,23,17,17,17,13,9,9,1,10,4,18,6,2,9,5,27,32,72,8,
+ 37,9,4,10,30,17,20,15,17,66,10,4,73,35,37,6,4,16,117,45,13,4,75,5,24,65,10,
+ 4,9,4,13,46,5,26,29,10,4,4,52,3,13,18,63,6,14,9,24,277,9,88,2,48,27,123,14,
+ 61,7,5,10,8,7,90,3,10,3,3,48,17,13,10,18,33,2,19,36,6,21,1,16,12,5,6,2,16,
+ 15,29,88,28,2,15,6,11,4,6,11,3,3,4,18,9,53,5,4,3,33,8,9,8,6,7,36,9,62,14,2,
+ 1,10,1,16,7,32,7,23,20,11,10,23,2,1,0,9,16,40,2,81,5,22,8,5,4,37,51,37,10,
+ 19,57,11,2,92,31,6,39,10,13,16,8,20,6,9,3,10,18,25,23,12,30,6,2,26,7,64,18,
+ 6,30,12,13,27,7,10,5,3,33,24,99,4,23,4,1,27,7,27,49,8,20,16,3,4,13,9,22,67,
+ 28,3,10,16,3,2,10,4,8,1,8,19,3,85,6,21,1,9,16,2,30,10,33,12,4,9,3,1,60,38,6,
+ 24,32,3,14,3,40,8,34,115,5,9,27,5,96,3,40,6,15,5,8,22,112,5,5,25,17,58,2,7,
+ 36,21,52,1,3,95,12,21,4,11,8,59,24,5,21,4,9,15,8,7,21,3,26,5,11,6,7,17,65,
+ 14,11,10,2,17,5,12,22,4,4,2,21,8,112,3,34,63,35,2,25,1,2,15,65,23,0,3,5,15,
+ 26,27,9,5,48,11,15,4,9,5,33,20,15,1,18,19,11,24,40,10,21,74,6,6,32,30,40,5,
+ 4,7,44,10,25,46,16,12,5,40,7,18,5,18,9,12,8,4,25,5,6,36,4,43,8,9,12,35,17,4,
+ 8,9,11,27,5,10,17,40,8,12,4,18,9,18,12,20,25,39,42,1,24,13,22,15,7,112,35,3,
+ 7,17,33,2,5,5,19,8,4,12,24,14,13,2,1,13,6,5,19,11,7,57,0,19,6,117,48,14,8,
+ 10,51,17,12,14,2,5,8,9,15,4,48,53,13,22,4,25,12,11,19,45,5,2,6,54,22,9,15,9,
+ 13,2,7,11,29,82,16,46,4,26,14,26,40,22,4,26,6,18,13,4,4,20,3,3,7,12,17,8,9,
+ 23,6,20,7,25,23,19,5,15,6,23,15,11,19,11,3,17,59,8,18,41,4,54,23,44,75,13,
+ 20,6,11,2,3,1,13,10,3,7,12,3,4,7,8,30,6,6,7,3,32,9,5,28,6,114,42,13,36,27,
+ 59,6,93,13,74,8,69,140,3,1,17,48,105,6,11,5,15,1,10,10,14,8,53,0,8,24,60,2,
+ 6,35,2,12,32,47,16,17,75,2,5,4,37,28,10,5,9,57,4,59,5,12,13,7,90,5,11,5,24,
+ 22,13,30,1,2,10,9,6,19,3,18,47,2,5,7,9,35,15,3,6,1,21,14,14,18,14,9,12,8,73,
+ 6,19,3,32,9,14,17,17,5,55,23,6,16,28,3,11,48,4,6,6,6,12,16,30,10,30,27,51,
+ 18,29,2,3,15,1,76,0,16,33,4,27,3,62,4,10,2,4,8,15,9,41,26,22,2,4,20,4,49,0,
+ 8,1,57,13,12,39,3,63,10,19,34,35,2,7,8,29,72,4,10,0,77,8,6,7,9,15,21,9,4,1,
+ 20,23,1,9,18,9,15,36,4,7,6,15,5,7,7,40,2,9,22,2,3,20,4,12,34,13,6,18,15,1,
+ 38,20,12,7,16,3,19,85,12,16,18,16,2,17,1,13,8,6,12,15,97,17,12,9,3,21,15,12,
+ 23,44,81,26,30,2,5,17,6,6,0,22,42,19,6,19,41,14,36,7,3,56,7,9,3,2,6,9,69,3,
+ 15,4,30,28,29,7,9,15,17,17,6,1,6,153,9,33,5,12,14,16,28,3,8,7,14,12,4,6,36,
+ 9,24,13,13,4,2,9,15,19,9,53,7,13,4,150,17,9,2,6,12,7,3,5,58,19,58,28,8,14,3,
+ 20,3,0,32,56,7,5,4,27,1,68,4,29,13,5,58,2,9,65,41,27,16,15,12,14,2,10,9,24,
+ 3,2,9,2,2,3,14,32,10,22,3,13,11,4,6,39,17,0,10,5,5,10,35,16,19,14,1,8,63,19,
+ 14,8,56,10,2,12,6,12,6,7,16,2,9,9,12,20,73,25,13,21,17,24,5,32,8,12,25,8,14,
+ 16,5,23,3,7,6,3,11,24,6,30,4,21,13,28,4,6,29,15,5,17,6,26,8,15,8,3,7,7,50,
+ 11,30,6,2,28,56,16,24,25,23,24,89,31,31,12,7,22,4,10,17,3,3,8,11,13,5,3,27,
+ 1,12,1,14,8,10,29,2,5,2,2,20,10,0,31,10,21,1,48,3,5,43,4,5,18,13,5,18,25,34,
+ 18,3,5,22,16,3,4,20,3,9,3,25,6,6,44,21,3,12,7,5,42,3,2,14,4,36,5,3,45,51,15,
+ 9,11,28,9,7,6,6,12,26,5,14,10,11,42,55,13,21,4,28,6,7,23,27,11,1,41,36,0,32,
+ 15,26,2,3,23,32,11,2,15,7,29,26,144,33,20,12,7,21,10,7,11,65,46,10,13,20,32,
+ 4,4,5,19,2,19,15,49,41,1,75,10,11,25,1,2,45,11,8,27,18,10,60,28,29,12,30,19,
+ 16,4,24,11,19,27,17,49,18,7,40,13,19,22,8,55,12,11,3,6,5,11,8,10,22,5,9,9,
+ 25,7,17,7,64,1,24,2,12,17,44,4,12,27,21,11,10,7,47,5,9,13,12,38,27,21,7,29,
+ 7,1,17,3,3,5,48,62,10,3,11,17,15,15,6,3,8,10,8,18,19,13,3,9,7,6,44,9,10,4,
+ 43,8,6,6,14,20,38,24,2,4,5,5,7,5,9,39,8,44,40,9,19,7,3,15,25,2,37,18,15,9,5,
+ 8,32,10,5,18,4,7,46,20,17,23,4,11,16,18,31,11,3,11,1,14,1,25,4,27,13,13,39,
+ 14,6,6,35,6,16,13,11,122,21,15,20,24,10,5,152,15,39,5,20,16,9,14,7,53,6,3,8,
+ 19,63,32,6,2,3,20,1,19,5,13,42,15,4,6,68,31,46,11,38,10,24,5,5,8,9,12,3,35,
+ 46,26,16,2,8,4,74,16,44,4,5,1,16,4,14,23,16,69,15,42,31,14,7,7,6,97,14,40,1,
+ 8,7,34,9,39,19,13,15,10,21,18,10,5,15,38,7,5,12,7,20,15,4,11,6,14,5,17,7,39,
+ 35,36,18,20,26,22,4,2,36,21,64,0,5,9,10,6,4,1,7,3,1,3,3,4,10,20,90,2,22,48,
+ 16,23,2,33,40,1,21,21,17,20,8,8,12,4,83,14,48,4,21,3,9,27,5,11,40,15,9,3,16,
+ 17,9,11,4,24,31,17,3,4,2,11,1,8,4,8,6,41,17,4,13,3,7,17,8,27,5,13,6,10,7,13,
+ 12,18,13,60,18,3,8,1,12,125,2,7,16,2,11,2,4,7,26,5,9,14,14,16,8,14,7,14,6,9,
+ 13,9,6,4,26,35,49,36,55,3,9,6,40,26,23,31,19,41,2,10,31,6,54,5,69,16,7,8,16,
+ 1,5,7,4,22,7,7,5,4,48,11,13,3,98,4,11,19,4,2,14,7,34,7,10,3,2,12,7,6,2,5,118
+ },
+};
+
+uint8_t data[1<<15];
+string data_str;
+void init_data() {
+ static bool initted = false;
+ if (initted) return;
+ initted = true;
+
+ // Repeatability. Kind of.
+ std::srand(42);
+ for (unsigned char & i : data) {
+ i = (uint8_t)rand();
+ }
+
+ data_str.assign((char*)data, sizeof(data));
+}
+
+
+BOOST_AUTO_TEST_SUITE( TBufferBaseTest )
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_GetBuffer ) {
+ init_data();
+
+ for (auto & d1 : dist) {
+ TMemoryBuffer buffer(16);
+ int offset = 0;
+ int index = 0;
+
+ while (offset < 1<<15) {
+ buffer.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ string output = buffer.getBufferAsString();
+ BOOST_CHECK_EQUAL(data_str, output);
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read ) {
+ init_data();
+
+ for (auto & d1 : dist) {
+ for (auto & d2 : dist) {
+ TMemoryBuffer buffer(16);
+ uint8_t data_out[1<<15];
+ int offset;
+ int index;
+
+ offset = 0;
+ index = 0;
+ while (offset < 1<<15) {
+ buffer.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ offset = 0;
+ index = 0;
+ while (offset < 1<<15) {
+ unsigned int got = buffer.read(&data_out[offset], d2[index]);
+ BOOST_CHECK_EQUAL(got, d2[index]);
+ offset += d2[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, sizeof(data)));
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_ReadString ) {
+ init_data();
+
+ for (auto & d1 : dist) {
+ for (auto & d2 : dist) {
+ TMemoryBuffer buffer(16);
+ string output;
+ int offset;
+ int index;
+
+ offset = 0;
+ index = 0;
+ while (offset < 1<<15) {
+ buffer.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ offset = 0;
+ index = 0;
+ while (offset < 1<<15) {
+ unsigned int got = buffer.readAppendToString(output, d2[index]);
+ BOOST_CHECK_EQUAL(got, d2[index]);
+ offset += d2[index];
+ index++;
+ }
+
+ BOOST_CHECK_EQUAL(output, data_str);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Multi1 ) {
+ init_data();
+
+ // Do shorter writes and reads so we don't align to power-of-two boundaries.
+
+ for (auto & d1 : dist) {
+ for (auto & d2 : dist) {
+ TMemoryBuffer buffer(16);
+ uint8_t data_out[1<<15];
+ int offset;
+ int index;
+
+ for (int iter = 0; iter < 6; iter++) {
+ offset = 0;
+ index = 0;
+ while (offset < (1<<15)-42) {
+ buffer.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ offset = 0;
+ index = 0;
+ while (offset < (1<<15)-42) {
+ buffer.read(&data_out[offset], d2[index]);
+ offset += d2[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, (1<<15)-42));
+
+ // Pull out the extra data.
+ buffer.read(data_out, 42);
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Multi2 ) {
+ init_data();
+
+ // Do shorter writes and reads so we don't align to power-of-two boundaries.
+ // Pull the buffer out of the loop so its state gets worked harder.
+ TMemoryBuffer buffer(16);
+
+ for (auto & d1 : dist) {
+ for (auto & d2 : dist) {
+ uint8_t data_out[1<<15];
+ int offset;
+ int index;
+
+ for (int iter = 0; iter < 6; iter++) {
+ offset = 0;
+ index = 0;
+ while (offset < (1<<15)-42) {
+ buffer.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ offset = 0;
+ index = 0;
+ while (offset < (1<<15)-42) {
+ buffer.read(&data_out[offset], d2[index]);
+ offset += d2[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, (1<<15)-42));
+
+ // Pull out the extra data.
+ buffer.read(data_out, 42);
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read_Incomplete ) {
+ init_data();
+
+ // Do shorter writes and reads so we don't align to power-of-two boundaries.
+ // Pull the buffer out of the loop so its state gets worked harder.
+
+ for (auto & d1 : dist) {
+ for (auto & d2 : dist) {
+ TMemoryBuffer buffer(16);
+ uint8_t data_out[1<<13];
+
+ int write_offset = 0;
+ int write_index = 0;
+ unsigned int to_write = (1<<14)-42;
+ while (to_write > 0) {
+ int write_amt = (std::min)(d1[write_index], to_write);
+ buffer.write(&data[write_offset], write_amt);
+ write_offset += write_amt;
+ write_index++;
+ to_write -= write_amt;
+ }
+
+ int read_offset = 0;
+ int read_index = 0;
+ unsigned int to_read = (1<<13)-42;
+ while (to_read > 0) {
+ int read_amt = (std::min)(d2[read_index], to_read);
+ int got = buffer.read(&data_out[read_offset], read_amt);
+ BOOST_CHECK_EQUAL(got, read_amt);
+ read_offset += read_amt;
+ read_index++;
+ to_read -= read_amt;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, (1<<13)-42));
+
+ int second_offset = write_offset;
+ int second_index = write_index-1;
+ unsigned int to_second = (1<<14)+42;
+ while (to_second > 0) {
+ int second_amt = (std::min)(d1[second_index], to_second);
+ //printf("%d\n", second_amt);
+ buffer.write(&data[second_offset], second_amt);
+ second_offset += second_amt;
+ second_index++;
+ to_second -= second_amt;
+ }
+
+ string output = buffer.getBufferAsString();
+ BOOST_CHECK_EQUAL(data_str.substr((1<<13)-42), output);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_BufferedTransport_Write ) {
+ init_data();
+
+ int sizes[] = {
+ 12, 15, 16, 17, 20,
+ 501, 512, 523,
+ 2000, 2048, 2096,
+ 1<<14, 1<<17,
+ };
+
+ for (int size : sizes) {
+ for (auto & d1 : dist) {
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(16));
+ TBufferedTransport trans(buffer, size);
+
+ int offset = 0;
+ int index = 0;
+ while (offset < 1<<15) {
+ trans.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+ trans.flush();
+
+ string output = buffer->getBufferAsString();
+ BOOST_CHECK_EQUAL(data_str, output);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_BufferedTransport_Read_Full ) {
+ init_data();
+
+ int sizes[] = {
+ 12, 15, 16, 17, 20,
+ 501, 512, 523,
+ 2000, 2048, 2096,
+ 1<<14, 1<<17,
+ };
+
+ for (int size : sizes) {
+ for (auto & d1 : dist) {
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(data, sizeof(data)));
+ TBufferedTransport trans(buffer, size);
+ uint8_t data_out[1<<15];
+
+ int offset = 0;
+ int index = 0;
+ while (offset < 1<<15) {
+ // Note: this doesn't work with "read" because TBufferedTransport
+ // doesn't try loop over reads, so we get short reads. We don't
+ // check the return value, so that messes us up.
+ trans.readAll(&data_out[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, sizeof(data)));
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_BufferedTransport_Read_Short ) {
+ init_data();
+
+ int sizes[] = {
+ 12, 15, 16, 17, 20,
+ 501, 512, 523,
+ 2000, 2048, 2096,
+ 1<<14, 1<<17,
+ };
+
+ for (int size : sizes) {
+ for (auto & d1 : dist) {
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(data, sizeof(data)));
+ shared_ptr<TShortReadTransport> tshort(new TShortReadTransport(buffer, 0.125));
+ TBufferedTransport trans(buffer, size);
+ uint8_t data_out[1<<15];
+
+ int offset = 0;
+ int index = 0;
+ while (offset < 1<<15) {
+ // Note: this doesn't work with "read" because TBufferedTransport
+ // doesn't try loop over reads, so we get short reads. We don't
+ // check the return value, so that messes us up.
+ trans.readAll(&data_out[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, sizeof(data)));
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_FramedTransport_Write ) {
+ init_data();
+
+ int sizes[] = {
+ 12, 15, 16, 17, 20,
+ 501, 512, 523,
+ 2000, 2048, 2096,
+ 1<<14, 1<<17,
+ };
+
+ for (int size : sizes) {
+ for (auto & d1 : dist) {
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(16));
+ TFramedTransport trans(buffer, size);
+
+ int offset = 0;
+ int index = 0;
+ while (offset < 1<<15) {
+ trans.write(&data[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+ trans.flush();
+
+ int32_t frame_size = -1;
+ buffer->read(reinterpret_cast<uint8_t*>(&frame_size), sizeof(frame_size));
+ frame_size = (int32_t)ntohl((uint32_t)frame_size);
+ BOOST_CHECK_EQUAL(frame_size, 1<<15);
+ BOOST_CHECK_EQUAL(data_str.size(), (unsigned int)frame_size);
+ string output = buffer->getBufferAsString();
+ BOOST_CHECK_EQUAL(data_str, output);
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_FramedTransport_Read ) {
+ init_data();
+
+ for (auto & d1 : dist) {
+ uint8_t data_out[1<<15];
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ TFramedTransport trans(buffer);
+ int32_t length = sizeof(data);
+ length = (int32_t)htonl((uint32_t)length);
+ buffer->write(reinterpret_cast<uint8_t*>(&length), sizeof(length));
+ buffer->write(data, sizeof(data));
+
+ int offset = 0;
+ int index = 0;
+ while (offset < 1<<15) {
+ // This should work with read because we have one huge frame.
+ trans.read(&data_out[offset], d1[index]);
+ offset += d1[index];
+ index++;
+ }
+
+ BOOST_CHECK(!memcmp(data, data_out, sizeof(data)));
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_FramedTransport_Write_Read ) {
+ init_data();
+
+ int sizes[] = {
+ 12, 15, 16, 17, 20,
+ 501, 512, 523,
+ 2000, 2048, 2096,
+ 1<<14, 1<<17,
+ };
+
+ int probs[] = { 1, 2, 4, 8, 16, 32, };
+
+ for (int size : sizes) {
+ for (int prob : probs) {
+ for (auto & d1 : dist) {
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(16));
+ TFramedTransport trans(buffer, size);
+ std::vector<uint8_t> data_out(1<<17, 0);
+ std::vector<int> flush_sizes;
+
+ int write_offset = 0;
+ int write_index = 0;
+ int flush_size = 0;
+ while (write_offset < 1<<15) {
+ trans.write(&data[write_offset], d1[write_index]);
+ write_offset += d1[write_index];
+ flush_size += d1[write_index];
+ write_index++;
+ if (flush_size > 0 && rand()%prob == 0) {
+ flush_sizes.push_back(flush_size);
+ flush_size = 0;
+ trans.flush();
+ }
+ }
+ if (flush_size != 0) {
+ flush_sizes.push_back(flush_size);
+ flush_size = 0;
+ trans.flush();
+ }
+
+ int read_offset = 0;
+ int read_index = 0;
+
+ for (int fsize : flush_sizes) {
+ // We are exploiting an implementation detail of TFramedTransport.
+ // The read buffer starts empty and it will never do more than one
+ // readFrame per read, so we should always get exactly one frame.
+ int got = trans.read(&data_out[read_offset], 1<<15);
+ BOOST_CHECK_EQUAL(got, fsize);
+ read_offset += got;
+ read_index++;
+ }
+
+ BOOST_CHECK_EQUAL((unsigned int)read_offset, sizeof(data));
+ BOOST_CHECK(!memcmp(data, &data_out[0], sizeof(data)));
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE( test_FramedTransport_Empty_Flush ) {
+ init_data();
+
+ string output1("\x00\x00\x00\x01""a", 5);
+ string output2("\x00\x00\x00\x01""a\x00\x00\x00\x02""bc", 11);
+
+ shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
+ TFramedTransport trans(buffer);
+
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
+ trans.flush();
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
+ trans.write((const uint8_t*)"a", 1);
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output1);
+ trans.flush();
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output1);
+ trans.write((const uint8_t*)"bc", 2);
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output1);
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output2);
+ trans.flush();
+ trans.flush();
+ BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output2);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TFDTransportTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TFDTransportTest.cpp
new file mode 100644
index 000000000..0ba035a5b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TFDTransportTest.cpp
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <cstdlib>
+#include <stdexcept>
+#include <thrift/Thrift.h>
+#include <thrift/transport/TFDTransport.h>
+
+#define BOOST_TEST_MODULE TFDTransportTest
+#include <boost/test/unit_test.hpp>
+
+// Disabled on MSVC because the RTL asserts on an invalid file descriptor
+// in both debug and release mode; at least in MSVCR100 (Visual Studio 2010)
+#if !defined(WIN32)
+
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TFDTransport;
+
+BOOST_AUTO_TEST_CASE(test_tfdtransport_1) {
+ BOOST_CHECK_NO_THROW(TFDTransport t(256, TFDTransport::CLOSE_ON_DESTROY));
+}
+
+BOOST_AUTO_TEST_CASE(test_tfdtransport_2) {
+ TFDTransport t(256, TFDTransport::CLOSE_ON_DESTROY);
+ BOOST_CHECK_THROW(t.close(), TTransportException);
+}
+
+#else
+
+BOOST_AUTO_TEST_CASE(test_tfdtransport_dummy) {
+ BOOST_CHECK(true);
+}
+
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TFileTransportTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TFileTransportTest.cpp
new file mode 100644
index 000000000..21c1f3b33
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TFileTransportTest.cpp
@@ -0,0 +1,404 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE // needed for getopt_long
+#endif
+
+#include <thrift/thrift-config.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <getopt.h>
+#include <boost/test/unit_test.hpp>
+
+#include <thrift/transport/TFileTransport.h>
+
+#ifdef __MINGW32__
+ #include <io.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <fcntl.h>
+ #include <sys\stat.h>
+#endif
+
+using namespace apache::thrift::transport;
+
+/**************************************************************************
+ * Global state
+ **************************************************************************/
+
+static const char* tmp_dir = "/tmp";
+
+class FsyncLog;
+FsyncLog* fsync_log;
+
+/**************************************************************************
+ * Helper code
+ **************************************************************************/
+
+/**
+ * Class to record calls to fsync
+ */
+class FsyncLog {
+public:
+ struct FsyncCall {
+ struct timeval time;
+ int fd;
+ };
+ typedef std::list<FsyncCall> CallList;
+
+ FsyncLog() = default;
+
+ void fsync(int fd) {
+ (void)fd;
+ FsyncCall call;
+ THRIFT_GETTIMEOFDAY(&call.time, nullptr);
+ calls_.push_back(call);
+ }
+
+ const CallList* getCalls() const { return &calls_; }
+
+private:
+ CallList calls_;
+};
+
+/**
+ * Helper class to clean up temporary files
+ */
+class TempFile {
+public:
+ TempFile(const char* directory, const char* prefix) {
+ #ifdef __MINGW32__
+ ((void)directory);
+ size_t path_len = strlen(prefix) + 8;
+ path_ = new char[path_len];
+ snprintf(path_, path_len, "%sXXXXXX", prefix);
+ if (_mktemp_s(path_,path_len) == 0) {
+ fd_ = open(path_,O_CREAT | O_RDWR | O_BINARY,S_IREAD | S_IWRITE);
+ if (fd_ < 0) {
+ throw apache::thrift::TException("_mktemp_s() failed");
+ }
+ } else {
+ throw apache::thrift::TException("_mktemp_s() failed");
+ }
+ #else
+ size_t path_len = strlen(directory) + strlen(prefix) + 8;
+ path_ = new char[path_len];
+ snprintf(path_, path_len, "%s/%sXXXXXX", directory, prefix);
+
+ fd_ = mkstemp(path_);
+ if (fd_ < 0) {
+ throw apache::thrift::TException("mkstemp() failed");
+ }
+ #endif
+ }
+
+ ~TempFile() {
+ unlink();
+ close();
+ }
+
+ const char* getPath() const { return path_; }
+
+ int getFD() const { return fd_; }
+
+ void unlink() {
+ if (path_) {
+ ::unlink(path_);
+ delete[] path_;
+ path_ = nullptr;
+ }
+ }
+
+ void close() {
+ if (fd_ < 0) {
+ return;
+ }
+
+ ::close(fd_);
+ fd_ = -1;
+ }
+
+private:
+ char* path_;
+ int fd_;
+};
+
+// Use our own version of fsync() for testing.
+// This returns immediately, so timing in test_destructor() isn't affected by
+// waiting on the actual filesystem.
+extern "C" int fsync(int fd) {
+ if (fsync_log) {
+ fsync_log->fsync(fd);
+ }
+ return 0;
+}
+
+int time_diff(const struct timeval* t1, const struct timeval* t2) {
+ return (t2->tv_usec - t1->tv_usec) + (t2->tv_sec - t1->tv_sec) * 1000000;
+}
+
+/**************************************************************************
+ * Test cases
+ **************************************************************************/
+
+/**
+ * Make sure the TFileTransport destructor exits "quickly".
+ *
+ * Previous versions had a bug causing the writer thread not to exit
+ * right away.
+ *
+ * It's kind of lame that we just check to see how long the destructor takes in
+ * wall-clock time. This could result in false failures on slower systems, or
+ * on heavily loaded machines.
+ */
+BOOST_AUTO_TEST_CASE(test_destructor) {
+ TempFile f(tmp_dir, "thrift.TFileTransportTest.");
+
+ unsigned int const NUM_ITERATIONS = 1000;
+
+ unsigned int num_over = 0;
+ for (unsigned int n = 0; n < NUM_ITERATIONS; ++n) {
+ BOOST_CHECK_EQUAL(0, ftruncate(f.getFD(), 0));
+
+ TFileTransport* transport = new TFileTransport(f.getPath());
+
+ // write something so that the writer thread gets started
+ transport->write(reinterpret_cast<const uint8_t*>("foo"), 3);
+
+ // Every other iteration, also call flush(), just in case that potentially
+ // has any effect on how the writer thread wakes up.
+ if (n & 0x1) {
+ transport->flush();
+ }
+
+ /*
+ * Time the call to the destructor
+ */
+ struct timeval start;
+ struct timeval end;
+
+ THRIFT_GETTIMEOFDAY(&start, nullptr);
+ delete transport;
+ THRIFT_GETTIMEOFDAY(&end, nullptr);
+
+ int delta = time_diff(&start, &end);
+
+ // If any attempt takes more than 500ms, treat that as a failure.
+ // Treat this as a fatal failure, so we'll return now instead of
+ // looping over a very slow operation.
+ BOOST_WARN( delta < 500000 );
+
+ // Normally, it takes less than 100ms on my dev box.
+ // However, if the box is heavily loaded, some of the test runs
+ // take longer, since we're just waiting for our turn on the CPU.
+ if (delta > 100000) {
+ ++num_over;
+ }
+ }
+
+ // Make sure fewer than 10% of the runs took longer than 1000us
+ BOOST_WARN(num_over < (NUM_ITERATIONS / 10));
+}
+
+/**
+ * Make sure setFlushMaxUs() is honored.
+ */
+void test_flush_max_us_impl(uint32_t flush_us, uint32_t write_us, uint32_t test_us) {
+ // TFileTransport only calls fsync() if data has been written,
+ // so make sure the write interval is smaller than the flush interval.
+ BOOST_WARN(write_us < flush_us);
+
+ TempFile f(tmp_dir, "thrift.TFileTransportTest.");
+
+ // Record calls to fsync()
+ FsyncLog log;
+ fsync_log = &log;
+
+ TFileTransport* transport = new TFileTransport(f.getPath());
+ // Don't flush because of # of bytes written
+ transport->setFlushMaxBytes(0xffffffff);
+ uint8_t buf[] = "a";
+ uint32_t buflen = sizeof(buf);
+
+ // Set the flush interval
+ transport->setFlushMaxUs(flush_us);
+
+ // Make one call to write, to start the writer thread now.
+ // (If we just let the thread get created during our test loop,
+ // the thread creation sometimes takes long enough to make the first
+ // fsync interval fail the check.)
+ transport->write(buf, buflen);
+
+ // Add one entry to the fsync log, just to mark the start time
+ log.fsync(-1);
+
+ // Loop doing write(), sleep(), ...
+ uint32_t total_time = 0;
+ while (true) {
+ transport->write(buf, buflen);
+ if (total_time > test_us) {
+ break;
+ }
+ usleep(write_us);
+ total_time += write_us;
+ }
+
+ delete transport;
+
+ // Stop logging new fsync() calls
+ fsync_log = nullptr;
+
+ // Examine the fsync() log
+ //
+ // TFileTransport uses pthread_cond_timedwait(), which only has millisecond
+ // resolution. In my testing, it normally wakes up about 1 millisecond late.
+ // However, sometimes it takes a bit longer. Allow 5ms leeway.
+ int max_allowed_delta = flush_us + 5000;
+
+ const FsyncLog::CallList* calls = log.getCalls();
+ // We added 1 fsync call above.
+ // Make sure TFileTransport called fsync at least once
+ BOOST_WARN_GE(calls->size(), static_cast<FsyncLog::CallList::size_type>(1));
+
+ const struct timeval* prev_time = nullptr;
+ for (const auto & call : *calls) {
+ if (prev_time) {
+ int delta = time_diff(prev_time, &call.time);
+ BOOST_WARN( delta < max_allowed_delta );
+ }
+ prev_time = &call.time;
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_flush_max_us1) {
+ // fsync every 10ms, write every 5ms, for 500ms
+ test_flush_max_us_impl(10000, 5000, 500000);
+}
+
+BOOST_AUTO_TEST_CASE(test_flush_max_us2) {
+ // fsync every 50ms, write every 20ms, for 500ms
+ test_flush_max_us_impl(50000, 20000, 500000);
+}
+
+BOOST_AUTO_TEST_CASE(test_flush_max_us3) {
+ // fsync every 400ms, write every 300ms, for 1s
+ test_flush_max_us_impl(400000, 300000, 1000000);
+}
+
+/**
+ * Make sure flush() is fast when there is nothing to do.
+ *
+ * TFileTransport used to have a bug where flush() would wait for the fsync
+ * timeout to expire.
+ */
+BOOST_AUTO_TEST_CASE(test_noop_flush) {
+ TempFile f(tmp_dir, "thrift.TFileTransportTest.");
+ TFileTransport transport(f.getPath());
+
+ // Write something to start the writer thread.
+ uint8_t buf[] = "a";
+ transport.write(buf, 1);
+
+ struct timeval start;
+ THRIFT_GETTIMEOFDAY(&start, nullptr);
+
+ for (unsigned int n = 0; n < 10; ++n) {
+ transport.flush();
+
+ struct timeval now;
+ THRIFT_GETTIMEOFDAY(&now, nullptr);
+
+ // Fail if at any point we've been running for longer than half a second.
+ // (With the buggy code, TFileTransport used to take 3 seconds per flush())
+ //
+ // Use a fatal fail so we break out early, rather than continuing to make
+ // many more slow flush() calls.
+ int delta = time_diff(&start, &now);
+ BOOST_WARN( delta < 2000000 );
+ }
+}
+
+/**************************************************************************
+ * General Initialization
+ **************************************************************************/
+
+void print_usage(FILE* f, const char* argv0) {
+ fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
+ fprintf(f, "Options:\n");
+ fprintf(f, " --tmp-dir=DIR, -t DIR\n");
+ fprintf(f, " --help\n");
+}
+
+void parse_args(int argc, char* argv[]) {
+ struct option long_opts[]
+ = {{"help", false, nullptr, 'h'}, {"tmp-dir", true, nullptr, 't'}, {nullptr, 0, nullptr, 0}};
+
+ while (true) {
+ optopt = 1;
+ int optchar = getopt_long(argc, argv, "ht:", long_opts, nullptr);
+ if (optchar == -1) {
+ break;
+ }
+
+ switch (optchar) {
+ case 't':
+ tmp_dir = optarg;
+ 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);
+ }
+ }
+}
+
+#ifdef BOOST_TEST_DYN_LINK
+static int myArgc = 0;
+static char **myArgv = nullptr;
+
+bool init_unit_test_suite() {
+ boost::unit_test::framework::master_test_suite().p_name.value = "TFileTransportTest";
+
+ // Parse arguments
+ parse_args(myArgc,myArgv);
+ return true;
+}
+
+int main( int argc, char* argv[] ) {
+ myArgc = argc;
+ myArgv = argv;
+ return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
+}
+#else
+boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+ boost::unit_test::framework::master_test_suite().p_name.value = "TFileTransportTest";
+
+ // Parse arguments
+ parse_args(argc, argv);
+ return NULL;
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TMemoryBufferTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TMemoryBufferTest.cpp
new file mode 100644
index 000000000..42f97112b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TMemoryBufferTest.cpp
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <iostream>
+#include <climits>
+#include <vector>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <memory>
+#include <thrift/transport/TBufferTransports.h>
+#include "gen-cpp/ThriftTest_types.h"
+
+BOOST_AUTO_TEST_SUITE(TMemoryBufferTest)
+
+using apache::thrift::protocol::TBinaryProtocol;
+using apache::thrift::transport::TMemoryBuffer;
+using apache::thrift::transport::TTransportException;
+using std::shared_ptr;
+using std::cout;
+using std::endl;
+using std::string;
+
+BOOST_AUTO_TEST_CASE(test_read_write_grow) {
+ // Added to test the fix for THRIFT-1248
+ TMemoryBuffer uut;
+ const int maxSize = 65536;
+ uint8_t verify[maxSize];
+ std::vector<uint8_t> buf;
+ buf.resize(maxSize);
+
+ for (uint32_t i = 0; i < maxSize; ++i) {
+ buf[i] = static_cast<uint8_t>(i);
+ }
+
+ for (uint32_t i = 1; i < maxSize; i *= 2) {
+ uut.write(&buf[0], i);
+ }
+
+ for (uint32_t i = 1; i < maxSize; i *= 2) {
+ uut.read(verify, i);
+ BOOST_CHECK_EQUAL(0, ::memcmp(verify, &buf[0], i));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_roundtrip) {
+ shared_ptr<TMemoryBuffer> strBuffer(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> binaryProtcol(new TBinaryProtocol(strBuffer));
+
+ thrift::test::Xtruct a;
+ a.i32_thing = 10;
+ a.i64_thing = 30;
+ a.string_thing = "holla back a";
+
+ a.write(binaryProtcol.get());
+ std::string serialized = strBuffer->getBufferAsString();
+
+ shared_ptr<TMemoryBuffer> strBuffer2(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> binaryProtcol2(new TBinaryProtocol(strBuffer2));
+
+ strBuffer2->resetBuffer((uint8_t*)serialized.data(), static_cast<uint32_t>(serialized.length()));
+ thrift::test::Xtruct a2;
+ a2.read(binaryProtcol2.get());
+
+ BOOST_CHECK(a == a2);
+}
+
+BOOST_AUTO_TEST_CASE(test_readAppendToString) {
+ string str1 = "abcd1234";
+ TMemoryBuffer buf((uint8_t*)str1.data(),
+ static_cast<uint32_t>(str1.length()),
+ TMemoryBuffer::COPY);
+
+ string str3 = "wxyz", str4 = "6789";
+ buf.readAppendToString(str3, 4);
+ buf.readAppendToString(str4, INT_MAX);
+
+ BOOST_CHECK(str3 == "wxyzabcd");
+ BOOST_CHECK(str4 == "67891234");
+}
+
+BOOST_AUTO_TEST_CASE(test_exceptions) {
+ char data[] = "foo\0bar";
+
+ TMemoryBuffer buf1((uint8_t*)data, 7, TMemoryBuffer::OBSERVE);
+ string str = buf1.getBufferAsString();
+ BOOST_CHECK(str.length() == 7);
+
+ buf1.resetBuffer();
+
+ BOOST_CHECK_THROW(buf1.write((const uint8_t*)"foo", 3), TTransportException);
+
+ TMemoryBuffer buf2((uint8_t*)data, 7, TMemoryBuffer::COPY);
+ BOOST_CHECK_NO_THROW(buf2.write((const uint8_t*)"bar", 3));
+}
+
+BOOST_AUTO_TEST_CASE(test_default_maximum_buffer_size)
+{
+ BOOST_CHECK_EQUAL((std::numeric_limits<uint32_t>::max)(), TMemoryBuffer().getMaxBufferSize());
+}
+
+BOOST_AUTO_TEST_CASE(test_default_buffer_size)
+{
+ BOOST_CHECK_EQUAL(1024, TMemoryBuffer().getBufferSize());
+}
+
+BOOST_AUTO_TEST_CASE(test_error_set_max_buffer_size_too_small)
+{
+ TMemoryBuffer buf;
+ BOOST_CHECK_THROW(buf.setMaxBufferSize(buf.getBufferSize() - 1), TTransportException);
+}
+
+BOOST_AUTO_TEST_CASE(test_maximum_buffer_size)
+{
+ TMemoryBuffer buf;
+ buf.setMaxBufferSize(8192);
+ std::vector<uint8_t> small_buff(1);
+
+ for (size_t i = 0; i < 8192; ++i)
+ {
+ buf.write(&small_buff[0], 1);
+ }
+
+ BOOST_CHECK_THROW(buf.write(&small_buff[0], 1), TTransportException);
+}
+
+BOOST_AUTO_TEST_CASE(test_memory_buffer_to_get_sizeof_objects)
+{
+ // This is a demonstration of how to use TMemoryBuffer to determine
+ // the serialized size of a thrift object in the Binary protocol.
+ // See THRIFT-3480
+
+ shared_ptr<TMemoryBuffer> memBuffer(new TMemoryBuffer());
+ shared_ptr<TBinaryProtocol> binaryProtcol(new TBinaryProtocol(memBuffer));
+
+ thrift::test::Xtruct object;
+ object.i32_thing = 10;
+ object.i64_thing = 30;
+ object.string_thing = "who's your daddy?";
+
+ uint32_t size = object.write(binaryProtcol.get());
+ BOOST_CHECK_EQUAL(47, size);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TNonblockingSSLServerTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TNonblockingSSLServerTest.cpp
new file mode 100644
index 000000000..dc40c1257
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TNonblockingSSLServerTest.cpp
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE TNonblockingSSLServerTest
+#include <boost/test/unit_test.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+
+#include "thrift/server/TNonblockingServer.h"
+#include "thrift/transport/TSSLSocket.h"
+#include "thrift/transport/TNonblockingSSLServerSocket.h"
+
+#include "gen-cpp/ParentService.h"
+
+#include <event.h>
+
+using namespace apache::thrift;
+using apache::thrift::concurrency::Guard;
+using apache::thrift::concurrency::Monitor;
+using apache::thrift::concurrency::Mutex;
+using apache::thrift::server::TServerEventHandler;
+using apache::thrift::transport::TSSLSocketFactory;
+using apache::thrift::transport::TSSLSocket;
+
+struct Handler : public test::ParentServiceIf {
+ void addString(const std::string& s) override { strings_.push_back(s); }
+ void getStrings(std::vector<std::string>& _return) override { _return = strings_; }
+ std::vector<std::string> strings_;
+
+ // dummy overrides not used in this test
+ int32_t incrementGeneration() override { return 0; }
+ int32_t getGeneration() override { return 0; }
+ void getDataWait(std::string&, const int32_t) override {}
+ void onewayWait() override {}
+ void exceptionWait(const std::string&) override {}
+ void unexpectedExceptionWait(const std::string&) override {}
+};
+
+boost::filesystem::path keyDir;
+boost::filesystem::path certFile(const std::string& filename)
+{
+ return keyDir / filename;
+}
+
+struct GlobalFixtureSSL
+{
+ GlobalFixtureSSL()
+ {
+ using namespace boost::unit_test::framework;
+ for (int i = 0; i < master_test_suite().argc; ++i)
+ {
+ BOOST_TEST_MESSAGE(boost::format("argv[%1%] = \"%2%\"") % i % master_test_suite().argv[i]);
+ }
+
+#ifdef __linux__
+ // OpenSSL calls send() without MSG_NOSIGPIPE so writing to a socket that has
+ // disconnected can cause a SIGPIPE signal...
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ TSSLSocketFactory::setManualOpenSSLInitialization(true);
+ apache::thrift::transport::initializeOpenSSL();
+
+ keyDir = boost::filesystem::current_path().parent_path().parent_path().parent_path() / "test" / "keys";
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ keyDir = boost::filesystem::path(master_test_suite().argv[master_test_suite().argc - 1]);
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ throw std::invalid_argument("The last argument to this test must be the directory containing the test certificate(s).");
+ }
+ }
+ }
+
+ virtual ~GlobalFixtureSSL()
+ {
+ apache::thrift::transport::cleanupOpenSSL();
+#ifdef __linux__
+ signal(SIGPIPE, SIG_DFL);
+#endif
+ }
+};
+
+#if (BOOST_VERSION >= 105900)
+BOOST_GLOBAL_FIXTURE(GlobalFixtureSSL);
+#else
+BOOST_GLOBAL_FIXTURE(GlobalFixtureSSL)
+#endif
+
+std::shared_ptr<TSSLSocketFactory> createServerSocketFactory() {
+ std::shared_ptr<TSSLSocketFactory> pServerSocketFactory;
+
+ pServerSocketFactory.reset(new TSSLSocketFactory());
+ pServerSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+ pServerSocketFactory->loadCertificate(certFile("server.crt").string().c_str());
+ pServerSocketFactory->loadPrivateKey(certFile("server.key").string().c_str());
+ pServerSocketFactory->server(true);
+ return pServerSocketFactory;
+}
+
+std::shared_ptr<TSSLSocketFactory> createClientSocketFactory() {
+ std::shared_ptr<TSSLSocketFactory> pClientSocketFactory;
+
+ pClientSocketFactory.reset(new TSSLSocketFactory());
+ pClientSocketFactory->authenticate(true);
+ pClientSocketFactory->loadCertificate(certFile("client.crt").string().c_str());
+ pClientSocketFactory->loadPrivateKey(certFile("client.key").string().c_str());
+ pClientSocketFactory->loadTrustedCertificates(certFile("CA.pem").string().c_str());
+ return pClientSocketFactory;
+}
+
+class Fixture {
+private:
+ struct ListenEventHandler : public TServerEventHandler {
+ public:
+ ListenEventHandler(Mutex* mutex) : listenMonitor_(mutex), ready_(false) {}
+
+ void preServe() override /* override */ {
+ Guard g(listenMonitor_.mutex());
+ ready_ = true;
+ listenMonitor_.notify();
+ }
+
+ Monitor listenMonitor_;
+ bool ready_;
+ };
+
+ struct Runner : public apache::thrift::concurrency::Runnable {
+ int port;
+ std::shared_ptr<event_base> userEventBase;
+ std::shared_ptr<TProcessor> processor;
+ std::shared_ptr<server::TNonblockingServer> server;
+ std::shared_ptr<ListenEventHandler> listenHandler;
+ std::shared_ptr<TSSLSocketFactory> pServerSocketFactory;
+ std::shared_ptr<transport::TNonblockingSSLServerSocket> socket;
+ Mutex mutex_;
+
+ Runner():port(0) {
+ listenHandler.reset(new ListenEventHandler(&mutex_));
+ }
+
+ void run() override {
+ // When binding to explicit port, allow retrying to workaround bind failures on ports in use
+ int retryCount = port ? 10 : 0;
+ pServerSocketFactory = createServerSocketFactory();
+ startServer(retryCount);
+ }
+
+ void readyBarrier() {
+ // block until server is listening and ready to accept connections
+ Guard g(mutex_);
+ while (!listenHandler->ready_) {
+ listenHandler->listenMonitor_.wait();
+ }
+ }
+ private:
+ void startServer(int retry_count) {
+ try {
+ socket.reset(new transport::TNonblockingSSLServerSocket(port, pServerSocketFactory));
+ server.reset(new server::TNonblockingServer(processor, socket));
+ server->setServerEventHandler(listenHandler);
+ server->setNumIOThreads(1);
+ if (userEventBase) {
+ server->registerEvents(userEventBase.get());
+ }
+ server->serve();
+ } catch (const transport::TTransportException&) {
+ if (retry_count > 0) {
+ ++port;
+ startServer(retry_count - 1);
+ } else {
+ throw;
+ }
+ }
+ }
+ };
+
+ struct EventDeleter {
+ void operator()(event_base* p) { event_base_free(p); }
+ };
+
+protected:
+ Fixture() : processor(new test::ParentServiceProcessor(std::make_shared<Handler>())) {}
+
+ ~Fixture() {
+ if (server) {
+ server->stop();
+ }
+ if (thread) {
+ thread->join();
+ }
+ }
+
+ void setEventBase(event_base* user_event_base) {
+ userEventBase_.reset(user_event_base, EventDeleter());
+ }
+
+ int startServer(int port) {
+ std::shared_ptr<Runner> runner(new Runner);
+ runner->port = port;
+ runner->processor = processor;
+ runner->userEventBase = userEventBase_;
+
+ std::unique_ptr<apache::thrift::concurrency::ThreadFactory> threadFactory(
+ new apache::thrift::concurrency::ThreadFactory(false));
+ thread = threadFactory->newThread(runner);
+ thread->start();
+ runner->readyBarrier();
+
+ server = runner->server;
+ return runner->port;
+ }
+
+ bool canCommunicate(int serverPort) {
+ std::shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ std::shared_ptr<TSSLSocket> socket = pClientSocketFactory->createSocket("localhost", serverPort);
+ socket->open();
+ test::ParentServiceClient client(std::make_shared<protocol::TBinaryProtocol>(
+ std::make_shared<transport::TFramedTransport>(socket)));
+ client.addString("foo");
+ std::vector<std::string> strings;
+ client.getStrings(strings);
+ return strings.size() == 1 && !(strings[0].compare("foo"));
+ }
+
+private:
+ std::shared_ptr<event_base> userEventBase_;
+ std::shared_ptr<test::ParentServiceProcessor> processor;
+protected:
+ std::shared_ptr<server::TNonblockingServer> server;
+private:
+ std::shared_ptr<apache::thrift::concurrency::Thread> thread;
+
+};
+
+BOOST_AUTO_TEST_SUITE(TNonblockingSSLServerTest)
+
+BOOST_FIXTURE_TEST_CASE(get_specified_port, Fixture) {
+ int specified_port = startServer(12345);
+ BOOST_REQUIRE_GE(specified_port, 12345);
+ BOOST_REQUIRE_EQUAL(server->getListenPort(), specified_port);
+ BOOST_CHECK(canCommunicate(specified_port));
+
+ server->stop();
+}
+
+BOOST_FIXTURE_TEST_CASE(get_assigned_port, Fixture) {
+ int specified_port = startServer(0);
+ BOOST_REQUIRE_EQUAL(specified_port, 0);
+ int assigned_port = server->getListenPort();
+ BOOST_REQUIRE_NE(assigned_port, 0);
+ BOOST_CHECK(canCommunicate(assigned_port));
+
+ server->stop();
+}
+
+BOOST_FIXTURE_TEST_CASE(provide_event_base, Fixture) {
+ event_base* eb = event_base_new();
+ setEventBase(eb);
+ startServer(0);
+
+ // assert that the server works
+ BOOST_CHECK(canCommunicate(server->getListenPort()));
+#if LIBEVENT_VERSION_NUMBER > 0x02010400
+ // also assert that the event_base is actually used when it's easy
+ BOOST_CHECK_GT(event_base_get_num_events(eb, EVENT_BASE_COUNT_ADDED), 0);
+#endif
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TNonblockingServerTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TNonblockingServerTest.cpp
new file mode 100644
index 000000000..f9aab4cc1
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TNonblockingServerTest.cpp
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE TNonblockingServerTest
+#include <boost/test/unit_test.hpp>
+#include <memory>
+
+#include "thrift/concurrency/Monitor.h"
+#include "thrift/concurrency/Thread.h"
+#include "thrift/server/TNonblockingServer.h"
+#include "thrift/transport/TNonblockingServerSocket.h"
+
+#include "gen-cpp/ParentService.h"
+
+#include <event.h>
+
+using apache::thrift::concurrency::Guard;
+using apache::thrift::concurrency::Monitor;
+using apache::thrift::concurrency::Mutex;
+using apache::thrift::concurrency::ThreadFactory;
+using apache::thrift::concurrency::Runnable;
+using apache::thrift::concurrency::Thread;
+using apache::thrift::concurrency::ThreadFactory;
+using apache::thrift::server::TServerEventHandler;
+using std::make_shared;
+using std::shared_ptr;
+
+using namespace apache::thrift;
+
+struct Handler : public test::ParentServiceIf {
+ void addString(const std::string& s) override { strings_.push_back(s); }
+ void getStrings(std::vector<std::string>& _return) override { _return = strings_; }
+ std::vector<std::string> strings_;
+
+ // dummy overrides not used in this test
+ int32_t incrementGeneration() override { return 0; }
+ int32_t getGeneration() override { return 0; }
+ void getDataWait(std::string&, const int32_t) override {}
+ void onewayWait() override {}
+ void exceptionWait(const std::string&) override {}
+ void unexpectedExceptionWait(const std::string&) override {}
+};
+
+class Fixture {
+private:
+ struct ListenEventHandler : public TServerEventHandler {
+ public:
+ ListenEventHandler(Mutex* mutex) : listenMonitor_(mutex), ready_(false) {}
+
+ void preServe() override /* override */ {
+ Guard g(listenMonitor_.mutex());
+ ready_ = true;
+ listenMonitor_.notify();
+ }
+
+ Monitor listenMonitor_;
+ bool ready_;
+ };
+
+ struct Runner : public Runnable {
+ int port;
+ shared_ptr<event_base> userEventBase;
+ shared_ptr<TProcessor> processor;
+ shared_ptr<server::TNonblockingServer> server;
+ shared_ptr<ListenEventHandler> listenHandler;
+ shared_ptr<transport::TNonblockingServerSocket> socket;
+ Mutex mutex_;
+
+ Runner() {
+ port = 0;
+ listenHandler.reset(new ListenEventHandler(&mutex_));
+ }
+
+ void run() override {
+ // When binding to explicit port, allow retrying to workaround bind failures on ports in use
+ int retryCount = port ? 10 : 0;
+ startServer(retryCount);
+ }
+
+ void readyBarrier() {
+ // block until server is listening and ready to accept connections
+ Guard g(mutex_);
+ while (!listenHandler->ready_) {
+ listenHandler->listenMonitor_.wait();
+ }
+ }
+ private:
+ void startServer(int retry_count) {
+ try {
+ socket.reset(new transport::TNonblockingServerSocket(port));
+ server.reset(new server::TNonblockingServer(processor, socket));
+ server->setServerEventHandler(listenHandler);
+ if (userEventBase) {
+ server->registerEvents(userEventBase.get());
+ }
+ server->serve();
+ } catch (const transport::TTransportException&) {
+ if (retry_count > 0) {
+ ++port;
+ startServer(retry_count - 1);
+ } else {
+ throw;
+ }
+ }
+ }
+ };
+
+ struct EventDeleter {
+ void operator()(event_base* p) { event_base_free(p); }
+ };
+
+protected:
+ Fixture() : processor(new test::ParentServiceProcessor(make_shared<Handler>())) {}
+
+ ~Fixture() {
+ if (server) {
+ server->stop();
+ }
+ if (thread) {
+ thread->join();
+ }
+ }
+
+ void setEventBase(event_base* user_event_base) {
+ userEventBase_.reset(user_event_base, EventDeleter());
+ }
+
+ int startServer(int port) {
+ shared_ptr<Runner> runner(new Runner);
+ runner->port = port;
+ runner->processor = processor;
+ runner->userEventBase = userEventBase_;
+
+ shared_ptr<ThreadFactory> threadFactory(
+ new ThreadFactory(false));
+ thread = threadFactory->newThread(runner);
+ thread->start();
+ runner->readyBarrier();
+
+ server = runner->server;
+ return runner->port;
+ }
+
+ bool canCommunicate(int serverPort) {
+ shared_ptr<transport::TSocket> socket(new transport::TSocket("localhost", serverPort));
+ socket->open();
+ test::ParentServiceClient client(make_shared<protocol::TBinaryProtocol>(
+ make_shared<transport::TFramedTransport>(socket)));
+ client.addString("foo");
+ std::vector<std::string> strings;
+ client.getStrings(strings);
+ return strings.size() == 1 && !(strings[0].compare("foo"));
+ }
+
+private:
+ shared_ptr<event_base> userEventBase_;
+ shared_ptr<test::ParentServiceProcessor> processor;
+protected:
+ shared_ptr<server::TNonblockingServer> server;
+private:
+ shared_ptr<apache::thrift::concurrency::Thread> thread;
+
+};
+
+BOOST_AUTO_TEST_SUITE(TNonblockingServerTest)
+
+BOOST_FIXTURE_TEST_CASE(get_specified_port, Fixture) {
+ int specified_port = startServer(12345);
+ BOOST_REQUIRE_GE(specified_port, 12345);
+ BOOST_REQUIRE_EQUAL(server->getListenPort(), specified_port);
+ BOOST_CHECK(canCommunicate(specified_port));
+
+ server->stop();
+}
+
+BOOST_FIXTURE_TEST_CASE(get_assigned_port, Fixture) {
+ int specified_port = startServer(0);
+ BOOST_REQUIRE_EQUAL(specified_port, 0);
+ int assigned_port = server->getListenPort();
+ BOOST_REQUIRE_NE(assigned_port, 0);
+ BOOST_CHECK(canCommunicate(assigned_port));
+
+ server->stop();
+}
+
+BOOST_FIXTURE_TEST_CASE(provide_event_base, Fixture) {
+ event_base* eb = event_base_new();
+ setEventBase(eb);
+ startServer(0);
+
+ // assert that the server works
+ BOOST_CHECK(canCommunicate(server->getListenPort()));
+#if LIBEVENT_VERSION_NUMBER > 0x02010400
+ // also assert that the event_base is actually used when it's easy
+ BOOST_CHECK_GT(event_base_get_num_events(eb, EVENT_BASE_COUNT_ADDED), 0);
+#endif
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TPipeInterruptTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TPipeInterruptTest.cpp
new file mode 100644
index 000000000..2423f5646
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TPipeInterruptTest.cpp
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifdef _WIN32
+
+#include <boost/test/test_tools.hpp>
+#include <boost/test/unit_test_suite.hpp>
+
+#include <boost/chrono/duration.hpp>
+#include <boost/date_time/posix_time/posix_time_duration.hpp>
+#include <boost/thread/thread.hpp>
+#include <thrift/transport/TPipe.h>
+#include <thrift/transport/TPipeServer.h>
+#include <memory>
+
+using apache::thrift::transport::TPipeServer;
+using apache::thrift::transport::TPipe;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using namespace apache::thrift;
+
+BOOST_AUTO_TEST_SUITE(TPipeInterruptTest)
+
+// TODO: duplicate the test cases in TSocketInterruptTest for pipes,
+// once pipes implement interruptChildren
+
+BOOST_AUTO_TEST_CASE(test_interrupt_before_accept) {
+ TPipeServer pipe1("TPipeInterruptTest");
+ pipe1.listen();
+ pipe1.interrupt();
+ BOOST_CHECK_THROW(pipe1.accept(), TTransportException);
+}
+
+static void acceptWorker(TPipeServer *pipe) {
+ try
+ {
+ for (;;)
+ {
+ std::shared_ptr<TTransport> temp = pipe->accept();
+ }
+ }
+ catch (...) {/*just want to make sure nothing crashes*/ }
+}
+
+static void interruptWorker(TPipeServer *pipe) {
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ pipe->interrupt();
+}
+
+BOOST_AUTO_TEST_CASE(stress_pipe_accept_interruption) {
+ int interruptIters = 10;
+
+ for (int i = 0; i < interruptIters; ++i)
+ {
+ TPipeServer pipeServer("TPipeInterruptTest");
+ pipeServer.listen();
+ boost::thread acceptThread(std::bind(acceptWorker, &pipeServer));
+ boost::thread interruptThread(std::bind(interruptWorker, &pipeServer));
+ try
+ {
+ for (;;)
+ {
+ TPipe client("TPipeInterruptTest");
+ client.setConnTimeout(1);
+ client.open();
+ }
+ } catch (...) { /*just testing for crashes*/ }
+ interruptThread.join();
+ acceptThread.join();
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TPipedTransportTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TPipedTransportTest.cpp
new file mode 100644
index 000000000..f3091a487
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TPipedTransportTest.cpp
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/Thrift.h>
+#include <memory>
+#include <thrift/transport/TTransportUtils.h>
+#include <thrift/transport/TBufferTransports.h>
+
+#define BOOST_TEST_MODULE TPipedTransportTest
+#include <boost/test/unit_test.hpp>
+
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TPipedTransport;
+using apache::thrift::transport::TMemoryBuffer;
+using namespace apache::thrift;
+
+BOOST_AUTO_TEST_CASE(test_read_write) {
+ std::shared_ptr<TMemoryBuffer> underlying(new TMemoryBuffer);
+ std::shared_ptr<TMemoryBuffer> pipe(new TMemoryBuffer);
+ std::shared_ptr<TPipedTransport> trans(new TPipedTransport(underlying, pipe));
+
+ uint8_t buffer[4];
+
+ underlying->write((uint8_t*)"abcd", 4);
+ trans->readAll(buffer, 2);
+ BOOST_CHECK(std::string((char*)buffer, 2) == "ab");
+ trans->readEnd();
+ BOOST_CHECK(pipe->getBufferAsString() == "ab");
+ pipe->resetBuffer();
+ underlying->write((uint8_t*)"ef", 2);
+ trans->readAll(buffer, 2);
+ BOOST_CHECK(std::string((char*)buffer, 2) == "cd");
+ trans->readAll(buffer, 2);
+ BOOST_CHECK(std::string((char*)buffer, 2) == "ef");
+ trans->readEnd();
+ BOOST_CHECK(pipe->getBufferAsString() == "cdef");
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TSSLSocketInterruptTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TSSLSocketInterruptTest.cpp
new file mode 100644
index 000000000..33f686cef
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TSSLSocketInterruptTest.cpp
@@ -0,0 +1,282 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/test/unit_test_suite.hpp>
+#include <boost/chrono/duration.hpp>
+#include <boost/date_time/posix_time/posix_time_duration.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+#include <memory>
+#include <thrift/transport/TSSLSocket.h>
+#include <thrift/transport/TSSLServerSocket.h>
+#ifdef __linux__
+#include <signal.h>
+#endif
+
+using apache::thrift::transport::TSSLServerSocket;
+using apache::thrift::transport::TSSLSocket;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TSSLSocketFactory;
+
+using std::static_pointer_cast;
+using std::shared_ptr;
+
+BOOST_AUTO_TEST_SUITE(TSSLSocketInterruptTest)
+
+boost::filesystem::path keyDir;
+boost::filesystem::path certFile(const std::string& filename)
+{
+ return keyDir / filename;
+}
+boost::mutex gMutex;
+
+struct GlobalFixtureSSL
+{
+ GlobalFixtureSSL()
+ {
+ using namespace boost::unit_test::framework;
+ for (int i = 0; i < master_test_suite().argc; ++i)
+ {
+ BOOST_TEST_MESSAGE(boost::format("argv[%1%] = \"%2%\"") % i % master_test_suite().argv[i]);
+ }
+
+#ifdef __linux__
+ // OpenSSL calls send() without MSG_NOSIGPIPE so writing to a socket that has
+ // disconnected can cause a SIGPIPE signal...
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ TSSLSocketFactory::setManualOpenSSLInitialization(true);
+ apache::thrift::transport::initializeOpenSSL();
+
+ keyDir = boost::filesystem::current_path().parent_path().parent_path().parent_path() / "test" / "keys";
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ keyDir = boost::filesystem::path(master_test_suite().argv[master_test_suite().argc - 1]);
+ if (!boost::filesystem::exists(certFile("server.crt")))
+ {
+ throw std::invalid_argument("The last argument to this test must be the directory containing the test certificate(s).");
+ }
+ }
+ }
+
+ virtual ~GlobalFixtureSSL()
+ {
+ apache::thrift::transport::cleanupOpenSSL();
+#ifdef __linux__
+ signal(SIGPIPE, SIG_DFL);
+#endif
+ }
+};
+
+#if (BOOST_VERSION >= 105900)
+BOOST_GLOBAL_FIXTURE(GlobalFixtureSSL);
+#else
+BOOST_GLOBAL_FIXTURE(GlobalFixtureSSL)
+#endif
+
+void readerWorker(shared_ptr<TTransport> tt, uint32_t expectedResult) {
+ uint8_t buf[4];
+ try {
+ tt->read(buf, 1);
+ BOOST_CHECK_EQUAL(expectedResult, tt->read(buf, 4));
+ } catch (const TTransportException& tx) {
+ BOOST_CHECK_EQUAL(TTransportException::TIMED_OUT, tx.getType());
+ }
+}
+
+void readerWorkerMustThrow(shared_ptr<TTransport> tt) {
+ try {
+ uint8_t buf[400];
+ tt->read(buf, 1);
+ tt->read(buf, 400);
+ BOOST_ERROR("should not have gotten here");
+ } catch (const TTransportException& tx) {
+ BOOST_CHECK_EQUAL(TTransportException::INTERRUPTED, tx.getType());
+ }
+}
+
+shared_ptr<TSSLSocketFactory> createServerSocketFactory() {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory;
+
+ pServerSocketFactory.reset(new TSSLSocketFactory());
+ pServerSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+ pServerSocketFactory->loadCertificate(certFile("server.crt").string().c_str());
+ pServerSocketFactory->loadPrivateKey(certFile("server.key").string().c_str());
+ pServerSocketFactory->server(true);
+ return pServerSocketFactory;
+}
+
+shared_ptr<TSSLSocketFactory> createClientSocketFactory() {
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory;
+
+ pClientSocketFactory.reset(new TSSLSocketFactory());
+ pClientSocketFactory->authenticate(true);
+ pClientSocketFactory->loadCertificate(certFile("client.crt").string().c_str());
+ pClientSocketFactory->loadPrivateKey(certFile("client.key").string().c_str());
+ pClientSocketFactory->loadTrustedCertificates(certFile("CA.pem").string().c_str());
+ return pClientSocketFactory;
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_read_while_handshaking) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.listen();
+ int port = sock1.getPort();
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
+ clientSock->open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ boost::thread readThread(std::bind(readerWorkerMustThrow, accepted));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // readThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(readThread.try_join_for(boost::chrono::milliseconds(20)),
+ "server socket interruptChildren did not interrupt child read");
+ clientSock->close();
+ accepted->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_read) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.listen();
+ int port = sock1.getPort();
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
+ clientSock->open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ boost::thread readThread(std::bind(readerWorkerMustThrow, accepted));
+ clientSock->write((const uint8_t*)"0", 1);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // readThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(readThread.try_join_for(boost::chrono::milliseconds(20)),
+ "server socket interruptChildren did not interrupt child read");
+ accepted->close();
+ clientSock->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_non_interruptable_child_read) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
+ sock1.listen();
+ int port = sock1.getPort();
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
+ clientSock->open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ static_pointer_cast<TSSLSocket>(accepted)->setRecvTimeout(1000);
+ boost::thread readThread(std::bind(readerWorker, accepted, 0));
+ clientSock->write((const uint8_t*)"0", 1);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // readThread is practically guaranteed to be blocking here
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(!readThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren interrupted child read");
+
+ // wait for receive timeout to kick in
+ readThread.join();
+ accepted->close();
+ clientSock->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_cannot_change_after_listen) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.listen();
+ BOOST_CHECK_THROW(sock1.setInterruptableChildren(false), std::logic_error);
+ sock1.close();
+}
+
+void peekerWorker(shared_ptr<TTransport> tt, bool expectedResult) {
+ uint8_t buf[400];
+ try {
+ tt->read(buf, 1);
+ BOOST_CHECK_EQUAL(expectedResult, tt->peek());
+ } catch (const TTransportException& tx) {
+ BOOST_CHECK_EQUAL(TTransportException::TIMED_OUT, tx.getType());
+ }
+}
+
+void peekerWorkerInterrupt(shared_ptr<TTransport> tt) {
+ uint8_t buf[400];
+ try {
+ tt->read(buf, 1);
+ tt->peek();
+ } catch (const TTransportException& tx) {
+ BOOST_CHECK_EQUAL(TTransportException::INTERRUPTED, tx.getType());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_peek) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.listen();
+ int port = sock1.getPort();
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
+ clientSock->open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ boost::thread peekThread(std::bind(peekerWorkerInterrupt, accepted));
+ clientSock->write((const uint8_t*)"0", 1);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // peekThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(peekThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren did not interrupt child peek");
+ accepted->close();
+ clientSock->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_ssl_non_interruptable_child_peek) {
+ shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
+ TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
+ sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
+ sock1.listen();
+ int port = sock1.getPort();
+ shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
+ shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
+ clientSock->open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ static_pointer_cast<TSSLSocket>(accepted)->setRecvTimeout(1000);
+ boost::thread peekThread(std::bind(peekerWorker, accepted, false));
+ clientSock->write((const uint8_t*)"0", 1);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // peekThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(!peekThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren interrupted child peek");
+
+ // wait for the receive timeout to kick in
+ peekThread.join();
+ accepted->close();
+ clientSock->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TServerIntegrationTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TServerIntegrationTest.cpp
new file mode 100644
index 000000000..b88c35bf4
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TServerIntegrationTest.cpp
@@ -0,0 +1,536 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE TServerIntegrationTest
+#include <atomic>
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/date_time/posix_time/ptime.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <thrift/server/TSimpleServer.h>
+#include <thrift/server/TThreadPoolServer.h>
+#include <thrift/server/TThreadedServer.h>
+#include <memory>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/transport/TServerSocket.h>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TTransport.h>
+#include "gen-cpp/ParentService.h"
+#include <string>
+#include <vector>
+
+using apache::thrift::concurrency::Guard;
+using apache::thrift::concurrency::Monitor;
+using apache::thrift::concurrency::Mutex;
+using apache::thrift::concurrency::Synchronized;
+using apache::thrift::protocol::TBinaryProtocol;
+using apache::thrift::protocol::TBinaryProtocolFactory;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TProtocolFactory;
+using apache::thrift::transport::TServerSocket;
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TSocket;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TTransportFactory;
+using apache::thrift::server::TServer;
+using apache::thrift::server::TServerEventHandler;
+using apache::thrift::server::TSimpleServer;
+using apache::thrift::server::TThreadPoolServer;
+using apache::thrift::server::TThreadedServer;
+using std::dynamic_pointer_cast;
+using std::make_shared;
+using std::shared_ptr;
+using apache::thrift::test::ParentServiceClient;
+using apache::thrift::test::ParentServiceIf;
+using apache::thrift::test::ParentServiceIfFactory;
+using apache::thrift::test::ParentServiceIfSingletonFactory;
+using apache::thrift::test::ParentServiceProcessor;
+using apache::thrift::test::ParentServiceProcessorFactory;
+using apache::thrift::TProcessor;
+using apache::thrift::TProcessorFactory;
+using boost::posix_time::milliseconds;
+
+/**
+ * preServe runs after listen() is successful, when we can connect
+ */
+class TServerReadyEventHandler : public TServerEventHandler, public Monitor {
+public:
+ TServerReadyEventHandler() : isListening_(false), accepted_(0) {}
+ ~TServerReadyEventHandler() override = default;
+ void preServe() override {
+ Synchronized sync(*this);
+ isListening_ = true;
+ notify();
+ }
+ void* createContext(shared_ptr<TProtocol> input,
+ shared_ptr<TProtocol> output) override {
+ Synchronized sync(*this);
+ ++accepted_;
+ notify();
+
+ (void)input;
+ (void)output;
+ return nullptr;
+ }
+ bool isListening() const { return isListening_; }
+ uint64_t acceptedCount() const { return accepted_; }
+
+private:
+ bool isListening_;
+ uint64_t accepted_;
+};
+
+/**
+ * Reusing another generated test, just something to serve up
+ */
+class ParentHandler : public ParentServiceIf {
+public:
+ ParentHandler() : generation_(0) {}
+
+ int32_t incrementGeneration() override {
+ Guard g(mutex_);
+ return ++generation_;
+ }
+
+ int32_t getGeneration() override {
+ Guard g(mutex_);
+ return generation_;
+ }
+
+ void addString(const std::string& s) override {
+ Guard g(mutex_);
+ strings_.push_back(s);
+ }
+
+ void getStrings(std::vector<std::string>& _return) override {
+ Guard g(mutex_);
+ _return = strings_;
+ }
+
+ void getDataWait(std::string& _return, const int32_t length) override {
+ THRIFT_UNUSED_VARIABLE(_return);
+ THRIFT_UNUSED_VARIABLE(length);
+ }
+
+ void onewayWait() override {}
+
+ void exceptionWait(const std::string& message) override { THRIFT_UNUSED_VARIABLE(message); }
+
+ void unexpectedExceptionWait(const std::string& message) override { THRIFT_UNUSED_VARIABLE(message); }
+
+protected:
+ Mutex mutex_;
+ int32_t generation_;
+ std::vector<std::string> strings_;
+};
+
+void autoSocketCloser(TSocket* pSock) {
+ pSock->close();
+ delete pSock;
+}
+
+template <class TServerType>
+class TServerIntegrationTestFixture {
+public:
+ TServerIntegrationTestFixture(const shared_ptr<TProcessorFactory>& _processorFactory)
+ : pServer(new TServerType(_processorFactory,
+ shared_ptr<TServerTransport>(
+ new TServerSocket("localhost", 0)),
+ shared_ptr<TTransportFactory>(new TTransportFactory),
+ shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory))),
+ pEventHandler(shared_ptr<TServerReadyEventHandler>(new TServerReadyEventHandler)),
+ bStressDone(false),
+ bStressConnectionCount(0),
+ bStressRequestCount(0) {
+ pServer->setServerEventHandler(pEventHandler);
+ }
+
+ TServerIntegrationTestFixture(const shared_ptr<TProcessor>& _processor)
+ : pServer(
+ new TServerType(_processor,
+ shared_ptr<TServerTransport>(new TServerSocket("localhost", 0)),
+ shared_ptr<TTransportFactory>(new TTransportFactory),
+ shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory))),
+ pEventHandler(shared_ptr<TServerReadyEventHandler>(new TServerReadyEventHandler)),
+ bStressDone(false),
+ bStressConnectionCount(0),
+ bStressRequestCount(0) {
+ pServer->setServerEventHandler(pEventHandler);
+ }
+
+ void startServer() {
+ pServerThread.reset(new boost::thread(std::bind(&TServerType::serve, pServer.get())));
+
+ // block until listen() completes so clients will be able to connect
+ Synchronized sync(*(pEventHandler.get()));
+ while (!pEventHandler->isListening()) {
+ pEventHandler->wait();
+ }
+
+ BOOST_TEST_MESSAGE(" server is listening");
+ }
+
+ void blockUntilAccepted(uint64_t numAccepted) {
+ Synchronized sync(*(pEventHandler.get()));
+ while (pEventHandler->acceptedCount() < numAccepted) {
+ pEventHandler->wait();
+ }
+
+ BOOST_TEST_MESSAGE(boost::format(" server has accepted %1%") % numAccepted);
+ }
+
+ void stopServer() {
+ if (pServerThread) {
+ pServer->stop();
+ BOOST_TEST_MESSAGE(" server stop completed");
+
+ pServerThread->join();
+ BOOST_TEST_MESSAGE(" server thread joined");
+ pServerThread.reset();
+ }
+ }
+
+ ~TServerIntegrationTestFixture() { stopServer(); }
+
+ /**
+ * Performs a baseline test where some clients are opened and issue a single operation
+ * and then disconnect at different intervals.
+ * \param[in] numToMake the number of concurrent clients
+ * \param[in] expectedHWM the high water mark we expect of concurrency
+ * \param[in] purpose a description of the test for logging purposes
+ */
+ void baseline(int64_t numToMake, int64_t expectedHWM, const std::string& purpose) {
+ BOOST_TEST_MESSAGE(boost::format("Testing %1%: %2% with %3% clients, expect %4% HWM")
+ % typeid(TServerType).name() % purpose % numToMake % expectedHWM);
+
+ startServer();
+
+ std::vector<shared_ptr<TSocket> > holdSockets;
+ std::vector<shared_ptr<boost::thread> > holdThreads;
+
+ for (int64_t i = 0; i < numToMake; ++i) {
+ shared_ptr<TSocket> pClientSock(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ holdSockets.push_back(pClientSock);
+ shared_ptr<TProtocol> pClientProtocol(new TBinaryProtocol(pClientSock));
+ ParentServiceClient client(pClientProtocol);
+ pClientSock->open();
+ client.incrementGeneration();
+ holdThreads.push_back(shared_ptr<boost::thread>(
+ new boost::thread(std::bind(&TServerIntegrationTestFixture::delayClose,
+ this,
+ pClientSock,
+ milliseconds(10 * numToMake)))));
+ }
+
+ BOOST_CHECK_EQUAL(expectedHWM, pServer->getConcurrentClientCountHWM());
+
+ BOOST_FOREACH (shared_ptr<boost::thread> pThread, holdThreads) { pThread->join(); }
+ holdThreads.clear();
+ holdSockets.clear();
+
+ stopServer();
+ }
+
+ /**
+ * Helper method used to close a connection after a delay.
+ * \param[in] toClose the connection to close
+ * \param[in] after the delay to impose
+ */
+ void delayClose(shared_ptr<TTransport> toClose, boost::posix_time::time_duration after) {
+ boost::this_thread::sleep(after);
+ toClose->close();
+ }
+
+ /**
+ * \returns the server port number
+ */
+ int getServerPort() {
+ auto* pSock = dynamic_cast<TServerSocket*>(pServer->getServerTransport().get());
+ if (!pSock) { throw std::logic_error("how come?"); }
+ return pSock->getPort();
+ }
+
+ /**
+ * Performs a stress test by spawning threads that connect, do a number of operations
+ * and disconnect, then a random delay, then do it over again. This is done for a fixed
+ * period of time to test for concurrency correctness.
+ * \param[in] numToMake the number of concurrent clients
+ */
+ void stress(int64_t numToMake, const boost::posix_time::time_duration& duration) {
+ BOOST_TEST_MESSAGE(boost::format("Stress testing %1% with %2% clients for %3% seconds")
+ % typeid(TServerType).name() % numToMake % duration.total_seconds());
+
+ startServer();
+
+ std::vector<shared_ptr<boost::thread> > holdThreads;
+ for (int64_t i = 0; i < numToMake; ++i) {
+ holdThreads.push_back(shared_ptr<boost::thread>(
+ new boost::thread(std::bind(&TServerIntegrationTestFixture::stressor, this))));
+ }
+
+ boost::this_thread::sleep(duration);
+ bStressDone = true;
+
+ BOOST_TEST_MESSAGE(boost::format(" serviced %1% connections (HWM %2%) totaling %3% requests")
+ % bStressConnectionCount % pServer->getConcurrentClientCountHWM() % bStressRequestCount);
+
+ BOOST_FOREACH (shared_ptr<boost::thread> pThread, holdThreads) { pThread->join(); }
+ holdThreads.clear();
+
+ BOOST_CHECK(bStressRequestCount > 0);
+
+ stopServer();
+ }
+
+ /**
+ * Helper method to stress the system
+ */
+ void stressor() {
+ while (!bStressDone) {
+ shared_ptr<TSocket> pSocket(new TSocket("localhost", getServerPort()), autoSocketCloser);
+ shared_ptr<TProtocol> pProtocol(new TBinaryProtocol(pSocket));
+ ParentServiceClient client(pProtocol);
+ pSocket->open();
+ bStressConnectionCount.fetch_add(1, std::memory_order_relaxed);
+ for (int i = 0; i < rand() % 1000; ++i) {
+ client.incrementGeneration();
+ bStressRequestCount.fetch_add(1, std::memory_order_relaxed);
+ }
+ }
+ }
+
+ shared_ptr<TServerType> pServer;
+ shared_ptr<TServerReadyEventHandler> pEventHandler;
+ shared_ptr<boost::thread> pServerThread;
+ std::atomic<bool> bStressDone;
+ std::atomic<int64_t> bStressConnectionCount;
+ std::atomic<int64_t> bStressRequestCount;
+};
+
+template <class TServerType>
+class TServerIntegrationProcessorFactoryTestFixture
+ : public TServerIntegrationTestFixture<TServerType> {
+public:
+ TServerIntegrationProcessorFactoryTestFixture()
+ : TServerIntegrationTestFixture<TServerType>(make_shared<ParentServiceProcessorFactory>(
+ make_shared<ParentServiceIfSingletonFactory>(
+ make_shared<ParentHandler>()))) {}
+};
+
+template <class TServerType>
+class TServerIntegrationProcessorTestFixture : public TServerIntegrationTestFixture<TServerType> {
+public:
+ TServerIntegrationProcessorTestFixture()
+ : TServerIntegrationTestFixture<TServerType>(
+ make_shared<ParentServiceProcessor>(make_shared<ParentHandler>())) {}
+};
+
+BOOST_AUTO_TEST_SUITE(constructors)
+
+BOOST_FIXTURE_TEST_CASE(test_simple_factory,
+ TServerIntegrationProcessorFactoryTestFixture<TSimpleServer>) {
+ baseline(3, 1, "factory");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_simple, TServerIntegrationProcessorTestFixture<TSimpleServer>) {
+ baseline(3, 1, "processor");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threaded_factory,
+ TServerIntegrationProcessorFactoryTestFixture<TThreadedServer>) {
+ baseline(10, 10, "factory");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threaded, TServerIntegrationProcessorTestFixture<TThreadedServer>) {
+ baseline(10, 10, "processor");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threaded_bound,
+ TServerIntegrationProcessorTestFixture<TThreadedServer>) {
+ pServer->setConcurrentClientLimit(4);
+ baseline(10, 4, "limit by server framework");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threaded_stress,
+ TServerIntegrationProcessorFactoryTestFixture<TThreadedServer>) {
+ stress(10, boost::posix_time::seconds(3));
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threadpool_factory,
+ TServerIntegrationProcessorFactoryTestFixture<TThreadPoolServer>) {
+ pServer->getThreadManager()->threadFactory(
+ shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory));
+ pServer->getThreadManager()->start();
+
+ // thread factory has 4 threads as a default
+ // thread factory however is a bad way to limit concurrent clients
+ // as accept() will be called to grab a 5th client socket, in this case
+ // and then the thread factory will block adding the thread to manage
+ // that client.
+ baseline(10, 5, "limit by thread manager");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threadpool,
+ TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
+ pServer->getThreadManager()->threadFactory(
+ shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory));
+ pServer->getThreadManager()->start();
+
+ // thread factory has 4 threads as a default
+ // thread factory however is a bad way to limit concurrent clients
+ // as accept() will be called to grab a 5th client socket, in this case
+ // and then the thread factory will block adding the thread to manage
+ // that client.
+ baseline(10, 5, "limit by thread manager");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threadpool_bound,
+ TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
+ pServer->getThreadManager()->threadFactory(
+ shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory));
+ pServer->getThreadManager()->start();
+ pServer->setConcurrentClientLimit(4);
+
+ baseline(10, 4, "server framework connection limit");
+}
+
+BOOST_FIXTURE_TEST_CASE(test_threadpool_stress,
+ TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
+ pServer->getThreadManager()->threadFactory(
+ shared_ptr<apache::thrift::concurrency::ThreadFactory>(
+ new apache::thrift::concurrency::ThreadFactory));
+ pServer->getThreadManager()->start();
+
+ stress(10, boost::posix_time::seconds(3));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_FIXTURE_TEST_SUITE(TServerIntegrationTest,
+ TServerIntegrationProcessorTestFixture<TThreadedServer>)
+
+BOOST_AUTO_TEST_CASE(test_stop_with_interruptable_clients_connected) {
+ // This tests THRIFT-2441 new behavior: stopping the server disconnects clients
+ BOOST_TEST_MESSAGE("Testing stop with interruptable clients");
+
+ startServer();
+
+ shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock1->open();
+
+ shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock2->open();
+
+ // Ensure they have been accepted
+ blockUntilAccepted(2);
+
+ // The test fixture destructor will force the sockets to disconnect
+ // Prior to THRIFT-2441, pServer->stop() would hang until clients disconnected
+ stopServer();
+
+ // extra proof the server end disconnected the clients
+ uint8_t buf[1];
+ BOOST_CHECK_EQUAL(0, pClientSock1->read(&buf[0], 1)); // 0 = disconnected
+ BOOST_CHECK_EQUAL(0, pClientSock2->read(&buf[0], 1)); // 0 = disconnected
+}
+
+BOOST_AUTO_TEST_CASE(test_stop_with_uninterruptable_clients_connected) {
+ // This tests pre-THRIFT-2441 behavior: stopping the server blocks until clients
+ // disconnect.
+ BOOST_TEST_MESSAGE("Testing stop with uninterruptable clients");
+
+ dynamic_pointer_cast<TServerSocket>(pServer->getServerTransport())
+ ->setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
+
+ startServer();
+
+ shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock1->open();
+
+ shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock2->open();
+
+ // Ensure they have been accepted
+ blockUntilAccepted(2);
+
+ boost::thread t1(std::bind(&TServerIntegrationTestFixture::delayClose,
+ this,
+ pClientSock1,
+ milliseconds(250)));
+ boost::thread t2(std::bind(&TServerIntegrationTestFixture::delayClose,
+ this,
+ pClientSock2,
+ milliseconds(250)));
+
+ // Once the clients disconnect the server will stop
+ stopServer();
+ BOOST_CHECK(pServer->getConcurrentClientCountHWM() > 0);
+ t1.join();
+ t2.join();
+}
+
+BOOST_AUTO_TEST_CASE(test_concurrent_client_limit) {
+ startServer();
+ BOOST_TEST_MESSAGE("Testing the concurrent client limit");
+
+ BOOST_CHECK_EQUAL(INT64_MAX, pServer->getConcurrentClientLimit());
+ pServer->setConcurrentClientLimit(2);
+ BOOST_CHECK_EQUAL(0, pServer->getConcurrentClientCount());
+ BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientLimit());
+
+ shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock1->open();
+ blockUntilAccepted(1);
+ BOOST_CHECK_EQUAL(1, pServer->getConcurrentClientCount());
+
+ shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock2->open();
+ blockUntilAccepted(2);
+ BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount());
+
+ // a third client cannot connect until one of the other two closes
+ boost::thread t2(std::bind(&TServerIntegrationTestFixture::delayClose,
+ this,
+ pClientSock2,
+ milliseconds(250)));
+ shared_ptr<TSocket> pClientSock3(new TSocket("localhost", getServerPort()),
+ autoSocketCloser);
+ pClientSock2->open();
+ blockUntilAccepted(2);
+ BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount());
+ BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCountHWM());
+
+ stopServer();
+ BOOST_CHECK(pServer->getConcurrentClientCountHWM() > 0);
+ t2.join();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TServerSocketTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TServerSocketTest.cpp
new file mode 100644
index 000000000..bec6d4756
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TServerSocketTest.cpp
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TServerSocket.h>
+#include <memory>
+#include "TTransportCheckThrow.h"
+#include <iostream>
+
+using apache::thrift::transport::TServerSocket;
+using apache::thrift::transport::TSocket;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using std::shared_ptr;
+
+BOOST_AUTO_TEST_SUITE(TServerSocketTest)
+
+BOOST_AUTO_TEST_CASE(test_bind_to_address) {
+ TServerSocket sock1("localhost", 0);
+ sock1.listen();
+ int port = sock1.getPort();
+ TSocket clientSock("localhost", port);
+ clientSock.open();
+ shared_ptr<TTransport> accepted = sock1.accept();
+ accepted->close();
+ sock1.close();
+
+ std::cout << "An error message from getaddrinfo on the console is expected:" << std::endl;
+ TServerSocket sock2("257.258.259.260", 0);
+ BOOST_CHECK_THROW(sock2.listen(), TTransportException);
+ sock2.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_listen_valid_port) {
+ TServerSocket sock1(-1);
+ TTRANSPORT_CHECK_THROW(sock1.listen(), TTransportException::BAD_ARGS);
+
+ TServerSocket sock2(65536);
+ TTRANSPORT_CHECK_THROW(sock2.listen(), TTransportException::BAD_ARGS);
+}
+
+BOOST_AUTO_TEST_CASE(test_close_before_listen) {
+ TServerSocket sock1("localhost", 0);
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_get_port) {
+ TServerSocket sock1("localHost", 888);
+ BOOST_CHECK_EQUAL(888, sock1.getPort());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TServerTransportTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TServerTransportTest.cpp
new file mode 100644
index 000000000..18a393ee0
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TServerTransportTest.cpp
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/test/auto_unit_test.hpp>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TServerTransport.h>
+#include <memory>
+
+using apache::thrift::transport::TServerTransport;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using std::shared_ptr;
+
+BOOST_AUTO_TEST_SUITE(TServerTransportTest)
+
+class TestTTransport : public TTransport {};
+
+class TestTServerTransport : public TServerTransport {
+public:
+ TestTServerTransport() : valid_(true) {}
+ void close() override {}
+ bool valid_;
+
+protected:
+ shared_ptr<TTransport> acceptImpl() override {
+ return valid_ ? std::make_shared<TestTTransport>()
+ : shared_ptr<TestTTransport>();
+ }
+};
+
+BOOST_AUTO_TEST_CASE(test_positive_accept) {
+ TestTServerTransport uut;
+ BOOST_CHECK(uut.accept());
+}
+
+BOOST_AUTO_TEST_CASE(test_negative_accept) {
+ TestTServerTransport uut;
+ uut.valid_ = false;
+ BOOST_CHECK_THROW(uut.accept(), TTransportException);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TSocketInterruptTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TSocketInterruptTest.cpp
new file mode 100644
index 000000000..366242f7c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TSocketInterruptTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE TSocketInterruptTest
+#include <boost/test/auto_unit_test.hpp>
+
+#include <boost/chrono/duration.hpp>
+#include <boost/date_time/posix_time/posix_time_duration.hpp>
+#include <boost/thread/thread.hpp>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TServerSocket.h>
+#include <memory>
+
+using apache::thrift::transport::TServerSocket;
+using apache::thrift::transport::TSocket;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using namespace apache::thrift;
+
+BOOST_AUTO_TEST_SUITE(TSocketInterruptTest)
+
+void readerWorker(std::shared_ptr<TTransport> tt, uint32_t expectedResult) {
+ uint8_t buf[4];
+ BOOST_CHECK_EQUAL(expectedResult, tt->read(buf, 4));
+}
+
+void readerWorkerMustThrow(std::shared_ptr<TTransport> tt) {
+ try {
+ uint8_t buf[4];
+ tt->read(buf, 4);
+ BOOST_ERROR("should not have gotten here");
+ } catch (const TTransportException& tx) {
+ BOOST_CHECK_EQUAL(TTransportException::INTERRUPTED, tx.getType());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_interruptable_child_read) {
+ TServerSocket sock1("localhost", 0);
+ sock1.listen();
+ int port = sock1.getPort();
+ TSocket clientSock("localhost", port);
+ clientSock.open();
+ std::shared_ptr<TTransport> accepted = sock1.accept();
+ boost::thread readThread(std::bind(readerWorkerMustThrow, accepted));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // readThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(readThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren did not interrupt child read");
+ clientSock.close();
+ accepted->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_non_interruptable_child_read) {
+ TServerSocket sock1("localhost", 0);
+ sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
+ sock1.listen();
+ int port = sock1.getPort();
+ TSocket clientSock("localhost", port);
+ clientSock.open();
+ std::shared_ptr<TTransport> accepted = sock1.accept();
+ boost::thread readThread(std::bind(readerWorker, accepted, 0));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // readThread is practically guaranteed to be blocking here
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(!readThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren interrupted child read");
+
+ // only way to proceed is to have the client disconnect
+ clientSock.close();
+ readThread.join();
+ accepted->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_cannot_change_after_listen) {
+ TServerSocket sock1("localhost", 0);
+ sock1.listen();
+ BOOST_CHECK_THROW(sock1.setInterruptableChildren(false), std::logic_error);
+ sock1.close();
+}
+
+void peekerWorker(std::shared_ptr<TTransport> tt, bool expectedResult) {
+ BOOST_CHECK_EQUAL(expectedResult, tt->peek());
+}
+
+BOOST_AUTO_TEST_CASE(test_interruptable_child_peek) {
+ TServerSocket sock1("localhost", 0);
+ sock1.listen();
+ int port = sock1.getPort();
+ TSocket clientSock("localhost", port);
+ clientSock.open();
+ std::shared_ptr<TTransport> accepted = sock1.accept();
+ // peek() will return false if child is interrupted
+ boost::thread peekThread(std::bind(peekerWorker, accepted, false));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // peekThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(peekThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren did not interrupt child peek");
+ clientSock.close();
+ accepted->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_CASE(test_non_interruptable_child_peek) {
+ TServerSocket sock1("localhost", 0);
+ sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
+ sock1.listen();
+ int port = sock1.getPort();
+ TSocket clientSock("localhost", port);
+ clientSock.open();
+ std::shared_ptr<TTransport> accepted = sock1.accept();
+ // peek() will return false when remote side is closed
+ boost::thread peekThread(std::bind(peekerWorker, accepted, false));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(50));
+ // peekThread is practically guaranteed to be blocking now
+ sock1.interruptChildren();
+ BOOST_CHECK_MESSAGE(!peekThread.try_join_for(boost::chrono::milliseconds(200)),
+ "server socket interruptChildren interrupted child peek");
+
+ // only way to proceed is to have the client disconnect
+ clientSock.close();
+ peekThread.join();
+ accepted->close();
+ sock1.close();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TTransportCheckThrow.h b/src/jaegertracing/thrift/lib/cpp/test/TTransportCheckThrow.h
new file mode 100644
index 000000000..92277b480
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TTransportCheckThrow.h
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#define TTRANSPORT_CHECK_THROW(_CALL, _TYPE) \
+ { \
+ bool caught = false; \
+ try { \
+ (_CALL); \
+ } catch (TTransportException & ex) { \
+ BOOST_CHECK_EQUAL(ex.getType(), _TYPE); \
+ caught = true; \
+ } \
+ BOOST_CHECK_MESSAGE(caught, "expected TTransportException but nothing was thrown"); \
+ }
+
+#define TTRANSPORT_REQUIRE_THROW(_CALL, _TYPE) \
+ { \
+ bool caught = false; \
+ try { \
+ (_CALL); \
+ } catch (TTransportException & ex) { \
+ BOOST_REQUIRE_EQUAL(ex.getType(), _TYPE); \
+ caught = true; \
+ } \
+ BOOST_REQUIRE_MESSAGE(caught, "expected TTransportException but nothing was thrown"); \
+ }
diff --git a/src/jaegertracing/thrift/lib/cpp/test/ThriftTest_extras.cpp b/src/jaegertracing/thrift/lib/cpp/test/ThriftTest_extras.cpp
new file mode 100644
index 000000000..af5606efb
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/ThriftTest_extras.cpp
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Extra functions required for ThriftTest_types to work
+
+#include <thrift/protocol/TDebugProtocol.h>
+#include "gen-cpp/ThriftTest_types.h"
+
+namespace thrift {
+namespace test {
+
+bool Insanity::operator<(thrift::test::Insanity const& other) const {
+ using apache::thrift::ThriftDebugString;
+ return ThriftDebugString(*this) < ThriftDebugString(other);
+}
+}
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/ToStringTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/ToStringTest.cpp
new file mode 100644
index 000000000..d204cb346
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/ToStringTest.cpp
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <vector>
+#include <map>
+
+#include <boost/test/auto_unit_test.hpp>
+
+#include <thrift/TToString.h>
+
+#include "gen-cpp/ThriftTest_types.h"
+#include "gen-cpp/OptionalRequiredTest_types.h"
+#include "gen-cpp/DebugProtoTest_types.h"
+
+using apache::thrift::to_string;
+
+BOOST_AUTO_TEST_SUITE(ToStringTest)
+
+BOOST_AUTO_TEST_CASE(base_types_to_string) {
+ BOOST_CHECK_EQUAL(to_string(10), "10");
+ BOOST_CHECK_EQUAL(to_string(true), "1");
+ BOOST_CHECK_EQUAL(to_string('a'), "a");
+ BOOST_CHECK_EQUAL(to_string(1.2), "1.2");
+ BOOST_CHECK_EQUAL(to_string("abc"), "abc");
+}
+
+BOOST_AUTO_TEST_CASE(empty_vector_to_string) {
+ std::vector<int> l;
+ BOOST_CHECK_EQUAL(to_string(l), "[]");
+}
+
+BOOST_AUTO_TEST_CASE(single_item_vector_to_string) {
+ std::vector<int> l;
+ l.push_back(100);
+ BOOST_CHECK_EQUAL(to_string(l), "[100]");
+}
+
+BOOST_AUTO_TEST_CASE(multiple_item_vector_to_string) {
+ std::vector<int> l;
+ l.push_back(100);
+ l.push_back(150);
+ BOOST_CHECK_EQUAL(to_string(l), "[100, 150]");
+}
+
+BOOST_AUTO_TEST_CASE(empty_map_to_string) {
+ std::map<int, std::string> m;
+ BOOST_CHECK_EQUAL(to_string(m), "{}");
+}
+
+BOOST_AUTO_TEST_CASE(single_item_map_to_string) {
+ std::map<int, std::string> m;
+ m[12] = "abc";
+ BOOST_CHECK_EQUAL(to_string(m), "{12: abc}");
+}
+
+BOOST_AUTO_TEST_CASE(multi_item_map_to_string) {
+ std::map<int, std::string> m;
+ m[12] = "abc";
+ m[31] = "xyz";
+ BOOST_CHECK_EQUAL(to_string(m), "{12: abc, 31: xyz}");
+}
+
+BOOST_AUTO_TEST_CASE(empty_set_to_string) {
+ std::set<char> s;
+ BOOST_CHECK_EQUAL(to_string(s), "{}");
+}
+
+BOOST_AUTO_TEST_CASE(single_item_set_to_string) {
+ std::set<char> s;
+ s.insert('c');
+ BOOST_CHECK_EQUAL(to_string(s), "{c}");
+}
+
+BOOST_AUTO_TEST_CASE(multi_item_set_to_string) {
+ std::set<char> s;
+ s.insert('a');
+ s.insert('z');
+ BOOST_CHECK_EQUAL(to_string(s), "{a, z}");
+}
+
+BOOST_AUTO_TEST_CASE(generated_empty_object_to_string) {
+ thrift::test::EmptyStruct e;
+ BOOST_CHECK_EQUAL(to_string(e), "EmptyStruct()");
+}
+
+BOOST_AUTO_TEST_CASE(generated_single_basic_field_object_to_string) {
+ thrift::test::StructA a;
+ a.__set_s("abcd");
+ BOOST_CHECK_EQUAL(to_string(a), "StructA(s=abcd)");
+}
+
+BOOST_AUTO_TEST_CASE(generated_two_basic_fields_object_to_string) {
+ thrift::test::Bonk a;
+ a.__set_message("abcd");
+ a.__set_type(1234);
+ BOOST_CHECK_EQUAL(to_string(a), "Bonk(message=abcd, type=1234)");
+}
+
+BOOST_AUTO_TEST_CASE(generated_optional_fields_object_to_string) {
+ thrift::test::Tricky2 a;
+ BOOST_CHECK_EQUAL(to_string(a), "Tricky2(im_optional=<null>)");
+ a.__set_im_optional(123);
+ BOOST_CHECK_EQUAL(to_string(a), "Tricky2(im_optional=123)");
+}
+
+BOOST_AUTO_TEST_CASE(generated_nested_object_to_string) {
+ thrift::test::OneField a;
+ BOOST_CHECK_EQUAL(to_string(a), "OneField(field=EmptyStruct())");
+}
+
+BOOST_AUTO_TEST_CASE(generated_nested_list_object_to_string) {
+ thrift::test::ListBonks l;
+ l.bonk.assign(2, thrift::test::Bonk());
+ l.bonk[0].__set_message("a");
+ l.bonk[1].__set_message("b");
+
+ BOOST_CHECK_EQUAL(to_string(l),
+ "ListBonks(bonk=[Bonk(message=a, type=0), Bonk(message=b, type=0)])");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TransportTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TransportTest.cpp
new file mode 100644
index 000000000..a890aa8ce
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TransportTest.cpp
@@ -0,0 +1,1089 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+
+#include <stdlib.h>
+#include <time.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <sstream>
+#include <fstream>
+
+#include <boost/mpl/list.hpp>
+#include <boost/shared_array.hpp>
+#include <boost/random.hpp>
+#include <boost/type_traits.hpp>
+#include <boost/test/unit_test.hpp>
+#include <boost/version.hpp>
+
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TFDTransport.h>
+#include <thrift/transport/TFileTransport.h>
+#include <thrift/transport/TZlibTransport.h>
+#include <thrift/transport/TSocket.h>
+
+#include <thrift/concurrency/FunctionRunner.h>
+#if _WIN32
+#include <thrift/transport/TPipe.h>
+#include <thrift/windows/TWinsockSingleton.h>
+#endif
+
+using namespace apache::thrift::transport;
+using namespace apache::thrift;
+
+static boost::mt19937 rng;
+
+void initrand(unsigned int seed) {
+ rng.seed(seed);
+}
+
+class SizeGenerator {
+public:
+ virtual ~SizeGenerator() = default;
+ virtual uint32_t nextSize() = 0;
+ virtual std::string describe() const = 0;
+};
+
+class ConstantSizeGenerator : public SizeGenerator {
+public:
+ ConstantSizeGenerator(uint32_t value) : value_(value) {}
+ uint32_t nextSize() override { return value_; }
+ std::string describe() const override {
+ std::ostringstream desc;
+ desc << value_;
+ return desc.str();
+ }
+
+private:
+ uint32_t value_;
+};
+
+class RandomSizeGenerator : public SizeGenerator {
+public:
+ RandomSizeGenerator(uint32_t min, uint32_t max)
+ : generator_(rng, boost::uniform_int<int>(min, max)) {}
+
+ uint32_t nextSize() override { return generator_(); }
+
+ std::string describe() const override {
+ std::ostringstream desc;
+ desc << "rand(" << getMin() << ", " << getMax() << ")";
+ return desc.str();
+ }
+
+ uint32_t getMin() const { return (generator_.distribution().min)(); }
+ uint32_t getMax() const { return (generator_.distribution().max)(); }
+
+private:
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<int> > generator_;
+};
+
+/**
+ * This class exists solely to make the TEST_RW() macro easier to use.
+ * - it can be constructed implicitly from an integer
+ * - it can contain either a ConstantSizeGenerator or a RandomSizeGenerator
+ * (TEST_RW can't take a SizeGenerator pointer or reference, since it needs
+ * to make a copy of the generator to bind it to the test function.)
+ */
+class GenericSizeGenerator : public SizeGenerator {
+public:
+ GenericSizeGenerator(uint32_t value) : generator_(new ConstantSizeGenerator(value)) {}
+ GenericSizeGenerator(uint32_t min, uint32_t max)
+ : generator_(new RandomSizeGenerator(min, max)) {}
+
+ uint32_t nextSize() override { return generator_->nextSize(); }
+ std::string describe() const override { return generator_->describe(); }
+
+private:
+ std::shared_ptr<SizeGenerator> generator_;
+};
+
+/**************************************************************************
+ * Classes to set up coupled transports
+ **************************************************************************/
+
+/**
+ * Helper class to represent a coupled pair of transports.
+ *
+ * Data written to the out transport can be read from the in transport.
+ *
+ * This is used as the base class for the various coupled transport
+ * implementations. It shouldn't be instantiated directly.
+ */
+template <class Transport_>
+class CoupledTransports {
+public:
+ virtual ~CoupledTransports() = default;
+ typedef Transport_ TransportType;
+
+ CoupledTransports() : in(), out() {}
+
+ std::shared_ptr<Transport_> in;
+ std::shared_ptr<Transport_> out;
+
+private:
+ CoupledTransports(const CoupledTransports&) = delete;
+ CoupledTransports& operator=(const CoupledTransports&) = delete;
+};
+
+/**
+ * Coupled TMemoryBuffers
+ */
+class CoupledMemoryBuffers : public CoupledTransports<TMemoryBuffer> {
+public:
+ CoupledMemoryBuffers() : buf(new TMemoryBuffer) {
+ in = buf;
+ out = buf;
+ }
+
+ std::shared_ptr<TMemoryBuffer> buf;
+};
+
+/**
+ * Helper template class for creating coupled transports that wrap
+ * another transport.
+ */
+template <class WrapperTransport_, class InnerCoupledTransports_>
+class CoupledWrapperTransportsT : public CoupledTransports<WrapperTransport_> {
+public:
+ CoupledWrapperTransportsT() {
+ if (inner_.in) {
+ this->in.reset(new WrapperTransport_(inner_.in));
+ }
+ if (inner_.out) {
+ this->out.reset(new WrapperTransport_(inner_.out));
+ }
+ }
+
+ InnerCoupledTransports_ inner_;
+};
+
+/**
+ * Coupled TBufferedTransports.
+ */
+template <class InnerTransport_>
+class CoupledBufferedTransportsT
+ : public CoupledWrapperTransportsT<TBufferedTransport, InnerTransport_> {};
+
+typedef CoupledBufferedTransportsT<CoupledMemoryBuffers> CoupledBufferedTransports;
+
+/**
+ * Coupled TFramedTransports.
+ */
+template <class InnerTransport_>
+class CoupledFramedTransportsT
+ : public CoupledWrapperTransportsT<TFramedTransport, InnerTransport_> {};
+
+typedef CoupledFramedTransportsT<CoupledMemoryBuffers> CoupledFramedTransports;
+
+/**
+ * Coupled TZlibTransports.
+ */
+template <class InnerTransport_>
+class CoupledZlibTransportsT : public CoupledWrapperTransportsT<TZlibTransport, InnerTransport_> {};
+
+typedef CoupledZlibTransportsT<CoupledMemoryBuffers> CoupledZlibTransports;
+
+#ifndef _WIN32
+// FD transport doesn't make much sense on Windows.
+/**
+ * Coupled TFDTransports.
+ */
+class CoupledFDTransports : public CoupledTransports<TFDTransport> {
+public:
+ CoupledFDTransports() {
+ int pipes[2];
+
+ if (pipe(pipes) != 0) {
+ return;
+ }
+
+ in.reset(new TFDTransport(pipes[0], TFDTransport::CLOSE_ON_DESTROY));
+ out.reset(new TFDTransport(pipes[1], TFDTransport::CLOSE_ON_DESTROY));
+ }
+};
+#else
+/**
+ * Coupled pipe transports
+ */
+class CoupledPipeTransports : public CoupledTransports<TPipe> {
+public:
+ HANDLE hRead;
+ HANDLE hWrite;
+
+ CoupledPipeTransports() {
+ BOOST_REQUIRE(CreatePipe(&hRead, &hWrite, NULL, 1048576 * 2));
+ in.reset(new TPipe(hRead, hWrite));
+ in->open();
+ out = in;
+ }
+};
+#endif
+
+/**
+ * Coupled TSockets
+ */
+class CoupledSocketTransports : public CoupledTransports<TSocket> {
+public:
+ CoupledSocketTransports() {
+ THRIFT_SOCKET sockets[2] = {0};
+ if (THRIFT_SOCKETPAIR(PF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
+ return;
+ }
+
+ in.reset(new TSocket(sockets[0]));
+ out.reset(new TSocket(sockets[1]));
+ out->setSendTimeout(100);
+ }
+};
+
+// These could be made to work on Windows, but I don't care enough to make it happen
+#ifndef _WIN32
+/**
+ * Coupled TFileTransports
+ */
+class CoupledFileTransports : public CoupledTransports<TFileTransport> {
+public:
+ CoupledFileTransports() {
+#ifndef _WIN32
+ const char* tmp_dir = "/tmp";
+#define FILENAME_SUFFIX "/thrift.transport_test"
+#else
+ const char* tmp_dir = getenv("TMP");
+#define FILENAME_SUFFIX "\\thrift.transport_test"
+#endif
+
+ // Create a temporary file to use
+ filename.resize(strlen(tmp_dir) + strlen(FILENAME_SUFFIX));
+ THRIFT_SNPRINTF(&filename[0], filename.size(), "%s" FILENAME_SUFFIX, tmp_dir);
+#undef FILENAME_SUFFIX
+
+ { std::ofstream dummy_creation(filename.c_str(), std::ofstream::trunc); }
+
+ in.reset(new TFileTransport(filename, true));
+ out.reset(new TFileTransport(filename));
+ }
+
+ ~CoupledFileTransports() override { remove(filename.c_str()); }
+
+ std::string filename;
+};
+#endif
+
+/**
+ * Wrapper around another CoupledTransports implementation that exposes the
+ * transports as TTransport pointers.
+ *
+ * This is used since accessing a transport via a "TTransport*" exercises a
+ * different code path than using the base pointer class. As part of the
+ * template code changes, most transport methods are no longer virtual.
+ */
+template <class CoupledTransports_>
+class CoupledTTransports : public CoupledTransports<TTransport> {
+public:
+ CoupledTTransports() : transports() {
+ in = transports.in;
+ out = transports.out;
+ }
+
+ CoupledTransports_ transports;
+};
+
+/**
+ * Wrapper around another CoupledTransports implementation that exposes the
+ * transports as TBufferBase pointers.
+ *
+ * This can only be instantiated with a transport type that is a subclass of
+ * TBufferBase.
+ */
+template <class CoupledTransports_>
+class CoupledBufferBases : public CoupledTransports<TBufferBase> {
+public:
+ CoupledBufferBases() : transports() {
+ in = transports.in;
+ out = transports.out;
+ }
+
+ CoupledTransports_ transports;
+};
+
+/**************************************************************************
+ * Alarm handling code for use in tests that check the transport blocking
+ * semantics.
+ *
+ * If the transport ends up blocking, we don't want to hang forever. We use
+ * SIGALRM to fire schedule signal to wake up and try to write data so the
+ * transport will unblock.
+ *
+ * It isn't really the safest thing in the world to be mucking around with
+ * complicated global data structures in a signal handler. It should probably
+ * be okay though, since we know the main thread should always be blocked in a
+ * read() request when the signal handler is running.
+ **************************************************************************/
+
+struct TriggerInfo {
+ TriggerInfo(int seconds, const std::shared_ptr<TTransport>& transport, uint32_t writeLength)
+ : timeoutSeconds(seconds), transport(transport), writeLength(writeLength), next(nullptr) {}
+
+ int timeoutSeconds;
+ std::shared_ptr<TTransport> transport;
+ uint32_t writeLength;
+ TriggerInfo* next;
+};
+
+apache::thrift::concurrency::Monitor g_alarm_monitor;
+TriggerInfo* g_triggerInfo;
+unsigned int g_numTriggersFired;
+bool g_teardown = false;
+
+void alarm_handler() {
+ TriggerInfo* info = nullptr;
+ {
+ apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
+ // The alarm timed out, which almost certainly means we're stuck
+ // on a transport that is incorrectly blocked.
+ ++g_numTriggersFired;
+
+ // Note: we print messages to stdout instead of stderr, since
+ // tools/test/runner only records stdout messages in the failure messages for
+ // boost tests. (boost prints its test info to stdout.)
+ printf("Timeout alarm expired; attempting to unblock transport\n");
+ if (g_triggerInfo == nullptr) {
+ printf(" trigger stack is empty!\n");
+ }
+
+ // Pop off the first TriggerInfo.
+ // If there is another one, schedule an alarm for it.
+ info = g_triggerInfo;
+ g_triggerInfo = info->next;
+ }
+
+ // Write some data to the transport to hopefully unblock it.
+ auto* buf = new uint8_t[info->writeLength];
+ memset(buf, 'b', info->writeLength);
+ boost::scoped_array<uint8_t> array(buf);
+ info->transport->write(buf, info->writeLength);
+ info->transport->flush();
+
+ delete info;
+}
+
+void alarm_handler_wrapper() {
+ int64_t timeout = 0; // timeout of 0 means wait forever
+ while (true) {
+ bool fireHandler = false;
+ {
+ apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
+ if (g_teardown)
+ return;
+ // calculate timeout
+ if (g_triggerInfo == nullptr) {
+ timeout = 0;
+ } else {
+ timeout = g_triggerInfo->timeoutSeconds * 1000;
+ }
+
+ int waitResult = g_alarm_monitor.waitForTimeRelative(timeout);
+ if (waitResult == THRIFT_ETIMEDOUT)
+ fireHandler = true;
+ }
+ if (fireHandler)
+ alarm_handler(); // calling outside the lock
+ }
+}
+
+/**
+ * Add a trigger to be scheduled "seconds" seconds after the
+ * last currently scheduled trigger.
+ *
+ * (Note that this is not "seconds" from now. That might be more logical, but
+ * would require slightly more complicated sorting, rather than just appending
+ * to the end.)
+ */
+void add_trigger(unsigned int seconds,
+ const std::shared_ptr<TTransport>& transport,
+ uint32_t write_len) {
+ auto* info = new TriggerInfo(seconds, transport, write_len);
+ {
+ apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
+ if (g_triggerInfo == nullptr) {
+ // This is the first trigger.
+ // Set g_triggerInfo, and schedule the alarm
+ g_triggerInfo = info;
+ g_alarm_monitor.notify();
+ } else {
+ // Add this trigger to the end of the list
+ TriggerInfo* prev = g_triggerInfo;
+ while (prev->next) {
+ prev = prev->next;
+ }
+ prev->next = info;
+ }
+ }
+}
+
+void clear_triggers() {
+ TriggerInfo* info = nullptr;
+
+ {
+ apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
+ info = g_triggerInfo;
+ g_triggerInfo = nullptr;
+ g_numTriggersFired = 0;
+ g_alarm_monitor.notify();
+ }
+
+ while (info != nullptr) {
+ TriggerInfo* next = info->next;
+ delete info;
+ info = next;
+ }
+}
+
+void set_trigger(unsigned int seconds,
+ const std::shared_ptr<TTransport>& transport,
+ uint32_t write_len) {
+ clear_triggers();
+ add_trigger(seconds, transport, write_len);
+}
+
+/**************************************************************************
+ * Test functions
+ **************************************************************************/
+
+/**
+ * Test interleaved write and read calls.
+ *
+ * Generates a buffer totalSize bytes long, then writes it to the transport,
+ * and verifies the written data can be read back correctly.
+ *
+ * Mode of operation:
+ * - call wChunkGenerator to figure out how large of a chunk to write
+ * - call wSizeGenerator to get the size for individual write() calls,
+ * and do this repeatedly until the entire chunk is written.
+ * - call rChunkGenerator to figure out how large of a chunk to read
+ * - call rSizeGenerator to get the size for individual read() calls,
+ * and do this repeatedly until the entire chunk is read.
+ * - repeat until the full buffer is written and read back,
+ * then compare the data read back against the original buffer
+ *
+ *
+ * - If any of the size generators return 0, this means to use the maximum
+ * possible size.
+ *
+ * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
+ * there are never more than maxOutstanding bytes waiting to be read back.
+ */
+template <class CoupledTransports>
+void test_rw(uint32_t totalSize,
+ SizeGenerator& wSizeGenerator,
+ SizeGenerator& rSizeGenerator,
+ SizeGenerator& wChunkGenerator,
+ SizeGenerator& rChunkGenerator,
+ uint32_t maxOutstanding) {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ boost::shared_array<uint8_t> wbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
+ boost::shared_array<uint8_t> rbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
+
+ // store some data in wbuf
+ for (uint32_t n = 0; n < totalSize; ++n) {
+ wbuf[n] = (n & 0xff);
+ }
+ // clear rbuf
+ memset(rbuf.get(), 0, totalSize);
+
+ uint32_t total_written = 0;
+ uint32_t total_read = 0;
+ while (total_read < totalSize) {
+ // Determine how large a chunk of data to write
+ uint32_t wchunk_size = wChunkGenerator.nextSize();
+ if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
+ wchunk_size = totalSize - total_written;
+ }
+
+ // Make sure (total_written - total_read) + wchunk_size
+ // is less than maxOutstanding
+ if (maxOutstanding > 0 && wchunk_size > maxOutstanding - (total_written - total_read)) {
+ wchunk_size = maxOutstanding - (total_written - total_read);
+ }
+
+ // Write the chunk
+ uint32_t chunk_written = 0;
+ while (chunk_written < wchunk_size) {
+ uint32_t write_size = wSizeGenerator.nextSize();
+ if (write_size == 0 || write_size > wchunk_size - chunk_written) {
+ write_size = wchunk_size - chunk_written;
+ }
+
+ try {
+ transports.out->write(wbuf.get() + total_written, write_size);
+ } catch (TTransportException& te) {
+ if (te.getType() == TTransportException::TIMED_OUT)
+ break;
+ throw te;
+ }
+ chunk_written += write_size;
+ total_written += write_size;
+ }
+
+ // Flush the data, so it will be available in the read transport
+ // Don't flush if wchunk_size is 0. (This should only happen if
+ // total_written == totalSize already, and we're only reading now.)
+ if (wchunk_size > 0) {
+ transports.out->flush();
+ }
+
+ // Determine how large a chunk of data to read back
+ uint32_t rchunk_size = rChunkGenerator.nextSize();
+ if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
+ rchunk_size = total_written - total_read;
+ }
+
+ // Read the chunk
+ uint32_t chunk_read = 0;
+ while (chunk_read < rchunk_size) {
+ uint32_t read_size = rSizeGenerator.nextSize();
+ if (read_size == 0 || read_size > rchunk_size - chunk_read) {
+ read_size = rchunk_size - chunk_read;
+ }
+
+ int bytes_read = -1;
+ try {
+ bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
+ } catch (TTransportException& e) {
+ BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size << ") threw exception \""
+ << e.what() << "\"; written so far: " << total_written << " / "
+ << totalSize << " bytes");
+ }
+
+ BOOST_REQUIRE_MESSAGE(bytes_read > 0,
+ "read(pos=" << total_read << ", size=" << read_size << ") returned "
+ << bytes_read << "; written so far: " << total_written
+ << " / " << totalSize << " bytes");
+ chunk_read += bytes_read;
+ total_read += bytes_read;
+ }
+ }
+
+ // make sure the data read back is identical to the data written
+ BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
+}
+
+template <class CoupledTransports>
+void test_read_part_available() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t write_buf[16];
+ uint8_t read_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Attemping to read 10 bytes when only 9 are available should return 9
+ // immediately.
+ transports.out->write(write_buf, 9);
+ transports.out->flush();
+ set_trigger(3, transports.out, 1);
+ uint32_t bytes_read = transports.in->read(read_buf, 10);
+ BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
+ BOOST_CHECK_EQUAL(bytes_read, (uint32_t)9);
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
+void test_read_part_available_in_chunks() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t write_buf[16];
+ uint8_t read_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Write 10 bytes (in a single frame, for transports that use framing)
+ transports.out->write(write_buf, 10);
+ transports.out->flush();
+
+ // Read 1 byte, to force the transport to read the frame
+ uint32_t bytes_read = transports.in->read(read_buf, 1);
+ BOOST_CHECK_EQUAL(bytes_read, 1u);
+
+ // Read more than what is remaining and verify the transport does not block
+ set_trigger(3, transports.out, 1);
+ bytes_read = transports.in->read(read_buf, 10);
+ BOOST_CHECK_EQUAL(g_numTriggersFired, 0u);
+ BOOST_CHECK_EQUAL(bytes_read, 9u);
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
+void test_read_partial_midframe() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t write_buf[16];
+ uint8_t read_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Attempt to read 10 bytes, when only 9 are available, but after we have
+ // already read part of the data that is available. This exercises a
+ // different code path for several of the transports.
+ //
+ // For transports that add their own framing (e.g., TFramedTransport and
+ // TFileTransport), the two flush calls break up the data in to a 10 byte
+ // frame and a 3 byte frame. The first read then puts us partway through the
+ // first frame, and then we attempt to read past the end of that frame, and
+ // through the next frame, too.
+ //
+ // For buffered transports that perform read-ahead (e.g.,
+ // TBufferedTransport), the read-ahead will most likely see all 13 bytes
+ // written on the first read. The next read will then attempt to read past
+ // the end of the read-ahead buffer.
+ //
+ // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
+ // transports that track framing internally.
+ transports.out->write(write_buf, 10);
+ transports.out->flush();
+ transports.out->write(write_buf, 3);
+ transports.out->flush();
+
+ // Now read 4 bytes, so that we are partway through the written data.
+ uint32_t bytes_read = transports.in->read(read_buf, 4);
+ BOOST_CHECK_EQUAL(bytes_read, (uint32_t)4);
+
+ // Now attempt to read 10 bytes. Only 9 more are available.
+ //
+ // We should be able to get all 9 bytes, but it might take multiple read
+ // calls, since it is valid for read() to return fewer bytes than requested.
+ // (Most transports do immediately return 9 bytes, but the framing transports
+ // tend to only return to the end of the current frame, which is 6 bytes in
+ // this case.)
+ uint32_t total_read = 0;
+ while (total_read < 9) {
+ set_trigger(3, transports.out, 1);
+ bytes_read = transports.in->read(read_buf, 10);
+ BOOST_REQUIRE_EQUAL(g_numTriggersFired, (unsigned int)0);
+ BOOST_REQUIRE_GT(bytes_read, (uint32_t)0);
+ total_read += bytes_read;
+ BOOST_REQUIRE_LE(total_read, (uint32_t)9);
+ }
+
+ BOOST_CHECK_EQUAL(total_read, (uint32_t)9);
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
+void test_borrow_part_available() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t write_buf[16];
+ uint8_t read_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Attemping to borrow 10 bytes when only 9 are available should return NULL
+ // immediately.
+ transports.out->write(write_buf, 9);
+ transports.out->flush();
+ set_trigger(3, transports.out, 1);
+ uint32_t borrow_len = 10;
+ const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
+ BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
+ BOOST_CHECK(borrowed_buf == nullptr);
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
+void test_read_none_available() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t read_buf[16];
+
+ // Attempting to read when no data is available should either block until
+ // some data is available, or fail immediately. (e.g., TSocket blocks,
+ // TMemoryBuffer just fails.)
+ //
+ // If the transport blocks, it should succeed once some data is available,
+ // even if less than the amount requested becomes available.
+ set_trigger(1, transports.out, 2);
+ add_trigger(1, transports.out, 8);
+ uint32_t bytes_read = transports.in->read(read_buf, 10);
+ if (bytes_read == 0) {
+ BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
+ clear_triggers();
+ } else {
+ BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)1);
+ BOOST_CHECK_EQUAL(bytes_read, (uint32_t)2);
+ }
+
+ clear_triggers();
+}
+
+template <class CoupledTransports>
+void test_borrow_none_available() {
+ CoupledTransports transports;
+ BOOST_REQUIRE(transports.in != nullptr);
+ BOOST_REQUIRE(transports.out != nullptr);
+
+ uint8_t write_buf[16];
+ memset(write_buf, 'a', sizeof(write_buf));
+
+ // Attempting to borrow when no data is available should fail immediately
+ set_trigger(1, transports.out, 10);
+ uint32_t borrow_len = 10;
+ const uint8_t* borrowed_buf = transports.in->borrow(nullptr, &borrow_len);
+ BOOST_CHECK(borrowed_buf == nullptr);
+ BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
+
+ clear_triggers();
+}
+
+/**************************************************************************
+ * Test case generation
+ *
+ * Pretty ugly and annoying. This would be much easier if we the unit test
+ * framework didn't force each test to be a separate function.
+ * - Writing a completely separate function definition for each of these would
+ * result in a lot of repetitive boilerplate code.
+ * - Combining many tests into a single function makes it more difficult to
+ * tell precisely which tests failed. It also means you can't get a progress
+ * update after each test, and the tests are already fairly slow.
+ * - Similar registration could be achieved with BOOST_TEST_CASE_TEMPLATE,
+ * but it requires a lot of awkward MPL code, and results in useless test
+ * case names. (The names are generated from std::type_info::name(), which
+ * is compiler-dependent. gcc returns mangled names.)
+ **************************************************************************/
+
+#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
+ addTestRW<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports), totalSize, ##__VA_ARGS__);
+
+#define TEST_RW(CoupledTransports, totalSize, ...) \
+ do { \
+ /* Add the test as specified, to test the non-virtual function calls */ \
+ ADD_TEST_RW(CoupledTransports, totalSize, ##__VA_ARGS__); \
+ /* \
+ * Also test using the transport as a TTransport*, to test \
+ * the read_virt()/write_virt() calls \
+ */ \
+ ADD_TEST_RW(CoupledTTransports<CoupledTransports>, totalSize, ##__VA_ARGS__); \
+ /* Test wrapping the transport with TBufferedTransport */ \
+ ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
+ /* Test wrapping the transport with TFramedTransports */ \
+ ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
+ /* Test wrapping the transport with TZlibTransport */ \
+ ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
+ } while (0)
+
+#define ADD_TEST_BLOCKING(CoupledTransports) \
+ addTestBlocking<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports));
+
+#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
+ ADD_TEST_BLOCKING(CoupledTransports); \
+ ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
+ ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
+ ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
+ ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
+
+class TransportTestGen {
+public:
+ TransportTestGen(boost::unit_test::test_suite* suite, float sizeMultiplier)
+ : suite_(suite), sizeMultiplier_(sizeMultiplier) {}
+
+ void generate() {
+ GenericSizeGenerator rand4k(1, 4096);
+
+ /*
+ * We do the basically the same set of tests for each transport type,
+ * although we tweak the parameters in some places.
+ */
+
+ // TMemoryBuffer tests
+ TEST_RW(CoupledMemoryBuffers, 1024 * 1024, 0, 0);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1);
+
+ TEST_RW(CoupledMemoryBuffers, 1024 * 256, 0, 0, rand4k, rand4k);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163, rand4k, rand4k);
+ TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1, rand4k, rand4k);
+
+ TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
+
+#ifndef _WIN32
+ // TFDTransport tests
+ // Since CoupledFDTransports tests with a pipe, writes will block
+ // if there is too much outstanding unread data in the pipe.
+ uint32_t fd_max_outstanding = 4096;
+ TEST_RW(CoupledFDTransports, 1024 * 1024, 0, 0, 0, 0, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, 0, 0, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, 0, 0, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, 0, 0, fd_max_outstanding);
+
+ TEST_RW(CoupledFDTransports, 1024 * 256, 0, 0, rand4k, rand4k, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, rand4k, rand4k, fd_max_outstanding);
+ TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, rand4k, rand4k, fd_max_outstanding);
+
+ TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
+#else
+ // TPipe tests (WIN32 only)
+ TEST_RW(CoupledPipeTransports, 1024 * 1024, 0, 0);
+ TEST_RW(CoupledPipeTransports, 1024 * 256, rand4k, rand4k);
+ TEST_RW(CoupledPipeTransports, 1024 * 256, 167, 163);
+ TEST_RW(CoupledPipeTransports, 1024 * 16, 1, 1);
+
+ TEST_RW(CoupledPipeTransports, 1024 * 256, 0, 0, rand4k, rand4k);
+ TEST_RW(CoupledPipeTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
+ TEST_RW(CoupledPipeTransports, 1024 * 256, 167, 163, rand4k, rand4k);
+ TEST_RW(CoupledPipeTransports, 1024 * 16, 1, 1, rand4k, rand4k);
+
+ TEST_BLOCKING_BEHAVIOR(CoupledPipeTransports);
+#endif //_WIN32
+
+ // TSocket tests
+ uint32_t socket_max_outstanding = 4096;
+ TEST_RW(CoupledSocketTransports, 1024 * 1024, 0, 0, 0, 0, socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports, 1024 * 256, rand4k, rand4k, 0, 0, socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, 0, 0, socket_max_outstanding);
+ // Doh. Apparently writing to a socket has some additional overhead for
+ // each send() call. If we have more than ~400 outstanding 1-byte write
+ // requests, additional send() calls start blocking.
+ TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, 0, 0, socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports, 1024 * 256, 0, 0, rand4k, rand4k, socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports,
+ 1024 * 256,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k,
+ socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, rand4k, rand4k, socket_max_outstanding);
+ TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, rand4k, rand4k, socket_max_outstanding);
+
+ TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
+
+// These could be made to work on Windows, but I don't care enough to make it happen
+#ifndef _WIN32
+ // TFileTransport tests
+ // We use smaller buffer sizes here, since TFileTransport is fairly slow.
+ //
+ // TFileTransport can't write more than 16MB at once
+ uint32_t max_write_at_once = 1024 * 1024 * 16 - 4;
+ TEST_RW(CoupledFileTransports, 1024 * 1024, max_write_at_once, 0);
+ TEST_RW(CoupledFileTransports, 1024 * 128, rand4k, rand4k);
+ TEST_RW(CoupledFileTransports, 1024 * 128, 167, 163);
+ TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1);
+
+ TEST_RW(CoupledFileTransports, 1024 * 64, 0, 0, rand4k, rand4k);
+ TEST_RW(CoupledFileTransports, 1024 * 64, rand4k, rand4k, rand4k, rand4k);
+ TEST_RW(CoupledFileTransports, 1024 * 64, 167, 163, rand4k, rand4k);
+ TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1, rand4k, rand4k);
+
+ TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
+#endif
+
+ // Add some tests that access TBufferedTransport and TFramedTransport
+ // via TTransport pointers and TBufferBase pointers.
+ ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
+ 1024 * 1024,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k);
+ ADD_TEST_RW(CoupledBufferBases<CoupledBufferedTransports>,
+ 1024 * 1024,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k);
+ ADD_TEST_RW(CoupledTTransports<CoupledFramedTransports>,
+ 1024 * 1024,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k);
+ ADD_TEST_RW(CoupledBufferBases<CoupledFramedTransports>,
+ 1024 * 1024,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k);
+
+ // Test using TZlibTransport via a TTransport pointer
+ ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
+ 1024 * 1024,
+ rand4k,
+ rand4k,
+ rand4k,
+ rand4k);
+ }
+
+#if (BOOST_VERSION >= 105900)
+#define MAKE_TEST_CASE(_FUNC, _NAME) boost::unit_test::make_test_case(_FUNC, _NAME, __FILE__, __LINE__)
+#else
+#define MAKE_TEST_CASE(_FUNC, _NAME) boost::unit_test::make_test_case(_FUNC, _NAME)
+#endif
+
+private:
+ template <class CoupledTransports>
+ void addTestRW(const char* transport_name,
+ uint32_t totalSize,
+ GenericSizeGenerator wSizeGen,
+ GenericSizeGenerator rSizeGen,
+ GenericSizeGenerator wChunkSizeGen = 0,
+ GenericSizeGenerator rChunkSizeGen = 0,
+ uint32_t maxOutstanding = 0,
+ uint32_t expectedFailures = 0) {
+ // adjust totalSize by the specified sizeMultiplier_ first
+ totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
+
+ std::ostringstream name;
+ name << transport_name << "::test_rw(" << totalSize << ", " << wSizeGen.describe() << ", "
+ << rSizeGen.describe() << ", " << wChunkSizeGen.describe() << ", "
+ << rChunkSizeGen.describe() << ", " << maxOutstanding << ")";
+
+#if (BOOST_VERSION >= 105900)
+ std::function<void ()> test_func
+#else
+ boost::unit_test::callback0<> test_func
+#endif
+ = std::bind(test_rw<CoupledTransports>,
+ totalSize,
+ wSizeGen,
+ rSizeGen,
+ wChunkSizeGen,
+ rChunkSizeGen,
+ maxOutstanding);
+ suite_->add(MAKE_TEST_CASE(test_func, name.str()), expectedFailures);
+ }
+
+ template <class CoupledTransports>
+ void addTestBlocking(const char* transportName, uint32_t expectedFailures = 0) {
+ char name[1024];
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_read_part_available<CoupledTransports>, name), expectedFailures);
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available_in_chunks()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_read_part_available_in_chunks<CoupledTransports>, name), expectedFailures);
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_partial_midframe()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_read_partial_midframe<CoupledTransports>, name), expectedFailures);
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_none_available()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_read_none_available<CoupledTransports>, name), expectedFailures);
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_part_available()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_borrow_part_available<CoupledTransports>, name), expectedFailures);
+
+ THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_none_available()", transportName);
+ suite_->add(MAKE_TEST_CASE(test_borrow_none_available<CoupledTransports>, name), expectedFailures);
+ }
+
+ boost::unit_test::test_suite* suite_;
+ // sizeMultiplier_ is configurable via the command line, and allows the
+ // user to adjust between smaller buffers that can be tested quickly,
+ // or larger buffers that more thoroughly exercise the code, but take
+ // longer.
+ float sizeMultiplier_;
+};
+
+/**************************************************************************
+ * General Initialization
+ **************************************************************************/
+
+struct global_fixture {
+ std::shared_ptr<apache::thrift::concurrency::Thread> alarmThread_;
+ global_fixture() {
+#if _WIN32
+ apache::thrift::transport::TWinsockSingleton::create();
+#endif
+
+ apache::thrift::concurrency::ThreadFactory factory;
+ factory.setDetached(false);
+
+ alarmThread_ = factory.newThread(
+ apache::thrift::concurrency::FunctionRunner::create(alarm_handler_wrapper));
+ alarmThread_->start();
+ }
+ ~global_fixture() {
+ {
+ apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
+ g_teardown = true;
+ g_alarm_monitor.notify();
+ }
+ alarmThread_->join();
+ }
+};
+
+#if (BOOST_VERSION >= 105900)
+BOOST_GLOBAL_FIXTURE(global_fixture);
+#else
+BOOST_GLOBAL_FIXTURE(global_fixture)
+#endif
+
+#ifdef BOOST_TEST_DYN_LINK
+bool init_unit_test_suite() {
+ struct timeval tv;
+ THRIFT_GETTIMEOFDAY(&tv, nullptr);
+ int seed = tv.tv_sec ^ tv.tv_usec;
+
+ initrand(seed);
+
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "TransportTest";
+ TransportTestGen transport_test_generator(suite, 1);
+ transport_test_generator.generate();
+ return true;
+}
+
+int main( int argc, char* argv[] ) {
+ return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
+}
+#else
+boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+ THRIFT_UNUSED_VARIABLE(argc);
+ THRIFT_UNUSED_VARIABLE(argv);
+ struct timeval tv;
+ THRIFT_GETTIMEOFDAY(&tv, NULL);
+ int seed = tv.tv_sec ^ tv.tv_usec;
+
+ initrand(seed);
+
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "TransportTest";
+ TransportTestGen transport_test_generator(suite, 1);
+ transport_test_generator.generate();
+ return NULL;
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/TypedefTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/TypedefTest.cpp
new file mode 100644
index 000000000..24e9265e6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/TypedefTest.cpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <boost/type_traits/is_same.hpp>
+#include <boost/static_assert.hpp>
+
+#include "gen-cpp/TypedefTest_types.h"
+
+BOOST_STATIC_ASSERT((boost::is_same<int32_t, thrift::test::MyInt32>::value));
+BOOST_STATIC_ASSERT((boost::is_same<std::string, thrift::test::MyString>::value));
+BOOST_STATIC_ASSERT(
+ (boost::is_same<thrift::test::TypedefTestStruct, thrift::test::MyStruct>::value));
diff --git a/src/jaegertracing/thrift/lib/cpp/test/UnitTestMain.cpp b/src/jaegertracing/thrift/lib/cpp/test/UnitTestMain.cpp
new file mode 100644
index 000000000..f0ef1e4a6
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/UnitTestMain.cpp
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define BOOST_TEST_MODULE thrift
+#include <boost/test/auto_unit_test.hpp>
diff --git a/src/jaegertracing/thrift/lib/cpp/test/ZlibTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/ZlibTest.cpp
new file mode 100644
index 000000000..3e2eb816c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/ZlibTest.cpp
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE // needed for getopt_long
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER <= 1700)
+// polynomial and std::fill_t warning happens in MSVC 2010, 2013, maybe others
+// https://svn.boost.org/trac/boost/ticket/11426
+#pragma warning(disable:4996)
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#include <cstddef>
+#include <fstream>
+#include <iostream>
+#include <memory>
+
+#include <boost/random.hpp>
+#include <boost/shared_array.hpp>
+#include <boost/test/unit_test.hpp>
+#include <boost/version.hpp>
+
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TZlibTransport.h>
+
+using namespace apache::thrift::transport;
+using std::shared_ptr;
+using std::string;
+
+boost::mt19937 rng;
+
+/*
+ * Utility code
+ */
+
+class SizeGenerator {
+public:
+ virtual ~SizeGenerator() = default;
+ virtual unsigned int getSize() = 0;
+};
+
+class ConstantSizeGenerator : public SizeGenerator {
+public:
+ ConstantSizeGenerator(unsigned int value) : value_(value) {}
+ unsigned int getSize() override { return value_; }
+
+private:
+ unsigned int value_;
+};
+
+class LogNormalSizeGenerator : public SizeGenerator {
+public:
+ LogNormalSizeGenerator(double mean, double std_dev)
+ : gen_(rng, boost::lognormal_distribution<double>(mean, std_dev)) {}
+
+ unsigned int getSize() override {
+ // Loop until we get a size of 1 or more
+ while (true) {
+ auto value = static_cast<unsigned int>(gen_());
+ if (value >= 1) {
+ return value;
+ }
+ }
+ }
+
+private:
+ boost::variate_generator<boost::mt19937, boost::lognormal_distribution<double> > gen_;
+};
+
+boost::shared_array<uint8_t> gen_uniform_buffer(uint32_t buf_len, uint8_t c) {
+ auto* buf = new uint8_t[buf_len];
+ memset(buf, c, buf_len);
+ return boost::shared_array<uint8_t>(buf);
+}
+
+boost::shared_array<uint8_t> gen_compressible_buffer(uint32_t buf_len) {
+ auto* buf = new uint8_t[buf_len];
+
+ // Generate small runs of alternately increasing and decreasing bytes
+ boost::uniform_smallint<uint32_t> run_length_distribution(1, 64);
+ boost::uniform_smallint<uint8_t> byte_distribution(0, UINT8_MAX);
+ boost::variate_generator<boost::mt19937, boost::uniform_smallint<uint8_t> >
+ byte_generator(rng, byte_distribution);
+ boost::variate_generator<boost::mt19937, boost::uniform_smallint<uint32_t> >
+ run_len_generator(rng, run_length_distribution);
+
+ uint32_t idx = 0;
+ int8_t step = 1;
+ while (idx < buf_len) {
+ uint32_t run_length = run_len_generator();
+ if (idx + run_length > buf_len) {
+ run_length = buf_len - idx;
+ }
+
+ uint8_t byte = byte_generator();
+ for (uint32_t n = 0; n < run_length; ++n) {
+ buf[idx] = byte;
+ ++idx;
+ byte += step;
+ }
+
+ step *= -1;
+ }
+
+ return boost::shared_array<uint8_t>(buf);
+}
+
+boost::shared_array<uint8_t> gen_random_buffer(uint32_t buf_len) {
+ auto* buf = new uint8_t[buf_len];
+
+ boost::uniform_smallint<uint8_t> distribution(0, UINT8_MAX);
+ boost::variate_generator<boost::mt19937, boost::uniform_smallint<uint8_t> >
+ generator(rng, distribution);
+
+ for (uint32_t n = 0; n < buf_len; ++n) {
+ buf[n] = generator();
+ }
+
+ return boost::shared_array<uint8_t>(buf);
+}
+
+/*
+ * Test functions
+ */
+
+void test_write_then_read(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ zlib_trans->write(buf.get(), buf_len);
+ zlib_trans->finish();
+
+ boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
+ uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
+ BOOST_REQUIRE_EQUAL(got, buf_len);
+ BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
+ zlib_trans->verifyChecksum();
+}
+
+void test_separate_checksum(const boost::shared_array<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));
+ zlib_trans->write(buf.get(), buf_len);
+ zlib_trans->finish();
+ string tmp_buf;
+ membuf->appendBufferToString(tmp_buf);
+ zlib_trans.reset(new TZlibTransport(membuf,
+ TZlibTransport::DEFAULT_URBUF_SIZE,
+ static_cast<uint32_t>(tmp_buf.length() - 1)));
+
+ boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
+ uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
+ BOOST_REQUIRE_EQUAL(got, buf_len);
+ BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
+ zlib_trans->verifyChecksum();
+}
+
+void test_incomplete_checksum(const boost::shared_array<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));
+ zlib_trans->write(buf.get(), buf_len);
+ zlib_trans->finish();
+ 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())),
+ static_cast<uint32_t>(tmp_buf.length()));
+
+ boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
+ uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
+ BOOST_REQUIRE_EQUAL(got, buf_len);
+ BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), 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 boost::shared_array<uint8_t> buf,
+ uint32_t buf_len,
+ const shared_ptr<SizeGenerator>& write_gen,
+ const shared_ptr<SizeGenerator>& read_gen) {
+ // Try it with a mix of read/write sizes.
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ unsigned int tot;
+
+ tot = 0;
+ while (tot < buf_len) {
+ uint32_t write_len = write_gen->getSize();
+ if (tot + write_len > buf_len) {
+ write_len = buf_len - tot;
+ }
+ zlib_trans->write(buf.get() + tot, write_len);
+ tot += write_len;
+ }
+
+ zlib_trans->finish();
+
+ tot = 0;
+ boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
+ while (tot < buf_len) {
+ uint32_t read_len = read_gen->getSize();
+ 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_LE(got, expected_read_len);
+ BOOST_REQUIRE_NE(got, (uint32_t)0);
+ tot += got;
+ }
+
+ BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
+ zlib_trans->verifyChecksum();
+}
+
+void test_invalid_checksum(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
+ // Verify checksum checking.
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ zlib_trans->write(buf.get(), buf_len);
+ zlib_trans->finish();
+ 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 = static_cast<int>(tmp_buf.size() - 1);
+ tmp_buf[index]++;
+ membuf->resetBuffer(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(tmp_buf.data())),
+ static_cast<uint32_t>(tmp_buf.length()));
+
+ boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
+ try {
+ zlib_trans->readAll(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);
+ }
+}
+
+void test_write_after_flush(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
+ // write some data
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ zlib_trans->write(buf.get(), buf_len);
+
+ // call finish()
+ zlib_trans->finish();
+
+ // make sure write() throws an error
+ try {
+ uint8_t write_buf[] = "a";
+ zlib_trans->write(write_buf, 1);
+ BOOST_ERROR("write() after finish() did not raise an exception");
+ } catch (TTransportException& ex) {
+ BOOST_CHECK_EQUAL(ex.getType(), TTransportException::BAD_ARGS);
+ }
+
+ // make sure flush() throws an error
+ try {
+ zlib_trans->flush();
+ BOOST_ERROR("flush() after finish() did not raise an exception");
+ } catch (TTransportException& ex) {
+ BOOST_CHECK_EQUAL(ex.getType(), TTransportException::BAD_ARGS);
+ }
+
+ // make sure finish() throws an error
+ try {
+ zlib_trans->finish();
+ BOOST_ERROR("finish() after finish() did not raise an exception");
+ } catch (TTransportException& ex) {
+ BOOST_CHECK_EQUAL(ex.getType(), TTransportException::BAD_ARGS);
+ }
+}
+
+void test_no_write() {
+ // Verify that no data is written to the underlying transport if we
+ // never write data to the TZlibTransport.
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ {
+ // Create a TZlibTransport object, and immediately destroy it
+ // when it goes out of scope.
+ TZlibTransport w_zlib_trans(membuf);
+ }
+
+ BOOST_CHECK_EQUAL(membuf->available_read(), (uint32_t)0);
+}
+
+void test_get_underlying_transport() {
+ shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
+ shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
+ BOOST_CHECK_EQUAL(membuf.get(), zlib_trans->getUnderlyingTransport().get());
+}
+
+/*
+ * Initialization
+ */
+
+#if (BOOST_VERSION >= 105900)
+#define ADD_TEST_CASE(suite, name, _FUNC, ...) \
+ do { \
+ ::std::ostringstream name_ss; \
+ name_ss << name << "-" << BOOST_STRINGIZE(_FUNC); \
+ ::std::function<void ()> test_func = \
+ ::std::bind(_FUNC, ##__VA_ARGS__); \
+ ::boost::unit_test::test_case* tc \
+ = ::boost::unit_test::make_test_case(test_func, name_ss.str(), __FILE__, __LINE__); \
+ (suite)->add(tc); \
+ } while (0)
+#else
+#define ADD_TEST_CASE(suite, name, _FUNC, ...) \
+ do { \
+ ::std::ostringstream name_ss; \
+ name_ss << name << "-" << BOOST_STRINGIZE(_FUNC); \
+ ::boost::unit_test::test_case* tc \
+ = ::boost::unit_test::make_test_case(::std::bind(_FUNC, \
+ ##__VA_ARGS__), \
+ name_ss.str()); \
+ (suite)->add(tc); \
+ } while (0)
+#endif
+
+void add_tests(boost::unit_test::test_suite* suite,
+ const boost::shared_array<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);
+ ADD_TEST_CASE(suite, name, test_invalid_checksum, buf, buf_len);
+ ADD_TEST_CASE(suite, name, test_write_after_flush, buf, buf_len);
+
+ shared_ptr<SizeGenerator> size_32k(new ConstantSizeGenerator(1 << 15));
+ shared_ptr<SizeGenerator> size_lognormal(new LogNormalSizeGenerator(20, 30));
+ ADD_TEST_CASE(suite, name << "-constant", test_read_write_mix, buf, buf_len, size_32k, size_32k);
+ ADD_TEST_CASE(suite,
+ name << "-lognormal-write",
+ test_read_write_mix,
+ buf,
+ buf_len,
+ size_lognormal,
+ size_32k);
+ ADD_TEST_CASE(suite,
+ name << "-lognormal-read",
+ test_read_write_mix,
+ buf,
+ buf_len,
+ size_32k,
+ size_lognormal);
+ ADD_TEST_CASE(suite,
+ name << "-lognormal-both",
+ test_read_write_mix,
+ buf,
+ buf_len,
+ size_lognormal,
+ size_lognormal);
+
+ // Test with a random size distribution,
+ // but use the exact same distribution for reading as for writing.
+ //
+ // Because the SizeGenerator makes a copy of the random number generator,
+ // both SizeGenerators should return the exact same set of values, since they
+ // both start with random number generators in the same state.
+ shared_ptr<SizeGenerator> write_size_gen(new LogNormalSizeGenerator(20, 30));
+ shared_ptr<SizeGenerator> read_size_gen(new LogNormalSizeGenerator(20, 30));
+ ADD_TEST_CASE(suite,
+ name << "-lognormal-same-distribution",
+ test_read_write_mix,
+ buf,
+ buf_len,
+ write_size_gen,
+ read_size_gen);
+}
+
+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");
+}
+
+#ifdef BOOST_TEST_DYN_LINK
+bool init_unit_test_suite() {
+ auto seed = static_cast<uint32_t>(time(nullptr));
+#ifdef HAVE_INTTYPES_H
+ printf("seed: %" PRIu32 "\n", seed);
+#endif
+ rng.seed(seed);
+
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "ZlibTest";
+
+ 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");
+
+ suite->add(BOOST_TEST_CASE(test_no_write));
+ suite->add(BOOST_TEST_CASE(test_get_underlying_transport));
+
+ return true;
+}
+
+int main( int argc, char* argv[] ) {
+ return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
+}
+#else
+boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+ THRIFT_UNUSED_VARIABLE(argc);
+ THRIFT_UNUSED_VARIABLE(argv);
+ uint32_t seed = static_cast<uint32_t>(time(NULL));
+#ifdef HAVE_INTTYPES_H
+ printf("seed: %" PRIu32 "\n", seed);
+#endif
+ rng.seed(seed);
+
+ boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
+ suite->p_name.value = "ZlibTest";
+
+ 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");
+
+ suite->add(BOOST_TEST_CASE(test_no_write));
+
+ return NULL;
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/concurrency/Tests.cpp b/src/jaegertracing/thrift/lib/cpp/test/concurrency/Tests.cpp
new file mode 100644
index 000000000..8c734c2d5
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/concurrency/Tests.cpp
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <iostream>
+#include <vector>
+#include <string>
+
+#include "ThreadFactoryTests.h"
+#include "TimerManagerTests.h"
+#include "ThreadManagerTests.h"
+
+// The test weight, where 10 is 10 times more threads than baseline
+// and the baseline is optimized for running in valgrind
+static int WEIGHT = 10;
+
+int main(int argc, char** argv) {
+
+ std::vector<std::string> args(argc - 1 > 1 ? argc - 1 : 1);
+
+ args[0] = "all";
+
+ for (int ix = 1; ix < argc; ix++) {
+ args[ix - 1] = std::string(argv[ix]);
+ }
+
+ if (getenv("VALGRIND") != nullptr) {
+ // lower the scale of every test
+ WEIGHT = 1;
+ }
+
+ bool runAll = args[0].compare("all") == 0;
+
+ if (runAll || args[0].compare("thread-factory") == 0) {
+
+ ThreadFactoryTests threadFactoryTests;
+
+ std::cout << "ThreadFactory tests..." << std::endl;
+
+ int reapLoops = 2 * WEIGHT;
+ int reapCount = 100 * WEIGHT;
+ size_t floodLoops = 3;
+ size_t floodCount = 500 * WEIGHT;
+
+ std::cout << "\t\tThreadFactory reap N threads test: N = " << reapLoops << "x" << reapCount << std::endl;
+
+ if (!threadFactoryTests.reapNThreads(reapLoops, reapCount)) {
+ std::cerr << "\t\ttThreadFactory reap N threads FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tThreadFactory flood N threads test: N = " << floodLoops << "x" << floodCount << std::endl;
+
+ if (!threadFactoryTests.floodNTest(floodLoops, floodCount)) {
+ std::cerr << "\t\ttThreadFactory flood N threads FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tThreadFactory synchronous start test" << std::endl;
+
+ if (!threadFactoryTests.synchStartTest()) {
+ std::cerr << "\t\ttThreadFactory synchronous start FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tThreadFactory monitor timeout test" << std::endl;
+
+ if (!threadFactoryTests.monitorTimeoutTest()) {
+ std::cerr << "\t\ttThreadFactory monitor timeout FAILED" << std::endl;
+ return 1;
+ }
+ }
+
+ if (runAll || args[0].compare("util") == 0) {
+
+ std::cout << "Util tests..." << std::endl;
+
+ std::cout << "\t\tUtil minimum time" << std::endl;
+
+ int64_t time00 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ int64_t time01 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ std::cout << "\t\t\tMinimum time: " << time01 - time00 << "ms" << std::endl;
+
+ time00 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ time01 = time00;
+ size_t count = 0;
+
+ while (time01 < time00 + 10) {
+ count++;
+ time01 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ }
+
+ std::cout << "\t\t\tscall per ms: " << count / (time01 - time00) << std::endl;
+ }
+
+ if (runAll || args[0].compare("timer-manager") == 0) {
+
+ std::cout << "TimerManager tests..." << std::endl;
+
+ std::cout << "\t\tTimerManager test00" << std::endl;
+
+ TimerManagerTests timerManagerTests;
+
+ if (!timerManagerTests.test00()) {
+ std::cerr << "\t\tTimerManager tests FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tTimerManager test01" << std::endl;
+
+ if (!timerManagerTests.test01()) {
+ std::cerr << "\t\tTimerManager tests FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tTimerManager test02" << std::endl;
+
+ if (!timerManagerTests.test02()) {
+ std::cerr << "\t\tTimerManager tests FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tTimerManager test03" << std::endl;
+
+ if (!timerManagerTests.test03()) {
+ std::cerr << "\t\tTimerManager tests FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tTimerManager test04" << std::endl;
+
+ if (!timerManagerTests.test04()) {
+ std::cerr << "\t\tTimerManager tests FAILED" << std::endl;
+ return 1;
+ }
+ }
+
+ if (runAll || args[0].compare("thread-manager") == 0) {
+
+ std::cout << "ThreadManager tests..." << std::endl;
+
+ {
+ size_t workerCount = 10 * WEIGHT;
+ size_t taskCount = 500 * WEIGHT;
+ int64_t delay = 10LL;
+
+ ThreadManagerTests threadManagerTests;
+
+ std::cout << "\t\tThreadManager api test:" << std::endl;
+
+ if (!threadManagerTests.apiTest()) {
+ std::cerr << "\t\tThreadManager apiTest FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tThreadManager load test: worker count: " << workerCount
+ << " task count: " << taskCount << " delay: " << delay << std::endl;
+
+ if (!threadManagerTests.loadTest(taskCount, delay, workerCount)) {
+ std::cerr << "\t\tThreadManager loadTest FAILED" << std::endl;
+ return 1;
+ }
+
+ std::cout << "\t\tThreadManager block test: worker count: " << workerCount
+ << " delay: " << delay << std::endl;
+
+ if (!threadManagerTests.blockTest(delay, workerCount)) {
+ std::cerr << "\t\tThreadManager blockTest FAILED" << std::endl;
+ return 1;
+ }
+ }
+ }
+
+ if (runAll || args[0].compare("thread-manager-benchmark") == 0) {
+
+ std::cout << "ThreadManager benchmark tests..." << std::endl;
+
+ {
+
+ size_t minWorkerCount = 2;
+
+ size_t maxWorkerCount = 8;
+
+ size_t tasksPerWorker = 100 * WEIGHT;
+
+ int64_t delay = 5LL;
+
+ for (size_t workerCount = minWorkerCount; workerCount <= maxWorkerCount; workerCount *= 4) {
+
+ size_t taskCount = workerCount * tasksPerWorker;
+
+ std::cout << "\t\tThreadManager load test: worker count: " << workerCount
+ << " task count: " << taskCount << " delay: " << delay << std::endl;
+
+ ThreadManagerTests threadManagerTests;
+
+ if (!threadManagerTests.loadTest(taskCount, delay, workerCount))
+ {
+ std::cerr << "\t\tThreadManager loadTest FAILED" << std::endl;
+ return 1;
+ }
+ }
+ }
+ }
+
+ std::cout << "ALL TESTS PASSED" << std::endl;
+ return 0;
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadFactoryTests.h b/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadFactoryTests.h
new file mode 100644
index 000000000..febe3f8b3
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadFactoryTests.h
@@ -0,0 +1,308 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+#include <thrift/concurrency/Thread.h>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/concurrency/Mutex.h>
+
+#include <assert.h>
+#include <iostream>
+#include <vector>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+namespace test {
+
+using std::shared_ptr;
+using namespace apache::thrift::concurrency;
+
+/**
+ * ThreadManagerTests class
+ *
+ * @version $Id:$
+ */
+class ThreadFactoryTests {
+
+public:
+ /**
+ * Reap N threads
+ */
+ class ReapNTask : public Runnable {
+
+ public:
+ ReapNTask(Monitor& monitor, int& activeCount) : _monitor(monitor), _count(activeCount) {}
+
+ void run() override {
+ Synchronized s(_monitor);
+
+ if (--_count == 0) {
+ _monitor.notify();
+ }
+ }
+
+ Monitor& _monitor;
+ int& _count;
+ };
+
+ bool reapNThreads(int loop = 1, int count = 10) {
+
+ ThreadFactory threadFactory = ThreadFactory();
+ shared_ptr<Monitor> monitor(new Monitor);
+
+ for (int lix = 0; lix < loop; lix++) {
+
+ int activeCount = 0;
+
+ std::vector<shared_ptr<Thread> > threads;
+ int tix;
+
+ for (tix = 0; tix < count; tix++) {
+ try {
+ ++activeCount;
+ threads.push_back(
+ threadFactory.newThread(shared_ptr<Runnable>(new ReapNTask(*monitor, activeCount))));
+ } catch (SystemResourceException& e) {
+ std::cout << "\t\t\tfailed to create " << lix* count + tix << " thread " << e.what()
+ << std::endl;
+ throw e;
+ }
+ }
+
+ tix = 0;
+ for (std::vector<shared_ptr<Thread> >::const_iterator thread = threads.begin();
+ thread != threads.end();
+ tix++, ++thread) {
+
+ try {
+ (*thread)->start();
+ } catch (SystemResourceException& e) {
+ std::cout << "\t\t\tfailed to start " << lix* count + tix << " thread " << e.what()
+ << std::endl;
+ throw e;
+ }
+ }
+
+ {
+ Synchronized s(*monitor);
+ while (activeCount > 0) {
+ monitor->wait(1000);
+ }
+ }
+
+ std::cout << "\t\t\treaped " << lix* count << " threads" << std::endl;
+ }
+
+ std::cout << "\t\t\tSuccess!" << std::endl;
+ return true;
+ }
+
+ class SynchStartTask : public Runnable {
+
+ public:
+ enum STATE { UNINITIALIZED, STARTING, STARTED, STOPPING, STOPPED };
+
+ SynchStartTask(Monitor& monitor, volatile STATE& state) : _monitor(monitor), _state(state) {}
+
+ void run() override {
+ {
+ Synchronized s(_monitor);
+ if (_state == SynchStartTask::STARTING) {
+ _state = SynchStartTask::STARTED;
+ _monitor.notify();
+ }
+ }
+
+ {
+ Synchronized s(_monitor);
+ while (_state == SynchStartTask::STARTED) {
+ _monitor.wait();
+ }
+
+ if (_state == SynchStartTask::STOPPING) {
+ _state = SynchStartTask::STOPPED;
+ _monitor.notifyAll();
+ }
+ }
+ }
+
+ private:
+ Monitor& _monitor;
+ volatile STATE& _state;
+ };
+
+ bool synchStartTest() {
+
+ Monitor monitor;
+
+ SynchStartTask::STATE state = SynchStartTask::UNINITIALIZED;
+
+ shared_ptr<SynchStartTask> task
+ = shared_ptr<SynchStartTask>(new SynchStartTask(monitor, state));
+
+ ThreadFactory threadFactory = ThreadFactory();
+
+ shared_ptr<Thread> thread = threadFactory.newThread(task);
+
+ if (state == SynchStartTask::UNINITIALIZED) {
+
+ state = SynchStartTask::STARTING;
+
+ thread->start();
+ }
+
+ {
+ Synchronized s(monitor);
+ while (state == SynchStartTask::STARTING) {
+ monitor.wait();
+ }
+ }
+
+ assert(state != SynchStartTask::STARTING);
+
+ {
+ Synchronized s(monitor);
+
+ try {
+ monitor.wait(100);
+ } catch (TimedOutException&) {
+ }
+
+ if (state == SynchStartTask::STARTED) {
+
+ state = SynchStartTask::STOPPING;
+
+ monitor.notify();
+ }
+
+ while (state == SynchStartTask::STOPPING) {
+ monitor.wait();
+ }
+ }
+
+ assert(state == SynchStartTask::STOPPED);
+
+ bool success = true;
+
+ std::cout << "\t\t\t" << (success ? "Success" : "Failure") << "!" << std::endl;
+
+ return true;
+ }
+
+ /**
+ * The only guarantee a monitor timeout can give you is that
+ * it will take "at least" as long as the timeout, no less.
+ * There is absolutely no guarantee around regaining execution
+ * near the timeout. On a busy system (like inside a third party
+ * CI environment) it could take quite a bit longer than the
+ * requested timeout, and that's ok.
+ */
+
+ bool monitorTimeoutTest(int64_t count = 1000, int64_t timeout = 2) {
+
+ Monitor monitor;
+
+ int64_t startTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ for (int64_t ix = 0; ix < count; ix++) {
+ {
+ Synchronized s(monitor);
+ try {
+ monitor.wait(timeout);
+ } catch (TimedOutException&) {
+ }
+ }
+ }
+
+ int64_t endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ bool success = (endTime - startTime) >= (count * timeout);
+
+ std::cout << "\t\t\t" << (success ? "Success" : "Failure")
+ << ": minimum required time to elapse " << count * timeout
+ << "ms; actual elapsed time " << endTime - startTime << "ms"
+ << std::endl;
+
+ return success;
+ }
+
+ class FloodTask : public Runnable {
+ public:
+ FloodTask(const size_t id, Monitor& mon) : _id(id), _mon(mon) {}
+ ~FloodTask() override {
+ if (_id % 10000 == 0) {
+ Synchronized sync(_mon);
+ std::cout << "\t\tthread " << _id << " done" << std::endl;
+ }
+ }
+
+ void run() override {
+ if (_id % 10000 == 0) {
+ Synchronized sync(_mon);
+ std::cout << "\t\tthread " << _id << " started" << std::endl;
+ }
+ }
+ const size_t _id;
+ Monitor& _mon;
+ };
+
+ void foo(ThreadFactory* tf) { (void)tf; }
+
+ bool floodNTest(size_t loop = 1, size_t count = 100000) {
+
+ bool success = false;
+ Monitor mon;
+
+ for (size_t lix = 0; lix < loop; lix++) {
+
+ ThreadFactory threadFactory = ThreadFactory();
+ threadFactory.setDetached(true);
+
+ for (size_t tix = 0; tix < count; tix++) {
+
+ try {
+
+ shared_ptr<FloodTask> task(new FloodTask(lix * count + tix, mon));
+ shared_ptr<Thread> thread = threadFactory.newThread(task);
+ thread->start();
+
+ } catch (TException& e) {
+
+ std::cout << "\t\t\tfailed to start " << lix* count + tix << " thread " << e.what()
+ << std::endl;
+
+ return success;
+ }
+ }
+
+ Synchronized sync(mon);
+ std::cout << "\t\t\tflooded " << (lix + 1) * count << " threads" << std::endl;
+ success = true;
+ }
+
+ return success;
+ }
+};
+
+}
+}
+}
+} // apache::thrift::concurrency::test
diff --git a/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadManagerTests.h b/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadManagerTests.h
new file mode 100644
index 000000000..fee7c7c51
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadManagerTests.h
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/thrift-config.h>
+#include <thrift/concurrency/ThreadManager.h>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Monitor.h>
+
+#include <assert.h>
+#include <deque>
+#include <set>
+#include <iostream>
+#include <stdint.h>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+namespace test {
+
+using namespace apache::thrift::concurrency;
+
+static std::deque<std::shared_ptr<Runnable> > m_expired;
+static void expiredNotifier(std::shared_ptr<Runnable> runnable)
+{
+ m_expired.push_back(runnable);
+}
+
+static void sleep_(int64_t millisec) {
+ Monitor _sleep;
+ Synchronized s(_sleep);
+
+ try {
+ _sleep.wait(millisec);
+ } catch (TimedOutException&) {
+ ;
+ } catch (...) {
+ assert(0);
+ }
+}
+
+class ThreadManagerTests {
+
+public:
+ class Task : public Runnable {
+
+ public:
+ Task(Monitor& monitor, size_t& count, int64_t timeout)
+ : _monitor(monitor), _count(count), _timeout(timeout), _startTime(0), _endTime(0), _done(false) {}
+
+ void run() override {
+
+ _startTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ sleep_(_timeout);
+
+ _endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ _done = true;
+
+ {
+ Synchronized s(_monitor);
+
+ // std::cout << "Thread " << _count << " completed " << std::endl;
+
+ _count--;
+ if (_count % 10000 == 0) {
+ _monitor.notify();
+ }
+ }
+ }
+
+ Monitor& _monitor;
+ size_t& _count;
+ int64_t _timeout;
+ int64_t _startTime;
+ int64_t _endTime;
+ bool _done;
+ Monitor _sleep;
+ };
+
+ /**
+ * Dispatch count tasks, each of which blocks for timeout milliseconds then
+ * completes. Verify that all tasks completed and that thread manager cleans
+ * up properly on delete.
+ */
+ bool loadTest(size_t count = 100, int64_t timeout = 100LL, size_t workerCount = 4) {
+
+ Monitor monitor;
+
+ size_t activeCount = count;
+
+ shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(workerCount);
+
+ shared_ptr<ThreadFactory> threadFactory
+ = shared_ptr<ThreadFactory>(new ThreadFactory(false));
+
+ threadManager->threadFactory(threadFactory);
+
+ threadManager->start();
+
+ std::set<shared_ptr<ThreadManagerTests::Task> > tasks;
+
+ for (size_t ix = 0; ix < count; ix++) {
+
+ tasks.insert(shared_ptr<ThreadManagerTests::Task>(
+ new ThreadManagerTests::Task(monitor, activeCount, timeout)));
+ }
+
+ int64_t time00 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ for (auto ix = tasks.begin();
+ ix != tasks.end();
+ ix++) {
+
+ threadManager->add(*ix);
+ }
+
+ std::cout << "\t\t\t\tloaded " << count << " tasks to execute" << std::endl;
+
+ {
+ Synchronized s(monitor);
+
+ while (activeCount > 0) {
+ std::cout << "\t\t\t\tactiveCount = " << activeCount << std::endl;
+ monitor.wait();
+ }
+ }
+
+ int64_t time01 = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+
+ int64_t firstTime = 9223372036854775807LL;
+ int64_t lastTime = 0;
+
+ double averageTime = 0;
+ int64_t minTime = 9223372036854775807LL;
+ int64_t maxTime = 0;
+
+ for (auto ix = tasks.begin();
+ ix != tasks.end();
+ ix++) {
+
+ shared_ptr<ThreadManagerTests::Task> task = *ix;
+
+ int64_t delta = task->_endTime - task->_startTime;
+
+ assert(delta > 0);
+
+ if (task->_startTime < firstTime) {
+ firstTime = task->_startTime;
+ }
+
+ if (task->_endTime > lastTime) {
+ lastTime = task->_endTime;
+ }
+
+ if (delta < minTime) {
+ minTime = delta;
+ }
+
+ if (delta > maxTime) {
+ maxTime = delta;
+ }
+
+ averageTime += delta;
+ }
+
+ averageTime /= count;
+
+ std::cout << "\t\t\tfirst start: " << firstTime << " Last end: " << lastTime
+ << " min: " << minTime << "ms max: " << maxTime << "ms average: " << averageTime
+ << "ms" << std::endl;
+
+ bool success = (time01 - time00) >= ((int64_t)count * timeout) / (int64_t)workerCount;
+
+ std::cout << "\t\t\t" << (success ? "Success" : "Failure")
+ << "! expected time: " << ((int64_t)count * timeout) / (int64_t)workerCount << "ms elapsed time: " << time01 - time00
+ << "ms" << std::endl;
+
+ return success;
+ }
+
+ class BlockTask : public Runnable {
+
+ public:
+ BlockTask(Monitor& entryMonitor, Monitor& blockMonitor, bool& blocked, Monitor& doneMonitor, size_t& count)
+ : _entryMonitor(entryMonitor), _entered(false), _blockMonitor(blockMonitor), _blocked(blocked), _doneMonitor(doneMonitor), _count(count) {}
+
+ void run() override {
+ {
+ Synchronized s(_entryMonitor);
+ _entered = true;
+ _entryMonitor.notify();
+ }
+
+ {
+ Synchronized s(_blockMonitor);
+ while (_blocked) {
+ _blockMonitor.wait();
+ }
+ }
+
+ {
+ Synchronized s(_doneMonitor);
+ if (--_count == 0) {
+ _doneMonitor.notify();
+ }
+ }
+ }
+
+ Monitor& _entryMonitor;
+ bool _entered;
+ Monitor& _blockMonitor;
+ bool& _blocked;
+ Monitor& _doneMonitor;
+ size_t& _count;
+ };
+
+ /**
+ * Block test. Create pendingTaskCountMax tasks. Verify that we block adding the
+ * pendingTaskCountMax + 1th task. Verify that we unblock when a task completes */
+
+ bool blockTest(int64_t timeout = 100LL, size_t workerCount = 2) {
+ (void)timeout;
+ bool success = false;
+
+ try {
+
+ Monitor entryMonitor; // not used by this test
+ Monitor blockMonitor;
+ bool blocked[] = {true, true, true};
+ Monitor doneMonitor;
+
+ size_t pendingTaskMaxCount = workerCount;
+
+ size_t activeCounts[] = {workerCount, pendingTaskMaxCount, 1};
+
+ shared_ptr<ThreadManager> threadManager
+ = ThreadManager::newSimpleThreadManager(workerCount, pendingTaskMaxCount);
+
+ shared_ptr<ThreadFactory> threadFactory
+ = shared_ptr<ThreadFactory>(new ThreadFactory());
+
+ threadManager->threadFactory(threadFactory);
+
+ threadManager->start();
+
+ std::vector<shared_ptr<ThreadManagerTests::BlockTask> > tasks;
+ tasks.reserve(workerCount + pendingTaskMaxCount);
+
+ for (size_t ix = 0; ix < workerCount; ix++) {
+
+ tasks.push_back(shared_ptr<ThreadManagerTests::BlockTask>(
+ new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[0], doneMonitor, activeCounts[0])));
+ }
+
+ for (size_t ix = 0; ix < pendingTaskMaxCount; ix++) {
+
+ tasks.push_back(shared_ptr<ThreadManagerTests::BlockTask>(
+ new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[1], doneMonitor, activeCounts[1])));
+ }
+
+ for (auto ix = tasks.begin();
+ ix != tasks.end();
+ ix++) {
+ threadManager->add(*ix);
+ }
+
+ if (!(success = (threadManager->totalTaskCount() == pendingTaskMaxCount + workerCount))) {
+ throw TException("Unexpected pending task count");
+ }
+
+ shared_ptr<ThreadManagerTests::BlockTask> extraTask(
+ new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[2], doneMonitor, activeCounts[2]));
+
+ try {
+ threadManager->add(extraTask, 1);
+ throw TException("Unexpected success adding task in excess of pending task count");
+ } catch (TooManyPendingTasksException&) {
+ throw TException("Should have timed out adding task in excess of pending task count");
+ } catch (TimedOutException&) {
+ // Expected result
+ }
+
+ try {
+ threadManager->add(extraTask, -1);
+ throw TException("Unexpected success adding task in excess of pending task count");
+ } catch (TimedOutException&) {
+ throw TException("Unexpected timeout adding task in excess of pending task count");
+ } catch (TooManyPendingTasksException&) {
+ // Expected result
+ }
+
+ std::cout << "\t\t\t"
+ << "Pending tasks " << threadManager->pendingTaskCount() << std::endl;
+
+ {
+ Synchronized s(blockMonitor);
+ blocked[0] = false;
+ blockMonitor.notifyAll();
+ }
+
+ {
+ Synchronized s(doneMonitor);
+ while (activeCounts[0] != 0) {
+ doneMonitor.wait();
+ }
+ }
+
+ std::cout << "\t\t\t"
+ << "Pending tasks " << threadManager->pendingTaskCount() << std::endl;
+
+ try {
+ threadManager->add(extraTask, 1);
+ } catch (TimedOutException&) {
+ std::cout << "\t\t\t"
+ << "add timed out unexpectedly" << std::endl;
+ throw TException("Unexpected timeout adding task");
+
+ } catch (TooManyPendingTasksException&) {
+ std::cout << "\t\t\t"
+ << "add encountered too many pending exepctions" << std::endl;
+ throw TException("Unexpected timeout adding task");
+ }
+
+ // Wake up tasks that were pending before and wait for them to complete
+
+ {
+ Synchronized s(blockMonitor);
+ blocked[1] = false;
+ blockMonitor.notifyAll();
+ }
+
+ {
+ Synchronized s(doneMonitor);
+ while (activeCounts[1] != 0) {
+ doneMonitor.wait();
+ }
+ }
+
+ // Wake up the extra task and wait for it to complete
+
+ {
+ Synchronized s(blockMonitor);
+ blocked[2] = false;
+ blockMonitor.notifyAll();
+ }
+
+ {
+ Synchronized s(doneMonitor);
+ while (activeCounts[2] != 0) {
+ doneMonitor.wait();
+ }
+ }
+
+ threadManager->stop();
+
+ if (!(success = (threadManager->totalTaskCount() == 0))) {
+ throw TException("Unexpected total task count");
+ }
+
+ } catch (TException& e) {
+ std::cout << "ERROR: " << e.what() << std::endl;
+ }
+
+ std::cout << "\t\t\t" << (success ? "Success" : "Failure") << std::endl;
+ return success;
+ }
+
+
+ bool apiTest() {
+
+ // prove currentTime has milliseconds granularity since many other things depend on it
+ int64_t a = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ sleep_(100);
+ int64_t b = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ if (b - a < 50 || b - a > 150) {
+ std::cerr << "\t\t\texpected 100ms gap, found " << (b-a) << "ms gap instead." << std::endl;
+ return false;
+ }
+
+ return apiTestWithThreadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+
+ }
+
+ bool apiTestWithThreadFactory(shared_ptr<ThreadFactory> threadFactory)
+ {
+ shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(1);
+ threadManager->threadFactory(threadFactory);
+
+ std::cout << "\t\t\t\tstarting.. " << std::endl;
+
+ threadManager->start();
+ threadManager->setExpireCallback(expiredNotifier); // std::bind(&ThreadManagerTests::expiredNotifier, this));
+
+#define EXPECT(FUNC, COUNT) { size_t c = FUNC; if (c != COUNT) { std::cerr << "expected " #FUNC" to be " #COUNT ", but was " << c << std::endl; return false; } }
+
+ EXPECT(threadManager->workerCount(), 1);
+ EXPECT(threadManager->idleWorkerCount(), 1);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tadd 2nd worker.. " << std::endl;
+
+ threadManager->addWorker();
+
+ EXPECT(threadManager->workerCount(), 2);
+ EXPECT(threadManager->idleWorkerCount(), 2);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tremove 2nd worker.. " << std::endl;
+
+ threadManager->removeWorker();
+
+ EXPECT(threadManager->workerCount(), 1);
+ EXPECT(threadManager->idleWorkerCount(), 1);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tremove 1st worker.. " << std::endl;
+
+ threadManager->removeWorker();
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tadd blocking task.. " << std::endl;
+
+ // We're going to throw a blocking task into the mix
+ Monitor entryMonitor; // signaled when task is running
+ Monitor blockMonitor; // to be signaled to unblock the task
+ bool blocked(true); // set to false before notifying
+ Monitor doneMonitor; // signaled when count reaches zero
+ size_t activeCount = 1;
+ shared_ptr<ThreadManagerTests::BlockTask> blockingTask(
+ new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked, doneMonitor, activeCount));
+ threadManager->add(blockingTask);
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 1);
+
+ std::cout << "\t\t\t\tadd other task.. " << std::endl;
+
+ shared_ptr<ThreadManagerTests::Task> otherTask(
+ new ThreadManagerTests::Task(doneMonitor, activeCount, 0));
+
+ threadManager->add(otherTask);
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 2);
+
+ std::cout << "\t\t\t\tremove blocking task specifically.. " << std::endl;
+
+ threadManager->remove(blockingTask);
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 1);
+
+ std::cout << "\t\t\t\tremove next pending task.." << std::endl;
+
+ shared_ptr<Runnable> nextTask = threadManager->removeNextPending();
+ if (nextTask != otherTask) {
+ std::cerr << "\t\t\t\t\texpected removeNextPending to return otherTask" << std::endl;
+ return false;
+ }
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tremove next pending task (none left).." << std::endl;
+
+ nextTask = threadManager->removeNextPending();
+ if (nextTask) {
+ std::cerr << "\t\t\t\t\texpected removeNextPending to return an empty Runnable" << std::endl;
+ return false;
+ }
+
+ std::cout << "\t\t\t\tadd 2 expired tasks and 1 not.." << std::endl;
+
+ shared_ptr<ThreadManagerTests::Task> expiredTask(
+ new ThreadManagerTests::Task(doneMonitor, activeCount, 0));
+
+ threadManager->add(expiredTask, 0, 1);
+ threadManager->add(blockingTask); // add one that hasn't expired to make sure it gets skipped
+ threadManager->add(expiredTask, 0, 1); // add a second expired to ensure removeExpiredTasks removes both
+
+ sleep_(50); // make sure enough time elapses for it to expire - the shortest expiration time is 1 millisecond
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 3);
+ EXPECT(threadManager->expiredTaskCount(), 0);
+
+ std::cout << "\t\t\t\tremove expired tasks.." << std::endl;
+
+ if (!m_expired.empty()) {
+ std::cerr << "\t\t\t\t\texpected m_expired to be empty" << std::endl;
+ return false;
+ }
+
+ threadManager->removeExpiredTasks();
+
+ if (m_expired.size() != 2) {
+ std::cerr << "\t\t\t\t\texpected m_expired to be set" << std::endl;
+ return false;
+ }
+
+ if (m_expired.front() != expiredTask) {
+ std::cerr << "\t\t\t\t\texpected m_expired[0] to be the expired task" << std::endl;
+ return false;
+ }
+ m_expired.pop_front();
+
+ if (m_expired.front() != expiredTask) {
+ std::cerr << "\t\t\t\t\texpected m_expired[1] to be the expired task" << std::endl;
+ return false;
+ }
+
+ m_expired.clear();
+
+ threadManager->remove(blockingTask);
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+ EXPECT(threadManager->expiredTaskCount(), 2);
+
+ std::cout << "\t\t\t\tadd expired task (again).." << std::endl;
+
+ threadManager->add(expiredTask, 0, 1); // expires in 1ms
+ sleep_(50); // make sure enough time elapses for it to expire - the shortest expiration time is 1ms
+
+ std::cout << "\t\t\t\tadd worker to consume expired task.." << std::endl;
+
+ threadManager->addWorker();
+ sleep_(100); // make sure it has time to spin up and expire the task
+
+ if (m_expired.empty()) {
+ std::cerr << "\t\t\t\t\texpected m_expired to be set" << std::endl;
+ return false;
+ }
+
+ if (m_expired.front() != expiredTask) {
+ std::cerr << "\t\t\t\t\texpected m_expired to be the expired task" << std::endl;
+ return false;
+ }
+
+ m_expired.clear();
+
+ EXPECT(threadManager->workerCount(), 1);
+ EXPECT(threadManager->idleWorkerCount(), 1);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+ EXPECT(threadManager->expiredTaskCount(), 3);
+
+ std::cout << "\t\t\t\ttry to remove too many workers" << std::endl;
+ try {
+ threadManager->removeWorker(2);
+ std::cerr << "\t\t\t\t\texpected InvalidArgumentException" << std::endl;
+ return false;
+ } catch (const InvalidArgumentException&) {
+ /* expected */
+ }
+
+ std::cout << "\t\t\t\tremove worker.. " << std::endl;
+
+ threadManager->removeWorker();
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+ EXPECT(threadManager->expiredTaskCount(), 3);
+
+ std::cout << "\t\t\t\tadd blocking task.. " << std::endl;
+
+ threadManager->add(blockingTask);
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 1);
+
+ std::cout << "\t\t\t\tadd worker.. " << std::endl;
+
+ threadManager->addWorker();
+ {
+ Synchronized s(entryMonitor);
+ while (!blockingTask->_entered) {
+ entryMonitor.wait();
+ }
+ }
+
+ EXPECT(threadManager->workerCount(), 1);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tunblock task and remove worker.. " << std::endl;
+
+ {
+ Synchronized s(blockMonitor);
+ blocked = false;
+ blockMonitor.notifyAll();
+ }
+ threadManager->removeWorker();
+
+ EXPECT(threadManager->workerCount(), 0);
+ EXPECT(threadManager->idleWorkerCount(), 0);
+ EXPECT(threadManager->pendingTaskCount(), 0);
+
+ std::cout << "\t\t\t\tcleanup.. " << std::endl;
+
+ blockingTask.reset();
+ threadManager.reset();
+ return true;
+ }
+};
+
+}
+}
+}
+} // apache::thrift::concurrency
+
+using namespace apache::thrift::concurrency::test;
diff --git a/src/jaegertracing/thrift/lib/cpp/test/concurrency/TimerManagerTests.h b/src/jaegertracing/thrift/lib/cpp/test/concurrency/TimerManagerTests.h
new file mode 100644
index 000000000..2d1a2620a
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/concurrency/TimerManagerTests.h
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <thrift/concurrency/TimerManager.h>
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Monitor.h>
+
+#include <assert.h>
+#include <chrono>
+#include <thread>
+#include <iostream>
+
+namespace apache {
+namespace thrift {
+namespace concurrency {
+namespace test {
+
+using namespace apache::thrift::concurrency;
+
+class TimerManagerTests {
+
+public:
+ class Task : public Runnable {
+ public:
+ Task(Monitor& monitor, uint64_t timeout)
+ : _timeout(timeout),
+ _startTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()),
+ _endTime(0),
+ _monitor(monitor),
+ _success(false),
+ _done(false) {}
+
+ ~Task() override { std::cerr << this << std::endl; }
+
+ void run() override {
+
+ _endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
+ _success = (_endTime - _startTime) >= _timeout;
+
+ {
+ Synchronized s(_monitor);
+ _done = true;
+ _monitor.notifyAll();
+ }
+ }
+
+ int64_t _timeout;
+ int64_t _startTime;
+ int64_t _endTime;
+ Monitor& _monitor;
+ bool _success;
+ bool _done;
+ };
+
+ /**
+ * This test creates two tasks and waits for the first to expire within 10%
+ * of the expected expiration time. It then verifies that the timer manager
+ * properly clean up itself and the remaining orphaned timeout task when the
+ * manager goes out of scope and its destructor is called.
+ */
+ bool test00(uint64_t timeout = 1000LL) {
+
+ shared_ptr<TimerManagerTests::Task> orphanTask
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, 10 * timeout));
+
+ {
+ TimerManager timerManager;
+ timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+ timerManager.start();
+ if (timerManager.state() != TimerManager::STARTED) {
+ std::cerr << "timerManager is not in the STARTED state, but should be" << std::endl;
+ return false;
+ }
+
+ // Don't create task yet, because its constructor sets the expected completion time, and we
+ // need to delay between inserting the two tasks into the run queue.
+ shared_ptr<TimerManagerTests::Task> task;
+
+ {
+ Synchronized s(_monitor);
+ timerManager.add(orphanTask, 10 * timeout);
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
+
+ task.reset(new TimerManagerTests::Task(_monitor, timeout));
+ timerManager.add(task, timeout);
+ _monitor.wait();
+ }
+
+ if (!task->_done) {
+ std::cerr << "task is not done, but it should have executed" << std::endl;
+ return false;
+ }
+
+ std::cout << "\t\t\t" << (task->_success ? "Success" : "Failure") << "!" << std::endl;
+ }
+
+ if (orphanTask->_done) {
+ std::cerr << "orphan task is done, but it should not have executed" << std::endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * This test creates two tasks, removes the first one then waits for the second one. It then
+ * verifies that the timer manager properly clean up itself and the remaining orphaned timeout
+ * task when the manager goes out of scope and its destructor is called.
+ */
+ bool test01(uint64_t timeout = 1000LL) {
+ TimerManager timerManager;
+ timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+ timerManager.start();
+ assert(timerManager.state() == TimerManager::STARTED);
+
+ Synchronized s(_monitor);
+
+ // Setup the two tasks
+ shared_ptr<TimerManagerTests::Task> taskToRemove
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 2));
+ timerManager.add(taskToRemove, taskToRemove->_timeout);
+
+ shared_ptr<TimerManagerTests::Task> task
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout));
+ timerManager.add(task, task->_timeout);
+
+ // Remove one task and wait until the other has completed
+ timerManager.remove(taskToRemove);
+ _monitor.wait(timeout * 2);
+
+ assert(!taskToRemove->_done);
+ assert(task->_done);
+
+ return true;
+ }
+
+ /**
+ * This test creates two tasks with the same callback and another one, then removes the two
+ * duplicated then waits for the last one. It then verifies that the timer manager properly
+ * clean up itself and the remaining orphaned timeout task when the manager goes out of scope
+ * and its destructor is called.
+ */
+ bool test02(uint64_t timeout = 1000LL) {
+ TimerManager timerManager;
+ timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+ timerManager.start();
+ assert(timerManager.state() == TimerManager::STARTED);
+
+ Synchronized s(_monitor);
+
+ // Setup the one tasks and add it twice
+ shared_ptr<TimerManagerTests::Task> taskToRemove
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 3));
+ timerManager.add(taskToRemove, taskToRemove->_timeout);
+ timerManager.add(taskToRemove, taskToRemove->_timeout * 2);
+
+ shared_ptr<TimerManagerTests::Task> task
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout));
+ timerManager.add(task, task->_timeout);
+
+ // Remove the first task (e.g. two timers) and wait until the other has completed
+ timerManager.remove(taskToRemove);
+ _monitor.wait(timeout * 2);
+
+ assert(!taskToRemove->_done);
+ assert(task->_done);
+
+ return true;
+ }
+
+ /**
+ * This test creates two tasks, removes the first one then waits for the second one. It then
+ * verifies that the timer manager properly clean up itself and the remaining orphaned timeout
+ * task when the manager goes out of scope and its destructor is called.
+ */
+ bool test03(uint64_t timeout = 1000LL) {
+ TimerManager timerManager;
+ timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+ timerManager.start();
+ assert(timerManager.state() == TimerManager::STARTED);
+
+ Synchronized s(_monitor);
+
+ // Setup the two tasks
+ shared_ptr<TimerManagerTests::Task> taskToRemove
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 2));
+ TimerManager::Timer timer = timerManager.add(taskToRemove, taskToRemove->_timeout);
+
+ shared_ptr<TimerManagerTests::Task> task
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout));
+ timerManager.add(task, task->_timeout);
+
+ // Remove one task and wait until the other has completed
+ timerManager.remove(timer);
+ _monitor.wait(timeout * 2);
+
+ assert(!taskToRemove->_done);
+ assert(task->_done);
+
+ // Verify behavior when removing the removed task
+ try {
+ timerManager.remove(timer);
+ assert(nullptr == "ERROR: This remove should send a NoSuchTaskException exception.");
+ } catch (NoSuchTaskException&) {
+ }
+
+ return true;
+ }
+
+ /**
+ * This test creates one task, and tries to remove it after it has expired.
+ */
+ bool test04(uint64_t timeout = 1000LL) {
+ TimerManager timerManager;
+ timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
+ timerManager.start();
+ assert(timerManager.state() == TimerManager::STARTED);
+
+ Synchronized s(_monitor);
+
+ // Setup the task
+ shared_ptr<TimerManagerTests::Task> task
+ = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 10));
+ TimerManager::Timer timer = timerManager.add(task, task->_timeout);
+ task.reset();
+
+ // Wait until the task has completed
+ _monitor.wait(timeout);
+
+ // Verify behavior when removing the expired task
+ // notify is called inside the task so the task may still
+ // be running when we get here, so we need to loop...
+ for (;;) {
+ try {
+ timerManager.remove(timer);
+ assert(nullptr == "ERROR: This remove should throw NoSuchTaskException, or UncancellableTaskException.");
+ } catch (const NoSuchTaskException&) {
+ break;
+ } catch (const UncancellableTaskException&) {
+ // the thread was still exiting; try again...
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ }
+
+ return true;
+ }
+
+ friend class TestTask;
+
+ Monitor _monitor;
+};
+
+}
+}
+}
+} // apache::thrift::concurrency
diff --git a/src/jaegertracing/thrift/lib/cpp/test/link/LinkTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/link/LinkTest.cpp
new file mode 100644
index 000000000..18e14d10c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/link/LinkTest.cpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+int main(int, char**) {
+ return 0;
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService1.cpp b/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService1.cpp
new file mode 100644
index 000000000..da1790be2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService1.cpp
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * This file is a part of a link test that makes sure generated
+ * templated service headers can be included from multiple
+ * implementation files.
+ */
+
+#include "gen-cpp/ParentService.h"
diff --git a/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService2.cpp b/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService2.cpp
new file mode 100644
index 000000000..da1790be2
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService2.cpp
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * This file is a part of a link test that makes sure generated
+ * templated service headers can be included from multiple
+ * implementation files.
+ */
+
+#include "gen-cpp/ParentService.h"
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.cpp b/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.cpp
new file mode 100644
index 000000000..c75955d27
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.cpp
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "EventLog.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+using namespace apache::thrift::concurrency;
+
+namespace {
+
+// Define environment variable DEBUG_EVENTLOG to enable debug logging
+// ex: $ DEBUG_EVENTLOG=1 processor_test
+static const char * DEBUG_EVENTLOG = getenv("DEBUG_EVENTLOG");
+
+void debug(const char* fmt, ...) {
+ if (DEBUG_EVENTLOG) {
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ }
+}
+}
+
+namespace apache {
+namespace thrift {
+namespace test {
+
+uint32_t EventLog::nextId_ = 0;
+
+#define EVENT_TYPE(value) EventType EventLog::value = #value
+EVENT_TYPE(ET_LOG_END);
+EVENT_TYPE(ET_CONN_CREATED);
+EVENT_TYPE(ET_CONN_DESTROYED);
+EVENT_TYPE(ET_CALL_STARTED);
+EVENT_TYPE(ET_CALL_FINISHED);
+EVENT_TYPE(ET_PROCESS);
+EVENT_TYPE(ET_PRE_READ);
+EVENT_TYPE(ET_POST_READ);
+EVENT_TYPE(ET_PRE_WRITE);
+EVENT_TYPE(ET_POST_WRITE);
+EVENT_TYPE(ET_ASYNC_COMPLETE);
+EVENT_TYPE(ET_HANDLER_ERROR);
+
+EVENT_TYPE(ET_CALL_INCREMENT_GENERATION);
+EVENT_TYPE(ET_CALL_GET_GENERATION);
+EVENT_TYPE(ET_CALL_ADD_STRING);
+EVENT_TYPE(ET_CALL_GET_STRINGS);
+EVENT_TYPE(ET_CALL_GET_DATA_WAIT);
+EVENT_TYPE(ET_CALL_ONEWAY_WAIT);
+EVENT_TYPE(ET_CALL_EXCEPTION_WAIT);
+EVENT_TYPE(ET_CALL_UNEXPECTED_EXCEPTION_WAIT);
+EVENT_TYPE(ET_CALL_SET_VALUE);
+EVENT_TYPE(ET_CALL_GET_VALUE);
+EVENT_TYPE(ET_WAIT_RETURN);
+
+EventLog::EventLog() {
+ id_ = nextId_++;
+ debug("New log: %d", id_);
+}
+
+void EventLog::append(EventType type,
+ uint32_t connectionId,
+ uint32_t callId,
+ const std::string& message) {
+ Synchronized s(monitor_);
+ debug("%d <-- %u, %u, %s \"%s\"", id_, connectionId, callId, type, message.c_str());
+
+ Event e(type, connectionId, callId, message);
+ events_.push_back(e);
+
+ monitor_.notify();
+}
+
+Event EventLog::waitForEvent(int64_t timeout) {
+ Synchronized s(monitor_);
+
+ try {
+ while (events_.empty()) {
+ monitor_.wait(timeout);
+ }
+ } catch (const TimedOutException &) {
+ return Event(ET_LOG_END, 0, 0, "");
+ }
+
+ Event event = events_.front();
+ events_.pop_front();
+ return event;
+}
+
+Event EventLog::waitForConnEvent(uint32_t connId, int64_t timeout) {
+ Synchronized s(monitor_);
+
+ auto it = events_.begin();
+ while (true) {
+ try {
+ // TODO: it would be nicer to honor timeout for the duration of this
+ // call, rather than restarting it for each call to wait(). It shouldn't
+ // be a big problem in practice, though.
+ while (it == events_.end()) {
+ monitor_.wait(timeout);
+ }
+ } catch (const TimedOutException &) {
+ return Event(ET_LOG_END, 0, 0, "");
+ }
+
+ if (it->connectionId == connId) {
+ Event event = *it;
+ events_.erase(it);
+ return event;
+ }
+ }
+}
+}
+}
+} // apache::thrift::test
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.h b/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.h
new file mode 100644
index 000000000..4f8275db9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.h
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_TEST_EVENTLOG_H_
+#define _THRIFT_TEST_EVENTLOG_H_ 1
+
+#include <thrift/concurrency/Monitor.h>
+
+namespace apache {
+namespace thrift {
+namespace test {
+
+// Initially I made EventType an enum, but using char* results
+// in much more readable error messages when there is a mismatch.
+// It also lets users of EventLog easily define their own new types.
+// Comparing the literal pointer values should be safe, barring any strange
+// linking setup that results in duplicate symbols.
+typedef const char* EventType;
+
+struct Event {
+ Event(EventType type, uint32_t connectionId, uint32_t callId, const std::string& message)
+ : type(type), connectionId(connectionId), callId(callId), message(message) {}
+
+ EventType type;
+ uint32_t connectionId;
+ uint32_t callId;
+ std::string message;
+};
+
+class EventLog {
+public:
+ static EventType ET_LOG_END;
+ static EventType ET_CONN_CREATED;
+ static EventType ET_CONN_DESTROYED;
+ static EventType ET_CALL_STARTED;
+ static EventType ET_CALL_FINISHED;
+ static EventType ET_PROCESS;
+ static EventType ET_PRE_READ;
+ static EventType ET_POST_READ;
+ static EventType ET_PRE_WRITE;
+ static EventType ET_POST_WRITE;
+ static EventType ET_ASYNC_COMPLETE;
+ static EventType ET_HANDLER_ERROR;
+
+ static EventType ET_CALL_INCREMENT_GENERATION;
+ static EventType ET_CALL_GET_GENERATION;
+ static EventType ET_CALL_ADD_STRING;
+ static EventType ET_CALL_GET_STRINGS;
+ static EventType ET_CALL_GET_DATA_WAIT;
+ static EventType ET_CALL_ONEWAY_WAIT;
+ static EventType ET_CALL_UNEXPECTED_EXCEPTION_WAIT;
+ static EventType ET_CALL_EXCEPTION_WAIT;
+ static EventType ET_WAIT_RETURN;
+ static EventType ET_CALL_SET_VALUE;
+ static EventType ET_CALL_GET_VALUE;
+
+ EventLog();
+
+ void append(EventType type,
+ uint32_t connectionId,
+ uint32_t callId,
+ const std::string& message = "");
+
+ Event waitForEvent(int64_t timeout = 500);
+ Event waitForConnEvent(uint32_t connId, int64_t timeout = 500);
+
+protected:
+ typedef std::list<Event> EventList;
+
+ concurrency::Monitor monitor_;
+ EventList events_;
+ uint32_t id_;
+
+ static uint32_t nextId_;
+};
+}
+}
+} // apache::thrift::test
+
+#endif // _THRIFT_TEST_EVENTLOG_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/Handlers.h b/src/jaegertracing/thrift/lib/cpp/test/processor/Handlers.h
new file mode 100644
index 000000000..05d19edd9
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/Handlers.h
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_PROCESSOR_TEST_HANDLERS_H_
+#define _THRIFT_PROCESSOR_TEST_HANDLERS_H_ 1
+
+#include "EventLog.h"
+#include "gen-cpp/ParentService.h"
+#include "gen-cpp/ChildService.h"
+
+namespace apache {
+namespace thrift {
+namespace test {
+
+class ParentHandler : virtual public ParentServiceIf {
+public:
+ ParentHandler(const std::shared_ptr<EventLog>& log)
+ : triggerMonitor(&mutex_), generation_(0), wait_(false), log_(log) {}
+
+ int32_t incrementGeneration() override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_INCREMENT_GENERATION, 0, 0);
+ return ++generation_;
+ }
+
+ int32_t getGeneration() override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_GET_GENERATION, 0, 0);
+ return generation_;
+ }
+
+ void addString(const std::string& s) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_ADD_STRING, 0, 0);
+ strings_.push_back(s);
+ }
+
+ void getStrings(std::vector<std::string>& _return) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_GET_STRINGS, 0, 0);
+ _return = strings_;
+ }
+
+ void getDataWait(std::string& _return, const int32_t length) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_GET_DATA_WAIT, 0, 0);
+
+ blockUntilTriggered();
+
+ _return.append(length, 'a');
+ }
+
+ void onewayWait() override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_ONEWAY_WAIT, 0, 0);
+
+ blockUntilTriggered();
+ }
+
+ void exceptionWait(const std::string& message) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_EXCEPTION_WAIT, 0, 0);
+
+ blockUntilTriggered();
+
+ MyError e;
+ e.message = message;
+ throw e;
+ }
+
+ void unexpectedExceptionWait(const std::string& message) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_UNEXPECTED_EXCEPTION_WAIT, 0, 0);
+
+ blockUntilTriggered();
+
+ MyError e;
+ e.message = message;
+ throw e;
+ }
+
+ /**
+ * After prepareTriggeredCall() is invoked, calls to any of the *Wait()
+ * functions won't return until triggerPendingCalls() is invoked
+ *
+ * This has to be a separate function invoked by the main test thread
+ * in order to to avoid race conditions.
+ */
+ void prepareTriggeredCall() {
+ concurrency::Guard g(mutex_);
+ wait_ = true;
+ }
+
+ /**
+ * Wake up all calls waiting in blockUntilTriggered()
+ */
+ void triggerPendingCalls() {
+ concurrency::Guard g(mutex_);
+ wait_ = false;
+ triggerMonitor.notifyAll();
+ }
+
+protected:
+ /**
+ * blockUntilTriggered() won't return until triggerPendingCalls() is invoked
+ * in another thread.
+ *
+ * This should only be called when already holding mutex_.
+ */
+ void blockUntilTriggered() {
+ while (wait_) {
+ triggerMonitor.waitForever();
+ }
+
+ // Log an event when we return
+ log_->append(EventLog::ET_WAIT_RETURN, 0, 0);
+ }
+
+ concurrency::Mutex mutex_;
+ concurrency::Monitor triggerMonitor;
+ int32_t generation_;
+ bool wait_;
+ std::vector<std::string> strings_;
+ std::shared_ptr<EventLog> log_;
+};
+
+#ifdef _WIN32
+ #pragma warning( push )
+ #pragma warning (disable : 4250 ) //inheriting methods via dominance
+#endif
+
+class ChildHandler : public ParentHandler, virtual public ChildServiceIf {
+public:
+ ChildHandler(const std::shared_ptr<EventLog>& log) : ParentHandler(log), value_(0) {}
+
+ int32_t setValue(const int32_t value) override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_SET_VALUE, 0, 0);
+
+ int32_t oldValue = value_;
+ value_ = value;
+ return oldValue;
+ }
+
+ int32_t getValue() override {
+ concurrency::Guard g(mutex_);
+ log_->append(EventLog::ET_CALL_GET_VALUE, 0, 0);
+
+ return value_;
+ }
+
+protected:
+ int32_t value_;
+};
+
+#ifdef _WIN32
+ #pragma warning( pop )
+#endif
+
+struct ConnContext {
+public:
+ ConnContext(std::shared_ptr<protocol::TProtocol> in,
+ std::shared_ptr<protocol::TProtocol> out,
+ uint32_t id)
+ : input(in), output(out), id(id) {}
+
+ std::shared_ptr<protocol::TProtocol> input;
+ std::shared_ptr<protocol::TProtocol> output;
+ uint32_t id;
+};
+
+struct CallContext {
+public:
+ CallContext(ConnContext* context, uint32_t id, const std::string& name)
+ : connContext(context), name(name), id(id) {}
+
+ ConnContext* connContext;
+ std::string name;
+ uint32_t id;
+};
+
+class ServerEventHandler : public server::TServerEventHandler {
+public:
+ ServerEventHandler(const std::shared_ptr<EventLog>& log) : nextId_(1), log_(log) {}
+
+ void preServe() override {}
+
+ void* createContext(std::shared_ptr<protocol::TProtocol> input,
+ std::shared_ptr<protocol::TProtocol> output) override {
+ ConnContext* context = new ConnContext(input, output, nextId_);
+ ++nextId_;
+ log_->append(EventLog::ET_CONN_CREATED, context->id, 0);
+ return context;
+ }
+
+ void deleteContext(void* serverContext,
+ std::shared_ptr<protocol::TProtocol> input,
+ std::shared_ptr<protocol::TProtocol> output) override {
+ auto* context = reinterpret_cast<ConnContext*>(serverContext);
+
+ if (input != context->input) {
+ abort();
+ }
+ if (output != context->output) {
+ abort();
+ }
+
+ log_->append(EventLog::ET_CONN_DESTROYED, context->id, 0);
+
+ delete context;
+ }
+
+ void processContext(void* serverContext,
+ std::shared_ptr<transport::TTransport> transport) override {
+// TODO: We currently don't test the behavior of the processContext()
+// calls. The various server implementations call processContext() at
+// slightly different times, and it is too annoying to try and account for
+// their various differences.
+//
+// TThreadedServer, TThreadPoolServer, and TSimpleServer usually wait until
+// they see the first byte of a request before calling processContext().
+// However, they don't wait for the first byte of the very first request,
+// and instead immediately call processContext() before any data is
+// received.
+//
+// TNonblockingServer always waits until receiving the full request before
+// calling processContext().
+#if 0
+ ConnContext* context = reinterpret_cast<ConnContext*>(serverContext);
+ log_->append(EventLog::ET_PROCESS, context->id, 0);
+#else
+ THRIFT_UNUSED_VARIABLE(serverContext);
+ THRIFT_UNUSED_VARIABLE(transport);
+#endif
+ }
+
+protected:
+ uint32_t nextId_;
+ std::shared_ptr<EventLog> log_;
+};
+
+class ProcessorEventHandler : public TProcessorEventHandler {
+public:
+ ProcessorEventHandler(const std::shared_ptr<EventLog>& log) : nextId_(1), log_(log) {}
+
+ void* getContext(const char* fnName, void* serverContext) override {
+ auto* connContext = reinterpret_cast<ConnContext*>(serverContext);
+
+ CallContext* context = new CallContext(connContext, nextId_, fnName);
+ ++nextId_;
+
+ log_->append(EventLog::ET_CALL_STARTED, connContext->id, context->id, fnName);
+ return context;
+ }
+
+ void freeContext(void* ctx, const char* fnName) override {
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_CALL_FINISHED, context->connContext->id, context->id, fnName);
+ delete context;
+ }
+
+ void preRead(void* ctx, const char* fnName) override {
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_PRE_READ, context->connContext->id, context->id, fnName);
+ }
+
+ void postRead(void* ctx, const char* fnName, uint32_t bytes) override {
+ THRIFT_UNUSED_VARIABLE(bytes);
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_POST_READ, context->connContext->id, context->id, fnName);
+ }
+
+ void preWrite(void* ctx, const char* fnName) override {
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_PRE_WRITE, context->connContext->id, context->id, fnName);
+ }
+
+ void postWrite(void* ctx, const char* fnName, uint32_t bytes) override {
+ THRIFT_UNUSED_VARIABLE(bytes);
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_POST_WRITE, context->connContext->id, context->id, fnName);
+ }
+
+ void asyncComplete(void* ctx, const char* fnName) override {
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_ASYNC_COMPLETE, context->connContext->id, context->id, fnName);
+ }
+
+ void handlerError(void* ctx, const char* fnName) override {
+ auto* context = reinterpret_cast<CallContext*>(ctx);
+ checkName(context, fnName);
+ log_->append(EventLog::ET_HANDLER_ERROR, context->connContext->id, context->id, fnName);
+ }
+
+protected:
+ void checkName(const CallContext* context, const char* fnName) {
+ // Note: we can't use BOOST_CHECK_EQUAL here, since the handler runs in a
+ // different thread from the test functions. Just abort if the names are
+ // different
+ if (context->name != fnName) {
+ fprintf(stderr,
+ "call context name mismatch: \"%s\" != \"%s\"\n",
+ context->name.c_str(),
+ fnName);
+ fflush(stderr);
+ abort();
+ }
+ }
+
+ uint32_t nextId_;
+ std::shared_ptr<EventLog> log_;
+};
+}
+}
+} // apache::thrift::test
+
+#endif // _THRIFT_PROCESSOR_TEST_HANDLERS_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/ProcessorTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/processor/ProcessorTest.cpp
new file mode 100644
index 000000000..a36ef3eec
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/ProcessorTest.cpp
@@ -0,0 +1,929 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * This file contains tests that ensure TProcessorEventHandler and
+ * TServerEventHandler are invoked properly by the various server
+ * implementations.
+ */
+
+#include <boost/test/unit_test.hpp>
+
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/Monitor.h>
+#include <thrift/protocol/TBinaryProtocol.h>
+#include <thrift/server/TThreadedServer.h>
+#include <thrift/server/TThreadPoolServer.h>
+#include <thrift/server/TNonblockingServer.h>
+#include <thrift/server/TSimpleServer.h>
+#include <thrift/transport/TSocket.h>
+#include <thrift/transport/TNonblockingServerSocket.h>
+
+#include "EventLog.h"
+#include "ServerThread.h"
+#include "Handlers.h"
+#include "gen-cpp/ChildService.h"
+
+using namespace apache::thrift;
+using namespace apache::thrift::concurrency;
+using namespace apache::thrift::protocol;
+using namespace apache::thrift::server;
+using namespace apache::thrift::test;
+using namespace apache::thrift::transport;
+using std::string;
+using std::vector;
+
+/*
+ * Traits classes that encapsulate how to create various types of servers.
+ */
+
+class TSimpleServerTraits {
+public:
+ typedef TSimpleServer ServerType;
+
+ std::shared_ptr<TSimpleServer> createServer(
+ const std::shared_ptr<TProcessor>& processor,
+ uint16_t port,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory) {
+ std::shared_ptr<TServerSocket> socket(new TServerSocket(port));
+ return std::shared_ptr<TSimpleServer>(
+ new TSimpleServer(processor, socket, transportFactory, protocolFactory));
+ }
+};
+
+class TThreadedServerTraits {
+public:
+ typedef TThreadedServer ServerType;
+
+ std::shared_ptr<TThreadedServer> createServer(
+ const std::shared_ptr<TProcessor>& processor,
+ uint16_t port,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory) {
+ std::shared_ptr<TServerSocket> socket(new TServerSocket(port));
+ return std::shared_ptr<TThreadedServer>(
+ new TThreadedServer(processor, socket, transportFactory, protocolFactory));
+ }
+};
+
+class TThreadPoolServerTraits {
+public:
+ typedef TThreadPoolServer ServerType;
+
+ std::shared_ptr<TThreadPoolServer> createServer(
+ const std::shared_ptr<TProcessor>& processor,
+ uint16_t port,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory) {
+ std::shared_ptr<TServerSocket> socket(new TServerSocket(port));
+
+ std::shared_ptr<ThreadFactory> threadFactory(new ThreadFactory);
+ std::shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(8);
+ threadManager->threadFactory(threadFactory);
+ threadManager->start();
+
+ return std::shared_ptr<TThreadPoolServer>(
+ new TThreadPoolServer(processor, socket, transportFactory, protocolFactory, threadManager));
+ }
+};
+
+class TNonblockingServerTraits {
+public:
+ typedef TNonblockingServer ServerType;
+
+ std::shared_ptr<TNonblockingServer> createServer(
+ const std::shared_ptr<TProcessor>& processor,
+ uint16_t port,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory) {
+ // TNonblockingServer automatically uses TFramedTransport.
+ // Raise an exception if the supplied transport factory is not a
+ // TFramedTransportFactory
+ auto* framedFactory
+ = dynamic_cast<TFramedTransportFactory*>(transportFactory.get());
+ if (framedFactory == nullptr) {
+ throw TException("TNonblockingServer must use TFramedTransport");
+ }
+
+ std::shared_ptr<TNonblockingServerSocket> socket(new TNonblockingServerSocket(port));
+ std::shared_ptr<ThreadFactory> threadFactory(new ThreadFactory);
+ std::shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(8);
+ threadManager->threadFactory(threadFactory);
+ threadManager->start();
+
+ return std::shared_ptr<TNonblockingServer>(
+ new TNonblockingServer(processor, protocolFactory, socket, threadManager));
+ }
+};
+
+class TNonblockingServerNoThreadsTraits {
+public:
+ typedef TNonblockingServer ServerType;
+
+ std::shared_ptr<TNonblockingServer> createServer(
+ const std::shared_ptr<TProcessor>& processor,
+ uint16_t port,
+ const std::shared_ptr<TTransportFactory>& transportFactory,
+ const std::shared_ptr<TProtocolFactory>& protocolFactory) {
+ // TNonblockingServer automatically uses TFramedTransport.
+ // Raise an exception if the supplied transport factory is not a
+ // TFramedTransportFactory
+ auto* framedFactory
+ = dynamic_cast<TFramedTransportFactory*>(transportFactory.get());
+ if (framedFactory == nullptr) {
+ throw TException("TNonblockingServer must use TFramedTransport");
+ }
+
+ std::shared_ptr<TNonblockingServerSocket> socket(new TNonblockingServerSocket(port));
+ // Use a NULL ThreadManager
+ std::shared_ptr<ThreadManager> threadManager;
+ return std::shared_ptr<TNonblockingServer>(
+ new TNonblockingServer(processor, protocolFactory, socket, threadManager));
+ }
+};
+
+/*
+ * Traits classes for controlling if we instantiate templated or generic
+ * protocol factories, processors, clients, etc.
+ *
+ * The goal is to allow the outer test code to select which server type is
+ * being tested, and whether or not we are testing the templated classes, or
+ * the generic classes.
+ *
+ * Each specific test case can control whether we create a child or parent
+ * server, and whether we use TFramedTransport or TBufferedTransport.
+ */
+
+class UntemplatedTraits {
+public:
+ typedef TBinaryProtocolFactory ProtocolFactory;
+ typedef TBinaryProtocol Protocol;
+
+ typedef ParentServiceProcessor ParentProcessor;
+ typedef ChildServiceProcessor ChildProcessor;
+ typedef ParentServiceClient ParentClient;
+ typedef ChildServiceClient ChildClient;
+};
+
+class TemplatedTraits {
+public:
+ typedef TBinaryProtocolFactoryT<TBufferBase> ProtocolFactory;
+ typedef TBinaryProtocolT<TBufferBase> Protocol;
+
+ typedef ParentServiceProcessorT<Protocol> ParentProcessor;
+ typedef ChildServiceProcessorT<Protocol> ChildProcessor;
+ typedef ParentServiceClientT<Protocol> ParentClient;
+ typedef ChildServiceClientT<Protocol> ChildClient;
+};
+
+template <typename TemplateTraits_>
+class ParentServiceTraits {
+public:
+ typedef typename TemplateTraits_::ParentProcessor Processor;
+ typedef typename TemplateTraits_::ParentClient Client;
+ typedef ParentHandler Handler;
+
+ typedef typename TemplateTraits_::ProtocolFactory ProtocolFactory;
+ typedef typename TemplateTraits_::Protocol Protocol;
+};
+
+template <typename TemplateTraits_>
+class ChildServiceTraits {
+public:
+ typedef typename TemplateTraits_::ChildProcessor Processor;
+ typedef typename TemplateTraits_::ChildClient Client;
+ typedef ChildHandler Handler;
+
+ typedef typename TemplateTraits_::ProtocolFactory ProtocolFactory;
+ typedef typename TemplateTraits_::Protocol Protocol;
+};
+
+// TODO: It would be nicer if the TTransportFactory types defined a typedef,
+// to allow us to figure out the exact transport type without having to pass it
+// in as a separate template parameter here.
+//
+// It would also be niec if they used covariant return types. Unfortunately,
+// since they return shared_ptr instead of raw pointers, covariant return types
+// won't work.
+template <typename ServerTraits_,
+ typename ServiceTraits_,
+ typename TransportFactory_ = TFramedTransportFactory,
+ typename Transport_ = TFramedTransport>
+class ServiceState : public ServerState {
+public:
+ typedef typename ServiceTraits_::Processor Processor;
+ typedef typename ServiceTraits_::Client Client;
+ typedef typename ServiceTraits_::Handler Handler;
+
+ ServiceState()
+ : port_(0),
+ log_(new EventLog),
+ handler_(new Handler(log_)),
+ processor_(new Processor(handler_)),
+ transportFactory_(new TransportFactory_),
+ protocolFactory_(new typename ServiceTraits_::ProtocolFactory),
+ serverEventHandler_(new ServerEventHandler(log_)),
+ processorEventHandler_(new ProcessorEventHandler(log_)) {
+ processor_->setEventHandler(processorEventHandler_);
+ }
+
+ std::shared_ptr<TServer> createServer(uint16_t port) override {
+ ServerTraits_ serverTraits;
+ return serverTraits.createServer(processor_, port, transportFactory_, protocolFactory_);
+ }
+
+ std::shared_ptr<TServerEventHandler> getServerEventHandler() override { return serverEventHandler_; }
+
+ void bindSuccessful(uint16_t port) override { port_ = port; }
+
+ uint16_t getPort() const { return port_; }
+
+ const std::shared_ptr<EventLog>& getLog() const { return log_; }
+
+ const std::shared_ptr<Handler>& getHandler() const { return handler_; }
+
+ std::shared_ptr<Client> createClient() {
+ typedef typename ServiceTraits_::Protocol Protocol;
+
+ std::shared_ptr<TSocket> socket(new TSocket("127.0.0.1", port_));
+ std::shared_ptr<Transport_> transport(new Transport_(socket));
+ std::shared_ptr<Protocol> protocol(new Protocol(transport));
+ transport->open();
+
+ std::shared_ptr<Client> client(new Client(protocol));
+ return client;
+ }
+
+private:
+ uint16_t port_;
+ std::shared_ptr<EventLog> log_;
+ std::shared_ptr<Handler> handler_;
+ std::shared_ptr<Processor> processor_;
+ std::shared_ptr<TTransportFactory> transportFactory_;
+ std::shared_ptr<TProtocolFactory> protocolFactory_;
+ std::shared_ptr<TServerEventHandler> serverEventHandler_;
+ std::shared_ptr<TProcessorEventHandler> processorEventHandler_;
+};
+
+/**
+ * Check that there are no more events in the log
+ */
+void checkNoEvents(const std::shared_ptr<EventLog>& log) {
+ // Wait for an event with a very short timeout period. We don't expect
+ // anything to be present, so we will normally wait for the full timeout.
+ // On the other hand, a non-zero timeout is nice since it does give a short
+ // window for events to arrive in case there is a problem.
+ Event event = log->waitForEvent(10);
+ BOOST_CHECK_EQUAL(EventLog::ET_LOG_END, event.type);
+}
+
+/**
+ * Check for the events that should be logged when a new connection is created.
+ *
+ * Returns the connection ID allocated by the server.
+ */
+uint32_t checkNewConnEvents(const std::shared_ptr<EventLog>& log) {
+ // Check for an ET_CONN_CREATED event
+ Event event = log->waitForEvent(2500);
+ BOOST_CHECK_EQUAL(EventLog::ET_CONN_CREATED, event.type);
+
+ // Some servers call the processContext() hook immediately.
+ // Others (TNonblockingServer) only call it once a full request is received.
+ // We don't check for it yet, to allow either behavior.
+
+ return event.connectionId;
+}
+
+/**
+ * Check for the events that should be logged when a connection is closed.
+ */
+void checkCloseEvents(const std::shared_ptr<EventLog>& log, uint32_t connId) {
+ // Check for an ET_CONN_DESTROYED event
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CONN_DESTROYED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+
+ // Make sure there are no more events
+ checkNoEvents(log);
+}
+
+/**
+ * Check for the events that should be logged when a call is received
+ * and the handler is invoked.
+ *
+ * It does not check for anything after the handler invocation.
+ *
+ * Returns the call ID allocated by the server.
+ */
+uint32_t checkCallHandlerEvents(const std::shared_ptr<EventLog>& log,
+ uint32_t connId,
+ EventType callType,
+ const string& callName) {
+ // Call started
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_STARTED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+ uint32_t callId = event.callId;
+
+ // Pre-read
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_PRE_READ, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // Post-read
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_POST_READ, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // Handler invocation
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(callType, event.type);
+ // The handler doesn't have any connection or call context,
+ // so the connectionId and callId in this event aren't valid
+
+ return callId;
+}
+
+/**
+ * Check for the events that should be after a handler returns.
+ */
+void checkCallPostHandlerEvents(const std::shared_ptr<EventLog>& log,
+ uint32_t connId,
+ uint32_t callId,
+ const string& callName) {
+ // Pre-write
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_PRE_WRITE, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // Post-write
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_POST_WRITE, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // Call finished
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // It is acceptable for servers to call processContext() again immediately
+ // to start waiting on the next request. However, some servers wait before
+ // getting either a partial request or the full request before calling
+ // processContext(). We don't check for the next call to processContext()
+ // yet.
+}
+
+/**
+ * Check for the events that should be logged when a call is made.
+ *
+ * This just calls checkCallHandlerEvents() followed by
+ * checkCallPostHandlerEvents().
+ *
+ * Returns the call ID allocated by the server.
+ */
+uint32_t checkCallEvents(const std::shared_ptr<EventLog>& log,
+ uint32_t connId,
+ EventType callType,
+ const string& callName) {
+ uint32_t callId = checkCallHandlerEvents(log, connId, callType, callName);
+ checkCallPostHandlerEvents(log, connId, callId, callName);
+
+ return callId;
+}
+
+/*
+ * Test functions
+ */
+
+template <typename State_>
+void testParentService(const std::shared_ptr<State_>& state) {
+ std::shared_ptr<typename State_::Client> client = state->createClient();
+
+ int32_t gen = client->getGeneration();
+ int32_t newGen = client->incrementGeneration();
+ BOOST_CHECK_EQUAL(gen + 1, newGen);
+ newGen = client->getGeneration();
+ BOOST_CHECK_EQUAL(gen + 1, newGen);
+
+ client->addString("foo");
+ client->addString("bar");
+ client->addString("asdf");
+
+ vector<string> strings;
+ client->getStrings(strings);
+ BOOST_REQUIRE_EQUAL(3, strings.size());
+ BOOST_REQUIRE_EQUAL("foo", strings[0]);
+ BOOST_REQUIRE_EQUAL("bar", strings[1]);
+ BOOST_REQUIRE_EQUAL("asdf", strings[2]);
+}
+
+template <typename State_>
+void testChildService(const std::shared_ptr<State_>& state) {
+ std::shared_ptr<typename State_::Client> client = state->createClient();
+
+ // Test calling some of the parent methids via the a child client
+ int32_t gen = client->getGeneration();
+ int32_t newGen = client->incrementGeneration();
+ BOOST_CHECK_EQUAL(gen + 1, newGen);
+ newGen = client->getGeneration();
+ BOOST_CHECK_EQUAL(gen + 1, newGen);
+
+ // Test some of the child methods
+ client->setValue(10);
+ BOOST_CHECK_EQUAL(10, client->getValue());
+ BOOST_CHECK_EQUAL(10, client->setValue(99));
+ BOOST_CHECK_EQUAL(99, client->getValue());
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testBasicService() {
+ typedef ServiceState<ServerTraits, ParentServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ testParentService(state);
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testInheritedService() {
+ typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ testParentService(state);
+ testChildService(state);
+}
+
+/**
+ * Test to make sure that the TServerEventHandler and TProcessorEventHandler
+ * methods are invoked in the correct order with the actual events.
+ */
+template <typename ServerTraits, typename TemplateTraits>
+void testEventSequencing() {
+ // We use TBufferedTransport for this test, instead of TFramedTransport.
+ // This way the server will start processing data as soon as it is received,
+ // instead of waiting for the full request. This is necessary so we can
+ // separate the preRead() and postRead() events.
+ typedef ServiceState<ServerTraits,
+ ChildServiceTraits<TemplateTraits>,
+ TBufferedTransportFactory,
+ TBufferedTransport> State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ const std::shared_ptr<EventLog>& log = state->getLog();
+
+ // Make sure we're at the end of the log
+ checkNoEvents(log);
+
+ state->getHandler()->prepareTriggeredCall();
+
+ // Make sure createContext() is called after a connection has been
+ // established. We open a plain socket instead of creating a client.
+ std::shared_ptr<TSocket> socket(new TSocket("127.0.0.1", state->getPort()));
+ socket->open();
+
+ // Make sure the proper events occurred after a new connection
+ uint32_t connId = checkNewConnEvents(log);
+
+ // Send a message header. We manually construct the request so that we
+ // can test the timing for the preRead() call.
+ string requestName = "getDataWait";
+ string eventName = "ParentService.getDataWait";
+ auto seqid = int32_t(time(nullptr));
+ TBinaryProtocol protocol(socket);
+ protocol.writeMessageBegin(requestName, T_CALL, seqid);
+ socket->flush();
+
+ // Make sure we saw the call started and pre-read events
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_STARTED, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ uint32_t callId = event.callId;
+
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_PRE_READ, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+
+ // Make sure there are no new events
+ checkNoEvents(log);
+
+ // Send the rest of the request
+ protocol.writeStructBegin("ParentService_getDataNotified_pargs");
+ protocol.writeFieldBegin("length", apache::thrift::protocol::T_I32, 1);
+ protocol.writeI32(8 * 1024 * 1024);
+ protocol.writeFieldEnd();
+ protocol.writeFieldStop();
+ protocol.writeStructEnd();
+ protocol.writeMessageEnd();
+ socket->writeEnd();
+ socket->flush();
+
+ // We should then see postRead()
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_POST_READ, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+
+ // Then the handler should be invoked
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_GET_DATA_WAIT, event.type);
+
+ // The handler won't respond until we notify it.
+ // Make sure there are no more events.
+ checkNoEvents(log);
+
+ // Notify the handler that it should return
+ // We just use a global lock for now, since it is easiest
+ state->getHandler()->triggerPendingCalls();
+
+ // The handler will log a separate event before it returns
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
+
+ // We should then see preWrite()
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_PRE_WRITE, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+
+ // We requested more data than can be buffered, and we aren't reading it,
+ // so the server shouldn't be able to finish its write yet.
+ // Make sure there are no more events.
+ checkNoEvents(log);
+
+ // Read the response header
+ string responseName;
+ int32_t responseSeqid = 0;
+ apache::thrift::protocol::TMessageType responseType;
+ protocol.readMessageBegin(responseName, responseType, responseSeqid);
+ BOOST_CHECK_EQUAL(responseSeqid, seqid);
+ BOOST_CHECK_EQUAL(requestName, responseName);
+ BOOST_CHECK_EQUAL(responseType, T_REPLY);
+ // Read the body. We just ignore it for now.
+ protocol.skip(T_STRUCT);
+
+ // Now that we have read, the server should have finished sending the data
+ // and called the postWrite() handler
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_POST_WRITE, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+
+ // Call finished should be last
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
+ BOOST_CHECK_EQUAL(eventName, event.message);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+
+ // There should be no more events
+ checkNoEvents(log);
+
+ // Close the connection, and make sure we get a connection destroyed event
+ socket->close();
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CONN_DESTROYED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+
+ // There should be no more events
+ checkNoEvents(log);
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testSeparateConnections() {
+ typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ const std::shared_ptr<EventLog>& log = state->getLog();
+
+ // Create a client
+ std::shared_ptr<typename State::Client> client1 = state->createClient();
+
+ // Make sure the expected events were logged
+ uint32_t client1Id = checkNewConnEvents(log);
+
+ // Create a second client
+ std::shared_ptr<typename State::Client> client2 = state->createClient();
+
+ // Make sure the expected events were logged
+ uint32_t client2Id = checkNewConnEvents(log);
+
+ // The two connections should have different IDs
+ BOOST_CHECK_NE(client1Id, client2Id);
+
+ // Make a call, and check for the proper events
+ int32_t value = 5;
+ client1->setValue(value);
+ uint32_t call1
+ = checkCallEvents(log, client1Id, EventLog::ET_CALL_SET_VALUE, "ChildService.setValue");
+
+ // Make a call with client2
+ int32_t v = client2->getValue();
+ BOOST_CHECK_EQUAL(value, v);
+ checkCallEvents(log, client2Id, EventLog::ET_CALL_GET_VALUE, "ChildService.getValue");
+
+ // Make another call with client1
+ v = client1->getValue();
+ BOOST_CHECK_EQUAL(value, v);
+ uint32_t call2
+ = checkCallEvents(log, client1Id, EventLog::ET_CALL_GET_VALUE, "ChildService.getValue");
+ BOOST_CHECK_NE(call1, call2);
+
+ // Close the second client, and check for the appropriate events
+ client2.reset();
+ checkCloseEvents(log, client2Id);
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testOnewayCall() {
+ typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ const std::shared_ptr<EventLog>& log = state->getLog();
+
+ // Create a client
+ std::shared_ptr<typename State::Client> client = state->createClient();
+ uint32_t connId = checkNewConnEvents(log);
+
+ // Make a oneway call
+ // It should return immediately, even though the server's handler
+ // won't return right away
+ state->getHandler()->prepareTriggeredCall();
+ client->onewayWait();
+ string callName = "ParentService.onewayWait";
+ uint32_t callId = checkCallHandlerEvents(log, connId, EventLog::ET_CALL_ONEWAY_WAIT, callName);
+
+ // There shouldn't be any more events
+ checkNoEvents(log);
+
+ // Trigger the handler to return
+ state->getHandler()->triggerPendingCalls();
+
+ // The handler will log an ET_WAIT_RETURN event when it wakes up
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
+
+ // Now we should see the async complete event, then call finished
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_ASYNC_COMPLETE, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // Destroy the client, and check for connection closed events
+ client.reset();
+ checkCloseEvents(log, connId);
+
+ checkNoEvents(log);
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testExpectedError() {
+ typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ const std::shared_ptr<EventLog>& log = state->getLog();
+
+ // Create a client
+ std::shared_ptr<typename State::Client> client = state->createClient();
+ uint32_t connId = checkNewConnEvents(log);
+
+ // Send the exceptionWait() call
+ state->getHandler()->prepareTriggeredCall();
+ string message = "test 1234 test";
+ client->send_exceptionWait(message);
+ string callName = "ParentService.exceptionWait";
+ uint32_t callId = checkCallHandlerEvents(log, connId, EventLog::ET_CALL_EXCEPTION_WAIT, callName);
+
+ // There shouldn't be any more events
+ checkNoEvents(log);
+
+ // Trigger the handler to return
+ state->getHandler()->triggerPendingCalls();
+
+ // The handler will log an ET_WAIT_RETURN event when it wakes up
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
+
+ // Now receive the response
+ try {
+ client->recv_exceptionWait();
+ BOOST_FAIL("expected MyError to be thrown");
+ } catch (const MyError& e) {
+ BOOST_CHECK_EQUAL(message, e.message);
+ // Check if std::exception::what() is handled properly
+ size_t message_pos = string(e.what()).find("TException - service has thrown: MyError");
+ BOOST_CHECK_NE(message_pos, string::npos);
+ }
+
+ // Now we should see the events for a normal call finish
+ checkCallPostHandlerEvents(log, connId, callId, callName);
+
+ // There shouldn't be any more events
+ checkNoEvents(log);
+
+ // Destroy the client, and check for connection closed events
+ client.reset();
+ checkCloseEvents(log, connId);
+
+ checkNoEvents(log);
+}
+
+template <typename ServerTraits, typename TemplateTraits>
+void testUnexpectedError() {
+ typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
+
+ // Start the server
+ std::shared_ptr<State> state(new State);
+ ServerThread serverThread(state, true);
+
+ const std::shared_ptr<EventLog>& log = state->getLog();
+
+ // Create a client
+ std::shared_ptr<typename State::Client> client = state->createClient();
+ uint32_t connId = checkNewConnEvents(log);
+
+ // Send the unexpectedExceptionWait() call
+ state->getHandler()->prepareTriggeredCall();
+ string message = "1234 test 5678";
+ client->send_unexpectedExceptionWait(message);
+ string callName = "ParentService.unexpectedExceptionWait";
+ uint32_t callId
+ = checkCallHandlerEvents(log, connId, EventLog::ET_CALL_UNEXPECTED_EXCEPTION_WAIT, callName);
+
+ // There shouldn't be any more events
+ checkNoEvents(log);
+
+ // Trigger the handler to return
+ state->getHandler()->triggerPendingCalls();
+
+ // The handler will log an ET_WAIT_RETURN event when it wakes up
+ Event event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
+
+ // Now receive the response
+ try {
+ client->recv_unexpectedExceptionWait();
+ BOOST_FAIL("expected TApplicationError to be thrown");
+ } catch (const TApplicationException&) {
+ }
+
+ // Now we should see a handler error event
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_HANDLER_ERROR, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // pre-write and post-write events aren't generated after a handler error
+ // (Even for non-oneway calls where a response is written.)
+ //
+ // A call finished event is logged when the call context is destroyed
+ event = log->waitForEvent();
+ BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
+ BOOST_CHECK_EQUAL(connId, event.connectionId);
+ BOOST_CHECK_EQUAL(callId, event.callId);
+ BOOST_CHECK_EQUAL(callName, event.message);
+
+ // There shouldn't be any more events
+ checkNoEvents(log);
+
+ // Destroy the client, and check for connection closed events
+ client.reset();
+ checkCloseEvents(log, connId);
+
+ checkNoEvents(log);
+}
+
+// Macro to define simple tests that can be used with all server types
+#define DEFINE_SIMPLE_TESTS(Server, Template) \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_basicService) { \
+ testBasicService<Server##Traits, Template##Traits>(); \
+ } \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_inheritedService) { \
+ testInheritedService<Server##Traits, Template##Traits>(); \
+ } \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_oneway) { \
+ testOnewayCall<Server##Traits, Template##Traits>(); \
+ } \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_exception) { \
+ testExpectedError<Server##Traits, Template##Traits>(); \
+ } \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_unexpectedException) { \
+ testUnexpectedError<Server##Traits, Template##Traits>(); \
+ }
+
+// Tests that require the server to process multiple connections concurrently
+// (i.e., not TSimpleServer)
+#define DEFINE_CONCURRENT_SERVER_TESTS(Server, Template) \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_separateConnections) { \
+ testSeparateConnections<Server##Traits, Template##Traits>(); \
+ }
+
+// The testEventSequencing() test manually generates a request for the server,
+// and doesn't work with TFramedTransport. Therefore we can't test it with
+// TNonblockingServer.
+#define DEFINE_NOFRAME_TESTS(Server, Template) \
+ BOOST_AUTO_TEST_CASE(Server##_##Template##_eventSequencing) { \
+ testEventSequencing<Server##Traits, Template##Traits>(); \
+ }
+
+#define DEFINE_TNONBLOCKINGSERVER_TESTS(Server, Template) \
+ DEFINE_SIMPLE_TESTS(Server, Template) \
+ DEFINE_CONCURRENT_SERVER_TESTS(Server, Template)
+
+#define DEFINE_ALL_SERVER_TESTS(Server, Template) \
+ DEFINE_SIMPLE_TESTS(Server, Template) \
+ DEFINE_CONCURRENT_SERVER_TESTS(Server, Template) \
+ DEFINE_NOFRAME_TESTS(Server, Template)
+
+DEFINE_ALL_SERVER_TESTS(TThreadedServer, Templated)
+DEFINE_ALL_SERVER_TESTS(TThreadedServer, Untemplated)
+DEFINE_ALL_SERVER_TESTS(TThreadPoolServer, Templated)
+DEFINE_ALL_SERVER_TESTS(TThreadPoolServer, Untemplated)
+
+DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServer, Templated)
+DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServer, Untemplated)
+DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServerNoThreads, Templated)
+DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServerNoThreads, Untemplated)
+
+DEFINE_SIMPLE_TESTS(TSimpleServer, Templated)
+DEFINE_SIMPLE_TESTS(TSimpleServer, Untemplated)
+DEFINE_NOFRAME_TESTS(TSimpleServer, Templated)
+DEFINE_NOFRAME_TESTS(TSimpleServer, Untemplated)
+
+// TODO: We should test TEventServer in the future.
+// For now, it is known not to work correctly with TProcessorEventHandler.
+#ifdef BOOST_TEST_DYN_LINK
+bool init_unit_test_suite() {
+ ::boost::unit_test::framework::master_test_suite().p_name.value = "ProcessorTest";
+ return true;
+}
+
+int main( int argc, char* argv[] ) {
+ return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
+}
+#else
+::boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
+ THRIFT_UNUSED_VARIABLE(argc);
+ THRIFT_UNUSED_VARIABLE(argv);
+ ::boost::unit_test::framework::master_test_suite().p_name.value = "ProcessorTest";
+ return NULL;
+}
+#endif
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.cpp b/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.cpp
new file mode 100644
index 000000000..b0505005b
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.cpp
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_TEST_SERVERTHREAD_TCC_
+#define _THRIFT_TEST_SERVERTHREAD_TCC_ 1
+
+#include "ServerThread.h"
+
+#include <thrift/concurrency/ThreadFactory.h>
+#include <thrift/concurrency/ThreadManager.h>
+#include <thrift/server/TThreadPoolServer.h>
+#include <thrift/transport/TBufferTransports.h>
+#include <thrift/transport/TServerSocket.h>
+
+namespace apache {
+namespace thrift {
+namespace test {
+
+void ServerThread::start() {
+ assert(!running_);
+ running_ = true;
+
+ helper_.reset(new Helper(this));
+
+ // Start the other thread
+ concurrency::ThreadFactory threadFactory;
+ threadFactory.setDetached(false);
+ thread_ = threadFactory.newThread(helper_);
+
+ thread_->start();
+
+ // Wait on the other thread to tell us that it has successfully
+ // bound to the port and started listening (or until an error occurs).
+ concurrency::Synchronized s(serverMonitor_);
+ while (!serving_ && !error_) {
+ serverMonitor_.waitForever();
+ }
+
+ if (error_) {
+ throw transport::TTransportException(transport::TTransportException::NOT_OPEN,
+ "failed to bind on server socket");
+ }
+}
+
+void ServerThread::stop() {
+ if (!running_) {
+ return;
+ }
+
+ // Tell the server to stop
+ server_->stop();
+ running_ = false;
+
+ // Wait for the server thread to exit
+ //
+ // Note: this only works if all client connections have closed. The servers
+ // generally wait for everything to be closed before exiting; there currently
+ // isn't a way to tell them to just exit now, and shut down existing
+ // connections.
+ thread_->join();
+}
+
+void ServerThread::run() {
+ /*
+ * Try binding to several ports, in case the one we want is already in use.
+ */
+ port_ = 12345;
+ unsigned int maxRetries = 10;
+ for (unsigned int n = 0; n < maxRetries; ++n) {
+ // Create the server
+ server_ = serverState_->createServer(port_);
+ // Install our helper as the server event handler, so that our
+ // preServe() method will be called once we've successfully bound to
+ // the port and are about to start listening.
+ server_->setServerEventHandler(helper_);
+
+ try {
+ // Try to serve requests
+ server_->serve();
+ } catch (const TException&) {
+ // TNonblockingServer throws a generic TException if it fails to bind.
+ // If we get a TException, we'll optimistically assume the bind failed.
+ ++port_;
+ continue;
+ }
+
+ // Seriously? serve() is pretty lame. If it fails to start serving it
+ // just returns rather than throwing an exception.
+ //
+ // We have to use our preServe() hook to tell if serve() successfully
+ // started serving and is returning because stop() is called, or if it just
+ // failed to start serving in the first place.
+ concurrency::Synchronized s(serverMonitor_);
+ if (serving_) {
+ // Oh good, we started serving and are exiting because
+ // we're trying to stop.
+ serving_ = false;
+ return;
+ } else {
+ // We never started serving, probably because we failed to bind to the
+ // port. Increment the port number and try again.
+ ++port_;
+ continue;
+ }
+ }
+
+ // We failed to bind on any port.
+ concurrency::Synchronized s(serverMonitor_);
+ error_ = true;
+ serverMonitor_.notify();
+}
+
+void ServerThread::preServe() {
+ // We bound to the port successfully, and are about to start serving requests
+ serverState_->bindSuccessful(port_);
+
+ // Set the real server event handler (replacing ourself)
+ std::shared_ptr<server::TServerEventHandler> serverEventHandler
+ = serverState_->getServerEventHandler();
+ server_->setServerEventHandler(serverEventHandler);
+
+ // Notify the main thread that we have successfully started serving requests
+ concurrency::Synchronized s(serverMonitor_);
+ serving_ = true;
+ serverMonitor_.notify();
+
+ // Invoke preServe() on the real event handler, since we ate
+ // the original preServe() event.
+ if (serverEventHandler) {
+ serverEventHandler->preServe();
+ }
+}
+}
+}
+} // apache::thrift::test
+
+#endif // _THRIFT_TEST_SERVERTHREAD_TCC_
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.h b/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.h
new file mode 100644
index 000000000..9cca2d600
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.h
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef _THRIFT_TEST_SERVERTHREAD_H_
+#define _THRIFT_TEST_SERVERTHREAD_H_ 1
+
+#include <thrift/TProcessor.h>
+#include <thrift/protocol/TProtocol.h>
+#include <thrift/server/TServer.h>
+#include <thrift/transport/TTransport.h>
+
+#include "EventLog.h"
+
+namespace apache {
+namespace thrift {
+namespace test {
+
+/**
+ * A helper class to tell ServerThread how to create the server
+ */
+class ServerState {
+public:
+ virtual ~ServerState() = default;
+
+ /**
+ * Create a server to listen on the specified port.
+ *
+ * If the server returned fails to bind to the specified port when serve() is
+ * called on it, createServer() may be called again on a different port.
+ */
+ virtual std::shared_ptr<server::TServer> createServer(uint16_t port) = 0;
+
+ /**
+ * Get the TServerEventHandler to set on the server.
+ *
+ * This is only called after the server successfully binds and is about to
+ * start serving traffic. It is invoked from the server thread, rather than
+ * the main thread.
+ */
+ virtual std::shared_ptr<server::TServerEventHandler> getServerEventHandler() {
+ return std::shared_ptr<server::TServerEventHandler>();
+ }
+
+ /**
+ * This method is called in the server thread after server binding succeeds.
+ *
+ * Subclasses may override this method if they wish to record the final
+ * port that was used for the server.
+ */
+ virtual void bindSuccessful(uint16_t /*port*/) {}
+};
+
+/**
+ * ServerThread starts a thrift server running in a separate thread.
+ */
+class ServerThread {
+public:
+ ServerThread(const std::shared_ptr<ServerState>& state, bool autoStart)
+ : port_(0),
+ running_(false),
+ serving_(false),
+ error_(false),
+ serverState_(state) {
+ if (autoStart) {
+ start();
+ }
+ }
+
+ void start();
+ void stop();
+
+ uint16_t getPort() const { return port_; }
+
+ ~ServerThread() {
+ if (running_) {
+ try {
+ stop();
+ } catch (...) {
+ GlobalOutput.printf("error shutting down server");
+ }
+ }
+ }
+
+protected:
+ // Annoying. thrift forces us to use shared_ptr, so we have to use
+ // a helper class that we can allocate on the heap and give to thrift.
+ // It would be simpler if we could just make Runnable and TServerEventHandler
+ // private base classes of ServerThread.
+ class Helper : public concurrency::Runnable, public server::TServerEventHandler {
+ public:
+ Helper(ServerThread* serverThread) : serverThread_(serverThread) {}
+
+ void run() override { serverThread_->run(); }
+
+ void preServe() override { serverThread_->preServe(); }
+
+ private:
+ ServerThread* serverThread_;
+ };
+
+ void run();
+ void preServe();
+
+ std::shared_ptr<Helper> helper_;
+
+ uint16_t port_;
+ bool running_;
+ bool serving_;
+ bool error_;
+ concurrency::Monitor serverMonitor_;
+
+ std::shared_ptr<ServerState> serverState_;
+ std::shared_ptr<server::TServer> server_;
+ std::shared_ptr<concurrency::Thread> thread_;
+};
+}
+}
+} // apache::thrift::test
+
+#endif // _THRIFT_TEST_SERVERTHREAD_H_
diff --git a/src/jaegertracing/thrift/lib/cpp/test/processor/proc.thrift b/src/jaegertracing/thrift/lib/cpp/test/processor/proc.thrift
new file mode 100644
index 000000000..ac3c5f953
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/processor/proc.thrift
@@ -0,0 +1,22 @@
+namespace cpp apache.thrift.test
+
+exception MyError {
+ 1: string message
+}
+
+service ParentService {
+ i32 incrementGeneration()
+ i32 getGeneration()
+ void addString(1: string s)
+ list<string> getStrings()
+
+ binary getDataWait(1: i32 length)
+ oneway void onewayWait()
+ void exceptionWait(1: string message) throws (2: MyError error)
+ void unexpectedExceptionWait(1: string message)
+}
+
+service ChildService extends ParentService {
+ i32 setValue(1: i32 value)
+ i32 getValue()
+}
diff --git a/src/jaegertracing/thrift/lib/cpp/test/qt/CMakeLists.txt b/src/jaegertracing/thrift/lib/cpp/test/qt/CMakeLists.txt
new file mode 100644
index 000000000..7f341cc4c
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/qt/CMakeLists.txt
@@ -0,0 +1,32 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+set(CMAKE_AUTOMOC ON)
+find_package(Qt5 REQUIRED COMPONENTS Test Network)
+set(TQTcpServerTest_Qt5_SOURCES
+ TQTcpServerTest.cpp
+)
+add_executable(TQTcpServerTest_Qt5 ${TQTcpServerTest_Qt5_SOURCES})
+target_link_libraries(TQTcpServerTest_Qt5 testgencpp_cob)
+LINK_AGAINST_THRIFT_LIBRARY(TQTcpServerTest_Qt5 thriftqt5)
+LINK_AGAINST_THRIFT_LIBRARY(TQTcpServerTest_Qt5 thrift)
+target_link_libraries(TQTcpServerTest_Qt5 Qt5::Test Qt5::Network)
+
+add_test(NAME TQTcpServerTest_Qt5 COMMAND TQTcpServerTest_Qt5)
+
diff --git a/src/jaegertracing/thrift/lib/cpp/test/qt/TQTcpServerTest.cpp b/src/jaegertracing/thrift/lib/cpp/test/qt/TQTcpServerTest.cpp
new file mode 100644
index 000000000..3371a9ae8
--- /dev/null
+++ b/src/jaegertracing/thrift/lib/cpp/test/qt/TQTcpServerTest.cpp
@@ -0,0 +1,113 @@
+#define BOOST_TEST_MODULE TQTcpServerTest
+#include <QTest>
+#include <iostream>
+
+#include <QTcpServer>
+#include <QTcpSocket>
+#include <QHostAddress>
+#include <QThread>
+
+#ifndef Q_MOC_RUN
+ #include "thrift/protocol/TBinaryProtocol.h"
+ #include "thrift/async/TAsyncProcessor.h"
+ #include "thrift/qt/TQTcpServer.h"
+ #include "thrift/qt/TQIODeviceTransport.h"
+
+ #include "gen-cpp/ParentService.h"
+#endif
+
+using namespace apache::thrift;
+
+struct AsyncHandler : public test::ParentServiceCobSvIf {
+ std::vector<std::string> strings;
+ void addString(std::function<void()> cob, const std::string& s) override {
+ strings.push_back(s);
+ cob();
+ }
+ void getStrings(std::function<void(std::vector<std::string> const& _return)> cob) override {
+ cob(strings);
+ }
+
+ // Overrides not used in this test
+ void incrementGeneration(std::function<void(int32_t const& _return)> cob) override {}
+ void getGeneration(std::function<void(int32_t const& _return)> cob) override {}
+ void getDataWait(std::function<void(std::string const& _return)> cob,
+ const int32_t length) override {}
+ void onewayWait(std::function<void()> cob) override {}
+ void exceptionWait(
+ std::function<void()> cob,
+ std::function<void(::apache::thrift::TDelayedException* _throw)> /* exn_cob */,
+ const std::string& message) override {}
+ void unexpectedExceptionWait(std::function<void()> cob, const std::string& message) override {}
+};
+
+class TQTcpServerTest : public QObject {
+ Q_OBJECT
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void test_communicate();
+
+private:
+ std::shared_ptr<QThread> serverThread;
+ std::shared_ptr<async::TQTcpServer> server;
+ std::shared_ptr<test::ParentServiceClient> client;
+};
+
+void TQTcpServerTest::initTestCase() {
+ // setup server
+ std::shared_ptr<QTcpServer> serverSocket = std::make_shared<QTcpServer>();
+ server.reset(new async::TQTcpServer(serverSocket,
+ std::make_shared<test::ParentServiceAsyncProcessor>(
+ std::make_shared<AsyncHandler>()),
+ std::make_shared<protocol::TBinaryProtocolFactory>()));
+ QVERIFY(serverSocket->listen(QHostAddress::LocalHost));
+ int port = serverSocket->serverPort();
+ QVERIFY(port > 0);
+
+ //setup server thread and move server to it
+ serverThread.reset(new QThread());
+ serverSocket->moveToThread(serverThread.get());
+ server->moveToThread(serverThread.get());
+ serverThread->start();
+
+ // setup client
+ std::shared_ptr<QTcpSocket> socket = std::make_shared<QTcpSocket>();
+ client.reset(new test::ParentServiceClient(std::make_shared<protocol::TBinaryProtocol>(
+ std::make_shared<transport::TQIODeviceTransport>(socket))));
+ socket->connectToHost(QHostAddress::LocalHost, port);
+ QVERIFY(socket->waitForConnected());
+}
+
+void TQTcpServerTest::cleanupTestCase() {
+ //first, stop the thread which holds the server
+ serverThread->quit();
+ serverThread->wait();
+ // now, it is safe to delete the server
+ server.reset();
+ // delete thread now
+ serverThread.reset();
+
+ // cleanup client
+ client.reset();
+}
+
+void TQTcpServerTest::test_communicate() {
+ client->addString("foo");
+ client->addString("bar");
+
+ std::vector<std::string> reply;
+ client->getStrings(reply);
+ QCOMPARE(QString::fromStdString(reply[0]), QString("foo"));
+ QCOMPARE(QString::fromStdString(reply[1]), QString("bar"));
+}
+
+
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
+QTEST_GUILESS_MAIN(TQTcpServerTest);
+#else
+#undef QT_GUI_LIB
+QTEST_MAIN(TQTcpServerTest);
+#endif
+#include "TQTcpServerTest.moc"