Moving from govendor to dep, updated dependencies (#48)

* Moving from govendor to dep.

* Making the pull request template more friendly.

* Fixing akward space in PR template.

* goimports run on whole project using ` goimports -w $(find . -type f -name '*.go' -not -path "./vendor/*" -not -path "./gen-go/*")`

source of command: https://gist.github.com/bgentry/fd1ffef7dbde01857f66
This commit is contained in:
Renan DelValle 2018-01-07 13:13:47 -08:00 committed by GitHub
parent 9631aa3aab
commit 8d445c1c77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2186 changed files with 400410 additions and 352 deletions

View file

@ -0,0 +1,47 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <stdio.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/protocol/TCompactProtocol.h>
#include <thrift/transport/TBufferTransports.h>
#define BOOST_TEST_MODULE AllProtocolTests
#include <boost/test/unit_test.hpp>
#include "AllProtocolTests.tcc"
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
char errorMessage[ERR_LEN];
BOOST_AUTO_TEST_CASE(test_binary_protocol) {
testProtocol<TBinaryProtocol>("TBinaryProtocol");
}
BOOST_AUTO_TEST_CASE(test_little_binary_protocol) {
testProtocol<TLEBinaryProtocol>("TLEBinaryProtocol");
}
BOOST_AUTO_TEST_CASE(test_compact_protocol) {
testProtocol<TCompactProtocol>("TCompactProtocol");
}

View file

@ -0,0 +1,225 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef _THRIFT_TEST_GENERICPROTOCOLTEST_TCC_
#define _THRIFT_TEST_GENERICPROTOCOLTEST_TCC_ 1
#include <limits>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/Thrift.h>
#include "GenericHelpers.h"
using boost::shared_ptr;
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
#define ERR_LEN 512
extern char errorMessage[ERR_LEN];
template <typename TProto, typename Val>
void testNaked(Val val) {
shared_ptr<TTransport> transport(new TMemoryBuffer());
shared_ptr<TProtocol> protocol(new TProto(transport));
GenericIO::write(protocol, val);
Val out;
GenericIO::read(protocol, out);
if (out != val) {
THRIFT_SNPRINTF(errorMessage,
ERR_LEN,
"Invalid naked test (type: %s)",
ClassNames::getName<Val>());
throw TException(errorMessage);
}
}
template <typename TProto, TType type, typename Val>
void testField(const Val val) {
shared_ptr<TTransport> transport(new TMemoryBuffer());
shared_ptr<TProtocol> protocol(new TProto(transport));
protocol->writeStructBegin("test_struct");
protocol->writeFieldBegin("test_field", type, (int16_t)15);
GenericIO::write(protocol, val);
protocol->writeFieldEnd();
protocol->writeStructEnd();
std::string name;
TType fieldType;
int16_t fieldId;
protocol->readStructBegin(name);
protocol->readFieldBegin(name, fieldType, fieldId);
if (fieldId != 15) {
THRIFT_SNPRINTF(errorMessage, ERR_LEN, "Invalid ID (type: %s)", typeid(val).name());
throw TException(errorMessage);
}
if (fieldType != type) {
THRIFT_SNPRINTF(errorMessage, ERR_LEN, "Invalid Field Type (type: %s)", typeid(val).name());
throw TException(errorMessage);
}
Val out;
GenericIO::read(protocol, out);
if (out != val) {
THRIFT_SNPRINTF(errorMessage, ERR_LEN, "Invalid value read (type: %s)", typeid(val).name());
throw TException(errorMessage);
}
protocol->readFieldEnd();
protocol->readStructEnd();
}
template <typename TProto>
void testMessage() {
struct TMessage {
const char* name;
TMessageType type;
int32_t seqid;
} messages[] = {{"short message name", T_CALL, 0},
{"1", T_REPLY, 12345},
{"loooooooooooooooooooooooooooooooooong", T_EXCEPTION, 1 << 16},
{"one way push", T_ONEWAY, 12},
{"Janky", T_CALL, 0}};
const int messages_count = sizeof(messages) / sizeof(TMessage);
for (int i = 0; i < messages_count; i++) {
shared_ptr<TTransport> transport(new TMemoryBuffer());
shared_ptr<TProtocol> protocol(new TProto(transport));
protocol->writeMessageBegin(messages[i].name, messages[i].type, messages[i].seqid);
protocol->writeMessageEnd();
std::string name;
TMessageType type;
int32_t seqid;
protocol->readMessageBegin(name, type, seqid);
if (name != messages[i].name || type != messages[i].type || seqid != messages[i].seqid) {
throw TException("readMessageBegin failed.");
}
}
}
template <typename TProto>
void testProtocol(const char* protoname) {
try {
testNaked<TProto, int8_t>((int8_t)123);
for (int32_t i = 0; i < 128; i++) {
testField<TProto, T_BYTE, int8_t>((int8_t)i);
testField<TProto, T_BYTE, int8_t>((int8_t)-i);
}
testNaked<TProto, int16_t>((int16_t)0);
testNaked<TProto, int16_t>((int16_t)1);
testNaked<TProto, int16_t>((int16_t)15000);
testNaked<TProto, int16_t>((int16_t)0x7fff);
testNaked<TProto, int16_t>((int16_t)-1);
testNaked<TProto, int16_t>((int16_t)-15000);
testNaked<TProto, int16_t>((int16_t)-0x7fff);
testNaked<TProto, int16_t>((std::numeric_limits<int16_t>::min)());
testNaked<TProto, int16_t>((std::numeric_limits<int16_t>::max)());
testField<TProto, T_I16, int16_t>((int16_t)0);
testField<TProto, T_I16, int16_t>((int16_t)1);
testField<TProto, T_I16, int16_t>((int16_t)7);
testField<TProto, T_I16, int16_t>((int16_t)150);
testField<TProto, T_I16, int16_t>((int16_t)15000);
testField<TProto, T_I16, int16_t>((int16_t)0x7fff);
testField<TProto, T_I16, int16_t>((int16_t)-1);
testField<TProto, T_I16, int16_t>((int16_t)-7);
testField<TProto, T_I16, int16_t>((int16_t)-150);
testField<TProto, T_I16, int16_t>((int16_t)-15000);
testField<TProto, T_I16, int16_t>((int16_t)-0x7fff);
testNaked<TProto, int32_t>(0);
testNaked<TProto, int32_t>(1);
testNaked<TProto, int32_t>(15000);
testNaked<TProto, int32_t>(0xffff);
testNaked<TProto, int32_t>(-1);
testNaked<TProto, int32_t>(-15000);
testNaked<TProto, int32_t>(-0xffff);
testNaked<TProto, int32_t>((std::numeric_limits<int32_t>::min)());
testNaked<TProto, int32_t>((std::numeric_limits<int32_t>::max)());
testField<TProto, T_I32, int32_t>(0);
testField<TProto, T_I32, int32_t>(1);
testField<TProto, T_I32, int32_t>(7);
testField<TProto, T_I32, int32_t>(150);
testField<TProto, T_I32, int32_t>(15000);
testField<TProto, T_I32, int32_t>(31337);
testField<TProto, T_I32, int32_t>(0xffff);
testField<TProto, T_I32, int32_t>(0xffffff);
testField<TProto, T_I32, int32_t>(-1);
testField<TProto, T_I32, int32_t>(-7);
testField<TProto, T_I32, int32_t>(-150);
testField<TProto, T_I32, int32_t>(-15000);
testField<TProto, T_I32, int32_t>(-0xffff);
testField<TProto, T_I32, int32_t>(-0xffffff);
testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::min)());
testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::max)());
testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::min)() + 10);
testNaked<TProto, int64_t>((std::numeric_limits<int32_t>::max)() - 16);
testNaked<TProto, int64_t>((std::numeric_limits<int64_t>::min)());
testNaked<TProto, int64_t>((std::numeric_limits<int64_t>::max)());
testNaked<TProto, int64_t>(0);
for (int64_t i = 0; i < 62; i++) {
testNaked<TProto, int64_t>(1L << i);
testNaked<TProto, int64_t>(-(1L << i));
}
testField<TProto, T_I64, int64_t>(0);
for (int i = 0; i < 62; i++) {
testField<TProto, T_I64, int64_t>(1L << i);
testField<TProto, T_I64, int64_t>(-(1L << i));
}
testNaked<TProto, double>(123.456);
testNaked<TProto, std::string>("");
testNaked<TProto, std::string>("short");
testNaked<TProto, std::string>("borderlinetiny");
testNaked<TProto, std::string>("a bit longer than the smallest possible");
testNaked<TProto, std::string>("\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA"); // kinda binary test
testField<TProto, T_STRING, std::string>("");
testField<TProto, T_STRING, std::string>("short");
testField<TProto, T_STRING, std::string>("borderlinetiny");
testField<TProto, T_STRING, std::string>("a bit longer than the smallest possible");
testMessage<TProto>();
printf("%s => OK\n", protoname);
} catch (TException e) {
THRIFT_SNPRINTF(errorMessage, ERR_LEN, "%s => Test FAILED: %s", protoname, e.what());
throw TException(errorMessage);
}
}
#endif

View file

