xref: /freebsd/contrib/kyua/utils/cmdline/parser_test.cpp (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
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