blob: 508d3e372d47e3cf3883d44b1feb5615aa007348 [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]));
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
Roger Meier02c827b2012-04-11 21:59:57 +0000564
David Reisse5c435c2010-10-06 17:10:38 +0000565template <class CoupledTransports>
566void test_read_part_available() {
567 CoupledTransports transports;
568 BOOST_REQUIRE(transports.in != NULL);
569 BOOST_REQUIRE(transports.out != NULL);
570
571 uint8_t write_buf[16];
572 uint8_t read_buf[16];
573 memset(write_buf, 'a', sizeof(write_buf));
574
575 // Attemping to read 10 bytes when only 9 are available should return 9
576 // immediately.
577 transports.out->write(write_buf, 9);
578 transports.out->flush();
579 set_trigger(3, transports.out, 1);
580 uint32_t bytes_read = transports.in->read(read_buf, 10);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000581 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
582 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 9);
David Reisse5c435c2010-10-06 17:10:38 +0000583
584 clear_triggers();
585}
586
587template <class CoupledTransports>
Roger Meier02c827b2012-04-11 21:59:57 +0000588void test_read_part_available_in_chunks() {
589 CoupledTransports transports;
590 BOOST_REQUIRE(transports.in != NULL);
591 BOOST_REQUIRE(transports.out != NULL);
592
593 uint8_t write_buf[16];
594 uint8_t read_buf[16];
595 memset(write_buf, 'a', sizeof(write_buf));
596
597 // Write 10 bytes (in a single frame, for transports that use framing)
598 transports.out->write(write_buf, 10);
599 transports.out->flush();
600
601 // Read 1 byte, to force the transport to read the frame
602 uint32_t bytes_read = transports.in->read(read_buf, 1);
603 BOOST_CHECK_EQUAL(bytes_read, 1);
604
605 // Read more than what is remaining and verify the transport does not block
606 set_trigger(3, transports.out, 1);
607 bytes_read = transports.in->read(read_buf, 10);
608 BOOST_CHECK_EQUAL(numTriggersFired, 0);
609 BOOST_CHECK_EQUAL(bytes_read, 9);
610
611 clear_triggers();
612}
613
614template <class CoupledTransports>
David Reiss0a2d81e2010-10-06 17:10:40 +0000615void test_read_partial_midframe() {
616 CoupledTransports transports;
617 BOOST_REQUIRE(transports.in != NULL);
618 BOOST_REQUIRE(transports.out != NULL);
619
620 uint8_t write_buf[16];
621 uint8_t read_buf[16];
622 memset(write_buf, 'a', sizeof(write_buf));
623
624 // Attempt to read 10 bytes, when only 9 are available, but after we have
625 // already read part of the data that is available. This exercises a
626 // different code path for several of the transports.
627 //
628 // For transports that add their own framing (e.g., TFramedTransport and
629 // TFileTransport), the two flush calls break up the data in to a 10 byte
630 // frame and a 3 byte frame. The first read then puts us partway through the
631 // first frame, and then we attempt to read past the end of that frame, and
632 // through the next frame, too.
633 //
634 // For buffered transports that perform read-ahead (e.g.,
635 // TBufferedTransport), the read-ahead will most likely see all 13 bytes
636 // written on the first read. The next read will then attempt to read past
637 // the end of the read-ahead buffer.
638 //
639 // Flush 10 bytes, then 3 bytes. This creates 2 separate frames for
640 // transports that track framing internally.
641 transports.out->write(write_buf, 10);
642 transports.out->flush();
643 transports.out->write(write_buf, 3);
644 transports.out->flush();
645
646 // Now read 4 bytes, so that we are partway through the written data.
647 uint32_t bytes_read = transports.in->read(read_buf, 4);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000648 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 4);
David Reiss0a2d81e2010-10-06 17:10:40 +0000649
650 // Now attempt to read 10 bytes. Only 9 more are available.
651 //
652 // We should be able to get all 9 bytes, but it might take multiple read
653 // calls, since it is valid for read() to return fewer bytes than requested.
654 // (Most transports do immediately return 9 bytes, but the framing transports
655 // tend to only return to the end of the current frame, which is 6 bytes in
656 // this case.)
657 uint32_t total_read = 0;
658 while (total_read < 9) {
659 set_trigger(3, transports.out, 1);
660 bytes_read = transports.in->read(read_buf, 10);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000661 BOOST_REQUIRE_EQUAL(numTriggersFired, (unsigned int) 0);
662 BOOST_REQUIRE_GT(bytes_read, (uint32_t) 0);
David Reiss0a2d81e2010-10-06 17:10:40 +0000663 total_read += bytes_read;
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000664 BOOST_REQUIRE_LE(total_read, (uint32_t) 9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000665 }
666
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000667 BOOST_CHECK_EQUAL(total_read, (uint32_t) 9);
David Reiss0a2d81e2010-10-06 17:10:40 +0000668
669 clear_triggers();
670}
671
672template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000673void test_borrow_part_available() {
674 CoupledTransports transports;
675 BOOST_REQUIRE(transports.in != NULL);
676 BOOST_REQUIRE(transports.out != NULL);
677
678 uint8_t write_buf[16];
679 uint8_t read_buf[16];
680 memset(write_buf, 'a', sizeof(write_buf));
681
682 // Attemping to borrow 10 bytes when only 9 are available should return NULL
683 // immediately.
684 transports.out->write(write_buf, 9);
685 transports.out->flush();
686 set_trigger(3, transports.out, 1);
687 uint32_t borrow_len = 10;
688 const uint8_t* borrowed_buf = transports.in->borrow(read_buf, &borrow_len);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000689 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000690 BOOST_CHECK(borrowed_buf == NULL);
691
692 clear_triggers();
693}
694
695template <class CoupledTransports>
696void test_read_none_available() {
697 CoupledTransports transports;
698 BOOST_REQUIRE(transports.in != NULL);
699 BOOST_REQUIRE(transports.out != NULL);
700
701 uint8_t write_buf[16];
702 uint8_t read_buf[16];
703 memset(write_buf, 'a', sizeof(write_buf));
704
705 // Attempting to read when no data is available should either block until
706 // some data is available, or fail immediately. (e.g., TSocket blocks,
707 // TMemoryBuffer just fails.)
708 //
709 // If the transport blocks, it should succeed once some data is available,
710 // even if less than the amount requested becomes available.
711 set_trigger(1, transports.out, 2);
712 add_trigger(1, transports.out, 8);
713 uint32_t bytes_read = transports.in->read(read_buf, 10);
714 if (bytes_read == 0) {
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000715 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000716 clear_triggers();
717 } else {
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000718 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 1);
719 BOOST_CHECK_EQUAL(bytes_read, (uint32_t) 2);
David Reisse5c435c2010-10-06 17:10:38 +0000720 }
721
722 clear_triggers();
723}
724
725template <class CoupledTransports>
726void test_borrow_none_available() {
727 CoupledTransports transports;
728 BOOST_REQUIRE(transports.in != NULL);
729 BOOST_REQUIRE(transports.out != NULL);
730
731 uint8_t write_buf[16];
732 memset(write_buf, 'a', sizeof(write_buf));
733
734 // Attempting to borrow when no data is available should fail immediately
735 set_trigger(1, transports.out, 10);
736 uint32_t borrow_len = 10;
737 const uint8_t* borrowed_buf = transports.in->borrow(NULL, &borrow_len);
738 BOOST_CHECK(borrowed_buf == NULL);
Christian Lavoie01c5ceb2010-11-04 20:35:15 +0000739 BOOST_CHECK_EQUAL(numTriggersFired, (unsigned int) 0);
David Reisse5c435c2010-10-06 17:10:38 +0000740
741 clear_triggers();
742}
743
David Reiss35dc7692010-10-06 17:10:19 +0000744/**************************************************************************
745 * Test case generation
746 *
747 * Pretty ugly and annoying. This would be much easier if we the unit test
748 * framework didn't force each test to be a separate function.
749 * - Writing a completely separate function definition for each of these would
750 * result in a lot of repetitive boilerplate code.
751 * - Combining many tests into a single function makes it more difficult to
752 * tell precisely which tests failed. It also means you can't get a progress
753 * update after each test, and the tests are already fairly slow.
754 * - Similar registration could be acheived with BOOST_TEST_CASE_TEMPLATE,
755 * but it requires a lot of awkward MPL code, and results in useless test
756 * case names. (The names are generated from std::type_info::name(), which
757 * is compiler-dependent. gcc returns mangled names.)
758 **************************************************************************/
759
David Reisse5c435c2010-10-06 17:10:38 +0000760#define ADD_TEST_RW(CoupledTransports, totalSize, ...) \
761 addTestRW< CoupledTransports >(BOOST_STRINGIZE(CoupledTransports), \
762 totalSize, ## __VA_ARGS__);
David Reissd4788df2010-10-06 17:10:37 +0000763
David Reiss35dc7692010-10-06 17:10:19 +0000764#define TEST_RW(CoupledTransports, totalSize, ...) \
765 do { \
766 /* Add the test as specified, to test the non-virtual function calls */ \
David Reisse5c435c2010-10-06 17:10:38 +0000767 ADD_TEST_RW(CoupledTransports, totalSize, ## __VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000768 /* \
769 * Also test using the transport as a TTransport*, to test \
770 * the read_virt()/write_virt() calls \
771 */ \
David Reisse5c435c2010-10-06 17:10:38 +0000772 ADD_TEST_RW(CoupledTTransports<CoupledTransports>, \
773 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000774 /* Test wrapping the transport with TBufferedTransport */ \
David Reisse5c435c2010-10-06 17:10:38 +0000775 ADD_TEST_RW(CoupledBufferedTransportsT<CoupledTransports>, \
776 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000777 /* Test wrapping the transport with TFramedTransports */ \
David Reisse5c435c2010-10-06 17:10:38 +0000778 ADD_TEST_RW(CoupledFramedTransportsT<CoupledTransports>, \
779 totalSize, ## __VA_ARGS__); \
David Reissd4788df2010-10-06 17:10:37 +0000780 /* Test wrapping the transport with TZlibTransport */ \
David Reisse5c435c2010-10-06 17:10:38 +0000781 ADD_TEST_RW(CoupledZlibTransportsT<CoupledTransports>, \
782 totalSize, ## __VA_ARGS__); \
David Reiss35dc7692010-10-06 17:10:19 +0000783 } while (0)
784
David Reisse5c435c2010-10-06 17:10:38 +0000785#define ADD_TEST_BLOCKING(CoupledTransports) \
786 addTestBlocking< CoupledTransports >(BOOST_STRINGIZE(CoupledTransports));
787
788#define TEST_BLOCKING_BEHAVIOR(CoupledTransports) \
789 ADD_TEST_BLOCKING(CoupledTransports); \
790 ADD_TEST_BLOCKING(CoupledTTransports<CoupledTransports>); \
791 ADD_TEST_BLOCKING(CoupledBufferedTransportsT<CoupledTransports>); \
792 ADD_TEST_BLOCKING(CoupledFramedTransportsT<CoupledTransports>); \
793 ADD_TEST_BLOCKING(CoupledZlibTransportsT<CoupledTransports>);
794
David Reiss35dc7692010-10-06 17:10:19 +0000795class TransportTestGen {
796 public:
David Reiss65e62d32010-10-06 17:10:35 +0000797 TransportTestGen(boost::unit_test::test_suite* suite,
798 float sizeMultiplier) :
799 suite_(suite),
800 sizeMultiplier_(sizeMultiplier) {}
David Reiss35dc7692010-10-06 17:10:19 +0000801
802 void generate() {
803 GenericSizeGenerator rand4k(1, 4096);
804
805 /*
806 * We do the basically the same set of tests for each transport type,
807 * although we tweak the parameters in some places.
808 */
809
David Reissd4788df2010-10-06 17:10:37 +0000810 // TMemoryBuffer tests
811 TEST_RW(CoupledMemoryBuffers, 1024*1024, 0, 0);
812 TEST_RW(CoupledMemoryBuffers, 1024*256, rand4k, rand4k);
813 TEST_RW(CoupledMemoryBuffers, 1024*256, 167, 163);
814 TEST_RW(CoupledMemoryBuffers, 1024*16, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000815
David Reissd4788df2010-10-06 17:10:37 +0000816 TEST_RW(CoupledMemoryBuffers, 1024*256, 0, 0, rand4k, rand4k);
817 TEST_RW(CoupledMemoryBuffers, 1024*256, rand4k, rand4k, rand4k, rand4k);
818 TEST_RW(CoupledMemoryBuffers, 1024*256, 167, 163, rand4k, rand4k);
819 TEST_RW(CoupledMemoryBuffers, 1024*16, 1, 1, rand4k, rand4k);
David Reisse94fa332010-10-06 17:10:26 +0000820
David Reisse5c435c2010-10-06 17:10:38 +0000821 TEST_BLOCKING_BEHAVIOR(CoupledMemoryBuffers);
822
David Reiss35dc7692010-10-06 17:10:19 +0000823 // TFDTransport tests
824 // Since CoupledFDTransports tests with a pipe, writes will block
825 // if there is too much outstanding unread data in the pipe.
826 uint32_t fd_max_outstanding = 4096;
David Reiss65e62d32010-10-06 17:10:35 +0000827 TEST_RW(CoupledFDTransports, 1024*1024, 0, 0,
David Reiss35dc7692010-10-06 17:10:19 +0000828 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000829 TEST_RW(CoupledFDTransports, 1024*256, rand4k, rand4k,
David Reiss35dc7692010-10-06 17:10:19 +0000830 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000831 TEST_RW(CoupledFDTransports, 1024*256, 167, 163,
David Reiss35dc7692010-10-06 17:10:19 +0000832 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000833 TEST_RW(CoupledFDTransports, 1024*16, 1, 1,
David Reiss35dc7692010-10-06 17:10:19 +0000834 0, 0, fd_max_outstanding);
835
David Reiss65e62d32010-10-06 17:10:35 +0000836 TEST_RW(CoupledFDTransports, 1024*256, 0, 0,
David Reiss35dc7692010-10-06 17:10:19 +0000837 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000838 TEST_RW(CoupledFDTransports, 1024*256, rand4k, rand4k,
David Reiss35dc7692010-10-06 17:10:19 +0000839 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000840 TEST_RW(CoupledFDTransports, 1024*256, 167, 163,
David Reiss35dc7692010-10-06 17:10:19 +0000841 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000842 TEST_RW(CoupledFDTransports, 1024*16, 1, 1,
David Reiss35dc7692010-10-06 17:10:19 +0000843 rand4k, rand4k, fd_max_outstanding);
844
David Reisse5c435c2010-10-06 17:10:38 +0000845 TEST_BLOCKING_BEHAVIOR(CoupledFDTransports);
846
David Reiss0c025e82010-10-06 17:10:36 +0000847 // TSocket tests
848 uint32_t socket_max_outstanding = 4096;
849 TEST_RW(CoupledSocketTransports, 1024*1024, 0, 0,
850 0, 0, socket_max_outstanding);
851 TEST_RW(CoupledSocketTransports, 1024*256, rand4k, rand4k,
852 0, 0, socket_max_outstanding);
853 TEST_RW(CoupledSocketTransports, 1024*256, 167, 163,
854 0, 0, socket_max_outstanding);
855 // Doh. Apparently writing to a socket has some additional overhead for
856 // each send() call. If we have more than ~400 outstanding 1-byte write
857 // requests, additional send() calls start blocking.
858 TEST_RW(CoupledSocketTransports, 1024*16, 1, 1,
859 0, 0, 400);
860 TEST_RW(CoupledSocketTransports, 1024*256, 0, 0,
861 rand4k, rand4k, socket_max_outstanding);
862 TEST_RW(CoupledSocketTransports, 1024*256, rand4k, rand4k,
863 rand4k, rand4k, socket_max_outstanding);
864 TEST_RW(CoupledSocketTransports, 1024*256, 167, 163,
865 rand4k, rand4k, socket_max_outstanding);
866 TEST_RW(CoupledSocketTransports, 1024*16, 1, 1,
867 rand4k, rand4k, 400);
868
David Reisse5c435c2010-10-06 17:10:38 +0000869 TEST_BLOCKING_BEHAVIOR(CoupledSocketTransports);
870
David Reiss35dc7692010-10-06 17:10:19 +0000871 // TFileTransport tests
872 // We use smaller buffer sizes here, since TFileTransport is fairly slow.
873 //
874 // TFileTransport can't write more than 16MB at once
875 uint32_t max_write_at_once = 1024*1024*16 - 4;
David Reiss65e62d32010-10-06 17:10:35 +0000876 TEST_RW(CoupledFileTransports, 1024*1024, max_write_at_once, 0);
877 TEST_RW(CoupledFileTransports, 1024*128, rand4k, rand4k);
878 TEST_RW(CoupledFileTransports, 1024*128, 167, 163);
879 TEST_RW(CoupledFileTransports, 1024*2, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000880
David Reiss65e62d32010-10-06 17:10:35 +0000881 TEST_RW(CoupledFileTransports, 1024*64, 0, 0, rand4k, rand4k);
882 TEST_RW(CoupledFileTransports, 1024*64,
David Reiss35dc7692010-10-06 17:10:19 +0000883 rand4k, rand4k, rand4k, rand4k);
David Reiss65e62d32010-10-06 17:10:35 +0000884 TEST_RW(CoupledFileTransports, 1024*64, 167, 163, rand4k, rand4k);
885 TEST_RW(CoupledFileTransports, 1024*2, 1, 1, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000886
David Reisse5c435c2010-10-06 17:10:38 +0000887 TEST_BLOCKING_BEHAVIOR(CoupledFileTransports);
888
David Reissd4788df2010-10-06 17:10:37 +0000889 // Add some tests that access TBufferedTransport and TFramedTransport
890 // via TTransport pointers and TBufferBase pointers.
David Reisse5c435c2010-10-06 17:10:38 +0000891 ADD_TEST_RW(CoupledTTransports<CoupledBufferedTransports>,
892 1024*1024, rand4k, rand4k, rand4k, rand4k);
893 ADD_TEST_RW(CoupledBufferBases<CoupledBufferedTransports>,
894 1024*1024, rand4k, rand4k, rand4k, rand4k);
895 ADD_TEST_RW(CoupledTTransports<CoupledFramedTransports>,
896 1024*1024, rand4k, rand4k, rand4k, rand4k);
897 ADD_TEST_RW(CoupledBufferBases<CoupledFramedTransports>,
898 1024*1024, rand4k, rand4k, rand4k, rand4k);
David Reissd4788df2010-10-06 17:10:37 +0000899
900 // Test using TZlibTransport via a TTransport pointer
David Reisse5c435c2010-10-06 17:10:38 +0000901 ADD_TEST_RW(CoupledTTransports<CoupledZlibTransports>,
902 1024*1024, rand4k, rand4k, rand4k, rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000903 }
904
905 private:
906 template <class CoupledTransports>
David Reisse5c435c2010-10-06 17:10:38 +0000907 void addTestRW(const char* transport_name, uint32_t totalSize,
908 GenericSizeGenerator wSizeGen, GenericSizeGenerator rSizeGen,
909 GenericSizeGenerator wChunkSizeGen = 0,
910 GenericSizeGenerator rChunkSizeGen = 0,
911 uint32_t maxOutstanding = 0,
912 uint32_t expectedFailures = 0) {
David Reiss65e62d32010-10-06 17:10:35 +0000913 // adjust totalSize by the specified sizeMultiplier_ first
914 totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
915
David Reiss35dc7692010-10-06 17:10:19 +0000916 std::ostringstream name;
917 name << transport_name << "::test_rw(" << totalSize << ", " <<
918 wSizeGen.describe() << ", " << rSizeGen.describe() << ", " <<
919 wChunkSizeGen.describe() << ", " << rChunkSizeGen.describe() << ", " <<
920 maxOutstanding << ")";
921
922 boost::unit_test::callback0<> test_func =
923 std::tr1::bind(test_rw<CoupledTransports>, totalSize,
924 wSizeGen, rSizeGen, wChunkSizeGen, rChunkSizeGen,
925 maxOutstanding);
926 boost::unit_test::test_case* tc =
927 boost::unit_test::make_test_case(test_func, name.str());
928 suite_->add(tc, expectedFailures);
Roger Meier0069cc42010-10-13 18:10:18 +0000929 }
David Reiss35dc7692010-10-06 17:10:19 +0000930
David Reisse5c435c2010-10-06 17:10:38 +0000931 template <class CoupledTransports>
932 void addTestBlocking(const char* transportName,
933 uint32_t expectedFailures = 0) {
934 char name[1024];
935 boost::unit_test::test_case* tc;
936
937 snprintf(name, sizeof(name), "%s::test_read_part_available()",
938 transportName);
939 tc = boost::unit_test::make_test_case(
940 test_read_part_available<CoupledTransports>, name);
941 suite_->add(tc, expectedFailures);
942
Roger Meier02c827b2012-04-11 21:59:57 +0000943 snprintf(name, sizeof(name), "%s::test_read_part_available_in_chunks()",
944 transportName);
945 tc = boost::unit_test::make_test_case(
946 test_read_part_available_in_chunks<CoupledTransports>, name);
947 suite_->add(tc, expectedFailures);
948
David Reiss0a2d81e2010-10-06 17:10:40 +0000949 snprintf(name, sizeof(name), "%s::test_read_partial_midframe()",
950 transportName);
951 tc = boost::unit_test::make_test_case(
952 test_read_partial_midframe<CoupledTransports>, name);
953 suite_->add(tc, expectedFailures);
954
David Reisse5c435c2010-10-06 17:10:38 +0000955 snprintf(name, sizeof(name), "%s::test_read_none_available()",
956 transportName);
957 tc = boost::unit_test::make_test_case(
958 test_read_none_available<CoupledTransports>, name);
959 suite_->add(tc, expectedFailures);
960
961 snprintf(name, sizeof(name), "%s::test_borrow_part_available()",
962 transportName);
963 tc = boost::unit_test::make_test_case(
964 test_borrow_part_available<CoupledTransports>, name);
965 suite_->add(tc, expectedFailures);
966
967 snprintf(name, sizeof(name), "%s::test_borrow_none_available()",
968 transportName);
969 tc = boost::unit_test::make_test_case(
970 test_borrow_none_available<CoupledTransports>, name);
971 suite_->add(tc, expectedFailures);
972 }
973
David Reiss35dc7692010-10-06 17:10:19 +0000974 boost::unit_test::test_suite* suite_;
David Reiss65e62d32010-10-06 17:10:35 +0000975 // sizeMultiplier_ is configurable via the command line, and allows the
976 // user to adjust between smaller buffers that can be tested quickly,
977 // or larger buffers that more thoroughly exercise the code, but take
978 // longer.
979 float sizeMultiplier_;
David Reiss35dc7692010-10-06 17:10:19 +0000980};
981
982/**************************************************************************
983 * General Initialization
984 **************************************************************************/
985
986void print_usage(FILE* f, const char* argv0) {
987 fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
988 fprintf(f, "Options:\n");
989 fprintf(f, " --seed=<N>, -s <N>\n");
990 fprintf(f, " --tmp-dir=DIR, -t DIR\n");
991 fprintf(f, " --help\n");
992}
993
David Reiss65e62d32010-10-06 17:10:35 +0000994struct Options {
David Reiss35dc7692010-10-06 17:10:19 +0000995 int seed;
David Reiss65e62d32010-10-06 17:10:35 +0000996 bool haveSeed;
997 float sizeMultiplier;
998};
999
1000void parse_args(int argc, char* argv[], Options* options) {
1001 bool have_seed = false;
1002 options->sizeMultiplier = 1;
David Reiss35dc7692010-10-06 17:10:19 +00001003
1004 struct option long_opts[] = {
1005 { "help", false, NULL, 'h' },
1006 { "seed", true, NULL, 's' },
1007 { "tmp-dir", true, NULL, 't' },
David Reiss65e62d32010-10-06 17:10:35 +00001008 { "size-multiplier", true, NULL, 'x' },
David Reiss35dc7692010-10-06 17:10:19 +00001009 { NULL, 0, NULL, 0 }
1010 };
1011
1012 while (true) {
1013 optopt = 1;
David Reiss65e62d32010-10-06 17:10:35 +00001014 int optchar = getopt_long(argc, argv, "hs:t:x:", long_opts, NULL);
David Reiss35dc7692010-10-06 17:10:19 +00001015 if (optchar == -1) {
1016 break;
1017 }
1018
1019 switch (optchar) {
1020 case 't':
1021 tmp_dir = optarg;
1022 break;
1023 case 's': {
1024 char *endptr;
David Reiss65e62d32010-10-06 17:10:35 +00001025 options->seed = strtol(optarg, &endptr, 0);
David Reiss35dc7692010-10-06 17:10:19 +00001026 if (endptr == optarg || *endptr != '\0') {
1027 fprintf(stderr, "invalid seed value \"%s\": must be an integer\n",
1028 optarg);
1029 exit(1);
1030 }
David Reiss65e62d32010-10-06 17:10:35 +00001031 have_seed = true;
David Reiss35dc7692010-10-06 17:10:19 +00001032 break;
1033 }
1034 case 'h':
1035 print_usage(stdout, argv[0]);
1036 exit(0);
David Reiss65e62d32010-10-06 17:10:35 +00001037 case 'x': {
1038 char *endptr;
1039 options->sizeMultiplier = strtof(optarg, &endptr);
1040 if (endptr == optarg || *endptr != '\0') {
1041 fprintf(stderr, "invalid size multiplier \"%s\": must be a number\n",
1042 optarg);
1043 exit(1);
1044 }
1045 if (options->sizeMultiplier < 0) {
1046 fprintf(stderr, "invalid size multiplier \"%s\": "
1047 "must be non-negative\n", optarg);
1048 exit(1);
1049 }
1050 break;
1051 }
David Reiss35dc7692010-10-06 17:10:19 +00001052 case '?':
1053 exit(1);
1054 default:
1055 // Only happens if someone adds another option to the optarg string,
1056 // but doesn't update the switch statement to handle it.
1057 fprintf(stderr, "unknown option \"-%c\"\n", optchar);
1058 exit(1);
1059 }
1060 }
1061
David Reiss65e62d32010-10-06 17:10:35 +00001062 if (!have_seed) {
1063 // choose a seed now if the user didn't specify one
Christian Lavoie4f42ef72010-11-04 18:51:42 +00001064 struct timeval tv;
1065 struct timezone tz;
1066 gettimeofday(&tv, &tz);
1067 options->seed = tv.tv_sec ^ tv.tv_usec;
David Reiss65e62d32010-10-06 17:10:35 +00001068 }
David Reiss35dc7692010-10-06 17:10:19 +00001069}
1070
1071boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
1072 // Parse arguments
David Reiss65e62d32010-10-06 17:10:35 +00001073 Options options;
1074 parse_args(argc, argv, &options);
1075
1076 initrand(options.seed);
David Reiss35dc7692010-10-06 17:10:19 +00001077
David Reiss109693c2010-10-06 17:10:42 +00001078 boost::unit_test::test_suite* suite =
1079 &boost::unit_test::framework::master_test_suite();
1080 suite->p_name.value = "TransportTest";
David Reiss65e62d32010-10-06 17:10:35 +00001081 TransportTestGen transport_test_generator(suite, options.sizeMultiplier);
David Reiss35dc7692010-10-06 17:10:19 +00001082 transport_test_generator.generate();
1083
David Reiss109693c2010-10-06 17:10:42 +00001084 return NULL;
David Reiss35dc7692010-10-06 17:10:19 +00001085}