Skip to content

Commit

Permalink
Merge pull request #268 from la10736/arg_destruct
Browse files Browse the repository at this point in the history
Arguments destructuring
  • Loading branch information
la10736 authored Aug 4, 2024
2 parents fcf732d + 3949076 commit 696eaf6
Show file tree
Hide file tree
Showing 29 changed files with 1,319 additions and 464 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Changed

- Now it's possible destructuring input values both for cases, values and fixtures. See [#231](https://github.com/la10736/rstest/issues/231) for details

### Add

- Implemented `#[ignore]` attribute to ignore test parameters during fixtures resolution/injection. See [#228](https://github.com/la10736/rstest/issues/228) for details
Expand Down
54 changes: 54 additions & 0 deletions rstest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,22 @@ pub mod timeout;
/// shorter name for argument that represent it in your fixture or test. You can rename the fixture
/// using `#[from(short_name)]` attribute like following example:
///
/// ## Destructuring
///
/// It's possible to destructure the fixture type but, in this case, your're forced to use renaming syntax
/// because it's not possible to guess the fixture name from this syntax:
///
/// ```
/// use rstest::*;
/// #[fixture]
/// fn two_values() -> (u32, u32) { (42, 24) }
///
/// #[rstest]
/// fn the_test(#[from(two_values)] (first, _): (u32, u32)) {
/// assert_eq!(42, first)
/// }
/// ```
///
/// ```
/// use rstest::*;
///
Expand Down Expand Up @@ -568,6 +584,9 @@ pub use rstest_macros::fixture;
/// - return results
/// - marked by `#[should_panic]` attribute
///
/// In the function signature, where you define your tests inputs, you can also destructuring
/// the values like any other rust function.
///
/// If the test function is an [`async` function](#async) `rstest` will run all tests as `async`
/// tests. You can use it just with `async-std` and you should include `attributes` in
/// `async-std`'s features.
Expand Down Expand Up @@ -624,6 +643,20 @@ pub use rstest_macros::fixture;
/// }
/// ```
///
/// The use of `#[from(...)]` attribute is mandatory if you need to destructure the value:
///
/// ```
/// use rstest::*;
///
/// #[fixture]
/// fn tuple() -> (u32, f32) { (42, 42.0) }
///
/// #[rstest]
/// fn the_test(#[from(tuple)] (u, _): (u32, f32)) {
/// assert_eq!(42, u)
/// }
/// ```
///
/// Sometimes is useful to have some parameters in your fixtures but your test would
/// override the fixture's default values in some cases. Like in
/// [fixture partial injection](attr.fixture.html#partial-injection) you use `#[with]`
Expand Down Expand Up @@ -897,6 +930,27 @@ pub use rstest_macros::fixture;
/// }
/// ```
///
/// ## Destructuring inputs
///
/// Both paramtrized case and values can be destructured:
///
/// ```
/// # use rstest::*;
/// struct S {
/// first: u32,
/// second: u32,
/// }
///
/// struct T(i32);
///
/// #[rstest]
/// #[case(S{first: 21, second: 42})]
/// fn some_test(#[case] S{first, second} : S, #[values(T(-1), T(1))] T(t): T) {
/// assert_eq!(1, t * t);
/// assert_eq!(2 * first, second);
/// }
/// ```
///
/// ## Files path as input arguments
///
/// If you need to create a test for each file in a given location you can use
Expand Down
81 changes: 59 additions & 22 deletions rstest/tests/fixture/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,9 @@ mod should {
output.stderr.str(),
format!(
r#"
--> {}/src/lib.rs:12:33
--> {name}/src/lib.rs:14:33
|
12 | fn error_cannot_resolve_fixture(no_fixture: u32) {{"#,
name
14 | fn error_cannot_resolve_fixture(no_fixture: u32) {{"#
)
.unindent()
);
Expand All @@ -260,11 +259,10 @@ mod should {
format!(
r#"
error[E0308]: mismatched types
--> {}/src/lib.rs:8:18
|
8 | let a: u32 = "";
"#,
name
--> {name}/src/lib.rs:10:18
|
10 | let a: u32 = "";
"#
)
.unindent()
);
Expand All @@ -277,20 +275,19 @@ mod should {
assert_in!(
output.stderr.str(),
format!(
"
r#"
error[E0308]: mismatched types
--> {}/src/lib.rs:16:29
",
name
--> {name}/src/lib.rs:17:29
"#
)
.unindent()
);

assert_in!(
output.stderr.str(),
"
16 | fn error_fixture_wrong_type(fixture: String) {
| ^^^^^^"
r#"
17 | fn error_fixture_wrong_type(fixture: String) {}
| ^^^^^^"#
.unindent()
);
}
Expand All @@ -304,12 +301,11 @@ mod should {
format!(
"
error: Missed argument: 'not_a_fixture' should be a test function argument.
--> {}/src/lib.rs:19:11
--> {name}/src/lib.rs:19:11
|
19 | #[fixture(not_a_fixture(24))]
| ^^^^^^^^^^^^^
",
name
"
)
.unindent()
);
Expand All @@ -324,15 +320,56 @@ mod should {
format!(
r#"
error: Duplicate argument: 'f' is already defined.
--> {}/src/lib.rs:33:23
--> {name}/src/lib.rs:32:23
|
33 | #[fixture(f("first"), f("second"))]
32 | #[fixture(f("first"), f("second"))]
| ^
"#,
name
"#
)
.unindent()
);
}

#[rstest]
fn on_destruct_implicit_fixture(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();

assert_in!(
output.stderr.str(),
format!(
r#"
error: To destruct a fixture you should provide a path to resolve it by '#[from(...)]' attribute.
--> {name}/src/lib.rs:48:35
|
48 | fn error_destruct_without_resolve(T(a): T) {{}}
| ^^^^^^^
"#
)
.unindent()
);
}

#[rstest]
fn on_destruct_explicit_fixture_without_from(errors_rs: &(Output, String)) {
let (output, name) = errors_rs.clone();

assert_in!(
output.stderr.str(),
format!(
r#"
error: To destruct a fixture you should provide a path to resolve it by '#[from(...)]' attribute.
--> {name}/src/lib.rs:51:57
|
51 | fn error_destruct_without_resolve_also_with(#[with(21)] T(a): T) {{}}
| ^^^^^^^
"#
)
.unindent()
);
assert_eq!(
1,
output.stderr.str().count("51 | fn error_destruct_without")
)
}

#[fixture]
Expand Down
32 changes: 24 additions & 8 deletions rstest/tests/resources/fixture/errors.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
use rstest::*;

#[fixture]
pub fn fixture() -> u32 { 42 }
pub fn fixture() -> u32 {
42
}

#[fixture]
fn error_inner(fixture: u32) {
let a: u32 = "";
}

#[fixture]
fn error_cannot_resolve_fixture(no_fixture: u32) {
}
fn error_cannot_resolve_fixture(no_fixture: u32) {}

#[fixture]
fn error_fixture_wrong_type(fixture: String) {
}
fn error_fixture_wrong_type(fixture: String) {}

#[fixture(not_a_fixture(24))]
fn error_inject_an_invalid_fixture(fixture: String) {
}
fn error_inject_an_invalid_fixture(fixture: String) {}

#[fixture]
fn name() -> &'static str {
Expand All @@ -31,5 +30,22 @@ fn f(name: &str) -> String {
}

#[fixture(f("first"), f("second"))]
fn error_inject_a_fixture_more_than_once(f: String) {
fn error_inject_a_fixture_more_than_once(f: String) {}

struct T(u32);

#[fixture]
fn structed() -> T {
T(42)
}

#[fixture]
fn structed_injectd(fixture: u32) -> T {
T(fixture)
}

#[fixture]
fn error_destruct_without_resolve(T(a): T) {}

#[fixture]
fn error_destruct_without_resolve_also_with(#[with(21)] T(a): T) {}
79 changes: 79 additions & 0 deletions rstest/tests/resources/rstest/destruct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use rstest::*;

struct T {
a: u32,
b: u32,
}

impl T {
fn new(a: u32, b: u32) -> Self {
Self { a, b }
}
}

struct S(u32, u32);

#[fixture]
fn fix() -> T {
T::new(1, 42)
}

#[fixture]
fn named() -> S {
S(1, 42)
}

#[fixture]
fn tuple() -> (u32, u32) {
(1, 42)
}

#[fixture]
fn swap(#[from(fix)] T { a, b }: T) -> T {
T::new(b, a)
}

#[rstest]
fn swapped(#[from(swap)] T { a, b }: T) {
assert_eq!(a, 42);
assert_eq!(b, 1);
}

#[rstest]
#[case::two_times_twenty_one(T::new(2, 21))]
#[case::six_times_seven(T{ a: 6, b: 7 })]
fn cases_destruct(
#[from(fix)] T { a, b }: T,
#[case] T { a: c, b: d }: T,
#[values(T::new(42, 1), T{ a: 3, b: 14})] T { a: e, b: f }: T,
) {
assert_eq!(a * b, 42);
assert_eq!(c * d, 42);
assert_eq!(e * f, 42);
}

#[rstest]
#[case::two_times_twenty_one(S(2, 21))]
#[case::six_times_seven(S(6, 7))]
fn cases_destruct_named_tuple(
#[from(named)] S(a, b): S,
#[case] S(c, d): S,
#[values(S(42, 1), S(3, 14))] S(e, f): S,
) {
assert_eq!(a * b, 42);
assert_eq!(c * d, 42);
assert_eq!(e * f, 42);
}

#[rstest]
#[case::two_times_twenty_one((2, 21))]
#[case::six_times_seven((6, 7))]
fn cases_destruct_tuple(
#[from(tuple)] (a, b): (u32, u32),
#[case] (c, d): (u32, u32),
#[values((42, 1), (3, 14))] (e, f): (u32, u32),
) {
assert_eq!(a * b, 42);
assert_eq!(c * d, 42);
assert_eq!(e * f, 42);
}
6 changes: 6 additions & 0 deletions rstest/tests/resources/rstest/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,9 @@ fn error_timeout_without_duration() {}

#[rstest]
fn error_absolute_path_files(#[files("/tmp/tmp.Q81idVZYAV/*.txt")] path: std::path::PathBuf) {}

struct T(u32, u32);

#[rstest]
#[case(T(3, 4))]
fn wrong_destruct_fixture(T(a, b): T, #[with(42)] T(c, d): T) {}
Loading

0 comments on commit 696eaf6

Please sign in to comment.