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 | |
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(x: array[i]); |
28 | return r; |
29 | } |
30 | |
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(array: cmdline3_, |
43 | size: sizeof(cmdline3_)/sizeof(const char*)); |
44 | parsed_options a3 = command_line_parser(cmdline3).options(desc).run(); |
45 | variables_map vm; |
46 | store(options: a3, m&: vm); |
47 | notify(m&: 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>(v: &i), "" ) |
59 | ("opt" , bool_switch(), "" ); |
60 | |
61 | const char* cmdline4_[] = { "--zee" , "--zak=13" }; |
62 | vector<string> cmdline4 = sv(array: cmdline4_, |
63 | size: sizeof(cmdline4_)/sizeof(const char*)); |
64 | parsed_options a4 = command_line_parser(cmdline4).options(desc).run(); |
65 | |
66 | variables_map vm2; |
67 | store(options: a4, m&: vm2); |
68 | notify(m&: 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(v: "42" )) |
78 | ("voo" , po::value<string>()) |
79 | ("iii" , po::value<int>()->default_value(v: 123)) |
80 | ; |
81 | const char* cmdline5_[] = { "--voo=1" }; |
82 | vector<string> cmdline5 = sv(array: cmdline5_, |
83 | size: sizeof(cmdline5_)/sizeof(const char*)); |
84 | parsed_options a5 = command_line_parser(cmdline5).options(desc: desc2).run(); |
85 | |
86 | variables_map vm3; |
87 | store(options: a5, m&: vm3); |
88 | notify(m&: 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(v: 100)) |
97 | ("iim" , po::value<int>()->implicit_value(v: 200)->default_value(v: 201)) |
98 | ("mmp,m" , po::value<int>()->implicit_value(v: 123)->default_value(v: 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(array: cmdline6_, |
105 | size: sizeof(cmdline6_)/sizeof(const char*)); |
106 | parsed_options a6 = command_line_parser(cmdline6).options(desc: desc3).run(); |
107 | |
108 | variables_map vm4; |
109 | store(options: a6, m&: vm4); |
110 | notify(m&: 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; |
118 | void notifier(const vector<int>& v) |
119 | { |
120 | stored_value = v.front(); |
121 | } |
122 | |
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(f: ¬ifier)) |
132 | ; |
133 | |
134 | |
135 | parsed_options parsed(&desc); |
136 | vector<option>& options = parsed.options; |
137 | vector<string> v; |
138 | v.push_back(x: "q" ); |
139 | options.push_back(x: option("foo" , vector<string>(1, "1" ))); |
140 | options.push_back(x: option("biz" , vector<string>(1, "a" ))); |
141 | options.push_back(x: option("baz" , v)); |
142 | options.push_back(x: option("bar" , vector<string>(1, "1" ))); |
143 | options.push_back(x: option("biz" , vector<string>(1, "b x" ))); |
144 | v.push_back(x: "w" ); |
145 | options.push_back(x: option("baz" , v)); |
146 | |
147 | variables_map vm; |
148 | store(options: parsed, m&: vm); |
149 | notify(m&: 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(x: option("int" , vector<string>(1, "13" ))); |
160 | |
161 | variables_map vm2; |
162 | store(options: parsed, m&: vm2); |
163 | notify(m&: 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(x: 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(x: option("int" , vector<string>(1, "2 3" ))); |
178 | variables_map vm4; |
179 | BOOST_CHECK_THROW(store(parsed, vm4), validation_error); |
180 | } |
181 | |
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(v: vector<int>(1, 1), textual: "" )) |
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(array: cmdline1_, |
199 | size: 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(array: cmdline2_, |
205 | size: sizeof(cmdline2_)/sizeof(const char*)); |
206 | |
207 | parsed_options p2 = command_line_parser(cmdline2).options(desc).run(); |
208 | |
209 | variables_map vm; |
210 | store(options: p1, m&: 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(options: p2, m&: 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 | |
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(x: "--help" ); |
258 | input1.push_back(x: "--includes=a" ); |
259 | parsed_options p1 = command_line_parser(input1).options(desc: desc1).run(); |
260 | |
261 | vector<string> input2; |
262 | input1.push_back(x: "--output" ); |
263 | parsed_options p2 = command_line_parser(input2).options(desc: desc2).run(); |
264 | |
265 | vector<string> input3; |
266 | input3.push_back(x: "--includes=b" ); |
267 | parsed_options p3 = command_line_parser(input3).options(desc: desc1).run(); |
268 | |
269 | |
270 | variables_map vm; |
271 | store(options: p1, m&: vm); |
272 | store(options: p2, m&: vm); |
273 | store(options: p3, m&: 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 | |
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 | |