blob: fdc5c6370fd35c3c002554c83852b3f34398f844 [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>
27#include <sstream>
28#include <tr1/functional>
29
30#include <boost/mpl/list.hpp>
31#include <boost/shared_array.hpp>
32#include <boost/random.hpp>
33#include <boost/type_traits.hpp>
34#include <boost/test/unit_test.hpp>
35
36#include <transport/TBufferTransports.h>
37#include <transport/TFDTransport.h>
38#include <transport/TFileTransport.h>
David Reisse94fa332010-10-06 17:10:26 +000039#include <transport/TZlibTransport.h>
David Reiss35dc7692010-10-06 17:10:19 +000040
41using namespace apache::thrift::transport;
42
43static boost::mt19937 rng;
44static const char* tmp_dir = "/tmp";
45
David Reiss65e62d32010-10-06 17:10:35 +000046void initrand(unsigned int seed) {
David Reiss35dc7692010-10-06 17:10:19 +000047 rng.seed(seed);
48}
49
50class SizeGenerator {
51 public:
52 virtual ~SizeGenerator() {}
53 virtual uint32_t nextSize() = 0;
54 virtual std::string describe() const = 0;
55};
56
57class ConstantSizeGenerator : public SizeGenerator {
58 public:
59 ConstantSizeGenerator(uint32_t value) : value_(value) {}
60 uint32_t nextSize() { return value_; }
61 std::string describe() const {
62 std::ostringstream desc;
63 desc << value_;
64 return desc.str();
65 }
66
67 private:
68 uint32_t value_;
69};
70
71class RandomSizeGenerator : public SizeGenerator {
72 public:
73 RandomSizeGenerator(uint32_t min, uint32_t max) :
74 generator_(rng, boost::uniform_int<int>(min, max)) {}
75
76 uint32_t nextSize() { return generator_(); }
77
78 std::string describe() const {
79 std::ostringstream desc;
80 desc << "rand(" << getMin() << ", " << getMax() << ")";
81 return desc.str();
82 }
83
84 uint32_t getMin() const { return generator_.distribution().min(); }
85 uint32_t getMax() const { return generator_.distribution().max(); }
86
87 private:
88 boost::variate_generator< boost::mt19937&, boost::uniform_int<int> >
89 generator_;
90};
91
92/**
93 * This class exists solely to make the TEST_RW() macro easier to use.
94 * - it can be constructed implicitly from an integer
95 * - it can contain either a ConstantSizeGenerator or a RandomSizeGenerator
96 * (TEST_RW can't take a SizeGenerator pointer or reference, since it needs
97 * to make a copy of the generator to bind it to the test function.)
98 */
99class GenericSizeGenerator : public SizeGenerator {
100 public:
101 GenericSizeGenerator(uint32_t value) :
102 generator_(new ConstantSizeGenerator(value)) {}
103 GenericSizeGenerator(uint32_t min, uint32_t max) :
104 generator_(new RandomSizeGenerator(min, max)) {}
105
106 uint32_t nextSize() { return generator_->nextSize(); }
107 std::string describe() const { return generator_->describe(); }
108
109 private:
110 boost::shared_ptr<SizeGenerator> generator_;
111};
112
113/**************************************************************************
114 * Classes to set up coupled transports
115 **************************************************************************/
116
117template <class Transport_>
118class CoupledTransports {
119 public:
120 typedef Transport_ TransportType;
121
122 CoupledTransports() : in(NULL), out(NULL) {}
123
124 Transport_* in;
125 Transport_* out;
126
127 private:
128 CoupledTransports(const CoupledTransports&);
129 CoupledTransports &operator=(const CoupledTransports&);
130};
131
132class CoupledMemoryBuffers : public CoupledTransports<TMemoryBuffer> {
133 public:
134 CoupledMemoryBuffers() {
135 in = &buf;
136 out = &buf;
137 }
138
139 TMemoryBuffer buf;
140};
141
142class CoupledBufferedTransports :
143 public CoupledTransports<TBufferedTransport> {
144 public:
145 CoupledBufferedTransports() :
146 buf(new TMemoryBuffer) {
147 in = new TBufferedTransport(buf);
148 out = new TBufferedTransport(buf);
149 }
150
151 ~CoupledBufferedTransports() {
152 delete in;
153 delete out;
154 }
155
156 boost::shared_ptr<TMemoryBuffer> buf;
157};
158
159class CoupledFramedTransports : public CoupledTransports<TFramedTransport> {
160 public:
161 CoupledFramedTransports() :
162 buf(new TMemoryBuffer) {
163 in = new TFramedTransport(buf);
164 out = new TFramedTransport(buf);
165 }
166
167 ~CoupledFramedTransports() {
168 delete in;
169 delete out;
170 }
171
172 boost::shared_ptr<TMemoryBuffer> buf;
173};
174
David Reisse94fa332010-10-06 17:10:26 +0000175class CoupledZlibTransports : public CoupledTransports<TZlibTransport> {
176 public:
177 CoupledZlibTransports() :
178 buf(new TMemoryBuffer) {
David Reissa0e11592010-10-06 17:10:27 +0000179 in = new TZlibTransport(buf);
180 out = new TZlibTransport(buf);
David Reisse94fa332010-10-06 17:10:26 +0000181 }
182
183 ~CoupledZlibTransports() {
184 delete in;
185 delete out;
186 }
187
188 boost::shared_ptr<TMemoryBuffer> buf;
189};
190
David Reiss35dc7692010-10-06 17:10:19 +0000191class CoupledFDTransports : public CoupledTransports<TFDTransport> {
192 public:
193 CoupledFDTransports() {
194 int pipes[2];
195
196 if (pipe(pipes) != 0) {
197 return;
198 }
199
200 in = new TFDTransport(pipes[0], TFDTransport::CLOSE_ON_DESTROY);
201 out = new TFDTransport(pipes[1], TFDTransport::CLOSE_ON_DESTROY);
202 }
203
204 ~CoupledFDTransports() {
205 delete in;
206 delete out;
207 }
208};
209
210class CoupledFileTransports : public CoupledTransports<TFileTransport> {
211 public:
212 CoupledFileTransports() {
213 // Create a temporary file to use
214 size_t filename_len = strlen(tmp_dir) + 32;
215 filename = new char[filename_len];
216 snprintf(filename, filename_len,
217 "%s/thrift.transport_test.XXXXXX", tmp_dir);
218 fd = mkstemp(filename);
219 if (fd < 0) {
220 return;
221 }
222
223 in = new TFileTransport(filename, true);
224 out = new TFileTransport(filename);
225 }
226
227 ~CoupledFileTransports() {
228 delete in;
229 delete out;
230
231 if (fd >= 0) {
232 close(fd);
233 unlink(filename);
234 }
235 delete[] filename;
236 }
237
238 char* filename;
239 int fd;
240};
241
242template <class CoupledTransports_>
243class CoupledTTransports : public CoupledTransports<TTransport> {
244 public:
245 CoupledTTransports() : transports() {
246 in = transports.in;
247 out = transports.out;
248 }
249
250 CoupledTransports_ transports;
251};
252
253template <class CoupledTransports_>
254class CoupledBufferBases : public CoupledTransports<TBufferBase> {
255 public:
256 CoupledBufferBases() : transports() {
257 in = transports.in;
258 out = transports.out;
259 }
260
261 CoupledTransports_ transports;
262};
263
264/*
265 * TODO: It would be nice to test TSocket, too.
266 * Unfortunately, TSocket/TServerSocket currently don't provide a low-level
267 * API that would allow us to create a connected socket pair.
268 *
269 * TODO: It would be nice to test TZlibTransport, too.
270 * However, TZlibTransport doesn't conform to quite the same semantics as other
271 * transports. No new data can be written to a TZlibTransport after flush() is
272 * called, since flush() terminates the zlib data stream. In the future maybe
273 * we should make TZlibTransport behave more like the other transports.
274 */
275
276/**************************************************************************
277 * Main testing function
278 **************************************************************************/
279
280/**
281 * Test interleaved write and read calls.
282 *
283 * Generates a buffer totalSize bytes long, then writes it to the transport,
284 * and verifies the written data can be read back correctly.
285 *
286 * Mode of operation:
287 * - call wChunkGenerator to figure out how large of a chunk to write
288 * - call wSizeGenerator to get the size for individual write() calls,
289 * and do this repeatedly until the entire chunk is written.
290 * - call rChunkGenerator to figure out how large of a chunk to read
291 * - call rSizeGenerator to get the size for individual read() calls,
292 * and do this repeatedly until the entire chunk is read.
293 * - repeat until the full buffer is written and read back,
294 * then compare the data read back against the original buffer
295 *
296 *
297 * - If any of the size generators return 0, this means to use the maximum
298 * possible size.
299 *
300 * - If maxOutstanding is non-zero, write chunk sizes will be chosen such that
301 * there are never more than maxOutstanding bytes waiting to be read back.
302 */
303template <class CoupledTransports>
304void test_rw(uint32_t totalSize,
305 SizeGenerator& wSizeGenerator,
306 SizeGenerator& rSizeGenerator,
307 SizeGenerator& wChunkGenerator,
308 SizeGenerator& rChunkGenerator,
309 uint32_t maxOutstanding) {
310 CoupledTransports transports;
311 BOOST_REQUIRE(transports.in != NULL);
312 BOOST_REQUIRE(transports.out != NULL);
313
314 boost::shared_array<uint8_t> wbuf =
315 boost::shared_array<uint8_t>(new uint8_t[totalSize]);
316 boost::shared_array<uint8_t> rbuf =
317 boost::shared_array<uint8_t>(new uint8_t[totalSize]);
318
319 // store some data in wbuf
320 for (uint32_t n = 0; n < totalSize; ++n) {
321 wbuf[n] = (n & 0xff);
322 }
323 // clear rbuf
324 memset(rbuf.get(), 0, totalSize);
325
326 uint32_t total_written = 0;
327 uint32_t total_read = 0;
328 while (total_read < totalSize) {
329 // Determine how large a chunk of data to write
330 uint32_t wchunk_size = wChunkGenerator.nextSize();
331 if (wchunk_size == 0 || wchunk_size > totalSize - total_written) {
332 wchunk_size = totalSize - total_written;
333 }
334
335 // Make sure (total_written - total_read) + wchunk_size
336 // is less than maxOutstanding
337 if (maxOutstanding > 0 &&
338 wchunk_size > maxOutstanding - (total_written - total_read)) {
339 wchunk_size = maxOutstanding - (total_written - total_read);
340 }
341
342 // Write the chunk
343 uint32_t chunk_written = 0;
344 while (chunk_written < wchunk_size) {
345 uint32_t write_size = wSizeGenerator.nextSize();
346 if (write_size == 0 || write_size > wchunk_size - chunk_written) {
347 write_size = wchunk_size - chunk_written;
348 }
349
350 transports.out->write(wbuf.get() + total_written, write_size);
351 chunk_written += write_size;
352 total_written += write_size;
353 }
354
355 // Flush the data, so it will be available in the read transport
356 // Don't flush if wchunk_size is 0. (This should only happen if
357 // total_written == totalSize already, and we're only reading now.)
358 if (wchunk_size > 0) {
359 transports.out->flush();
360 }
361
362 // Determine how large a chunk of data to read back
363 uint32_t rchunk_size = rChunkGenerator.nextSize();
364 if (rchunk_size == 0 || rchunk_size > total_written - total_read) {
365 rchunk_size = total_written - total_read;
366 }
367
368 // Read the chunk
369 uint32_t chunk_read = 0;
370 while (chunk_read < rchunk_size) {
371 uint32_t read_size = rSizeGenerator.nextSize();
372 if (read_size == 0 || read_size > rchunk_size - chunk_read) {
373 read_size = rchunk_size - chunk_read;
374 }
375
David Reisse94fa332010-10-06 17:10:26 +0000376 int bytes_read = -1;
377 try {
378 bytes_read = transports.in->read(rbuf.get() + total_read, read_size);
379 } catch (TTransportException& e) {
380 BOOST_FAIL("read(pos=" << total_read << ", size=" << read_size <<
381 ") threw exception \"" << e.what() <<
382 "\"; written so far: " << total_written << " / " <<
383 totalSize << " bytes");
384 }
385
David Reiss35dc7692010-10-06 17:10:19 +0000386 BOOST_REQUIRE_MESSAGE(bytes_read > 0,
387 "read(pos=" << total_read << ", size=" <<
388 read_size << ") returned " << bytes_read <<
389 "; written so far: " << total_written << " / " <<
390 totalSize << " bytes");
391 chunk_read += bytes_read;
392 total_read += bytes_read;
393 }
394 }
395
396 // make sure the data read back is identical to the data written
397 BOOST_CHECK_EQUAL(memcmp(rbuf.get(), wbuf.get(), totalSize), 0);
398}
399
400/**************************************************************************
401 * Test case generation
402 *
403 * Pretty ugly and annoying. This would be much easier if we the unit test
404 * framework didn't force each test to be a separate function.
405 * - Writing a completely separate function definition for each of these would
406 * result in a lot of repetitive boilerplate code.
407 * - Combining many tests into a single function makes it more difficult to
408 * tell precisely which tests failed. It also means you can't get a progress
409 * update after each test, and the tests are already fairly slow.
410 * - Similar registration could be acheived with BOOST_TEST_CASE_TEMPLATE,
411 * but it requires a lot of awkward MPL code, and results in useless test
412 * case names. (The names are generated from std::type_info::name(), which
413 * is compiler-dependent. gcc returns mangled names.)
414 **************************************************************************/
415
416#define TEST_RW(CoupledTransports, totalSize, ...) \
417 do { \
418 /* Add the test as specified, to test the non-virtual function calls */ \
419 addTest<CoupledTransports>(BOOST_STRINGIZE(CoupledTransports), \
420 totalSize, ## __VA_ARGS__); \
421 /* \
422 * Also test using the transport as a TTransport*, to test \
423 * the read_virt()/write_virt() calls \
424 */ \
425 addTest< CoupledTTransports<CoupledTransports> >( \
426 BOOST_STRINGIZE(CoupledTTransports<CoupledTransports>), \
427 totalSize, ## __VA_ARGS__); \
428 } while (0)
429
430#define TEST_RW_BUF(CoupledTransports, totalSize, ...) \
431 do { \
432 /* Add the standard tests */ \
433 TEST_RW(CoupledTransports, totalSize, ## __VA_ARGS__); \
434 /* Also test using the transport as a TBufferBase* */ \
435 addTest< CoupledBufferBases<CoupledTransports> >( \
436 BOOST_STRINGIZE(CoupledBufferBases<CoupledTransports>), \
437 totalSize, ## __VA_ARGS__); \
438 } while (0)
439
440// We use the same tests for all of the buffered transports
441// This is a helper macro so we don't have to copy-and-paste them.
442#define BUFFER_TESTS(CoupledTransports) \
David Reiss65e62d32010-10-06 17:10:35 +0000443 TEST_RW_BUF(CoupledTransports, 1024*1024, 0, 0); \
444 TEST_RW_BUF(CoupledTransports, 1024*256, rand4k, rand4k); \
445 TEST_RW_BUF(CoupledTransports, 1024*256, 167, 163); \
446 TEST_RW_BUF(CoupledTransports, 1024*16, 1, 1); \
David Reiss35dc7692010-10-06 17:10:19 +0000447 \
David Reiss65e62d32010-10-06 17:10:35 +0000448 TEST_RW_BUF(CoupledTransports, 1024*256, 0, 0, rand4k, rand4k); \
449 TEST_RW_BUF(CoupledTransports, 1024*256, \
David Reiss35dc7692010-10-06 17:10:19 +0000450 rand4k, rand4k, rand4k, rand4k); \
David Reiss65e62d32010-10-06 17:10:35 +0000451 TEST_RW_BUF(CoupledTransports, 1024*256, 167, 163, rand4k, rand4k); \
452 TEST_RW_BUF(CoupledTransports, 1024*16, 1, 1, rand4k, rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000453
454class TransportTestGen {
455 public:
David Reiss65e62d32010-10-06 17:10:35 +0000456 TransportTestGen(boost::unit_test::test_suite* suite,
457 float sizeMultiplier) :
458 suite_(suite),
459 sizeMultiplier_(sizeMultiplier) {}
David Reiss35dc7692010-10-06 17:10:19 +0000460
461 void generate() {
462 GenericSizeGenerator rand4k(1, 4096);
463
464 /*
465 * We do the basically the same set of tests for each transport type,
466 * although we tweak the parameters in some places.
467 */
468
469 // Buffered transport tests
470 BUFFER_TESTS(CoupledMemoryBuffers)
471 BUFFER_TESTS(CoupledBufferedTransports)
472 BUFFER_TESTS(CoupledFramedTransports)
473
David Reiss65e62d32010-10-06 17:10:35 +0000474 TEST_RW(CoupledZlibTransports, 1024*256, 0, 0);
475 TEST_RW(CoupledZlibTransports, 1024*256, rand4k, rand4k);
476 TEST_RW(CoupledZlibTransports, 1024*128, 167, 163);
477 TEST_RW(CoupledZlibTransports, 1024*2, 1, 1);
David Reisse94fa332010-10-06 17:10:26 +0000478
David Reiss65e62d32010-10-06 17:10:35 +0000479 TEST_RW(CoupledZlibTransports, 1024*256, 0, 0, rand4k, rand4k);
480 TEST_RW(CoupledZlibTransports, 1024*256,
David Reisse94fa332010-10-06 17:10:26 +0000481 rand4k, rand4k, rand4k, rand4k);
David Reiss65e62d32010-10-06 17:10:35 +0000482 TEST_RW(CoupledZlibTransports, 1024*128, 167, 163, rand4k, rand4k);
483 TEST_RW(CoupledZlibTransports, 1024*2, 1, 1, rand4k, rand4k);
David Reisse94fa332010-10-06 17:10:26 +0000484
David Reiss35dc7692010-10-06 17:10:19 +0000485 // TFDTransport tests
486 // Since CoupledFDTransports tests with a pipe, writes will block
487 // if there is too much outstanding unread data in the pipe.
488 uint32_t fd_max_outstanding = 4096;
David Reiss65e62d32010-10-06 17:10:35 +0000489 TEST_RW(CoupledFDTransports, 1024*1024, 0, 0,
David Reiss35dc7692010-10-06 17:10:19 +0000490 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000491 TEST_RW(CoupledFDTransports, 1024*256, rand4k, rand4k,
David Reiss35dc7692010-10-06 17:10:19 +0000492 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000493 TEST_RW(CoupledFDTransports, 1024*256, 167, 163,
David Reiss35dc7692010-10-06 17:10:19 +0000494 0, 0, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000495 TEST_RW(CoupledFDTransports, 1024*16, 1, 1,
David Reiss35dc7692010-10-06 17:10:19 +0000496 0, 0, fd_max_outstanding);
497
David Reiss65e62d32010-10-06 17:10:35 +0000498 TEST_RW(CoupledFDTransports, 1024*256, 0, 0,
David Reiss35dc7692010-10-06 17:10:19 +0000499 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000500 TEST_RW(CoupledFDTransports, 1024*256, rand4k, rand4k,
David Reiss35dc7692010-10-06 17:10:19 +0000501 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000502 TEST_RW(CoupledFDTransports, 1024*256, 167, 163,
David Reiss35dc7692010-10-06 17:10:19 +0000503 rand4k, rand4k, fd_max_outstanding);
David Reiss65e62d32010-10-06 17:10:35 +0000504 TEST_RW(CoupledFDTransports, 1024*16, 1, 1,
David Reiss35dc7692010-10-06 17:10:19 +0000505 rand4k, rand4k, fd_max_outstanding);
506
507 // TFileTransport tests
508 // We use smaller buffer sizes here, since TFileTransport is fairly slow.
509 //
510 // TFileTransport can't write more than 16MB at once
511 uint32_t max_write_at_once = 1024*1024*16 - 4;
David Reiss65e62d32010-10-06 17:10:35 +0000512 TEST_RW(CoupledFileTransports, 1024*1024, max_write_at_once, 0);
513 TEST_RW(CoupledFileTransports, 1024*128, rand4k, rand4k);
514 TEST_RW(CoupledFileTransports, 1024*128, 167, 163);
515 TEST_RW(CoupledFileTransports, 1024*2, 1, 1);
David Reiss35dc7692010-10-06 17:10:19 +0000516
David Reiss65e62d32010-10-06 17:10:35 +0000517 TEST_RW(CoupledFileTransports, 1024*64, 0, 0, rand4k, rand4k);
518 TEST_RW(CoupledFileTransports, 1024*64,
David Reiss35dc7692010-10-06 17:10:19 +0000519 rand4k, rand4k, rand4k, rand4k);
David Reiss65e62d32010-10-06 17:10:35 +0000520 TEST_RW(CoupledFileTransports, 1024*64, 167, 163, rand4k, rand4k);
521 TEST_RW(CoupledFileTransports, 1024*2, 1, 1, rand4k, rand4k);
David Reiss35dc7692010-10-06 17:10:19 +0000522 }
523
524 private:
525 template <class CoupledTransports>
526 void addTest(const char* transport_name, uint32_t totalSize,
527 GenericSizeGenerator wSizeGen, GenericSizeGenerator rSizeGen,
528 GenericSizeGenerator wChunkSizeGen = 0,
529 GenericSizeGenerator rChunkSizeGen = 0,
530 uint32_t maxOutstanding = 0,
531 uint32_t expectedFailures = 0) {
David Reiss65e62d32010-10-06 17:10:35 +0000532 // adjust totalSize by the specified sizeMultiplier_ first
533 totalSize = static_cast<uint32_t>(totalSize * sizeMultiplier_);
534
David Reiss35dc7692010-10-06 17:10:19 +0000535 std::ostringstream name;
536 name << transport_name << "::test_rw(" << totalSize << ", " <<
537 wSizeGen.describe() << ", " << rSizeGen.describe() << ", " <<
538 wChunkSizeGen.describe() << ", " << rChunkSizeGen.describe() << ", " <<
539 maxOutstanding << ")";
540
541 boost::unit_test::callback0<> test_func =
542 std::tr1::bind(test_rw<CoupledTransports>, totalSize,
543 wSizeGen, rSizeGen, wChunkSizeGen, rChunkSizeGen,
544 maxOutstanding);
545 boost::unit_test::test_case* tc =
546 boost::unit_test::make_test_case(test_func, name.str());
547 suite_->add(tc, expectedFailures);
548 };
549
550 boost::unit_test::test_suite* suite_;
David Reiss65e62d32010-10-06 17:10:35 +0000551 // sizeMultiplier_ is configurable via the command line, and allows the
552 // user to adjust between smaller buffers that can be tested quickly,
553 // or larger buffers that more thoroughly exercise the code, but take
554 // longer.
555 float sizeMultiplier_;
David Reiss35dc7692010-10-06 17:10:19 +0000556};
557
558/**************************************************************************
559 * General Initialization
560 **************************************************************************/
561
562void print_usage(FILE* f, const char* argv0) {
563 fprintf(f, "Usage: %s [boost_options] [options]\n", argv0);
564 fprintf(f, "Options:\n");
565 fprintf(f, " --seed=<N>, -s <N>\n");
566 fprintf(f, " --tmp-dir=DIR, -t DIR\n");
567 fprintf(f, " --help\n");
568}
569
David Reiss65e62d32010-10-06 17:10:35 +0000570struct Options {
David Reiss35dc7692010-10-06 17:10:19 +0000571 int seed;
David Reiss65e62d32010-10-06 17:10:35 +0000572 bool haveSeed;
573 float sizeMultiplier;
574};
575
576void parse_args(int argc, char* argv[], Options* options) {
577 bool have_seed = false;
578 options->sizeMultiplier = 1;
David Reiss35dc7692010-10-06 17:10:19 +0000579
580 struct option long_opts[] = {
581 { "help", false, NULL, 'h' },
582 { "seed", true, NULL, 's' },
583 { "tmp-dir", true, NULL, 't' },
David Reiss65e62d32010-10-06 17:10:35 +0000584 { "size-multiplier", true, NULL, 'x' },
David Reiss35dc7692010-10-06 17:10:19 +0000585 { NULL, 0, NULL, 0 }
586 };
587
588 while (true) {
589 optopt = 1;
David Reiss65e62d32010-10-06 17:10:35 +0000590 int optchar = getopt_long(argc, argv, "hs:t:x:", long_opts, NULL);
David Reiss35dc7692010-10-06 17:10:19 +0000591 if (optchar == -1) {
592 break;
593 }
594
595 switch (optchar) {
596 case 't':
597 tmp_dir = optarg;
598 break;
599 case 's': {
600 char *endptr;
David Reiss65e62d32010-10-06 17:10:35 +0000601 options->seed = strtol(optarg, &endptr, 0);
David Reiss35dc7692010-10-06 17:10:19 +0000602 if (endptr == optarg || *endptr != '\0') {
603 fprintf(stderr, "invalid seed value \"%s\": must be an integer\n",
604 optarg);
605 exit(1);
606 }
David Reiss65e62d32010-10-06 17:10:35 +0000607 have_seed = true;
David Reiss35dc7692010-10-06 17:10:19 +0000608 break;
609 }
610 case 'h':
611 print_usage(stdout, argv[0]);
612 exit(0);
David Reiss65e62d32010-10-06 17:10:35 +0000613 case 'x': {
614 char *endptr;
615 options->sizeMultiplier = strtof(optarg, &endptr);
616 if (endptr == optarg || *endptr != '\0') {
617 fprintf(stderr, "invalid size multiplier \"%s\": must be a number\n",
618 optarg);
619 exit(1);
620 }
621 if (options->sizeMultiplier < 0) {
622 fprintf(stderr, "invalid size multiplier \"%s\": "
623 "must be non-negative\n", optarg);
624 exit(1);
625 }
626 break;
627 }
David Reiss35dc7692010-10-06 17:10:19 +0000628 case '?':
629 exit(1);
630 default:
631 // Only happens if someone adds another option to the optarg string,
632 // but doesn't update the switch statement to handle it.
633 fprintf(stderr, "unknown option \"-%c\"\n", optchar);
634 exit(1);
635 }
636 }
637
David Reiss65e62d32010-10-06 17:10:35 +0000638 if (!have_seed) {
639 // choose a seed now if the user didn't specify one
640 struct timespec t;
641 clock_gettime(CLOCK_REALTIME, &t);
642 options->seed = t.tv_sec + t.tv_nsec;
643 }
David Reiss35dc7692010-10-06 17:10:19 +0000644}
645
646boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) {
647 // Parse arguments
David Reiss65e62d32010-10-06 17:10:35 +0000648 Options options;
649 parse_args(argc, argv, &options);
650
651 initrand(options.seed);
David Reiss35dc7692010-10-06 17:10:19 +0000652
653 boost::unit_test::test_suite* suite = BOOST_TEST_SUITE("TransportTests");
David Reiss65e62d32010-10-06 17:10:35 +0000654 TransportTestGen transport_test_generator(suite, options.sizeMultiplier);
David Reiss35dc7692010-10-06 17:10:19 +0000655 transport_test_generator.generate();
656
657 return suite;
658}