1 use std::ffi::OsString;
2
3 use super::utils;
4
5 use clap::{arg, error::ErrorKind, Arg, ArgAction, Command};
6
7 static ALLOW_EXT_SC: &str = "\
8 Usage: clap-test [COMMAND]
9
10 Options:
11 -h, --help Print help
12 -V, --version Print version
13 ";
14
15 static DONT_COLLAPSE_ARGS: &str = "\
16 Usage: clap-test [arg1] [arg2] [arg3]
17
18 Arguments:
19 [arg1] some
20 [arg2] some
21 [arg3] some
22
23 Options:
24 -h, --help Print help
25 -V, --version Print version
26 ";
27
28 #[test]
sub_command_negate_required()29 fn sub_command_negate_required() {
30 Command::new("sub_command_negate")
31 .subcommand_negates_reqs(true)
32 .arg(Arg::new("test").required(true).index(1))
33 .subcommand(Command::new("sub1"))
34 .try_get_matches_from(vec!["myprog", "sub1"])
35 .unwrap();
36 }
37
38 #[test]
sub_command_negate_required_2()39 fn sub_command_negate_required_2() {
40 let result = Command::new("sub_command_negate")
41 .subcommand_negates_reqs(true)
42 .arg(Arg::new("test").required(true).index(1))
43 .subcommand(Command::new("sub1"))
44 .try_get_matches_from(vec![""]);
45 assert!(result.is_err());
46 let err = result.err().unwrap();
47 assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
48 }
49
50 #[test]
sub_command_required()51 fn sub_command_required() {
52 let result = Command::new("sc_required")
53 .subcommand_required(true)
54 .subcommand(Command::new("sub1"))
55 .try_get_matches_from(vec![""]);
56 assert!(result.is_err());
57 let err = result.err().unwrap();
58 assert_eq!(err.kind(), ErrorKind::MissingSubcommand);
59 }
60
61 #[test]
62 #[cfg(feature = "error-context")]
sub_command_required_error()63 fn sub_command_required_error() {
64 static ERROR: &str = "\
65 error: 'sc_required' requires a subcommand but one was not provided
66 [subcommands: sub1, help]
67
68 Usage: sc_required <COMMAND>
69
70 For more information, try '--help'.
71 ";
72
73 let cmd = Command::new("sc_required")
74 .subcommand_required(true)
75 .subcommand(Command::new("sub1"));
76 utils::assert_output(cmd, "sc_required", ERROR, true);
77 }
78
79 #[test]
arg_required_else_help()80 fn arg_required_else_help() {
81 let result = Command::new("arg_required")
82 .arg_required_else_help(true)
83 .arg(Arg::new("test").index(1))
84 .try_get_matches_from(vec![""]);
85
86 assert!(result.is_err());
87 let err = result.err().unwrap();
88 assert_eq!(
89 err.kind(),
90 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
91 );
92 }
93
94 #[test]
arg_required_else_help_over_req_arg()95 fn arg_required_else_help_over_req_arg() {
96 let result = Command::new("arg_required")
97 .arg_required_else_help(true)
98 .arg(Arg::new("test").index(1).required(true))
99 .try_get_matches_from(vec![""]);
100
101 assert!(result.is_err());
102 let err = result.err().unwrap();
103 assert_eq!(
104 err.kind(),
105 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
106 );
107 }
108
109 #[test]
arg_required_else_help_over_req_subcommand()110 fn arg_required_else_help_over_req_subcommand() {
111 let result = Command::new("sub_required")
112 .arg_required_else_help(true)
113 .subcommand_required(true)
114 .subcommand(Command::new("sub1"))
115 .try_get_matches_from(vec![""]);
116
117 assert!(result.is_err());
118 let err = result.err().unwrap();
119 assert_eq!(
120 err.kind(),
121 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
122 );
123 }
124
125 #[test]
arg_required_else_help_with_default()126 fn arg_required_else_help_with_default() {
127 let result = Command::new("arg_required")
128 .arg_required_else_help(true)
129 .arg(arg!(--input <PATH>).default_value("-"))
130 .try_get_matches_from(vec![""]);
131
132 assert!(result.is_err());
133 let err = result.err().unwrap();
134 assert_eq!(
135 err.kind(),
136 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
137 );
138 }
139
140 #[test]
arg_required_else_help_error_message()141 fn arg_required_else_help_error_message() {
142 static ARG_REQUIRED_ELSE_HELP: &str = "\
143 Usage: test [OPTIONS]
144
145 Options:
146 -i, --info Provides more info
147 -h, --help Print help
148 -V, --version Print version
149 ";
150
151 let cmd = Command::new("test")
152 .arg_required_else_help(true)
153 .version("1.0")
154 .arg(
155 Arg::new("info")
156 .help("Provides more info")
157 .short('i')
158 .long("info")
159 .action(ArgAction::SetTrue),
160 );
161 utils::assert_output(
162 cmd,
163 "test",
164 ARG_REQUIRED_ELSE_HELP,
165 true, // Unlike normal displaying of help, we should provide a fatal exit code
166 );
167 }
168
169 #[cfg(not(feature = "suggestions"))]
170 #[test]
infer_subcommands_fail_no_args()171 fn infer_subcommands_fail_no_args() {
172 let m = Command::new("prog")
173 .infer_subcommands(true)
174 .subcommand(Command::new("test"))
175 .subcommand(Command::new("temp"))
176 .try_get_matches_from(vec!["prog", "te"]);
177 assert!(m.is_err(), "{:#?}", m.unwrap());
178 assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
179 }
180
181 #[cfg(feature = "suggestions")]
182 #[test]
infer_subcommands_fail_no_args()183 fn infer_subcommands_fail_no_args() {
184 let m = Command::new("prog")
185 .infer_subcommands(true)
186 .subcommand(Command::new("test"))
187 .subcommand(Command::new("temp"))
188 .try_get_matches_from(vec!["prog", "te"]);
189 assert!(m.is_err(), "{:#?}", m.unwrap());
190 assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
191 }
192
193 #[test]
infer_subcommands_fail_with_args()194 fn infer_subcommands_fail_with_args() {
195 let m = Command::new("prog")
196 .infer_subcommands(true)
197 .arg(Arg::new("some"))
198 .subcommand(Command::new("test"))
199 .subcommand(Command::new("temp"))
200 .try_get_matches_from(vec!["prog", "t"]);
201 assert!(m.is_ok(), "{:?}", m.unwrap_err().kind());
202 assert_eq!(
203 m.unwrap().get_one::<String>("some").map(|v| v.as_str()),
204 Some("t")
205 );
206 }
207
208 #[test]
infer_subcommands_fail_with_args2()209 fn infer_subcommands_fail_with_args2() {
210 let m = Command::new("prog")
211 .infer_subcommands(true)
212 .arg(Arg::new("some"))
213 .subcommand(Command::new("test"))
214 .subcommand(Command::new("temp"))
215 .try_get_matches_from(vec!["prog", "te"]);
216 assert!(m.is_ok(), "{:?}", m.unwrap_err().kind());
217 assert_eq!(
218 m.unwrap().get_one::<String>("some").map(|v| v.as_str()),
219 Some("te")
220 );
221 }
222
223 #[test]
infer_subcommands_pass()224 fn infer_subcommands_pass() {
225 let m = Command::new("prog")
226 .infer_subcommands(true)
227 .subcommand(Command::new("test"))
228 .try_get_matches_from(vec!["prog", "te"])
229 .unwrap();
230 assert_eq!(m.subcommand_name(), Some("test"));
231 }
232
233 #[test]
infer_subcommands_pass_close()234 fn infer_subcommands_pass_close() {
235 let m = Command::new("prog")
236 .infer_subcommands(true)
237 .subcommand(Command::new("test"))
238 .subcommand(Command::new("temp"))
239 .try_get_matches_from(vec!["prog", "tes"])
240 .unwrap();
241 assert_eq!(m.subcommand_name(), Some("test"));
242 }
243
244 #[test]
infer_subcommands_pass_exact_match()245 fn infer_subcommands_pass_exact_match() {
246 let m = Command::new("prog")
247 .infer_subcommands(true)
248 .subcommand(Command::new("test"))
249 .subcommand(Command::new("testa"))
250 .subcommand(Command::new("testb"))
251 .try_get_matches_from(vec!["prog", "test"])
252 .unwrap();
253 assert_eq!(m.subcommand_name(), Some("test"));
254 }
255
256 #[cfg(feature = "suggestions")]
257 #[test]
infer_subcommands_fail_suggestions()258 fn infer_subcommands_fail_suggestions() {
259 let m = Command::new("prog")
260 .infer_subcommands(true)
261 .subcommand(Command::new("test"))
262 .subcommand(Command::new("temp"))
263 .try_get_matches_from(vec!["prog", "temps"]);
264 assert!(m.is_err(), "{:#?}", m.unwrap());
265 assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
266 }
267
268 #[cfg(not(feature = "suggestions"))]
269 #[test]
infer_subcommands_fail_suggestions()270 fn infer_subcommands_fail_suggestions() {
271 let m = Command::new("prog")
272 .infer_subcommands(true)
273 .subcommand(Command::new("test"))
274 .subcommand(Command::new("temp"))
275 .try_get_matches_from(vec!["prog", "temps"]);
276 assert!(m.is_err(), "{:#?}", m.unwrap());
277 assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
278 }
279
280 #[test]
no_bin_name()281 fn no_bin_name() {
282 let result = Command::new("arg_required")
283 .no_binary_name(true)
284 .arg(Arg::new("test").required(true).index(1))
285 .try_get_matches_from(vec!["testing"]);
286 assert!(result.is_ok(), "{}", result.unwrap_err());
287 let matches = result.unwrap();
288 assert_eq!(
289 matches
290 .get_one::<String>("test")
291 .map(|v| v.as_str())
292 .unwrap(),
293 "testing"
294 );
295 }
296
297 #[test]
skip_possible_values()298 fn skip_possible_values() {
299 static SKIP_POS_VALS: &str = "\
300 tests stuff
301
302 Usage: test [OPTIONS] [arg1]
303
304 Arguments:
305 [arg1] some pos arg
306
307 Options:
308 -o, --opt <opt> some option
309 -h, --help Print help
310 -V, --version Print version
311 ";
312
313 let cmd = Command::new("test")
314 .author("Kevin K.")
315 .about("tests stuff")
316 .version("1.3")
317 .hide_possible_values(true)
318 .args([
319 arg!(-o --opt <opt> "some option").value_parser(["one", "two"]),
320 arg!([arg1] "some pos arg").value_parser(["three", "four"]),
321 ]);
322
323 utils::assert_output(cmd, "test --help", SKIP_POS_VALS, false);
324 }
325
326 #[test]
stop_delim_values_only_pos_follows()327 fn stop_delim_values_only_pos_follows() {
328 let r = Command::new("onlypos")
329 .dont_delimit_trailing_values(true)
330 .args([arg!(f: -f <flag> "some opt"), arg!([arg] ... "some arg")])
331 .try_get_matches_from(vec!["", "--", "-f", "-g,x"]);
332 assert!(r.is_ok(), "{}", r.unwrap_err());
333 let m = r.unwrap();
334 assert!(m.contains_id("arg"));
335 assert!(!m.contains_id("f"));
336 assert_eq!(
337 m.get_many::<String>("arg")
338 .unwrap()
339 .map(|v| v.as_str())
340 .collect::<Vec<_>>(),
341 ["-f", "-g,x"]
342 );
343 }
344
345 #[test]
dont_delim_values_trailingvararg()346 fn dont_delim_values_trailingvararg() {
347 let m = Command::new("positional")
348 .dont_delimit_trailing_values(true)
349 .arg(arg!([opt] ... "some pos").trailing_var_arg(true))
350 .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"])
351 .unwrap();
352 assert!(m.contains_id("opt"));
353 assert_eq!(
354 m.get_many::<String>("opt")
355 .unwrap()
356 .map(|v| v.as_str())
357 .collect::<Vec<_>>(),
358 ["test", "--foo", "-Wl,-bar"]
359 );
360 }
361
362 #[test]
delim_values_only_pos_follows()363 fn delim_values_only_pos_follows() {
364 let r = Command::new("onlypos")
365 .args([arg!(f: -f [flag] "some opt"), arg!([arg] ... "some arg")])
366 .try_get_matches_from(vec!["", "--", "-f", "-g,x"]);
367 assert!(r.is_ok(), "{}", r.unwrap_err());
368 let m = r.unwrap();
369 assert!(m.contains_id("arg"));
370 assert!(!m.contains_id("f"));
371 assert_eq!(
372 m.get_many::<String>("arg")
373 .unwrap()
374 .map(|v| v.as_str())
375 .collect::<Vec<_>>(),
376 ["-f", "-g,x"]
377 );
378 }
379
380 #[test]
delim_values_trailingvararg()381 fn delim_values_trailingvararg() {
382 let m = Command::new("positional")
383 .arg(arg!([opt] ... "some pos").trailing_var_arg(true))
384 .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"])
385 .unwrap();
386 assert!(m.contains_id("opt"));
387 assert_eq!(
388 m.get_many::<String>("opt")
389 .unwrap()
390 .map(|v| v.as_str())
391 .collect::<Vec<_>>(),
392 ["test", "--foo", "-Wl,-bar"]
393 );
394 }
395
396 #[test]
delim_values_only_pos_follows_with_delim()397 fn delim_values_only_pos_follows_with_delim() {
398 let r = Command::new("onlypos")
399 .args([
400 arg!(f: -f [flag] "some opt"),
401 arg!([arg] ... "some arg").value_delimiter(','),
402 ])
403 .try_get_matches_from(vec!["", "--", "-f", "-g,x"]);
404 assert!(r.is_ok(), "{}", r.unwrap_err());
405 let m = r.unwrap();
406 assert!(m.contains_id("arg"));
407 assert!(!m.contains_id("f"));
408 assert_eq!(
409 m.get_many::<String>("arg")
410 .unwrap()
411 .map(|v| v.as_str())
412 .collect::<Vec<_>>(),
413 ["-f", "-g", "x"]
414 );
415 }
416
417 #[test]
delim_values_trailingvararg_with_delim()418 fn delim_values_trailingvararg_with_delim() {
419 let m = Command::new("positional")
420 .arg(
421 arg!([opt] ... "some pos")
422 .value_delimiter(',')
423 .trailing_var_arg(true),
424 )
425 .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"])
426 .unwrap();
427 assert!(m.contains_id("opt"));
428 assert_eq!(
429 m.get_many::<String>("opt")
430 .unwrap()
431 .map(|v| v.as_str())
432 .collect::<Vec<_>>(),
433 ["test", "--foo", "-Wl", "-bar"]
434 );
435 }
436
437 #[test]
leading_hyphen_short()438 fn leading_hyphen_short() {
439 let res = Command::new("leadhy")
440 .arg(Arg::new("some").allow_hyphen_values(true))
441 .arg(Arg::new("other").short('o').action(ArgAction::SetTrue))
442 .try_get_matches_from(vec!["", "-bar", "-o"]);
443 assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind());
444 let m = res.unwrap();
445 assert!(m.contains_id("some"));
446 assert!(m.contains_id("other"));
447 assert_eq!(
448 m.get_one::<String>("some").map(|v| v.as_str()).unwrap(),
449 "-bar"
450 );
451 assert_eq!(
452 *m.get_one::<bool>("other").expect("defaulted by clap"),
453 true
454 );
455 }
456
457 #[test]
leading_hyphen_long()458 fn leading_hyphen_long() {
459 let res = Command::new("leadhy")
460 .arg(Arg::new("some").allow_hyphen_values(true))
461 .arg(Arg::new("other").short('o').action(ArgAction::SetTrue))
462 .try_get_matches_from(vec!["", "--bar", "-o"]);
463 assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind());
464 let m = res.unwrap();
465 assert!(m.contains_id("some"));
466 assert!(m.contains_id("other"));
467 assert_eq!(
468 m.get_one::<String>("some").map(|v| v.as_str()).unwrap(),
469 "--bar"
470 );
471 assert_eq!(
472 *m.get_one::<bool>("other").expect("defaulted by clap"),
473 true
474 );
475 }
476
477 #[test]
leading_hyphen_opt()478 fn leading_hyphen_opt() {
479 let res = Command::new("leadhy")
480 .arg(
481 Arg::new("some")
482 .action(ArgAction::Set)
483 .long("opt")
484 .allow_hyphen_values(true),
485 )
486 .arg(Arg::new("other").short('o').action(ArgAction::SetTrue))
487 .try_get_matches_from(vec!["", "--opt", "--bar", "-o"]);
488 assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind());
489 let m = res.unwrap();
490 assert!(m.contains_id("some"));
491 assert!(m.contains_id("other"));
492 assert_eq!(
493 m.get_one::<String>("some").map(|v| v.as_str()).unwrap(),
494 "--bar"
495 );
496 assert_eq!(
497 *m.get_one::<bool>("other").expect("defaulted by clap"),
498 true
499 );
500 }
501
502 #[test]
allow_negative_numbers_success()503 fn allow_negative_numbers_success() {
504 let res = Command::new("negnum")
505 .arg(Arg::new("panum").allow_negative_numbers(true))
506 .arg(
507 Arg::new("onum")
508 .short('o')
509 .action(ArgAction::Set)
510 .allow_negative_numbers(true),
511 )
512 .try_get_matches_from(vec!["negnum", "-20", "-o", "-1.2"]);
513 assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind());
514 let m = res.unwrap();
515 assert_eq!(
516 m.get_one::<String>("panum").map(|v| v.as_str()).unwrap(),
517 "-20"
518 );
519 assert_eq!(
520 m.get_one::<String>("onum").map(|v| v.as_str()).unwrap(),
521 "-1.2"
522 );
523 }
524
525 #[test]
allow_negative_numbers_fail()526 fn allow_negative_numbers_fail() {
527 let res = Command::new("negnum")
528 .arg(Arg::new("panum").allow_negative_numbers(true))
529 .arg(
530 Arg::new("onum")
531 .short('o')
532 .action(ArgAction::Set)
533 .allow_negative_numbers(true),
534 )
535 .try_get_matches_from(vec!["negnum", "--foo", "-o", "-1.2"]);
536 assert!(res.is_err());
537 assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument)
538 }
539
540 #[test]
trailing_var_arg_with_hyphen_values_escape_first()541 fn trailing_var_arg_with_hyphen_values_escape_first() {
542 assert_trailing_var_args(&["test", "--", "foo", "bar"], Some(&["foo", "bar"]), false);
543 }
544
545 #[test]
trailing_var_arg_with_hyphen_values_escape_middle()546 fn trailing_var_arg_with_hyphen_values_escape_middle() {
547 assert_trailing_var_args(
548 &["test", "foo", "--", "bar"],
549 Some(&["foo", "--", "bar"]),
550 false,
551 );
552 }
553
554 #[test]
trailing_var_arg_with_hyphen_values_short_first()555 fn trailing_var_arg_with_hyphen_values_short_first() {
556 assert_trailing_var_args(&["test", "-p", "foo", "bar"], Some(&["foo", "bar"]), true);
557 }
558
559 #[test]
trailing_var_arg_with_hyphen_values_short_middle()560 fn trailing_var_arg_with_hyphen_values_short_middle() {
561 assert_trailing_var_args(
562 &["test", "foo", "-p", "bar"],
563 Some(&["foo", "-p", "bar"]),
564 false,
565 );
566 }
567
568 #[test]
trailing_var_arg_with_hyphen_values_long_first()569 fn trailing_var_arg_with_hyphen_values_long_first() {
570 assert_trailing_var_args(
571 &["test", "--prog", "foo", "bar"],
572 Some(&["foo", "bar"]),
573 true,
574 );
575 }
576
577 #[test]
trailing_var_arg_with_hyphen_values_long_middle()578 fn trailing_var_arg_with_hyphen_values_long_middle() {
579 assert_trailing_var_args(
580 &["test", "foo", "--prog", "bar"],
581 Some(&["foo", "--prog", "bar"]),
582 false,
583 );
584 }
585
586 #[track_caller]
assert_trailing_var_args( input: &[&str], expected_var_arg: Option<&[&str]>, expected_flag: bool, )587 fn assert_trailing_var_args(
588 input: &[&str],
589 expected_var_arg: Option<&[&str]>,
590 expected_flag: bool,
591 ) {
592 let cmd = Command::new("test").arg(arg!(-p - -prog)).arg(
593 arg!([opt] ... "some pos")
594 .trailing_var_arg(true)
595 .allow_hyphen_values(true),
596 );
597 let m = cmd.try_get_matches_from(input);
598 assert!(
599 m.is_ok(),
600 "failed with args {:?}: {}",
601 input,
602 m.unwrap_err()
603 );
604 let m = m.unwrap();
605
606 let actual_var_args = m
607 .get_many::<String>("opt")
608 .map(|v| v.map(|s| s.as_str()).collect::<Vec<_>>());
609 assert_eq!(actual_var_args.as_deref(), expected_var_arg);
610 assert_eq!(m.get_flag("prog"), expected_flag);
611 }
612
613 #[test]
disable_help_subcommand()614 fn disable_help_subcommand() {
615 let result = Command::new("disablehelp")
616 .disable_help_subcommand(true)
617 .subcommand(Command::new("sub1"))
618 .try_get_matches_from(vec!["", "help"]);
619 assert!(result.is_err());
620 let err = result.err().unwrap();
621 assert_eq!(err.kind(), ErrorKind::InvalidSubcommand);
622 }
623
624 #[test]
dont_collapse_args()625 fn dont_collapse_args() {
626 let cmd = Command::new("clap-test").version("v1.4.8").args([
627 Arg::new("arg1").help("some"),
628 Arg::new("arg2").help("some"),
629 Arg::new("arg3").help("some"),
630 ]);
631 utils::assert_output(cmd, "clap-test --help", DONT_COLLAPSE_ARGS, false);
632 }
633
634 #[test]
require_eq()635 fn require_eq() {
636 static REQUIRE_EQUALS: &str = "\
637 Usage: clap-test --opt=<FILE>
638
639 Options:
640 -o, --opt=<FILE> some
641 -h, --help Print help
642 -V, --version Print version
643 ";
644
645 let cmd = Command::new("clap-test").version("v1.4.8").arg(
646 Arg::new("opt")
647 .long("opt")
648 .short('o')
649 .required(true)
650 .require_equals(true)
651 .value_name("FILE")
652 .help("some"),
653 );
654 utils::assert_output(cmd, "clap-test --help", REQUIRE_EQUALS, false);
655 }
656
657 #[test]
propagate_vals_down()658 fn propagate_vals_down() {
659 let m = Command::new("myprog")
660 .arg(arg!([cmd] "command to run").global(true))
661 .subcommand(Command::new("foo"))
662 .try_get_matches_from(vec!["myprog", "set", "foo"]);
663 assert!(m.is_ok(), "{:?}", m.unwrap_err().kind());
664 let m = m.unwrap();
665 assert_eq!(m.get_one::<String>("cmd").map(|v| v.as_str()), Some("set"));
666 let sub_m = m.subcommand_matches("foo").unwrap();
667 assert_eq!(
668 sub_m.get_one::<String>("cmd").map(|v| v.as_str()),
669 Some("set")
670 );
671 }
672
673 #[test]
allow_missing_positional()674 fn allow_missing_positional() {
675 let m = Command::new("test")
676 .allow_missing_positional(true)
677 .arg(arg!([src] "some file").default_value("src"))
678 .arg(arg!(<dest> "some file"))
679 .try_get_matches_from(vec!["test", "file"]);
680 assert!(m.is_ok(), "{:?}", m.unwrap_err().kind());
681 let m = m.unwrap();
682 assert_eq!(m.get_one::<String>("src").map(|v| v.as_str()), Some("src"));
683 assert_eq!(
684 m.get_one::<String>("dest").map(|v| v.as_str()),
685 Some("file")
686 );
687 }
688
689 #[test]
allow_missing_positional_no_default()690 fn allow_missing_positional_no_default() {
691 let m = Command::new("test")
692 .allow_missing_positional(true)
693 .arg(arg!([src] "some file"))
694 .arg(arg!(<dest> "some file"))
695 .try_get_matches_from(vec!["test", "file"]);
696 assert!(m.is_ok(), "{:?}", m.unwrap_err().kind());
697 let m = m.unwrap();
698 assert_eq!(m.get_one::<String>("src").map(|v| v.as_str()), None);
699 assert_eq!(
700 m.get_one::<String>("dest").map(|v| v.as_str()),
701 Some("file")
702 );
703 }
704
705 #[test]
missing_positional_no_hyphen()706 fn missing_positional_no_hyphen() {
707 let r = Command::new("bench")
708 .allow_missing_positional(true)
709 .arg(arg!([BENCH] "some bench"))
710 .arg(arg!([ARGS] ... "some args"))
711 .try_get_matches_from(vec!["bench", "foo", "arg1", "arg2", "arg3"]);
712 assert!(r.is_ok(), "{:?}", r.unwrap_err().kind());
713
714 let m = r.unwrap();
715
716 let expected_bench = Some("foo");
717 let expected_args = vec!["arg1", "arg2", "arg3"];
718
719 assert_eq!(
720 m.get_one::<String>("BENCH").map(|v| v.as_str()),
721 expected_bench
722 );
723 assert_eq!(
724 m.get_many::<String>("ARGS")
725 .unwrap()
726 .map(|v| v.as_str())
727 .collect::<Vec<_>>(),
728 &*expected_args
729 );
730 }
731
732 #[test]
missing_positional_hyphen()733 fn missing_positional_hyphen() {
734 let r = Command::new("bench")
735 .allow_missing_positional(true)
736 .arg(arg!([BENCH] "some bench"))
737 .arg(arg!([ARGS] ... "some args"))
738 .try_get_matches_from(vec!["bench", "--", "arg1", "arg2", "arg3"]);
739 assert!(r.is_ok(), "{:?}", r.unwrap_err().kind());
740
741 let m = r.unwrap();
742
743 let expected_bench = None;
744 let expected_args = vec!["arg1", "arg2", "arg3"];
745
746 assert_eq!(
747 m.get_one::<String>("BENCH").map(|v| v.as_str()),
748 expected_bench
749 );
750 assert_eq!(
751 m.get_many::<String>("ARGS")
752 .unwrap()
753 .map(|v| v.as_str())
754 .collect::<Vec<_>>(),
755 &*expected_args
756 );
757 }
758
759 #[test]
missing_positional_hyphen_far_back()760 fn missing_positional_hyphen_far_back() {
761 let r = Command::new("bench")
762 .allow_missing_positional(true)
763 .arg(arg!([BENCH1] "some bench"))
764 .arg(arg!([BENCH2] "some bench"))
765 .arg(arg!([BENCH3] "some bench"))
766 .arg(arg!([ARGS] ... "some args"))
767 .try_get_matches_from(vec!["bench", "foo", "--", "arg1", "arg2", "arg3"]);
768 assert!(r.is_ok(), "{:?}", r.unwrap_err().kind());
769
770 let m = r.unwrap();
771
772 let expected_bench1 = Some("foo");
773 let expected_bench2 = None;
774 let expected_bench3 = None;
775 let expected_args = vec!["arg1", "arg2", "arg3"];
776
777 assert_eq!(
778 m.get_one::<String>("BENCH1").map(|v| v.as_str()),
779 expected_bench1
780 );
781 assert_eq!(
782 m.get_one::<String>("BENCH2").map(|v| v.as_str()),
783 expected_bench2
784 );
785 assert_eq!(
786 m.get_one::<String>("BENCH3").map(|v| v.as_str()),
787 expected_bench3
788 );
789 assert_eq!(
790 m.get_many::<String>("ARGS")
791 .unwrap()
792 .map(|v| v.as_str())
793 .collect::<Vec<_>>(),
794 &*expected_args
795 );
796 }
797
798 #[test]
missing_positional_hyphen_req_error()799 fn missing_positional_hyphen_req_error() {
800 let r = Command::new("bench")
801 .allow_missing_positional(true)
802 .arg(arg!([BENCH1] "some bench"))
803 .arg(arg!(<BENCH2> "some bench"))
804 .arg(arg!([ARGS] ... "some args"))
805 .try_get_matches_from(vec!["bench", "foo", "--", "arg1", "arg2", "arg3"]);
806 assert!(r.is_err());
807 assert_eq!(r.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
808 }
809
810 #[test]
issue_1066_allow_leading_hyphen_and_unknown_args_option()811 fn issue_1066_allow_leading_hyphen_and_unknown_args_option() {
812 let res = Command::new("prog")
813 .arg(
814 arg!(--"some-argument" <val>)
815 .required(true)
816 .allow_hyphen_values(true),
817 )
818 .try_get_matches_from(vec!["prog", "-fish"]);
819
820 assert!(res.is_err());
821 assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument);
822 }
823
824 #[test]
issue_1437_allow_hyphen_values_for_positional_arg()825 fn issue_1437_allow_hyphen_values_for_positional_arg() {
826 let m = Command::new("tmp")
827 .arg(
828 Arg::new("pat")
829 .allow_hyphen_values(true)
830 .required(true)
831 .action(ArgAction::Set),
832 )
833 .try_get_matches_from(["tmp", "-file"])
834 .unwrap();
835 assert_eq!(
836 m.get_one::<String>("pat").map(|v| v.as_str()),
837 Some("-file")
838 );
839 }
840
841 #[test]
issue_3880_allow_long_flag_hyphen_value_for_positional_arg()842 fn issue_3880_allow_long_flag_hyphen_value_for_positional_arg() {
843 let m = Command::new("prog")
844 .arg(
845 Arg::new("pat")
846 .allow_hyphen_values(true)
847 .required(true)
848 .action(ArgAction::Set),
849 )
850 .try_get_matches_from(["", "--file"])
851 .unwrap();
852
853 assert_eq!(
854 m.get_one::<String>("pat").map(|v| v.as_str()),
855 Some("--file")
856 );
857 }
858
859 #[test]
issue_1093_allow_ext_sc()860 fn issue_1093_allow_ext_sc() {
861 let cmd = Command::new("clap-test")
862 .version("v1.4.8")
863 .allow_external_subcommands(true);
864 utils::assert_output(cmd, "clap-test --help", ALLOW_EXT_SC, false);
865 }
866
867 #[test]
allow_ext_sc_empty_args()868 fn allow_ext_sc_empty_args() {
869 let res = Command::new("clap-test")
870 .version("v1.4.8")
871 .allow_external_subcommands(true)
872 .try_get_matches_from(vec!["clap-test", "external-cmd"]);
873
874 assert!(res.is_ok(), "{}", res.unwrap_err());
875
876 match res.unwrap().subcommand() {
877 Some((name, args)) => {
878 assert_eq!(name, "external-cmd");
879 assert_eq!(
880 args.get_many::<OsString>("").unwrap().collect::<Vec<_>>(),
881 Vec::<&OsString>::new(),
882 );
883 }
884 _ => unreachable!(),
885 }
886 }
887
888 #[test]
allow_ext_sc_when_sc_required()889 fn allow_ext_sc_when_sc_required() {
890 let res = Command::new("clap-test")
891 .version("v1.4.8")
892 .allow_external_subcommands(true)
893 .subcommand_required(true)
894 .try_get_matches_from(vec!["clap-test", "external-cmd", "foo"]);
895
896 assert!(res.is_ok(), "{}", res.unwrap_err());
897
898 match res.unwrap().subcommand() {
899 Some((name, args)) => {
900 assert_eq!(name, "external-cmd");
901 assert_eq!(
902 args.get_many::<OsString>("")
903 .unwrap()
904 .cloned()
905 .collect::<Vec<_>>(),
906 vec![OsString::from("foo")]
907 );
908 }
909 _ => unreachable!(),
910 }
911 }
912
913 #[test]
external_subcommand_looks_like_built_in()914 fn external_subcommand_looks_like_built_in() {
915 let res = Command::new("cargo")
916 .version("1.26.0")
917 .allow_external_subcommands(true)
918 .subcommand(Command::new("install"))
919 .try_get_matches_from(vec!["cargo", "install-update", "foo"]);
920 assert!(res.is_ok(), "{}", res.unwrap_err());
921 let m = res.unwrap();
922 match m.subcommand() {
923 Some((name, args)) => {
924 assert_eq!(name, "install-update");
925 assert_eq!(
926 args.get_many::<OsString>("")
927 .unwrap()
928 .cloned()
929 .collect::<Vec<_>>(),
930 vec![OsString::from("foo")]
931 );
932 }
933 _ => panic!("external_subcommand didn't work"),
934 }
935 }
936
937 #[test]
built_in_subcommand_escaped()938 fn built_in_subcommand_escaped() {
939 let res = Command::new("cargo")
940 .version("1.26.0")
941 .allow_external_subcommands(true)
942 .subcommand(Command::new("install"))
943 .try_get_matches_from(vec!["cargo", "--", "install", "foo"]);
944 assert!(res.is_ok(), "{}", res.unwrap_err());
945 let m = res.unwrap();
946 match m.subcommand() {
947 Some((name, args)) => {
948 assert_eq!(name, "install");
949 assert_eq!(
950 args.get_many::<OsString>("")
951 .unwrap()
952 .cloned()
953 .collect::<Vec<_>>(),
954 vec![OsString::from("foo")]
955 );
956 }
957 _ => panic!("external_subcommand didn't work"),
958 }
959 }
960
961 #[test]
aaos_opts_w_other_overrides()962 fn aaos_opts_w_other_overrides() {
963 // opts with other overrides
964 let res = Command::new("posix")
965 .args_override_self(true)
966 .arg(arg!(--opt <val> "some option").action(ArgAction::Set))
967 .arg(
968 arg!(--other <val> "some other option")
969 .overrides_with("opt")
970 .action(ArgAction::Set),
971 )
972 .try_get_matches_from(vec!["", "--opt=some", "--other=test", "--opt=other"]);
973 assert!(res.is_ok(), "{}", res.unwrap_err());
974 let m = res.unwrap();
975 assert!(m.contains_id("opt"));
976 assert!(!m.contains_id("other"));
977 assert_eq!(
978 m.get_one::<String>("opt").map(|v| v.as_str()),
979 Some("other")
980 );
981 }
982
983 #[test]
aaos_opts_w_other_overrides_rev()984 fn aaos_opts_w_other_overrides_rev() {
985 // opts with other overrides, rev
986 let res = Command::new("posix")
987 .args_override_self(true)
988 .arg(
989 arg!(--opt <val> "some option")
990 .required(true)
991 .action(ArgAction::Set),
992 )
993 .arg(
994 arg!(--other <val> "some other option")
995 .required(true)
996 .overrides_with("opt")
997 .action(ArgAction::Set),
998 )
999 .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--other=val"]);
1000 assert!(res.is_ok(), "{}", res.unwrap_err());
1001 let m = res.unwrap();
1002 assert!(!m.contains_id("opt"));
1003 assert!(m.contains_id("other"));
1004 assert_eq!(
1005 m.get_one::<String>("other").map(|v| v.as_str()),
1006 Some("val")
1007 );
1008 }
1009
1010 #[test]
aaos_opts_w_other_overrides_2()1011 fn aaos_opts_w_other_overrides_2() {
1012 // opts with other overrides
1013 let res = Command::new("posix")
1014 .args_override_self(true)
1015 .arg(
1016 arg!(--opt <val> "some option")
1017 .overrides_with("other")
1018 .action(ArgAction::Set),
1019 )
1020 .arg(arg!(--other <val> "some other option").action(ArgAction::Set))
1021 .try_get_matches_from(vec!["", "--opt=some", "--other=test", "--opt=other"]);
1022 assert!(res.is_ok(), "{}", res.unwrap_err());
1023 let m = res.unwrap();
1024 assert!(m.contains_id("opt"));
1025 assert!(!m.contains_id("other"));
1026 assert_eq!(
1027 m.get_one::<String>("opt").map(|v| v.as_str()),
1028 Some("other")
1029 );
1030 }
1031
1032 #[test]
aaos_opts_w_other_overrides_rev_2()1033 fn aaos_opts_w_other_overrides_rev_2() {
1034 // opts with other overrides, rev
1035 let res = Command::new("posix")
1036 .args_override_self(true)
1037 .arg(
1038 arg!(--opt <val> "some option")
1039 .required(true)
1040 .overrides_with("other")
1041 .action(ArgAction::Set),
1042 )
1043 .arg(
1044 arg!(--other <val> "some other option")
1045 .required(true)
1046 .action(ArgAction::Set),
1047 )
1048 .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--other=val"]);
1049 assert!(res.is_ok(), "{}", res.unwrap_err());
1050 let m = res.unwrap();
1051 assert!(!m.contains_id("opt"));
1052 assert!(m.contains_id("other"));
1053 assert_eq!(
1054 m.get_one::<String>("other").map(|v| v.as_str()),
1055 Some("val")
1056 );
1057 }
1058
1059 #[test]
aaos_opts_w_override_as_conflict_1()1060 fn aaos_opts_w_override_as_conflict_1() {
1061 // opts with other overrides, rev
1062 let res = Command::new("posix")
1063 .arg(
1064 arg!(--opt <val> "some option")
1065 .required(true)
1066 .overrides_with("other")
1067 .action(ArgAction::Set),
1068 )
1069 .arg(
1070 arg!(--other <val> "some other option")
1071 .required(true)
1072 .action(ArgAction::Set),
1073 )
1074 .try_get_matches_from(vec!["", "--opt=some"]);
1075 assert!(res.is_ok(), "{}", res.unwrap_err());
1076 let m = res.unwrap();
1077 assert!(m.contains_id("opt"));
1078 assert!(!m.contains_id("other"));
1079 assert_eq!(m.get_one::<String>("opt").map(|v| v.as_str()), Some("some"));
1080 }
1081
1082 #[test]
aaos_opts_w_override_as_conflict_2()1083 fn aaos_opts_w_override_as_conflict_2() {
1084 // opts with other overrides, rev
1085 let res = Command::new("posix")
1086 .arg(
1087 arg!(--opt <val> "some option")
1088 .required(true)
1089 .overrides_with("other")
1090 .action(ArgAction::Set),
1091 )
1092 .arg(
1093 arg!(--other <val> "some other option")
1094 .required(true)
1095 .action(ArgAction::Set),
1096 )
1097 .try_get_matches_from(vec!["", "--other=some"]);
1098 assert!(res.is_ok(), "{}", res.unwrap_err());
1099 let m = res.unwrap();
1100 assert!(!m.contains_id("opt"));
1101 assert!(m.contains_id("other"));
1102 assert_eq!(
1103 m.get_one::<String>("other").map(|v| v.as_str()),
1104 Some("some")
1105 );
1106 }
1107
1108 #[test]
aaos_opts_mult_req_delims()1109 fn aaos_opts_mult_req_delims() {
1110 // opts with multiple and require delims
1111 let res = Command::new("posix")
1112 .arg(
1113 arg!(--opt <val> ... "some option")
1114 .action(ArgAction::Set)
1115 .value_delimiter(',')
1116 .action(ArgAction::Append),
1117 )
1118 .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--opt=one,two"]);
1119 assert!(res.is_ok(), "{}", res.unwrap_err());
1120 let m = res.unwrap();
1121 assert!(m.contains_id("opt"));
1122 assert_eq!(
1123 m.get_many::<String>("opt")
1124 .unwrap()
1125 .map(|v| v.as_str())
1126 .collect::<Vec<_>>(),
1127 ["some", "other", "one", "two"]
1128 );
1129 }
1130
1131 #[test]
aaos_opts_mult()1132 fn aaos_opts_mult() {
1133 // opts with multiple
1134 let res = Command::new("posix")
1135 .arg(
1136 arg!(--opt <val> ... "some option")
1137 .num_args(1..)
1138 .action(ArgAction::Append),
1139 )
1140 .try_get_matches_from(vec![
1141 "",
1142 "--opt",
1143 "first",
1144 "overrides",
1145 "--opt",
1146 "some",
1147 "other",
1148 "val",
1149 ]);
1150 assert!(res.is_ok(), "{}", res.unwrap_err());
1151 let m = res.unwrap();
1152 assert!(m.contains_id("opt"));
1153 assert_eq!(
1154 m.get_many::<String>("opt")
1155 .unwrap()
1156 .map(|v| v.as_str())
1157 .collect::<Vec<_>>(),
1158 ["first", "overrides", "some", "other", "val"]
1159 );
1160 }
1161
1162 #[test]
aaos_pos_mult()1163 fn aaos_pos_mult() {
1164 // opts with multiple
1165 let res = Command::new("posix")
1166 .arg(arg!([val] ... "some pos"))
1167 .try_get_matches_from(vec!["", "some", "other", "value"]);
1168 assert!(res.is_ok(), "{}", res.unwrap_err());
1169 let m = res.unwrap();
1170 assert!(m.contains_id("val"));
1171 assert_eq!(
1172 m.get_many::<String>("val")
1173 .unwrap()
1174 .map(|v| v.as_str())
1175 .collect::<Vec<_>>(),
1176 ["some", "other", "value"]
1177 );
1178 }
1179
1180 #[test]
aaos_option_use_delim_false()1181 fn aaos_option_use_delim_false() {
1182 let m = Command::new("posix")
1183 .args_override_self(true)
1184 .arg(
1185 arg!(--opt <val> "some option")
1186 .required(true)
1187 .action(ArgAction::Set),
1188 )
1189 .try_get_matches_from(vec!["", "--opt=some,other", "--opt=one,two"])
1190 .unwrap();
1191 assert!(m.contains_id("opt"));
1192 assert_eq!(
1193 m.get_many::<String>("opt")
1194 .unwrap()
1195 .map(|v| v.as_str())
1196 .collect::<Vec<_>>(),
1197 ["one,two"]
1198 );
1199 }
1200
1201 #[test]
1202 #[cfg(feature = "color")]
color_is_global()1203 fn color_is_global() {
1204 let mut cmd = Command::new("myprog")
1205 .color(clap::ColorChoice::Never)
1206 .subcommand(Command::new("foo"));
1207 cmd.build();
1208 assert_eq!(cmd.get_color(), clap::ColorChoice::Never);
1209
1210 let sub = cmd.get_subcommands().collect::<Vec<_>>()[0];
1211 assert_eq!(sub.get_color(), clap::ColorChoice::Never);
1212 }
1213