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