@ -0,0 +1,70 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <boost/test/auto_unit_test.hpp>
#include <thrift/protocol/TBase64Utils.h>
using apache::thrift::protocol::base64_encode;
using apache::thrift::protocol::base64_decode;
BOOST_AUTO_TEST_SUITE(Base64Test)
void setupTestData(int i, uint8_t* data, int& len) {
len = 0;
do {
data[len] = (uint8_t)(i & 0xFF);
i >>= 8;
len++;
} while ((len < 3) && (i != 0));
BOOST_ASSERT(i == 0);
}
void checkEncoding(uint8_t* data, int len) {
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()

View file

@ -0,0 +1,243 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <iostream>
#define _USE_MATH_DEFINES
#include <math.h>
#include "thrift/transport/TBufferTransports.h"
#include "thrift/protocol/TBinaryProtocol.h"
#include "gen-cpp/DebugProtoTest_types.h"
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
class Timer {
public:
timeval vStart;
Timer() { THRIFT_GETTIMEOFDAY(&vStart, 0); }
void start() { THRIFT_GETTIMEOFDAY(&vStart, 0); }
double frame() {
timeval vEnd;
THRIFT_GETTIMEOFDAY(&vEnd, 0);
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 std;
using namespace thrift::test::debug;
using namespace apache::thrift::transport;
using namespace apache::thrift::protocol;
using namespace boost;
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;
boost::shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer(num*1000));
uint8_t* data = NULL;
uint32_t datasize = 0;
{
buf->resetBuffer();
TBinaryProtocolT<TMemoryBuffer> prot(buf);
double elapsed = 0.0;
Timer timer;
for (int i = 0; i < num; i++) {
ooe.write(&prot);
}
elapsed = timer.frame();
cout << "Write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
buf->getBuffer(&data, &datasize);
{
boost::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
TBinaryProtocolT<TMemoryBuffer> prot(buf2);
OneOfEach ooe2;
double elapsed = 0.0;
Timer timer;
for (int i = 0; i < num; i++) {
ooe2.read(&prot);
}
elapsed = timer.frame();
cout << " Read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
{
buf->resetBuffer();
TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf);
double elapsed = 0.0;
Timer timer;
for (int i = 0; i < num; i++) {
ooe.write(&prot);
}
elapsed = timer.frame();
cout << "Write little endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
{
OneOfEach ooe2;
boost::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf2);
double elapsed = 0.0;
Timer timer;
for (int i = 0; i < num; i++) {
ooe2.read(&prot);
}
elapsed = timer.frame();
cout << " Read little endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
{
buf->resetBuffer();
TBinaryProtocolT<TMemoryBuffer> prot(buf);
double elapsed = 0.0;
Timer timer;
for (int i = 0; i < num; i++) {
ooe.write(&prot);
}
elapsed = timer.frame();
cout << "Write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
{
boost::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
TBinaryProtocolT<TMemoryBuffer> prot(buf2);
OneOfEach ooe2;
double elapsed = 0.0;
Timer timer;
for (int i = 0; i < num; i++) {
ooe2.read(&prot);
}
elapsed = timer.frame();
cout << " Read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
data = NULL;
datasize = 0;
num = 10000000;
ListDoublePerf listDoublePerf;
listDoublePerf.field.reserve(num);
for (int x = 0; x < num; ++x)
listDoublePerf.field.push_back(double(x));
buf.reset(new TMemoryBuffer(num * 100));
{
buf->resetBuffer();
TBinaryProtocolT<TMemoryBuffer> prot(buf);
double elapsed = 0.0;
Timer timer;
listDoublePerf.write(&prot);
elapsed = timer.frame();
cout << "Double write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
buf->getBuffer(&data, &datasize);
{
boost::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
TBinaryProtocolT<TMemoryBuffer> prot(buf2);
ListDoublePerf listDoublePerf2;
double elapsed = 0.0;
Timer timer;
listDoublePerf2.read(&prot);
elapsed = timer.frame();
cout << " Double read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
{
buf->resetBuffer();
TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf);
double elapsed = 0.0;
Timer timer;
listDoublePerf.write(&prot);
elapsed = timer.frame();
cout << "Double write little endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
{
ListDoublePerf listDoublePerf2;
boost::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
TBinaryProtocolT<TMemoryBuffer, TNetworkLittleEndian> prot(buf2);
double elapsed = 0.0;
Timer timer;
listDoublePerf2.read(&prot);
elapsed = timer.frame();
cout << " Double read little endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
{
buf->resetBuffer();
TBinaryProtocolT<TMemoryBuffer> prot(buf);
double elapsed = 0.0;
Timer timer;
listDoublePerf.write(&prot);
elapsed = timer.frame();
cout << "Double write big endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
{
boost::shared_ptr<TMemoryBuffer> buf2(new TMemoryBuffer(data, datasize));
TBinaryProtocolT<TMemoryBuffer> prot(buf2);
ListDoublePerf listDoublePerf2;
double elapsed = 0.0;
Timer timer;
listDoublePerf2.read(&prot);
elapsed = timer.frame();
cout << " Double read big endian: " << num / (1000 * elapsed) << " kHz" << endl;
}
return 0;
}

View file

@ -0,0 +1,361 @@
#
# 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_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
#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/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/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
TMemoryBufferTest.cpp
TBufferBaseTest.cpp
Base64Test.cpp
ToStringTest.cpp
TypedefTest.cpp
TServerSocketTest.cpp
TServerTransportTest.cpp
)
if(NOT WITH_BOOSTTHREADS AND NOT WITH_STDTHREADS AND NOT MSVC)
list(APPEND UnitTest_SOURCES RWMutexStarveTest.cpp)
endif()
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(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)
endif()
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_QT4)
set(CMAKE_AUTOMOC ON)
find_package(Qt4 REQUIRED COMPONENTS QtTest)
set(TQTcpServerTest_SOURCES
qt/TQTcpServerTest.cpp
)
add_executable(TQTcpServerTest ${TQTcpServerTest_SOURCES})
target_link_libraries(TQTcpServerTest testgencpp_cob thriftqt Qt4::QtTest)
LINK_AGAINST_THRIFT_LIBRARY(TQTcpServerTest thrift)
add_test(NAME TQTcpServerTest COMMAND TQTcpServerTest)
endif()
if(WITH_QT5)
add_subdirectory(qt)
endif()
#
# Common thrift code generation rules
#
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/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
)

View file

@ -0,0 +1,309 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#define _USE_MATH_DEFINES
#include <cmath>
#include "gen-cpp/DebugProtoTest_types.h"
#include <thrift/protocol/TDebugProtocol.h>
#define BOOST_TEST_MODULE DebugProtoTest
#include <boost/test/unit_test.hpp>
using namespace thrift::test::debug;
static std::auto_ptr<OneOfEach> ooe;
void testCaseSetup_1() {
ooe.reset(new OneOfEach);
ooe->im_true = true;
ooe->im_false = false;
ooe->a_bite = 0x7f;
ooe->integer16 = 27000;
ooe->integer32 = 1 << 24;
ooe->integer64 = (uint64_t)6000 * 1000 * 1000;
ooe->double_precision = M_PI;
ooe->some_characters = "Debug THIS!";
ooe->zomg_unicode = "\xd7\n\a\t";
}
BOOST_AUTO_TEST_CASE(test_debug_proto_1) {
testCaseSetup_1();
const std::string expected_result(
"OneOfEach {\n"
" 01: im_true (bool) = true,\n"
" 02: im_false (bool) = false,\n"
" 03: a_bite (byte) = 0x7f,\n"
" 04: integer16 (i16) = 27000,\n"
" 05: integer32 (i32) = 16777216,\n"
" 06: integer64 (i64) = 6000000000,\n"
" 07: double_precision (double) = 3.1415926535897931,\n"
" 08: some_characters (string) = \"Debug THIS!\",\n"
" 09: zomg_unicode (string) = \"\\xd7\\n\\a\\t\",\n"
" 10: what_who (bool) = false,\n"
" 11: base64 (string) = \"\",\n"
" 12: byte_list (list) = list<byte>[3] {\n"
" [0] = 0x01,\n"
" [1] = 0x02,\n"
" [2] = 0x03,\n"
" },\n"
" 13: i16_list (list) = list<i16>[3] {\n"
" [0] = 1,\n"
" [1] = 2,\n"
" [2] = 3,\n"
" },\n"
" 14: i64_list (list) = list<i64>[3] {\n"
" [0] = 1,\n"
" [1] = 2,\n"
" [2] = 3,\n"
" },\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(*ooe));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
static std::auto_ptr<Nesting> n;
void testCaseSetup_2() {
testCaseSetup_1();
n.reset(new Nesting);
n->my_ooe = *ooe;
n->my_ooe.integer16 = 16;
n->my_ooe.integer32 = 32;
n->my_ooe.integer64 = 64;
n->my_ooe.double_precision = (std::sqrt(5.0) + 1) / 2;
n->my_ooe.some_characters = ":R (me going \"rrrr\")";
n->my_ooe.zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20\xd0\x9d\xce"
"\xbf\xe2\x85\xbf\xd0\xbe\xc9\xa1\xd0\xb3\xd0"
"\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
"\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
"\xbc";
n->my_bonk.type = 31337;
n->my_bonk.message = "I am a bonk... xor!";
}
BOOST_AUTO_TEST_CASE(test_debug_proto_2) {
testCaseSetup_2();
const std::string expected_result(
"Nesting {\n"
" 01: my_bonk (struct) = Bonk {\n"
" 01: type (i32) = 31337,\n"
" 02: message (string) = \"I am a bonk... xor!\",\n"
" },\n"
" 02: my_ooe (struct) = OneOfEach {\n"
" 01: im_true (bool) = true,\n"
" 02: im_false (bool) = false,\n"
" 03: a_bite (byte) = 0x7f,\n"
" 04: integer16 (i16) = 16,\n"
" 05: integer32 (i32) = 32,\n"
" 06: integer64 (i64) = 64,\n"
" 07: double_precision (double) = 1.6180339887498949,\n"
" 08: some_characters (string) = \":R (me going \\\"rrrr\\\")\",\n"
" 09: zomg_unicode (string) = \"\\xd3\\x80\\xe2\\x85\\xae\\xce\\x9d \\xd"
"0\\x9d\\xce\\xbf\\xe2\\x85\\xbf\\xd0\\xbe\\xc9\\xa1\\xd0\\xb3\\xd0\\xb0"
"\\xcf\\x81\\xe2\\x84\\x8e \\xce\\x91tt\\xce\\xb1\\xe2\\x85\\xbd\\xce\\xb"
"a\\xc7\\x83\\xe2\\x80\\xbc\",\n"
" 10: what_who (bool) = false,\n"
" 11: base64 (string) = \"\",\n"
" 12: byte_list (list) = list<byte>[3] {\n"
" [0] = 0x01,\n"
" [1] = 0x02,\n"
" [2] = 0x03,\n"
" },\n"
" 13: i16_list (list) = list<i16>[3] {\n"
" [0] = 1,\n"
" [1] = 2,\n"
" [2] = 3,\n"
" },\n"
" 14: i64_list (list) = list<i64>[3] {\n"
" [0] = 1,\n"
" [1] = 2,\n"
" [2] = 3,\n"
" },\n"
" },\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(*n));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
static std::auto_ptr<HolyMoley> hm;
void testCaseSetup_3() {
testCaseSetup_2();
hm.reset(new HolyMoley);
hm->big.push_back(*ooe);
hm->big.push_back(n->my_ooe);
hm->big[0].a_bite = 0x22;
hm->big[1].a_bite = 0x33;
std::vector<std::string> stage1;
stage1.push_back("and a one");
stage1.push_back("and a two");
hm->contain.insert(stage1);
stage1.clear();
stage1.push_back("then a one, two");
stage1.push_back("three!");
stage1.push_back("FOUR!!");
hm->contain.insert(stage1);
stage1.clear();
hm->contain.insert(stage1);
std::vector<Bonk> stage2;
hm->bonks["nothing"] = stage2;
stage2.resize(stage2.size() + 1);
stage2.back().type = 1;
stage2.back().message = "Wait.";
stage2.resize(stage2.size() + 1);
stage2.back().type = 2;
stage2.back().message = "What?";
hm->bonks["something"] = stage2;
stage2.clear();
stage2.resize(stage2.size() + 1);
stage2.back().type = 3;
stage2.back().message = "quoth";
stage2.resize(stage2.size() + 1);
stage2.back().type = 4;
stage2.back().message = "the raven";
stage2.resize(stage2.size() + 1);
stage2.back().type = 5;
stage2.back().message = "nevermore";
hm->bonks["poe"] = stage2;
}
BOOST_AUTO_TEST_CASE(test_debug_proto_3) {
testCaseSetup_3();
const std::string expected_result(
"HolyMoley {\n"
" 01: big (list) = list<struct>[2] {\n"
" [0] = OneOfEach {\n"
" 01: im_true (bool) = true,\n"
" 02: im_false (bool) = false,\n"
" 03: a_bite (byte) = 0x22,\n"
" 04: integer16 (i16) = 27000,\n"
" 05: integer32 (i32) = 16777216,\n"
" 06: integer64 (i64) = 6000000000,\n"
" 07: double_precision (double) = 3.1415926535897931,\n"
" 08: some_characters (string) = \"Debug THIS!\",\n"
" 09: zomg_unicode (string) = \"\\xd7\\n\\a\\t\",\n"
" 10: what_who (bool) = false,\n"
" 11: base64 (string) = \"\",\n"
" 12: byte_list (list) = list<byte>[3] {\n"
" [0] = 0x01,\n"
" [1] = 0x02,\n"
" [2] = 0x03,\n"
" },\n"
" 13: i16_list (list) = list<i16>[3] {\n"
" [0] = 1,\n"
" [1] = 2,\n"
" [2] = 3,\n"
" },\n"
" 14: i64_list (list) = list<i64>[3] {\n"
" [0] = 1,\n"
" [1] = 2,\n"
" [2] = 3,\n"
" },\n"
" },\n"
" [1] = OneOfEach {\n"
" 01: im_true (bool) = true,\n"
" 02: im_false (bool) = false,\n"
" 03: a_bite (byte) = 0x33,\n"
" 04: integer16 (i16) = 16,\n"
" 05: integer32 (i32) = 32,\n"
" 06: integer64 (i64) = 64,\n"
" 07: double_precision (double) = 1.6180339887498949,\n"
" 08: some_characters (string) = \":R (me going \\\"rrrr\\\")\",\n"
" 09: zomg_unicode (string) = \"\\xd3\\x80\\xe2\\x85\\xae\\xce\\x9d \\"
"xd0\\x9d\\xce\\xbf\\xe2\\x85\\xbf\\xd0\\xbe\\xc9\\xa1\\xd0\\xb3\\xd0\\xb"
"0\\xcf\\x81\\xe2\\x84\\x8e \\xce\\x91tt\\xce\\xb1\\xe2\\x85\\xbd\\xce\\x"
"ba\\xc7\\x83\\xe2\\x80\\xbc\",\n"
" 10: what_who (bool) = false,\n"
" 11: base64 (string) = \"\",\n"
" 12: byte_list (list) = list<byte>[3] {\n"
" [0] = 0x01,\n"
" [1] = 0x02,\n"
" [2] = 0x03,\n"
" },\n"
" 13: i16_list (list) = list<i16>[3] {\n"
" [0] = 1,\n"
" [1] = 2,\n"
" [2] = 3,\n"
" },\n"
" 14: i64_list (list) = list<i64>[3] {\n"
" [0] = 1,\n"
" [1] = 2,\n"
" [2] = 3,\n"
" },\n"
" },\n"
" },\n"
" 02: contain (set) = set<list>[3] {\n"
" list<string>[0] {\n"
" },\n"
" list<string>[2] {\n"
" [0] = \"and a one\",\n"
" [1] = \"and a two\",\n"
" },\n"
" list<string>[3] {\n"
" [0] = \"then a one, two\",\n"
" [1] = \"three!\",\n"
" [2] = \"FOUR!!\",\n"
" },\n"
" },\n"
" 03: bonks (map) = map<string,list>[3] {\n"
" \"nothing\" -> list<struct>[0] {\n"
" },\n"
" \"poe\" -> list<struct>[3] {\n"
" [0] = Bonk {\n"
" 01: type (i32) = 3,\n"
" 02: message (string) = \"quoth\",\n"
" },\n"
" [1] = Bonk {\n"
" 01: type (i32) = 4,\n"
" 02: message (string) = \"the raven\",\n"
" },\n"
" [2] = Bonk {\n"
" 01: type (i32) = 5,\n"
" 02: message (string) = \"nevermore\",\n"
" },\n"
" },\n"
" \"something\" -> list<struct>[2] {\n"
" [0] = Bonk {\n"
" 01: type (i32) = 1,\n"
" 02: message (string) = \"Wait.\",\n"
" },\n"
" [1] = Bonk {\n"
" 01: type (i32) = 2,\n"
" 02: message (string) = \"What?\",\n"
" },\n"
" },\n"
" },\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(*hm));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}

View file

@ -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;
}
}
}
}

View file

@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#define BOOST_TEST_MODULE EnumTest
#include <boost/test/unit_test.hpp>
#include "gen-cpp/EnumTest_types.h"
BOOST_AUTO_TEST_SUITE(EnumTest)
BOOST_AUTO_TEST_CASE(test_enum) {
// 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_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()

View file

@ -0,0 +1,110 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef _THRIFT_TEST_GENERICHELPERS_H_
#define _THRIFT_TEST_GENERICHELPERS_H_ 1
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/Thrift.h>
using boost::shared_ptr;
using namespace apache::thrift::protocol;
/* ClassName Helper for cleaner exceptions */
class ClassNames {
public:
template <typename T>
static const char* getName() {
return "Unknown type";
}
};
template <>
const char* ClassNames::getName<int8_t>() {
return "byte";
}
template <>
const char* ClassNames::getName<int16_t>() {
return "short";
}
template <>
const char* ClassNames::getName<int32_t>() {
return "int";
}
template <>
const char* ClassNames::getName<int64_t>() {
return "long";
}
template <>
const char* ClassNames::getName<double>() {
return "double";
}
template <>
const char* ClassNames::getName<std::string>() {
return "string";
}
/* Generic Protocol I/O function for tests */
class GenericIO {
public:
/* Write functions */
static uint32_t write(shared_ptr<TProtocol> proto, const int8_t& val) {
return proto->writeByte(val);
}
static uint32_t write(shared_ptr<TProtocol> proto, const int16_t& val) {
return proto->writeI16(val);
}
static uint32_t write(shared_ptr<TProtocol> proto, const int32_t& val) {
return proto->writeI32(val);
}
static uint32_t write(shared_ptr<TProtocol> proto, const double& val) {
return proto->writeDouble(val);
}
static uint32_t write(shared_ptr<TProtocol> proto, const int64_t& val) {
return proto->writeI64(val);
}
static uint32_t write(shared_ptr<TProtocol> proto, const std::string& val) {
return proto->writeString(val);
}
/* Read functions */
static uint32_t read(shared_ptr<TProtocol> proto, int8_t& val) { return proto->readByte(val); }
static uint32_t read(shared_ptr<TProtocol> proto, int16_t& val) { return proto->readI16(val); }
static uint32_t read(shared_ptr<TProtocol> proto, int32_t& val) { return proto->readI32(val); }
static uint32_t read(shared_ptr<TProtocol> proto, int64_t& val) { return proto->readI64(val); }
static uint32_t read(shared_ptr<TProtocol> proto, double& val) { return proto->readDouble(val); }
static uint32_t read(shared_ptr<TProtocol> proto, std::string& val) {
return proto->readString(val);
}
};
#endif

View file

@ -0,0 +1,340 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#define _USE_MATH_DEFINES
#include <cmath>
#include <iomanip>
#include <sstream>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/protocol/TJSONProtocol.h>
#include "gen-cpp/DebugProtoTest_types.h"
#define BOOST_TEST_MODULE JSONProtoTest
#include <boost/test/unit_test.hpp>
using namespace thrift::test::debug;
using apache::thrift::transport::TMemoryBuffer;
using apache::thrift::protocol::TJSONProtocol;
static std::auto_ptr<OneOfEach> ooe;
void testCaseSetup_1() {
ooe.reset(new OneOfEach);
ooe->im_true = true;
ooe->im_false = false;
ooe->a_bite = 0x7f;
ooe->integer16 = 27000;
ooe->integer32 = 1 << 24;
ooe->integer64 = (uint64_t)6000 * 1000 * 1000;
ooe->double_precision = M_PI;
ooe->some_characters = "JSON THIS! \"\1";
ooe->zomg_unicode = "\xd7\n\a\t";
ooe->base64 = "\1\2\3\255";
}
BOOST_AUTO_TEST_CASE(test_json_proto_1) {
testCaseSetup_1();
const std::string expected_result(
"{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
"\"5\":{\"i32\":16777216},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
"535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001\"},\"9\":{\"str\":\"\xd7\\"
"n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":\"AQIDrQ\"},\"12\":{\"lst\""
":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
"\",3,1,2,3]}}");
const std::string result(apache::thrift::ThriftJSONString(*ooe));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
static std::auto_ptr<Nesting> n;
void testCaseSetup_2() {
testCaseSetup_1();
n.reset(new Nesting);
n->my_ooe = *ooe;
n->my_ooe.integer16 = 16;
n->my_ooe.integer32 = 32;
n->my_ooe.integer64 = 64;
n->my_ooe.double_precision = (std::sqrt(5.0) + 1) / 2;
n->my_ooe.some_characters = ":R (me going \"rrrr\")";
n->my_ooe.zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20\xd0\x9d\xce"
"\xbf\xe2\x85\xbf\xd0\xbe\xc9\xa1\xd0\xb3\xd0"
"\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
"\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
"\xbc";
n->my_bonk.type = 31337;
n->my_bonk.message = "I am a bonk... xor!";
}
BOOST_AUTO_TEST_CASE(test_json_proto_2) {
testCaseSetup_2();
const std::string expected_result(
"{\"1\":{\"rec\":{\"1\":{\"i32\":31337},\"2\":{\"str\":\"I am a bonk... xor"
"!\"}}},\"2\":{\"rec\":{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127"
"},\"4\":{\"i16\":16},\"5\":{\"i32\":32},\"6\":{\"i64\":64},\"7\":{\"dbl\":"
"1.6180339887498949},\"8\":{\"str\":\":R (me going \\\"rrrr\\\")\"},\"9\":{"
"\"str\":\"ӀΝ Нοⅿоɡгаρℎ Αttακǃ\"},\"10\":{\"tf\":0},\"11\":{\"str\":\""
"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2"
",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}}}}"
);
const std::string result(apache::thrift::ThriftJSONString(*n));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
static std::auto_ptr<HolyMoley> hm;
void testCaseSetup_3() {
testCaseSetup_2();
hm.reset(new HolyMoley);
hm->big.push_back(*ooe);
hm->big.push_back(n->my_ooe);
hm->big[0].a_bite = 0x22;
hm->big[1].a_bite = 0x33;
std::vector<std::string> stage1;
stage1.push_back("and a one");
stage1.push_back("and a two");
hm->contain.insert(stage1);
stage1.clear();
stage1.push_back("then a one, two");
stage1.push_back("three!");
stage1.push_back("FOUR!!");
hm->contain.insert(stage1);
stage1.clear();
hm->contain.insert(stage1);
std::vector<Bonk> stage2;
hm->bonks["nothing"] = stage2;
stage2.resize(stage2.size() + 1);
stage2.back().type = 1;
stage2.back().message = "Wait.";
stage2.resize(stage2.size() + 1);
stage2.back().type = 2;
stage2.back().message = "What?";
hm->bonks["something"] = stage2;
stage2.clear();
stage2.resize(stage2.size() + 1);
stage2.back().type = 3;
stage2.back().message = "quoth";
stage2.resize(stage2.size() + 1);
stage2.back().type = 4;
stage2.back().message = "the raven";
stage2.resize(stage2.size() + 1);
stage2.back().type = 5;
stage2.back().message = "nevermore";
hm->bonks["poe"] = stage2;
}
BOOST_AUTO_TEST_CASE(test_json_proto_3) {
testCaseSetup_3();
const std::string expected_result(
"{\"1\":{\"lst\":[\"rec\",2,{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":"
"34},\"4\":{\"i16\":27000},\"5\":{\"i32\":16777216},\"6\":{\"i64\":6000000000"
"},\"7\":{\"dbl\":3.1415926535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001"
"\"},\"9\":{\"str\":\"\xd7\\n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":"
"\"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2"
",3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}},{\"1\":{\"tf\":1},\"2\":{\"tf\":0},"
"\"3\":{\"i8\":51},\"4\":{\"i16\":16},\"5\":{\"i32\":32},\"6\":{\"i64\":64},"
"\"7\":{\"dbl\":1.6180339887498949},\"8\":{\"str\":\":R (me going \\\"rrrr\\\""
")\"},\"9\":{\"str\":\"ӀΝ Нοⅿоɡгаρℎ Αttακǃ\"},\"10\":{\"tf\":0},\"11\":{"
"\"str\":\"AQIDrQ\"},\"12\":{\"lst\":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16"
"\",3,1,2,3]},\"14\":{\"lst\":[\"i64\",3,1,2,3]}}]},\"2\":{\"set\":[\"lst\",3"
",[\"str\",0],[\"str\",2,\"and a one\",\"and a two\"],[\"str\",3,\"then a one"
", two\",\"three!\",\"FOUR!!\"]]},\"3\":{\"map\":[\"str\",\"lst\",3,{\"nothin"
"g\":[\"rec\",0],\"poe\":[\"rec\",3,{\"1\":{\"i32\":3},\"2\":{\"str\":\"quoth"
"\"}},{\"1\":{\"i32\":4},\"2\":{\"str\":\"the raven\"}},{\"1\":{\"i32\":5},\""
"2\":{\"str\":\"nevermore\"}}],\"something\":[\"rec\",2,{\"1\":{\"i32\":1},\""
"2\":{\"str\":\"Wait.\"}},{\"1\":{\"i32\":2},\"2\":{\"str\":\"What?\"}}]}]}}"
);
const std::string result(apache::thrift::ThriftJSONString(*hm));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
BOOST_AUTO_TEST_CASE(test_json_proto_4) {
testCaseSetup_1();
boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
boost::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
ooe->write(proto.get());
OneOfEach ooe2;
ooe2.read(proto.get());
BOOST_CHECK(*ooe == ooe2);
}
BOOST_AUTO_TEST_CASE(test_json_proto_5) {
testCaseSetup_3();
boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
boost::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
hm->write(proto.get());
HolyMoley hm2;
hm2.read(proto.get());
BOOST_CHECK(*hm == hm2);
hm2.big[0].a_bite = 0x00;
BOOST_CHECK(*hm != hm2);
}
BOOST_AUTO_TEST_CASE(test_json_proto_6) {
Doubles dub;
dub.nan = HUGE_VAL / HUGE_VAL;
dub.inf = HUGE_VAL;
dub.neginf = -HUGE_VAL;
dub.repeating = 10.0 / 3.0;
dub.big = 1E+305;
dub.tiny = 1E-305;
dub.zero = 0.0;
dub.negzero = -0.0;
const std::string expected_result(
"{\"1\":{\"dbl\":\"NaN\"},\"2\":{\"dbl\":\"Infinity\"},\"3\":{\"dbl\":\"-Infi"
"nity\"},\"4\":{\"dbl\":3.3333333333333335},\"5\":{\"dbl\":9.9999999999999994e+"
"304},\"6\":{\"dbl\":1e-305},\"7\":{\"dbl\":0},\"8\":{\"dbl\":-0}}"
);
const std::string result(apache::thrift::ThriftJSONString(dub));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
BOOST_AUTO_TEST_CASE(test_json_proto_7) {
boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
boost::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
Base64 base;
base.a = 123;
base.b1 = "1";
base.b2 = "12";
base.b3 = "123";
base.b4 = "1234";
base.b5 = "12345";
base.b6 = "123456";
base.write(proto.get());
Base64 base2;
base2.read(proto.get());
BOOST_CHECK(base == base2);
}
BOOST_AUTO_TEST_CASE(test_json_proto_8) {
const char* json_string =
"{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
"\"5\":{\"i32\":16.77216},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
"535897931},\"8\":{\"str\":\"JSON THIS! \\\"\\u0001\"},\"9\":{\"str\":\"\xd7\\"
"n\\u0007\\t\"},\"10\":{\"tf\":0},\"11\":{\"str\":\"AQIDrQ\"},\"12\":{\"lst\""
":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
"\",3,1,2,3]}}";
boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
(uint8_t*)(json_string), strlen(json_string)*sizeof(char)));
boost::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
OneOfEach ooe2;
BOOST_CHECK_THROW(ooe2.read(proto.get()),
apache::thrift::protocol::TProtocolException);
}
static std::string toHexSequence(const std::string& str) {
std::stringstream ss;
ss << std::hex << std::setfill('0');
for (std::size_t i = 0; i < str.size(); i++) {
ss << "\\x" << int(uint8_t(str[i]));
}
return ss.str();
}
BOOST_AUTO_TEST_CASE(test_json_unicode_escaped) {
const char json_string[] =
"{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
"\"5\":{\"i32\":16},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
"535897931},\"8\":{\"str\":\"JSON THIS!\"},\"9\":{\"str\":\"\\u0e01 \\ud835\\udd3e\"},"
"\"10\":{\"tf\":0},\"11\":{\"str\":\"000000\"},\"12\":{\"lst\""
":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
"\",3,1,2,3]}}";
const char* expected_zomg_unicode = "\xe0\xb8\x81 \xf0\x9d\x94\xbe";
boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
(uint8_t*)(json_string), sizeof(json_string)));
boost::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
OneOfEach ooe2;
ooe2.read(proto.get());
BOOST_CHECK_MESSAGE(!ooe2.zomg_unicode.compare(expected_zomg_unicode),
"Expected:\n" << toHexSequence(expected_zomg_unicode) << "\nGotten:\n"
<< toHexSequence(ooe2.zomg_unicode));
}
BOOST_AUTO_TEST_CASE(test_json_unicode_escaped_missing_low_surrogate) {
const char json_string[] =
"{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
"\"5\":{\"i32\":16},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
"535897931},\"8\":{\"str\":\"JSON THIS!\"},\"9\":{\"str\":\"\\ud835\"},"
"\"10\":{\"tf\":0},\"11\":{\"str\":\"000000\"},\"12\":{\"lst\""
":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
"\",3,1,2,3]}}";
boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
(uint8_t*)(json_string), sizeof(json_string)));
boost::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
OneOfEach ooe2;
BOOST_CHECK_THROW(ooe2.read(proto.get()),
apache::thrift::protocol::TProtocolException);
}
BOOST_AUTO_TEST_CASE(test_json_unicode_escaped_missing_hi_surrogate) {
const char json_string[] =
"{\"1\":{\"tf\":1},\"2\":{\"tf\":0},\"3\":{\"i8\":127},\"4\":{\"i16\":27000},"
"\"5\":{\"i32\":16},\"6\":{\"i64\":6000000000},\"7\":{\"dbl\":3.1415926"
"535897931},\"8\":{\"str\":\"JSON THIS!\"},\"9\":{\"str\":\"\\udd3e\"},"
"\"10\":{\"tf\":0},\"11\":{\"str\":\"000000\"},\"12\":{\"lst\""
":[\"i8\",3,1,2,3]},\"13\":{\"lst\":[\"i16\",3,1,2,3]},\"14\":{\"lst\":[\"i64"
"\",3,1,2,3]}}";
boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(
(uint8_t*)(json_string), sizeof(json_string)));
boost::shared_ptr<TJSONProtocol> proto(new TJSONProtocol(buffer));
OneOfEach ooe2;
BOOST_CHECK_THROW(ooe2.read(proto.get()),
apache::thrift::protocol::TProtocolException);
}

374
vendor/git.apache.org/thrift.git/lib/cpp/test/Makefile.am generated vendored Executable file
View file

@ -0,0 +1,374 @@
#
# 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
BUILT_SOURCES = 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/proc_types.h
noinst_LTLIBRARIES = libtestgencpp.la libprocessortest.la
nodist_libtestgencpp_la_SOURCES = \
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/ThriftTest_constants.cpp \
gen-cpp/ThriftTest_constants.h \
gen-cpp/TypedefTest_types.cpp \
gen-cpp/TypedefTest_types.h \
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
if AMX_HAVE_LIBEVENT
noinst_PROGRAMS += \
processor_test
check_PROGRAMS += \
TNonblockingServerTest
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 \
TMemoryBufferTest.cpp \
TBufferBaseTest.cpp \
Base64Test.cpp \
ToStringTest.cpp \
TypedefTest.cpp \
TServerSocketTest.cpp \
TServerTransportTest.cpp \
TTransportCheckThrow.h
if !WITH_BOOSTTHREADS
UnitTests_SOURCES += \
RWMutexStarveTest.cpp
endif
UnitTests_LDADD = \
libtestgencpp.la \
$(BOOST_TEST_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)
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)
#
# 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
#
THRIFT = $(top_builddir)/compiler/cpp/thrift
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/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/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 -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

View file

@ -0,0 +1,94 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// To show that this test actually tests something, you can change
// MANUAL_OPENSSL_INIT to 0 to cause automatic OpenSSL init/cleanup,
// which will cause the test to fail
#define MANUAL_OPENSSL_INIT 1
#ifdef _WIN32
#include <WinSock2.h>
#endif
#include <boost/test/unit_test.hpp>
#include <openssl/evp.h>
#include <thrift/transport/TSSLSocket.h>
using namespace std;
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 != NULL);
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

View file

@ -0,0 +1,386 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* Contains some contributions under the Thrift Software License.
* Please see doc/old-thrift-license.txt in the Thrift distribution for
* details.
*/
#include <map>
#include <thrift/protocol/TDebugProtocol.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TBufferTransports.h>
#include "gen-cpp/OptionalRequiredTest_types.h"
#define BOOST_TEST_MODULE OptionalRequiredTest
#include <boost/test/unit_test.hpp>
using namespace thrift::test;
using namespace apache::thrift;
using namespace apache::thrift::transport;
using namespace apache::thrift::protocol;
/*
template<typename Struct>
void trywrite(const Struct& s, bool should_work) {
bool worked;
try {
TBinaryProtocol protocol(boost::shared_ptr<TTransport>(new TMemoryBuffer));
s.write(&protocol);
worked = true;
} catch (TProtocolException & ex) {
worked = false;
}
BOOST_CHECK(worked == should_work);
}
*/
template <typename Struct1, typename Struct2>
void write_to_read(const Struct1& w, Struct2& r) {
TBinaryProtocol protocol(boost::shared_ptr<TTransport>(new TMemoryBuffer));
w.write(&protocol);
r.read(&protocol);
}
BOOST_AUTO_TEST_CASE(test_optional_required_1) {
OldSchool o;
const std::string expected_result(
"OldSchool {\n"
" 01: im_int (i16) = 0,\n"
" 02: im_str (string) = \"\",\n"
" 03: im_big (list) = list<map>[0] {\n"
" },\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(o));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
BOOST_AUTO_TEST_CASE(test_optional_required_2_1) {
Simple s;
const std::string expected_result(
"Simple {\n"
" 01: im_default (i16) = 0,\n"
" 02: im_required (i16) = 0,\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(s));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
BOOST_AUTO_TEST_CASE(test_optional_required_2_2) {
Simple s;
s.im_optional = 10;
const std::string expected_result(
"Simple {\n"
" 01: im_default (i16) = 0,\n"
" 02: im_required (i16) = 0,\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(s));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
BOOST_AUTO_TEST_CASE(test_optional_required_2_3) {
Simple s;
s.im_optional = 10;
s.__isset.im_optional = true;
const std::string expected_result(
"Simple {\n"
" 01: im_default (i16) = 0,\n"
" 02: im_required (i16) = 0,\n"
" 03: im_optional (i16) = 10,\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(s));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
BOOST_AUTO_TEST_CASE(test_optional_required_2_4) {
Simple s;
s.__isset.im_optional = true;
const std::string expected_result(
"Simple {\n"
" 01: im_default (i16) = 0,\n"
" 02: im_required (i16) = 0,\n"
" 03: im_optional (i16) = 0,\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(s));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
BOOST_AUTO_TEST_CASE(test_optional_required_2_5) {
Simple s;
s.__isset.im_optional = true;
s.im_optional = 10;
const std::string expected_result(
"Simple {\n"
" 01: im_default (i16) = 0,\n"
" 02: im_required (i16) = 0,\n"
" 03: im_optional (i16) = 10,\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(s));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
BOOST_AUTO_TEST_CASE(test_optional_required_3) {
// assign/copy-construct with non-required fields
Simple s1, s2;
s1.__isset.im_default = true;
s1.__set_im_optional(10);
BOOST_CHECK(s1.__isset.im_default);
BOOST_CHECK(s1.__isset.im_optional);
s2 = s1;
BOOST_CHECK(s2.__isset.im_default);
BOOST_CHECK(s2.__isset.im_optional);
Simple s3(s1);
BOOST_CHECK(s3.__isset.im_default);
BOOST_CHECK(s3.__isset.im_optional);
}
BOOST_AUTO_TEST_CASE(test_optional_required_4) {
// Write-to-read with optional fields.
Simple s1, s2, s3;
s1.im_optional = 10;
BOOST_CHECK(!s1.__isset.im_default);
// BOOST_CHECK(!s1.__isset.im_required); // Compile error.
BOOST_CHECK(!s1.__isset.im_optional);
write_to_read(s1, s2);
BOOST_CHECK(s2.__isset.im_default);
// BOOST_CHECK( s2.__isset.im_required); // Compile error.
BOOST_CHECK(!s2.__isset.im_optional);
BOOST_CHECK(s3.im_optional == 0);
s1.__isset.im_optional = true;
write_to_read(s1, s3);
BOOST_CHECK(s3.__isset.im_default);
// BOOST_CHECK( s3.__isset.im_required); // Compile error.
BOOST_CHECK(s3.__isset.im_optional);
BOOST_CHECK(s3.im_optional == 10);
}
BOOST_AUTO_TEST_CASE(test_optional_required_5) {
// Writing between optional and default.
Tricky1 t1;
Tricky2 t2;
t2.im_optional = 10;
write_to_read(t2, t1);
write_to_read(t1, t2);
BOOST_CHECK(!t1.__isset.im_default);
BOOST_CHECK(t2.__isset.im_optional);
BOOST_CHECK(t1.im_default == t2.im_optional);
BOOST_CHECK(t1.im_default == 0);
}
BOOST_AUTO_TEST_CASE(test_optional_required_6) {
// Writing between default and required.
Tricky1 t1;
Tricky3 t3;
write_to_read(t1, t3);
write_to_read(t3, t1);
BOOST_CHECK(t1.__isset.im_default);
}
BOOST_AUTO_TEST_CASE(test_optional_required_7) {
// Writing between optional and required.
Tricky2 t2;
Tricky3 t3;
t2.__isset.im_optional = true;
write_to_read(t2, t3);
write_to_read(t3, t2);
}
BOOST_AUTO_TEST_CASE(test_optional_required_8) {
// Mu-hu-ha-ha-ha!
Tricky2 t2;
Tricky3 t3;
try {
write_to_read(t2, t3);
abort();
} catch (const TProtocolException&) {
}
write_to_read(t3, t2);
BOOST_CHECK(t2.__isset.im_optional);
}
BOOST_AUTO_TEST_CASE(test_optional_required_9) {
Complex c;
const std::string expected_result(
"Complex {\n"
" 01: cp_default (i16) = 0,\n"
" 02: cp_required (i16) = 0,\n"
" 04: the_map (map) = map<i16,struct>[0] {\n"
" },\n"
" 05: req_simp (struct) = Simple {\n"
" 01: im_default (i16) = 0,\n"
" 02: im_required (i16) = 0,\n"
" },\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(c));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
BOOST_AUTO_TEST_CASE(test_optional_required_10) {
Tricky1 t1;
Tricky2 t2;
// Compile error.
//(void)(t1 == t2);
}
BOOST_AUTO_TEST_CASE(test_optional_required_11) {
OldSchool o1, o2, o3;
BOOST_CHECK(o1 == o2);
o1.im_int = o2.im_int = 10;
BOOST_CHECK(o1 == o2);
o1.__isset.im_int = true;
o2.__isset.im_int = false;
BOOST_CHECK(o1 == o2);
o1.im_int = 20;
o1.__isset.im_int = false;
BOOST_CHECK(o1 != o2);
o1.im_int = 10;
BOOST_CHECK(o1 == o2);
o1.im_str = o2.im_str = "foo";
BOOST_CHECK(o1 == o2);
o1.__isset.im_str = o2.__isset.im_str = true;
BOOST_CHECK(o1 == o2);
std::map<int32_t, std::string> mymap;
mymap[1] = "bar";
mymap[2] = "baz";
o1.im_big.push_back(std::map<int32_t, std::string>());
BOOST_CHECK(o1 != o2);
o2.im_big.push_back(std::map<int32_t, std::string>());
BOOST_CHECK(o1 == o2);
o2.im_big.push_back(mymap);
BOOST_CHECK(o1 != o2);
o1.im_big.push_back(mymap);
BOOST_CHECK(o1 == o2);
TBinaryProtocol protocol(boost::shared_ptr<TTransport>(new TMemoryBuffer));
o1.write(&protocol);
o1.im_big.push_back(mymap);
mymap[3] = "qux";
o2.im_big.push_back(mymap);
BOOST_CHECK(o1 != o2);
o1.im_big.back()[3] = "qux";
BOOST_CHECK(o1 == o2);
o3.read(&protocol);
o3.im_big.push_back(mymap);
BOOST_CHECK(o1 == o3);
const std::string expected_result(
"OldSchool {\n"
" 01: im_int (i16) = 10,\n"
" 02: im_str (string) = \"foo\",\n"
" 03: im_big (list) = list<map>[3] {\n"
" [0] = map<i32,string>[0] {\n"
" },\n"
" [1] = map<i32,string>[2] {\n"
" 1 -> \"bar\",\n"
" 2 -> \"baz\",\n"
" },\n"
" [2] = map<i32,string>[3] {\n"
" 1 -> \"bar\",\n"
" 2 -> \"baz\",\n"
" 3 -> \"qux\",\n"
" },\n"
" },\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(o3));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}
BOOST_AUTO_TEST_CASE(test_optional_required_12) {
Tricky2 t1, t2;
BOOST_CHECK(t1.__isset.im_optional == false);
BOOST_CHECK(t2.__isset.im_optional == false);
BOOST_CHECK(t1 == t2);
t1.im_optional = 5;
BOOST_CHECK(t1 == t2);
t2.im_optional = 5;
BOOST_CHECK(t1 == t2);
t1.__isset.im_optional = true;
BOOST_CHECK(t1 != t2);
t2.__isset.im_optional = true;
BOOST_CHECK(t1 == t2);
t1.im_optional = 10;
BOOST_CHECK(t1 != t2);
t2.__isset.im_optional = false;
BOOST_CHECK(t1 != t2);
}
BOOST_AUTO_TEST_CASE(test_optional_required_13) {
OptionalDefault t1, t2;
BOOST_CHECK(t1.__isset.opt_int == true);
BOOST_CHECK(t1.__isset.opt_str == true);
BOOST_CHECK(t1.opt_int == t2.opt_int);
BOOST_CHECK(t1.opt_str == t2.opt_str);
write_to_read(t1, t2);
BOOST_CHECK(t2.__isset.opt_int == true);
BOOST_CHECK(t2.__isset.opt_str == true);
BOOST_CHECK(t1.opt_int == t2.opt_int);
BOOST_CHECK(t1.opt_str == t2.opt_str);
const std::string expected_result(
"OptionalDefault {\n"
" 01: opt_int (i16) = 1234,\n"
" 02: opt_str (string) = \"default\",\n"
"}");
const std::string result(apache::thrift::ThriftDebugString(t2));
BOOST_CHECK_MESSAGE(!expected_result.compare(result),
"Expected:\n" << expected_result << "\nGotten:\n" << result);
}

View file

@ -0,0 +1,159 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <iostream>
#include <unistd.h>
#include <boost/shared_ptr.hpp>
#include <boost/test/unit_test.hpp>
#include "thrift/concurrency/Mutex.h"
#include "thrift/concurrency/PosixThreadFactory.h"
using boost::shared_ptr;
using boost::unit_test::test_suite;
using boost::unit_test::framework::master_test_suite;
using namespace apache::thrift::concurrency;
using namespace std;
class Locker : public Runnable {
protected:
Locker(boost::shared_ptr<ReadWriteMutex> rwlock, bool writer)
: rwlock_(rwlock), writer_(writer), started_(false), gotLock_(false), signaled_(false) {}
public:
virtual void run() {
started_ = true;
if (writer_) {
rwlock_->acquireWrite();
} else {
rwlock_->acquireRead();
}
gotLock_ = true;
while (!signaled_) {
usleep(5000);
}
rwlock_->release();
}
bool started() const { return started_; }
bool gotLock() const { return gotLock_; }
void signal() { signaled_ = true; }
protected:
boost::shared_ptr<ReadWriteMutex> rwlock_;
bool writer_;
volatile bool started_;
volatile bool gotLock_;
volatile bool signaled_;
};
class Reader : public Locker {
public:
Reader(boost::shared_ptr<ReadWriteMutex> rwlock) : Locker(rwlock, false) {}
};
class Writer : public Locker {
public:
Writer(boost::shared_ptr<ReadWriteMutex> rwlock) : Locker(rwlock, true) {}
};
void test_starve(PosixThreadFactory::POLICY policy) {
// the man pages for pthread_wrlock_rdlock suggest that any OS guarantee about
// writer starvation may be influenced by the scheduling policy, so let's try
// all 3 policies to see if any of them work.
PosixThreadFactory factory(policy);
factory.setDetached(false);
boost::shared_ptr<ReadWriteMutex> rwlock(new NoStarveReadWriteMutex());
boost::shared_ptr<Reader> reader1(new Reader(rwlock));
boost::shared_ptr<Reader> reader2(new Reader(rwlock));
boost::shared_ptr<Writer> writer(new Writer(rwlock));
boost::shared_ptr<Thread> treader1 = factory.newThread(reader1);
boost::shared_ptr<Thread> treader2 = factory.newThread(reader2);
boost::shared_ptr<Thread> twriter = factory.newThread(writer);
// launch a reader and make sure he has the lock
treader1->start();
while (!reader1->gotLock()) {
usleep(2000);
}
// launch a writer and make sure he's blocked on the lock
twriter->start();
while (!writer->started()) {
usleep(2000);
}
// tricky part... we can never be 100% sure that the writer is actually
// blocked on the lock, but we can pretty reasonably sure because we know
// he just executed the line immediately before getting the lock, and
// we'll wait a full second for him to get on it.
sleep(1);
// launch a second reader... if the RWMutex guarantees that writers won't
// starve, this reader should not be able to acquire the lock until the writer
// has acquired and released it.
treader2->start();
while (!reader2->started()) {
usleep(2000);
}
// again... can't be 100% sure the reader is waiting on (or has) the lock
// but we can be close.
sleep(1);
// tell reader 1 to let go of the lock
reader1->signal();
// wait for someone to get the lock
while (!reader2->gotLock() && !writer->gotLock()) {
usleep(2000);
}
// the test succeeded if the WRITER got the lock.
bool success = writer->gotLock();
// tell everyone we're done and wait for them to finish
reader2->signal();
writer->signal();
treader1->join();
treader2->join();
twriter->join();
// make sure it worked.
BOOST_CHECK_MESSAGE(success, "writer is starving");
}
BOOST_AUTO_TEST_SUITE(RWMutexStarveTest)
BOOST_AUTO_TEST_CASE(test_starve_other) {
test_starve(PosixThreadFactory::OTHER);
}
BOOST_AUTO_TEST_CASE(test_starve_rr) {
test_starve(PosixThreadFactory::ROUND_ROBIN);
}
BOOST_AUTO_TEST_CASE(test_starve_fifo) {
test_starve(PosixThreadFactory::FIFO);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -0,0 +1,91 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* Contains some contributions under the Thrift Software License.
* Please see doc/old-thrift-license.txt in the Thrift distribution for
* details.
*/
#include "gen-cpp/Recursive_types.h"
#include <thrift/transport/TBufferTransports.h>
#include <thrift/protocol/TBinaryProtocol.h>
#define BOOST_TEST_MODULE RecursiveTest
#include <boost/test/unit_test.hpp>
using apache::thrift::transport::TMemoryBuffer;
using apache::thrift::protocol::TBinaryProtocol;
using boost::shared_ptr;
BOOST_AUTO_TEST_CASE(test_recursive_1) {
shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
RecTree tree;
RecTree child;
tree.children.push_back(child);
tree.write(prot.get());
RecTree result;
result.read(prot.get());
BOOST_CHECK(tree == result);
}
BOOST_AUTO_TEST_CASE(test_recursive_2) {
shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
RecList l;
boost::shared_ptr<RecList> l2(new RecList);
l.nextitem = l2;
l.write(prot.get());
RecList resultlist;
resultlist.read(prot.get());
BOOST_CHECK(resultlist.nextitem != NULL);
BOOST_CHECK(resultlist.nextitem->nextitem == NULL);
}
BOOST_AUTO_TEST_CASE(test_recursive_3) {
shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
CoRec c;
boost::shared_ptr<CoRec2> r(new CoRec2);
c.other = r;
c.write(prot.get());
c.read(prot.get());
BOOST_CHECK(c.other != NULL);
BOOST_CHECK(c.other->other.other == NULL);
}
BOOST_AUTO_TEST_CASE(test_recursive_4) {
shared_ptr<TMemoryBuffer> buf(new TMemoryBuffer());
shared_ptr<TBinaryProtocol> prot(new TBinaryProtocol(buf));
boost::shared_ptr<RecList> depthLimit(new RecList);
depthLimit->nextitem = depthLimit;
BOOST_CHECK_THROW(depthLimit->write(prot.get()),
apache::thrift::protocol::TProtocolException);
depthLimit->nextitem.reset();
}

View file

@ -0,0 +1,276 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#define BOOST_TEST_MODULE SecurityTest
#include <boost/test/unit_test.hpp>
#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <thrift/transport/TSSLServerSocket.h>
#include <thrift/transport/TSSLSocket.h>
#include <thrift/transport/TTransport.h>
#include <vector>
#ifdef __linux__
#include <signal.h>
#endif
using apache::thrift::transport::TSSLServerSocket;
using apache::thrift::transport::TServerTransport;
using apache::thrift::transport::TSSLSocket;
using apache::thrift::transport::TSSLSocketFactory;
using apache::thrift::transport::TTransport;
using apache::thrift::transport::TTransportException;
using apache::thrift::transport::TTransportFactory;
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);
boost::shared_ptr<TSSLSocketFactory> pServerSocketFactory;
boost::shared_ptr<TSSLServerSocket> pServerSocket;
pServerSocketFactory.reset(new TSSLSocketFactory(static_cast<apache::thrift::transport::SSLProtocol>(protocol)));
pServerSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
pServerSocketFactory->loadCertificate(certFile("server.crt").string().c_str());
pServerSocketFactory->loadPrivateKey(certFile("server.key").string().c_str());
pServerSocketFactory->server(true);
pServerSocket.reset(new TSSLServerSocket("localhost", 0, pServerSocketFactory));
boost::shared_ptr<TTransport> connectedClient;
try
{
pServerSocket->listen();
mPort = pServerSocket->getPort();
mCVar.notify_one();
lock.unlock();
connectedClient = pServerSocket->accept();
uint8_t buf[2];
buf[0] = 'O';
buf[1] = 'K';
connectedClient->write(&buf[0], 2);
connectedClient->flush();
}
catch (apache::thrift::transport::TTransportException& ex)
{
boost::mutex::scoped_lock lock(gMutex);
BOOST_TEST_MESSAGE(boost::format("SRV %1% Exception: %2%") % boost::this_thread::get_id() % ex.what());
}
if (connectedClient)
{
connectedClient->close();
connectedClient.reset();
}
pServerSocket->close();
pServerSocket.reset();
}
catch (std::exception& ex)
{
BOOST_FAIL(boost::format("%1%: %2%") % typeid(ex).name() % ex.what());
}
}
void client(apache::thrift::transport::SSLProtocol protocol)
{
try
{
boost::shared_ptr<TSSLSocketFactory> pClientSocketFactory;
boost::shared_ptr<TSSLSocket> pClientSocket;
try
{
pClientSocketFactory.reset(new TSSLSocketFactory(static_cast<apache::thrift::transport::SSLProtocol>(protocol)));
pClientSocketFactory->authenticate(true);
pClientSocketFactory->loadCertificate(certFile("client.crt").string().c_str());
pClientSocketFactory->loadPrivateKey(certFile("client.key").string().c_str());
pClientSocketFactory->loadTrustedCertificates(certFile("CA.pem").string().c_str());
pClientSocket = pClientSocketFactory->createSocket("localhost", mPort);
pClientSocket->open();
uint8_t buf[3];
buf[0] = 0;
buf[1] = 0;
BOOST_CHECK_EQUAL(2, pClientSocket->read(&buf[0], 2));
BOOST_CHECK_EQUAL(0, memcmp(&buf[0], "OK", 2));
mConnected = true;
}
catch (apache::thrift::transport::TTransportException& ex)
{
boost::mutex::scoped_lock lock(gMutex);
BOOST_TEST_MESSAGE(boost::format("CLI %1% Exception: %2%") % boost::this_thread::get_id() % ex.what());
}
if (pClientSocket)
{
pClientSocket->close();
pClientSocket.reset();
}
}
catch (std::exception& ex)
{
BOOST_FAIL(boost::format("%1%: %2%") % typeid(ex).name() % ex.what());
}
}
static const char *protocol2str(size_t protocol)
{
static const char *strings[apache::thrift::transport::LATEST + 1] =
{
"SSLTLS",
"SSLv2",
"SSLv3",
"TLSv1_0",
"TLSv1_1",
"TLSv1_2"
};
return strings[protocol];
}
boost::mutex mMutex;
boost::condition_variable mCVar;
int mPort;
bool mConnected;
};
BOOST_FIXTURE_TEST_SUITE(BOOST_TEST_MODULE, SecurityFixture)
BOOST_AUTO_TEST_CASE(ssl_security_matrix)
{
try
{
// matrix of connection success between client and server with different SSLProtocol selections
bool matrix[apache::thrift::transport::LATEST + 1][apache::thrift::transport::LATEST + 1] =
{
// server = SSLTLS SSLv2 SSLv3 TLSv1_0 TLSv1_1 TLSv1_2
// client
/* SSLTLS */ { true, false, false, true, true, true },
/* SSLv2 */ { false, false, false, false, false, false },
/* SSLv3 */ { false, false, true, false, false, false },
/* TLSv1_0 */ { true, false, false, true, false, false },
/* TLSv1_1 */ { true, false, false, false, true, false },
/* TLSv1_2 */ { true, false, false, false, false, true }
};
for (size_t si = 0; si <= apache::thrift::transport::LATEST; ++si)
{
for (size_t ci = 0; ci <= apache::thrift::transport::LATEST; ++ci)
{
if (si == 1 || ci == 1)
{
// Skip all SSLv2 cases - protocol not supported
continue;
}
#ifdef OPENSSL_NO_SSL3
if (si == 2 || ci == 2)
{
// Skip all SSLv3 cases - protocol not supported
continue;
}
#endif
boost::mutex::scoped_lock lock(mMutex);
BOOST_TEST_MESSAGE(boost::format("TEST: Server = %1%, Client = %2%")
% protocol2str(si) % protocol2str(ci));
mConnected = false;
boost::thread_group threads;
threads.create_thread(boost::bind(&SecurityFixture::server, this, static_cast<apache::thrift::transport::SSLProtocol>(si)));
mCVar.wait(lock); // wait for listen() to succeed
lock.unlock();
threads.create_thread(boost::bind(&SecurityFixture::client, this, static_cast<apache::thrift::transport::SSLProtocol>(ci)));
threads.join_all();
BOOST_CHECK_MESSAGE(mConnected == matrix[ci][si],
boost::format(" Server = %1%, Client = %2% expected mConnected == %3% but was %4%")
% protocol2str(si) % protocol2str(ci) % matrix[ci][si] % mConnected);
}
}
}
catch (std::exception& ex)
{
BOOST_FAIL(boost::format("%1%: %2%") % typeid(ex).name() % ex.what());
}
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -0,0 +1,103 @@
#define _USE_MATH_DEFINES
#include <cmath>
#include <thrift/transport/TTransportUtils.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <gen-cpp/DebugProtoTest_types.h>
using namespace thrift::test::debug;
using namespace apache::thrift::transport;
using namespace apache::thrift::protocol;
#define BOOST_TEST_MODULE SpecializationTest
#include <boost/test/unit_test.hpp>
typedef TBinaryProtocolT<TMemoryBuffer> MyProtocol;
// typedef TBinaryProtocolT<TTransport> MyProtocol;
BOOST_AUTO_TEST_CASE(test_specialization_1) {
OneOfEach ooe;
ooe.im_true = true;
ooe.im_false = false;
ooe.a_bite = 0x7f;
ooe.integer16 = 27000;
ooe.integer32 = 1 << 24;
ooe.integer64 = (uint64_t)6000 * 1000 * 1000;
ooe.double_precision = M_PI;
ooe.some_characters = "JSON THIS! \"\1";
ooe.zomg_unicode = "\xd7\n\a\t";
ooe.base64 = "\1\2\3\255";
Nesting n;
n.my_ooe = ooe;
n.my_ooe.integer16 = 16;
n.my_ooe.integer32 = 32;
n.my_ooe.integer64 = 64;
n.my_ooe.double_precision = (std::sqrt(5.0) + 1) / 2;
n.my_ooe.some_characters = ":R (me going \"rrrr\")";
n.my_ooe.zomg_unicode = "\xd3\x80\xe2\x85\xae\xce\x9d\x20\xd0\x9d\xce"
"\xbf\xe2\x85\xbf\xd0\xbe\xc9\xa1\xd0\xb3\xd0"
"\xb0\xcf\x81\xe2\x84\x8e\x20\xce\x91\x74\x74"
"\xce\xb1\xe2\x85\xbd\xce\xba\xc7\x83\xe2\x80"
"\xbc";
n.my_bonk.type = 31337;
n.my_bonk.message = "I am a bonk... xor!";
HolyMoley hm;
hm.big.push_back(ooe);
hm.big.push_back(n.my_ooe);
hm.big[0].a_bite = 0x22;
hm.big[1].a_bite = 0x33;
std::vector<std::string> stage1;
stage1.push_back("and a one");
stage1.push_back("and a two");
hm.contain.insert(stage1);
stage1.clear();
stage1.push_back("then a one, two");
stage1.push_back("three!");
stage1.push_back("FOUR!!");
hm.contain.insert(stage1);
stage1.clear();
hm.contain.insert(stage1);
std::vector<Bonk> stage2;
hm.bonks["nothing"] = stage2;
stage2.resize(stage2.size() + 1);
stage2.back().type = 1;
stage2.back().message = "Wait.";
stage2.resize(stage2.size() + 1);
stage2.back().type = 2;
stage2.back().message = "What?";
hm.bonks["something"] = stage2;
stage2.clear();
stage2.resize(stage2.size() + 1);
stage2.back().type = 3;
stage2.back().message = "quoth";
stage2.resize(stage2.size() + 1);
stage2.back().type = 4;
stage2.back().message = "the raven";
stage2.resize(stage2.size() + 1);
stage2.back().type = 5;
stage2.back().message = "nevermore";
hm.bonks["poe"] = stage2;
boost::shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
boost::shared_ptr<TProtocol> proto(new MyProtocol(buffer));
ooe.write(proto.get());
OneOfEach ooe2;
ooe2.read(proto.get());
BOOST_CHECK(ooe == ooe2);
hm.write(proto.get());
HolyMoley hm2;
hm2.read(proto.get());
BOOST_CHECK(hm == hm2);
hm2.big[0].a_bite = 0x00;
BOOST_CHECK(hm != hm2);
}

View file

@ -0,0 +1,645 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <algorithm>
#include <boost/test/auto_unit_test.hpp>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/transport/TShortReadTransport.h>
using std::string;
using boost::shared_ptr;
using apache::thrift::transport::TMemoryBuffer;
using apache::thrift::transport::TBufferedTransport;
using apache::thrift::transport::TFramedTransport;
using apache::thrift::transport::test::TShortReadTransport;
// 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 (size_t i = 0; i < (sizeof(data)/sizeof(data[0])); ++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 (int d1 = 0; d1 < 3; d1++) {
TMemoryBuffer buffer(16);
int offset = 0;
int index = 0;
while (offset < 1<<15) {
buffer.write(&data[offset], dist[d1][index]);
offset += dist[d1][index];
index++;
}
string output = buffer.getBufferAsString();
BOOST_CHECK_EQUAL(data_str, output);
}
}
BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_Read ) {
init_data();
for (int d1 = 0; d1 < 3; d1++) {
for (int d2 = 0; d2 < 3; d2++) {
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], dist[d1][index]);
offset += dist[d1][index];
index++;
}
offset = 0;
index = 0;
while (offset < 1<<15) {
unsigned int got = buffer.read(&data_out[offset], dist[d2][index]);
BOOST_CHECK_EQUAL(got, dist[d2][index]);
offset += dist[d2][index];
index++;
}
BOOST_CHECK(!memcmp(data, data_out, sizeof(data)));
}
}
}
BOOST_AUTO_TEST_CASE( test_MemoryBuffer_Write_ReadString ) {
init_data();
for (int d1 = 0; d1 < 3; d1++) {
for (int d2 = 0; d2 < 3; d2++) {
TMemoryBuffer buffer(16);
string output;
int offset;
int index;
offset = 0;
index = 0;
while (offset < 1<<15) {
buffer.write(&data[offset], dist[d1][index]);
offset += dist[d1][index];
index++;
}
offset = 0;
index = 0;
while (offset < 1<<15) {
unsigned int got = buffer.readAppendToString(output, dist[d2][index]);
BOOST_CHECK_EQUAL(got, dist[d2][index]);
offset += dist[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 (int d1 = 0; d1 < 3; d1++) {
for (int d2 = 0; d2 < 3; d2++) {
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], dist[d1][index]);
offset += dist[d1][index];
index++;
}
offset = 0;
index = 0;
while (offset < (1<<15)-42) {
buffer.read(&data_out[offset], dist[d2][index]);
offset += dist[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 (int d1 = 0; d1 < 3; d1++) {
for (int d2 = 0; d2 < 3; d2++) {
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], dist[d1][index]);
offset += dist[d1][index];
index++;
}
offset = 0;
index = 0;
while (offset < (1<<15)-42) {
buffer.read(&data_out[offset], dist[d2][index]);
offset += dist[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 (int d1 = 0; d1 < 3; d1++) {
for (int d2 = 0; d2 < 3; d2++) {
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)(dist[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)(dist[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)(dist[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 (size_t i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) {
int size = sizes[i];
for (int d1 = 0; d1 < 3; d1++) {
shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(16));
TBufferedTransport trans(buffer, size);
int offset = 0;
int index = 0;
while (offset < 1<<15) {
trans.write(&data[offset], dist[d1][index]);
offset += dist[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 (size_t i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) {
int size = sizes[i];
for (int d1 = 0; d1 < 3; d1++) {
shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(data, sizeof(data)));
TBufferedTransport trans(buffer, size);
uint8_t data_out[1<<15];
int offset = 0;
int index = 0;
while (offset < 1<<15) {
// Note: this doesn't work with "read" because TBufferedTransport
// doesn't try loop over reads, so we get short reads. We don't
// check the return value, so that messes us up.
trans.readAll(&data_out[offset], dist[d1][index]);
offset += dist[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 (size_t i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) {
int size = sizes[i];
for (int d1 = 0; d1 < 3; d1++) {
shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(data, sizeof(data)));
shared_ptr<TShortReadTransport> tshort(new TShortReadTransport(buffer, 0.125));
TBufferedTransport trans(buffer, size);
uint8_t data_out[1<<15];
int offset = 0;
int index = 0;
while (offset < 1<<15) {
// Note: this doesn't work with "read" because TBufferedTransport
// doesn't try loop over reads, so we get short reads. We don't
// check the return value, so that messes us up.
trans.readAll(&data_out[offset], dist[d1][index]);
offset += dist[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 (size_t i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) {
int size = sizes[i];
for (int d1 = 0; d1 < 3; d1++) {
shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(16));
TFramedTransport trans(buffer, size);
int offset = 0;
int index = 0;
while (offset < 1<<15) {
trans.write(&data[offset], dist[d1][index]);
offset += dist[d1][index];
index++;
}
trans.flush();
int32_t frame_size = -1;
buffer->read(reinterpret_cast<uint8_t*>(&frame_size), sizeof(frame_size));
frame_size = (int32_t)ntohl((uint32_t)frame_size);
BOOST_CHECK_EQUAL(frame_size, 1<<15);
BOOST_CHECK_EQUAL(data_str.size(), (unsigned int)frame_size);
string output = buffer->getBufferAsString();
BOOST_CHECK_EQUAL(data_str, output);
}
}
}
BOOST_AUTO_TEST_CASE( test_FramedTransport_Read ) {
init_data();
for (int d1 = 0; d1 < 3; d1++) {
uint8_t data_out[1<<15];
shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
TFramedTransport trans(buffer);
int32_t length = sizeof(data);
length = (int32_t)htonl((uint32_t)length);
buffer->write(reinterpret_cast<uint8_t*>(&length), sizeof(length));
buffer->write(data, sizeof(data));
int offset = 0;
int index = 0;
while (offset < 1<<15) {
// This should work with read because we have one huge frame.
trans.read(&data_out[offset], dist[d1][index]);
offset += dist[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 (size_t i = 0; i < sizeof (sizes) / sizeof (sizes[0]); i++) {
int size = sizes[i];
for (size_t j = 0; j < sizeof (probs) / sizeof (probs[0]); j++) {
int prob = probs[j];
for (int d1 = 0; d1 < 3; d1++) {
shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer(16));
TFramedTransport trans(buffer, size);
uint8_t data_out[1<<15];
std::vector<int> flush_sizes;
int write_offset = 0;
int write_index = 0;
int flush_size = 0;
while (write_offset < 1<<15) {
trans.write(&data[write_offset], dist[d1][write_index]);
write_offset += dist[d1][write_index];
flush_size += dist[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 (unsigned int k = 0; k < flush_sizes.size(); k++) {
int fsize = flush_sizes[k];
// 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, sizeof(data)));
}
}
}
}
BOOST_AUTO_TEST_CASE( test_FramedTransport_Empty_Flush ) {
init_data();
string output1("\x00\x00\x00\x01""a", 5);
string output2("\x00\x00\x00\x01""a\x00\x00\x00\x02""bc", 11);
shared_ptr<TMemoryBuffer> buffer(new TMemoryBuffer());
TFramedTransport trans(buffer);
BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
trans.flush();
BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
trans.flush();
trans.flush();
BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
trans.write((const uint8_t*)"a", 1);
BOOST_CHECK_EQUAL(buffer->getBufferAsString(), "");
trans.flush();
BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output1);
trans.flush();
trans.flush();
BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output1);
trans.write((const uint8_t*)"bc", 2);
BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output1);
trans.flush();
BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output2);
trans.flush();
trans.flush();
BOOST_CHECK_EQUAL(buffer->getBufferAsString(), output2);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <cstdlib>
#include <stdexcept>
#include <thrift/Thrift.h>
#include <thrift/transport/TFDTransport.h>
#define BOOST_TEST_MODULE TFDTransportTest
#include <boost/test/unit_test.hpp>
// Disabled on MSVC because the RTL asserts on an invalid file descriptor
// in both debug and release mode; at least in MSVCR100 (Visual Studio 2010)
#if !defined(WIN32)
using apache::thrift::transport::TTransportException;
using apache::thrift::transport::TFDTransport;
BOOST_AUTO_TEST_CASE(test_tfdtransport_1) {
BOOST_CHECK_NO_THROW(TFDTransport t(256, TFDTransport::CLOSE_ON_DESTROY));
}
BOOST_AUTO_TEST_CASE(test_tfdtransport_2) {
TFDTransport t(256, TFDTransport::CLOSE_ON_DESTROY);
BOOST_CHECK_THROW(t.close(), TTransportException);
}
#else
BOOST_AUTO_TEST_CASE(test_tfdtransport_dummy) {
BOOST_CHECK(true);
}
#endif

View file

@ -0,0 +1,417 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE // needed for getopt_long
#endif
#include <thrift/thrift-config.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <getopt.h>
#include <boost/test/unit_test.hpp>
#include <thrift/transport/TFileTransport.h>
#ifdef __MINGW32__
#include <io.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys\stat.h>
#endif
using namespace apache::thrift::transport;
/**************************************************************************
* Global state
**************************************************************************/
static const char* tmp_dir = "/tmp";
class FsyncLog;
FsyncLog* fsync_log;
/**************************************************************************
* Helper code
**************************************************************************/
// Provide BOOST_WARN_LT() and BOOST_WARN_GT(), in case we're compiled
// with an older version of boost
#ifndef BOOST_WARN_LT
#define BOOST_WARN_CMP(a, b, op, check_fn) \
check_fn((a)op(b), \
"check " BOOST_STRINGIZE(a) " " BOOST_STRINGIZE(op) " " BOOST_STRINGIZE( \
b) " failed: " BOOST_STRINGIZE(a) "=" \
<< (a) << " " BOOST_STRINGIZE(b) "=" << (b))
#define BOOST_WARN_LT(a, b) BOOST_WARN_CMP(a, b, <, BOOST_WARN_MESSAGE)
#define BOOST_WARN_GT(a, b) BOOST_WARN_CMP(a, b, >, BOOST_WARN_MESSAGE)
#define BOOST_WARN_LT(a, b) BOOST_WARN_CMP(a, b, <, BOOST_WARN_MESSAGE)
#endif // BOOST_WARN_LT
/**
* Class to record calls to fsync
*/
class FsyncLog {
public:
struct FsyncCall {
struct timeval time;
int fd;
};
typedef std::list<FsyncCall> CallList;
FsyncLog() {}
void fsync(int fd) {
(void)fd;
FsyncCall call;
gettimeofday(&call.time, NULL);
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__
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_ = NULL;
}
}
void close() {
if (fd_ < 0) {
return;
}
::close(fd_);
fd_ = -1;
}
private:
char* path_;
int fd_;
};
// Use our own version of fsync() for testing.
// This returns immediately, so timing in test_destructor() isn't affected by
// waiting on the actual filesystem.
extern "C" int fsync(int fd) {
if (fsync_log) {
fsync_log->fsync(fd);
}
return 0;
}
int time_diff(const struct timeval* t1, const struct timeval* t2) {
return (t2->tv_usec - t1->tv_usec) + (t2->tv_sec - t1->tv_sec) * 1000000;
}
/**************************************************************************
* Test cases
**************************************************************************/
/**
* Make sure the TFileTransport destructor exits "quickly".
*
* Previous versions had a bug causing the writer thread not to exit
* right away.
*
* It's kind of lame that we just check to see how long the destructor takes in
* wall-clock time. This could result in false failures on slower systems, or
* on heavily loaded machines.
*/
BOOST_AUTO_TEST_CASE(test_destructor) {
TempFile f(tmp_dir, "thrift.TFileTransportTest.");
unsigned int const NUM_ITERATIONS = 1000;
unsigned int num_over = 0;
for (unsigned int n = 0; n < NUM_ITERATIONS; ++n) {
BOOST_CHECK_EQUAL(0, ftruncate(f.getFD(), 0));
TFileTransport* transport = new TFileTransport(f.getPath());
// write something so that the writer thread gets started
transport->write(reinterpret_cast<const uint8_t*>("foo"), 3);
// Every other iteration, also call flush(), just in case that potentially
// has any effect on how the writer thread wakes up.
if (n & 0x1) {
transport->flush();
}
/*
* Time the call to the destructor
*/
struct timeval start;
struct timeval end;
gettimeofday(&start, NULL);
delete transport;
gettimeofday(&end, NULL);
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_LT(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 = NULL;
// Examine the fsync() log
//
// TFileTransport uses pthread_cond_timedwait(), which only has millisecond
// resolution. In my testing, it normally wakes up about 1 millisecond late.
// However, sometimes it takes a bit longer. Allow 5ms leeway.
int max_allowed_delta = flush_us + 5000;
const FsyncLog::CallList* calls = log.getCalls();
// We added 1 fsync call above.
// Make sure TFileTransport called fsync at least once
BOOST_WARN_GE(calls->size(), static_cast<FsyncLog::CallList::size_type>(1));
const struct timeval* prev_time = NULL;
for (FsyncLog::CallList::const_iterator it = calls->begin(); it != calls->end(); ++it) {
if (prev_time) {
int delta = time_diff(prev_time, &it->time);
BOOST_WARN_LT(delta, max_allowed_delta);
}
prev_time = &it->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;
gettimeofday(&start, NULL);
for (unsigned int n = 0; n < 10; ++n) {
transport.flush();
struct timeval now;
gettimeofday(&now, NULL);
// 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_LT(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, NULL, 'h'}, {"tmp-dir", true, NULL, 't'}, {NULL, 0, NULL, 0}};
while (true) {
optopt = 1;
int optchar = getopt_long(argc, argv, "ht:", long_opts, NULL);
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 = NULL;
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

View file

@ -0,0 +1,119 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <boost/test/auto_unit_test.hpp>
#include <iostream>
#include <climits>
#include <vector>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include "gen-cpp/ThriftTest_types.h"
BOOST_AUTO_TEST_SUITE(TMemoryBufferTest)
using apache::thrift::protocol::TBinaryProtocol;
using apache::thrift::transport::TMemoryBuffer;
using apache::thrift::transport::TTransportException;
using boost::shared_ptr;
using std::cout;
using std::endl;
using std::string;
BOOST_AUTO_TEST_CASE(test_read_write_grow) {
// Added to test the fix for THRIFT-1248
TMemoryBuffer uut;
const int maxSize = 65536;
uint8_t verify[maxSize];
std::vector<uint8_t> buf;
buf.resize(maxSize);
for (uint32_t i = 0; i < maxSize; ++i) {
buf[i] = static_cast<uint8_t>(i);
}
for (uint32_t i = 1; i < maxSize; i *= 2) {
uut.write(&buf[0], i);
}
for (uint32_t i = 1; i < maxSize; i *= 2) {
uut.read(verify, i);
BOOST_CHECK_EQUAL(0, ::memcmp(verify, &buf[0], i));
}
}
BOOST_AUTO_TEST_CASE(test_roundtrip) {
shared_ptr<TMemoryBuffer> strBuffer(new TMemoryBuffer());
shared_ptr<TBinaryProtocol> binaryProtcol(new TBinaryProtocol(strBuffer));
thrift::test::Xtruct a;
a.i32_thing = 10;
a.i64_thing = 30;
a.string_thing = "holla back a";
a.write(binaryProtcol.get());
std::string serialized = strBuffer->getBufferAsString();
shared_ptr<TMemoryBuffer> strBuffer2(new TMemoryBuffer());
shared_ptr<TBinaryProtocol> binaryProtcol2(new TBinaryProtocol(strBuffer2));
strBuffer2->resetBuffer((uint8_t*)serialized.data(), static_cast<uint32_t>(serialized.length()));
thrift::test::Xtruct a2;
a2.read(binaryProtcol2.get());
BOOST_CHECK(a == a2);
}
BOOST_AUTO_TEST_CASE(test_copy) {
string* str1 = new string("abcd1234");
const char* data1 = str1->data();
TMemoryBuffer buf((uint8_t*)str1->data(),
static_cast<uint32_t>(str1->length()),
TMemoryBuffer::COPY);
delete str1;
string* str2 = new string("plsreuse");
bool obj_reuse = (str1 == str2);
bool dat_reuse = (data1 == str2->data());
BOOST_TEST_MESSAGE("Object reuse: " << obj_reuse << " Data reuse: " << dat_reuse
<< ((obj_reuse && dat_reuse) ? " YAY!" : ""));
delete str2;
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_SUITE_END()

View file

@ -0,0 +1,178 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#define BOOST_TEST_MODULE TNonblockingServerTest
#include <boost/test/unit_test.hpp>
#include <boost/smart_ptr.hpp>
#include "thrift/concurrency/Thread.h"
#include "thrift/server/TNonblockingServer.h"
#include "gen-cpp/ParentService.h"
#include <event.h>
using namespace apache::thrift;
struct Handler : public test::ParentServiceIf {
void addString(const std::string& s) { strings_.push_back(s); }
void getStrings(std::vector<std::string>& _return) { _return = strings_; }
std::vector<std::string> strings_;
// dummy overrides not used in this test
int32_t incrementGeneration() { return 0; }
int32_t getGeneration() { return 0; }
void getDataWait(std::string&, const int32_t) {}
void onewayWait() {}
void exceptionWait(const std::string&) {}
void unexpectedExceptionWait(const std::string&) {}
};
class Fixture {
private:
struct Runner : public apache::thrift::concurrency::Runnable {
int port;
boost::shared_ptr<event_base> userEventBase;
boost::shared_ptr<TProcessor> processor;
boost::shared_ptr<server::TNonblockingServer> server;
virtual void run() {
// When binding to explicit port, allow retrying to workaround bind failures on ports in use
int retryCount = port ? 10 : 0;
startServer(retryCount);
}
private:
void startServer(int retry_count) {
try {
server.reset(new server::TNonblockingServer(processor, port));
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(boost::make_shared<Handler>())) {}
~Fixture() {
if (server) {
server->stop();
}
if (thread) {
thread->join();
}
}
void setEventBase(event_base* user_event_base) {
userEventBase_.reset(user_event_base, EventDeleter());
}
int startServer(int port) {
boost::shared_ptr<Runner> runner(new Runner);
runner->port = port;
runner->processor = processor;
runner->userEventBase = userEventBase_;
boost::scoped_ptr<apache::thrift::concurrency::ThreadFactory> threadFactory(
new apache::thrift::concurrency::PlatformThreadFactory(
#if !USE_BOOST_THREAD && !USE_STD_THREAD
concurrency::PlatformThreadFactory::OTHER, concurrency::PlatformThreadFactory::NORMAL,
1,
#endif
false));
thread = threadFactory->newThread(runner);
thread->start();
// wait 100 ms for the server to begin listening
THRIFT_SLEEP_USEC(100000);
server = runner->server;
return runner->port;
}
bool canCommunicate(int serverPort) {
boost::shared_ptr<transport::TSocket> socket(new transport::TSocket("localhost", serverPort));
socket->open();
test::ParentServiceClient client(boost::make_shared<protocol::TBinaryProtocol>(
boost::make_shared<transport::TFramedTransport>(socket)));
client.addString("foo");
std::vector<std::string> strings;
client.getStrings(strings);
return strings.size() == 1 && !(strings[0].compare("foo"));
}
private:
boost::shared_ptr<event_base> userEventBase_;
boost::shared_ptr<test::ParentServiceProcessor> processor;
protected:
boost::shared_ptr<server::TNonblockingServer> server;
private:
boost::shared_ptr<apache::thrift::concurrency::Thread> thread;
};
BOOST_AUTO_TEST_SUITE(TNonblockingServerTest)
BOOST_FIXTURE_TEST_CASE(get_specified_port, Fixture) {
int specified_port = startServer(12345);
BOOST_REQUIRE_GE(specified_port, 12345);
BOOST_REQUIRE_EQUAL(server->getListenPort(), specified_port);
BOOST_CHECK(canCommunicate(specified_port));
server->stop();
BOOST_CHECK_EQUAL(server->getListenPort(), specified_port);
}
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_CHECK_EQUAL(server->getListenPort(), 0);
}
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()

View file

@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifdef _WIN32
#include <boost/test/test_tools.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/bind.hpp>
#include <boost/chrono/duration.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>
#include <boost/thread/thread.hpp>
#include <thrift/transport/TPipe.h>
#include <thrift/transport/TPipeServer.h>
using apache::thrift::transport::TPipeServer;
using apache::thrift::transport::TPipe;
using apache::thrift::transport::TTransport;
using apache::thrift::transport::TTransportException;
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 (;;)
{
boost::shared_ptr<TTransport> temp = pipe->accept();
}
}
catch (...) {/*just want to make sure nothing crashes*/ }
}
static void interruptWorker(TPipeServer *pipe) {
boost::this_thread::sleep(boost::posix_time::milliseconds(10));
pipe->interrupt();
}
BOOST_AUTO_TEST_CASE(stress_pipe_accept_interruption) {
int interruptIters = 10;
for (int i = 0; i < interruptIters; ++i)
{
TPipeServer pipeServer("TPipeInterruptTest");
pipeServer.listen();
boost::thread acceptThread(boost::bind(acceptWorker, &pipeServer));
boost::thread interruptThread(boost::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

View file

@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <thrift/Thrift.h>
#include <thrift/transport/TTransportUtils.h>
#include <thrift/transport/TBufferTransports.h>
#define BOOST_TEST_MODULE TPipedTransportTest
#include <boost/test/unit_test.hpp>
using apache::thrift::transport::TTransportException;
using apache::thrift::transport::TPipedTransport;
using apache::thrift::transport::TMemoryBuffer;
BOOST_AUTO_TEST_CASE(test_read_write) {
boost::shared_ptr<TMemoryBuffer> underlying(new TMemoryBuffer);
boost::shared_ptr<TMemoryBuffer> pipe(new TMemoryBuffer);
boost::shared_ptr<TPipedTransport> trans(new TPipedTransport(underlying, pipe));
uint8_t buffer[4];
underlying->write((uint8_t*)"abcd", 4);
trans->readAll(buffer, 2);
BOOST_CHECK(std::string((char*)buffer, 2) == "ab");
trans->readEnd();
BOOST_CHECK(pipe->getBufferAsString() == "ab");
pipe->resetBuffer();
underlying->write((uint8_t*)"ef", 2);
trans->readAll(buffer, 2);
BOOST_CHECK(std::string((char*)buffer, 2) == "cd");
trans->readAll(buffer, 2);
BOOST_CHECK(std::string((char*)buffer, 2) == "ef");
trans->readEnd();
BOOST_CHECK(pipe->getBufferAsString() == "cdef");
}

View file

@ -0,0 +1,280 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/bind.hpp>
#include <boost/chrono/duration.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>
#include <boost/thread/thread.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/shared_ptr.hpp>
#include <thrift/transport/TSSLSocket.h>
#include <thrift/transport/TSSLServerSocket.h>
#ifdef __linux__
#include <signal.h>
#endif
using apache::thrift::transport::TSSLServerSocket;
using apache::thrift::transport::TSSLSocket;
using apache::thrift::transport::TTransport;
using apache::thrift::transport::TTransportException;
using apache::thrift::transport::TSSLSocketFactory;
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(boost::shared_ptr<TTransport> tt, uint32_t expectedResult) {
uint8_t buf[4];
try {
tt->read(buf, 1);
BOOST_CHECK_EQUAL(expectedResult, tt->read(buf, 4));
} catch (const TTransportException& tx) {
BOOST_CHECK_EQUAL(TTransportException::TIMED_OUT, tx.getType());
}
}
void readerWorkerMustThrow(boost::shared_ptr<TTransport> tt) {
try {
uint8_t buf[400];
tt->read(buf, 1);
tt->read(buf, 400);
BOOST_ERROR("should not have gotten here");
} catch (const TTransportException& tx) {
BOOST_CHECK_EQUAL(TTransportException::INTERRUPTED, tx.getType());
}
}
boost::shared_ptr<TSSLSocketFactory> createServerSocketFactory() {
boost::shared_ptr<TSSLSocketFactory> pServerSocketFactory;
pServerSocketFactory.reset(new TSSLSocketFactory());
pServerSocketFactory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
pServerSocketFactory->loadCertificate(certFile("server.crt").string().c_str());
pServerSocketFactory->loadPrivateKey(certFile("server.key").string().c_str());
pServerSocketFactory->server(true);
return pServerSocketFactory;
}
boost::shared_ptr<TSSLSocketFactory> createClientSocketFactory() {
boost::shared_ptr<TSSLSocketFactory> pClientSocketFactory;
pClientSocketFactory.reset(new TSSLSocketFactory());
pClientSocketFactory->authenticate(true);
pClientSocketFactory->loadCertificate(certFile("client.crt").string().c_str());
pClientSocketFactory->loadPrivateKey(certFile("client.key").string().c_str());
pClientSocketFactory->loadTrustedCertificates(certFile("CA.pem").string().c_str());
return pClientSocketFactory;
}
BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_read_while_handshaking) {
boost::shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
sock1.listen();
int port = sock1.getPort();
boost::shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
boost::shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
clientSock->open();
boost::shared_ptr<TTransport> accepted = sock1.accept();
boost::thread readThread(boost::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) {
boost::shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
sock1.listen();
int port = sock1.getPort();
boost::shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
boost::shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
clientSock->open();
boost::shared_ptr<TTransport> accepted = sock1.accept();
boost::thread readThread(boost::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) {
boost::shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
sock1.listen();
int port = sock1.getPort();
boost::shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
boost::shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
clientSock->open();
boost::shared_ptr<TTransport> accepted = sock1.accept();
boost::static_pointer_cast<TSSLSocket>(accepted)->setRecvTimeout(1000);
boost::thread readThread(boost::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) {
boost::shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
sock1.listen();
BOOST_CHECK_THROW(sock1.setInterruptableChildren(false), std::logic_error);
sock1.close();
}
void peekerWorker(boost::shared_ptr<TTransport> tt, bool expectedResult) {
uint8_t buf[400];
try {
tt->read(buf, 1);
tt->peek();
} catch (const TTransportException& tx) {
BOOST_CHECK_EQUAL(TTransportException::TIMED_OUT, tx.getType());
}
}
void peekerWorkerInterrupt(boost::shared_ptr<TTransport> tt) {
uint8_t buf[400];
try {
tt->read(buf, 1);
tt->peek();
} catch (const TTransportException& tx) {
BOOST_CHECK_EQUAL(TTransportException::INTERRUPTED, tx.getType());
}
}
BOOST_AUTO_TEST_CASE(test_ssl_interruptable_child_peek) {
boost::shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
sock1.listen();
int port = sock1.getPort();
boost::shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
boost::shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
clientSock->open();
boost::shared_ptr<TTransport> accepted = sock1.accept();
boost::thread peekThread(boost::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) {
boost::shared_ptr<TSSLSocketFactory> pServerSocketFactory = createServerSocketFactory();
TSSLServerSocket sock1("localhost", 0, pServerSocketFactory);
sock1.setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
sock1.listen();
int port = sock1.getPort();
boost::shared_ptr<TSSLSocketFactory> pClientSocketFactory = createClientSocketFactory();
boost::shared_ptr<TSSLSocket> clientSock = pClientSocketFactory->createSocket("localhost", port);
clientSock->open();
boost::shared_ptr<TTransport> accepted = sock1.accept();
boost::static_pointer_cast<TSSLSocket>(accepted)->setRecvTimeout(1000);
boost::thread peekThread(boost::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()

View file

@ -0,0 +1,534 @@
/*
* 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 <boost/test/auto_unit_test.hpp>
#include <boost/atomic.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <thrift/server/TSimpleServer.h>
#include <thrift/server/TThreadPoolServer.h>
#include <thrift/server/TThreadedServer.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransport.h>
#include "gen-cpp/ParentService.h"
#include <string>
#include <vector>
using apache::thrift::concurrency::Guard;
using apache::thrift::concurrency::Monitor;
using apache::thrift::concurrency::Mutex;
using apache::thrift::concurrency::Synchronized;
using apache::thrift::protocol::TBinaryProtocol;
using apache::thrift::protocol::TBinaryProtocolFactory;
using apache::thrift::protocol::TProtocol;
using apache::thrift::protocol::TProtocolFactory;
using apache::thrift::transport::TServerSocket;
using apache::thrift::transport::TServerTransport;
using apache::thrift::transport::TSocket;
using apache::thrift::transport::TTransport;
using apache::thrift::transport::TTransportException;
using apache::thrift::transport::TTransportFactory;
using apache::thrift::server::TServer;
using apache::thrift::server::TServerEventHandler;
using apache::thrift::server::TSimpleServer;
using apache::thrift::server::TThreadPoolServer;
using apache::thrift::server::TThreadedServer;
using 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) {}
virtual ~TServerReadyEventHandler() {}
virtual void preServe() {
Synchronized sync(*this);
isListening_ = true;
notify();
}
virtual void* createContext(boost::shared_ptr<TProtocol> input,
boost::shared_ptr<TProtocol> output) {
Synchronized sync(*this);
++accepted_;
notify();
(void)input;
(void)output;
return NULL;
}
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() {
Guard g(mutex_);
return ++generation_;
}
int32_t getGeneration() {
Guard g(mutex_);
return generation_;
}
void addString(const std::string& s) {
Guard g(mutex_);
strings_.push_back(s);
}
void getStrings(std::vector<std::string>& _return) {
Guard g(mutex_);
_return = strings_;
}
void getDataWait(std::string& _return, const int32_t length) {
THRIFT_UNUSED_VARIABLE(_return);
THRIFT_UNUSED_VARIABLE(length);
}
void onewayWait() {}
void exceptionWait(const std::string& message) { THRIFT_UNUSED_VARIABLE(message); }
void unexpectedExceptionWait(const std::string& message) { THRIFT_UNUSED_VARIABLE(message); }
protected:
Mutex mutex_;
int32_t generation_;
std::vector<std::string> strings_;
};
void autoSocketCloser(TSocket* pSock) {
pSock->close();
delete pSock;
}
template <class TServerType>
class TServerIntegrationTestFixture {
public:
TServerIntegrationTestFixture(const boost::shared_ptr<TProcessorFactory>& _processorFactory)
: pServer(new TServerType(_processorFactory,
boost::shared_ptr<TServerTransport>(
new TServerSocket("localhost", 0)),
boost::shared_ptr<TTransportFactory>(new TTransportFactory),
boost::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory))),
pEventHandler(boost::shared_ptr<TServerReadyEventHandler>(new TServerReadyEventHandler)),
bStressDone(false),
bStressConnectionCount(0),
bStressRequestCount(0) {
pServer->setServerEventHandler(pEventHandler);
}
TServerIntegrationTestFixture(const boost::shared_ptr<TProcessor>& _processor)
: pServer(
new TServerType(_processor,
boost::shared_ptr<TServerTransport>(new TServerSocket("localhost", 0)),
boost::shared_ptr<TTransportFactory>(new TTransportFactory),
boost::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory))),
pEventHandler(boost::shared_ptr<TServerReadyEventHandler>(new TServerReadyEventHandler)),
bStressDone(false),
bStressConnectionCount(0),
bStressRequestCount(0) {
pServer->setServerEventHandler(pEventHandler);
}
void startServer() {
pServerThread.reset(new boost::thread(boost::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<boost::shared_ptr<TSocket> > holdSockets;
std::vector<boost::shared_ptr<boost::thread> > holdThreads;
for (int64_t i = 0; i < numToMake; ++i) {
boost::shared_ptr<TSocket> pClientSock(new TSocket("localhost", getServerPort()),
autoSocketCloser);
holdSockets.push_back(pClientSock);
boost::shared_ptr<TProtocol> pClientProtocol(new TBinaryProtocol(pClientSock));
ParentServiceClient client(pClientProtocol);
pClientSock->open();
client.incrementGeneration();
holdThreads.push_back(boost::shared_ptr<boost::thread>(
new boost::thread(boost::bind(&TServerIntegrationTestFixture::delayClose,
this,
pClientSock,
milliseconds(10 * numToMake)))));
}
BOOST_CHECK_EQUAL(expectedHWM, pServer->getConcurrentClientCountHWM());
BOOST_FOREACH (boost::shared_ptr<boost::thread> pThread, holdThreads) { pThread->join(); }
holdThreads.clear();
holdSockets.clear();
stopServer();
}
/**
* Helper method used to close a connection after a delay.
* \param[in] toClose the connection to close
* \param[in] after the delay to impose
*/
void delayClose(boost::shared_ptr<TTransport> toClose, boost::posix_time::time_duration after) {
boost::this_thread::sleep(after);
toClose->close();
}
/**
* \returns the server port number
*/
int getServerPort() {
TServerSocket* pSock = dynamic_cast<TServerSocket*>(pServer->getServerTransport().get());
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<boost::shared_ptr<boost::thread> > holdThreads;
for (int64_t i = 0; i < numToMake; ++i) {
holdThreads.push_back(boost::shared_ptr<boost::thread>(
new boost::thread(boost::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 (boost::shared_ptr<boost::thread> pThread, holdThreads) { pThread->join(); }
holdThreads.clear();
BOOST_CHECK(bStressRequestCount > 0);
stopServer();
}
/**
* Helper method to stress the system
*/
void stressor() {
while (!bStressDone) {
boost::shared_ptr<TSocket> pSocket(new TSocket("localhost", getServerPort()), autoSocketCloser);
boost::shared_ptr<TProtocol> pProtocol(new TBinaryProtocol(pSocket));
ParentServiceClient client(pProtocol);
pSocket->open();
bStressConnectionCount.fetch_add(1, boost::memory_order_relaxed);
for (int i = 0; i < rand() % 1000; ++i) {
client.incrementGeneration();
bStressRequestCount.fetch_add(1, boost::memory_order_relaxed);
}
}
}
boost::shared_ptr<TServerType> pServer;
boost::shared_ptr<TServerReadyEventHandler> pEventHandler;
boost::shared_ptr<boost::thread> pServerThread;
bool bStressDone;
boost::atomic_int64_t bStressConnectionCount;
boost::atomic_int64_t bStressRequestCount;
};
template <class TServerType>
class TServerIntegrationProcessorFactoryTestFixture
: public TServerIntegrationTestFixture<TServerType> {
public:
TServerIntegrationProcessorFactoryTestFixture()
: TServerIntegrationTestFixture<TServerType>(boost::make_shared<ParentServiceProcessorFactory>(
boost::make_shared<ParentServiceIfSingletonFactory>(
boost::make_shared<ParentHandler>()))) {}
};
template <class TServerType>
class TServerIntegrationProcessorTestFixture : public TServerIntegrationTestFixture<TServerType> {
public:
TServerIntegrationProcessorTestFixture()
: TServerIntegrationTestFixture<TServerType>(
boost::make_shared<ParentServiceProcessor>(boost::make_shared<ParentHandler>())) {}
};
BOOST_AUTO_TEST_SUITE(constructors)
BOOST_FIXTURE_TEST_CASE(test_simple_factory,
TServerIntegrationProcessorFactoryTestFixture<TSimpleServer>) {
baseline(3, 1, "factory");
}
BOOST_FIXTURE_TEST_CASE(test_simple, TServerIntegrationProcessorTestFixture<TSimpleServer>) {
baseline(3, 1, "processor");
}
BOOST_FIXTURE_TEST_CASE(test_threaded_factory,
TServerIntegrationProcessorFactoryTestFixture<TThreadedServer>) {
baseline(10, 10, "factory");
}
BOOST_FIXTURE_TEST_CASE(test_threaded, TServerIntegrationProcessorTestFixture<TThreadedServer>) {
baseline(10, 10, "processor");
}
BOOST_FIXTURE_TEST_CASE(test_threaded_bound,
TServerIntegrationProcessorTestFixture<TThreadedServer>) {
pServer->setConcurrentClientLimit(4);
baseline(10, 4, "limit by server framework");
}
BOOST_FIXTURE_TEST_CASE(test_threaded_stress,
TServerIntegrationProcessorFactoryTestFixture<TThreadedServer>) {
stress(10, boost::posix_time::seconds(3));
}
BOOST_FIXTURE_TEST_CASE(test_threadpool_factory,
TServerIntegrationProcessorFactoryTestFixture<TThreadPoolServer>) {
pServer->getThreadManager()->threadFactory(
boost::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
new apache::thrift::concurrency::PlatformThreadFactory));
pServer->getThreadManager()->start();
// thread factory has 4 threads as a default
// thread factory however is a bad way to limit concurrent clients
// as accept() will be called to grab a 5th client socket, in this case
// and then the thread factory will block adding the thread to manage
// that client.
baseline(10, 5, "limit by thread manager");
}
BOOST_FIXTURE_TEST_CASE(test_threadpool,
TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
pServer->getThreadManager()->threadFactory(
boost::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
new apache::thrift::concurrency::PlatformThreadFactory));
pServer->getThreadManager()->start();
// thread factory has 4 threads as a default
// thread factory however is a bad way to limit concurrent clients
// as accept() will be called to grab a 5th client socket, in this case
// and then the thread factory will block adding the thread to manage
// that client.
baseline(10, 5, "limit by thread manager");
}
BOOST_FIXTURE_TEST_CASE(test_threadpool_bound,
TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
pServer->getThreadManager()->threadFactory(
boost::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
new apache::thrift::concurrency::PlatformThreadFactory));
pServer->getThreadManager()->start();
pServer->setConcurrentClientLimit(4);
baseline(10, 4, "server framework connection limit");
}
BOOST_FIXTURE_TEST_CASE(test_threadpool_stress,
TServerIntegrationProcessorTestFixture<TThreadPoolServer>) {
pServer->getThreadManager()->threadFactory(
boost::shared_ptr<apache::thrift::concurrency::ThreadFactory>(
new apache::thrift::concurrency::PlatformThreadFactory));
pServer->getThreadManager()->start();
stress(10, boost::posix_time::seconds(3));
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_FIXTURE_TEST_SUITE(TServerIntegrationTest,
TServerIntegrationProcessorTestFixture<TThreadedServer>)
BOOST_AUTO_TEST_CASE(test_stop_with_interruptable_clients_connected) {
// This tests THRIFT-2441 new behavior: stopping the server disconnects clients
BOOST_TEST_MESSAGE("Testing stop with interruptable clients");
startServer();
boost::shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
autoSocketCloser);
pClientSock1->open();
boost::shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
autoSocketCloser);
pClientSock2->open();
// Ensure they have been accepted
blockUntilAccepted(2);
// The test fixture destructor will force the sockets to disconnect
// Prior to THRIFT-2441, pServer->stop() would hang until clients disconnected
stopServer();
// extra proof the server end disconnected the clients
uint8_t buf[1];
BOOST_CHECK_EQUAL(0, pClientSock1->read(&buf[0], 1)); // 0 = disconnected
BOOST_CHECK_EQUAL(0, pClientSock2->read(&buf[0], 1)); // 0 = disconnected
}
BOOST_AUTO_TEST_CASE(test_stop_with_uninterruptable_clients_connected) {
// This tests pre-THRIFT-2441 behavior: stopping the server blocks until clients
// disconnect.
BOOST_TEST_MESSAGE("Testing stop with uninterruptable clients");
boost::dynamic_pointer_cast<TServerSocket>(pServer->getServerTransport())
->setInterruptableChildren(false); // returns to pre-THRIFT-2441 behavior
startServer();
boost::shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
autoSocketCloser);
pClientSock1->open();
boost::shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
autoSocketCloser);
pClientSock2->open();
// Ensure they have been accepted
blockUntilAccepted(2);
boost::thread t1(boost::bind(&TServerIntegrationTestFixture::delayClose,
this,
pClientSock1,
milliseconds(250)));
boost::thread t2(boost::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());
boost::shared_ptr<TSocket> pClientSock1(new TSocket("localhost", getServerPort()),
autoSocketCloser);
pClientSock1->open();
blockUntilAccepted(1);
BOOST_CHECK_EQUAL(1, pServer->getConcurrentClientCount());
boost::shared_ptr<TSocket> pClientSock2(new TSocket("localhost", getServerPort()),
autoSocketCloser);
pClientSock2->open();
blockUntilAccepted(2);
BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount());
// a third client cannot connect until one of the other two closes
boost::thread t2(boost::bind(&TServerIntegrationTestFixture::delayClose,
this,
pClientSock2,
milliseconds(250)));
boost::shared_ptr<TSocket> pClientSock3(new TSocket("localhost", getServerPort()),
autoSocketCloser);
pClientSock2->open();
blockUntilAccepted(2);
BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCount());
BOOST_CHECK_EQUAL(2, pServer->getConcurrentClientCountHWM());
stopServer();
BOOST_CHECK(pServer->getConcurrentClientCountHWM() > 0);
t2.join();
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <boost/test/auto_unit_test.hpp>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TServerSocket.h>
#include "TTransportCheckThrow.h"
#include <iostream>
using apache::thrift::transport::TServerSocket;
using apache::thrift::transport::TSocket;
using apache::thrift::transport::TTransport;
using apache::thrift::transport::TTransportException;
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();
boost::shared_ptr<TTransport> accepted = sock1.accept();
accepted->close();
sock1.close();
std::cout << "An error message from getaddrinfo on the console is expected:" << std::endl;
TServerSocket sock2("257.258.259.260", 0);
BOOST_CHECK_THROW(sock2.listen(), TTransportException);
sock2.close();
}
BOOST_AUTO_TEST_CASE(test_listen_valid_port) {
TServerSocket sock1(-1);
TTRANSPORT_CHECK_THROW(sock1.listen(), TTransportException::BAD_ARGS);
TServerSocket sock2(65536);
TTRANSPORT_CHECK_THROW(sock2.listen(), TTransportException::BAD_ARGS);
}
BOOST_AUTO_TEST_CASE(test_close_before_listen) {
TServerSocket sock1("localhost", 0);
sock1.close();
}
BOOST_AUTO_TEST_CASE(test_get_port) {
TServerSocket sock1("localHost", 888);
BOOST_CHECK_EQUAL(888, sock1.getPort());
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <boost/test/auto_unit_test.hpp>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TServerTransport.h>
using apache::thrift::transport::TServerTransport;
using apache::thrift::transport::TTransport;
using apache::thrift::transport::TTransportException;
BOOST_AUTO_TEST_SUITE(TServerTransportTest)
class TestTTransport : public TTransport {};
class TestTServerTransport : public TServerTransport {
public:
TestTServerTransport() : valid_(true) {}
void close() {}
bool valid_;
protected:
boost::shared_ptr<TTransport> acceptImpl() {
return valid_ ? boost::shared_ptr<TestTTransport>(new TestTTransport)
: boost::shared_ptr<TestTTransport>();
}
};
BOOST_AUTO_TEST_CASE(test_positive_accept) {
TestTServerTransport uut;
BOOST_CHECK(uut.accept());
}
BOOST_AUTO_TEST_CASE(test_negative_accept) {
TestTServerTransport uut;
uut.valid_ = false;
BOOST_CHECK_THROW(uut.accept(), TTransportException);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -0,0 +1,145 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#define BOOST_TEST_MODULE TSocketInterruptTest
#include <boost/test/auto_unit_test.hpp>
#include <boost/bind.hpp>
#include <boost/chrono/duration.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>
#include <boost/thread/thread.hpp>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TServerSocket.h>
using apache::thrift::transport::TServerSocket;
using apache::thrift::transport::TSocket;
using apache::thrift::transport::TTransport;
using apache::thrift::transport::TTransportException;
BOOST_AUTO_TEST_SUITE(TSocketInterruptTest)
void readerWorker(boost::shared_ptr<TTransport> tt, uint32_t expectedResult) {
uint8_t buf[4];
BOOST_CHECK_EQUAL(expectedResult, tt->read(buf, 4));
}
void readerWorkerMustThrow(boost::shared_ptr<TTransport> tt) {
try {
uint8_t buf[4];
tt->read(buf, 4);
BOOST_ERROR("should not have gotten here");
} catch (const TTransportException& tx) {
BOOST_CHECK_EQUAL(TTransportException::INTERRUPTED, tx.getType());
}
}
BOOST_AUTO_TEST_CASE(test_interruptable_child_read) {
TServerSocket sock1("localhost", 0);
sock1.listen();
int port = sock1.getPort();
TSocket clientSock("localhost", port);
clientSock.open();
boost::shared_ptr<TTransport> accepted = sock1.accept();
boost::thread readThread(boost::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();
boost::shared_ptr<TTransport> accepted = sock1.accept();
boost::thread readThread(boost::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(boost::shared_ptr<TTransport> tt, bool expectedResult) {
BOOST_CHECK_EQUAL(expectedResult, tt->peek());
}
BOOST_AUTO_TEST_CASE(test_interruptable_child_peek) {
TServerSocket sock1("localhost", 0);
sock1.listen();
int port = sock1.getPort();
TSocket clientSock("localhost", port);
clientSock.open();
boost::shared_ptr<TTransport> accepted = sock1.accept();
// peek() will return false if child is interrupted
boost::thread peekThread(boost::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();
boost::shared_ptr<TTransport> accepted = sock1.accept();
// peek() will return false when remote side is closed
boost::thread peekThread(boost::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()

View file

@ -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"); \
}

View file

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// Extra functions required for ThriftTest_types to work
#include <thrift/protocol/TDebugProtocol.h>
#include "gen-cpp/ThriftTest_types.h"
namespace thrift {
namespace test {
bool Insanity::operator<(thrift::test::Insanity const& other) const {
using apache::thrift::ThriftDebugString;
return ThriftDebugString(*this) < ThriftDebugString(other);
}
}
}

View file

@ -0,0 +1,137 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <vector>
#include <map>
#include <boost/test/auto_unit_test.hpp>
#include <thrift/TToString.h>
#include "gen-cpp/ThriftTest_types.h"
#include "gen-cpp/OptionalRequiredTest_types.h"
#include "gen-cpp/DebugProtoTest_types.h"
using apache::thrift::to_string;
BOOST_AUTO_TEST_SUITE(ToStringTest)
BOOST_AUTO_TEST_CASE(base_types_to_string) {
BOOST_CHECK_EQUAL(to_string(10), "10");
BOOST_CHECK_EQUAL(to_string(true), "1");
BOOST_CHECK_EQUAL(to_string('a'), "a");
BOOST_CHECK_EQUAL(to_string(1.2), "1.2");
BOOST_CHECK_EQUAL(to_string("abc"), "abc");
}
BOOST_AUTO_TEST_CASE(empty_vector_to_string) {
std::vector<int> l;
BOOST_CHECK_EQUAL(to_string(l), "[]");
}
BOOST_AUTO_TEST_CASE(single_item_vector_to_string) {
std::vector<int> l;
l.push_back(100);
BOOST_CHECK_EQUAL(to_string(l), "[100]");
}
BOOST_AUTO_TEST_CASE(multiple_item_vector_to_string) {
std::vector<int> l;
l.push_back(100);
l.push_back(150);
BOOST_CHECK_EQUAL(to_string(l), "[100, 150]");
}
BOOST_AUTO_TEST_CASE(empty_map_to_string) {
std::map<int, std::string> m;
BOOST_CHECK_EQUAL(to_string(m), "{}");
}
BOOST_AUTO_TEST_CASE(single_item_map_to_string) {
std::map<int, std::string> m;
m[12] = "abc";
BOOST_CHECK_EQUAL(to_string(m), "{12: abc}");
}
BOOST_AUTO_TEST_CASE(multi_item_map_to_string) {
std::map<int, std::string> m;
m[12] = "abc";
m[31] = "xyz";
BOOST_CHECK_EQUAL(to_string(m), "{12: abc, 31: xyz}");
}
BOOST_AUTO_TEST_CASE(empty_set_to_string) {
std::set<char> s;
BOOST_CHECK_EQUAL(to_string(s), "{}");
}
BOOST_AUTO_TEST_CASE(single_item_set_to_string) {
std::set<char> s;
s.insert('c');
BOOST_CHECK_EQUAL(to_string(s), "{c}");
}
BOOST_AUTO_TEST_CASE(multi_item_set_to_string) {
std::set<char> s;
s.insert('a');
s.insert('z');
BOOST_CHECK_EQUAL(to_string(s), "{a, z}");
}
BOOST_AUTO_TEST_CASE(generated_empty_object_to_string) {
thrift::test::EmptyStruct e;
BOOST_CHECK_EQUAL(to_string(e), "EmptyStruct()");
}
BOOST_AUTO_TEST_CASE(generated_single_basic_field_object_to_string) {
thrift::test::StructA a;
a.__set_s("abcd");
BOOST_CHECK_EQUAL(to_string(a), "StructA(s=abcd)");
}
BOOST_AUTO_TEST_CASE(generated_two_basic_fields_object_to_string) {
thrift::test::Bonk a;
a.__set_message("abcd");
a.__set_type(1234);
BOOST_CHECK_EQUAL(to_string(a), "Bonk(message=abcd, type=1234)");
}
BOOST_AUTO_TEST_CASE(generated_optional_fields_object_to_string) {
thrift::test::Tricky2 a;
BOOST_CHECK_EQUAL(to_string(a), "Tricky2(im_optional=<null>)");
a.__set_im_optional(123);
BOOST_CHECK_EQUAL(to_string(a), "Tricky2(im_optional=123)");
}
BOOST_AUTO_TEST_CASE(generated_nested_object_to_string) {
thrift::test::OneField a;
BOOST_CHECK_EQUAL(to_string(a), "OneField(field=EmptyStruct())");
}
BOOST_AUTO_TEST_CASE(generated_nested_list_object_to_string) {
thrift::test::ListBonks l;
l.bonk.assign(2, thrift::test::Bonk());
l.bonk[0].__set_message("a");
l.bonk[1].__set_message("b");
BOOST_CHECK_EQUAL(to_string(l),
"ListBonks(bonk=[Bonk(message=a, type=0), Bonk(message=b, type=0)])");
}
BOOST_AUTO_TEST_SUITE_END()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <boost/type_traits/is_same.hpp>
#include <boost/static_assert.hpp>
#include "gen-cpp/TypedefTest_types.h"
BOOST_STATIC_ASSERT((boost::is_same<int32_t, thrift::test::MyInt32>::value));
BOOST_STATIC_ASSERT((boost::is_same<std::string, thrift::test::MyString>::value));
BOOST_STATIC_ASSERT(
(boost::is_same<thrift::test::TypedefTestStruct, thrift::test::MyStruct>::value));

View file

@ -0,0 +1,21 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#define BOOST_TEST_MODULE thrift
#include <boost/test/auto_unit_test.hpp>

View file

@ -0,0 +1,461 @@
/*
* 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 __STDC_FORMAT_MACROS
#ifndef _GNU_SOURCE
#define _GNU_SOURCE // needed for getopt_long
#endif
#include <stdint.h>
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#include <cstddef>
#include <fstream>
#include <iostream>
#include <thrift/cxxfunctional.h>
#include <boost/function.hpp>
#include <boost/random.hpp>
#include <boost/shared_array.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/version.hpp>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/transport/TZlibTransport.h>
using namespace std;
using namespace apache::thrift::transport;
boost::mt19937 rng;
/*
* Utility code
*/
class SizeGenerator {
public:
virtual ~SizeGenerator() {}
virtual unsigned int getSize() = 0;
};
class ConstantSizeGenerator : public SizeGenerator {
public:
ConstantSizeGenerator(unsigned int value) : value_(value) {}
virtual unsigned int getSize() { return value_; }
private:
unsigned int value_;
};
class LogNormalSizeGenerator : public SizeGenerator {
public:
LogNormalSizeGenerator(double mean, double std_dev)
: gen_(rng, boost::lognormal_distribution<double>(mean, std_dev)) {}
virtual unsigned int getSize() {
// Loop until we get a size of 1 or more
while (true) {
unsigned int value = static_cast<unsigned int>(gen_());
if (value >= 1) {
return value;
}
}
}
private:
boost::variate_generator<boost::mt19937, boost::lognormal_distribution<double> > gen_;
};
boost::shared_array<uint8_t> gen_uniform_buffer(uint32_t buf_len, uint8_t c) {
uint8_t* buf = new uint8_t[buf_len];
memset(buf, c, buf_len);
return boost::shared_array<uint8_t>(buf);
}
boost::shared_array<uint8_t> gen_compressible_buffer(uint32_t buf_len) {
uint8_t* buf = new uint8_t[buf_len];
// Generate small runs of alternately increasing and decreasing bytes
boost::uniform_smallint<uint32_t> run_length_distribution(1, 64);
boost::uniform_smallint<uint8_t> byte_distribution(0, UINT8_MAX);
boost::variate_generator<boost::mt19937, boost::uniform_smallint<uint8_t> >
byte_generator(rng, byte_distribution);
boost::variate_generator<boost::mt19937, boost::uniform_smallint<uint32_t> >
run_len_generator(rng, run_length_distribution);
uint32_t idx = 0;
int8_t step = 1;
while (idx < buf_len) {
uint32_t run_length = run_len_generator();
if (idx + run_length > buf_len) {
run_length = buf_len - idx;
}
uint8_t byte = byte_generator();
for (uint32_t n = 0; n < run_length; ++n) {
buf[idx] = byte;
++idx;
byte += step;
}
step *= -1;
}
return boost::shared_array<uint8_t>(buf);
}
boost::shared_array<uint8_t> gen_random_buffer(uint32_t buf_len) {
uint8_t* buf = new uint8_t[buf_len];
boost::uniform_smallint<uint8_t> distribution(0, UINT8_MAX);
boost::variate_generator<boost::mt19937, boost::uniform_smallint<uint8_t> >
generator(rng, distribution);
for (uint32_t n = 0; n < buf_len; ++n) {
buf[n] = generator();
}
return boost::shared_array<uint8_t>(buf);
}
/*
* Test functions
*/
void test_write_then_read(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
boost::shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
boost::shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
zlib_trans->write(buf.get(), buf_len);
zlib_trans->finish();
boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
BOOST_REQUIRE_EQUAL(got, buf_len);
BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
zlib_trans->verifyChecksum();
}
void test_separate_checksum(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
// This one is tricky. I separate the last byte of the stream out
// into a separate crbuf_. The last byte is part of the checksum,
// so the entire read goes fine, but when I go to verify the checksum
// it isn't there. The original implementation complained that
// the stream was not complete. I'm about to go fix that.
// It worked. Awesome.
boost::shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
boost::shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
zlib_trans->write(buf.get(), buf_len);
zlib_trans->finish();
string tmp_buf;
membuf->appendBufferToString(tmp_buf);
zlib_trans.reset(new TZlibTransport(membuf,
TZlibTransport::DEFAULT_URBUF_SIZE,
static_cast<uint32_t>(tmp_buf.length() - 1)));
boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
BOOST_REQUIRE_EQUAL(got, buf_len);
BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
zlib_trans->verifyChecksum();
}
void test_incomplete_checksum(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
// Make sure we still get that "not complete" error if
// it really isn't complete.
boost::shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
boost::shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
zlib_trans->write(buf.get(), buf_len);
zlib_trans->finish();
string tmp_buf;
membuf->appendBufferToString(tmp_buf);
tmp_buf.erase(tmp_buf.length() - 1);
membuf->resetBuffer(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(tmp_buf.data())),
static_cast<uint32_t>(tmp_buf.length()));
boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
uint32_t got = zlib_trans->readAll(mirror.get(), buf_len);
BOOST_REQUIRE_EQUAL(got, buf_len);
BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
try {
zlib_trans->verifyChecksum();
BOOST_ERROR("verifyChecksum() did not report an error");
} catch (TTransportException& ex) {
BOOST_CHECK_EQUAL(ex.getType(), TTransportException::CORRUPTED_DATA);
}
}
void test_read_write_mix(const boost::shared_array<uint8_t> buf,
uint32_t buf_len,
const boost::shared_ptr<SizeGenerator>& write_gen,
const boost::shared_ptr<SizeGenerator>& read_gen) {
// Try it with a mix of read/write sizes.
boost::shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
boost::shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
unsigned int tot;
tot = 0;
while (tot < buf_len) {
uint32_t write_len = write_gen->getSize();
if (tot + write_len > buf_len) {
write_len = buf_len - tot;
}
zlib_trans->write(buf.get() + tot, write_len);
tot += write_len;
}
zlib_trans->finish();
tot = 0;
boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
while (tot < buf_len) {
uint32_t read_len = read_gen->getSize();
uint32_t expected_read_len = read_len;
if (tot + read_len > buf_len) {
expected_read_len = buf_len - tot;
}
uint32_t got = zlib_trans->read(mirror.get() + tot, read_len);
BOOST_REQUIRE_LE(got, expected_read_len);
BOOST_REQUIRE_NE(got, (uint32_t)0);
tot += got;
}
BOOST_CHECK_EQUAL(memcmp(mirror.get(), buf.get(), buf_len), 0);
zlib_trans->verifyChecksum();
}
void test_invalid_checksum(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
// Verify checksum checking.
boost::shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
boost::shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
zlib_trans->write(buf.get(), buf_len);
zlib_trans->finish();
string tmp_buf;
membuf->appendBufferToString(tmp_buf);
// Modify a byte at the end of the buffer (part of the checksum).
// On rare occasions, modifying a byte in the middle of the buffer
// isn't caught by the checksum.
//
// (This happens especially often for the uniform buffer. The
// re-inflated data is correct, however. I suspect in this case that
// we're more likely to modify bytes that are part of zlib metadata
// instead of the actual compressed data.)
//
// I've also seen some failure scenarios where a checksum failure isn't
// reported, but zlib keeps trying to decode past the end of the data.
// (When this occurs, verifyChecksum() throws an exception indicating
// that the end of the data hasn't been reached.) I haven't seen this
// error when only modifying checksum bytes.
int index = static_cast<int>(tmp_buf.size() - 1);
tmp_buf[index]++;
membuf->resetBuffer(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(tmp_buf.data())),
static_cast<uint32_t>(tmp_buf.length()));
boost::shared_array<uint8_t> mirror(new uint8_t[buf_len]);
try {
zlib_trans->readAll(mirror.get(), buf_len);
zlib_trans->verifyChecksum();
BOOST_ERROR("verifyChecksum() did not report an error");
} catch (TZlibTransportException& ex) {
BOOST_CHECK_EQUAL(ex.getType(), TTransportException::INTERNAL_ERROR);
}
}
void test_write_after_flush(const boost::shared_array<uint8_t> buf, uint32_t buf_len) {
// write some data
boost::shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
boost::shared_ptr<TZlibTransport> zlib_trans(new TZlibTransport(membuf));
zlib_trans->write(buf.get(), buf_len);
// call finish()
zlib_trans->finish();
// make sure write() throws an error
try {
uint8_t write_buf[] = "a";
zlib_trans->write(write_buf, 1);
BOOST_ERROR("write() after finish() did not raise an exception");
} catch (TTransportException& ex) {
BOOST_CHECK_EQUAL(ex.getType(), TTransportException::BAD_ARGS);
}
// make sure flush() throws an error
try {
zlib_trans->flush();
BOOST_ERROR("flush() after finish() did not raise an exception");
} catch (TTransportException& ex) {
BOOST_CHECK_EQUAL(ex.getType(), TTransportException::BAD_ARGS);
}
// make sure finish() throws an error
try {
zlib_trans->finish();
BOOST_ERROR("finish() after finish() did not raise an exception");
} catch (TTransportException& ex) {
BOOST_CHECK_EQUAL(ex.getType(), TTransportException::BAD_ARGS);
}
}
void test_no_write() {
// Verify that no data is written to the underlying transport if we
// never write data to the TZlibTransport.
boost::shared_ptr<TMemoryBuffer> membuf(new TMemoryBuffer());
{
// Create a TZlibTransport object, and immediately destroy it
// when it goes out of scope.
TZlibTransport w_zlib_trans(membuf);
}
BOOST_CHECK_EQUAL(membuf->available_read(), (uint32_t)0);
}
/*
* Initialization
*/
#if (BOOST_VERSION >= 105900)
#define ADD_TEST_CASE(suite, name, _FUNC, ...) \
do { \
::std::ostringstream name_ss; \
name_ss << name << "-" << BOOST_STRINGIZE(_FUNC); \
boost::function<void ()> test_func = ::apache::thrift::stdcxx::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(::apache::thrift::stdcxx::bind(_FUNC, \
##__VA_ARGS__), \
name_ss.str()); \
(suite)->add(tc); \
} while (0)
#endif
void add_tests(boost::unit_test::test_suite* suite,
const boost::shared_array<uint8_t>& buf,
uint32_t buf_len,
const char* name) {
ADD_TEST_CASE(suite, name, test_write_then_read, buf, buf_len);
ADD_TEST_CASE(suite, name, test_separate_checksum, buf, buf_len);
ADD_TEST_CASE(suite, name, test_incomplete_checksum, buf, buf_len);
ADD_TEST_CASE(suite, name, test_invalid_checksum, buf, buf_len);
ADD_TEST_CASE(suite, name, test_write_after_flush, buf, buf_len);
boost::shared_ptr<SizeGenerator> size_32k(new ConstantSizeGenerator(1 << 15));
boost::shared_ptr<SizeGenerator> size_lognormal(new LogNormalSizeGenerator(20, 30));
ADD_TEST_CASE(suite, name << "-constant", test_read_write_mix, buf, buf_len, size_32k, size_32k);
ADD_TEST_CASE(suite,
name << "-lognormal-write",
test_read_write_mix,
buf,
buf_len,
size_lognormal,
size_32k);
ADD_TEST_CASE(suite,
name << "-lognormal-read",
test_read_write_mix,
buf,
buf_len,
size_32k,
size_lognormal);
ADD_TEST_CASE(suite,
name << "-lognormal-both",
test_read_write_mix,
buf,
buf_len,
size_lognormal,
size_lognormal);
// Test with a random size distribution,
// but use the exact same distribution for reading as for writing.
//
// Because the SizeGenerator makes a copy of the random number generator,
// both SizeGenerators should return the exact same set of values, since they
// both start with random number generators in the same state.
boost::shared_ptr<SizeGenerator> write_size_gen(new LogNormalSizeGenerator(20, 30));
boost::shared_ptr<SizeGenerator> read_size_gen(new LogNormalSizeGenerator(20, 30));
ADD_TEST_CASE(suite,
name << "-lognormal-same-distribution",
test_read_write_mix,
buf,
buf_len,
write_size_gen,
read_size_gen);
}
void print_usage(FILE* f, const char* argv0) {
fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
fprintf(f, "Options:\n");
fprintf(f, " --seed=<N>, -s <N>\n");
fprintf(f, " --help\n");
}
#ifdef BOOST_TEST_DYN_LINK
bool init_unit_test_suite() {
uint32_t seed = static_cast<uint32_t>(time(NULL));
#ifdef HAVE_INTTYPES_H
printf("seed: %" PRIu32 "\n", seed);
#endif
rng.seed(seed);
boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
suite->p_name.value = "ZlibTest";
uint32_t buf_len = 1024 * 32;
add_tests(suite, gen_uniform_buffer(buf_len, 'a'), buf_len, "uniform");
add_tests(suite, gen_compressible_buffer(buf_len), buf_len, "compressible");
add_tests(suite, gen_random_buffer(buf_len), buf_len, "random");
suite->add(BOOST_TEST_CASE(test_no_write));
return true;
}
int main( int argc, char* argv[] ) {
return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
}
#else
boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
THRIFT_UNUSED_VARIABLE(argc);
THRIFT_UNUSED_VARIABLE(argv);
uint32_t seed = static_cast<uint32_t>(time(NULL));
#ifdef HAVE_INTTYPES_H
printf("seed: %" PRIu32 "\n", seed);
#endif
rng.seed(seed);
boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
suite->p_name.value = "ZlibTest";
uint32_t buf_len = 1024 * 32;
add_tests(suite, gen_uniform_buffer(buf_len, 'a'), buf_len, "uniform");
add_tests(suite, gen_compressible_buffer(buf_len), buf_len, "compressible");
add_tests(suite, gen_random_buffer(buf_len), buf_len, "random");
suite->add(BOOST_TEST_CASE(test_no_write));
return NULL;
}
#endif

View file

@ -0,0 +1,189 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <iostream>
#include <vector>
#include <string>
#include "ThreadFactoryTests.h"
#include "TimerManagerTests.h"
#include "ThreadManagerTests.h"
int main(int argc, char** argv) {
std::string arg;
std::vector<std::string> args(argc - 1 > 1 ? argc - 1 : 1);
args[0] = "all";
for (int ix = 1; ix < argc; ix++) {
args[ix - 1] = std::string(argv[ix]);
}
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 = 20;
int reapCount = 1000;
size_t floodLoops = 3;
size_t floodCount = 20000;
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 = Util::currentTime();
int64_t time01 = Util::currentTime();
std::cout << "\t\t\tMinimum time: " << time01 - time00 << "ms" << std::endl;
time00 = Util::currentTime();
time01 = time00;
size_t count = 0;
while (time01 < time00 + 10) {
count++;
time01 = Util::currentTime();
}
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;
}
}
if (runAll || args[0].compare("thread-manager") == 0) {
std::cout << "ThreadManager tests..." << std::endl;
{
size_t workerCount = 100;
size_t taskCount = 50000;
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 = 64;
size_t tasksPerWorker = 1000;
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;
}

View file

@ -0,0 +1,313 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <thrift/thrift-config.h>
#include <thrift/concurrency/Thread.h>
#include <thrift/concurrency/PlatformThreadFactory.h>
#include <thrift/concurrency/Monitor.h>
#include <thrift/concurrency/Util.h>
#include <assert.h>
#include <iostream>
#include <set>
namespace apache {
namespace thrift {
namespace concurrency {
namespace test {
using boost::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() {
Synchronized s(_monitor);
_count--;
// std::cout << "\t\t\tthread count: " << _count << std::endl;
if (_count == 0) {
_monitor.notify();
}
}
Monitor& _monitor;
int& _count;
};
bool reapNThreads(int loop = 1, int count = 10) {
PlatformThreadFactory threadFactory = PlatformThreadFactory();
shared_ptr<Monitor> monitor(new Monitor);
for (int lix = 0; lix < loop; lix++) {
int* activeCount = new int(count);
std::set<shared_ptr<Thread> > threads;
int tix;
for (tix = 0; tix < count; tix++) {
try {
threads.insert(
threadFactory.newThread(shared_ptr<Runnable>(new ReapNTask(*monitor, *activeCount))));
} catch (SystemResourceException& e) {
std::cout << "\t\t\tfailed to create " << lix* count + tix << " thread " << e.what()
<< std::endl;
throw e;
}
}
tix = 0;
for (std::set<shared_ptr<Thread> >::const_iterator thread = threads.begin();
thread != threads.end();
tix++, ++thread) {
try {
(*thread)->start();
} catch (SystemResourceException& e) {
std::cout << "\t\t\tfailed to start " << lix* count + tix << " thread " << e.what()
<< std::endl;
throw e;
}
}
{
Synchronized s(*monitor);
while (*activeCount > 0) {
monitor->wait(1000);
}
}
delete activeCount;
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() {
{
Synchronized s(_monitor);
if (_state == SynchStartTask::STARTING) {
_state = SynchStartTask::STARTED;
_monitor.notify();
}
}
{
Synchronized s(_monitor);
while (_state == SynchStartTask::STARTED) {
_monitor.wait();
}
if (_state == SynchStartTask::STOPPING) {
_state = SynchStartTask::STOPPED;
_monitor.notifyAll();
}
}
}
private:
Monitor& _monitor;
volatile STATE& _state;
};
bool synchStartTest() {
Monitor monitor;
SynchStartTask::STATE state = SynchStartTask::UNINITIALIZED;
shared_ptr<SynchStartTask> task
= shared_ptr<SynchStartTask>(new SynchStartTask(monitor, state));
PlatformThreadFactory threadFactory = PlatformThreadFactory();
shared_ptr<Thread> thread = threadFactory.newThread(task);
if (state == SynchStartTask::UNINITIALIZED) {
state = SynchStartTask::STARTING;
thread->start();
}
{
Synchronized s(monitor);
while (state == SynchStartTask::STARTING) {
monitor.wait();
}
}
assert(state != SynchStartTask::STARTING);
{
Synchronized s(monitor);
try {
monitor.wait(100);
} catch (TimedOutException&) {
}
if (state == SynchStartTask::STARTED) {
state = SynchStartTask::STOPPING;
monitor.notify();
}
while (state == SynchStartTask::STOPPING) {
monitor.wait();
}
}
assert(state == SynchStartTask::STOPPED);
bool success = true;
std::cout << "\t\t\t" << (success ? "Success" : "Failure") << "!" << std::endl;
return true;
}
/**
* The only guarantee a monitor timeout can give you is that
* it will take "at least" as long as the timeout, no less.
* There is absolutely no guarantee around regaining execution
* near the timeout. On a busy system (like inside a third party
* CI environment) it could take quite a bit longer than the
* requested timeout, and that's ok.
*/
bool monitorTimeoutTest(int64_t count = 1000, int64_t timeout = 2) {
Monitor monitor;
int64_t startTime = Util::currentTime();
for (int64_t ix = 0; ix < count; ix++) {
{
Synchronized s(monitor);
try {
monitor.wait(timeout);
} catch (TimedOutException&) {
}
}
}
int64_t endTime = Util::currentTime();
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) : _id(id) {}
~FloodTask() {
if (_id % 10000 == 0) {
std::cout << "\t\tthread " << _id << " done" << std::endl;
}
}
void run() {
if (_id % 10000 == 0) {
std::cout << "\t\tthread " << _id << " started" << std::endl;
}
}
const size_t _id;
};
void foo(PlatformThreadFactory* tf) { (void)tf; }
bool floodNTest(size_t loop = 1, size_t count = 100000) {
bool success = false;
for (size_t lix = 0; lix < loop; lix++) {
PlatformThreadFactory threadFactory = PlatformThreadFactory();
threadFactory.setDetached(true);
for (size_t tix = 0; tix < count; tix++) {
try {
shared_ptr<FloodTask> task(new FloodTask(lix * count + tix));
shared_ptr<Thread> thread = threadFactory.newThread(task);
thread->start();
} catch (TException& e) {
std::cout << "\t\t\tfailed to start " << lix* count + tix << " thread " << e.what()
<< std::endl;
return success;
}
}
std::cout << "\t\t\tflooded " << (lix + 1) * count << " threads" << std::endl;
success = true;
}
return success;
}
};
}
}
}
} // apache::thrift::concurrency::test

View file

@ -0,0 +1,685 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <thrift/thrift-config.h>
#include <thrift/concurrency/ThreadManager.h>
#include <thrift/concurrency/PlatformThreadFactory.h>
#include <thrift/concurrency/Monitor.h>
#include <thrift/concurrency/Util.h>
#include <assert.h>
#include <deque>
#include <set>
#include <iostream>
#include <stdint.h>
namespace apache {
namespace thrift {
namespace concurrency {
namespace test {
using namespace apache::thrift::concurrency;
static std::deque<boost::shared_ptr<Runnable> > m_expired;
static void expiredNotifier(boost::shared_ptr<Runnable> runnable)
{
m_expired.push_back(runnable);
}
static void sleep_(int64_t millisec) {
Monitor _sleep;
Synchronized s(_sleep);
try {
_sleep.wait(millisec);
} catch (TimedOutException&) {
;
} catch (...) {
assert(0);
}
}
class ThreadManagerTests {
public:
class Task : public Runnable {
public:
Task(Monitor& monitor, size_t& count, int64_t timeout)
: _monitor(monitor), _count(count), _timeout(timeout), _startTime(0), _endTime(0), _done(false) {}
void run() {
_startTime = Util::currentTime();
sleep_(_timeout);
_endTime = Util::currentTime();
_done = true;
{
Synchronized s(_monitor);
// std::cout << "Thread " << _count << " completed " << std::endl;
_count--;
if (_count % 10000 == 0) {
_monitor.notify();
}
}
}
Monitor& _monitor;
size_t& _count;
int64_t _timeout;
int64_t _startTime;
int64_t _endTime;
bool _done;
Monitor _sleep;
};
/**
* Dispatch count tasks, each of which blocks for timeout milliseconds then
* completes. Verify that all tasks completed and that thread manager cleans
* up properly on delete.
*/
bool loadTest(size_t count = 100, int64_t timeout = 100LL, size_t workerCount = 4) {
Monitor monitor;
size_t activeCount = count;
shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(workerCount);
shared_ptr<PlatformThreadFactory> threadFactory
= shared_ptr<PlatformThreadFactory>(new PlatformThreadFactory());
#if !USE_BOOST_THREAD && !USE_STD_THREAD
threadFactory->setPriority(PosixThreadFactory::HIGHEST);
#endif
threadManager->threadFactory(threadFactory);
threadManager->start();
std::set<shared_ptr<ThreadManagerTests::Task> > tasks;
for (size_t ix = 0; ix < count; ix++) {
tasks.insert(shared_ptr<ThreadManagerTests::Task>(
new ThreadManagerTests::Task(monitor, activeCount, timeout)));
}
int64_t time00 = Util::currentTime();
for (std::set<shared_ptr<ThreadManagerTests::Task> >::iterator 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 = Util::currentTime();
int64_t firstTime = 9223372036854775807LL;
int64_t lastTime = 0;
double averageTime = 0;
int64_t minTime = 9223372036854775807LL;
int64_t maxTime = 0;
for (std::set<shared_ptr<ThreadManagerTests::Task> >::iterator ix = tasks.begin();
ix != tasks.end();
ix++) {
shared_ptr<ThreadManagerTests::Task> task = *ix;
int64_t delta = task->_endTime - task->_startTime;
assert(delta > 0);
if (task->_startTime < firstTime) {
firstTime = task->_startTime;
}
if (task->_endTime > lastTime) {
lastTime = task->_endTime;
}
if (delta < minTime) {
minTime = delta;
}
if (delta > maxTime) {
maxTime = delta;
}
averageTime += delta;
}
averageTime /= count;
std::cout << "\t\t\tfirst start: " << firstTime << " Last end: " << lastTime
<< " min: " << minTime << "ms max: " << maxTime << "ms average: " << averageTime
<< "ms" << std::endl;
bool success = (time01 - time00) >= ((int64_t)count * timeout) / (int64_t)workerCount;
std::cout << "\t\t\t" << (success ? "Success" : "Failure")
<< "! expected time: " << ((int64_t)count * timeout) / (int64_t)workerCount << "ms elapsed time: " << time01 - time00
<< "ms" << std::endl;
return success;
}
class BlockTask : public Runnable {
public:
BlockTask(Monitor& entryMonitor, Monitor& blockMonitor, bool& blocked, Monitor& doneMonitor, size_t& count)
: _entryMonitor(entryMonitor), _entered(false), _blockMonitor(blockMonitor), _blocked(blocked), _doneMonitor(doneMonitor), _count(count) {}
void run() {
{
Synchronized s(_entryMonitor);
_entered = true;
_entryMonitor.notify();
}
{
Synchronized s(_blockMonitor);
while (_blocked) {
_blockMonitor.wait();
}
}
{
Synchronized s(_doneMonitor);
if (--_count == 0) {
_doneMonitor.notify();
}
}
}
Monitor& _entryMonitor;
bool _entered;
Monitor& _blockMonitor;
bool& _blocked;
Monitor& _doneMonitor;
size_t& _count;
};
/**
* Block test. Create pendingTaskCountMax tasks. Verify that we block adding the
* pendingTaskCountMax + 1th task. Verify that we unblock when a task completes */
bool blockTest(int64_t timeout = 100LL, size_t workerCount = 2) {
(void)timeout;
bool success = false;
try {
Monitor entryMonitor; // not used by this test
Monitor blockMonitor;
bool blocked[] = {true, true, true};
Monitor doneMonitor;
size_t pendingTaskMaxCount = workerCount;
size_t activeCounts[] = {workerCount, pendingTaskMaxCount, 1};
shared_ptr<ThreadManager> threadManager
= ThreadManager::newSimpleThreadManager(workerCount, pendingTaskMaxCount);
shared_ptr<PlatformThreadFactory> threadFactory
= shared_ptr<PlatformThreadFactory>(new PlatformThreadFactory());
#if !USE_BOOST_THREAD && !USE_STD_THREAD
threadFactory->setPriority(PosixThreadFactory::HIGHEST);
#endif
threadManager->threadFactory(threadFactory);
threadManager->start();
std::vector<shared_ptr<ThreadManagerTests::BlockTask> > tasks;
tasks.reserve(workerCount + pendingTaskMaxCount);
for (size_t ix = 0; ix < workerCount; ix++) {
tasks.push_back(shared_ptr<ThreadManagerTests::BlockTask>(
new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[0], doneMonitor, activeCounts[0])));
}
for (size_t ix = 0; ix < pendingTaskMaxCount; ix++) {
tasks.push_back(shared_ptr<ThreadManagerTests::BlockTask>(
new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[1], doneMonitor, activeCounts[1])));
}
for (std::vector<shared_ptr<ThreadManagerTests::BlockTask> >::iterator ix = tasks.begin();
ix != tasks.end();
ix++) {
threadManager->add(*ix);
}
if (!(success = (threadManager->totalTaskCount() == pendingTaskMaxCount + workerCount))) {
throw TException("Unexpected pending task count");
}
shared_ptr<ThreadManagerTests::BlockTask> extraTask(
new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked[2], doneMonitor, activeCounts[2]));
try {
threadManager->add(extraTask, 1);
throw TException("Unexpected success adding task in excess of pending task count");
} catch (TooManyPendingTasksException&) {
throw TException("Should have timed out adding task in excess of pending task count");
} catch (TimedOutException&) {
// Expected result
}
try {
threadManager->add(extraTask, -1);
throw TException("Unexpected success adding task in excess of pending task count");
} catch (TimedOutException&) {
throw TException("Unexpected timeout adding task in excess of pending task count");
} catch (TooManyPendingTasksException&) {
// Expected result
}
std::cout << "\t\t\t"
<< "Pending tasks " << threadManager->pendingTaskCount() << std::endl;
{
Synchronized s(blockMonitor);
blocked[0] = false;
blockMonitor.notifyAll();
}
{
Synchronized s(doneMonitor);
while (activeCounts[0] != 0) {
doneMonitor.wait();
}
}
std::cout << "\t\t\t"
<< "Pending tasks " << threadManager->pendingTaskCount() << std::endl;
try {
threadManager->add(extraTask, 1);
} catch (TimedOutException&) {
std::cout << "\t\t\t"
<< "add timed out unexpectedly" << std::endl;
throw TException("Unexpected timeout adding task");
} catch (TooManyPendingTasksException&) {
std::cout << "\t\t\t"
<< "add encountered too many pending exepctions" << std::endl;
throw TException("Unexpected timeout adding task");
}
// Wake up tasks that were pending before and wait for them to complete
{
Synchronized s(blockMonitor);
blocked[1] = false;
blockMonitor.notifyAll();
}
{
Synchronized s(doneMonitor);
while (activeCounts[1] != 0) {
doneMonitor.wait();
}
}
// Wake up the extra task and wait for it to complete
{
Synchronized s(blockMonitor);
blocked[2] = false;
blockMonitor.notifyAll();
}
{
Synchronized s(doneMonitor);
while (activeCounts[2] != 0) {
doneMonitor.wait();
}
}
threadManager->stop();
if (!(success = (threadManager->totalTaskCount() == 0))) {
throw TException("Unexpected total task count");
}
} catch (TException& e) {
std::cout << "ERROR: " << e.what() << std::endl;
}
std::cout << "\t\t\t" << (success ? "Success" : "Failure") << std::endl;
return success;
}
bool apiTest() {
// prove currentTime has milliseconds granularity since many other things depend on it
int64_t a = Util::currentTime();
sleep_(100);
int64_t b = Util::currentTime();
if (b - a < 50 || b - a > 150) {
std::cerr << "\t\t\texpected 100ms gap, found " << (b-a) << "ms gap instead." << std::endl;
return false;
}
#if !USE_BOOST_THREAD && !USE_STD_THREAD
// test once with a detached thread factory and once with a joinable thread factory
shared_ptr<PosixThreadFactory> threadFactory
= shared_ptr<PosixThreadFactory>(new PosixThreadFactory(false));
std::cout << "\t\t\tapiTest with joinable thread factory" << std::endl;
if (!apiTestWithThreadFactory(threadFactory)) {
return false;
}
threadFactory.reset(new PosixThreadFactory(true));
std::cout << "\t\t\tapiTest with detached thread factory" << std::endl;
return apiTestWithThreadFactory(threadFactory);
#else
return apiTestWithThreadFactory(shared_ptr<PlatformThreadFactory>(new PlatformThreadFactory()));
#endif
}
bool apiTestWithThreadFactory(shared_ptr<PlatformThreadFactory> threadFactory)
{
shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(1);
threadManager->threadFactory(threadFactory);
#if !USE_BOOST_THREAD && !USE_STD_THREAD
threadFactory->setPriority(PosixThreadFactory::HIGHEST);
// verify we cannot change the thread factory to one with the opposite detached setting
shared_ptr<PlatformThreadFactory> threadFactory2
= shared_ptr<PosixThreadFactory>(new PlatformThreadFactory(
PosixThreadFactory::ROUND_ROBIN,
PosixThreadFactory::NORMAL,
1,
!threadFactory->isDetached()));
try {
threadManager->threadFactory(threadFactory2);
// if the call succeeded we changed the thread factory to one that had the opposite setting for "isDetached()".
// this is bad, because the thread manager checks with the thread factory to see if it should join threads
// as they are leaving - so the detached status of new threads cannot change while there are existing threads.
std::cerr << "\t\t\tShould not be able to change thread factory detached disposition" << std::endl;
return false;
}
catch (InvalidArgumentException& ex) {
/* expected */
}
#endif
std::cout << "\t\t\t\tstarting.. " << std::endl;
threadManager->start();
threadManager->setExpireCallback(expiredNotifier); // apache::thrift::stdcxx::bind(&ThreadManagerTests::expiredNotifier, this));
#define EXPECT(FUNC, COUNT) { size_t c = FUNC; if (c != COUNT) { std::cerr << "expected " #FUNC" to be " #COUNT ", but was " << c << std::endl; return false; } }
EXPECT(threadManager->workerCount(), 1);
EXPECT(threadManager->idleWorkerCount(), 1);
EXPECT(threadManager->pendingTaskCount(), 0);
std::cout << "\t\t\t\tadd 2nd worker.. " << std::endl;
threadManager->addWorker();
EXPECT(threadManager->workerCount(), 2);
EXPECT(threadManager->idleWorkerCount(), 2);
EXPECT(threadManager->pendingTaskCount(), 0);
std::cout << "\t\t\t\tremove 2nd worker.. " << std::endl;
threadManager->removeWorker();
EXPECT(threadManager->workerCount(), 1);
EXPECT(threadManager->idleWorkerCount(), 1);
EXPECT(threadManager->pendingTaskCount(), 0);
std::cout << "\t\t\t\tremove 1st worker.. " << std::endl;
threadManager->removeWorker();
EXPECT(threadManager->workerCount(), 0);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 0);
std::cout << "\t\t\t\tadd blocking task.. " << std::endl;
// We're going to throw a blocking task into the mix
Monitor entryMonitor; // signaled when task is running
Monitor blockMonitor; // to be signaled to unblock the task
bool blocked(true); // set to false before notifying
Monitor doneMonitor; // signaled when count reaches zero
size_t activeCount = 1;
shared_ptr<ThreadManagerTests::BlockTask> blockingTask(
new ThreadManagerTests::BlockTask(entryMonitor, blockMonitor, blocked, doneMonitor, activeCount));
threadManager->add(blockingTask);
EXPECT(threadManager->workerCount(), 0);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 1);
std::cout << "\t\t\t\tadd other task.. " << std::endl;
shared_ptr<ThreadManagerTests::Task> otherTask(
new ThreadManagerTests::Task(doneMonitor, activeCount, 0));
threadManager->add(otherTask);
EXPECT(threadManager->workerCount(), 0);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 2);
std::cout << "\t\t\t\tremove blocking task specifically.. " << std::endl;
threadManager->remove(blockingTask);
EXPECT(threadManager->workerCount(), 0);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 1);
std::cout << "\t\t\t\tremove next pending task.." << std::endl;
shared_ptr<Runnable> nextTask = threadManager->removeNextPending();
if (nextTask != otherTask) {
std::cerr << "\t\t\t\t\texpected removeNextPending to return otherTask" << std::endl;
return false;
}
EXPECT(threadManager->workerCount(), 0);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 0);
std::cout << "\t\t\t\tremove next pending task (none left).." << std::endl;
nextTask = threadManager->removeNextPending();
if (nextTask) {
std::cerr << "\t\t\t\t\texpected removeNextPending to return an empty Runnable" << std::endl;
return false;
}
std::cout << "\t\t\t\tadd 2 expired tasks and 1 not.." << std::endl;
shared_ptr<ThreadManagerTests::Task> expiredTask(
new ThreadManagerTests::Task(doneMonitor, activeCount, 0));
threadManager->add(expiredTask, 0, 1);
threadManager->add(blockingTask); // add one that hasn't expired to make sure it gets skipped
threadManager->add(expiredTask, 0, 1); // add a second expired to ensure removeExpiredTasks removes both
sleep_(50); // make sure enough time elapses for it to expire - the shortest expiration time is 1 millisecond
EXPECT(threadManager->workerCount(), 0);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 3);
EXPECT(threadManager->expiredTaskCount(), 0);
std::cout << "\t\t\t\tremove expired tasks.." << std::endl;
if (!m_expired.empty()) {
std::cerr << "\t\t\t\t\texpected m_expired to be empty" << std::endl;
return false;
}
threadManager->removeExpiredTasks();
if (m_expired.size() != 2) {
std::cerr << "\t\t\t\t\texpected m_expired to be set" << std::endl;
return false;
}
if (m_expired.front() != expiredTask) {
std::cerr << "\t\t\t\t\texpected m_expired[0] to be the expired task" << std::endl;
return false;
}
m_expired.pop_front();
if (m_expired.front() != expiredTask) {
std::cerr << "\t\t\t\t\texpected m_expired[1] to be the expired task" << std::endl;
return false;
}
m_expired.clear();
threadManager->remove(blockingTask);
EXPECT(threadManager->workerCount(), 0);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 0);
EXPECT(threadManager->expiredTaskCount(), 2);
std::cout << "\t\t\t\tadd expired task (again).." << std::endl;
threadManager->add(expiredTask, 0, 1); // expires in 1ms
sleep_(50); // make sure enough time elapses for it to expire - the shortest expiration time is 1ms
std::cout << "\t\t\t\tadd worker to consume expired task.." << std::endl;
threadManager->addWorker();
sleep_(100); // make sure it has time to spin up and expire the task
if (m_expired.empty()) {
std::cerr << "\t\t\t\t\texpected m_expired to be set" << std::endl;
return false;
}
if (m_expired.front() != expiredTask) {
std::cerr << "\t\t\t\t\texpected m_expired to be the expired task" << std::endl;
return false;
}
m_expired.clear();
EXPECT(threadManager->workerCount(), 1);
EXPECT(threadManager->idleWorkerCount(), 1);
EXPECT(threadManager->pendingTaskCount(), 0);
EXPECT(threadManager->expiredTaskCount(), 3);
std::cout << "\t\t\t\ttry to remove too many workers" << std::endl;
try {
threadManager->removeWorker(2);
std::cerr << "\t\t\t\t\texpected InvalidArgumentException" << std::endl;
return false;
} catch (const InvalidArgumentException&) {
/* expected */
}
std::cout << "\t\t\t\tremove worker.. " << std::endl;
threadManager->removeWorker();
EXPECT(threadManager->workerCount(), 0);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 0);
EXPECT(threadManager->expiredTaskCount(), 3);
std::cout << "\t\t\t\tadd blocking task.. " << std::endl;
threadManager->add(blockingTask);
EXPECT(threadManager->workerCount(), 0);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 1);
std::cout << "\t\t\t\tadd worker.. " << std::endl;
threadManager->addWorker();
{
Synchronized s(entryMonitor);
while (!blockingTask->_entered) {
entryMonitor.wait();
}
}
EXPECT(threadManager->workerCount(), 1);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 0);
std::cout << "\t\t\t\tunblock task and remove worker.. " << std::endl;
{
Synchronized s(blockMonitor);
blocked = false;
blockMonitor.notifyAll();
}
threadManager->removeWorker();
EXPECT(threadManager->workerCount(), 0);
EXPECT(threadManager->idleWorkerCount(), 0);
EXPECT(threadManager->pendingTaskCount(), 0);
std::cout << "\t\t\t\tcleanup.. " << std::endl;
blockingTask.reset();
threadManager.reset();
return true;
}
};
}
}
}
} // apache::thrift::concurrency
using namespace apache::thrift::concurrency::test;

View file

@ -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 <thrift/concurrency/TimerManager.h>
#include <thrift/concurrency/PlatformThreadFactory.h>
#include <thrift/concurrency/Monitor.h>
#include <thrift/concurrency/Util.h>
#include <assert.h>
#include <iostream>
namespace apache {
namespace thrift {
namespace concurrency {
namespace test {
using namespace apache::thrift::concurrency;
class TimerManagerTests {
public:
class Task : public Runnable {
public:
Task(Monitor& monitor, int64_t timeout)
: _timeout(timeout),
_startTime(Util::currentTime()),
_endTime(0),
_monitor(monitor),
_success(false),
_done(false) {}
~Task() { std::cerr << this << std::endl; }
void run() {
_endTime = Util::currentTime();
_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(int64_t timeout = 1000LL) {
shared_ptr<TimerManagerTests::Task> orphanTask
= shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, 10 * timeout));
{
TimerManager timerManager;
timerManager.threadFactory(shared_ptr<PlatformThreadFactory>(new PlatformThreadFactory()));
timerManager.start();
assert(timerManager.state() == TimerManager::STARTED);
// Don't create task yet, because its constructor sets the expected completion time, and we
// need to delay between inserting the two tasks into the run queue.
shared_ptr<TimerManagerTests::Task> task;
{
Synchronized s(_monitor);
timerManager.add(orphanTask, 10 * timeout);
try {
// Wait for 1 second in order to give timerManager a chance to start sleeping in response
// to adding orphanTask. We need to do this so we can verify that adding the second task
// kicks the dispatcher out of the current wait and starts the new 1 second wait.
_monitor.wait(1000);
assert(
0 == "ERROR: This wait should time out. TimerManager dispatcher may have a problem.");
} catch (TimedOutException&) {
}
task.reset(new TimerManagerTests::Task(_monitor, timeout));
timerManager.add(task, timeout);
_monitor.wait();
}
assert(task->_done);
std::cout << "\t\t\t" << (task->_success ? "Success" : "Failure") << "!" << std::endl;
}
// timerManager.stop(); This is where it happens via destructor
assert(!orphanTask->_done);
return true;
}
friend class TestTask;
Monitor _monitor;
};
}
}
}
} // apache::thrift::concurrency

View file

@ -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;
}

View file

@ -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"

View file

@ -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"

View file

@ -0,0 +1,136 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "EventLog.h"
#include <stdarg.h>
#include <stdlib.h>
using namespace std;
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 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 (TimedOutException ex) {
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_);
EventList::iterator 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 (TimedOutException ex) {
return Event(ET_LOG_END, 0, 0, "");
}
if (it->connectionId == connId) {
Event event = *it;
events_.erase(it);
return event;
}
}
}
}
}
} // apache::thrift::test

View file

@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef _THRIFT_TEST_EVENTLOG_H_
#define _THRIFT_TEST_EVENTLOG_H_ 1
#include <thrift/concurrency/Monitor.h>
namespace apache {
namespace thrift {
namespace test {
// Initially I made EventType an enum, but using char* results
// in much more readable error messages when there is a mismatch.
// It also lets users of EventLog easily define their own new types.
// Comparing the literal pointer values should be safe, barring any strange
// linking setup that results in duplicate symbols.
typedef const char* EventType;
struct Event {
Event(EventType type, uint32_t connectionId, uint32_t callId, const std::string& message)
: type(type), connectionId(connectionId), callId(callId), message(message) {}
EventType type;
uint32_t connectionId;
uint32_t callId;
std::string message;
};
class EventLog {
public:
static EventType ET_LOG_END;
static EventType ET_CONN_CREATED;
static EventType ET_CONN_DESTROYED;
static EventType ET_CALL_STARTED;
static EventType ET_CALL_FINISHED;
static EventType ET_PROCESS;
static EventType ET_PRE_READ;
static EventType ET_POST_READ;
static EventType ET_PRE_WRITE;
static EventType ET_POST_WRITE;
static EventType ET_ASYNC_COMPLETE;
static EventType ET_HANDLER_ERROR;
static EventType ET_CALL_INCREMENT_GENERATION;
static EventType ET_CALL_GET_GENERATION;
static EventType ET_CALL_ADD_STRING;
static EventType ET_CALL_GET_STRINGS;
static EventType ET_CALL_GET_DATA_WAIT;
static EventType ET_CALL_ONEWAY_WAIT;
static EventType ET_CALL_UNEXPECTED_EXCEPTION_WAIT;
static EventType ET_CALL_EXCEPTION_WAIT;
static EventType ET_WAIT_RETURN;
static EventType ET_CALL_SET_VALUE;
static EventType ET_CALL_GET_VALUE;
EventLog();
void append(EventType type,
uint32_t connectionId,
uint32_t callId,
const std::string& message = "");
Event waitForEvent(int64_t timeout = 500);
Event waitForConnEvent(uint32_t connId, int64_t timeout = 500);
protected:
typedef std::list<Event> EventList;
concurrency::Monitor monitor_;
EventList events_;
uint32_t id_;
static uint32_t nextId_;
};
}
}
} // apache::thrift::test
#endif // _THRIFT_TEST_EVENTLOG_H_

View file

@ -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 boost::shared_ptr<EventLog>& log)
: triggerMonitor(&mutex_), generation_(0), wait_(false), log_(log) {}
int32_t incrementGeneration() {
concurrency::Guard g(mutex_);
log_->append(EventLog::ET_CALL_INCREMENT_GENERATION, 0, 0);
return ++generation_;
}
int32_t getGeneration() {
concurrency::Guard g(mutex_);
log_->append(EventLog::ET_CALL_GET_GENERATION, 0, 0);
return generation_;
}
void addString(const std::string& s) {
concurrency::Guard g(mutex_);
log_->append(EventLog::ET_CALL_ADD_STRING, 0, 0);
strings_.push_back(s);
}
void getStrings(std::vector<std::string>& _return) {
concurrency::Guard g(mutex_);
log_->append(EventLog::ET_CALL_GET_STRINGS, 0, 0);
_return = strings_;
}
void getDataWait(std::string& _return, const int32_t length) {
concurrency::Guard g(mutex_);
log_->append(EventLog::ET_CALL_GET_DATA_WAIT, 0, 0);
blockUntilTriggered();
_return.append(length, 'a');
}
void onewayWait() {
concurrency::Guard g(mutex_);
log_->append(EventLog::ET_CALL_ONEWAY_WAIT, 0, 0);
blockUntilTriggered();
}
void exceptionWait(const std::string& message) {
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) {
concurrency::Guard g(mutex_);
log_->append(EventLog::ET_CALL_UNEXPECTED_EXCEPTION_WAIT, 0, 0);
blockUntilTriggered();
MyError e;
e.message = message;
throw e;
}
/**
* After prepareTriggeredCall() is invoked, calls to any of the *Wait()
* functions won't return until triggerPendingCalls() is invoked
*
* This has to be a separate function invoked by the main test thread
* in order to to avoid race conditions.
*/
void prepareTriggeredCall() {
concurrency::Guard g(mutex_);
wait_ = true;
}
/**
* Wake up all calls waiting in blockUntilTriggered()
*/
void triggerPendingCalls() {
concurrency::Guard g(mutex_);
wait_ = false;
triggerMonitor.notifyAll();
}
protected:
/**
* blockUntilTriggered() won't return until triggerPendingCalls() is invoked
* in another thread.
*
* This should only be called when already holding mutex_.
*/
void blockUntilTriggered() {
while (wait_) {
triggerMonitor.waitForever();
}
// Log an event when we return
log_->append(EventLog::ET_WAIT_RETURN, 0, 0);
}
concurrency::Mutex mutex_;
concurrency::Monitor triggerMonitor;
int32_t generation_;
bool wait_;
std::vector<std::string> strings_;
boost::shared_ptr<EventLog> log_;
};
#ifdef _WIN32
#pragma warning( push )
#pragma warning (disable : 4250 ) //inheriting methods via dominance
#endif
class ChildHandler : public ParentHandler, virtual public ChildServiceIf {
public:
ChildHandler(const boost::shared_ptr<EventLog>& log) : ParentHandler(log), value_(0) {}
int32_t setValue(const int32_t value) {
concurrency::Guard g(mutex_);
log_->append(EventLog::ET_CALL_SET_VALUE, 0, 0);
int32_t oldValue = value_;
value_ = value;
return oldValue;
}
int32_t getValue() {
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(boost::shared_ptr<protocol::TProtocol> in,
boost::shared_ptr<protocol::TProtocol> out,
uint32_t id)
: input(in), output(out), id(id) {}
boost::shared_ptr<protocol::TProtocol> input;
boost::shared_ptr<protocol::TProtocol> output;
uint32_t id;
};
struct CallContext {
public:
CallContext(ConnContext* context, uint32_t id, const std::string& name)
: connContext(context), name(name), id(id) {}
ConnContext* connContext;
std::string name;
uint32_t id;
};
class ServerEventHandler : public server::TServerEventHandler {
public:
ServerEventHandler(const boost::shared_ptr<EventLog>& log) : nextId_(1), log_(log) {}
virtual void preServe() {}
virtual void* createContext(boost::shared_ptr<protocol::TProtocol> input,
boost::shared_ptr<protocol::TProtocol> output) {
ConnContext* context = new ConnContext(input, output, nextId_);
++nextId_;
log_->append(EventLog::ET_CONN_CREATED, context->id, 0);
return context;
}
virtual void deleteContext(void* serverContext,
boost::shared_ptr<protocol::TProtocol> input,
boost::shared_ptr<protocol::TProtocol> output) {
ConnContext* context = reinterpret_cast<ConnContext*>(serverContext);
if (input != context->input) {
abort();
}
if (output != context->output) {
abort();
}
log_->append(EventLog::ET_CONN_DESTROYED, context->id, 0);
delete context;
}
virtual void processContext(void* serverContext,
boost::shared_ptr<transport::TTransport> transport) {
// TODO: We currently don't test the behavior of the processContext()
// calls. The various server implementations call processContext() at
// slightly different times, and it is too annoying to try and account for
// their various differences.
//
// TThreadedServer, TThreadPoolServer, and TSimpleServer usually wait until
// they see the first byte of a request before calling processContext().
// However, they don't wait for the first byte of the very first request,
// and instead immediately call processContext() before any data is
// received.
//
// TNonblockingServer always waits until receiving the full request before
// calling processContext().
#if 0
ConnContext* context = reinterpret_cast<ConnContext*>(serverContext);
log_->append(EventLog::ET_PROCESS, context->id, 0);
#else
THRIFT_UNUSED_VARIABLE(serverContext);
THRIFT_UNUSED_VARIABLE(transport);
#endif
}
protected:
uint32_t nextId_;
boost::shared_ptr<EventLog> log_;
};
class ProcessorEventHandler : public TProcessorEventHandler {
public:
ProcessorEventHandler(const boost::shared_ptr<EventLog>& log) : nextId_(1), log_(log) {}
void* getContext(const char* fnName, void* serverContext) {
ConnContext* connContext = reinterpret_cast<ConnContext*>(serverContext);
CallContext* context = new CallContext(connContext, nextId_, fnName);
++nextId_;
log_->append(EventLog::ET_CALL_STARTED, connContext->id, context->id, fnName);
return context;
}
void freeContext(void* ctx, const char* fnName) {
CallContext* context = reinterpret_cast<CallContext*>(ctx);
checkName(context, fnName);
log_->append(EventLog::ET_CALL_FINISHED, context->connContext->id, context->id, fnName);
delete context;
}
void preRead(void* ctx, const char* fnName) {
CallContext* context = reinterpret_cast<CallContext*>(ctx);
checkName(context, fnName);
log_->append(EventLog::ET_PRE_READ, context->connContext->id, context->id, fnName);
}
void postRead(void* ctx, const char* fnName, uint32_t bytes) {
THRIFT_UNUSED_VARIABLE(bytes);
CallContext* context = reinterpret_cast<CallContext*>(ctx);
checkName(context, fnName);
log_->append(EventLog::ET_POST_READ, context->connContext->id, context->id, fnName);
}
void preWrite(void* ctx, const char* fnName) {
CallContext* context = reinterpret_cast<CallContext*>(ctx);
checkName(context, fnName);
log_->append(EventLog::ET_PRE_WRITE, context->connContext->id, context->id, fnName);
}
void postWrite(void* ctx, const char* fnName, uint32_t bytes) {
THRIFT_UNUSED_VARIABLE(bytes);
CallContext* context = reinterpret_cast<CallContext*>(ctx);
checkName(context, fnName);
log_->append(EventLog::ET_POST_WRITE, context->connContext->id, context->id, fnName);
}
void asyncComplete(void* ctx, const char* fnName) {
CallContext* context = reinterpret_cast<CallContext*>(ctx);
checkName(context, fnName);
log_->append(EventLog::ET_ASYNC_COMPLETE, context->connContext->id, context->id, fnName);
}
void handlerError(void* ctx, const char* fnName) {
CallContext* context = reinterpret_cast<CallContext*>(ctx);
checkName(context, fnName);
log_->append(EventLog::ET_HANDLER_ERROR, context->connContext->id, context->id, fnName);
}
protected:
void checkName(const CallContext* context, const char* fnName) {
// Note: we can't use BOOST_CHECK_EQUAL here, since the handler runs in a
// different thread from the test functions. Just abort if the names are
// different
if (context->name != fnName) {
fprintf(stderr,
"call context name mismatch: \"%s\" != \"%s\"\n",
context->name.c_str(),
fnName);
fflush(stderr);
abort();
}
}
uint32_t nextId_;
boost::shared_ptr<EventLog> log_;
};
}
}
} // apache::thrift::test
#endif // _THRIFT_PROCESSOR_TEST_HANDLERS_H_

View file

@ -0,0 +1,927 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* This file contains tests that ensure TProcessorEventHandler and
* TServerEventHandler are invoked properly by the various server
* implementations.
*/
#include <boost/test/unit_test.hpp>
#include <thrift/concurrency/PlatformThreadFactory.h>
#include <thrift/concurrency/Monitor.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TThreadedServer.h>
#include <thrift/server/TThreadPoolServer.h>
#include <thrift/server/TNonblockingServer.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TSocket.h>
#include "EventLog.h"
#include "ServerThread.h"
#include "Handlers.h"
#include "gen-cpp/ChildService.h"
using namespace std;
using namespace boost;
using namespace apache::thrift;
using namespace apache::thrift::concurrency;
using namespace apache::thrift::protocol;
using namespace apache::thrift::server;
using namespace apache::thrift::transport;
using namespace apache::thrift::test;
/*
* Traits classes that encapsulate how to create various types of servers.
*/
class TSimpleServerTraits {
public:
typedef TSimpleServer ServerType;
boost::shared_ptr<TSimpleServer> createServer(
const boost::shared_ptr<TProcessor>& processor,
uint16_t port,
const boost::shared_ptr<TTransportFactory>& transportFactory,
const boost::shared_ptr<TProtocolFactory>& protocolFactory) {
boost::shared_ptr<TServerSocket> socket(new TServerSocket(port));
return boost::shared_ptr<TSimpleServer>(
new TSimpleServer(processor, socket, transportFactory, protocolFactory));
}
};
class TThreadedServerTraits {
public:
typedef TThreadedServer ServerType;
boost::shared_ptr<TThreadedServer> createServer(
const boost::shared_ptr<TProcessor>& processor,
uint16_t port,
const boost::shared_ptr<TTransportFactory>& transportFactory,
const boost::shared_ptr<TProtocolFactory>& protocolFactory) {
boost::shared_ptr<TServerSocket> socket(new TServerSocket(port));
return boost::shared_ptr<TThreadedServer>(
new TThreadedServer(processor, socket, transportFactory, protocolFactory));
}
};
class TThreadPoolServerTraits {
public:
typedef TThreadPoolServer ServerType;
boost::shared_ptr<TThreadPoolServer> createServer(
const boost::shared_ptr<TProcessor>& processor,
uint16_t port,
const boost::shared_ptr<TTransportFactory>& transportFactory,
const boost::shared_ptr<TProtocolFactory>& protocolFactory) {
boost::shared_ptr<TServerSocket> socket(new TServerSocket(port));
boost::shared_ptr<PlatformThreadFactory> threadFactory(new PlatformThreadFactory);
boost::shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(8);
threadManager->threadFactory(threadFactory);
threadManager->start();
return boost::shared_ptr<TThreadPoolServer>(
new TThreadPoolServer(processor, socket, transportFactory, protocolFactory, threadManager));
}
};
class TNonblockingServerTraits {
public:
typedef TNonblockingServer ServerType;
boost::shared_ptr<TNonblockingServer> createServer(
const boost::shared_ptr<TProcessor>& processor,
uint16_t port,
const boost::shared_ptr<TTransportFactory>& transportFactory,
const boost::shared_ptr<TProtocolFactory>& protocolFactory) {
// TNonblockingServer automatically uses TFramedTransport.
// Raise an exception if the supplied transport factory is not a
// TFramedTransportFactory
TFramedTransportFactory* framedFactory
= dynamic_cast<TFramedTransportFactory*>(transportFactory.get());
if (framedFactory == NULL) {
throw TException("TNonblockingServer must use TFramedTransport");
}
boost::shared_ptr<PlatformThreadFactory> threadFactory(new PlatformThreadFactory);
boost::shared_ptr<ThreadManager> threadManager = ThreadManager::newSimpleThreadManager(8);
threadManager->threadFactory(threadFactory);
threadManager->start();
return boost::shared_ptr<TNonblockingServer>(
new TNonblockingServer(processor, protocolFactory, port, threadManager));
}
};
class TNonblockingServerNoThreadsTraits {
public:
typedef TNonblockingServer ServerType;
boost::shared_ptr<TNonblockingServer> createServer(
const boost::shared_ptr<TProcessor>& processor,
uint16_t port,
const boost::shared_ptr<TTransportFactory>& transportFactory,
const boost::shared_ptr<TProtocolFactory>& protocolFactory) {
// TNonblockingServer automatically uses TFramedTransport.
// Raise an exception if the supplied transport factory is not a
// TFramedTransportFactory
TFramedTransportFactory* framedFactory
= dynamic_cast<TFramedTransportFactory*>(transportFactory.get());
if (framedFactory == NULL) {
throw TException("TNonblockingServer must use TFramedTransport");
}
// Use a NULL ThreadManager
boost::shared_ptr<ThreadManager> threadManager;
return boost::shared_ptr<TNonblockingServer>(
new TNonblockingServer(processor, protocolFactory, port, threadManager));
}
};
/*
* Traits classes for controlling if we instantiate templated or generic
* protocol factories, processors, clients, etc.
*
* The goal is to allow the outer test code to select which server type is
* being tested, and whether or not we are testing the templated classes, or
* the generic classes.
*
* Each specific test case can control whether we create a child or parent
* server, and whether we use TFramedTransport or TBufferedTransport.
*/
class UntemplatedTraits {
public:
typedef TBinaryProtocolFactory ProtocolFactory;
typedef TBinaryProtocol Protocol;
typedef ParentServiceProcessor ParentProcessor;
typedef ChildServiceProcessor ChildProcessor;
typedef ParentServiceClient ParentClient;
typedef ChildServiceClient ChildClient;
};
class TemplatedTraits {
public:
typedef TBinaryProtocolFactoryT<TBufferBase> ProtocolFactory;
typedef TBinaryProtocolT<TBufferBase> Protocol;
typedef ParentServiceProcessorT<Protocol> ParentProcessor;
typedef ChildServiceProcessorT<Protocol> ChildProcessor;
typedef ParentServiceClientT<Protocol> ParentClient;
typedef ChildServiceClientT<Protocol> ChildClient;
};
template <typename TemplateTraits_>
class ParentServiceTraits {
public:
typedef typename TemplateTraits_::ParentProcessor Processor;
typedef typename TemplateTraits_::ParentClient Client;
typedef ParentHandler Handler;
typedef typename TemplateTraits_::ProtocolFactory ProtocolFactory;
typedef typename TemplateTraits_::Protocol Protocol;
};
template <typename TemplateTraits_>
class ChildServiceTraits {
public:
typedef typename TemplateTraits_::ChildProcessor Processor;
typedef typename TemplateTraits_::ChildClient Client;
typedef ChildHandler Handler;
typedef typename TemplateTraits_::ProtocolFactory ProtocolFactory;
typedef typename TemplateTraits_::Protocol Protocol;
};
// TODO: It would be nicer if the TTransportFactory types defined a typedef,
// to allow us to figure out the exact transport type without having to pass it
// in as a separate template parameter here.
//
// It would also be niec if they used covariant return types. Unfortunately,
// since they return shared_ptr instead of raw pointers, covariant return types
// won't work.
template <typename ServerTraits_,
typename ServiceTraits_,
typename TransportFactory_ = TFramedTransportFactory,
typename Transport_ = TFramedTransport>
class ServiceState : public ServerState {
public:
typedef typename ServiceTraits_::Processor Processor;
typedef typename ServiceTraits_::Client Client;
typedef typename ServiceTraits_::Handler Handler;
ServiceState()
: port_(0),
log_(new EventLog),
handler_(new Handler(log_)),
processor_(new Processor(handler_)),
transportFactory_(new TransportFactory_),
protocolFactory_(new typename ServiceTraits_::ProtocolFactory),
serverEventHandler_(new ServerEventHandler(log_)),
processorEventHandler_(new ProcessorEventHandler(log_)) {
processor_->setEventHandler(processorEventHandler_);
}
boost::shared_ptr<TServer> createServer(uint16_t port) {
ServerTraits_ serverTraits;
return serverTraits.createServer(processor_, port, transportFactory_, protocolFactory_);
}
boost::shared_ptr<TServerEventHandler> getServerEventHandler() { return serverEventHandler_; }
void bindSuccessful(uint16_t port) { port_ = port; }
uint16_t getPort() const { return port_; }
const boost::shared_ptr<EventLog>& getLog() const { return log_; }
const boost::shared_ptr<Handler>& getHandler() const { return handler_; }
boost::shared_ptr<Client> createClient() {
typedef typename ServiceTraits_::Protocol Protocol;
boost::shared_ptr<TSocket> socket(new TSocket("127.0.0.1", port_));
boost::shared_ptr<Transport_> transport(new Transport_(socket));
boost::shared_ptr<Protocol> protocol(new Protocol(transport));
transport->open();
boost::shared_ptr<Client> client(new Client(protocol));
return client;
}
private:
uint16_t port_;
boost::shared_ptr<EventLog> log_;
boost::shared_ptr<Handler> handler_;
boost::shared_ptr<Processor> processor_;
boost::shared_ptr<TTransportFactory> transportFactory_;
boost::shared_ptr<TProtocolFactory> protocolFactory_;
boost::shared_ptr<TServerEventHandler> serverEventHandler_;
boost::shared_ptr<TProcessorEventHandler> processorEventHandler_;
};
/**
* Check that there are no more events in the log
*/
void checkNoEvents(const boost::shared_ptr<EventLog>& log) {
// Wait for an event with a very short timeout period. We don't expect
// anything to be present, so we will normally wait for the full timeout.
// On the other hand, a non-zero timeout is nice since it does give a short
// window for events to arrive in case there is a problem.
Event event = log->waitForEvent(10);
BOOST_CHECK_EQUAL(EventLog::ET_LOG_END, event.type);
}
/**
* Check for the events that should be logged when a new connection is created.
*
* Returns the connection ID allocated by the server.
*/
uint32_t checkNewConnEvents(const boost::shared_ptr<EventLog>& log) {
// Check for an ET_CONN_CREATED event
Event event = log->waitForEvent(2500);
BOOST_CHECK_EQUAL(EventLog::ET_CONN_CREATED, event.type);
// Some servers call the processContext() hook immediately.
// Others (TNonblockingServer) only call it once a full request is received.
// We don't check for it yet, to allow either behavior.
return event.connectionId;
}
/**
* Check for the events that should be logged when a connection is closed.
*/
void checkCloseEvents(const boost::shared_ptr<EventLog>& log, uint32_t connId) {
// Check for an ET_CONN_DESTROYED event
Event event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_CONN_DESTROYED, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
// Make sure there are no more events
checkNoEvents(log);
}
/**
* Check for the events that should be logged when a call is received
* and the handler is invoked.
*
* It does not check for anything after the handler invocation.
*
* Returns the call ID allocated by the server.
*/
uint32_t checkCallHandlerEvents(const boost::shared_ptr<EventLog>& log,
uint32_t connId,
EventType callType,
const string& callName) {
// Call started
Event event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_CALL_STARTED, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callName, event.message);
uint32_t callId = event.callId;
// Pre-read
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_PRE_READ, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
BOOST_CHECK_EQUAL(callName, event.message);
// Post-read
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_POST_READ, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
BOOST_CHECK_EQUAL(callName, event.message);
// Handler invocation
event = log->waitForEvent();
BOOST_CHECK_EQUAL(callType, event.type);
// The handler doesn't have any connection or call context,
// so the connectionId and callId in this event aren't valid
return callId;
}
/**
* Check for the events that should be after a handler returns.
*/
void checkCallPostHandlerEvents(const boost::shared_ptr<EventLog>& log,
uint32_t connId,
uint32_t callId,
const string& callName) {
// Pre-write
Event event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_PRE_WRITE, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
BOOST_CHECK_EQUAL(callName, event.message);
// Post-write
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_POST_WRITE, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
BOOST_CHECK_EQUAL(callName, event.message);
// Call finished
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
BOOST_CHECK_EQUAL(callName, event.message);
// It is acceptable for servers to call processContext() again immediately
// to start waiting on the next request. However, some servers wait before
// getting either a partial request or the full request before calling
// processContext(). We don't check for the next call to processContext()
// yet.
}
/**
* Check for the events that should be logged when a call is made.
*
* This just calls checkCallHandlerEvents() followed by
* checkCallPostHandlerEvents().
*
* Returns the call ID allocated by the server.
*/
uint32_t checkCallEvents(const boost::shared_ptr<EventLog>& log,
uint32_t connId,
EventType callType,
const string& callName) {
uint32_t callId = checkCallHandlerEvents(log, connId, callType, callName);
checkCallPostHandlerEvents(log, connId, callId, callName);
return callId;
}
/*
* Test functions
*/
template <typename State_>
void testParentService(const boost::shared_ptr<State_>& state) {
boost::shared_ptr<typename State_::Client> client = state->createClient();
int32_t gen = client->getGeneration();
int32_t newGen = client->incrementGeneration();
BOOST_CHECK_EQUAL(gen + 1, newGen);
newGen = client->getGeneration();
BOOST_CHECK_EQUAL(gen + 1, newGen);
client->addString("foo");
client->addString("bar");
client->addString("asdf");
vector<string> strings;
client->getStrings(strings);
BOOST_REQUIRE_EQUAL(3, strings.size());
BOOST_REQUIRE_EQUAL("foo", strings[0]);
BOOST_REQUIRE_EQUAL("bar", strings[1]);
BOOST_REQUIRE_EQUAL("asdf", strings[2]);
}
template <typename State_>
void testChildService(const boost::shared_ptr<State_>& state) {
boost::shared_ptr<typename State_::Client> client = state->createClient();
// Test calling some of the parent methids via the a child client
int32_t gen = client->getGeneration();
int32_t newGen = client->incrementGeneration();
BOOST_CHECK_EQUAL(gen + 1, newGen);
newGen = client->getGeneration();
BOOST_CHECK_EQUAL(gen + 1, newGen);
// Test some of the child methods
client->setValue(10);
BOOST_CHECK_EQUAL(10, client->getValue());
BOOST_CHECK_EQUAL(10, client->setValue(99));
BOOST_CHECK_EQUAL(99, client->getValue());
}
template <typename ServerTraits, typename TemplateTraits>
void testBasicService() {
typedef ServiceState<ServerTraits, ParentServiceTraits<TemplateTraits> > State;
// Start the server
boost::shared_ptr<State> state(new State);
ServerThread serverThread(state, true);
testParentService(state);
}
template <typename ServerTraits, typename TemplateTraits>
void testInheritedService() {
typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
// Start the server
boost::shared_ptr<State> state(new State);
ServerThread serverThread(state, true);
testParentService(state);
testChildService(state);
}
/**
* Test to make sure that the TServerEventHandler and TProcessorEventHandler
* methods are invoked in the correct order with the actual events.
*/
template <typename ServerTraits, typename TemplateTraits>
void testEventSequencing() {
// We use TBufferedTransport for this test, instead of TFramedTransport.
// This way the server will start processing data as soon as it is received,
// instead of waiting for the full request. This is necessary so we can
// separate the preRead() and postRead() events.
typedef ServiceState<ServerTraits,
ChildServiceTraits<TemplateTraits>,
TBufferedTransportFactory,
TBufferedTransport> State;
// Start the server
boost::shared_ptr<State> state(new State);
ServerThread serverThread(state, true);
const boost::shared_ptr<EventLog>& log = state->getLog();
// Make sure we're at the end of the log
checkNoEvents(log);
state->getHandler()->prepareTriggeredCall();
// Make sure createContext() is called after a connection has been
// established. We open a plain socket instead of creating a client.
boost::shared_ptr<TSocket> socket(new TSocket("127.0.0.1", state->getPort()));
socket->open();
// Make sure the proper events occurred after a new connection
uint32_t connId = checkNewConnEvents(log);
// Send a message header. We manually construct the request so that we
// can test the timing for the preRead() call.
string requestName = "getDataWait";
string eventName = "ParentService.getDataWait";
int32_t seqid = int32_t(time(NULL));
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
std::string responseName;
int32_t responseSeqid = 0;
apache::thrift::protocol::TMessageType responseType;
protocol.readMessageBegin(responseName, responseType, responseSeqid);
BOOST_CHECK_EQUAL(responseSeqid, seqid);
BOOST_CHECK_EQUAL(requestName, responseName);
BOOST_CHECK_EQUAL(responseType, T_REPLY);
// Read the body. We just ignore it for now.
protocol.skip(T_STRUCT);
// Now that we have read, the server should have finished sending the data
// and called the postWrite() handler
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_POST_WRITE, event.type);
BOOST_CHECK_EQUAL(eventName, event.message);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
// Call finished should be last
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
BOOST_CHECK_EQUAL(eventName, event.message);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
// There should be no more events
checkNoEvents(log);
// Close the connection, and make sure we get a connection destroyed event
socket->close();
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_CONN_DESTROYED, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
// There should be no more events
checkNoEvents(log);
}
template <typename ServerTraits, typename TemplateTraits>
void testSeparateConnections() {
typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
// Start the server
boost::shared_ptr<State> state(new State);
ServerThread serverThread(state, true);
const boost::shared_ptr<EventLog>& log = state->getLog();
// Create a client
boost::shared_ptr<typename State::Client> client1 = state->createClient();
// Make sure the expected events were logged
uint32_t client1Id = checkNewConnEvents(log);
// Create a second client
boost::shared_ptr<typename State::Client> client2 = state->createClient();
// Make sure the expected events were logged
uint32_t client2Id = checkNewConnEvents(log);
// The two connections should have different IDs
BOOST_CHECK_NE(client1Id, client2Id);
// Make a call, and check for the proper events
int32_t value = 5;
client1->setValue(value);
uint32_t call1
= checkCallEvents(log, client1Id, EventLog::ET_CALL_SET_VALUE, "ChildService.setValue");
// Make a call with client2
int32_t v = client2->getValue();
BOOST_CHECK_EQUAL(value, v);
checkCallEvents(log, client2Id, EventLog::ET_CALL_GET_VALUE, "ChildService.getValue");
// Make another call with client1
v = client1->getValue();
BOOST_CHECK_EQUAL(value, v);
uint32_t call2
= checkCallEvents(log, client1Id, EventLog::ET_CALL_GET_VALUE, "ChildService.getValue");
BOOST_CHECK_NE(call1, call2);
// Close the second client, and check for the appropriate events
client2.reset();
checkCloseEvents(log, client2Id);
}
template <typename ServerTraits, typename TemplateTraits>
void testOnewayCall() {
typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
// Start the server
boost::shared_ptr<State> state(new State);
ServerThread serverThread(state, true);
const boost::shared_ptr<EventLog>& log = state->getLog();
// Create a client
boost::shared_ptr<typename State::Client> client = state->createClient();
uint32_t connId = checkNewConnEvents(log);
// Make a oneway call
// It should return immediately, even though the server's handler
// won't return right away
state->getHandler()->prepareTriggeredCall();
client->onewayWait();
string callName = "ParentService.onewayWait";
uint32_t callId = checkCallHandlerEvents(log, connId, EventLog::ET_CALL_ONEWAY_WAIT, callName);
// There shouldn't be any more events
checkNoEvents(log);
// Trigger the handler to return
state->getHandler()->triggerPendingCalls();
// The handler will log an ET_WAIT_RETURN event when it wakes up
Event event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
// Now we should see the async complete event, then call finished
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_ASYNC_COMPLETE, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
BOOST_CHECK_EQUAL(callName, event.message);
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
BOOST_CHECK_EQUAL(callName, event.message);
// Destroy the client, and check for connection closed events
client.reset();
checkCloseEvents(log, connId);
checkNoEvents(log);
}
template <typename ServerTraits, typename TemplateTraits>
void testExpectedError() {
typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
// Start the server
boost::shared_ptr<State> state(new State);
ServerThread serverThread(state, true);
const boost::shared_ptr<EventLog>& log = state->getLog();
// Create a client
boost::shared_ptr<typename State::Client> client = state->createClient();
uint32_t connId = checkNewConnEvents(log);
// Send the exceptionWait() call
state->getHandler()->prepareTriggeredCall();
string message = "test 1234 test";
client->send_exceptionWait(message);
string callName = "ParentService.exceptionWait";
uint32_t callId = checkCallHandlerEvents(log, connId, EventLog::ET_CALL_EXCEPTION_WAIT, callName);
// There shouldn't be any more events
checkNoEvents(log);
// Trigger the handler to return
state->getHandler()->triggerPendingCalls();
// The handler will log an ET_WAIT_RETURN event when it wakes up
Event event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
// Now receive the response
try {
client->recv_exceptionWait();
BOOST_FAIL("expected MyError to be thrown");
} catch (const MyError& e) {
BOOST_CHECK_EQUAL(message, e.message);
// Check if std::exception::what() is handled properly
size_t message_pos = std::string(e.what()).find("TException - service has thrown: MyError");
BOOST_CHECK_NE(message_pos, std::string::npos);
}
// Now we should see the events for a normal call finish
checkCallPostHandlerEvents(log, connId, callId, callName);
// There shouldn't be any more events
checkNoEvents(log);
// Destroy the client, and check for connection closed events
client.reset();
checkCloseEvents(log, connId);
checkNoEvents(log);
}
template <typename ServerTraits, typename TemplateTraits>
void testUnexpectedError() {
typedef ServiceState<ServerTraits, ChildServiceTraits<TemplateTraits> > State;
// Start the server
boost::shared_ptr<State> state(new State);
ServerThread serverThread(state, true);
const boost::shared_ptr<EventLog>& log = state->getLog();
// Create a client
boost::shared_ptr<typename State::Client> client = state->createClient();
uint32_t connId = checkNewConnEvents(log);
// Send the unexpectedExceptionWait() call
state->getHandler()->prepareTriggeredCall();
string message = "1234 test 5678";
client->send_unexpectedExceptionWait(message);
string callName = "ParentService.unexpectedExceptionWait";
uint32_t callId
= checkCallHandlerEvents(log, connId, EventLog::ET_CALL_UNEXPECTED_EXCEPTION_WAIT, callName);
// There shouldn't be any more events
checkNoEvents(log);
// Trigger the handler to return
state->getHandler()->triggerPendingCalls();
// The handler will log an ET_WAIT_RETURN event when it wakes up
Event event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_WAIT_RETURN, event.type);
// Now receive the response
try {
client->recv_unexpectedExceptionWait();
BOOST_FAIL("expected TApplicationError to be thrown");
} catch (const TApplicationException&) {
}
// Now we should see a handler error event
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_HANDLER_ERROR, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
BOOST_CHECK_EQUAL(callName, event.message);
// pre-write and post-write events aren't generated after a handler error
// (Even for non-oneway calls where a response is written.)
//
// A call finished event is logged when the call context is destroyed
event = log->waitForEvent();
BOOST_CHECK_EQUAL(EventLog::ET_CALL_FINISHED, event.type);
BOOST_CHECK_EQUAL(connId, event.connectionId);
BOOST_CHECK_EQUAL(callId, event.callId);
BOOST_CHECK_EQUAL(callName, event.message);
// There shouldn't be any more events
checkNoEvents(log);
// Destroy the client, and check for connection closed events
client.reset();
checkCloseEvents(log, connId);
checkNoEvents(log);
}
// Macro to define simple tests that can be used with all server types
#define DEFINE_SIMPLE_TESTS(Server, Template) \
BOOST_AUTO_TEST_CASE(Server##_##Template##_basicService) { \
testBasicService<Server##Traits, Template##Traits>(); \
} \
BOOST_AUTO_TEST_CASE(Server##_##Template##_inheritedService) { \
testInheritedService<Server##Traits, Template##Traits>(); \
} \
BOOST_AUTO_TEST_CASE(Server##_##Template##_oneway) { \
testOnewayCall<Server##Traits, Template##Traits>(); \
} \
BOOST_AUTO_TEST_CASE(Server##_##Template##_exception) { \
testExpectedError<Server##Traits, Template##Traits>(); \
} \
BOOST_AUTO_TEST_CASE(Server##_##Template##_unexpectedException) { \
testUnexpectedError<Server##Traits, Template##Traits>(); \
}
// Tests that require the server to process multiple connections concurrently
// (i.e., not TSimpleServer)
#define DEFINE_CONCURRENT_SERVER_TESTS(Server, Template) \
BOOST_AUTO_TEST_CASE(Server##_##Template##_separateConnections) { \
testSeparateConnections<Server##Traits, Template##Traits>(); \
}
// The testEventSequencing() test manually generates a request for the server,
// and doesn't work with TFramedTransport. Therefore we can't test it with
// TNonblockingServer.
#define DEFINE_NOFRAME_TESTS(Server, Template) \
BOOST_AUTO_TEST_CASE(Server##_##Template##_eventSequencing) { \
testEventSequencing<Server##Traits, Template##Traits>(); \
}
#define DEFINE_TNONBLOCKINGSERVER_TESTS(Server, Template) \
DEFINE_SIMPLE_TESTS(Server, Template) \
DEFINE_CONCURRENT_SERVER_TESTS(Server, Template)
#define DEFINE_ALL_SERVER_TESTS(Server, Template) \
DEFINE_SIMPLE_TESTS(Server, Template) \
DEFINE_CONCURRENT_SERVER_TESTS(Server, Template) \
DEFINE_NOFRAME_TESTS(Server, Template)
DEFINE_ALL_SERVER_TESTS(TThreadedServer, Templated)
DEFINE_ALL_SERVER_TESTS(TThreadedServer, Untemplated)
DEFINE_ALL_SERVER_TESTS(TThreadPoolServer, Templated)
DEFINE_ALL_SERVER_TESTS(TThreadPoolServer, Untemplated)
DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServer, Templated)
DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServer, Untemplated)
DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServerNoThreads, Templated)
DEFINE_TNONBLOCKINGSERVER_TESTS(TNonblockingServerNoThreads, Untemplated)
DEFINE_SIMPLE_TESTS(TSimpleServer, Templated)
DEFINE_SIMPLE_TESTS(TSimpleServer, Untemplated)
DEFINE_NOFRAME_TESTS(TSimpleServer, Templated)
DEFINE_NOFRAME_TESTS(TSimpleServer, Untemplated)
// TODO: We should test TEventServer in the future.
// For now, it is known not to work correctly with TProcessorEventHandler.
#ifdef BOOST_TEST_DYN_LINK
bool init_unit_test_suite() {
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
unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
THRIFT_UNUSED_VARIABLE(argc);
THRIFT_UNUSED_VARIABLE(argv);
unit_test::framework::master_test_suite().p_name.value = "ProcessorTest";
return NULL;
}
#endif

View file

@ -0,0 +1,150 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef _THRIFT_TEST_SERVERTHREAD_TCC_
#define _THRIFT_TEST_SERVERTHREAD_TCC_ 1
#include "ServerThread.h"
#include <thrift/concurrency/PlatformThreadFactory.h>
#include <thrift/concurrency/ThreadManager.h>
#include <thrift/server/TThreadPoolServer.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/transport/TServerSocket.h>
namespace apache {
namespace thrift {
namespace test {
void ServerThread::start() {
assert(!running_);
running_ = true;
// Start the other thread
concurrency::PlatformThreadFactory 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)
boost::shared_ptr<server::TServerEventHandler> serverEventHandler
= serverState_->getServerEventHandler();
server_->setServerEventHandler(serverEventHandler);
// Notify the main thread that we have successfully started serving requests
concurrency::Synchronized s(serverMonitor_);
serving_ = true;
serverMonitor_.notify();
// Invoke preServe() on the real event handler, since we ate
// the original preServe() event.
if (serverEventHandler) {
serverEventHandler->preServe();
}
}
}
}
} // apache::thrift::test
#endif // _THRIFT_TEST_SERVERTHREAD_TCC_

View file

@ -0,0 +1,136 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef _THRIFT_TEST_SERVERTHREAD_H_
#define _THRIFT_TEST_SERVERTHREAD_H_ 1
#include <thrift/TProcessor.h>
#include <thrift/protocol/TProtocol.h>
#include <thrift/server/TServer.h>
#include <thrift/transport/TTransport.h>
#include "EventLog.h"
namespace apache {
namespace thrift {
namespace test {
/**
* A helper class to tell ServerThread how to create the server
*/
class ServerState {
public:
virtual ~ServerState() {}
/**
* 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 boost::shared_ptr<server::TServer> createServer(uint16_t port) = 0;
/**
* Get the TServerEventHandler to set on the server.
*
* This is only called after the server successfully binds and is about to
* start serving traffic. It is invoked from the server thread, rather than
* the main thread.
*/
virtual boost::shared_ptr<server::TServerEventHandler> getServerEventHandler() {
return boost::shared_ptr<server::TServerEventHandler>();
}
/**
* This method is called in the server thread after server binding succeeds.
*
* Subclasses may override this method if they wish to record the final
* port that was used for the server.
*/
virtual void bindSuccessful(uint16_t /*port*/) {}
};
/**
* ServerThread starts a thrift server running in a separate thread.
*/
class ServerThread {
public:
ServerThread(const boost::shared_ptr<ServerState>& state, bool autoStart)
: helper_(new Helper(this)),
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() { serverThread_->run(); }
void preServe() { serverThread_->preServe(); }
private:
ServerThread* serverThread_;
};
void run();
void preServe();
boost::shared_ptr<Helper> helper_;
uint16_t port_;
bool running_;
bool serving_;
bool error_;
concurrency::Monitor serverMonitor_;
boost::shared_ptr<ServerState> serverState_;
boost::shared_ptr<server::TServer> server_;
boost::shared_ptr<concurrency::Thread> thread_;
};
}
}
} // apache::thrift::test
#endif // _THRIFT_TEST_SERVERTHREAD_H_

View file

@ -0,0 +1,22 @@
namespace cpp apache.thrift.test
exception MyError {
1: string message
}
service ParentService {
i32 incrementGeneration()
i32 getGeneration()
void addString(1: string s)
list<string> getStrings()
binary getDataWait(1: i32 length)
oneway void onewayWait()
void exceptionWait(1: string message) throws (2: MyError error)
void unexpectedExceptionWait(1: string message)
}
service ChildService extends ParentService {
i32 setValue(1: i32 value)
i32 getValue()
}

View file

@ -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)

View file

@ -0,0 +1,114 @@
#define BOOST_TEST_MODULE TQTcpServerTest
#include <QTest>
#include <iostream>
#include <QTcpServer>
#include <QTcpSocket>
#include <QHostAddress>
#include <QThread>
#ifndef Q_MOC_RUN
#include <boost/smart_ptr.hpp>
#include "thrift/protocol/TBinaryProtocol.h"
#include "thrift/async/TAsyncProcessor.h"
#include "thrift/qt/TQTcpServer.h"
#include "thrift/qt/TQIODeviceTransport.h"
#include "gen-cpp/ParentService.h"
#endif
using namespace apache::thrift;
struct AsyncHandler : public test::ParentServiceCobSvIf {
std::vector<std::string> strings;
virtual void addString(tcxx::function<void()> cob, const std::string& s) {
strings.push_back(s);
cob();
}
virtual void getStrings(tcxx::function<void(std::vector<std::string> const& _return)> cob) {
cob(strings);
}
// Overrides not used in this test
virtual void incrementGeneration(tcxx::function<void(int32_t const& _return)> cob) {}
virtual void getGeneration(tcxx::function<void(int32_t const& _return)> cob) {}
virtual void getDataWait(tcxx::function<void(std::string const& _return)> cob,
const int32_t length) {}
virtual void onewayWait(tcxx::function<void()> cob) {}
virtual void exceptionWait(
tcxx::function<void()> cob,
tcxx::function<void(::apache::thrift::TDelayedException* _throw)> /* exn_cob */,
const std::string& message) {}
virtual void unexpectedExceptionWait(tcxx::function<void()> cob, const std::string& message) {}
};
class TQTcpServerTest : public QObject {
Q_OBJECT
private slots:
void initTestCase();
void cleanupTestCase();
void test_communicate();
private:
boost::shared_ptr<QThread> serverThread;
boost::shared_ptr<async::TQTcpServer> server;
boost::shared_ptr<test::ParentServiceClient> client;
};
void TQTcpServerTest::initTestCase() {
// setup server
boost::shared_ptr<QTcpServer> serverSocket = boost::make_shared<QTcpServer>();
server.reset(new async::TQTcpServer(serverSocket,
boost::make_shared<test::ParentServiceAsyncProcessor>(
boost::make_shared<AsyncHandler>()),
boost::make_shared<protocol::TBinaryProtocolFactory>()));
QVERIFY(serverSocket->listen(QHostAddress::LocalHost));
int port = serverSocket->serverPort();
QVERIFY(port > 0);
//setup server thread and move server to it
serverThread.reset(new QThread());
serverSocket->moveToThread(serverThread.get());
server->moveToThread(serverThread.get());
serverThread->start();
// setup client
boost::shared_ptr<QTcpSocket> socket = boost::make_shared<QTcpSocket>();
client.reset(new test::ParentServiceClient(boost::make_shared<protocol::TBinaryProtocol>(
boost::make_shared<transport::TQIODeviceTransport>(socket))));
socket->connectToHost(QHostAddress::LocalHost, port);
QVERIFY(socket->waitForConnected());
}
void TQTcpServerTest::cleanupTestCase() {
//first, stop the thread which holds the server
serverThread->quit();
serverThread->wait();
// now, it is safe to delete the server
server.reset();
// delete thread now
serverThread.reset();
// cleanup client
client.reset();
}
void TQTcpServerTest::test_communicate() {
client->addString("foo");
client->addString("bar");
std::vector<std::string> reply;
client->getStrings(reply);
QCOMPARE(QString::fromStdString(reply[0]), QString("foo"));
QCOMPARE(QString::fromStdString(reply[1]), QString("bar"));
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
QTEST_GUILESS_MAIN(TQTcpServerTest);
#else
#undef QT_GUI_LIB
QTEST_MAIN(TQTcpServerTest);
#endif
#include "TQTcpServerTest.moc"