Skip to content

Commit

Permalink
Polish up
Browse files Browse the repository at this point in the history
  • Loading branch information
daxpedda committed Apr 1, 2021
1 parent 28e86f8 commit 5bb673a
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 33 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions rusty-fork-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
89 changes: 60 additions & 29 deletions rusty-fork-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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();
Expand All @@ -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()
}
}
}
Expand All @@ -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" {
Expand All @@ -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() {
Expand Down Expand Up @@ -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");
Expand All @@ -203,7 +234,7 @@ mod test {
}

#[tokio::test]
#[test_fork]
#[fork_test]
async fn my_test() {
assert!(true);
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

0 comments on commit 5bb673a

Please sign in to comment.