blob: 4233e6e24907e9607660c7770f9e17d69efac314 [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
Roger Meier49ff8b12012-04-13 09:12:31 +000037#include <thrift/transport/TBufferTransports.h>
38#include <thrift/transport/TFDTransport.h>
39#include <thrift/transport/TFileTransport.h>
40#include <thrift/transport/TZlibTransport.h>
41#include <thrift/transport/TSocket.h>
David Reiss35dc7692010-10-06 17:10:19 +000042
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]));
Roger Meier967600e2013-05-03 22:39:53 +0200239 out->setSendTimeout(100);
David Reiss0c025e82010-10-06 17:10:36 +0000240 }
241};
242
243/**
244 * Coupled TFileTransports
245 */
David Reiss35dc7692010-10-06 17:10:19 +0000246class CoupledFileTransports : public CoupledTransports<TFileTransport> {
247 public:
248 CoupledFileTransports() {
249 // Create a temporary file to use
250 size_t filename_len = strlen(tmp_dir) + 32;
251 filename = new char[filename_len];
252 snprintf(filename, filename_len,
253 "%s/thrift.transport_test.XXXXXX", tmp_dir);
254 fd = mkstemp(filename);
255 if (fd < 0) {
256 return;
257 }
258
David Reissd4788df2010-10-06 17:10:37 +0000259 in.reset(new TFileTransport(filename, true));
260 out.reset(new TFileTransport(filename));
David Reiss35dc7692010-10-06 17:10:19 +0000261 }
262
263 ~CoupledFileTransports() {
David Reiss35dc7692010-10-06 17:10:19 +0000264 if (fd >= 0) {
265 close(fd);
266 unlink(filename);
267 }
268 delete[] filename;
269 }
270
271 char* filename;
272 int fd;
273};
274
David Reiss0c025e82010-10-06 17:10:36 +0000275/**
276 * Wrapper around another CoupledTransports implementation that exposes the
277 * transports as TTransport pointers.
278 *
279 * This is used since accessing a transport via a "TTransport*" exercises a
280 * different code path than using the base pointer class. As part of the
281 * template code changes, most transport methods are no longer virtual.
282 */
David Reiss35dc7692010-10-06 17:10:19 +0000283template <class CoupledTransports_>
284class CoupledTTransports : public CoupledTransports<TTransport> {
285 public:
286 CoupledTTransports() : transports() {
287 in = transports.in;
288 out = transports.out;
289 }
290
291 CoupledTransports_ transports;
292};
293
David Reiss0c025e82010-10-06 17:10:36 +0000294/**
295 * Wrapper around another CoupledTransports implementation that exposes the
296 * transports as TBufferBase pointers.
297 *
298 * This can only be instantiated with a transport type that is a subclass of
299 * TBufferBase.
300 */
David Reiss35dc7692010-10-06 17:10:19 +0000301template <class CoupledTransports_>
302class CoupledBufferBases : public CoupledTransports<TBufferBase> {
303 public:
304 CoupledBufferBases() : transports() {
305 in = transports.in;
306 out = transports.out;
307 }
308
309 CoupledTransports_ transports;
310};
311
David Reiss35dc7692010-10-06 17:10:19 +0000312/**************************************************************************
David Reisse5c435c2010-10-06 17:10:38 +0000313 * Alarm handling code for use in tests that check the transport blocking
314 * semantics.
315 *
316 * If the transport ends up blocking, we don't want to hang forever. We use
317 * SIGALRM to fire schedule signal to wake up and try to write data so the
318 * transport will unblock.
319 *
320 * It isn't really the safest thing in the world to be mucking around with
321 * complicated global data structures in a signal handler. It should probably
322 * be okay though, since we know the main thread should always be blocked in a
323 * read() request when the signal handler is running.
324 **************************************************************************/
325
326struct TriggerInfo {
327 TriggerInfo(int seconds, const boost::shared_ptr<TTransport>& transport,
328 uint32_t writeLength) :
329 timeoutSeconds(seconds),
330 transport(transport),
331 writeLength(writeLength),
332 next(NULL) {}
333
334 int timeoutSeconds;
335 boost::shared_ptr<TTransport> transport;
336 uint32_t writeLength;
337 TriggerInfo* next;
338};
339
340TriggerInfo* triggerInfo;
341unsigned int numTriggersFired;
342
343void set_alarm();
344
345void alarm_handler(int signum) {
Roger Meier3b771a12010-11-17 22:11:26 +0000346 (void) signum;
David Reisse5c435c2010-10-06 17:10:38 +0000347 // The alarm timed out, which almost certainly means we're stuck
348 // on a transport that is incorrectly blocked.
349 ++numTriggersFired;
350
351 // Note: we print messages to stdout instead of stderr, since
352 // tools/test/runner only records stdout messages in the failure messages for
353 // boost tests. (boost prints its test info to stdout.)
354 printf("Timeout alarm expired; attempting to unblock transport\n");
355 if (triggerInfo == NULL) {
356 printf(" trigger stack is empty!\n");
357 }
358
359 // Pop off the first TriggerInfo.
360 // If there is another one, schedule an alarm for it.
361 TriggerInfo* info = triggerInfo;
362 triggerInfo = info->next;
363 set_alarm();
364
365 // Write some data to the transport to hopefully unblock it.
Roger Meier0069cc42010-10-13 18:10:18 +0000366 uint8_t* buf = new uint8_t[info->writeLength];
David Reisse5c435c2010-10-06 17:10:38 +0000367 memset(buf, 'b', info->writeLength);
Roger Meier0069cc42010-10-13 18:10:18 +0000368 boost::scoped_array<uint8_t> array(buf);
David Reisse5c435c2010-10-06 17:10:38 +0000369 info->transport->write(buf, info->writeLength);
370 info->transport->flush();
371
372 delete info;
373}
374
375void set_alarm() {
376 if (triggerInfo == NULL) {
377 // clear any alarm
378 alarm(0);
379 return;
380 }
381
382 struct sigaction action;
383 memset(&action, 0, sizeof(action));
384 action.sa_handler = alarm_handler;
Christian Lavoie4f42ef72010-11-04 18:51:42 +0000385 action.sa_flags = SA_RESETHAND;
David Reisse5c435c2010-10-06 17:10:38 +0000386 sigemptyset(&action.sa_mask);
387 sigaction(SIGALRM, &action, NULL);
388
389 alarm(triggerInfo->timeoutSeconds);
390}
391
392/**
393 * Add a trigger to be scheduled "seconds" seconds after the
394 * last currently scheduled trigger.
395 *
396 * (Note that this is not "seconds" from now. That might be more logical, but
397 * would require slightly more complicated sorting, rather than just appending
398 * to the end.)
399 */
400void add_trigger(unsigned int seconds,
401 const boost::shared_ptr<TTransport> &transport,
402 uint32_t write_len) {
403 TriggerInfo* info = new TriggerInfo(seconds, transport, write_len);
404
405 if (triggerInfo == NULL) {
406 // This is the first trigger.
407 // Set triggerInfo, and schedule the alarm
408 triggerInfo = info;
409 set_alarm();
410 } else {
411 // Add this trigger to the end of the list
412 TriggerInfo* prev = triggerInfo;
413 while (prev->next) {
414 prev = prev->next;
415 }
416
417 prev->next = info;
418 }
419}
420
421void clear_triggers() {
422 TriggerInfo *info = triggerInfo;
423 alarm(0);
424 triggerInfo = NULL;
425 numTriggersFired = 0;
426
427 while (info != NULL) {
428 TriggerInfo* next = info->next;
429 delete info;
430 info = next;
431 }
432}
433
434void set_trigger(unsigned int seconds,
435 const boost::shared_ptr<TTransport> &transport,
436 uint32_t write_len) {
437 clear_triggers();
438 add_trigger(seconds, transport, write_len);
439}
440
441/**************************************************************************
442 * Test functions
David Reiss35dc7692010-10-06 17:10:19 +0000443 **************************************************************************/
444
445/**
446 * Test interleaved write and read calls.
447 *
448 * Generates a buffer totalSize bytes long, then writes it to the transport,
449 * and verifies the written data can be read back correctly.
450 *
451 * Mode of operation:
452 * - call wChunkGenerator to figure out how large of a chunk to write
453 * - call wSizeGenerator to get the size for individual write() calls,
454 * and do this repeatedly until the entire chunk is written.
455 * - call rChunkGenerator to figure out how large of a chunk to read
456 * - call rSizeGenerator to get the size for individual read() calls,
457 * and do this repeatedly until the entire chunk is read.
458 * - repeat until the full buffer is written and read back,
459 * then compare the data read back against the original buffer
460 *
461 *
462 * - If any of the size generators return 0, this means to use the maximum
463 * possible size.
464 *
465 * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
466 * there are never more than maxOutstanding bytes waiting to be read back.
467 */
468template <class CoupledTransports>
469void test_rw(uint32_t totalSize,
470 SizeGenerator& wSizeGenerator,
471 SizeGenerator& rSizeGenerator,
472 SizeGenerator& wChunkGenerator,
473 SizeGenerator& rChunkGenerator,
474 uint32_t maxOutstanding) {
475 CoupledTransports transports;
476 BOOST_REQUIRE(transports.in != NULL);
477 BOOST_REQUIRE(transports.out != NULL);
478
479 boost::shared_array<uint8_t> wbuf =
480 boost::shared_array<uint8_t>(new uint8_t[totalSize]);
481 boost::shared_array<uint8_t> rbuf =
482 boost::shared_array<uint8_t>(new uint8_t[totalSize]);
483
484 // store some data in wbuf
485 for (uint32_t n = 0; n < totalSize; ++n) {
486 wbuf[n] = (n & 0xff);
487 }
488 // clear rbuf
489 memset(rbuf.get(), 0, totalSize);
490
491 uint32_t total_written = 0;
492 uint32_t total_read = 0;
493 while (total_read < totalSize) {
494 // Determine how large a chunk of data to write
495 uint32_t wchunk_size = wChunkGenerator.nextSize();
496 if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
497 wchunk_size = totalSize - total_written;
498 }
499
500 // Make sure (total_written - total_read) + wchunk_size
501 // is less than maxOutstanding
502 if (maxOutstanding > 0 &&
503 wchunk_size > maxOutstanding - (total_written - total_read)) {
504 wchunk_size = maxOutstanding - (total_written - total_read);
505 }
506
507 // Write the chunk
508 uint32_t chunk_written = 0;
509 while (chunk_written < wchunk_size) {
510 uint32_t write_size = wSizeGenerator.nextSize();
511 if (write_size == 0 || write_size > wchunk_size - chunk_written) {
512 write_size = wchunk_size - chunk_written;
513 }
514
Roger Meier967600e2013-05-03 22:39:53 +0200515 try {
516 transports.out->write(wbuf.get() + total_written, write_size);
517 }
518 catch (TTransportException & te) {
519 if (te.getType() == TTransportException::TIMED_OUT)
520 break;
521 throw te;
522 }
David Reiss35dc7692010-10-06 17:10:19 +0000523 chunk_written += write_size;
524 total_written += write_size;
525 }
526
527 // Flush the data, so it will be available in the read transport
528 // Don't flush if wchunk_size is 0. (This should only happen if
529 // total_written == totalSize already, and we're only reading now.)
530 if (wchunk_size > 0) {
531 transports.out->flush();
532 }
533
534 // Determine how large a chunk of data to read back
535 uint32_t rchunk_size = rChunkGenerator.nextSize();
536 if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
537 rchunk_size = total_written - total_read;
538 }
539
540 // Read the chunk
541 uint32_t chunk_read = 0;
542 while (chunk_read < rchunk_size) {
543 uint32_t read_size = rSizeGenerator.nextSize();
544 if (read_size == 0 || read_size > rchunk_size - chunk_read) {
545 read_size = rchunk_size - chunk_read;
546 }
547
David Reisse94fa332010-10-06 17:10:26 +0000548 int bytes_read = -1;
549 try {
550 bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
551 } catch (TTransportException& e) {
552 BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size <<
553 ") threw exception \"" << e.what() <<
554 "\"; written so far: " << total_written << " / " <<
555 totalSize << " bytes");
556 }
557
David Reiss35dc7692010-10-06 17:10:19 +0000558 BOOST_REQUIRE_MESSAGE(bytes_read > 0,
559 "read(pos=" << total_read << ", size=" <<
560 read_size << ") returned " << bytes_read <<
561 "; written so far: " << total_written << " / " <<
562 totalSize << " bytes");
563 chunk_read += bytes_read;
564 total_read += bytes_read;
565 }
566 }
567
568 // make sure the data read back is identical to the data written
569 BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
570}
571
Roger Meier02c827b2012-04-11 21:59:57 +0000572
David Reisse5c435c2010-10-06 17:10:38 +0000573template <class CoupledTransports>
574void test_read_part_available() {
575 CoupledTransports transports;
576 BOOST_REQUIRE(transports.in != NULL);
577 BOOST_REQUIRE(transports.out != NULL);
578
579 uint8_t write_buf[16];
580 uint8_t read_buf[16];
581 memset(write_buf, 'a', sizeof(write_buf));
582
583 // Attemping to read 10 bytes when only 9 are available should return 9
584 // immediately.
585 transports.out->write(write_buf, 9);
586 transports.out->flush();
587 set_trigger(3, transports.out, 1);
588 uint32_t bytes_read = transports.in->read(read_buf, 10);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000589 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
590 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 9);
David Reisse5c435c2010-10-06 17:10:38 +0000591
592 clear_triggers();
593}
594
595template <class CoupledTransports>
Roger Meier02c827b2012-04-11 21:59:57 +0000596void test_read_part_available_in_chunks() {
597 CoupledTransports transports;
598 BOOST_REQUIRE(transports.in != NULL);
599 BOOST_REQUIRE(transports.out != NULL);
600
601 uint8_t write_buf[16];
602 uint8_t read_buf[16];
603 memset(write_buf, 'a', sizeof(write_buf));
604
605 // Write 10 bytes (in a single frame, for transports that use framing)
606 transports.out->write(write_buf, 10);
607 transports.out->flush();
608
609 // Read 1 byte, to force the transport to read the frame
610 uint32_t bytes_read = transports.in->read(read_buf, 1);
611 BOOST_CHECK_EQUAL(bytes_read, 1);
612
613 // Read more than what is remaining and verify the transport does not block
614 set_trigger(3, transports.out, 1);
615 bytes_read = transports.in->read(read_buf, 10);
616 BOOST_CHECK_EQUAL(numTriggersFired, 0);
617 BOOST_CHECK_EQUAL(bytes_read, 9);
618
619 clear_triggers();
620}
621
622template <class CoupledTransports>
David Reiss0a2d81e2010-10-06 17:10:40 +0000623void test_read_partial_midframe() {
624 CoupledTransports transports;
625 BOOST_REQUIRE(transports.in != NULL);
626 BOOST_REQUIRE(transports.out != NULL);
627
628 uint8_t write_buf[16];
629 uint8_t read_buf[16];
630 memset(write_buf, 'a', sizeof(write_buf));
631
632 // Attempt to read 10 bytes, when only 9 are available, but after we have
633 // already read part of the data that is available. This exercises a
634 // different code path for several of the transports.
635 //
636 // For transports that add their own framing (e.g., TFramedTransport and
637 // TFileTransport), the two flush calls break up the data in to a 10 byte
638 // frame and a 3 byte frame. The first read then puts us partway through the
639 // first frame, and then we attempt to read past the end of that frame, and
640 // through the next frame, too.
641 //
642 // For buffered transports that perform read-ahead (e.g.,
643 // TBufferedTransport), the read-ahead will most likely see all 13 bytes
644 // written on the first read. The next read will then attempt to read past
645 // the end of the read-ahead buffer.
646 //
647 // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
648 // transports that track framing internally.
649 transports.out->write(write_buf, 10);
650 transports.out->flush();
651 transports.out->write(write_buf, 3);
652 transports.out->flush();
653
654 // Now read 4 bytes, so that we are partway through the written data.
655 uint32_t bytes_read = transports.in->read(read_buf, 4);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000656 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 4);
David Reiss0a2d81e2010-10-06 17:10:40 +0000657
658 // Now attempt to read 10 bytes. Only 9 more are available.
659 //
660 // We should be able to get all 9 bytes, but it might take multiple read
661 // calls, since it is valid for read() to return fewer bytes than requested.
662 // (Most transports do immediately return 9 bytes, but the framing transports
663 // tend to only return to the end of the current frame, which is 6 bytes in
664 // this case.)
665 uint32_t total_read = 0;
666 while (total_read < 9) {
667 set_trigger(3, transports.out, 1);
668 bytes_read = transports.in->read(read_buf, 10);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000669 BOOST_REQUIRE_EQUAL(numTriggersFired, (unsigned int) 0);
670 BOOST_REQUIRE_GT(bytes_read, (uint32_t) 0);
David Reiss0a2d81e2010-10-06 17:10:40 +0000671 total_read += bytes_read;
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000672 BOOST_REQUIRE_LE(total_read, (uint32_t) 9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000673 }
674
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000675 BOOST_CHECK_EQUAL(total_read, (uint32_t) 9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000676
677 clear_triggers();
678}
679
680template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000681void test_borrow_part_available() {
682 CoupledTransports transports;
683 BOOST_REQUIRE(transports.in != NULL);
684 BOOST_REQUIRE(transports.out != NULL);
685
686 uint8_t write_buf[16];
687 uint8_t read_buf[16];
688 memset(write_buf, 'a', sizeof(write_buf));
689
690 // Attemping to borrow 10 bytes when only 9 are available should return NULL
691 // immediately.
692 transports.out->write(write_buf, 9);
693 transports.out->flush();
694 set_trigger(3, transports.out, 1);
695 uint32_t borrow_len = 10;
696 const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000697 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000698 BOOST_CHECK(borrowed_buf == NULL);
699
700 clear_triggers();
701}
702
703template <class CoupledTransports>
704void test_read_none_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 // Attempting to read when no data is available should either block until
714 // some data is available, or fail immediately. (e.g., TSocket blocks,
715 // TMemoryBuffer just fails.)
716 //
717 // If the transport blocks, it should succeed once some data is available,
718 // even if less than the amount requested becomes available.
719 set_trigger(1, transports.out, 2);
720 add_trigger(1, transports.out, 8);
721 uint32_t bytes_read = transports.in->read(read_buf, 10);
722 if (bytes_read == 0) {
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000723 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000724 clear_triggers();
725 } else {
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000726 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 1);
727 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 2);
David Reisse5c435c2010-10-06 17:10:38 +0000728 }
729
730 clear_triggers();
731}
732
733template <class CoupledTransports>
734void test_borrow_none_available() {
735 CoupledTransports transports;
736 BOOST_REQUIRE(transports.in != NULL);
737 BOOST_REQUIRE(transports.out != NULL);
738
739 uint8_t write_buf[16];
740 memset(write_buf, 'a', sizeof(write_buf));
741
742 // Attempting to borrow when no data is available should fail immediately
743 set_trigger(1, transports.out, 10);
744 uint32_t borrow_len = 10;
745 const uint8_t* borrowed_buf = transports.in->borrow(NULL, &borrow_len);
746 BOOST_CHECK(borrowed_buf == NULL);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000747 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000748
749 clear_triggers();
750}
751
David Reiss35dc7692010-10-06 17:10:19 +0000752/**************************************************************************
753 * Test case generation
754 *
755 * Pretty ugly and annoying. This would be much easier if we the unit test
756 * framework didn't force each test to be a separate function.
757 * - Writing a completely separate function definition for each of these would
758 * result in a lot of repetitive boilerplate code.
759 * - Combining many tests into a single function makes it more difficult to
760 * tell precisely which tests failed. It also means you can't get a progress
761 * update after each test, and the tests are already fairly slow.
762 * - Similar registration could be acheived with BOOST_TEST_CASE_TEMPLATE,
763 * but it requires a lot of awkward MPL code, and results in useless test
764 * case names. (The names are generated from std::type_info::name(), which
765 * is compiler-dependent. gcc returns mangled names.)
766 **************************************************************************/
767
David Reisse5c435c2010-10-06 17:10:38 +0000768#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
769 addTestRW< CoupledTransports >(BOOST_STRINGIZE(CoupledTransports), \
770 totalSize, ## __VA_ARGS__);
David Reissd4788df2010-10-06 17:10:37 +0000771
David Reiss35dc7692010-10-06 17:10:19 +0000772#define TEST_RW(CoupledTransports, totalSize, ...) \
773 do { \
774 /* Add the test as specified, to test the non-virtual function calls */ \
David Reisse5c435c2010-10-06 17:10:38 +0000775 ADD_TEST_RW(CoupledTransports, totalSize, ## __VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000776 /* \
777 * Also test using the transport as a TTransport*, to test \
778 * the read_virt()/write_virt() calls \
779 */ \
David Reisse5c435c2010-10-06 17:10:38 +0000780 ADD_TEST_RW(CoupledTTransports<CoupledTransports>, \
781 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000782 /* Test wrapping the transport with TBufferedTransport */ \
David Reisse5c435c2010-10-06 17:10:38 +0000783 ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, \
784 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000785 /* Test wrapping the transport with TFramedTransports */ \
David Reisse5c435c2010-10-06 17:10:38 +0000786 ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, \
787 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000788 /* Test wrapping the transport with TZlibTransport */ \
David Reisse5c435c2010-10-06 17:10:38 +0000789 ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, \
790 totalSize, ## __VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000791 } while (0)
792
David Reisse5c435c2010-10-06 17:10:38 +0000793#define ADD_TEST_BLOCKING(CoupledTransports) \
794 addTestBlocking< CoupledTransports >(BOOST_STRINGIZE(CoupledTransports));
795
796#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
797 ADD_TEST_BLOCKING(CoupledTransports); \
798 ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
799 ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
800 ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
801 ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
802
David Reiss35dc7692010-10-06 17:10:19 +0000803class TransportTestGen {
804 public:
David Reiss65e62d32010-10-06 17:10:35 +0000805 TransportTestGen(boost::unit_test::test_suite* suite,
806 float sizeMultiplier) :
807 suite_(suite),
808 sizeMultiplier_(sizeMultiplier) {}
David Reiss35dc7692010-10-06 17:10:19 +0000809
810 void generate() {
811 GenericSizeGenerator rand4k(1, 4096);
812
813 /*
814 * We do the basically the same set of tests for each transport type,
815 * although we tweak the parameters in some places.
816 */
817
David Reissd4788df2010-10-06 17:10:37 +0000818 // TMemoryBuffer tests
819 TEST_RW(CoupledMemoryBuffers, 1024*1024, 0, 0);
820 TEST_RW(CoupledMemoryBuffers, 1024*256, rand4k, rand4k);
821 TEST_RW(CoupledMemoryBuffers, 1024*256, 167, 163);
822 TEST_RW(CoupledMemoryBuffers, 1024*16, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000823
David Reissd4788df2010-10-06 17:10:37 +0000824 TEST_RW(CoupledMemoryBuffers, 1024*256, 0, 0, rand4k, rand4k);
825 TEST_RW(CoupledMemoryBuffers, 1024*256, rand4k, rand4k, rand4k, rand4k);
826 TEST_RW(CoupledMemoryBuffers, 1024*256, 167, 163, rand4k, rand4k);
827 TEST_RW(CoupledMemoryBuffers, 1024*16, 1, 1, rand4k, rand4k);
David Reisse94fa332010-10-06 17:10:26 +0000828
David Reisse5c435c2010-10-06 17:10:38 +0000829 TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
830
David Reiss35dc7692010-10-06 17:10:19 +0000831 // TFDTransport tests
832 // Since CoupledFDTransports tests with a pipe, writes will block
833 // if there is too much outstanding unread data in the pipe.
834 uint32_t fd_max_outstanding = 4096;
David Reiss65e62d32010-10-06 17:10:35 +0000835 TEST_RW(CoupledFDTransports, 1024*1024, 0, 0,
David Reiss35dc7692010-10-06 17:10:19 +0000836 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000837 TEST_RW(CoupledFDTransports, 1024*256, rand4k, rand4k,
David Reiss35dc7692010-10-06 17:10:19 +0000838 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000839 TEST_RW(CoupledFDTransports, 1024*256, 167, 163,
David Reiss35dc7692010-10-06 17:10:19 +0000840 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000841 TEST_RW(CoupledFDTransports, 1024*16, 1, 1,
David Reiss35dc7692010-10-06 17:10:19 +0000842 0, 0, fd_max_outstanding);
843
David Reiss65e62d32010-10-06 17:10:35 +0000844 TEST_RW(CoupledFDTransports, 1024*256, 0, 0,
David Reiss35dc7692010-10-06 17:10:19 +0000845 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000846 TEST_RW(CoupledFDTransports, 1024*256, rand4k, rand4k,
David Reiss35dc7692010-10-06 17:10:19 +0000847 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000848 TEST_RW(CoupledFDTransports, 1024*256, 167, 163,
David Reiss35dc7692010-10-06 17:10:19 +0000849 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000850 TEST_RW(CoupledFDTransports, 1024*16, 1, 1,
David Reiss35dc7692010-10-06 17:10:19 +0000851 rand4k, rand4k, fd_max_outstanding);
852
David Reisse5c435c2010-10-06 17:10:38 +0000853 TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
854
David Reiss0c025e82010-10-06 17:10:36 +0000855 // TSocket tests
856 uint32_t socket_max_outstanding = 4096;
857 TEST_RW(CoupledSocketTransports, 1024*1024, 0, 0,
858 0, 0, socket_max_outstanding);
859 TEST_RW(CoupledSocketTransports, 1024*256, rand4k, rand4k,
860 0, 0, socket_max_outstanding);
861 TEST_RW(CoupledSocketTransports, 1024*256, 167, 163,
862 0, 0, socket_max_outstanding);
863 // Doh. Apparently writing to a socket has some additional overhead for
864 // each send() call. If we have more than ~400 outstanding 1-byte write
865 // requests, additional send() calls start blocking.
866 TEST_RW(CoupledSocketTransports, 1024*16, 1, 1,
Roger Meier967600e2013-05-03 22:39:53 +0200867 0, 0, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000868 TEST_RW(CoupledSocketTransports, 1024*256, 0, 0,
869 rand4k, rand4k, socket_max_outstanding);
870 TEST_RW(CoupledSocketTransports, 1024*256, rand4k, rand4k,
871 rand4k, rand4k, socket_max_outstanding);
872 TEST_RW(CoupledSocketTransports, 1024*256, 167, 163,
873 rand4k, rand4k, socket_max_outstanding);
874 TEST_RW(CoupledSocketTransports, 1024*16, 1, 1,
Roger Meier967600e2013-05-03 22:39:53 +0200875 rand4k, rand4k, socket_max_outstanding);
David Reiss0c025e82010-10-06 17:10:36 +0000876
David Reisse5c435c2010-10-06 17:10:38 +0000877 TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
878
David Reiss35dc7692010-10-06 17:10:19 +0000879 // TFileTransport tests
880 // We use smaller buffer sizes here, since TFileTransport is fairly slow.
881 //
882 // TFileTransport can't write more than 16MB at once
883 uint32_t max_write_at_once = 1024*1024*16 - 4;
David Reiss65e62d32010-10-06 17:10:35 +0000884 TEST_RW(CoupledFileTransports, 1024*1024, max_write_at_once, 0);
885 TEST_RW(CoupledFileTransports, 1024*128, rand4k, rand4k);
886 TEST_RW(CoupledFileTransports, 1024*128, 167, 163);
887 TEST_RW(CoupledFileTransports, 1024*2, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000888
David Reiss65e62d32010-10-06 17:10:35 +0000889 TEST_RW(CoupledFileTransports, 1024*64, 0, 0, rand4k, rand4k);
890 TEST_RW(CoupledFileTransports, 1024*64,
David Reiss35dc7692010-10-06 17:10:19 +0000891 rand4k, rand4k, rand4k, rand4k);
David Reiss65e62d32010-10-06 17:10:35 +0000892 TEST_RW(CoupledFileTransports, 1024*64, 167, 163, rand4k, rand4k);
893 TEST_RW(CoupledFileTransports, 1024*2, 1, 1, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000894
David Reisse5c435c2010-10-06 17:10:38 +0000895 TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
896
David Reissd4788df2010-10-06 17:10:37 +0000897 // Add some tests that access TBufferedTransport and TFramedTransport
898 // via TTransport pointers and TBufferBase pointers.
David Reisse5c435c2010-10-06 17:10:38 +0000899 ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
900 1024*1024, rand4k, rand4k, rand4k, rand4k);
901 ADD_TEST_RW(CoupledBufferBases<CoupledBufferedTransports>,
902 1024*1024, rand4k, rand4k, rand4k, rand4k);
903 ADD_TEST_RW(CoupledTTransports<CoupledFramedTransports>,
904 1024*1024, rand4k, rand4k, rand4k, rand4k);
905 ADD_TEST_RW(CoupledBufferBases<CoupledFramedTransports>,
906 1024*1024, rand4k, rand4k, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000907
908 // Test using TZlibTransport via a TTransport pointer
David Reisse5c435c2010-10-06 17:10:38 +0000909 ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
910 1024*1024, rand4k, rand4k, rand4k, rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000911 }
912
913 private:
914 template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000915 void addTestRW(const char* transport_name, uint32_t totalSize,
916 GenericSizeGenerator wSizeGen, GenericSizeGenerator rSizeGen,
917 GenericSizeGenerator wChunkSizeGen = 0,
918 GenericSizeGenerator rChunkSizeGen = 0,
919 uint32_t maxOutstanding = 0,
920 uint32_t expectedFailures = 0) {
David Reiss65e62d32010-10-06 17:10:35 +0000921 // adjust totalSize by the specified sizeMultiplier_ first
922 totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
923
David Reiss35dc7692010-10-06 17:10:19 +0000924 std::ostringstream name;
925 name << transport_name << "::test_rw(" << totalSize << ", " <<
926 wSizeGen.describe() << ", " << rSizeGen.describe() << ", " <<
927 wChunkSizeGen.describe() << ", " << rChunkSizeGen.describe() << ", " <<
928 maxOutstanding << ")";
929
930 boost::unit_test::callback0<> test_func =
931 std::tr1::bind(test_rw<CoupledTransports>, totalSize,
932 wSizeGen, rSizeGen, wChunkSizeGen, rChunkSizeGen,
933 maxOutstanding);
934 boost::unit_test::test_case* tc =
935 boost::unit_test::make_test_case(test_func, name.str());
936 suite_->add(tc, expectedFailures);
Roger Meier0069cc42010-10-13 18:10:18 +0000937 }
David Reiss35dc7692010-10-06 17:10:19 +0000938
David Reisse5c435c2010-10-06 17:10:38 +0000939 template <class CoupledTransports>
940 void addTestBlocking(const char* transportName,
941 uint32_t expectedFailures = 0) {
942 char name[1024];
943 boost::unit_test::test_case* tc;
944
945 snprintf(name, sizeof(name), "%s::test_read_part_available()",
946 transportName);
947 tc = boost::unit_test::make_test_case(
948 test_read_part_available<CoupledTransports>, name);
949 suite_->add(tc, expectedFailures);
950
Roger Meier02c827b2012-04-11 21:59:57 +0000951 snprintf(name, sizeof(name), "%s::test_read_part_available_in_chunks()",
952 transportName);
953 tc = boost::unit_test::make_test_case(
954 test_read_part_available_in_chunks<CoupledTransports>, name);
955 suite_->add(tc, expectedFailures);
956
David Reiss0a2d81e2010-10-06 17:10:40 +0000957 snprintf(name, sizeof(name), "%s::test_read_partial_midframe()",
958 transportName);
959 tc = boost::unit_test::make_test_case(
960 test_read_partial_midframe<CoupledTransports>, name);
961 suite_->add(tc, expectedFailures);
962
David Reisse5c435c2010-10-06 17:10:38 +0000963 snprintf(name, sizeof(name), "%s::test_read_none_available()",
964 transportName);
965 tc = boost::unit_test::make_test_case(
966 test_read_none_available<CoupledTransports>, name);
967 suite_->add(tc, expectedFailures);
968
969 snprintf(name, sizeof(name), "%s::test_borrow_part_available()",
970 transportName);
971 tc = boost::unit_test::make_test_case(
972 test_borrow_part_available<CoupledTransports>, name);
973 suite_->add(tc, expectedFailures);
974
975 snprintf(name, sizeof(name), "%s::test_borrow_none_available()",
976 transportName);
977 tc = boost::unit_test::make_test_case(
978 test_borrow_none_available<CoupledTransports>, name);
979 suite_->add(tc, expectedFailures);
980 }
981
David Reiss35dc7692010-10-06 17:10:19 +0000982 boost::unit_test::test_suite* suite_;
David Reiss65e62d32010-10-06 17:10:35 +0000983 // sizeMultiplier_ is configurable via the command line, and allows the
984 // user to adjust between smaller buffers that can be tested quickly,
985 // or larger buffers that more thoroughly exercise the code, but take
986 // longer.
987 float sizeMultiplier_;
David Reiss35dc7692010-10-06 17:10:19 +0000988};
989
990/**************************************************************************
991 * General Initialization
992 **************************************************************************/
993
994void print_usage(FILE* f, const char* argv0) {
995 fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
996 fprintf(f, "Options:\n");
997 fprintf(f, " --seed=<N>, -s <N>\n");
998 fprintf(f, " --tmp-dir=DIR, -t DIR\n");
999 fprintf(f, " --help\n");
1000}
1001
David Reiss65e62d32010-10-06 17:10:35 +00001002struct Options {
David Reiss35dc7692010-10-06 17:10:19 +00001003 int seed;
David Reiss65e62d32010-10-06 17:10:35 +00001004 bool haveSeed;
1005 float sizeMultiplier;
1006};
1007
1008void parse_args(int argc, char* argv[], Options* options) {
1009 bool have_seed = false;
1010 options->sizeMultiplier = 1;
David Reiss35dc7692010-10-06 17:10:19 +00001011
1012 struct option long_opts[] = {
1013 { "help", false, NULL, 'h' },
1014 { "seed", true, NULL, 's' },
1015 { "tmp-dir", true, NULL, 't' },
David Reiss65e62d32010-10-06 17:10:35 +00001016 { "size-multiplier", true, NULL, 'x' },
David Reiss35dc7692010-10-06 17:10:19 +00001017 { NULL, 0, NULL, 0 }
1018 };
1019
1020 while (true) {
1021 optopt = 1;
David Reiss65e62d32010-10-06 17:10:35 +00001022 int optchar = getopt_long(argc, argv, "hs:t:x:", long_opts, NULL);
David Reiss35dc7692010-10-06 17:10:19 +00001023 if (optchar == -1) {
1024 break;
1025 }
1026
1027 switch (optchar) {
1028 case 't':
1029 tmp_dir = optarg;
1030 break;
1031 case 's': {
1032 char *endptr;
David Reiss65e62d32010-10-06 17:10:35 +00001033 options->seed = strtol(optarg, &endptr, 0);
David Reiss35dc7692010-10-06 17:10:19 +00001034 if (endptr == optarg || *endptr != '\0') {
1035 fprintf(stderr, "invalid seed value \"%s\": must be an integer\n",
1036 optarg);
1037 exit(1);
1038 }
David Reiss65e62d32010-10-06 17:10:35 +00001039 have_seed = true;
David Reiss35dc7692010-10-06 17:10:19 +00001040 break;
1041 }
1042 case 'h':
1043 print_usage(stdout, argv[0]);
1044 exit(0);
David Reiss65e62d32010-10-06 17:10:35 +00001045 case 'x': {
1046 char *endptr;
1047 options->sizeMultiplier = strtof(optarg, &endptr);
1048 if (endptr == optarg || *endptr != '\0') {
1049 fprintf(stderr, "invalid size multiplier \"%s\": must be a number\n",
1050 optarg);
1051 exit(1);
1052 }
1053 if (options->sizeMultiplier < 0) {
1054 fprintf(stderr, "invalid size multiplier \"%s\": "
1055 "must be non-negative\n", optarg);
1056 exit(1);
1057 }
1058 break;
1059 }
David Reiss35dc7692010-10-06 17:10:19 +00001060 case '?':
1061 exit(1);
1062 default:
1063 // Only happens if someone adds another option to the optarg string,
1064 // but doesn't update the switch statement to handle it.
1065 fprintf(stderr, "unknown option \"-%c\"\n", optchar);
1066 exit(1);
1067 }
1068 }
1069
David Reiss65e62d32010-10-06 17:10:35 +00001070 if (!have_seed) {
1071 // choose a seed now if the user didn't specify one
Christian Lavoie4f42ef72010-11-04 18:51:42 +00001072 struct timeval tv;
1073 struct timezone tz;
1074 gettimeofday(&tv, &tz);
1075 options->seed = tv.tv_sec ^ tv.tv_usec;
David Reiss65e62d32010-10-06 17:10:35 +00001076 }
David Reiss35dc7692010-10-06 17:10:19 +00001077}
1078
1079boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
1080 // Parse arguments
David Reiss65e62d32010-10-06 17:10:35 +00001081 Options options;
1082 parse_args(argc, argv, &options);
1083
1084 initrand(options.seed);
David Reiss35dc7692010-10-06 17:10:19 +00001085
David Reiss109693c2010-10-06 17:10:42 +00001086 boost::unit_test::test_suite* suite =
1087 &boost::unit_test::framework::master_test_suite();
1088 suite->p_name.value = "TransportTest";
David Reiss65e62d32010-10-06 17:10:35 +00001089 TransportTestGen transport_test_generator(suite, options.sizeMultiplier);
David Reiss35dc7692010-10-06 17:10:19 +00001090 transport_test_generator.generate();
1091
David Reiss109693c2010-10-06 17:10:42 +00001092 return NULL;
David Reiss35dc7692010-10-06 17:10:19 +00001093}