Skip to content

Commit

Permalink
Merge pull request #104 from palfrey/serial-for-mod
Browse files Browse the repository at this point in the history
Attributes at a mod-level
  • Loading branch information
palfrey committed Jan 6, 2024
2 parents cee1dc1 + 0dec256 commit ae14645
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 24 deletions.
27 changes: 19 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -33,6 +33,8 @@ For cases like doctests and integration tests where the tests are run as separat
similar properties but based off file locking. Note that there are no guarantees about one test with `serial` and another with
`file_serial` as they lock using different methods.

All of the attributes can also be applied at a `mod` level and will be automagically applied to all test functions in that block.

## Usage
The minimum supported Rust version here is 1.68.2. Note this is minimum _supported_, as it may well compile with lower versions, but they're not supported at all. Upgrades to this will require at a major version bump. 1.x supports 1.51 if you need a lower version than that.

Expand Down
17 changes: 17 additions & 0 deletions serial_test/src/lib.rs
Expand Up @@ -42,6 +42,23 @@
//! }
//! ````
//!
//! All of the attributes can also be applied at a `mod` level and will be automagically applied to all test functions in that block
//! ````
//! #[cfg(test)]
//! #[serial]
//! mod serial_attr_tests {
//! fn foo() {
//! // Won't have `serial` applied, because not a test function
//! println!("Nothing");
//! }
//!
//! #[test]
//! fn test_bar() {
//! // Will be run serially
//! }
//!}
//! ````
//!
//! ## Feature flags
#![cfg_attr(
feature = "docsrs",
Expand Down
1 change: 1 addition & 0 deletions serial_test_derive/Cargo.toml
Expand Up @@ -19,6 +19,7 @@ proc-macro2 = "1.0.60" # Because of https://github.com/dtolnay/proc-macro2/issue

[dev-dependencies]
env_logger = "0.10"
prettyplease = "0.2"

[features]
async = []
135 changes: 119 additions & 16 deletions serial_test_derive/src/lib.rs
Expand Up @@ -10,6 +10,7 @@ use proc_macro::TokenStream;
use proc_macro2::{Literal, TokenTree};
use quote::{format_ident, quote, ToTokens, TokenStreamExt};
use std::ops::Deref;
use syn::Result as SynResult;

/// Allows for the creation of serialised Rust tests
/// ````
Expand Down Expand Up @@ -193,7 +194,7 @@ pub fn file_parallel(attr: TokenStream, input: TokenStream) -> TokenStream {
}

// Based off of https://github.com/dtolnay/quote/issues/20#issuecomment-437341743
#[derive(Default, Debug)]
#[derive(Default, Debug, Clone)]
struct QuoteOption<T>(Option<T>);

