blob: 48ea913d14d5617ab9ea7dc1be48ad9fcec9aaaf [file] [log] [blame]
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +09001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#define BOOST_TEST_MODULE TNonblockingServerTest
21#include <boost/test/unit_test.hpp>
22#include <boost/smart_ptr.hpp>
23
24#include "thrift/concurrency/Thread.h"
25#include "thrift/server/TNonblockingServer.h"
26
27#include "gen-cpp/ParentService.h"
28
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +090029#include <event.h>
30
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090031using namespace apache::thrift;
32
33struct Handler : public test::ParentServiceIf {
34 void addString(const std::string& s) { strings_.push_back(s); }
35 void getStrings(std::vector<std::string>& _return) { _return = strings_; }
36 std::vector<std::string> strings_;
37
38 // dummy overrides not used in this test
39 int32_t incrementGeneration() { return 0; }
40 int32_t getGeneration() { return 0; }
ben-craigfae08e72015-07-15 11:34:47 -050041 void getDataWait(std::string&, const int32_t) {}
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090042 void onewayWait() {}
43 void exceptionWait(const std::string&) {}
44 void unexpectedExceptionWait(const std::string&) {}
45};
46
47class Fixture {
48private:
Ben Craig7207c222015-07-06 08:40:35 -050049 struct Runner : public apache::thrift::concurrency::Runnable {
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090050 int port;
51 boost::shared_ptr<event_base> userEventBase;
52 boost::shared_ptr<TProcessor> processor;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090053 boost::shared_ptr<server::TNonblockingServer> server;
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090054
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090055 virtual void run() {
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090056 // When binding to explicit port, allow retrying to workaround bind failures on ports in use
57 int retryCount = port ? 10 : 0;
58 startServer(retryCount);
59 }
60
61 private:
62 void startServer(int retry_count) {
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090063 try {
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090064 server.reset(new server::TNonblockingServer(processor, port));
65 if (userEventBase) {
66 server->registerEvents(userEventBase.get());
67 }
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090068 server->serve();
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +090069 } catch (const transport::TTransportException&) {
70 if (retry_count > 0) {
71 ++port;
72 startServer(retry_count - 1);
73 } else {
74 throw;
75 }
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090076 }
77 }
78 };
79
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +090080 struct EventDeleter {
81 void operator()(event_base* p) { event_base_free(p); }
82 };
83
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +090084protected:
85 Fixture() : processor(new test::ParentServiceProcessor(boost::make_shared<Handler>())) {}
86
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +090087 ~Fixture() {
88 if (server) {
89 server->stop();
90 }
91 if (thread) {
92 thread->join();
93 }
94 }
95
96 void setEventBase(event_base* user_event_base) {
97 userEventBase_.reset(user_event_base, EventDeleter());
98 }
99
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900100 int startServer(int port) {
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900101 boost::shared_ptr<Runner> runner(new Runner);
102 runner->port = port;
103 runner->processor = processor;
104 runner->userEventBase = userEventBase_;
105
Ben Craig7207c222015-07-06 08:40:35 -0500106 boost::scoped_ptr<apache::thrift::concurrency::ThreadFactory> threadFactory(
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900107 new apache::thrift::concurrency::PlatformThreadFactory(
Nobuaki Sukegawa28256642014-12-16 03:24:37 +0900108#if !USE_BOOST_THREAD && !USE_STD_THREAD
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900109 concurrency::PlatformThreadFactory::OTHER, concurrency::PlatformThreadFactory::NORMAL,
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900110 1,
111#endif
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900112 false));
113 thread = threadFactory->newThread(runner);
114 thread->start();
115 // wait 100 ms for the server to begin listening
116 THRIFT_SLEEP_USEC(100000);
117 server = runner->server;
118 return runner->port;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900119 }
120
121 bool canCommunicate(int serverPort) {
122 boost::shared_ptr<transport::TSocket> socket(new transport::TSocket("localhost", serverPort));
123 socket->open();
124 test::ParentServiceClient client(boost::make_shared<protocol::TBinaryProtocol>(
125 boost::make_shared<transport::TFramedTransport>(socket)));
126 client.addString("foo");
127 std::vector<std::string> strings;
128 client.getStrings(strings);
129 return strings.size() == 1 && !(strings[0].compare("foo"));
130 }
131
132private:
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900133 boost::shared_ptr<event_base> userEventBase_;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900134 boost::shared_ptr<test::ParentServiceProcessor> processor;
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900135protected:
136 boost::shared_ptr<server::TNonblockingServer> server;
Nobuaki Sukegawa8cc91752016-05-15 00:24:41 +0900137private:
138 boost::shared_ptr<apache::thrift::concurrency::Thread> thread;
139
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900140};
141
142BOOST_AUTO_TEST_SUITE(TNonblockingServerTest)
143
144BOOST_FIXTURE_TEST_CASE(get_specified_port, Fixture) {
145 int specified_port = startServer(12345);
146 BOOST_REQUIRE_GE(specified_port, 12345);
147 BOOST_REQUIRE_EQUAL(server->getListenPort(), specified_port);
148 BOOST_CHECK(canCommunicate(specified_port));
149
150 server->stop();
151 BOOST_CHECK_EQUAL(server->getListenPort(), specified_port);
152}
153
154BOOST_FIXTURE_TEST_CASE(get_assigned_port, Fixture) {
155 int specified_port = startServer(0);
156 BOOST_REQUIRE_EQUAL(specified_port, 0);
157 int assigned_port = server->getListenPort();
158 BOOST_REQUIRE_NE(assigned_port, 0);
159 BOOST_CHECK(canCommunicate(assigned_port));
160
161 server->stop();
162 BOOST_CHECK_EQUAL(server->getListenPort(), 0);
163}
164
Nobuaki Sukegawa8016af82015-01-02 23:14:22 +0900165BOOST_FIXTURE_TEST_CASE(provide_event_base, Fixture) {
166 event_base* eb = event_base_new();
167 setEventBase(eb);
168 startServer(0);
169
170 // assert that the server works
171 BOOST_CHECK(canCommunicate(server->getListenPort()));
172#if LIBEVENT_VERSION_NUMBER > 0x02010400
173 // also assert that the event_base is actually used when it's easy
174 BOOST_CHECK_GT(event_base_get_num_events(eb, EVENT_BASE_COUNT_ADDED), 0);
175#endif
176}
177
Nobuaki Sukegawad0d7a652014-12-07 21:36:51 +0900178BOOST_AUTO_TEST_SUITE_END()