blob: a890aa8ce3e0c66dd93ec8ceba0f22bc2697fd4b [file] [log] [blame]
David Reiss35dc7692010-10-06 17:10:19 +00001/*
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 */
Marco Molteni83494252015-04-16 13:50:20 +020019
tpcwangf98d59f2016-03-23 16:18:52 -070020#include <thrift/thrift-config.h>
Marco Molteni83494252015-04-16 13:50:20 +020021
David Reiss35dc7692010-10-06 17:10:19 +000022#include <stdlib.h>
23#include <time.h>
Marco Molteni83494252015-04-16 13:50:20 +020024#ifdef HAVE_SYS_SOCKET_H
25#include <sys/socket.h>
26#endif
David Reiss35dc7692010-10-06 17:10:19 +000027#include <sstream>
Jake Farrell5d02b802014-01-07 21:42:01 -050028#include <fstream>
David Reiss35dc7692010-10-06 17:10:19 +000029
30#include <boost/mpl/list.hpp>
31#include <boost/shared_array.hpp>
32#include <boost/random.hpp>
33#include <boost/type_traits.hpp>
34#include <boost/test/unit_test.hpp>
Konrad Grochowskie9bdb412015-09-25 20:17:36 +020035#include <boost/version.hpp>
David Reiss35dc7692010-10-06 17:10:19 +000036
Roger Meier49ff8b12012-04-13 09:12:31 +000037#include <thrift/transport/TBufferTransports.h>
38#include <thrift/transport/TFDTransport.h>
39#include <thrift/transport/TFileTransport.h>
40#include <thrift/transport/TZlibTransport.h>
41#include <thrift/transport/TSocket.h>
David Reiss35dc7692010-10-06 17:10:19 +000042
Jake Farrell5d02b802014-01-07 21:42:01 -050043#include <thrift/concurrency/FunctionRunner.h>
44#if _WIN32
James E. King, III82ae9572017-08-05 12:23:54 -040045#include <thrift/transport/TPipe.h>
Konrad Grochowski16a23a62014-11-13 15:33:38 +010046#include <thrift/windows/TWinsockSingleton.h>
Jake Farrell5d02b802014-01-07 21:42:01 -050047#endif
48
David Reiss35dc7692010-10-06 17:10:19 +000049using namespace apache::thrift::transport;
James E. King, III82ae9572017-08-05 12:23:54 -040050using namespace apache::thrift;
David Reiss35dc7692010-10-06 17:10:19 +000051
52static boost::mt19937 rng;
David Reiss35dc7692010-10-06 17:10:19 +000053
David Reiss65e62d32010-10-06 17:10:35 +000054void initrand(unsigned int seed) {
David Reiss35dc7692010-10-06 17:10:19 +000055 rng.seed(seed);
56}
57
58class SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010059public:
Sebastian Zenker042580f2019-01-29 15:48:12 +010060 virtual ~SizeGenerator() = default;
David Reiss35dc7692010-10-06 17:10:19 +000061 virtual uint32_t nextSize() = 0;
62 virtual std::string describe() const = 0;
63};
64
65class ConstantSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010066public:
David Reiss35dc7692010-10-06 17:10:19 +000067 ConstantSizeGenerator(uint32_t value) : value_(value) {}
Sebastian Zenker042580f2019-01-29 15:48:12 +010068 uint32_t nextSize() override { return value_; }
69 std::string describe() const override {
David Reiss35dc7692010-10-06 17:10:19 +000070 std::ostringstream desc;
71 desc << value_;
72 return desc.str();
73 }
74
Konrad Grochowski16a23a62014-11-13 15:33:38 +010075private:
David Reiss35dc7692010-10-06 17:10:19 +000076 uint32_t value_;
77};
78
79class RandomSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010080public:
81 RandomSizeGenerator(uint32_t min, uint32_t max)
82 : generator_(rng, boost::uniform_int<int>(min, max)) {}
David Reiss35dc7692010-10-06 17:10:19 +000083
Sebastian Zenker042580f2019-01-29 15:48:12 +010084 uint32_t nextSize() override { return generator_(); }
David Reiss35dc7692010-10-06 17:10:19 +000085
Sebastian Zenker042580f2019-01-29 15:48:12 +010086 std::string describe() const override {
David Reiss35dc7692010-10-06 17:10:19 +000087 std::ostringstream desc;
88 desc << "rand(" << getMin() << ", " << getMax() << ")";
89 return desc.str();
90 }
91
Jake Farrell5d02b802014-01-07 21:42:01 -050092 uint32_t getMin() const { return (generator_.distribution().min)(); }
93 uint32_t getMax() const { return (generator_.distribution().max)(); }
David Reiss35dc7692010-10-06 17:10:19 +000094
Konrad Grochowski16a23a62014-11-13 15:33:38 +010095private:
96 boost::variate_generator<boost::mt19937&, boost::uniform_int<int> > generator_;
David Reiss35dc7692010-10-06 17:10:19 +000097};
98
99/**
100 * This class exists solely to make the TEST_RW() macro easier to use.
101 * - it can be constructed implicitly from an integer
102 * - it can contain either a ConstantSizeGenerator or a RandomSizeGenerator
103 * (TEST_RW can't take a SizeGenerator pointer or reference, since it needs
104 * to make a copy of the generator to bind it to the test function.)
105 */
106class GenericSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100107public:
108 GenericSizeGenerator(uint32_t value) : generator_(new ConstantSizeGenerator(value)) {}
109 GenericSizeGenerator(uint32_t min, uint32_t max)
110 : generator_(new RandomSizeGenerator(min, max)) {}
David Reiss35dc7692010-10-06 17:10:19 +0000111
Sebastian Zenker042580f2019-01-29 15:48:12 +0100112 uint32_t nextSize() override { return generator_->nextSize(); }
113 std::string describe() const override { return generator_->describe(); }
David Reiss35dc7692010-10-06 17:10:19 +0000114
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100115private:
cyy316723a2019-01-05 16:35:14 +0800116 std::shared_ptr<SizeGenerator> generator_;
David Reiss35dc7692010-10-06 17:10:19 +0000117};
118
119/**************************************************************************
120 * Classes to set up coupled transports
121 **************************************************************************/
122
David Reiss0c025e82010-10-06 17:10:36 +0000123/**
124 * Helper class to represent a coupled pair of transports.
125 *
126 * Data written to the out transport can be read from the in transport.
127 *
128 * This is used as the base class for the various coupled transport
129 * implementations. It shouldn't be instantiated directly.
130 */
David Reiss35dc7692010-10-06 17:10:19 +0000131template <class Transport_>
132class CoupledTransports {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100133public:
Sebastian Zenker042580f2019-01-29 15:48:12 +0100134 virtual ~CoupledTransports() = default;
David Reiss35dc7692010-10-06 17:10:19 +0000135 typedef Transport_ TransportType;
136
David Reissd4788df2010-10-06 17:10:37 +0000137 CoupledTransports() : in(), out() {}
David Reiss35dc7692010-10-06 17:10:19 +0000138
cyy316723a2019-01-05 16:35:14 +0800139 std::shared_ptr<Transport_> in;
140 std::shared_ptr<Transport_> out;
David Reiss35dc7692010-10-06 17:10:19 +0000141
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100142private:
cyy64750162019-02-08 13:40:59 +0800143 CoupledTransports(const CoupledTransports&) = delete;
144 CoupledTransports& operator=(const CoupledTransports&) = delete;
David Reiss35dc7692010-10-06 17:10:19 +0000145};
146
David Reiss0c025e82010-10-06 17:10:36 +0000147/**
148 * Coupled TMemoryBuffers
149 */
David Reiss35dc7692010-10-06 17:10:19 +0000150class CoupledMemoryBuffers : public CoupledTransports<TMemoryBuffer> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100151public:
152 CoupledMemoryBuffers() : buf(new TMemoryBuffer) {
David Reissd4788df2010-10-06 17:10:37 +0000153 in = buf;
154 out = buf;
David Reiss35dc7692010-10-06 17:10:19 +0000155 }
156
cyy316723a2019-01-05 16:35:14 +0800157 std::shared_ptr<TMemoryBuffer> buf;
David Reissd4788df2010-10-06 17:10:37 +0000158};
159
160/**
161 * Helper template class for creating coupled transports that wrap
162 * another transport.
163 */
164template <class WrapperTransport_, class InnerCoupledTransports_>
165class CoupledWrapperTransportsT : public CoupledTransports<WrapperTransport_> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100166public:
David Reissd4788df2010-10-06 17:10:37 +0000167 CoupledWrapperTransportsT() {
168 if (inner_.in) {
169 this->in.reset(new WrapperTransport_(inner_.in));
170 }
171 if (inner_.out) {
172 this->out.reset(new WrapperTransport_(inner_.out));
173 }
174 }
175
176 InnerCoupledTransports_ inner_;
David Reiss35dc7692010-10-06 17:10:19 +0000177};
178
David Reiss0c025e82010-10-06 17:10:36 +0000179/**
180 * Coupled TBufferedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000181 */
David Reissd4788df2010-10-06 17:10:37 +0000182template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100183class CoupledBufferedTransportsT
184 : public CoupledWrapperTransportsT<TBufferedTransport, InnerTransport_> {};
David Reiss35dc7692010-10-06 17:10:19 +0000185
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100186typedef CoupledBufferedTransportsT<CoupledMemoryBuffers> CoupledBufferedTransports;
David Reissd4788df2010-10-06 17:10:37 +0000187
David Reiss0c025e82010-10-06 17:10:36 +0000188/**
189 * Coupled TFramedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000190 */
David Reissd4788df2010-10-06 17:10:37 +0000191template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100192class CoupledFramedTransportsT
193 : public CoupledWrapperTransportsT<TFramedTransport, InnerTransport_> {};
David Reiss35dc7692010-10-06 17:10:19 +0000194
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100195typedef CoupledFramedTransportsT<CoupledMemoryBuffers> CoupledFramedTransports;
David Reissd4788df2010-10-06 17:10:37 +0000196
David Reiss0c025e82010-10-06 17:10:36 +0000197/**
198 * Coupled TZlibTransports.
199 */
David Reissd4788df2010-10-06 17:10:37 +0000200template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100201class CoupledZlibTransportsT : public CoupledWrapperTransportsT<TZlibTransport, InnerTransport_> {};
David Reisse94fa332010-10-06 17:10:26 +0000202
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100203typedef CoupledZlibTransportsT<CoupledMemoryBuffers> CoupledZlibTransports;
David Reissd4788df2010-10-06 17:10:37 +0000204
Jake Farrell5d02b802014-01-07 21:42:01 -0500205#ifndef _WIN32
206// FD transport doesn't make much sense on Windows.
David Reiss0c025e82010-10-06 17:10:36 +0000207/**
208 * Coupled TFDTransports.
209 */
David Reiss35dc7692010-10-06 17:10:19 +0000210class CoupledFDTransports : public CoupledTransports<TFDTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100211public:
David Reiss35dc7692010-10-06 17:10:19 +0000212 CoupledFDTransports() {
213 int pipes[2];
214
215 if (pipe(pipes) != 0) {
216 return;
217 }
218
David Reissd4788df2010-10-06 17:10:37 +0000219 in.reset(new TFDTransport(pipes[0], TFDTransport::CLOSE_ON_DESTROY));
220 out.reset(new TFDTransport(pipes[1], TFDTransport::CLOSE_ON_DESTROY));
David Reiss35dc7692010-10-06 17:10:19 +0000221 }
222};
James E. King, III82ae9572017-08-05 12:23:54 -0400223#else
224/**
225 * Coupled pipe transports
226 */
227class CoupledPipeTransports : public CoupledTransports<TPipe> {
228public:
229 HANDLE hRead;
230 HANDLE hWrite;
231
232 CoupledPipeTransports() {
233 BOOST_REQUIRE(CreatePipe(&hRead, &hWrite, NULL, 1048576 * 2));
234 in.reset(new TPipe(hRead, hWrite));
235 in->open();
236 out = in;
237 }
238};
Jake Farrell5d02b802014-01-07 21:42:01 -0500239#endif
David Reiss35dc7692010-10-06 17:10:19 +0000240
David Reiss0c025e82010-10-06 17:10:36 +0000241/**
242 * Coupled TSockets
243 */
244class CoupledSocketTransports : public CoupledTransports<TSocket> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100245public:
David Reiss0c025e82010-10-06 17:10:36 +0000246 CoupledSocketTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500247 THRIFT_SOCKET sockets[2] = {0};
248 if (THRIFT_SOCKETPAIR(PF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
David Reiss0c025e82010-10-06 17:10:36 +0000249 return;
250 }
251
David Reissd4788df2010-10-06 17:10:37 +0000252 in.reset(new TSocket(sockets[0]));
253 out.reset(new TSocket(sockets[1]));
Roger Meier967600e2013-05-03 22:39:53 +0200254 out->setSendTimeout(100);
David Reiss0c025e82010-10-06 17:10:36 +0000255 }
256};
257
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100258// These could be made to work on Windows, but I don't care enough to make it happen
Jake Farrell5d02b802014-01-07 21:42:01 -0500259#ifndef _WIN32
David Reiss0c025e82010-10-06 17:10:36 +0000260/**
261 * Coupled TFileTransports
262 */
David Reiss35dc7692010-10-06 17:10:19 +0000263class CoupledFileTransports : public CoupledTransports<TFileTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100264public:
David Reiss35dc7692010-10-06 17:10:19 +0000265 CoupledFileTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500266#ifndef _WIN32
267 const char* tmp_dir = "/tmp";
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100268#define FILENAME_SUFFIX "/thrift.transport_test"
Jake Farrell5d02b802014-01-07 21:42:01 -0500269#else
270 const char* tmp_dir = getenv("TMP");
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100271#define FILENAME_SUFFIX "\\thrift.transport_test"
Jake Farrell5d02b802014-01-07 21:42:01 -0500272#endif
273
David Reiss35dc7692010-10-06 17:10:19 +0000274 // Create a temporary file to use
Jake Farrell5d02b802014-01-07 21:42:01 -0500275 filename.resize(strlen(tmp_dir) + strlen(FILENAME_SUFFIX));
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100276 THRIFT_SNPRINTF(&filename[0], filename.size(), "%s" FILENAME_SUFFIX, tmp_dir);
277#undef FILENAME_SUFFIX
Jake Farrell5d02b802014-01-07 21:42:01 -0500278
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100279 { std::ofstream dummy_creation(filename.c_str(), std::ofstream::trunc); }
David Reiss35dc7692010-10-06 17:10:19 +0000280
David Reissd4788df2010-10-06 17:10:37 +0000281 in.reset(new TFileTransport(filename, true));
282 out.reset(new TFileTransport(filename));
David Reiss35dc7692010-10-06 17:10:19 +0000283 }
284
Sebastian Zenker042580f2019-01-29 15:48:12 +0100285 ~CoupledFileTransports() override { remove(filename.c_str()); }
David Reiss35dc7692010-10-06 17:10:19 +0000286
Jake Farrell5d02b802014-01-07 21:42:01 -0500287 std::string filename;
David Reiss35dc7692010-10-06 17:10:19 +0000288};
Jake Farrell5d02b802014-01-07 21:42:01 -0500289#endif
David Reiss35dc7692010-10-06 17:10:19 +0000290
David Reiss0c025e82010-10-06 17:10:36 +0000291/**
292 * Wrapper around another CoupledTransports implementation that exposes the
293 * transports as TTransport pointers.
294 *
295 * This is used since accessing a transport via a "TTransport*" exercises a
296 * different code path than using the base pointer class. As part of the
297 * template code changes, most transport methods are no longer virtual.
298 */
David Reiss35dc7692010-10-06 17:10:19 +0000299template <class CoupledTransports_>
300class CoupledTTransports : public CoupledTransports<TTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100301public:
David Reiss35dc7692010-10-06 17:10:19 +0000302 CoupledTTransports() : transports() {
303 in = transports.in;
304 out = transports.out;
305 }
306
307 CoupledTransports_ transports;
308};
309
David Reiss0c025e82010-10-06 17:10:36 +0000310/**
311 * Wrapper around another CoupledTransports implementation that exposes the
312 * transports as TBufferBase pointers.
313 *
314 * This can only be instantiated with a transport type that is a subclass of
315 * TBufferBase.
316 */
David Reiss35dc7692010-10-06 17:10:19 +0000317template <class CoupledTransports_>
318class CoupledBufferBases : public CoupledTransports<TBufferBase> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100319public:
David Reiss35dc7692010-10-06 17:10:19 +0000320 CoupledBufferBases() : transports() {
321 in = transports.in;
322 out = transports.out;
323 }
324
325 CoupledTransports_ transports;
326};
327
David Reiss35dc7692010-10-06 17:10:19 +0000328/**************************************************************************
David Reisse5c435c2010-10-06 17:10:38 +0000329 * Alarm handling code for use in tests that check the transport blocking
330 * semantics.
331 *
332 * If the transport ends up blocking, we don't want to hang forever. We use
333 * SIGALRM to fire schedule signal to wake up and try to write data so the
334 * transport will unblock.
335 *
336 * It isn't really the safest thing in the world to be mucking around with
337 * complicated global data structures in a signal handler. It should probably
338 * be okay though, since we know the main thread should always be blocked in a
339 * read() request when the signal handler is running.
340 **************************************************************************/
341
342struct TriggerInfo {
cyy316723a2019-01-05 16:35:14 +0800343 TriggerInfo(int seconds, const std::shared_ptr<TTransport>& transport, uint32_t writeLength)
Sebastian Zenker042580f2019-01-29 15:48:12 +0100344 : timeoutSeconds(seconds), transport(transport), writeLength(writeLength), next(nullptr) {}
David Reisse5c435c2010-10-06 17:10:38 +0000345
346 int timeoutSeconds;
cyy316723a2019-01-05 16:35:14 +0800347 std::shared_ptr<TTransport> transport;
David Reisse5c435c2010-10-06 17:10:38 +0000348 uint32_t writeLength;
349 TriggerInfo* next;
350};
351
Jake Farrell5d02b802014-01-07 21:42:01 -0500352apache::thrift::concurrency::Monitor g_alarm_monitor;
353TriggerInfo* g_triggerInfo;
354unsigned int g_numTriggersFired;
355bool g_teardown = false;
David Reisse5c435c2010-10-06 17:10:38 +0000356
Jake Farrell5d02b802014-01-07 21:42:01 -0500357void alarm_handler() {
Sebastian Zenker042580f2019-01-29 15:48:12 +0100358 TriggerInfo* info = nullptr;
Jake Farrell5d02b802014-01-07 21:42:01 -0500359 {
360 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
361 // The alarm timed out, which almost certainly means we're stuck
362 // on a transport that is incorrectly blocked.
363 ++g_numTriggersFired;
David Reisse5c435c2010-10-06 17:10:38 +0000364
Jake Farrell5d02b802014-01-07 21:42:01 -0500365 // Note: we print messages to stdout instead of stderr, since
366 // tools/test/runner only records stdout messages in the failure messages for
367 // boost tests. (boost prints its test info to stdout.)
368 printf("Timeout alarm expired; attempting to unblock transport\n");
Sebastian Zenker042580f2019-01-29 15:48:12 +0100369 if (g_triggerInfo == nullptr) {
Jake Farrell5d02b802014-01-07 21:42:01 -0500370 printf(" trigger stack is empty!\n");
371 }
David Reisse5c435c2010-10-06 17:10:38 +0000372
Jake Farrell5d02b802014-01-07 21:42:01 -0500373 // Pop off the first TriggerInfo.
374 // If there is another one, schedule an alarm for it.
375 info = g_triggerInfo;
376 g_triggerInfo = info->next;
David Reisse5c435c2010-10-06 17:10:38 +0000377 }
378
David Reisse5c435c2010-10-06 17:10:38 +0000379 // Write some data to the transport to hopefully unblock it.
Sebastian Zenker042580f2019-01-29 15:48:12 +0100380 auto* buf = new uint8_t[info->writeLength];
David Reisse5c435c2010-10-06 17:10:38 +0000381 memset(buf, 'b', info->writeLength);
Roger Meier0069cc42010-10-13 18:10:18 +0000382 boost::scoped_array<uint8_t> array(buf);
David Reisse5c435c2010-10-06 17:10:38 +0000383 info->transport->write(buf, info->writeLength);
384 info->transport->flush();
385
386 delete info;
387}
388
Jake Farrell5d02b802014-01-07 21:42:01 -0500389void alarm_handler_wrapper() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100390 int64_t timeout = 0; // timeout of 0 means wait forever
391 while (true) {
Jake Farrell5d02b802014-01-07 21:42:01 -0500392 bool fireHandler = false;
393 {
394 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100395 if (g_teardown)
396 return;
397 // calculate timeout
Sebastian Zenker042580f2019-01-29 15:48:12 +0100398 if (g_triggerInfo == nullptr) {
Jake Farrell5d02b802014-01-07 21:42:01 -0500399 timeout = 0;
400 } else {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100401 timeout = g_triggerInfo->timeoutSeconds * 1000;
Jake Farrell5d02b802014-01-07 21:42:01 -0500402 }
403
404 int waitResult = g_alarm_monitor.waitForTimeRelative(timeout);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100405 if (waitResult == THRIFT_ETIMEDOUT)
Jake Farrell5d02b802014-01-07 21:42:01 -0500406 fireHandler = true;
407 }
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100408 if (fireHandler)
409 alarm_handler(); // calling outside the lock
David Reisse5c435c2010-10-06 17:10:38 +0000410 }
David Reisse5c435c2010-10-06 17:10:38 +0000411}
412
413/**
414 * Add a trigger to be scheduled "seconds" seconds after the
415 * last currently scheduled trigger.
416 *
417 * (Note that this is not "seconds" from now. That might be more logical, but
418 * would require slightly more complicated sorting, rather than just appending
419 * to the end.)
420 */
421void add_trigger(unsigned int seconds,
cyy316723a2019-01-05 16:35:14 +0800422 const std::shared_ptr<TTransport>& transport,
David Reisse5c435c2010-10-06 17:10:38 +0000423 uint32_t write_len) {
Sebastian Zenker042580f2019-01-29 15:48:12 +0100424 auto* info = new TriggerInfo(seconds, transport, write_len);
Jake Farrell5d02b802014-01-07 21:42:01 -0500425 {
426 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
Sebastian Zenker042580f2019-01-29 15:48:12 +0100427 if (g_triggerInfo == nullptr) {
Jake Farrell5d02b802014-01-07 21:42:01 -0500428 // This is the first trigger.
429 // Set g_triggerInfo, and schedule the alarm
430 g_triggerInfo = info;
431 g_alarm_monitor.notify();
432 } else {
433 // Add this trigger to the end of the list
434 TriggerInfo* prev = g_triggerInfo;
435 while (prev->next) {
436 prev = prev->next;
437 }
438 prev->next = info;
David Reisse5c435c2010-10-06 17:10:38 +0000439 }
David Reisse5c435c2010-10-06 17:10:38 +0000440 }
441}
442
443void clear_triggers() {
Sebastian Zenker042580f2019-01-29 15:48:12 +0100444 TriggerInfo* info = nullptr;
Jake Farrell5d02b802014-01-07 21:42:01 -0500445
446 {
447 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
448 info = g_triggerInfo;
Sebastian Zenker042580f2019-01-29 15:48:12 +0100449 g_triggerInfo = nullptr;
Jake Farrell5d02b802014-01-07 21:42:01 -0500450 g_numTriggersFired = 0;
451 g_alarm_monitor.notify();
452 }
David Reisse5c435c2010-10-06 17:10:38 +0000453
Sebastian Zenker042580f2019-01-29 15:48:12 +0100454 while (info != nullptr) {
David Reisse5c435c2010-10-06 17:10:38 +0000455 TriggerInfo* next = info->next;
456 delete info;
457 info = next;
458 }
459}
460
461void set_trigger(unsigned int seconds,
cyy316723a2019-01-05 16:35:14 +0800462 const std::shared_ptr<TTransport>& transport,
David Reisse5c435c2010-10-06 17:10:38 +0000463 uint32_t write_len) {
464 clear_triggers();
465 add_trigger(seconds, transport, write_len);
466}
467
468/**************************************************************************
469 * Test functions
David Reiss35dc7692010-10-06 17:10:19 +0000470 **************************************************************************/
471
472/**
473 * Test interleaved write and read calls.
474 *
475 * Generates a buffer totalSize bytes long, then writes it to the transport,
476 * and verifies the written data can be read back correctly.
477 *
478 * Mode of operation:
479 * - call wChunkGenerator to figure out how large of a chunk to write
480 * - call wSizeGenerator to get the size for individual write() calls,
481 * and do this repeatedly until the entire chunk is written.
482 * - call rChunkGenerator to figure out how large of a chunk to read
483 * - call rSizeGenerator to get the size for individual read() calls,
484 * and do this repeatedly until the entire chunk is read.
485 * - repeat until the full buffer is written and read back,
486 * then compare the data read back against the original buffer
487 *
488 *
489 * - If any of the size generators return 0, this means to use the maximum
490 * possible size.
491 *
492 * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
493 * there are never more than maxOutstanding bytes waiting to be read back.
494 */
495template <class CoupledTransports>
496void test_rw(uint32_t totalSize,
497 SizeGenerator& wSizeGenerator,
498 SizeGenerator& rSizeGenerator,
499 SizeGenerator& wChunkGenerator,
500 SizeGenerator& rChunkGenerator,
501 uint32_t maxOutstanding) {
502 CoupledTransports transports;
Sebastian Zenker042580f2019-01-29 15:48:12 +0100503 BOOST_REQUIRE(transports.in != nullptr);
504 BOOST_REQUIRE(transports.out != nullptr);
David Reiss35dc7692010-10-06 17:10:19 +0000505
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100506 boost::shared_array<uint8_t> wbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
507 boost::shared_array<uint8_t> rbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
David Reiss35dc7692010-10-06 17:10:19 +0000508
509 // store some data in wbuf
510 for (uint32_t n = 0; n < totalSize; ++n) {
511 wbuf[n] = (n & 0xff);
512 }
513 // clear rbuf
514 memset(rbuf.get(), 0, totalSize);
515
516 uint32_t total_written = 0;
517 uint32_t total_read = 0;
518 while (total_read < totalSize) {
519 // Determine how large a chunk of data to write
520 uint32_t wchunk_size = wChunkGenerator.nextSize();
521 if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
522 wchunk_size = totalSize - total_written;
523 }
524
525 // Make sure (total_written - total_read) + wchunk_size
526 // is less than maxOutstanding
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100527 if (maxOutstanding > 0 && wchunk_size > maxOutstanding - (total_written - total_read)) {
David Reiss35dc7692010-10-06 17:10:19 +0000528 wchunk_size = maxOutstanding - (total_written - total_read);
529 }
530
531 // Write the chunk
532 uint32_t chunk_written = 0;
533 while (chunk_written < wchunk_size) {
534 uint32_t write_size = wSizeGenerator.nextSize();
535 if (write_size == 0 || write_size > wchunk_size - chunk_written) {
536 write_size = wchunk_size - chunk_written;
537 }
538
Roger Meier967600e2013-05-03 22:39:53 +0200539 try {
540 transports.out->write(wbuf.get() + total_written, write_size);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100541 } catch (TTransportException& te) {
Roger Meier967600e2013-05-03 22:39:53 +0200542 if (te.getType() == TTransportException::TIMED_OUT)
543 break;
544 throw te;
545 }
David Reiss35dc7692010-10-06 17:10:19 +0000546 chunk_written += write_size;
547 total_written += write_size;
548 }
549
550 // Flush the data, so it will be available in the read transport
551 // Don't flush if wchunk_size is 0. (This should only happen if
552 // total_written == totalSize already, and we're only reading now.)
553 if (wchunk_size > 0) {
554 transports.out->flush();
555 }
556
557 // Determine how large a chunk of data to read back
558 uint32_t rchunk_size = rChunkGenerator.nextSize();
559 if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
560 rchunk_size = total_written - total_read;
561 }
562
563 // Read the chunk
564 uint32_t chunk_read = 0;
565 while (chunk_read < rchunk_size) {
566 uint32_t read_size = rSizeGenerator.nextSize();
567 if (read_size == 0 || read_size > rchunk_size - chunk_read) {
568 read_size = rchunk_size - chunk_read;
569 }
570
David Reisse94fa332010-10-06 17:10:26 +0000571 int bytes_read = -1;
572 try {
573 bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
574 } catch (TTransportException& e) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100575 BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size << ") threw exception \""
576 << e.what() << "\"; written so far: " << total_written << " / "
577 << totalSize << " bytes");
David Reisse94fa332010-10-06 17:10:26 +0000578 }
579
David Reiss35dc7692010-10-06 17:10:19 +0000580 BOOST_REQUIRE_MESSAGE(bytes_read > 0,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100581 "read(pos=" << total_read << ", size=" << read_size << ") returned "
582 << bytes_read << "; written so far: " << total_written
583 << " / " << totalSize << " bytes");
David Reiss35dc7692010-10-06 17:10:19 +0000584 chunk_read += bytes_read;
585 total_read += bytes_read;
586 }
587 }
588
589 // make sure the data read back is identical to the data written
590 BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
591}
592
David Reisse5c435c2010-10-06 17:10:38 +0000593template <class CoupledTransports>
594void test_read_part_available() {
595 CoupledTransports transports;
Sebastian Zenker042580f2019-01-29 15:48:12 +0100596 BOOST_REQUIRE(transports.in != nullptr);
597 BOOST_REQUIRE(transports.out != nullptr);
David Reisse5c435c2010-10-06 17:10:38 +0000598
599 uint8_t write_buf[16];
600 uint8_t read_buf[16];
601 memset(write_buf, 'a', sizeof(write_buf));
602
603 // Attemping to read 10 bytes when only 9 are available should return 9
604 // immediately.
605 transports.out->write(write_buf, 9);
606 transports.out->flush();
607 set_trigger(3, transports.out, 1);
608 uint32_t bytes_read = transports.in->read(read_buf, 10);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100609 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
610 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)9);
David Reisse5c435c2010-10-06 17:10:38 +0000611
612 clear_triggers();
613}
614
615template <class CoupledTransports>
Roger Meier02c827b2012-04-11 21:59:57 +0000616void test_read_part_available_in_chunks() {
617 CoupledTransports transports;
Sebastian Zenker042580f2019-01-29 15:48:12 +0100618 BOOST_REQUIRE(transports.in != nullptr);
619 BOOST_REQUIRE(transports.out != nullptr);
Roger Meier02c827b2012-04-11 21:59:57 +0000620
621 uint8_t write_buf[16];
622 uint8_t read_buf[16];
623 memset(write_buf, 'a', sizeof(write_buf));
624
625 // Write 10 bytes (in a single frame, for transports that use framing)
626 transports.out->write(write_buf, 10);
627 transports.out->flush();
628
629 // Read 1 byte, to force the transport to read the frame
630 uint32_t bytes_read = transports.in->read(read_buf, 1);
Jake Farrell5d02b802014-01-07 21:42:01 -0500631 BOOST_CHECK_EQUAL(bytes_read, 1u);
Roger Meier02c827b2012-04-11 21:59:57 +0000632
633 // Read more than what is remaining and verify the transport does not block
634 set_trigger(3, transports.out, 1);
635 bytes_read = transports.in->read(read_buf, 10);
Jake Farrell5d02b802014-01-07 21:42:01 -0500636 BOOST_CHECK_EQUAL(g_numTriggersFired, 0u);
637 BOOST_CHECK_EQUAL(bytes_read, 9u);
Roger Meier02c827b2012-04-11 21:59:57 +0000638
639 clear_triggers();
640}
641
642template <class CoupledTransports>
David Reiss0a2d81e2010-10-06 17:10:40 +0000643void test_read_partial_midframe() {
644 CoupledTransports transports;
Sebastian Zenker042580f2019-01-29 15:48:12 +0100645 BOOST_REQUIRE(transports.in != nullptr);
646 BOOST_REQUIRE(transports.out != nullptr);
David Reiss0a2d81e2010-10-06 17:10:40 +0000647
648 uint8_t write_buf[16];
649 uint8_t read_buf[16];
650 memset(write_buf, 'a', sizeof(write_buf));
651
652 // Attempt to read 10 bytes, when only 9 are available, but after we have
653 // already read part of the data that is available. This exercises a
654 // different code path for several of the transports.
655 //
656 // For transports that add their own framing (e.g., TFramedTransport and
657 // TFileTransport), the two flush calls break up the data in to a 10 byte
658 // frame and a 3 byte frame. The first read then puts us partway through the
659 // first frame, and then we attempt to read past the end of that frame, and
660 // through the next frame, too.
661 //
662 // For buffered transports that perform read-ahead (e.g.,
663 // TBufferedTransport), the read-ahead will most likely see all 13 bytes
664 // written on the first read. The next read will then attempt to read past
665 // the end of the read-ahead buffer.
666 //
667 // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
668 // transports that track framing internally.
669 transports.out->write(write_buf, 10);
670 transports.out->flush();
671 transports.out->write(write_buf, 3);
672 transports.out->flush();
673
674 // Now read 4 bytes, so that we are partway through the written data.
675 uint32_t bytes_read = transports.in->read(read_buf, 4);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100676 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)4);
David Reiss0a2d81e2010-10-06 17:10:40 +0000677
678 // Now attempt to read 10 bytes. Only 9 more are available.
679 //
680 // We should be able to get all 9 bytes, but it might take multiple read
681 // calls, since it is valid for read() to return fewer bytes than requested.
682 // (Most transports do immediately return 9 bytes, but the framing transports
683 // tend to only return to the end of the current frame, which is 6 bytes in
684 // this case.)
685 uint32_t total_read = 0;
686 while (total_read < 9) {
687 set_trigger(3, transports.out, 1);
688 bytes_read = transports.in->read(read_buf, 10);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100689 BOOST_REQUIRE_EQUAL(g_numTriggersFired, (unsigned int)0);
690 BOOST_REQUIRE_GT(bytes_read, (uint32_t)0);
David Reiss0a2d81e2010-10-06 17:10:40 +0000691 total_read += bytes_read;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100692 BOOST_REQUIRE_LE(total_read, (uint32_t)9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000693 }
694
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100695 BOOST_CHECK_EQUAL(total_read, (uint32_t)9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000696
697 clear_triggers();
698}
699
700template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000701void test_borrow_part_available() {
702 CoupledTransports transports;
Sebastian Zenker042580f2019-01-29 15:48:12 +0100703 BOOST_REQUIRE(transports.in != nullptr);
704 BOOST_REQUIRE(transports.out != nullptr);
David Reisse5c435c2010-10-06 17:10:38 +0000705
706 uint8_t write_buf[16];
707 uint8_t read_buf[16];
708 memset(write_buf, 'a', sizeof(write_buf));
709
710 // Attemping to borrow 10 bytes when only 9 are available should return NULL
711 // immediately.
712 transports.out->write(write_buf, 9);
713 transports.out->flush();
714 set_trigger(3, transports.out, 1);
715 uint32_t borrow_len = 10;
716 const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100717 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
Sebastian Zenker042580f2019-01-29 15:48:12 +0100718 BOOST_CHECK(borrowed_buf == nullptr);
David Reisse5c435c2010-10-06 17:10:38 +0000719
720 clear_triggers();
721}
722
723template <class CoupledTransports>
724void test_read_none_available() {
725 CoupledTransports transports;
Sebastian Zenker042580f2019-01-29 15:48:12 +0100726 BOOST_REQUIRE(transports.in != nullptr);
727 BOOST_REQUIRE(transports.out != nullptr);
David Reisse5c435c2010-10-06 17:10:38 +0000728
David Reisse5c435c2010-10-06 17:10:38 +0000729 uint8_t read_buf[16];
David Reisse5c435c2010-10-06 17:10:38 +0000730
731 // Attempting to read when no data is available should either block until
732 // some data is available, or fail immediately. (e.g., TSocket blocks,
733 // TMemoryBuffer just fails.)
734 //
735 // If the transport blocks, it should succeed once some data is available,
736 // even if less than the amount requested becomes available.
737 set_trigger(1, transports.out, 2);
738 add_trigger(1, transports.out, 8);
739 uint32_t bytes_read = transports.in->read(read_buf, 10);
740 if (bytes_read == 0) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100741 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000742 clear_triggers();
743 } else {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100744 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)1);
745 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)2);
David Reisse5c435c2010-10-06 17:10:38 +0000746 }
747
748 clear_triggers();
749}
750
751template <class CoupledTransports>
752void test_borrow_none_available() {
753 CoupledTransports transports;
Sebastian Zenker042580f2019-01-29 15:48:12 +0100754 BOOST_REQUIRE(transports.in != nullptr);
755 BOOST_REQUIRE(transports.out != nullptr);
David Reisse5c435c2010-10-06 17:10:38 +0000756
757 uint8_t write_buf[16];
758 memset(write_buf, 'a', sizeof(write_buf));
759
760 // Attempting to borrow when no data is available should fail immediately
761 set_trigger(1, transports.out, 10);
762 uint32_t borrow_len = 10;
Sebastian Zenker042580f2019-01-29 15:48:12 +0100763 const uint8_t* borrowed_buf = transports.in->borrow(nullptr, &borrow_len);
764 BOOST_CHECK(borrowed_buf == nullptr);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100765 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000766
767 clear_triggers();
768}
769
David Reiss35dc7692010-10-06 17:10:19 +0000770/**************************************************************************
771 * Test case generation
772 *
773 * Pretty ugly and annoying. This would be much easier if we the unit test
774 * framework didn't force each test to be a separate function.
775 * - Writing a completely separate function definition for each of these would
776 * result in a lot of repetitive boilerplate code.
777 * - Combining many tests into a single function makes it more difficult to
778 * tell precisely which tests failed. It also means you can't get a progress
779 * update after each test, and the tests are already fairly slow.
Konrad Grochowski3b5dacb2014-11-24 10:55:31 +0100780 * - Similar registration could be achieved with BOOST_TEST_CASE_TEMPLATE,
David Reiss35dc7692010-10-06 17:10:19 +0000781 * but it requires a lot of awkward MPL code, and results in useless test
782 * case names. (The names are generated from std::type_info::name(), which
783 * is compiler-dependent. gcc returns mangled names.)
784 **************************************************************************/
785
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100786#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
787 addTestRW<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports), totalSize, ##__VA_ARGS__);
David Reissd4788df2010-10-06 17:10:37 +0000788
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100789#define TEST_RW(CoupledTransports, totalSize, ...) \
790 do { \
791 /* Add the test as specified, to test the non-virtual function calls */ \
792 ADD_TEST_RW(CoupledTransports, totalSize, ##__VA_ARGS__); \
793 /* \
794 * Also test using the transport as a TTransport*, to test \
795 * the read_virt()/write_virt() calls \
796 */ \
797 ADD_TEST_RW(CoupledTTransports<CoupledTransports>, totalSize, ##__VA_ARGS__); \
798 /* Test wrapping the transport with TBufferedTransport */ \
799 ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
800 /* Test wrapping the transport with TFramedTransports */ \
801 ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
802 /* Test wrapping the transport with TZlibTransport */ \
803 ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000804 } while (0)
805
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100806#define ADD_TEST_BLOCKING(CoupledTransports) \
807 addTestBlocking<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports));
David Reisse5c435c2010-10-06 17:10:38 +0000808
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100809#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
810 ADD_TEST_BLOCKING(CoupledTransports); \
811 ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
812 ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
813 ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
David Reisse5c435c2010-10-06 17:10:38 +0000814 ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
815
David Reiss35dc7692010-10-06 17:10:19 +0000816class TransportTestGen {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100817public:
818 TransportTestGen(boost::unit_test::test_suite* suite, float sizeMultiplier)
819 : suite_(suite), sizeMultiplier_(sizeMultiplier) {}
David Reiss35dc7692010-10-06 17:10:19 +0000820
821 void generate() {
822 GenericSizeGenerator rand4k(1, 4096);
823
824 /*
825 * We do the basically the same set of tests for each transport type,
826 * although we tweak the parameters in some places.
827 */
828
David Reissd4788df2010-10-06 17:10:37 +0000829 // TMemoryBuffer tests
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100830 TEST_RW(CoupledMemoryBuffers, 1024 * 1024, 0, 0);
831 TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k);
832 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163);
833 TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000834
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100835 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 0, 0, rand4k, rand4k);
836 TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
837 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163, rand4k, rand4k);
838 TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1, rand4k, rand4k);
David Reisse94fa332010-10-06 17:10:26 +0000839
David Reisse5c435c2010-10-06 17:10:38 +0000840 TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
841
Jake Farrell5d02b802014-01-07 21:42:01 -0500842#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000843 // TFDTransport tests
844 // Since CoupledFDTransports tests with a pipe, writes will block
845 // if there is too much outstanding unread data in the pipe.
846 uint32_t fd_max_outstanding = 4096;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100847 TEST_RW(CoupledFDTransports, 1024 * 1024, 0, 0, 0, 0, fd_max_outstanding);
848 TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, 0, 0, fd_max_outstanding);
849 TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, 0, 0, fd_max_outstanding);
850 TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, 0, 0, fd_max_outstanding);
David Reiss35dc7692010-10-06 17:10:19 +0000851
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100852 TEST_RW(CoupledFDTransports, 1024 * 256, 0, 0, rand4k, rand4k, fd_max_outstanding);
853 TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k, fd_max_outstanding);
854 TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, rand4k, rand4k, fd_max_outstanding);
855 TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, rand4k, rand4k, fd_max_outstanding);
David Reiss35dc7692010-10-06 17:10:19 +0000856
David Reisse5c435c2010-10-06 17:10:38 +0000857 TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
James E. King, III82ae9572017-08-05 12:23:54 -0400858#else
859 // TPipe tests (WIN32 only)
860 TEST_RW(CoupledPipeTransports, 1024 * 1024, 0, 0);
861 TEST_RW(CoupledPipeTransports, 1024 * 256, rand4k, rand4k);
862 TEST_RW(CoupledPipeTransports, 1024 * 256, 167, 163);
863 TEST_RW(CoupledPipeTransports, 1024 * 16, 1, 1);
864
865 TEST_RW(CoupledPipeTransports, 1024 * 256, 0, 0, rand4k, rand4k);
866 TEST_RW(CoupledPipeTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
867 TEST_RW(CoupledPipeTransports, 1024 * 256, 167, 163, rand4k, rand4k);
868 TEST_RW(CoupledPipeTransports, 1024 * 16, 1, 1, rand4k, rand4k);
869
870 TEST_BLOCKING_BEHAVIOR(CoupledPipeTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500871#endif //_WIN32
David Reisse5c435c2010-10-06 17:10:38 +0000872
David Reiss0c025e82010-10-06 17:10:36 +0000873 // TSocket tests
874 uint32_t socket_max_outstanding = 4096;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100875 TEST_RW(CoupledSocketTransports, 1024 * 1024, 0, 0, 0, 0, socket_max_outstanding);
876 TEST_RW(CoupledSocketTransports, 1024 * 256, rand4k, rand4k, 0, 0, socket_max_outstanding);
877 TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, 0, 0, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000878 // Doh. Apparently writing to a socket has some additional overhead for
879 // each send() call. If we have more than ~400 outstanding 1-byte write
880 // requests, additional send() calls start blocking.
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100881 TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, 0, 0, socket_max_outstanding);
882 TEST_RW(CoupledSocketTransports, 1024 * 256, 0, 0, rand4k, rand4k, socket_max_outstanding);
883 TEST_RW(CoupledSocketTransports,
884 1024 * 256,
885 rand4k,
886 rand4k,
887 rand4k,
888 rand4k,
889 socket_max_outstanding);
890 TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, rand4k, rand4k, socket_max_outstanding);
891 TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, rand4k, rand4k, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000892
David Reisse5c435c2010-10-06 17:10:38 +0000893 TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
894
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100895// These could be made to work on Windows, but I don't care enough to make it happen
Jake Farrell5d02b802014-01-07 21:42:01 -0500896#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000897 // TFileTransport tests
898 // We use smaller buffer sizes here, since TFileTransport is fairly slow.
899 //
900 // TFileTransport can't write more than 16MB at once
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100901 uint32_t max_write_at_once = 1024 * 1024 * 16 - 4;
902 TEST_RW(CoupledFileTransports, 1024 * 1024, max_write_at_once, 0);
903 TEST_RW(CoupledFileTransports, 1024 * 128, rand4k, rand4k);
904 TEST_RW(CoupledFileTransports, 1024 * 128, 167, 163);
905 TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000906
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100907 TEST_RW(CoupledFileTransports, 1024 * 64, 0, 0, rand4k, rand4k);
908 TEST_RW(CoupledFileTransports, 1024 * 64, rand4k, rand4k, rand4k, rand4k);
909 TEST_RW(CoupledFileTransports, 1024 * 64, 167, 163, rand4k, rand4k);
910 TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000911
David Reisse5c435c2010-10-06 17:10:38 +0000912 TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500913#endif
David Reisse5c435c2010-10-06 17:10:38 +0000914
David Reissd4788df2010-10-06 17:10:37 +0000915 // Add some tests that access TBufferedTransport and TFramedTransport
916 // via TTransport pointers and TBufferBase pointers.
David Reisse5c435c2010-10-06 17:10:38 +0000917 ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100918 1024 * 1024,
919 rand4k,
920 rand4k,
921 rand4k,
922 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000923 ADD_TEST_RW(CoupledBufferBases<CoupledBufferedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100924 1024 * 1024,
925 rand4k,
926 rand4k,
927 rand4k,
928 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000929 ADD_TEST_RW(CoupledTTransports<CoupledFramedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100930 1024 * 1024,
931 rand4k,
932 rand4k,
933 rand4k,
934 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000935 ADD_TEST_RW(CoupledBufferBases<CoupledFramedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100936 1024 * 1024,
937 rand4k,
938 rand4k,
939 rand4k,
940 rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000941
942 // Test using TZlibTransport via a TTransport pointer
David Reisse5c435c2010-10-06 17:10:38 +0000943 ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100944 1024 * 1024,
945 rand4k,
946 rand4k,
947 rand4k,
948 rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000949 }
950
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200951#if (BOOST_VERSION >= 105900)
952#define MAKE_TEST_CASE(_FUNC, _NAME) boost::unit_test::make_test_case(_FUNC, _NAME, __FILE__, __LINE__)
953#else
954#define MAKE_TEST_CASE(_FUNC, _NAME) boost::unit_test::make_test_case(_FUNC, _NAME)
955#endif
956
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100957private:
David Reiss35dc7692010-10-06 17:10:19 +0000958 template <class CoupledTransports>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100959 void addTestRW(const char* transport_name,
960 uint32_t totalSize,
961 GenericSizeGenerator wSizeGen,
962 GenericSizeGenerator rSizeGen,
David Reisse5c435c2010-10-06 17:10:38 +0000963 GenericSizeGenerator wChunkSizeGen = 0,
964 GenericSizeGenerator rChunkSizeGen = 0,
965 uint32_t maxOutstanding = 0,
966 uint32_t expectedFailures = 0) {
David Reiss65e62d32010-10-06 17:10:35 +0000967 // adjust totalSize by the specified sizeMultiplier_ first
968 totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
969
David Reiss35dc7692010-10-06 17:10:19 +0000970 std::ostringstream name;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100971 name << transport_name << "::test_rw(" << totalSize << ", " << wSizeGen.describe() << ", "
972 << rSizeGen.describe() << ", " << wChunkSizeGen.describe() << ", "
973 << rChunkSizeGen.describe() << ", " << maxOutstanding << ")";
David Reiss35dc7692010-10-06 17:10:19 +0000974
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200975#if (BOOST_VERSION >= 105900)
cyy316723a2019-01-05 16:35:14 +0800976 std::function<void ()> test_func
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200977#else
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100978 boost::unit_test::callback0<> test_func
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200979#endif
cyy316723a2019-01-05 16:35:14 +0800980 = std::bind(test_rw<CoupledTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100981 totalSize,
982 wSizeGen,
983 rSizeGen,
984 wChunkSizeGen,
985 rChunkSizeGen,
986 maxOutstanding);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200987 suite_->add(MAKE_TEST_CASE(test_func, name.str()), expectedFailures);
Roger Meier0069cc42010-10-13 18:10:18 +0000988 }
David Reiss35dc7692010-10-06 17:10:19 +0000989
David Reisse5c435c2010-10-06 17:10:38 +0000990 template <class CoupledTransports>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100991 void addTestBlocking(const char* transportName, uint32_t expectedFailures = 0) {
David Reisse5c435c2010-10-06 17:10:38 +0000992 char name[1024];
David Reisse5c435c2010-10-06 17:10:38 +0000993
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100994 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200995 suite_->add(MAKE_TEST_CASE(test_read_part_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +0000996
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100997 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available_in_chunks()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200998 suite_->add(MAKE_TEST_CASE(test_read_part_available_in_chunks<CoupledTransports>, name), expectedFailures);
Roger Meier02c827b2012-04-11 21:59:57 +0000999
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001000 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_partial_midframe()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001001 suite_->add(MAKE_TEST_CASE(test_read_partial_midframe<CoupledTransports>, name), expectedFailures);
David Reiss0a2d81e2010-10-06 17:10:40 +00001002
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001003 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_none_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001004 suite_->add(MAKE_TEST_CASE(test_read_none_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +00001005
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001006 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_part_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001007 suite_->add(MAKE_TEST_CASE(test_borrow_part_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +00001008
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001009 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_none_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001010 suite_->add(MAKE_TEST_CASE(test_borrow_none_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +00001011 }
1012
David Reiss35dc7692010-10-06 17:10:19 +00001013 boost::unit_test::test_suite* suite_;
David Reiss65e62d32010-10-06 17:10:35 +00001014 // sizeMultiplier_ is configurable via the command line, and allows the
1015 // user to adjust between smaller buffers that can be tested quickly,
1016 // or larger buffers that more thoroughly exercise the code, but take
1017 // longer.
1018 float sizeMultiplier_;
David Reiss35dc7692010-10-06 17:10:19 +00001019};
1020
1021/**************************************************************************
1022 * General Initialization
1023 **************************************************************************/
1024
Jake Farrell5d02b802014-01-07 21:42:01 -05001025struct global_fixture {
cyy316723a2019-01-05 16:35:14 +08001026 std::shared_ptr<apache::thrift::concurrency::Thread> alarmThread_;
Jake Farrell5d02b802014-01-07 21:42:01 -05001027 global_fixture() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001028#if _WIN32
Jake Farrell5d02b802014-01-07 21:42:01 -05001029 apache::thrift::transport::TWinsockSingleton::create();
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001030#endif
David Reiss35dc7692010-10-06 17:10:19 +00001031
cyyca8af9b2019-01-11 22:13:12 +08001032 apache::thrift::concurrency::ThreadFactory factory;
Jake Farrell5d02b802014-01-07 21:42:01 -05001033 factory.setDetached(false);
1034
1035 alarmThread_ = factory.newThread(
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001036 apache::thrift::concurrency::FunctionRunner::create(alarm_handler_wrapper));
Jake Farrell5d02b802014-01-07 21:42:01 -05001037 alarmThread_->start();
1038 }
1039 ~global_fixture() {
1040 {
1041 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
1042 g_teardown = true;
1043 g_alarm_monitor.notify();
1044 }
1045 alarmThread_->join();
1046 }
David Reiss65e62d32010-10-06 17:10:35 +00001047};
1048
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001049#if (BOOST_VERSION >= 105900)
1050BOOST_GLOBAL_FIXTURE(global_fixture);
1051#else
Jake Farrell5d02b802014-01-07 21:42:01 -05001052BOOST_GLOBAL_FIXTURE(global_fixture)
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001053#endif
David Reiss35dc7692010-10-06 17:10:19 +00001054
Antonio Di Monaco796667b2016-01-04 23:05:19 +01001055#ifdef BOOST_TEST_DYN_LINK
1056bool init_unit_test_suite() {
1057 struct timeval tv;
Sebastian Zenker042580f2019-01-29 15:48:12 +01001058 THRIFT_GETTIMEOFDAY(&tv, nullptr);
Antonio Di Monaco796667b2016-01-04 23:05:19 +01001059 int seed = tv.tv_sec ^ tv.tv_usec;
1060
1061 initrand(seed);
1062
1063 boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
1064 suite->p_name.value = "TransportTest";
1065 TransportTestGen transport_test_generator(suite, 1);
1066 transport_test_generator.generate();
1067 return true;
1068}
1069
1070int main( int argc, char* argv[] ) {
1071 return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
1072}
1073#else
David Reiss35dc7692010-10-06 17:10:19 +00001074boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
Konrad Grochowskib3f5ffc2014-11-06 19:32:59 +01001075 THRIFT_UNUSED_VARIABLE(argc);
1076 THRIFT_UNUSED_VARIABLE(argv);
Jake Farrell5d02b802014-01-07 21:42:01 -05001077 struct timeval tv;
1078 THRIFT_GETTIMEOFDAY(&tv, NULL);
1079 int seed = tv.tv_sec ^ tv.tv_usec;
David Reiss65e62d32010-10-06 17:10:35 +00001080
Jake Farrell5d02b802014-01-07 21:42:01 -05001081 initrand(seed);
David Reiss35dc7692010-10-06 17:10:19 +00001082
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001083 boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
David Reiss109693c2010-10-06 17:10:42 +00001084 suite->p_name.value = "TransportTest";
Jake Farrell5d02b802014-01-07 21:42:01 -05001085 TransportTestGen transport_test_generator(suite, 1);
David Reiss35dc7692010-10-06 17:10:19 +00001086 transport_test_generator.generate();
David Reiss109693c2010-10-06 17:10:42 +00001087 return NULL;
David Reiss35dc7692010-10-06 17:10:19 +00001088}
Antonio Di Monaco796667b2016-01-04 23:05:19 +01001089#endif