refactor(tests): Prepare for Special Type experiments

In experimenting on #1772, I want to write test cases for various
combinations of required or not, values vs occurrences, etc.  There
wasn't really a clear place to put these.

On top of that, I wanted there to be a clear place in the tests for
describing the behavior of special types, to make it easier to audit and
easier to see how a PR for #1772 changes things.

As part of this effort in organizing these tests, I reduced the number
of tests that use special types.  This better focuses these tests on the
cases they are intending to cover, rather than pulling in unrelated
features.  This makes it easier to audit special types and makes it so
failures give more focused results, making it easier to see what broke.
This commit is contained in:
Ed Page 2021-11-04 10:15:04 -05:00
parent 4dfa56a9e4
commit 78e4c90326
13 changed files with 271 additions and 336 deletions

View file

@ -297,7 +297,41 @@ fn multiple_alias() {
} }
#[test] #[test]
fn option() { fn skip_variant() {
#[derive(ArgEnum, PartialEq, Debug, Clone)]
#[allow(dead_code)] // silence warning about `Baz` being unused
enum ArgChoice {
Foo,
Bar,
#[clap(skip)]
Baz,
}
assert_eq!(
ArgChoice::value_variants()
.iter()
.map(ArgEnum::to_possible_value)
.map(Option::unwrap)
.collect::<Vec<_>>(),
vec![PossibleValue::new("foo"), PossibleValue::new("bar")]
);
assert!(ArgChoice::from_str("foo", true).is_ok());
assert!(ArgChoice::from_str("bar", true).is_ok());
assert!(ArgChoice::from_str("baz", true).is_err());
}
#[test]
fn from_str_invalid() {
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
}
assert!(ArgChoice::from_str("bar", true).is_err());
}
#[test]
fn option_type() {
#[derive(ArgEnum, PartialEq, Debug, Clone)] #[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice { enum ArgChoice {
Foo, Foo,
@ -327,7 +361,7 @@ fn option() {
} }
#[test] #[test]
fn option_option() { fn option_option_type() {
#[derive(ArgEnum, PartialEq, Debug, Clone)] #[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice { enum ArgChoice {
Foo, Foo,
@ -361,7 +395,7 @@ fn option_option() {
} }
#[test] #[test]
fn vector() { fn vec_type() {
#[derive(ArgEnum, PartialEq, Debug, Clone)] #[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice { enum ArgChoice {
Foo, Foo,
@ -391,7 +425,7 @@ fn vector() {
} }
#[test] #[test]
fn option_vector() { fn option_vec_type() {
#[derive(ArgEnum, PartialEq, Debug, Clone)] #[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice { enum ArgChoice {
Foo, Foo,
@ -423,37 +457,3 @@ fn option_vector() {
); );
assert!(Opt::try_parse_from(&["", "-a", "fOo"]).is_err()); assert!(Opt::try_parse_from(&["", "-a", "fOo"]).is_err());
} }
#[test]
fn skip_variant() {
#[derive(ArgEnum, PartialEq, Debug, Clone)]
#[allow(dead_code)] // silence warning about `Baz` being unused
enum ArgChoice {
Foo,
Bar,
#[clap(skip)]
Baz,
}
assert_eq!(
ArgChoice::value_variants()
.iter()
.map(ArgEnum::to_possible_value)
.map(Option::unwrap)
.collect::<Vec<_>>(),
vec![PossibleValue::new("foo"), PossibleValue::new("bar")]
);
assert!(ArgChoice::from_str("foo", true).is_ok());
assert!(ArgChoice::from_str("bar", true).is_ok());
assert!(ArgChoice::from_str("baz", true).is_err());
}
#[test]
fn from_str_invalid() {
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
}
assert!(ArgChoice::from_str("bar", true).is_err());
}

View file

