Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto await feature #186

Merged
merged 14 commits into from Mar 19, 2023
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,8 @@
## [0.17.0] Unreleased
### Add

- Add `#[awt]` and `#[timeout(awt)]` to `.await` future input

### Changed

### Fixed
Expand Down
23 changes: 23 additions & 0 deletions README.md
Expand Up @@ -180,6 +180,29 @@ async fn my_async_test(#[future] base: u32, #[case] expected: u32, #[future] #[c
}
```

As you noted you should `.await` all _future_ values and this some times can be really boring.
In this case you can use `#[timeout(awt)]` to _awaiting_ an input or annotating your function
with `#[awt]` attributes to globally `.await` all your _future_ inputs. Previous code can be
simplified like follow:
```rust
use rstest::*;
# #[fixture]
# async fn base() -> u32 { 42 }
#[rstest]
#[case(21, async { 2 })]
#[case(6, async { 7 })]
#[awt]
async fn global(#[future] base: u32, #[case] expected: u32, #[future] #[case] div: u32) {
assert_eq!(expected, base / div);
}
#[rstest]
#[case(21, async { 2 })]
#[case(6, async { 7 })]
async fn single(#[future] base: u32, #[case] expected: u32, #[future(awt)] #[case] div: u32) {
assert_eq!(expected, base.await / div);
}
```

### Test `#[timeout()]`

