mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 06:42:33 +00:00
Merge #1797
1797: debug_assertion validations r=CreepySkeleton a=pksunkara Co-authored-by: Pavan Kumar Sunkara <pavan.sss1991@gmail.com>
This commit is contained in:
commit
f4f5200eff
16 changed files with 391 additions and 194 deletions
|
@ -36,6 +36,9 @@ jobs:
|
|||
- rust: beta
|
||||
- rust: nightly
|
||||
cache: false
|
||||
- name: Release profile tests
|
||||
script:
|
||||
- cargo test --release --features yaml unstable
|
||||
- name: Linting (fmt + clippy)
|
||||
before_script:
|
||||
- rustup component add clippy
|
||||
|
|
|
@ -36,6 +36,7 @@ fn flatten() {
|
|||
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn flatten_twice() {
|
||||
|
|
|
@ -1422,12 +1422,6 @@ impl<'b> App<'b> {
|
|||
self._derive_display_order();
|
||||
self._create_help_and_version();
|
||||
|
||||
// Perform expensive debug assertions
|
||||
#[cfg(debug_assertions)]
|
||||
for a in self.args.args.iter() {
|
||||
self._arg_debug_asserts(a);
|
||||
}
|
||||
|
||||
let mut pos_counter = 1;
|
||||
for a in self.args.args.iter_mut() {
|
||||
// Fill in the groups
|
||||
|
@ -1460,19 +1454,16 @@ impl<'b> App<'b> {
|
|||
}
|
||||
}
|
||||
|
||||
debug_assert!(self._app_debug_asserts());
|
||||
self.args._build();
|
||||
self.settings.set(AppSettings::Built);
|
||||
Self::_panic_on_missing_help(&self, self.g_settings.is_set(AppSettings::HelpRequired));
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
self._debug_asserts();
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn _panic_on_missing_help(_app: &App, _help_required_globally: bool) {}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn _panic_on_missing_help(app: &App, help_required_globally: bool) {
|
||||
if app.is_set(AppSettings::HelpRequired) || help_required_globally {
|
||||
let args_missing_help: Vec<String> = app
|
||||
fn _panic_on_missing_help(&self, help_required_globally: bool) {
|
||||
if self.is_set(AppSettings::HelpRequired) || help_required_globally {
|
||||
let args_missing_help: Vec<String> = self
|
||||
.args
|
||||
.args
|
||||
.iter()
|
||||
|
@ -1481,41 +1472,159 @@ impl<'b> App<'b> {
|
|||
.collect();
|
||||
|
||||
if !args_missing_help.is_empty() {
|
||||
let abort_message = format!("AppSettings::HelpRequired is enabled for the App {}, but at least one of its arguments does not have either `help` or `long_help` set. List of such arguments: {}", app.name, args_missing_help.join(", "));
|
||||
panic!(abort_message);
|
||||
panic!(format!(
|
||||
"AppSettings::HelpRequired is enabled for the App {}, but at least one of its arguments does not have either `help` or `long_help` set. List of such arguments: {}",
|
||||
self.name,
|
||||
args_missing_help.join(", ")
|
||||
));
|
||||
}
|
||||
}
|
||||
for sub_app in &app.subcommands {
|
||||
Self::_panic_on_missing_help(&sub_app, help_required_globally);
|
||||
|
||||
for sub_app in &self.subcommands {
|
||||
sub_app._panic_on_missing_help(help_required_globally);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform some expensive assertions on the Parser itself
|
||||
fn _app_debug_asserts(&self) -> bool {
|
||||
debugln!("App::_app_debug_asserts;");
|
||||
for name in self.args.args.iter().map(|x| x.id) {
|
||||
if self.args.args.iter().filter(|x| x.id == name).count() > 1 {
|
||||
panic!(format!(
|
||||
"Arg names must be unique, found '{}' more than once",
|
||||
name
|
||||
));
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn _debug_asserts(&self) {
|
||||
debugln!("App::_debug_asserts;");
|
||||
|
||||
for arg in &self.args.args {
|
||||
arg._debug_asserts();
|
||||
|
||||
// Name conflicts
|
||||
assert!(
|
||||
self.args.args.iter().filter(|x| x.id == arg.id).count() < 2,
|
||||
"Argument name must be unique\n\n\t'{}' is already in use",
|
||||
arg.name,
|
||||
);
|
||||
|
||||
// Long conflicts
|
||||
if let Some(l) = arg.long {
|
||||
assert!(
|
||||
self.args.args.iter().filter(|x| x.long == Some(l)).count() < 2,
|
||||
"Argument long must be unique\n\n\t'--{}' is already in use",
|
||||
l
|
||||
);
|
||||
}
|
||||
|
||||
// Short conflicts
|
||||
if let Some(s) = arg.short {
|
||||
assert!(
|
||||
self.args.args.iter().filter(|x| x.short == Some(s)).count() < 2,
|
||||
"Argument short must be unique\n\n\t'-{}' is already in use",
|
||||
s
|
||||
);
|
||||
}
|
||||
|
||||
// Index conflicts
|
||||
if let Some(idx) = arg.index {
|
||||
assert!(
|
||||
positionals!(self).fold(0, |acc, p| if p.index == Some(idx) {
|
||||
acc + 1
|
||||
} else {
|
||||
acc
|
||||
}) < 2,
|
||||
"Argument '{}' has the same index as another positional argument\n\n\t \
|
||||
Use Arg::setting(ArgSettings::MultipleValues) to allow one \
|
||||
positional argument to take multiple values",
|
||||
arg.name
|
||||
);
|
||||
}
|
||||
|
||||
// requires, r_if, r_unless
|
||||
if let Some(reqs) = &arg.requires {
|
||||
for req in reqs {
|
||||
assert!(
|
||||
self.args.args.iter().any(|x| x.id == req.1)
|
||||
|| self.groups.iter().any(|x| x.id == req.1),
|
||||
"Argument or group specified in 'requires*' for '{}' does not exist",
|
||||
arg.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(reqs) = &arg.r_ifs {
|
||||
for req in reqs {
|
||||
assert!(
|
||||
self.args.args.iter().any(|x| x.id == req.0)
|
||||
|| self.groups.iter().any(|x| x.id == req.0),
|
||||
"Argument or group specified in 'required_if*' for '{}' does not exist",
|
||||
arg.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(reqs) = &arg.r_unless {
|
||||
for req in reqs {
|
||||
assert!(
|
||||
self.args.args.iter().any(|x| x.id == *req)
|
||||
|| self.groups.iter().any(|x| x.id == *req),
|
||||
"Argument or group specified in 'required_unless*' for '{}' does not exist",
|
||||
arg.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// blacklist
|
||||
if let Some(reqs) = &arg.blacklist {
|
||||
for req in reqs {
|
||||
assert!(
|
||||
self.args.args.iter().any(|x| x.id == *req)
|
||||
|| self.groups.iter().any(|x| x.id == *req),
|
||||
"Argument or group specified in 'conflicts_with*' for '{}' does not exist",
|
||||
arg.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if arg.is_set(ArgSettings::Last) {
|
||||
assert!(
|
||||
arg.long.is_none(),
|
||||
"Flags or Options may not have last(true) set. '{}' has both a long and last(true) set.",
|
||||
arg.name
|
||||
);
|
||||
assert!(
|
||||
arg.short.is_none(),
|
||||
"Flags or Options may not have last(true) set. '{}' has both a short and last(true) set.",
|
||||
arg.name
|
||||
);
|
||||
}
|
||||
|
||||
assert!(
|
||||
!(arg.is_set(ArgSettings::Required) && arg.global),
|
||||
"Global arguments cannot be required.\n\n\t'{}' is marked as global and required",
|
||||
arg.name
|
||||
);
|
||||
}
|
||||
|
||||
for group in &self.groups {
|
||||
// Name conflicts
|
||||
assert!(
|
||||
self.groups.iter().filter(|x| x.id == group.id).count() < 2,
|
||||
"Argument group name must be unique\n\n\t'{}' is already in use",
|
||||
group.name,
|
||||
);
|
||||
|
||||
// Groups should not have naming conflicts with Args
|
||||
assert!(
|
||||
!self.args.args.iter().any(|x| x.id == group.id),
|
||||
"Argument group name '{}' must not conflict with argument name",
|
||||
group.name,
|
||||
);
|
||||
|
||||
// Args listed inside groups should exist
|
||||
for arg in &group.args {
|
||||
assert!(
|
||||
self.args.args.iter().any(|x| x.id == *arg),
|
||||
"Argument group '{}' contains non-existent argument",
|
||||
group.name,
|
||||
)
|
||||
}
|
||||
}
|
||||
// * Args listed inside groups should exist
|
||||
// * Groups should not have naming conflicts with Args
|
||||
|
||||
// * Will be removed as a part of removing String types
|
||||
// let g = groups!(self).find(|g| {
|
||||
// g.args
|
||||
// .iter()
|
||||
// .any(|arg| !(find!(self, arg).is_some() || groups!(self).any(|g| &g.name == arg)))
|
||||
// });
|
||||
// assert!(
|
||||
// g.is_none(),
|
||||
// "The group '{}' contains an arg that doesn't exist or has a naming conflict with a group.",
|
||||
// g.unwrap().name
|
||||
// );
|
||||
true
|
||||
self._panic_on_missing_help(self.g_settings.is_set(AppSettings::HelpRequired));
|
||||
}
|
||||
|
||||
pub(crate) fn _propagate(&mut self, prop: Propagation) {
|
||||
|
@ -1641,66 +1750,6 @@ impl<'b> App<'b> {
|
|||
}
|
||||
}
|
||||
|
||||
// Perform expensive assertions on the Arg instance
|
||||
fn _arg_debug_asserts(&self, a: &Arg) -> bool {
|
||||
debugln!("App::_arg_debug_asserts:{}", a.name);
|
||||
|
||||
// Long conflicts
|
||||
if let Some(l) = a.long {
|
||||
assert!(
|
||||
self.args.args.iter().filter(|x| x.long == Some(l)).count() < 2,
|
||||
"Argument long must be unique\n\n\t'--{}' is already in use",
|
||||
l
|
||||
);
|
||||
}
|
||||
|
||||
// Short conflicts
|
||||
if let Some(s) = a.short {
|
||||
assert!(
|
||||
self.args.args.iter().filter(|x| x.short == Some(s)).count() < 2,
|
||||
"Argument short must be unique\n\n\t'-{}' is already in use",
|
||||
s
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(idx) = a.index {
|
||||
// No index conflicts
|
||||
assert!(
|
||||
positionals!(self).fold(0, |acc, p| if p.index == Some(idx) {
|
||||
acc + 1
|
||||
} else {
|
||||
acc
|
||||
}) < 2,
|
||||
"Argument '{}' has the same index as another positional \
|
||||
argument\n\n\tUse Arg::setting(ArgSettings::MultipleValues) to allow one \
|
||||
positional argument to take multiple values",
|
||||
a.name
|
||||
);
|
||||
}
|
||||
if a.is_set(ArgSettings::Last) {
|
||||
assert!(
|
||||
a.long.is_none(),
|
||||
"Flags or Options may not have last(true) set. '{}' has both a long and \
|
||||
last(true) set.",
|
||||
a.name
|
||||
);
|
||||
assert!(
|
||||
a.short.is_none(),
|
||||
"Flags or Options may not have last(true) set. '{}' has both a short and \
|
||||
last(true) set.",
|
||||
a.name
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
!(a.is_set(ArgSettings::Required) && a.global),
|
||||
"Global arguments cannot be required.\n\n\t'{}' is marked as \
|
||||
global and required",
|
||||
a.name
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
// used in clap_generate (https://github.com/clap-rs/clap_generate)
|
||||
#[doc(hidden)]
|
||||
pub fn _build_bin_names(&mut self) {
|
||||
|
|
|
@ -666,7 +666,7 @@ pub enum AppSettings {
|
|||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// ```rust,should_panic
|
||||
/// ```rust,no_run
|
||||
/// # use clap::{App, Arg, AppSettings};
|
||||
/// App::new("myapp")
|
||||
/// .setting(AppSettings::HelpRequired)
|
||||
|
|
|
@ -64,10 +64,11 @@ pub struct Arg<'help> {
|
|||
#[doc(hidden)]
|
||||
pub blacklist: Option<Vec<Id>>,
|
||||
pub(crate) settings: ArgFlags,
|
||||
pub(crate) r_unless: Option<Vec<Id>>,
|
||||
pub(crate) overrides: Option<Vec<Id>>,
|
||||
pub(crate) groups: Option<Vec<Id>>,
|
||||
pub(crate) requires: Option<Vec<(Option<&'help str>, Id)>>,
|
||||
pub(crate) r_ifs: Option<Vec<(Id, &'help str)>>,
|
||||
pub(crate) r_unless: Option<Vec<Id>>,
|
||||
#[doc(hidden)]
|
||||
pub short: Option<char>,
|
||||
#[doc(hidden)]
|
||||
|
@ -90,7 +91,6 @@ pub struct Arg<'help> {
|
|||
pub(crate) terminator: Option<&'help str>,
|
||||
#[doc(hidden)]
|
||||
pub index: Option<u64>,
|
||||
pub(crate) r_ifs: Option<Vec<(Id, &'help str)>>,
|
||||
#[doc(hidden)]
|
||||
pub help_heading: Option<&'help str>,
|
||||
pub(crate) global: bool,
|
||||
|
@ -107,6 +107,7 @@ impl<'help> Arg<'help> {
|
|||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new instance of [`Arg`] using a unique string name. The name will be used to get
|
||||
/// information about whether or not the argument was used at runtime, get values, set
|
||||
/// relationships with other args, etc..
|
||||
|
@ -1113,13 +1114,11 @@ impl<'help> Arg<'help> {
|
|||
/// [Conflicting]: ./struct.Arg.html#method.conflicts_with
|
||||
/// [override]: ./struct.Arg.html#method.overrides_with
|
||||
pub fn requires<T: Key>(mut self, arg_id: T) -> Self {
|
||||
let name = arg_id.key();
|
||||
let arg = arg_id.key();
|
||||
if let Some(ref mut vec) = self.requires {
|
||||
vec.push((None, name));
|
||||
vec.push((None, arg));
|
||||
} else {
|
||||
let mut vec = vec![];
|
||||
vec.push((None, name));
|
||||
self.requires = Some(vec);
|
||||
self.requires = Some(vec![(None, arg)]);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@ -1495,11 +1494,7 @@ impl<'help> Arg<'help> {
|
|||
vec.push((None, s.key()));
|
||||
}
|
||||
} else {
|
||||
let mut vec = vec![];
|
||||
for s in names {
|
||||
vec.push((None, s.key()));
|
||||
}
|
||||
self.requires = Some(vec);
|
||||
self.requires = Some(names.iter().map(|s| (None, s.key())).collect());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@ -4132,6 +4127,21 @@ impl<'help> Arg<'help> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Arg<'a> {
|
||||
pub(crate) fn _debug_asserts(&self) {
|
||||
debugln!("Arg::_debug_asserts:{};", self.name);
|
||||
|
||||
// Self conflict
|
||||
if let Some(vec) = &self.blacklist {
|
||||
assert!(
|
||||
!vec.iter().any(|&x| x == self.id),
|
||||
"Argument '{}' cannot conflict with itself",
|
||||
self.name,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'help, 'z> From<&'z Arg<'help>> for Arg<'help> {
|
||||
fn from(a: &'z Arg<'help>) -> Self {
|
||||
a.clone()
|
||||
|
|
|
@ -373,6 +373,7 @@ where
|
|||
}
|
||||
|
||||
debug_assert!(self._verify_positionals());
|
||||
|
||||
// Set the LowIndexMultiple flag if required
|
||||
if positionals!(self.app).any(|a| {
|
||||
a.is_set(ArgSettings::MultipleValues)
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
use clap::{App, Arg};
|
||||
|
||||
#[test]
|
||||
fn two_conflicting_arguments() {
|
||||
let a = App::new("two_conflicting_arguments")
|
||||
.arg(
|
||||
Arg::with_name("develop")
|
||||
.long("develop")
|
||||
.conflicts_with("production"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("production")
|
||||
.long("production")
|
||||
.conflicts_with("develop"),
|
||||
)
|
||||
.try_get_matches_from(vec!["", "--develop", "--production"]);
|
||||
|
||||
assert!(a.is_err());
|
||||
let a = a.unwrap_err();
|
||||
assert_eq!(
|
||||
a.cause,
|
||||
"The argument \'--develop\' cannot be used with \'--production\'"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_conflicting_arguments() {
|
||||
let a = App::new("two_conflicting_arguments")
|
||||
.arg(
|
||||
Arg::with_name("one")
|
||||
.long("one")
|
||||
.conflicts_with_all(&["two", "three"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("two")
|
||||
.long("two")
|
||||
.conflicts_with_all(&["one", "three"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("three")
|
||||
.long("three")
|
||||
.conflicts_with_all(&["one", "two"]),
|
||||
)
|
||||
.try_get_matches_from(vec!["", "--one", "--two", "--three"]);
|
||||
|
||||
assert!(a.is_err());
|
||||
let a = a.unwrap_err();
|
||||
assert_eq!(
|
||||
a.cause,
|
||||
"The argument \'--one\' cannot be used with \'--two\'"
|
||||
);
|
||||
}
|
|
@ -127,3 +127,80 @@ fn conflict_with_unused_default_value() {
|
|||
assert_eq!(m.value_of("opt"), Some("default"));
|
||||
assert!(m.is_present("flag"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_conflicting_arguments() {
|
||||
let a = App::new("two_conflicting_arguments")
|
||||
.arg(
|
||||
Arg::with_name("develop")
|
||||
.long("develop")
|
||||
.conflicts_with("production"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("production")
|
||||
.long("production")
|
||||
.conflicts_with("develop"),
|
||||
)
|
||||
.try_get_matches_from(vec!["", "--develop", "--production"]);
|
||||
|
||||
assert!(a.is_err());
|
||||
let a = a.unwrap_err();
|
||||
assert_eq!(
|
||||
a.cause,
|
||||
"The argument \'--develop\' cannot be used with \'--production\'"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_conflicting_arguments() {
|
||||
let a = App::new("two_conflicting_arguments")
|
||||
.arg(
|
||||
Arg::with_name("one")
|
||||
.long("one")
|
||||
.conflicts_with_all(&["two", "three"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("two")
|
||||
.long("two")
|
||||
.conflicts_with_all(&["one", "three"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("three")
|
||||
.long("three")
|
||||
.conflicts_with_all(&["one", "two"]),
|
||||
)
|
||||
.try_get_matches_from(vec!["", "--one", "--two", "--three"]);
|
||||
|
||||
assert!(a.is_err());
|
||||
let a = a.unwrap_err();
|
||||
assert_eq!(
|
||||
a.cause,
|
||||
"The argument \'--one\' cannot be used with \'--two\'"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic = "Argument 'config' cannot conflict with itself"]
|
||||
fn self_conflicting_arg() {
|
||||
let _ = App::new("prog")
|
||||
.arg(
|
||||
Arg::with_name("config")
|
||||
.long("config")
|
||||
.conflicts_with("config"),
|
||||
)
|
||||
.try_get_matches_from(vec!["", "--config"]);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic = "Argument or group specified in 'conflicts_with*' for 'config' does not exist"]
|
||||
fn conflicts_with_invalid_arg() {
|
||||
let _ = App::new("prog")
|
||||
.arg(
|
||||
Arg::with_name("config")
|
||||
.long("config")
|
||||
.conflicts_with("extra"),
|
||||
)
|
||||
.try_get_matches_from(vec!["", "--config"]);
|
||||
}
|
||||
|
|
2
tests/fixtures/app.yml
vendored
2
tests/fixtures/app.yml
vendored
|
@ -9,7 +9,7 @@ args:
|
|||
short: h
|
||||
long: help
|
||||
help: prints help with a nonstandard description
|
||||
- opt:
|
||||
- option:
|
||||
short: o
|
||||
long: option
|
||||
multiple: true
|
||||
|
|
|
@ -41,13 +41,9 @@ fn required_group_missing_arg() {
|
|||
assert_eq!(err.kind, ErrorKind::MissingRequiredArgument);
|
||||
}
|
||||
|
||||
// This tests a programmer error and will only succeed with debug_assertions
|
||||
// #[cfg(debug_assertions)]
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
// This used to provide a nice, programmer-friendly error.
|
||||
// Now the error directs the programmer to file a bug report with clap.
|
||||
// #[should_panic(expected = "The group 'req' contains the arg 'flg' that doesn't actually exist.")]
|
||||
#[should_panic(expected = "internal error")]
|
||||
#[should_panic = "Argument group 'req' contains non-existent argument"]
|
||||
fn non_existing_arg() {
|
||||
let _ = App::new("group")
|
||||
.arg("-f, --flag 'some flag'")
|
||||
|
@ -60,6 +56,37 @@ fn non_existing_arg() {
|
|||
.try_get_matches_from(vec![""]);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic = "Argument group name must be unique\n\n\t'req' is already in use"]
|
||||
fn unique_group_name() {
|
||||
let _ = App::new("group")
|
||||
.arg("-f, --flag 'some flag'")
|
||||
.arg("-c, --color 'some other flag'")
|
||||
.group(ArgGroup::with_name("req").args(&["flag"]).required(true))
|
||||
.group(ArgGroup::with_name("req").args(&["color"]).required(true))
|
||||
.try_get_matches_from(vec![""]);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic = "Argument group name '' must not conflict with argument name"]
|
||||
fn groups_with_name_of_arg_name() {
|
||||
let _ = App::new("group")
|
||||
.arg(Arg::with_name("a").long("a").group("a"))
|
||||
.try_get_matches_from(vec!["", "--a"]);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic = "Argument group name 'a' must not conflict with argument name"]
|
||||
fn arg_group_with_name_of_arg_name() {
|
||||
let _ = App::new("group")
|
||||
.arg(Arg::with_name("a").long("a").group("a"))
|
||||
.group(ArgGroup::with_name("a"))
|
||||
.try_get_matches_from(vec!["", "--a"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_single_value() {
|
||||
let res = App::new("group")
|
||||
|
|
|
@ -347,6 +347,16 @@ FLAGS:
|
|||
-H, --help Print help information
|
||||
-v, --version Print version information";
|
||||
|
||||
static HELP_CONFLICT: &str = "conflict
|
||||
|
||||
USAGE:
|
||||
conflict [FLAGS]
|
||||
|
||||
FLAGS:
|
||||
--help Prints help information
|
||||
-h
|
||||
-V, --version Prints version information";
|
||||
|
||||
static LAST_ARG: &str = "last 0.1
|
||||
|
||||
USAGE:
|
||||
|
@ -1162,6 +1172,28 @@ fn customize_version_and_help() {
|
|||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arg_short_conflict_with_help() {
|
||||
let app = App::new("conflict").arg(Arg::with_name("home").short('h'));
|
||||
|
||||
assert!(utils::compare_output(
|
||||
app,
|
||||
"conflict --help",
|
||||
HELP_CONFLICT,
|
||||
false
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic = "Argument short must be unique\n\n\t'-h' is already in use"]
|
||||
fn arg_short_conflict_with_help_mut_arg() {
|
||||
let _ = App::new("conflict")
|
||||
.arg(Arg::with_name("home").short('h'))
|
||||
.mut_arg("help", |h| h.short('h'))
|
||||
.try_get_matches_from(vec![""]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last_arg_mult_usage() {
|
||||
let app = App::new("last")
|
||||
|
@ -1569,8 +1601,9 @@ fn issue_1487() {
|
|||
assert!(utils::compare_output(app, "ctest -h", ISSUE_1487, false));
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic = "AppSettings::HelpRequired is enabled for the App"]
|
||||
fn help_required_but_not_given() {
|
||||
App::new("myapp")
|
||||
.setting(AppSettings::HelpRequired)
|
||||
|
@ -1578,8 +1611,9 @@ fn help_required_but_not_given() {
|
|||
.get_matches();
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic = "AppSettings::HelpRequired is enabled for the App"]
|
||||
fn help_required_but_not_given_settings_after_args() {
|
||||
App::new("myapp")
|
||||
.arg(Arg::with_name("foo"))
|
||||
|
@ -1587,8 +1621,9 @@ fn help_required_but_not_given_settings_after_args() {
|
|||
.get_matches();
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic = "AppSettings::HelpRequired is enabled for the App"]
|
||||
fn help_required_but_not_given_for_one_of_two_arguments() {
|
||||
App::new("myapp")
|
||||
.setting(AppSettings::HelpRequired)
|
||||
|
@ -1610,8 +1645,9 @@ fn help_required_locally_but_not_given_for_subcommand() {
|
|||
.get_matches();
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
#[should_panic = "AppSettings::HelpRequired is enabled for the App"]
|
||||
fn help_required_globally_but_not_given_for_subcommand() {
|
||||
App::new("myapp")
|
||||
.global_setting(AppSettings::HelpRequired)
|
||||
|
|
|
@ -39,7 +39,7 @@ fn quoted_app_name() {
|
|||
(version: "0.1")
|
||||
(about: "tests clap library")
|
||||
(author: "Kevin K. <kbknapp@gmail.com>")
|
||||
(@arg opt: -o --option +takes_value ... "tests options")
|
||||
(@arg option: -o --option +takes_value ... "tests options")
|
||||
(@arg positional: index(1) "tests positionals")
|
||||
(@arg flag: -f --flag ... +global "tests flags")
|
||||
(@arg flag2: -F conflicts_with[flag] requires[option2]
|
||||
|
@ -80,7 +80,7 @@ fn quoted_arg_long_name() {
|
|||
(version: "0.1")
|
||||
(about: "tests clap library")
|
||||
(author: "Kevin K. <kbknapp@gmail.com>")
|
||||
(@arg opt: -o --option +takes_value ... "tests options")
|
||||
(@arg option: -o --option +takes_value ... "tests options")
|
||||
(@arg positional: index(1) "tests positionals")
|
||||
(@arg flag: -f --flag ... +global "tests flags")
|
||||
(@arg flag2: -F conflicts_with[flag] requires[option2]
|
||||
|
@ -118,7 +118,7 @@ fn quoted_arg_name() {
|
|||
(version: "0.1")
|
||||
(about: "tests clap library")
|
||||
(author: "Kevin K. <kbknapp@gmail.com>")
|
||||
(@arg opt: -o --option +takes_value ... "tests options")
|
||||
(@arg option: -o --option +takes_value ... "tests options")
|
||||
(@arg ("positional-arg"): index(1) "tests positionals")
|
||||
(@arg flag: -f --flag ... +global "tests flags")
|
||||
(@arg flag2: -F conflicts_with[flag] requires[option2]
|
||||
|
|
|
@ -964,10 +964,10 @@ fn req_delimiter_complex() {
|
|||
// This tests a programmer error and will only succeed with debug_assertions
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic(expected = "When using a positional argument with \
|
||||
#[should_panic = "When using a positional argument with \
|
||||
.multiple(true) that is *not the last* positional argument, the last \
|
||||
positional argument (i.e the one with the highest index) *must* have \
|
||||
.required(true) or .last(true) set.")]
|
||||
.required(true) or .last(true) set."]
|
||||
fn low_index_positional_not_required() {
|
||||
let _ = App::new("lip")
|
||||
.arg(
|
||||
|
@ -983,8 +983,8 @@ fn low_index_positional_not_required() {
|
|||
// This tests a programmer error and will only succeed with debug_assertions
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic(expected = "Only one positional argument with .multiple(true) \
|
||||
set is allowed per command, unless the second one also has .last(true) set")]
|
||||
#[should_panic = "Only one positional argument with .multiple(true) \
|
||||
set is allowed per command, unless the second one also has .last(true) set"]
|
||||
fn low_index_positional_last_multiple_too() {
|
||||
let _ = App::new("lip")
|
||||
.arg(
|
||||
|
@ -1005,8 +1005,8 @@ fn low_index_positional_last_multiple_too() {
|
|||
// This tests a programmer error and will only succeed with debug_assertions
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic(expected = "Only the last positional argument, or second to \
|
||||
last positional argument may be set to .multiple(true)")]
|
||||
#[should_panic = "Only the last positional argument, or second to \
|
||||
last positional argument may be set to .multiple(true)"]
|
||||
fn low_index_positional_too_far_back() {
|
||||
let _ = App::new("lip")
|
||||
.arg(
|
||||
|
|
|
@ -219,8 +219,8 @@ fn single_positional_required_usage_string() {
|
|||
// This tests a programmer error and will only succeed with debug_assertions
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic(expected = "Found positional argument which is not required \
|
||||
with a lower index than a required positional argument")]
|
||||
#[should_panic = "Found positional argument which is not required \
|
||||
with a lower index than a required positional argument"]
|
||||
fn missing_required() {
|
||||
let _ = App::new("test")
|
||||
.arg("[FILE1] 'some file'")
|
||||
|
|
|
@ -728,3 +728,51 @@ fn issue_1643_args_mutually_require_each_other() {
|
|||
|
||||
app.get_matches_from(&["test", "-u", "hello", "-r", "farewell"]);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic = "Argument or group specified in 'requires*' for 'config' does not exist"]
|
||||
fn requires_invalid_arg() {
|
||||
let _ = App::new("prog")
|
||||
.arg(Arg::with_name("config").requires("extra").long("config"))
|
||||
.try_get_matches_from(vec!["", "--config"]);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic = "Argument or group specified in 'requires*' for 'config' does not exist"]
|
||||
fn requires_if_invalid_arg() {
|
||||
let _ = App::new("prog")
|
||||
.arg(
|
||||
Arg::with_name("config")
|
||||
.requires_if("val", "extra")
|
||||
.long("config"),
|
||||
)
|
||||
.try_get_matches_from(vec!["", "--config"]);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic = "Argument or group specified in 'required_if*' for 'config' does not exist"]
|
||||
fn required_if_invalid_arg() {
|
||||
let _ = App::new("prog")
|
||||
.arg(
|
||||
Arg::with_name("config")
|
||||
.required_if("extra", "val")
|
||||
.long("config"),
|
||||
)
|
||||
.try_get_matches_from(vec!["", "--config"]);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic = "Argument or group specified in 'required_unless*' for 'config' does not exist"]
|
||||
fn required_unless_invalid_arg() {
|
||||
let _ = App::new("prog")
|
||||
.arg(
|
||||
Arg::with_name("config")
|
||||
.required_unless("extra")
|
||||
.long("config"),
|
||||
)
|
||||
.try_get_matches_from(vec![""]);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use clap::{App, Arg};
|
||||
|
||||
// This tests a programmer error and will only succeed with debug_assertions
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic(expected = "Arg names must be unique")]
|
||||
#[should_panic = "Argument name must be unique\n\n\t'arg1' is already in use"]
|
||||
fn unique_arg_names() {
|
||||
let _ = App::new("some")
|
||||
.args(&[
|
||||
|
@ -13,10 +12,9 @@ fn unique_arg_names() {
|
|||
.try_get_matches();
|
||||
}
|
||||
|
||||
// This tests a programmer error and will only succeed with debug_assertions
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic(expected = "Argument short must be unique")]
|
||||
#[should_panic = "Argument short must be unique\n\n\t'-a' is already in use"]
|
||||
fn unique_arg_shorts() {
|
||||
let _ = App::new("some")
|
||||
.args(&[
|
||||
|
@ -26,10 +24,9 @@ fn unique_arg_shorts() {
|
|||
.try_get_matches();
|
||||
}
|
||||
|
||||
// This tests a programmer error and will only succeed with debug_assertions
|
||||
#[cfg(debug_assertions)]
|
||||
#[test]
|
||||
#[should_panic(expected = "Argument long must be unique")]
|
||||
#[should_panic = "Argument long must be unique\n\n\t'--long' is already in use"]
|
||||
fn unique_arg_longs() {
|
||||
let _ = App::new("some")
|
||||
.args(&[
|
||||
|
|
Loading…
Reference in a new issue