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