291 lines
9.7 KiB
C++
291 lines
9.7 KiB
C++
// 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;
|
|
}
|
|
|