blob: d6ab457e2f7c39e2c7a618cfe7f6dca0ef2de665 [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>
James E. King, III82ae9572017-08-05 12:23:54 -040029#include <thrift/stdcxx.h>
David Reiss35dc7692010-10-06 17:10:19 +000030
31#include <boost/mpl/list.hpp>
32#include <boost/shared_array.hpp>
33#include <boost/random.hpp>
34#include <boost/type_traits.hpp>
35#include <boost/test/unit_test.hpp>
Konrad Grochowskie9bdb412015-09-25 20:17:36 +020036#include <boost/version.hpp>
David Reiss35dc7692010-10-06 17:10:19 +000037
Roger Meier49ff8b12012-04-13 09:12:31 +000038#include <thrift/transport/TBufferTransports.h>
39#include <thrift/transport/TFDTransport.h>
40#include <thrift/transport/TFileTransport.h>
41#include <thrift/transport/TZlibTransport.h>
42#include <thrift/transport/TSocket.h>
David Reiss35dc7692010-10-06 17:10:19 +000043
Jake Farrell5d02b802014-01-07 21:42:01 -050044#include <thrift/concurrency/FunctionRunner.h>
45#if _WIN32
James E. King, III82ae9572017-08-05 12:23:54 -040046#include <thrift/transport/TPipe.h>
Konrad Grochowski16a23a62014-11-13 15:33:38 +010047#include <thrift/windows/TWinsockSingleton.h>
Jake Farrell5d02b802014-01-07 21:42:01 -050048#endif
49
David Reiss35dc7692010-10-06 17:10:19 +000050using namespace apache::thrift::transport;
James E. King, III82ae9572017-08-05 12:23:54 -040051using namespace apache::thrift;
David Reiss35dc7692010-10-06 17:10:19 +000052
53static boost::mt19937 rng;
David Reiss35dc7692010-10-06 17:10:19 +000054
David Reiss65e62d32010-10-06 17:10:35 +000055void initrand(unsigned int seed) {
David Reiss35dc7692010-10-06 17:10:19 +000056 rng.seed(seed);
57}
58
59class SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010060public:
David Reiss35dc7692010-10-06 17:10:19 +000061 virtual ~SizeGenerator() {}
62 virtual uint32_t nextSize() = 0;
63 virtual std::string describe() const = 0;
64};
65
66class ConstantSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010067public:
David Reiss35dc7692010-10-06 17:10:19 +000068 ConstantSizeGenerator(uint32_t value) : value_(value) {}
69 uint32_t nextSize() { return value_; }
70 std::string describe() const {
71 std::ostringstream desc;
72 desc << value_;
73 return desc.str();
74 }
75
Konrad Grochowski16a23a62014-11-13 15:33:38 +010076private:
David Reiss35dc7692010-10-06 17:10:19 +000077 uint32_t value_;
78};
79
80class RandomSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010081public:
82 RandomSizeGenerator(uint32_t min, uint32_t max)
83 : generator_(rng, boost::uniform_int<int>(min, max)) {}
David Reiss35dc7692010-10-06 17:10:19 +000084
85 uint32_t nextSize() { return generator_(); }
86
87 std::string describe() const {
88 std::ostringstream desc;
89 desc << "rand(" << getMin() << ", " << getMax() << ")";
90 return desc.str();
91 }
92
Jake Farrell5d02b802014-01-07 21:42:01 -050093 uint32_t getMin() const { return (generator_.distribution().min)(); }
94 uint32_t getMax() const { return (generator_.distribution().max)(); }
David Reiss35dc7692010-10-06 17:10:19 +000095
Konrad Grochowski16a23a62014-11-13 15:33:38 +010096private:
97 boost::variate_generator<boost::mt19937&, boost::uniform_int<int> > generator_;
David Reiss35dc7692010-10-06 17:10:19 +000098};
99
100/**
101 * This class exists solely to make the TEST_RW() macro easier to use.
102 * - it can be constructed implicitly from an integer
103 * - it can contain either a ConstantSizeGenerator or a RandomSizeGenerator
104 * (TEST_RW can't take a SizeGenerator pointer or reference, since it needs
105 * to make a copy of the generator to bind it to the test function.)
106 */
107class GenericSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100108public:
109 GenericSizeGenerator(uint32_t value) : generator_(new ConstantSizeGenerator(value)) {}
110 GenericSizeGenerator(uint32_t min, uint32_t max)
111 : generator_(new RandomSizeGenerator(min, max)) {}
David Reiss35dc7692010-10-06 17:10:19 +0000112
113 uint32_t nextSize() { return generator_->nextSize(); }
114 std::string describe() const { return generator_->describe(); }
115
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100116private:
James E. King, III82ae9572017-08-05 12:23:54 -0400117 stdcxx::shared_ptr<SizeGenerator> generator_;
David Reiss35dc7692010-10-06 17:10:19 +0000118};
119
120/**************************************************************************
121 * Classes to set up coupled transports
122 **************************************************************************/
123
David Reiss0c025e82010-10-06 17:10:36 +0000124/**
125 * Helper class to represent a coupled pair of transports.
126 *
127 * Data written to the out transport can be read from the in transport.
128 *
129 * This is used as the base class for the various coupled transport
130 * implementations. It shouldn't be instantiated directly.
131 */
David Reiss35dc7692010-10-06 17:10:19 +0000132template <class Transport_>
133class CoupledTransports {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100134public:
Roger Meier6f7681f2011-11-06 12:04:28 +0000135 virtual ~CoupledTransports() {}
David Reiss35dc7692010-10-06 17:10:19 +0000136 typedef Transport_ TransportType;
137
David Reissd4788df2010-10-06 17:10:37 +0000138 CoupledTransports() : in(), out() {}
David Reiss35dc7692010-10-06 17:10:19 +0000139
James E. King, III82ae9572017-08-05 12:23:54 -0400140 stdcxx::shared_ptr<Transport_> in;
141 stdcxx::shared_ptr<Transport_> out;
David Reiss35dc7692010-10-06 17:10:19 +0000142
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100143private:
David Reiss35dc7692010-10-06 17:10:19 +0000144 CoupledTransports(const CoupledTransports&);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100145 CoupledTransports& operator=(const CoupledTransports&);
David Reiss35dc7692010-10-06 17:10:19 +0000146};
147
David Reiss0c025e82010-10-06 17:10:36 +0000148/**
149 * Coupled TMemoryBuffers
150 */
David Reiss35dc7692010-10-06 17:10:19 +0000151class CoupledMemoryBuffers : public CoupledTransports<TMemoryBuffer> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100152public:
153 CoupledMemoryBuffers() : buf(new TMemoryBuffer) {
David Reissd4788df2010-10-06 17:10:37 +0000154 in = buf;
155 out = buf;
David Reiss35dc7692010-10-06 17:10:19 +0000156 }
157
James E. King, III82ae9572017-08-05 12:23:54 -0400158 stdcxx::shared_ptr<TMemoryBuffer> buf;
David Reissd4788df2010-10-06 17:10:37 +0000159};
160
161/**
162 * Helper template class for creating coupled transports that wrap
163 * another transport.
164 */
165template <class WrapperTransport_, class InnerCoupledTransports_>
166class CoupledWrapperTransportsT : public CoupledTransports<WrapperTransport_> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100167public:
David Reissd4788df2010-10-06 17:10:37 +0000168 CoupledWrapperTransportsT() {
169 if (inner_.in) {
170 this->in.reset(new WrapperTransport_(inner_.in));
171 }
172 if (inner_.out) {
173 this->out.reset(new WrapperTransport_(inner_.out));
174 }
175 }
176
177 InnerCoupledTransports_ inner_;
David Reiss35dc7692010-10-06 17:10:19 +0000178};
179
David Reiss0c025e82010-10-06 17:10:36 +0000180/**
181 * Coupled TBufferedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000182 */
David Reissd4788df2010-10-06 17:10:37 +0000183template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100184class CoupledBufferedTransportsT
185 : public CoupledWrapperTransportsT<TBufferedTransport, InnerTransport_> {};
David Reiss35dc7692010-10-06 17:10:19 +0000186
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100187typedef CoupledBufferedTransportsT<CoupledMemoryBuffers> CoupledBufferedTransports;
David Reissd4788df2010-10-06 17:10:37 +0000188
David Reiss0c025e82010-10-06 17:10:36 +0000189/**
190 * Coupled TFramedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000191 */
David Reissd4788df2010-10-06 17:10:37 +0000192template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100193class CoupledFramedTransportsT
194 : public CoupledWrapperTransportsT<TFramedTransport, InnerTransport_> {};
David Reiss35dc7692010-10-06 17:10:19 +0000195
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100196typedef CoupledFramedTransportsT<CoupledMemoryBuffers> CoupledFramedTransports;
David Reissd4788df2010-10-06 17:10:37 +0000197
David Reiss0c025e82010-10-06 17:10:36 +0000198/**
199 * Coupled TZlibTransports.
200 */
David Reissd4788df2010-10-06 17:10:37 +0000201template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100202class CoupledZlibTransportsT : public CoupledWrapperTransportsT<TZlibTransport, InnerTransport_> {};
David Reisse94fa332010-10-06 17:10:26 +0000203
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100204typedef CoupledZlibTransportsT<CoupledMemoryBuffers> CoupledZlibTransports;
David Reissd4788df2010-10-06 17:10:37 +0000205
Jake Farrell5d02b802014-01-07 21:42:01 -0500206#ifndef _WIN32
207// FD transport doesn't make much sense on Windows.
David Reiss0c025e82010-10-06 17:10:36 +0000208/**
209 * Coupled TFDTransports.
210 */
David Reiss35dc7692010-10-06 17:10:19 +0000211class CoupledFDTransports : public CoupledTransports<TFDTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100212public:
David Reiss35dc7692010-10-06 17:10:19 +0000213 CoupledFDTransports() {
214 int pipes[2];
215
216 if (pipe(pipes) != 0) {
217 return;
218 }
219
David Reissd4788df2010-10-06 17:10:37 +0000220 in.reset(new TFDTransport(pipes[0], TFDTransport::CLOSE_ON_DESTROY));
221 out.reset(new TFDTransport(pipes[1], TFDTransport::CLOSE_ON_DESTROY));
David Reiss35dc7692010-10-06 17:10:19 +0000222 }
223};
James E. King, III82ae9572017-08-05 12:23:54 -0400224#else
225/**
226 * Coupled pipe transports
227 */
228class CoupledPipeTransports : public CoupledTransports<TPipe> {
229public:
230 HANDLE hRead;
231 HANDLE hWrite;
232
233 CoupledPipeTransports() {
234 BOOST_REQUIRE(CreatePipe(&hRead, &hWrite, NULL, 1048576 * 2));
235 in.reset(new TPipe(hRead, hWrite));
236 in->open();
237 out = in;
238 }
239};
Jake Farrell5d02b802014-01-07 21:42:01 -0500240#endif
David Reiss35dc7692010-10-06 17:10:19 +0000241
David Reiss0c025e82010-10-06 17:10:36 +0000242/**
243 * Coupled TSockets
244 */
245class CoupledSocketTransports : public CoupledTransports<TSocket> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100246public:
David Reiss0c025e82010-10-06 17:10:36 +0000247 CoupledSocketTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500248 THRIFT_SOCKET sockets[2] = {0};
249 if (THRIFT_SOCKETPAIR(PF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
David Reiss0c025e82010-10-06 17:10:36 +0000250 return;
251 }
252
David Reissd4788df2010-10-06 17:10:37 +0000253 in.reset(new TSocket(sockets[0]));
254 out.reset(new TSocket(sockets[1]));
Roger Meier967600e2013-05-03 22:39:53 +0200255 out->setSendTimeout(100);
David Reiss0c025e82010-10-06 17:10:36 +0000256 }
257};
258
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100259// 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 -0500260#ifndef _WIN32
David Reiss0c025e82010-10-06 17:10:36 +0000261/**
262 * Coupled TFileTransports
263 */
David Reiss35dc7692010-10-06 17:10:19 +0000264class CoupledFileTransports : public CoupledTransports<TFileTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100265public:
David Reiss35dc7692010-10-06 17:10:19 +0000266 CoupledFileTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500267#ifndef _WIN32
268 const char* tmp_dir = "/tmp";
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100269#define FILENAME_SUFFIX "/thrift.transport_test"
Jake Farrell5d02b802014-01-07 21:42:01 -0500270#else
271 const char* tmp_dir = getenv("TMP");
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100272#define FILENAME_SUFFIX "\\thrift.transport_test"
Jake Farrell5d02b802014-01-07 21:42:01 -0500273#endif
274
David Reiss35dc7692010-10-06 17:10:19 +0000275 // Create a temporary file to use
Jake Farrell5d02b802014-01-07 21:42:01 -0500276 filename.resize(strlen(tmp_dir) + strlen(FILENAME_SUFFIX));
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100277 THRIFT_SNPRINTF(&filename[0], filename.size(), "%s" FILENAME_SUFFIX, tmp_dir);
278#undef FILENAME_SUFFIX
Jake Farrell5d02b802014-01-07 21:42:01 -0500279
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100280 { std::ofstream dummy_creation(filename.c_str(), std::ofstream::trunc); }
David Reiss35dc7692010-10-06 17:10:19 +0000281
David Reissd4788df2010-10-06 17:10:37 +0000282 in.reset(new TFileTransport(filename, true));
283 out.reset(new TFileTransport(filename));
David Reiss35dc7692010-10-06 17:10:19 +0000284 }
285
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100286 ~CoupledFileTransports() { remove(filename.c_str()); }
David Reiss35dc7692010-10-06 17:10:19 +0000287
Jake Farrell5d02b802014-01-07 21:42:01 -0500288 std::string filename;
David Reiss35dc7692010-10-06 17:10:19 +0000289};
Jake Farrell5d02b802014-01-07 21:42:01 -0500290#endif
David Reiss35dc7692010-10-06 17:10:19 +0000291
David Reiss0c025e82010-10-06 17:10:36 +0000292/**
293 * Wrapper around another CoupledTransports implementation that exposes the
294 * transports as TTransport pointers.
295 *
296 * This is used since accessing a transport via a "TTransport*" exercises a
297 * different code path than using the base pointer class. As part of the
298 * template code changes, most transport methods are no longer virtual.
299 */
David Reiss35dc7692010-10-06 17:10:19 +0000300template <class CoupledTransports_>
301class CoupledTTransports : public CoupledTransports<TTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100302public:
David Reiss35dc7692010-10-06 17:10:19 +0000303 CoupledTTransports() : transports() {
304 in = transports.in;
305 out = transports.out;
306 }
307
308 CoupledTransports_ transports;
309};
310
David Reiss0c025e82010-10-06 17:10:36 +0000311/**
312 * Wrapper around another CoupledTransports implementation that exposes the
313 * transports as TBufferBase pointers.
314 *
315 * This can only be instantiated with a transport type that is a subclass of
316 * TBufferBase.
317 */
David Reiss35dc7692010-10-06 17:10:19 +0000318template <class CoupledTransports_>
319class CoupledBufferBases : public CoupledTransports<TBufferBase> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100320public:
David Reiss35dc7692010-10-06 17:10:19 +0000321 CoupledBufferBases() : transports() {
322 in = transports.in;
323 out = transports.out;
324 }
325
326 CoupledTransports_ transports;
327};
328
David Reiss35dc7692010-10-06 17:10:19 +0000329/**************************************************************************
David Reisse5c435c2010-10-06 17:10:38 +0000330 * Alarm handling code for use in tests that check the transport blocking
331 * semantics.
332 *
333 * If the transport ends up blocking, we don't want to hang forever. We use
334 * SIGALRM to fire schedule signal to wake up and try to write data so the
335 * transport will unblock.
336 *
337 * It isn't really the safest thing in the world to be mucking around with
338 * complicated global data structures in a signal handler. It should probably
339 * be okay though, since we know the main thread should always be blocked in a
340 * read() request when the signal handler is running.
341 **************************************************************************/
342
343struct TriggerInfo {
James E. King, III82ae9572017-08-05 12:23:54 -0400344 TriggerInfo(int seconds, const stdcxx::shared_ptr<TTransport>& transport, uint32_t writeLength)
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100345 : timeoutSeconds(seconds), transport(transport), writeLength(writeLength), next(NULL) {}
David Reisse5c435c2010-10-06 17:10:38 +0000346
347 int timeoutSeconds;
James E. King, III82ae9572017-08-05 12:23:54 -0400348 stdcxx::shared_ptr<TTransport> transport;
David Reisse5c435c2010-10-06 17:10:38 +0000349 uint32_t writeLength;
350 TriggerInfo* next;
351};
352
Jake Farrell5d02b802014-01-07 21:42:01 -0500353apache::thrift::concurrency::Monitor g_alarm_monitor;
354TriggerInfo* g_triggerInfo;
355unsigned int g_numTriggersFired;
356bool g_teardown = false;
David Reisse5c435c2010-10-06 17:10:38 +0000357
Jake Farrell5d02b802014-01-07 21:42:01 -0500358void alarm_handler() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100359 TriggerInfo* info = NULL;
Jake Farrell5d02b802014-01-07 21:42:01 -0500360 {
361 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
362 // The alarm timed out, which almost certainly means we're stuck
363 // on a transport that is incorrectly blocked.
364 ++g_numTriggersFired;
David Reisse5c435c2010-10-06 17:10:38 +0000365
Jake Farrell5d02b802014-01-07 21:42:01 -0500366 // Note: we print messages to stdout instead of stderr, since
367 // tools/test/runner only records stdout messages in the failure messages for
368 // boost tests. (boost prints its test info to stdout.)
369 printf("Timeout alarm expired; attempting to unblock transport\n");
370 if (g_triggerInfo == NULL) {
371 printf(" trigger stack is empty!\n");
372 }
David Reisse5c435c2010-10-06 17:10:38 +0000373
Jake Farrell5d02b802014-01-07 21:42:01 -0500374 // Pop off the first TriggerInfo.
375 // If there is another one, schedule an alarm for it.
376 info = g_triggerInfo;
377 g_triggerInfo = info->next;
David Reisse5c435c2010-10-06 17:10:38 +0000378 }
379
David Reisse5c435c2010-10-06 17:10:38 +0000380 // Write some data to the transport to hopefully unblock it.
Roger Meier0069cc42010-10-13 18:10:18 +0000381 uint8_t* buf = new uint8_t[info->writeLength];
David Reisse5c435c2010-10-06 17:10:38 +0000382 memset(buf, 'b', info->writeLength);
Roger Meier0069cc42010-10-13 18:10:18 +0000383 boost::scoped_array<uint8_t> array(buf);
David Reisse5c435c2010-10-06 17:10:38 +0000384 info->transport->write(buf, info->writeLength);
385 info->transport->flush();
386
387 delete info;
388}
389
Jake Farrell5d02b802014-01-07 21:42:01 -0500390void alarm_handler_wrapper() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100391 int64_t timeout = 0; // timeout of 0 means wait forever
392 while (true) {
Jake Farrell5d02b802014-01-07 21:42:01 -0500393 bool fireHandler = false;
394 {
395 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100396 if (g_teardown)
397 return;
398 // calculate timeout
Jake Farrell5d02b802014-01-07 21:42:01 -0500399 if (g_triggerInfo == NULL) {
400 timeout = 0;
401 } else {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100402 timeout = g_triggerInfo->timeoutSeconds * 1000;
Jake Farrell5d02b802014-01-07 21:42:01 -0500403 }
404
405 int waitResult = g_alarm_monitor.waitForTimeRelative(timeout);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100406 if (waitResult == THRIFT_ETIMEDOUT)
Jake Farrell5d02b802014-01-07 21:42:01 -0500407 fireHandler = true;
408 }
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100409 if (fireHandler)
410 alarm_handler(); // calling outside the lock
David Reisse5c435c2010-10-06 17:10:38 +0000411 }
David Reisse5c435c2010-10-06 17:10:38 +0000412}
413
414/**
415 * Add a trigger to be scheduled "seconds" seconds after the
416 * last currently scheduled trigger.
417 *
418 * (Note that this is not "seconds" from now. That might be more logical, but
419 * would require slightly more complicated sorting, rather than just appending
420 * to the end.)
421 */
422void add_trigger(unsigned int seconds,
James E. King, III82ae9572017-08-05 12:23:54 -0400423 const stdcxx::shared_ptr<TTransport>& transport,
David Reisse5c435c2010-10-06 17:10:38 +0000424 uint32_t write_len) {
425 TriggerInfo* info = new TriggerInfo(seconds, transport, write_len);
Jake Farrell5d02b802014-01-07 21:42:01 -0500426 {
427 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
428 if (g_triggerInfo == NULL) {
429 // This is the first trigger.
430 // Set g_triggerInfo, and schedule the alarm
431 g_triggerInfo = info;
432 g_alarm_monitor.notify();
433 } else {
434 // Add this trigger to the end of the list
435 TriggerInfo* prev = g_triggerInfo;
436 while (prev->next) {
437 prev = prev->next;
438 }
439 prev->next = info;
David Reisse5c435c2010-10-06 17:10:38 +0000440 }
David Reisse5c435c2010-10-06 17:10:38 +0000441 }
442}
443
444void clear_triggers() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100445 TriggerInfo* info = NULL;
Jake Farrell5d02b802014-01-07 21:42:01 -0500446
447 {
448 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
449 info = g_triggerInfo;
450 g_triggerInfo = NULL;
451 g_numTriggersFired = 0;
452 g_alarm_monitor.notify();
453 }
David Reisse5c435c2010-10-06 17:10:38 +0000454
455 while (info != NULL) {
456 TriggerInfo* next = info->next;
457 delete info;
458 info = next;
459 }
460}
461
462void set_trigger(unsigned int seconds,
James E. King, III82ae9572017-08-05 12:23:54 -0400463 const stdcxx::shared_ptr<TTransport>& transport,
David Reisse5c435c2010-10-06 17:10:38 +0000464 uint32_t write_len) {
465 clear_triggers();
466 add_trigger(seconds, transport, write_len);
467}
468
469/**************************************************************************
470 * Test functions
David Reiss35dc7692010-10-06 17:10:19 +0000471 **************************************************************************/
472
473/**
474 * Test interleaved write and read calls.
475 *
476 * Generates a buffer totalSize bytes long, then writes it to the transport,
477 * and verifies the written data can be read back correctly.
478 *
479 * Mode of operation:
480 * - call wChunkGenerator to figure out how large of a chunk to write
481 * - call wSizeGenerator to get the size for individual write() calls,
482 * and do this repeatedly until the entire chunk is written.
483 * - call rChunkGenerator to figure out how large of a chunk to read
484 * - call rSizeGenerator to get the size for individual read() calls,
485 * and do this repeatedly until the entire chunk is read.
486 * - repeat until the full buffer is written and read back,
487 * then compare the data read back against the original buffer
488 *
489 *
490 * - If any of the size generators return 0, this means to use the maximum
491 * possible size.
492 *
493 * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
494 * there are never more than maxOutstanding bytes waiting to be read back.
495 */
496template <class CoupledTransports>
497void test_rw(uint32_t totalSize,
498 SizeGenerator& wSizeGenerator,
499 SizeGenerator& rSizeGenerator,
500 SizeGenerator& wChunkGenerator,
501 SizeGenerator& rChunkGenerator,
502 uint32_t maxOutstanding) {
503 CoupledTransports transports;
504 BOOST_REQUIRE(transports.in != NULL);
505 BOOST_REQUIRE(transports.out != NULL);
506
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100507 boost::shared_array<uint8_t> wbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
508 boost::shared_array<uint8_t> rbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
David Reiss35dc7692010-10-06 17:10:19 +0000509
510 // store some data in wbuf
511 for (uint32_t n = 0; n < totalSize; ++n) {
512 wbuf[n] = (n & 0xff);
513 }
514 // clear rbuf
515 memset(rbuf.get(), 0, totalSize);
516
517 uint32_t total_written = 0;
518 uint32_t total_read = 0;
519 while (total_read < totalSize) {
520 // Determine how large a chunk of data to write
521 uint32_t wchunk_size = wChunkGenerator.nextSize();
522 if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
523 wchunk_size = totalSize - total_written;
524 }
525
526 // Make sure (total_written - total_read) + wchunk_size
527 // is less than maxOutstanding
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100528 if (maxOutstanding > 0 && wchunk_size > maxOutstanding - (total_written - total_read)) {
David Reiss35dc7692010-10-06 17:10:19 +0000529 wchunk_size = maxOutstanding - (total_written - total_read);
530 }
531
532 // Write the chunk
533 uint32_t chunk_written = 0;
534 while (chunk_written < wchunk_size) {
535 uint32_t write_size = wSizeGenerator.nextSize();
536 if (write_size == 0 || write_size > wchunk_size - chunk_written) {
537 write_size = wchunk_size - chunk_written;
538 }
539
Roger Meier967600e2013-05-03 22:39:53 +0200540 try {
541 transports.out->write(wbuf.get() + total_written, write_size);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100542 } catch (TTransportException& te) {
Roger Meier967600e2013-05-03 22:39:53 +0200543 if (te.getType() == TTransportException::TIMED_OUT)
544 break;
545 throw te;
546 }
David Reiss35dc7692010-10-06 17:10:19 +0000547 chunk_written += write_size;
548 total_written += write_size;
549 }
550
551 // Flush the data, so it will be available in the read transport
552 // Don't flush if wchunk_size is 0. (This should only happen if
553 // total_written == totalSize already, and we're only reading now.)
554 if (wchunk_size > 0) {
555 transports.out->flush();
556 }
557
558 // Determine how large a chunk of data to read back
559 uint32_t rchunk_size = rChunkGenerator.nextSize();
560 if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
561 rchunk_size = total_written - total_read;
562 }
563
564 // Read the chunk
565 uint32_t chunk_read = 0;
566 while (chunk_read < rchunk_size) {
567 uint32_t read_size = rSizeGenerator.nextSize();
568 if (read_size == 0 || read_size > rchunk_size - chunk_read) {
569 read_size = rchunk_size - chunk_read;
570 }
571
David Reisse94fa332010-10-06 17:10:26 +0000572 int bytes_read = -1;
573 try {
574 bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
575 } catch (TTransportException& e) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100576 BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size << ") threw exception \""
577 << e.what() << "\"; written so far: " << total_written << " / "
578 << totalSize << " bytes");
David Reisse94fa332010-10-06 17:10:26 +0000579 }
580
David Reiss35dc7692010-10-06 17:10:19 +0000581 BOOST_REQUIRE_MESSAGE(bytes_read > 0,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100582 "read(pos=" << total_read << ", size=" << read_size << ") returned "
583 << bytes_read << "; written so far: " << total_written
584 << " / " << totalSize << " bytes");
David Reiss35dc7692010-10-06 17:10:19 +0000585 chunk_read += bytes_read;
586 total_read += bytes_read;
587 }
588 }
589
590 // make sure the data read back is identical to the data written
591 BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
592}
593
David Reisse5c435c2010-10-06 17:10:38 +0000594template <class CoupledTransports>
595void test_read_part_available() {
596 CoupledTransports transports;
597 BOOST_REQUIRE(transports.in != NULL);
598 BOOST_REQUIRE(transports.out != NULL);
599
600 uint8_t write_buf[16];
601 uint8_t read_buf[16];
602 memset(write_buf, 'a', sizeof(write_buf));
603
604 // Attemping to read 10 bytes when only 9 are available should return 9
605 // immediately.
606 transports.out->write(write_buf, 9);
607 transports.out->flush();
608 set_trigger(3, transports.out, 1);
609 uint32_t bytes_read = transports.in->read(read_buf, 10);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100610 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
611 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)9);
David Reisse5c435c2010-10-06 17:10:38 +0000612
613 clear_triggers();
614}
615
616template <class CoupledTransports>
Roger Meier02c827b2012-04-11 21:59:57 +0000617void test_read_part_available_in_chunks() {
618 CoupledTransports transports;
619 BOOST_REQUIRE(transports.in != NULL);
620 BOOST_REQUIRE(transports.out != NULL);
621
622 uint8_t write_buf[16];
623 uint8_t read_buf[16];
624 memset(write_buf, 'a', sizeof(write_buf));
625
626 // Write 10 bytes (in a single frame, for transports that use framing)
627 transports.out->write(write_buf, 10);
628 transports.out->flush();
629
630 // Read 1 byte, to force the transport to read the frame
631 uint32_t bytes_read = transports.in->read(read_buf, 1);
Jake Farrell5d02b802014-01-07 21:42:01 -0500632 BOOST_CHECK_EQUAL(bytes_read, 1u);
Roger Meier02c827b2012-04-11 21:59:57 +0000633
634 // Read more than what is remaining and verify the transport does not block
635 set_trigger(3, transports.out, 1);
636 bytes_read = transports.in->read(read_buf, 10);
Jake Farrell5d02b802014-01-07 21:42:01 -0500637 BOOST_CHECK_EQUAL(g_numTriggersFired, 0u);
638 BOOST_CHECK_EQUAL(bytes_read, 9u);
Roger Meier02c827b2012-04-11 21:59:57 +0000639
640 clear_triggers();
641}
642
643template <class CoupledTransports>
David Reiss0a2d81e2010-10-06 17:10:40 +0000644void test_read_partial_midframe() {
645 CoupledTransports transports;
646 BOOST_REQUIRE(transports.in != NULL);
647 BOOST_REQUIRE(transports.out != NULL);
648
649 uint8_t write_buf[16];
650 uint8_t read_buf[16];
651 memset(write_buf, 'a', sizeof(write_buf));
652
653 // Attempt to read 10 bytes, when only 9 are available, but after we have
654 // already read part of the data that is available. This exercises a
655 // different code path for several of the transports.
656 //
657 // For transports that add their own framing (e.g., TFramedTransport and
658 // TFileTransport), the two flush calls break up the data in to a 10 byte
659 // frame and a 3 byte frame. The first read then puts us partway through the
660 // first frame, and then we attempt to read past the end of that frame, and
661 // through the next frame, too.
662 //
663 // For buffered transports that perform read-ahead (e.g.,
664 // TBufferedTransport), the read-ahead will most likely see all 13 bytes
665 // written on the first read. The next read will then attempt to read past
666 // the end of the read-ahead buffer.
667 //
668 // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
669 // transports that track framing internally.
670 transports.out->write(write_buf, 10);
671 transports.out->flush();
672 transports.out->write(write_buf, 3);
673 transports.out->flush();
674
675 // Now read 4 bytes, so that we are partway through the written data.
676 uint32_t bytes_read = transports.in->read(read_buf, 4);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100677 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)4);
David Reiss0a2d81e2010-10-06 17:10:40 +0000678
679 // Now attempt to read 10 bytes. Only 9 more are available.
680 //
681 // We should be able to get all 9 bytes, but it might take multiple read
682 // calls, since it is valid for read() to return fewer bytes than requested.
683 // (Most transports do immediately return 9 bytes, but the framing transports
684 // tend to only return to the end of the current frame, which is 6 bytes in
685 // this case.)
686 uint32_t total_read = 0;
687 while (total_read < 9) {
688 set_trigger(3, transports.out, 1);
689 bytes_read = transports.in->read(read_buf, 10);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100690 BOOST_REQUIRE_EQUAL(g_numTriggersFired, (unsigned int)0);
691 BOOST_REQUIRE_GT(bytes_read, (uint32_t)0);
David Reiss0a2d81e2010-10-06 17:10:40 +0000692 total_read += bytes_read;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100693 BOOST_REQUIRE_LE(total_read, (uint32_t)9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000694 }
695
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100696 BOOST_CHECK_EQUAL(total_read, (uint32_t)9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000697
698 clear_triggers();
699}
700
701template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000702void test_borrow_part_available() {
703 CoupledTransports transports;
704 BOOST_REQUIRE(transports.in != NULL);
705 BOOST_REQUIRE(transports.out != NULL);
706
707 uint8_t write_buf[16];
708 uint8_t read_buf[16];
709 memset(write_buf, 'a', sizeof(write_buf));
710
711 // Attemping to borrow 10 bytes when only 9 are available should return NULL
712 // immediately.
713 transports.out->write(write_buf, 9);
714 transports.out->flush();
715 set_trigger(3, transports.out, 1);
716 uint32_t borrow_len = 10;
717 const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100718 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000719 BOOST_CHECK(borrowed_buf == NULL);
720
721 clear_triggers();
722}
723
724template <class CoupledTransports>
725void test_read_none_available() {
726 CoupledTransports transports;
727 BOOST_REQUIRE(transports.in != NULL);
728 BOOST_REQUIRE(transports.out != NULL);
729
730 uint8_t write_buf[16];
731 uint8_t read_buf[16];
732 memset(write_buf, 'a', sizeof(write_buf));
733
734 // Attempting to read when no data is available should either block until
735 // some data is available, or fail immediately. (e.g., TSocket blocks,
736 // TMemoryBuffer just fails.)
737 //
738 // If the transport blocks, it should succeed once some data is available,
739 // even if less than the amount requested becomes available.
740 set_trigger(1, transports.out, 2);
741 add_trigger(1, transports.out, 8);
742 uint32_t bytes_read = transports.in->read(read_buf, 10);
743 if (bytes_read == 0) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100744 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000745 clear_triggers();
746 } else {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100747 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)1);
748 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)2);
David Reisse5c435c2010-10-06 17:10:38 +0000749 }
750
751 clear_triggers();
752}
753
754template <class CoupledTransports>
755void test_borrow_none_available() {
756 CoupledTransports transports;
757 BOOST_REQUIRE(transports.in != NULL);
758 BOOST_REQUIRE(transports.out != NULL);
759
760 uint8_t write_buf[16];
761 memset(write_buf, 'a', sizeof(write_buf));
762
763 // Attempting to borrow when no data is available should fail immediately
764 set_trigger(1, transports.out, 10);
765 uint32_t borrow_len = 10;
766 const uint8_t* borrowed_buf = transports.in->borrow(NULL, &borrow_len);
767 BOOST_CHECK(borrowed_buf == NULL);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100768 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000769
770 clear_triggers();
771}
772
David Reiss35dc7692010-10-06 17:10:19 +0000773/**************************************************************************
774 * Test case generation
775 *
776 * Pretty ugly and annoying. This would be much easier if we the unit test
777 * framework didn't force each test to be a separate function.
778 * - Writing a completely separate function definition for each of these would
779 * result in a lot of repetitive boilerplate code.
780 * - Combining many tests into a single function makes it more difficult to
781 * tell precisely which tests failed. It also means you can't get a progress
782 * update after each test, and the tests are already fairly slow.
Konrad Grochowski3b5dacb2014-11-24 10:55:31 +0100783 * - Similar registration could be achieved with BOOST_TEST_CASE_TEMPLATE,
David Reiss35dc7692010-10-06 17:10:19 +0000784 * but it requires a lot of awkward MPL code, and results in useless test
785 * case names. (The names are generated from std::type_info::name(), which
786 * is compiler-dependent. gcc returns mangled names.)
787 **************************************************************************/
788
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100789#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
790 addTestRW<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports), totalSize, ##__VA_ARGS__);
David Reissd4788df2010-10-06 17:10:37 +0000791
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100792#define TEST_RW(CoupledTransports, totalSize, ...) \
793 do { \
794 /* Add the test as specified, to test the non-virtual function calls */ \
795 ADD_TEST_RW(CoupledTransports, totalSize, ##__VA_ARGS__); \
796 /* \
797 * Also test using the transport as a TTransport*, to test \
798 * the read_virt()/write_virt() calls \
799 */ \
800 ADD_TEST_RW(CoupledTTransports<CoupledTransports>, totalSize, ##__VA_ARGS__); \
801 /* Test wrapping the transport with TBufferedTransport */ \
802 ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
803 /* Test wrapping the transport with TFramedTransports */ \
804 ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
805 /* Test wrapping the transport with TZlibTransport */ \
806 ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000807 } while (0)
808
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100809#define ADD_TEST_BLOCKING(CoupledTransports) \
810 addTestBlocking<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports));
David Reisse5c435c2010-10-06 17:10:38 +0000811
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100812#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
813 ADD_TEST_BLOCKING(CoupledTransports); \
814 ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
815 ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
816 ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
David Reisse5c435c2010-10-06 17:10:38 +0000817 ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
818
David Reiss35dc7692010-10-06 17:10:19 +0000819class TransportTestGen {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100820public:
821 TransportTestGen(boost::unit_test::test_suite* suite, float sizeMultiplier)
822 : suite_(suite), sizeMultiplier_(sizeMultiplier) {}
David Reiss35dc7692010-10-06 17:10:19 +0000823
824 void generate() {
825 GenericSizeGenerator rand4k(1, 4096);
826
827 /*
828 * We do the basically the same set of tests for each transport type,
829 * although we tweak the parameters in some places.
830 */
831
David Reissd4788df2010-10-06 17:10:37 +0000832 // TMemoryBuffer tests
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100833 TEST_RW(CoupledMemoryBuffers, 1024 * 1024, 0, 0);
834 TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k);
835 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163);
836 TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000837
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100838 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 0, 0, rand4k, rand4k);
839 TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
840 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163, rand4k, rand4k);
841 TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1, rand4k, rand4k);
David Reisse94fa332010-10-06 17:10:26 +0000842
David Reisse5c435c2010-10-06 17:10:38 +0000843 TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
844
Jake Farrell5d02b802014-01-07 21:42:01 -0500845#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000846 // TFDTransport tests
847 // Since CoupledFDTransports tests with a pipe, writes will block
848 // if there is too much outstanding unread data in the pipe.
849 uint32_t fd_max_outstanding = 4096;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100850 TEST_RW(CoupledFDTransports, 1024 * 1024, 0, 0, 0, 0, fd_max_outstanding);
851 TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, 0, 0, fd_max_outstanding);
852 TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, 0, 0, fd_max_outstanding);
853 TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, 0, 0, fd_max_outstanding);
David Reiss35dc7692010-10-06 17:10:19 +0000854
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100855 TEST_RW(CoupledFDTransports, 1024 * 256, 0, 0, rand4k, rand4k, fd_max_outstanding);
856 TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k, fd_max_outstanding);
857 TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, rand4k, rand4k, fd_max_outstanding);
858 TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, rand4k, rand4k, fd_max_outstanding);
David Reiss35dc7692010-10-06 17:10:19 +0000859
David Reisse5c435c2010-10-06 17:10:38 +0000860 TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
James E. King, III82ae9572017-08-05 12:23:54 -0400861#else
862 // TPipe tests (WIN32 only)
863 TEST_RW(CoupledPipeTransports, 1024 * 1024, 0, 0);
864 TEST_RW(CoupledPipeTransports, 1024 * 256, rand4k, rand4k);
865 TEST_RW(CoupledPipeTransports, 1024 * 256, 167, 163);
866 TEST_RW(CoupledPipeTransports, 1024 * 16, 1, 1);
867
868 TEST_RW(CoupledPipeTransports, 1024 * 256, 0, 0, rand4k, rand4k);
869 TEST_RW(CoupledPipeTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
870 TEST_RW(CoupledPipeTransports, 1024 * 256, 167, 163, rand4k, rand4k);
871 TEST_RW(CoupledPipeTransports, 1024 * 16, 1, 1, rand4k, rand4k);
872
873 TEST_BLOCKING_BEHAVIOR(CoupledPipeTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500874#endif //_WIN32
David Reisse5c435c2010-10-06 17:10:38 +0000875
David Reiss0c025e82010-10-06 17:10:36 +0000876 // TSocket tests
877 uint32_t socket_max_outstanding = 4096;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100878 TEST_RW(CoupledSocketTransports, 1024 * 1024, 0, 0, 0, 0, socket_max_outstanding);
879 TEST_RW(CoupledSocketTransports, 1024 * 256, rand4k, rand4k, 0, 0, socket_max_outstanding);
880 TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, 0, 0, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000881 // Doh. Apparently writing to a socket has some additional overhead for
882 // each send() call. If we have more than ~400 outstanding 1-byte write
883 // requests, additional send() calls start blocking.
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100884 TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, 0, 0, socket_max_outstanding);
885 TEST_RW(CoupledSocketTransports, 1024 * 256, 0, 0, rand4k, rand4k, socket_max_outstanding);
886 TEST_RW(CoupledSocketTransports,
887 1024 * 256,
888 rand4k,
889 rand4k,
890 rand4k,
891 rand4k,
892 socket_max_outstanding);
893 TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, rand4k, rand4k, socket_max_outstanding);
894 TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, rand4k, rand4k, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000895
David Reisse5c435c2010-10-06 17:10:38 +0000896 TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
897
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100898// 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 -0500899#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000900 // TFileTransport tests
901 // We use smaller buffer sizes here, since TFileTransport is fairly slow.
902 //
903 // TFileTransport can't write more than 16MB at once
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100904 uint32_t max_write_at_once = 1024 * 1024 * 16 - 4;
905 TEST_RW(CoupledFileTransports, 1024 * 1024, max_write_at_once, 0);
906 TEST_RW(CoupledFileTransports, 1024 * 128, rand4k, rand4k);
907 TEST_RW(CoupledFileTransports, 1024 * 128, 167, 163);
908 TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000909
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100910 TEST_RW(CoupledFileTransports, 1024 * 64, 0, 0, rand4k, rand4k);
911 TEST_RW(CoupledFileTransports, 1024 * 64, rand4k, rand4k, rand4k, rand4k);
912 TEST_RW(CoupledFileTransports, 1024 * 64, 167, 163, rand4k, rand4k);
913 TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000914
David Reisse5c435c2010-10-06 17:10:38 +0000915 TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500916#endif
David Reisse5c435c2010-10-06 17:10:38 +0000917
David Reissd4788df2010-10-06 17:10:37 +0000918 // Add some tests that access TBufferedTransport and TFramedTransport
919 // via TTransport pointers and TBufferBase pointers.
David Reisse5c435c2010-10-06 17:10:38 +0000920 ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100921 1024 * 1024,
922 rand4k,
923 rand4k,
924 rand4k,
925 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000926 ADD_TEST_RW(CoupledBufferBases<CoupledBufferedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100927 1024 * 1024,
928 rand4k,
929 rand4k,
930 rand4k,
931 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000932 ADD_TEST_RW(CoupledTTransports<CoupledFramedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100933 1024 * 1024,
934 rand4k,
935 rand4k,
936 rand4k,
937 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000938 ADD_TEST_RW(CoupledBufferBases<CoupledFramedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100939 1024 * 1024,
940 rand4k,
941 rand4k,
942 rand4k,
943 rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000944
945 // Test using TZlibTransport via a TTransport pointer
David Reisse5c435c2010-10-06 17:10:38 +0000946 ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100947 1024 * 1024,
948 rand4k,
949 rand4k,
950 rand4k,
951 rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000952 }
953
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200954#if (BOOST_VERSION >= 105900)
955#define MAKE_TEST_CASE(_FUNC, _NAME) boost::unit_test::make_test_case(_FUNC, _NAME, __FILE__, __LINE__)
956#else
957#define MAKE_TEST_CASE(_FUNC, _NAME) boost::unit_test::make_test_case(_FUNC, _NAME)
958#endif
959
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100960private:
David Reiss35dc7692010-10-06 17:10:19 +0000961 template <class CoupledTransports>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100962 void addTestRW(const char* transport_name,
963 uint32_t totalSize,
964 GenericSizeGenerator wSizeGen,
965 GenericSizeGenerator rSizeGen,
David Reisse5c435c2010-10-06 17:10:38 +0000966 GenericSizeGenerator wChunkSizeGen = 0,
967 GenericSizeGenerator rChunkSizeGen = 0,
968 uint32_t maxOutstanding = 0,
969 uint32_t expectedFailures = 0) {
David Reiss65e62d32010-10-06 17:10:35 +0000970 // adjust totalSize by the specified sizeMultiplier_ first
971 totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
972
David Reiss35dc7692010-10-06 17:10:19 +0000973 std::ostringstream name;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100974 name << transport_name << "::test_rw(" << totalSize << ", " << wSizeGen.describe() << ", "
975 << rSizeGen.describe() << ", " << wChunkSizeGen.describe() << ", "
976 << rChunkSizeGen.describe() << ", " << maxOutstanding << ")";
David Reiss35dc7692010-10-06 17:10:19 +0000977
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200978#if (BOOST_VERSION >= 105900)
James E. King, III82ae9572017-08-05 12:23:54 -0400979 stdcxx::function<void ()> test_func
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200980#else
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100981 boost::unit_test::callback0<> test_func
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200982#endif
James E. King, III82ae9572017-08-05 12:23:54 -0400983 = stdcxx::bind(test_rw<CoupledTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100984 totalSize,
985 wSizeGen,
986 rSizeGen,
987 wChunkSizeGen,
988 rChunkSizeGen,
989 maxOutstanding);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200990 suite_->add(MAKE_TEST_CASE(test_func, name.str()), expectedFailures);
Roger Meier0069cc42010-10-13 18:10:18 +0000991 }
David Reiss35dc7692010-10-06 17:10:19 +0000992
David Reisse5c435c2010-10-06 17:10:38 +0000993 template <class CoupledTransports>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100994 void addTestBlocking(const char* transportName, uint32_t expectedFailures = 0) {
David Reisse5c435c2010-10-06 17:10:38 +0000995 char name[1024];
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()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200998 suite_->add(MAKE_TEST_CASE(test_read_part_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +0000999
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001000 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available_in_chunks()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001001 suite_->add(MAKE_TEST_CASE(test_read_part_available_in_chunks<CoupledTransports>, name), expectedFailures);
Roger Meier02c827b2012-04-11 21:59:57 +00001002
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001003 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_partial_midframe()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001004 suite_->add(MAKE_TEST_CASE(test_read_partial_midframe<CoupledTransports>, name), expectedFailures);
David Reiss0a2d81e2010-10-06 17:10:40 +00001005
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001006 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_none_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001007 suite_->add(MAKE_TEST_CASE(test_read_none_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_part_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001010 suite_->add(MAKE_TEST_CASE(test_borrow_part_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +00001011
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001012 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_none_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001013 suite_->add(MAKE_TEST_CASE(test_borrow_none_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +00001014 }
1015
David Reiss35dc7692010-10-06 17:10:19 +00001016 boost::unit_test::test_suite* suite_;
David Reiss65e62d32010-10-06 17:10:35 +00001017 // sizeMultiplier_ is configurable via the command line, and allows the
1018 // user to adjust between smaller buffers that can be tested quickly,
1019 // or larger buffers that more thoroughly exercise the code, but take
1020 // longer.
1021 float sizeMultiplier_;
David Reiss35dc7692010-10-06 17:10:19 +00001022};
1023
1024/**************************************************************************
1025 * General Initialization
1026 **************************************************************************/
1027
Jake Farrell5d02b802014-01-07 21:42:01 -05001028struct global_fixture {
James E. King, III82ae9572017-08-05 12:23:54 -04001029 stdcxx::shared_ptr<apache::thrift::concurrency::Thread> alarmThread_;
Jake Farrell5d02b802014-01-07 21:42:01 -05001030 global_fixture() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001031#if _WIN32
Jake Farrell5d02b802014-01-07 21:42:01 -05001032 apache::thrift::transport::TWinsockSingleton::create();
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001033#endif
David Reiss35dc7692010-10-06 17:10:19 +00001034
Jake Farrell5d02b802014-01-07 21:42:01 -05001035 apache::thrift::concurrency::PlatformThreadFactory factory;
1036 factory.setDetached(false);
1037
1038 alarmThread_ = factory.newThread(
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001039 apache::thrift::concurrency::FunctionRunner::create(alarm_handler_wrapper));
Jake Farrell5d02b802014-01-07 21:42:01 -05001040 alarmThread_->start();
1041 }
1042 ~global_fixture() {
1043 {
1044 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
1045 g_teardown = true;
1046 g_alarm_monitor.notify();
1047 }
1048 alarmThread_->join();
1049 }
David Reiss65e62d32010-10-06 17:10:35 +00001050};
1051
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001052#if (BOOST_VERSION >= 105900)
1053BOOST_GLOBAL_FIXTURE(global_fixture);
1054#else
Jake Farrell5d02b802014-01-07 21:42:01 -05001055BOOST_GLOBAL_FIXTURE(global_fixture)
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001056#endif
David Reiss35dc7692010-10-06 17:10:19 +00001057
Antonio Di Monaco796667b2016-01-04 23:05:19 +01001058#ifdef BOOST_TEST_DYN_LINK
1059bool init_unit_test_suite() {
1060 struct timeval tv;
1061 THRIFT_GETTIMEOFDAY(&tv, NULL);
1062 int seed = tv.tv_sec ^ tv.tv_usec;
1063
1064 initrand(seed);
1065
1066 boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
1067 suite->p_name.value = "TransportTest";
1068 TransportTestGen transport_test_generator(suite, 1);
1069 transport_test_generator.generate();
1070 return true;
1071}
1072
1073int main( int argc, char* argv[] ) {
1074 return ::boost::unit_test::unit_test_main(&init_unit_test_suite,argc,argv);
1075}
1076#else
David Reiss35dc7692010-10-06 17:10:19 +00001077boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
Konrad Grochowskib3f5ffc2014-11-06 19:32:59 +01001078 THRIFT_UNUSED_VARIABLE(argc);
1079 THRIFT_UNUSED_VARIABLE(argv);
Jake Farrell5d02b802014-01-07 21:42:01 -05001080 struct timeval tv;
1081 THRIFT_GETTIMEOFDAY(&tv, NULL);
1082 int seed = tv.tv_sec ^ tv.tv_usec;
David Reiss65e62d32010-10-06 17:10:35 +00001083
Jake Farrell5d02b802014-01-07 21:42:01 -05001084 initrand(seed);
David Reiss35dc7692010-10-06 17:10:19 +00001085
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001086 boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
David Reiss109693c2010-10-06 17:10:42 +00001087 suite->p_name.value = "TransportTest";
Jake Farrell5d02b802014-01-07 21:42:01 -05001088 TransportTestGen transport_test_generator(suite, 1);
David Reiss35dc7692010-10-06 17:10:19 +00001089 transport_test_generator.generate();
David Reiss109693c2010-10-06 17:10:42 +00001090 return NULL;
David Reiss35dc7692010-10-06 17:10:19 +00001091}
Antonio Di Monaco796667b2016-01-04 23:05:19 +01001092#endif