Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/kyua/utils/cmdline/parser_test.cpp
48178 views
1
// Copyright 2010 The Kyua Authors.
2
// All rights reserved.
3
//
4
// Redistribution and use in source and binary forms, with or without
5
// modification, are permitted provided that the following conditions are
6
// met:
7
//
8
// * Redistributions of source code must retain the above copyright
9
// notice, this list of conditions and the following disclaimer.
10
// * Redistributions in binary form must reproduce the above copyright
11
// notice, this list of conditions and the following disclaimer in the
12
// documentation and/or other materials provided with the distribution.
13
// * Neither the name of Google Inc. nor the names of its contributors
14
// may be used to endorse or promote products derived from this software
15
// without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
#include "utils/cmdline/parser.ipp"
30
31
#if defined(HAVE_CONFIG_H)
32
#include "config.h"
33
#endif
34
35
extern "C" {
36
#include <fcntl.h>
37
#include <getopt.h>
38
#include <unistd.h>
39
}
40
41
#include <cstdlib>
42
#include <cstring>
43
#include <fstream>
44
#include <iostream>
45
#include <string>
46
#include <utility>
47
48
#include <atf-c++.hpp>
49
50
#include "utils/cmdline/exceptions.hpp"
51
#include "utils/cmdline/options.hpp"
52
#include "utils/format/macros.hpp"
53
#include "utils/sanity.hpp"
54
55
namespace cmdline = utils::cmdline;
56
57
using cmdline::base_option;
58
using cmdline::bool_option;
59
using cmdline::int_option;
60
using cmdline::parse;
61
using cmdline::parsed_cmdline;
62
using cmdline::string_option;
63
64
65
namespace {
66
67
68
/// Mock option type to check the validate and convert methods sequence.
69
///
70
/// Instances of this option accept a string argument that must be either "zero"
71
/// or "one". These are validated and converted to integers.
72
class mock_option : public base_option {
73
public:
74
/// Constructs the new option.
75
///
76
/// \param long_name_ The long name for the option. All other option
77
/// properties are irrelevant for the tests using this, so they are set
78
/// to arbitrary values.
79
mock_option(const char* long_name_) :
80
base_option(long_name_, "Irrelevant description", "arg")
81
{
82
}
83
84
/// The type of the argument of this option.
85
typedef int option_type;
86
87
/// Checks that the user-provided option is valid.
88
///
89
/// \param str The user argument; must be "zero" or "one".
90
///
91
/// \throw cmdline::option_argument_value_error If str is not valid.
92
void
93
validate(const std::string& str) const
94
{
95
if (str != "zero" && str != "one")
96
throw cmdline::option_argument_value_error(F("--%s") % long_name(),
97
str, "Unknown value");
98
}
99
100
/// Converts the user-provided argument to our native integer type.
101
///
102
/// \param str The user argument; must be "zero" or "one".
103
///
104
/// \return 0 if the input is "zero", or 1 if the input is "one".
105
///
106
/// \throw std::runtime_error If str is not valid. In real life, this
107
/// should be a precondition because validate() has already ensured that
108
/// the values passed to convert() are correct. However, we raise an
109
/// exception here because we are actually validating that this code
110
/// sequence holds true.
111
static int
112
convert(const std::string& str)
113
{
114
if (str == "zero")
115
return 0;
116
else if (str == "one")
117
return 1;
118
else {
119
// This would generally be an assertion but, given that this is
120
// test code, we want to catch any errors regardless of how the
121
// binary is built.
122
throw std::runtime_error("Value not validated properly.");
123
}
124
}
125
};
126
127
128
/// Redirects stdout and stderr to a file.
129
///
130
/// This fails the test case in case of any error.
131
///
132
/// \param file The name of the file to redirect stdout and stderr to.
133
///
134
/// \return A copy of the old stdout and stderr file descriptors.
135
static std::pair< int, int >
136
mock_stdfds(const char* file)
137
{
138
std::cout.flush();
139
std::cerr.flush();
140
141
const int oldout = ::dup(STDOUT_FILENO);
142
ATF_REQUIRE(oldout != -1);
143
const int olderr = ::dup(STDERR_FILENO);
144
ATF_REQUIRE(olderr != -1);
145
146
const int fd = ::open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
147
ATF_REQUIRE(fd != -1);
148
ATF_REQUIRE(::dup2(fd, STDOUT_FILENO) != -1);
149
ATF_REQUIRE(::dup2(fd, STDERR_FILENO) != -1);
150
::close(fd);
151
152
return std::make_pair(oldout, olderr);
153
}
154
155
156
/// Restores stdout and stderr after a call to mock_stdfds.
157
///
158
/// \param oldfds The copy of the previous stdout and stderr as returned by the
159
/// call to mock_fds().
160
static void
161
restore_stdfds(const std::pair< int, int >& oldfds)
162
{
163
ATF_REQUIRE(::dup2(oldfds.first, STDOUT_FILENO) != -1);
164
::close(oldfds.first);
165
ATF_REQUIRE(::dup2(oldfds.second, STDERR_FILENO) != -1);
166
::close(oldfds.second);
167
}
168
169
170
/// Checks whether a '+:' prefix to the short options of getopt_long works.
171
///
172
/// It turns out that the getopt_long(3) implementation of Ubuntu 10.04.1 (and
173
/// very likely other distributions) does not properly report a missing argument
174
/// to a second long option as such. Instead of returning ':' when the second
175
/// long option provided on the command line does not carry a required argument,
176
/// it will mistakenly return '?' which translates to "unknown option".
177
///
178
/// As a result of this bug, we cannot properly detect that 'flag2' requires an
179
/// argument in a command line like: 'progname --flag1=foo --flag2'.
180
///
181
/// I am not sure if we could fully workaround the issue in the implementation
182
/// of our library. For the time being I am just using this bug detection in
183
/// the test cases to prevent failures that are not really our fault.
184
///
185
/// \return bool True if getopt_long is broken and does not interpret '+:'
186
/// correctly; False otherwise.
187
static bool
188
is_getopt_long_pluscolon_broken(void)
189
{
190
struct ::option long_options[] = {
191
{ "flag1", 1, NULL, '1' },
192
{ "flag2", 1, NULL, '2' },
193
{ NULL, 0, NULL, 0 }
194
};
195
196
const int argc = 3;
197
char* argv[4];
198
argv[0] = ::strdup("progname");
199
argv[1] = ::strdup("--flag1=a");
200
argv[2] = ::strdup("--flag2");
201
argv[3] = NULL;
202
203
const int old_opterr = ::opterr;
204
::opterr = 0;
205
206
bool got_colon = false;
207
208
int opt;
209
while ((opt = ::getopt_long(argc, argv, "+:", long_options, NULL)) != -1) {
210
switch (opt) {
211
case '1': break;
212
case '2': break;
213
case ':': got_colon = true; break;
214
case '?': break;
215
default: UNREACHABLE; break;
216
}
217
}
218
219
::opterr = old_opterr;
220
::optind = 1;
221
#if defined(HAVE_GETOPT_WITH_OPTRESET)
222
::optreset = 1;
223
#endif
224
225
for (char** arg = &argv[0]; *arg != NULL; arg++)
226
std::free(*arg);
227
228
return !got_colon;
229
}
230
231
232
} // anonymous namespace
233
234
235
ATF_TEST_CASE_WITHOUT_HEAD(progname__no_options);
236
ATF_TEST_CASE_BODY(progname__no_options)
237
{
238
const int argc = 1;
239
const char* const argv[] = {"progname", NULL};
240
std::vector< const base_option* > options;
241
const parsed_cmdline cmdline = parse(argc, argv, options);
242
243
ATF_REQUIRE(cmdline.arguments().empty());
244
}
245
246
247
ATF_TEST_CASE_WITHOUT_HEAD(progname__some_options);
248
ATF_TEST_CASE_BODY(progname__some_options)
249
{
250
const int argc = 1;
251
const char* const argv[] = {"progname", NULL};
252
const string_option a('a', "a_option", "Foo", NULL);
253
const string_option b('b', "b_option", "Bar", "arg", "foo");
254
const string_option c("c_option", "Baz", NULL);
255
const string_option d("d_option", "Wohoo", "arg", "bar");
256
std::vector< const base_option* > options;
257
options.push_back(&a);
258
options.push_back(&b);
259
options.push_back(&c);
260
options.push_back(&d);
261
const parsed_cmdline cmdline = parse(argc, argv, options);
262
263
ATF_REQUIRE_EQ("foo", cmdline.get_option< string_option >("b_option"));
264
ATF_REQUIRE_EQ("bar", cmdline.get_option< string_option >("d_option"));
265
ATF_REQUIRE(cmdline.arguments().empty());
266
}
267
268
269
ATF_TEST_CASE_WITHOUT_HEAD(some_args__no_options);
270
ATF_TEST_CASE_BODY(some_args__no_options)
271
{
272
const int argc = 5;
273
const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL};
274
std::vector< const base_option* > options;
275
const parsed_cmdline cmdline = parse(argc, argv, options);
276
277
ATF_REQUIRE(!cmdline.has_option("c"));
278
ATF_REQUIRE(!cmdline.has_option("opt"));
279
ATF_REQUIRE_EQ(4, cmdline.arguments().size());
280
ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]);
281
ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]);
282
ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]);
283
ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]);
284
}
285
286
287
ATF_TEST_CASE_WITHOUT_HEAD(some_args__some_options);
288
ATF_TEST_CASE_BODY(some_args__some_options)
289
{
290
const int argc = 5;
291
const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL};
292
const string_option c('c', "opt", "Description", NULL);
293
std::vector< const base_option* > options;
294
options.push_back(&c);
295
const parsed_cmdline cmdline = parse(argc, argv, options);
296
297
ATF_REQUIRE(!cmdline.has_option("c"));
298
ATF_REQUIRE(!cmdline.has_option("opt"));
299
ATF_REQUIRE_EQ(4, cmdline.arguments().size());
300
ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]);
301
ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]);
302
ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]);
303
ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]);
304
}
305
306
307
ATF_TEST_CASE_WITHOUT_HEAD(some_options__all_known);
308
ATF_TEST_CASE_BODY(some_options__all_known)
309
{
310
const int argc = 14;
311
const char* const argv[] = {
312
"progname",
313
"-a",
314
"-bvalue_b",
315
"-c", "value_c",
316
//"-d", // Options with default optional values are unsupported.
317
"-evalue_e", // Has default; overriden.
318
"--f_long",
319
"--g_long=value_g",
320
"--h_long", "value_h",
321
//"--i_long", // Options with default optional values are unsupported.
322
"--j_long", "value_j", // Has default; overriden as separate argument.
323
"arg1", "arg2", NULL,
324
};
325
const bool_option a('a', "a_long", "");
326
const string_option b('b', "b_long", "Description", "arg");
327
const string_option c('c', "c_long", "ABCD", "foo");
328
const string_option d('d', "d_long", "Description", "bar", "default_d");
329
const string_option e('e', "e_long", "Description", "baz", "default_e");
330
const bool_option f("f_long", "Description");
331
const string_option g("g_long", "Description", "arg");
332
const string_option h("h_long", "Description", "foo");
333
const string_option i("i_long", "EFGH", "bar", "default_i");
334
const string_option j("j_long", "Description", "baz", "default_j");
335
std::vector< const base_option* > options;
336
options.push_back(&a);
337
options.push_back(&b);
338
options.push_back(&c);
339
options.push_back(&d);
340
options.push_back(&e);
341
options.push_back(&f);
342
options.push_back(&g);
343
options.push_back(&h);
344
options.push_back(&i);
345
options.push_back(&j);
346
const parsed_cmdline cmdline = parse(argc, argv, options);
347
348
ATF_REQUIRE(cmdline.has_option("a_long"));
349
ATF_REQUIRE_EQ("value_b", cmdline.get_option< string_option >("b_long"));
350
ATF_REQUIRE_EQ("value_c", cmdline.get_option< string_option >("c_long"));
351
ATF_REQUIRE_EQ("default_d", cmdline.get_option< string_option >("d_long"));
352
ATF_REQUIRE_EQ("value_e", cmdline.get_option< string_option >("e_long"));
353
ATF_REQUIRE(cmdline.has_option("f_long"));
354
ATF_REQUIRE_EQ("value_g", cmdline.get_option< string_option >("g_long"));
355
ATF_REQUIRE_EQ("value_h", cmdline.get_option< string_option >("h_long"));
356
ATF_REQUIRE_EQ("default_i", cmdline.get_option< string_option >("i_long"));
357
ATF_REQUIRE_EQ("value_j", cmdline.get_option< string_option >("j_long"));
358
ATF_REQUIRE_EQ(2, cmdline.arguments().size());
359
ATF_REQUIRE_EQ("arg1", cmdline.arguments()[0]);
360
ATF_REQUIRE_EQ("arg2", cmdline.arguments()[1]);
361
}
362
363
364
ATF_TEST_CASE_WITHOUT_HEAD(some_options__multi);
365
ATF_TEST_CASE_BODY(some_options__multi)
366
{
367
const int argc = 9;
368
const char* const argv[] = {
369
"progname",
370
"-a1",
371
"-bvalue1",
372
"-a2",
373
"--a_long=3",
374
"-bvalue2",
375
"--b_long=value3",
376
"arg1", "arg2", NULL,
377
};
378
const int_option a('a', "a_long", "Description", "arg");
379
const string_option b('b', "b_long", "Description", "arg");
380
std::vector< const base_option* > options;
381
options.push_back(&a);
382
options.push_back(&b);
383
const parsed_cmdline cmdline = parse(argc, argv, options);
384
385
{
386
ATF_REQUIRE_EQ(3, cmdline.get_option< int_option >("a_long"));
387
const std::vector< int > multi =
388
cmdline.get_multi_option< int_option >("a_long");
389
ATF_REQUIRE_EQ(3, multi.size());
390
ATF_REQUIRE_EQ(1, multi[0]);
391
ATF_REQUIRE_EQ(2, multi[1]);
392
ATF_REQUIRE_EQ(3, multi[2]);
393
}
394
395
{
396
ATF_REQUIRE_EQ("value3", cmdline.get_option< string_option >("b_long"));
397
const std::vector< std::string > multi =
398
cmdline.get_multi_option< string_option >("b_long");
399
ATF_REQUIRE_EQ(3, multi.size());
400
ATF_REQUIRE_EQ("value1", multi[0]);
401
ATF_REQUIRE_EQ("value2", multi[1]);
402
ATF_REQUIRE_EQ("value3", multi[2]);
403
}
404
}
405
406
407
ATF_TEST_CASE_WITHOUT_HEAD(subcommands);
408
ATF_TEST_CASE_BODY(subcommands)
409
{
410
const int argc = 5;
411
const char* const argv[] = {"progname", "--flag1", "subcommand",
412
"--flag2", "arg", NULL};
413
const bool_option flag1("flag1", "");
414
std::vector< const base_option* > options;
415
options.push_back(&flag1);
416
const parsed_cmdline cmdline = parse(argc, argv, options);
417
418
ATF_REQUIRE( cmdline.has_option("flag1"));
419
ATF_REQUIRE(!cmdline.has_option("flag2"));
420
ATF_REQUIRE_EQ(3, cmdline.arguments().size());
421
ATF_REQUIRE_EQ("subcommand", cmdline.arguments()[0]);
422
ATF_REQUIRE_EQ("--flag2", cmdline.arguments()[1]);
423
ATF_REQUIRE_EQ("arg", cmdline.arguments()[2]);
424
425
const bool_option flag2("flag2", "");
426
std::vector< const base_option* > options2;
427
options2.push_back(&flag2);
428
const parsed_cmdline cmdline2 = parse(cmdline.arguments(), options2);
429
430
ATF_REQUIRE(!cmdline2.has_option("flag1"));
431
ATF_REQUIRE( cmdline2.has_option("flag2"));
432
ATF_REQUIRE_EQ(1, cmdline2.arguments().size());
433
ATF_REQUIRE_EQ("arg", cmdline2.arguments()[0]);
434
}
435
436
437
ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__short);
438
ATF_TEST_CASE_BODY(missing_option_argument_error__short)
439
{
440
const int argc = 3;
441
const char* const argv[] = {"progname", "-a3", "-b", NULL};
442
const string_option flag1('a', "flag1", "Description", "arg");
443
const string_option flag2('b', "flag2", "Description", "arg");
444
std::vector< const base_option* > options;
445
options.push_back(&flag1);
446
options.push_back(&flag2);
447
448
try {
449
parse(argc, argv, options);
450
fail("missing_option_argument_error not raised");
451
} catch (const cmdline::missing_option_argument_error& e) {
452
ATF_REQUIRE_EQ("-b", e.option());
453
} catch (const cmdline::unknown_option_error& e) {
454
if (is_getopt_long_pluscolon_broken())
455
expect_fail("Your getopt_long is broken");
456
fail("Got unknown_option_error instead of "
457
"missing_option_argument_error");
458
}
459
}
460
461
462
ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__shortblock);
463
ATF_TEST_CASE_BODY(missing_option_argument_error__shortblock)
464
{
465
const int argc = 3;
466
const char* const argv[] = {"progname", "-ab3", "-ac", NULL};
467
const bool_option flag1('a', "flag1", "Description");
468
const string_option flag2('b', "flag2", "Description", "arg");
469
const string_option flag3('c', "flag2", "Description", "arg");
470
std::vector< const base_option* > options;
471
options.push_back(&flag1);
472
options.push_back(&flag2);
473
options.push_back(&flag3);
474
475
try {
476
parse(argc, argv, options);
477
fail("missing_option_argument_error not raised");
478
} catch (const cmdline::missing_option_argument_error& e) {
479
ATF_REQUIRE_EQ("-c", e.option());
480
} catch (const cmdline::unknown_option_error& e) {
481
if (is_getopt_long_pluscolon_broken())
482
expect_fail("Your getopt_long is broken");
483
fail("Got unknown_option_error instead of "
484
"missing_option_argument_error");
485
}
486
}
487
488
489
ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__long);
490
ATF_TEST_CASE_BODY(missing_option_argument_error__long)
491
{
492
const int argc = 3;
493
const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL};
494
const string_option flag1("flag1", "Description", "arg");
495
const string_option flag2("flag2", "Description", "arg");
496
std::vector< const base_option* > options;
497
options.push_back(&flag1);
498
options.push_back(&flag2);
499
500
try {
501
parse(argc, argv, options);
502
fail("missing_option_argument_error not raised");
503
} catch (const cmdline::missing_option_argument_error& e) {
504
ATF_REQUIRE_EQ("--flag2", e.option());
505
} catch (const cmdline::unknown_option_error& e) {
506
if (is_getopt_long_pluscolon_broken())
507
expect_fail("Your getopt_long is broken");
508
fail("Got unknown_option_error instead of "
509
"missing_option_argument_error");
510
}
511
}
512
513
514
ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__short);
515
ATF_TEST_CASE_BODY(unknown_option_error__short)
516
{
517
const int argc = 3;
518
const char* const argv[] = {"progname", "-a", "-b", NULL};
519
const bool_option flag1('a', "flag1", "Description");
520
std::vector< const base_option* > options;
521
options.push_back(&flag1);
522
523
try {
524
parse(argc, argv, options);
525
fail("unknown_option_error not raised");
526
} catch (const cmdline::unknown_option_error& e) {
527
ATF_REQUIRE_EQ("-b", e.option());
528
}
529
}
530
531
532
ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__shortblock);
533
ATF_TEST_CASE_BODY(unknown_option_error__shortblock)
534
{
535
const int argc = 3;
536
const char* const argv[] = {"progname", "-a", "-bdc", NULL};
537
const bool_option flag1('a', "flag1", "Description");
538
const bool_option flag2('b', "flag2", "Description");
539
const bool_option flag3('c', "flag3", "Description");
540
std::vector< const base_option* > options;
541
options.push_back(&flag1);
542
options.push_back(&flag2);
543
options.push_back(&flag3);
544
545
try {
546
parse(argc, argv, options);
547
fail("unknown_option_error not raised");
548
} catch (const cmdline::unknown_option_error& e) {
549
ATF_REQUIRE_EQ("-d", e.option());
550
}
551
}
552
553
554
ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__long);
555
ATF_TEST_CASE_BODY(unknown_option_error__long)
556
{
557
const int argc = 3;
558
const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL};
559
const string_option flag1("flag1", "Description", "arg");
560
std::vector< const base_option* > options;
561
options.push_back(&flag1);
562
563
try {
564
parse(argc, argv, options);
565
fail("unknown_option_error not raised");
566
} catch (const cmdline::unknown_option_error& e) {
567
ATF_REQUIRE_EQ("--flag2", e.option());
568
}
569
}
570
571
572
ATF_TEST_CASE_WITHOUT_HEAD(unknown_plus_option_error);
573
ATF_TEST_CASE_BODY(unknown_plus_option_error)
574
{
575
const int argc = 2;
576
const char* const argv[] = {"progname", "-+", NULL};
577
const cmdline::options_vector options;
578
579
try {
580
parse(argc, argv, options);
581
fail("unknown_option_error not raised");
582
} catch (const cmdline::unknown_option_error& e) {
583
ATF_REQUIRE_EQ("-+", e.option());
584
} catch (const cmdline::missing_option_argument_error& e) {
585
fail("Looks like getopt_long thinks a + option is defined and it "
586
"even requires an argument");
587
}
588
}
589
590
591
ATF_TEST_CASE_WITHOUT_HEAD(option_types);
592
ATF_TEST_CASE_BODY(option_types)
593
{
594
const int argc = 3;
595
const char* const argv[] = {"progname", "--flag1=a", "--flag2=one", NULL};
596
const string_option flag1("flag1", "The flag1", "arg");
597
const mock_option flag2("flag2");
598
std::vector< const base_option* > options;
599
options.push_back(&flag1);
600
options.push_back(&flag2);
601
602
const parsed_cmdline cmdline = parse(argc, argv, options);
603
604
ATF_REQUIRE(cmdline.has_option("flag1"));
605
ATF_REQUIRE(cmdline.has_option("flag2"));
606
ATF_REQUIRE_EQ("a", cmdline.get_option< string_option >("flag1"));
607
ATF_REQUIRE_EQ(1, cmdline.get_option< mock_option >("flag2"));
608
}
609
610
611
ATF_TEST_CASE_WITHOUT_HEAD(option_validation_error);
612
ATF_TEST_CASE_BODY(option_validation_error)
613
{
614
const int argc = 3;
615
const char* const argv[] = {"progname", "--flag1=zero", "--flag2=foo",
616
NULL};
617
const mock_option flag1("flag1");
618
const mock_option flag2("flag2");
619
std::vector< const base_option* > options;
620
options.push_back(&flag1);
621
options.push_back(&flag2);
622
623
try {
624
parse(argc, argv, options);
625
fail("option_argument_value_error not raised");
626
} catch (const cmdline::option_argument_value_error& e) {
627
ATF_REQUIRE_EQ("--flag2", e.option());
628
ATF_REQUIRE_EQ("foo", e.argument());
629
}
630
}
631
632
633
ATF_TEST_CASE_WITHOUT_HEAD(silent_errors);
634
ATF_TEST_CASE_BODY(silent_errors)
635
{
636
const int argc = 2;
637
const char* const argv[] = {"progname", "-h", NULL};
638
cmdline::options_vector options;
639
640
try {
641
std::pair< int, int > oldfds = mock_stdfds("output.txt");
642
try {
643
parse(argc, argv, options);
644
} catch (...) {
645
restore_stdfds(oldfds);
646
throw;
647
}
648
restore_stdfds(oldfds);
649
fail("unknown_option_error not raised");
650
} catch (const cmdline::unknown_option_error& e) {
651
ATF_REQUIRE_EQ("-h", e.option());
652
}
653
654
std::ifstream input("output.txt");
655
ATF_REQUIRE(input);
656
657
bool has_output = false;
658
std::string line;
659
while (std::getline(input, line).good()) {
660
std::cout << line << '\n';
661
has_output = true;
662
}
663
664
if (has_output)
665
fail("getopt_long printed messages on stdout/stderr by itself");
666
}
667
668
669
ATF_INIT_TEST_CASES(tcs)
670
{
671
ATF_ADD_TEST_CASE(tcs, progname__no_options);
672
ATF_ADD_TEST_CASE(tcs, progname__some_options);
673
ATF_ADD_TEST_CASE(tcs, some_args__no_options);
674
ATF_ADD_TEST_CASE(tcs, some_args__some_options);
675
ATF_ADD_TEST_CASE(tcs, some_options__all_known);
676
ATF_ADD_TEST_CASE(tcs, some_options__multi);
677
ATF_ADD_TEST_CASE(tcs, subcommands);
678
ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__short);
679
ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__shortblock);
680
ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__long);
681
ATF_ADD_TEST_CASE(tcs, unknown_option_error__short);
682
ATF_ADD_TEST_CASE(tcs, unknown_option_error__shortblock);
683
ATF_ADD_TEST_CASE(tcs, unknown_option_error__long);
684
ATF_ADD_TEST_CASE(tcs, unknown_plus_option_error);
685
ATF_ADD_TEST_CASE(tcs, option_types);
686
ATF_ADD_TEST_CASE(tcs, option_validation_error);
687
ATF_ADD_TEST_CASE(tcs, silent_errors);
688
}
689
690