blob: 7bd8aa0febb55e7b8534fd7d522e9eef825b7552 [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
20#include "config.h"
21
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>
29#include <thrift/cxxfunctional.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>
36
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
Konrad Grochowski16a23a62014-11-13 15:33:38 +010045#include <thrift/windows/TWinsockSingleton.h>
Jake Farrell5d02b802014-01-07 21:42:01 -050046#endif
47
David Reiss35dc7692010-10-06 17:10:19 +000048using namespace apache::thrift::transport;
49
50static boost::mt19937 rng;
David Reiss35dc7692010-10-06 17:10:19 +000051
David Reiss65e62d32010-10-06 17:10:35 +000052void initrand(unsigned int seed) {
David Reiss35dc7692010-10-06 17:10:19 +000053 rng.seed(seed);
54}
55
56class SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010057public:
David Reiss35dc7692010-10-06 17:10:19 +000058 virtual ~SizeGenerator() {}
59 virtual uint32_t nextSize() = 0;
60 virtual std::string describe() const = 0;
61};
62
63class ConstantSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010064public:
David Reiss35dc7692010-10-06 17:10:19 +000065 ConstantSizeGenerator(uint32_t value) : value_(value) {}
66 uint32_t nextSize() { return value_; }
67 std::string describe() const {
68 std::ostringstream desc;
69 desc << value_;
70 return desc.str();
71 }
72
Konrad Grochowski16a23a62014-11-13 15:33:38 +010073private:
David Reiss35dc7692010-10-06 17:10:19 +000074 uint32_t value_;
75};
76
77class RandomSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010078public:
79 RandomSizeGenerator(uint32_t min, uint32_t max)
80 : generator_(rng, boost::uniform_int<int>(min, max)) {}
David Reiss35dc7692010-10-06 17:10:19 +000081
82 uint32_t nextSize() { return generator_(); }
83
84 std::string describe() const {
85 std::ostringstream desc;
86 desc << "rand(" << getMin() << ", " << getMax() << ")";
87 return desc.str();
88 }
89
Jake Farrell5d02b802014-01-07 21:42:01 -050090 uint32_t getMin() const { return (generator_.distribution().min)(); }
91 uint32_t getMax() const { return (generator_.distribution().max)(); }
David Reiss35dc7692010-10-06 17:10:19 +000092
Konrad Grochowski16a23a62014-11-13 15:33:38 +010093private:
94 boost::variate_generator<boost::mt19937&, boost::uniform_int<int> > generator_;
David Reiss35dc7692010-10-06 17:10:19 +000095};
96
97/**
98 * This class exists solely to make the TEST_RW() macro easier to use.
99 * - it can be constructed implicitly from an integer
100 * - it can contain either a ConstantSizeGenerator or a RandomSizeGenerator
101 * (TEST_RW can't take a SizeGenerator pointer or reference, since it needs
102 * to make a copy of the generator to bind it to the test function.)
103 */
104class GenericSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100105public:
106 GenericSizeGenerator(uint32_t value) : generator_(new ConstantSizeGenerator(value)) {}
107 GenericSizeGenerator(uint32_t min, uint32_t max)
108 : generator_(new RandomSizeGenerator(min, max)) {}
David Reiss35dc7692010-10-06 17:10:19 +0000109
110 uint32_t nextSize() { return generator_->nextSize(); }
111 std::string describe() const { return generator_->describe(); }
112
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100113private:
David Reiss35dc7692010-10-06 17:10:19 +0000114 boost::shared_ptr<SizeGenerator> generator_;
115};
116
117/**************************************************************************
118 * Classes to set up coupled transports
119 **************************************************************************/
120
David Reiss0c025e82010-10-06 17:10:36 +0000121/**
122 * Helper class to represent a coupled pair of transports.
123 *
124 * Data written to the out transport can be read from the in transport.
125 *
126 * This is used as the base class for the various coupled transport
127 * implementations. It shouldn't be instantiated directly.
128 */
David Reiss35dc7692010-10-06 17:10:19 +0000129template <class Transport_>
130class CoupledTransports {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100131public:
Roger Meier6f7681f2011-11-06 12:04:28 +0000132 virtual ~CoupledTransports() {}
David Reiss35dc7692010-10-06 17:10:19 +0000133 typedef Transport_ TransportType;
134
David Reissd4788df2010-10-06 17:10:37 +0000135 CoupledTransports() : in(), out() {}
David Reiss35dc7692010-10-06 17:10:19 +0000136
David Reissd4788df2010-10-06 17:10:37 +0000137 boost::shared_ptr<Transport_> in;
138 boost::shared_ptr<Transport_> out;
David Reiss35dc7692010-10-06 17:10:19 +0000139
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100140private:
David Reiss35dc7692010-10-06 17:10:19 +0000141 CoupledTransports(const CoupledTransports&);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100142 CoupledTransports& operator=(const CoupledTransports&);
David Reiss35dc7692010-10-06 17:10:19 +0000143};
144
David Reiss0c025e82010-10-06 17:10:36 +0000145/**
146 * Coupled TMemoryBuffers
147 */
David Reiss35dc7692010-10-06 17:10:19 +0000148class CoupledMemoryBuffers : public CoupledTransports<TMemoryBuffer> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100149public:
150 CoupledMemoryBuffers() : buf(new TMemoryBuffer) {
David Reissd4788df2010-10-06 17:10:37 +0000151 in = buf;
152 out = buf;
David Reiss35dc7692010-10-06 17:10:19 +0000153 }
154
David Reissd4788df2010-10-06 17:10:37 +0000155 boost::shared_ptr<TMemoryBuffer> buf;
156};
157
158/**
159 * Helper template class for creating coupled transports that wrap
160 * another transport.
161 */
162template <class WrapperTransport_, class InnerCoupledTransports_>
163class CoupledWrapperTransportsT : public CoupledTransports<WrapperTransport_> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100164public:
David Reissd4788df2010-10-06 17:10:37 +0000165 CoupledWrapperTransportsT() {
166 if (inner_.in) {
167 this->in.reset(new WrapperTransport_(inner_.in));
168 }
169 if (inner_.out) {
170 this->out.reset(new WrapperTransport_(inner_.out));
171 }
172 }
173
174 InnerCoupledTransports_ inner_;
David Reiss35dc7692010-10-06 17:10:19 +0000175};
176
David Reiss0c025e82010-10-06 17:10:36 +0000177/**
178 * Coupled TBufferedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000179 */
David Reissd4788df2010-10-06 17:10:37 +0000180template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100181class CoupledBufferedTransportsT
182 : public CoupledWrapperTransportsT<TBufferedTransport, InnerTransport_> {};
David Reiss35dc7692010-10-06 17:10:19 +0000183
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100184typedef CoupledBufferedTransportsT<CoupledMemoryBuffers> CoupledBufferedTransports;
David Reissd4788df2010-10-06 17:10:37 +0000185
David Reiss0c025e82010-10-06 17:10:36 +0000186/**
187 * Coupled TFramedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000188 */
David Reissd4788df2010-10-06 17:10:37 +0000189template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100190class CoupledFramedTransportsT
191 : public CoupledWrapperTransportsT<TFramedTransport, InnerTransport_> {};
David Reiss35dc7692010-10-06 17:10:19 +0000192
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100193typedef CoupledFramedTransportsT<CoupledMemoryBuffers> CoupledFramedTransports;
David Reissd4788df2010-10-06 17:10:37 +0000194
David Reiss0c025e82010-10-06 17:10:36 +0000195/**
196 * Coupled TZlibTransports.
197 */
David Reissd4788df2010-10-06 17:10:37 +0000198template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100199class CoupledZlibTransportsT : public CoupledWrapperTransportsT<TZlibTransport, InnerTransport_> {};
David Reisse94fa332010-10-06 17:10:26 +0000200
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100201typedef CoupledZlibTransportsT<CoupledMemoryBuffers> CoupledZlibTransports;
David Reissd4788df2010-10-06 17:10:37 +0000202
Jake Farrell5d02b802014-01-07 21:42:01 -0500203#ifndef _WIN32
204// FD transport doesn't make much sense on Windows.
David Reiss0c025e82010-10-06 17:10:36 +0000205/**
206 * Coupled TFDTransports.
207 */
David Reiss35dc7692010-10-06 17:10:19 +0000208class CoupledFDTransports : public CoupledTransports<TFDTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100209public:
David Reiss35dc7692010-10-06 17:10:19 +0000210 CoupledFDTransports() {
211 int pipes[2];
212
213 if (pipe(pipes) != 0) {
214 return;
215 }
216
David Reissd4788df2010-10-06 17:10:37 +0000217 in.reset(new TFDTransport(pipes[0], TFDTransport::CLOSE_ON_DESTROY));
218 out.reset(new TFDTransport(pipes[1], TFDTransport::CLOSE_ON_DESTROY));
David Reiss35dc7692010-10-06 17:10:19 +0000219 }
220};
Jake Farrell5d02b802014-01-07 21:42:01 -0500221#endif
David Reiss35dc7692010-10-06 17:10:19 +0000222
David Reiss0c025e82010-10-06 17:10:36 +0000223/**
224 * Coupled TSockets
225 */
226class CoupledSocketTransports : public CoupledTransports<TSocket> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100227public:
David Reiss0c025e82010-10-06 17:10:36 +0000228 CoupledSocketTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500229 THRIFT_SOCKET sockets[2] = {0};
230 if (THRIFT_SOCKETPAIR(PF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
David Reiss0c025e82010-10-06 17:10:36 +0000231 return;
232 }
233
David Reissd4788df2010-10-06 17:10:37 +0000234 in.reset(new TSocket(sockets[0]));
235 out.reset(new TSocket(sockets[1]));
Roger Meier967600e2013-05-03 22:39:53 +0200236 out->setSendTimeout(100);
David Reiss0c025e82010-10-06 17:10:36 +0000237 }
238};
239
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100240// 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 -0500241#ifndef _WIN32
David Reiss0c025e82010-10-06 17:10:36 +0000242/**
243 * Coupled TFileTransports
244 */
David Reiss35dc7692010-10-06 17:10:19 +0000245class CoupledFileTransports : public CoupledTransports<TFileTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100246public:
David Reiss35dc7692010-10-06 17:10:19 +0000247 CoupledFileTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500248#ifndef _WIN32
249 const char* tmp_dir = "/tmp";
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100250#define FILENAME_SUFFIX "/thrift.transport_test"
Jake Farrell5d02b802014-01-07 21:42:01 -0500251#else
252 const char* tmp_dir = getenv("TMP");
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100253#define FILENAME_SUFFIX "\\thrift.transport_test"
Jake Farrell5d02b802014-01-07 21:42:01 -0500254#endif
255
David Reiss35dc7692010-10-06 17:10:19 +0000256 // Create a temporary file to use
Jake Farrell5d02b802014-01-07 21:42:01 -0500257 filename.resize(strlen(tmp_dir) + strlen(FILENAME_SUFFIX));
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100258 THRIFT_SNPRINTF(&filename[0], filename.size(), "%s" FILENAME_SUFFIX, tmp_dir);
259#undef FILENAME_SUFFIX
Jake Farrell5d02b802014-01-07 21:42:01 -0500260
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100261 { std::ofstream dummy_creation(filename.c_str(), std::ofstream::trunc); }
David Reiss35dc7692010-10-06 17:10:19 +0000262
David Reissd4788df2010-10-06 17:10:37 +0000263 in.reset(new TFileTransport(filename, true));
264 out.reset(new TFileTransport(filename));
David Reiss35dc7692010-10-06 17:10:19 +0000265 }
266
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100267 ~CoupledFileTransports() { remove(filename.c_str()); }
David Reiss35dc7692010-10-06 17:10:19 +0000268
Jake Farrell5d02b802014-01-07 21:42:01 -0500269 std::string filename;
David Reiss35dc7692010-10-06 17:10:19 +0000270};
Jake Farrell5d02b802014-01-07 21:42:01 -0500271#endif
David Reiss35dc7692010-10-06 17:10:19 +0000272
David Reiss0c025e82010-10-06 17:10:36 +0000273/**
274 * Wrapper around another CoupledTransports implementation that exposes the
275 * transports as TTransport pointers.
276 *
277 * This is used since accessing a transport via a "TTransport*" exercises a
278 * different code path than using the base pointer class. As part of the
279 * template code changes, most transport methods are no longer virtual.
280 */
David Reiss35dc7692010-10-06 17:10:19 +0000281template <class CoupledTransports_>
282class CoupledTTransports : public CoupledTransports<TTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100283public:
David Reiss35dc7692010-10-06 17:10:19 +0000284 CoupledTTransports() : transports() {
285 in = transports.in;
286 out = transports.out;
287 }
288
289 CoupledTransports_ transports;
290};
291
David Reiss0c025e82010-10-06 17:10:36 +0000292/**
293 * Wrapper around another CoupledTransports implementation that exposes the
294 * transports as TBufferBase pointers.
295 *
296 * This can only be instantiated with a transport type that is a subclass of
297 * TBufferBase.
298 */
David Reiss35dc7692010-10-06 17:10:19 +0000299template <class CoupledTransports_>
300class CoupledBufferBases : public CoupledTransports<TBufferBase> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100301public:
David Reiss35dc7692010-10-06 17:10:19 +0000302 CoupledBufferBases() : transports() {
303 in = transports.in;
304 out = transports.out;
305 }
306
307 CoupledTransports_ transports;
308};
309
David Reiss35dc7692010-10-06 17:10:19 +0000310/**************************************************************************
David Reisse5c435c2010-10-06 17:10:38 +0000311 * Alarm handling code for use in tests that check the transport blocking
312 * semantics.
313 *
314 * If the transport ends up blocking, we don't want to hang forever. We use
315 * SIGALRM to fire schedule signal to wake up and try to write data so the
316 * transport will unblock.
317 *
318 * It isn't really the safest thing in the world to be mucking around with
319 * complicated global data structures in a signal handler. It should probably
320 * be okay though, since we know the main thread should always be blocked in a
321 * read() request when the signal handler is running.
322 **************************************************************************/
323
324struct TriggerInfo {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100325 TriggerInfo(int seconds, const boost::shared_ptr<TTransport>& transport, uint32_t writeLength)
326 : timeoutSeconds(seconds), transport(transport), writeLength(writeLength), next(NULL) {}
David Reisse5c435c2010-10-06 17:10:38 +0000327
328 int timeoutSeconds;
329 boost::shared_ptr<TTransport> transport;
330 uint32_t writeLength;
331 TriggerInfo* next;
332};
333
Jake Farrell5d02b802014-01-07 21:42:01 -0500334apache::thrift::concurrency::Monitor g_alarm_monitor;
335TriggerInfo* g_triggerInfo;
336unsigned int g_numTriggersFired;
337bool g_teardown = false;
David Reisse5c435c2010-10-06 17:10:38 +0000338
Jake Farrell5d02b802014-01-07 21:42:01 -0500339void alarm_handler() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100340 TriggerInfo* info = NULL;
Jake Farrell5d02b802014-01-07 21:42:01 -0500341 {
342 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
343 // The alarm timed out, which almost certainly means we're stuck
344 // on a transport that is incorrectly blocked.
345 ++g_numTriggersFired;
David Reisse5c435c2010-10-06 17:10:38 +0000346
Jake Farrell5d02b802014-01-07 21:42:01 -0500347 // Note: we print messages to stdout instead of stderr, since
348 // tools/test/runner only records stdout messages in the failure messages for
349 // boost tests. (boost prints its test info to stdout.)
350 printf("Timeout alarm expired; attempting to unblock transport\n");
351 if (g_triggerInfo == NULL) {
352 printf(" trigger stack is empty!\n");
353 }
David Reisse5c435c2010-10-06 17:10:38 +0000354
Jake Farrell5d02b802014-01-07 21:42:01 -0500355 // Pop off the first TriggerInfo.
356 // If there is another one, schedule an alarm for it.
357 info = g_triggerInfo;
358 g_triggerInfo = info->next;
David Reisse5c435c2010-10-06 17:10:38 +0000359 }
360
David Reisse5c435c2010-10-06 17:10:38 +0000361 // Write some data to the transport to hopefully unblock it.
Roger Meier0069cc42010-10-13 18:10:18 +0000362 uint8_t* buf = new uint8_t[info->writeLength];
David Reisse5c435c2010-10-06 17:10:38 +0000363 memset(buf, 'b', info->writeLength);
Roger Meier0069cc42010-10-13 18:10:18 +0000364 boost::scoped_array<uint8_t> array(buf);
David Reisse5c435c2010-10-06 17:10:38 +0000365 info->transport->write(buf, info->writeLength);
366 info->transport->flush();
367
368 delete info;
369}
370
Jake Farrell5d02b802014-01-07 21:42:01 -0500371void alarm_handler_wrapper() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100372 int64_t timeout = 0; // timeout of 0 means wait forever
373 while (true) {
Jake Farrell5d02b802014-01-07 21:42:01 -0500374 bool fireHandler = false;
375 {
376 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100377 if (g_teardown)
378 return;
379 // calculate timeout
Jake Farrell5d02b802014-01-07 21:42:01 -0500380 if (g_triggerInfo == NULL) {
381 timeout = 0;
382 } else {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100383 timeout = g_triggerInfo->timeoutSeconds * 1000;
Jake Farrell5d02b802014-01-07 21:42:01 -0500384 }
385
386 int waitResult = g_alarm_monitor.waitForTimeRelative(timeout);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100387 if (waitResult == THRIFT_ETIMEDOUT)
Jake Farrell5d02b802014-01-07 21:42:01 -0500388 fireHandler = true;
389 }
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100390 if (fireHandler)
391 alarm_handler(); // calling outside the lock
David Reisse5c435c2010-10-06 17:10:38 +0000392 }
David Reisse5c435c2010-10-06 17:10:38 +0000393}
394
395/**
396 * Add a trigger to be scheduled "seconds" seconds after the
397 * last currently scheduled trigger.
398 *
399 * (Note that this is not "seconds" from now. That might be more logical, but
400 * would require slightly more complicated sorting, rather than just appending
401 * to the end.)
402 */
403void add_trigger(unsigned int seconds,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100404 const boost::shared_ptr<TTransport>& transport,
David Reisse5c435c2010-10-06 17:10:38 +0000405 uint32_t write_len) {
406 TriggerInfo* info = new TriggerInfo(seconds, transport, write_len);
Jake Farrell5d02b802014-01-07 21:42:01 -0500407 {
408 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
409 if (g_triggerInfo == NULL) {
410 // This is the first trigger.
411 // Set g_triggerInfo, and schedule the alarm
412 g_triggerInfo = info;
413 g_alarm_monitor.notify();
414 } else {
415 // Add this trigger to the end of the list
416 TriggerInfo* prev = g_triggerInfo;
417 while (prev->next) {
418 prev = prev->next;
419 }
420 prev->next = info;
David Reisse5c435c2010-10-06 17:10:38 +0000421 }
David Reisse5c435c2010-10-06 17:10:38 +0000422 }
423}
424
425void clear_triggers() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100426 TriggerInfo* info = NULL;
Jake Farrell5d02b802014-01-07 21:42:01 -0500427
428 {
429 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
430 info = g_triggerInfo;
431 g_triggerInfo = NULL;
432 g_numTriggersFired = 0;
433 g_alarm_monitor.notify();
434 }
David Reisse5c435c2010-10-06 17:10:38 +0000435
436 while (info != NULL) {
437 TriggerInfo* next = info->next;
438 delete info;
439 info = next;
440 }
441}
442
443void set_trigger(unsigned int seconds,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100444 const boost::shared_ptr<TTransport>& transport,
David Reisse5c435c2010-10-06 17:10:38 +0000445 uint32_t write_len) {
446 clear_triggers();
447 add_trigger(seconds, transport, write_len);
448}
449
450/**************************************************************************
451 * Test functions
David Reiss35dc7692010-10-06 17:10:19 +0000452 **************************************************************************/
453
454/**
455 * Test interleaved write and read calls.
456 *
457 * Generates a buffer totalSize bytes long, then writes it to the transport,
458 * and verifies the written data can be read back correctly.
459 *
460 * Mode of operation:
461 * - call wChunkGenerator to figure out how large of a chunk to write
462 * - call wSizeGenerator to get the size for individual write() calls,
463 * and do this repeatedly until the entire chunk is written.
464 * - call rChunkGenerator to figure out how large of a chunk to read
465 * - call rSizeGenerator to get the size for individual read() calls,
466 * and do this repeatedly until the entire chunk is read.
467 * - repeat until the full buffer is written and read back,
468 * then compare the data read back against the original buffer
469 *
470 *
471 * - If any of the size generators return 0, this means to use the maximum
472 * possible size.
473 *
474 * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
475 * there are never more than maxOutstanding bytes waiting to be read back.
476 */
477template <class CoupledTransports>
478void test_rw(uint32_t totalSize,
479 SizeGenerator& wSizeGenerator,
480 SizeGenerator& rSizeGenerator,
481 SizeGenerator& wChunkGenerator,
482 SizeGenerator& rChunkGenerator,
483 uint32_t maxOutstanding) {
484 CoupledTransports transports;
485 BOOST_REQUIRE(transports.in != NULL);
486 BOOST_REQUIRE(transports.out != NULL);
487
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100488 boost::shared_array<uint8_t> wbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
489 boost::shared_array<uint8_t> rbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
David Reiss35dc7692010-10-06 17:10:19 +0000490
491 // store some data in wbuf
492 for (uint32_t n = 0; n < totalSize; ++n) {
493 wbuf[n] = (n & 0xff);
494 }
495 // clear rbuf
496 memset(rbuf.get(), 0, totalSize);
497
498 uint32_t total_written = 0;
499 uint32_t total_read = 0;
500 while (total_read < totalSize) {
501 // Determine how large a chunk of data to write
502 uint32_t wchunk_size = wChunkGenerator.nextSize();
503 if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
504 wchunk_size = totalSize - total_written;
505 }
506
507 // Make sure (total_written - total_read) + wchunk_size
508 // is less than maxOutstanding
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100509 if (maxOutstanding > 0 && wchunk_size > maxOutstanding - (total_written - total_read)) {
David Reiss35dc7692010-10-06 17:10:19 +0000510 wchunk_size = maxOutstanding - (total_written - total_read);
511 }
512
513 // Write the chunk
514 uint32_t chunk_written = 0;
515 while (chunk_written < wchunk_size) {
516 uint32_t write_size = wSizeGenerator.nextSize();
517 if (write_size == 0 || write_size > wchunk_size - chunk_written) {
518 write_size = wchunk_size - chunk_written;
519 }
520
Roger Meier967600e2013-05-03 22:39:53 +0200521 try {
522 transports.out->write(wbuf.get() + total_written, write_size);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100523 } catch (TTransportException& te) {
Roger Meier967600e2013-05-03 22:39:53 +0200524 if (te.getType() == TTransportException::TIMED_OUT)
525 break;
526 throw te;
527 }
David Reiss35dc7692010-10-06 17:10:19 +0000528 chunk_written += write_size;
529 total_written += write_size;
530 }
531
532 // Flush the data, so it will be available in the read transport
533 // Don't flush if wchunk_size is 0. (This should only happen if
534 // total_written == totalSize already, and we're only reading now.)
535 if (wchunk_size > 0) {
536 transports.out->flush();
537 }
538
539 // Determine how large a chunk of data to read back
540 uint32_t rchunk_size = rChunkGenerator.nextSize();
541 if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
542 rchunk_size = total_written - total_read;
543 }
544
545 // Read the chunk
546 uint32_t chunk_read = 0;
547 while (chunk_read < rchunk_size) {
548 uint32_t read_size = rSizeGenerator.nextSize();
549 if (read_size == 0 || read_size > rchunk_size - chunk_read) {
550 read_size = rchunk_size - chunk_read;
551 }
552
David Reisse94fa332010-10-06 17:10:26 +0000553 int bytes_read = -1;
554 try {
555 bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
556 } catch (TTransportException& e) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100557 BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size << ") threw exception \""
558 << e.what() << "\"; written so far: " << total_written << " / "
559 << totalSize << " bytes");
David Reisse94fa332010-10-06 17:10:26 +0000560 }
561
David Reiss35dc7692010-10-06 17:10:19 +0000562 BOOST_REQUIRE_MESSAGE(bytes_read > 0,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100563 "read(pos=" << total_read << ", size=" << read_size << ") returned "
564 << bytes_read << "; written so far: " << total_written
565 << " / " << totalSize << " bytes");
David Reiss35dc7692010-10-06 17:10:19 +0000566 chunk_read += bytes_read;
567 total_read += bytes_read;
568 }
569 }
570
571 // make sure the data read back is identical to the data written
572 BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
573}
574
David Reisse5c435c2010-10-06 17:10:38 +0000575template <class CoupledTransports>
576void test_read_part_available() {
577 CoupledTransports transports;
578 BOOST_REQUIRE(transports.in != NULL);
579 BOOST_REQUIRE(transports.out != NULL);
580
581 uint8_t write_buf[16];
582 uint8_t read_buf[16];
583 memset(write_buf, 'a', sizeof(write_buf));
584
585 // Attemping to read 10 bytes when only 9 are available should return 9
586 // immediately.
587 transports.out->write(write_buf, 9);
588 transports.out->flush();
589 set_trigger(3, transports.out, 1);
590 uint32_t bytes_read = transports.in->read(read_buf, 10);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100591 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
592 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)9);
David Reisse5c435c2010-10-06 17:10:38 +0000593
594 clear_triggers();
595}
596
597template <class CoupledTransports>
Roger Meier02c827b2012-04-11 21:59:57 +0000598void test_read_part_available_in_chunks() {
599 CoupledTransports transports;
600 BOOST_REQUIRE(transports.in != NULL);
601 BOOST_REQUIRE(transports.out != NULL);
602
603 uint8_t write_buf[16];
604 uint8_t read_buf[16];
605 memset(write_buf, 'a', sizeof(write_buf));
606
607 // Write 10 bytes (in a single frame, for transports that use framing)
608 transports.out->write(write_buf, 10);
609 transports.out->flush();
610
611 // Read 1 byte, to force the transport to read the frame
612 uint32_t bytes_read = transports.in->read(read_buf, 1);
Jake Farrell5d02b802014-01-07 21:42:01 -0500613 BOOST_CHECK_EQUAL(bytes_read, 1u);
Roger Meier02c827b2012-04-11 21:59:57 +0000614
615 // Read more than what is remaining and verify the transport does not block
616 set_trigger(3, transports.out, 1);
617 bytes_read = transports.in->read(read_buf, 10);
Jake Farrell5d02b802014-01-07 21:42:01 -0500618 BOOST_CHECK_EQUAL(g_numTriggersFired, 0u);
619 BOOST_CHECK_EQUAL(bytes_read, 9u);
Roger Meier02c827b2012-04-11 21:59:57 +0000620
621 clear_triggers();
622}
623
624template <class CoupledTransports>
David Reiss0a2d81e2010-10-06 17:10:40 +0000625void test_read_partial_midframe() {
626 CoupledTransports transports;
627 BOOST_REQUIRE(transports.in != NULL);
628 BOOST_REQUIRE(transports.out != NULL);
629
630 uint8_t write_buf[16];
631 uint8_t read_buf[16];
632 memset(write_buf, 'a', sizeof(write_buf));
633
634 // Attempt to read 10 bytes, when only 9 are available, but after we have
635 // already read part of the data that is available. This exercises a
636 // different code path for several of the transports.
637 //
638 // For transports that add their own framing (e.g., TFramedTransport and
639 // TFileTransport), the two flush calls break up the data in to a 10 byte
640 // frame and a 3 byte frame. The first read then puts us partway through the
641 // first frame, and then we attempt to read past the end of that frame, and
642 // through the next frame, too.
643 //
644 // For buffered transports that perform read-ahead (e.g.,
645 // TBufferedTransport), the read-ahead will most likely see all 13 bytes
646 // written on the first read. The next read will then attempt to read past
647 // the end of the read-ahead buffer.
648 //
649 // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
650 // transports that track framing internally.
651 transports.out->write(write_buf, 10);
652 transports.out->flush();
653 transports.out->write(write_buf, 3);
654 transports.out->flush();
655
656 // Now read 4 bytes, so that we are partway through the written data.
657 uint32_t bytes_read = transports.in->read(read_buf, 4);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100658 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)4);
David Reiss0a2d81e2010-10-06 17:10:40 +0000659
660 // Now attempt to read 10 bytes. Only 9 more are available.
661 //
662 // We should be able to get all 9 bytes, but it might take multiple read
663 // calls, since it is valid for read() to return fewer bytes than requested.
664 // (Most transports do immediately return 9 bytes, but the framing transports
665 // tend to only return to the end of the current frame, which is 6 bytes in
666 // this case.)
667 uint32_t total_read = 0;
668 while (total_read < 9) {
669 set_trigger(3, transports.out, 1);
670 bytes_read = transports.in->read(read_buf, 10);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100671 BOOST_REQUIRE_EQUAL(g_numTriggersFired, (unsigned int)0);
672 BOOST_REQUIRE_GT(bytes_read, (uint32_t)0);
David Reiss0a2d81e2010-10-06 17:10:40 +0000673 total_read += bytes_read;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100674 BOOST_REQUIRE_LE(total_read, (uint32_t)9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000675 }
676
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100677 BOOST_CHECK_EQUAL(total_read, (uint32_t)9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000678
679 clear_triggers();
680}
681
682template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000683void test_borrow_part_available() {
684 CoupledTransports transports;
685 BOOST_REQUIRE(transports.in != NULL);
686 BOOST_REQUIRE(transports.out != NULL);
687
688 uint8_t write_buf[16];
689 uint8_t read_buf[16];
690 memset(write_buf, 'a', sizeof(write_buf));
691
692 // Attemping to borrow 10 bytes when only 9 are available should return NULL
693 // immediately.
694 transports.out->write(write_buf, 9);
695 transports.out->flush();
696 set_trigger(3, transports.out, 1);
697 uint32_t borrow_len = 10;
698 const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100699 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000700 BOOST_CHECK(borrowed_buf == NULL);
701
702 clear_triggers();
703}
704
705template <class CoupledTransports>
706void test_read_none_available() {
707 CoupledTransports transports;
708 BOOST_REQUIRE(transports.in != NULL);
709 BOOST_REQUIRE(transports.out != NULL);
710
711 uint8_t write_buf[16];
712 uint8_t read_buf[16];
713 memset(write_buf, 'a', sizeof(write_buf));
714
715 // Attempting to read when no data is available should either block until
716 // some data is available, or fail immediately. (e.g., TSocket blocks,
717 // TMemoryBuffer just fails.)
718 //
719 // If the transport blocks, it should succeed once some data is available,
720 // even if less than the amount requested becomes available.
721 set_trigger(1, transports.out, 2);
722 add_trigger(1, transports.out, 8);
723 uint32_t bytes_read = transports.in->read(read_buf, 10);
724 if (bytes_read == 0) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100725 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000726 clear_triggers();
727 } else {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100728 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)1);
729 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)2);
David Reisse5c435c2010-10-06 17:10:38 +0000730 }
731
732 clear_triggers();
733}
734
735template <class CoupledTransports>
736void test_borrow_none_available() {
737 CoupledTransports transports;
738 BOOST_REQUIRE(transports.in != NULL);
739 BOOST_REQUIRE(transports.out != NULL);
740
741 uint8_t write_buf[16];
742 memset(write_buf, 'a', sizeof(write_buf));
743
744 // Attempting to borrow when no data is available should fail immediately
745 set_trigger(1, transports.out, 10);
746 uint32_t borrow_len = 10;
747 const uint8_t* borrowed_buf = transports.in->borrow(NULL, &borrow_len);
748 BOOST_CHECK(borrowed_buf == NULL);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100749 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000750
751 clear_triggers();
752}
753
David Reiss35dc7692010-10-06 17:10:19 +0000754/**************************************************************************
755 * Test case generation
756 *
757 * Pretty ugly and annoying. This would be much easier if we the unit test
758 * framework didn't force each test to be a separate function.
759 * - Writing a completely separate function definition for each of these would
760 * result in a lot of repetitive boilerplate code.
761 * - Combining many tests into a single function makes it more difficult to
762 * tell precisely which tests failed. It also means you can't get a progress
763 * update after each test, and the tests are already fairly slow.
Konrad Grochowski3b5dacb2014-11-24 10:55:31 +0100764 * - Similar registration could be achieved with BOOST_TEST_CASE_TEMPLATE,
David Reiss35dc7692010-10-06 17:10:19 +0000765 * but it requires a lot of awkward MPL code, and results in useless test
766 * case names. (The names are generated from std::type_info::name(), which
767 * is compiler-dependent. gcc returns mangled names.)
768 **************************************************************************/
769
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100770#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
771 addTestRW<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports), totalSize, ##__VA_ARGS__);
David Reissd4788df2010-10-06 17:10:37 +0000772
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100773#define TEST_RW(CoupledTransports, totalSize, ...) \
774 do { \
775 /* Add the test as specified, to test the non-virtual function calls */ \
776 ADD_TEST_RW(CoupledTransports, totalSize, ##__VA_ARGS__); \
777 /* \
778 * Also test using the transport as a TTransport*, to test \
779 * the read_virt()/write_virt() calls \
780 */ \
781 ADD_TEST_RW(CoupledTTransports<CoupledTransports>, totalSize, ##__VA_ARGS__); \
782 /* Test wrapping the transport with TBufferedTransport */ \
783 ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
784 /* Test wrapping the transport with TFramedTransports */ \
785 ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
786 /* Test wrapping the transport with TZlibTransport */ \
787 ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000788 } while (0)
789
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100790#define ADD_TEST_BLOCKING(CoupledTransports) \
791 addTestBlocking<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports));
David Reisse5c435c2010-10-06 17:10:38 +0000792
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100793#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
794 ADD_TEST_BLOCKING(CoupledTransports); \
795 ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
796 ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
797 ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
David Reisse5c435c2010-10-06 17:10:38 +0000798 ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
799
David Reiss35dc7692010-10-06 17:10:19 +0000800class TransportTestGen {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100801public:
802 TransportTestGen(boost::unit_test::test_suite* suite, float sizeMultiplier)
803 : suite_(suite), sizeMultiplier_(sizeMultiplier) {}
David Reiss35dc7692010-10-06 17:10:19 +0000804
805 void generate() {
806 GenericSizeGenerator rand4k(1, 4096);
807
808 /*
809 * We do the basically the same set of tests for each transport type,
810 * although we tweak the parameters in some places.
811 */
812
David Reissd4788df2010-10-06 17:10:37 +0000813 // TMemoryBuffer tests
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100814 TEST_RW(CoupledMemoryBuffers, 1024 * 1024, 0, 0);
815 TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k);
816 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163);
817 TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000818
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100819 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 0, 0, rand4k, rand4k);
820 TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
821 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163, rand4k, rand4k);
822 TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1, rand4k, rand4k);
David Reisse94fa332010-10-06 17:10:26 +0000823
David Reisse5c435c2010-10-06 17:10:38 +0000824 TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
825
Jake Farrell5d02b802014-01-07 21:42:01 -0500826#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000827 // TFDTransport tests
828 // Since CoupledFDTransports tests with a pipe, writes will block
829 // if there is too much outstanding unread data in the pipe.
830 uint32_t fd_max_outstanding = 4096;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100831 TEST_RW(CoupledFDTransports, 1024 * 1024, 0, 0, 0, 0, fd_max_outstanding);
832 TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, 0, 0, fd_max_outstanding);
833 TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, 0, 0, fd_max_outstanding);
834 TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, 0, 0, fd_max_outstanding);
David Reiss35dc7692010-10-06 17:10:19 +0000835
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100836 TEST_RW(CoupledFDTransports, 1024 * 256, 0, 0, rand4k, rand4k, fd_max_outstanding);
837 TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k, fd_max_outstanding);
838 TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, rand4k, rand4k, fd_max_outstanding);
839 TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, rand4k, rand4k, fd_max_outstanding);
David Reiss35dc7692010-10-06 17:10:19 +0000840
David Reisse5c435c2010-10-06 17:10:38 +0000841 TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500842#endif //_WIN32
David Reisse5c435c2010-10-06 17:10:38 +0000843
David Reiss0c025e82010-10-06 17:10:36 +0000844 // TSocket tests
845 uint32_t socket_max_outstanding = 4096;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100846 TEST_RW(CoupledSocketTransports, 1024 * 1024, 0, 0, 0, 0, socket_max_outstanding);
847 TEST_RW(CoupledSocketTransports, 1024 * 256, rand4k, rand4k, 0, 0, socket_max_outstanding);
848 TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, 0, 0, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000849 // Doh. Apparently writing to a socket has some additional overhead for
850 // each send() call. If we have more than ~400 outstanding 1-byte write
851 // requests, additional send() calls start blocking.
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100852 TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, 0, 0, socket_max_outstanding);
853 TEST_RW(CoupledSocketTransports, 1024 * 256, 0, 0, rand4k, rand4k, socket_max_outstanding);
854 TEST_RW(CoupledSocketTransports,
855 1024 * 256,
856 rand4k,
857 rand4k,
858 rand4k,
859 rand4k,
860 socket_max_outstanding);
861 TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, rand4k, rand4k, socket_max_outstanding);
862 TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, rand4k, rand4k, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000863
David Reisse5c435c2010-10-06 17:10:38 +0000864 TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
865
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100866// 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 -0500867#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000868 // TFileTransport tests
869 // We use smaller buffer sizes here, since TFileTransport is fairly slow.
870 //
871 // TFileTransport can't write more than 16MB at once
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100872 uint32_t max_write_at_once = 1024 * 1024 * 16 - 4;
873 TEST_RW(CoupledFileTransports, 1024 * 1024, max_write_at_once, 0);
874 TEST_RW(CoupledFileTransports, 1024 * 128, rand4k, rand4k);
875 TEST_RW(CoupledFileTransports, 1024 * 128, 167, 163);
876 TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000877
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100878 TEST_RW(CoupledFileTransports, 1024 * 64, 0, 0, rand4k, rand4k);
879 TEST_RW(CoupledFileTransports, 1024 * 64, rand4k, rand4k, rand4k, rand4k);
880 TEST_RW(CoupledFileTransports, 1024 * 64, 167, 163, rand4k, rand4k);
881 TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000882
David Reisse5c435c2010-10-06 17:10:38 +0000883 TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500884#endif
David Reisse5c435c2010-10-06 17:10:38 +0000885
David Reissd4788df2010-10-06 17:10:37 +0000886 // Add some tests that access TBufferedTransport and TFramedTransport
887 // via TTransport pointers and TBufferBase pointers.
David Reisse5c435c2010-10-06 17:10:38 +0000888 ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100889 1024 * 1024,
890 rand4k,
891 rand4k,
892 rand4k,
893 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000894 ADD_TEST_RW(CoupledBufferBases<CoupledBufferedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100895 1024 * 1024,
896 rand4k,
897 rand4k,
898 rand4k,
899 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000900 ADD_TEST_RW(CoupledTTransports<CoupledFramedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100901 1024 * 1024,
902 rand4k,
903 rand4k,
904 rand4k,
905 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000906 ADD_TEST_RW(CoupledBufferBases<CoupledFramedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100907 1024 * 1024,
908 rand4k,
909 rand4k,
910 rand4k,
911 rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000912
913 // Test using TZlibTransport via a TTransport pointer
David Reisse5c435c2010-10-06 17:10:38 +0000914 ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100915 1024 * 1024,
916 rand4k,
917 rand4k,
918 rand4k,
919 rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000920 }
921
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100922private:
David Reiss35dc7692010-10-06 17:10:19 +0000923 template <class CoupledTransports>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100924 void addTestRW(const char* transport_name,
925 uint32_t totalSize,
926 GenericSizeGenerator wSizeGen,
927 GenericSizeGenerator rSizeGen,
David Reisse5c435c2010-10-06 17:10:38 +0000928 GenericSizeGenerator wChunkSizeGen = 0,
929 GenericSizeGenerator rChunkSizeGen = 0,
930 uint32_t maxOutstanding = 0,
931 uint32_t expectedFailures = 0) {
David Reiss65e62d32010-10-06 17:10:35 +0000932 // adjust totalSize by the specified sizeMultiplier_ first
933 totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
934
David Reiss35dc7692010-10-06 17:10:19 +0000935 std::ostringstream name;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100936 name << transport_name << "::test_rw(" << totalSize << ", " << wSizeGen.describe() << ", "
937 << rSizeGen.describe() << ", " << wChunkSizeGen.describe() << ", "
938 << rChunkSizeGen.describe() << ", " << maxOutstanding << ")";
David Reiss35dc7692010-10-06 17:10:19 +0000939
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100940 boost::unit_test::callback0<> test_func
941 = apache::thrift::stdcxx::bind(test_rw<CoupledTransports>,
942 totalSize,
943 wSizeGen,
944 rSizeGen,
945 wChunkSizeGen,
946 rChunkSizeGen,
947 maxOutstanding);
948 boost::unit_test::test_case* tc = boost::unit_test::make_test_case(test_func, name.str());
David Reiss35dc7692010-10-06 17:10:19 +0000949 suite_->add(tc, expectedFailures);
Roger Meier0069cc42010-10-13 18:10:18 +0000950 }
David Reiss35dc7692010-10-06 17:10:19 +0000951
David Reisse5c435c2010-10-06 17:10:38 +0000952 template <class CoupledTransports>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100953 void addTestBlocking(const char* transportName, uint32_t expectedFailures = 0) {
David Reisse5c435c2010-10-06 17:10:38 +0000954 char name[1024];
955 boost::unit_test::test_case* tc;
956
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100957 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available()", transportName);
958 tc = boost::unit_test::make_test_case(test_read_part_available<CoupledTransports>, name);
David Reisse5c435c2010-10-06 17:10:38 +0000959 suite_->add(tc, expectedFailures);
960
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100961 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available_in_chunks()", transportName);
962 tc = boost::unit_test::make_test_case(test_read_part_available_in_chunks<CoupledTransports>,
963 name);
Roger Meier02c827b2012-04-11 21:59:57 +0000964 suite_->add(tc, expectedFailures);
965
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100966 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_partial_midframe()", transportName);
967 tc = boost::unit_test::make_test_case(test_read_partial_midframe<CoupledTransports>, name);
David Reiss0a2d81e2010-10-06 17:10:40 +0000968 suite_->add(tc, expectedFailures);
969
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100970 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_none_available()", transportName);
971 tc = boost::unit_test::make_test_case(test_read_none_available<CoupledTransports>, name);
David Reisse5c435c2010-10-06 17:10:38 +0000972 suite_->add(tc, expectedFailures);
973
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100974 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_part_available()", transportName);
975 tc = boost::unit_test::make_test_case(test_borrow_part_available<CoupledTransports>, name);
David Reisse5c435c2010-10-06 17:10:38 +0000976 suite_->add(tc, expectedFailures);
977
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100978 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_none_available()", transportName);
979 tc = boost::unit_test::make_test_case(test_borrow_none_available<CoupledTransports>, name);
David Reisse5c435c2010-10-06 17:10:38 +0000980 suite_->add(tc, expectedFailures);
981 }
982
David Reiss35dc7692010-10-06 17:10:19 +0000983 boost::unit_test::test_suite* suite_;
David Reiss65e62d32010-10-06 17:10:35 +0000984 // sizeMultiplier_ is configurable via the command line, and allows the
985 // user to adjust between smaller buffers that can be tested quickly,
986 // or larger buffers that more thoroughly exercise the code, but take
987 // longer.
988 float sizeMultiplier_;
David Reiss35dc7692010-10-06 17:10:19 +0000989};
990
991/**************************************************************************
992 * General Initialization
993 **************************************************************************/
994
Jake Farrell5d02b802014-01-07 21:42:01 -0500995struct global_fixture {
996 boost::shared_ptr<apache::thrift::concurrency::Thread> alarmThread_;
997 global_fixture() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100998#if _WIN32
Jake Farrell5d02b802014-01-07 21:42:01 -0500999 apache::thrift::transport::TWinsockSingleton::create();
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001000#endif
David Reiss35dc7692010-10-06 17:10:19 +00001001
Jake Farrell5d02b802014-01-07 21:42:01 -05001002 apache::thrift::concurrency::PlatformThreadFactory factory;
1003 factory.setDetached(false);
1004
1005 alarmThread_ = factory.newThread(
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001006 apache::thrift::concurrency::FunctionRunner::create(alarm_handler_wrapper));
Jake Farrell5d02b802014-01-07 21:42:01 -05001007 alarmThread_->start();
1008 }
1009 ~global_fixture() {
1010 {
1011 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
1012 g_teardown = true;
1013 g_alarm_monitor.notify();
1014 }
1015 alarmThread_->join();
1016 }
David Reiss65e62d32010-10-06 17:10:35 +00001017};
1018
Jake Farrell5d02b802014-01-07 21:42:01 -05001019BOOST_GLOBAL_FIXTURE(global_fixture)
David Reiss35dc7692010-10-06 17:10:19 +00001020
1021boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
Konrad Grochowskib3f5ffc2014-11-06 19:32:59 +01001022 THRIFT_UNUSED_VARIABLE(argc);
1023 THRIFT_UNUSED_VARIABLE(argv);
Jake Farrell5d02b802014-01-07 21:42:01 -05001024 struct timeval tv;
1025 THRIFT_GETTIMEOFDAY(&tv, NULL);
1026 int seed = tv.tv_sec ^ tv.tv_usec;
David Reiss65e62d32010-10-06 17:10:35 +00001027
Jake Farrell5d02b802014-01-07 21:42:01 -05001028 initrand(seed);
David Reiss35dc7692010-10-06 17:10:19 +00001029
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001030 boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
David Reiss109693c2010-10-06 17:10:42 +00001031 suite->p_name.value = "TransportTest";
Jake Farrell5d02b802014-01-07 21:42:01 -05001032 TransportTestGen transport_test_generator(suite, 1);
David Reiss35dc7692010-10-06 17:10:19 +00001033 transport_test_generator.generate();
David Reiss109693c2010-10-06 17:10:42 +00001034 return NULL;
David Reiss35dc7692010-10-06 17:10:19 +00001035}