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
35extern "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
55namespace cmdline = utils::cmdline;
56
57using cmdline::base_option;
58using cmdline::bool_option;
59using cmdline::int_option;
60using cmdline::parse;
61using cmdline::parsed_cmdline;
62using cmdline::string_option;
63
64
65namespace {
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.
72class mock_option : public base_option {
73public:
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.
135static std::pair< int, int >
136mock_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().
160static void
161restore_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.
187static bool
188is_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
235ATF_TEST_CASE_WITHOUT_HEAD(progname__no_options);
236ATF_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
247ATF_TEST_CASE_WITHOUT_HEAD(progname__some_options);
248ATF_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
269ATF_TEST_CASE_WITHOUT_HEAD(some_args__no_options);
270ATF_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
287ATF_TEST_CASE_WITHOUT_HEAD(some_args__some_options);
288ATF_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
307ATF_TEST_CASE_WITHOUT_HEAD(some_options__all_known);
308ATF_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
364ATF_TEST_CASE_WITHOUT_HEAD(some_options__multi);
365ATF_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
407ATF_TEST_CASE_WITHOUT_HEAD(subcommands);
408ATF_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
437ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__short);
438ATF_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
462ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__shortblock);
463ATF_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
489ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__long);
490ATF_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
514ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__short);
515ATF_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
532ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__shortblock);
533ATF_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
554ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__long);
555ATF_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
572ATF_TEST_CASE_WITHOUT_HEAD(unknown_plus_option_error);
573ATF_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
591ATF_TEST_CASE_WITHOUT_HEAD(option_types);
592ATF_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
611ATF_TEST_CASE_WITHOUT_HEAD(option_validation_error);
612ATF_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
633ATF_TEST_CASE_WITHOUT_HEAD(silent_errors);
634ATF_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
669ATF_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