Skip to content

Commit

Permalink
#148: implemented feature gated async timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
la10736 committed Jun 19, 2022
1 parent 9976258 commit 7e1c9bb
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 8 deletions.
10 changes: 7 additions & 3 deletions rstest/Cargo.toml
Expand Up @@ -14,12 +14,16 @@ readme = "README.md"
repository = "https://github.com/la10736/rstest"
version = "0.14.0"

[features]
async-timeout = ["dep:futures", "dep:futures-timer", "rstest_macros/async-timeout"]
default = ["async-timeout"]

[lib]

[dependencies]
futures = "0.3.15"
futures-timer = "3.0.2"
rstest_macros = {version = "0.14.0", path = "../rstest_macros"}
futures = {version = "0.3.15", optional = true}
futures-timer = {version = "3.0.2", optional = true}
rstest_macros = {version = "0.14.0", path = "../rstest_macros", default-features = false}

[dev-dependencies]
actix-rt = "2.2.0"
Expand Down
4 changes: 4 additions & 0 deletions rstest/src/timeout.rs
@@ -1,6 +1,8 @@
use std::{sync::mpsc, time::Duration};

#[cfg(feature = "async-timeout")]
use futures::{select, Future, FutureExt};
#[cfg(feature = "async-timeout")]
use futures_timer::Delay;

pub fn execute_with_timeout_sync<T: 'static + Send, F: Fn() -> T + Send + 'static>(
Expand All @@ -14,6 +16,7 @@ pub fn execute_with_timeout_sync<T: 'static + Send, F: Fn() -> T + Send + 'stati
.unwrap_or_else(|_| panic!("Timeout {:?} expired", timeout))
}

#[cfg(feature = "async-timeout")]
pub async fn execute_with_timeout_async<T, Fut: Future<Output = T>, F: Fn() -> Fut>(
code: F,
timeout: Duration,
Expand All @@ -29,6 +32,7 @@ pub async fn execute_with_timeout_async<T, Fut: Future<Output = T>, F: Fn() -> F
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "async-timeout")]
mod async_version {

use super::*;
Expand Down
6 changes: 4 additions & 2 deletions rstest_macros/Cargo.toml
Expand Up @@ -16,7 +16,9 @@ version = "0.14.0"
[lib]
proc-macro = true

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
async-timeout = ["rstest/async-timeout"]
default = ["async-timeout"]

[dependencies]
cfg-if = "1.0.0"
Expand All @@ -28,7 +30,7 @@ syn = {version = "1.0.72", features = ["full", "parsing", "extra-traits", "visit
actix-rt = "2.2.0"
async-std = {version = "1.9.0", features = ["attributes"]}
pretty_assertions = "1.0.0"
rstest = {version = "0.14.0", path = "../rstest"}
rstest = {version = "0.14.0", path = "../rstest", default-features = false}
rstest_reuse = {version = "0.3.0", path = "../rstest_reuse"}
rstest_test = {version = "0.7.0", path = "../rstest_test"}

Expand Down
32 changes: 29 additions & 3 deletions rstest_macros/src/parse/mod.rs
Expand Up @@ -3,7 +3,7 @@ use syn::{
parse::{Parse, ParseStream},
parse_quote,
punctuated::Punctuated,
token::{self, Paren},
token::{self, Async, Paren},
visit_mut::VisitMut,
FnArg, Ident, ItemFn, Token,
};
Expand Down Expand Up @@ -573,6 +573,23 @@ impl CheckTimeoutAttributesFunction {
pub(crate) fn take(self) -> Result<(), ErrorsVec> {
self.0
}

fn check_if_can_implement_timeous<'a, 'b, 'c>(
&self,
timeouts: &'c [&'a syn::Attribute],
asyncness: Option<&'b Async>,
) -> Option<syn::Error> {
if cfg!(feature = "async-timeout") || timeouts.is_empty() {
None
} else {
asyncness.map(|a| {
syn::Error::new(
a.span,
"Enable async-timeout feature to use timeout in async tests",
)
})
}
}
}

impl Default for CheckTimeoutAttributesFunction {
Expand All @@ -583,13 +600,22 @@ impl Default for CheckTimeoutAttributesFunction {

impl VisitMut for CheckTimeoutAttributesFunction {
fn visit_item_fn_mut(&mut self, node: &mut ItemFn) {
let errors = node
let timeouts = node
.attrs
.iter()
.filter(|&a| attr_is(a, "timeout"))
.map(|attr| attr.parse_args::<syn::Expr>())
.collect::<Vec<_>>();
let mut errors = timeouts
.iter()
.map(|&attr| attr.parse_args::<syn::Expr>())
.filter_map(Result::err)
.collect::<Vec<_>>();

if let Some(e) =
self.check_if_can_implement_timeous(timeouts.as_slice(), node.sig.asyncness.as_ref())
{
errors.push(e);
}
if !errors.is_empty() {
*self = Self(Err(errors.into()));
}
Expand Down
33 changes: 33 additions & 0 deletions rstest_macros/src/parse/rstest.rs
Expand Up @@ -301,6 +301,39 @@ mod test {
assert_eq!(2, errors.len());
}

#[cfg(feature = "async-timeout")]
#[test]
fn should_parse_async_timeout() {
let mut item_fn = r#"
#[timeout(Duration::from_millis(20))]
async fn test_fn(#[case] arg: u32) {
}
"#
.ast();

let mut info = RsTestInfo::default();

info.extend_with_function_attrs(&mut item_fn).unwrap();
}

#[cfg(not(feature = "async-timeout"))]
#[test]
fn should_return_error_for_async_timeout() {
let mut item_fn = r#"
#[timeout(Duration::from_millis(20))]
async fn test_fn(#[case] arg: u32) {
}
"#
.ast();

let mut info = RsTestInfo::default();

let errors = info.extend_with_function_attrs(&mut item_fn).unwrap_err();

assert_eq!(1, errors.len());
assert!(format!("{:?}", errors).contains("async-timeout feature"))
}

fn parse_rstest<S: AsRef<str>>(rstest_data: S) -> RsTestInfo {
parse_meta(rstest_data)
}
Expand Down

0 comments on commit 7e1c9bb

Please sign in to comment.