blob: 78c276474bd633fc6f3705f48cfd54c799313c85 [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
39 #include <thrift/windows/TWinsockSingleton.h>
40#endif
41
42
David Reiss35dc7692010-10-06 17:10:19 +000043using namespace apache::thrift::transport;
44
45static boost::mt19937 rng;
David Reiss35dc7692010-10-06 17:10:19 +000046
David Reiss65e62d32010-10-06 17:10:35 +000047void initrand(unsigned int seed) {
David Reiss35dc7692010-10-06 17:10:19 +000048 rng.seed(seed);
49}
50
51class SizeGenerator {
52 public:
53 virtual ~SizeGenerator() {}
54 virtual uint32_t nextSize() = 0;
55 virtual std::string describe() const = 0;
56};
57
58class ConstantSizeGenerator : public SizeGenerator {
59 public:
60 ConstantSizeGenerator(uint32_t value) : value_(value) {}
61 uint32_t nextSize() { return value_; }
62 std::string describe() const {
63 std::ostringstream desc;
64 desc << value_;
65 return desc.str();
66 }
67
68 private:
69 uint32_t value_;
70};
71
72class RandomSizeGenerator : public SizeGenerator {
73 public:
74 RandomSizeGenerator(uint32_t min, uint32_t max) :
75 generator_(rng, boost::uniform_int<int>(min, max)) {}
76
77 uint32_t nextSize() { return generator_(); }
78
79 std::string describe() const {
80 std::ostringstream desc;
81 desc << "rand(" << getMin() << ", " << getMax() << ")";
82 return desc.str();
83 }
84
Jake Farrell5d02b802014-01-07 21:42:01 -050085 uint32_t getMin() const { return (generator_.distribution().min)(); }
86 uint32_t getMax() const { return (generator_.distribution().max)(); }
David Reiss35dc7692010-10-06 17:10:19 +000087
88 private:
89 boost::variate_generator< boost::mt19937&, boost::uniform_int<int> >
90 generator_;
91};
92
93/**
94 * This class exists solely to make the TEST_RW() macro easier to use.
95 * - it can be constructed implicitly from an integer
96 * - it can contain either a ConstantSizeGenerator or a RandomSizeGenerator
97 * (TEST_RW can't take a SizeGenerator pointer or reference, since it needs
98 * to make a copy of the generator to bind it to the test function.)
99 */
100class GenericSizeGenerator : public SizeGenerator {
101 public:
102 GenericSizeGenerator(uint32_t value) :
103 generator_(new ConstantSizeGenerator(value)) {}
104 GenericSizeGenerator(uint32_t min, uint32_t max) :
105 generator_(new RandomSizeGenerator(min, max)) {}
106
107 uint32_t nextSize() { return generator_->nextSize(); }
108 std::string describe() const { return generator_->describe(); }
109
110 private:
111 boost::shared_ptr<SizeGenerator> generator_;
112};
113
114/**************************************************************************
115 * Classes to set up coupled transports
116 **************************************************************************/
117
David Reiss0c025e82010-10-06 17:10:36 +0000118/**
119 * Helper class to represent a coupled pair of transports.
120 *
121 * Data written to the out transport can be read from the in transport.
122 *
123 * This is used as the base class for the various coupled transport
124 * implementations. It shouldn't be instantiated directly.
125 */
David Reiss35dc7692010-10-06 17:10:19 +0000126template <class Transport_>
127class CoupledTransports {
128 public:
Roger Meier6f7681f2011-11-06 12:04:28 +0000129 virtual ~CoupledTransports() {}
David Reiss35dc7692010-10-06 17:10:19 +0000130 typedef Transport_ TransportType;
131
David Reissd4788df2010-10-06 17:10:37 +0000132 CoupledTransports() : in(), out() {}
David Reiss35dc7692010-10-06 17:10:19 +0000133
David Reissd4788df2010-10-06 17:10:37 +0000134 boost::shared_ptr<Transport_> in;
135 boost::shared_ptr<Transport_> out;
David Reiss35dc7692010-10-06 17:10:19 +0000136
137 private:
138 CoupledTransports(const CoupledTransports&);
139 CoupledTransports &operator=(const CoupledTransports&);
140};
141
David Reiss0c025e82010-10-06 17:10:36 +0000142/**
143 * Coupled TMemoryBuffers
144 */
David Reiss35dc7692010-10-06 17:10:19 +0000145class CoupledMemoryBuffers : public CoupledTransports<TMemoryBuffer> {
146 public:
David Reissd4788df2010-10-06 17:10:37 +0000147 CoupledMemoryBuffers() :
148 buf(new TMemoryBuffer) {
149 in = buf;
150 out = buf;
David Reiss35dc7692010-10-06 17:10:19 +0000151 }
152
David Reissd4788df2010-10-06 17:10:37 +0000153 boost::shared_ptr<TMemoryBuffer> buf;
154};
155
156/**
157 * Helper template class for creating coupled transports that wrap
158 * another transport.
159 */
160template <class WrapperTransport_, class InnerCoupledTransports_>
161class CoupledWrapperTransportsT : public CoupledTransports<WrapperTransport_> {
162 public:
163 CoupledWrapperTransportsT() {
164 if (inner_.in) {
165 this->in.reset(new WrapperTransport_(inner_.in));
166 }
167 if (inner_.out) {
168 this->out.reset(new WrapperTransport_(inner_.out));
169 }
170 }
171
172 InnerCoupledTransports_ inner_;
David Reiss35dc7692010-10-06 17:10:19 +0000173};
174
David Reiss0c025e82010-10-06 17:10:36 +0000175/**
176 * Coupled TBufferedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000177 */
David Reissd4788df2010-10-06 17:10:37 +0000178template <class InnerTransport_>
179class CoupledBufferedTransportsT :
180 public CoupledWrapperTransportsT<TBufferedTransport, InnerTransport_> {
David Reiss35dc7692010-10-06 17:10:19 +0000181};
182
David Reissd4788df2010-10-06 17:10:37 +0000183typedef CoupledBufferedTransportsT<CoupledMemoryBuffers>
184 CoupledBufferedTransports;
185
David Reiss0c025e82010-10-06 17:10:36 +0000186/**
187 * Coupled TFramedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000188 */
David Reissd4788df2010-10-06 17:10:37 +0000189template <class InnerTransport_>
190class CoupledFramedTransportsT :
191 public CoupledWrapperTransportsT<TFramedTransport, InnerTransport_> {
David Reiss35dc7692010-10-06 17:10:19 +0000192};
193
David Reissd4788df2010-10-06 17:10:37 +0000194typedef CoupledFramedTransportsT<CoupledMemoryBuffers>
195 CoupledFramedTransports;
196
David Reiss0c025e82010-10-06 17:10:36 +0000197/**
198 * Coupled TZlibTransports.
199 */
David Reissd4788df2010-10-06 17:10:37 +0000200template <class InnerTransport_>
201class CoupledZlibTransportsT :
202 public CoupledWrapperTransportsT<TZlibTransport, InnerTransport_> {
David Reisse94fa332010-10-06 17:10:26 +0000203};
204
David Reissd4788df2010-10-06 17:10:37 +0000205typedef CoupledZlibTransportsT<CoupledMemoryBuffers>
206 CoupledZlibTransports;
207
Jake Farrell5d02b802014-01-07 21:42:01 -0500208#ifndef _WIN32
209// FD transport doesn't make much sense on Windows.
David Reiss0c025e82010-10-06 17:10:36 +0000210/**
211 * Coupled TFDTransports.
212 */
David Reiss35dc7692010-10-06 17:10:19 +0000213class CoupledFDTransports : public CoupledTransports<TFDTransport> {
214 public:
215 CoupledFDTransports() {
216 int pipes[2];
217
218 if (pipe(pipes) != 0) {
219 return;
220 }
221
David Reissd4788df2010-10-06 17:10:37 +0000222 in.reset(new TFDTransport(pipes[0], TFDTransport::CLOSE_ON_DESTROY));
223 out.reset(new TFDTransport(pipes[1], TFDTransport::CLOSE_ON_DESTROY));
David Reiss35dc7692010-10-06 17:10:19 +0000224 }
225};
Jake Farrell5d02b802014-01-07 21:42:01 -0500226#endif
David Reiss35dc7692010-10-06 17:10:19 +0000227
David Reiss0c025e82010-10-06 17:10:36 +0000228/**
229 * Coupled TSockets
230 */
231class CoupledSocketTransports : public CoupledTransports<TSocket> {
232 public:
233 CoupledSocketTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500234 THRIFT_SOCKET sockets[2] = {0};
235 if (THRIFT_SOCKETPAIR(PF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
David Reiss0c025e82010-10-06 17:10:36 +0000236 return;
237 }
238
David Reissd4788df2010-10-06 17:10:37 +0000239 in.reset(new TSocket(sockets[0]));
240 out.reset(new TSocket(sockets[1]));
Roger Meier967600e2013-05-03 22:39:53 +0200241 out->setSendTimeout(100);
David Reiss0c025e82010-10-06 17:10:36 +0000242 }
243};
244
Jake Farrell5d02b802014-01-07 21:42:01 -0500245//These could be made to work on Windows, but I don't care enough to make it happen
246#ifndef _WIN32
David Reiss0c025e82010-10-06 17:10:36 +0000247/**
248 * Coupled TFileTransports
249 */
David Reiss35dc7692010-10-06 17:10:19 +0000250class CoupledFileTransports : public CoupledTransports<TFileTransport> {
251 public:
252 CoupledFileTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500253#ifndef _WIN32
254 const char* tmp_dir = "/tmp";
255 #define FILENAME_SUFFIX "/thrift.transport_test"
256#else
257 const char* tmp_dir = getenv("TMP");
258 #define FILENAME_SUFFIX "\\thrift.transport_test"
259#endif
260
David Reiss35dc7692010-10-06 17:10:19 +0000261 // Create a temporary file to use
Jake Farrell5d02b802014-01-07 21:42:01 -0500262 filename.resize(strlen(tmp_dir) + strlen(FILENAME_SUFFIX));
263 THRIFT_SNPRINTF(&filename[0], filename.size(),
264 "%s" FILENAME_SUFFIX, tmp_dir);
265 #undef FILENAME_SUFFIX
266
267 {
268 std::ofstream dummy_creation(filename.c_str(), std::ofstream::trunc);
David Reiss35dc7692010-10-06 17:10:19 +0000269 }
270
David Reissd4788df2010-10-06 17:10:37 +0000271 in.reset(new TFileTransport(filename, true));
272 out.reset(new TFileTransport(filename));
David Reiss35dc7692010-10-06 17:10:19 +0000273 }
274
275 ~CoupledFileTransports() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500276 remove(filename.c_str());
David Reiss35dc7692010-10-06 17:10:19 +0000277 }
278
Jake Farrell5d02b802014-01-07 21:42:01 -0500279 std::string filename;
David Reiss35dc7692010-10-06 17:10:19 +0000280};
Jake Farrell5d02b802014-01-07 21:42:01 -0500281#endif
David Reiss35dc7692010-10-06 17:10:19 +0000282
David Reiss0c025e82010-10-06 17:10:36 +0000283/**
284 * Wrapper around another CoupledTransports implementation that exposes the
285 * transports as TTransport pointers.
286 *
287 * This is used since accessing a transport via a "TTransport*" exercises a
288 * different code path than using the base pointer class. As part of the
289 * template code changes, most transport methods are no longer virtual.
290 */
David Reiss35dc7692010-10-06 17:10:19 +0000291template <class CoupledTransports_>
292class CoupledTTransports : public CoupledTransports<TTransport> {
293 public:
294 CoupledTTransports() : transports() {
295 in = transports.in;
296 out = transports.out;
297 }
298
299 CoupledTransports_ transports;
300};
301
David Reiss0c025e82010-10-06 17:10:36 +0000302/**
303 * Wrapper around another CoupledTransports implementation that exposes the
304 * transports as TBufferBase pointers.
305 *
306 * This can only be instantiated with a transport type that is a subclass of
307 * TBufferBase.
308 */
David Reiss35dc7692010-10-06 17:10:19 +0000309template <class CoupledTransports_>
310class CoupledBufferBases : public CoupledTransports<TBufferBase> {
311 public:
312 CoupledBufferBases() : transports() {
313 in = transports.in;
314 out = transports.out;
315 }
316
317 CoupledTransports_ transports;
318};
319
David Reiss35dc7692010-10-06 17:10:19 +0000320/**************************************************************************
David Reisse5c435c2010-10-06 17:10:38 +0000321 * Alarm handling code for use in tests that check the transport blocking
322 * semantics.
323 *
324 * If the transport ends up blocking, we don't want to hang forever. We use
325 * SIGALRM to fire schedule signal to wake up and try to write data so the
326 * transport will unblock.
327 *
328 * It isn't really the safest thing in the world to be mucking around with
329 * complicated global data structures in a signal handler. It should probably
330 * be okay though, since we know the main thread should always be blocked in a
331 * read() request when the signal handler is running.
332 **************************************************************************/
333
334struct TriggerInfo {
335 TriggerInfo(int seconds, const boost::shared_ptr<TTransport>& transport,
336 uint32_t writeLength) :
337 timeoutSeconds(seconds),
338 transport(transport),
339 writeLength(writeLength),
340 next(NULL) {}
341
342 int timeoutSeconds;
343 boost::shared_ptr<TTransport> transport;
344 uint32_t writeLength;
345 TriggerInfo* next;
346};
347
Jake Farrell5d02b802014-01-07 21:42:01 -0500348apache::thrift::concurrency::Monitor g_alarm_monitor;
349TriggerInfo* g_triggerInfo;
350unsigned int g_numTriggersFired;
351bool g_teardown = false;
David Reisse5c435c2010-10-06 17:10:38 +0000352
Jake Farrell5d02b802014-01-07 21:42:01 -0500353void alarm_handler() {
354 TriggerInfo *info = NULL;
355 {
356 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
357 // The alarm timed out, which almost certainly means we're stuck
358 // on a transport that is incorrectly blocked.
359 ++g_numTriggersFired;
David Reisse5c435c2010-10-06 17:10:38 +0000360
Jake Farrell5d02b802014-01-07 21:42:01 -0500361 // Note: we print messages to stdout instead of stderr, since
362 // tools/test/runner only records stdout messages in the failure messages for
363 // boost tests. (boost prints its test info to stdout.)
364 printf("Timeout alarm expired; attempting to unblock transport\n");
365 if (g_triggerInfo == NULL) {
366 printf(" trigger stack is empty!\n");
367 }
David Reisse5c435c2010-10-06 17:10:38 +0000368
Jake Farrell5d02b802014-01-07 21:42:01 -0500369 // Pop off the first TriggerInfo.
370 // If there is another one, schedule an alarm for it.
371 info = g_triggerInfo;
372 g_triggerInfo = info->next;
David Reisse5c435c2010-10-06 17:10:38 +0000373 }
374
David Reisse5c435c2010-10-06 17:10:38 +0000375 // Write some data to the transport to hopefully unblock it.
Roger Meier0069cc42010-10-13 18:10:18 +0000376 uint8_t* buf = new uint8_t[info->writeLength];
David Reisse5c435c2010-10-06 17:10:38 +0000377 memset(buf, 'b', info->writeLength);
Roger Meier0069cc42010-10-13 18:10:18 +0000378 boost::scoped_array<uint8_t> array(buf);
David Reisse5c435c2010-10-06 17:10:38 +0000379 info->transport->write(buf, info->writeLength);
380 info->transport->flush();
381
382 delete info;
383}
384
Jake Farrell5d02b802014-01-07 21:42:01 -0500385void alarm_handler_wrapper() {
386 int64_t timeout = 0; //timeout of 0 means wait forever
387 while(true) {
388 bool fireHandler = false;
389 {
390 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
391 if(g_teardown)
392 return;
393 //calculate timeout
394 if (g_triggerInfo == NULL) {
395 timeout = 0;
396 } else {
397 timeout = g_triggerInfo->timeoutSeconds * 1000;
398 }
399
400 int waitResult = g_alarm_monitor.waitForTimeRelative(timeout);
401 if(waitResult == THRIFT_ETIMEDOUT)
402 fireHandler = true;
403 }
404 if(fireHandler)
405 alarm_handler(); //calling outside the lock
David Reisse5c435c2010-10-06 17:10:38 +0000406 }
David Reisse5c435c2010-10-06 17:10:38 +0000407}
408
409/**
410 * Add a trigger to be scheduled "seconds" seconds after the
411 * last currently scheduled trigger.
412 *
413 * (Note that this is not "seconds" from now. That might be more logical, but
414 * would require slightly more complicated sorting, rather than just appending
415 * to the end.)
416 */
417void add_trigger(unsigned int seconds,
418 const boost::shared_ptr<TTransport> &transport,
419 uint32_t write_len) {
420 TriggerInfo* info = new TriggerInfo(seconds, transport, write_len);
Jake Farrell5d02b802014-01-07 21:42:01 -0500421 {
422 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
423 if (g_triggerInfo == NULL) {
424 // This is the first trigger.
425 // Set g_triggerInfo, and schedule the alarm
426 g_triggerInfo = info;
427 g_alarm_monitor.notify();
428 } else {
429 // Add this trigger to the end of the list
430 TriggerInfo* prev = g_triggerInfo;
431 while (prev->next) {
432 prev = prev->next;
433 }
434 prev->next = info;
David Reisse5c435c2010-10-06 17:10:38 +0000435 }
David Reisse5c435c2010-10-06 17:10:38 +0000436 }
437}
438
439void clear_triggers() {
Jake Farrell5d02b802014-01-07 21:42:01 -0500440 TriggerInfo *info = NULL;
441
442 {
443 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
444 info = g_triggerInfo;
445 g_triggerInfo = NULL;
446 g_numTriggersFired = 0;
447 g_alarm_monitor.notify();
448 }
David Reisse5c435c2010-10-06 17:10:38 +0000449
450 while (info != NULL) {
451 TriggerInfo* next = info->next;
452 delete info;
453 info = next;
454 }
455}
456
457void set_trigger(unsigned int seconds,
458 const boost::shared_ptr<TTransport> &transport,
459 uint32_t write_len) {
460 clear_triggers();
461 add_trigger(seconds, transport, write_len);
462}
463
464/**************************************************************************
465 * Test functions
David Reiss35dc7692010-10-06 17:10:19 +0000466 **************************************************************************/
467
468/**
469 * Test interleaved write and read calls.
470 *
471 * Generates a buffer totalSize bytes long, then writes it to the transport,
472 * and verifies the written data can be read back correctly.
473 *
474 * Mode of operation:
475 * - call wChunkGenerator to figure out how large of a chunk to write
476 * - call wSizeGenerator to get the size for individual write() calls,
477 * and do this repeatedly until the entire chunk is written.
478 * - call rChunkGenerator to figure out how large of a chunk to read
479 * - call rSizeGenerator to get the size for individual read() calls,
480 * and do this repeatedly until the entire chunk is read.
481 * - repeat until the full buffer is written and read back,
482 * then compare the data read back against the original buffer
483 *
484 *
485 * - If any of the size generators return 0, this means to use the maximum
486 * possible size.
487 *
488 * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
489 * there are never more than maxOutstanding bytes waiting to be read back.
490 */
491template <class CoupledTransports>
492void test_rw(uint32_t totalSize,
493 SizeGenerator& wSizeGenerator,
494 SizeGenerator& rSizeGenerator,
495 SizeGenerator& wChunkGenerator,
496 SizeGenerator& rChunkGenerator,
497 uint32_t maxOutstanding) {
498 CoupledTransports transports;
499 BOOST_REQUIRE(transports.in != NULL);
500 BOOST_REQUIRE(transports.out != NULL);
501
502 boost::shared_array<uint8_t> wbuf =
503 boost::shared_array<uint8_t>(new uint8_t[totalSize]);
504 boost::shared_array<uint8_t> rbuf =
505 boost::shared_array<uint8_t>(new uint8_t[totalSize]);
506
507 // store some data in wbuf
508 for (uint32_t n = 0; n < totalSize; ++n) {
509 wbuf[n] = (n & 0xff);
510 }
511 // clear rbuf
512 memset(rbuf.get(), 0, totalSize);
513
514 uint32_t total_written = 0;
515 uint32_t total_read = 0;
516 while (total_read < totalSize) {
517 // Determine how large a chunk of data to write
518 uint32_t wchunk_size = wChunkGenerator.nextSize();
519 if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
520 wchunk_size = totalSize - total_written;
521 }
522
523 // Make sure (total_written - total_read) + wchunk_size
524 // is less than maxOutstanding
525 if (maxOutstanding > 0 &&
526 wchunk_size > maxOutstanding - (total_written - total_read)) {
527 wchunk_size = maxOutstanding - (total_written - total_read);
528 }
529
530 // Write the chunk
531 uint32_t chunk_written = 0;
532 while (chunk_written < wchunk_size) {
533 uint32_t write_size = wSizeGenerator.nextSize();
534 if (write_size == 0 || write_size > wchunk_size - chunk_written) {
535 write_size = wchunk_size - chunk_written;
536 }
537
Roger Meier967600e2013-05-03 22:39:53 +0200538 try {
539 transports.out->write(wbuf.get() + total_written, write_size);
540 }
541 catch (TTransportException & te) {
542 if (te.getType() == TTransportException::TIMED_OUT)
543 break;
544 throw te;
545 }
David Reiss35dc7692010-10-06 17:10:19 +0000546 chunk_written += write_size;
547 total_written += write_size;
548 }
549
550 // Flush the data, so it will be available in the read transport
551 // Don't flush if wchunk_size is 0. (This should only happen if
552 // total_written == totalSize already, and we're only reading now.)
553 if (wchunk_size > 0) {
554 transports.out->flush();
555 }
556
557 // Determine how large a chunk of data to read back
558 uint32_t rchunk_size = rChunkGenerator.nextSize();
559 if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
560 rchunk_size = total_written - total_read;
561 }
562
563 // Read the chunk
564 uint32_t chunk_read = 0;
565 while (chunk_read < rchunk_size) {
566 uint32_t read_size = rSizeGenerator.nextSize();
567 if (read_size == 0 || read_size > rchunk_size - chunk_read) {
568 read_size = rchunk_size - chunk_read;
569 }
570
David Reisse94fa332010-10-06 17:10:26 +0000571 int bytes_read = -1;
572 try {
573 bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
574 } catch (TTransportException& e) {
575 BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size <<
576 ") threw exception \"" << e.what() <<
577 "\"; written so far: " << total_written << " / " <<
578 totalSize << " bytes");
579 }
580
David Reiss35dc7692010-10-06 17:10:19 +0000581 BOOST_REQUIRE_MESSAGE(bytes_read > 0,
582 "read(pos=" << total_read << ", size=" <<
583 read_size << ") returned " << bytes_read <<
584 "; written so far: " << total_written << " / " <<
585 totalSize << " bytes");
586 chunk_read += bytes_read;
587 total_read += bytes_read;
588 }
589 }
590
591 // make sure the data read back is identical to the data written
592 BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
593}
594
Roger Meier02c827b2012-04-11 21:59:57 +0000595
David Reisse5c435c2010-10-06 17:10:38 +0000596template <class CoupledTransports>
597void test_read_part_available() {
598 CoupledTransports transports;
599 BOOST_REQUIRE(transports.in != NULL);
600 BOOST_REQUIRE(transports.out != NULL);
601
602 uint8_t write_buf[16];
603 uint8_t read_buf[16];
604 memset(write_buf, 'a', sizeof(write_buf));
605
606 // Attemping to read 10 bytes when only 9 are available should return 9
607 // immediately.
608 transports.out->write(write_buf, 9);
609 transports.out->flush();
610 set_trigger(3, transports.out, 1);
611 uint32_t bytes_read = transports.in->read(read_buf, 10);
Jake Farrell5d02b802014-01-07 21:42:01 -0500612 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int) 0);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000613 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 9);
David Reisse5c435c2010-10-06 17:10:38 +0000614
615 clear_triggers();
616}
617
618template <class CoupledTransports>
Roger Meier02c827b2012-04-11 21:59:57 +0000619void test_read_part_available_in_chunks() {
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 // Write 10 bytes (in a single frame, for transports that use framing)
629 transports.out->write(write_buf, 10);
630 transports.out->flush();
631
632 // Read 1 byte, to force the transport to read the frame
633 uint32_t bytes_read = transports.in->read(read_buf, 1);
Jake Farrell5d02b802014-01-07 21:42:01 -0500634 BOOST_CHECK_EQUAL(bytes_read, 1u);
Roger Meier02c827b2012-04-11 21:59:57 +0000635
636 // Read more than what is remaining and verify the transport does not block
637 set_trigger(3, transports.out, 1);
638 bytes_read = transports.in->read(read_buf, 10);
Jake Farrell5d02b802014-01-07 21:42:01 -0500639 BOOST_CHECK_EQUAL(g_numTriggersFired, 0u);
640 BOOST_CHECK_EQUAL(bytes_read, 9u);
Roger Meier02c827b2012-04-11 21:59:57 +0000641
642 clear_triggers();
643}
644
645template <class CoupledTransports>
David Reiss0a2d81e2010-10-06 17:10:40 +0000646void test_read_partial_midframe() {
647 CoupledTransports transports;
648 BOOST_REQUIRE(transports.in != NULL);
649 BOOST_REQUIRE(transports.out != NULL);
650
651 uint8_t write_buf[16];
652 uint8_t read_buf[16];
653 memset(write_buf, 'a', sizeof(write_buf));
654
655 // Attempt to read 10 bytes, when only 9 are available, but after we have
656 // already read part of the data that is available. This exercises a
657 // different code path for several of the transports.
658 //
659 // For transports that add their own framing (e.g., TFramedTransport and
660 // TFileTransport), the two flush calls break up the data in to a 10 byte
661 // frame and a 3 byte frame. The first read then puts us partway through the
662 // first frame, and then we attempt to read past the end of that frame, and
663 // through the next frame, too.
664 //
665 // For buffered transports that perform read-ahead (e.g.,
666 // TBufferedTransport), the read-ahead will most likely see all 13 bytes
667 // written on the first read. The next read will then attempt to read past
668 // the end of the read-ahead buffer.
669 //
670 // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
671 // transports that track framing internally.
672 transports.out->write(write_buf, 10);
673 transports.out->flush();
674 transports.out->write(write_buf, 3);
675 transports.out->flush();
676
677 // Now read 4 bytes, so that we are partway through the written data.
678 uint32_t bytes_read = transports.in->read(read_buf, 4);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000679 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 4);
David Reiss0a2d81e2010-10-06 17:10:40 +0000680
681 // Now attempt to read 10 bytes. Only 9 more are available.
682 //
683 // We should be able to get all 9 bytes, but it might take multiple read
684 // calls, since it is valid for read() to return fewer bytes than requested.
685 // (Most transports do immediately return 9 bytes, but the framing transports
686 // tend to only return to the end of the current frame, which is 6 bytes in
687 // this case.)
688 uint32_t total_read = 0;
689 while (total_read < 9) {
690 set_trigger(3, transports.out, 1);
691 bytes_read = transports.in->read(read_buf, 10);
Jake Farrell5d02b802014-01-07 21:42:01 -0500692 BOOST_REQUIRE_EQUAL(g_numTriggersFired, (unsigned int) 0);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000693 BOOST_REQUIRE_GT(bytes_read, (uint32_t) 0);
David Reiss0a2d81e2010-10-06 17:10:40 +0000694 total_read += bytes_read;
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000695 BOOST_REQUIRE_LE(total_read, (uint32_t) 9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000696 }
697
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000698 BOOST_CHECK_EQUAL(total_read, (uint32_t) 9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000699
700 clear_triggers();
701}
702
703template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000704void test_borrow_part_available() {
705 CoupledTransports transports;
706 BOOST_REQUIRE(transports.in != NULL);
707 BOOST_REQUIRE(transports.out != NULL);
708
709 uint8_t write_buf[16];
710 uint8_t read_buf[16];
711 memset(write_buf, 'a', sizeof(write_buf));
712
713 // Attemping to borrow 10 bytes when only 9 are available should return NULL
714 // immediately.
715 transports.out->write(write_buf, 9);
716 transports.out->flush();
717 set_trigger(3, transports.out, 1);
718 uint32_t borrow_len = 10;
719 const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
Jake Farrell5d02b802014-01-07 21:42:01 -0500720 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000721 BOOST_CHECK(borrowed_buf == NULL);
722
723 clear_triggers();
724}
725
726template <class CoupledTransports>
727void test_read_none_available() {
728 CoupledTransports transports;
729 BOOST_REQUIRE(transports.in != NULL);
730 BOOST_REQUIRE(transports.out != NULL);
731
732 uint8_t write_buf[16];
733 uint8_t read_buf[16];
734 memset(write_buf, 'a', sizeof(write_buf));
735
736 // Attempting to read when no data is available should either block until
737 // some data is available, or fail immediately. (e.g., TSocket blocks,
738 // TMemoryBuffer just fails.)
739 //
740 // If the transport blocks, it should succeed once some data is available,
741 // even if less than the amount requested becomes available.
742 set_trigger(1, transports.out, 2);
743 add_trigger(1, transports.out, 8);
744 uint32_t bytes_read = transports.in->read(read_buf, 10);
745 if (bytes_read == 0) {
Jake Farrell5d02b802014-01-07 21:42:01 -0500746 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000747 clear_triggers();
748 } else {
Jake Farrell5d02b802014-01-07 21:42:01 -0500749 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int) 1);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000750 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 2);
David Reisse5c435c2010-10-06 17:10:38 +0000751 }
752
753 clear_triggers();
754}
755
756template <class CoupledTransports>
757void test_borrow_none_available() {
758 CoupledTransports transports;
759 BOOST_REQUIRE(transports.in != NULL);
760 BOOST_REQUIRE(transports.out != NULL);
761
762 uint8_t write_buf[16];
763 memset(write_buf, 'a', sizeof(write_buf));
764
765 // Attempting to borrow when no data is available should fail immediately
766 set_trigger(1, transports.out, 10);
767 uint32_t borrow_len = 10;
768 const uint8_t* borrowed_buf = transports.in->borrow(NULL, &borrow_len);
769 BOOST_CHECK(borrowed_buf == NULL);
Jake Farrell5d02b802014-01-07 21:42:01 -0500770 BOOST_CHECK_EQUAL(g_numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000771
772 clear_triggers();
773}
774
David Reiss35dc7692010-10-06 17:10:19 +0000775/**************************************************************************
776 * Test case generation
777 *
778 * Pretty ugly and annoying. This would be much easier if we the unit test
779 * framework didn't force each test to be a separate function.
780 * - Writing a completely separate function definition for each of these would
781 * result in a lot of repetitive boilerplate code.
782 * - Combining many tests into a single function makes it more difficult to
783 * tell precisely which tests failed. It also means you can't get a progress
784 * update after each test, and the tests are already fairly slow.
785 * - Similar registration could be acheived with BOOST_TEST_CASE_TEMPLATE,
786 * but it requires a lot of awkward MPL code, and results in useless test
787 * case names. (The names are generated from std::type_info::name(), which
788 * is compiler-dependent. gcc returns mangled names.)
789 **************************************************************************/
790
David Reisse5c435c2010-10-06 17:10:38 +0000791#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
792 addTestRW< CoupledTransports >(BOOST_STRINGIZE(CoupledTransports), \
793 totalSize, ## __VA_ARGS__);
David Reissd4788df2010-10-06 17:10:37 +0000794
David Reiss35dc7692010-10-06 17:10:19 +0000795#define TEST_RW(CoupledTransports, totalSize, ...) \
796 do { \
797 /* Add the test as specified, to test the non-virtual function calls */ \
David Reisse5c435c2010-10-06 17:10:38 +0000798 ADD_TEST_RW(CoupledTransports, totalSize, ## __VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000799 /* \
800 * Also test using the transport as a TTransport*, to test \
801 * the read_virt()/write_virt() calls \
802 */ \
David Reisse5c435c2010-10-06 17:10:38 +0000803 ADD_TEST_RW(CoupledTTransports<CoupledTransports>, \
804 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000805 /* Test wrapping the transport with TBufferedTransport */ \
David Reisse5c435c2010-10-06 17:10:38 +0000806 ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, \
807 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000808 /* Test wrapping the transport with TFramedTransports */ \
David Reisse5c435c2010-10-06 17:10:38 +0000809 ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, \
810 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000811 /* Test wrapping the transport with TZlibTransport */ \
David Reisse5c435c2010-10-06 17:10:38 +0000812 ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, \
813 totalSize, ## __VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000814 } while (0)
815
David Reisse5c435c2010-10-06 17:10:38 +0000816#define ADD_TEST_BLOCKING(CoupledTransports) \
817 addTestBlocking< CoupledTransports >(BOOST_STRINGIZE(CoupledTransports));
818
819#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
820 ADD_TEST_BLOCKING(CoupledTransports); \
821 ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
822 ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
823 ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
824 ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
825
David Reiss35dc7692010-10-06 17:10:19 +0000826class TransportTestGen {
827 public:
David Reiss65e62d32010-10-06 17:10:35 +0000828 TransportTestGen(boost::unit_test::test_suite* suite,
829 float sizeMultiplier) :
830 suite_(suite),
831 sizeMultiplier_(sizeMultiplier) {}
David Reiss35dc7692010-10-06 17:10:19 +0000832
833 void generate() {
834 GenericSizeGenerator rand4k(1, 4096);
835
836 /*
837 * We do the basically the same set of tests for each transport type,
838 * although we tweak the parameters in some places.
839 */
840
David Reissd4788df2010-10-06 17:10:37 +0000841 // TMemoryBuffer tests
842 TEST_RW(CoupledMemoryBuffers, 1024*1024, 0, 0);
843 TEST_RW(CoupledMemoryBuffers, 1024*256, rand4k, rand4k);
844 TEST_RW(CoupledMemoryBuffers, 1024*256, 167, 163);
845 TEST_RW(CoupledMemoryBuffers, 1024*16, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000846
David Reissd4788df2010-10-06 17:10:37 +0000847 TEST_RW(CoupledMemoryBuffers, 1024*256, 0, 0, rand4k, rand4k);
848 TEST_RW(CoupledMemoryBuffers, 1024*256, rand4k, rand4k, rand4k, rand4k);
849 TEST_RW(CoupledMemoryBuffers, 1024*256, 167, 163, rand4k, rand4k);
850 TEST_RW(CoupledMemoryBuffers, 1024*16, 1, 1, rand4k, rand4k);
David Reisse94fa332010-10-06 17:10:26 +0000851
David Reisse5c435c2010-10-06 17:10:38 +0000852 TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
853
Jake Farrell5d02b802014-01-07 21:42:01 -0500854#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000855 // TFDTransport tests
856 // Since CoupledFDTransports tests with a pipe, writes will block
857 // if there is too much outstanding unread data in the pipe.
858 uint32_t fd_max_outstanding = 4096;
David Reiss65e62d32010-10-06 17:10:35 +0000859 TEST_RW(CoupledFDTransports, 1024*1024, 0, 0,
David Reiss35dc7692010-10-06 17:10:19 +0000860 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000861 TEST_RW(CoupledFDTransports, 1024*256, rand4k, rand4k,
David Reiss35dc7692010-10-06 17:10:19 +0000862 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000863 TEST_RW(CoupledFDTransports, 1024*256, 167, 163,
David Reiss35dc7692010-10-06 17:10:19 +0000864 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000865 TEST_RW(CoupledFDTransports, 1024*16, 1, 1,
David Reiss35dc7692010-10-06 17:10:19 +0000866 0, 0, fd_max_outstanding);
867
David Reiss65e62d32010-10-06 17:10:35 +0000868 TEST_RW(CoupledFDTransports, 1024*256, 0, 0,
David Reiss35dc7692010-10-06 17:10:19 +0000869 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000870 TEST_RW(CoupledFDTransports, 1024*256, rand4k, rand4k,
David Reiss35dc7692010-10-06 17:10:19 +0000871 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000872 TEST_RW(CoupledFDTransports, 1024*256, 167, 163,
David Reiss35dc7692010-10-06 17:10:19 +0000873 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000874 TEST_RW(CoupledFDTransports, 1024*16, 1, 1,
David Reiss35dc7692010-10-06 17:10:19 +0000875 rand4k, rand4k, fd_max_outstanding);
876
David Reisse5c435c2010-10-06 17:10:38 +0000877 TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500878#endif //_WIN32
David Reisse5c435c2010-10-06 17:10:38 +0000879
David Reiss0c025e82010-10-06 17:10:36 +0000880 // TSocket tests
881 uint32_t socket_max_outstanding = 4096;
882 TEST_RW(CoupledSocketTransports, 1024*1024, 0, 0,
883 0, 0, socket_max_outstanding);
884 TEST_RW(CoupledSocketTransports, 1024*256, rand4k, rand4k,
885 0, 0, socket_max_outstanding);
886 TEST_RW(CoupledSocketTransports, 1024*256, 167, 163,
887 0, 0, socket_max_outstanding);
888 // Doh. Apparently writing to a socket has some additional overhead for
889 // each send() call. If we have more than ~400 outstanding 1-byte write
890 // requests, additional send() calls start blocking.
891 TEST_RW(CoupledSocketTransports, 1024*16, 1, 1,
Roger Meier967600e2013-05-03 22:39:53 +0200892 0, 0, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000893 TEST_RW(CoupledSocketTransports, 1024*256, 0, 0,
894 rand4k, rand4k, socket_max_outstanding);
895 TEST_RW(CoupledSocketTransports, 1024*256, rand4k, rand4k,
896 rand4k, rand4k, socket_max_outstanding);
897 TEST_RW(CoupledSocketTransports, 1024*256, 167, 163,
898 rand4k, rand4k, socket_max_outstanding);
899 TEST_RW(CoupledSocketTransports, 1024*16, 1, 1,
Roger Meier967600e2013-05-03 22:39:53 +0200900 rand4k, rand4k, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000901
David Reisse5c435c2010-10-06 17:10:38 +0000902 TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
903
Jake Farrell5d02b802014-01-07 21:42:01 -0500904//These could be made to work on Windows, but I don't care enough to make it happen
905#ifndef _WIN32
David Reiss35dc7692010-10-06 17:10:19 +0000906 // TFileTransport tests
907 // We use smaller buffer sizes here, since TFileTransport is fairly slow.
908 //
909 // TFileTransport can't write more than 16MB at once
910 uint32_t max_write_at_once = 1024*1024*16 - 4;
David Reiss65e62d32010-10-06 17:10:35 +0000911 TEST_RW(CoupledFileTransports, 1024*1024, max_write_at_once, 0);
912 TEST_RW(CoupledFileTransports, 1024*128, rand4k, rand4k);
913 TEST_RW(CoupledFileTransports, 1024*128, 167, 163);
914 TEST_RW(CoupledFileTransports, 1024*2, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000915
David Reiss65e62d32010-10-06 17:10:35 +0000916 TEST_RW(CoupledFileTransports, 1024*64, 0, 0, rand4k, rand4k);
917 TEST_RW(CoupledFileTransports, 1024*64,
David Reiss35dc7692010-10-06 17:10:19 +0000918 rand4k, rand4k, rand4k, rand4k);
David Reiss65e62d32010-10-06 17:10:35 +0000919 TEST_RW(CoupledFileTransports, 1024*64, 167, 163, rand4k, rand4k);
920 TEST_RW(CoupledFileTransports, 1024*2, 1, 1, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000921
David Reisse5c435c2010-10-06 17:10:38 +0000922 TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
Jake Farrell5d02b802014-01-07 21:42:01 -0500923#endif
David Reisse5c435c2010-10-06 17:10:38 +0000924
David Reissd4788df2010-10-06 17:10:37 +0000925 // Add some tests that access TBufferedTransport and TFramedTransport
926 // via TTransport pointers and TBufferBase pointers.
David Reisse5c435c2010-10-06 17:10:38 +0000927 ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
928 1024*1024, rand4k, rand4k, rand4k, rand4k);
929 ADD_TEST_RW(CoupledBufferBases<CoupledBufferedTransports>,
930 1024*1024, rand4k, rand4k, rand4k, rand4k);
931 ADD_TEST_RW(CoupledTTransports<CoupledFramedTransports>,
932 1024*1024, rand4k, rand4k, rand4k, rand4k);
933 ADD_TEST_RW(CoupledBufferBases<CoupledFramedTransports>,
934 1024*1024, rand4k, rand4k, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000935
936 // Test using TZlibTransport via a TTransport pointer
David Reisse5c435c2010-10-06 17:10:38 +0000937 ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
938 1024*1024, rand4k, rand4k, rand4k, rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000939 }
940
941 private:
942 template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000943 void addTestRW(const char* transport_name, uint32_t totalSize,
944 GenericSizeGenerator wSizeGen, GenericSizeGenerator rSizeGen,
945 GenericSizeGenerator wChunkSizeGen = 0,
946 GenericSizeGenerator rChunkSizeGen = 0,
947 uint32_t maxOutstanding = 0,
948 uint32_t expectedFailures = 0) {
David Reiss65e62d32010-10-06 17:10:35 +0000949 // adjust totalSize by the specified sizeMultiplier_ first
950 totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
951
David Reiss35dc7692010-10-06 17:10:19 +0000952 std::ostringstream name;
953 name << transport_name << "::test_rw(" << totalSize << ", " <<
954 wSizeGen.describe() << ", " << rSizeGen.describe() << ", " <<
955 wChunkSizeGen.describe() << ", " << rChunkSizeGen.describe() << ", " <<
956 maxOutstanding << ")";
957
958 boost::unit_test::callback0<> test_func =
Jake Farrell5d02b802014-01-07 21:42:01 -0500959 apache::thrift::stdcxx::bind(test_rw<CoupledTransports>, totalSize,
David Reiss35dc7692010-10-06 17:10:19 +0000960 wSizeGen, rSizeGen, wChunkSizeGen, rChunkSizeGen,
961 maxOutstanding);
962 boost::unit_test::test_case* tc =
963 boost::unit_test::make_test_case(test_func, name.str());
964 suite_->add(tc, expectedFailures);
Roger Meier0069cc42010-10-13 18:10:18 +0000965 }
David Reiss35dc7692010-10-06 17:10:19 +0000966
David Reisse5c435c2010-10-06 17:10:38 +0000967 template <class CoupledTransports>
968 void addTestBlocking(const char* transportName,
969 uint32_t expectedFailures = 0) {
970 char name[1024];
971 boost::unit_test::test_case* tc;
972
Jake Farrell5d02b802014-01-07 21:42:01 -0500973 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available()",
David Reisse5c435c2010-10-06 17:10:38 +0000974 transportName);
975 tc = boost::unit_test::make_test_case(
976 test_read_part_available<CoupledTransports>, name);
977 suite_->add(tc, expectedFailures);
978
Jake Farrell5d02b802014-01-07 21:42:01 -0500979 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_part_available_in_chunks()",
Roger Meier02c827b2012-04-11 21:59:57 +0000980 transportName);
981 tc = boost::unit_test::make_test_case(
982 test_read_part_available_in_chunks<CoupledTransports>, name);
983 suite_->add(tc, expectedFailures);
984
Jake Farrell5d02b802014-01-07 21:42:01 -0500985 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_partial_midframe()",
David Reiss0a2d81e2010-10-06 17:10:40 +0000986 transportName);
987 tc = boost::unit_test::make_test_case(
988 test_read_partial_midframe<CoupledTransports>, name);
989 suite_->add(tc, expectedFailures);
990
Jake Farrell5d02b802014-01-07 21:42:01 -0500991 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_read_none_available()",
David Reisse5c435c2010-10-06 17:10:38 +0000992 transportName);
993 tc = boost::unit_test::make_test_case(
994 test_read_none_available<CoupledTransports>, name);
995 suite_->add(tc, expectedFailures);
996
Jake Farrell5d02b802014-01-07 21:42:01 -0500997 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_part_available()",
David Reisse5c435c2010-10-06 17:10:38 +0000998 transportName);
999 tc = boost::unit_test::make_test_case(
1000 test_borrow_part_available<CoupledTransports>, name);
1001 suite_->add(tc, expectedFailures);
1002
Jake Farrell5d02b802014-01-07 21:42:01 -05001003 THRIFT_SNPRINTF(name, sizeof(name), "%s::test_borrow_none_available()",
David Reisse5c435c2010-10-06 17:10:38 +00001004 transportName);
1005 tc = boost::unit_test::make_test_case(
1006 test_borrow_none_available<CoupledTransports>, name);
1007 suite_->add(tc, expectedFailures);
1008 }
1009
David Reiss35dc7692010-10-06 17:10:19 +00001010 boost::unit_test::test_suite* suite_;
David Reiss65e62d32010-10-06 17:10:35 +00001011 // sizeMultiplier_ is configurable via the command line, and allows the
1012 // user to adjust between smaller buffers that can be tested quickly,
1013 // or larger buffers that more thoroughly exercise the code, but take
1014 // longer.
1015 float sizeMultiplier_;
David Reiss35dc7692010-10-06 17:10:19 +00001016};
1017
1018/**************************************************************************
1019 * General Initialization
1020 **************************************************************************/
1021
Jake Farrell5d02b802014-01-07 21:42:01 -05001022struct global_fixture {
1023 boost::shared_ptr<apache::thrift::concurrency::Thread> alarmThread_;
1024 global_fixture() {
1025 #if _WIN32
1026 apache::thrift::transport::TWinsockSingleton::create();
1027 #endif
David Reiss35dc7692010-10-06 17:10:19 +00001028
Jake Farrell5d02b802014-01-07 21:42:01 -05001029 apache::thrift::concurrency::PlatformThreadFactory factory;
1030 factory.setDetached(false);
1031
1032 alarmThread_ = factory.newThread(
1033 apache::thrift::concurrency::FunctionRunner::create(alarm_handler_wrapper));
1034 alarmThread_->start();
1035 }
1036 ~global_fixture() {
1037 {
1038 apache::thrift::concurrency::Synchronized s(g_alarm_monitor);
1039 g_teardown = true;
1040 g_alarm_monitor.notify();
1041 }
1042 alarmThread_->join();
1043 }
David Reiss65e62d32010-10-06 17:10:35 +00001044};
1045
Jake Farrell5d02b802014-01-07 21:42:01 -05001046BOOST_GLOBAL_FIXTURE(global_fixture)
David Reiss35dc7692010-10-06 17:10:19 +00001047
1048boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
Jake Farrell5d02b802014-01-07 21:42:01 -05001049 struct timeval tv;
1050 THRIFT_GETTIMEOFDAY(&tv, NULL);
1051 int seed = tv.tv_sec ^ tv.tv_usec;
David Reiss65e62d32010-10-06 17:10:35 +00001052
Jake Farrell5d02b802014-01-07 21:42:01 -05001053 initrand(seed);
David Reiss35dc7692010-10-06 17:10:19 +00001054
David Reiss109693c2010-10-06 17:10:42 +00001055 boost::unit_test::test_suite* suite =
1056 &boost::unit_test::framework::master_test_suite();
1057 suite->p_name.value = "TransportTest";
Jake Farrell5d02b802014-01-07 21:42:01 -05001058 TransportTestGen transport_test_generator(suite, 1);
David Reiss35dc7692010-10-06 17:10:19 +00001059 transport_test_generator.generate();
David Reiss109693c2010-10-06 17:10:42 +00001060 return NULL;
David Reiss35dc7692010-10-06 17:10:19 +00001061}