From 19fcec84d8d7d21e796c7624e521b60d28ee21ed Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:45:59 +0200 Subject: Adding upstream version 16.2.11+ds. Signed-off-by: Daniel Baumann --- .../thrift/lib/cpp/test/AllProtocolTests.cpp | 47 + .../thrift/lib/cpp/test/AllProtocolTests.tcc | 225 ++++ .../thrift/lib/cpp/test/AnnotationTest.cpp | 68 ++ .../thrift/lib/cpp/test/Base64Test.cpp | 74 ++ .../thrift/lib/cpp/test/Benchmark.cpp | 244 +++++ .../thrift/lib/cpp/test/CMakeLists.txt | 390 +++++++ .../thrift/lib/cpp/test/DebugProtoTest.cpp | 310 ++++++ .../thrift/lib/cpp/test/DebugProtoTest_extras.cpp | 35 + src/jaegertracing/thrift/lib/cpp/test/EnumTest.cpp | 108 ++ .../thrift/lib/cpp/test/GenericHelpers.h | 107 ++ .../thrift/lib/cpp/test/JSONProtoTest.cpp | 343 ++++++ src/jaegertracing/thrift/lib/cpp/test/Makefile.am | 425 ++++++++ .../thrift/lib/cpp/test/OneWayHTTPTest.cpp | 242 +++++ .../thrift/lib/cpp/test/OneWayTest.thrift | 45 + .../thrift/lib/cpp/test/OpenSSLManualInitTest.cpp | 93 ++ .../thrift/lib/cpp/test/OptionalRequiredTest.cpp | 386 +++++++ .../thrift/lib/cpp/test/RecursiveTest.cpp | 92 ++ .../lib/cpp/test/RenderedDoubleConstantsTest.cpp | 122 +++ .../thrift/lib/cpp/test/SecurityTest.cpp | 278 +++++ .../thrift/lib/cpp/test/SpecializationTest.cpp | 103 ++ .../thrift/lib/cpp/test/TBufferBaseTest.cpp | 639 ++++++++++++ .../thrift/lib/cpp/test/TFDTransportTest.cpp | 50 + .../thrift/lib/cpp/test/TFileTransportTest.cpp | 404 ++++++++ .../thrift/lib/cpp/test/TMemoryBufferTest.cpp | 160 +++ .../lib/cpp/test/TNonblockingSSLServerTest.cpp | 286 +++++ .../thrift/lib/cpp/test/TNonblockingServerTest.cpp | 215 ++++ .../thrift/lib/cpp/test/TPipeInterruptTest.cpp | 90 ++ .../thrift/lib/cpp/test/TPipedTransportTest.cpp | 53 + .../lib/cpp/test/TSSLSocketInterruptTest.cpp | 282 +++++ .../thrift/lib/cpp/test/TServerIntegrationTest.cpp | 536 ++++++++++ .../thrift/lib/cpp/test/TServerSocketTest.cpp | 69 ++ .../thrift/lib/cpp/test/TServerTransportTest.cpp | 58 ++ .../thrift/lib/cpp/test/TSocketInterruptTest.cpp | 146 +++ .../thrift/lib/cpp/test/TTransportCheckThrow.h | 44 + .../thrift/lib/cpp/test/ThriftTest_extras.cpp | 33 + .../thrift/lib/cpp/test/ToStringTest.cpp | 137 +++ .../thrift/lib/cpp/test/TransportTest.cpp | 1089 ++++++++++++++++++++ .../thrift/lib/cpp/test/TypedefTest.cpp | 28 + .../thrift/lib/cpp/test/UnitTestMain.cpp | 21 + src/jaegertracing/thrift/lib/cpp/test/ZlibTest.cpp | 475 +++++++++ .../thrift/lib/cpp/test/concurrency/Tests.cpp | 224 ++++ .../lib/cpp/test/concurrency/ThreadFactoryTests.h | 308 ++++++ .../lib/cpp/test/concurrency/ThreadManagerTests.h | 639 ++++++++++++ .../lib/cpp/test/concurrency/TimerManagerTests.h | 273 +++++ .../thrift/lib/cpp/test/link/LinkTest.cpp | 22 + .../thrift/lib/cpp/test/link/TemplatedService1.cpp | 26 + .../thrift/lib/cpp/test/link/TemplatedService2.cpp | 26 + .../thrift/lib/cpp/test/processor/EventLog.cpp | 135 +++ .../thrift/lib/cpp/test/processor/EventLog.h | 95 ++ .../thrift/lib/cpp/test/processor/Handlers.h | 338 ++++++ .../lib/cpp/test/processor/ProcessorTest.cpp | 929 +++++++++++++++++ .../thrift/lib/cpp/test/processor/ServerThread.cpp | 152 +++ .../thrift/lib/cpp/test/processor/ServerThread.h | 135 +++ .../thrift/lib/cpp/test/processor/proc.thrift | 22 + .../thrift/lib/cpp/test/qt/CMakeLists.txt | 32 + .../thrift/lib/cpp/test/qt/TQTcpServerTest.cpp | 113 ++ 56 files changed, 12021 insertions(+) create mode 100644 src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/AllProtocolTests.tcc create mode 100644 src/jaegertracing/thrift/lib/cpp/test/AnnotationTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/Base64Test.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/Benchmark.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/CMakeLists.txt create mode 100644 src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/DebugProtoTest_extras.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/EnumTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/GenericHelpers.h create mode 100644 src/jaegertracing/thrift/lib/cpp/test/JSONProtoTest.cpp create mode 100755 src/jaegertracing/thrift/lib/cpp/test/Makefile.am create mode 100644 src/jaegertracing/thrift/lib/cpp/test/OneWayHTTPTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/OneWayTest.thrift create mode 100644 src/jaegertracing/thrift/lib/cpp/test/OpenSSLManualInitTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/OptionalRequiredTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/RecursiveTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/RenderedDoubleConstantsTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/SecurityTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/SpecializationTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TBufferBaseTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TFDTransportTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TFileTransportTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TMemoryBufferTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TNonblockingSSLServerTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TNonblockingServerTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TPipeInterruptTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TPipedTransportTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TSSLSocketInterruptTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TServerIntegrationTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TServerSocketTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TServerTransportTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TSocketInterruptTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TTransportCheckThrow.h create mode 100644 src/jaegertracing/thrift/lib/cpp/test/ThriftTest_extras.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/ToStringTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TransportTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/TypedefTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/UnitTestMain.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/ZlibTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/concurrency/Tests.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadFactoryTests.h create mode 100644 src/jaegertracing/thrift/lib/cpp/test/concurrency/ThreadManagerTests.h create mode 100644 src/jaegertracing/thrift/lib/cpp/test/concurrency/TimerManagerTests.h create mode 100644 src/jaegertracing/thrift/lib/cpp/test/link/LinkTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService1.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/link/TemplatedService2.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/processor/EventLog.h create mode 100644 src/jaegertracing/thrift/lib/cpp/test/processor/Handlers.h create mode 100644 src/jaegertracing/thrift/lib/cpp/test/processor/ProcessorTest.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.cpp create mode 100644 src/jaegertracing/thrift/lib/cpp/test/processor/ServerThread.h create mode 100644 src/jaegertracing/thrift/lib/cpp/test/processor/proc.thrift create mode 100644 src/jaegertracing/thrift/lib/cpp/test/qt/CMakeLists.txt create mode 100644 src/jaegertracing/thrift/lib/cpp/test/qt/TQTcpServerTest.cpp (limited to 'src/jaegertracing/thrift/lib/cpp/test') 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 + +#include +#include +#include + +#define BOOST_TEST_MODULE AllProtocolTests +#include + +#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"); +} + +BOOST_AUTO_TEST_CASE(test_little_binary_protocol) { + testProtocol("TLEBinaryProtocol"); +} + +BOOST_AUTO_TEST_CASE(test_compact_protocol) { + testProtocol("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 + +#include +#include +#include + +#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 +void testNaked(Val val) { + shared_ptr transport(new TMemoryBuffer()); + shared_ptr 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()); + throw TException(errorMessage); + } +} + +template +void testField(const Val val) { + shared_ptr transport(new TMemoryBuffer()); + shared_ptr 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 +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 transport(new TMemoryBuffer()); + shared_ptr 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 +void testProtocol(const char* protoname) { + try { + testNaked((int8_t)123); + + for (int32_t i = 0; i < 128; i++) { + testField((int8_t)i); + testField((int8_t)-i); + } + + testNaked((int16_t)0); + testNaked((int16_t)1); + testNaked((int16_t)15000); + testNaked((int16_t)0x7fff); + testNaked((int16_t)-1); + testNaked((int16_t)-15000); + testNaked((int16_t)-0x7fff); + testNaked((std::numeric_limits::min)()); + testNaked((std::numeric_limits::max)()); + + testField((int16_t)0); + testField((int16_t)1); + testField((int16_t)7); + testField((int16_t)150); + testField((int16_t)15000); + testField((int16_t)0x7fff); + testField((int16_t)-1); + testField((int16_t)-7); + testField((int16_t)-150); + testField((int16_t)-15000); + testField((int16_t)-0x7fff); + + testNaked(0); + testNaked(1); + testNaked(15000); + testNaked(0xffff); + testNaked(-1); + testNaked(-15000); + testNaked(-0xffff); + testNaked((std::numeric_limits::min)()); + testNaked((std::numeric_limits::max)()); + + testField(0); + testField(1); + testField(7); + testField(150); + testField(15000); + testField(31337); + testField(0xffff); + testField(0xffffff); + testField(-1); + testField(-7); + testField(-150); + testField(-15000); + testField(-0xffff); + testField(-0xffffff); + testNaked((std::numeric_limits::min)()); + testNaked((std::numeric_limits::max)()); + testNaked((std::numeric_limits::min)() + 10); + testNaked((std::numeric_limits::max)() - 16); + testNaked((std::numeric_limits::min)()); + testNaked((std::numeric_limits::max)()); + + testNaked(0); + for (int64_t i = 0; i < 62; i++) { + testNaked(1LL << i); + testNaked(-(1LL << i)); + } + + testField(0); + for (int i = 0; i < 62; i++) { + testField(1LL << i); + testField(-(1LL << i)); + } + + testNaked(123.456); + + testNaked(""); + testNaked("short"); + testNaked("borderlinetiny"); + testNaked("a bit longer than the smallest possible"); + testNaked("\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA"); // kinda binary test + + testField(""); + testField("short"); + testField("borderlinetiny"); + testField("a bit longer than the smallest possible"); + + testMessage(); + + 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 +#include "gen-cpp/AnnotationTest_types.h" +#include +#include + +// 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 +#include + +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 +#endif +#include +#define _USE_MATH_DEFINES +#include +#include +#include "thrift/protocol/TBinaryProtocol.h" +#include "thrift/transport/TBufferTransports.h" +#include "gen-cpp/DebugProtoTest_types.h" + +#ifdef HAVE_SYS_TIME_H +#include +#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 buf(new TMemoryBuffer(num*1000)); + + uint8_t* data = nullptr; + uint32_t datasize = 0; + + { + buf->resetBuffer(); + TBinaryProtocolT 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 buf2(new TMemoryBuffer(data, datasize)); + TBinaryProtocolT 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 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 buf2(new TMemoryBuffer(data, datasize)); + TBinaryProtocolT 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 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 buf2(new TMemoryBuffer(data, datasize)); + TBinaryProtocolT 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 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 buf2(new TMemoryBuffer(data, datasize)); + TBinaryProtocolT 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 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 buf2(new TMemoryBuffer(data, datasize)); + TBinaryProtocolT 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 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 buf2(new TMemoryBuffer(data, datasize)); + TBinaryProtocolT 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 +#include "gen-cpp/DebugProtoTest_types.h" +#include +#include + +#define BOOST_TEST_MODULE DebugProtoTest +#include + +using namespace thrift::test::debug; + +static ::std::shared_ptr 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[3] {\n" + " [0] = 0x01,\n" + " [1] = 0x02,\n" + " [2] = 0x03,\n" + " },\n" + " 13: i16_list (list) = list[3] {\n" + " [0] = 1,\n" + " [1] = 2,\n" + " [2] = 3,\n" + " },\n" + " 14: i64_list (list) = list[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 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[3] {\n" + " [0] = 0x01,\n" + " [1] = 0x02,\n" + " [2] = 0x03,\n" + " },\n" + " 13: i16_list (list) = list[3] {\n" + " [0] = 1,\n" + " [1] = 2,\n" + " [2] = 3,\n" + " },\n" + " 14: i64_list (list) = list[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 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 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 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[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[3] {\n" + " [0] = 0x01,\n" + " [1] = 0x02,\n" + " [2] = 0x03,\n" + " },\n" + " 13: i16_list (list) = list[3] {\n" + " [0] = 1,\n" + " [1] = 2,\n" + " [2] = 3,\n" + " },\n" + " 14: i64_list (list) = list[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[3] {\n" + " [0] = 0x01,\n" + " [1] = 0x02,\n" + " [2] = 0x03,\n" + " },\n" + " 13: i16_list (list) = list[3] {\n" + " [0] = 1,\n" + " [1] = 2,\n" + " [2] = 3,\n" + " },\n" + " 14: i64_list (list) = list[3] {\n" + " [0] = 1,\n" + " [1] = 2,\n" + " [2] = 3,\n" + " },\n" + " },\n" + " },\n" + " 02: contain (set) = set[3] {\n" + " list[0] {\n" + " },\n" + " list[2] {\n" + " [0] = \"and a one\",\n" + " [1] = \"and a two\",\n" + " },\n" + " list[3] {\n" + " [0] = \"then a one, two\",\n" + " [1] = \"three!\",\n" + " [2] = \"FOUR!!\",\n" + " },\n" + " },\n" + " 03: bonks (map) = map[3] {\n" + " \"nothing\" -> list[0] {\n" + " },\n" + " \"poe\" -> list[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[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 +#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 +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(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(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 +#include +#include + +/* ClassName Helper for cleaner exceptions */ +class ClassNames { +public: + template + static const char* getName() { + return "Unknown type"; + } +}; + +template <> +const char* ClassNames::getName() { + return "byte"; +} +template <> +const char* ClassNames::getName() { + return "short"; +} +template <> +const char* ClassNames::getName() { + return "int"; +} +template <> +const char* ClassNames::getName() { + return "long"; +} +template <> +const char* ClassNames::getName() { + return "double"; +} +template <> +const char* ClassNames::getName() { + return "string"; +} + +/* Generic Protocol I/O function for tests */ +class GenericIO { +public: + /* Write functions */ + + static uint32_t write(std::shared_ptr proto, const int8_t& val) { + return proto->writeByte(val); + } + + static uint32_t write(std::shared_ptr proto, const int16_t& val) { + return proto->writeI16(val); + } + + static uint32_t write(std::shared_ptr proto, const int32_t& val) { + return proto->writeI32(val); + } + + static uint32_t write(std::shared_ptr proto, const double& val) { + return proto->writeDouble(val); + } + + static uint32_t write(std::shared_ptr proto, const int64_t& val) { + return proto->writeI64(val); + } + + static uint32_t write(std::shared_ptr proto, const std::string& val) { + return proto->writeString(val); + } + + /* Read functions */ + + static uint32_t read(std::shared_ptr proto, int8_t& val) { return proto->readByte(val); } + + static uint32_t read(std::shared_ptr proto, int16_t& val) { return proto->readI16(val); } + + static uint32_t read(std::shared_ptr proto, int32_t& val) { return proto->readI32(val); } + + static uint32_t read(std::shared_ptr proto, int64_t& val) { return proto->readI64(val); } + + static uint32_t read(std::shared_ptr proto, double& val) { return proto->readDouble(val); } + + static uint32_t read(std::shared_ptr 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 +#include +#include +#include +#include +#include +#include "gen-cpp/DebugProtoTest_types.h" + +#define BOOST_TEST_MODULE JSONProtoTest +#include + +using namespace thrift::test::debug; +using namespace apache::thrift; +using apache::thrift::transport::TMemoryBuffer; +using apache::thrift::protocol::TJSONProtocol; + +static std::shared_ptr 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 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 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 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 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 buffer(new TMemoryBuffer()); + std::shared_ptr 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 buffer(new TMemoryBuffer()); + std::shared_ptr 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 buffer(new TMemoryBuffer()); + std::shared_ptr 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 buffer(new TMemoryBuffer( + (uint8_t*)(json_string), static_cast(bufSiz))); + std::shared_ptr 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 buffer(new TMemoryBuffer( + (uint8_t*)(json_string), sizeof(json_string))); + std::shared_ptr 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 buffer(new TMemoryBuffer( + (uint8_t*)(json_string), sizeof(json_string))); + std::shared_ptr 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 buffer(new TMemoryBuffer( + (uint8_t*)(json_string), sizeof(json_string))); + std::shared_ptr 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 input, + shared_ptr 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 transport) + : TBufferedTransport(transport, 10240), + blocked_(false) { + } + + uint32_t write_buffer_length() { + auto have_bytes = static_cast(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 ss = std::make_shared(0) ; + TThreadedServer server( + std::make_shared(std::make_shared()), + ss, //port + std::make_shared(), + std::make_shared()); + + std::shared_ptr 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 socket(new TSocket("localhost", port)); + socket->setRecvTimeout(10000) ; // 1000msec should be enough + std::shared_ptr blockable_transport(new TBlockableBufferedTransport(socket)); + std::shared_ptr transport(new THttpClient(blockable_transport, "localhost", "/service")); + std::shared_ptr 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 +#endif + +#include +#include +#include + +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 +#include +#include +#include +#include "gen-cpp/OptionalRequiredTest_types.h" + +#define BOOST_TEST_MODULE OptionalRequiredTest +#include + +using namespace thrift::test; +using namespace apache::thrift; +using namespace apache::thrift::transport; +using namespace apache::thrift::protocol; + +/* +template +void trywrite(const Struct& s, bool should_work) { + bool worked; + try { + TBinaryProtocol protocol(std::shared_ptr(new TMemoryBuffer)); + s.write(&protocol); + worked = true; + } catch (TProtocolException & ex) { + worked = false; + } + BOOST_CHECK(worked == should_work); +} +*/ + +template +void write_to_read(const Struct1& w, Struct2& r) { + TBinaryProtocol protocol(std::shared_ptr(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[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[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 mymap; + mymap[1] = "bar"; + mymap[2] = "baz"; + o1.im_big.push_back(std::map()); + BOOST_CHECK(o1 != o2); + o2.im_big.push_back(std::map()); + 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(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[3] {\n" + " [0] = map[0] {\n" + " },\n" + " [1] = map[2] {\n" + " 1 -> \"bar\",\n" + " 2 -> \"baz\",\n" + " },\n" + " [2] = map[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 +#include +#include + +#define BOOST_TEST_MODULE RecursiveTest +#include + +using apache::thrift::transport::TMemoryBuffer; +using apache::thrift::protocol::TBinaryProtocol; +using std::shared_ptr; + +BOOST_AUTO_TEST_CASE(test_recursive_1) { + shared_ptr buf(new TMemoryBuffer()); + shared_ptr 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 buf(new TMemoryBuffer()); + shared_ptr prot(new TBinaryProtocol(buf)); + + RecList l; + shared_ptr 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 buf(new TMemoryBuffer()); + shared_ptr prot(new TBinaryProtocol(buf)); + + CoRec c; + shared_ptr 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 buf(new TMemoryBuffer()); + shared_ptr prot(new TBinaryProtocol(buf)); + + shared_ptr 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 +#include +#include + +#include "gen-cpp/DoubleConstantsTest_constants.h" +using namespace thrift::test; + +#define BOOST_TEST_MODULE RenderedDoubleConstantsTest +#include + +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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#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 pServerSocketFactory; + shared_ptr pServerSocket; + + pServerSocketFactory.reset(new TSSLSocketFactory(static_cast(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 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 pClientSocketFactory; + shared_ptr pClientSocket; + + try + { + pClientSocketFactory.reset(new TSSLSocketFactory(static_cast(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(si))); + mCVar.wait(lock); // wait for listen() to succeed + lock.unlock(); + (void)threads.create_thread(bind(&SecurityFixture::client, this, static_cast(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 +#include +#include +#include + +using namespace thrift::test::debug; +using namespace apache::thrift::transport; +using namespace apache::thrift::protocol; + +#define BOOST_TEST_MODULE SpecializationTest +#include + +typedef TBinaryProtocolT MyProtocol; +// typedef TBinaryProtocolT 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 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 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 buffer(new TMemoryBuffer()); + std::shared_ptr 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 +#include +#include +#include +#include + +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 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 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 buffer(new TMemoryBuffer(data, sizeof(data))); + shared_ptr 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 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(&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 buffer(new TMemoryBuffer()); + TFramedTransport trans(buffer); + int32_t length = sizeof(data); + length = (int32_t)htonl((uint32_t)length); + buffer->write(reinterpret_cast(&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 buffer(new TMemoryBuffer(16)); + TFramedTransport trans(buffer, size); + std::vector data_out(1<<17, 0); + std::vector 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 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 +#include +#include +#include + +#define BOOST_TEST_MODULE TFDTransportTest +#include + +// 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 + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#include + +#include + +#ifdef __MINGW32__ + #include + #include + #include + #include + #include +#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 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("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(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 +#include +#include +#include +#include +#include +#include +#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 buf; + buf.resize(maxSize); + + for (uint32_t i = 0; i < maxSize; ++i) { + buf[i] = static_cast(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 strBuffer(new TMemoryBuffer()); + shared_ptr 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 strBuffer2(new TMemoryBuffer()); + shared_ptr binaryProtcol2(new TBinaryProtocol(strBuffer2)); + + strBuffer2->resetBuffer((uint8_t*)serialized.data(), static_cast(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(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::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 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 memBuffer(new TMemoryBuffer()); + shared_ptr 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 +#include +#include + +#include "thrift/server/TNonblockingServer.h" +#include "thrift/transport/TSSLSocket.h" +#include "thrift/transport/TNonblockingSSLServerSocket.h" + +#include "gen-cpp/ParentService.h" + +#include + +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& _return) override { _return = strings_; } + std::vector 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 createServerSocketFactory() { + std::shared_ptr 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 createClientSocketFactory() { + std::shared_ptr 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 userEventBase; + std::shared_ptr processor; + std::shared_ptr server; + std::shared_ptr listenHandler; + std::shared_ptr pServerSocketFactory; + std::shared_ptr 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())) {} + + ~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(new Runner); + runner->port = port; + runner->processor = processor; + runner->userEventBase = userEventBase_; + + std::unique_ptr 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 pClientSocketFactory = createClientSocketFactory(); + std::shared_ptr socket = pClientSocketFactory->createSocket("localhost", serverPort); + socket->open(); + test::ParentServiceClient client(std::make_shared( + std::make_shared(socket))); + client.addString("foo"); + std::vector strings; + client.getStrings(strings); + return strings.size() == 1 && !(strings[0].compare("foo")); + } + +private: + std::shared_ptr userEventBase_; + std::shared_ptr processor; +protected: + std::shared_ptr server; +private: + std::shared_ptr 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 +#include + +#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 + +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& _return) override { _return = strings_; } + std::vector 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 userEventBase; + shared_ptr processor; + shared_ptr server; + shared_ptr listenHandler; + shared_ptr 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())) {} + + ~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(new Runner); + runner->port = port; + runner->processor = processor; + runner->userEventBase = userEventBase_; + + shared_ptr threadFactory( + new ThreadFactory(false)); + thread = threadFactory->newThread(runner); + thread->start(); + runner->readyBarrier(); + + server = runner->server; + return runner->port; + } + + bool canCommunicate(int serverPort) { + shared_ptr socket(new transport::TSocket("localhost", serverPort)); + socket->open(); + test::ParentServiceClient client(make_shared( + make_shared(socket))); + client.addString("foo"); + std::vector strings; + client.getStrings(strings); + return strings.size() == 1 && !(strings[0].compare("foo")); + } + +private: + shared_ptr userEventBase_; + shared_ptr processor; +protected: + shared_ptr server; +private: + shared_ptr 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 +#include + +#include +#include +#include +#include +#include +#include + +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 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 +#include +#include +#include + +#define BOOST_TEST_MODULE TPipedTransportTest +#include + +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 underlying(new TMemoryBuffer); + std::shared_ptr pipe(new TMemoryBuffer); + std::shared_ptr 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#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 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 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 createServerSocketFactory() { + shared_ptr 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 createClientSocketFactory() { + shared_ptr 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 pServerSocketFactory = createServerSocketFactory(); + TSSLServerSocket sock1("localhost", 0, pServerSocketFactory); + sock1.listen(); + int port = sock1.getPort(); + shared_ptr pClientSocketFactory = createClientSocketFactory(); + shared_ptr clientSock = pClientSocketFactory->createSocket("localhost", port); + clientSock->open(); + shared_ptr 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 pServerSocketFactory = createServerSocketFactory(); + TSSLServerSocket sock1("localhost", 0, pServerSocketFactory); + sock1.listen(); + int port = sock1.getPort(); + shared_ptr pClientSocketFactory = createClientSocketFactory(); + shared_ptr clientSock = pClientSocketFactory->createSocket("localhost", port); + clientSock->open(); + shared_ptr 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 pServerSocketFactory = createServerSocketFactory(); + TSSLServerSocket sock1("localhost", 0, pServerSocketFactory); + sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior + sock1.listen(); + int port = sock1.getPort(); + shared_ptr pClientSocketFactory = createClientSocketFactory(); + shared_ptr clientSock = pClientSocketFactory->createSocket("localhost", port); + clientSock->open(); + shared_ptr accepted = sock1.accept(); + static_pointer_cast(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 pServerSocketFactory = createServerSocketFactory(); + TSSLServerSocket sock1("localhost", 0, pServerSocketFactory); + sock1.listen(); + BOOST_CHECK_THROW(sock1.setInterruptableChildren(false), std::logic_error); + sock1.close(); +} + +void peekerWorker(shared_ptr 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 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 pServerSocketFactory = createServerSocketFactory(); + TSSLServerSocket sock1("localhost", 0, pServerSocketFactory); + sock1.listen(); + int port = sock1.getPort(); + shared_ptr pClientSocketFactory = createClientSocketFactory(); + shared_ptr clientSock = pClientSocketFactory->createSocket("localhost", port); + clientSock->open(); + shared_ptr 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 pServerSocketFactory = createServerSocketFactory(); + TSSLServerSocket sock1("localhost", 0, pServerSocketFactory); + sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior + sock1.listen(); + int port = sock1.getPort(); + shared_ptr pClientSocketFactory = createClientSocketFactory(); + shared_ptr clientSock = pClientSocketFactory->createSocket("localhost", port); + clientSock->open(); + shared_ptr accepted = sock1.accept(); + static_pointer_cast(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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gen-cpp/ParentService.h" +#include +#include + +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 input, + shared_ptr 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& _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 strings_; +}; + +void autoSocketCloser(TSocket* pSock) { + pSock->close(); + delete pSock; +} + +template +class TServerIntegrationTestFixture { +public: + TServerIntegrationTestFixture(const shared_ptr& _processorFactory) + : pServer(new TServerType(_processorFactory, + shared_ptr( + new TServerSocket("localhost", 0)), + shared_ptr(new TTransportFactory), + shared_ptr(new TBinaryProtocolFactory))), + pEventHandler(shared_ptr(new TServerReadyEventHandler)), + bStressDone(false), + bStressConnectionCount(0), + bStressRequestCount(0) { + pServer->setServerEventHandler(pEventHandler); + } + + TServerIntegrationTestFixture(const shared_ptr& _processor) + : pServer( + new TServerType(_processor, + shared_ptr(new TServerSocket("localhost", 0)), + shared_ptr(new TTransportFactory), + shared_ptr(new TBinaryProtocolFactory))), + pEventHandler(shared_ptr(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 > holdSockets; + std::vector > holdThreads; + + for (int64_t i = 0; i < numToMake; ++i) { + shared_ptr pClientSock(new TSocket("localhost", getServerPort()), + autoSocketCloser); + holdSockets.push_back(pClientSock); + shared_ptr pClientProtocol(new TBinaryProtocol(pClientSock)); + ParentServiceClient client(pClientProtocol); + pClientSock->open(); + client.incrementGeneration(); + holdThreads.push_back(shared_ptr( + new boost::thread(std::bind(&TServerIntegrationTestFixture::delayClose, + this, + pClientSock, + milliseconds(10 * numToMake))))); + } + + BOOST_CHECK_EQUAL(expectedHWM, pServer->getConcurrentClientCountHWM()); + + BOOST_FOREACH (shared_ptr 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 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(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 > holdThreads; + for (int64_t i = 0; i < numToMake; ++i) { + holdThreads.push_back(shared_ptr( + 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 pThread, holdThreads) { pThread->join(); } + holdThreads.clear(); + + BOOST_CHECK(bStressRequestCount > 0); + + stopServer(); + } + + /** + * Helper method to stress the system + */ + void stressor() { + while (!bStressDone) { + shared_ptr pSocket(new TSocket("localhost", getServerPort()), autoSocketCloser); + shared_ptr 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 pServer; + shared_ptr pEventHandler; + shared_ptr pServerThread; + std::atomic bStressDone; + std::atomic bStressConnectionCount; + std::atomic bStressRequestCount; +}; + +template +class TServerIntegrationProcessorFactoryTestFixture + : public TServerIntegrationTestFixture { +public: + TServerIntegrationProcessorFactoryTestFixture() + : TServerIntegrationTestFixture(make_shared( + make_shared( + make_shared()))) {} +}; + +template +class TServerIntegrationProcessorTestFixture : public TServerIntegrationTestFixture { +public: + TServerIntegrationProcessorTestFixture() + : TServerIntegrationTestFixture( + make_shared(make_shared())) {} +}; + +BOOST_AUTO_TEST_SUITE(constructors) + +BOOST_FIXTURE_TEST_CASE(test_simple_factory, + TServerIntegrationProcessorFactoryTestFixture) { + baseline(3, 1, "factory"); +} + +BOOST_FIXTURE_TEST_CASE(test_simple, TServerIntegrationProcessorTestFixture) { + baseline(3, 1, "processor"); +} + +BOOST_FIXTURE_TEST_CASE(test_threaded_factory, + TServerIntegrationProcessorFactoryTestFixture) { + baseline(10, 10, "factory"); +} + +BOOST_FIXTURE_TEST_CASE(test_threaded, TServerIntegrationProcessorTestFixture) { + baseline(10, 10, "processor"); +} + +BOOST_FIXTURE_TEST_CASE(test_threaded_bound, + TServerIntegrationProcessorTestFixture) { + pServer->setConcurrentClientLimit(4); + baseline(10, 4, "limit by server framework"); +} + +BOOST_FIXTURE_TEST_CASE(test_threaded_stress, + TServerIntegrationProcessorFactoryTestFixture) { + stress(10, boost::posix_time::seconds(3)); +} + +BOOST_FIXTURE_TEST_CASE(test_threadpool_factory, + TServerIntegrationProcessorFactoryTestFixture) { + pServer->getThreadManager()->threadFactory( + shared_ptr( + 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) { + pServer->getThreadManager()->threadFactory( + shared_ptr( + 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) { + pServer->getThreadManager()->threadFactory( + shared_ptr( + 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) { + pServer->getThreadManager()->threadFactory( + shared_ptr( + 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) + +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 pClientSock1(new TSocket("localhost", getServerPort()), + autoSocketCloser); + pClientSock1->open(); + + shared_ptr 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(pServer->getServerTransport()) + ->setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior + + startServer(); + + shared_ptr pClientSock1(new TSocket("localhost", getServerPort()), + autoSocketCloser); + pClientSock1->open(); + + shared_ptr 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 pClientSock1(new TSocket("localhost", getServerPort()), + autoSocketCloser); + pClientSock1->open(); + blockUntilAccepted(1); + BOOST_CHECK_EQUAL(1, pServer->getConcurrentClientCount()); + + shared_ptr 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 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 +#include +#include +#include +#include "TTransportCheckThrow.h" +#include + +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 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 +#include +#include +#include + +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 acceptImpl() override { + return valid_ ? std::make_shared() + : shared_ptr(); + } +}; + +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 + +#include +#include +#include +#include +#include +#include + +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 tt, uint32_t expectedResult) { + uint8_t buf[4]; + BOOST_CHECK_EQUAL(expectedResult, tt->read(buf, 4)); +} + +void readerWorkerMustThrow(std::shared_ptr 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 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 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 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 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 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 +#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 +#include + +#include + +#include + +#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 l; + BOOST_CHECK_EQUAL(to_string(l), "[]"); +} + +BOOST_AUTO_TEST_CASE(single_item_vector_to_string) { + std::vector l; + l.push_back(100); + BOOST_CHECK_EQUAL(to_string(l), "[100]"); +} + +BOOST_AUTO_TEST_CASE(multiple_item_vector_to_string) { + std::vector 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 m; + BOOST_CHECK_EQUAL(to_string(m), "{}"); +} + +BOOST_AUTO_TEST_CASE(single_item_map_to_string) { + std::map m; + m[12] = "abc"; + BOOST_CHECK_EQUAL(to_string(m), "{12: abc}"); +} + +BOOST_AUTO_TEST_CASE(multi_item_map_to_string) { + std::map 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 s; + BOOST_CHECK_EQUAL(to_string(s), "{}"); +} + +BOOST_AUTO_TEST_CASE(single_item_set_to_string) { + std::set s; + s.insert('c'); + BOOST_CHECK_EQUAL(to_string(s), "{c}"); +} + +BOOST_AUTO_TEST_CASE(multi_item_set_to_string) { + std::set 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=)"); + 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 + +#include +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#if _WIN32 +#include +#include +#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(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 > 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 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 CoupledTransports { +public: + virtual ~CoupledTransports() = default; + typedef Transport_ TransportType; + + CoupledTransports() : in(), out() {} + + std::shared_ptr in; + std::shared_ptr out; + +private: + CoupledTransports(const CoupledTransports&) = delete; + CoupledTransports& operator=(const CoupledTransports&) = delete; +}; + +/** + * Coupled TMemoryBuffers + */ +class CoupledMemoryBuffers : public CoupledTransports { +public: + CoupledMemoryBuffers() : buf(new TMemoryBuffer) { + in = buf; + out = buf; + } + + std::shared_ptr buf; +}; + +/** + * Helper template class for creating coupled transports that wrap + * another transport. + */ +template +class CoupledWrapperTransportsT : public CoupledTransports { +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 CoupledBufferedTransportsT + : public CoupledWrapperTransportsT {}; + +typedef CoupledBufferedTransportsT CoupledBufferedTransports; + +/** + * Coupled TFramedTransports. + */ +template +class CoupledFramedTransportsT + : public CoupledWrapperTransportsT {}; + +typedef CoupledFramedTransportsT CoupledFramedTransports; + +/** + * Coupled TZlibTransports. + */ +template +class CoupledZlibTransportsT : public CoupledWrapperTransportsT {}; + +typedef CoupledZlibTransportsT CoupledZlibTransports; + +#ifndef _WIN32 +// FD transport doesn't make much sense on Windows. +/** + * Coupled TFDTransports. + */ +class CoupledFDTransports : public CoupledTransports { +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 { +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 { +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 { +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 CoupledTTransports : public CoupledTransports { +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 CoupledBufferBases : public CoupledTransports { +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& transport, uint32_t writeLength) + : timeoutSeconds(seconds), transport(transport), writeLength(writeLength), next(nullptr) {} + + int timeoutSeconds; + std::shared_ptr 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 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& 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& 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 +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 wbuf = boost::shared_array(new uint8_t[totalSize]); + boost::shared_array rbuf = boost::shared_array(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 +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 +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 +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 +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 +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 +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(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, totalSize, ##__VA_ARGS__); \ + /* Test wrapping the transport with TBufferedTransport */ \ + ADD_TEST_RW(CoupledBufferedTransportsT, totalSize, ##__VA_ARGS__); \ + /* Test wrapping the transport with TFramedTransports */ \ + ADD_TEST_RW(CoupledFramedTransportsT, totalSize, ##__VA_ARGS__); \ + /* Test wrapping the transport with TZlibTransport */ \ + ADD_TEST_RW(CoupledZlibTransportsT, totalSize, ##__VA_ARGS__); \ + } while (0) + +#define ADD_TEST_BLOCKING(CoupledTransports) \ + addTestBlocking(BOOST_STRINGIZE(CoupledTransports)); + +#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \ + ADD_TEST_BLOCKING(CoupledTransports); \ + ADD_TEST_BLOCKING(CoupledTTransports); \ + ADD_TEST_BLOCKING(CoupledBufferedTransportsT); \ + ADD_TEST_BLOCKING(CoupledFramedTransportsT); \ + ADD_TEST_BLOCKING(CoupledZlibTransportsT); + +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, + 1024 * 1024, + rand4k, + rand4k, + rand4k, + rand4k); + ADD_TEST_RW(CoupledBufferBases, + 1024 * 1024, + rand4k, + rand4k, + rand4k, + rand4k); + ADD_TEST_RW(CoupledTTransports, + 1024 * 1024, + rand4k, + rand4k, + rand4k, + rand4k); + ADD_TEST_RW(CoupledBufferBases, + 1024 * 1024, + rand4k, + rand4k, + rand4k, + rand4k); + + // Test using TZlibTransport via a TTransport pointer + ADD_TEST_RW(CoupledTTransports, + 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 + 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(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 test_func +#else + boost::unit_test::callback0<> test_func +#endif + = std::bind(test_rw, + totalSize, + wSizeGen, + rSizeGen, + wChunkSizeGen, + rChunkSizeGen, + maxOutstanding); + suite_->add(MAKE_TEST_CASE(test_func, name.str()), expectedFailures); + } + + template + 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, 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, name), expectedFailures); + + THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_partial_midframe()", transportName); + suite_->add(MAKE_TEST_CASE(test_read_partial_midframe, name), expectedFailures); + + THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_none_available()", transportName); + suite_->add(MAKE_TEST_CASE(test_read_none_available, name), expectedFailures); + + THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_part_available()", transportName); + suite_->add(MAKE_TEST_CASE(test_borrow_part_available, name), expectedFailures); + + THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_none_available()", transportName); + suite_->add(MAKE_TEST_CASE(test_borrow_none_available, 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 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 +#include + +#include "gen-cpp/TypedefTest_types.h" + +BOOST_STATIC_ASSERT((boost::is_same::value)); +BOOST_STATIC_ASSERT((boost::is_same::value)); +BOOST_STATIC_ASSERT( + (boost::is_same::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 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 +#endif +#ifdef HAVE_INTTYPES_H +#include +#endif +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +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(mean, std_dev)) {} + + unsigned int getSize() override { + // Loop until we get a size of 1 or more + while (true) { + auto value = static_cast(gen_()); + if (value >= 1) { + return value; + } + } + } + +private: + boost::variate_generator > gen_; +}; + +boost::shared_array 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(buf); +} + +boost::shared_array 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 run_length_distribution(1, 64); + boost::uniform_smallint byte_distribution(0, UINT8_MAX); + boost::variate_generator > + byte_generator(rng, byte_distribution); + boost::variate_generator > + 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(buf); +} + +boost::shared_array gen_random_buffer(uint32_t buf_len) { + auto* buf = new uint8_t[buf_len]; + + boost::uniform_smallint distribution(0, UINT8_MAX); + boost::variate_generator > + generator(rng, distribution); + + for (uint32_t n = 0; n < buf_len; ++n) { + buf[n] = generator(); + } + + return boost::shared_array(buf); +} + +/* + * Test functions + */ + +void test_write_then_read(const boost::shared_array buf, uint32_t buf_len) { + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf)); + zlib_trans->write(buf.get(), buf_len); + zlib_trans->finish(); + + boost::shared_array 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 buf, uint32_t buf_len) { + // This one is tricky. I separate the last byte of the stream out + // into a separate crbuf_. The last byte is part of the checksum, + // so the entire read goes fine, but when I go to verify the checksum + // it isn't there. The original implementation complained that + // the stream was not complete. I'm about to go fix that. + // It worked. Awesome. + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf)); + 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(tmp_buf.length() - 1))); + + boost::shared_array 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 buf, uint32_t buf_len) { + // Make sure we still get that "not complete" error if + // it really isn't complete. + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr zlib_trans(new TZlibTransport(membuf)); + 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(reinterpret_cast(tmp_buf.data())), + static_cast(tmp_buf.length())); + + boost::shared_array 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 buf, + uint32_t buf_len, + const shared_ptr& write_gen, + const shared_ptr& read_gen) { + // Try it with a mix of read/write sizes. + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr 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 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 buf, uint32_t buf_len) { + // Verify checksum checking. + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr 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(tmp_buf.size() - 1); + tmp_buf[index]++; + membuf->resetBuffer(const_cast(reinterpret_cast(tmp_buf.data())), + static_cast(tmp_buf.length())); + + boost::shared_array 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 buf, uint32_t buf_len) { + // write some data + shared_ptr membuf(new TMemoryBuffer()); + shared_ptr 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 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 membuf(new TMemoryBuffer()); + shared_ptr 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 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& 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 size_32k(new ConstantSizeGenerator(1 << 15)); + shared_ptr 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 write_size_gen(new LogNormalSizeGenerator(20, 30)); + shared_ptr 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=, -s \n"); + fprintf(f, " --help\n"); +} + +#ifdef BOOST_TEST_DYN_LINK +bool init_unit_test_suite() { + auto seed = static_cast(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(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 +#include +#include + +#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 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::steady_clock::now().time_since_epoch()).count(); + int64_t time01 = std::chrono::duration_cast(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::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::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 +#include +#include +#include +#include + +#include +#include +#include + +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(new Monitor); + + for (int lix = 0; lix < loop; lix++) { + + int activeCount = 0; + + std::vector > threads; + int tix; + + for (tix = 0; tix < count; tix++) { + try { + ++activeCount; + threads.push_back( + threadFactory.newThread(shared_ptr(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 >::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 task + = shared_ptr(new SynchStartTask(monitor, state)); + + ThreadFactory threadFactory = ThreadFactory(); + + shared_ptr 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::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::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 task(new FloodTask(lix * count + tix, mon)); + shared_ptr 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace apache { +namespace thrift { +namespace concurrency { +namespace test { + +using namespace apache::thrift::concurrency; + +static std::deque > m_expired; +static void expiredNotifier(std::shared_ptr 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::steady_clock::now().time_since_epoch()).count(); + + sleep_(_timeout); + + _endTime = std::chrono::duration_cast(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::newSimpleThreadManager(workerCount); + + shared_ptr threadFactory + = shared_ptr(new ThreadFactory(false)); + + threadManager->threadFactory(threadFactory); + + threadManager->start(); + + std::set > tasks; + + for (size_t ix = 0; ix < count; ix++) { + + tasks.insert(shared_ptr( + new ThreadManagerTests::Task(monitor, activeCount, timeout))); + } + + int64_t time00 = std::chrono::duration_cast(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::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 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::newSimpleThreadManager(workerCount, pendingTaskMaxCount); + + shared_ptr threadFactory + = shared_ptr(new ThreadFactory()); + + threadManager->threadFactory(threadFactory); + + threadManager->start(); + + std::vector > tasks; + tasks.reserve(workerCount + pendingTaskMaxCount); + + for (size_t ix = 0; ix < workerCount; ix++) { + + tasks.push_back(shared_ptr( + new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[0], doneMonitor, activeCounts[0]))); + } + + for (size_t ix = 0; ix < pendingTaskMaxCount; ix++) { + + tasks.push_back(shared_ptr( + 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 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::steady_clock::now().time_since_epoch()).count(); + sleep_(100); + int64_t b = std::chrono::duration_cast(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(new ThreadFactory())); + + } + + bool apiTestWithThreadFactory(shared_ptr threadFactory) + { + shared_ptr 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 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 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 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 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 +#include +#include + +#include +#include +#include +#include + +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::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::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 orphanTask + = shared_ptr(new TimerManagerTests::Task(_monitor, 10 * timeout)); + + { + TimerManager timerManager; + timerManager.threadFactory(shared_ptr(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 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(new ThreadFactory())); + timerManager.start(); + assert(timerManager.state() == TimerManager::STARTED); + + Synchronized s(_monitor); + + // Setup the two tasks + shared_ptr taskToRemove + = shared_ptr(new TimerManagerTests::Task(_monitor, timeout / 2)); + timerManager.add(taskToRemove, taskToRemove->_timeout); + + shared_ptr task + = shared_ptr(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(new ThreadFactory())); + timerManager.start(); + assert(timerManager.state() == TimerManager::STARTED); + + Synchronized s(_monitor); + + // Setup the one tasks and add it twice + shared_ptr taskToRemove + = shared_ptr(new TimerManagerTests::Task(_monitor, timeout / 3)); + timerManager.add(taskToRemove, taskToRemove->_timeout); + timerManager.add(taskToRemove, taskToRemove->_timeout * 2); + + shared_ptr task + = shared_ptr(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(new ThreadFactory())); + timerManager.start(); + assert(timerManager.state() == TimerManager::STARTED); + + Synchronized s(_monitor); + + // Setup the two tasks + shared_ptr taskToRemove + = shared_ptr(new TimerManagerTests::Task(_monitor, timeout / 2)); + TimerManager::Timer timer = timerManager.add(taskToRemove, taskToRemove->_timeout); + + shared_ptr task + = shared_ptr(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(new ThreadFactory())); + timerManager.start(); + assert(timerManager.state() == TimerManager::STARTED); + + Synchronized s(_monitor); + + // Setup the task + shared_ptr task + = shared_ptr(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 +#include + +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 + +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 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& 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& _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 strings_; + std::shared_ptr 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& 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 in, + std::shared_ptr out, + uint32_t id) + : input(in), output(out), id(id) {} + + std::shared_ptr input; + std::shared_ptr 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& log) : nextId_(1), log_(log) {} + + void preServe() override {} + + void* createContext(std::shared_ptr input, + std::shared_ptr 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 input, + std::shared_ptr output) override { + auto* context = reinterpret_cast(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) 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(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 log_; +}; + +class ProcessorEventHandler : public TProcessorEventHandler { +public: + ProcessorEventHandler(const std::shared_ptr& log) : nextId_(1), log_(log) {} + + void* getContext(const char* fnName, void* serverContext) override { + auto* connContext = reinterpret_cast(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(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(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(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(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(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(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(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 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 createServer( + const std::shared_ptr& processor, + uint16_t port, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) { + std::shared_ptr socket(new TServerSocket(port)); + return std::shared_ptr( + new TSimpleServer(processor, socket, transportFactory, protocolFactory)); + } +}; + +class TThreadedServerTraits { +public: + typedef TThreadedServer ServerType; + + std::shared_ptr createServer( + const std::shared_ptr& processor, + uint16_t port, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) { + std::shared_ptr socket(new TServerSocket(port)); + return std::shared_ptr( + new TThreadedServer(processor, socket, transportFactory, protocolFactory)); + } +}; + +class TThreadPoolServerTraits { +public: + typedef TThreadPoolServer ServerType; + + std::shared_ptr createServer( + const std::shared_ptr& processor, + uint16_t port, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) { + std::shared_ptr socket(new TServerSocket(port)); + + std::shared_ptr threadFactory(new ThreadFactory); + std::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(8); + threadManager->threadFactory(threadFactory); + threadManager->start(); + + return std::shared_ptr( + new TThreadPoolServer(processor, socket, transportFactory, protocolFactory, threadManager)); + } +}; + +class TNonblockingServerTraits { +public: + typedef TNonblockingServer ServerType; + + std::shared_ptr createServer( + const std::shared_ptr& processor, + uint16_t port, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) { + // TNonblockingServer automatically uses TFramedTransport. + // Raise an exception if the supplied transport factory is not a + // TFramedTransportFactory + auto* framedFactory + = dynamic_cast(transportFactory.get()); + if (framedFactory == nullptr) { + throw TException("TNonblockingServer must use TFramedTransport"); + } + + std::shared_ptr socket(new TNonblockingServerSocket(port)); + std::shared_ptr threadFactory(new ThreadFactory); + std::shared_ptr threadManager = ThreadManager::newSimpleThreadManager(8); + threadManager->threadFactory(threadFactory); + threadManager->start(); + + return std::shared_ptr( + new TNonblockingServer(processor, protocolFactory, socket, threadManager)); + } +}; + +class TNonblockingServerNoThreadsTraits { +public: + typedef TNonblockingServer ServerType; + + std::shared_ptr createServer( + const std::shared_ptr& processor, + uint16_t port, + const std::shared_ptr& transportFactory, + const std::shared_ptr& protocolFactory) { + // TNonblockingServer automatically uses TFramedTransport. + // Raise an exception if the supplied transport factory is not a + // TFramedTransportFactory + auto* framedFactory + = dynamic_cast(transportFactory.get()); + if (framedFactory == nullptr) { + throw TException("TNonblockingServer must use TFramedTransport"); + } + + std::shared_ptr socket(new TNonblockingServerSocket(port)); + // Use a NULL ThreadManager + std::shared_ptr threadManager; + return std::shared_ptr( + 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 ProtocolFactory; + typedef TBinaryProtocolT Protocol; + + typedef ParentServiceProcessorT ParentProcessor; + typedef ChildServiceProcessorT ChildProcessor; + typedef ParentServiceClientT ParentClient; + typedef ChildServiceClientT ChildClient; +}; + +template +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 +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 +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 createServer(uint16_t port) override { + ServerTraits_ serverTraits; + return serverTraits.createServer(processor_, port, transportFactory_, protocolFactory_); + } + + std::shared_ptr getServerEventHandler() override { return serverEventHandler_; } + + void bindSuccessful(uint16_t port) override { port_ = port; } + + uint16_t getPort() const { return port_; } + + const std::shared_ptr& getLog() const { return log_; } + + const std::shared_ptr& getHandler() const { return handler_; } + + std::shared_ptr createClient() { + typedef typename ServiceTraits_::Protocol Protocol; + + std::shared_ptr socket(new TSocket("127.0.0.1", port_)); + std::shared_ptr transport(new Transport_(socket)); + std::shared_ptr protocol(new Protocol(transport)); + transport->open(); + + std::shared_ptr client(new Client(protocol)); + return client; + } + +private: + uint16_t port_; + std::shared_ptr log_; + std::shared_ptr handler_; + std::shared_ptr processor_; + std::shared_ptr transportFactory_; + std::shared_ptr protocolFactory_; + std::shared_ptr serverEventHandler_; + std::shared_ptr processorEventHandler_; +}; + +/** + * Check that there are no more events in the log + */ +void checkNoEvents(const std::shared_ptr& 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& 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& 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& 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& 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& 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 +void testParentService(const std::shared_ptr& state) { + std::shared_ptr 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 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 +void testChildService(const std::shared_ptr& state) { + std::shared_ptr 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 +void testBasicService() { + typedef ServiceState > State; + + // Start the server + std::shared_ptr state(new State); + ServerThread serverThread(state, true); + + testParentService(state); +} + +template +void testInheritedService() { + typedef ServiceState > State; + + // Start the server + std::shared_ptr 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 +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, + TBufferedTransportFactory, + TBufferedTransport> State; + + // Start the server + std::shared_ptr state(new State); + ServerThread serverThread(state, true); + + const std::shared_ptr& 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 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 +void testSeparateConnections() { + typedef ServiceState > State; + + // Start the server + std::shared_ptr state(new State); + ServerThread serverThread(state, true); + + const std::shared_ptr& log = state->getLog(); + + // Create a client + std::shared_ptr client1 = state->createClient(); + + // Make sure the expected events were logged + uint32_t client1Id = checkNewConnEvents(log); + + // Create a second client + std::shared_ptr 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 +void testOnewayCall() { + typedef ServiceState > State; + + // Start the server + std::shared_ptr state(new State); + ServerThread serverThread(state, true); + + const std::shared_ptr& log = state->getLog(); + + // Create a client + std::shared_ptr 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 +void testExpectedError() { + typedef ServiceState > State; + + // Start the server + std::shared_ptr state(new State); + ServerThread serverThread(state, true); + + const std::shared_ptr& log = state->getLog(); + + // Create a client + std::shared_ptr 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 +void testUnexpectedError() { + typedef ServiceState > State; + + // Start the server + std::shared_ptr state(new State); + ServerThread serverThread(state, true); + + const std::shared_ptr& log = state->getLog(); + + // Create a client + std::shared_ptr 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(); \ + } \ + BOOST_AUTO_TEST_CASE(Server##_##Template##_inheritedService) { \ + testInheritedService(); \ + } \ + BOOST_AUTO_TEST_CASE(Server##_##Template##_oneway) { \ + testOnewayCall(); \ + } \ + BOOST_AUTO_TEST_CASE(Server##_##Template##_exception) { \ + testExpectedError(); \ + } \ + BOOST_AUTO_TEST_CASE(Server##_##Template##_unexpectedException) { \ + testUnexpectedError(); \ + } + +// 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(); \ + } + +// 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(); \ + } + +#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 +#include +#include +#include +#include + +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 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 +#include +#include +#include + +#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 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 getServerEventHandler() { + return std::shared_ptr(); + } + + /** + * 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& 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_; + + uint16_t port_; + bool running_; + bool serving_; + bool error_; + concurrency::Monitor serverMonitor_; + + std::shared_ptr serverState_; + std::shared_ptr server_; + std::shared_ptr 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 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 +#include + +#include +#include +#include +#include + +#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 strings; + void addString(std::function cob, const std::string& s) override { + strings.push_back(s); + cob(); + } + void getStrings(std::function const& _return)> cob) override { + cob(strings); + } + + // Overrides not used in this test + void incrementGeneration(std::function cob) override {} + void getGeneration(std::function cob) override {} + void getDataWait(std::function cob, + const int32_t length) override {} + void onewayWait(std::function cob) override {} + void exceptionWait( + std::function cob, + std::function /* exn_cob */, + const std::string& message) override {} + void unexpectedExceptionWait(std::function 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 serverThread; + std::shared_ptr server; + std::shared_ptr client; +}; + +void TQTcpServerTest::initTestCase() { + // setup server + std::shared_ptr serverSocket = std::make_shared(); + server.reset(new async::TQTcpServer(serverSocket, + std::make_shared( + std::make_shared()), + std::make_shared())); + 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 socket = std::make_shared(); + client.reset(new test::ParentServiceClient(std::make_shared( + std::make_shared(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 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" -- cgit v1.2.3