1use std::ffi::OsString;
2
3use super::utils;
4
5use clap::{arg, error::ErrorKind, Arg, ArgAction, Command};
6
7static ALLOW_EXT_SC: &str = "\
8Usage: clap-test [COMMAND]
9
10Options:
11  -h, --help     Print help
12  -V, --version  Print version
13";
14
15static DONT_COLLAPSE_ARGS: &str = "\
16Usage: clap-test [arg1] [arg2] [arg3]
17
18Arguments:
19  [arg1]  some
20  [arg2]  some
21  [arg3]  some
22
23Options:
24  -h, --help     Print help
25  -V, --version  Print version
26";
27
28#[test]
29fn 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]
39fn 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]
51fn 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")]
63fn sub_command_required_error() {
64    static ERROR: &str = "\
65error: 'sc_required' requires a subcommand but one was not provided
66  [subcommands: sub1, help]
67
68Usage: sc_required <COMMAND>
69
70For 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]
80fn 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]
95fn 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]
110fn 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]
126fn 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]
141fn arg_required_else_help_error_message() {
142    static ARG_REQUIRED_ELSE_HELP: &str = "\
143Usage: test [OPTIONS]
144
145Options:
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]
171fn 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]
183fn 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]
194fn 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]
209fn 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]
224fn 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]
234fn 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]
245fn 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]
258fn 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]
270fn 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]
281fn 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]
298fn skip_possible_values() {
299    static SKIP_POS_VALS: &str = "\
300tests stuff
301
302Usage: test [OPTIONS] [arg1]
303
304Arguments:
305  [arg1]  some pos arg
306
307Options:
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]
327fn 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]
346fn 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]
363fn 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]
381fn 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]
397fn 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]
418fn 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]
438fn 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]
458fn 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]
478fn 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]
503fn 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]
526fn 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]
541fn trailing_var_arg_with_hyphen_values_escape_first() {
542    assert_trailing_var_args(&["test", "--", "foo", "bar"], Some(&["foo", "bar"]), false);
543}
544
545#[test]
546fn 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]
555fn 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]
560fn 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]
569fn 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]
578fn 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]
587fn 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]
614fn 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]
625fn 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]
635fn require_eq() {
636    static REQUIRE_EQUALS: &str = "\
637Usage: clap-test --opt=<FILE>
638
639Options:
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]
658fn 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]
674fn 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]
690fn 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]
706fn 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]
733fn 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]
760fn 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]
799fn 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]
811fn 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]
825fn 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]
842fn 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]
860fn 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]
868fn 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]
889fn 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]
914fn 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]
938fn 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]
962fn 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]
984fn 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]
1011fn 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]
1033fn 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]
1060fn 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]
1083fn 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]
1109fn 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]
1132fn 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]
1163fn 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]
1181fn 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")]
1203fn 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