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 | #include <boost/program_options/cmdline.hpp> |
7 | #include <boost/program_options/options_description.hpp> |
8 | #include <boost/program_options/detail/cmdline.hpp> |
9 | using namespace boost::program_options; |
10 | using boost::program_options::detail::cmdline; |
11 | |
12 | #include <iostream> |
13 | #include <sstream> |
14 | #include <vector> |
15 | #include <cassert> |
16 | using namespace std; |
17 | |
18 | #include "minitest.hpp" |
19 | |
20 | /* To facilitate testing, declare a number of error codes. Otherwise, |
21 | we'd have to specify the type of exception that should be thrown. |
22 | */ |
23 | |
24 | const int s_success = 0; |
25 | const int s_unknown_option = 1; |
26 | const int s_ambiguous_option = 2; |
27 | const int s_long_not_allowed = 3; |
28 | const int s_long_adjacent_not_allowed = 4; |
29 | const int s_short_adjacent_not_allowed = 5; |
30 | const int s_empty_adjacent_parameter = 6; |
31 | const int s_missing_parameter = 7; |
32 | const int = 8; |
33 | const int s_unrecognized_line = 9; |
34 | |
35 | int translate_syntax_error_kind(invalid_command_line_syntax::kind_t k) |
36 | { |
37 | invalid_command_line_syntax::kind_t table[] = { |
38 | invalid_command_line_syntax::long_not_allowed, |
39 | invalid_command_line_syntax::long_adjacent_not_allowed, |
40 | invalid_command_line_syntax::short_adjacent_not_allowed, |
41 | invalid_command_line_syntax::empty_adjacent_parameter, |
42 | invalid_command_line_syntax::missing_parameter, |
43 | invalid_command_line_syntax::extra_parameter, |
44 | invalid_command_line_syntax::unrecognized_line |
45 | }; |
46 | invalid_command_line_syntax::kind_t *b, *e, *i; |
47 | b = table; |
48 | e = table + sizeof(table)/sizeof(table[0]); |
49 | i = std::find(first: b, last: e, val: k); |
50 | assert(i != e); |
51 | return std::distance(first: b, last: i) + 3; |
52 | } |
53 | |
54 | struct test_case { |
55 | const char* input; |
56 | int expected_status; |
57 | const char* expected_result; |
58 | }; |
59 | |
60 | |
61 | /* Parses the syntax description in 'syntax' and initialized |
62 | 'cmd' accordingly' |
63 | The "boost::program_options" in parameter type is needed because CW9 |
64 | has std::detail and it causes an ambiguity. |
65 | */ |
66 | void apply_syntax(options_description& desc, |
67 | const char* syntax) |
68 | { |
69 | |
70 | string s; |
71 | stringstream ss; |
72 | ss << syntax; |
73 | while(ss >> s) { |
74 | value_semantic* v = 0; |
75 | |
76 | if (*(s.end()-1) == '=') { |
77 | v = value<string>(); |
78 | s.resize(n: s.size()-1); |
79 | } else if (*(s.end()-1) == '?') { |
80 | v = value<string>()->implicit_value(v: "default" ); |
81 | s.resize(n: s.size()-1); |
82 | } else if (*(s.end()-1) == '*') { |
83 | v = value<vector<string> >()->multitoken(); |
84 | s.resize(n: s.size()-1); |
85 | } else if (*(s.end()-1) == '+') { |
86 | v = value<vector<string> >()->multitoken(); |
87 | s.resize(n: s.size()-1); |
88 | } |
89 | if (v) { |
90 | desc.add_options() |
91 | (s.c_str(), v, "" ); |
92 | } else { |
93 | desc.add_options() |
94 | (s.c_str(), "" ); |
95 | } |
96 | } |
97 | } |
98 | |
99 | void test_cmdline(const char* syntax, |
100 | command_line_style::style_t style, |
101 | const test_case* cases) |
102 | { |
103 | for (int i = 0; cases[i].input; ++i) { |
104 | // Parse input |
105 | vector<string> xinput; |
106 | { |
107 | string s; |
108 | stringstream ss; |
109 | ss << cases[i].input; |
110 | while (ss >> s) { |
111 | xinput.push_back(x: s); |
112 | } |
113 | } |
114 | options_description desc; |
115 | apply_syntax(desc, syntax); |
116 | |
117 | cmdline cmd(xinput); |
118 | cmd.style(style); |
119 | cmd.set_options_description(desc); |
120 | |
121 | |
122 | string result; |
123 | int status = 0; |
124 | |
125 | try { |
126 | vector<option> options = cmd.run(); |
127 | |
128 | for(unsigned j = 0; j < options.size(); ++j) |
129 | { |
130 | option opt = options[j]; |
131 | |
132 | if (opt.position_key != -1) { |
133 | if (!result.empty()) |
134 | result += " " ; |
135 | result += opt.value[0]; |
136 | } else { |
137 | if (!result.empty()) |
138 | result += " " ; |
139 | result += opt.string_key + ":" ; |
140 | for (size_t k = 0; k < opt.value.size(); ++k) { |
141 | if (k != 0) |
142 | result += "-" ; |
143 | result += opt.value[k]; |
144 | } |
145 | } |
146 | } |
147 | } |
148 | catch(unknown_option&) { |
149 | status = s_unknown_option; |
150 | } |
151 | catch(ambiguous_option&) { |
152 | status = s_ambiguous_option; |
153 | } |
154 | catch(invalid_command_line_syntax& e) { |
155 | status = translate_syntax_error_kind(k: e.kind()); |
156 | } |
157 | BOOST_CHECK_EQUAL(status, cases[i].expected_status); |
158 | BOOST_CHECK_EQUAL(result, cases[i].expected_result); |
159 | } |
160 | } |
161 | |
162 | void test_long_options() |
163 | { |
164 | using namespace command_line_style; |
165 | cmdline::style_t style = cmdline::style_t( |
166 | allow_long | long_allow_adjacent); |
167 | |
168 | test_case test_cases1[] = { |
169 | // Test that long options are recognized and everything else |
170 | // is treated like arguments |
171 | {.input: "--foo foo -123 /asd" , .expected_status: s_success, .expected_result: "foo: foo -123 /asd" }, |
172 | |
173 | // Unknown option |
174 | {.input: "--unk" , .expected_status: s_unknown_option, .expected_result: "" }, |
175 | |
176 | // Test that abbreviated names do not work |
177 | {.input: "--fo" , .expected_status: s_unknown_option, .expected_result: "" }, |
178 | |
179 | // Test for disallowed parameter |
180 | {.input: "--foo=13" , .expected_status: s_extra_parameter, .expected_result: "" }, |
181 | |
182 | // Test option with required parameter |
183 | {.input: "--bar=" , .expected_status: s_empty_adjacent_parameter, .expected_result: "" }, |
184 | {.input: "--bar" , .expected_status: s_missing_parameter, .expected_result: "" }, |
185 | |
186 | {.input: "--bar=123" , .expected_status: s_success, .expected_result: "bar:123" }, |
187 | {.input: 0, .expected_status: 0, .expected_result: 0} |
188 | }; |
189 | test_cmdline(syntax: "foo bar=" , style, cases: test_cases1); |
190 | |
191 | |
192 | style = cmdline::style_t( |
193 | allow_long | long_allow_next); |
194 | |
195 | test_case test_cases2[] = { |
196 | {.input: "--bar 10" , .expected_status: s_success, .expected_result: "bar:10" }, |
197 | {.input: "--bar" , .expected_status: s_missing_parameter, .expected_result: "" }, |
198 | // Since --bar accepts a parameter, --foo is |
199 | // considered a value, even though it looks like |
200 | // an option. |
201 | {.input: "--bar --foo" , .expected_status: s_success, .expected_result: "bar:--foo" }, |
202 | {.input: 0, .expected_status: 0, .expected_result: 0} |
203 | }; |
204 | test_cmdline(syntax: "foo bar=" , style, cases: test_cases2); |
205 | style = cmdline::style_t( |
206 | allow_long | long_allow_adjacent |
207 | | long_allow_next); |
208 | |
209 | test_case test_cases3[] = { |
210 | {.input: "--bar=10" , .expected_status: s_success, .expected_result: "bar:10" }, |
211 | {.input: "--bar 11" , .expected_status: s_success, .expected_result: "bar:11" }, |
212 | {.input: 0, .expected_status: 0, .expected_result: 0} |
213 | }; |
214 | test_cmdline(syntax: "foo bar=" , style, cases: test_cases3); |
215 | |
216 | style = cmdline::style_t( |
217 | allow_long | long_allow_adjacent |
218 | | long_allow_next | case_insensitive); |
219 | |
220 | // Test case insensitive style. |
221 | // Note that option names are normalized to lower case. |
222 | test_case test_cases4[] = { |
223 | {.input: "--foo" , .expected_status: s_success, .expected_result: "foo:" }, |
224 | {.input: "--Foo" , .expected_status: s_success, .expected_result: "foo:" }, |
225 | {.input: "--bar=Ab" , .expected_status: s_success, .expected_result: "bar:Ab" }, |
226 | {.input: "--Bar=ab" , .expected_status: s_success, .expected_result: "bar:ab" }, |
227 | {.input: "--giz" , .expected_status: s_success, .expected_result: "Giz:" }, |
228 | {.input: 0, .expected_status: 0, .expected_result: 0} |
229 | }; |
230 | test_cmdline(syntax: "foo bar= baz? Giz" , style, cases: test_cases4); |
231 | } |
232 | |
233 | void test_short_options() |
234 | { |
235 | using namespace command_line_style; |
236 | cmdline::style_t style; |
237 | |
238 | style = cmdline::style_t( |
239 | allow_short | allow_dash_for_short |
240 | | short_allow_adjacent); |
241 | |
242 | test_case test_cases1[] = { |
243 | {.input: "-d d /bar" , .expected_status: s_success, .expected_result: "-d: d /bar" }, |
244 | // This is treated as error when long options are disabled |
245 | {.input: "--foo" , .expected_status: s_success, .expected_result: "--foo" }, |
246 | {.input: "-d13" , .expected_status: s_extra_parameter, .expected_result: "" }, |
247 | {.input: "-f14" , .expected_status: s_success, .expected_result: "-f:14" }, |
248 | {.input: "-g -f1" , .expected_status: s_success, .expected_result: "-g: -f:1" }, |
249 | {.input: "-f" , .expected_status: s_missing_parameter, .expected_result: "" }, |
250 | {.input: 0, .expected_status: 0, .expected_result: 0} |
251 | }; |
252 | test_cmdline(syntax: ",d ,f= ,g" , style, cases: test_cases1); |
253 | |
254 | style = cmdline::style_t( |
255 | allow_short | allow_dash_for_short |
256 | | short_allow_next); |
257 | |
258 | test_case test_cases2[] = { |
259 | {.input: "-f 13" , .expected_status: s_success, .expected_result: "-f:13" }, |
260 | {.input: "-f -13" , .expected_status: s_success, .expected_result: "-f:-13" }, |
261 | {.input: "-f" , .expected_status: s_missing_parameter, .expected_result: "" }, |
262 | {.input: "-f /foo" , .expected_status: s_success, .expected_result: "-f:/foo" }, |
263 | {.input: "-f -d" , .expected_status: s_missing_parameter, .expected_result: "" }, |
264 | {.input: 0, .expected_status: 0, .expected_result: 0} |
265 | }; |
266 | test_cmdline(syntax: ",d ,f=" , style, cases: test_cases2); |
267 | |
268 | style = cmdline::style_t( |
269 | allow_short | short_allow_next |
270 | | allow_dash_for_short | short_allow_adjacent); |
271 | |
272 | test_case test_cases3[] = { |
273 | {.input: "-f10" , .expected_status: s_success, .expected_result: "-f:10" }, |
274 | {.input: "-f 10" , .expected_status: s_success, .expected_result: "-f:10" }, |
275 | {.input: "-f -d" , .expected_status: s_missing_parameter, .expected_result: "" }, |
276 | {.input: 0, .expected_status: 0, .expected_result: 0} |
277 | }; |
278 | test_cmdline(syntax: ",d ,f=" , style, cases: test_cases3); |
279 | |
280 | style = cmdline::style_t( |
281 | allow_short | short_allow_next |
282 | | allow_dash_for_short |
283 | | short_allow_adjacent | allow_sticky); |
284 | |
285 | test_case test_cases4[] = { |
286 | {.input: "-de" , .expected_status: s_success, .expected_result: "-d: -e:" }, |
287 | {.input: "-df10" , .expected_status: s_success, .expected_result: "-d: -f:10" }, |
288 | // FIXME: review |
289 | //{"-d12", s_extra_parameter, ""}, |
290 | {.input: "-f12" , .expected_status: s_success, .expected_result: "-f:12" }, |
291 | {.input: "-fe" , .expected_status: s_success, .expected_result: "-f:e" }, |
292 | {.input: 0, .expected_status: 0, .expected_result: 0} |
293 | }; |
294 | test_cmdline(syntax: ",d ,f= ,e" , style, cases: test_cases4); |
295 | |
296 | } |
297 | |
298 | |
299 | void test_dos_options() |
300 | { |
301 | using namespace command_line_style; |
302 | cmdline::style_t style; |
303 | |
304 | style = cmdline::style_t( |
305 | allow_short |
306 | | allow_slash_for_short | short_allow_adjacent); |
307 | |
308 | test_case test_cases1[] = { |
309 | {.input: "/d d -bar" , .expected_status: s_success, .expected_result: "-d: d -bar" }, |
310 | {.input: "--foo" , .expected_status: s_success, .expected_result: "--foo" }, |
311 | {.input: "/d13" , .expected_status: s_extra_parameter, .expected_result: "" }, |
312 | {.input: "/f14" , .expected_status: s_success, .expected_result: "-f:14" }, |
313 | {.input: "/f" , .expected_status: s_missing_parameter, .expected_result: "" }, |
314 | {.input: 0, .expected_status: 0, .expected_result: 0} |
315 | }; |
316 | test_cmdline(syntax: ",d ,f=" , style, cases: test_cases1); |
317 | |
318 | style = cmdline::style_t( |
319 | allow_short |
320 | | allow_slash_for_short | short_allow_next |
321 | | short_allow_adjacent | allow_sticky); |
322 | |
323 | test_case test_cases2[] = { |
324 | {.input: "/de" , .expected_status: s_extra_parameter, .expected_result: "" }, |
325 | {.input: "/fe" , .expected_status: s_success, .expected_result: "-f:e" }, |
326 | {.input: 0, .expected_status: 0, .expected_result: 0} |
327 | }; |
328 | test_cmdline(syntax: ",d ,f= ,e" , style, cases: test_cases2); |
329 | |
330 | } |
331 | |
332 | |
333 | void test_disguised_long() |
334 | { |
335 | using namespace command_line_style; |
336 | cmdline::style_t style; |
337 | |
338 | style = cmdline::style_t( |
339 | allow_short | short_allow_adjacent |
340 | | allow_dash_for_short |
341 | | short_allow_next | allow_long_disguise |
342 | | long_allow_adjacent); |
343 | |
344 | test_case test_cases1[] = { |
345 | {.input: "-foo -f" , .expected_status: s_success, .expected_result: "foo: foo:" }, |
346 | {.input: "-goo=x -gy" , .expected_status: s_success, .expected_result: "goo:x goo:y" }, |
347 | {.input: "-bee=x -by" , .expected_status: s_success, .expected_result: "bee:x bee:y" }, |
348 | {.input: 0, .expected_status: 0, .expected_result: 0} |
349 | }; |
350 | test_cmdline(syntax: "foo,f goo,g= bee,b?" , style, cases: test_cases1); |
351 | |
352 | style = cmdline::style_t(style | allow_slash_for_short); |
353 | test_case test_cases2[] = { |
354 | {.input: "/foo -f" , .expected_status: s_success, .expected_result: "foo: foo:" }, |
355 | {.input: "/goo=x" , .expected_status: s_success, .expected_result: "goo:x" }, |
356 | {.input: 0, .expected_status: 0, .expected_result: 0} |
357 | }; |
358 | test_cmdline(syntax: "foo,f goo,g= bee,b?" , style, cases: test_cases2); |
359 | } |
360 | |
361 | void test_guessing() |
362 | { |
363 | using namespace command_line_style; |
364 | cmdline::style_t style; |
365 | |
366 | style = cmdline::style_t( |
367 | allow_short | short_allow_adjacent |
368 | | allow_dash_for_short |
369 | | allow_long | long_allow_adjacent |
370 | | allow_guessing | allow_long_disguise); |
371 | |
372 | test_case test_cases1[] = { |
373 | {.input: "--opt1" , .expected_status: s_success, .expected_result: "opt123:" }, |
374 | {.input: "--opt" , .expected_status: s_ambiguous_option, .expected_result: "" }, |
375 | {.input: "--f=1" , .expected_status: s_success, .expected_result: "foo:1" }, |
376 | {.input: "-far" , .expected_status: s_success, .expected_result: "foo:ar" }, |
377 | {.input: 0, .expected_status: 0, .expected_result: 0} |
378 | }; |
379 | test_cmdline(syntax: "opt123 opt56 foo,f=" , style, cases: test_cases1); |
380 | |
381 | test_case test_cases2[] = { |
382 | {.input: "--fname file --fname2 file2" , .expected_status: s_success, .expected_result: "fname: file fname2: file2" }, |
383 | {.input: "--fnam file --fnam file2" , .expected_status: s_ambiguous_option, .expected_result: "" }, |
384 | {.input: "--fnam file --fname2 file2" , .expected_status: s_ambiguous_option, .expected_result: "" }, |
385 | {.input: "--fname2 file2 --fnam file" , .expected_status: s_ambiguous_option, .expected_result: "" }, |
386 | {.input: 0, .expected_status: 0, .expected_result: 0} |
387 | }; |
388 | test_cmdline(syntax: "fname fname2" , style, cases: test_cases2); |
389 | } |
390 | |
391 | void test_arguments() |
392 | { |
393 | using namespace command_line_style; |
394 | cmdline::style_t style; |
395 | |
396 | style = cmdline::style_t( |
397 | allow_short | allow_long |
398 | | allow_dash_for_short |
399 | | short_allow_adjacent | long_allow_adjacent); |
400 | |
401 | test_case test_cases1[] = { |
402 | {.input: "-f file -gx file2" , .expected_status: s_success, .expected_result: "-f: file -g:x file2" }, |
403 | {.input: "-f - -gx - -- -e" , .expected_status: s_success, .expected_result: "-f: - -g:x - -e" }, |
404 | {.input: 0, .expected_status: 0, .expected_result: 0} |
405 | }; |
406 | test_cmdline(syntax: ",f ,g= ,e" , style, cases: test_cases1); |
407 | |
408 | // "--" should stop options regardless of whether long options are |
409 | // allowed or not. |
410 | |
411 | style = cmdline::style_t( |
412 | allow_short | short_allow_adjacent |
413 | | allow_dash_for_short); |
414 | |
415 | test_case test_cases2[] = { |
416 | {.input: "-f - -gx - -- -e" , .expected_status: s_success, .expected_result: "-f: - -g:x - -e" }, |
417 | {.input: 0, .expected_status: 0, .expected_result: 0} |
418 | }; |
419 | test_cmdline(syntax: ",f ,g= ,e" , style, cases: test_cases2); |
420 | } |
421 | |
422 | void test_prefix() |
423 | { |
424 | using namespace command_line_style; |
425 | cmdline::style_t style; |
426 | |
427 | style = cmdline::style_t( |
428 | allow_short | allow_long |
429 | | allow_dash_for_short |
430 | | short_allow_adjacent | long_allow_adjacent |
431 | ); |
432 | |
433 | test_case test_cases1[] = { |
434 | {.input: "--foo.bar=12" , .expected_status: s_success, .expected_result: "foo.bar:12" }, |
435 | {.input: 0, .expected_status: 0, .expected_result: 0} |
436 | }; |
437 | |
438 | test_cmdline(syntax: "foo*=" , style, cases: test_cases1); |
439 | } |
440 | |
441 | |
442 | pair<string, string> at_option_parser(string const&s) |
443 | { |
444 | if ('@' == s[0]) |
445 | return std::make_pair(x: string("response-file" ), y: s.substr(pos: 1)); |
446 | else |
447 | return pair<string, string>(); |
448 | } |
449 | |
450 | pair<string, string> at_option_parser_broken(string const&s) |
451 | { |
452 | if ('@' == s[0]) |
453 | return std::make_pair(x: string("some garbage" ), y: s.substr(pos: 1)); |
454 | else |
455 | return pair<string, string>(); |
456 | } |
457 | |
458 | |
459 | |
460 | void test_additional_parser() |
461 | { |
462 | options_description desc; |
463 | desc.add_options() |
464 | ("response-file" , value<string>(), "response file" ) |
465 | ("foo" , value<int>(), "foo" ) |
466 | ("bar,baz" , value<int>(), "bar" ) |
467 | ; |
468 | |
469 | vector<string> input; |
470 | input.push_back(x: "@config" ); |
471 | input.push_back(x: "--foo=1" ); |
472 | input.push_back(x: "--baz=11" ); |
473 | |
474 | cmdline cmd(input); |
475 | cmd.set_options_description(desc); |
476 | cmd.set_additional_parser(at_option_parser); |
477 | |
478 | vector<option> result = cmd.run(); |
479 | |
480 | BOOST_REQUIRE(result.size() == 3); |
481 | BOOST_CHECK_EQUAL(result[0].string_key, "response-file" ); |
482 | BOOST_CHECK_EQUAL(result[0].value[0], "config" ); |
483 | BOOST_CHECK_EQUAL(result[1].string_key, "foo" ); |
484 | BOOST_CHECK_EQUAL(result[1].value[0], "1" ); |
485 | BOOST_CHECK_EQUAL(result[2].string_key, "bar" ); |
486 | BOOST_CHECK_EQUAL(result[2].value[0], "11" ); |
487 | |
488 | // Test that invalid options returned by additional style |
489 | // parser are detected. |
490 | cmdline cmd2(input); |
491 | cmd2.set_options_description(desc); |
492 | cmd2.set_additional_parser(at_option_parser_broken); |
493 | |
494 | BOOST_CHECK_THROW(cmd2.run(), unknown_option); |
495 | |
496 | } |
497 | |
498 | vector<option> at_option_parser2(vector<string>& args) |
499 | { |
500 | vector<option> result; |
501 | if ('@' == args[0][0]) { |
502 | // Simulate reading the response file. |
503 | result.push_back(x: option("foo" , vector<string>(1, "1" ))); |
504 | result.push_back(x: option("bar" , vector<string>(1, "1" ))); |
505 | args.erase(position: args.begin()); |
506 | } |
507 | return result; |
508 | } |
509 | |
510 | |
511 | void test_style_parser() |
512 | { |
513 | options_description desc; |
514 | desc.add_options() |
515 | ("foo" , value<int>(), "foo" ) |
516 | ("bar" , value<int>(), "bar" ) |
517 | ; |
518 | |
519 | vector<string> input; |
520 | input.push_back(x: "@config" ); |
521 | |
522 | cmdline cmd(input); |
523 | cmd.set_options_description(desc); |
524 | cmd.extra_style_parser(s: at_option_parser2); |
525 | |
526 | vector<option> result = cmd.run(); |
527 | |
528 | BOOST_REQUIRE(result.size() == 2); |
529 | BOOST_CHECK_EQUAL(result[0].string_key, "foo" ); |
530 | BOOST_CHECK_EQUAL(result[0].value[0], "1" ); |
531 | BOOST_CHECK_EQUAL(result[1].string_key, "bar" ); |
532 | BOOST_CHECK_EQUAL(result[1].value[0], "1" ); |
533 | } |
534 | |
535 | void test_unregistered() |
536 | { |
537 | // Check unregisted option when no options are registed at all. |
538 | options_description desc; |
539 | |
540 | vector<string> input; |
541 | input.push_back(x: "--foo=1" ); |
542 | input.push_back(x: "--bar" ); |
543 | input.push_back(x: "1" ); |
544 | input.push_back(x: "-b" ); |
545 | input.push_back(x: "-biz" ); |
546 | |
547 | cmdline cmd(input); |
548 | cmd.set_options_description(desc); |
549 | cmd.allow_unregistered(); |
550 | |
551 | vector<option> result = cmd.run(); |
552 | BOOST_REQUIRE(result.size() == 5); |
553 | // --foo=1 |
554 | BOOST_CHECK_EQUAL(result[0].string_key, "foo" ); |
555 | BOOST_CHECK_EQUAL(result[0].unregistered, true); |
556 | BOOST_CHECK_EQUAL(result[0].value[0], "1" ); |
557 | // --bar |
558 | BOOST_CHECK_EQUAL(result[1].string_key, "bar" ); |
559 | BOOST_CHECK_EQUAL(result[1].unregistered, true); |
560 | BOOST_CHECK(result[1].value.empty()); |
561 | // '1' is considered a positional option, not a value to |
562 | // --bar |
563 | BOOST_CHECK(result[2].string_key.empty()); |
564 | BOOST_CHECK(result[2].position_key == 0); |
565 | BOOST_CHECK_EQUAL(result[2].unregistered, false); |
566 | BOOST_CHECK_EQUAL(result[2].value[0], "1" ); |
567 | // -b |
568 | BOOST_CHECK_EQUAL(result[3].string_key, "-b" ); |
569 | BOOST_CHECK_EQUAL(result[3].unregistered, true); |
570 | BOOST_CHECK(result[3].value.empty()); |
571 | // -biz |
572 | BOOST_CHECK_EQUAL(result[4].string_key, "-b" ); |
573 | BOOST_CHECK_EQUAL(result[4].unregistered, true); |
574 | BOOST_CHECK_EQUAL(result[4].value[0], "iz" ); |
575 | |
576 | // Check sticky short options together with unregisted options. |
577 | |
578 | desc.add_options() |
579 | ("help,h" , "" ) |
580 | ("magic,m" , value<string>(), "" ) |
581 | ; |
582 | |
583 | input.clear(); |
584 | input.push_back(x: "-hc" ); |
585 | input.push_back(x: "-mc" ); |
586 | |
587 | |
588 | cmdline cmd2(input); |
589 | cmd2.set_options_description(desc); |
590 | cmd2.allow_unregistered(); |
591 | |
592 | result = cmd2.run(); |
593 | |
594 | BOOST_REQUIRE(result.size() == 3); |
595 | BOOST_CHECK_EQUAL(result[0].string_key, "help" ); |
596 | BOOST_CHECK_EQUAL(result[0].unregistered, false); |
597 | BOOST_CHECK(result[0].value.empty()); |
598 | BOOST_CHECK_EQUAL(result[1].string_key, "-c" ); |
599 | BOOST_CHECK_EQUAL(result[1].unregistered, true); |
600 | BOOST_CHECK(result[1].value.empty()); |
601 | BOOST_CHECK_EQUAL(result[2].string_key, "magic" ); |
602 | BOOST_CHECK_EQUAL(result[2].unregistered, false); |
603 | BOOST_CHECK_EQUAL(result[2].value[0], "c" ); |
604 | |
605 | // CONSIDER: |
606 | // There's a corner case: |
607 | // -foo |
608 | // when 'allow_long_disguise' is set. Should this be considered |
609 | // disguised long option 'foo' or short option '-f' with value 'oo'? |
610 | // It's not clear yet, so I'm leaving the decision till later. |
611 | } |
612 | |
613 | void test_implicit_value() |
614 | { |
615 | using namespace command_line_style; |
616 | cmdline::style_t style; |
617 | |
618 | style = cmdline::style_t( |
619 | allow_long | long_allow_adjacent |
620 | ); |
621 | |
622 | test_case test_cases1[] = { |
623 | // 'bar' does not even look like option, so is consumed |
624 | {.input: "--foo bar" , .expected_status: s_success, .expected_result: "foo:bar" }, |
625 | // '--bar' looks like option, and such option exists, so we don't consume this token |
626 | {.input: "--foo --bar" , .expected_status: s_success, .expected_result: "foo: bar:" }, |
627 | // '--biz' looks like option, but does not match any existing one. |
628 | // Presently this results in parse error, since |
629 | // (1) in cmdline.cpp:finish_option, we only consume following tokens if they are |
630 | // requires |
631 | // (2) in cmdline.cpp:run, we let options consume following positional options |
632 | // For --biz, an exception is thrown between 1 and 2. |
633 | // We might want to fix that in future. |
634 | {.input: "--foo --biz" , .expected_status: s_unknown_option, .expected_result: "" }, |
635 | {.input: 0, .expected_status: 0, .expected_result: 0} |
636 | }; |
637 | |
638 | test_cmdline(syntax: "foo? bar?" , style, cases: test_cases1); |
639 | } |
640 | |
641 | int main(int /*ac*/, char** /*av*/) |
642 | { |
643 | test_long_options(); |
644 | test_short_options(); |
645 | test_dos_options(); |
646 | test_disguised_long(); |
647 | test_guessing(); |
648 | test_arguments(); |
649 | test_prefix(); |
650 | test_additional_parser(); |
651 | test_style_parser(); |
652 | test_unregistered(); |
653 | test_implicit_value(); |
654 | |
655 | return 0; |
656 | } |
657 | |