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