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.
mock_option(const char * long_name_)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
validate(const std::string & str) const93 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
convert(const std::string & str)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 >
mock_stdfds(const char * file)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
restore_stdfds(const std::pair<int,int> & oldfds)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
is_getopt_long_pluscolon_broken(void)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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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);
ATF_TEST_CASE_BODY(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
ATF_INIT_TEST_CASES(tcs)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