Skip to content

Commit

Permalink
refactor(tests): Prepare for Special Type experiments
Browse files Browse the repository at this point in the history
In experimenting on clap-rs#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 clap-rs#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.
  • Loading branch information
epage committed Nov 4, 2021
1 parent 879dd23 commit 27df2bd
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 325 deletions.
76 changes: 38 additions & 38 deletions clap_derive/tests/arg_enum.rs
Expand Up @@ -297,7 +297,41 @@ fn multiple_alias() {
}

#[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)]
enum ArgChoice {
Foo,
Expand Down Expand Up @@ -327,7 +361,7 @@ fn option() {
}

#[test]
fn option_option() {
fn option_option_type() {
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Expand Down Expand Up @@ -361,7 +395,7 @@ fn option_option() {
}

#[test]
fn vector() {
fn vec_type() {
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Expand Down Expand Up @@ -391,7 +425,7 @@ fn vector() {
}

#[test]
fn option_vector() {
fn option_vec_type() {
#[derive(ArgEnum, PartialEq, Debug, Clone)]
enum ArgChoice {
Foo,
Expand Down Expand Up @@ -423,37 +457,3 @@ fn option_vector() {
);
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());
}
88 changes: 35 additions & 53 deletions clap_derive/tests/arguments.rs
Expand Up @@ -29,20 +29,6 @@ fn required_argument() {
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]
fn argument_with_default() {
#[derive(Parser, PartialEq, Debug)]
Expand All @@ -58,45 +44,6 @@ fn argument_with_default() {
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]
fn auto_value_name() {
#[derive(Parser, PartialEq, Debug)]
Expand Down Expand Up @@ -136,3 +83,38 @@ fn explicit_value_name() {
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
);
}
9 changes: 2 additions & 7 deletions clap_derive/tests/basic.rs
Expand Up @@ -19,17 +19,12 @@ fn basic() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short = 'a', long = "arg")]
arg: Vec<i32>,
arg: i32,
}
assert_eq!(
Opt { arg: vec![24] },
Opt { arg: 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", "--arg", "24", "42"]).unwrap()
);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion clap_derive/tests/doc-comments-help.rs
Expand Up @@ -108,7 +108,7 @@ fn top_long_doc_comment_both_help_long_help() {
/// Or something else
Foo {
#[clap(about = "foo")]
bars: Vec<String>,
bars: String,
},
}

Expand Down
17 changes: 11 additions & 6 deletions clap_derive/tests/explicit_name_no_renaming.rs
Expand Up @@ -7,15 +7,20 @@ use utils::*;
fn explicit_short_long_no_rename() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short = '.', long = ".foo", multiple_occurrences(true))]
foo: Vec<String>,
#[clap(short = '.', long = ".foo")]
foo: String,
}

assert_eq!(
Opt { foo: "long".into() },
Opt::try_parse_from(&["test", "--.foo", "long"]).unwrap()
);

assert_eq!(
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()
);
}

Expand All @@ -24,9 +29,9 @@ fn explicit_name_no_rename() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(name = ".options")]
foo: Vec<String>,
foo: String,
}

let help = get_long_help::<Opt>();
assert!(help.contains("[.options]..."))
assert!(help.contains("<.options>"))
}
45 changes: 37 additions & 8 deletions clap_derive/tests/flags.rs
Expand Up @@ -15,7 +15,7 @@
use clap::Parser;

#[test]
fn unique_flag() {
fn bool_type_is_flag() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long)]
Expand All @@ -41,7 +41,7 @@ fn unique_flag() {
}

#[test]
fn multiple_flag() {
fn from_occurrences() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long, parse(from_occurrences))]
Expand Down Expand Up @@ -74,12 +74,12 @@ fn multiple_flag() {
assert!(Opt::try_parse_from(&["test", "-a", "foo"]).is_err());
}

fn parse_from_flag(b: bool) -> std::sync::atomic::AtomicBool {
std::sync::atomic::AtomicBool::new(b)
}

#[test]
fn non_bool_flags() {
fn non_bool_type_flag() {
fn parse_from_flag(b: bool) -> std::sync::atomic::AtomicBool {
std::sync::atomic::AtomicBool::new(b)
}

#[derive(Parser, Debug)]
struct Opt {
#[clap(short, long, parse(from_flag = parse_from_flag))]
Expand All @@ -106,7 +106,7 @@ fn non_bool_flags() {
}

#[test]
fn combined_flags() {
fn mixed_type_flags() {
#[derive(Parser, PartialEq, Debug)]
struct Opt {
#[clap(short, long)]
Expand Down Expand Up @@ -158,3 +158,32 @@ fn combined_flags() {
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()
);
}

0 comments on commit 27df2bd

Please sign in to comment.