@ -29,20 +29,6 @@ fn required_argument() {
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err()); assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
} }
#[test]
fn optional_argument() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: Option<i32>,
}
assert_eq!(
Opt { arg: Some(42) },
Opt::try_parse_from(&["test", "42"]).unwrap()
);
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
}
#[test] #[test]
fn argument_with_default() { fn argument_with_default() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
@ -58,45 +44,6 @@ fn argument_with_default() {
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err()); assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
} }
#[test]
fn arguments() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: Vec<i32>,
}
assert_eq!(
Opt { arg: vec![24] },
Opt::try_parse_from(&["test", "24"]).unwrap()
);
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { arg: vec![24, 42] },
Opt::try_parse_from(&["test", "24", "42"]).unwrap()
);
}
#[test]
fn arguments_safe() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: Vec<i32>,
}
assert_eq!(
Opt { arg: vec![24] },
Opt::try_parse_from(&["test", "24"]).unwrap()
);
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { arg: vec![24, 42] },
Opt::try_parse_from(&["test", "24", "42"]).unwrap()
);
assert_eq!(
clap::ErrorKind::ValueValidation,
Opt::try_parse_from(&["test", "NOPE"]).err().unwrap().kind
);
}
#[test] #[test]
fn auto_value_name() { fn auto_value_name() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
@ -136,3 +83,38 @@ fn explicit_value_name() {
Opt::try_parse_from(&["test", "10"]).unwrap() Opt::try_parse_from(&["test", "10"]).unwrap()
); );
} }
#[test]
fn option_type_is_optional() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: Option<i32>,
}
assert_eq!(
Opt { arg: Some(42) },
Opt::try_parse_from(&["test", "42"]).unwrap()
);
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
assert!(Opt::try_parse_from(&["test", "42", "24"]).is_err());
}
#[test]
fn vec_type_is_multiple_values() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: Vec<i32>,
}
assert_eq!(
Opt { arg: vec![24] },
Opt::try_parse_from(&["test", "24"]).unwrap()
);
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { arg: vec![24, 42] },
Opt::try_parse_from(&["test", "24", "42"]).unwrap()
);
assert_eq!(
clap::ErrorKind::ValueValidation,
Opt::try_parse_from(&["test", "NOPE"]).err().unwrap().kind
);
}

View file

@ -19,17 +19,12 @@ fn basic() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
struct Opt { struct Opt {
#[clap(short = 'a', long = "arg")] #[clap(short = 'a', long = "arg")]
arg: Vec<i32>, arg: i32,
} }
assert_eq!( assert_eq!(
Opt { arg: vec![24] }, Opt { arg: 24 },
Opt::try_parse_from(&["test", "-a24"]).unwrap() Opt::try_parse_from(&["test", "-a24"]).unwrap()
); );
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { arg: vec![24, 42] },
Opt::try_parse_from(&["test", "--arg", "24", "42"]).unwrap()
);
} }
#[test] #[test]

View file

@ -108,7 +108,7 @@ fn top_long_doc_comment_both_help_long_help() {
/// Or something else /// Or something else
Foo { Foo {
#[clap(about = "foo")] #[clap(about = "foo")]
bars: Vec<String>, bars: String,
}, },
} }

View file

@ -7,15 +7,20 @@ use utils::*;
fn explicit_short_long_no_rename() { fn explicit_short_long_no_rename() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
struct Opt { struct Opt {
#[clap(short = '.', long = ".foo", multiple_occurrences(true))] #[clap(short = '.', long = ".foo")]
foo: Vec<String>, foo: String,
} }
assert_eq!(
Opt { foo: "long".into() },
Opt::try_parse_from(&["test", "--.foo", "long"]).unwrap()
);
assert_eq!( assert_eq!(
Opt { Opt {
foo: vec!["short".into(), "long".into()] foo: "short".into(),
}, },
Opt::try_parse_from(&["test", "-.", "short", "--.foo", "long"]).unwrap() Opt::try_parse_from(&["test", "-.", "short"]).unwrap()
); );
} }
@ -24,9 +29,9 @@ fn explicit_name_no_rename() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
struct Opt { struct Opt {
#[clap(name = ".options")] #[clap(name = ".options")]
foo: Vec<String>, foo: String,
} }
let help = get_long_help::<Opt>(); let help = get_long_help::<Opt>();
assert!(help.contains("[.options]...")) assert!(help.contains("<.options>"))
} }

View file

