• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use clap::{arg, error::ErrorKind, Arg, ArgAction, Command};
2 
3 use super::utils;
4 
5 #[test]
subcommand()6 fn subcommand() {
7     let m = Command::new("test")
8         .subcommand(
9             Command::new("some").arg(
10                 Arg::new("test")
11                     .short('t')
12                     .long("test")
13                     .action(ArgAction::Set)
14                     .help("testing testing"),
15             ),
16         )
17         .arg(Arg::new("other").long("other"))
18         .try_get_matches_from(vec!["myprog", "some", "--test", "testing"])
19         .unwrap();
20 
21     assert_eq!(m.subcommand_name().unwrap(), "some");
22     let sub_m = m.subcommand_matches("some").unwrap();
23     assert!(sub_m.contains_id("test"));
24     assert_eq!(
25         sub_m.get_one::<String>("test").map(|v| v.as_str()).unwrap(),
26         "testing"
27     );
28 }
29 
30 #[test]
subcommand_none_given()31 fn subcommand_none_given() {
32     let m = Command::new("test")
33         .subcommand(
34             Command::new("some").arg(
35                 Arg::new("test")
36                     .short('t')
37                     .long("test")
38                     .action(ArgAction::Set)
39                     .help("testing testing"),
40             ),
41         )
42         .arg(Arg::new("other").long("other"))
43         .try_get_matches_from(vec![""])
44         .unwrap();
45 
46     assert!(m.subcommand_name().is_none());
47 }
48 
49 #[test]
subcommand_multiple()50 fn subcommand_multiple() {
51     let m = Command::new("test")
52         .subcommands(vec![
53             Command::new("some").arg(
54                 Arg::new("test")
55                     .short('t')
56                     .long("test")
57                     .action(ArgAction::Set)
58                     .help("testing testing"),
59             ),
60             Command::new("add").arg(Arg::new("roster").short('r')),
61         ])
62         .arg(Arg::new("other").long("other"))
63         .try_get_matches_from(vec!["myprog", "some", "--test", "testing"])
64         .unwrap();
65 
66     assert!(m.subcommand_matches("some").is_some());
67     assert!(m.subcommand_matches("add").is_none());
68     assert_eq!(m.subcommand_name().unwrap(), "some");
69     let sub_m = m.subcommand_matches("some").unwrap();
70     assert!(sub_m.contains_id("test"));
71     assert_eq!(
72         sub_m.get_one::<String>("test").map(|v| v.as_str()).unwrap(),
73         "testing"
74     );
75 }
76 
77 #[test]
single_alias()78 fn single_alias() {
79     let m = Command::new("myprog")
80         .subcommand(Command::new("test").alias("do-stuff"))
81         .try_get_matches_from(vec!["myprog", "do-stuff"])
82         .unwrap();
83     assert_eq!(m.subcommand_name(), Some("test"));
84 }
85 
86 #[test]
multiple_aliases()87 fn multiple_aliases() {
88     let m = Command::new("myprog")
89         .subcommand(Command::new("test").aliases(["do-stuff", "test-stuff"]))
90         .try_get_matches_from(vec!["myprog", "test-stuff"])
91         .unwrap();
92     assert_eq!(m.subcommand_name(), Some("test"));
93 }
94 
95 #[test]
96 #[cfg(feature = "suggestions")]
97 #[cfg(feature = "error-context")]
subcmd_did_you_mean_output()98 fn subcmd_did_you_mean_output() {
99     #[cfg(feature = "suggestions")]
100     static DYM_SUBCMD: &str = "\
101 error: unrecognized subcommand 'subcm'
102 
103   note: subcommand 'subcmd' exists
104   note: to pass 'subcm' as a value, use 'dym -- subcm'
105 
106 Usage: dym [COMMAND]
107 
108 For more information, try '--help'.
109 ";
110 
111     let cmd = Command::new("dym").subcommand(Command::new("subcmd"));
112     utils::assert_output(cmd, "dym subcm", DYM_SUBCMD, true);
113 }
114 
115 #[test]
116 #[cfg(feature = "suggestions")]
117 #[cfg(feature = "error-context")]
subcmd_did_you_mean_output_ambiguous()118 fn subcmd_did_you_mean_output_ambiguous() {
119     #[cfg(feature = "suggestions")]
120     static DYM_SUBCMD_AMBIGUOUS: &str = "\
121 error: unrecognized subcommand 'te'
122 
123   note: subcommands 'test', 'temp' exist
124   note: to pass 'te' as a value, use 'dym -- te'
125 
126 Usage: dym [COMMAND]
127 
128 For more information, try '--help'.
129 ";
130 
131     let cmd = Command::new("dym")
132         .subcommand(Command::new("test"))
133         .subcommand(Command::new("temp"));
134     utils::assert_output(cmd, "dym te", DYM_SUBCMD_AMBIGUOUS, true);
135 }
136 
137 #[test]
138 #[cfg(feature = "suggestions")]
139 #[cfg(feature = "error-context")]
subcmd_did_you_mean_output_arg()140 fn subcmd_did_you_mean_output_arg() {
141     static EXPECTED: &str = "\
142 error: unexpected argument '--subcmarg' found
143 
144   note: 'subcmd --subcmdarg' exists
145 
146 Usage: dym [COMMAND]
147 
148 For more information, try '--help'.
149 ";
150 
151     let cmd = Command::new("dym")
152         .subcommand(Command::new("subcmd").arg(arg!(-s --subcmdarg <subcmdarg> "tests")));
153 
154     utils::assert_output(cmd, "dym --subcmarg subcmd", EXPECTED, true);
155 }
156 
157 #[test]
158 #[cfg(feature = "suggestions")]
159 #[cfg(feature = "error-context")]
subcmd_did_you_mean_output_arg_false_positives()160 fn subcmd_did_you_mean_output_arg_false_positives() {
161     static EXPECTED: &str = "\
162 error: unexpected argument '--subcmarg' found
163 
164 Usage: dym [COMMAND]
165 
166 For more information, try '--help'.
167 ";
168 
169     let cmd = Command::new("dym")
170         .subcommand(Command::new("subcmd").arg(arg!(-s --subcmdarg <subcmdarg> "tests")));
171 
172     utils::assert_output(cmd, "dym --subcmarg foo", EXPECTED, true);
173 }
174 
175 #[test]
alias_help()176 fn alias_help() {
177     let m = Command::new("myprog")
178         .subcommand(Command::new("test").alias("do-stuff"))
179         .try_get_matches_from(vec!["myprog", "help", "do-stuff"]);
180     assert!(m.is_err());
181     assert_eq!(m.unwrap_err().kind(), ErrorKind::DisplayHelp);
182 }
183 
184 #[test]
visible_aliases_help_output()185 fn visible_aliases_help_output() {
186     static VISIBLE_ALIAS_HELP: &str = "\
187 Usage: clap-test [COMMAND]
188 
189 Commands:
190   test  Some help [aliases: dongle, done]
191   help  Print this message or the help of the given subcommand(s)
192 
193 Options:
194   -h, --help     Print help
195   -V, --version  Print version
196 ";
197 
198     let cmd = Command::new("clap-test").version("2.6").subcommand(
199         Command::new("test")
200             .about("Some help")
201             .alias("invisible")
202             .visible_alias("dongle")
203             .visible_alias("done"),
204     );
205     utils::assert_output(cmd, "clap-test --help", VISIBLE_ALIAS_HELP, false);
206 }
207 
208 #[test]
invisible_aliases_help_output()209 fn invisible_aliases_help_output() {
210     static INVISIBLE_ALIAS_HELP: &str = "\
211 Usage: clap-test [COMMAND]
212 
213 Commands:
214   test  Some help
215   help  Print this message or the help of the given subcommand(s)
216 
217 Options:
218   -h, --help     Print help
219   -V, --version  Print version
220 ";
221 
222     let cmd = Command::new("clap-test")
223         .version("2.6")
224         .subcommand(Command::new("test").about("Some help").alias("invisible"));
225     utils::assert_output(cmd, "clap-test --help", INVISIBLE_ALIAS_HELP, false);
226 }
227 
228 #[test]
229 #[cfg(feature = "unstable-replace")]
replace()230 fn replace() {
231     let m = Command::new("prog")
232         .subcommand(
233             Command::new("module").subcommand(Command::new("install").about("Install module")),
234         )
235         .replace("install", ["module", "install"])
236         .try_get_matches_from(vec!["prog", "install"])
237         .unwrap();
238 
239     assert_eq!(m.subcommand_name(), Some("module"));
240     assert_eq!(
241         m.subcommand_matches("module").unwrap().subcommand_name(),
242         Some("install")
243     );
244 }
245 
246 #[test]
issue_1031_args_with_same_name()247 fn issue_1031_args_with_same_name() {
248     let res = Command::new("prog")
249         .arg(arg!(--"ui-path" <PATH>).required(true))
250         .subcommand(Command::new("signer"))
251         .try_get_matches_from(vec!["prog", "--ui-path", "signer"]);
252 
253     assert!(res.is_ok(), "{:?}", res.unwrap_err().kind());
254     let m = res.unwrap();
255     assert_eq!(
256         m.get_one::<String>("ui-path").map(|v| v.as_str()),
257         Some("signer")
258     );
259 }
260 
261 #[test]
issue_1031_args_with_same_name_no_more_vals()262 fn issue_1031_args_with_same_name_no_more_vals() {
263     let res = Command::new("prog")
264         .arg(arg!(--"ui-path" <PATH>).required(true))
265         .subcommand(Command::new("signer"))
266         .try_get_matches_from(vec!["prog", "--ui-path", "value", "signer"]);
267 
268     assert!(res.is_ok(), "{:?}", res.unwrap_err().kind());
269     let m = res.unwrap();
270     assert_eq!(
271         m.get_one::<String>("ui-path").map(|v| v.as_str()),
272         Some("value")
273     );
274     assert_eq!(m.subcommand_name(), Some("signer"));
275 }
276 
277 #[test]
issue_1161_multiple_hyphen_hyphen()278 fn issue_1161_multiple_hyphen_hyphen() {
279     // from example 22
280     let res = Command::new("myprog")
281         .arg(Arg::new("eff").short('f'))
282         .arg(Arg::new("pea").short('p').action(ArgAction::Set))
283         .arg(
284             Arg::new("slop")
285                 .action(ArgAction::Set)
286                 .num_args(1..)
287                 .last(true),
288         )
289         .try_get_matches_from(vec![
290             "-f",
291             "-p=bob",
292             "--",
293             "sloppy",
294             "slop",
295             "-a",
296             "--",
297             "subprogram",
298             "position",
299             "args",
300         ]);
301 
302     assert!(res.is_ok(), "{:?}", res.unwrap_err().kind());
303     let m = res.unwrap();
304 
305     let expected = Some(vec![
306         "sloppy",
307         "slop",
308         "-a",
309         "--",
310         "subprogram",
311         "position",
312         "args",
313     ]);
314     let actual = m
315         .get_many::<String>("slop")
316         .map(|vals| vals.map(|s| s.as_str()).collect::<Vec<_>>());
317 
318     assert_eq!(expected, actual);
319 }
320 
321 #[test]
issue_1722_not_emit_error_when_arg_follows_similar_to_a_subcommand()322 fn issue_1722_not_emit_error_when_arg_follows_similar_to_a_subcommand() {
323     let m = Command::new("myprog")
324         .subcommand(Command::new("subcommand"))
325         .arg(Arg::new("argument"))
326         .try_get_matches_from(vec!["myprog", "--", "subcommand"]);
327     assert_eq!(
328         m.unwrap().get_one::<String>("argument").map(|v| v.as_str()),
329         Some("subcommand")
330     );
331 }
332 
333 #[test]
subcommand_placeholder_test()334 fn subcommand_placeholder_test() {
335     let mut cmd = Command::new("myprog")
336         .subcommand(Command::new("subcommand"))
337         .subcommand_value_name("TEST_PLACEHOLDER")
338         .subcommand_help_heading("TEST_HEADER");
339 
340     assert_eq!(
341         &cmd.render_usage().to_string(),
342         "Usage: myprog [TEST_PLACEHOLDER]"
343     );
344 
345     let help_text = cmd.render_help().to_string();
346 
347     assert!(help_text.contains("TEST_HEADER:"));
348 }
349 
350 #[test]
351 #[cfg(feature = "error-context")]
subcommand_used_after_double_dash()352 fn subcommand_used_after_double_dash() {
353     static SUBCMD_AFTER_DOUBLE_DASH: &str = "\
354 error: unexpected argument 'subcmd' found
355 
356   note: subcommand 'subcmd' exists; to use it, remove the '--' before it
357 
358 Usage: cmd [COMMAND]
359 
360 For more information, try '--help'.
361 ";
362 
363     let cmd = Command::new("cmd").subcommand(Command::new("subcmd"));
364 
365     utils::assert_output(cmd, "cmd -- subcmd", SUBCMD_AFTER_DOUBLE_DASH, true);
366 }
367 
368 #[test]
subcommand_after_argument()369 fn subcommand_after_argument() {
370     let m = Command::new("myprog")
371         .arg(Arg::new("some_text"))
372         .subcommand(Command::new("test"))
373         .try_get_matches_from(vec!["myprog", "teat", "test"])
374         .unwrap();
375     assert_eq!(
376         m.get_one::<String>("some_text").map(|v| v.as_str()),
377         Some("teat")
378     );
379     assert_eq!(m.subcommand().unwrap().0, "test");
380 }
381 
382 #[test]
subcommand_after_argument_looks_like_help()383 fn subcommand_after_argument_looks_like_help() {
384     let m = Command::new("myprog")
385         .arg(Arg::new("some_text"))
386         .subcommand(Command::new("test"))
387         .try_get_matches_from(vec!["myprog", "helt", "test"])
388         .unwrap();
389     assert_eq!(
390         m.get_one::<String>("some_text").map(|v| v.as_str()),
391         Some("helt")
392     );
393     assert_eq!(m.subcommand().unwrap().0, "test");
394 }
395 
396 #[test]
issue_2494_subcommand_is_present()397 fn issue_2494_subcommand_is_present() {
398     let cmd = Command::new("opt")
399         .arg(Arg::new("global").long("global").action(ArgAction::SetTrue))
400         .subcommand(Command::new("global"));
401 
402     let m = cmd
403         .clone()
404         .try_get_matches_from(["opt", "--global", "global"])
405         .unwrap();
406     assert_eq!(m.subcommand_name().unwrap(), "global");
407     assert!(*m.get_one::<bool>("global").expect("defaulted by clap"));
408 
409     let m = cmd
410         .clone()
411         .try_get_matches_from(["opt", "--global"])
412         .unwrap();
413     assert!(m.subcommand_name().is_none());
414     assert!(*m.get_one::<bool>("global").expect("defaulted by clap"));
415 
416     let m = cmd.try_get_matches_from(["opt", "global"]).unwrap();
417     assert_eq!(m.subcommand_name().unwrap(), "global");
418     assert!(!*m.get_one::<bool>("global").expect("defaulted by clap"));
419 }
420 
421 #[test]
422 #[cfg(feature = "error-context")]
subcommand_not_recognized()423 fn subcommand_not_recognized() {
424     let cmd = Command::new("fake")
425         .subcommand(Command::new("sub"))
426         .disable_help_subcommand(true)
427         .infer_subcommands(true);
428     utils::assert_output(
429         cmd,
430         "fake help",
431         "error: unrecognized subcommand 'help'
432 
433 Usage: fake [COMMAND]
434 
435 For more information, try '--help'.
436 ",
437         true,
438     );
439 }
440 
441 #[test]
busybox_like_multicall()442 fn busybox_like_multicall() {
443     fn applet_commands() -> [Command; 2] {
444         [Command::new("true"), Command::new("false")]
445     }
446     let cmd = Command::new("busybox")
447         .multicall(true)
448         .subcommand(Command::new("busybox").subcommands(applet_commands()))
449         .subcommands(applet_commands());
450 
451     let m = cmd
452         .clone()
453         .try_get_matches_from(["busybox", "true"])
454         .unwrap();
455     assert_eq!(m.subcommand_name(), Some("busybox"));
456     assert_eq!(m.subcommand().unwrap().1.subcommand_name(), Some("true"));
457 
458     let m = cmd.clone().try_get_matches_from(["true"]).unwrap();
459     assert_eq!(m.subcommand_name(), Some("true"));
460 
461     let m = cmd.clone().try_get_matches_from(["a.out"]);
462     assert!(m.is_err());
463     assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
464 }
465 
466 #[test]
hostname_like_multicall()467 fn hostname_like_multicall() {
468     let mut cmd = Command::new("hostname")
469         .multicall(true)
470         .subcommand(Command::new("hostname"))
471         .subcommand(Command::new("dnsdomainname"));
472 
473     let m = cmd.clone().try_get_matches_from(["hostname"]).unwrap();
474     assert_eq!(m.subcommand_name(), Some("hostname"));
475 
476     let m = cmd.clone().try_get_matches_from(["dnsdomainname"]).unwrap();
477     assert_eq!(m.subcommand_name(), Some("dnsdomainname"));
478 
479     let m = cmd.clone().try_get_matches_from(["a.out"]);
480     assert!(m.is_err());
481     assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
482 
483     let m = cmd.try_get_matches_from_mut(["hostname", "hostname"]);
484     assert!(m.is_err());
485     assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument);
486 
487     let m = cmd.try_get_matches_from(["hostname", "dnsdomainname"]);
488     assert!(m.is_err());
489     assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument);
490 }
491 
492 #[test]
493 #[cfg(feature = "error-context")]
bad_multicall_command_error()494 fn bad_multicall_command_error() {
495     let cmd = Command::new("repl")
496         .version("1.0.0")
497         .propagate_version(true)
498         .multicall(true)
499         .subcommand(Command::new("foo"))
500         .subcommand(Command::new("bar"));
501 
502     let err = cmd.clone().try_get_matches_from(["world"]).unwrap_err();
503     assert_eq!(err.kind(), ErrorKind::InvalidSubcommand);
504     static HELLO_EXPECTED: &str = "\
505 error: unrecognized subcommand 'world'
506 
507 Usage: <COMMAND>
508 
509 For more information, try 'help'.
510 ";
511     utils::assert_eq(HELLO_EXPECTED, err.to_string());
512 
513     #[cfg(feature = "suggestions")]
514     {
515         let err = cmd.clone().try_get_matches_from(["baz"]).unwrap_err();
516         assert_eq!(err.kind(), ErrorKind::InvalidSubcommand);
517         static BAZ_EXPECTED: &str = "\
518 error: unrecognized subcommand 'baz'
519 
520   note: subcommand 'bar' exists
521   note: to pass 'baz' as a value, use ' -- baz'
522 
523 Usage: <COMMAND>
524 
525 For more information, try 'help'.
526 ";
527         utils::assert_eq(BAZ_EXPECTED, err.to_string());
528     }
529 
530     // Verify whatever we did to get the above to work didn't disable `--help` and `--version`.
531 
532     let err = cmd
533         .clone()
534         .try_get_matches_from(["foo", "--help"])
535         .unwrap_err();
536     assert_eq!(err.kind(), ErrorKind::DisplayHelp);
537 
538     let err = cmd
539         .clone()
540         .try_get_matches_from(["foo", "--version"])
541         .unwrap_err();
542     assert_eq!(err.kind(), ErrorKind::DisplayVersion);
543 }
544 
545 #[test]
546 #[should_panic = "Command repl: Arguments like oh-no cannot be set on a multicall command"]
cant_have_args_with_multicall()547 fn cant_have_args_with_multicall() {
548     let mut cmd = Command::new("repl")
549         .version("1.0.0")
550         .propagate_version(true)
551         .multicall(true)
552         .subcommand(Command::new("foo"))
553         .subcommand(Command::new("bar"))
554         .arg(Arg::new("oh-no"));
555     cmd.build();
556 }
557 
558 #[test]
multicall_help_flag()559 fn multicall_help_flag() {
560     static EXPECTED: &str = "\
561 Usage: foo bar [value]
562 
563 Arguments:
564   [value]
565 
566 Options:
567   -h, --help     Print help
568   -V, --version  Print version
569 ";
570     let cmd = Command::new("repl")
571         .version("1.0.0")
572         .propagate_version(true)
573         .multicall(true)
574         .subcommand(Command::new("foo").subcommand(Command::new("bar").arg(Arg::new("value"))));
575     utils::assert_output(cmd, "foo bar --help", EXPECTED, false);
576 }
577 
578 #[test]
multicall_help_subcommand()579 fn multicall_help_subcommand() {
580     static EXPECTED: &str = "\
581 Usage: foo bar [value]
582 
583 Arguments:
584   [value]
585 
586 Options:
587   -h, --help     Print help
588   -V, --version  Print version
589 ";
590     let cmd = Command::new("repl")
591         .version("1.0.0")
592         .propagate_version(true)
593         .multicall(true)
594         .subcommand(Command::new("foo").subcommand(Command::new("bar").arg(Arg::new("value"))));
595     utils::assert_output(cmd, "help foo bar", EXPECTED, false);
596 }
597 
598 #[test]
multicall_render_help()599 fn multicall_render_help() {
600     static EXPECTED: &str = "\
601 Usage: foo bar [value]
602 
603 Arguments:
604   [value]
605 
606 Options:
607   -h, --help     Print help
608   -V, --version  Print version
609 ";
610     let mut cmd = Command::new("repl")
611         .version("1.0.0")
612         .propagate_version(true)
613         .multicall(true)
614         .subcommand(Command::new("foo").subcommand(Command::new("bar").arg(Arg::new("value"))));
615     cmd.build();
616     let subcmd = cmd.find_subcommand_mut("foo").unwrap();
617     let subcmd = subcmd.find_subcommand_mut("bar").unwrap();
618 
619     let help = subcmd.render_help().to_string();
620     utils::assert_eq(EXPECTED, help);
621 }
622 
623 #[test]
624 #[should_panic = "Command test: command name `repeat` is duplicated"]
duplicate_subcommand()625 fn duplicate_subcommand() {
626     Command::new("test")
627         .subcommand(Command::new("repeat"))
628         .subcommand(Command::new("repeat"))
629         .build()
630 }
631 
632 #[test]
633 #[should_panic = "Command test: command `unique` alias `repeat` is duplicated"]
duplicate_subcommand_alias()634 fn duplicate_subcommand_alias() {
635     Command::new("test")
636         .subcommand(Command::new("repeat"))
637         .subcommand(Command::new("unique").alias("repeat"))
638         .build()
639 }
640