Skip to content

Commit

Permalink
Add await_future attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
blastrock committed Dec 21, 2022
1 parent 6a05c41 commit 8530852
Show file tree
Hide file tree
Showing 15 changed files with 465 additions and 49 deletions.
8 changes: 5 additions & 3 deletions rstest/tests/fixture/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,11 @@ mod should {
}
}

#[test]
fn resolve_async_fixture() {
let prj = prj("async_fixture.rs");
#[rstest]
#[case::future("async_fixture.rs")]
#[case::await_future("await_fixture.rs")]
fn resolve_async_fixture(#[case] file: &str) {
let prj = prj(file);
prj.add_dependency("async-std", r#"{version="*", features=["attributes"]}"#);

let output = prj.run_tests().unwrap();
Expand Down
73 changes: 73 additions & 0 deletions rstest/tests/resources/fixture/await_fixture.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use std::io::prelude::*;

use rstest::*;

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

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

#[fixture(fortytwo = async { 42 })]
async fn nest_fixture_with_default(#[await_future] 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(#[await_future] nest_fixture: u32) {
assert_eq!(42, nest_fixture);
}

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

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

#[rstest]
async fn use_async_fixture(#[await_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]
async fn use_async_impl_output<T: Read>(#[await_future] async_impl_output: T) {
let reader = async_impl_output;
}

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

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

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

#[rstest(two_args_mix_fixture(async { 3 }, 1))]
async fn use_two_args_mix_fixture_inject_both(#[await_future] two_args_mix_fixture: u32) {
assert_eq!(31, two_args_mix_fixture);
}
28 changes: 28 additions & 0 deletions rstest/tests/resources/rstest/cases/await.rs
Original file line number Diff line number Diff line change
@@ -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]
#[await_future]
value: u32,
) {
assert_eq!(expected, value);
}

#[rstest]
#[case::pass(42, async { 42 })]
async fn my_async_test_revert(
#[case] expected: u32,
#[await_future]
#[case]
value: u32,
) {
assert_eq!(expected, value);
}
11 changes: 11 additions & 0 deletions rstest/tests/resources/rstest/matrix/await.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use rstest::*;

#[rstest]
async fn my_async_test(
#[await_future]
#[values(async { 1 }, async { 2 })]
first: u32,
#[values(42, 21)] second: u32,
) {
assert_eq!(42, first * second);
}
28 changes: 28 additions & 0 deletions rstest/tests/resources/rstest/single/await.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use rstest::*;

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

#[rstest]
async fn should_pass(#[await_future] fixture: u32) {
assert_eq!(fixture, 42);
}

#[rstest]
async fn should_fail(#[await_future] fixture: u32) {
assert_ne!(fixture, 42);
}

#[rstest]
#[should_panic]
async fn should_panic_pass(#[await_future] fixture: u32) {
panic!(format!("My panic -> fixture = {}", fixture));
}

#[rstest]
#[should_panic]
async fn should_panic_fail(#[await_future] fixture: u32) {
assert_eq!(fixture, 42);
}
24 changes: 15 additions & 9 deletions rstest/tests/rstest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,11 @@ mod single {
.assert(output);
}

#[test]
fn should_run_async_function() {
let prj = prj(res("async.rs"));
#[rstest]
#[case::future("async.rs")]
#[case::await_future("await.rs")]
fn should_run_async_function(#[case] file: &str) {
let prj = prj(res(file));
prj.add_dependency("async-std", r#"{version="*", features=["attributes"]}"#);

let output = prj.run_tests().unwrap();
Expand Down Expand Up @@ -456,9 +458,11 @@ mod cases {
.assert(output);
}

#[test]
fn should_run_async_function() {
let prj = prj(res("async.rs"));
#[rstest]
#[case::future("async.rs")]
#[case::await_future("await.rs")]
fn should_run_async_function(#[case] file: &str) {
let prj = prj(res(file));
prj.add_dependency("async-std", r#"{version="*", features=["attributes"]}"#);

let output = prj.run_tests().unwrap();
Expand Down Expand Up @@ -775,9 +779,11 @@ mod matrix {
.assert(output);
}

#[test]
fn should_run_async_function() {
let prj = prj(res("async.rs"));
#[rstest]
#[case::future("async.rs")]
#[case::await_future("await.rs")]
fn should_run_async_function(#[case] file: &str) {
let prj = prj(res(file));
prj.add_dependency("async-std", r#"{version="*", features=["attributes"]}"#);

let output = prj.run_tests().unwrap();
Expand Down
26 changes: 21 additions & 5 deletions rstest_macros/src/parse/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use syn::{
};

use super::{
extract_argument_attrs, extract_default_return_type, extract_defaults, extract_fixtures,
extract_partials_return_type, parse_vector_trailing_till_double_comma, Attributes,
ExtendWithFunctionAttrs, Fixture,
extract_argument_attrs, extract_awaits, extract_default_return_type, extract_defaults,
extract_fixtures, extract_partials_return_type, parse_vector_trailing_till_double_comma,
Attributes, ExtendWithFunctionAttrs, Fixture,
};
use crate::{
error::ErrorsVec,
Expand All @@ -21,10 +21,21 @@ use crate::{parse::Attribute, utils::attr_in};
use proc_macro2::TokenStream;
use quote::{format_ident, ToTokens};

#[derive(PartialEq, Debug, Default)]
#[derive(PartialEq, Debug)]
pub(crate) struct FixtureInfo {
pub(crate) data: FixtureData,
pub(crate) attributes: FixtureModifiers,
pub(crate) await_args: Vec<Ident>,
}

impl Default for FixtureInfo {
fn default() -> Self {
Self {
data: Default::default(),
attributes: Default::default(),
await_args: Default::default(),
}
}
}

impl Parse for FixtureModifiers {
Expand All @@ -44,6 +55,7 @@ impl Parse for FixtureInfo {
.parse::<Token![::]>()
.or_else(|_| Ok(Default::default()))
.and_then(|_| input.parse())?,
..Default::default()
}
})
}
Expand All @@ -56,6 +68,7 @@ impl ExtendWithFunctionAttrs for FixtureInfo {
) -> std::result::Result<(), ErrorsVec> {
let composed_tuple!(
fixtures,
await_args,
defaults,
default_return_type,
partials_return_type,
Expand All @@ -71,6 +84,7 @@ impl ExtendWithFunctionAttrs for FixtureInfo {
.map(|i| Some(i.ident()))
.collect::<Vec<_>>()
),
extract_awaits(item_fn),
extract_defaults(item_fn),
extract_default_return_type(item_fn),
extract_partials_return_type(item_fn),
Expand All @@ -82,6 +96,7 @@ impl ExtendWithFunctionAttrs for FixtureInfo {
.map(|f| f.into())
.chain(defaults.into_iter().map(|d| d.into())),
);
self.await_args = await_args;
if let Some(return_type) = default_return_type {
self.attributes.set_default_return_type(return_type);
}
Expand Down Expand Up @@ -389,6 +404,7 @@ mod should {
],
}
.into(),
await_args: vec![],
};

assert_eq!(expected, data);
Expand Down Expand Up @@ -484,7 +500,7 @@ mod extend {

#[test]
fn rename_with_attributes() {
let mut item_fn = r#"
let mut item_fn: ItemFn = r#"
fn test_fn(
#[from(long_fixture_name)]
#[with(42, "other")] short: u32,
Expand Down
19 changes: 15 additions & 4 deletions rstest_macros/src/parse/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub(crate) struct ReplaceFutureAttribute {
errors: Vec<syn::Error>,
}

fn extend_generics_with_lifetimes<'a, 'b>(
pub fn extend_generics_with_lifetimes<'a, 'b>(
generics: impl Iterator<Item = &'a syn::GenericParam>,
lifetimes: impl Iterator<Item = &'b syn::Lifetime>,
) -> syn::Generics {
Expand Down Expand Up @@ -66,6 +66,17 @@ impl VisitMut for ReplaceFutureAttribute {
}));
return;
}
let awaits = extract_arg_attributes(t, |a| attr_is(a, "await_future"));
if !awaits.is_empty() {
self.errors.extend(futures.iter().skip(1).map(|attr| {
syn::Error::new_spanned(
attr.into_token_stream(),
"#[await] and #[future] are mutually exclusive.".to_owned(),
)
}));
return;
}

let ty = &mut t.ty;
use syn::Type::*;
match ty.as_ref() {
Expand Down Expand Up @@ -127,13 +138,13 @@ mod should {
)]
#[case::more_than_one(
"fn f(#[future] a: u32, #[future] b: String, #[future] c: std::collection::HashMap<usize, String>) {}",
r#"fn f(a: impl std::future::Future<Output = u32>,
b: impl std::future::Future<Output = String>,
r#"fn f(a: impl std::future::Future<Output = u32>,
b: impl std::future::Future<Output = String>,
c: impl std::future::Future<Output = std::collection::HashMap<usize, String>>) {}"#,
)]
#[case::just_one(
"fn f(a: u32, #[future] b: String) {}",
r#"fn f(a: u32,
r#"fn f(a: u32,
b: impl std::future::Future<Output = String>) {}"#
)]
#[case::generics(
Expand Down

0 comments on commit 8530852

Please sign in to comment.