clap/tests/builder/require.rs
Ed Page 02d27b5ce3 fix(usage): Make dont_collapse_args_in_usage the default
The setting was added to resolve #769.  The reason it was optional is out
of concern for applications with a lot of positional arguments.  I think
those cases are rare enough that we should just push people to override
the usage.  Positional arguments are generally important enough, even if
optional, to show.

As a side effect, this fixed some bugs with
`dont_collapse_args_in_usage` where it would repeat an argument in a
smart usage.

As a side effect, smart usage now shows `--` when it should
2022-08-30 16:12:49 -05:00

1467 lines
44 KiB
Rust

use super::utils;
use clap::builder::ArgPredicate;
use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command};
static REQUIRE_EQUALS: &str = "error: The following required arguments were not provided:
--opt=<FILE>
Usage:
clap-test --opt=<FILE>
For more information try --help
";
static REQUIRE_EQUALS_FILTERED: &str = "error: The following required arguments were not provided:
--opt=<FILE>
Usage:
clap-test --opt=<FILE> --foo=<FILE>
For more information try --help
";
static REQUIRE_EQUALS_FILTERED_GROUP: &str =
"error: The following required arguments were not provided:
--opt=<FILE>
Usage:
clap-test --opt=<FILE> --foo=<FILE> <--g1=<FILE>|--g2=<FILE>>
For more information try --help
";
static MISSING_REQ: &str = "error: The following required arguments were not provided:
--long-option-2 <option2>
<positional>
<positional2>
Usage:
clap-test --long-option-2 <option2> -F <positional> <positional2> [positional3]...
For more information try --help
";
static COND_REQ_IN_USAGE: &str = "error: The following required arguments were not provided:
--output <output>
Usage:
test --target <target> --input <input> --output <output>
For more information try --help
";
#[test]
fn flag_required() {
let result = Command::new("flag_required")
.arg(arg!(-f --flag "some flag").requires("color"))
.arg(arg!(-c --color "third flag"))
.try_get_matches_from(vec!["", "-f"]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn flag_required_2() {
let m = Command::new("flag_required")
.arg(
arg!(-f --flag "some flag")
.requires("color")
.action(ArgAction::SetTrue),
)
.arg(arg!(-c --color "third flag").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["", "-f", "-c"])
.unwrap();
assert!(*m.get_one::<bool>("color").expect("defaulted by clap"));
assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
#[test]
fn option_required() {
let result = Command::new("option_required")
.arg(arg!(f: -f <flag> "some flag").required(false).requires("c"))
.arg(arg!(c: -c <color> "third flag").required(false))
.try_get_matches_from(vec!["", "-f", "val"]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn option_required_2() {
let m = Command::new("option_required")
.arg(arg!(f: -f <flag> "some flag").required(false).requires("c"))
.arg(arg!(c: -c <color> "third flag").required(false))
.try_get_matches_from(vec!["", "-f", "val", "-c", "other_val"])
.unwrap();
assert!(m.contains_id("c"));
assert_eq!(
m.get_one::<String>("c").map(|v| v.as_str()).unwrap(),
"other_val"
);
assert!(m.contains_id("f"));
assert_eq!(m.get_one::<String>("f").map(|v| v.as_str()).unwrap(), "val");
}
#[test]
fn positional_required() {
let result = Command::new("positional_required")
.arg(Arg::new("flag").index(1).required(true))
.try_get_matches_from(vec![""]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn positional_required_2() {
let m = Command::new("positional_required")
.arg(Arg::new("flag").index(1).required(true))
.try_get_matches_from(vec!["", "someval"])
.unwrap();
assert!(m.contains_id("flag"));
assert_eq!(
m.get_one::<String>("flag").map(|v| v.as_str()).unwrap(),
"someval"
);
}
#[test]
fn positional_required_with_requires() {
let cmd = Command::new("positional_required")
.arg(Arg::new("flag").required(true).requires("opt"))
.arg(Arg::new("opt"))
.arg(Arg::new("bar"));
utils::assert_output(cmd, "clap-test", POSITIONAL_REQ, true);
}
static POSITIONAL_REQ: &str = "error: The following required arguments were not provided:
<flag>
<opt>
Usage:
clap-test <flag> <opt> [bar]
For more information try --help
";
#[test]
fn positional_required_with_requires_if_no_value() {
let cmd = Command::new("positional_required")
.arg(Arg::new("flag").required(true).requires_if("val", "opt"))
.arg(Arg::new("opt"))
.arg(Arg::new("bar"));
utils::assert_output(cmd, "clap-test", POSITIONAL_REQ_IF_NO_VAL, true);
}
static POSITIONAL_REQ_IF_NO_VAL: &str = "error: The following required arguments were not provided:
<flag>
Usage:
clap-test <flag> [opt] [bar]
For more information try --help
";
#[test]
fn positional_required_with_requires_if_value() {
let cmd = Command::new("positional_required")
.arg(Arg::new("flag").required(true).requires_if("val", "opt"))
.arg(Arg::new("foo").required(true))
.arg(Arg::new("opt"))
.arg(Arg::new("bar"));
utils::assert_output(cmd, "clap-test val", POSITIONAL_REQ_IF_VAL, true);
}
static POSITIONAL_REQ_IF_VAL: &str = "error: The following required arguments were not provided:
<foo>
<opt>
Usage:
clap-test <flag> <foo> <opt> [bar]
For more information try --help
";
#[test]
fn group_required() {
let result = Command::new("group_required")
.arg(arg!(-f --flag "some flag"))
.group(ArgGroup::new("gr").required(true).arg("some").arg("other"))
.arg(arg!(--some "some arg"))
.arg(arg!(--other "other arg"))
.try_get_matches_from(vec!["", "-f"]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn group_required_2() {
let m = Command::new("group_required")
.arg(arg!(-f --flag "some flag").action(ArgAction::SetTrue))
.group(ArgGroup::new("gr").required(true).arg("some").arg("other"))
.arg(arg!(--some "some arg").action(ArgAction::SetTrue))
.arg(arg!(--other "other arg").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["", "-f", "--some"])
.unwrap();
assert!(*m.get_one::<bool>("some").expect("defaulted by clap"));
assert!(!*m.get_one::<bool>("other").expect("defaulted by clap"));
assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
#[test]
fn group_required_3() {
let m = Command::new("group_required")
.arg(arg!(-f --flag "some flag").action(ArgAction::SetTrue))
.group(ArgGroup::new("gr").required(true).arg("some").arg("other"))
.arg(arg!(--some "some arg").action(ArgAction::SetTrue))
.arg(arg!(--other "other arg").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["", "-f", "--other"])
.unwrap();
assert!(!*m.get_one::<bool>("some").expect("defaulted by clap"));
assert!(*m.get_one::<bool>("other").expect("defaulted by clap"));
assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
#[test]
fn arg_require_group() {
let result = Command::new("arg_require_group")
.arg(arg!(-f --flag "some flag").requires("gr"))
.group(ArgGroup::new("gr").arg("some").arg("other"))
.arg(arg!(--some "some arg"))
.arg(arg!(--other "other arg"))
.try_get_matches_from(vec!["", "-f"]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn arg_require_group_2() {
let res = Command::new("arg_require_group")
.arg(
arg!(-f --flag "some flag")
.requires("gr")
.action(ArgAction::SetTrue),
)
.group(ArgGroup::new("gr").arg("some").arg("other"))
.arg(arg!(--some "some arg").action(ArgAction::SetTrue))
.arg(arg!(--other "other arg").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["", "-f", "--some"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(*m.get_one::<bool>("some").expect("defaulted by clap"));
assert!(!*m.get_one::<bool>("other").expect("defaulted by clap"));
assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
#[test]
fn arg_require_group_3() {
let res = Command::new("arg_require_group")
.arg(
arg!(-f --flag "some flag")
.requires("gr")
.action(ArgAction::SetTrue),
)
.group(ArgGroup::new("gr").arg("some").arg("other"))
.arg(arg!(--some "some arg").action(ArgAction::SetTrue))
.arg(arg!(--other "other arg").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["", "-f", "--other"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(!*m.get_one::<bool>("some").expect("defaulted by clap"));
assert!(*m.get_one::<bool>("other").expect("defaulted by clap"));
assert!(*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
// REQUIRED_UNLESS
#[test]
fn issue_753() {
let m = Command::new("test")
.arg(arg!(
-l --list "List available interfaces (and stop there)"
))
.arg(
arg!(
-i --iface <INTERFACE> "Ethernet interface for fetching NTP packets"
)
.required(false)
.required_unless_present("list"),
)
.arg(
arg!(-f --file <TESTFILE> "Fetch NTP packets from pcap file")
.required(false)
.conflicts_with("iface")
.required_unless_present("list"),
)
.arg(
arg!(-s --server <SERVER_IP> "NTP server IP address")
.required(false)
.required_unless_present("list"),
)
.try_get_matches_from(vec!["test", "--list"]);
assert!(m.is_ok(), "{}", m.unwrap_err());
}
#[test]
fn required_unless_present() {
let res = Command::new("unlesstest")
.arg(
Arg::new("cfg")
.required_unless_present("dbg")
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("dbg").long("debug").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["unlesstest", "--debug"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(*m.get_one::<bool>("dbg").expect("defaulted by clap"));
assert!(!m.contains_id("cfg"));
}
#[test]
fn required_unless_present_err() {
let res = Command::new("unlesstest")
.arg(
Arg::new("cfg")
.required_unless_present("dbg")
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("dbg").long("debug"))
.try_get_matches_from(vec!["unlesstest"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn required_unless_present_with_optional_value() {
let res = Command::new("unlesstest")
.arg(Arg::new("opt").long("opt").num_args(0..=1))
.arg(
Arg::new("cfg")
.required_unless_present("dbg")
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("dbg").long("debug"))
.try_get_matches_from(vec!["unlesstest", "--opt"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
// REQUIRED_UNLESS_ALL
#[test]
fn required_unless_present_all() {
let res = Command::new("unlessall")
.arg(
Arg::new("cfg")
.required_unless_present_all(["dbg", "infile"])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("dbg").long("debug").action(ArgAction::SetTrue))
.arg(Arg::new("infile").short('i').action(ArgAction::Set))
.try_get_matches_from(vec!["unlessall", "--debug", "-i", "file"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(*m.get_one::<bool>("dbg").expect("defaulted by clap"));
assert!(m.contains_id("infile"));
assert!(!m.contains_id("cfg"));
}
#[test]
fn required_unless_all_err() {
let res = Command::new("unlessall")
.arg(
Arg::new("cfg")
.required_unless_present_all(["dbg", "infile"])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("dbg").long("debug").action(ArgAction::SetTrue))
.arg(Arg::new("infile").short('i').action(ArgAction::Set))
.try_get_matches_from(vec!["unlessall", "--debug"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
// REQUIRED_UNLESS_ONE
#[test]
fn required_unless_present_any() {
let res = Command::new("unlessone")
.arg(
Arg::new("cfg")
.required_unless_present_any(["dbg", "infile"])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("dbg").long("debug").action(ArgAction::SetTrue))
.arg(Arg::new("infile").short('i').action(ArgAction::Set))
.try_get_matches_from(vec!["unlessone", "--debug"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(*m.get_one::<bool>("dbg").expect("defaulted by clap"));
assert!(!m.contains_id("cfg"));
}
#[test]
fn required_unless_any_2() {
// This tests that the required_unless_present_any works when the second arg in the array is used
// instead of the first.
let res = Command::new("unlessone")
.arg(
Arg::new("cfg")
.required_unless_present_any(["dbg", "infile"])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("dbg").long("debug").action(ArgAction::SetTrue))
.arg(Arg::new("infile").short('i').action(ArgAction::Set))
.try_get_matches_from(vec!["unlessone", "-i", "file"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(m.contains_id("infile"));
assert!(!m.contains_id("cfg"));
}
#[test]
fn required_unless_any_works_with_short() {
// GitHub issue: https://github.com/clap-rs/clap/issues/1135
let res = Command::new("unlessone")
.arg(
Arg::new("a")
.conflicts_with("b")
.short('a')
.action(ArgAction::SetTrue),
)
.arg(Arg::new("b").short('b').action(ArgAction::SetTrue))
.arg(
Arg::new("x")
.short('x')
.action(ArgAction::SetTrue)
.required_unless_present_any(["a", "b"]),
)
.try_get_matches_from(vec!["unlessone", "-a"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_unless_any_works_with_short_err() {
let res = Command::new("unlessone")
.arg(
Arg::new("a")
.conflicts_with("b")
.short('a')
.action(ArgAction::SetTrue),
)
.arg(Arg::new("b").short('b').action(ArgAction::SetTrue))
.arg(
Arg::new("x")
.short('x')
.action(ArgAction::SetTrue)
.required_unless_present_any(["a", "b"]),
)
.try_get_matches_from(vec!["unlessone"]);
assert!(res.is_err());
}
#[test]
fn required_unless_any_works_without() {
let res = Command::new("unlessone")
.arg(
Arg::new("a")
.conflicts_with("b")
.short('a')
.action(ArgAction::SetTrue),
)
.arg(Arg::new("b").short('b').action(ArgAction::SetTrue))
.arg(Arg::new("x").required_unless_present_any(["a", "b"]))
.try_get_matches_from(vec!["unlessone", "-a"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_unless_any_works_with_long() {
let res = Command::new("unlessone")
.arg(
Arg::new("a")
.conflicts_with("b")
.short('a')
.action(ArgAction::SetTrue),
)
.arg(Arg::new("b").short('b').action(ArgAction::SetTrue))
.arg(
Arg::new("x")
.long("x_is_the_option")
.action(ArgAction::SetTrue)
.required_unless_present_any(["a", "b"]),
)
.try_get_matches_from(vec!["unlessone", "-a"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_unless_any_1() {
let res = Command::new("unlessone")
.arg(
Arg::new("cfg")
.required_unless_present_any(["dbg", "infile"])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("dbg").long("debug").action(ArgAction::SetTrue))
.arg(Arg::new("infile").short('i').action(ArgAction::Set))
.try_get_matches_from(vec!["unlessone", "--debug"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
let m = res.unwrap();
assert!(!m.contains_id("infile"));
assert!(!m.contains_id("cfg"));
assert!(*m.get_one::<bool>("dbg").expect("defaulted by clap"));
}
#[test]
fn required_unless_any_err() {
let res = Command::new("unlessone")
.arg(
Arg::new("cfg")
.required_unless_present_any(["dbg", "infile"])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("dbg").long("debug").action(ArgAction::SetTrue))
.arg(Arg::new("infile").short('i').action(ArgAction::Set))
.try_get_matches_from(vec!["unlessone"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn missing_required_output() {
utils::assert_output(utils::complex_app(), "clap-test -F", MISSING_REQ, true);
}
// Conditional external requirements
#[test]
fn requires_if_present_val() {
let res = Command::new("unlessone")
.arg(
Arg::new("cfg")
.requires_if("my.cfg", "extra")
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").long("extra").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["unlessone", "--config=my.cfg"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn requires_if_present_mult() {
let res = Command::new("unlessone")
.arg(
Arg::new("cfg")
.requires_ifs([("my.cfg", "extra"), ("other.cfg", "other")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").long("extra").action(ArgAction::SetTrue))
.arg(Arg::new("other").long("other").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["unlessone", "--config=other.cfg"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn requires_if_present_mult_pass() {
let res = Command::new("unlessone")
.arg(
Arg::new("cfg")
.requires_ifs([("my.cfg", "extra"), ("other.cfg", "other")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").long("extra").action(ArgAction::SetTrue))
.arg(Arg::new("other").long("other").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["unlessone", "--config=some.cfg"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn requires_if_present_val_no_present_pass() {
let res = Command::new("unlessone")
.arg(
Arg::new("cfg")
.requires_if("my.cfg", "extra")
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").long("extra").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["unlessone"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
// Conditionally required
#[test]
fn required_if_val_present_pass() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq("extra", "val")
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.try_get_matches_from(vec!["ri", "--extra", "val", "--config", "my.cfg"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_if_val_present_fail() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq("extra", "val")
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.try_get_matches_from(vec!["ri", "--extra", "val"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn required_if_val_present_ignore_case_pass() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq("extra", "Val")
.action(ArgAction::Set)
.long("config"),
)
.arg(
Arg::new("extra")
.action(ArgAction::Set)
.long("extra")
.ignore_case(true),
)
.try_get_matches_from(vec!["ri", "--extra", "vaL", "--config", "my.cfg"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_if_val_present_ignore_case_fail() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq("extra", "Val")
.action(ArgAction::Set)
.long("config"),
)
.arg(
Arg::new("extra")
.action(ArgAction::Set)
.long("extra")
.ignore_case(true),
)
.try_get_matches_from(vec!["ri", "--extra", "vaL"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn required_if_all_values_present_pass() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq_all([("extra", "val"), ("option", "spec")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.arg(Arg::new("option").action(ArgAction::Set).long("option"))
.try_get_matches_from(vec![
"ri", "--extra", "val", "--option", "spec", "--config", "my.cfg",
]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_if_some_values_present_pass() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq_all([("extra", "val"), ("option", "spec")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.arg(Arg::new("option").action(ArgAction::Set).long("option"))
.try_get_matches_from(vec!["ri", "--extra", "val"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_if_all_values_present_fail() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq_all([("extra", "val"), ("option", "spec")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.arg(Arg::new("option").action(ArgAction::Set).long("option"))
.try_get_matches_from(vec!["ri", "--extra", "val", "--option", "spec"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn required_if_any_all_values_present_pass() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq_all([("extra", "val"), ("option", "spec")])
.required_if_eq_any([("extra", "val2"), ("option", "spec2")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.arg(Arg::new("option").action(ArgAction::Set).long("option"))
.try_get_matches_from(vec![
"ri", "--extra", "val", "--option", "spec", "--config", "my.cfg",
]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_if_any_all_values_present_fail() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq_all([("extra", "val"), ("option", "spec")])
.required_if_eq_any([("extra", "val2"), ("option", "spec2")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.arg(Arg::new("option").action(ArgAction::Set).long("option"))
.try_get_matches_from(vec!["ri", "--extra", "val", "--option", "spec"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn list_correct_required_args() {
let cmd = Command::new("Test cmd")
.version("1.0")
.author("F0x06")
.about("Arg test")
.arg(
Arg::new("target")
.action(ArgAction::Set)
.required(true)
.value_parser(["file", "stdout"])
.long("target"),
)
.arg(
Arg::new("input")
.action(ArgAction::Set)
.required(true)
.long("input"),
)
.arg(
Arg::new("output")
.action(ArgAction::Set)
.required(true)
.long("output"),
);
utils::assert_output(
cmd,
"test --input somepath --target file",
COND_REQ_IN_USAGE,
true,
);
}
#[test]
fn required_if_val_present_fail_error_output() {
let cmd = Command::new("Test cmd")
.version("1.0")
.author("F0x06")
.about("Arg test")
.arg(
Arg::new("target")
.action(ArgAction::Set)
.required(true)
.value_parser(["file", "stdout"])
.long("target"),
)
.arg(
Arg::new("input")
.action(ArgAction::Set)
.required(true)
.long("input"),
)
.arg(
Arg::new("output")
.action(ArgAction::Set)
.required_if_eq("target", "file")
.long("output"),
);
utils::assert_output(
cmd,
"test --input somepath --target file",
COND_REQ_IN_USAGE,
true,
);
}
#[test]
fn required_if_wrong_val() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq("extra", "val")
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.try_get_matches_from(vec!["ri", "--extra", "other"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_ifs_val_present_pass() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq_any([("extra", "val"), ("option", "spec")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("option").action(ArgAction::Set).long("option"))
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.try_get_matches_from(vec!["ri", "--option", "spec", "--config", "my.cfg"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_ifs_val_present_fail() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq_any([("extra", "val"), ("option", "spec")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.arg(Arg::new("option").action(ArgAction::Set).long("option"))
.try_get_matches_from(vec!["ri", "--option", "spec"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn required_ifs_wrong_val() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq_any([("extra", "val"), ("option", "spec")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.arg(Arg::new("option").action(ArgAction::Set).long("option"))
.try_get_matches_from(vec!["ri", "--option", "other"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn required_ifs_wrong_val_mult_fail() {
let res = Command::new("ri")
.arg(
Arg::new("cfg")
.required_if_eq_any([("extra", "val"), ("option", "spec")])
.action(ArgAction::Set)
.long("config"),
)
.arg(Arg::new("extra").action(ArgAction::Set).long("extra"))
.arg(Arg::new("option").action(ArgAction::Set).long("option"))
.try_get_matches_from(vec!["ri", "--extra", "other", "--option", "spec"]);
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument);
}
#[test]
fn require_eq() {
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", REQUIRE_EQUALS, true);
}
#[test]
fn require_eq_filtered() {
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"),
)
.arg(
Arg::new("foo")
.long("foo")
.short('f')
.required(true)
.require_equals(true)
.value_name("FILE")
.help("some other arg"),
);
utils::assert_output(cmd, "clap-test -f=blah", REQUIRE_EQUALS_FILTERED, true);
}
#[test]
fn require_eq_filtered_group() {
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"),
)
.arg(
Arg::new("foo")
.long("foo")
.short('f')
.required(true)
.require_equals(true)
.value_name("FILE")
.help("some other arg"),
)
.arg(
Arg::new("g1")
.long("g1")
.require_equals(true)
.value_name("FILE"),
)
.arg(
Arg::new("g2")
.long("g2")
.require_equals(true)
.value_name("FILE"),
)
.group(
ArgGroup::new("test_group")
.args(["g1", "g2"])
.required(true),
);
utils::assert_output(
cmd,
"clap-test -f=blah --g1=blah",
REQUIRE_EQUALS_FILTERED_GROUP,
true,
);
}
static ISSUE_1158: &str = "error: The following required arguments were not provided:
-x <X>
-y <Y>
-z <Z>
Usage:
example -x <X> -y <Y> -z <Z> <ID>
For more information try --help
";
fn issue_1158_app() -> Command {
Command::new("example")
.arg(
arg!(-c --config <FILE> "Custom config file.")
.required(false)
.required_unless_present("ID")
.conflicts_with("ID"),
)
.arg(
arg!([ID] "ID")
.required_unless_present("config")
.conflicts_with("config")
.requires_ifs([
(ArgPredicate::IsPresent, "x"),
(ArgPredicate::IsPresent, "y"),
(ArgPredicate::IsPresent, "z"),
]),
)
.arg(arg!(x: -x <X> "X").required(false))
.arg(arg!(y: -y <Y> "Y").required(false))
.arg(arg!(z: -z <Z> "Z").required(false))
}
#[test]
fn multiple_required_unless_usage_printing() {
static MULTIPLE_REQUIRED_UNLESS_USAGE: &str =
"error: The following required arguments were not provided:
--a <a>
--b <b>
Usage:
test --c <c> --a <a> --b <b>
For more information try --help
";
let cmd = Command::new("test")
.arg(
Arg::new("a")
.long("a")
.action(ArgAction::Set)
.required_unless_present("b")
.conflicts_with("b"),
)
.arg(
Arg::new("b")
.long("b")
.action(ArgAction::Set)
.required_unless_present("a")
.conflicts_with("a"),
)
.arg(
Arg::new("c")
.long("c")
.action(ArgAction::Set)
.required_unless_present("d")
.conflicts_with("d"),
)
.arg(
Arg::new("d")
.long("d")
.action(ArgAction::Set)
.required_unless_present("c")
.conflicts_with("c"),
);
utils::assert_output(cmd, "test --c asd", MULTIPLE_REQUIRED_UNLESS_USAGE, true);
}
#[test]
fn issue_1158_conflicting_requirements() {
let cmd = issue_1158_app();
utils::assert_output(cmd, "example id", ISSUE_1158, true);
}
#[test]
fn issue_1158_conflicting_requirements_rev() {
let res = issue_1158_app().try_get_matches_from(&["", "--config", "some.conf"]);
assert!(res.is_ok(), "{}", res.unwrap_err());
}
#[test]
fn issue_1643_args_mutually_require_each_other() {
use clap::*;
let cmd = Command::new("test")
.arg(
Arg::new("relation_id")
.help("The relation id to get the data from")
.long("relation-id")
.short('r')
.action(ArgAction::Set)
.requires("remote_unit_name"),
)
.arg(
Arg::new("remote_unit_name")
.help("The name of the remote unit to get data from")
.long("remote-unit")
.short('u')
.action(ArgAction::Set)
.requires("relation_id"),
);
cmd.try_get_matches_from(&["test", "-u", "hello", "-r", "farewell"])
.unwrap();
}
#[test]
fn short_flag_require_equals_with_minvals_zero() {
let m = Command::new("foo")
.arg(
Arg::new("check")
.short('c')
.num_args(0..)
.require_equals(true),
)
.arg(Arg::new("unique").short('u').action(ArgAction::SetTrue))
.try_get_matches_from(&["foo", "-cu"])
.unwrap();
assert!(m.contains_id("check"));
assert!(*m.get_one::<bool>("unique").expect("defaulted by clap"));
}
#[test]
fn issue_2624() {
let matches = Command::new("foo")
.arg(
Arg::new("check")
.short('c')
.long("check")
.require_equals(true)
.num_args(0..)
.value_parser(["silent", "quiet", "diagnose-first"]),
)
.arg(
Arg::new("unique")
.short('u')
.long("unique")
.action(ArgAction::SetTrue),
)
.try_get_matches_from(&["foo", "-cu"])
.unwrap();
assert!(matches.contains_id("check"));
assert!(*matches
.get_one::<bool>("unique")
.expect("defaulted by clap"));
}
#[test]
fn required_unless_all_with_any() {
let cmd = Command::new("prog")
.arg(Arg::new("foo").long("foo").action(ArgAction::SetTrue))
.arg(Arg::new("bar").long("bar").action(ArgAction::SetTrue))
.arg(Arg::new("baz").long("baz").action(ArgAction::SetTrue))
.arg(
Arg::new("flag")
.long("flag")
.action(ArgAction::SetTrue)
.required_unless_present_any(["foo"])
.required_unless_present_all(["bar", "baz"]),
);
let result = cmd.clone().try_get_matches_from(vec!["myprog"]);
assert!(result.is_err(), "{:?}", result.unwrap());
let result = cmd.clone().try_get_matches_from(vec!["myprog", "--foo"]);
assert!(result.is_ok(), "{:?}", result.unwrap_err());
let matches = result.unwrap();
assert!(!*matches.get_one::<bool>("flag").expect("defaulted by clap"));
let result = cmd
.clone()
.try_get_matches_from(vec!["myprog", "--bar", "--baz"]);
assert!(result.is_ok(), "{:?}", result.unwrap_err());
let matches = result.unwrap();
assert!(!*matches.get_one::<bool>("flag").expect("defaulted by clap"));
let result = cmd.try_get_matches_from(vec!["myprog", "--bar"]);
assert!(result.is_err(), "{:?}", result.unwrap());
}
#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command prog: Argument or group 'extra' specified in 'requires*' for 'config' does not exist"]
fn requires_invalid_arg() {
let _ = Command::new("prog")
.arg(Arg::new("config").requires("extra").long("config"))
.try_get_matches_from(vec!["", "--config"]);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command prog: Argument or group 'extra' specified in 'requires*' for 'config' does not exist"]
fn requires_if_invalid_arg() {
let _ = Command::new("prog")
.arg(
Arg::new("config")
.requires_if("val", "extra")
.long("config"),
)
.try_get_matches_from(vec!["", "--config"]);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command prog: Argument or group 'extra' specified in 'required_if_eq*' for 'config' does not exist"]
fn required_if_invalid_arg() {
let _ = Command::new("prog")
.arg(
Arg::new("config")
.required_if_eq("extra", "val")
.long("config"),
)
.try_get_matches_from(vec!["", "--config"]);
}
#[cfg(debug_assertions)]
#[test]
#[should_panic = "Command prog: Argument or group 'extra' specified in 'required_unless*' for 'config' does not exist"]
fn required_unless_invalid_arg() {
let _ = Command::new("prog")
.arg(
Arg::new("config")
.required_unless_present("extra")
.long("config"),
)
.try_get_matches_from(vec![""]);
}
#[test]
fn requires_with_default_value() {
let result = Command::new("prog")
.arg(
Arg::new("opt")
.long("opt")
.default_value("default")
.requires("flag"),
)
.arg(Arg::new("flag").long("flag").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["myprog"]);
assert!(
result.is_ok(),
"requires should ignore default_value: {:?}",
result.unwrap_err()
);
let m = result.unwrap();
assert_eq!(
m.get_one::<String>("opt").map(|v| v.as_str()),
Some("default")
);
assert!(!*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
#[test]
fn requires_if_with_default_value() {
let result = Command::new("prog")
.arg(
Arg::new("opt")
.long("opt")
.default_value("default")
.requires_if("default", "flag"),
)
.arg(Arg::new("flag").long("flag").action(ArgAction::SetTrue))
.try_get_matches_from(vec!["myprog"]);
assert!(
result.is_ok(),
"requires_if should ignore default_value: {:?}",
result.unwrap_err()
);
let m = result.unwrap();
assert_eq!(
m.get_one::<String>("opt").map(|v| v.as_str()),
Some("default")
);
assert!(!*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
#[test]
fn group_requires_with_default_value() {
let result = Command::new("prog")
.arg(Arg::new("opt").long("opt").default_value("default"))
.arg(Arg::new("flag").long("flag").action(ArgAction::SetTrue))
.group(ArgGroup::new("one").arg("opt").requires("flag"))
.try_get_matches_from(vec!["myprog"]);
assert!(
result.is_ok(),
"arg group requires should ignore default_value: {:?}",
result.unwrap_err()
);
let m = result.unwrap();
assert_eq!(
m.get_one::<String>("opt").map(|v| v.as_str()),
Some("default")
);
assert!(!*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
#[test]
fn required_if_eq_on_default_value() {
let result = Command::new("prog")
.arg(Arg::new("opt").long("opt").default_value("default"))
.arg(
Arg::new("flag")
.long("flag")
.action(ArgAction::SetTrue)
.required_if_eq("opt", "default"),
)
.try_get_matches_from(vec!["myprog"]);
assert!(
result.is_ok(),
"required_if_eq should ignore default_value: {:?}",
result.unwrap_err()
);
let m = result.unwrap();
assert_eq!(
m.get_one::<String>("opt").map(|v| v.as_str()),
Some("default")
);
assert!(!*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
#[test]
fn required_if_eq_all_on_default_value() {
let result = Command::new("prog")
.arg(Arg::new("opt").long("opt").default_value("default"))
.arg(
Arg::new("flag")
.long("flag")
.action(ArgAction::SetTrue)
.required_if_eq_all([("opt", "default")]),
)
.try_get_matches_from(vec!["myprog"]);
assert!(
result.is_ok(),
"required_if_eq_all should ignore default_value: {:?}",
result.unwrap_err()
);
let m = result.unwrap();
assert_eq!(
m.get_one::<String>("opt").map(|v| v.as_str()),
Some("default")
);
assert!(!*m.get_one::<bool>("flag").expect("defaulted by clap"));
}
#[test]
fn required_unless_on_default_value() {
let result = Command::new("prog")
.arg(Arg::new("opt").long("opt").default_value("default"))
.arg(Arg::new("flag").long("flag").required_unless_present("opt"))
.try_get_matches_from(vec!["myprog"]);
assert!(result.is_err(), "{:?}", result.unwrap());
}
#[test]
fn required_unless_all_on_default_value() {
let result = Command::new("prog")
.arg(Arg::new("opt").long("opt").default_value("default"))
.arg(
Arg::new("flag")
.long("flag")
.required_unless_present_all(["opt"]),
)
.try_get_matches_from(vec!["myprog"]);
assert!(result.is_err(), "{:?}", result.unwrap());
}
#[test]
fn required_error_doesnt_duplicate() {
let cmd = Command::new("Clap-created-USAGE-string-bug")
.arg(Arg::new("a").required(true))
.arg(
Arg::new("b")
.short('b')
.action(ArgAction::Set)
.conflicts_with("c"),
)
.arg(
Arg::new("c")
.short('c')
.action(ArgAction::Set)
.conflicts_with("b"),
);
const EXPECTED: &str = "\
error: The argument '-b <b>' cannot be used with '-c <c>'
Usage:
clap-test -b <b> <a>
For more information try --help
";
utils::assert_output(cmd, "clap-test aaa -b bbb -c ccc", EXPECTED, true);
}
#[test]
fn required_require_with_group_shows_flag() {
let cmd = Command::new("test")
.arg(arg!(--"require-first").requires("first"))
.arg(arg!(--first).group("either_or_both"))
.arg(arg!(--second).group("either_or_both"))
.group(
ArgGroup::new("either_or_both")
.multiple(true)
.required(true),
);
const EXPECTED: &str = "\
error: The following required arguments were not provided:
--first
Usage:
test --require-first <--first|--second>
For more information try --help
";
utils::assert_output(cmd, "test --require-first --second", EXPECTED, true);
}