Skip to content

Commit

Permalink
Multiple long option names support:
Browse files Browse the repository at this point in the history
* Added tests
* Expanded existing tests to account for the multiple-long-names feature.
  • Loading branch information
Eyal Rozenberg committed Jun 15, 2018
1 parent 8fef823 commit e493a62
Show file tree
Hide file tree
Showing 8 changed files with 338 additions and 91 deletions.
3 changes: 1 addition & 2 deletions src/options_description.cpp
Expand Up @@ -196,8 +196,7 @@ namespace boost { namespace program_options {
while(std::getline(iss, name, ',')) {
m_long_names.push_back(name);
}
bool got_any_option_names = !m_long_names.empty();
assert(got_any_option_names);
assert(!m_long_names.empty() && "No option names were specified");

bool try_interpreting_last_name_as_a_switch = m_long_names.size() > 1;
if (try_interpreting_last_name_as_a_switch) {
Expand Down
6 changes: 5 additions & 1 deletion test/cmdline_test.cpp
Expand Up @@ -463,23 +463,27 @@ void test_additional_parser()
desc.add_options()
("response-file", value<string>(), "response file")
("foo", value<int>(), "foo")
("bar,baz", value<int>(), "bar")
;

vector<string> input;
input.push_back("@config");
input.push_back("--foo=1");
input.push_back("--baz=11");

cmdline cmd(input);
cmd.set_options_description(desc);
cmd.set_additional_parser(at_option_parser);

vector<option> result = cmd.run();

BOOST_REQUIRE(result.size() == 2);
BOOST_REQUIRE(result.size() == 3);
BOOST_CHECK_EQUAL(result[0].string_key, "response-file");
BOOST_CHECK_EQUAL(result[0].value[0], "config");
BOOST_CHECK_EQUAL(result[1].string_key, "foo");
BOOST_CHECK_EQUAL(result[1].value[0], "1");
BOOST_CHECK_EQUAL(result[2].string_key, "bar");
BOOST_CHECK_EQUAL(result[2].value[0], "11");

// Test that invalid options returned by additional style
// parser are detected.
Expand Down
2 changes: 1 addition & 1 deletion test/config_test.cfg
Expand Up @@ -5,5 +5,5 @@ b = true

[m1]
v1 = 1

v2 = 2
v3 = 3
108 changes: 104 additions & 4 deletions test/exception_test.cpp
Expand Up @@ -45,6 +45,56 @@ void test_ambiguous()
}


void test_ambiguous_long()
{
options_description desc;
desc.add_options()
("cfgfile,c", value<string>()->multitoken(), "the config file")
("output,c", value<string>(), "the output file")
("output,o", value<string>(), "the output file")
;

const char* cmdline[] = {"program", "--cfgfile", "file", "--output", "anotherfile"};

variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
}
catch (ambiguous_option& e)
{
BOOST_CHECK_EQUAL(e.alternatives().size(), 2);
BOOST_CHECK_EQUAL(e.get_option_name(), "--output");
BOOST_CHECK_EQUAL(e.alternatives()[0], "output");
BOOST_CHECK_EQUAL(e.alternatives()[1], "output");
}
}

void test_ambiguous_multiple_long_names()
{
options_description desc;
desc.add_options()
("cfgfile,foo,c", value<string>()->multitoken(), "the config file")
("output,foo,o", value<string>(), "the output file")
;

const char* cmdline[] = {"program", "--foo", "file"};

variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
}
catch (ambiguous_option& e)
{
BOOST_CHECK_EQUAL(e.alternatives().size(), 2);
BOOST_CHECK_EQUAL(e.get_option_name(), "--foo");
BOOST_CHECK_EQUAL(e.alternatives()[0], "cfgfile");
BOOST_CHECK_EQUAL(e.alternatives()[1], "output");
}
}



void test_unknown_option()
{
Expand Down Expand Up @@ -100,7 +150,6 @@ void test_multiple_values()
}