impl<T: ToTokens> ToTokens for QuoteOption<T> {
Expand Down Expand Up @@ -318,13 +319,63 @@ fn fs_parallel_core(
parallel_setup(input, config, "fs")
}

#[allow(clippy::cmp_owned)]
fn core_setup(
input: proc_macro2::TokenStream,
config: Config,
config: &Config,
prefix: &str,
kind: &str,
) -> proc_macro2::TokenStream {
let fn_ast: SynResult<syn::ItemFn> = syn::parse2(input.clone());
if let Ok(ast) = fn_ast {
return fn_setup(ast, config, prefix, kind);
};
let mod_ast: SynResult<syn::ItemMod> = syn::parse2(input);
match mod_ast {
Ok(mut ast) => {
let new_content = ast.content.clone().map(|(brace, items)| {
let new_items = items
.into_iter()
.map(|item| match item {
syn::Item::Fn(item_fn)
if item_fn.attrs.iter().any(|attr| {
attr.meta
.path()
.segments
.first()
.unwrap()
.ident
.to_string()
.contains("test")
}) =>
{
syn::parse2(fn_setup(item_fn, config, prefix, kind)).unwrap()
}
other => other,
})
.collect();
(brace, new_items)
});
if let Some(nc) = new_content {
ast.content.replace(nc);
}
ast.attrs.retain(|attr| {
attr.meta.path().segments.first().unwrap().ident.to_string() != "serial"
});
ast.into_token_stream()
}
Err(_) => {
panic!("Attribute applied to something other than mod or fn!");
}
}
}

fn fn_setup(
ast: syn::ItemFn,
config: &Config,
prefix: &str,
kind: &str,
) -> proc_macro2::TokenStream {
let ast: syn::ItemFn = syn::parse2(input).unwrap();
let asyncness = ast.sig.asyncness;
if asyncness.is_some() && cfg!(not(feature = "async")) {
panic!("async testing attempted with async feature disabled in serial_test!");
Expand All @@ -337,8 +388,8 @@ fn core_setup(
};
let block = ast.block;
let attrs: Vec<syn::Attribute> = ast.attrs.into_iter().collect();
let names = config.names;
let path = config.path;
let names = config.names.clone();
let path = config.path.clone();
if let Some(ret) = return_type {
match asyncness {
Some(_) => {
Expand Down Expand Up @@ -401,23 +452,40 @@ fn serial_setup(
config: Config,
prefix: &str,
) -> proc_macro2::TokenStream {
core_setup(input, config, prefix, "serial")
core_setup(input, &config, prefix, "serial")
}

fn parallel_setup(
input: proc_macro2::TokenStream,
config: Config,
prefix: &str,
) -> proc_macro2::TokenStream {
core_setup(input, config, prefix, "parallel")
core_setup(input, &config, prefix, "parallel")
}

#[cfg(test)]
mod tests {
use super::{fs_serial_core, local_serial_core};
use proc_macro2::TokenStream;
use quote::quote;
use std::iter::FromIterator;

fn unparse(input: TokenStream) -> String {
let item = syn::parse2(input).unwrap();
let file = syn::File {
attrs: vec![],
items: vec![item],
shebang: None,
};

prettyplease::unparse(&file)
}

fn compare_streams(first: TokenStream, second: TokenStream) {
let f = unparse(first);
assert_eq!(f, unparse(second));
}

#[test]
fn test_serial() {
let attrs = proc_macro2::TokenStream::new();
Expand All @@ -432,7 +500,7 @@ mod tests {
serial_test::local_serial_core(vec![""], ::std::option::Option::None, || {} );
}
};
assert_eq!(format!("{}", compare), format!("{}", stream));
compare_streams(compare, stream);
}

#[test]
Expand All @@ -449,7 +517,7 @@ mod tests {
serial_test::local_serial_core(vec![""], ::std::option::Option::None, || {} );
}
};
assert_eq!(format!("{}", compare), format!("{}", stream));
compare_streams(compare, stream);
}

#[test]
Expand All @@ -470,10 +538,10 @@ mod tests {
#[should_panic(expected = "Testing panic")]
#[something_else]
fn foo () {
serial_test::local_serial_core(vec![""], ::std::option::Option::None, || {} );
serial_test::local_serial_core(vec![""], ::std::option::Option::None, || {} );
}
};
assert_eq!(format!("{}", compare), format!("{}", stream));
compare_streams(compare, stream);
}

#[test]
Expand Down Expand Up @@ -527,7 +595,7 @@ mod tests {
serial_test::fs_serial_core(vec!["foo"], ::std::option::Option::None, || {} );
}
};
assert_eq!(format!("{}", compare), format!("{}", stream));
compare_streams(compare, stream);
}

#[test]
Expand All @@ -547,7 +615,7 @@ mod tests {
serial_test::fs_serial_core(vec![""], ::std::option::Option::None, || {} );
}
};
assert_eq!(format!("{}", compare), format!("{}", stream));
compare_streams(compare, stream);
}

#[test]
Expand All @@ -567,7 +635,7 @@ mod tests {
serial_test::fs_serial_core(vec!["foo"], ::std::option::Option::Some("bar_path"), || {} );
}
};
assert_eq!(format!("{}", compare), format!("{}", stream));
compare_streams(compare, stream);
}

#[test]
Expand All @@ -587,7 +655,7 @@ mod tests {
serial_test::local_serial_core(vec!["one"], ::std::option::Option::None, || {} );
}
};
assert_eq!(format!("{}", compare), format!("{}", stream));
compare_streams(compare, stream);
}

#[test]
Expand All @@ -607,6 +675,41 @@ mod tests {
serial_test::local_serial_core(vec!["one", "two"], ::std::option::Option::None, || {} );
}
};
assert_eq!(format!("{}", compare), format!("{}", stream));
compare_streams(compare, stream);
}

#[test]
fn test_mod() {
let attrs = proc_macro2::TokenStream::new();
let input = quote! {
#[cfg(test)]
#[serial]
mod serial_attr_tests {
pub fn foo() {
println!("Nothing");
}

#[test]
fn bar() {}
}
};
let stream = local_serial_core(
proc_macro2::TokenStream::from_iter(attrs.into_iter()),
input,
);
let compare = quote! {
#[cfg(test)]
mod serial_attr_tests {
pub fn foo() {
println!("Nothing");
}

#[test]
fn bar() {
serial_test::local_serial_core(vec![""], ::std::option::Option::None, || {} );
}
}
};
compare_streams(compare, stream);
}
}
10 changes: 10 additions & 0 deletions serial_test_test/src/lib.rs
Expand Up @@ -39,6 +39,8 @@
//! ```

use lazy_static::lazy_static;
#[cfg(test)]
use serial_test::{parallel, serial};
use std::{
convert::TryInto,
env, fs,
Expand Down Expand Up @@ -82,6 +84,14 @@ pub fn fs_test_fn(count: usize) {
assert_eq!(loaded, count);
}

#[cfg(test)]
#[serial]
mod serial_attr_tests {}

#[cfg(test)]
#[parallel]
mod parallel_attr_tests {}

#[cfg(test)]
mod tests {
use super::{init, test_fn};
Expand Down

0 comments on commit ae14645

Please sign in to comment.