@ -15,7 +15,7 @@
use clap::Parser; use clap::Parser;
#[test] #[test]
fn unique_flag() { fn bool_type_is_flag() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
struct Opt { struct Opt {
#[clap(short, long)] #[clap(short, long)]
@ -41,7 +41,7 @@ fn unique_flag() {
} }
#[test] #[test]
fn multiple_flag() { fn from_occurrences() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
struct Opt { struct Opt {
#[clap(short, long, parse(from_occurrences))] #[clap(short, long, parse(from_occurrences))]
@ -74,12 +74,12 @@ fn multiple_flag() {
assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err()); assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err());
} }
#[test]
fn non_bool_type_flag() {
fn parse_from_flag(b: bool) -> std::sync::atomic::AtomicBool { fn parse_from_flag(b: bool) -> std::sync::atomic::AtomicBool {
std::sync::atomic::AtomicBool::new(b) std::sync::atomic::AtomicBool::new(b)
} }
#[test]
fn non_bool_flags() {
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Opt { struct Opt {
#[clap(short, long, parse(from_flag = parse_from_flag))] #[clap(short, long, parse(from_flag = parse_from_flag))]
@ -106,7 +106,7 @@ fn non_bool_flags() {
} }
#[test] #[test]
fn combined_flags() { fn mixed_type_flags() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
struct Opt { struct Opt {
#[clap(short, long)] #[clap(short, long)]
@ -158,3 +158,32 @@ fn combined_flags() {
Opt::try_parse_from(&["test", "-bb", "-a", "-bb"]).unwrap() Opt::try_parse_from(&["test", "-bb", "-a", "-bb"]).unwrap()
); );
} }
#[test]
fn ignore_qualified_bool_type() {
mod inner {
#[allow(non_camel_case_types)]
#[derive(PartialEq, Debug)]
pub struct bool(pub String);
impl std::str::FromStr for self::bool {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(self::bool(s.into()))
}
}
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: inner::bool,
}
assert_eq!(
Opt {
arg: inner::bool("success".into())
},
Opt::try_parse_from(&["test", "success"]).unwrap()
);
}

View file

@ -6,10 +6,10 @@ fn arg_help_heading_applied() {
struct CliOptions { struct CliOptions {
#[clap(long)] #[clap(long)]
#[clap(help_heading = Some("HEADING A"))] #[clap(help_heading = Some("HEADING A"))]
should_be_in_section_a: Option<u32>, should_be_in_section_a: u32,
#[clap(long)] #[clap(long)]
no_section: Option<u32>, no_section: u32,
} }
let app = CliOptions::into_app(); let app = CliOptions::into_app();
@ -34,10 +34,10 @@ fn app_help_heading_applied() {
struct CliOptions { struct CliOptions {
#[clap(long)] #[clap(long)]
#[clap(help_heading = Some("HEADING A"))] #[clap(help_heading = Some("HEADING A"))]
should_be_in_section_a: Option<u32>, should_be_in_section_a: u32,
#[clap(long)] #[clap(long)]
should_be_in_default_section: Option<u32>, should_be_in_default_section: u32,
} }
let app = CliOptions::into_app(); let app = CliOptions::into_app();
@ -72,21 +72,21 @@ fn app_help_heading_flattened() {
sub_a: SubA, sub_a: SubA,
#[clap(long)] #[clap(long)]
should_be_in_default_section: Option<u32>, should_be_in_default_section: u32,
} }
#[derive(Debug, Clone, Args)] #[derive(Debug, Clone, Args)]
#[clap(help_heading = "HEADING A")] #[clap(help_heading = "HEADING A")]
struct OptionsA { struct OptionsA {
#[clap(long)] #[clap(long)]
should_be_in_section_a: Option<u32>, should_be_in_section_a: u32,
} }
#[derive(Debug, Clone, Args)] #[derive(Debug, Clone, Args)]
#[clap(help_heading = "HEADING B")] #[clap(help_heading = "HEADING B")]
struct OptionsB { struct OptionsB {
#[clap(long)] #[clap(long)]
should_be_in_section_b: Option<u32>, should_be_in_section_b: u32,
} }
#[derive(Debug, Clone, Subcommand)] #[derive(Debug, Clone, Subcommand)]
@ -98,20 +98,20 @@ fn app_help_heading_flattened() {
SubAOne, SubAOne,
#[clap(help_heading = "SUB A")] #[clap(help_heading = "SUB A")]
SubATwo { SubATwo {
should_be_in_sub_a: Option<u32>, should_be_in_sub_a: u32,
}, },
} }
#[derive(Debug, Clone, Subcommand)] #[derive(Debug, Clone, Subcommand)]
enum SubB { enum SubB {
#[clap(help_heading = "SUB B")] #[clap(help_heading = "SUB B")]
SubBOne { should_be_in_sub_b: Option<u32> }, SubBOne { should_be_in_sub_b: u32 },
} }
#[derive(Debug, Clone, Subcommand)] #[derive(Debug, Clone, Subcommand)]
enum SubC { enum SubC {
#[clap(help_heading = "SUB C")] #[clap(help_heading = "SUB C")]
SubCOne { should_be_in_sub_c: Option<u32> }, SubCOne { should_be_in_sub_c: u32 },
} }
let app = CliOptions::into_app(); let app = CliOptions::into_app();
@ -172,7 +172,7 @@ fn flatten_field_with_help_heading() {
#[derive(Debug, Clone, Args)] #[derive(Debug, Clone, Args)]
struct OptionsA { struct OptionsA {
#[clap(long)] #[clap(long)]
should_be_in_section_a: Option<u32>, should_be_in_section_a: u32,
} }
let app = CliOptions::into_app(); let app = CliOptions::into_app();

View file

@ -63,7 +63,7 @@ fn issue_324() {
#[clap(version = my_version())] #[clap(version = my_version())]
struct Opt { struct Opt {
#[clap(subcommand)] #[clap(subcommand)]
_cmd: Option<SubCommand>, _cmd: SubCommand,
} }
#[derive(Subcommand)] #[derive(Subcommand)]

View file

@ -42,21 +42,6 @@ fn required_option() {
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err()); assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
} }
#[test]
fn optional_option() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short)]
arg: Option<i32>,
}
assert_eq!(
Opt { arg: Some(42) },
Opt::try_parse_from(&["test", "-a42"]).unwrap()
);
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
}
#[test] #[test]
fn option_with_default() { fn option_with_default() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
@ -87,41 +72,6 @@ fn option_with_raw_default() {
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err()); assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
} }
#[test]
fn options() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, multiple_occurrences(true))]
arg: Vec<i32>,
}
assert_eq!(
Opt { arg: vec![24] },
Opt::try_parse_from(&["test", "-a24"]).unwrap()
);
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { arg: vec![24, 42] },
Opt::try_parse_from(&["test", "-a24", "--arg", "42"]).unwrap()
);
}
#[test]
fn default_value() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, default_value = "test")]
arg: String,
}
assert_eq!(
Opt { arg: "test".into() },
Opt::try_parse_from(&["test"]).unwrap()
);
assert_eq!(
Opt { arg: "foo".into() },
Opt::try_parse_from(&["test", "-afoo"]).unwrap()
);
}
#[test] #[test]
fn option_from_str() { fn option_from_str() {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -147,7 +97,81 @@ fn option_from_str() {
} }
#[test] #[test]
fn optional_argument_for_optional_option() { fn option_type_is_optional() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short)]
arg: Option<i32>,
}
assert_eq!(
Opt { arg: Some(42) },
Opt::try_parse_from(&["test", "-a42"]).unwrap()
);
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
assert!(Opt::try_parse_from(&["test", "-a42", "-a24"]).is_err());
}
#[test]
fn required_with_option_type() {
#[derive(Debug, PartialEq, Eq, Parser)]
#[clap(setting(clap::AppSettings::SubcommandsNegateReqs))]
struct Opt {
#[clap(required = true)]
req_str: Option<String>,
#[clap(subcommand)]
cmd: Option<SubCommands>,
}
#[derive(Debug, PartialEq, Eq, Subcommand)]
enum SubCommands {
ExSub {
#[clap(short, long, parse(from_occurrences))]
verbose: u8,
},
}
assert_eq!(
Opt {
req_str: Some(("arg").into()),
cmd: None,
},
Opt::try_parse_from(&["test", "arg"]).unwrap()
);
assert_eq!(
Opt {
req_str: None,
cmd: Some(SubCommands::ExSub { verbose: 1 }),
},
Opt::try_parse_from(&["test", "ex-sub", "-v"]).unwrap()
);
assert!(Opt::try_parse_from(&["test"]).is_err());
}
#[test]
fn ignore_qualified_option_type() {
fn parser(s: &str) -> Option<String> {
Some(s.to_string())
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(parse(from_str = parser))]
arg: ::std::option::Option<String>,
}
assert_eq!(
Opt {
arg: Some("success".into())
},
Opt::try_parse_from(&["test", "success"]).unwrap()
);
}
#[test]
fn option_option_type_is_optional_value() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
struct Opt { struct Opt {
#[clap(short, multiple_occurrences(true))] #[clap(short, multiple_occurrences(true))]
@ -169,7 +193,7 @@ fn optional_argument_for_optional_option() {
} }
#[test] #[test]
fn option_option_help() { fn option_option_type_help() {
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
struct Opt { struct Opt {
#[clap(long, value_name = "val")] #[clap(long, value_name = "val")]
@ -181,7 +205,7 @@ fn option_option_help() {
} }
#[test] #[test]
fn two_option_options() { fn two_option_option_types() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
struct Opt { struct Opt {
#[clap(short)] #[clap(short)]
@ -235,10 +259,48 @@ fn two_option_options() {
} }
#[test] #[test]
fn optional_vec() { fn vec_type_is_multiple_values() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
struct Opt { struct Opt {
#[clap(short, multiple_occurrences(true))] #[clap(short, long)]
arg: Vec<i32>,
}
assert_eq!(
Opt { arg: vec![24] },
Opt::try_parse_from(&["test", "-a24"]).unwrap()
);
assert_eq!(Opt { arg: vec![] }, Opt::try_parse_from(&["test"]).unwrap());
assert_eq!(
Opt { arg: vec![24, 42] },
Opt::try_parse_from(&["test", "-a", "24", "42"]).unwrap()
);
}
#[test]
fn ignore_qualified_vec_type() {
fn parser(s: &str) -> Vec<String> {
vec![s.to_string()]
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(parse(from_str = parser))]
arg: ::std::vec::Vec<String>,
}
assert_eq!(
Opt {
arg: vec!["success".into()]
},
Opt::try_parse_from(&["test", "success"]).unwrap()
);
}
#[test]
fn option_vec_type() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short)]
arg: Option<Vec<i32>>, arg: Option<Vec<i32>>,
} }
assert_eq!( assert_eq!(
@ -246,27 +308,6 @@ fn optional_vec() {
Opt::try_parse_from(&["test", "-a", "1"]).unwrap() Opt::try_parse_from(&["test", "-a", "1"]).unwrap()
); );
assert_eq!(
Opt {
arg: Some(vec![1, 2])
},
Opt::try_parse_from(&["test", "-a1", "-a2"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(vec![1, 2])
},
Opt::try_parse_from(&["test", "-a1", "-a2", "-a"]).unwrap()
);
assert_eq!(
Opt {
arg: Some(vec![1, 2])
},
Opt::try_parse_from(&["test", "-a1", "-a", "-a2"]).unwrap()
);
assert_eq!( assert_eq!(
Opt { Opt {
arg: Some(vec![1, 2]) arg: Some(vec![1, 2])
@ -274,34 +315,22 @@ fn optional_vec() {
Opt::try_parse_from(&["test", "-a", "1", "2"]).unwrap() Opt::try_parse_from(&["test", "-a", "1", "2"]).unwrap()
); );
assert_eq!(
Opt {
arg: Some(vec![1, 2, 3])
},
Opt::try_parse_from(&["test", "-a", "1", "2", "-a", "3"]).unwrap()
);
assert_eq!( assert_eq!(
Opt { arg: Some(vec![]) }, Opt { arg: Some(vec![]) },
Opt::try_parse_from(&["test", "-a"]).unwrap() Opt::try_parse_from(&["test", "-a"]).unwrap()
); );
assert_eq!(
Opt { arg: Some(vec![]) },
Opt::try_parse_from(&["test", "-a", "-a"]).unwrap()
);
assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap()); assert_eq!(Opt { arg: None }, Opt::try_parse_from(&["test"]).unwrap());
} }
#[test] #[test]
fn two_optional_vecs() { fn two_option_vec_types() {
#[derive(Parser, PartialEq, Debug)] #[derive(Parser, PartialEq, Debug)]
struct Opt { struct Opt {
#[clap(short, multiple_occurrences(true))] #[clap(short)]
arg: Option<Vec<i32>>, arg: Option<Vec<i32>>,
#[clap(short, multiple_occurrences(true))] #[clap(short)]
b: Option<Vec<i32>>, b: Option<Vec<i32>>,
} }
@ -316,9 +345,9 @@ fn two_optional_vecs() {
assert_eq!( assert_eq!(
Opt { Opt {
arg: Some(vec![1]), arg: Some(vec![1]),
b: Some(vec![]) b: Some(vec![1])
}, },
Opt::try_parse_from(&["test", "-a", "-b", "-a1"]).unwrap() Opt::try_parse_from(&["test", "-a", "1", "-b", "1"]).unwrap()
); );
assert_eq!( assert_eq!(
@ -326,7 +355,7 @@ fn two_optional_vecs() {
arg: Some(vec![1, 2]), arg: Some(vec![1, 2]),
b: Some(vec![1, 2]) b: Some(vec![1, 2])
}, },
Opt::try_parse_from(&["test", "-a1", "-a2", "-b1", "-b2"]).unwrap() Opt::try_parse_from(&["test", "-a", "1", "2", "-b", "1", "2"]).unwrap()
); );
assert_eq!( assert_eq!(
@ -334,42 +363,3 @@ fn two_optional_vecs() {
Opt::try_parse_from(&["test"]).unwrap() Opt::try_parse_from(&["test"]).unwrap()
); );
} }
#[test]
fn required_option_type() {
#[derive(Debug, PartialEq, Eq, Parser)]
#[clap(setting(clap::AppSettings::SubcommandsNegateReqs))]
struct Opt {
#[clap(required = true)]
req_str: Option<String>,
#[clap(subcommand)]
cmd: Option<SubCommands>,
}
#[derive(Debug, PartialEq, Eq, Subcommand)]
enum SubCommands {
ExSub {
#[clap(short, long, parse(from_occurrences))]
verbose: u8,
},
}
assert_eq!(
Opt {
req_str: Some(("arg").into()),
cmd: None,
},
Opt::try_parse_from(&["test", "arg"]).unwrap()
);
assert_eq!(
Opt {
req_str: None,
cmd: Some(SubCommands::ExSub { verbose: 1 }),
},
Opt::try_parse_from(&["test", "ex-sub", "-v"]).unwrap()
);
assert!(Opt::try_parse_from(&["test"]).is_err());
}

View file

@ -30,7 +30,7 @@ mod subcommands {
/// foo /// foo
Foo { Foo {
/// foo /// foo
bars: Vec<String>, bars: String,
}, },
} }
} }

View file

@ -4,14 +4,21 @@ use clap::Parser;
fn raw_idents() { fn raw_idents() {
#[derive(Parser, Debug, PartialEq)] #[derive(Parser, Debug, PartialEq)]
struct Opt { struct Opt {
#[clap(short, long, multiple_occurrences(true))] #[clap(short, long)]
r#type: Vec<String>, r#type: String,
} }
assert_eq!( assert_eq!(
Opt { Opt {
r#type: vec!["long".into(), "short".into()] r#type: "long".into()
}, },
Opt::try_parse_from(&["test", "--type", "long", "-t", "short"]).unwrap() Opt::try_parse_from(&["test", "--type", "long"]).unwrap()
);
assert_eq!(
Opt {
r#type: "short".into()
},
Opt::try_parse_from(&["test", "-t", "short"]).unwrap()
); );
} }

View file

@ -1,73 +0,0 @@
//! Checks that types like `::std::option::Option` are not special
use clap::Parser;
#[rustversion::all(since(1.37), stable)]
#[test]
fn special_types_bool() {
mod inner {
#[allow(non_camel_case_types)]
#[derive(PartialEq, Debug)]
pub struct bool(pub String);
impl std::str::FromStr for self::bool {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(self::bool(s.into()))
}
}
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
arg: inner::bool,
}
assert_eq!(
Opt {
arg: inner::bool("success".into())
},
Opt::try_parse_from(&["test", "success"]).unwrap()
);
}
#[test]
fn special_types_option() {
fn parser(s: &str) -> Option<String> {
Some(s.to_string())
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(parse(from_str = parser))]
arg: ::std::option::Option<String>,
}
assert_eq!(
Opt {
arg: Some("success".into())
},
Opt::try_parse_from(&["test", "success"]).unwrap()
);
}
#[test]
fn special_types_vec() {
fn parser(s: &str) -> Vec<String> {
vec![s.to_string()]
}
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(parse(from_str = parser))]
arg: ::std::vec::Vec<String>,
}
assert_eq!(
Opt {
arg: vec!["success".into()]
},
Opt::try_parse_from(&["test", "success"]).unwrap()
);
}