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