From 84cb492c2e39f57022e64172d0a97c4305a0c242 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Mon, 8 Jun 2020 12:50:26 +0200 Subject: [PATCH 1/5] Implement returning `Result` from test functions. --- src/fork_test.rs | 73 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 13 deletions(-) diff --git a/src/fork_test.rs b/src/fork_test.rs index a5531d1..5a5d0f9 100644 --- a/src/fork_test.rs +++ b/src/fork_test.rs @@ -72,13 +72,52 @@ macro_rules! rusty_fork_test { (#![rusty_fork(timeout_ms = $timeout:expr)] $( $(#[$meta:meta])* - fn $test_name:ident() $body:block + fn $test_name:ident()$( -> $ret:ty)? $body:block )*) => { $( + $crate::rusty_fork_test_ret!($timeout, [$($meta)*], $test_name, $body$(, $ret)?); + )* }; + + ($( + $(#[$meta:meta])* + fn $test_name:ident()$( -> $ret:ty)? $body:block + )*) => { + $crate::rusty_fork_test! { + #![rusty_fork(timeout_ms = 0)] + + $($(#[$meta])* fn $test_name()$( -> $ret)? $body)* + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! rusty_fork_test_ret { + ($timeout:expr, [$($meta:meta)*], $test_name:ident, $body:block) => { + $crate::rusty_fork_test_inner!($timeout, [$($meta)*], $test_name, fn body_fn() $body); + }; + + ($timeout:expr, [$($meta:meta)*], $test_name:ident, $body:block, $ret:ty) => { + $crate::rusty_fork_test_inner!( + $timeout, + [$($meta)*], + $test_name, + fn body_fn() { + fn body_fn() -> $ret $body + body_fn().unwrap(); + } + ); + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! rusty_fork_test_inner { + ($timeout:expr, [$($meta:meta)*], $test_name:ident, $body_fn:stmt) => { $(#[$meta])* fn $test_name() { // Eagerly convert everything to function pointers so that all // tests use the same instantiation of `fork`. - fn body_fn() $body + $body_fn let body: fn () = body_fn; fn supervise_fn(child: &mut $crate::ChildWrapper, @@ -95,17 +134,6 @@ macro_rules! rusty_fork_test { $crate::fork_test::no_configure_child, supervise, body).expect("forking test failed") } - )* }; - - ($( - $(#[$meta:meta])* - fn $test_name:ident() $body:block - )*) => { - rusty_fork_test! { - #![rusty_fork(timeout_ms = 0)] - - $($(#[$meta])* fn $test_name() $body)* - } }; } @@ -170,6 +198,8 @@ fn wait_timeout(_: &mut ChildWrapper, _: u64) { #[cfg(test)] mod test { + use std::io::Result; + rusty_fork_test! { #[test] fn trivial() { } @@ -185,6 +215,23 @@ mod test { fn aborting_child() { ::std::process::abort(); } + + #[test] + fn trivial_result() -> Result<()> { + Ok(()) + } + + #[test] + #[should_panic] + fn panicking_child_result() -> Result<()> { + panic!("just testing a panic, nothing to see here"); + } + + #[test] + #[should_panic] + fn aborting_child_result() -> Result<()> { + ::std::process::abort(); + } } rusty_fork_test! { From 28e86f85b564f80e37004fb4af21f41d8332702d Mon Sep 17 00:00:00 2001 From: daxpedda Date: Mon, 8 Jun 2020 20:22:43 +0200 Subject: [PATCH 2/5] Implement proc macro feature. --- Cargo.toml | 5 + rusty-fork-macro/Cargo.toml | 26 +++++ rusty-fork-macro/src/lib.rs | 210 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 + 4 files changed, 243 insertions(+) create mode 100644 rusty-fork-macro/Cargo.toml create mode 100644 rusty-fork-macro/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 06eccad..26099a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,13 @@ fork-like interface. [badges] travis-ci = { repository = "AltSysrq/rusty-fork" } +[workspace] +members = ["rusty-fork-macro"] + [dependencies] fnv = "1.0" quick-error = "1.2" +rusty-fork-macro = { path = "rusty-fork-macro", optional = true } tempfile = "3.0" wait-timeout = { version = "0.2", optional = true } @@ -29,4 +33,5 @@ wait-timeout = { version = "0.2", optional = true } [features] default = [ "timeout" ] +macro = [ "rusty-fork-macro" ] timeout = [ "wait-timeout" ] diff --git a/rusty-fork-macro/Cargo.toml b/rusty-fork-macro/Cargo.toml new file mode 100644 index 0000000..54e4a0b --- /dev/null +++ b/rusty-fork-macro/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "rusty-fork-macro" +version = "0.1.0" +authors = ["Jason Lingle"] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/altsysrq/rusty-fork" +documentation = "https://docs.rs/rusty-fork-macro" +keywords = ["testing", "process", "fork"] +categories = ["development-tools::testing"] +edition = "2018" + +description = """ +Proc-macro library for rusty-fork. +""" + +[lib] +proc-macro = true + +[dependencies] +quote = "1" +syn = { version = "1", features = ["full"] } + +[dev-dependencies] +rusty-fork = { path = "../", features = ["macro", "timeout"] } +tokio = { version = "0.2", features = ["rt-threaded", "macros"] } diff --git a/rusty-fork-macro/src/lib.rs b/rusty-fork-macro/src/lib.rs new file mode 100644 index 0000000..131187d --- /dev/null +++ b/rusty-fork-macro/src/lib.rs @@ -0,0 +1,210 @@ +#![deny(missing_docs, unsafe_code)] +#![allow(unused_imports, unused_variables)] + +//! Proc-macro crate of `rusty-fork`. + +use proc_macro::TokenStream; +use quote::ToTokens; +use syn::{AttributeArgs, Error, ItemFn, Lit, Meta, NestedMeta, ReturnType}; + +/// Run Rust tests in subprocesses. +/// +/// The basic usage is to simply put this macro around your test functions. +/// +/// ``` +/// # /* +/// #[cfg(test)] +/// # */ +/// mod test { +/// use rusty_fork::test_fork; +/// +/// # /* +/// #[test_fork] +/// # */ +/// # pub +/// fn my_test() { +/// assert_eq!(2, 1 + 1); +/// } +/// +/// // more tests... +/// } +/// # +/// # fn main() { test::my_test(); } +/// ``` +/// +/// Each test will be run in its own process. If the subprocess exits +/// unsuccessfully for any reason, including due to signals, the test fails. +/// +/// It is also possible to specify a timeout which is applied to all tests in +/// the block, like so: +/// +/// ``` +/// use rusty_fork::test_fork; +/// +/// # /* +/// #[test_fork(timeout_ms = 1000)] +/// # */ +/// fn my_test() { +/// do_some_expensive_computation(); +/// } +/// # fn do_some_expensive_computation() { } +/// # fn main() { my_test(); } +/// ``` +/// +/// If any individual test takes more than the given timeout, the child is +/// terminated and the test panics. +/// +/// Using the timeout feature requires the `timeout` feature for this crate to +/// be enabled (which it is by default). +#[proc_macro_attribute] +pub fn test_fork(args: TokenStream, item: TokenStream) -> TokenStream { + let args = syn::parse_macro_input!(args as AttributeArgs); + + let mut crate_name = quote::quote! { rusty_fork }; + let mut timeout = quote::quote! { 0 }; + + for arg in args { + if let NestedMeta::Meta(meta) = arg { + if let Meta::NameValue(name_value) = meta { + if let Some(ident) = name_value.path.get_ident() { + match ident.to_string().as_str() { + "timeout_ms" => { + if let Lit::Int(int) = name_value.lit { + timeout = int.to_token_stream(); + } + } + "crate" => { + if let Lit::Str(str) = name_value.lit { + crate_name = str.to_token_stream(); + } + } + _ => (), + } + } + } + } + } + + let item = syn::parse_macro_input!(item as ItemFn); + + let fn_attrs = &item.attrs; + let fn_sig = &item.sig; + let fn_body = &item.block; + let fn_name = &fn_sig.ident; + + let mut test = quote::quote! { #[test] }; + + for attr in fn_attrs { + if let Some(ident) = attr.path.get_ident() { + if ident == "test" { + test = quote::quote! {}; + } + } + } + + if let Some(asyncness) = fn_sig.asyncness { + return Error::new( + asyncness.span, + "put `#[test_fork]` last to let other macros process first", + ) + .to_compile_error() + .into(); + } + + let body_fn = if let ReturnType::Type(_, ret_ty) = &fn_sig.output { + quote::quote! { + fn body_fn() { + fn body_fn() -> #ret_ty #fn_body + body_fn().unwrap(); + } + } + } else { + quote::quote! { + fn body_fn() #fn_body + } + }; + + (quote::quote! { + #test + #(#fn_attrs)* + fn #fn_name() { + // Eagerly convert everything to function pointers so that all + // tests use the same instantiation of `fork`. + #body_fn + let body: fn () = body_fn; + + fn supervise_fn( + child: &mut ::#crate_name::ChildWrapper, + _file: &mut ::std::fs::File + ) { + ::#crate_name::fork_test::supervise_child(child, #timeout) + } + let supervise: + fn (&mut ::#crate_name::ChildWrapper, &mut ::std::fs::File) = + supervise_fn; + ::#crate_name::fork( + ::#crate_name::rusty_fork_test_name!(#fn_name), + ::#crate_name::rusty_fork_id!(), + ::#crate_name::fork_test::no_configure_child, + supervise, + body + ).expect("forking test failed") + } + }) + .into() +} + +#[cfg(test)] +mod test { + use rusty_fork::test_fork; + use std::io::Result; + + #[test_fork] + fn trivials() {} + + #[test_fork] + #[should_panic] + fn panicking_child() { + panic!("just testing a panic, nothing to see here"); + } + + #[test_fork] + #[should_panic] + fn aborting_child() { + ::std::process::abort(); + } + + #[test_fork] + fn trivial_result() -> Result<()> { + Ok(()) + } + + #[test_fork] + #[should_panic] + fn panicking_child_result() -> Result<()> { + panic!("just testing a panic, nothing to see here"); + } + + #[test_fork] + #[should_panic] + fn aborting_child_result() -> Result<()> { + ::std::process::abort(); + } + + #[test_fork(timeout_ms = 1000)] + fn timeout_passes() {} + + #[test_fork(timeout_ms = 1000)] + #[should_panic] + fn timeout_fails() { + println!("hello from child"); + ::std::thread::sleep(::std::time::Duration::from_millis(10000)); + println!("goodbye from child"); + } + + #[tokio::test] + #[test_fork] + async fn my_test() { + assert!(true); + } +} diff --git a/src/lib.rs b/src/lib.rs index d1fe900..dbb9fbf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,3 +126,5 @@ pub use crate::sugar::RustyForkId; pub use crate::error::{Error, Result}; pub use crate::fork::fork; pub use crate::child_wrapper::{ChildWrapper, ExitStatusWrapper}; +#[cfg(feature = "macro")] +pub use rusty_fork_macro::test_fork; From 5bb673a43670b620c13241702442c9882cb8ccf5 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Thu, 1 Apr 2021 12:22:03 +0200 Subject: [PATCH 3/5] Polish up --- README.md | 6 ++- rusty-fork-macro/Cargo.toml | 4 +- rusty-fork-macro/src/lib.rs | 89 +++++++++++++++++++++++++------------ src/lib.rs | 2 +- 4 files changed, 68 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index fc06ee1..1ed7d03 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ the test and forcibly terminate it and produce a normal test failure. as the current working directory, can do so without interfering with other tests. -This crate itself provides two things: +This crate itself provides three things: - The [`rusty_fork_test!`](macro.rusty_fork_test.html) macro, which is a simple way to wrap standard Rust tests to be run in subprocesses with @@ -29,6 +29,10 @@ optional timeouts. - The [`fork`](fn.fork.html) function which can be used as a building block to make other types of process isolation strategies. +- The [`#[fork_test]`] proc macro, which works exactly like `rusty_fork_test!`, + but with the additionaly feature of supporting `async` functions. It is gated + behind the [`macro`] crate feature flag. + ## Quick Start If you just want to run normal Rust tests in isolated processes, getting diff --git a/rusty-fork-macro/Cargo.toml b/rusty-fork-macro/Cargo.toml index 54e4a0b..8b014fc 100644 --- a/rusty-fork-macro/Cargo.toml +++ b/rusty-fork-macro/Cargo.toml @@ -2,8 +2,8 @@ name = "rusty-fork-macro" version = "0.1.0" authors = ["Jason Lingle"] -license = "MIT/Apache-2.0" -readme = "README.md" +license = "MIT OR Apache-2.0" +readme = "../README.md" repository = "https://github.com/altsysrq/rusty-fork" documentation = "https://docs.rs/rusty-fork-macro" keywords = ["testing", "process", "fork"] diff --git a/rusty-fork-macro/src/lib.rs b/rusty-fork-macro/src/lib.rs index 131187d..efc0f30 100644 --- a/rusty-fork-macro/src/lib.rs +++ b/rusty-fork-macro/src/lib.rs @@ -16,10 +16,10 @@ use syn::{AttributeArgs, Error, ItemFn, Lit, Meta, NestedMeta, ReturnType}; /// #[cfg(test)] /// # */ /// mod test { -/// use rusty_fork::test_fork; +/// use rusty_fork::fork_test; /// /// # /* -/// #[test_fork] +/// #[fork_test] /// # */ /// # pub /// fn my_test() { @@ -39,10 +39,10 @@ use syn::{AttributeArgs, Error, ItemFn, Lit, Meta, NestedMeta, ReturnType}; /// the block, like so: /// /// ``` -/// use rusty_fork::test_fork; +/// use rusty_fork::fork_test; /// /// # /* -/// #[test_fork(timeout_ms = 1000)] +/// #[fork_test(timeout_ms = 1000)] /// # */ /// fn my_test() { /// do_some_expensive_computation(); @@ -56,29 +56,55 @@ use syn::{AttributeArgs, Error, ItemFn, Lit, Meta, NestedMeta, ReturnType}; /// /// Using the timeout feature requires the `timeout` feature for this crate to /// be enabled (which it is by default). +/// +/// ``` +/// use rusty_fork::fork_test; +/// +/// # /* +/// #[fork_test(crate = rusty_fork)] +/// # */ +/// fn my_test() { +/// assert_eq!(2, 1 + 1); +/// } +/// # fn main() { my_test(); } +/// ``` +/// +/// Sometimes the crate dependency might be renamed, in cases like this use the `crate` attribute +/// to pass the new name to rusty-fork. #[proc_macro_attribute] -pub fn test_fork(args: TokenStream, item: TokenStream) -> TokenStream { +pub fn fork_test(args: TokenStream, item: TokenStream) -> TokenStream { let args = syn::parse_macro_input!(args as AttributeArgs); + // defaults let mut crate_name = quote::quote! { rusty_fork }; let mut timeout = quote::quote! { 0 }; + // may be changed by the user for arg in args { - if let NestedMeta::Meta(meta) = arg { - if let Meta::NameValue(name_value) = meta { - if let Some(ident) = name_value.path.get_ident() { - match ident.to_string().as_str() { - "timeout_ms" => { - if let Lit::Int(int) = name_value.lit { - timeout = int.to_token_stream(); - } + if let NestedMeta::Meta(Meta::NameValue(name_value)) = arg { + if let Some(ident) = name_value.path.get_ident() { + match ident.to_string().as_str() { + "timeout_ms" => { + if let Lit::Int(int) = name_value.lit { + timeout = int.to_token_stream(); } - "crate" => { - if let Lit::Str(str) = name_value.lit { - crate_name = str.to_token_stream(); - } + } + "crate" => { + if let Lit::Str(str) = name_value.lit { + crate_name = str.to_token_stream(); } - _ => (), + } + // we don't support using invalid attributes + attribute => { + return Error::new( + ident.span(), + format!( + "`{}` is not a valid attribute for `#[fork_test]`", + attribute + ), + ) + .to_compile_error() + .into() } } } @@ -92,8 +118,10 @@ pub fn test_fork(args: TokenStream, item: TokenStream) -> TokenStream { let fn_body = &item.block; let fn_name = &fn_sig.ident; + // the default is that we add the `#[test]` for the use let mut test = quote::quote! { #[test] }; + // we should still support a use case where the user adds it himself for attr in fn_attrs { if let Some(ident) = attr.path.get_ident() { if ident == "test" { @@ -102,15 +130,18 @@ pub fn test_fork(args: TokenStream, item: TokenStream) -> TokenStream { } } + // we don't support async functions, whatever library the user uses to support this, should + // process first if let Some(asyncness) = fn_sig.asyncness { return Error::new( asyncness.span, - "put `#[test_fork]` last to let other macros process first", + "put `#[fork_test]` after the macro that enables `async` support", ) .to_compile_error() .into(); } + // support returning `Result` let body_fn = if let ReturnType::Type(_, ret_ty) = &fn_sig.output { quote::quote! { fn body_fn() { @@ -156,45 +187,45 @@ pub fn test_fork(args: TokenStream, item: TokenStream) -> TokenStream { #[cfg(test)] mod test { - use rusty_fork::test_fork; + use rusty_fork::fork_test; use std::io::Result; - #[test_fork] + #[fork_test] fn trivials() {} - #[test_fork] + #[fork_test] #[should_panic] fn panicking_child() { panic!("just testing a panic, nothing to see here"); } - #[test_fork] + #[fork_test] #[should_panic] fn aborting_child() { ::std::process::abort(); } - #[test_fork] + #[fork_test] fn trivial_result() -> Result<()> { Ok(()) } - #[test_fork] + #[fork_test] #[should_panic] fn panicking_child_result() -> Result<()> { panic!("just testing a panic, nothing to see here"); } - #[test_fork] + #[fork_test] #[should_panic] fn aborting_child_result() -> Result<()> { ::std::process::abort(); } - #[test_fork(timeout_ms = 1000)] + #[fork_test(timeout_ms = 1000)] fn timeout_passes() {} - #[test_fork(timeout_ms = 1000)] + #[fork_test(timeout_ms = 1000)] #[should_panic] fn timeout_fails() { println!("hello from child"); @@ -203,7 +234,7 @@ mod test { } #[tokio::test] - #[test_fork] + #[fork_test] async fn my_test() { assert!(true); } diff --git a/src/lib.rs b/src/lib.rs index dbb9fbf..cdd1f6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,4 +127,4 @@ pub use crate::error::{Error, Result}; pub use crate::fork::fork; pub use crate::child_wrapper::{ChildWrapper, ExitStatusWrapper}; #[cfg(feature = "macro")] -pub use rusty_fork_macro::test_fork; +pub use rusty_fork_macro::fork_test; From 5a8e54f32739d02458524ec874b7e5e88d92e948 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Thu, 1 Apr 2021 12:42:22 +0200 Subject: [PATCH 4/5] Rely on #10 and update Tokio --- rusty-fork-macro/Cargo.toml | 2 +- rusty-fork-macro/src/lib.rs | 75 ++++++++++++++----------------------- 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/rusty-fork-macro/Cargo.toml b/rusty-fork-macro/Cargo.toml index 8b014fc..56f3d12 100644 --- a/rusty-fork-macro/Cargo.toml +++ b/rusty-fork-macro/Cargo.toml @@ -23,4 +23,4 @@ syn = { version = "1", features = ["full"] } [dev-dependencies] rusty-fork = { path = "../", features = ["macro", "timeout"] } -tokio = { version = "0.2", features = ["rt-threaded", "macros"] } +tokio = { version = "1", features = ["macros", "rt"] } diff --git a/rusty-fork-macro/src/lib.rs b/rusty-fork-macro/src/lib.rs index efc0f30..7438683 100644 --- a/rusty-fork-macro/src/lib.rs +++ b/rusty-fork-macro/src/lib.rs @@ -77,7 +77,7 @@ pub fn fork_test(args: TokenStream, item: TokenStream) -> TokenStream { // defaults let mut crate_name = quote::quote! { rusty_fork }; - let mut timeout = quote::quote! { 0 }; + let mut timeout = quote::quote! {}; // may be changed by the user for arg in args { @@ -86,7 +86,7 @@ pub fn fork_test(args: TokenStream, item: TokenStream) -> TokenStream { match ident.to_string().as_str() { "timeout_ms" => { if let Lit::Int(int) = name_value.lit { - timeout = int.to_token_stream(); + timeout = quote::quote! { #![rusty_fork(timeout_ms = #int)] } } } "crate" => { @@ -113,16 +113,16 @@ pub fn fork_test(args: TokenStream, item: TokenStream) -> TokenStream { let item = syn::parse_macro_input!(item as ItemFn); - let fn_attrs = &item.attrs; - let fn_sig = &item.sig; - let fn_body = &item.block; - let fn_name = &fn_sig.ident; + let fn_attrs = item.attrs; + let fn_vis = item.vis; + let fn_sig = item.sig; + let fn_body = item.block; // the default is that we add the `#[test]` for the use let mut test = quote::quote! { #[test] }; // we should still support a use case where the user adds it himself - for attr in fn_attrs { + for attr in &fn_attrs { if let Some(ident) = attr.path.get_ident() { if ident == "test" { test = quote::quote! {}; @@ -141,45 +141,13 @@ pub fn fork_test(args: TokenStream, item: TokenStream) -> TokenStream { .into(); } - // support returning `Result` - let body_fn = if let ReturnType::Type(_, ret_ty) = &fn_sig.output { - quote::quote! { - fn body_fn() { - fn body_fn() -> #ret_ty #fn_body - body_fn().unwrap(); - } - } - } else { - quote::quote! { - fn body_fn() #fn_body - } - }; - (quote::quote! { - #test - #(#fn_attrs)* - fn #fn_name() { - // Eagerly convert everything to function pointers so that all - // tests use the same instantiation of `fork`. - #body_fn - let body: fn () = body_fn; - - fn supervise_fn( - child: &mut ::#crate_name::ChildWrapper, - _file: &mut ::std::fs::File - ) { - ::#crate_name::fork_test::supervise_child(child, #timeout) - } - let supervise: - fn (&mut ::#crate_name::ChildWrapper, &mut ::std::fs::File) = - supervise_fn; - ::#crate_name::fork( - ::#crate_name::rusty_fork_test_name!(#fn_name), - ::#crate_name::rusty_fork_id!(), - ::#crate_name::fork_test::no_configure_child, - supervise, - body - ).expect("forking test failed") + ::#crate_name::rusty_fork_test! { + #timeout + + #test + #(#fn_attrs)* + #fn_vis #fn_sig #fn_body } }) .into() @@ -235,7 +203,20 @@ mod test { #[tokio::test] #[fork_test] - async fn my_test() { - assert!(true); + async fn async_test() { + tokio::task::spawn(async { + println!("hello from child"); + }) + .await + .unwrap(); + } + + #[tokio::test] + #[fork_test] + async fn async_return_test() -> std::result::Result<(), tokio::task::JoinError> { + tokio::task::spawn(async { + println!("hello from child"); + }) + .await } } From a5b7d7b4578881788f8d5a86f8ffa268309b42e1 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Thu, 1 Apr 2021 12:46:27 +0200 Subject: [PATCH 5/5] Improve docs and add preamble --- README.md | 4 ++-- rusty-fork-macro/src/lib.rs | 12 ++++++++++-- src/lib.rs | 6 +++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1ed7d03..dc7a935 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ optional timeouts. - The [`fork`](fn.fork.html) function which can be used as a building block to make other types of process isolation strategies. -- The [`#[fork_test]`] proc macro, which works exactly like `rusty_fork_test!`, +- The `#[fork_test]` proc macro, which works exactly like `rusty_fork_test!`, but with the additionaly feature of supporting `async` functions. It is gated - behind the [`macro`] crate feature flag. + behind the `macro` crate feature flag. ## Quick Start diff --git a/rusty-fork-macro/src/lib.rs b/rusty-fork-macro/src/lib.rs index 7438683..3f1533b 100644 --- a/rusty-fork-macro/src/lib.rs +++ b/rusty-fork-macro/src/lib.rs @@ -1,11 +1,19 @@ +//- +// Copyright 2018 +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + #![deny(missing_docs, unsafe_code)] -#![allow(unused_imports, unused_variables)] //! Proc-macro crate of `rusty-fork`. use proc_macro::TokenStream; use quote::ToTokens; -use syn::{AttributeArgs, Error, ItemFn, Lit, Meta, NestedMeta, ReturnType}; +use syn::{AttributeArgs, Error, ItemFn, Lit, Meta, NestedMeta}; /// Run Rust tests in subprocesses. /// diff --git a/src/lib.rs b/src/lib.rs index cdd1f6d..f0abc0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,7 +26,7 @@ //! as the current working directory, can do so without interfering with other //! tests. //! -//! This crate itself provides two things: +//! This crate itself provides three things: //! //! - The [`rusty_fork_test!`](macro.rusty_fork_test.html) macro, which is a //! simple way to wrap standard Rust tests to be run in subprocesses with @@ -35,6 +35,10 @@ //! - The [`fork`](fn.fork.html) function which can be used as a building block //! to make other types of process isolation strategies. //! +//! - The [`#[fork_test]`](crate::fork_test) proc macro, which works exactly like +//! `rusty_fork_test!`, but with the additionaly feature of supporting `async` functions. It is +//1 gated behind the `macro` crate feature flag. +//! //! ## Quick Start //! //! If you just want to run normal Rust tests in isolated processes, getting