| // Copyright Vladimir Prus 2002-2004. |
| // Distributed under the Boost Software License, Version 1.0. |
| // (See accompanying file LICENSE_1_0.txt |
| // or copy at http://www.boost.org/LICENSE_1_0.txt) |
| |
| |
| #include <boost/program_options/variables_map.hpp> |
| #include <boost/program_options/options_description.hpp> |
| #include <boost/program_options/parsers.hpp> |
| #include <boost/program_options/detail/utf8_codecvt_facet.hpp> |
| using namespace boost::program_options; |
| // We'll use po::value everywhere to workaround vc6 bug. |
| namespace po = boost::program_options; |
| |
| #include <boost/function.hpp> |
| using namespace boost; |
| |
| #include <sstream> |
| using namespace std; |
| |
| #include "minitest.hpp" |
| |
| vector<string> sv(const char* array[], unsigned size) |
| { |
| vector<string> r; |
| for (unsigned i = 0; i < size; ++i) |
| r.push_back(array[i]); |
| return r; |
| } |
| |
| void test_variable_map() |
| { |
| options_description desc; |
| desc.add_options() |
| ("foo,f", new untyped_value) |
| ("bar,b", po::value<string>()) |
| ("biz,z", po::value<string>()) |
| ("baz", new untyped_value()) |
| ("output,o", new untyped_value(), "") |
| ; |
| const char* cmdline3_[] = { "--foo='12'", "--bar=11", "-z3", "-ofoo" }; |
| vector<string> cmdline3 = sv(cmdline3_, |
| sizeof(cmdline3_)/sizeof(const char*)); |
| parsed_options a3 = command_line_parser(cmdline3).options(desc).run(); |
| variables_map vm; |
| store(a3, vm); |
| notify(vm); |
| BOOST_REQUIRE(vm.size() == 4); |
| BOOST_CHECK(vm["foo"].as<string>() == "'12'"); |
| BOOST_CHECK(vm["bar"].as<string>() == "11"); |
| BOOST_CHECK(vm.count("biz") == 1); |
| BOOST_CHECK(vm["biz"].as<string>() == "3"); |
| BOOST_CHECK(vm["output"].as<string>() == "foo"); |
| |
| int i; |
| desc.add_options() |
| ("zee", bool_switch(), "") |
| ("zak", po::value<int>(&i), "") |
| ("opt", bool_switch(), ""); |
| |
| const char* cmdline4_[] = { "--zee", "--zak=13" }; |
| vector<string> cmdline4 = sv(cmdline4_, |
| sizeof(cmdline4_)/sizeof(const char*)); |
| parsed_options a4 = command_line_parser(cmdline4).options(desc).run(); |
| |
| variables_map vm2; |
| store(a4, vm2); |
| notify(vm2); |
| BOOST_REQUIRE(vm2.size() == 3); |
| BOOST_CHECK(vm2["zee"].as<bool>() == true); |
| BOOST_CHECK(vm2["zak"].as<int>() == 13); |
| BOOST_CHECK(vm2["opt"].as<bool>() == false); |
| BOOST_CHECK(i == 13); |
| |
| options_description desc2; |
| desc2.add_options() |
| ("vee", po::value<string>()->default_value("42")) |
| ("voo", po::value<string>()) |
| ("iii", po::value<int>()->default_value(123)) |
| ; |
| const char* cmdline5_[] = { "--voo=1" }; |
| vector<string> cmdline5 = sv(cmdline5_, |
| sizeof(cmdline5_)/sizeof(const char*)); |
| parsed_options a5 = command_line_parser(cmdline5).options(desc2).run(); |
| |
| variables_map vm3; |
| store(a5, vm3); |
| notify(vm3); |
| BOOST_REQUIRE(vm3.size() == 3); |
| BOOST_CHECK(vm3["vee"].as<string>() == "42"); |
| BOOST_CHECK(vm3["voo"].as<string>() == "1"); |
| BOOST_CHECK(vm3["iii"].as<int>() == 123); |
| |
| options_description desc3; |
| desc3.add_options() |
| ("imp", po::value<int>()->implicit_value(100)) |
| ("iim", po::value<int>()->implicit_value(200)->default_value(201)) |
| ("mmp,m", po::value<int>()->implicit_value(123)->default_value(124)) |
| ("foo", po::value<int>()) |
| ; |
| /* The -m option is implicit. It does not have value in inside the token, |
| and we should not grab the next token. */ |
| const char* cmdline6_[] = { "--imp=1", "-m", "--foo=1" }; |
| vector<string> cmdline6 = sv(cmdline6_, |
| sizeof(cmdline6_)/sizeof(const char*)); |
| parsed_options a6 = command_line_parser(cmdline6).options(desc3).run(); |
| |
| variables_map vm4; |
| store(a6, vm4); |
| notify(vm4); |
| BOOST_REQUIRE(vm4.size() == 4); |
| BOOST_CHECK(vm4["imp"].as<int>() == 1); |
| BOOST_CHECK(vm4["iim"].as<int>() == 201); |
| BOOST_CHECK(vm4["mmp"].as<int>() == 123); |
| } |
| |
| int stored_value; |
| void notifier(const vector<int>& v) |
| { |
| stored_value = v.front(); |
| } |
| |
| void test_semantic_values() |
| { |
| options_description desc; |
| desc.add_options() |
| ("foo", new untyped_value()) |
| ("bar", po::value<int>()) |
| ("biz", po::value< vector<string> >()) |
| ("baz", po::value< vector<string> >()->multitoken()) |
| ("int", po::value< vector<int> >()->notifier(¬ifier)) |
| ; |
| |
| |
| parsed_options parsed(&desc); |
| vector<option>& options = parsed.options; |
| vector<string> v; |
| v.push_back("q"); |
| options.push_back(option("foo", vector<string>(1, "1"))); |
| options.push_back(option("biz", vector<string>(1, "a"))); |
| options.push_back(option("baz", v)); |
| options.push_back(option("bar", vector<string>(1, "1"))); |
| options.push_back(option("biz", vector<string>(1, "b x"))); |
| v.push_back("w"); |
| options.push_back(option("baz", v)); |
| |
| variables_map vm; |
| store(parsed, vm); |
| notify(vm); |
| BOOST_REQUIRE(vm.count("biz") == 1); |
| BOOST_REQUIRE(vm.count("baz") == 1); |
| const vector<string> av = vm["biz"].as< vector<string> >(); |
| const vector<string> av2 = vm["baz"].as< vector<string> >(); |
| string exp1[] = { "a", "b x" }; |
| BOOST_CHECK(av == vector<string>(exp1, exp1 + 2)); |
| string exp2[] = { "q", "q", "w" }; |
| BOOST_CHECK(av2 == vector<string>(exp2, exp2 + 3)); |
| |
| options.push_back(option("int", vector<string>(1, "13"))); |
| |
| variables_map vm2; |
| store(parsed, vm2); |
| notify(vm2); |
| BOOST_REQUIRE(vm2.count("int") == 1); |
| BOOST_CHECK(vm2["int"].as< vector<int> >() == vector<int>(1, 13)); |
| BOOST_CHECK_EQUAL(stored_value, 13); |
| |
| vector<option> saved_options = options; |
| |
| options.push_back(option("bar", vector<string>(1, "2"))); |
| variables_map vm3; |
| BOOST_CHECK_THROW(store(parsed, vm3), multiple_occurrences); |
| |
| options = saved_options; |
| // Now try passing two int in one 'argv' element. |
| // This should not work. |
| options.push_back(option("int", vector<string>(1, "2 3"))); |
| variables_map vm4; |
| BOOST_CHECK_THROW(store(parsed, vm4), validation_error); |
| } |
| |
| void test_priority() |
| { |
| options_description desc; |
| desc.add_options() |
| // Value of this option will be specified in two sources, |
| // and only first one should be used. |
| ("first", po::value< vector<int > >()) |
| // Value of this option will have default value in the first source, |
| // and explicit assignment in the second, so the second should be used. |
| ("second", po::value< vector<int > >()->default_value(vector<int>(1, 1), "")) |
| ("aux", po::value< vector<int > >()) |
| // This will have values in both sources, and values should be combined |
| ("include", po::value< vector<int> >()->composing()) |
| ; |
| |
| const char* cmdline1_[] = { "--first=1", "--aux=10", "--first=3", "--include=1" }; |
| vector<string> cmdline1 = sv(cmdline1_, |
| sizeof(cmdline1_)/sizeof(const char*)); |
| |
| parsed_options p1 = command_line_parser(cmdline1).options(desc).run(); |
| |
| const char* cmdline2_[] = { "--first=12", "--second=7", "--include=7" }; |
| vector<string> cmdline2 = sv(cmdline2_, |
| sizeof(cmdline2_)/sizeof(const char*)); |
| |
| parsed_options p2 = command_line_parser(cmdline2).options(desc).run(); |
| |
| variables_map vm; |
| store(p1, vm); |
| |
| BOOST_REQUIRE(vm.count("first") == 1); |
| BOOST_REQUIRE(vm["first"].as< vector<int> >().size() == 2); |
| BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[0], 1); |
| BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[1], 3); |
| |
| BOOST_REQUIRE(vm.count("second") == 1); |
| BOOST_REQUIRE(vm["second"].as< vector<int> >().size() == 1); |
| BOOST_CHECK_EQUAL(vm["second"].as< vector<int> >()[0], 1); |
| |
| store(p2, vm); |
| |
| // Value should not change. |
| BOOST_REQUIRE(vm.count("first") == 1); |
| BOOST_REQUIRE(vm["first"].as< vector<int> >().size() == 2); |
| BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[0], 1); |
| BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[1], 3); |
| |
| // Value should change to 7 |
| BOOST_REQUIRE(vm.count("second") == 1); |
| BOOST_REQUIRE(vm["second"].as< vector<int> >().size() == 1); |
| BOOST_CHECK_EQUAL(vm["second"].as< vector<int> >()[0], 7); |
| |
| BOOST_REQUIRE(vm.count("include") == 1); |
| BOOST_REQUIRE(vm["include"].as< vector<int> >().size() == 2); |
| BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[0], 1); |
| BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[1], 7); |
| } |
| |
| void test_multiple_assignments_with_different_option_description() |
| { |
| // Test that if we store option twice into the same variable_map, |
| // and some of the options stored the first time are not present |
| // in the options descrription provided the second time, we don't crash. |
| |
| options_description desc1(""); |
| desc1.add_options() |
| ("help,h", "") |
| ("includes", po::value< vector<string> >()->composing(), ""); |
| ; |
| |
| options_description desc2(""); |
| desc2.add_options() |
| ("output,o", ""); |
| |
| vector<string> input1; |
| input1.push_back("--help"); |
| input1.push_back("--includes=a"); |
| parsed_options p1 = command_line_parser(input1).options(desc1).run(); |
| |
| vector<string> input2; |
| input1.push_back("--output"); |
| parsed_options p2 = command_line_parser(input2).options(desc2).run(); |
| |
| vector<string> input3; |
| input3.push_back("--includes=b"); |
| parsed_options p3 = command_line_parser(input3).options(desc1).run(); |
| |
| |
| variables_map vm; |
| store(p1, vm); |
| store(p2, vm); |
| store(p3, vm); |
| |
| BOOST_REQUIRE(vm.count("help") == 1); |
| BOOST_REQUIRE(vm.count("includes") == 1); |
| BOOST_CHECK_EQUAL(vm["includes"].as< vector<string> >()[0], "a"); |
| BOOST_CHECK_EQUAL(vm["includes"].as< vector<string> >()[1], "b"); |
| |
| } |
| |
| int main(int, char* []) |
| { |
| test_variable_map(); |
| test_semantic_values(); |
| test_priority(); |
| test_multiple_assignments_with_different_option_description(); |
| return 0; |
| } |
| |