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