blob: 767e56326ad85b549dabd604b6d957c5ed6d1780 [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 */
19#ifndef _GNU_SOURCE
20#define _GNU_SOURCE // needed for getopt_long
21#endif
22
23#include <stdlib.h>
24#include <time.h>
25#include <unistd.h>
26#include <getopt.h>
David Reisse5c435c2010-10-06 17:10:38 +000027#include <signal.h>
David Reiss35dc7692010-10-06 17:10:19 +000028#include <sstream>
29#include <tr1/functional>
30
31#include <boost/mpl/list.hpp>
32#include <boost/shared_array.hpp>
33#include <boost/random.hpp>
34#include <boost/type_traits.hpp>
35#include <boost/test/unit_test.hpp>
36
37#include <transport/TBufferTransports.h>
38#include <transport/TFDTransport.h>
39#include <transport/TFileTransport.h>
David Reisse94fa332010-10-06 17:10:26 +000040#include <transport/TZlibTransport.h>
David Reiss0c025e82010-10-06 17:10:36 +000041#include <transport/TSocket.h>
David Reiss35dc7692010-10-06 17:10:19 +000042
43using namespace apache::thrift::transport;
44
45static boost::mt19937 rng;
46static const char* tmp_dir = "/tmp";
47
David Reiss65e62d32010-10-06 17:10:35 +000048void initrand(unsigned int seed) {
David Reiss35dc7692010-10-06 17:10:19 +000049 rng.seed(seed);
50}
51
52class SizeGenerator {
53 public:
54 virtual ~SizeGenerator() {}
55 virtual uint32_t nextSize() = 0;
56 virtual std::string describe() const = 0;
57};
58
59class ConstantSizeGenerator : public SizeGenerator {
60 public:
61 ConstantSizeGenerator(uint32_t value) : value_(value) {}
62 uint32_t nextSize() { return value_; }
63 std::string describe() const {
64 std::ostringstream desc;
65 desc << value_;
66 return desc.str();
67 }
68
69 private:
70 uint32_t value_;
71};
72
73class RandomSizeGenerator : public SizeGenerator {
74 public:
75 RandomSizeGenerator(uint32_t min, uint32_t max) :
76 generator_(rng, boost::uniform_int<int>(min, max)) {}
77
78 uint32_t nextSize() { return generator_(); }
79
80 std::string describe() const {
81 std::ostringstream desc;
82 desc << "rand(" << getMin() << ", " << getMax() << ")";
83 return desc.str();
84 }
85
86 uint32_t getMin() const { return generator_.distribution().min(); }
87 uint32_t getMax() const { return generator_.distribution().max(); }
88
89 private:
90 boost::variate_generator< boost::mt19937&, boost::uniform_int<int> >
91 generator_;
92};
93
94/**
95 * This class exists solely to make the TEST_RW() macro easier to use.
96 * - it can be constructed implicitly from an integer
97 * - it can contain either a ConstantSizeGenerator or a RandomSizeGenerator
98 * (TEST_RW can't take a SizeGenerator pointer or reference, since it needs
99 * to make a copy of the generator to bind it to the test function.)
100 */
101class GenericSizeGenerator : public SizeGenerator {
102 public:
103 GenericSizeGenerator(uint32_t value) :
104 generator_(new ConstantSizeGenerator(value)) {}
105 GenericSizeGenerator(uint32_t min, uint32_t max) :
106 generator_(new RandomSizeGenerator(min, max)) {}
107
108 uint32_t nextSize() { return generator_->nextSize(); }
109 std::string describe() const { return generator_->describe(); }
110
111 private:
112 boost::shared_ptr<SizeGenerator> generator_;
113};
114
115/**************************************************************************
116 * Classes to set up coupled transports
117 **************************************************************************/
118
David Reiss0c025e82010-10-06 17:10:36 +0000119/**
120 * Helper class to represent a coupled pair of transports.
121 *
122 * Data written to the out transport can be read from the in transport.
123 *
124 * This is used as the base class for the various coupled transport
125 * implementations. It shouldn't be instantiated directly.
126 */
David Reiss35dc7692010-10-06 17:10:19 +0000127template <class Transport_>
128class CoupledTransports {
129 public:
Roger Meier6f7681f2011-11-06 12:04:28 +0000130 virtual ~CoupledTransports() {}
David Reiss35dc7692010-10-06 17:10:19 +0000131 typedef Transport_ TransportType;
132
David Reissd4788df2010-10-06 17:10:37 +0000133 CoupledTransports() : in(), out() {}
David Reiss35dc7692010-10-06 17:10:19 +0000134
David Reissd4788df2010-10-06 17:10:37 +0000135 boost::shared_ptr<Transport_> in;
136 boost::shared_ptr<Transport_> out;
David Reiss35dc7692010-10-06 17:10:19 +0000137
138 private:
139 CoupledTransports(const CoupledTransports&);
140 CoupledTransports &operator=(const CoupledTransports&);
141};
142
David Reiss0c025e82010-10-06 17:10:36 +0000143/**
144 * Coupled TMemoryBuffers
145 */
David Reiss35dc7692010-10-06 17:10:19 +0000146class CoupledMemoryBuffers : public CoupledTransports<TMemoryBuffer> {
147 public:
David Reissd4788df2010-10-06 17:10:37 +0000148 CoupledMemoryBuffers() :
149 buf(new TMemoryBuffer) {
150 in = buf;
151 out = buf;
David Reiss35dc7692010-10-06 17:10:19 +0000152 }
153
David Reissd4788df2010-10-06 17:10:37 +0000154 boost::shared_ptr<TMemoryBuffer> buf;
155};
156
157/**
158 * Helper template class for creating coupled transports that wrap
159 * another transport.
160 */
161template <class WrapperTransport_, class InnerCoupledTransports_>
162class CoupledWrapperTransportsT : public CoupledTransports<WrapperTransport_> {
163 public:
164 CoupledWrapperTransportsT() {
165 if (inner_.in) {
166 this->in.reset(new WrapperTransport_(inner_.in));
167 }
168 if (inner_.out) {
169 this->out.reset(new WrapperTransport_(inner_.out));
170 }
171 }
172
173 InnerCoupledTransports_ inner_;
David Reiss35dc7692010-10-06 17:10:19 +0000174};
175
David Reiss0c025e82010-10-06 17:10:36 +0000176/**
177 * Coupled TBufferedTransports.
David Reiss0c025e82010-10-06 17:10:36 +0000178 */
David Reissd4788df2010-10-06 17:10:37 +0000179template <class InnerTransport_>
180class CoupledBufferedTransportsT :
181 public CoupledWrapperTransportsT<TBufferedTransport, InnerTransport_> {
David Reiss35dc7692010-10-06 17:10:19 +0000182};
183
David Reissd4788df2010-10-06 17:10:37 +0000184typedef CoupledBufferedTransportsT<CoupledMemoryBuffers>
185 CoupledBufferedTransports;
186
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_>
191class CoupledFramedTransportsT :
192 public CoupledWrapperTransportsT<TFramedTransport, InnerTransport_> {
David Reiss35dc7692010-10-06 17:10:19 +0000193};
194
David Reissd4788df2010-10-06 17:10:37 +0000195typedef CoupledFramedTransportsT<CoupledMemoryBuffers>
196 CoupledFramedTransports;
197
David Reiss0c025e82010-10-06 17:10:36 +0000198/**
199 * Coupled TZlibTransports.
200 */
David Reissd4788df2010-10-06 17:10:37 +0000201template <class InnerTransport_>
202class CoupledZlibTransportsT :
203 public CoupledWrapperTransportsT<TZlibTransport, InnerTransport_> {
David Reisse94fa332010-10-06 17:10:26 +0000204};
205
David Reissd4788df2010-10-06 17:10:37 +0000206typedef CoupledZlibTransportsT<CoupledMemoryBuffers>
207 CoupledZlibTransports;
208
David Reiss0c025e82010-10-06 17:10:36 +0000209/**
210 * Coupled TFDTransports.
211 */
David Reiss35dc7692010-10-06 17:10:19 +0000212class CoupledFDTransports : public CoupledTransports<TFDTransport> {
213 public:
214 CoupledFDTransports() {
215 int pipes[2];
216
217 if (pipe(pipes) != 0) {
218 return;
219 }
220
David Reissd4788df2010-10-06 17:10:37 +0000221 in.reset(new TFDTransport(pipes[0], TFDTransport::CLOSE_ON_DESTROY));
222 out.reset(new TFDTransport(pipes[1], TFDTransport::CLOSE_ON_DESTROY));
David Reiss35dc7692010-10-06 17:10:19 +0000223 }
224};
225
David Reiss0c025e82010-10-06 17:10:36 +0000226/**
227 * Coupled TSockets
228 */
229class CoupledSocketTransports : public CoupledTransports<TSocket> {
230 public:
231 CoupledSocketTransports() {
232 int sockets[2];
233 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
234 return;
235 }
236
David Reissd4788df2010-10-06 17:10:37 +0000237 in.reset(new TSocket(sockets[0]));
238 out.reset(new TSocket(sockets[1]));
David Reiss0c025e82010-10-06 17:10:36 +0000239 }
240};
241
242/**
243 * Coupled TFileTransports
244 */
David Reiss35dc7692010-10-06 17:10:19 +0000245class CoupledFileTransports : public CoupledTransports<TFileTransport> {
246 public:
247 CoupledFileTransports() {
248 // Create a temporary file to use
249 size_t filename_len = strlen(tmp_dir) + 32;
250 filename = new char[filename_len];
251 snprintf(filename, filename_len,
252 "%s/thrift.transport_test.XXXXXX", tmp_dir);
253 fd = mkstemp(filename);
254 if (fd < 0) {
255 return;
256 }
257
David Reissd4788df2010-10-06 17:10:37 +0000258 in.reset(new TFileTransport(filename, true));
259 out.reset(new TFileTransport(filename));
David Reiss35dc7692010-10-06 17:10:19 +0000260 }
261
262 ~CoupledFileTransports() {
David Reiss35dc7692010-10-06 17:10:19 +0000263 if (fd >= 0) {
264 close(fd);
265 unlink(filename);
266 }
267 delete[] filename;
268 }
269
270 char* filename;
271 int fd;
272};
273
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> {
284 public:
285 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> {
302 public:
303 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 {
326 TriggerInfo(int seconds, const boost::shared_ptr<TTransport>& transport,
327 uint32_t writeLength) :
328 timeoutSeconds(seconds),
329 transport(transport),
330 writeLength(writeLength),
331 next(NULL) {}
332
333 int timeoutSeconds;
334 boost::shared_ptr<TTransport> transport;
335 uint32_t writeLength;
336 TriggerInfo* next;
337};
338
339TriggerInfo* triggerInfo;
340unsigned int numTriggersFired;
341
342void set_alarm();
343
344void alarm_handler(int signum) {
Roger Meier3b771a12010-11-17 22:11:26 +0000345 (void) signum;
David Reisse5c435c2010-10-06 17:10:38 +0000346 // The alarm timed out, which almost certainly means we're stuck
347 // on a transport that is incorrectly blocked.
348 ++numTriggersFired;
349
350 // Note: we print messages to stdout instead of stderr, since
351 // tools/test/runner only records stdout messages in the failure messages for
352 // boost tests. (boost prints its test info to stdout.)
353 printf("Timeout alarm expired; attempting to unblock transport\n");
354 if (triggerInfo == NULL) {
355 printf(" trigger stack is empty!\n");
356 }
357
358 // Pop off the first TriggerInfo.
359 // If there is another one, schedule an alarm for it.
360 TriggerInfo* info = triggerInfo;
361 triggerInfo = info->next;
362 set_alarm();
363
364 // Write some data to the transport to hopefully unblock it.
Roger Meier0069cc42010-10-13 18:10:18 +0000365 uint8_t* buf = new uint8_t[info->writeLength];
David Reisse5c435c2010-10-06 17:10:38 +0000366 memset(buf, 'b', info->writeLength);
Roger Meier0069cc42010-10-13 18:10:18 +0000367 boost::scoped_array<uint8_t> array(buf);
David Reisse5c435c2010-10-06 17:10:38 +0000368 info->transport->write(buf, info->writeLength);
369 info->transport->flush();
370
371 delete info;
372}
373
374void set_alarm() {
375 if (triggerInfo == NULL) {
376 // clear any alarm
377 alarm(0);
378 return;
379 }
380
381 struct sigaction action;
382 memset(&action, 0, sizeof(action));
383 action.sa_handler = alarm_handler;
Christian Lavoie4f42ef72010-11-04 18:51:42 +0000384 action.sa_flags = SA_RESETHAND;
David Reisse5c435c2010-10-06 17:10:38 +0000385 sigemptyset(&action.sa_mask);
386 sigaction(SIGALRM, &action, NULL);
387
388 alarm(triggerInfo->timeoutSeconds);
389}
390
391/**
392 * Add a trigger to be scheduled "seconds" seconds after the
393 * last currently scheduled trigger.
394 *
395 * (Note that this is not "seconds" from now. That might be more logical, but
396 * would require slightly more complicated sorting, rather than just appending
397 * to the end.)
398 */
399void add_trigger(unsigned int seconds,
400 const boost::shared_ptr<TTransport> &transport,
401 uint32_t write_len) {
402 TriggerInfo* info = new TriggerInfo(seconds, transport, write_len);
403
404 if (triggerInfo == NULL) {
405 // This is the first trigger.
406 // Set triggerInfo, and schedule the alarm
407 triggerInfo = info;
408 set_alarm();
409 } else {
410 // Add this trigger to the end of the list
411 TriggerInfo* prev = triggerInfo;
412 while (prev->next) {
413 prev = prev->next;
414 }
415
416 prev->next = info;
417 }
418}
419
420void clear_triggers() {
421 TriggerInfo *info = triggerInfo;
422 alarm(0);
423 triggerInfo = NULL;
424 numTriggersFired = 0;
425
426 while (info != NULL) {
427 TriggerInfo* next = info->next;
428 delete info;
429 info = next;
430 }
431}
432
433void set_trigger(unsigned int seconds,
434 const boost::shared_ptr<TTransport> &transport,
435 uint32_t write_len) {
436 clear_triggers();
437 add_trigger(seconds, transport, write_len);
438}
439
440/**************************************************************************
441 * Test functions
David Reiss35dc7692010-10-06 17:10:19 +0000442 **************************************************************************/
443
444/**
445 * Test interleaved write and read calls.
446 *
447 * Generates a buffer totalSize bytes long, then writes it to the transport,
448 * and verifies the written data can be read back correctly.
449 *
450 * Mode of operation:
451 * - call wChunkGenerator to figure out how large of a chunk to write
452 * - call wSizeGenerator to get the size for individual write() calls,
453 * and do this repeatedly until the entire chunk is written.
454 * - call rChunkGenerator to figure out how large of a chunk to read
455 * - call rSizeGenerator to get the size for individual read() calls,
456 * and do this repeatedly until the entire chunk is read.
457 * - repeat until the full buffer is written and read back,
458 * then compare the data read back against the original buffer
459 *
460 *
461 * - If any of the size generators return 0, this means to use the maximum
462 * possible size.
463 *
464 * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
465 * there are never more than maxOutstanding bytes waiting to be read back.
466 */
467template <class CoupledTransports>
468void test_rw(uint32_t totalSize,
469 SizeGenerator& wSizeGenerator,
470 SizeGenerator& rSizeGenerator,
471 SizeGenerator& wChunkGenerator,
472 SizeGenerator& rChunkGenerator,
473 uint32_t maxOutstanding) {
474 CoupledTransports transports;
475 BOOST_REQUIRE(transports.in != NULL);
476 BOOST_REQUIRE(transports.out != NULL);
477
478 boost::shared_array<uint8_t> wbuf =
479 boost::shared_array<uint8_t>(new uint8_t[totalSize]);
480 boost::shared_array<uint8_t> rbuf =
481 boost::shared_array<uint8_t>(new uint8_t[totalSize]);
482
483 // store some data in wbuf
484 for (uint32_t n = 0; n < totalSize; ++n) {
485 wbuf[n] = (n & 0xff);
486 }
487 // clear rbuf
488 memset(rbuf.get(), 0, totalSize);
489
490 uint32_t total_written = 0;
491 uint32_t total_read = 0;
492 while (total_read < totalSize) {
493 // Determine how large a chunk of data to write
494 uint32_t wchunk_size = wChunkGenerator.nextSize();
495 if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
496 wchunk_size = totalSize - total_written;
497 }
498
499 // Make sure (total_written - total_read) + wchunk_size
500 // is less than maxOutstanding
501 if (maxOutstanding > 0 &&
502 wchunk_size > maxOutstanding - (total_written - total_read)) {
503 wchunk_size = maxOutstanding - (total_written - total_read);
504 }
505
506 // Write the chunk
507 uint32_t chunk_written = 0;
508 while (chunk_written < wchunk_size) {
509 uint32_t write_size = wSizeGenerator.nextSize();
510 if (write_size == 0 || write_size > wchunk_size - chunk_written) {
511 write_size = wchunk_size - chunk_written;
512 }
513
514 transports.out->write(wbuf.get() + total_written, write_size);
515 chunk_written += write_size;
516 total_written += write_size;
517 }
518
519 // Flush the data, so it will be available in the read transport
520 // Don't flush if wchunk_size is 0. (This should only happen if
521 // total_written == totalSize already, and we're only reading now.)
522 if (wchunk_size > 0) {
523 transports.out->flush();
524 }
525
526 // Determine how large a chunk of data to read back
527 uint32_t rchunk_size = rChunkGenerator.nextSize();
528 if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
529 rchunk_size = total_written - total_read;
530 }
531
532 // Read the chunk
533 uint32_t chunk_read = 0;
534 while (chunk_read < rchunk_size) {
535 uint32_t read_size = rSizeGenerator.nextSize();
536 if (read_size == 0 || read_size > rchunk_size - chunk_read) {
537 read_size = rchunk_size - chunk_read;
538 }
539
David Reisse94fa332010-10-06 17:10:26 +0000540 int bytes_read = -1;
541 try {
542 bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
543 } catch (TTransportException& e) {
544 BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size <<
545 ") threw exception \"" << e.what() <<
546 "\"; written so far: " << total_written << " / " <<
547 totalSize << " bytes");
548 }
549
David Reiss35dc7692010-10-06 17:10:19 +0000550 BOOST_REQUIRE_MESSAGE(bytes_read > 0,
551 "read(pos=" << total_read << ", size=" <<
552 read_size << ") returned " << bytes_read <<
553 "; written so far: " << total_written << " / " <<
554 totalSize << " bytes");
555 chunk_read += bytes_read;
556 total_read += bytes_read;
557 }
558 }
559
560 // make sure the data read back is identical to the data written
561 BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
562}
563
David Reisse5c435c2010-10-06 17:10:38 +0000564template <class CoupledTransports>
565void test_read_part_available() {
566 CoupledTransports transports;
567 BOOST_REQUIRE(transports.in != NULL);
568 BOOST_REQUIRE(transports.out != NULL);
569
570 uint8_t write_buf[16];
571 uint8_t read_buf[16];
572 memset(write_buf, 'a', sizeof(write_buf));
573
574 // Attemping to read 10 bytes when only 9 are available should return 9
575 // immediately.
576 transports.out->write(write_buf, 9);
577 transports.out->flush();
578 set_trigger(3, transports.out, 1);
579 uint32_t bytes_read = transports.in->read(read_buf, 10);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000580 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
581 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 9);
David Reisse5c435c2010-10-06 17:10:38 +0000582
583 clear_triggers();
584}
585
586template <class CoupledTransports>
David Reiss0a2d81e2010-10-06 17:10:40 +0000587void test_read_partial_midframe() {
588 CoupledTransports transports;
589 BOOST_REQUIRE(transports.in != NULL);
590 BOOST_REQUIRE(transports.out != NULL);
591
592 uint8_t write_buf[16];
593 uint8_t read_buf[16];
594 memset(write_buf, 'a', sizeof(write_buf));
595
596 // Attempt to read 10 bytes, when only 9 are available, but after we have
597 // already read part of the data that is available. This exercises a
598 // different code path for several of the transports.
599 //
600 // For transports that add their own framing (e.g., TFramedTransport and
601 // TFileTransport), the two flush calls break up the data in to a 10 byte
602 // frame and a 3 byte frame. The first read then puts us partway through the
603 // first frame, and then we attempt to read past the end of that frame, and
604 // through the next frame, too.
605 //
606 // For buffered transports that perform read-ahead (e.g.,
607 // TBufferedTransport), the read-ahead will most likely see all 13 bytes
608 // written on the first read. The next read will then attempt to read past
609 // the end of the read-ahead buffer.
610 //
611 // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
612 // transports that track framing internally.
613 transports.out->write(write_buf, 10);
614 transports.out->flush();
615 transports.out->write(write_buf, 3);
616 transports.out->flush();
617
618 // Now read 4 bytes, so that we are partway through the written data.
619 uint32_t bytes_read = transports.in->read(read_buf, 4);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000620 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 4);
David Reiss0a2d81e2010-10-06 17:10:40 +0000621
622 // Now attempt to read 10 bytes. Only 9 more are available.
623 //
624 // We should be able to get all 9 bytes, but it might take multiple read
625 // calls, since it is valid for read() to return fewer bytes than requested.
626 // (Most transports do immediately return 9 bytes, but the framing transports
627 // tend to only return to the end of the current frame, which is 6 bytes in
628 // this case.)
629 uint32_t total_read = 0;
630 while (total_read < 9) {
631 set_trigger(3, transports.out, 1);
632 bytes_read = transports.in->read(read_buf, 10);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000633 BOOST_REQUIRE_EQUAL(numTriggersFired, (unsigned int) 0);
634 BOOST_REQUIRE_GT(bytes_read, (uint32_t) 0);
David Reiss0a2d81e2010-10-06 17:10:40 +0000635 total_read += bytes_read;
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000636 BOOST_REQUIRE_LE(total_read, (uint32_t) 9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000637 }
638
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000639 BOOST_CHECK_EQUAL(total_read, (uint32_t) 9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000640
641 clear_triggers();
642}
643
644template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000645void test_borrow_part_available() {
646 CoupledTransports transports;
647 BOOST_REQUIRE(transports.in != NULL);
648 BOOST_REQUIRE(transports.out != NULL);
649
650 uint8_t write_buf[16];
651 uint8_t read_buf[16];
652 memset(write_buf, 'a', sizeof(write_buf));
653
654 // Attemping to borrow 10 bytes when only 9 are available should return NULL
655 // immediately.
656 transports.out->write(write_buf, 9);
657 transports.out->flush();
658 set_trigger(3, transports.out, 1);
659 uint32_t borrow_len = 10;
660 const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000661 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000662 BOOST_CHECK(borrowed_buf == NULL);
663
664 clear_triggers();
665}
666
667template <class CoupledTransports>
668void test_read_none_available() {
669 CoupledTransports transports;
670 BOOST_REQUIRE(transports.in != NULL);
671 BOOST_REQUIRE(transports.out != NULL);
672
673 uint8_t write_buf[16];
674 uint8_t read_buf[16];
675 memset(write_buf, 'a', sizeof(write_buf));
676
677 // Attempting to read when no data is available should either block until
678 // some data is available, or fail immediately. (e.g., TSocket blocks,
679 // TMemoryBuffer just fails.)
680 //
681 // If the transport blocks, it should succeed once some data is available,
682 // even if less than the amount requested becomes available.
683 set_trigger(1, transports.out, 2);
684 add_trigger(1, transports.out, 8);
685 uint32_t bytes_read = transports.in->read(read_buf, 10);
686 if (bytes_read == 0) {
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000687 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000688 clear_triggers();
689 } else {
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000690 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 1);
691 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 2);
David Reisse5c435c2010-10-06 17:10:38 +0000692 }
693
694 clear_triggers();
695}
696
697template <class CoupledTransports>
698void test_borrow_none_available() {
699 CoupledTransports transports;
700 BOOST_REQUIRE(transports.in != NULL);
701 BOOST_REQUIRE(transports.out != NULL);
702
703 uint8_t write_buf[16];
704 memset(write_buf, 'a', sizeof(write_buf));
705
706 // Attempting to borrow when no data is available should fail immediately
707 set_trigger(1, transports.out, 10);
708 uint32_t borrow_len = 10;
709 const uint8_t* borrowed_buf = transports.in->borrow(NULL, &borrow_len);
710 BOOST_CHECK(borrowed_buf == NULL);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000711 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000712
713 clear_triggers();
714}
715
David Reiss35dc7692010-10-06 17:10:19 +0000716/**************************************************************************
717 * Test case generation
718 *
719 * Pretty ugly and annoying. This would be much easier if we the unit test
720 * framework didn't force each test to be a separate function.
721 * - Writing a completely separate function definition for each of these would
722 * result in a lot of repetitive boilerplate code.
723 * - Combining many tests into a single function makes it more difficult to
724 * tell precisely which tests failed. It also means you can't get a progress
725 * update after each test, and the tests are already fairly slow.
726 * - Similar registration could be acheived with BOOST_TEST_CASE_TEMPLATE,
727 * but it requires a lot of awkward MPL code, and results in useless test
728 * case names. (The names are generated from std::type_info::name(), which
729 * is compiler-dependent. gcc returns mangled names.)
730 **************************************************************************/
731
David Reisse5c435c2010-10-06 17:10:38 +0000732#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
733 addTestRW< CoupledTransports >(BOOST_STRINGIZE(CoupledTransports), \
734 totalSize, ## __VA_ARGS__);
David Reissd4788df2010-10-06 17:10:37 +0000735
David Reiss35dc7692010-10-06 17:10:19 +0000736#define TEST_RW(CoupledTransports, totalSize, ...) \
737 do { \
738 /* Add the test as specified, to test the non-virtual function calls */ \
David Reisse5c435c2010-10-06 17:10:38 +0000739 ADD_TEST_RW(CoupledTransports, totalSize, ## __VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000740 /* \
741 * Also test using the transport as a TTransport*, to test \
742 * the read_virt()/write_virt() calls \
743 */ \
David Reisse5c435c2010-10-06 17:10:38 +0000744 ADD_TEST_RW(CoupledTTransports<CoupledTransports>, \
745 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000746 /* Test wrapping the transport with TBufferedTransport */ \
David Reisse5c435c2010-10-06 17:10:38 +0000747 ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, \
748 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000749 /* Test wrapping the transport with TFramedTransports */ \
David Reisse5c435c2010-10-06 17:10:38 +0000750 ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, \
751 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000752 /* Test wrapping the transport with TZlibTransport */ \
David Reisse5c435c2010-10-06 17:10:38 +0000753 ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, \
754 totalSize, ## __VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000755 } while (0)
756
David Reisse5c435c2010-10-06 17:10:38 +0000757#define ADD_TEST_BLOCKING(CoupledTransports) \
758 addTestBlocking< CoupledTransports >(BOOST_STRINGIZE(CoupledTransports));
759
760#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
761 ADD_TEST_BLOCKING(CoupledTransports); \
762 ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
763 ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
764 ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
765 ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
766
David Reiss35dc7692010-10-06 17:10:19 +0000767class TransportTestGen {
768 public:
David Reiss65e62d32010-10-06 17:10:35 +0000769 TransportTestGen(boost::unit_test::test_suite* suite,
770 float sizeMultiplier) :
771 suite_(suite),
772 sizeMultiplier_(sizeMultiplier) {}
David Reiss35dc7692010-10-06 17:10:19 +0000773
774 void generate() {
775 GenericSizeGenerator rand4k(1, 4096);
776
777 /*
778 * We do the basically the same set of tests for each transport type,
779 * although we tweak the parameters in some places.
780 */
781
David Reissd4788df2010-10-06 17:10:37 +0000782 // TMemoryBuffer tests
783 TEST_RW(CoupledMemoryBuffers, 1024*1024, 0, 0);
784 TEST_RW(CoupledMemoryBuffers, 1024*256, rand4k, rand4k);
785 TEST_RW(CoupledMemoryBuffers, 1024*256, 167, 163);
786 TEST_RW(CoupledMemoryBuffers, 1024*16, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000787
David Reissd4788df2010-10-06 17:10:37 +0000788 TEST_RW(CoupledMemoryBuffers, 1024*256, 0, 0, rand4k, rand4k);
789 TEST_RW(CoupledMemoryBuffers, 1024*256, rand4k, rand4k, rand4k, rand4k);
790 TEST_RW(CoupledMemoryBuffers, 1024*256, 167, 163, rand4k, rand4k);
791 TEST_RW(CoupledMemoryBuffers, 1024*16, 1, 1, rand4k, rand4k);
David Reisse94fa332010-10-06 17:10:26 +0000792
David Reisse5c435c2010-10-06 17:10:38 +0000793 TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
794
David Reiss35dc7692010-10-06 17:10:19 +0000795 // TFDTransport tests
796 // Since CoupledFDTransports tests with a pipe, writes will block
797 // if there is too much outstanding unread data in the pipe.
798 uint32_t fd_max_outstanding = 4096;
David Reiss65e62d32010-10-06 17:10:35 +0000799 TEST_RW(CoupledFDTransports, 1024*1024, 0, 0,
David Reiss35dc7692010-10-06 17:10:19 +0000800 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000801 TEST_RW(CoupledFDTransports, 1024*256, rand4k, rand4k,
David Reiss35dc7692010-10-06 17:10:19 +0000802 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000803 TEST_RW(CoupledFDTransports, 1024*256, 167, 163,
David Reiss35dc7692010-10-06 17:10:19 +0000804 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000805 TEST_RW(CoupledFDTransports, 1024*16, 1, 1,
David Reiss35dc7692010-10-06 17:10:19 +0000806 0, 0, fd_max_outstanding);
807
David Reiss65e62d32010-10-06 17:10:35 +0000808 TEST_RW(CoupledFDTransports, 1024*256, 0, 0,
David Reiss35dc7692010-10-06 17:10:19 +0000809 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000810 TEST_RW(CoupledFDTransports, 1024*256, rand4k, rand4k,
David Reiss35dc7692010-10-06 17:10:19 +0000811 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000812 TEST_RW(CoupledFDTransports, 1024*256, 167, 163,
David Reiss35dc7692010-10-06 17:10:19 +0000813 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000814 TEST_RW(CoupledFDTransports, 1024*16, 1, 1,
David Reiss35dc7692010-10-06 17:10:19 +0000815 rand4k, rand4k, fd_max_outstanding);
816
David Reisse5c435c2010-10-06 17:10:38 +0000817 TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
818
David Reiss0c025e82010-10-06 17:10:36 +0000819 // TSocket tests
820 uint32_t socket_max_outstanding = 4096;
821 TEST_RW(CoupledSocketTransports, 1024*1024, 0, 0,
822 0, 0, socket_max_outstanding);
823 TEST_RW(CoupledSocketTransports, 1024*256, rand4k, rand4k,
824 0, 0, socket_max_outstanding);
825 TEST_RW(CoupledSocketTransports, 1024*256, 167, 163,
826 0, 0, socket_max_outstanding);
827 // Doh. Apparently writing to a socket has some additional overhead for
828 // each send() call. If we have more than ~400 outstanding 1-byte write
829 // requests, additional send() calls start blocking.
830 TEST_RW(CoupledSocketTransports, 1024*16, 1, 1,
831 0, 0, 400);
832 TEST_RW(CoupledSocketTransports, 1024*256, 0, 0,
833 rand4k, rand4k, socket_max_outstanding);
834 TEST_RW(CoupledSocketTransports, 1024*256, rand4k, rand4k,
835 rand4k, rand4k, socket_max_outstanding);
836 TEST_RW(CoupledSocketTransports, 1024*256, 167, 163,
837 rand4k, rand4k, socket_max_outstanding);
838 TEST_RW(CoupledSocketTransports, 1024*16, 1, 1,
839 rand4k, rand4k, 400);
840
David Reisse5c435c2010-10-06 17:10:38 +0000841 TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
842
David Reiss35dc7692010-10-06 17:10:19 +0000843 // TFileTransport tests
844 // We use smaller buffer sizes here, since TFileTransport is fairly slow.
845 //
846 // TFileTransport can't write more than 16MB at once
847 uint32_t max_write_at_once = 1024*1024*16 - 4;
David Reiss65e62d32010-10-06 17:10:35 +0000848 TEST_RW(CoupledFileTransports, 1024*1024, max_write_at_once, 0);
849 TEST_RW(CoupledFileTransports, 1024*128, rand4k, rand4k);
850 TEST_RW(CoupledFileTransports, 1024*128, 167, 163);
851 TEST_RW(CoupledFileTransports, 1024*2, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000852
David Reiss65e62d32010-10-06 17:10:35 +0000853 TEST_RW(CoupledFileTransports, 1024*64, 0, 0, rand4k, rand4k);
854 TEST_RW(CoupledFileTransports, 1024*64,
David Reiss35dc7692010-10-06 17:10:19 +0000855 rand4k, rand4k, rand4k, rand4k);
David Reiss65e62d32010-10-06 17:10:35 +0000856 TEST_RW(CoupledFileTransports, 1024*64, 167, 163, rand4k, rand4k);
857 TEST_RW(CoupledFileTransports, 1024*2, 1, 1, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000858
David Reisse5c435c2010-10-06 17:10:38 +0000859 TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
860
David Reissd4788df2010-10-06 17:10:37 +0000861 // Add some tests that access TBufferedTransport and TFramedTransport
862 // via TTransport pointers and TBufferBase pointers.
David Reisse5c435c2010-10-06 17:10:38 +0000863 ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
864 1024*1024, rand4k, rand4k, rand4k, rand4k);
865 ADD_TEST_RW(CoupledBufferBases<CoupledBufferedTransports>,
866 1024*1024, rand4k, rand4k, rand4k, rand4k);
867 ADD_TEST_RW(CoupledTTransports<CoupledFramedTransports>,
868 1024*1024, rand4k, rand4k, rand4k, rand4k);
869 ADD_TEST_RW(CoupledBufferBases<CoupledFramedTransports>,
870 1024*1024, rand4k, rand4k, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000871
872 // Test using TZlibTransport via a TTransport pointer
David Reisse5c435c2010-10-06 17:10:38 +0000873 ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
874 1024*1024, rand4k, rand4k, rand4k, rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000875 }
876
877 private:
878 template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000879 void addTestRW(const char* transport_name, uint32_t totalSize,
880 GenericSizeGenerator wSizeGen, GenericSizeGenerator rSizeGen,
881 GenericSizeGenerator wChunkSizeGen = 0,
882 GenericSizeGenerator rChunkSizeGen = 0,
883 uint32_t maxOutstanding = 0,
884 uint32_t expectedFailures = 0) {
David Reiss65e62d32010-10-06 17:10:35 +0000885 // adjust totalSize by the specified sizeMultiplier_ first
886 totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
887
David Reiss35dc7692010-10-06 17:10:19 +0000888 std::ostringstream name;
889 name << transport_name << "::test_rw(" << totalSize << ", " <<
890 wSizeGen.describe() << ", " << rSizeGen.describe() << ", " <<
891 wChunkSizeGen.describe() << ", " << rChunkSizeGen.describe() << ", " <<
892 maxOutstanding << ")";
893
894 boost::unit_test::callback0<> test_func =
895 std::tr1::bind(test_rw<CoupledTransports>, totalSize,
896 wSizeGen, rSizeGen, wChunkSizeGen, rChunkSizeGen,
897 maxOutstanding);
898 boost::unit_test::test_case* tc =
899 boost::unit_test::make_test_case(test_func, name.str());
900 suite_->add(tc, expectedFailures);
Roger Meier0069cc42010-10-13 18:10:18 +0000901 }
David Reiss35dc7692010-10-06 17:10:19 +0000902
David Reisse5c435c2010-10-06 17:10:38 +0000903 template <class CoupledTransports>
904 void addTestBlocking(const char* transportName,
905 uint32_t expectedFailures = 0) {
906 char name[1024];
907 boost::unit_test::test_case* tc;
908
909 snprintf(name, sizeof(name), "%s::test_read_part_available()",
910 transportName);
911 tc = boost::unit_test::make_test_case(
912 test_read_part_available<CoupledTransports>, name);
913 suite_->add(tc, expectedFailures);
914
David Reiss0a2d81e2010-10-06 17:10:40 +0000915 snprintf(name, sizeof(name), "%s::test_read_partial_midframe()",
916 transportName);
917 tc = boost::unit_test::make_test_case(
918 test_read_partial_midframe<CoupledTransports>, name);
919 suite_->add(tc, expectedFailures);
920
David Reisse5c435c2010-10-06 17:10:38 +0000921 snprintf(name, sizeof(name), "%s::test_read_none_available()",
922 transportName);
923 tc = boost::unit_test::make_test_case(
924 test_read_none_available<CoupledTransports>, name);
925 suite_->add(tc, expectedFailures);
926
927 snprintf(name, sizeof(name), "%s::test_borrow_part_available()",
928 transportName);
929 tc = boost::unit_test::make_test_case(
930 test_borrow_part_available<CoupledTransports>, name);
931 suite_->add(tc, expectedFailures);
932
933 snprintf(name, sizeof(name), "%s::test_borrow_none_available()",
934 transportName);
935 tc = boost::unit_test::make_test_case(
936 test_borrow_none_available<CoupledTransports>, name);
937 suite_->add(tc, expectedFailures);
938 }
939
David Reiss35dc7692010-10-06 17:10:19 +0000940 boost::unit_test::test_suite* suite_;
David Reiss65e62d32010-10-06 17:10:35 +0000941 // sizeMultiplier_ is configurable via the command line, and allows the
942 // user to adjust between smaller buffers that can be tested quickly,
943 // or larger buffers that more thoroughly exercise the code, but take
944 // longer.
945 float sizeMultiplier_;
David Reiss35dc7692010-10-06 17:10:19 +0000946};
947
948/**************************************************************************
949 * General Initialization
950 **************************************************************************/
951
952void print_usage(FILE* f, const char* argv0) {
953 fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
954 fprintf(f, "Options:\n");
955 fprintf(f, " --seed=<N>, -s <N>\n");
956 fprintf(f, " --tmp-dir=DIR, -t DIR\n");
957 fprintf(f, " --help\n");
958}
959
David Reiss65e62d32010-10-06 17:10:35 +0000960struct Options {
David Reiss35dc7692010-10-06 17:10:19 +0000961 int seed;
David Reiss65e62d32010-10-06 17:10:35 +0000962 bool haveSeed;
963 float sizeMultiplier;
964};
965
966void parse_args(int argc, char* argv[], Options* options) {
967 bool have_seed = false;
968 options->sizeMultiplier = 1;
David Reiss35dc7692010-10-06 17:10:19 +0000969
970 struct option long_opts[] = {
971 { "help", false, NULL, 'h' },
972 { "seed", true, NULL, 's' },
973 { "tmp-dir", true, NULL, 't' },
David Reiss65e62d32010-10-06 17:10:35 +0000974 { "size-multiplier", true, NULL, 'x' },
David Reiss35dc7692010-10-06 17:10:19 +0000975 { NULL, 0, NULL, 0 }
976 };
977
978 while (true) {
979 optopt = 1;
David Reiss65e62d32010-10-06 17:10:35 +0000980 int optchar = getopt_long(argc, argv, "hs:t:x:", long_opts, NULL);
David Reiss35dc7692010-10-06 17:10:19 +0000981 if (optchar == -1) {
982 break;
983 }
984
985 switch (optchar) {
986 case 't':
987 tmp_dir = optarg;
988 break;
989 case 's': {
990 char *endptr;
David Reiss65e62d32010-10-06 17:10:35 +0000991 options->seed = strtol(optarg, &endptr, 0);
David Reiss35dc7692010-10-06 17:10:19 +0000992 if (endptr == optarg || *endptr != '\0') {
993 fprintf(stderr, "invalid seed value \"%s\": must be an integer\n",
994 optarg);
995 exit(1);
996 }
David Reiss65e62d32010-10-06 17:10:35 +0000997 have_seed = true;
David Reiss35dc7692010-10-06 17:10:19 +0000998 break;
999 }
1000 case 'h':
1001 print_usage(stdout, argv[0]);
1002 exit(0);
David Reiss65e62d32010-10-06 17:10:35 +00001003 case 'x': {
1004 char *endptr;
1005 options->sizeMultiplier = strtof(optarg, &endptr);
1006 if (endptr == optarg || *endptr != '\0') {
1007 fprintf(stderr, "invalid size multiplier \"%s\": must be a number\n",
1008 optarg);
1009 exit(1);
1010 }
1011 if (options->sizeMultiplier < 0) {
1012 fprintf(stderr, "invalid size multiplier \"%s\": "
1013 "must be non-negative\n", optarg);
1014 exit(1);
1015 }
1016 break;
1017 }
David Reiss35dc7692010-10-06 17:10:19 +00001018 case '?':
1019 exit(1);
1020 default:
1021 // Only happens if someone adds another option to the optarg string,
1022 // but doesn't update the switch statement to handle it.
1023 fprintf(stderr, "unknown option \"-%c\"\n", optchar);
1024 exit(1);
1025 }
1026 }
1027
David Reiss65e62d32010-10-06 17:10:35 +00001028 if (!have_seed) {
1029 // choose a seed now if the user didn't specify one
Christian Lavoie4f42ef72010-11-04 18:51:42 +00001030 struct timeval tv;
1031 struct timezone tz;
1032 gettimeofday(&tv, &tz);
1033 options->seed = tv.tv_sec ^ tv.tv_usec;
David Reiss65e62d32010-10-06 17:10:35 +00001034 }
David Reiss35dc7692010-10-06 17:10:19 +00001035}
1036
1037boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
1038 // Parse arguments
David Reiss65e62d32010-10-06 17:10:35 +00001039 Options options;
1040 parse_args(argc, argv, &options);
1041
1042 initrand(options.seed);
David Reiss35dc7692010-10-06 17:10:19 +00001043
David Reiss109693c2010-10-06 17:10:42 +00001044 boost::unit_test::test_suite* suite =
1045 &boost::unit_test::framework::master_test_suite();
1046 suite->p_name.value = "TransportTest";
David Reiss65e62d32010-10-06 17:10:35 +00001047 TransportTestGen transport_test_generator(suite, options.sizeMultiplier);
David Reiss35dc7692010-10-06 17:10:19 +00001048 transport_test_generator.generate();
1049
David Reiss109693c2010-10-06 17:10:42 +00001050 return NULL;
David Reiss35dc7692010-10-06 17:10:19 +00001051}