1 // Copyright Vladimir Prus 2002-2004. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt 4 // or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 7 #include <boost/program_options/variables_map.hpp> 8 #include <boost/program_options/options_description.hpp> 9 #include <boost/program_options/parsers.hpp> 10 #include <boost/program_options/detail/utf8_codecvt_facet.hpp> 11 using namespace boost::program_options; 12 // We'll use po::value everywhere to workaround vc6 bug. 13 namespace po = boost::program_options; 14 15 #include <boost/function.hpp> 16 using namespace boost; 17 18 #include <sstream> 19 using namespace std; 20 21 #include "minitest.hpp" 22 sv(const char * array[],unsigned size)23 vector<string> sv(const char* array[], unsigned size) 24 { 25 vector<string> r; 26 for (unsigned i = 0; i < size; ++i) 27 r.push_back(array[i]); 28 return r; 29 } 30 test_variable_map()31 void test_variable_map() 32 { 33 options_description desc; 34 desc.add_options() 35 ("foo,f", new untyped_value) 36 ("bar,b", po::value<string>()) 37 ("biz,z", po::value<string>()) 38 ("baz", new untyped_value()) 39 ("output,o", new untyped_value(), "") 40 ; 41 const char* cmdline3_[] = { "--foo='12'", "--bar=11", "-z3", "-ofoo" }; 42 vector<string> cmdline3 = sv(cmdline3_, 43 sizeof(cmdline3_)/sizeof(const char*)); 44 parsed_options a3 = command_line_parser(cmdline3).options(desc).run(); 45 variables_map vm; 46 store(a3, vm); 47 notify(vm); 48 BOOST_REQUIRE(vm.size() == 4); 49 BOOST_CHECK(vm["foo"].as<string>() == "'12'"); 50 BOOST_CHECK(vm["bar"].as<string>() == "11"); 51 BOOST_CHECK(vm.count("biz") == 1); 52 BOOST_CHECK(vm["biz"].as<string>() == "3"); 53 BOOST_CHECK(vm["output"].as<string>() == "foo"); 54 55 int i; 56 desc.add_options() 57 ("zee", bool_switch(), "") 58 ("zak", po::value<int>(&i), "") 59 ("opt", bool_switch(), ""); 60 61 const char* cmdline4_[] = { "--zee", "--zak=13" }; 62 vector<string> cmdline4 = sv(cmdline4_, 63 sizeof(cmdline4_)/sizeof(const char*)); 64 parsed_options a4 = command_line_parser(cmdline4).options(desc).run(); 65 66 variables_map vm2; 67 store(a4, vm2); 68 notify(vm2); 69 BOOST_REQUIRE(vm2.size() == 3); 70 BOOST_CHECK(vm2["zee"].as<bool>() == true); 71 BOOST_CHECK(vm2["zak"].as<int>() == 13); 72 BOOST_CHECK(vm2["opt"].as<bool>() == false); 73 BOOST_CHECK(i == 13); 74 75 options_description desc2; 76 desc2.add_options() 77 ("vee", po::value<string>()->default_value("42")) 78 ("voo", po::value<string>()) 79 ("iii", po::value<int>()->default_value(123)) 80 ; 81 const char* cmdline5_[] = { "--voo=1" }; 82 vector<string> cmdline5 = sv(cmdline5_, 83 sizeof(cmdline5_)/sizeof(const char*)); 84 parsed_options a5 = command_line_parser(cmdline5).options(desc2).run(); 85 86 variables_map vm3; 87 store(a5, vm3); 88 notify(vm3); 89 BOOST_REQUIRE(vm3.size() == 3); 90 BOOST_CHECK(vm3["vee"].as<string>() == "42"); 91 BOOST_CHECK(vm3["voo"].as<string>() == "1"); 92 BOOST_CHECK(vm3["iii"].as<int>() == 123); 93 94 options_description desc3; 95 desc3.add_options() 96 ("imp", po::value<int>()->implicit_value(100)) 97 ("iim", po::value<int>()->implicit_value(200)->default_value(201)) 98 ("mmp,m", po::value<int>()->implicit_value(123)->default_value(124)) 99 ("foo", po::value<int>()) 100 ; 101 /* The -m option is implicit. It does not have value in inside the token, 102 and we should not grab the next token. */ 103 const char* cmdline6_[] = { "--imp=1", "-m", "--foo=1" }; 104 vector<string> cmdline6 = sv(cmdline6_, 105 sizeof(cmdline6_)/sizeof(const char*)); 106 parsed_options a6 = command_line_parser(cmdline6).options(desc3).run(); 107 108 variables_map vm4; 109 store(a6, vm4); 110 notify(vm4); 111 BOOST_REQUIRE(vm4.size() == 4); 112 BOOST_CHECK(vm4["imp"].as<int>() == 1); 113 BOOST_CHECK(vm4["iim"].as<int>() == 201); 114 BOOST_CHECK(vm4["mmp"].as<int>() == 123); 115 } 116 117 int stored_value; notifier(const vector<int> & v)118 void notifier(const vector<int>& v) 119 { 120 stored_value = v.front(); 121 } 122 test_semantic_values()123 void test_semantic_values() 124 { 125 options_description desc; 126 desc.add_options() 127 ("foo", new untyped_value()) 128 ("bar", po::value<int>()) 129 ("biz", po::value< vector<string> >()) 130 ("baz", po::value< vector<string> >()->multitoken()) 131 ("int", po::value< vector<int> >()->notifier(¬ifier)) 132 ; 133 134 135 parsed_options parsed(&desc); 136 vector<option>& options = parsed.options; 137 vector<string> v; 138 v.push_back("q"); 139 options.push_back(option("foo", vector<string>(1, "1"))); 140 options.push_back(option("biz", vector<string>(1, "a"))); 141 options.push_back(option("baz", v)); 142 options.push_back(option("bar", vector<string>(1, "1"))); 143 options.push_back(option("biz", vector<string>(1, "b x"))); 144 v.push_back("w"); 145 options.push_back(option("baz", v)); 146 147 variables_map vm; 148 store(parsed, vm); 149 notify(vm); 150 BOOST_REQUIRE(vm.count("biz") == 1); 151 BOOST_REQUIRE(vm.count("baz") == 1); 152 const vector<string> av = vm["biz"].as< vector<string> >(); 153 const vector<string> av2 = vm["baz"].as< vector<string> >(); 154 string exp1[] = { "a", "b x" }; 155 BOOST_CHECK(av == vector<string>(exp1, exp1 + 2)); 156 string exp2[] = { "q", "q", "w" }; 157 BOOST_CHECK(av2 == vector<string>(exp2, exp2 + 3)); 158 159 options.push_back(option("int", vector<string>(1, "13"))); 160 161 variables_map vm2; 162 store(parsed, vm2); 163 notify(vm2); 164 BOOST_REQUIRE(vm2.count("int") == 1); 165 BOOST_CHECK(vm2["int"].as< vector<int> >() == vector<int>(1, 13)); 166 BOOST_CHECK_EQUAL(stored_value, 13); 167 168 vector<option> saved_options = options; 169 170 options.push_back(option("bar", vector<string>(1, "2"))); 171 variables_map vm3; 172 BOOST_CHECK_THROW(store(parsed, vm3), multiple_occurrences); 173 174 options = saved_options; 175 // Now try passing two int in one 'argv' element. 176 // This should not work. 177 options.push_back(option("int", vector<string>(1, "2 3"))); 178 variables_map vm4; 179 BOOST_CHECK_THROW(store(parsed, vm4), validation_error); 180 } 181 test_priority()182 void test_priority() 183 { 184 options_description desc; 185 desc.add_options() 186 // Value of this option will be specified in two sources, 187 // and only first one should be used. 188 ("first", po::value< vector<int > >()) 189 // Value of this option will have default value in the first source, 190 // and explicit assignment in the second, so the second should be used. 191 ("second", po::value< vector<int > >()->default_value(vector<int>(1, 1), "")) 192 ("aux", po::value< vector<int > >()) 193 // This will have values in both sources, and values should be combined 194 ("include", po::value< vector<int> >()->composing()) 195 ; 196 197 const char* cmdline1_[] = { "--first=1", "--aux=10", "--first=3", "--include=1" }; 198 vector<string> cmdline1 = sv(cmdline1_, 199 sizeof(cmdline1_)/sizeof(const char*)); 200 201 parsed_options p1 = command_line_parser(cmdline1).options(desc).run(); 202 203 const char* cmdline2_[] = { "--first=12", "--second=7", "--include=7" }; 204 vector<string> cmdline2 = sv(cmdline2_, 205 sizeof(cmdline2_)/sizeof(const char*)); 206 207 parsed_options p2 = command_line_parser(cmdline2).options(desc).run(); 208 209 variables_map vm; 210 store(p1, vm); 211 212 BOOST_REQUIRE(vm.count("first") == 1); 213 BOOST_REQUIRE(vm["first"].as< vector<int> >().size() == 2); 214 BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[0], 1); 215 BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[1], 3); 216 217 BOOST_REQUIRE(vm.count("second") == 1); 218 BOOST_REQUIRE(vm["second"].as< vector<int> >().size() == 1); 219 BOOST_CHECK_EQUAL(vm["second"].as< vector<int> >()[0], 1); 220 221 store(p2, vm); 222 223 // Value should not change. 224 BOOST_REQUIRE(vm.count("first") == 1); 225 BOOST_REQUIRE(vm["first"].as< vector<int> >().size() == 2); 226 BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[0], 1); 227 BOOST_CHECK_EQUAL(vm["first"].as< vector<int> >()[1], 3); 228 229 // Value should change to 7 230 BOOST_REQUIRE(vm.count("second") == 1); 231 BOOST_REQUIRE(vm["second"].as< vector<int> >().size() == 1); 232 BOOST_CHECK_EQUAL(vm["second"].as< vector<int> >()[0], 7); 233 234 BOOST_REQUIRE(vm.count("include") == 1); 235 BOOST_REQUIRE(vm["include"].as< vector<int> >().size() == 2); 236 BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[0], 1); 237 BOOST_CHECK_EQUAL(vm["include"].as< vector<int> >()[1], 7); 238 } 239 test_multiple_assignments_with_different_option_description()240 void test_multiple_assignments_with_different_option_description() 241 { 242 // Test that if we store option twice into the same variable_map, 243 // and some of the options stored the first time are not present 244 // in the options descrription provided the second time, we don't crash. 245 246 options_description desc1(""); 247 desc1.add_options() 248 ("help,h", "") 249 ("includes", po::value< vector<string> >()->composing(), ""); 250 ; 251 252 options_description desc2(""); 253 desc2.add_options() 254 ("output,o", ""); 255 256 vector<string> input1; 257 input1.push_back("--help"); 258 input1.push_back("--includes=a"); 259 parsed_options p1 = command_line_parser(input1).options(desc1).run(); 260 261 vector<string> input2; 262 input1.push_back("--output"); 263 parsed_options p2 = command_line_parser(input2).options(desc2).run(); 264 265 vector<string> input3; 266 input3.push_back("--includes=b"); 267 parsed_options p3 = command_line_parser(input3).options(desc1).run(); 268 269 270 variables_map vm; 271 store(p1, vm); 272 store(p2, vm); 273 store(p3, vm); 274 275 BOOST_REQUIRE(vm.count("help") == 1); 276 BOOST_REQUIRE(vm.count("includes") == 1); 277 BOOST_CHECK_EQUAL(vm["includes"].as< vector<string> >()[0], "a"); 278 BOOST_CHECK_EQUAL(vm["includes"].as< vector<string> >()[1], "b"); 279 280 } 281 main(int,char * [])282 int main(int, char* []) 283 { 284 test_variable_map(); 285 test_semantic_values(); 286 test_priority(); 287 test_multiple_assignments_with_different_option_description(); 288 return 0; 289 } 290 291