Skip to content

Commit

Permalink
Auto await feature (#186)
Browse files Browse the repository at this point in the history
* WIP: (Refactoring) Implemented the future
boilerplate in the rendering stage. Just render
and test (missing the parse stage).

* WIP: removed `ReplaceFutureAttribute` hack in
`fixture` and moved the parsing code in
`extend_with_function_attrs` trait impl

* Move test in the right module and simplfied it

* Add check also for no future args

* Refactored and removed useless tests
that're already tested in future module

* Use Arguments also in tests and removed
the old impl. Also recoverd tests for
errors but implemented them in future
module

* WIP: Implementing await policy

* WIP: implemented the render part.

* WIP: implemented tests for is_future_await
logic

* WIP: Implemented future options parsing.
Missed the global one

* WIP implemented all global parser,
pass arguments info down to the
renderers and impleneted all tests
in render code to check that the
parsed info will used correctly.

* Changed impl: we should await in the original
method because we want the future in the
signature. Now E2E tests for fixture are
enabled and work.

* Integration tests

* Add documentation and changelog info
  • Loading branch information
la10736 committed Mar 19, 2023
1 parent 23b5311 commit 91c60bd
Show file tree
Hide file tree
Showing 27 changed files with 1,524 additions and 218 deletions.
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);
}

0 comments on commit 91c60bd

Please sign in to comment.