108334c51SBrooks Davis // Copyright 2010 The Kyua Authors.
208334c51SBrooks Davis // All rights reserved.
308334c51SBrooks Davis //
408334c51SBrooks Davis // Redistribution and use in source and binary forms, with or without
508334c51SBrooks Davis // modification, are permitted provided that the following conditions are
608334c51SBrooks Davis // met:
708334c51SBrooks Davis //
808334c51SBrooks Davis // * Redistributions of source code must retain the above copyright
908334c51SBrooks Davis // notice, this list of conditions and the following disclaimer.
1008334c51SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
1108334c51SBrooks Davis // notice, this list of conditions and the following disclaimer in the
1208334c51SBrooks Davis // documentation and/or other materials provided with the distribution.
1308334c51SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
1408334c51SBrooks Davis // may be used to endorse or promote products derived from this software
1508334c51SBrooks Davis // without specific prior written permission.
1608334c51SBrooks Davis //
1708334c51SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1808334c51SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1908334c51SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2008334c51SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2108334c51SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2208334c51SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2308334c51SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2408334c51SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2508334c51SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2608334c51SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2708334c51SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2808334c51SBrooks Davis
2908334c51SBrooks Davis #include "utils/cmdline/parser.ipp"
3008334c51SBrooks Davis
3108334c51SBrooks Davis #if defined(HAVE_CONFIG_H)
3208334c51SBrooks Davis #include "config.h"
3308334c51SBrooks Davis #endif
3408334c51SBrooks Davis
3508334c51SBrooks Davis extern "C" {
3608334c51SBrooks Davis #include <fcntl.h>
3708334c51SBrooks Davis #include <getopt.h>
3808334c51SBrooks Davis #include <unistd.h>
3908334c51SBrooks Davis }
4008334c51SBrooks Davis
4108334c51SBrooks Davis #include <cstdlib>
4208334c51SBrooks Davis #include <cstring>
4308334c51SBrooks Davis #include <fstream>
4408334c51SBrooks Davis #include <iostream>
4508334c51SBrooks Davis #include <string>
4608334c51SBrooks Davis #include <utility>
4708334c51SBrooks Davis
4808334c51SBrooks Davis #include <atf-c++.hpp>
4908334c51SBrooks Davis
5008334c51SBrooks Davis #include "utils/cmdline/exceptions.hpp"
5108334c51SBrooks Davis #include "utils/cmdline/options.hpp"
5208334c51SBrooks Davis #include "utils/format/macros.hpp"
5308334c51SBrooks Davis #include "utils/sanity.hpp"
5408334c51SBrooks Davis
5508334c51SBrooks Davis namespace cmdline = utils::cmdline;
5608334c51SBrooks Davis
5708334c51SBrooks Davis using cmdline::base_option;
5808334c51SBrooks Davis using cmdline::bool_option;
5908334c51SBrooks Davis using cmdline::int_option;
6008334c51SBrooks Davis using cmdline::parse;
6108334c51SBrooks Davis using cmdline::parsed_cmdline;
6208334c51SBrooks Davis using cmdline::string_option;
6308334c51SBrooks Davis
6408334c51SBrooks Davis
6508334c51SBrooks Davis namespace {
6608334c51SBrooks Davis
6708334c51SBrooks Davis
6808334c51SBrooks Davis /// Mock option type to check the validate and convert methods sequence.
6908334c51SBrooks Davis ///
7008334c51SBrooks Davis /// Instances of this option accept a string argument that must be either "zero"
7108334c51SBrooks Davis /// or "one". These are validated and converted to integers.
7208334c51SBrooks Davis class mock_option : public base_option {
7308334c51SBrooks Davis public:
7408334c51SBrooks Davis /// Constructs the new option.
7508334c51SBrooks Davis ///
7608334c51SBrooks Davis /// \param long_name_ The long name for the option. All other option
7708334c51SBrooks Davis /// properties are irrelevant for the tests using this, so they are set
7808334c51SBrooks Davis /// to arbitrary values.
mock_option(const char * long_name_)7908334c51SBrooks Davis mock_option(const char* long_name_) :
8008334c51SBrooks Davis base_option(long_name_, "Irrelevant description", "arg")
8108334c51SBrooks Davis {
8208334c51SBrooks Davis }
8308334c51SBrooks Davis
8408334c51SBrooks Davis /// The type of the argument of this option.
8508334c51SBrooks Davis typedef int option_type;
8608334c51SBrooks Davis
8708334c51SBrooks Davis /// Checks that the user-provided option is valid.
8808334c51SBrooks Davis ///
8908334c51SBrooks Davis /// \param str The user argument; must be "zero" or "one".
9008334c51SBrooks Davis ///
9108334c51SBrooks Davis /// \throw cmdline::option_argument_value_error If str is not valid.
9208334c51SBrooks Davis void
validate(const std::string & str) const9308334c51SBrooks Davis validate(const std::string& str) const
9408334c51SBrooks Davis {
9508334c51SBrooks Davis if (str != "zero" && str != "one")
9608334c51SBrooks Davis throw cmdline::option_argument_value_error(F("--%s") % long_name(),
9708334c51SBrooks Davis str, "Unknown value");
9808334c51SBrooks Davis }
9908334c51SBrooks Davis
10008334c51SBrooks Davis /// Converts the user-provided argument to our native integer type.
10108334c51SBrooks Davis ///
10208334c51SBrooks Davis /// \param str The user argument; must be "zero" or "one".
10308334c51SBrooks Davis ///
10408334c51SBrooks Davis /// \return 0 if the input is "zero", or 1 if the input is "one".
10508334c51SBrooks Davis ///
10608334c51SBrooks Davis /// \throw std::runtime_error If str is not valid. In real life, this
10708334c51SBrooks Davis /// should be a precondition because validate() has already ensured that
10808334c51SBrooks Davis /// the values passed to convert() are correct. However, we raise an
10908334c51SBrooks Davis /// exception here because we are actually validating that this code
11008334c51SBrooks Davis /// sequence holds true.
11108334c51SBrooks Davis static int
convert(const std::string & str)11208334c51SBrooks Davis convert(const std::string& str)
11308334c51SBrooks Davis {
11408334c51SBrooks Davis if (str == "zero")
11508334c51SBrooks Davis return 0;
11608334c51SBrooks Davis else if (str == "one")
11708334c51SBrooks Davis return 1;
11808334c51SBrooks Davis else {
11908334c51SBrooks Davis // This would generally be an assertion but, given that this is
12008334c51SBrooks Davis // test code, we want to catch any errors regardless of how the
12108334c51SBrooks Davis // binary is built.
12208334c51SBrooks Davis throw std::runtime_error("Value not validated properly.");
12308334c51SBrooks Davis }
12408334c51SBrooks Davis }
12508334c51SBrooks Davis };
12608334c51SBrooks Davis
12708334c51SBrooks Davis
12808334c51SBrooks Davis /// Redirects stdout and stderr to a file.
12908334c51SBrooks Davis ///
13008334c51SBrooks Davis /// This fails the test case in case of any error.
13108334c51SBrooks Davis ///
13208334c51SBrooks Davis /// \param file The name of the file to redirect stdout and stderr to.
13308334c51SBrooks Davis ///
13408334c51SBrooks Davis /// \return A copy of the old stdout and stderr file descriptors.
13508334c51SBrooks Davis static std::pair< int, int >
mock_stdfds(const char * file)13608334c51SBrooks Davis mock_stdfds(const char* file)
13708334c51SBrooks Davis {
13808334c51SBrooks Davis std::cout.flush();
13908334c51SBrooks Davis std::cerr.flush();
14008334c51SBrooks Davis
14108334c51SBrooks Davis const int oldout = ::dup(STDOUT_FILENO);
14208334c51SBrooks Davis ATF_REQUIRE(oldout != -1);
14308334c51SBrooks Davis const int olderr = ::dup(STDERR_FILENO);
14408334c51SBrooks Davis ATF_REQUIRE(olderr != -1);
14508334c51SBrooks Davis
14608334c51SBrooks Davis const int fd = ::open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
14708334c51SBrooks Davis ATF_REQUIRE(fd != -1);
14808334c51SBrooks Davis ATF_REQUIRE(::dup2(fd, STDOUT_FILENO) != -1);
14908334c51SBrooks Davis ATF_REQUIRE(::dup2(fd, STDERR_FILENO) != -1);
15008334c51SBrooks Davis ::close(fd);
15108334c51SBrooks Davis
15208334c51SBrooks Davis return std::make_pair(oldout, olderr);
15308334c51SBrooks Davis }
15408334c51SBrooks Davis
15508334c51SBrooks Davis
15608334c51SBrooks Davis /// Restores stdout and stderr after a call to mock_stdfds.
15708334c51SBrooks Davis ///
15808334c51SBrooks Davis /// \param oldfds The copy of the previous stdout and stderr as returned by the
15908334c51SBrooks Davis /// call to mock_fds().
16008334c51SBrooks Davis static void
restore_stdfds(const std::pair<int,int> & oldfds)16108334c51SBrooks Davis restore_stdfds(const std::pair< int, int >& oldfds)
16208334c51SBrooks Davis {
16308334c51SBrooks Davis ATF_REQUIRE(::dup2(oldfds.first, STDOUT_FILENO) != -1);
16408334c51SBrooks Davis ::close(oldfds.first);
16508334c51SBrooks Davis ATF_REQUIRE(::dup2(oldfds.second, STDERR_FILENO) != -1);
16608334c51SBrooks Davis ::close(oldfds.second);
16708334c51SBrooks Davis }
16808334c51SBrooks Davis
16908334c51SBrooks Davis
17008334c51SBrooks Davis /// Checks whether a '+:' prefix to the short options of getopt_long works.
17108334c51SBrooks Davis ///
17208334c51SBrooks Davis /// It turns out that the getopt_long(3) implementation of Ubuntu 10.04.1 (and
17308334c51SBrooks Davis /// very likely other distributions) does not properly report a missing argument
17408334c51SBrooks Davis /// to a second long option as such. Instead of returning ':' when the second
17508334c51SBrooks Davis /// long option provided on the command line does not carry a required argument,
17608334c51SBrooks Davis /// it will mistakenly return '?' which translates to "unknown option".
17708334c51SBrooks Davis ///
17808334c51SBrooks Davis /// As a result of this bug, we cannot properly detect that 'flag2' requires an
17908334c51SBrooks Davis /// argument in a command line like: 'progname --flag1=foo --flag2'.
18008334c51SBrooks Davis ///
18108334c51SBrooks Davis /// I am not sure if we could fully workaround the issue in the implementation
18208334c51SBrooks Davis /// of our library. For the time being I am just using this bug detection in
18308334c51SBrooks Davis /// the test cases to prevent failures that are not really our fault.
18408334c51SBrooks Davis ///
18508334c51SBrooks Davis /// \return bool True if getopt_long is broken and does not interpret '+:'
18608334c51SBrooks Davis /// correctly; False otherwise.
18708334c51SBrooks Davis static bool
is_getopt_long_pluscolon_broken(void)18808334c51SBrooks Davis is_getopt_long_pluscolon_broken(void)
18908334c51SBrooks Davis {
19008334c51SBrooks Davis struct ::option long_options[] = {
19108334c51SBrooks Davis { "flag1", 1, NULL, '1' },
19208334c51SBrooks Davis { "flag2", 1, NULL, '2' },
19308334c51SBrooks Davis { NULL, 0, NULL, 0 }
19408334c51SBrooks Davis };
19508334c51SBrooks Davis
19608334c51SBrooks Davis const int argc = 3;
19708334c51SBrooks Davis char* argv[4];
19808334c51SBrooks Davis argv[0] = ::strdup("progname");
19908334c51SBrooks Davis argv[1] = ::strdup("--flag1=a");
20008334c51SBrooks Davis argv[2] = ::strdup("--flag2");
20108334c51SBrooks Davis argv[3] = NULL;
20208334c51SBrooks Davis
20308334c51SBrooks Davis const int old_opterr = ::opterr;
20408334c51SBrooks Davis ::opterr = 0;
20508334c51SBrooks Davis
20608334c51SBrooks Davis bool got_colon = false;
20708334c51SBrooks Davis
20808334c51SBrooks Davis int opt;
20908334c51SBrooks Davis while ((opt = ::getopt_long(argc, argv, "+:", long_options, NULL)) != -1) {
21008334c51SBrooks Davis switch (opt) {
21108334c51SBrooks Davis case '1': break;
21208334c51SBrooks Davis case '2': break;
21308334c51SBrooks Davis case ':': got_colon = true; break;
21408334c51SBrooks Davis case '?': break;
21508334c51SBrooks Davis default: UNREACHABLE; break;
21608334c51SBrooks Davis }
21708334c51SBrooks Davis }
21808334c51SBrooks Davis
21908334c51SBrooks Davis ::opterr = old_opterr;
22008334c51SBrooks Davis ::optind = 1;
22108334c51SBrooks Davis #if defined(HAVE_GETOPT_WITH_OPTRESET)
22208334c51SBrooks Davis ::optreset = 1;
22308334c51SBrooks Davis #endif
22408334c51SBrooks Davis
22508334c51SBrooks Davis for (char** arg = &argv[0]; *arg != NULL; arg++)
22608334c51SBrooks Davis std::free(*arg);
22708334c51SBrooks Davis
22808334c51SBrooks Davis return !got_colon;
22908334c51SBrooks Davis }
23008334c51SBrooks Davis
23108334c51SBrooks Davis
23208334c51SBrooks Davis } // anonymous namespace
23308334c51SBrooks Davis
23408334c51SBrooks Davis
23508334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(progname__no_options);
ATF_TEST_CASE_BODY(progname__no_options)23608334c51SBrooks Davis ATF_TEST_CASE_BODY(progname__no_options)
23708334c51SBrooks Davis {
23808334c51SBrooks Davis const int argc = 1;
23908334c51SBrooks Davis const char* const argv[] = {"progname", NULL};
24008334c51SBrooks Davis std::vector< const base_option* > options;
24108334c51SBrooks Davis const parsed_cmdline cmdline = parse(argc, argv, options);
24208334c51SBrooks Davis
24308334c51SBrooks Davis ATF_REQUIRE(cmdline.arguments().empty());
24408334c51SBrooks Davis }
24508334c51SBrooks Davis
24608334c51SBrooks Davis
24708334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(progname__some_options);
ATF_TEST_CASE_BODY(progname__some_options)24808334c51SBrooks Davis ATF_TEST_CASE_BODY(progname__some_options)
24908334c51SBrooks Davis {
25008334c51SBrooks Davis const int argc = 1;
25108334c51SBrooks Davis const char* const argv[] = {"progname", NULL};
25208334c51SBrooks Davis const string_option a('a', "a_option", "Foo", NULL);
25308334c51SBrooks Davis const string_option b('b', "b_option", "Bar", "arg", "foo");
25408334c51SBrooks Davis const string_option c("c_option", "Baz", NULL);
25508334c51SBrooks Davis const string_option d("d_option", "Wohoo", "arg", "bar");
25608334c51SBrooks Davis std::vector< const base_option* > options;
25708334c51SBrooks Davis options.push_back(&a);
25808334c51SBrooks Davis options.push_back(&b);
25908334c51SBrooks Davis options.push_back(&c);
26008334c51SBrooks Davis options.push_back(&d);
26108334c51SBrooks Davis const parsed_cmdline cmdline = parse(argc, argv, options);
26208334c51SBrooks Davis
26308334c51SBrooks Davis ATF_REQUIRE_EQ("foo", cmdline.get_option< string_option >("b_option"));
26408334c51SBrooks Davis ATF_REQUIRE_EQ("bar", cmdline.get_option< string_option >("d_option"));
26508334c51SBrooks Davis ATF_REQUIRE(cmdline.arguments().empty());
26608334c51SBrooks Davis }
26708334c51SBrooks Davis
26808334c51SBrooks Davis
26908334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(some_args__no_options);
ATF_TEST_CASE_BODY(some_args__no_options)27008334c51SBrooks Davis ATF_TEST_CASE_BODY(some_args__no_options)
27108334c51SBrooks Davis {
27208334c51SBrooks Davis const int argc = 5;
27308334c51SBrooks Davis const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL};
27408334c51SBrooks Davis std::vector< const base_option* > options;
27508334c51SBrooks Davis const parsed_cmdline cmdline = parse(argc, argv, options);
27608334c51SBrooks Davis
27708334c51SBrooks Davis ATF_REQUIRE(!cmdline.has_option("c"));
27808334c51SBrooks Davis ATF_REQUIRE(!cmdline.has_option("opt"));
27908334c51SBrooks Davis ATF_REQUIRE_EQ(4, cmdline.arguments().size());
28008334c51SBrooks Davis ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]);
28108334c51SBrooks Davis ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]);
28208334c51SBrooks Davis ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]);
28308334c51SBrooks Davis ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]);
28408334c51SBrooks Davis }
28508334c51SBrooks Davis
28608334c51SBrooks Davis
28708334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(some_args__some_options);
ATF_TEST_CASE_BODY(some_args__some_options)28808334c51SBrooks Davis ATF_TEST_CASE_BODY(some_args__some_options)
28908334c51SBrooks Davis {
29008334c51SBrooks Davis const int argc = 5;
29108334c51SBrooks Davis const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL};
29208334c51SBrooks Davis const string_option c('c', "opt", "Description", NULL);
29308334c51SBrooks Davis std::vector< const base_option* > options;
29408334c51SBrooks Davis options.push_back(&c);
29508334c51SBrooks Davis const parsed_cmdline cmdline = parse(argc, argv, options);
29608334c51SBrooks Davis
29708334c51SBrooks Davis ATF_REQUIRE(!cmdline.has_option("c"));
29808334c51SBrooks Davis ATF_REQUIRE(!cmdline.has_option("opt"));
29908334c51SBrooks Davis ATF_REQUIRE_EQ(4, cmdline.arguments().size());
30008334c51SBrooks Davis ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]);
30108334c51SBrooks Davis ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]);
30208334c51SBrooks Davis ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]);
30308334c51SBrooks Davis ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]);
30408334c51SBrooks Davis }
30508334c51SBrooks Davis
30608334c51SBrooks Davis
30708334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(some_options__all_known);
ATF_TEST_CASE_BODY(some_options__all_known)30808334c51SBrooks Davis ATF_TEST_CASE_BODY(some_options__all_known)
30908334c51SBrooks Davis {
31008334c51SBrooks Davis const int argc = 14;
31108334c51SBrooks Davis const char* const argv[] = {
31208334c51SBrooks Davis "progname",
31308334c51SBrooks Davis "-a",
31408334c51SBrooks Davis "-bvalue_b",
31508334c51SBrooks Davis "-c", "value_c",
31608334c51SBrooks Davis //"-d", // Options with default optional values are unsupported.
31708334c51SBrooks Davis "-evalue_e", // Has default; overriden.
31808334c51SBrooks Davis "--f_long",
31908334c51SBrooks Davis "--g_long=value_g",
32008334c51SBrooks Davis "--h_long", "value_h",
32108334c51SBrooks Davis //"--i_long", // Options with default optional values are unsupported.
32208334c51SBrooks Davis "--j_long", "value_j", // Has default; overriden as separate argument.
32308334c51SBrooks Davis "arg1", "arg2", NULL,
32408334c51SBrooks Davis };
32508334c51SBrooks Davis const bool_option a('a', "a_long", "");
32608334c51SBrooks Davis const string_option b('b', "b_long", "Description", "arg");
32708334c51SBrooks Davis const string_option c('c', "c_long", "ABCD", "foo");
32808334c51SBrooks Davis const string_option d('d', "d_long", "Description", "bar", "default_d");
32908334c51SBrooks Davis const string_option e('e', "e_long", "Description", "baz", "default_e");
33008334c51SBrooks Davis const bool_option f("f_long", "Description");
33108334c51SBrooks Davis const string_option g("g_long", "Description", "arg");
33208334c51SBrooks Davis const string_option h("h_long", "Description", "foo");
33308334c51SBrooks Davis const string_option i("i_long", "EFGH", "bar", "default_i");
33408334c51SBrooks Davis const string_option j("j_long", "Description", "baz", "default_j");
33508334c51SBrooks Davis std::vector< const base_option* > options;
33608334c51SBrooks Davis options.push_back(&a);
33708334c51SBrooks Davis options.push_back(&b);
33808334c51SBrooks Davis options.push_back(&c);
33908334c51SBrooks Davis options.push_back(&d);
34008334c51SBrooks Davis options.push_back(&e);
34108334c51SBrooks Davis options.push_back(&f);
34208334c51SBrooks Davis options.push_back(&g);
34308334c51SBrooks Davis options.push_back(&h);
34408334c51SBrooks Davis options.push_back(&i);
34508334c51SBrooks Davis options.push_back(&j);
34608334c51SBrooks Davis const parsed_cmdline cmdline = parse(argc, argv, options);
34708334c51SBrooks Davis
34808334c51SBrooks Davis ATF_REQUIRE(cmdline.has_option("a_long"));
34908334c51SBrooks Davis ATF_REQUIRE_EQ("value_b", cmdline.get_option< string_option >("b_long"));
35008334c51SBrooks Davis ATF_REQUIRE_EQ("value_c", cmdline.get_option< string_option >("c_long"));
35108334c51SBrooks Davis ATF_REQUIRE_EQ("default_d", cmdline.get_option< string_option >("d_long"));
35208334c51SBrooks Davis ATF_REQUIRE_EQ("value_e", cmdline.get_option< string_option >("e_long"));
35308334c51SBrooks Davis ATF_REQUIRE(cmdline.has_option("f_long"));
35408334c51SBrooks Davis ATF_REQUIRE_EQ("value_g", cmdline.get_option< string_option >("g_long"));
35508334c51SBrooks Davis ATF_REQUIRE_EQ("value_h", cmdline.get_option< string_option >("h_long"));
35608334c51SBrooks Davis ATF_REQUIRE_EQ("default_i", cmdline.get_option< string_option >("i_long"));
35708334c51SBrooks Davis ATF_REQUIRE_EQ("value_j", cmdline.get_option< string_option >("j_long"));
35808334c51SBrooks Davis ATF_REQUIRE_EQ(2, cmdline.arguments().size());
35908334c51SBrooks Davis ATF_REQUIRE_EQ("arg1", cmdline.arguments()[0]);
36008334c51SBrooks Davis ATF_REQUIRE_EQ("arg2", cmdline.arguments()[1]);
36108334c51SBrooks Davis }
36208334c51SBrooks Davis
36308334c51SBrooks Davis
36408334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(some_options__multi);
ATF_TEST_CASE_BODY(some_options__multi)36508334c51SBrooks Davis ATF_TEST_CASE_BODY(some_options__multi)
36608334c51SBrooks Davis {
36708334c51SBrooks Davis const int argc = 9;
36808334c51SBrooks Davis const char* const argv[] = {
36908334c51SBrooks Davis "progname",
37008334c51SBrooks Davis "-a1",
37108334c51SBrooks Davis "-bvalue1",
37208334c51SBrooks Davis "-a2",
37308334c51SBrooks Davis "--a_long=3",
37408334c51SBrooks Davis "-bvalue2",
37508334c51SBrooks Davis "--b_long=value3",
37608334c51SBrooks Davis "arg1", "arg2", NULL,
37708334c51SBrooks Davis };
37808334c51SBrooks Davis const int_option a('a', "a_long", "Description", "arg");
37908334c51SBrooks Davis const string_option b('b', "b_long", "Description", "arg");
38008334c51SBrooks Davis std::vector< const base_option* > options;
38108334c51SBrooks Davis options.push_back(&a);
38208334c51SBrooks Davis options.push_back(&b);
38308334c51SBrooks Davis const parsed_cmdline cmdline = parse(argc, argv, options);
38408334c51SBrooks Davis
38508334c51SBrooks Davis {
38608334c51SBrooks Davis ATF_REQUIRE_EQ(3, cmdline.get_option< int_option >("a_long"));
38708334c51SBrooks Davis const std::vector< int > multi =
38808334c51SBrooks Davis cmdline.get_multi_option< int_option >("a_long");
38908334c51SBrooks Davis ATF_REQUIRE_EQ(3, multi.size());
39008334c51SBrooks Davis ATF_REQUIRE_EQ(1, multi[0]);
39108334c51SBrooks Davis ATF_REQUIRE_EQ(2, multi[1]);
39208334c51SBrooks Davis ATF_REQUIRE_EQ(3, multi[2]);
39308334c51SBrooks Davis }
39408334c51SBrooks Davis
39508334c51SBrooks Davis {
39608334c51SBrooks Davis ATF_REQUIRE_EQ("value3", cmdline.get_option< string_option >("b_long"));
39708334c51SBrooks Davis const std::vector< std::string > multi =
39808334c51SBrooks Davis cmdline.get_multi_option< string_option >("b_long");
39908334c51SBrooks Davis ATF_REQUIRE_EQ(3, multi.size());
40008334c51SBrooks Davis ATF_REQUIRE_EQ("value1", multi[0]);
40108334c51SBrooks Davis ATF_REQUIRE_EQ("value2", multi[1]);
40208334c51SBrooks Davis ATF_REQUIRE_EQ("value3", multi[2]);
40308334c51SBrooks Davis }
40408334c51SBrooks Davis }
40508334c51SBrooks Davis
40608334c51SBrooks Davis
40708334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(subcommands);
ATF_TEST_CASE_BODY(subcommands)40808334c51SBrooks Davis ATF_TEST_CASE_BODY(subcommands)
40908334c51SBrooks Davis {
41008334c51SBrooks Davis const int argc = 5;
41108334c51SBrooks Davis const char* const argv[] = {"progname", "--flag1", "subcommand",
41208334c51SBrooks Davis "--flag2", "arg", NULL};
41308334c51SBrooks Davis const bool_option flag1("flag1", "");
41408334c51SBrooks Davis std::vector< const base_option* > options;
41508334c51SBrooks Davis options.push_back(&flag1);
41608334c51SBrooks Davis const parsed_cmdline cmdline = parse(argc, argv, options);
41708334c51SBrooks Davis
41808334c51SBrooks Davis ATF_REQUIRE( cmdline.has_option("flag1"));
41908334c51SBrooks Davis ATF_REQUIRE(!cmdline.has_option("flag2"));
42008334c51SBrooks Davis ATF_REQUIRE_EQ(3, cmdline.arguments().size());
42108334c51SBrooks Davis ATF_REQUIRE_EQ("subcommand", cmdline.arguments()[0]);
42208334c51SBrooks Davis ATF_REQUIRE_EQ("--flag2", cmdline.arguments()[1]);
42308334c51SBrooks Davis ATF_REQUIRE_EQ("arg", cmdline.arguments()[2]);
42408334c51SBrooks Davis
42508334c51SBrooks Davis const bool_option flag2("flag2", "");
42608334c51SBrooks Davis std::vector< const base_option* > options2;
42708334c51SBrooks Davis options2.push_back(&flag2);
42808334c51SBrooks Davis const parsed_cmdline cmdline2 = parse(cmdline.arguments(), options2);
42908334c51SBrooks Davis
43008334c51SBrooks Davis ATF_REQUIRE(!cmdline2.has_option("flag1"));
43108334c51SBrooks Davis ATF_REQUIRE( cmdline2.has_option("flag2"));
43208334c51SBrooks Davis ATF_REQUIRE_EQ(1, cmdline2.arguments().size());
43308334c51SBrooks Davis ATF_REQUIRE_EQ("arg", cmdline2.arguments()[0]);
43408334c51SBrooks Davis }
43508334c51SBrooks Davis
43608334c51SBrooks Davis
43708334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__short);
ATF_TEST_CASE_BODY(missing_option_argument_error__short)43808334c51SBrooks Davis ATF_TEST_CASE_BODY(missing_option_argument_error__short)
43908334c51SBrooks Davis {
44008334c51SBrooks Davis const int argc = 3;
44108334c51SBrooks Davis const char* const argv[] = {"progname", "-a3", "-b", NULL};
44208334c51SBrooks Davis const string_option flag1('a', "flag1", "Description", "arg");
44308334c51SBrooks Davis const string_option flag2('b', "flag2", "Description", "arg");
44408334c51SBrooks Davis std::vector< const base_option* > options;
44508334c51SBrooks Davis options.push_back(&flag1);
44608334c51SBrooks Davis options.push_back(&flag2);
44708334c51SBrooks Davis
44808334c51SBrooks Davis try {
44908334c51SBrooks Davis parse(argc, argv, options);
45008334c51SBrooks Davis fail("missing_option_argument_error not raised");
45108334c51SBrooks Davis } catch (const cmdline::missing_option_argument_error& e) {
45208334c51SBrooks Davis ATF_REQUIRE_EQ("-b", e.option());
45308334c51SBrooks Davis } catch (const cmdline::unknown_option_error& e) {
45408334c51SBrooks Davis if (is_getopt_long_pluscolon_broken())
45508334c51SBrooks Davis expect_fail("Your getopt_long is broken");
45608334c51SBrooks Davis fail("Got unknown_option_error instead of "
45708334c51SBrooks Davis "missing_option_argument_error");
45808334c51SBrooks Davis }
45908334c51SBrooks Davis }
46008334c51SBrooks Davis
46108334c51SBrooks Davis
46208334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__shortblock);
ATF_TEST_CASE_BODY(missing_option_argument_error__shortblock)46308334c51SBrooks Davis ATF_TEST_CASE_BODY(missing_option_argument_error__shortblock)
46408334c51SBrooks Davis {
46508334c51SBrooks Davis const int argc = 3;
46608334c51SBrooks Davis const char* const argv[] = {"progname", "-ab3", "-ac", NULL};
46708334c51SBrooks Davis const bool_option flag1('a', "flag1", "Description");
46808334c51SBrooks Davis const string_option flag2('b', "flag2", "Description", "arg");
46908334c51SBrooks Davis const string_option flag3('c', "flag2", "Description", "arg");
47008334c51SBrooks Davis std::vector< const base_option* > options;
47108334c51SBrooks Davis options.push_back(&flag1);
47208334c51SBrooks Davis options.push_back(&flag2);
47308334c51SBrooks Davis options.push_back(&flag3);
47408334c51SBrooks Davis
47508334c51SBrooks Davis try {
47608334c51SBrooks Davis parse(argc, argv, options);
47708334c51SBrooks Davis fail("missing_option_argument_error not raised");
47808334c51SBrooks Davis } catch (const cmdline::missing_option_argument_error& e) {
47908334c51SBrooks Davis ATF_REQUIRE_EQ("-c", e.option());
48008334c51SBrooks Davis } catch (const cmdline::unknown_option_error& e) {
48108334c51SBrooks Davis if (is_getopt_long_pluscolon_broken())
48208334c51SBrooks Davis expect_fail("Your getopt_long is broken");
48308334c51SBrooks Davis fail("Got unknown_option_error instead of "
48408334c51SBrooks Davis "missing_option_argument_error");
48508334c51SBrooks Davis }
48608334c51SBrooks Davis }
48708334c51SBrooks Davis
48808334c51SBrooks Davis
48908334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__long);
ATF_TEST_CASE_BODY(missing_option_argument_error__long)49008334c51SBrooks Davis ATF_TEST_CASE_BODY(missing_option_argument_error__long)
49108334c51SBrooks Davis {
49208334c51SBrooks Davis const int argc = 3;
49308334c51SBrooks Davis const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL};
49408334c51SBrooks Davis const string_option flag1("flag1", "Description", "arg");
49508334c51SBrooks Davis const string_option flag2("flag2", "Description", "arg");
49608334c51SBrooks Davis std::vector< const base_option* > options;
49708334c51SBrooks Davis options.push_back(&flag1);
49808334c51SBrooks Davis options.push_back(&flag2);
49908334c51SBrooks Davis
50008334c51SBrooks Davis try {
50108334c51SBrooks Davis parse(argc, argv, options);
50208334c51SBrooks Davis fail("missing_option_argument_error not raised");
50308334c51SBrooks Davis } catch (const cmdline::missing_option_argument_error& e) {
50408334c51SBrooks Davis ATF_REQUIRE_EQ("--flag2", e.option());
50508334c51SBrooks Davis } catch (const cmdline::unknown_option_error& e) {
50608334c51SBrooks Davis if (is_getopt_long_pluscolon_broken())
50708334c51SBrooks Davis expect_fail("Your getopt_long is broken");
50808334c51SBrooks Davis fail("Got unknown_option_error instead of "
50908334c51SBrooks Davis "missing_option_argument_error");
51008334c51SBrooks Davis }
51108334c51SBrooks Davis }
51208334c51SBrooks Davis
51308334c51SBrooks Davis
51408334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__short);
ATF_TEST_CASE_BODY(unknown_option_error__short)51508334c51SBrooks Davis ATF_TEST_CASE_BODY(unknown_option_error__short)
51608334c51SBrooks Davis {
51708334c51SBrooks Davis const int argc = 3;
51808334c51SBrooks Davis const char* const argv[] = {"progname", "-a", "-b", NULL};
51908334c51SBrooks Davis const bool_option flag1('a', "flag1", "Description");
52008334c51SBrooks Davis std::vector< const base_option* > options;
52108334c51SBrooks Davis options.push_back(&flag1);
52208334c51SBrooks Davis
52308334c51SBrooks Davis try {
52408334c51SBrooks Davis parse(argc, argv, options);
52508334c51SBrooks Davis fail("unknown_option_error not raised");
52608334c51SBrooks Davis } catch (const cmdline::unknown_option_error& e) {
52708334c51SBrooks Davis ATF_REQUIRE_EQ("-b", e.option());
52808334c51SBrooks Davis }
52908334c51SBrooks Davis }
53008334c51SBrooks Davis
53108334c51SBrooks Davis
53208334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__shortblock);
ATF_TEST_CASE_BODY(unknown_option_error__shortblock)53308334c51SBrooks Davis ATF_TEST_CASE_BODY(unknown_option_error__shortblock)
53408334c51SBrooks Davis {
53508334c51SBrooks Davis const int argc = 3;
53608334c51SBrooks Davis const char* const argv[] = {"progname", "-a", "-bdc", NULL};
53708334c51SBrooks Davis const bool_option flag1('a', "flag1", "Description");
53808334c51SBrooks Davis const bool_option flag2('b', "flag2", "Description");
53908334c51SBrooks Davis const bool_option flag3('c', "flag3", "Description");
54008334c51SBrooks Davis std::vector< const base_option* > options;
54108334c51SBrooks Davis options.push_back(&flag1);
54208334c51SBrooks Davis options.push_back(&flag2);
54308334c51SBrooks Davis options.push_back(&flag3);
54408334c51SBrooks Davis
54508334c51SBrooks Davis try {
54608334c51SBrooks Davis parse(argc, argv, options);
54708334c51SBrooks Davis fail("unknown_option_error not raised");
54808334c51SBrooks Davis } catch (const cmdline::unknown_option_error& e) {
54908334c51SBrooks Davis ATF_REQUIRE_EQ("-d", e.option());
55008334c51SBrooks Davis }
55108334c51SBrooks Davis }
55208334c51SBrooks Davis
55308334c51SBrooks Davis
55408334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__long);
ATF_TEST_CASE_BODY(unknown_option_error__long)55508334c51SBrooks Davis ATF_TEST_CASE_BODY(unknown_option_error__long)
55608334c51SBrooks Davis {
55708334c51SBrooks Davis const int argc = 3;
55808334c51SBrooks Davis const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL};
55908334c51SBrooks Davis const string_option flag1("flag1", "Description", "arg");
56008334c51SBrooks Davis std::vector< const base_option* > options;
56108334c51SBrooks Davis options.push_back(&flag1);
56208334c51SBrooks Davis
56308334c51SBrooks Davis try {
56408334c51SBrooks Davis parse(argc, argv, options);
56508334c51SBrooks Davis fail("unknown_option_error not raised");
56608334c51SBrooks Davis } catch (const cmdline::unknown_option_error& e) {
56708334c51SBrooks Davis ATF_REQUIRE_EQ("--flag2", e.option());
56808334c51SBrooks Davis }
56908334c51SBrooks Davis }
57008334c51SBrooks Davis
57108334c51SBrooks Davis
57208334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(unknown_plus_option_error);
ATF_TEST_CASE_BODY(unknown_plus_option_error)57308334c51SBrooks Davis ATF_TEST_CASE_BODY(unknown_plus_option_error)
57408334c51SBrooks Davis {
57508334c51SBrooks Davis const int argc = 2;
57608334c51SBrooks Davis const char* const argv[] = {"progname", "-+", NULL};
57708334c51SBrooks Davis const cmdline::options_vector options;
57808334c51SBrooks Davis
57908334c51SBrooks Davis try {
58008334c51SBrooks Davis parse(argc, argv, options);
58108334c51SBrooks Davis fail("unknown_option_error not raised");
58208334c51SBrooks Davis } catch (const cmdline::unknown_option_error& e) {
58308334c51SBrooks Davis ATF_REQUIRE_EQ("-+", e.option());
58408334c51SBrooks Davis } catch (const cmdline::missing_option_argument_error& e) {
58508334c51SBrooks Davis fail("Looks like getopt_long thinks a + option is defined and it "
58608334c51SBrooks Davis "even requires an argument");
58708334c51SBrooks Davis }
58808334c51SBrooks Davis }
58908334c51SBrooks Davis
59008334c51SBrooks Davis
59108334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(option_types);
ATF_TEST_CASE_BODY(option_types)59208334c51SBrooks Davis ATF_TEST_CASE_BODY(option_types)
59308334c51SBrooks Davis {
59408334c51SBrooks Davis const int argc = 3;
59508334c51SBrooks Davis const char* const argv[] = {"progname", "--flag1=a", "--flag2=one", NULL};
59608334c51SBrooks Davis const string_option flag1("flag1", "The flag1", "arg");
59708334c51SBrooks Davis const mock_option flag2("flag2");
59808334c51SBrooks Davis std::vector< const base_option* > options;
59908334c51SBrooks Davis options.push_back(&flag1);
60008334c51SBrooks Davis options.push_back(&flag2);
60108334c51SBrooks Davis
60208334c51SBrooks Davis const parsed_cmdline cmdline = parse(argc, argv, options);
60308334c51SBrooks Davis
60408334c51SBrooks Davis ATF_REQUIRE(cmdline.has_option("flag1"));
60508334c51SBrooks Davis ATF_REQUIRE(cmdline.has_option("flag2"));
60608334c51SBrooks Davis ATF_REQUIRE_EQ("a", cmdline.get_option< string_option >("flag1"));
60708334c51SBrooks Davis ATF_REQUIRE_EQ(1, cmdline.get_option< mock_option >("flag2"));
60808334c51SBrooks Davis }
60908334c51SBrooks Davis
61008334c51SBrooks Davis
61108334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(option_validation_error);
ATF_TEST_CASE_BODY(option_validation_error)61208334c51SBrooks Davis ATF_TEST_CASE_BODY(option_validation_error)
61308334c51SBrooks Davis {
61408334c51SBrooks Davis const int argc = 3;
61508334c51SBrooks Davis const char* const argv[] = {"progname", "--flag1=zero", "--flag2=foo",
61608334c51SBrooks Davis NULL};
61708334c51SBrooks Davis const mock_option flag1("flag1");
61808334c51SBrooks Davis const mock_option flag2("flag2");
61908334c51SBrooks Davis std::vector< const base_option* > options;
62008334c51SBrooks Davis options.push_back(&flag1);
62108334c51SBrooks Davis options.push_back(&flag2);
62208334c51SBrooks Davis
62308334c51SBrooks Davis try {
62408334c51SBrooks Davis parse(argc, argv, options);
62508334c51SBrooks Davis fail("option_argument_value_error not raised");
62608334c51SBrooks Davis } catch (const cmdline::option_argument_value_error& e) {
62708334c51SBrooks Davis ATF_REQUIRE_EQ("--flag2", e.option());
62808334c51SBrooks Davis ATF_REQUIRE_EQ("foo", e.argument());
62908334c51SBrooks Davis }
63008334c51SBrooks Davis }
63108334c51SBrooks Davis
63208334c51SBrooks Davis
63308334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(silent_errors);
ATF_TEST_CASE_BODY(silent_errors)63408334c51SBrooks Davis ATF_TEST_CASE_BODY(silent_errors)
63508334c51SBrooks Davis {
63608334c51SBrooks Davis const int argc = 2;
63708334c51SBrooks Davis const char* const argv[] = {"progname", "-h", NULL};
63808334c51SBrooks Davis cmdline::options_vector options;
63908334c51SBrooks Davis
64008334c51SBrooks Davis try {
64108334c51SBrooks Davis std::pair< int, int > oldfds = mock_stdfds("output.txt");
64208334c51SBrooks Davis try {
64308334c51SBrooks Davis parse(argc, argv, options);
64408334c51SBrooks Davis } catch (...) {
64508334c51SBrooks Davis restore_stdfds(oldfds);
64608334c51SBrooks Davis throw;
64708334c51SBrooks Davis }
64808334c51SBrooks Davis restore_stdfds(oldfds);
64908334c51SBrooks Davis fail("unknown_option_error not raised");
65008334c51SBrooks Davis } catch (const cmdline::unknown_option_error& e) {
65108334c51SBrooks Davis ATF_REQUIRE_EQ("-h", e.option());
65208334c51SBrooks Davis }
65308334c51SBrooks Davis
65408334c51SBrooks Davis std::ifstream input("output.txt");
65508334c51SBrooks Davis ATF_REQUIRE(input);
65608334c51SBrooks Davis
65708334c51SBrooks Davis bool has_output = false;
65808334c51SBrooks Davis std::string line;
65908334c51SBrooks Davis while (std::getline(input, line).good()) {
66008334c51SBrooks Davis std::cout << line << '\n';
66108334c51SBrooks Davis has_output = true;
66208334c51SBrooks Davis }
66308334c51SBrooks Davis
66408334c51SBrooks Davis if (has_output)
66508334c51SBrooks Davis fail("getopt_long printed messages on stdout/stderr by itself");
66608334c51SBrooks Davis }
66708334c51SBrooks Davis
66808334c51SBrooks Davis
ATF_INIT_TEST_CASES(tcs)66908334c51SBrooks Davis ATF_INIT_TEST_CASES(tcs)
67008334c51SBrooks Davis {
67108334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, progname__no_options);
67208334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, progname__some_options);
67308334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, some_args__no_options);
67408334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, some_args__some_options);
67508334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, some_options__all_known);
67608334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, some_options__multi);
67708334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, subcommands);
67808334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__short);
67908334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__shortblock);
68008334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__long);
68108334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, unknown_option_error__short);
68208334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, unknown_option_error__shortblock);
68308334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, unknown_option_error__long);
68408334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, unknown_plus_option_error);
68508334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, option_types);
68608334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, option_validation_error);
68708334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, silent_errors);
68808334c51SBrooks Davis }
689