blob: c056a5d0a190769195c14cc215684261a8ed5890 [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>
Konrad Grochowskie9bdb412015-09-25 20:17:36 +020036#include <boost/version.hpp>
David Reiss35dc7692010-10-06 17:10:19 +000037
Roger Meier49ff8b12012-04-13 09:12:31 +000038#include <thrift/transport/TBufferTransports.h>
39#include <thrift/transport/TFDTransport.h>
40#include <thrift/transport/TFileTransport.h>
41#include <thrift/transport/TZlibTransport.h>
42#include <thrift/transport/TSocket.h>
David Reiss35dc7692010-10-06 17:10:19 +000043
Jake Farrell5d02b802014-01-07 21:42:01 -050044#include <thrift/concurrency/FunctionRunner.h>
45#if _WIN32
Konrad Grochowski16a23a62014-11-13 15:33:38 +010046#include <thrift/windows/TWinsockSingleton.h>
Jake Farrell5d02b802014-01-07 21:42:01 -050047#endif
48
David Reiss35dc7692010-10-06 17:10:19 +000049using namespace apache::thrift::transport;
50
51static boost::mt19937 rng;
David Reiss35dc7692010-10-06 17:10:19 +000052
David Reiss65e62d32010-10-06 17:10:35 +000053void initrand(unsigned int seed) {
David Reiss35dc7692010-10-06 17:10:19 +000054 rng.seed(seed);
55}
56
57class SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010058public:
David Reiss35dc7692010-10-06 17:10:19 +000059 virtual ~SizeGenerator() {}
60 virtual uint32_t nextSize() = 0;
61 virtual std::string describe() const = 0;
62};
63
64class ConstantSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010065public:
David Reiss35dc7692010-10-06 17:10:19 +000066 ConstantSizeGenerator(uint32_t value) : value_(value) {}
67 uint32_t nextSize() { return value_; }
68 std::string describe() const {
69 std::ostringstream desc;
70 desc << value_;
71 return desc.str();
72 }
73
Konrad Grochowski16a23a62014-11-13 15:33:38 +010074private:
David Reiss35dc7692010-10-06 17:10:19 +000075 uint32_t value_;
76};
77
78class RandomSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010079public:
80 RandomSizeGenerator(uint32_t min, uint32_t max)
81 : generator_(rng, boost::uniform_int<int>(min, max)) {}
David Reiss35dc7692010-10-06 17:10:19 +000082
83 uint32_t nextSize() { return generator_(); }
84
85 std::string describe() const {
86 std::ostringstream desc;
87 desc << "rand(" << getMin() << ", " << getMax() << ")";
88 return desc.str();
89 }
90
Jake Farrell5d02b802014-01-07 21:42:01 -050091 uint32_t getMin() const { return (generator_.distribution().min)(); }
92 uint32_t getMax() const { return (generator_.distribution().max)(); }
David Reiss35dc7692010-10-06 17:10:19 +000093
Konrad Grochowski16a23a62014-11-13 15:33:38 +010094private:
95 boost::variate_generator<boost::mt19937&, boost::uniform_int<int> > generator_;
David Reiss35dc7692010-10-06 17:10:19 +000096};
97
98/**
99 * This class exists solely to make the TEST_RW() macro easier to use.
100 * - it can be constructed implicitly from an integer
101 * - it can contain either a ConstantSizeGenerator or a RandomSizeGenerator
102 * (TEST_RW can't take a SizeGenerator pointer or reference, since it needs
103 * to make a copy of the generator to bind it to the test function.)
104 */
105class GenericSizeGenerator : public SizeGenerator {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100106public:
107 GenericSizeGenerator(uint32_t value) : generator_(new ConstantSizeGenerator(value)) {}
108 GenericSizeGenerator(uint32_t min, uint32_t max)
109 : generator_(new RandomSizeGenerator(min, max)) {}
David Reiss35dc7692010-10-06 17:10:19 +0000110
111 uint32_t nextSize() { return generator_->nextSize(); }
112 std::string describe() const { return generator_->describe(); }
113
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100114private:
David Reiss35dc7692010-10-06 17:10:19 +0000115 boost::shared_ptr<SizeGenerator> generator_;
116};
117
118/**************************************************************************
119 * Classes to set up coupled transports
120 **************************************************************************/
121
David Reiss0c025e82010-10-06 17:10:36 +0000122/**
123 * Helper class to represent a coupled pair of transports.
124 *
125 * Data written to the out transport can be read from the in transport.
126 *
127 * This is used as the base class for the various coupled transport
128 * implementations. It shouldn't be instantiated directly.
129 */
David Reiss35dc7692010-10-06 17:10:19 +0000130template <class Transport_>
131class CoupledTransports {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100132public:
Roger Meier6f7681f2011-11-06 12:04:28 +0000133 virtual ~CoupledTransports() {}
David Reiss35dc7692010-10-06 17:10:19 +0000134 typedef Transport_ TransportType;
135
David Reissd4788df2010-10-06 17:10:37 +0000136 CoupledTransports() : in(), out() {}
David Reiss35dc7692010-10-06 17:10:19 +0000137
David Reissd4788df2010-10-06 17:10:37 +0000138 boost::shared_ptr<Transport_> in;
139 boost::shared_ptr<Transport_> out;
David Reiss35dc7692010-10-06 17:10:19 +0000140
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100141private:
David Reiss35dc7692010-10-06 17:10:19 +0000142 CoupledTransports(const CoupledTransports&);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100143 CoupledTransports& operator=(const CoupledTransports&);
David Reiss35dc7692010-10-06 17:10:19 +0000144};
145
David Reiss0c025e82010-10-06 17:10:36 +0000146/**
147 * Coupled TMemoryBuffers
148 */
David Reiss35dc7692010-10-06 17:10:19 +0000149class CoupledMemoryBuffers : public CoupledTransports<TMemoryBuffer> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100150public:
151 CoupledMemoryBuffers() : buf(new TMemoryBuffer) {
David Reissd4788df2010-10-06 17:10:37 +0000152 in = buf;
153 out = buf;
David Reiss35dc7692010-10-06 17:10:19 +0000154 }
155
David Reissd4788df2010-10-06 17:10:37 +0000156 boost::shared_ptr<TMemoryBuffer> buf;
157};
158
159/**
160 * Helper template class for creating coupled transports that wrap
161 * another transport.
162 */
163template <class WrapperTransport_, class InnerCoupledTransports_>
164class CoupledWrapperTransportsT : public CoupledTransports<WrapperTransport_> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100165public:
David Reissd4788df2010-10-06 17:10:37 +0000166 CoupledWrapperTransportsT() {
167 if (inner_.in) {
168 this->in.reset(new WrapperTransport_(inner_.in));
169 }
170 if (inner_.out) {
171 this->out.reset(new WrapperTransport_(inner_.out));
172 }
173 }
174
175 InnerCoupledTransports_ inner_;
David Reiss35dc7692010-10-06 17:10:19 +0000176};
177
David Reiss0c025e82010-10-06 17:10:36 +0000178/**
179 * Coupled TBufferedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000180 */
David Reissd4788df2010-10-06 17:10:37 +0000181template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100182class CoupledBufferedTransportsT
183 : public CoupledWrapperTransportsT<TBufferedTransport, InnerTransport_> {};
David Reiss35dc7692010-10-06 17:10:19 +0000184
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100185typedef CoupledBufferedTransportsT<CoupledMemoryBuffers> CoupledBufferedTransports;
David Reissd4788df2010-10-06 17:10:37 +0000186
David Reiss0c025e82010-10-06 17:10:36 +0000187/**
188 * Coupled TFramedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000189 */
David Reissd4788df2010-10-06 17:10:37 +0000190template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100191class CoupledFramedTransportsT
192 : public CoupledWrapperTransportsT<TFramedTransport, InnerTransport_> {};
David Reiss35dc7692010-10-06 17:10:19 +0000193
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100194typedef CoupledFramedTransportsT<CoupledMemoryBuffers> CoupledFramedTransports;
David Reissd4788df2010-10-06 17:10:37 +0000195
David Reiss0c025e82010-10-06 17:10:36 +0000196/**
197 * Coupled TZlibTransports.
198 */
David Reissd4788df2010-10-06 17:10:37 +0000199template <class InnerTransport_>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100200class CoupledZlibTransportsT : public CoupledWrapperTransportsT<TZlibTransport, InnerTransport_> {};
David Reisse94fa332010-10-06 17:10:26 +0000201
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100202typedef CoupledZlibTransportsT<CoupledMemoryBuffers> CoupledZlibTransports;
David Reissd4788df2010-10-06 17:10:37 +0000203
Jake Farrell5d02b802014-01-07 21:42:01 -0500204#ifndef _WIN32
205// FD transport doesn't make much sense on Windows.
David Reiss0c025e82010-10-06 17:10:36 +0000206/**
207 * Coupled TFDTransports.
208 */
David Reiss35dc7692010-10-06 17:10:19 +0000209class CoupledFDTransports : public CoupledTransports<TFDTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100210public:
David Reiss35dc7692010-10-06 17:10:19 +0000211 CoupledFDTransports() {
212 int pipes[2];
213
214 if (pipe(pipes) != 0) {
215 return;
216 }
217
David Reissd4788df2010-10-06 17:10:37 +0000218 in.reset(new TFDTransport(pipes[0], TFDTransport::CLOSE_ON_DESTROY));
219 out.reset(new TFDTransport(pipes[1], TFDTransport::CLOSE_ON_DESTROY));
David Reiss35dc7692010-10-06 17:10:19 +0000220 }
221};
Jake Farrell5d02b802014-01-07 21:42:01 -0500222#endif
David Reiss35dc7692010-10-06 17:10:19 +0000223
David Reiss0c025e82010-10-06 17:10:36 +0000224/**
225 * Coupled TSockets
226 */
227class CoupledSocketTransports : public CoupledTransports<TSocket> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100228public:
David Reiss0c025e82010-10-06 17:10:36 +0000229 CoupledSocketTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500230 THRIFT_SOCKET sockets[2] = {0};
231 if (THRIFT_SOCKETPAIR(PF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
David Reiss0c025e82010-10-06 17:10:36 +0000232 return;
233 }
234
David Reissd4788df2010-10-06 17:10:37 +0000235 in.reset(new TSocket(sockets[0]));
236 out.reset(new TSocket(sockets[1]));
Roger Meier967600e2013-05-03 22:39:53 +0200237 out->setSendTimeout(100);
David Reiss0c025e82010-10-06 17:10:36 +0000238 }
239};
240
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100241// 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 -0500242#ifndef _WIN32
David Reiss0c025e82010-10-06 17:10:36 +0000243/**
244 * Coupled TFileTransports
245 */
David Reiss35dc7692010-10-06 17:10:19 +0000246class CoupledFileTransports : public CoupledTransports<TFileTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100247public:
David Reiss35dc7692010-10-06 17:10:19 +0000248 CoupledFileTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500249#ifndef _WIN32
250 const char* tmp_dir = "/tmp";
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100251#define FILENAME_SUFFIX "/thrift.transport_test"
Jake Farrell5d02b802014-01-07 21:42:01 -0500252#else
253 const char* tmp_dir = getenv("TMP");
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100254#define FILENAME_SUFFIX "\\thrift.transport_test"
Jake Farrell5d02b802014-01-07 21:42:01 -0500255#endif
256
David Reiss35dc7692010-10-06 17:10:19 +0000257 // Create a temporary file to use
Jake Farrell5d02b802014-01-07 21:42:01 -0500258 filename.resize(strlen(tmp_dir) + strlen(FILENAME_SUFFIX));
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100259 THRIFT_SNPRINTF(&filename[0], filename.size(), "%s" FILENAME_SUFFIX, tmp_dir);
260#undef FILENAME_SUFFIX
Jake Farrell5d02b802014-01-07 21:42:01 -0500261
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100262 { std::ofstream dummy_creation(filename.c_str(), std::ofstream::trunc); }
David Reiss35dc7692010-10-06 17:10:19 +0000263
David Reissd4788df2010-10-06 17:10:37 +0000264 in.reset(new TFileTransport(filename, true));
265 out.reset(new TFileTransport(filename));
David Reiss35dc7692010-10-06 17:10:19 +0000266 }
267
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100268 ~CoupledFileTransports() { remove(filename.c_str()); }
David Reiss35dc7692010-10-06 17:10:19 +0000269
Jake Farrell5d02b802014-01-07 21:42:01 -0500270 std::string filename;
David Reiss35dc7692010-10-06 17:10:19 +0000271};
Jake Farrell5d02b802014-01-07 21:42:01 -0500272#endif
David Reiss35dc7692010-10-06 17:10:19 +0000273
David Reiss0c025e82010-10-06 17:10:36 +0000274/**
275 * Wrapper around another CoupledTransports implementation that exposes the
276 * transports as TTransport pointers.
277 *
278 * This is used since accessing a transport via a "TTransport*" exercises a
279 * different code path than using the base pointer class. As part of the
280 * template code changes, most transport methods are no longer virtual.
281 */
David Reiss35dc7692010-10-06 17:10:19 +0000282template <class CoupledTransports_>
283class CoupledTTransports : public CoupledTransports<TTransport> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100284public:
David Reiss35dc7692010-10-06 17:10:19 +0000285 CoupledTTransports() : transports() {
286 in = transports.in;
287 out = transports.out;
288 }
289
290 CoupledTransports_ transports;
291};
292
David Reiss0c025e82010-10-06 17:10:36 +0000293/**
294 * Wrapper around another CoupledTransports implementation that exposes the
295 * transports as TBufferBase pointers.
296 *
297 * This can only be instantiated with a transport type that is a subclass of
298 * TBufferBase.
299 */
David Reiss35dc7692010-10-06 17:10:19 +0000300template <class CoupledTransports_>
301class CoupledBufferBases : public CoupledTransports<TBufferBase> {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100302public:
David Reiss35dc7692010-10-06 17:10:19 +0000303 CoupledBufferBases() : transports() {
304 in = transports.in;
305 out = transports.out;
306 }
307
308 CoupledTransports_ transports;
309};
310
David Reiss35dc7692010-10-06 17:10:19 +0000311/**************************************************************************
David Reisse5c435c2010-10-06 17:10:38 +0000312 * Alarm handling code for use in tests that check the transport blocking
313 * semantics.
314 *
315 * If the transport ends up blocking, we don't want to hang forever. We use
316 * SIGALRM to fire schedule signal to wake up and try to write data so the
317 * transport will unblock.
318 *
319 * It isn't really the safest thing in the world to be mucking around with
320 * complicated global data structures in a signal handler. It should probably
321 * be okay though, since we know the main thread should always be blocked in a
322 * read() request when the signal handler is running.
323 **************************************************************************/
324
325struct TriggerInfo {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100326 TriggerInfo(int seconds, const boost::shared_ptr<TTransport>& transport, uint32_t writeLength)
327 : timeoutSeconds(seconds), transport(transport), writeLength(writeLength), next(NULL) {}
David Reisse5c435c2010-10-06 17:10:38 +0000328
329 int timeoutSeconds;
330 boost::shared_ptr<TTransport> transport;
331 uint32_t writeLength;
332 TriggerInfo* next;
333};
334
Jake Farrell5d02b802014-01-07 21:42:01 -0500335apache::thrift::concurrency::Monitor g_alarm_monitor;
336TriggerInfo* g_triggerInfo;
337unsigned int g_numTriggersFired;
338bool g_teardown = false;
David Reisse5c435c2010-10-06 17:10:38 +0000339
Jake Farrell5d02b802014-01-07 21:42:01 -0500340void alarm_handler() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100341 TriggerInfo* info = NULL;
Jake Farrell5d02b802014-01-07 21:42:01 -0500342 {
343 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
344 // The alarm timed out, which almost certainly means we're stuck
345 // on a transport that is incorrectly blocked.
346 ++g_numTriggersFired;
David Reisse5c435c2010-10-06 17:10:38 +0000347
Jake Farrell5d02b802014-01-07 21:42:01 -0500348 // Note: we print messages to stdout instead of stderr, since
349 // tools/test/runner only records stdout messages in the failure messages for
350 // boost tests. (boost prints its test info to stdout.)
351 printf("Timeout alarm expired; attempting to unblock transport\n");
352 if (g_triggerInfo == NULL) {
353 printf(" trigger stack is empty!\n");
354 }
David Reisse5c435c2010-10-06 17:10:38 +0000355
Jake Farrell5d02b802014-01-07 21:42:01 -0500356 // Pop off the first TriggerInfo.
357 // If there is another one, schedule an alarm for it.
358 info = g_triggerInfo;
359 g_triggerInfo = info->next;
David Reisse5c435c2010-10-06 17:10:38 +0000360 }
361
David Reisse5c435c2010-10-06 17:10:38 +0000362 // Write some data to the transport to hopefully unblock it.
Roger Meier0069cc42010-10-13 18:10:18 +0000363 uint8_t* buf = new uint8_t[info->writeLength];
David Reisse5c435c2010-10-06 17:10:38 +0000364 memset(buf, 'b', info->writeLength);
Roger Meier0069cc42010-10-13 18:10:18 +0000365 boost::scoped_array<uint8_t> array(buf);
David Reisse5c435c2010-10-06 17:10:38 +0000366 info->transport->write(buf, info->writeLength);
367 info->transport->flush();
368
369 delete info;
370}
371
Jake Farrell5d02b802014-01-07 21:42:01 -0500372void alarm_handler_wrapper() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100373 int64_t timeout = 0; // timeout of 0 means wait forever
374 while (true) {
Jake Farrell5d02b802014-01-07 21:42:01 -0500375 bool fireHandler = false;
376 {
377 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100378 if (g_teardown)
379 return;
380 // calculate timeout
Jake Farrell5d02b802014-01-07 21:42:01 -0500381 if (g_triggerInfo == NULL) {
382 timeout = 0;
383 } else {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100384 timeout = g_triggerInfo->timeoutSeconds * 1000;
Jake Farrell5d02b802014-01-07 21:42:01 -0500385 }
386
387 int waitResult = g_alarm_monitor.waitForTimeRelative(timeout);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100388 if (waitResult == THRIFT_ETIMEDOUT)
Jake Farrell5d02b802014-01-07 21:42:01 -0500389 fireHandler = true;
390 }
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100391 if (fireHandler)
392 alarm_handler(); // calling outside the lock
David Reisse5c435c2010-10-06 17:10:38 +0000393 }
David Reisse5c435c2010-10-06 17:10:38 +0000394}
395
396/**
397 * Add a trigger to be scheduled "seconds" seconds after the
398 * last currently scheduled trigger.
399 *
400 * (Note that this is not "seconds" from now. That might be more logical, but
401 * would require slightly more complicated sorting, rather than just appending
402 * to the end.)
403 */
404void add_trigger(unsigned int seconds,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100405 const boost::shared_ptr<TTransport>& transport,
David Reisse5c435c2010-10-06 17:10:38 +0000406 uint32_t write_len) {
407 TriggerInfo* info = new TriggerInfo(seconds, transport, write_len);
Jake Farrell5d02b802014-01-07 21:42:01 -0500408 {
409 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
410 if (g_triggerInfo == NULL) {
411 // This is the first trigger.
412 // Set g_triggerInfo, and schedule the alarm
413 g_triggerInfo = info;
414 g_alarm_monitor.notify();
415 } else {
416 // Add this trigger to the end of the list
417 TriggerInfo* prev = g_triggerInfo;
418 while (prev->next) {
419 prev = prev->next;
420 }
421 prev->next = info;
David Reisse5c435c2010-10-06 17:10:38 +0000422 }
David Reisse5c435c2010-10-06 17:10:38 +0000423 }
424}
425
426void clear_triggers() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100427 TriggerInfo* info = NULL;
Jake Farrell5d02b802014-01-07 21:42:01 -0500428
429 {
430 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
431 info = g_triggerInfo;
432 g_triggerInfo = NULL;
433 g_numTriggersFired = 0;
434 g_alarm_monitor.notify();
435 }
David Reisse5c435c2010-10-06 17:10:38 +0000436
437 while (info != NULL) {
438 TriggerInfo* next = info->next;
439 delete info;
440 info = next;
441 }
442}
443
444void set_trigger(unsigned int seconds,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100445 const boost::shared_ptr<TTransport>& transport,
David Reisse5c435c2010-10-06 17:10:38 +0000446 uint32_t write_len) {
447 clear_triggers();
448 add_trigger(seconds, transport, write_len);
449}
450
451/**************************************************************************
452 * Test functions
David Reiss35dc7692010-10-06 17:10:19 +0000453 **************************************************************************/
454
455/**
456 * Test interleaved write and read calls.
457 *
458 * Generates a buffer totalSize bytes long, then writes it to the transport,
459 * and verifies the written data can be read back correctly.
460 *
461 * Mode of operation:
462 * - call wChunkGenerator to figure out how large of a chunk to write
463 * - call wSizeGenerator to get the size for individual write() calls,
464 * and do this repeatedly until the entire chunk is written.
465 * - call rChunkGenerator to figure out how large of a chunk to read
466 * - call rSizeGenerator to get the size for individual read() calls,
467 * and do this repeatedly until the entire chunk is read.
468 * - repeat until the full buffer is written and read back,
469 * then compare the data read back against the original buffer
470 *
471 *
472 * - If any of the size generators return 0, this means to use the maximum
473 * possible size.
474 *
475 * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
476 * there are never more than maxOutstanding bytes waiting to be read back.
477 */
478template <class CoupledTransports>
479void test_rw(uint32_t totalSize,
480 SizeGenerator& wSizeGenerator,
481 SizeGenerator& rSizeGenerator,
482 SizeGenerator& wChunkGenerator,
483 SizeGenerator& rChunkGenerator,
484 uint32_t maxOutstanding) {
485 CoupledTransports transports;
486 BOOST_REQUIRE(transports.in != NULL);
487 BOOST_REQUIRE(transports.out != NULL);
488
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100489 boost::shared_array<uint8_t> wbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
490 boost::shared_array<uint8_t> rbuf = boost::shared_array<uint8_t>(new uint8_t[totalSize]);
David Reiss35dc7692010-10-06 17:10:19 +0000491
492 // store some data in wbuf
493 for (uint32_t n = 0; n < totalSize; ++n) {
494 wbuf[n] = (n & 0xff);
495 }
496 // clear rbuf
497 memset(rbuf.get(), 0, totalSize);
498
499 uint32_t total_written = 0;
500 uint32_t total_read = 0;
501 while (total_read < totalSize) {
502 // Determine how large a chunk of data to write
503 uint32_t wchunk_size = wChunkGenerator.nextSize();
504 if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
505 wchunk_size = totalSize - total_written;
506 }
507
508 // Make sure (total_written - total_read) + wchunk_size
509 // is less than maxOutstanding
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100510 if (maxOutstanding > 0 && wchunk_size > maxOutstanding - (total_written - total_read)) {
David Reiss35dc7692010-10-06 17:10:19 +0000511 wchunk_size = maxOutstanding - (total_written - total_read);
512 }
513
514 // Write the chunk
515 uint32_t chunk_written = 0;
516 while (chunk_written < wchunk_size) {
517 uint32_t write_size = wSizeGenerator.nextSize();
518 if (write_size == 0 || write_size > wchunk_size - chunk_written) {
519 write_size = wchunk_size - chunk_written;
520 }
521
Roger Meier967600e2013-05-03 22:39:53 +0200522 try {
523 transports.out->write(wbuf.get() + total_written, write_size);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100524 } catch (TTransportException& te) {
Roger Meier967600e2013-05-03 22:39:53 +0200525 if (te.getType() == TTransportException::TIMED_OUT)
526 break;
527 throw te;
528 }
David Reiss35dc7692010-10-06 17:10:19 +0000529 chunk_written += write_size;
530 total_written += write_size;
531 }
532
533 // Flush the data, so it will be available in the read transport
534 // Don't flush if wchunk_size is 0. (This should only happen if
535 // total_written == totalSize already, and we're only reading now.)
536 if (wchunk_size > 0) {
537 transports.out->flush();
538 }
539
540 // Determine how large a chunk of data to read back
541 uint32_t rchunk_size = rChunkGenerator.nextSize();
542 if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
543 rchunk_size = total_written - total_read;
544 }
545
546 // Read the chunk
547 uint32_t chunk_read = 0;
548 while (chunk_read < rchunk_size) {
549 uint32_t read_size = rSizeGenerator.nextSize();
550 if (read_size == 0 || read_size > rchunk_size - chunk_read) {
551 read_size = rchunk_size - chunk_read;
552 }
553
David Reisse94fa332010-10-06 17:10:26 +0000554 int bytes_read = -1;
555 try {
556 bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
557 } catch (TTransportException& e) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100558 BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size << ") threw exception \""
559 << e.what() << "\"; written so far: " << total_written << " / "
560 << totalSize << " bytes");
David Reisse94fa332010-10-06 17:10:26 +0000561 }
562
David Reiss35dc7692010-10-06 17:10:19 +0000563 BOOST_REQUIRE_MESSAGE(bytes_read > 0,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100564 "read(pos=" << total_read << ", size=" << read_size << ") returned "
565 << bytes_read << "; written so far: " << total_written
566 << " / " << totalSize << " bytes");
David Reiss35dc7692010-10-06 17:10:19 +0000567 chunk_read += bytes_read;
568 total_read += bytes_read;
569 }
570 }
571
572 // make sure the data read back is identical to the data written
573 BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
574}
575
David Reisse5c435c2010-10-06 17:10:38 +0000576template <class CoupledTransports>
577void test_read_part_available() {
578 CoupledTransports transports;
579 BOOST_REQUIRE(transports.in != NULL);
580 BOOST_REQUIRE(transports.out != NULL);
581
582 uint8_t write_buf[16];
583 uint8_t read_buf[16];
584 memset(write_buf, 'a', sizeof(write_buf));
585
586 // Attemping to read 10 bytes when only 9 are available should return 9
587 // immediately.
588 transports.out->write(write_buf, 9);
589 transports.out->flush();
590 set_trigger(3, transports.out, 1);
591 uint32_t bytes_read = transports.in->read(read_buf, 10);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100592 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
593 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)9);
David Reisse5c435c2010-10-06 17:10:38 +0000594
595 clear_triggers();
596}
597
598template <class CoupledTransports>
Roger Meier02c827b2012-04-11 21:59:57 +0000599void test_read_part_available_in_chunks() {
600 CoupledTransports transports;
601 BOOST_REQUIRE(transports.in != NULL);
602 BOOST_REQUIRE(transports.out != NULL);
603
604 uint8_t write_buf[16];
605 uint8_t read_buf[16];
606 memset(write_buf, 'a', sizeof(write_buf));
607
608 // Write 10 bytes (in a single frame, for transports that use framing)
609 transports.out->write(write_buf, 10);
610 transports.out->flush();
611
612 // Read 1 byte, to force the transport to read the frame
613 uint32_t bytes_read = transports.in->read(read_buf, 1);
Jake Farrell5d02b802014-01-07 21:42:01 -0500614 BOOST_CHECK_EQUAL(bytes_read, 1u);
Roger Meier02c827b2012-04-11 21:59:57 +0000615
616 // Read more than what is remaining and verify the transport does not block
617 set_trigger(3, transports.out, 1);
618 bytes_read = transports.in->read(read_buf, 10);
Jake Farrell5d02b802014-01-07 21:42:01 -0500619 BOOST_CHECK_EQUAL(g_numTriggersFired, 0u);
620 BOOST_CHECK_EQUAL(bytes_read, 9u);
Roger Meier02c827b2012-04-11 21:59:57 +0000621
622 clear_triggers();
623}
624
625template <class CoupledTransports>
David Reiss0a2d81e2010-10-06 17:10:40 +0000626void test_read_partial_midframe() {
627 CoupledTransports transports;
628 BOOST_REQUIRE(transports.in != NULL);
629 BOOST_REQUIRE(transports.out != NULL);
630
631 uint8_t write_buf[16];
632 uint8_t read_buf[16];
633 memset(write_buf, 'a', sizeof(write_buf));
634
635 // Attempt to read 10 bytes, when only 9 are available, but after we have
636 // already read part of the data that is available. This exercises a
637 // different code path for several of the transports.
638 //
639 // For transports that add their own framing (e.g., TFramedTransport and
640 // TFileTransport), the two flush calls break up the data in to a 10 byte
641 // frame and a 3 byte frame. The first read then puts us partway through the
642 // first frame, and then we attempt to read past the end of that frame, and
643 // through the next frame, too.
644 //
645 // For buffered transports that perform read-ahead (e.g.,
646 // TBufferedTransport), the read-ahead will most likely see all 13 bytes
647 // written on the first read. The next read will then attempt to read past
648 // the end of the read-ahead buffer.
649 //
650 // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
651 // transports that track framing internally.
652 transports.out->write(write_buf, 10);
653 transports.out->flush();
654 transports.out->write(write_buf, 3);
655 transports.out->flush();
656
657 // Now read 4 bytes, so that we are partway through the written data.
658 uint32_t bytes_read = transports.in->read(read_buf, 4);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100659 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)4);
David Reiss0a2d81e2010-10-06 17:10:40 +0000660
661 // Now attempt to read 10 bytes. Only 9 more are available.
662 //
663 // We should be able to get all 9 bytes, but it might take multiple read
664 // calls, since it is valid for read() to return fewer bytes than requested.
665 // (Most transports do immediately return 9 bytes, but the framing transports
666 // tend to only return to the end of the current frame, which is 6 bytes in
667 // this case.)
668 uint32_t total_read = 0;
669 while (total_read < 9) {
670 set_trigger(3, transports.out, 1);
671 bytes_read = transports.in->read(read_buf, 10);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100672 BOOST_REQUIRE_EQUAL(g_numTriggersFired, (unsigned int)0);
673 BOOST_REQUIRE_GT(bytes_read, (uint32_t)0);
David Reiss0a2d81e2010-10-06 17:10:40 +0000674 total_read += bytes_read;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100675 BOOST_REQUIRE_LE(total_read, (uint32_t)9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000676 }
677
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100678 BOOST_CHECK_EQUAL(total_read, (uint32_t)9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000679
680 clear_triggers();
681}
682
683template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000684void test_borrow_part_available() {
685 CoupledTransports transports;
686 BOOST_REQUIRE(transports.in != NULL);
687 BOOST_REQUIRE(transports.out != NULL);
688
689 uint8_t write_buf[16];
690 uint8_t read_buf[16];
691 memset(write_buf, 'a', sizeof(write_buf));
692
693 // Attemping to borrow 10 bytes when only 9 are available should return NULL
694 // immediately.
695 transports.out->write(write_buf, 9);
696 transports.out->flush();
697 set_trigger(3, transports.out, 1);
698 uint32_t borrow_len = 10;
699 const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100700 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000701 BOOST_CHECK(borrowed_buf == NULL);
702
703 clear_triggers();
704}
705
706template <class CoupledTransports>
707void test_read_none_available() {
708 CoupledTransports transports;
709 BOOST_REQUIRE(transports.in != NULL);
710 BOOST_REQUIRE(transports.out != NULL);
711
712 uint8_t write_buf[16];
713 uint8_t read_buf[16];
714 memset(write_buf, 'a', sizeof(write_buf));
715
716 // Attempting to read when no data is available should either block until
717 // some data is available, or fail immediately. (e.g., TSocket blocks,
718 // TMemoryBuffer just fails.)
719 //
720 // If the transport blocks, it should succeed once some data is available,
721 // even if less than the amount requested becomes available.
722 set_trigger(1, transports.out, 2);
723 add_trigger(1, transports.out, 8);
724 uint32_t bytes_read = transports.in->read(read_buf, 10);
725 if (bytes_read == 0) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100726 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000727 clear_triggers();
728 } else {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100729 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)1);
730 BOOST_CHECK_EQUAL(bytes_read, (uint32_t)2);
David Reisse5c435c2010-10-06 17:10:38 +0000731 }
732
733 clear_triggers();
734}
735
736template <class CoupledTransports>
737void test_borrow_none_available() {
738 CoupledTransports transports;
739 BOOST_REQUIRE(transports.in != NULL);
740 BOOST_REQUIRE(transports.out != NULL);
741
742 uint8_t write_buf[16];
743 memset(write_buf, 'a', sizeof(write_buf));
744
745 // Attempting to borrow when no data is available should fail immediately
746 set_trigger(1, transports.out, 10);
747 uint32_t borrow_len = 10;
748 const uint8_t* borrowed_buf = transports.in->borrow(NULL, &borrow_len);
749 BOOST_CHECK(borrowed_buf == NULL);
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100750 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int)0);
David Reisse5c435c2010-10-06 17:10:38 +0000751
752 clear_triggers();
753}
754
David Reiss35dc7692010-10-06 17:10:19 +0000755/**************************************************************************
756 * Test case generation
757 *
758 * Pretty ugly and annoying. This would be much easier if we the unit test
759 * framework didn't force each test to be a separate function.
760 * - Writing a completely separate function definition for each of these would
761 * result in a lot of repetitive boilerplate code.
762 * - Combining many tests into a single function makes it more difficult to
763 * tell precisely which tests failed. It also means you can't get a progress
764 * update after each test, and the tests are already fairly slow.
Konrad Grochowski3b5dacb2014-11-24 10:55:31 +0100765 * - Similar registration could be achieved with BOOST_TEST_CASE_TEMPLATE,
David Reiss35dc7692010-10-06 17:10:19 +0000766 * but it requires a lot of awkward MPL code, and results in useless test
767 * case names. (The names are generated from std::type_info::name(), which
768 * is compiler-dependent. gcc returns mangled names.)
769 **************************************************************************/
770
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100771#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
772 addTestRW<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports), totalSize, ##__VA_ARGS__);
David Reissd4788df2010-10-06 17:10:37 +0000773
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100774#define TEST_RW(CoupledTransports, totalSize, ...) \
775 do { \
776 /* Add the test as specified, to test the non-virtual function calls */ \
777 ADD_TEST_RW(CoupledTransports, totalSize, ##__VA_ARGS__); \
778 /* \
779 * Also test using the transport as a TTransport*, to test \
780 * the read_virt()/write_virt() calls \
781 */ \
782 ADD_TEST_RW(CoupledTTransports<CoupledTransports>, totalSize, ##__VA_ARGS__); \
783 /* Test wrapping the transport with TBufferedTransport */ \
784 ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
785 /* Test wrapping the transport with TFramedTransports */ \
786 ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
787 /* Test wrapping the transport with TZlibTransport */ \
788 ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, totalSize, ##__VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000789 } while (0)
790
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100791#define ADD_TEST_BLOCKING(CoupledTransports) \
792 addTestBlocking<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports));
David Reisse5c435c2010-10-06 17:10:38 +0000793
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100794#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
795 ADD_TEST_BLOCKING(CoupledTransports); \
796 ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
797 ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
798 ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
David Reisse5c435c2010-10-06 17:10:38 +0000799 ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
800
David Reiss35dc7692010-10-06 17:10:19 +0000801class TransportTestGen {
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100802public:
803 TransportTestGen(boost::unit_test::test_suite* suite, float sizeMultiplier)
804 : suite_(suite), sizeMultiplier_(sizeMultiplier) {}
David Reiss35dc7692010-10-06 17:10:19 +0000805
806 void generate() {
807 GenericSizeGenerator rand4k(1, 4096);
808
809 /*
810 * We do the basically the same set of tests for each transport type,
811 * although we tweak the parameters in some places.
812 */
813
David Reissd4788df2010-10-06 17:10:37 +0000814 // TMemoryBuffer tests
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100815 TEST_RW(CoupledMemoryBuffers, 1024 * 1024, 0, 0);
816 TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k);
817 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163);
818 TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000819
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100820 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 0, 0, rand4k, rand4k);
821 TEST_RW(CoupledMemoryBuffers, 1024 * 256, rand4k, rand4k, rand4k, rand4k);
822 TEST_RW(CoupledMemoryBuffers, 1024 * 256, 167, 163, rand4k, rand4k);
823 TEST_RW(CoupledMemoryBuffers, 1024 * 16, 1, 1, rand4k, rand4k);
David Reisse94fa332010-10-06 17:10:26 +0000824
David Reisse5c435c2010-10-06 17:10:38 +0000825 TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
826
Jake Farrell5d02b802014-01-07 21:42:01 -0500827#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000828 // TFDTransport tests
829 // Since CoupledFDTransports tests with a pipe, writes will block
830 // if there is too much outstanding unread data in the pipe.
831 uint32_t fd_max_outstanding = 4096;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100832 TEST_RW(CoupledFDTransports, 1024 * 1024, 0, 0, 0, 0, fd_max_outstanding);
833 TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, 0, 0, fd_max_outstanding);
834 TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, 0, 0, fd_max_outstanding);
835 TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, 0, 0, fd_max_outstanding);
David Reiss35dc7692010-10-06 17:10:19 +0000836
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100837 TEST_RW(CoupledFDTransports, 1024 * 256, 0, 0, rand4k, rand4k, fd_max_outstanding);
838 TEST_RW(CoupledFDTransports, 1024 * 256, rand4k, rand4k, rand4k, rand4k, fd_max_outstanding);
839 TEST_RW(CoupledFDTransports, 1024 * 256, 167, 163, rand4k, rand4k, fd_max_outstanding);
840 TEST_RW(CoupledFDTransports, 1024 * 16, 1, 1, rand4k, rand4k, fd_max_outstanding);
David Reiss35dc7692010-10-06 17:10:19 +0000841
David Reisse5c435c2010-10-06 17:10:38 +0000842 TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500843#endif //_WIN32
David Reisse5c435c2010-10-06 17:10:38 +0000844
David Reiss0c025e82010-10-06 17:10:36 +0000845 // TSocket tests
846 uint32_t socket_max_outstanding = 4096;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100847 TEST_RW(CoupledSocketTransports, 1024 * 1024, 0, 0, 0, 0, socket_max_outstanding);
848 TEST_RW(CoupledSocketTransports, 1024 * 256, rand4k, rand4k, 0, 0, socket_max_outstanding);
849 TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, 0, 0, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000850 // Doh. Apparently writing to a socket has some additional overhead for
851 // each send() call. If we have more than ~400 outstanding 1-byte write
852 // requests, additional send() calls start blocking.
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100853 TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, 0, 0, socket_max_outstanding);
854 TEST_RW(CoupledSocketTransports, 1024 * 256, 0, 0, rand4k, rand4k, socket_max_outstanding);
855 TEST_RW(CoupledSocketTransports,
856 1024 * 256,
857 rand4k,
858 rand4k,
859 rand4k,
860 rand4k,
861 socket_max_outstanding);
862 TEST_RW(CoupledSocketTransports, 1024 * 256, 167, 163, rand4k, rand4k, socket_max_outstanding);
863 TEST_RW(CoupledSocketTransports, 1024 * 16, 1, 1, rand4k, rand4k, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000864
David Reisse5c435c2010-10-06 17:10:38 +0000865 TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
866
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100867// 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 -0500868#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000869 // TFileTransport tests
870 // We use smaller buffer sizes here, since TFileTransport is fairly slow.
871 //
872 // TFileTransport can't write more than 16MB at once
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100873 uint32_t max_write_at_once = 1024 * 1024 * 16 - 4;
874 TEST_RW(CoupledFileTransports, 1024 * 1024, max_write_at_once, 0);
875 TEST_RW(CoupledFileTransports, 1024 * 128, rand4k, rand4k);
876 TEST_RW(CoupledFileTransports, 1024 * 128, 167, 163);
877 TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000878
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100879 TEST_RW(CoupledFileTransports, 1024 * 64, 0, 0, rand4k, rand4k);
880 TEST_RW(CoupledFileTransports, 1024 * 64, rand4k, rand4k, rand4k, rand4k);
881 TEST_RW(CoupledFileTransports, 1024 * 64, 167, 163, rand4k, rand4k);
882 TEST_RW(CoupledFileTransports, 1024 * 2, 1, 1, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000883
David Reisse5c435c2010-10-06 17:10:38 +0000884 TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500885#endif
David Reisse5c435c2010-10-06 17:10:38 +0000886
David Reissd4788df2010-10-06 17:10:37 +0000887 // Add some tests that access TBufferedTransport and TFramedTransport
888 // via TTransport pointers and TBufferBase pointers.
David Reisse5c435c2010-10-06 17:10:38 +0000889 ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100890 1024 * 1024,
891 rand4k,
892 rand4k,
893 rand4k,
894 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000895 ADD_TEST_RW(CoupledBufferBases<CoupledBufferedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100896 1024 * 1024,
897 rand4k,
898 rand4k,
899 rand4k,
900 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000901 ADD_TEST_RW(CoupledTTransports<CoupledFramedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100902 1024 * 1024,
903 rand4k,
904 rand4k,
905 rand4k,
906 rand4k);
David Reisse5c435c2010-10-06 17:10:38 +0000907 ADD_TEST_RW(CoupledBufferBases<CoupledFramedTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100908 1024 * 1024,
909 rand4k,
910 rand4k,
911 rand4k,
912 rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000913
914 // Test using TZlibTransport via a TTransport pointer
David Reisse5c435c2010-10-06 17:10:38 +0000915 ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100916 1024 * 1024,
917 rand4k,
918 rand4k,
919 rand4k,
920 rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000921 }
922
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200923#if (BOOST_VERSION >= 105900)
924#define MAKE_TEST_CASE(_FUNC, _NAME) boost::unit_test::make_test_case(_FUNC, _NAME, __FILE__, __LINE__)
925#else
926#define MAKE_TEST_CASE(_FUNC, _NAME) boost::unit_test::make_test_case(_FUNC, _NAME)
927#endif
928
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100929private:
David Reiss35dc7692010-10-06 17:10:19 +0000930 template <class CoupledTransports>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100931 void addTestRW(const char* transport_name,
932 uint32_t totalSize,
933 GenericSizeGenerator wSizeGen,
934 GenericSizeGenerator rSizeGen,
David Reisse5c435c2010-10-06 17:10:38 +0000935 GenericSizeGenerator wChunkSizeGen = 0,
936 GenericSizeGenerator rChunkSizeGen = 0,
937 uint32_t maxOutstanding = 0,
938 uint32_t expectedFailures = 0) {
David Reiss65e62d32010-10-06 17:10:35 +0000939 // adjust totalSize by the specified sizeMultiplier_ first
940 totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
941
David Reiss35dc7692010-10-06 17:10:19 +0000942 std::ostringstream name;
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100943 name << transport_name << "::test_rw(" << totalSize << ", " << wSizeGen.describe() << ", "
944 << rSizeGen.describe() << ", " << wChunkSizeGen.describe() << ", "
945 << rChunkSizeGen.describe() << ", " << maxOutstanding << ")";
David Reiss35dc7692010-10-06 17:10:19 +0000946
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200947#if (BOOST_VERSION >= 105900)
948 boost::function<void ()> test_func
949#else
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100950 boost::unit_test::callback0<> test_func
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200951#endif
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100952 = apache::thrift::stdcxx::bind(test_rw<CoupledTransports>,
953 totalSize,
954 wSizeGen,
955 rSizeGen,
956 wChunkSizeGen,
957 rChunkSizeGen,
958 maxOutstanding);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200959 suite_->add(MAKE_TEST_CASE(test_func, name.str()), expectedFailures);
Roger Meier0069cc42010-10-13 18:10:18 +0000960 }
David Reiss35dc7692010-10-06 17:10:19 +0000961
David Reisse5c435c2010-10-06 17:10:38 +0000962 template <class CoupledTransports>
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100963 void addTestBlocking(const char* transportName, uint32_t expectedFailures = 0) {
David Reisse5c435c2010-10-06 17:10:38 +0000964 char name[1024];
David Reisse5c435c2010-10-06 17:10:38 +0000965
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100966 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200967 suite_->add(MAKE_TEST_CASE(test_read_part_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +0000968
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100969 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available_in_chunks()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200970 suite_->add(MAKE_TEST_CASE(test_read_part_available_in_chunks<CoupledTransports>, name), expectedFailures);
Roger Meier02c827b2012-04-11 21:59:57 +0000971
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100972 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_partial_midframe()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200973 suite_->add(MAKE_TEST_CASE(test_read_partial_midframe<CoupledTransports>, name), expectedFailures);
David Reiss0a2d81e2010-10-06 17:10:40 +0000974
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100975 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_none_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200976 suite_->add(MAKE_TEST_CASE(test_read_none_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +0000977
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100978 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_part_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200979 suite_->add(MAKE_TEST_CASE(test_borrow_part_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +0000980
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100981 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_none_available()", transportName);
Konrad Grochowskie9bdb412015-09-25 20:17:36 +0200982 suite_->add(MAKE_TEST_CASE(test_borrow_none_available<CoupledTransports>, name), expectedFailures);
David Reisse5c435c2010-10-06 17:10:38 +0000983 }
984
David Reiss35dc7692010-10-06 17:10:19 +0000985 boost::unit_test::test_suite* suite_;
David Reiss65e62d32010-10-06 17:10:35 +0000986 // sizeMultiplier_ is configurable via the command line, and allows the
987 // user to adjust between smaller buffers that can be tested quickly,
988 // or larger buffers that more thoroughly exercise the code, but take
989 // longer.
990 float sizeMultiplier_;
David Reiss35dc7692010-10-06 17:10:19 +0000991};
992
993/**************************************************************************
994 * General Initialization
995 **************************************************************************/
996
Jake Farrell5d02b802014-01-07 21:42:01 -0500997struct global_fixture {
998 boost::shared_ptr<apache::thrift::concurrency::Thread> alarmThread_;
999 global_fixture() {
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001000#if _WIN32
Jake Farrell5d02b802014-01-07 21:42:01 -05001001 apache::thrift::transport::TWinsockSingleton::create();
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001002#endif
David Reiss35dc7692010-10-06 17:10:19 +00001003
Jake Farrell5d02b802014-01-07 21:42:01 -05001004 apache::thrift::concurrency::PlatformThreadFactory factory;
1005 factory.setDetached(false);
1006
1007 alarmThread_ = factory.newThread(
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001008 apache::thrift::concurrency::FunctionRunner::create(alarm_handler_wrapper));
Jake Farrell5d02b802014-01-07 21:42:01 -05001009 alarmThread_->start();
1010 }
1011 ~global_fixture() {
1012 {
1013 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
1014 g_teardown = true;
1015 g_alarm_monitor.notify();
1016 }
1017 alarmThread_->join();
1018 }
David Reiss65e62d32010-10-06 17:10:35 +00001019};
1020
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001021#if (BOOST_VERSION >= 105900)
1022BOOST_GLOBAL_FIXTURE(global_fixture);
1023#else
Jake Farrell5d02b802014-01-07 21:42:01 -05001024BOOST_GLOBAL_FIXTURE(global_fixture)
Konrad Grochowskie9bdb412015-09-25 20:17:36 +02001025#endif
David Reiss35dc7692010-10-06 17:10:19 +00001026
1027boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
Konrad Grochowskib3f5ffc2014-11-06 19:32:59 +01001028 THRIFT_UNUSED_VARIABLE(argc);
1029 THRIFT_UNUSED_VARIABLE(argv);
Jake Farrell5d02b802014-01-07 21:42:01 -05001030 struct timeval tv;
1031 THRIFT_GETTIMEOFDAY(&tv, NULL);
1032 int seed = tv.tv_sec ^ tv.tv_usec;
David Reiss65e62d32010-10-06 17:10:35 +00001033
Jake Farrell5d02b802014-01-07 21:42:01 -05001034 initrand(seed);
David Reiss35dc7692010-10-06 17:10:19 +00001035
Konrad Grochowski16a23a62014-11-13 15:33:38 +01001036 boost::unit_test::test_suite* suite = &boost::unit_test::framework::master_test_suite();
David Reiss109693c2010-10-06 17:10:42 +00001037 suite->p_name.value = "TransportTest";
Jake Farrell5d02b802014-01-07 21:42:01 -05001038 TransportTestGen transport_test_generator(suite, 1);
David Reiss35dc7692010-10-06 17:10:19 +00001039 transport_test_generator.generate();
David Reiss109693c2010-10-06 17:10:42 +00001040 return NULL;
David Reiss35dc7692010-10-06 17:10:19 +00001041}