You can define an execution timeout for your tests with `#[timeout(<duration>)]` attribute. Timeouts
Expand Down
38 changes: 32 additions & 6 deletions playground/src/main.rs
@@ -1,12 +1,38 @@
use rstest::*;
fn valid_user(name: &str, age: u8) -> bool {
true

#[fixture]
#[awt]
async fn two_args_mix_fixture(
#[future]
#[default(async { 4 })]
four: u32,
#[default(2)] two: u32,
) -> u32 {
four * 10 + two
}

// #[rstest]
// #[awt]
// async fn use_two_args_mix_fixture(#[future] two_args_mix_fixture: u32) {
// assert_eq!(42, two_args_mix_fixture);
// }

// #[rstest]
// #[awt]
// async fn use_two_args_mix_fixture_inject_first(
// #[future]
// #[with(async { 5 })]
// two_args_mix_fixture: u32,
// ) {
// assert_eq!(52, two_args_mix_fixture);
// }

#[rstest]
fn should_accept_all_corner_cases(
#[values("J", "A", "A________________________________________21")] name: &str,
#[values(14, 100)] age: u8,
#[awt]
async fn use_two_args_mix_fixture_inject_both(
#[future]
#[with(async { 3 }, 1)]
two_args_mix_fixture: u32,
) {
assert!(valid_user(name, age))
assert_eq!(31, two_args_mix_fixture);
}
2 changes: 1 addition & 1 deletion rstest/Cargo.toml
Expand Up @@ -23,7 +23,7 @@ default = ["async-timeout"]
[dependencies]
futures = {version = "0.3.21", optional = true}
futures-timer = {version = "3.0.2", optional = true}
rstest_macros = {version = "0.16.0", path = "../rstest_macros", default-features = false}
rstest_macros = {version = "0.17.0", path = "../rstest_macros", default-features = false}

[dev-dependencies]
actix-rt = "2.7.0"
Expand Down
9 changes: 6 additions & 3 deletions rstest/tests/fixture/mod.rs
Expand Up @@ -93,9 +93,12 @@ mod should {
}
}

#[test]
fn resolve_async_fixture() {
let prj = prj("async_fixture.rs");
#[rstest]
#[case::base("async_fixture.rs")]
#[case::use_global("await_complete_fixture.rs")]
#[case::use_selective("await_partial_fixture.rs")]
fn resolve_async_fixture(#[case] code: &str) {
let prj = prj(code);
prj.add_dependency("async-std", r#"{version="*", features=["attributes"]}"#);

let output = prj.run_tests().unwrap();
Expand Down
105 changes: 105 additions & 0 deletions rstest/tests/resources/fixture/await_complete_fixture.rs
@@ -0,0 +1,105 @@
use std::io::prelude::*;

use rstest::*;

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

#[fixture]
#[awt]
async fn nest_fixture(#[future] async_u32: u32) -> u32 {
async_u32
}

#[fixture]
#[awt]
async fn nest_fixture_with_default(
#[future]
#[default(async { 42 })]
fortytwo: u32,
) -> u32 {
fortytwo
}

#[rstest]
async fn default_is_async() {
assert_eq!(42, async_u32::default().await);
}

#[rstest]
#[awt]
async fn use_async_nest_fixture_default(#[future] nest_fixture: u32) {
assert_eq!(42, nest_fixture);
}

#[rstest]
#[awt]
async fn use_async_nest_fixture_injected(
#[future]
#[with(async { 24 })]
nest_fixture: u32,
) {
assert_eq!(24, nest_fixture);
}

#[rstest]
#[awt]
async fn use_async_nest_fixture_with_default(#[future] nest_fixture_with_default: u32) {
assert_eq!(42, nest_fixture_with_default);
}

#[rstest]
#[awt]
async fn use_async_fixture(#[future] async_u32: u32) {
assert_eq!(42, async_u32);
}

#[fixture]
async fn async_impl_output() -> impl Read {
std::io::Cursor::new(vec![1, 2, 3, 4, 5])
}

#[rstest]
#[awt]
async fn use_async_impl_output<T: Read>(#[future] async_impl_output: T) {
let reader = async_impl_output;
}

#[fixture]
#[awt]
async fn two_args_mix_fixture(
#[future]
#[default(async { 4 })]
four: u32,
#[default(2)] two: u32,
) -> u32 {
four * 10 + two
}

#[rstest]
#[awt]
async fn use_two_args_mix_fixture(#[future] two_args_mix_fixture: u32) {
assert_eq!(42, two_args_mix_fixture);
}

#[rstest]
#[awt]
async fn use_two_args_mix_fixture_inject_first(
#[future]
#[with(async { 5 })]
two_args_mix_fixture: u32,
) {
assert_eq!(52, two_args_mix_fixture);
}

#[rstest]
#[awt]
async fn use_two_args_mix_fixture_inject_both(
#[future]
#[with(async { 3 }, 1)]
two_args_mix_fixture: u32,
) {
assert_eq!(31, two_args_mix_fixture);
}
94 changes: 94 additions & 0 deletions rstest/tests/resources/fixture/await_partial_fixture.rs
@@ -0,0 +1,94 @@
use std::io::prelude::*;

use rstest::*;

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

#[fixture]
async fn nest_fixture(#[future(awt)] async_u32: u32) -> u32 {
async_u32
}

#[fixture]
async fn nest_fixture_with_default(
#[future(awt)]
#[default(async { 42 })]
fortytwo: u32,
) -> u32 {
fortytwo
}

#[rstest]
async fn default_is_async() {
assert_eq!(42, async_u32::default().await);
}

#[rstest]
async fn use_async_nest_fixture_default(#[future(awt)] nest_fixture: u32) {
assert_eq!(42, nest_fixture);
}

#[rstest]
async fn use_async_nest_fixture_injected(
#[future(awt)]
#[with(async { 24 })]
nest_fixture: u32,
) {
assert_eq!(24, nest_fixture);
}

#[rstest]
async fn use_async_nest_fixture_with_default(#[future(awt)] nest_fixture_with_default: u32) {
assert_eq!(42, nest_fixture_with_default);
}

#[rstest]
async fn use_async_fixture(#[future(awt)] async_u32: u32) {
assert_eq!(42, async_u32);
}

#[fixture]
async fn async_impl_output() -> impl Read {
std::io::Cursor::new(vec![1, 2, 3, 4, 5])
}

#[rstest]
async fn use_async_impl_output<T: Read>(#[future(awt)] async_impl_output: T) {
let reader = async_impl_output;
}

#[fixture]
async fn two_args_mix_fixture(
#[future(awt)]
#[default(async { 4 })]
four: u32,
#[default(2)] two: u32,
) -> u32 {
four * 10 + two
}

#[rstest]
async fn use_two_args_mix_fixture(#[future(awt)] two_args_mix_fixture: u32) {
assert_eq!(42, two_args_mix_fixture);
}

#[rstest]
async fn use_two_args_mix_fixture_inject_first(
#[future(awt)]
#[with(async { 5 })]
two_args_mix_fixture: u32,
) {
assert_eq!(52, two_args_mix_fixture);
}

#[rstest]
async fn use_two_args_mix_fixture_inject_both(
#[future(awt)]
#[with(async { 3 }, 1)]
two_args_mix_fixture: u32,
) {
assert_eq!(31, two_args_mix_fixture);
}
13 changes: 10 additions & 3 deletions rstest/tests/resources/fixture/no_warning.rs
@@ -1,10 +1,17 @@
use rstest::*;

#[fixture]
fn val() -> i32 { 21 }
fn val() -> i32 {
21
}

#[fixture]
fn fortytwo(mut val: i32) -> i32 { val *= 2; val }
fn fortytwo(mut val: i32) -> i32 {
val *= 2;
val
}

#[rstest]
fn the_test(fortytwo: i32) { assert_eq!(fortytwo, 42); }
fn the_test(fortytwo: i32) {
assert_eq!(fortytwo, 42);
}
28 changes: 28 additions & 0 deletions rstest/tests/resources/rstest/cases/async_awt.rs
@@ -0,0 +1,28 @@
use rstest::*;

#[rstest]
#[case::pass(42, async { 42 })]
#[case::fail(42, async { 41 })]
#[should_panic]
#[case::pass_panic(42, async { 41 })]
#[should_panic]
#[case::fail_panic(42, async { 42 })]
async fn my_async_test(
#[case] expected: u32,
#[case]
#[future(awt)]
value: u32,
) {
assert_eq!(expected, value);
}

#[rstest]
#[case::pass(42, async { 42 })]
async fn my_async_test_revert(
#[case] expected: u32,
#[future(awt)]
#[case]
value: u32,
) {
assert_eq!(expected, value);
}
30 changes: 30 additions & 0 deletions rstest/tests/resources/rstest/cases/async_awt_global.rs
@@ -0,0 +1,30 @@
use rstest::*;

#[rstest]
#[case::pass(42, async { 42 })]
#[case::fail(42, async { 41 })]
#[should_panic]
#[case::pass_panic(42, async { 41 })]
#[should_panic]
#[case::fail_panic(42, async { 42 })]
#[awt]
async fn my_async_test(
#[case] expected: u32,
#[case]
#[future]
value: u32,
) {
assert_eq!(expected, value);
}

#[rstest]
#[case::pass(42, async { 42 })]
#[awt]
async fn my_async_test_revert(
#[case] expected: u32,
#[future]
#[case]
value: u32,
) {
assert_eq!(expected, value);
}