blob: 451fcef6370cdf83195d6f1864d253b11a3ca63f [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 */
David Reiss35dc7692010-10-06 17:10:19 +000019#include <stdlib.h>
20#include <time.h>
David Reiss35dc7692010-10-06 17:10:19 +000021#include <sstream>
Jake Farrell5d02b802014-01-07 21:42:01 -050022#include <fstream>
23#include <thrift/cxxfunctional.h>
David Reiss35dc7692010-10-06 17:10:19 +000024
25#include <boost/mpl/list.hpp>
26#include <boost/shared_array.hpp>
27#include <boost/random.hpp>
28#include <boost/type_traits.hpp>
29#include <boost/test/unit_test.hpp>
30
Roger Meier49ff8b12012-04-13 09:12:31 +000031#include <thrift/transport/TBufferTransports.h>
32#include <thrift/transport/TFDTransport.h>
33#include <thrift/transport/TFileTransport.h>
34#include <thrift/transport/TZlibTransport.h>
35#include <thrift/transport/TSocket.h>
David Reiss35dc7692010-10-06 17:10:19 +000036
Jake Farrell5d02b802014-01-07 21:42:01 -050037#include <thrift/concurrency/FunctionRunner.h>
38#if _WIN32
Konrad Grochowski16a23a62014-11-13 15:33:38 +010039#include <thrift/windows/TWinsockSingleton.h>
Jake Farrell5d02b802014-01-07 21:42:01 -050040#endif
41
David Reiss35dc7692010-10-06 17:10:19 +000042using namespace apache::thrift::transport;
43
44static boost::mt19937 rng;
David Reiss35dc7692010-10-06 17:10:19 +000045
David Reiss65e62d32010-10-06 17:10:35 +000046void initrand(unsigned int seed) {
David Reiss35dc7692010-10-06 17:10:19 +000047 rng.seed(seed);
48}
49
50class SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010051public:
David Reiss35dc7692010-10-06 17:10:19 +000052 virtual ~SizeGenerator() {}
53 virtual uint32_t nextSize() = 0;
54 virtual std::string describe() const = 0;
55};
56
57class ConstantSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010058public:
David Reiss35dc7692010-10-06 17:10:19 +000059 ConstantSizeGenerator(uint32_t value) : value_(value) {}
60 uint32_t nextSize() { return value_; }
61 std::string describe() const {
62 std::ostringstream desc;
63 desc << value_;
64 return desc.str();
65 }
66
Konrad Grochowski16a23a62014-11-13 15:33:38 +010067private:
David Reiss35dc7692010-10-06 17:10:19 +000068 uint32_t value_;
69};
70
71class RandomSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010072public:
73 RandomSizeGenerator(uint32_t min, uint32_t max)
74 : generator_(rng, boost::uniform_int<int>(min, max)) {}
David Reiss35dc7692010-10-06 17:10:19 +000075
76 uint32_t nextSize() { return generator_(); }
77
78 std::string describe() const {
79 std::ostringstream desc;
80 desc << "rand(" << getMin() << ", " << getMax() << ")";
81 return desc.str();
82 }
83
Jake Farrell5d02b802014-01-07 21:42:01 -050084 uint32_t getMin() const { return (generator_.distribution().min)(); }
85 uint32_t getMax() const { return (generator_.distribution().max)(); }
David Reiss35dc7692010-10-06 17:10:19 +000086
Konrad Grochowski16a23a62014-11-13 15:33:38 +010087private:
88 boost::variate_generator<boost::mt19937&, boost::uniform_int<int> > generator_;
David Reiss35dc7692010-10-06 17:10:19 +000089};
90
91/**
92 * This class exists solely to make the TEST_RW() macro easier to use.
93 * - it can be constructed implicitly from an integer
94 * - it can contain either a ConstantSizeGenerator or a RandomSizeGenerator
95 * (TEST_RW can't take a SizeGenerator pointer or reference, since it needs
96 * to make a copy of the generator to bind it to the test function.)
97 */
98class GenericSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010099public:
100 GenericSizeGenerator(uint32_t value) : generator_(new ConstantSizeGenerator(value)) {}
101 GenericSizeGenerator(uint32_t min, uint32_t max)
102 : generator_(new RandomSizeGenerator(min, max)) {}
David Reiss35dc7692010-10-06 17:10:19 +0000103
104 uint32_t nextSize() { return generator_->nextSize(); }
105 std::string describe() const { return generator_->describe(); }
106
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100107private:
David Reiss35dc7692010-10-06 17:10:19 +0000108 boost::shared_ptr<SizeGenerator> generator_;
109};
110
111/**************************************************************************
112 * Classes to set up coupled transports
113 **************************************************************************/
114
David Reiss0c025e82010-10-06 17:10:36 +0000115/**
116 * Helper class to represent a coupled pair of transports.
117 *
118 * Data written to the out transport can be read from the in transport.
119 *
120 * This is used as the base class for the various coupled transport
121 * implementations. It shouldn't be instantiated directly.
122 */
David Reiss35dc7692010-10-06 17:10:19 +0000123template <class Transport_>
124class CoupledTransports {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100125public:
Roger Meier6f7681f2011-11-06 12:04:28 +0000126 virtual ~CoupledTransports() {}
David Reiss35dc7692010-10-06 17:10:19 +0000127 typedef Transport_ TransportType;
128
David Reissd4788df2010-10-06 17:10:37 +0000129 CoupledTransports() : in(), out() {}
David Reiss35dc7692010-10-06 17:10:19 +0000130
David Reissd4788df2010-10-06 17:10:37 +0000131 boost::shared_ptr<Transport_> in;
132 boost::shared_ptr<Transport_> out;
David Reiss35dc7692010-10-06 17:10:19 +0000133
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100134private:
David Reiss35dc7692010-10-06 17:10:19 +0000135 CoupledTransports(const CoupledTransports&);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100136 CoupledTransports& operator=(const CoupledTransports&);
David Reiss35dc7692010-10-06 17:10:19 +0000137};
138
David Reiss0c025e82010-10-06 17:10:36 +0000139/**
140 * Coupled TMemoryBuffers
141 */
David Reiss35dc7692010-10-06 17:10:19 +0000142class CoupledMemoryBuffers : public CoupledTransports<TMemoryBuffer> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100143public:
144 CoupledMemoryBuffers() : buf(new TMemoryBuffer) {
David Reissd4788df2010-10-06 17:10:37 +0000145 in = buf;
146 out = buf;
David Reiss35dc7692010-10-06 17:10:19 +0000147 }
148
David Reissd4788df2010-10-06 17:10:37 +0000149 boost::shared_ptr<TMemoryBuffer> buf;
150};
151
152/**
153 * Helper template class for creating coupled transports that wrap
154 * another transport.
155 */
156template <class WrapperTransport_, class InnerCoupledTransports_>
157class CoupledWrapperTransportsT : public CoupledTransports<WrapperTransport_> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100158public:
David Reissd4788df2010-10-06 17:10:37 +0000159 CoupledWrapperTransportsT() {
160 if (inner_.in) {
161 this->in.reset(new WrapperTransport_(inner_.in));
162 }
163 if (inner_.out) {
164 this->out.reset(new WrapperTransport_(inner_.out));
165 }
166 }
167
168 InnerCoupledTransports_ inner_;
David Reiss35dc7692010-10-06 17:10:19 +0000169};
170
David Reiss0c025e82010-10-06 17:10:36 +0000171/**
172 * Coupled TBufferedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000173 */
David Reissd4788df2010-10-06 17:10:37 +0000174template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100175class CoupledBufferedTransportsT
176 : public CoupledWrapperTransportsT<TBufferedTransport, InnerTransport_> {};
David Reiss35dc7692010-10-06 17:10:19 +0000177
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100178typedef CoupledBufferedTransportsT<CoupledMemoryBuffers> CoupledBufferedTransports;
David Reissd4788df2010-10-06 17:10:37 +0000179
David Reiss0c025e82010-10-06 17:10:36 +0000180/**
181 * Coupled TFramedTransports.
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 CoupledFramedTransportsT
185 : public CoupledWrapperTransportsT<TFramedTransport, InnerTransport_> {};
David Reiss35dc7692010-10-06 17:10:19 +0000186
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100187typedef CoupledFramedTransportsT<CoupledMemoryBuffers> CoupledFramedTransports;
David Reissd4788df2010-10-06 17:10:37 +0000188
David Reiss0c025e82010-10-06 17:10:36 +0000189/**
190 * Coupled TZlibTransports.
191 */
David Reissd4788df2010-10-06 17:10:37 +0000192template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100193class CoupledZlibTransportsT : public CoupledWrapperTransportsT<TZlibTransport, InnerTransport_> {};
David Reisse94fa332010-10-06 17:10:26 +0000194
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100195typedef CoupledZlibTransportsT<CoupledMemoryBuffers> CoupledZlibTransports;
David Reissd4788df2010-10-06 17:10:37 +0000196
Jake Farrell5d02b802014-01-07 21:42:01 -0500197#ifndef _WIN32
198// FD transport doesn't make much sense on Windows.
David Reiss0c025e82010-10-06 17:10:36 +0000199/**
200 * Coupled TFDTransports.
201 */
David Reiss35dc7692010-10-06 17:10:19 +0000202class CoupledFDTransports : public CoupledTransports<TFDTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100203public:
David Reiss35dc7692010-10-06 17:10:19 +0000204 CoupledFDTransports() {
205 int pipes[2];
206
207 if (pipe(pipes) != 0) {
208 return;
209 }
210
David Reissd4788df2010-10-06 17:10:37 +0000211 in.reset(new TFDTransport(pipes[0], TFDTransport::CLOSE_ON_DESTROY));
212 out.reset(new TFDTransport(pipes[1], TFDTransport::CLOSE_ON_DESTROY));
David Reiss35dc7692010-10-06 17:10:19 +0000213 }
214};
Jake Farrell5d02b802014-01-07 21:42:01 -0500215#endif
David Reiss35dc7692010-10-06 17:10:19 +0000216
David Reiss0c025e82010-10-06 17:10:36 +0000217/**
218 * Coupled TSockets
219 */
220class CoupledSocketTransports : public CoupledTransports<TSocket> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100221public:
David Reiss0c025e82010-10-06 17:10:36 +0000222 CoupledSocketTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500223 THRIFT_SOCKET sockets[2] = {0};
224 if (THRIFT_SOCKETPAIR(PF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
David Reiss0c025e82010-10-06 17:10:36 +0000225 return;
226 }
227
David Reissd4788df2010-10-06 17:10:37 +0000228 in.reset(new TSocket(sockets[0]));
229 out.reset(new TSocket(sockets[1]));
Roger Meier967600e2013-05-03 22:39:53 +0200230 out->setSendTimeout(100);
David Reiss0c025e82010-10-06 17:10:36 +0000231 }
232};
233
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100234// 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 -0500235#ifndef _WIN32
David Reiss0c025e82010-10-06 17:10:36 +0000236/**
237 * Coupled TFileTransports
238 */
David Reiss35dc7692010-10-06 17:10:19 +0000239class CoupledFileTransports : public CoupledTransports<TFileTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100240public:
David Reiss35dc7692010-10-06 17:10:19 +0000241 CoupledFileTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500242#ifndef _WIN32
243 const char* tmp_dir = "/tmp";
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100244#define FILENAME_SUFFIX "/thrift.transport_test"
Jake Farrell5d02b802014-01-07 21:42:01 -0500245#else
246 const char* tmp_dir = getenv("TMP");
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100247#define FILENAME_SUFFIX "\\thrift.transport_test"
Jake Farrell5d02b802014-01-07 21:42:01 -0500248#endif
249
David Reiss35dc7692010-10-06 17:10:19 +0000250 // Create a temporary file to use
Jake Farrell5d02b802014-01-07 21:42:01 -0500251 filename.resize(strlen(tmp_dir) + strlen(FILENAME_SUFFIX));
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100252 THRIFT_SNPRINTF(&filename[0], filename.size(), "%s" FILENAME_SUFFIX, tmp_dir);
253#undef FILENAME_SUFFIX
Jake Farrell5d02b802014-01-07 21:42:01 -0500254
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100255 { std::ofstream dummy_creation(filename.c_str(), std::ofstream::trunc); }
David Reiss35dc7692010-10-06 17:10:19 +0000256
David Reissd4788df2010-10-06 17:10:37 +0000257 in.reset(new TFileTransport(filename, true));
258 out.reset(new TFileTransport(filename));
David Reiss35dc7692010-10-06 17:10:19 +0000259 }
260
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100261 ~CoupledFileTransports() { remove(filename.c_str()); }
David Reiss35dc7692010-10-06 17:10:19 +0000262
Jake Farrell5d02b802014-01-07 21:42:01 -0500263 std::string filename;
David Reiss35dc7692010-10-06 17:10:19 +0000264};
Jake Farrell5d02b802014-01-07 21:42:01 -0500265#endif
David Reiss35dc7692010-10-06 17:10:19 +0000266
David Reiss0c025e82010-10-06 17:10:36 +0000267/**
268 * Wrapper around another CoupledTransports implementation that exposes the
269 * transports as TTransport pointers.
270 *
271 * This is used since accessing a transport via a "TTransport*" exercises a
272 * different code path than using the base pointer class. As part of the
273 * template code changes, most transport methods are no longer virtual.
274 */
David Reiss35dc7692010-10-06 17:10:19 +0000275template <class CoupledTransports_>
276class CoupledTTransports : public CoupledTransports<TTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100277public:
David Reiss35dc7692010-10-06 17:10:19 +0000278 CoupledTTransports() : transports() {
279 in = transports.in;
280 out = transports.out;
281 }
282
283 CoupledTransports_ transports;
284};
285
David Reiss0c025e82010-10-06 17:10:36 +0000286/**
287 * Wrapper around another CoupledTransports implementation that exposes the
288 * transports as TBufferBase pointers.
289 *
290 * This can only be instantiated with a transport type that is a subclass of
291 * TBufferBase.
292 */
David Reiss35dc7692010-10-06 17:10:19 +0000293template <class CoupledTransports_>
294class CoupledBufferBases : public CoupledTransports<TBufferBase> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100295public:
David Reiss35dc7692010-10-06 17:10:19 +0000296 CoupledBufferBases() : transports() {
297 in = transports.in;
298 out = transports.out;
299 }
300
301 CoupledTransports_ transports;
302};
303
David Reiss35dc7692010-10-06 17:10:19 +0000304/**************************************************************************
David Reisse5c435c2010-10-06 17:10:38 +0000305 * Alarm handling code for use in tests that check the transport blocking
306 * semantics.
307 *
308 * If the transport ends up blocking, we don't want to hang forever. We use
309 * SIGALRM to fire schedule signal to wake up and try to write data so the
310 * transport will unblock.
311 *
312 * It isn't really the safest thing in the world to be mucking around with
313 * complicated global data structures in a signal handler. It should probably
314 * be okay though, since we know the main thread should always be blocked in a
315 * read() request when the signal handler is running.
316 **************************************************************************/
317
318struct TriggerInfo {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100319 TriggerInfo(int seconds, const boost::shared_ptr<TTransport>& transport, uint32_t writeLength)
320 : timeoutSeconds(seconds), transport(transport), writeLength(writeLength), next(NULL) {}
David Reisse5c435c2010-10-06 17:10:38 +0000321
322 int timeoutSeconds;
323 boost::shared_ptr<TTransport> transport;
324 uint32_t writeLength;
325 TriggerInfo* next;
326};
327
Jake Farrell5d02b802014-01-07 21:42:01 -0500328apache::thrift::concurrency::Monitor g_alarm_monitor;
329TriggerInfo* g_triggerInfo;
330unsigned int g_numTriggersFired;
331bool g_teardown = false;
David Reisse5c435c2010-10-06 17:10:38 +0000332
Jake Farrell5d02b802014-01-07 21:42:01 -0500333void alarm_handler() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100334 TriggerInfo* info = NULL;
Jake Farrell5d02b802014-01-07 21:42:01 -0500335 {
336 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
337 // The alarm timed out, which almost certainly means we're stuck
338 // on a transport that is incorrectly blocked.
339 ++g_numTriggersFired;
David Reisse5c435c2010-10-06 17:10:38 +0000340
Jake Farrell5d02b802014-01-07 21:42:01 -0500341 // Note: we print messages to stdout instead of stderr, since
342 // tools/test/runner only records stdout messages in the failure messages for
343 // boost tests. (boost prints its test info to stdout.)
344 printf("Timeout alarm expired; attempting to unblock transport\n");
345 if (g_triggerInfo == NULL) {
346 printf(" trigger stack is empty!\n");
347 }
David Reisse5c435c2010-10-06 17:10:38 +0000348
Jake Farrell5d02b802014-01-07 21:42:01 -0500349 // Pop off the first TriggerInfo.
350 // If there is another one, schedule an alarm for it.
351 info = g_triggerInfo;
352 g_triggerInfo = info->next;
David Reisse5c435c2010-10-06 17:10:38 +0000353 }
354
David Reisse5c435c2010-10-06 17:10:38 +0000355 // Write some data to the transport to hopefully unblock it.
Roger Meier0069cc42010-10-13 18:10:18 +0000356 uint8_t* buf = new uint8_t[info->writeLength];
David Reisse5c435c2010-10-06 17:10:38 +0000357 memset(buf, 'b', info->writeLength);
Roger Meier0069cc42010-10-13 18:10:18 +0000358 boost::scoped_array<uint8_t> array(buf);
David Reisse5c435c2010-10-06 17:10:38 +0000359 info->transport->write(buf, info->writeLength);
360 info->transport->flush();
361
362 delete info;
363}
364
Jake Farrell5d02b802014-01-07 21:42:01 -0500365void alarm_handler_wrapper() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100366 int64_t timeout = 0; // timeout of 0 means wait forever
367 while (true) {
Jake Farrell5d02b802014-01-07 21:42:01 -0500368 bool fireHandler = false;
369 {
370 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100371 if (g_teardown)
372 return;
373 // calculate timeout
Jake Farrell5d02b802014-01-07 21:42:01 -0500374 if (g_triggerInfo == NULL) {
375 timeout = 0;
376 } else {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100377 timeout = g_triggerInfo->timeoutSeconds * 1000;
Jake Farrell5d02b802014-01-07 21:42:01 -0500378 }
379
380 int waitResult = g_alarm_monitor.waitForTimeRelative(timeout);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100381 if (waitResult == THRIFT_ETIMEDOUT)
Jake Farrell5d02b802014-01-07 21:42:01 -0500382 fireHandler = true;
383 }
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100384 if (fireHandler)
385 alarm_handler(); // calling outside the lock
David Reisse5c435c2010-10-06 17:10:38 +0000386 }
David Reisse5c435c2010-10-06 17:10:38 +0000387}
388
389/**
390 * Add a trigger to be scheduled "seconds" seconds after the
391 * last currently scheduled trigger.
392 *
393 * (Note that this is not "seconds" from now. That might be more logical, but
394 * would require slightly more complicated sorting, rather than just appending
395 * to the end.)
396 */
397void add_trigger(unsigned int seconds,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100398 const boost::shared_ptr<TTransport>& transport,
David Reisse5c435c2010-10-06 17:10:38 +0000399 uint32_t write_len) {
400 TriggerInfo* info = new TriggerInfo(seconds, transport, write_len);
Jake Farrell5d02b802014-01-07 21:42:01 -0500401 {
402 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
403 if (g_triggerInfo == NULL) {
404 // This is the first trigger.
405 // Set g_triggerInfo, and schedule the alarm
406 g_triggerInfo = info;
407 g_alarm_monitor.notify();
408 } else {
409 // Add this trigger to the end of the list
410 TriggerInfo* prev = g_triggerInfo;
411 while (prev->next) {
412 prev = prev->next;
413 }
414 prev->next = info;
David Reisse5c435c2010-10-06 17:10:38 +0000415 }
David Reisse5c435c2010-10-06 17:10:38 +0000416 }
417}
418
419void clear_triggers() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100420 TriggerInfo* info = NULL;
Jake Farrell5d02b802014-01-07 21:42:01 -0500421
422 {
423 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
424 info = g_triggerInfo;
425 g_triggerInfo = NULL;
426 g_numTriggersFired = 0;
427 g_alarm_monitor.notify();
428 }
David Reisse5c435c2010-10-06 17:10:38 +0000429
430 while (info != NULL) {
431 TriggerInfo* next = info->next;
432 delete info;
433 info = next;
434 }
435}
436
437void set_trigger(unsigned int seconds,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100438 const boost::shared_ptr<TTransport>& transport,
David Reisse5c435c2010-10-06 17:10:38 +0000439 uint32_t write_len) {
440 clear_triggers();
441 add_trigger(seconds, transport, write_len);
442}
443
444/**************************************************************************
445 * Test functions
David Reiss35dc7692010-10-06 17:10:19 +0000446 **************************************************************************/
447
448/**
449 * Test interleaved write and read calls.
450 *
451 * Generates a buffer totalSize bytes long, then writes it to the transport,
452 * and verifies the written data can be read back correctly.
453 *
454 * Mode of operation:
455 * - call wChunkGenerator to figure out how large of a chunk to write
456 * - call wSizeGenerator to get the size for individual write() calls,
457 * and do this repeatedly until the entire chunk is written.
458 * - call rChunkGenerator to figure out how large of a chunk to read
459 * - call rSizeGenerator to get the size for individual read() calls,
460 * and do this repeatedly until the entire chunk is read.
461 * - repeat until the full buffer is written and read back,
462 * then compare the data read back against the original buffer
463 *
464 *
465 * - If any of the size generators return 0, this means to use the maximum
466 * possible size.
467 *
468 * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
469 * there are never more than maxOutstanding bytes waiting to be read back.
470 */
471template <class CoupledTransports>
472void test_rw(uint32_t totalSize,
473 SizeGenerator& wSizeGenerator,
474 SizeGenerator& rSizeGenerator,
475 SizeGenerator& wChunkGenerator,
476 SizeGenerator& rChunkGenerator,
477 uint32_t maxOutstanding) {
478 CoupledTransports transports;
479 BOOST_REQUIRE(transports.in != NULL);
480 BOOST_REQUIRE(transports.out != NULL);
481
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100482 boost::shared_array<uint8_t> wbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
483 boost::shared_array<uint8_t> rbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
David Reiss35dc7692010-10-06 17:10:19 +0000484
485 // store some data in wbuf
486 for (uint32_t n = 0; n < totalSize; ++n) {
487 wbuf[n] = (n & 0xff);
488 }
489 // clear rbuf
490 memset(rbuf.get(), 0, totalSize);
491
492 uint32_t total_written = 0;
493 uint32_t total_read = 0;
494 while (total_read < totalSize) {
495 // Determine how large a chunk of data to write
496 uint32_t wchunk_size = wChunkGenerator.nextSize();
497 if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
498 wchunk_size = totalSize - total_written;
499 }
500
501 // Make sure (total_written - total_read) + wchunk_size
502 // is less than maxOutstanding
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100503 if (maxOutstanding > 0 && wchunk_size > maxOutstanding - (total_written - total_read)) {
David Reiss35dc7692010-10-06 17:10:19 +0000504 wchunk_size = maxOutstanding - (total_written - total_read);
505 }
506
507 // Write the chunk
508 uint32_t chunk_written = 0;
509 while (chunk_written < wchunk_size) {
510 uint32_t write_size = wSizeGenerator.nextSize();
511 if (write_size == 0 || write_size > wchunk_size - chunk_written) {
512 write_size = wchunk_size - chunk_written;
513 }
514
Roger Meier967600e2013-05-03 22:39:53 +0200515 try {
516 transports.out->write(wbuf.get() + total_written, write_size);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100517 } catch (TTransportException& te) {
Roger Meier967600e2013-05-03 22:39:53 +0200518 if (te.getType() == TTransportException::TIMED_OUT)
519 break;
520 throw te;
521 }
David Reiss35dc7692010-10-06 17:10:19 +0000522 chunk_written += write_size;
523 total_written += write_size;
524 }
525
526 // Flush the data, so it will be available in the read transport
527 // Don't flush if wchunk_size is 0. (This should only happen if
528 // total_written == totalSize already, and we're only reading now.)
529 if (wchunk_size > 0) {
530 transports.out->flush();
531 }
532
533 // Determine how large a chunk of data to read back
534 uint32_t rchunk_size = rChunkGenerator.nextSize();
535 if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
536 rchunk_size = total_written - total_read;
537 }
538
539 // Read the chunk
540 uint32_t chunk_read = 0;
541 while (chunk_read < rchunk_size) {
542 uint32_t read_size = rSizeGenerator.nextSize();
543 if (read_size == 0 || read_size > rchunk_size - chunk_read) {
544 read_size = rchunk_size - chunk_read;
545 }
546
David Reisse94fa332010-10-06 17:10:26 +0000547 int bytes_read = -1;
548 try {
549 bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
550 } catch (TTransportException& e) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100551 BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size << ") threw exception \""
552 << e.what() << "\"; written so far: " << total_written << " / "
553 << totalSize << " bytes");
David Reisse94fa332010-10-06 17:10:26 +0000554 }
555
David Reiss35dc7692010-10-06 17:10:19 +0000556 BOOST_REQUIRE_MESSAGE(bytes_read > 0,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100557 "read(pos=" << total_read << ", size=" << read_size << ") returned "
558 << bytes_read << "; written so far: " << total_written
559 << " / " << totalSize << " bytes");
David Reiss35dc7692010-10-06 17:10:19 +0000560 chunk_read += bytes_read;
561 total_read += bytes_read;
562 }
563 }
564
565 // make sure the data read back is identical to the data written
566 BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
567}
568
David Reisse5c435c2010-10-06 17:10:38 +0000569template <class CoupledTransports>
570void test_read_part_available() {
571 CoupledTransports transports;
572 BOOST_REQUIRE(transports.in != NULL);
573 BOOST_REQUIRE(transports.out != NULL);
574
575 uint8_t write_buf[16];
576 uint8_t read_buf[16];
577 memset(write_buf, 'a', sizeof(write_buf));
578
579 // Attemping to read 10 bytes when only 9 are available should return 9
580 // immediately.
581 transports.out->write(write_buf, 9);
582 transports.out->flush();
583 set_trigger(3, transports.out, 1);
584 uint32_t bytes_read = transports.in->read(read_buf, 10);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100585 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
586 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)9);
David Reisse5c435c2010-10-06 17:10:38 +0000587
588 clear_triggers();
589}
590
591template <class CoupledTransports>
Roger Meier02c827b2012-04-11 21:59:57 +0000592void test_read_part_available_in_chunks() {
593 CoupledTransports transports;
594 BOOST_REQUIRE(transports.in != NULL);
595 BOOST_REQUIRE(transports.out != NULL);
596
597 uint8_t write_buf[16];
598 uint8_t read_buf[16];
599 memset(write_buf, 'a', sizeof(write_buf));
600
601 // Write 10 bytes (in a single frame, for transports that use framing)
602 transports.out->write(write_buf, 10);
603 transports.out->flush();
604
605 // Read 1 byte, to force the transport to read the frame
606 uint32_t bytes_read = transports.in->read(read_buf, 1);
Jake Farrell5d02b802014-01-07 21:42:01 -0500607 BOOST_CHECK_EQUAL(bytes_read, 1u);
Roger Meier02c827b2012-04-11 21:59:57 +0000608
609 // Read more than what is remaining and verify the transport does not block
610 set_trigger(3, transports.out, 1);
611 bytes_read = transports.in->read(read_buf, 10);
Jake Farrell5d02b802014-01-07 21:42:01 -0500612 BOOST_CHECK_EQUAL(g_numTriggersFired, 0u);
613 BOOST_CHECK_EQUAL(bytes_read, 9u);
Roger Meier02c827b2012-04-11 21:59:57 +0000614
615 clear_triggers();
616}
617
618template <class CoupledTransports>
David Reiss0a2d81e2010-10-06 17:10:40 +0000619void test_read_partial_midframe() {
620 CoupledTransports transports;
621 BOOST_REQUIRE(transports.in != NULL);
622 BOOST_REQUIRE(transports.out != NULL);
623
624 uint8_t write_buf[16];
625 uint8_t read_buf[16];
626 memset(write_buf, 'a', sizeof(write_buf));
627
628 // Attempt to read 10 bytes, when only 9 are available, but after we have
629 // already read part of the data that is available. This exercises a
630 // different code path for several of the transports.
631 //
632 // For transports that add their own framing (e.g., TFramedTransport and
633 // TFileTransport), the two flush calls break up the data in to a 10 byte
634 // frame and a 3 byte frame. The first read then puts us partway through the
635 // first frame, and then we attempt to read past the end of that frame, and
636 // through the next frame, too.
637 //
638 // For buffered transports that perform read-ahead (e.g.,
639 // TBufferedTransport), the read-ahead will most likely see all 13 bytes
640 // written on the first read. The next read will then attempt to read past
641 // the end of the read-ahead buffer.
642 //
643 // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
644 // transports that track framing internally.
645 transports.out->write(write_buf, 10);
646 transports.out->flush();
647 transports.out->write(write_buf, 3);
648 transports.out->flush();
649
650 // Now read 4 bytes, so that we are partway through the written data.
651 uint32_t bytes_read = transports.in->read(read_buf, 4);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100652 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)4);
David Reiss0a2d81e2010-10-06 17:10:40 +0000653
654 // Now attempt to read 10 bytes. Only 9 more are available.
655 //
656 // We should be able to get all 9 bytes, but it might take multiple read
657 // calls, since it is valid for read() to return fewer bytes than requested.
658 // (Most transports do immediately return 9 bytes, but the framing transports
659 // tend to only return to the end of the current frame, which is 6 bytes in
660 // this case.)
661 uint32_t total_read = 0;
662 while (total_read < 9) {
663 set_trigger(3, transports.out, 1);
664 bytes_read = transports.in->read(read_buf, 10);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100665 BOOST_REQUIRE_EQUAL(g_numTriggersFired, (unsigned int)0);
666 BOOST_REQUIRE_GT(bytes_read, (uint32_t)0);
David Reiss0a2d81e2010-10-06 17:10:40 +0000667 total_read += bytes_read;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100668 BOOST_REQUIRE_LE(total_read, (uint32_t)9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000669 }
670
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100671 BOOST_CHECK_EQUAL(total_read, (uint32_t)9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000672
673 clear_triggers();
674}
675
676template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000677void test_borrow_part_available() {
678 CoupledTransports transports;
679 BOOST_REQUIRE(transports.in != NULL);
680 BOOST_REQUIRE(transports.out != NULL);
681
682 uint8_t write_buf[16];
683 uint8_t read_buf[16];
684 memset(write_buf, 'a', sizeof(write_buf));
685
686 // Attemping to borrow 10 bytes when only 9 are available should return NULL
687 // immediately.
688 transports.out->write(write_buf, 9);
689 transports.out->flush();
690 set_trigger(3, transports.out, 1);
691 uint32_t borrow_len = 10;
692 const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100693 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000694 BOOST_CHECK(borrowed_buf == NULL);
695
696 clear_triggers();
697}
698
699template <class CoupledTransports>
700void test_read_none_available() {
701 CoupledTransports transports;
702 BOOST_REQUIRE(transports.in != NULL);
703 BOOST_REQUIRE(transports.out != NULL);
704
705 uint8_t write_buf[16];
706 uint8_t read_buf[16];
707 memset(write_buf, 'a', sizeof(write_buf));
708
709 // Attempting to read when no data is available should either block until
710 // some data is available, or fail immediately. (e.g., TSocket blocks,
711 // TMemoryBuffer just fails.)
712 //
713 // If the transport blocks, it should succeed once some data is available,
714 // even if less than the amount requested becomes available.
715 set_trigger(1, transports.out, 2);
716 add_trigger(1, transports.out, 8);
717 uint32_t bytes_read = transports.in->read(read_buf, 10);
718 if (bytes_read == 0) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100719 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000720 clear_triggers();
721 } else {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100722 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)1);
723 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)2);
David Reisse5c435c2010-10-06 17:10:38 +0000724 }
725
726 clear_triggers();
727}
728
729template <class CoupledTransports>
730void test_borrow_none_available() {
731 CoupledTransports transports;
732 BOOST_REQUIRE(transports.in != NULL);
733 BOOST_REQUIRE(transports.out != NULL);
734
735 uint8_t write_buf[16];
736 memset(write_buf, 'a', sizeof(write_buf));
737
738 // Attempting to borrow when no data is available should fail immediately
739 set_trigger(1, transports.out, 10);
740 uint32_t borrow_len = 10;
741 const uint8_t* borrowed_buf = transports.in->borrow(NULL, &borrow_len);
742 BOOST_CHECK(borrowed_buf == NULL);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100743 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000744
745 clear_triggers();
746}
747
David Reiss35dc7692010-10-06 17:10:19 +0000748/**************************************************************************
749 * Test case generation
750 *
751 * Pretty ugly and annoying. This would be much easier if we the unit test
752 * framework didn't force each test to be a separate function.
753 * - Writing a completely separate function definition for each of these would
754 * result in a lot of repetitive boilerplate code.
755 * - Combining many tests into a single function makes it more difficult to
756 * tell precisely which tests failed. It also means you can't get a progress
757 * update after each test, and the tests are already fairly slow.
Konrad Grochowski3b5dacb2014-11-24 10:55:31 +0100758 * - Similar registration could be achieved with BOOST_TEST_CASE_TEMPLATE,
David Reiss35dc7692010-10-06 17:10:19 +0000759 * but it requires a lot of awkward MPL code, and results in useless test
760 * case names. (The names are generated from std::type_info::name(), which
761 * is compiler-dependent. gcc returns mangled names.)
762 **************************************************************************/
763
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100764#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
765 addTestRW<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports), totalSize, ##__VA_ARGS__);
David Reissd4788df2010-10-06 17:10:37 +0000766
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100767#define TEST_RW(CoupledTransports, totalSize, ...) \
768 do { \
769 /* Add the test as specified, to test the non-virtual function calls */ \
770 ADD_TEST_RW(CoupledTransports, totalSize, ##__VA_ARGS__); \
771 /* \
772 * Also test using the transport as a TTransport*, to test \
773 * the read_virt()/write_virt() calls \
774 */ \
775 ADD_TEST_RW(CoupledTTransports<CoupledTransports>, totalSize, ##__VA_ARGS__); \
776 /* Test wrapping the transport with TBufferedTransport */ \
777 ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
778 /* Test wrapping the transport with TFramedTransports */ \
779 ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
780 /* Test wrapping the transport with TZlibTransport */ \
781 ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000782 } while (0)
783
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100784#define ADD_TEST_BLOCKING(CoupledTransports) \
785 addTestBlocking<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports));
David Reisse5c435c2010-10-06 17:10:38 +0000786
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100787#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
788 ADD_TEST_BLOCKING(CoupledTransports); \
789 ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
790 ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
791 ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
David Reisse5c435c2010-10-06 17:10:38 +0000792 ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
793
David Reiss35dc7692010-10-06 17:10:19 +0000794class TransportTestGen {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100795public:
796 TransportTestGen(boost::unit_test::test_suite* suite, float sizeMultiplier)
797 : suite_(suite), sizeMultiplier_(sizeMultiplier) {}
David Reiss35dc7692010-10-06 17:10:19 +0000798
799 void generate() {
800 GenericSizeGenerator rand4k(1, 4096);
801
802 /*
803 * We do the basically the same set of tests for each transport type,
804 * although we tweak the parameters in some places.
805 */
806
David Reissd4788df2010-10-06 17:10:37 +0000807 // TMemoryBuffer tests
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100808 TEST_RW(CoupledMemoryBuffers, 1024 * 1024, 0, 0);
809 TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k);
810 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163);
811 TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000812
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100813 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 0, 0, rand4k, rand4k);
814 TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
815 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163, rand4k, rand4k);
816 TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1, rand4k, rand4k);
David Reisse94fa332010-10-06 17:10:26 +0000817
David Reisse5c435c2010-10-06 17:10:38 +0000818 TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
819
Jake Farrell5d02b802014-01-07 21:42:01 -0500820#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000821 // TFDTransport tests
822 // Since CoupledFDTransports tests with a pipe, writes will block
823 // if there is too much outstanding unread data in the pipe.
824 uint32_t fd_max_outstanding = 4096;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100825 TEST_RW(CoupledFDTransports, 1024 * 1024, 0, 0, 0, 0, fd_max_outstanding);
826 TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, 0, 0, fd_max_outstanding);
827 TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, 0, 0, fd_max_outstanding);
828 TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, 0, 0, fd_max_outstanding);
David Reiss35dc7692010-10-06 17:10:19 +0000829
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100830 TEST_RW(CoupledFDTransports, 1024 * 256, 0, 0, rand4k, rand4k, fd_max_outstanding);
831 TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k, fd_max_outstanding);
832 TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, rand4k, rand4k, fd_max_outstanding);
833 TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, rand4k, rand4k, fd_max_outstanding);
David Reiss35dc7692010-10-06 17:10:19 +0000834
David Reisse5c435c2010-10-06 17:10:38 +0000835 TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500836#endif //_WIN32
David Reisse5c435c2010-10-06 17:10:38 +0000837
David Reiss0c025e82010-10-06 17:10:36 +0000838 // TSocket tests
839 uint32_t socket_max_outstanding = 4096;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100840 TEST_RW(CoupledSocketTransports, 1024 * 1024, 0, 0, 0, 0, socket_max_outstanding);
841 TEST_RW(CoupledSocketTransports, 1024 * 256, rand4k, rand4k, 0, 0, socket_max_outstanding);
842 TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, 0, 0, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000843 // Doh. Apparently writing to a socket has some additional overhead for
844 // each send() call. If we have more than ~400 outstanding 1-byte write
845 // requests, additional send() calls start blocking.
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100846 TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, 0, 0, socket_max_outstanding);
847 TEST_RW(CoupledSocketTransports, 1024 * 256, 0, 0, rand4k, rand4k, socket_max_outstanding);
848 TEST_RW(CoupledSocketTransports,
849 1024 * 256,
850 rand4k,
851 rand4k,
852 rand4k,
853 rand4k,
854 socket_max_outstanding);
855 TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, rand4k, rand4k, socket_max_outstanding);
856 TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, rand4k, rand4k, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000857
David Reisse5c435c2010-10-06 17:10:38 +0000858 TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
859
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100860// 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 -0500861#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000862 // TFileTransport tests
863 // We use smaller buffer sizes here, since TFileTransport is fairly slow.
864 //
865 // TFileTransport can't write more than 16MB at once
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100866 uint32_t max_write_at_once = 1024 * 1024 * 16 - 4;
867 TEST_RW(CoupledFileTransports, 1024 * 1024, max_write_at_once, 0);
868 TEST_RW(CoupledFileTransports, 1024 * 128, rand4k, rand4k);
869 TEST_RW(CoupledFileTransports, 1024 * 128, 167, 163);
870 TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000871
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100872 TEST_RW(CoupledFileTransports, 1024 * 64, 0, 0, rand4k, rand4k);
873 TEST_RW(CoupledFileTransports, 1024 * 64, rand4k, rand4k, rand4k, rand4k);
874 TEST_RW(CoupledFileTransports, 1024 * 64, 167, 163, rand4k, rand4k);
875 TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000876
David Reisse5c435c2010-10-06 17:10:38 +0000877 TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500878#endif
David Reisse5c435c2010-10-06 17:10:38 +0000879
David Reissd4788df2010-10-06 17:10:37 +0000880 // Add some tests that access TBufferedTransport and TFramedTransport
881 // via TTransport pointers and TBufferBase pointers.
David Reisse5c435c2010-10-06 17:10:38 +0000882 ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100883 1024 * 1024,
884 rand4k,
885 rand4k,
886 rand4k,
887 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000888 ADD_TEST_RW(CoupledBufferBases<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(CoupledTTransports<CoupledFramedTransports>,
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(CoupledBufferBases<CoupledFramedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100901 1024 * 1024,
902 rand4k,
903 rand4k,
904 rand4k,
905 rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000906
907 // Test using TZlibTransport via a TTransport pointer
David Reisse5c435c2010-10-06 17:10:38 +0000908 ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100909 1024 * 1024,
910 rand4k,
911 rand4k,
912 rand4k,
913 rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000914 }
915
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100916private:
David Reiss35dc7692010-10-06 17:10:19 +0000917 template <class CoupledTransports>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100918 void addTestRW(const char* transport_name,
919 uint32_t totalSize,
920 GenericSizeGenerator wSizeGen,
921 GenericSizeGenerator rSizeGen,
David Reisse5c435c2010-10-06 17:10:38 +0000922 GenericSizeGenerator wChunkSizeGen = 0,
923 GenericSizeGenerator rChunkSizeGen = 0,
924 uint32_t maxOutstanding = 0,
925 uint32_t expectedFailures = 0) {
David Reiss65e62d32010-10-06 17:10:35 +0000926 // adjust totalSize by the specified sizeMultiplier_ first
927 totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
928
David Reiss35dc7692010-10-06 17:10:19 +0000929 std::ostringstream name;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100930 name << transport_name << "::test_rw(" << totalSize << ", " << wSizeGen.describe() << ", "
931 << rSizeGen.describe() << ", " << wChunkSizeGen.describe() << ", "
932 << rChunkSizeGen.describe() << ", " << maxOutstanding << ")";
David Reiss35dc7692010-10-06 17:10:19 +0000933
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100934 boost::unit_test::callback0<> test_func
935 = apache::thrift::stdcxx::bind(test_rw<CoupledTransports>,
936 totalSize,
937 wSizeGen,
938 rSizeGen,
939 wChunkSizeGen,
940 rChunkSizeGen,
941 maxOutstanding);
942 boost::unit_test::test_case* tc = boost::unit_test::make_test_case(test_func, name.str());
David Reiss35dc7692010-10-06 17:10:19 +0000943 suite_->add(tc, expectedFailures);
Roger Meier0069cc42010-10-13 18:10:18 +0000944 }
David Reiss35dc7692010-10-06 17:10:19 +0000945
David Reisse5c435c2010-10-06 17:10:38 +0000946 template <class CoupledTransports>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100947 void addTestBlocking(const char* transportName, uint32_t expectedFailures = 0) {
David Reisse5c435c2010-10-06 17:10:38 +0000948 char name[1024];
949 boost::unit_test::test_case* tc;
950
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100951 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available()", transportName);
952 tc = boost::unit_test::make_test_case(test_read_part_available<CoupledTransports>, name);
David Reisse5c435c2010-10-06 17:10:38 +0000953 suite_->add(tc, expectedFailures);
954
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100955 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available_in_chunks()", transportName);
956 tc = boost::unit_test::make_test_case(test_read_part_available_in_chunks<CoupledTransports>,
957 name);
Roger Meier02c827b2012-04-11 21:59:57 +0000958 suite_->add(tc, expectedFailures);
959
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100960 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_partial_midframe()", transportName);
961 tc = boost::unit_test::make_test_case(test_read_partial_midframe<CoupledTransports>, name);
David Reiss0a2d81e2010-10-06 17:10:40 +0000962 suite_->add(tc, expectedFailures);
963
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100964 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_none_available()", transportName);
965 tc = boost::unit_test::make_test_case(test_read_none_available<CoupledTransports>, name);
David Reisse5c435c2010-10-06 17:10:38 +0000966 suite_->add(tc, expectedFailures);
967
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100968 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_part_available()", transportName);
969 tc = boost::unit_test::make_test_case(test_borrow_part_available<CoupledTransports>, name);
David Reisse5c435c2010-10-06 17:10:38 +0000970 suite_->add(tc, expectedFailures);
971
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100972 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_none_available()", transportName);
973 tc = boost::unit_test::make_test_case(test_borrow_none_available<CoupledTransports>, name);
David Reisse5c435c2010-10-06 17:10:38 +0000974 suite_->add(tc, expectedFailures);
975 }
976
David Reiss35dc7692010-10-06 17:10:19 +0000977 boost::unit_test::test_suite* suite_;
David Reiss65e62d32010-10-06 17:10:35 +0000978 // sizeMultiplier_ is configurable via the command line, and allows the
979 // user to adjust between smaller buffers that can be tested quickly,
980 // or larger buffers that more thoroughly exercise the code, but take
981 // longer.
982 float sizeMultiplier_;
David Reiss35dc7692010-10-06 17:10:19 +0000983};
984
985/**************************************************************************
986 * General Initialization
987 **************************************************************************/
988
Jake Farrell5d02b802014-01-07 21:42:01 -0500989struct global_fixture {
990 boost::shared_ptr<apache::thrift::concurrency::Thread> alarmThread_;
991 global_fixture() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100992#if _WIN32
Jake Farrell5d02b802014-01-07 21:42:01 -0500993 apache::thrift::transport::TWinsockSingleton::create();
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100994#endif
David Reiss35dc7692010-10-06 17:10:19 +0000995
Jake Farrell5d02b802014-01-07 21:42:01 -0500996 apache::thrift::concurrency::PlatformThreadFactory factory;
997 factory.setDetached(false);
998
999 alarmThread_ = factory.newThread(
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001000 apache::thrift::concurrency::FunctionRunner::create(alarm_handler_wrapper));
Jake Farrell5d02b802014-01-07 21:42:01 -05001001 alarmThread_->start();
1002 }
1003 ~global_fixture() {
1004 {
1005 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
1006 g_teardown = true;
1007 g_alarm_monitor.notify();
1008 }
1009 alarmThread_->join();
1010 }
David Reiss65e62d32010-10-06 17:10:35 +00001011};
1012
Jake Farrell5d02b802014-01-07 21:42:01 -05001013BOOST_GLOBAL_FIXTURE(global_fixture)
David Reiss35dc7692010-10-06 17:10:19 +00001014
1015boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
Konrad Grochowskib3f5ffc2014-11-06 19:32:59 +01001016 THRIFT_UNUSED_VARIABLE(argc);
1017 THRIFT_UNUSED_VARIABLE(argv);
Jake Farrell5d02b802014-01-07 21:42:01 -05001018 struct timeval tv;
1019 THRIFT_GETTIMEOFDAY(&tv, NULL);
1020 int seed = tv.tv_sec ^ tv.tv_usec;
David Reiss65e62d32010-10-06 17:10:35 +00001021
Jake Farrell5d02b802014-01-07 21:42:01 -05001022 initrand(seed);
David Reiss35dc7692010-10-06 17:10:19 +00001023
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001024 boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
David Reiss109693c2010-10-06 17:10:42 +00001025 suite->p_name.value = "TransportTest";
Jake Farrell5d02b802014-01-07 21:42:01 -05001026 TransportTestGen transport_test_generator(suite, 1);
David Reiss35dc7692010-10-06 17:10:19 +00001027 transport_test_generator.generate();
David Reiss109693c2010-10-06 17:10:42 +00001028 return NULL;
David Reiss35dc7692010-10-06 17:10:19 +00001029}