1797: debug_assertion validations r=CreepySkeleton a=pksunkara



Co-authored-by: Pavan Kumar Sunkara <pavan.sss1991@gmail.com>
This commit is contained in:
bors[bot] 2020-04-10 01:35:44 +00:00 committed by GitHub
commit f4f5200eff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 391 additions and 194 deletions

View file

@ -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

View file

@ -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() {

View file

@ -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) {

View file

@ -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)

View file

@ -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()

View file

@ -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)

View file

@ -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\'"
);
}

View file

@ -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"]);
}

View file

@ -9,7 +9,7 @@ args:
short: h
long: help
help: prints help with a nonstandard description
- opt:
- option:
short: o
long: option
multiple: true

View file

@ -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")

View file

@ -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)

View file

@ -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]

View file

@ -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(

View file

@ -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'")

View 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![""]);
}

View file

@ -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(&[