void test_multiple_occurrences()
{
options_description desc;
Expand All @@ -109,20 +158,67 @@ void test_multiple_occurrences()
;

const char* cmdline[] = {"program", "--cfgfile", "file", "-c", "anotherfile"};

variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (multiple_occurrences& e)
{
BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' cannot be specified more than once");
}
}

void test_multiple_occurrences_with_different_names()
{
options_description desc;
desc.add_options()
("cfgfile,config-file,c", value<string>(), "the configfile")
;

const char* cmdline[] = {"program", "--config-file", "file", "--cfgfile", "anotherfile"};

variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (multiple_occurrences& e)
{
BOOST_CHECK( (e.get_option_name() == "--cfgfile") || (e.get_option_name() == "--configfile"));
BOOST_CHECK(
(string(e.what()) == "option '--cfgfile' cannot be specified more than once") ||
(string(e.what()) == "option '--configfile' cannot be specified more than once")
);
}
}


void test_multiple_occurrences_with_non_key_names()
{
options_description desc;
desc.add_options()
("cfgfile,config-file,c", value<string>(), "the configfile")
;

const char* cmdline[] = {"program", "--config-file", "file", "-c", "anotherfile"};

variables_map vm;
try {
store(parse_command_line(sizeof(cmdline)/sizeof(const char*),
const_cast<char**>(cmdline), desc), vm);
notify(vm);
}
catch (multiple_occurrences& e)
{
BOOST_CHECK_EQUAL(e.get_option_name(), "--cfgfile");
BOOST_CHECK_EQUAL(string(e.what()), "option '--cfgfile' cannot be specified more than once");
}
}


void test_missing_value()
Expand Down Expand Up @@ -154,9 +250,13 @@ void test_missing_value()
int main(int /*ac*/, char** /*av*/)
{
test_ambiguous();
test_ambiguous_long();
test_ambiguous_multiple_long_names();
test_unknown_option();
test_multiple_values();
test_multiple_occurrences();
test_multiple_occurrences_with_different_names();
test_multiple_occurrences_with_non_key_names();
test_missing_value();

return 0;
Expand Down
49 changes: 49 additions & 0 deletions test/options_description_test.cpp
Expand Up @@ -74,6 +74,25 @@ void test_approximation()
// BOOST_CHECK(*(++a.begin()) == "foo");
}

void test_approximation_with_multiname_options()
{
options_description desc;
desc.add_options()
("foo", new untyped_value())
("fee", new untyped_value())
("fe,baz", new untyped_value())
("chroots,all-chroots", new untyped_value())
("sessions,all-sessions", new untyped_value())
("everything,all", new untyped_value())
("qux,fo", new untyped_value())
;

BOOST_CHECK_EQUAL(desc.find("fo", true).long_name(), "qux");

BOOST_CHECK_EQUAL(desc.find("all", true).long_name(), "everything");
BOOST_CHECK_EQUAL(desc.find("all-ch", true).long_name(), "chroots");
}

void test_formatting()
{
// Long option descriptions used to crash on MSVC-8.0.
Expand Down Expand Up @@ -124,6 +143,21 @@ void test_formatting()
);
}

void test_multiname_option_formatting()
{
options_description desc;
desc.add_options()
("foo,bar", new untyped_value(), "a multiple-name option")
;

stringstream ss;
ss << desc;
BOOST_CHECK_EQUAL(ss.str(),
" --foo arg a multiple-name option\n"
);
}


void test_formatting_description_length()
{
{
Expand Down Expand Up @@ -245,12 +279,27 @@ void test_value_name()
);
}

void test_multiname_key_and_switch_selection()
{
// cases:
// foo,f -> f
// foo, c -> c
// foo,f,g -> g
// f,g,h -> h
// f,foo throws
// foo,bar -> no switch
// foo,f,bar -> no switch

// what about empty strings - consecutive ,'s ?
}

int main(int, char* [])
{
test_type();
test_approximation();
test_formatting();
test_multiname_key_and_switch_selection();
test_multiname_option_formatting();
test_formatting_description_length();
test_long_default_value();
test_word_wrapping();
Expand Down

0 comments on commit e493a62

Please sign in to comment.