From 0114d979cddd50bf88b1c406402a049113ffcf06 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Thu, 6 May 2021 03:28:06 -0400 Subject: [PATCH 1/5] async test function attr --- futures-macro/src/executor.rs | 27 +++++++++++++++++++++++++++ futures-macro/src/lib.rs | 7 +++++++ futures-test/Cargo.toml | 1 + futures-test/src/lib.rs | 14 ++++++++++++++ futures-util/src/sink/mod.rs | 2 +- 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 futures-macro/src/executor.rs diff --git a/futures-macro/src/executor.rs b/futures-macro/src/executor.rs new file mode 100644 index 0000000000..ac008acc7f --- /dev/null +++ b/futures-macro/src/executor.rs @@ -0,0 +1,27 @@ +use proc_macro::TokenStream; +use quote::quote; + +pub(crate) fn test(_: TokenStream, item: TokenStream) -> TokenStream { + let mut input = syn::parse_macro_input!(item as syn::ItemFn); + let attrs = &input.attrs; + let vis = &input.vis; + let sig = &mut input.sig; + let body = &input.block; + + if sig.asyncness.is_none() { + return syn::Error::new_spanned(sig.fn_token, "Only async functions are supported") + .to_compile_error() + .into(); + } + + sig.asyncness = None; + + let gen = quote! { + #(#attrs)* + #vis #sig { + ::futures_test::__private::block_on(async move { #body }) + } + }; + + gen.into() +} diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs index 98408ebfe6..f3cc774142 100644 --- a/futures-macro/src/lib.rs +++ b/futures-macro/src/lib.rs @@ -14,6 +14,7 @@ extern crate proc_macro; use proc_macro::TokenStream; +mod executor; mod join; mod select; @@ -44,3 +45,9 @@ pub fn select_internal(input: TokenStream) -> TokenStream { pub fn select_biased_internal(input: TokenStream) -> TokenStream { crate::select::select_biased(input) } + +/// The `test` attribute. +#[proc_macro_attribute] +pub fn test_internal(input: TokenStream, item: TokenStream) -> TokenStream { + crate::executor::test(input, item) +} diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml index 87baba18ab..f5f9a93e48 100644 --- a/futures-test/Cargo.toml +++ b/futures-test/Cargo.toml @@ -18,6 +18,7 @@ futures-io = { version = "0.3.14", path = "../futures-io", default-features = fa futures-util = { version = "=0.4.0-alpha.0", path = "../futures-util", default-features = false } futures-executor = { version = "=0.4.0-alpha.0", path = "../futures-executor", default-features = false } futures-sink = { version = "=0.4.0-alpha.0", path = "../futures-sink", default-features = false } +futures-macro = { version = "=0.4.0-alpha.0", path = "../futures-macro", default-features = false } pin-utils = { version = "0.1.0", default-features = false } pin-project = "1.0.1" diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 4c26a56987..109cc95434 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -16,6 +16,7 @@ compile_error!( #[cfg(feature = "std")] pub mod __private { pub use futures_core::{future, stream, task}; + pub use futures_executor::block_on; pub use std::{ option::Option::{None, Some}, pin::Pin, @@ -49,3 +50,16 @@ pub mod io; mod assert_unmoved; mod interleave_pending; mod track_closed; + +/// Enables an `async` test function. The generated future will be run to completion with +/// [`futures_executor::block_on`](futures_executor::block_on). +/// +/// ```no_run +/// #[futures_test::test] +/// async fn my_test() { +/// let fut = async { true }; +/// assert!(fut.await); +/// } +/// ``` +#[cfg(feature = "std")] +pub use futures_macro::test_internal as test; diff --git a/futures-util/src/sink/mod.rs b/futures-util/src/sink/mod.rs index bb3c5c46f2..6ac5bd3146 100644 --- a/futures-util/src/sink/mod.rs +++ b/futures-util/src/sink/mod.rs @@ -243,7 +243,7 @@ pub trait SinkExt: Sink { /// This future will drive the stream to keep producing items until it is /// exhausted, sending each item to the sink. It will complete once both the /// stream is exhausted, the sink has received all items, and the sink has - /// been flushed. Note that the sink is **not** closed. If the stream produces + /// been flushed. Note that the sink is **not** closed. If the stream produces /// an error, that error will be returned by this future without flushing the sink. /// /// Doing `sink.send_all(stream)` is roughly equivalent to From 422434ced2ba41c10e320f9785838ca9adb9c87d Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Thu, 6 May 2021 03:39:52 -0400 Subject: [PATCH 2/5] don't skip async-test macro doc tests --- futures-test/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 109cc95434..00cd980903 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -54,7 +54,7 @@ mod track_closed; /// Enables an `async` test function. The generated future will be run to completion with /// [`futures_executor::block_on`](futures_executor::block_on). /// -/// ```no_run +/// ``` /// #[futures_test::test] /// async fn my_test() { /// let fut = async { true }; From 3d5f4a86cbd7a812b94c1721bb7164e93f445668 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Thu, 6 May 2021 11:38:38 -0400 Subject: [PATCH 3/5] Apply suggestions from code review Co-authored-by: Taiki Endo --- futures-macro/src/executor.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/futures-macro/src/executor.rs b/futures-macro/src/executor.rs index ac008acc7f..48f510ffaa 100644 --- a/futures-macro/src/executor.rs +++ b/futures-macro/src/executor.rs @@ -1,25 +1,29 @@ use proc_macro::TokenStream; use quote::quote; -pub(crate) fn test(_: TokenStream, item: TokenStream) -> TokenStream { +pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { + if !args.is_empty() { + return syn::Error::new_spanned(args, "invalid argument")) + .to_compile_error() + .into(); + } let mut input = syn::parse_macro_input!(item as syn::ItemFn); let attrs = &input.attrs; let vis = &input.vis; let sig = &mut input.sig; let body = &input.block; - if sig.asyncness.is_none() { + if sig.asyncness.take().is_none() { return syn::Error::new_spanned(sig.fn_token, "Only async functions are supported") .to_compile_error() .into(); } - sig.asyncness = None; - let gen = quote! { + #[::core::prelude::v1::test] #(#attrs)* #vis #sig { - ::futures_test::__private::block_on(async move { #body }) + ::futures_test::__private::block_on(async move #body) } }; From 7c02280ed2a67cd6f4125561b1d546531fbb4266 Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Thu, 6 May 2021 11:57:42 -0400 Subject: [PATCH 4/5] add tests for async-test macro --- futures-macro/src/executor.rs | 3 ++- futures/tests/test_macro.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 futures/tests/test_macro.rs diff --git a/futures-macro/src/executor.rs b/futures-macro/src/executor.rs index 48f510ffaa..1efb48c7c7 100644 --- a/futures-macro/src/executor.rs +++ b/futures-macro/src/executor.rs @@ -3,10 +3,11 @@ use quote::quote; pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { if !args.is_empty() { - return syn::Error::new_spanned(args, "invalid argument")) + return syn::Error::new_spanned(proc_macro2::TokenStream::from(args), "invalid argument") .to_compile_error() .into(); } + let mut input = syn::parse_macro_input!(item as syn::ItemFn); let attrs = &input.attrs; let vis = &input.vis; diff --git a/futures/tests/test_macro.rs b/futures/tests/test_macro.rs new file mode 100644 index 0000000000..4b3b44634e --- /dev/null +++ b/futures/tests/test_macro.rs @@ -0,0 +1,18 @@ +#[cfg(test)] +mod tests { + #[futures_test::test] + async fn it_works() { + let fut = async { true }; + assert!(fut.await); + + let fut = async { false }; + assert!(!fut.await); + } + + #[futures_test::test] + #[should_panic] + async fn it_is_being_run() { + let fut = async { false }; + assert!(fut.await); + } +} From 1699535afb8279c879c40972eb6b9e741049356f Mon Sep 17 00:00:00 2001 From: ibraheemdev Date: Fri, 7 May 2021 11:07:23 -0400 Subject: [PATCH 5/5] apply suggestions from code review --- futures-test/src/lib.rs | 12 ++++++++++++ futures/tests/test_macro.rs | 27 ++++++++++++--------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/futures-test/src/lib.rs b/futures-test/src/lib.rs index 00cd980903..1117bb36cf 100644 --- a/futures-test/src/lib.rs +++ b/futures-test/src/lib.rs @@ -61,5 +61,17 @@ mod track_closed; /// assert!(fut.await); /// } /// ``` +/// +/// This is equivalent to the following code: +/// +/// ``` +/// #[test] +/// fn my_test() { +/// futures::executor::block_on(async move { +/// let fut = async { true }; +/// assert!(fut.await); +/// }) +/// } +/// ``` #[cfg(feature = "std")] pub use futures_macro::test_internal as test; diff --git a/futures/tests/test_macro.rs b/futures/tests/test_macro.rs index 4b3b44634e..2f391997ea 100644 --- a/futures/tests/test_macro.rs +++ b/futures/tests/test_macro.rs @@ -1,18 +1,15 @@ -#[cfg(test)] -mod tests { - #[futures_test::test] - async fn it_works() { - let fut = async { true }; - assert!(fut.await); +#[futures_test::test] +async fn it_works() { + let fut = async { true }; + assert!(fut.await); - let fut = async { false }; - assert!(!fut.await); - } + let fut = async { false }; + assert!(!fut.await); +} - #[futures_test::test] - #[should_panic] - async fn it_is_being_run() { - let fut = async { false }; - assert!(fut.await); - } +#[should_panic] +#[futures_test::test] +async fn it_is_being_run() { + let fut = async { false }; + assert!(fut.await); }