clap/tests/builder/app_settings.rs
Ed Page 85ad452c9b fix!: Remove Arg::rwquire_value_delimiter
In clap v3, `require_value_delimiter` activated an alternative parse
mode where
- `multiple_values` meant "multiple values within a single arg"
- `number_of_values` having no parse impact, only validation impact
- `value_names` being delimited values

For unbounded `number_of_values`, this is exactly what `value_delimiter`
provides.  The only value is if someone wanted `value_name` to be
`<file1>,<file2>,...` which can be useful and we might look into adding
back in.

Alternatively, this could be used for cases like key-value pairs but
that has issues like not allowing the delimiter in the value which might
be ok in some cases but not others.  We already instead document that
people should instead use `ValueParser` for this case.

In removing this, we remove points of confusion at how the different
multiple values and delimited value calls interact with each other.  I
know I would set `require_value_delimiter(true).multiple_values(true)`
when it turns out all I needed was `value_delimiter(',')`.

This also reduces the API surface area which makes it easier to discover
what features we do provide.

While this isn't big, this is also yet another small step towards
reducing binary size and compile times.
2022-08-03 21:26:36 -05:00

1312 lines
38 KiB
Rust

use std::ffi::OsString;
use super::utils;
use clap::{arg, error::ErrorKind, Arg, ArgAction, Command};
static ALLOW_EXT_SC: &str = "clap-test v1.4.8
USAGE:
clap-test [SUBCOMMAND]
OPTIONS:
-h, --help Print help information
-V, --version Print version information
";
static DONT_COLLAPSE_ARGS: &str = "clap-test v1.4.8
USAGE:
clap-test [arg1] [arg2] [arg3]
ARGS:
<arg1> some
<arg2> some
<arg3> some
OPTIONS:
-h, --help Print help information
-V, --version Print version information
";
#[test]
fn sub_command_negate_required() {
Command::new("sub_command_negate")
.subcommand_negates_reqs(true)
.arg(Arg::new("test").required(true).index(1))
.subcommand(Command::new("sub1"))
.try_get_matches_from(vec!["myprog", "sub1"])
.unwrap();
}
#[test]
fn sub_command_negate_required_2() {
let result = Command::new("sub_command_negate")
.subcommand_negates_reqs(true)
.arg(Arg::new("test").required(true).index(1))
.subcommand(Command::new("sub1"))
.try_get_matches_from(vec![""]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn sub_command_required() {
let result = Command::new("sc_required")
.subcommand_required(true)
.subcommand(Command::new("sub1"))
.try_get_matches_from(vec![""]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(err.kind(), ErrorKind::MissingSubcommand);
}
#[test]
fn arg_required_else_help() {
let result = Command::new("arg_required")
.arg_required_else_help(true)
.arg(Arg::new("test").index(1))
.try_get_matches_from(vec![""]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(
err.kind(),
ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
);
}
#[test]
fn arg_required_else_help_over_req_arg() {
let result = Command::new("arg_required")
.arg_required_else_help(true)
.arg(Arg::new("test").index(1).required(true))
.try_get_matches_from(vec![""]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(
err.kind(),
ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
);
}
#[test]
fn arg_required_else_help_over_req_subcommand() {
let result = Command::new("sub_required")
.arg_required_else_help(true)
.subcommand_required(true)
.subcommand(Command::new("sub1"))
.try_get_matches_from(vec![""]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(
err.kind(),
ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
);
}
#[test]
fn arg_required_else_help_with_default() {
let result = Command::new("arg_required")
.arg_required_else_help(true)
.arg(arg!(--input <PATH>).required(false).default_value("-"))
.try_get_matches_from(vec![""]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(
err.kind(),
ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand
);
}
#[test]
fn arg_required_else_help_error_message() {
static ARG_REQUIRED_ELSE_HELP: &str = "test 1.0
USAGE:
test [OPTIONS]
OPTIONS:
-i, --info Provides more info
-h, --help Print help information
-V, --version Print version information
";
let cmd = Command::new("test")
.arg_required_else_help(true)
.version("1.0")
.arg(
Arg::new("info")
.help("Provides more info")
.short('i')
.long("info")
.action(ArgAction::SetTrue),
);
utils::assert_output(
cmd,
"test",
ARG_REQUIRED_ELSE_HELP,
true, // Unlike normal displaying of help, we should provide a fatal exit code
);
}
#[cfg(not(feature = "suggestions"))]
#[test]
fn infer_subcommands_fail_no_args() {
let m = Command::new("prog")
.infer_subcommands(true)
.subcommand(Command::new("test"))
.subcommand(Command::new("temp"))
.try_get_matches_from(vec!["prog", "te"]);
assert!(m.is_err(), "{:#?}", m.unwrap());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
}
#[cfg(feature = "suggestions")]
#[test]
fn infer_subcommands_fail_no_args() {
let m = Command::new("prog")
.infer_subcommands(true)
.subcommand(Command::new("test"))
.subcommand(Command::new("temp"))
.try_get_matches_from(vec!["prog", "te"]);
assert!(m.is_err(), "{:#?}", m.unwrap());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
}
#[test]
fn infer_subcommands_fail_with_args() {
let m = Command::new("prog")
.infer_subcommands(true)
.arg(Arg::new("some"))
.subcommand(Command::new("test"))
.subcommand(Command::new("temp"))
.try_get_matches_from(vec!["prog", "t"]);
assert!(m.is_ok(), "{:?}", m.unwrap_err().kind());
assert_eq!(
m.unwrap().get_one::<String>("some").map(|v| v.as_str()),
Some("t")
);
}
#[test]
fn infer_subcommands_fail_with_args2() {
let m = Command::new("prog")
.infer_subcommands(true)
.arg(Arg::new("some"))
.subcommand(Command::new("test"))
.subcommand(Command::new("temp"))
.try_get_matches_from(vec!["prog", "te"]);
assert!(m.is_ok(), "{:?}", m.unwrap_err().kind());
assert_eq!(
m.unwrap().get_one::<String>("some").map(|v| v.as_str()),
Some("te")
);
}
#[test]
fn infer_subcommands_pass() {
let m = Command::new("prog")
.infer_subcommands(true)
.subcommand(Command::new("test"))
.try_get_matches_from(vec!["prog", "te"])
.unwrap();
assert_eq!(m.subcommand_name(), Some("test"));
}
#[test]
fn infer_subcommands_pass_close() {
let m = Command::new("prog")
.infer_subcommands(true)
.subcommand(Command::new("test"))
.subcommand(Command::new("temp"))
.try_get_matches_from(vec!["prog", "tes"])
.unwrap();
assert_eq!(m.subcommand_name(), Some("test"));
}
#[test]
fn infer_subcommands_pass_exact_match() {
let m = Command::new("prog")
.infer_subcommands(true)
.subcommand(Command::new("test"))
.subcommand(Command::new("testa"))
.subcommand(Command::new("testb"))
.try_get_matches_from(vec!["prog", "test"])
.unwrap();
assert_eq!(m.subcommand_name(), Some("test"));
}
#[cfg(feature = "suggestions")]
#[test]
fn infer_subcommands_fail_suggestions() {
let m = Command::new("prog")
.infer_subcommands(true)
.subcommand(Command::new("test"))
.subcommand(Command::new("temp"))
.try_get_matches_from(vec!["prog", "temps"]);
assert!(m.is_err(), "{:#?}", m.unwrap());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
}
#[cfg(not(feature = "suggestions"))]
#[test]
fn infer_subcommands_fail_suggestions() {
let m = Command::new("prog")
.infer_subcommands(true)
.subcommand(Command::new("test"))
.subcommand(Command::new("temp"))
.try_get_matches_from(vec!["prog", "temps"]);
assert!(m.is_err(), "{:#?}", m.unwrap());
assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand);
}
#[test]
fn no_bin_name() {
let result = Command::new("arg_required")
.no_binary_name(true)
.arg(Arg::new("test").required(true).index(1))
.try_get_matches_from(vec!["testing"]);
assert!(result.is_ok(), "{}", result.unwrap_err());
let matches = result.unwrap();
assert_eq!(
matches
.get_one::<String>("test")
.map(|v| v.as_str())
.unwrap(),
"testing"
);
}
#[test]
fn skip_possible_values() {
static SKIP_POS_VALS: &str = "test 1.3
Kevin K.
tests stuff
USAGE:
test [OPTIONS] [arg1]
ARGS:
<arg1> some pos arg
OPTIONS:
-o, --opt <opt> some option
-h, --help Print help information
-V, --version Print version information
";
let cmd = Command::new("test")
.author("Kevin K.")
.about("tests stuff")
.version("1.3")
.hide_possible_values(true)
.args(&[
arg!(-o --opt <opt> "some option")
.required(false)
.value_parser(["one", "two"]),
arg!([arg1] "some pos arg").value_parser(["three", "four"]),
]);
utils::assert_output(cmd, "test --help", SKIP_POS_VALS, false);
}
#[test]
fn stop_delim_values_only_pos_follows() {
let r = Command::new("onlypos")
.dont_delimit_trailing_values(true)
.args(&[
arg!(f: -f <flag> "some opt").required(false),
arg!([arg] ... "some arg"),
])
.try_get_matches_from(vec!["", "--", "-f", "-g,x"]);
assert!(r.is_ok(), "{}", r.unwrap_err());
let m = r.unwrap();
assert!(m.contains_id("arg"));
assert!(!m.contains_id("f"));
assert_eq!(
m.get_many::<String>("arg")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["-f", "-g,x"]
);
}
#[test]
fn dont_delim_values_trailingvararg() {
let m = Command::new("positional")
.trailing_var_arg(true)
.dont_delimit_trailing_values(true)
.arg(arg!([opt] ... "some pos"))
.try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"])
.unwrap();
assert!(m.contains_id("opt"));
assert_eq!(
m.get_many::<String>("opt")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["test", "--foo", "-Wl,-bar"]
);
}
#[test]
fn delim_values_only_pos_follows() {
let r = Command::new("onlypos")
.args(&[arg!(f: -f [flag] "some opt"), arg!([arg] ... "some arg")])
.try_get_matches_from(vec!["", "--", "-f", "-g,x"]);
assert!(r.is_ok(), "{}", r.unwrap_err());
let m = r.unwrap();
assert!(m.contains_id("arg"));
assert!(!m.contains_id("f"));
assert_eq!(
m.get_many::<String>("arg")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["-f", "-g,x"]
);
}
#[test]
fn delim_values_trailingvararg() {
let m = Command::new("positional")
.trailing_var_arg(true)
.arg(arg!([opt] ... "some pos"))
.try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"])
.unwrap();
assert!(m.contains_id("opt"));
assert_eq!(
m.get_many::<String>("opt")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["test", "--foo", "-Wl,-bar"]
);
}
#[test]
fn delim_values_only_pos_follows_with_delim() {
let r = Command::new("onlypos")
.args(&[
arg!(f: -f [flag] "some opt"),
arg!([arg] ... "some arg").value_delimiter(','),
])
.try_get_matches_from(vec!["", "--", "-f", "-g,x"]);
assert!(r.is_ok(), "{}", r.unwrap_err());
let m = r.unwrap();
assert!(m.contains_id("arg"));
assert!(!m.contains_id("f"));
assert_eq!(
m.get_many::<String>("arg")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["-f", "-g", "x"]
);
}
#[test]
fn delim_values_trailingvararg_with_delim() {
let m = Command::new("positional")
.trailing_var_arg(true)
.arg(arg!([opt] ... "some pos").value_delimiter(','))
.try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"])
.unwrap();
assert!(m.contains_id("opt"));
assert_eq!(
m.get_many::<String>("opt")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["test", "--foo", "-Wl", "-bar"]
);
}
#[test]
fn leading_hyphen_short() {
let res = Command::new("leadhy")
.allow_hyphen_values(true)
.arg(Arg::new("some"))
.arg(Arg::new("other").short('o').action(ArgAction::SetTrue))
.try_get_matches_from(vec!["", "-bar", "-o"]);
assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind());
let m = res.unwrap();
assert!(m.contains_id("some"));
assert!(m.contains_id("other"));
assert_eq!(
m.get_one::<String>("some").map(|v| v.as_str()).unwrap(),
"-bar"
);
assert_eq!(
*m.get_one::<bool>("other").expect("defaulted by clap"),
true
);
}
#[test]
fn leading_hyphen_long() {
let res = Command::new("leadhy")
.allow_hyphen_values(true)
.arg(Arg::new("some"))
.arg(Arg::new("other").short('o').action(ArgAction::SetTrue))
.try_get_matches_from(vec!["", "--bar", "-o"]);
assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind());
let m = res.unwrap();
assert!(m.contains_id("some"));
assert!(m.contains_id("other"));
assert_eq!(
m.get_one::<String>("some").map(|v| v.as_str()).unwrap(),
"--bar"
);
assert_eq!(
*m.get_one::<bool>("other").expect("defaulted by clap"),
true
);
}
#[test]
fn leading_hyphen_opt() {
let res = Command::new("leadhy")
.allow_hyphen_values(true)
.arg(Arg::new("some").action(ArgAction::Set).long("opt"))
.arg(Arg::new("other").short('o').action(ArgAction::SetTrue))
.try_get_matches_from(vec!["", "--opt", "--bar", "-o"]);
assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind());
let m = res.unwrap();
assert!(m.contains_id("some"));
assert!(m.contains_id("other"));
assert_eq!(
m.get_one::<String>("some").map(|v| v.as_str()).unwrap(),
"--bar"
);
assert_eq!(
*m.get_one::<bool>("other").expect("defaulted by clap"),
true
);
}
#[test]
fn allow_negative_numbers() {
let res = Command::new("negnum")
.allow_negative_numbers(true)
.arg(Arg::new("panum"))
.arg(Arg::new("onum").short('o').action(ArgAction::Set))
.try_get_matches_from(vec!["negnum", "-20", "-o", "-1.2"]);
assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind());
let m = res.unwrap();
assert_eq!(
m.get_one::<String>("panum").map(|v| v.as_str()).unwrap(),
"-20"
);
assert_eq!(
m.get_one::<String>("onum").map(|v| v.as_str()).unwrap(),
"-1.2"
);
}
#[test]
fn allow_negative_numbers_fail() {
let res = Command::new("negnum")
.allow_negative_numbers(true)
.arg(Arg::new("panum"))
.arg(Arg::new("onum").short('o').action(ArgAction::Set))
.try_get_matches_from(vec!["negnum", "--foo", "-o", "-1.2"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument)
}
#[test]
fn leading_double_hyphen_trailingvararg() {
let m = Command::new("positional")
.trailing_var_arg(true)
.allow_hyphen_values(true)
.arg(arg!([opt] ... "some pos"))
.try_get_matches_from(vec!["", "--foo", "-Wl", "bar"])
.unwrap();
assert!(m.contains_id("opt"));
assert_eq!(
m.get_many::<String>("opt")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["--foo", "-Wl", "bar"]
);
}
#[test]
fn disable_help_subcommand() {
let result = Command::new("disablehelp")
.disable_help_subcommand(true)
.subcommand(Command::new("sub1"))
.try_get_matches_from(vec!["", "help"]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(err.kind(), ErrorKind::UnknownArgument);
}
#[test]
fn dont_collapse_args() {
let cmd = Command::new("clap-test")
.version("v1.4.8")
.dont_collapse_args_in_usage(true)
.args(&[
Arg::new("arg1").help("some"),
Arg::new("arg2").help("some"),
Arg::new("arg3").help("some"),
]);
utils::assert_output(cmd, "clap-test --help", DONT_COLLAPSE_ARGS, false);
}
#[test]
fn require_eq() {
static REQUIRE_EQUALS: &str = "clap-test v1.4.8
USAGE:
clap-test --opt=<FILE>
OPTIONS:
-o, --opt=<FILE> some
-h, --help Print help information
-V, --version Print version information
";
let cmd = Command::new("clap-test").version("v1.4.8").arg(
Arg::new("opt")
.long("opt")
.short('o')
.required(true)
.require_equals(true)
.value_name("FILE")
.help("some"),
);
utils::assert_output(cmd, "clap-test --help", REQUIRE_EQUALS, false);
}
#[test]
fn args_negate_subcommands_one_level() {
let res = Command::new("disablehelp")
.args_conflicts_with_subcommands(true)
.subcommand_negates_reqs(true)
.arg(arg!(<arg1> "some arg"))
.arg(arg!(<arg2> "some arg"))
.subcommand(
Command::new("sub1").subcommand(Command::new("sub2").subcommand(Command::new("sub3"))),
)
.try_get_matches_from(vec!["", "pickles", "sub1"]);
assert!(res.is_ok(), "error: {:?}", res.unwrap_err().kind());
let m = res.unwrap();
assert_eq!(
m.get_one::<String>("arg2").map(|v| v.as_str()),
Some("sub1")
);
}
#[test]
fn args_negate_subcommands_two_levels() {
let res = Command::new("disablehelp")
.args_conflicts_with_subcommands(true)
.subcommand_negates_reqs(true)
.arg(arg!(<arg1> "some arg"))
.arg(arg!(<arg2> "some arg"))
.subcommand(
Command::new("sub1")
.args_conflicts_with_subcommands(true)
.subcommand_negates_reqs(true)
.arg(arg!(<arg> "some"))
.arg(arg!(<arg2> "some"))
.subcommand(Command::new("sub2").subcommand(Command::new("sub3"))),
)
.try_get_matches_from(vec!["", "sub1", "arg", "sub2"]);
assert!(res.is_ok(), "error: {:?}", res.unwrap_err().kind());
let m = res.unwrap();
assert_eq!(
m.subcommand_matches("sub1")
.unwrap()
.get_one::<String>("arg2")
.map(|v| v.as_str()),
Some("sub2")
);
}
#[test]
fn propagate_vals_down() {
let m = Command::new("myprog")
.arg(arg!([cmd] "command to run").global(true))
.subcommand(Command::new("foo"))
.try_get_matches_from(vec!["myprog", "set", "foo"]);
assert!(m.is_ok(), "{:?}", m.unwrap_err().kind());
let m = m.unwrap();
assert_eq!(m.get_one::<String>("cmd").map(|v| v.as_str()), Some("set"));
let sub_m = m.subcommand_matches("foo").unwrap();
assert_eq!(
sub_m.get_one::<String>("cmd").map(|v| v.as_str()),
Some("set")
);
}
#[test]
fn allow_missing_positional() {
let m = Command::new("test")
.allow_missing_positional(true)
.arg(arg!([src] "some file").default_value("src"))
.arg(arg!(<dest> "some file"))
.try_get_matches_from(vec!["test", "file"]);
assert!(m.is_ok(), "{:?}", m.unwrap_err().kind());
let m = m.unwrap();
assert_eq!(m.get_one::<String>("src").map(|v| v.as_str()), Some("src"));
assert_eq!(
m.get_one::<String>("dest").map(|v| v.as_str()),
Some("file")
);
}
#[test]
fn allow_missing_positional_no_default() {
let m = Command::new("test")
.allow_missing_positional(true)
.arg(arg!([src] "some file"))
.arg(arg!(<dest> "some file"))
.try_get_matches_from(vec!["test", "file"]);
assert!(m.is_ok(), "{:?}", m.unwrap_err().kind());
let m = m.unwrap();
assert_eq!(m.get_one::<String>("src").map(|v| v.as_str()), None);
assert_eq!(
m.get_one::<String>("dest").map(|v| v.as_str()),
Some("file")
);
}
#[test]
fn missing_positional_no_hyphen() {
let r = Command::new("bench")
.allow_missing_positional(true)
.arg(arg!([BENCH] "some bench"))
.arg(arg!([ARGS] ... "some args"))
.try_get_matches_from(vec!["bench", "foo", "arg1", "arg2", "arg3"]);
assert!(r.is_ok(), "{:?}", r.unwrap_err().kind());
let m = r.unwrap();
let expected_bench = Some("foo");
let expected_args = vec!["arg1", "arg2", "arg3"];
assert_eq!(
m.get_one::<String>("BENCH").map(|v| v.as_str()),
expected_bench
);
assert_eq!(
m.get_many::<String>("ARGS")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&*expected_args
);
}
#[test]
fn missing_positional_hyphen() {
let r = Command::new("bench")
.allow_missing_positional(true)
.arg(arg!([BENCH] "some bench"))
.arg(arg!([ARGS] ... "some args"))
.try_get_matches_from(vec!["bench", "--", "arg1", "arg2", "arg3"]);
assert!(r.is_ok(), "{:?}", r.unwrap_err().kind());
let m = r.unwrap();
let expected_bench = None;
let expected_args = vec!["arg1", "arg2", "arg3"];
assert_eq!(
m.get_one::<String>("BENCH").map(|v| v.as_str()),
expected_bench
);
assert_eq!(
m.get_many::<String>("ARGS")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&*expected_args
);
}
#[test]
fn missing_positional_hyphen_far_back() {
let r = Command::new("bench")
.allow_missing_positional(true)
.arg(arg!([BENCH1] "some bench"))
.arg(arg!([BENCH2] "some bench"))
.arg(arg!([BENCH3] "some bench"))
.arg(arg!([ARGS] ... "some args"))
.try_get_matches_from(vec!["bench", "foo", "--", "arg1", "arg2", "arg3"]);
assert!(r.is_ok(), "{:?}", r.unwrap_err().kind());
let m = r.unwrap();
let expected_bench1 = Some("foo");
let expected_bench2 = None;
let expected_bench3 = None;
let expected_args = vec!["arg1", "arg2", "arg3"];
assert_eq!(
m.get_one::<String>("BENCH1").map(|v| v.as_str()),
expected_bench1
);
assert_eq!(
m.get_one::<String>("BENCH2").map(|v| v.as_str()),
expected_bench2
);
assert_eq!(
m.get_one::<String>("BENCH3").map(|v| v.as_str()),
expected_bench3
);
assert_eq!(
m.get_many::<String>("ARGS")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&*expected_args
);
}
#[test]
fn missing_positional_hyphen_req_error() {
let r = Command::new("bench")
.allow_missing_positional(true)
.arg(arg!([BENCH1] "some bench"))
.arg(arg!(<BENCH2> "some bench"))
.arg(arg!([ARGS] ... "some args"))
.try_get_matches_from(vec!["bench", "foo", "--", "arg1", "arg2", "arg3"]);
assert!(r.is_err());
assert_eq!(r.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn issue_1066_allow_leading_hyphen_and_unknown_args() {
let res = Command::new("prog")
.allow_hyphen_values(true)
.arg(arg!(--"some-argument"))
.try_get_matches_from(vec!["prog", "hello"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument);
}
#[test]
fn issue_1066_allow_leading_hyphen_and_unknown_args_no_vals() {
let res = Command::new("prog")
.allow_hyphen_values(true)
.arg(arg!(--"some-argument"))
.try_get_matches_from(vec!["prog", "--hello"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument);
}
#[test]
fn issue_1066_allow_leading_hyphen_and_unknown_args_option() {
let res = Command::new("prog")
.allow_hyphen_values(true)
.arg(arg!(--"some-argument" <val>))
.try_get_matches_from(vec!["prog", "-hello"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument);
}
#[test]
fn issue_1437_allow_hyphen_values_for_positional_arg() {
let m = Command::new("tmp")
.arg(
Arg::new("pat")
.allow_hyphen_values(true)
.required(true)
.action(ArgAction::Set),
)
.try_get_matches_from(["tmp", "-file"])
.unwrap();
assert_eq!(
m.get_one::<String>("pat").map(|v| v.as_str()),
Some("-file")
);
}
#[test]
fn issue_1093_allow_ext_sc() {
let cmd = Command::new("clap-test")
.version("v1.4.8")
.allow_external_subcommands(true);
utils::assert_output(cmd, "clap-test --help", ALLOW_EXT_SC, false);
}
#[test]
fn allow_ext_sc_empty_args() {
let res = Command::new("clap-test")
.version("v1.4.8")
.allow_external_subcommands(true)
.try_get_matches_from(vec!["clap-test", "external-cmd"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
match res.unwrap().subcommand() {
Some((name, args)) => {
assert_eq!(name, "external-cmd");
assert_eq!(
args.get_many::<OsString>("").unwrap().collect::<Vec<_>>(),
Vec::<&OsString>::new(),
);
}
_ => unreachable!(),
}
}
#[test]
fn allow_ext_sc_when_sc_required() {
let res = Command::new("clap-test")
.version("v1.4.8")
.allow_external_subcommands(true)
.subcommand_required(true)
.try_get_matches_from(vec!["clap-test", "external-cmd", "foo"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
match res.unwrap().subcommand() {
Some((name, args)) => {
assert_eq!(name, "external-cmd");
assert_eq!(
args.get_many::<OsString>("")
.unwrap()
.cloned()
.collect::<Vec<_>>(),
vec![OsString::from("foo")]
);
}
_ => unreachable!(),
}
}
#[test]
fn external_subcommand_looks_like_built_in() {
let res = Command::new("cargo")
.version("1.26.0")
.allow_external_subcommands(true)
.subcommand(Command::new("install"))
.try_get_matches_from(vec!["cargo", "install-update", "foo"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
match m.subcommand() {
Some((name, args)) => {
assert_eq!(name, "install-update");
assert_eq!(
args.get_many::<OsString>("")
.unwrap()
.cloned()
.collect::<Vec<_>>(),
vec![OsString::from("foo")]
);
}
_ => panic!("external_subcommand didn't work"),
}
}
#[test]
fn built_in_subcommand_escaped() {
let res = Command::new("cargo")
.version("1.26.0")
.allow_external_subcommands(true)
.subcommand(Command::new("install"))
.try_get_matches_from(vec!["cargo", "--", "install", "foo"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
match m.subcommand() {
Some((name, args)) => {
assert_eq!(name, "install");
assert_eq!(
args.get_many::<OsString>("")
.unwrap()
.cloned()
.collect::<Vec<_>>(),
vec![OsString::from("foo")]
);
}
_ => panic!("external_subcommand didn't work"),
}
}
#[test]
fn aaos_flags() {
// flags
let cmd = Command::new("posix").arg(arg!(--flag "some flag").action(ArgAction::SetTrue));
let res = cmd.clone().try_get_matches_from(vec!["", "--flag"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("flag"));
assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
let res = cmd.try_get_matches_from(vec!["", "--flag", "--flag", "--flag", "--flag"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("flag"));
assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
#[test]
fn aaos_flags_mult() {
// flags with multiple
let cmd = Command::new("posix").arg(arg!(--flag "some flag").action(ArgAction::Count));
let res = cmd.clone().try_get_matches_from(vec!["", "--flag"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("flag"));
assert_eq!(*m.get_one::<u8>("flag").expect("defaulted by clap"), 1);
let res = cmd.try_get_matches_from(vec!["", "--flag", "--flag", "--flag", "--flag"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("flag"));
assert_eq!(*m.get_one::<u8>("flag").expect("defaulted by clap"), 4);
}
#[test]
fn aaos_opts() {
// opts
let res = Command::new("posix")
.arg(arg!(--opt <val> "some option").action(ArgAction::Set))
.try_get_matches_from(vec!["", "--opt=some", "--opt=other"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("opt"));
assert_eq!(
m.get_one::<String>("opt").map(|v| v.as_str()),
Some("other")
);
}
#[test]
fn aaos_opts_w_other_overrides() {
// opts with other overrides
let res = Command::new("posix")
.arg(
arg!(--opt <val> "some option")
.required(false)
.action(ArgAction::Set),
)
.arg(
arg!(--other <val> "some other option")
.required(false)
.overrides_with("opt")
.action(ArgAction::Set),
)
.try_get_matches_from(vec!["", "--opt=some", "--other=test", "--opt=other"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("opt"));
assert!(!m.contains_id("other"));
assert_eq!(
m.get_one::<String>("opt").map(|v| v.as_str()),
Some("other")
);
}
#[test]
fn aaos_opts_w_other_overrides_rev() {
// opts with other overrides, rev
let res = Command::new("posix")
.arg(
arg!(--opt <val> "some option")
.required(true)
.action(ArgAction::Set),
)
.arg(
arg!(--other <val> "some other option")
.required(true)
.overrides_with("opt")
.action(ArgAction::Set),
)
.try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--other=val"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(!m.contains_id("opt"));
assert!(m.contains_id("other"));
assert_eq!(
m.get_one::<String>("other").map(|v| v.as_str()),
Some("val")
);
}
#[test]
fn aaos_opts_w_other_overrides_2() {
// opts with other overrides
let res = Command::new("posix")
.arg(
arg!(--opt <val> "some option")
.required(false)
.overrides_with("other")
.action(ArgAction::Set),
)
.arg(
arg!(--other <val> "some other option")
.required(false)
.action(ArgAction::Set),
)
.try_get_matches_from(vec!["", "--opt=some", "--other=test", "--opt=other"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("opt"));
assert!(!m.contains_id("other"));
assert_eq!(
m.get_one::<String>("opt").map(|v| v.as_str()),
Some("other")
);
}
#[test]
fn aaos_opts_w_other_overrides_rev_2() {
// opts with other overrides, rev
let res = Command::new("posix")
.arg(
arg!(--opt <val> "some option")
.required(true)
.overrides_with("other")
.action(ArgAction::Set),
)
.arg(
arg!(--other <val> "some other option")
.required(true)
.action(ArgAction::Set),
)
.try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--other=val"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(!m.contains_id("opt"));
assert!(m.contains_id("other"));
assert_eq!(
m.get_one::<String>("other").map(|v| v.as_str()),
Some("val")
);
}
#[test]
fn aaos_opts_w_override_as_conflict_1() {
// opts with other overrides, rev
let res = Command::new("posix")
.arg(
arg!(--opt <val> "some option")
.required(true)
.overrides_with("other")
.action(ArgAction::Set),
)
.arg(
arg!(--other <val> "some other option")
.required(true)
.action(ArgAction::Set),
)
.try_get_matches_from(vec!["", "--opt=some"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("opt"));
assert!(!m.contains_id("other"));
assert_eq!(m.get_one::<String>("opt").map(|v| v.as_str()), Some("some"));
}
#[test]
fn aaos_opts_w_override_as_conflict_2() {
// opts with other overrides, rev
let res = Command::new("posix")
.arg(
arg!(--opt <val> "some option")
.required(true)
.overrides_with("other")
.action(ArgAction::Set),
)
.arg(
arg!(--other <val> "some other option")
.required(true)
.action(ArgAction::Set),
)
.try_get_matches_from(vec!["", "--other=some"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(!m.contains_id("opt"));
assert!(m.contains_id("other"));
assert_eq!(
m.get_one::<String>("other").map(|v| v.as_str()),
Some("some")
);
}
#[test]
fn aaos_opts_mult_req_delims() {
// opts with multiple and require delims
let res = Command::new("posix")
.arg(
arg!(--opt <val> ... "some option")
.action(ArgAction::Set)
.value_delimiter(',')
.action(ArgAction::Append),
)
.try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--opt=one,two"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("opt"));
assert_eq!(
m.get_many::<String>("opt")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["some", "other", "one", "two"]
);
}
#[test]
fn aaos_opts_mult() {
// opts with multiple
let res = Command::new("posix")
.arg(
arg!(--opt <val> ... "some option")
.num_args(1..)
.action(ArgAction::Append),
)
.try_get_matches_from(vec![
"",
"--opt",
"first",
"overrides",
"--opt",
"some",
"other",
"val",
]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("opt"));
assert_eq!(
m.get_many::<String>("opt")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["first", "overrides", "some", "other", "val"]
);
}
#[test]
fn aaos_pos_mult() {
// opts with multiple
let res = Command::new("posix")
.arg(arg!([val] ... "some pos"))
.try_get_matches_from(vec!["", "some", "other", "value"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("val"));
assert_eq!(
m.get_many::<String>("val")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["some", "other", "value"]
);
}
#[test]
fn aaos_option_use_delim_false() {
let m = Command::new("posix")
.arg(arg!(--opt <val> "some option").action(ArgAction::Set))
.try_get_matches_from(vec!["", "--opt=some,other", "--opt=one,two"])
.unwrap();
assert!(m.contains_id("opt"));
assert_eq!(
m.get_many::<String>("opt")
.unwrap()
.map(|v| v.as_str())
.collect::<Vec<_>>(),
&["one,two"]
);
}
#[test]
fn no_auto_help() {
let cmd = Command::new("myprog")
.subcommand(Command::new("foo"))
.mut_arg("help", |v| v.action(ArgAction::SetTrue));
let result = cmd.clone().try_get_matches_from("myprog --help".split(' '));
assert!(result.is_ok(), "{}", result.unwrap_err());
assert_eq!(result.unwrap().get_one::<bool>("help").copied(), Some(true));
let result = cmd.clone().try_get_matches_from("myprog -h".split(' '));
assert!(result.is_ok(), "{}", result.unwrap_err());
assert_eq!(result.unwrap().get_one::<bool>("help").copied(), Some(true));
}
#[test]
fn no_auto_version() {
let cmd = Command::new("myprog")
.version("3.0")
.mut_arg("version", |v| v.action(ArgAction::SetTrue));
let result = cmd
.clone()
.try_get_matches_from("myprog --version".split(' '));
assert!(result.is_ok(), "{}", result.unwrap_err());
assert_eq!(
result.unwrap().get_one::<bool>("version").copied(),
Some(true)
);
let result = cmd.clone().try_get_matches_from("myprog -V".split(' '));
assert!(result.is_ok(), "{}", result.unwrap_err());
assert_eq!(
result.unwrap().get_one::<bool>("version").copied(),
Some(true)
);
}
#[test]
fn no_auto_version_mut_arg() {
let cmd = Command::new("myprog")
.version("3.0")
.mut_arg("version", |v| {
v.action(ArgAction::SetTrue).help("custom help")
});
let result = cmd
.clone()
.try_get_matches_from("myprog --version".split(' '));
assert!(result.is_ok(), "{}", result.unwrap_err());
assert_eq!(
result.unwrap().get_one::<bool>("version").copied(),
Some(true)
);
let result = cmd.clone().try_get_matches_from("myprog -V".split(' '));
assert!(result.is_ok(), "{}", result.unwrap_err());
assert_eq!(
result.unwrap().get_one::<bool>("version").copied(),
Some(true)
);
}
#[test]
#[cfg(feature = "color")]
fn color_is_global() {
let mut cmd = Command::new("myprog")
.color(clap::ColorChoice::Never)
.subcommand(Command::new("foo"));
cmd.build();
assert_eq!(cmd.get_color(), clap::ColorChoice::Never);
let sub = cmd.get_subcommands().collect::<Vec<_>>()[0];
assert_eq!(sub.get_color(), clap::ColorChoice::Never);
}