From 29c6cc633dc4a31ca978d06463dddd61fdb44b90 Mon Sep 17 00:00:00 2001 From: Tom Parker-Shemilt Date: Wed, 5 Feb 2020 20:16:49 +0000 Subject: [PATCH 1/3] Allow tests with return values --- serial_test/src/lib.rs | 4 ++-- serial_test_test/src/lib.rs | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/serial_test/src/lib.rs b/serial_test/src/lib.rs index b12d518..4ada434 100644 --- a/serial_test/src/lib.rs +++ b/serial_test/src/lib.rs @@ -29,7 +29,7 @@ lazy_static! { } #[doc(hidden)] -pub fn serial_core(name: &str, function: fn()) { +pub fn serial_core(name: &str, function: fn() -> T) -> T { // Check if a new key is needed. Just need a read lock, which can be done in sync with everyone else let new_key = { let unlock = LOCKS.read().unwrap(); @@ -46,7 +46,7 @@ pub fn serial_core(name: &str, function: fn()) { let unlock = LOCKS.read().unwrap(); // _guard needs to be named to avoid being instant dropped let _guard = unlock.deref()[name].lock(); - function(); + function() } // Re-export #[serial]. diff --git a/serial_test_test/src/lib.rs b/serial_test_test/src/lib.rs index 1d51941..17debaa 100644 --- a/serial_test_test/src/lib.rs +++ b/serial_test_test/src/lib.rs @@ -76,4 +76,11 @@ mod tests { init(); panic!("Testing panic"); } + + #[test] + #[serial] + fn test_can_return() -> Result<(), ()> { + let res: Result<(), ()> = Ok(()); + res + } } From 448d49e639cfa27a32d1a20f8567611aa7b77610 Mon Sep 17 00:00:00 2001 From: Tom Parker-Shemilt Date: Thu, 6 Feb 2020 15:53:05 +0000 Subject: [PATCH 2/3] Redo serial_core with return so we don't need an explicit type --- serial_test/src/lib.rs | 23 ++++++++++++++++++++++- serial_test_derive/src/lib.rs | 33 ++++++++++++++++++++++++++------- serial_test_test/src/lib.rs | 3 +-- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/serial_test/src/lib.rs b/serial_test/src/lib.rs index 4ada434..3848cdc 100644 --- a/serial_test/src/lib.rs +++ b/serial_test/src/lib.rs @@ -29,7 +29,7 @@ lazy_static! { } #[doc(hidden)] -pub fn serial_core(name: &str, function: fn() -> T) -> T { +pub fn serial_core_with_return(name: &str, function: fn() -> Result<(), E>) -> Result<(), E> { // Check if a new key is needed. Just need a read lock, which can be done in sync with everyone else let new_key = { let unlock = LOCKS.read().unwrap(); @@ -49,6 +49,27 @@ pub fn serial_core(name: &str, function: fn() -> T) -> T { function() } +#[doc(hidden)] +pub fn serial_core(name: &str, function: fn()) { + // Check if a new key is needed. Just need a read lock, which can be done in sync with everyone else + let new_key = { + let unlock = LOCKS.read().unwrap(); + !unlock.deref().contains_key(name) + }; + if new_key { + // This is the rare path, which avoids the multi-writer situation mostly + LOCKS + .write() + .unwrap() + .deref_mut() + .insert(name.to_string(), ReentrantMutex::new(())); + } + let unlock = LOCKS.read().unwrap(); + // _guard needs to be named to avoid being instant dropped + let _guard = unlock.deref()[name].lock(); + function(); +} + // Re-export #[serial]. #[allow(unused_imports)] pub use serial_test_derive::serial; diff --git a/serial_test_derive/src/lib.rs b/serial_test_derive/src/lib.rs index e938e2d..932a601 100644 --- a/serial_test_derive/src/lib.rs +++ b/serial_test_derive/src/lib.rs @@ -7,6 +7,7 @@ use proc_macro::TokenStream; use proc_macro2::TokenTree; use quote::quote; use syn; +use std::ops::Deref; /// Allows for the creation of serialised Rust tests /// ```` @@ -78,6 +79,12 @@ fn serial_core( }; let ast: syn::ItemFn = syn::parse2(input).unwrap(); let name = ast.sig.ident; + let return_type = match ast.sig.output { + syn::ReturnType::Default => None, + syn::ReturnType::Type(_rarrow, ref box_type) => { + Some(box_type.deref()) + } + }; let block = ast.block; let attrs: Vec = ast .attrs @@ -96,13 +103,25 @@ fn serial_core( } }) .collect(); - let gen = quote! { - #(#attrs) - * - fn #name () { - serial_test::serial_core(#key, || { - #block - }); + let gen = if let Some(ret) = return_type { + quote! { + #(#attrs) + * + fn #name () -> #ret { + serial_test::serial_core_with_return(#key, || { + #block + }) + } + } + } else { + quote! { + #(#attrs) + * + fn #name () { + serial_test::serial_core(#key, || { + #block + }); + } } }; return gen.into(); diff --git a/serial_test_test/src/lib.rs b/serial_test_test/src/lib.rs index 17debaa..074c925 100644 --- a/serial_test_test/src/lib.rs +++ b/serial_test_test/src/lib.rs @@ -80,7 +80,6 @@ mod tests { #[test] #[serial] fn test_can_return() -> Result<(), ()> { - let res: Result<(), ()> = Ok(()); - res + Ok(()) } } From 801d1bb3276ba0fbcb3ae07793e9693462d11536 Mon Sep 17 00:00:00 2001 From: Tom Parker-Shemilt Date: Thu, 6 Feb 2020 15:57:46 +0000 Subject: [PATCH 3/3] De-duplicate the checking for new keys --- serial_test/src/lib.rs | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/serial_test/src/lib.rs b/serial_test/src/lib.rs index 3848cdc..a4930ab 100644 --- a/serial_test/src/lib.rs +++ b/serial_test/src/lib.rs @@ -28,8 +28,7 @@ lazy_static! { Arc::new(RwLock::new(HashMap::new())); } -#[doc(hidden)] -pub fn serial_core_with_return(name: &str, function: fn() -> Result<(), E>) -> Result<(), E> { +fn check_new_key(name: &str) { // Check if a new key is needed. Just need a read lock, which can be done in sync with everyone else let new_key = { let unlock = LOCKS.read().unwrap(); @@ -43,6 +42,12 @@ pub fn serial_core_with_return(name: &str, function: fn() -> Result<(), E>) - .deref_mut() .insert(name.to_string(), ReentrantMutex::new(())); } +} + +#[doc(hidden)] +pub fn serial_core_with_return(name: &str, function: fn() -> Result<(), E>) -> Result<(), E> { + check_new_key(name); + let unlock = LOCKS.read().unwrap(); // _guard needs to be named to avoid being instant dropped let _guard = unlock.deref()[name].lock(); @@ -51,19 +56,8 @@ pub fn serial_core_with_return(name: &str, function: fn() -> Result<(), E>) - #[doc(hidden)] pub fn serial_core(name: &str, function: fn()) { - // Check if a new key is needed. Just need a read lock, which can be done in sync with everyone else - let new_key = { - let unlock = LOCKS.read().unwrap(); - !unlock.deref().contains_key(name) - }; - if new_key { - // This is the rare path, which avoids the multi-writer situation mostly - LOCKS - .write() - .unwrap() - .deref_mut() - .insert(name.to_string(), ReentrantMutex::new(())); - } + check_new_key(name); + let unlock = LOCKS.read().unwrap(); // _guard needs to be named to avoid being instant dropped let _guard = unlock.deref()[name].lock();