diff options
Diffstat (limited to 'utils/cmdline/parser_test.cpp')
-rw-r--r-- | utils/cmdline/parser_test.cpp | 688 |
1 files changed, 688 insertions, 0 deletions
diff --git a/utils/cmdline/parser_test.cpp b/utils/cmdline/parser_test.cpp new file mode 100644 index 0000000000000..96370d279d2ee --- /dev/null +++ b/utils/cmdline/parser_test.cpp @@ -0,0 +1,688 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "utils/cmdline/parser.ipp" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +extern "C" { +#include <fcntl.h> +#include <getopt.h> +#include <unistd.h> +} + +#include <cstdlib> +#include <cstring> +#include <fstream> +#include <iostream> +#include <string> +#include <utility> + +#include <atf-c++.hpp> + +#include "utils/cmdline/exceptions.hpp" +#include "utils/cmdline/options.hpp" +#include "utils/format/macros.hpp" +#include "utils/sanity.hpp" + +namespace cmdline = utils::cmdline; + +using cmdline::base_option; +using cmdline::bool_option; +using cmdline::int_option; +using cmdline::parse; +using cmdline::parsed_cmdline; +using cmdline::string_option; + + +namespace { + + +/// Mock option type to check the validate and convert methods sequence. +/// +/// Instances of this option accept a string argument that must be either "zero" +/// or "one". These are validated and converted to integers. +class mock_option : public base_option { +public: + /// Constructs the new option. + /// + /// \param long_name_ The long name for the option. All other option + /// properties are irrelevant for the tests using this, so they are set + /// to arbitrary values. + mock_option(const char* long_name_) : + base_option(long_name_, "Irrelevant description", "arg") + { + } + + /// The type of the argument of this option. + typedef int option_type; + + /// Checks that the user-provided option is valid. + /// + /// \param str The user argument; must be "zero" or "one". + /// + /// \throw cmdline::option_argument_value_error If str is not valid. + void + validate(const std::string& str) const + { + if (str != "zero" && str != "one") + throw cmdline::option_argument_value_error(F("--%s") % long_name(), + str, "Unknown value"); + } + + /// Converts the user-provided argument to our native integer type. + /// + /// \param str The user argument; must be "zero" or "one". + /// + /// \return 0 if the input is "zero", or 1 if the input is "one". + /// + /// \throw std::runtime_error If str is not valid. In real life, this + /// should be a precondition because validate() has already ensured that + /// the values passed to convert() are correct. However, we raise an + /// exception here because we are actually validating that this code + /// sequence holds true. + static int + convert(const std::string& str) + { + if (str == "zero") + return 0; + else if (str == "one") + return 1; + else { + // This would generally be an assertion but, given that this is + // test code, we want to catch any errors regardless of how the + // binary is built. + throw std::runtime_error("Value not validated properly."); + } + } +}; + + +/// Redirects stdout and stderr to a file. +/// +/// This fails the test case in case of any error. +/// +/// \param file The name of the file to redirect stdout and stderr to. +/// +/// \return A copy of the old stdout and stderr file descriptors. +static std::pair< int, int > +mock_stdfds(const char* file) +{ + std::cout.flush(); + std::cerr.flush(); + + const int oldout = ::dup(STDOUT_FILENO); + ATF_REQUIRE(oldout != -1); + const int olderr = ::dup(STDERR_FILENO); + ATF_REQUIRE(olderr != -1); + + const int fd = ::open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644); + ATF_REQUIRE(fd != -1); + ATF_REQUIRE(::dup2(fd, STDOUT_FILENO) != -1); + ATF_REQUIRE(::dup2(fd, STDERR_FILENO) != -1); + ::close(fd); + + return std::make_pair(oldout, olderr); +} + + +/// Restores stdout and stderr after a call to mock_stdfds. +/// +/// \param oldfds The copy of the previous stdout and stderr as returned by the +/// call to mock_fds(). +static void +restore_stdfds(const std::pair< int, int >& oldfds) +{ + ATF_REQUIRE(::dup2(oldfds.first, STDOUT_FILENO) != -1); + ::close(oldfds.first); + ATF_REQUIRE(::dup2(oldfds.second, STDERR_FILENO) != -1); + ::close(oldfds.second); +} + + +/// Checks whether a '+:' prefix to the short options of getopt_long works. +/// +/// It turns out that the getopt_long(3) implementation of Ubuntu 10.04.1 (and +/// very likely other distributions) does not properly report a missing argument +/// to a second long option as such. Instead of returning ':' when the second +/// long option provided on the command line does not carry a required argument, +/// it will mistakenly return '?' which translates to "unknown option". +/// +/// As a result of this bug, we cannot properly detect that 'flag2' requires an +/// argument in a command line like: 'progname --flag1=foo --flag2'. +/// +/// I am not sure if we could fully workaround the issue in the implementation +/// of our library. For the time being I am just using this bug detection in +/// the test cases to prevent failures that are not really our fault. +/// +/// \return bool True if getopt_long is broken and does not interpret '+:' +/// correctly; False otherwise. +static bool +is_getopt_long_pluscolon_broken(void) +{ + struct ::option long_options[] = { + { "flag1", 1, NULL, '1' }, + { "flag2", 1, NULL, '2' }, + { NULL, 0, NULL, 0 } + }; + + const int argc = 3; + char* argv[4]; + argv[0] = ::strdup("progname"); + argv[1] = ::strdup("--flag1=a"); + argv[2] = ::strdup("--flag2"); + argv[3] = NULL; + + const int old_opterr = ::opterr; + ::opterr = 0; + + bool got_colon = false; + + int opt; + while ((opt = ::getopt_long(argc, argv, "+:", long_options, NULL)) != -1) { + switch (opt) { + case '1': break; + case '2': break; + case ':': got_colon = true; break; + case '?': break; + default: UNREACHABLE; break; + } + } + + ::opterr = old_opterr; + ::optind = 1; +#if defined(HAVE_GETOPT_WITH_OPTRESET) + ::optreset = 1; +#endif + + for (char** arg = &argv[0]; *arg != NULL; arg++) + std::free(*arg); + + return !got_colon; +} + + +} // anonymous namespace + + +ATF_TEST_CASE_WITHOUT_HEAD(progname__no_options); +ATF_TEST_CASE_BODY(progname__no_options) +{ + const int argc = 1; + const char* const argv[] = {"progname", NULL}; + std::vector< const base_option* > options; + const parsed_cmdline cmdline = parse(argc, argv, options); + + ATF_REQUIRE(cmdline.arguments().empty()); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(progname__some_options); +ATF_TEST_CASE_BODY(progname__some_options) +{ + const int argc = 1; + const char* const argv[] = {"progname", NULL}; + const string_option a('a', "a_option", "Foo", NULL); + const string_option b('b', "b_option", "Bar", "arg", "foo"); + const string_option c("c_option", "Baz", NULL); + const string_option d("d_option", "Wohoo", "arg", "bar"); + std::vector< const base_option* > options; + options.push_back(&a); + options.push_back(&b); + options.push_back(&c); + options.push_back(&d); + const parsed_cmdline cmdline = parse(argc, argv, options); + + ATF_REQUIRE_EQ("foo", cmdline.get_option< string_option >("b_option")); + ATF_REQUIRE_EQ("bar", cmdline.get_option< string_option >("d_option")); + ATF_REQUIRE(cmdline.arguments().empty()); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(some_args__no_options); +ATF_TEST_CASE_BODY(some_args__no_options) +{ + const int argc = 5; + const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL}; + std::vector< const base_option* > options; + const parsed_cmdline cmdline = parse(argc, argv, options); + + ATF_REQUIRE(!cmdline.has_option("c")); + ATF_REQUIRE(!cmdline.has_option("opt")); + ATF_REQUIRE_EQ(4, cmdline.arguments().size()); + ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]); + ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]); + ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]); + ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(some_args__some_options); +ATF_TEST_CASE_BODY(some_args__some_options) +{ + const int argc = 5; + const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL}; + const string_option c('c', "opt", "Description", NULL); + std::vector< const base_option* > options; + options.push_back(&c); + const parsed_cmdline cmdline = parse(argc, argv, options); + + ATF_REQUIRE(!cmdline.has_option("c")); + ATF_REQUIRE(!cmdline.has_option("opt")); + ATF_REQUIRE_EQ(4, cmdline.arguments().size()); + ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]); + ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]); + ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]); + ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(some_options__all_known); +ATF_TEST_CASE_BODY(some_options__all_known) +{ + const int argc = 14; + const char* const argv[] = { + "progname", + "-a", + "-bvalue_b", + "-c", "value_c", + //"-d", // Options with default optional values are unsupported. + "-evalue_e", // Has default; overriden. + "--f_long", + "--g_long=value_g", + "--h_long", "value_h", + //"--i_long", // Options with default optional values are unsupported. + "--j_long", "value_j", // Has default; overriden as separate argument. + "arg1", "arg2", NULL, + }; + const bool_option a('a', "a_long", ""); + const string_option b('b', "b_long", "Description", "arg"); + const string_option c('c', "c_long", "ABCD", "foo"); + const string_option d('d', "d_long", "Description", "bar", "default_d"); + const string_option e('e', "e_long", "Description", "baz", "default_e"); + const bool_option f("f_long", "Description"); + const string_option g("g_long", "Description", "arg"); + const string_option h("h_long", "Description", "foo"); + const string_option i("i_long", "EFGH", "bar", "default_i"); + const string_option j("j_long", "Description", "baz", "default_j"); + std::vector< const base_option* > options; + options.push_back(&a); + options.push_back(&b); + options.push_back(&c); + options.push_back(&d); + options.push_back(&e); + options.push_back(&f); + options.push_back(&g); + options.push_back(&h); + options.push_back(&i); + options.push_back(&j); + const parsed_cmdline cmdline = parse(argc, argv, options); + + ATF_REQUIRE(cmdline.has_option("a_long")); + ATF_REQUIRE_EQ("value_b", cmdline.get_option< string_option >("b_long")); + ATF_REQUIRE_EQ("value_c", cmdline.get_option< string_option >("c_long")); + ATF_REQUIRE_EQ("default_d", cmdline.get_option< string_option >("d_long")); + ATF_REQUIRE_EQ("value_e", cmdline.get_option< string_option >("e_long")); + ATF_REQUIRE(cmdline.has_option("f_long")); + ATF_REQUIRE_EQ("value_g", cmdline.get_option< string_option >("g_long")); + ATF_REQUIRE_EQ("value_h", cmdline.get_option< string_option >("h_long")); + ATF_REQUIRE_EQ("default_i", cmdline.get_option< string_option >("i_long")); + ATF_REQUIRE_EQ("value_j", cmdline.get_option< string_option >("j_long")); + ATF_REQUIRE_EQ(2, cmdline.arguments().size()); + ATF_REQUIRE_EQ("arg1", cmdline.arguments()[0]); + ATF_REQUIRE_EQ("arg2", cmdline.arguments()[1]); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(some_options__multi); +ATF_TEST_CASE_BODY(some_options__multi) +{ + const int argc = 9; + const char* const argv[] = { + "progname", + "-a1", + "-bvalue1", + "-a2", + "--a_long=3", + "-bvalue2", + "--b_long=value3", + "arg1", "arg2", NULL, + }; + const int_option a('a', "a_long", "Description", "arg"); + const string_option b('b', "b_long", "Description", "arg"); + std::vector< const base_option* > options; + options.push_back(&a); + options.push_back(&b); + const parsed_cmdline cmdline = parse(argc, argv, options); + + { + ATF_REQUIRE_EQ(3, cmdline.get_option< int_option >("a_long")); + const std::vector< int > multi = + cmdline.get_multi_option< int_option >("a_long"); + ATF_REQUIRE_EQ(3, multi.size()); + ATF_REQUIRE_EQ(1, multi[0]); + ATF_REQUIRE_EQ(2, multi[1]); + ATF_REQUIRE_EQ(3, multi[2]); + } + + { + ATF_REQUIRE_EQ("value3", cmdline.get_option< string_option >("b_long")); + const std::vector< std::string > multi = + cmdline.get_multi_option< string_option >("b_long"); + ATF_REQUIRE_EQ(3, multi.size()); + ATF_REQUIRE_EQ("value1", multi[0]); + ATF_REQUIRE_EQ("value2", multi[1]); + ATF_REQUIRE_EQ("value3", multi[2]); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(subcommands); +ATF_TEST_CASE_BODY(subcommands) +{ + const int argc = 5; + const char* const argv[] = {"progname", "--flag1", "subcommand", + "--flag2", "arg", NULL}; + const bool_option flag1("flag1", ""); + std::vector< const base_option* > options; + options.push_back(&flag1); + const parsed_cmdline cmdline = parse(argc, argv, options); + + ATF_REQUIRE( cmdline.has_option("flag1")); + ATF_REQUIRE(!cmdline.has_option("flag2")); + ATF_REQUIRE_EQ(3, cmdline.arguments().size()); + ATF_REQUIRE_EQ("subcommand", cmdline.arguments()[0]); + ATF_REQUIRE_EQ("--flag2", cmdline.arguments()[1]); + ATF_REQUIRE_EQ("arg", cmdline.arguments()[2]); + + const bool_option flag2("flag2", ""); + std::vector< const base_option* > options2; + options2.push_back(&flag2); + const parsed_cmdline cmdline2 = parse(cmdline.arguments(), options2); + + ATF_REQUIRE(!cmdline2.has_option("flag1")); + ATF_REQUIRE( cmdline2.has_option("flag2")); + ATF_REQUIRE_EQ(1, cmdline2.arguments().size()); + ATF_REQUIRE_EQ("arg", cmdline2.arguments()[0]); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__short); +ATF_TEST_CASE_BODY(missing_option_argument_error__short) +{ + const int argc = 3; + const char* const argv[] = {"progname", "-a3", "-b", NULL}; + const string_option flag1('a', "flag1", "Description", "arg"); + const string_option flag2('b', "flag2", "Description", "arg"); + std::vector< const base_option* > options; + options.push_back(&flag1); + options.push_back(&flag2); + + try { + parse(argc, argv, options); + fail("missing_option_argument_error not raised"); + } catch (const cmdline::missing_option_argument_error& e) { + ATF_REQUIRE_EQ("-b", e.option()); + } catch (const cmdline::unknown_option_error& e) { + if (is_getopt_long_pluscolon_broken()) + expect_fail("Your getopt_long is broken"); + fail("Got unknown_option_error instead of " + "missing_option_argument_error"); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__shortblock); +ATF_TEST_CASE_BODY(missing_option_argument_error__shortblock) +{ + const int argc = 3; + const char* const argv[] = {"progname", "-ab3", "-ac", NULL}; + const bool_option flag1('a', "flag1", "Description"); + const string_option flag2('b', "flag2", "Description", "arg"); + const string_option flag3('c', "flag2", "Description", "arg"); + std::vector< const base_option* > options; + options.push_back(&flag1); + options.push_back(&flag2); + options.push_back(&flag3); + + try { + parse(argc, argv, options); + fail("missing_option_argument_error not raised"); + } catch (const cmdline::missing_option_argument_error& e) { + ATF_REQUIRE_EQ("-c", e.option()); + } catch (const cmdline::unknown_option_error& e) { + if (is_getopt_long_pluscolon_broken()) + expect_fail("Your getopt_long is broken"); + fail("Got unknown_option_error instead of " + "missing_option_argument_error"); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__long); +ATF_TEST_CASE_BODY(missing_option_argument_error__long) +{ + const int argc = 3; + const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL}; + const string_option flag1("flag1", "Description", "arg"); + const string_option flag2("flag2", "Description", "arg"); + std::vector< const base_option* > options; + options.push_back(&flag1); + options.push_back(&flag2); + + try { + parse(argc, argv, options); + fail("missing_option_argument_error not raised"); + } catch (const cmdline::missing_option_argument_error& e) { + ATF_REQUIRE_EQ("--flag2", e.option()); + } catch (const cmdline::unknown_option_error& e) { + if (is_getopt_long_pluscolon_broken()) + expect_fail("Your getopt_long is broken"); + fail("Got unknown_option_error instead of " + "missing_option_argument_error"); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__short); +ATF_TEST_CASE_BODY(unknown_option_error__short) +{ + const int argc = 3; + const char* const argv[] = {"progname", "-a", "-b", NULL}; + const bool_option flag1('a', "flag1", "Description"); + std::vector< const base_option* > options; + options.push_back(&flag1); + + try { + parse(argc, argv, options); + fail("unknown_option_error not raised"); + } catch (const cmdline::unknown_option_error& e) { + ATF_REQUIRE_EQ("-b", e.option()); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__shortblock); +ATF_TEST_CASE_BODY(unknown_option_error__shortblock) +{ + const int argc = 3; + const char* const argv[] = {"progname", "-a", "-bdc", NULL}; + const bool_option flag1('a', "flag1", "Description"); + const bool_option flag2('b', "flag2", "Description"); + const bool_option flag3('c', "flag3", "Description"); + std::vector< const base_option* > options; + options.push_back(&flag1); + options.push_back(&flag2); + options.push_back(&flag3); + + try { + parse(argc, argv, options); + fail("unknown_option_error not raised"); + } catch (const cmdline::unknown_option_error& e) { + ATF_REQUIRE_EQ("-d", e.option()); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__long); +ATF_TEST_CASE_BODY(unknown_option_error__long) +{ + const int argc = 3; + const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL}; + const string_option flag1("flag1", "Description", "arg"); + std::vector< const base_option* > options; + options.push_back(&flag1); + + try { + parse(argc, argv, options); + fail("unknown_option_error not raised"); + } catch (const cmdline::unknown_option_error& e) { + ATF_REQUIRE_EQ("--flag2", e.option()); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(unknown_plus_option_error); +ATF_TEST_CASE_BODY(unknown_plus_option_error) +{ + const int argc = 2; + const char* const argv[] = {"progname", "-+", NULL}; + const cmdline::options_vector options; + + try { + parse(argc, argv, options); + fail("unknown_option_error not raised"); + } catch (const cmdline::unknown_option_error& e) { + ATF_REQUIRE_EQ("-+", e.option()); + } catch (const cmdline::missing_option_argument_error& e) { + fail("Looks like getopt_long thinks a + option is defined and it " + "even requires an argument"); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(option_types); +ATF_TEST_CASE_BODY(option_types) +{ + const int argc = 3; + const char* const argv[] = {"progname", "--flag1=a", "--flag2=one", NULL}; + const string_option flag1("flag1", "The flag1", "arg"); + const mock_option flag2("flag2"); + std::vector< const base_option* > options; + options.push_back(&flag1); + options.push_back(&flag2); + + const parsed_cmdline cmdline = parse(argc, argv, options); + + ATF_REQUIRE(cmdline.has_option("flag1")); + ATF_REQUIRE(cmdline.has_option("flag2")); + ATF_REQUIRE_EQ("a", cmdline.get_option< string_option >("flag1")); + ATF_REQUIRE_EQ(1, cmdline.get_option< mock_option >("flag2")); +} + + +ATF_TEST_CASE_WITHOUT_HEAD(option_validation_error); +ATF_TEST_CASE_BODY(option_validation_error) +{ + const int argc = 3; + const char* const argv[] = {"progname", "--flag1=zero", "--flag2=foo", + NULL}; + const mock_option flag1("flag1"); + const mock_option flag2("flag2"); + std::vector< const base_option* > options; + options.push_back(&flag1); + options.push_back(&flag2); + + try { + parse(argc, argv, options); + fail("option_argument_value_error not raised"); + } catch (const cmdline::option_argument_value_error& e) { + ATF_REQUIRE_EQ("--flag2", e.option()); + ATF_REQUIRE_EQ("foo", e.argument()); + } +} + + +ATF_TEST_CASE_WITHOUT_HEAD(silent_errors); +ATF_TEST_CASE_BODY(silent_errors) +{ + const int argc = 2; + const char* const argv[] = {"progname", "-h", NULL}; + cmdline::options_vector options; + + try { + std::pair< int, int > oldfds = mock_stdfds("output.txt"); + try { + parse(argc, argv, options); + } catch (...) { + restore_stdfds(oldfds); + throw; + } + restore_stdfds(oldfds); + fail("unknown_option_error not raised"); + } catch (const cmdline::unknown_option_error& e) { + ATF_REQUIRE_EQ("-h", e.option()); + } + + std::ifstream input("output.txt"); + ATF_REQUIRE(input); + + bool has_output = false; + std::string line; + while (std::getline(input, line).good()) { + std::cout << line << '\n'; + has_output = true; + } + + if (has_output) + fail("getopt_long printed messages on stdout/stderr by itself"); +} + + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, progname__no_options); + ATF_ADD_TEST_CASE(tcs, progname__some_options); + ATF_ADD_TEST_CASE(tcs, some_args__no_options); + ATF_ADD_TEST_CASE(tcs, some_args__some_options); + ATF_ADD_TEST_CASE(tcs, some_options__all_known); + ATF_ADD_TEST_CASE(tcs, some_options__multi); + ATF_ADD_TEST_CASE(tcs, subcommands); + ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__short); + ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__shortblock); + ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__long); + ATF_ADD_TEST_CASE(tcs, unknown_option_error__short); + ATF_ADD_TEST_CASE(tcs, unknown_option_error__shortblock); + ATF_ADD_TEST_CASE(tcs, unknown_option_error__long); + ATF_ADD_TEST_CASE(tcs, unknown_plus_option_error); + ATF_ADD_TEST_CASE(tcs, option_types); + ATF_ADD_TEST_CASE(tcs, option_validation_error); + ATF_ADD_TEST_CASE(tcs, silent_errors); +} |