Path: blob/main/contrib/kyua/utils/cmdline/parser_test.cpp
48178 views
// Copyright 2010 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include "utils/cmdline/parser.ipp"2930#if defined(HAVE_CONFIG_H)31#include "config.h"32#endif3334extern "C" {35#include <fcntl.h>36#include <getopt.h>37#include <unistd.h>38}3940#include <cstdlib>41#include <cstring>42#include <fstream>43#include <iostream>44#include <string>45#include <utility>4647#include <atf-c++.hpp>4849#include "utils/cmdline/exceptions.hpp"50#include "utils/cmdline/options.hpp"51#include "utils/format/macros.hpp"52#include "utils/sanity.hpp"5354namespace cmdline = utils::cmdline;5556using cmdline::base_option;57using cmdline::bool_option;58using cmdline::int_option;59using cmdline::parse;60using cmdline::parsed_cmdline;61using cmdline::string_option;626364namespace {656667/// Mock option type to check the validate and convert methods sequence.68///69/// Instances of this option accept a string argument that must be either "zero"70/// or "one". These are validated and converted to integers.71class mock_option : public base_option {72public:73/// Constructs the new option.74///75/// \param long_name_ The long name for the option. All other option76/// properties are irrelevant for the tests using this, so they are set77/// to arbitrary values.78mock_option(const char* long_name_) :79base_option(long_name_, "Irrelevant description", "arg")80{81}8283/// The type of the argument of this option.84typedef int option_type;8586/// Checks that the user-provided option is valid.87///88/// \param str The user argument; must be "zero" or "one".89///90/// \throw cmdline::option_argument_value_error If str is not valid.91void92validate(const std::string& str) const93{94if (str != "zero" && str != "one")95throw cmdline::option_argument_value_error(F("--%s") % long_name(),96str, "Unknown value");97}9899/// Converts the user-provided argument to our native integer type.100///101/// \param str The user argument; must be "zero" or "one".102///103/// \return 0 if the input is "zero", or 1 if the input is "one".104///105/// \throw std::runtime_error If str is not valid. In real life, this106/// should be a precondition because validate() has already ensured that107/// the values passed to convert() are correct. However, we raise an108/// exception here because we are actually validating that this code109/// sequence holds true.110static int111convert(const std::string& str)112{113if (str == "zero")114return 0;115else if (str == "one")116return 1;117else {118// This would generally be an assertion but, given that this is119// test code, we want to catch any errors regardless of how the120// binary is built.121throw std::runtime_error("Value not validated properly.");122}123}124};125126127/// Redirects stdout and stderr to a file.128///129/// This fails the test case in case of any error.130///131/// \param file The name of the file to redirect stdout and stderr to.132///133/// \return A copy of the old stdout and stderr file descriptors.134static std::pair< int, int >135mock_stdfds(const char* file)136{137std::cout.flush();138std::cerr.flush();139140const int oldout = ::dup(STDOUT_FILENO);141ATF_REQUIRE(oldout != -1);142const int olderr = ::dup(STDERR_FILENO);143ATF_REQUIRE(olderr != -1);144145const int fd = ::open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);146ATF_REQUIRE(fd != -1);147ATF_REQUIRE(::dup2(fd, STDOUT_FILENO) != -1);148ATF_REQUIRE(::dup2(fd, STDERR_FILENO) != -1);149::close(fd);150151return std::make_pair(oldout, olderr);152}153154155/// Restores stdout and stderr after a call to mock_stdfds.156///157/// \param oldfds The copy of the previous stdout and stderr as returned by the158/// call to mock_fds().159static void160restore_stdfds(const std::pair< int, int >& oldfds)161{162ATF_REQUIRE(::dup2(oldfds.first, STDOUT_FILENO) != -1);163::close(oldfds.first);164ATF_REQUIRE(::dup2(oldfds.second, STDERR_FILENO) != -1);165::close(oldfds.second);166}167168169/// Checks whether a '+:' prefix to the short options of getopt_long works.170///171/// It turns out that the getopt_long(3) implementation of Ubuntu 10.04.1 (and172/// very likely other distributions) does not properly report a missing argument173/// to a second long option as such. Instead of returning ':' when the second174/// long option provided on the command line does not carry a required argument,175/// it will mistakenly return '?' which translates to "unknown option".176///177/// As a result of this bug, we cannot properly detect that 'flag2' requires an178/// argument in a command line like: 'progname --flag1=foo --flag2'.179///180/// I am not sure if we could fully workaround the issue in the implementation181/// of our library. For the time being I am just using this bug detection in182/// the test cases to prevent failures that are not really our fault.183///184/// \return bool True if getopt_long is broken and does not interpret '+:'185/// correctly; False otherwise.186static bool187is_getopt_long_pluscolon_broken(void)188{189struct ::option long_options[] = {190{ "flag1", 1, NULL, '1' },191{ "flag2", 1, NULL, '2' },192{ NULL, 0, NULL, 0 }193};194195const int argc = 3;196char* argv[4];197argv[0] = ::strdup("progname");198argv[1] = ::strdup("--flag1=a");199argv[2] = ::strdup("--flag2");200argv[3] = NULL;201202const int old_opterr = ::opterr;203::opterr = 0;204205bool got_colon = false;206207int opt;208while ((opt = ::getopt_long(argc, argv, "+:", long_options, NULL)) != -1) {209switch (opt) {210case '1': break;211case '2': break;212case ':': got_colon = true; break;213case '?': break;214default: UNREACHABLE; break;215}216}217218::opterr = old_opterr;219::optind = 1;220#if defined(HAVE_GETOPT_WITH_OPTRESET)221::optreset = 1;222#endif223224for (char** arg = &argv[0]; *arg != NULL; arg++)225std::free(*arg);226227return !got_colon;228}229230231} // anonymous namespace232233234ATF_TEST_CASE_WITHOUT_HEAD(progname__no_options);235ATF_TEST_CASE_BODY(progname__no_options)236{237const int argc = 1;238const char* const argv[] = {"progname", NULL};239std::vector< const base_option* > options;240const parsed_cmdline cmdline = parse(argc, argv, options);241242ATF_REQUIRE(cmdline.arguments().empty());243}244245246ATF_TEST_CASE_WITHOUT_HEAD(progname__some_options);247ATF_TEST_CASE_BODY(progname__some_options)248{249const int argc = 1;250const char* const argv[] = {"progname", NULL};251const string_option a('a', "a_option", "Foo", NULL);252const string_option b('b', "b_option", "Bar", "arg", "foo");253const string_option c("c_option", "Baz", NULL);254const string_option d("d_option", "Wohoo", "arg", "bar");255std::vector< const base_option* > options;256options.push_back(&a);257options.push_back(&b);258options.push_back(&c);259options.push_back(&d);260const parsed_cmdline cmdline = parse(argc, argv, options);261262ATF_REQUIRE_EQ("foo", cmdline.get_option< string_option >("b_option"));263ATF_REQUIRE_EQ("bar", cmdline.get_option< string_option >("d_option"));264ATF_REQUIRE(cmdline.arguments().empty());265}266267268ATF_TEST_CASE_WITHOUT_HEAD(some_args__no_options);269ATF_TEST_CASE_BODY(some_args__no_options)270{271const int argc = 5;272const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL};273std::vector< const base_option* > options;274const parsed_cmdline cmdline = parse(argc, argv, options);275276ATF_REQUIRE(!cmdline.has_option("c"));277ATF_REQUIRE(!cmdline.has_option("opt"));278ATF_REQUIRE_EQ(4, cmdline.arguments().size());279ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]);280ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]);281ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]);282ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]);283}284285286ATF_TEST_CASE_WITHOUT_HEAD(some_args__some_options);287ATF_TEST_CASE_BODY(some_args__some_options)288{289const int argc = 5;290const char* const argv[] = {"progname", "foo", "-c", "--opt", "bar", NULL};291const string_option c('c', "opt", "Description", NULL);292std::vector< const base_option* > options;293options.push_back(&c);294const parsed_cmdline cmdline = parse(argc, argv, options);295296ATF_REQUIRE(!cmdline.has_option("c"));297ATF_REQUIRE(!cmdline.has_option("opt"));298ATF_REQUIRE_EQ(4, cmdline.arguments().size());299ATF_REQUIRE_EQ("foo", cmdline.arguments()[0]);300ATF_REQUIRE_EQ("-c", cmdline.arguments()[1]);301ATF_REQUIRE_EQ("--opt", cmdline.arguments()[2]);302ATF_REQUIRE_EQ("bar", cmdline.arguments()[3]);303}304305306ATF_TEST_CASE_WITHOUT_HEAD(some_options__all_known);307ATF_TEST_CASE_BODY(some_options__all_known)308{309const int argc = 14;310const char* const argv[] = {311"progname",312"-a",313"-bvalue_b",314"-c", "value_c",315//"-d", // Options with default optional values are unsupported.316"-evalue_e", // Has default; overriden.317"--f_long",318"--g_long=value_g",319"--h_long", "value_h",320//"--i_long", // Options with default optional values are unsupported.321"--j_long", "value_j", // Has default; overriden as separate argument.322"arg1", "arg2", NULL,323};324const bool_option a('a', "a_long", "");325const string_option b('b', "b_long", "Description", "arg");326const string_option c('c', "c_long", "ABCD", "foo");327const string_option d('d', "d_long", "Description", "bar", "default_d");328const string_option e('e', "e_long", "Description", "baz", "default_e");329const bool_option f("f_long", "Description");330const string_option g("g_long", "Description", "arg");331const string_option h("h_long", "Description", "foo");332const string_option i("i_long", "EFGH", "bar", "default_i");333const string_option j("j_long", "Description", "baz", "default_j");334std::vector< const base_option* > options;335options.push_back(&a);336options.push_back(&b);337options.push_back(&c);338options.push_back(&d);339options.push_back(&e);340options.push_back(&f);341options.push_back(&g);342options.push_back(&h);343options.push_back(&i);344options.push_back(&j);345const parsed_cmdline cmdline = parse(argc, argv, options);346347ATF_REQUIRE(cmdline.has_option("a_long"));348ATF_REQUIRE_EQ("value_b", cmdline.get_option< string_option >("b_long"));349ATF_REQUIRE_EQ("value_c", cmdline.get_option< string_option >("c_long"));350ATF_REQUIRE_EQ("default_d", cmdline.get_option< string_option >("d_long"));351ATF_REQUIRE_EQ("value_e", cmdline.get_option< string_option >("e_long"));352ATF_REQUIRE(cmdline.has_option("f_long"));353ATF_REQUIRE_EQ("value_g", cmdline.get_option< string_option >("g_long"));354ATF_REQUIRE_EQ("value_h", cmdline.get_option< string_option >("h_long"));355ATF_REQUIRE_EQ("default_i", cmdline.get_option< string_option >("i_long"));356ATF_REQUIRE_EQ("value_j", cmdline.get_option< string_option >("j_long"));357ATF_REQUIRE_EQ(2, cmdline.arguments().size());358ATF_REQUIRE_EQ("arg1", cmdline.arguments()[0]);359ATF_REQUIRE_EQ("arg2", cmdline.arguments()[1]);360}361362363ATF_TEST_CASE_WITHOUT_HEAD(some_options__multi);364ATF_TEST_CASE_BODY(some_options__multi)365{366const int argc = 9;367const char* const argv[] = {368"progname",369"-a1",370"-bvalue1",371"-a2",372"--a_long=3",373"-bvalue2",374"--b_long=value3",375"arg1", "arg2", NULL,376};377const int_option a('a', "a_long", "Description", "arg");378const string_option b('b', "b_long", "Description", "arg");379std::vector< const base_option* > options;380options.push_back(&a);381options.push_back(&b);382const parsed_cmdline cmdline = parse(argc, argv, options);383384{385ATF_REQUIRE_EQ(3, cmdline.get_option< int_option >("a_long"));386const std::vector< int > multi =387cmdline.get_multi_option< int_option >("a_long");388ATF_REQUIRE_EQ(3, multi.size());389ATF_REQUIRE_EQ(1, multi[0]);390ATF_REQUIRE_EQ(2, multi[1]);391ATF_REQUIRE_EQ(3, multi[2]);392}393394{395ATF_REQUIRE_EQ("value3", cmdline.get_option< string_option >("b_long"));396const std::vector< std::string > multi =397cmdline.get_multi_option< string_option >("b_long");398ATF_REQUIRE_EQ(3, multi.size());399ATF_REQUIRE_EQ("value1", multi[0]);400ATF_REQUIRE_EQ("value2", multi[1]);401ATF_REQUIRE_EQ("value3", multi[2]);402}403}404405406ATF_TEST_CASE_WITHOUT_HEAD(subcommands);407ATF_TEST_CASE_BODY(subcommands)408{409const int argc = 5;410const char* const argv[] = {"progname", "--flag1", "subcommand",411"--flag2", "arg", NULL};412const bool_option flag1("flag1", "");413std::vector< const base_option* > options;414options.push_back(&flag1);415const parsed_cmdline cmdline = parse(argc, argv, options);416417ATF_REQUIRE( cmdline.has_option("flag1"));418ATF_REQUIRE(!cmdline.has_option("flag2"));419ATF_REQUIRE_EQ(3, cmdline.arguments().size());420ATF_REQUIRE_EQ("subcommand", cmdline.arguments()[0]);421ATF_REQUIRE_EQ("--flag2", cmdline.arguments()[1]);422ATF_REQUIRE_EQ("arg", cmdline.arguments()[2]);423424const bool_option flag2("flag2", "");425std::vector< const base_option* > options2;426options2.push_back(&flag2);427const parsed_cmdline cmdline2 = parse(cmdline.arguments(), options2);428429ATF_REQUIRE(!cmdline2.has_option("flag1"));430ATF_REQUIRE( cmdline2.has_option("flag2"));431ATF_REQUIRE_EQ(1, cmdline2.arguments().size());432ATF_REQUIRE_EQ("arg", cmdline2.arguments()[0]);433}434435436ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__short);437ATF_TEST_CASE_BODY(missing_option_argument_error__short)438{439const int argc = 3;440const char* const argv[] = {"progname", "-a3", "-b", NULL};441const string_option flag1('a', "flag1", "Description", "arg");442const string_option flag2('b', "flag2", "Description", "arg");443std::vector< const base_option* > options;444options.push_back(&flag1);445options.push_back(&flag2);446447try {448parse(argc, argv, options);449fail("missing_option_argument_error not raised");450} catch (const cmdline::missing_option_argument_error& e) {451ATF_REQUIRE_EQ("-b", e.option());452} catch (const cmdline::unknown_option_error& e) {453if (is_getopt_long_pluscolon_broken())454expect_fail("Your getopt_long is broken");455fail("Got unknown_option_error instead of "456"missing_option_argument_error");457}458}459460461ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__shortblock);462ATF_TEST_CASE_BODY(missing_option_argument_error__shortblock)463{464const int argc = 3;465const char* const argv[] = {"progname", "-ab3", "-ac", NULL};466const bool_option flag1('a', "flag1", "Description");467const string_option flag2('b', "flag2", "Description", "arg");468const string_option flag3('c', "flag2", "Description", "arg");469std::vector< const base_option* > options;470options.push_back(&flag1);471options.push_back(&flag2);472options.push_back(&flag3);473474try {475parse(argc, argv, options);476fail("missing_option_argument_error not raised");477} catch (const cmdline::missing_option_argument_error& e) {478ATF_REQUIRE_EQ("-c", e.option());479} catch (const cmdline::unknown_option_error& e) {480if (is_getopt_long_pluscolon_broken())481expect_fail("Your getopt_long is broken");482fail("Got unknown_option_error instead of "483"missing_option_argument_error");484}485}486487488ATF_TEST_CASE_WITHOUT_HEAD(missing_option_argument_error__long);489ATF_TEST_CASE_BODY(missing_option_argument_error__long)490{491const int argc = 3;492const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL};493const string_option flag1("flag1", "Description", "arg");494const string_option flag2("flag2", "Description", "arg");495std::vector< const base_option* > options;496options.push_back(&flag1);497options.push_back(&flag2);498499try {500parse(argc, argv, options);501fail("missing_option_argument_error not raised");502} catch (const cmdline::missing_option_argument_error& e) {503ATF_REQUIRE_EQ("--flag2", e.option());504} catch (const cmdline::unknown_option_error& e) {505if (is_getopt_long_pluscolon_broken())506expect_fail("Your getopt_long is broken");507fail("Got unknown_option_error instead of "508"missing_option_argument_error");509}510}511512513ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__short);514ATF_TEST_CASE_BODY(unknown_option_error__short)515{516const int argc = 3;517const char* const argv[] = {"progname", "-a", "-b", NULL};518const bool_option flag1('a', "flag1", "Description");519std::vector< const base_option* > options;520options.push_back(&flag1);521522try {523parse(argc, argv, options);524fail("unknown_option_error not raised");525} catch (const cmdline::unknown_option_error& e) {526ATF_REQUIRE_EQ("-b", e.option());527}528}529530531ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__shortblock);532ATF_TEST_CASE_BODY(unknown_option_error__shortblock)533{534const int argc = 3;535const char* const argv[] = {"progname", "-a", "-bdc", NULL};536const bool_option flag1('a', "flag1", "Description");537const bool_option flag2('b', "flag2", "Description");538const bool_option flag3('c', "flag3", "Description");539std::vector< const base_option* > options;540options.push_back(&flag1);541options.push_back(&flag2);542options.push_back(&flag3);543544try {545parse(argc, argv, options);546fail("unknown_option_error not raised");547} catch (const cmdline::unknown_option_error& e) {548ATF_REQUIRE_EQ("-d", e.option());549}550}551552553ATF_TEST_CASE_WITHOUT_HEAD(unknown_option_error__long);554ATF_TEST_CASE_BODY(unknown_option_error__long)555{556const int argc = 3;557const char* const argv[] = {"progname", "--flag1=a", "--flag2", NULL};558const string_option flag1("flag1", "Description", "arg");559std::vector< const base_option* > options;560options.push_back(&flag1);561562try {563parse(argc, argv, options);564fail("unknown_option_error not raised");565} catch (const cmdline::unknown_option_error& e) {566ATF_REQUIRE_EQ("--flag2", e.option());567}568}569570571ATF_TEST_CASE_WITHOUT_HEAD(unknown_plus_option_error);572ATF_TEST_CASE_BODY(unknown_plus_option_error)573{574const int argc = 2;575const char* const argv[] = {"progname", "-+", NULL};576const cmdline::options_vector options;577578try {579parse(argc, argv, options);580fail("unknown_option_error not raised");581} catch (const cmdline::unknown_option_error& e) {582ATF_REQUIRE_EQ("-+", e.option());583} catch (const cmdline::missing_option_argument_error& e) {584fail("Looks like getopt_long thinks a + option is defined and it "585"even requires an argument");586}587}588589590ATF_TEST_CASE_WITHOUT_HEAD(option_types);591ATF_TEST_CASE_BODY(option_types)592{593const int argc = 3;594const char* const argv[] = {"progname", "--flag1=a", "--flag2=one", NULL};595const string_option flag1("flag1", "The flag1", "arg");596const mock_option flag2("flag2");597std::vector< const base_option* > options;598options.push_back(&flag1);599options.push_back(&flag2);600601const parsed_cmdline cmdline = parse(argc, argv, options);602603ATF_REQUIRE(cmdline.has_option("flag1"));604ATF_REQUIRE(cmdline.has_option("flag2"));605ATF_REQUIRE_EQ("a", cmdline.get_option< string_option >("flag1"));606ATF_REQUIRE_EQ(1, cmdline.get_option< mock_option >("flag2"));607}608609610ATF_TEST_CASE_WITHOUT_HEAD(option_validation_error);611ATF_TEST_CASE_BODY(option_validation_error)612{613const int argc = 3;614const char* const argv[] = {"progname", "--flag1=zero", "--flag2=foo",615NULL};616const mock_option flag1("flag1");617const mock_option flag2("flag2");618std::vector< const base_option* > options;619options.push_back(&flag1);620options.push_back(&flag2);621622try {623parse(argc, argv, options);624fail("option_argument_value_error not raised");625} catch (const cmdline::option_argument_value_error& e) {626ATF_REQUIRE_EQ("--flag2", e.option());627ATF_REQUIRE_EQ("foo", e.argument());628}629}630631632ATF_TEST_CASE_WITHOUT_HEAD(silent_errors);633ATF_TEST_CASE_BODY(silent_errors)634{635const int argc = 2;636const char* const argv[] = {"progname", "-h", NULL};637cmdline::options_vector options;638639try {640std::pair< int, int > oldfds = mock_stdfds("output.txt");641try {642parse(argc, argv, options);643} catch (...) {644restore_stdfds(oldfds);645throw;646}647restore_stdfds(oldfds);648fail("unknown_option_error not raised");649} catch (const cmdline::unknown_option_error& e) {650ATF_REQUIRE_EQ("-h", e.option());651}652653std::ifstream input("output.txt");654ATF_REQUIRE(input);655656bool has_output = false;657std::string line;658while (std::getline(input, line).good()) {659std::cout << line << '\n';660has_output = true;661}662663if (has_output)664fail("getopt_long printed messages on stdout/stderr by itself");665}666667668ATF_INIT_TEST_CASES(tcs)669{670ATF_ADD_TEST_CASE(tcs, progname__no_options);671ATF_ADD_TEST_CASE(tcs, progname__some_options);672ATF_ADD_TEST_CASE(tcs, some_args__no_options);673ATF_ADD_TEST_CASE(tcs, some_args__some_options);674ATF_ADD_TEST_CASE(tcs, some_options__all_known);675ATF_ADD_TEST_CASE(tcs, some_options__multi);676ATF_ADD_TEST_CASE(tcs, subcommands);677ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__short);678ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__shortblock);679ATF_ADD_TEST_CASE(tcs, missing_option_argument_error__long);680ATF_ADD_TEST_CASE(tcs, unknown_option_error__short);681ATF_ADD_TEST_CASE(tcs, unknown_option_error__shortblock);682ATF_ADD_TEST_CASE(tcs, unknown_option_error__long);683ATF_ADD_TEST_CASE(tcs, unknown_plus_option_error);684ATF_ADD_TEST_CASE(tcs, option_types);685ATF_ADD_TEST_CASE(tcs, option_validation_error);686ATF_ADD_TEST_CASE(tcs, silent_errors);687}688689690