--- /dev/null
+{
+ "git": {
+ "sha1": "b12f16cfe4e714805fdb00de6f15ecfc986ecfd8"
+ },
+ "path_in_vcs": "rstest_reuse"
+}
\ No newline at end of file
--- /dev/null
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "rstest_reuse"
+version = "0.5.0"
+authors = ["Michele d'Amico <michele.damico@gmail.com>"]
+description = """
+Reuse rstest attributes: create a set of tests and apply it
+to every scenario you want to test.
+"""
+homepage = "https://github.com/la10736/rstest"
+readme = "README.md"
+keywords = [
+ "test",
+ "fixture",
+]
+categories = ["development-tools::testing"]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/la10736/rstest"
+
+[lib]
+proc-macro = true
+
+[dependencies.quote]
+version = "1.0.9"
+
+[dependencies.rand]
+version = "0.8.5"
+
+[dependencies.syn]
+version = "1.0.72"
+features = ["full"]
+
+[dev-dependencies.lazy_static]
+version = "1.4.0"
+
+[dev-dependencies.rstest]
+version = "0.16.0"
+
+[dev-dependencies.rstest_test]
+version = "0.10.0"
+
+[dev-dependencies.temp_testdir]
+version = "0.2.3"
+
+[build-dependencies.rustc_version]
+version = "0.4.0"
--- /dev/null
+[package]
+authors = ["Michele d'Amico <michele.damico@gmail.com>"]
+categories = ["development-tools::testing"]
+description = """
+Reuse rstest attributes: create a set of tests and apply it
+to every scenario you want to test.
+"""
+edition = "2018"
+homepage = "https://github.com/la10736/rstest"
+keywords = ["test", "fixture"]
+license = "MIT/Apache-2.0"
+name = "rstest_reuse"
+readme = "README.md"
+repository = "https://github.com/la10736/rstest"
+version = "0.5.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[lib]
+proc-macro = true
+
+[dependencies]
+quote = "1.0.9"
+rand = "0.8.5"
+syn = {version = "1.0.72", features = ["full"]}
+
+[dev-dependencies]
+lazy_static = "1.4.0"
+rstest_test = {version = "0.10.0", path = "../rstest_test"}
+temp_testdir = "0.2.3"
+# To compile doc tests
+rstest = {version = "0.16.0"}
+
+[build-dependencies]
+rustc_version = "0.4.0"
--- /dev/null
+[![Crate][crate-image]][crate-link]
+[![Status][test-action-image]][test-action-link]
+[![Apache 2.0 Licensed][license-apache-image]][license-apache-link]
+[![MIT Licensed][license-mit-image]][license-mit-link]
+
+# Reuse `rstest`'s parametrized cases
+
+:warning: [**Version 0.5.0 introduce a breaking change**](#dismiss-macro_use-attribute-support)
+
+This crate give a way to define a tests set and apply them to every case you need to
+test. With `rstest` crate you can define a tests list but if you want to apply the same tests
+to another test function you must rewrite all cases or write some macros that do the job.
+
+Both solutions have some drawbreak:
+- intruduce duplication
+- macros makes code harder to read and shift out the focus from tests core
+
+The aim of this crate is solve this problem. `rstest_resuse` expose two attributes:
+- `#[template]`: to define a template
+- `#[apply]`: to apply a defined template to create tests
+
+Here is a simple example:
+
+```rust
+use rstest::rstest;
+use rstest_reuse::{self, *};
+// Here we define the template. This define
+// * The test list name to `two_simple_cases`
+// * cases: here two cases
+#[template]
+#[rstest]
+#[case(2, 2)]
+#[case(4/2, 2)]
+// Define a and b as cases arguments
+fn two_simple_cases(#[case] a: u32, #[case] b: u32) {}
+// Here we apply the `two_simple_cases` template: That is expanded in
+// #[template]
+// #[rstest]
+// #[case(2, 2)]
+// #[case(4/2, 2)]
+// fn it_works(#[case] a: u32,#[case] b: u32) {
+// assert!(a == b);
+// }
+#[apply(two_simple_cases)]
+fn it_works(a: u32, b: u32) {
+ assert!(a == b);
+}
+// Here we reuse the `two_simple_cases` template to create two
+// other tests
+#[apply(two_simple_cases)]
+fn it_fail(a: u32, b: u32) {
+ assert!(a != b);
+}
+```
+
+If we run `cargo test` we have:
+
+```text
+ Finished test [unoptimized + debuginfo] target(s) in 0.05s
+ Running target/debug/deps/playground-8a1212f8b5eb00ce
+running 4 tests
+test it_fail::case_1 ... FAILED
+test it_works::case_1 ... ok
+test it_works::case_2 ... ok
+test it_fail::case_2 ... FAILED
+failures:
+---- it_fail::case_1 stdout ----
+thread 'it_fail::case_1' panicked at 'assertion failed: a != b', src/main.rs:34:5
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+---- it_fail::case_2 stdout ----
+thread 'it_fail::case_2' panicked at 'assertion failed: a != b', src/main.rs:34:5
+failures:
+ it_fail::case_1
+ it_fail::case_2
+test result: FAILED. 2 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out
+error: test failed, to rerun pass '--bin playground'
+```
+
+Simple and neat!
+
+Note that if the test arguments names match the template's ones you can don't
+repeate the arguments attributes.
+
+## Composition and Values
+
+If you need to add some cases or values when apply a template you can leverage on
+composition. Here a simple example:
+
+```rust
+#[template]
+#[rstest]
+#[case(2, 2)]
+#[case(4/2, 2)]
+fn base(#[case] a: u32, #[case] b: u32) {}
+
+// Here we add a new case and an argument in a value list:
+#[apply(base)]
+#[case(9/3, 3)]
+fn it_works(a: u32, b: u32, #[values("a", "b")] t: &str) {
+ assert!(a == b);
+ assert!("abcd".contains(t))
+}
+```
+
+run 6 tests:
+
+```
+running 6 tests
+test it_works::case_1::t_2 ... ok
+test it_works::case_2::t_2 ... ok
+test it_works::case_2::t_1 ... ok
+test it_works::case_3::t_2 ... ok
+test it_works::case_3::t_1 ... ok
+test it_works::case_1::t_1 ... ok
+```
+
+Template can also used for values and with arguments if you need:
+
+```rust
+#[template]
+#[rstest]
+fn base(#[with(42)] fix: u32, #[values(1,2,3)] v: u32) {}
+
+#[fixture]
+fn fix(#[default(0)] inner: u32) -> u32 {
+ inner
+}
+
+#[apply(base)]
+fn use_it_with_fixture(fix: u32, v: u32) {
+ assert!(fix%v == 0);
+}
+
+#[apply(base)]
+fn use_it_without_fixture(v: u32) {
+ assert!(24 % v == 0);
+}
+```
+
+Run also 6 tests:
+
+```
+running 6 tests
+test use_it_with_fixture::v_1 ... ok
+test use_it_without_fixture::v_1 ... ok
+test use_it_with_fixture::v_3 ... ok
+test use_it_without_fixture::v_2 ... ok
+test use_it_without_fixture::v_3 ... ok
+test use_it_with_fixture::v_2 ... ok
+```
+
+## Cavelets
+
+### `use rstest_resuse` at the top of your crate
+You **should** add `use rstest_resuse` at the top of your crate:
+
+```rust
+#[cfg(test)]
+use rstest_reuse;
+```
+
+This is due `rstest_reuse::template` define a macro that need to call a `rstest_resuse`'s macro.
+I hope to remove this in the future but for now we should live with it.
+
+Note that
+
+```rust
+use rstest_reuse::*;
+```
+is not enougth: this statment doesn't include `rstest_reuse` but just its public items.
+
+## `#[export]` Attribute
+
+:warning: **Version 0.5.0 introduce a breaking change**
+
+Now `#[export]` attribute give you the possibility to export your template across crates
+but don't lift the macro definition at the top of your crate (that was the default behaviour
+prior the 0.5.0 version).
+
+Now if you want put your template at the root of your crate you can define it in the root
+module or reexport it at the top with something like the following line at the top of
+your crate:
+
+```rust
+pub use my::modules::path::of::my::template::my_template;
+```
+
+When you want to export your template you should also take care to declare `rstest_reuse` as `pub`
+at the top of your crate to enable to use it from the modules that would import the template.
+
+So in this case in the crate that would export template you should put at the root of your
+crate
+
+```rust
+#[cfg(test)]
+pub use rstest_reuse;
+```
+
+And not just `use rstest_reuse` like in the standard cases.
+
+## Dismiss `#[macro_use]` Attribute Support
+
+:warning: **Version 0.5.0 introduce a breaking change**
+
+Till version 0.4.0 you can use `#[macro_use]` to annotate your modules and lift your
+macro template to the up level. Now `rstest` leverege only on import and paths like all
+othter function and original macro is hidden by a random name.
+
+So now if you would use your template from other module you should import it like any
+other symbol.
+
+```rust
+mod inner {
+ pub(crate) mod sub {
+ use rstest_reuse::*;
+ #[template]
+ #[rstest(a, b,
+ case(2, 2),
+ case(4/2, 2),
+ )
+ ]
+ fn two_simple_cases(a: u32, b: u32) {}
+ }
+}
+use rstest_reuse::*;
+use rstest::*;
+
+#[apply(inner::sub::two_simple_cases)]
+fn it_works_by_path(a: u32, b: u32) {
+ assert!(a == b);
+}
+
+use inner::sub::two_simple_cases
+#[apply(inner::sub::two_simple_cases)]
+fn it_works_after_use(a: u32, b: u32) {
+ assert!(a == b);
+}
+
+```
+
+## Disclamer
+
+This crate is in a development stage. I don't know if I'll include it in `rstest` or change some syntax in the future.
+
+I did't test it in a lot of cases: if you have some cases where it doesn't works file a ticket on [`rstest`][rstest-link]
+
+
+## License
+
+Licensed under either of
+
+* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
+[license-apache-link])
+
+* MIT license [LICENSE-MIT](LICENSE-MIT) or [license-MIT-link]
+at your option.
+
+[//]: # (links)
+
+[crate-image]: https://img.shields.io/crates/v/rstest_reuse.svg
+[crate-link]: https://crates.io/crates/rstest_reuse
+[test-action-image]: https://github.com/la10736/rstest/workflows/Test/badge.svg
+[test-action-link]: https://github.com/la10736/rstest/actions?query=workflow:Test
+[license-apache-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg
+[license-mit-image]: https://img.shields.io/badge/license-MIT-blue.svg
+[license-apache-link]: http://www.apache.org/licenses/LICENSE-2.0
+[license-MIT-link]: http://opensource.org/licenses/MIT
+[rstest-link]: https://github.com/la10736/rstest
--- /dev/null
+use rustc_version::{Version, version};
+
+fn main() {
+ let ver = version().unwrap();
+ assert!(ver.major >= 1);
+
+ if ver <= Version::parse("1.50.0").unwrap() {
+ println!("cargo:rustc-cfg=sanitize_multiple_should_panic_compiler_bug");
+ }
+}
--- /dev/null
+# TODO list
+
+- [ ] Update rustup
+- [ ] Update dependency `cargo upgrade`
+- [ ] Run all test
+ - [ ] Stable: `RSTEST_TEST_CHANNEL=stable; cargo +${RSTEST_TEST_CHANNEL} test`
+ - [ ] Beta: `RSTEST_TEST_CHANNEL=beta; cargo +${RSTEST_TEST_CHANNEL} test`
+ - [ ] Nightly: `RSTEST_TEST_CHANNEL=nightly; cargo +${RSTEST_TEST_CHANNEL} test`
+- [ ] Check Cargo.toml version
+- [ ] Check README
+- [ ] prepare deploy `cargo publish --dry-run`
+- [ ] deploy `cargo publish`
+- [ ] Update `rstest` dependency
+- [ ] Change next version
+ - [ ] `Cargo.toml`
--- /dev/null
+//! # Reuse `rstest`'s parametrized cases
+//!
+//! This crate give a way to define a tests set and apply them to every case you need to
+//! test.
+//!
+//! With `rstest` crate you can define a tests list but if you want to apply the same tests
+//! to another test function you must rewrite all cases or write some macros that do the job.
+//! Both solutions have some drawbreak:
+//!
+//! - introduce duplication
+//! - macros makes code harder to read and shift out the focus from tests core
+//!
+//! The aim of this crate is solve this problem. `rstest_reuse` expose two attributes:
+//!
+//! - `#[template]`: to define a template
+//! - `#[apply]`: to apply a defined template to create tests
+//!
+//! Here is a simple example:
+//!
+//! ```
+//! use rstest::rstest;
+//! use rstest_reuse::{self, *};
+//!
+//! // Here we define the template. This define
+//! // * The test list name to `two_simple_cases`
+//! // * cases: here two cases that feed the `a`, `b` values
+//! #[template]
+//! #[rstest]
+//! #[case(2, 2)]
+//! #[case(4/2, 2)]
+//! fn two_simple_cases(#[case] a: u32,#[case] b: u32) {}
+//!
+//! // Here we apply the `two_simple_cases` template: That is expanded in
+//! // #[rstest]
+//! // #[case(2, 2)]
+//! // #[case(4/2, 2)]
+//! // fn it_works(#[case] a: u32,#[case] b: u32) {
+//! // assert!(a == b);
+//! // }
+//! #[apply(two_simple_cases)]
+//! fn it_works(a: u32, b: u32) {
+//! assert!(a == b);
+//! }
+//!
+//!
+//! // Here we reuse the `two_simple_cases` template to create two
+//! // other tests
+//! #[apply(two_simple_cases)]
+//! fn it_fail(a: u32, b: u32) {
+//! assert!(a != b);
+//! }
+//! ```
+//! If we run `cargo test` we have:
+//!
+//! ```text
+//! Finished test [unoptimized + debuginfo] target(s) in 0.05s
+//! Running target/debug/deps/playground-8a1212f8b5eb00ce
+//!
+//! running 4 tests
+//! test it_fail::case_1 ... FAILED
+//! test it_works::case_1 ... ok
+//! test it_works::case_2 ... ok
+//! test it_fail::case_2 ... FAILED
+//!
+//! failures:
+//!
+//! ---- it_fail::case_1 stdout ----
+//! thread 'it_fail::case_1' panicked at 'assertion failed: a != b', src/main.rs:34:5
+//! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+//!
+//! ---- it_fail::case_2 stdout ----
+//! thread 'it_fail::case_2' panicked at 'assertion failed: a != b', src/main.rs:34:5
+//!
+//!
+//! failures:
+//! it_fail::case_1
+//! it_fail::case_2
+//!
+//! test result: FAILED. 2 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out
+//!
+//! error: test failed, to rerun pass '--bin playground'
+//! ```
+//!
+//! Simple and neat!
+//!
+//! Note that if the test arguments names match the template's ones you can don't
+//! repeate the arguments attributes.
+//!
+//! ## Composition and Values
+//!
+//! If you need to add some cases or values when apply a template you can leverage on
+//! composition. Here a simple example:
+//!
+//! ```
+//! use rstest::rstest;
+//! use rstest_reuse::{self, *};
+//!
+//! #[template]
+//! #[rstest]
+//! #[case(2, 2)]
+//! #[case(4/2, 2)]
+//! fn base(#[case] a: u32, #[case] b: u32) {}
+//!
+//! // Here we add a new case and an argument in a value list:
+//! #[apply(base)]
+//! #[case(9/3, 3)]
+//! fn it_works(a: u32, b: u32, #[values("a", "b")] t: &str) {
+//! assert!(a == b);
+//! assert!("abcd".contains(t))
+//! }
+//! ```
+//!
+//! `cargo test` runs 6 tests:
+//!
+//! ```text
+//! running 6 tests
+//! test it_works::case_1::t_2 ... ok
+//! test it_works::case_2::t_2 ... ok
+//! test it_works::case_2::t_1 ... ok
+//! test it_works::case_3::t_2 ... ok
+//! test it_works::case_3::t_1 ... ok
+//! test it_works::case_1::t_1 ... ok
+//! ```
+//!
+//! Template can also used for `#[values]` and `#[with]` arguments if you need:
+//!
+//! ```
+//! use rstest::*;
+//! use rstest_reuse::{self, *};
+//!
+//! #[template]
+//! #[rstest]
+//! fn base(#[with(42)] fix: u32, #[values(1,2,3)] v: u32) {}
+//!
+//! #[fixture]
+//! fn fix(#[default(0)] inner: u32) -> u32 {
+//! inner
+//! }
+//!
+//! #[apply(base)]
+//! fn use_it_with_fixture(fix: u32, v: u32) {
+//! assert!(fix%v == 0);
+//! }
+//!
+//! #[apply(base)]
+//! fn use_it_without_fixture(v: u32) {
+//! assert!(24 % v == 0);
+//! }
+//! ```
+//!
+//! `cargo test` runs 6 tests:
+//!
+//! ```text
+//! running 6 tests
+//! test use_it_with_fixture::v_1 ... ok
+//! test use_it_without_fixture::v_1 ... ok
+//! test use_it_with_fixture::v_3 ... ok
+//! test use_it_without_fixture::v_2 ... ok
+//! test use_it_without_fixture::v_3 ... ok
+//! test use_it_with_fixture::v_2 ... ok
+//! ```
+//!
+//!
+//! ## Cavelets
+//!
+//! ### `use rstest_reuse` at the top of your crate
+//!
+//! You **should** add `use rstest_reuse` at the top of your crate:
+//!
+//! ```
+//! #[cfg(test)]
+//! use rstest_reuse;
+//! ```
+//!
+//! This is due `rstest_reuse::template` define a macro that need to call a `rstest_reuse`'s macro.
+//! I hope to remove this in the future but for now we should live with it.
+//!
+//! Note that
+//! ```
+//! use rstest_reuse::*;
+//! ```
+//! is not enougth: this statment doesn't include `rstest_reuse` but just its public items.
+//!
+//! ## Disclamer
+//!
+//! This crate is in developer stage. I don't know if I'll include it in `rstest` or changing some syntax in
+//! the future.
+//!
+//! I did't test it in a lot of cases: if you have some cases where it doesn't works file a ticket on
+//! [`rstest`](https://github.com/la10736/rstest)
+
+extern crate proc_macro;
+use std::collections::HashMap;
+
+use proc_macro::TokenStream;
+use quote::{format_ident, quote};
+use syn::{
+ self, parse, parse::Parse, parse_macro_input, Attribute, Ident, ItemFn, PatType, Path, Token,
+};
+
+struct MergeAttrs {
+ template: ItemFn,
+ function: ItemFn,
+}
+
+impl Parse for MergeAttrs {
+ fn parse(input: parse::ParseStream) -> syn::Result<Self> {
+ let template = input.parse()?;
+ let _comma: Token![,] = input.parse()?;
+ let function = input.parse()?;
+ Ok(Self { template, function })
+ }
+}
+
+#[cfg(sanitize_multiple_should_panic_compiler_bug)]
+fn is_should_panic(attr: &syn::Attribute) -> bool {
+ let should_panic: Ident = syn::parse_str("should_panic").unwrap();
+ attr.path.is_ident(&should_panic)
+}
+
+#[cfg(sanitize_multiple_should_panic_compiler_bug)]
+fn sanitize_should_panic_duplication_bug(
+ mut attributes: Vec<syn::Attribute>,
+) -> Vec<syn::Attribute> {
+ if attributes.len() != 2 || attributes[0] != attributes[1] || !is_should_panic(&attributes[0]) {
+ // Nothing to do
+ return attributes;
+ }
+ attributes.pop();
+ attributes
+}
+
+fn collect_template_args(template: &ItemFn) -> HashMap<&Ident, &PatType> {
+ template
+ .sig
+ .inputs
+ .iter()
+ .filter_map(|arg| match arg {
+ syn::FnArg::Typed(a) => Some(a),
+ _ => None,
+ })
+ .filter_map(|arg| match *arg.pat {
+ syn::Pat::Ident(ref id) => Some((&id.ident, arg)),
+ _ => None,
+ })
+ .collect()
+}
+
+fn merge_arg_attributes(dest: &mut Vec<Attribute>, source: &[Attribute]) {
+ for s in source.iter() {
+ if !dest.contains(s) {
+ dest.push(s.clone())
+ }
+ }
+}
+
+fn resolve_template_arg<'a>(
+ template: &HashMap<&'a Ident, &'a PatType>,
+ arg: &Ident,
+) -> Option<&'a PatType> {
+ let id_name = arg.to_string();
+ match (template.get(arg), id_name.starts_with('_')) {
+ (Some(&arg), _) => Some(arg),
+ (None, true) => template.get(&format_ident!("{}", id_name[1..])).copied(),
+ _ => None,
+ }
+}
+
+fn expand_function_arguments(dest: &mut ItemFn, source: &ItemFn) {
+ let to_merge_args = collect_template_args(source);
+
+ for arg in dest.sig.inputs.iter_mut() {
+ if let syn::FnArg::Typed(a) = arg {
+ if let syn::Pat::Ident(ref id) = *a.pat {
+ if let Some(source_arg) = resolve_template_arg(&to_merge_args, &id.ident) {
+ merge_arg_attributes(&mut a.attrs, &source_arg.attrs);
+ }
+ }
+ }
+ }
+}
+
+#[doc(hidden)]
+#[proc_macro]
+pub fn merge_attrs(item: TokenStream) -> TokenStream {
+ let MergeAttrs {
+ template,
+ mut function,
+ } = parse_macro_input!(item as MergeAttrs);
+
+ expand_function_arguments(&mut function, &template);
+
+ let mut attrs = template.attrs;
+ #[cfg(sanitize_multiple_should_panic_compiler_bug)]
+ {
+ function.attrs = sanitize_should_panic_duplication_bug(function.attrs);
+ }
+ attrs.append(&mut function.attrs);
+ function.attrs = attrs;
+
+ let tokens = quote! {
+ #function
+ };
+ tokens.into()
+}
+
+fn get_export(attributes: &[Attribute]) -> Option<&Attribute> {
+ attributes
+ .iter()
+ .find(|&attr| attr.path.is_ident(&format_ident!("export")))
+}
+
+/// Define a template where the name is given from the function name. This attribute register all
+/// attributes. The function signature don't really mater but to make it clear is better that you
+/// use a signature like if you're wrinting a standard `rstest`.
+///
+/// If you need to export the template at the root of your crate or use it from another crate you
+/// should annotate it with `#[export]` attribute. This attribute add `#[macro_export]` attribute to
+/// the template macro and make possible to use it from another crate.
+///
+/// When define a template you can also set the arguments attributes like `#[case]`, `#[values]`
+/// and `#[with]`: when you apply it attributes will be copied to the matched by name arguments.
+///
+#[proc_macro_attribute]
+pub fn template(_args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> TokenStream {
+ let mut template: ItemFn = parse(input).unwrap();
+
+ let rstest_index = template
+ .attrs
+ .iter()
+ .position(|attr| attr.path.is_ident(&format_ident!("rstest")));
+
+ let mut attributes = template.attrs;
+
+ template.attrs = match rstest_index {
+ Some(idx) => attributes.split_off(idx),
+ None => std::mem::take(&mut attributes),
+ };
+
+ let (macro_attribute, visibility) = match get_export(&attributes) {
+ Some(_) => (
+ quote! {
+ #[macro_export]
+ },
+ quote! {
+ pub
+ },
+ ),
+ None => (
+ quote! {},
+ quote! {
+ pub(crate)
+ },
+ ),
+ };
+
+ let macro_name = template.sig.ident.clone();
+ let macro_name_rand = format_ident!("{}_{}", macro_name, rand::random::<u64>());
+
+ let tokens = quote! {
+ /// Apply #macro_name template to given body
+ #macro_attribute
+ macro_rules! #macro_name_rand {
+ ( $test:item ) => {
+ $crate::rstest_reuse::merge_attrs! {
+ #template,
+ $test
+ }
+ }
+ }
+ #[allow(unused_imports)]
+ #visibility use #macro_name_rand as #macro_name;
+ };
+ tokens.into()
+}
+
+/// Apply a defined template. The function signature should satisfy the template attributes
+/// but can also add some other fixtures.
+/// Example:
+///
+/// ```
+/// use rstest::{rstest, fixture};
+/// use rstest_reuse::{self, *};
+///
+/// #[fixture]
+/// fn empty () -> Vec<u32> {
+/// Vec::new()
+/// }
+///
+/// #[template]
+/// #[rstest]
+/// #[case(2, 2)]
+/// #[case(4/2, 2)]
+/// fn two_simple_cases(#[case] a: u32, #[case] b: u32) {}
+///
+/// #[apply(two_simple_cases)]
+/// fn it_works(mut empty: Vec<u32>, a: u32, b: u32) {
+/// empty.append(a);
+/// assert!(empty.last() == b);
+/// }
+/// ```
+/// When use `#[apply]` you can also
+/// 1. Use a path for template
+/// 2. Ignore an argument by underscore
+/// 3. add some cases
+/// 4. add some values
+///
+///
+/// ```
+/// use rstest::{rstest, fixture};
+/// use rstest_reuse::{self, *};
+///
+/// #[fixture]
+/// fn fix (#[default(0)] inner: u32) -> u32 {
+/// inner
+/// }
+///
+/// mod outer {
+/// pub(crate) mod inner {
+/// use rstest_reuse::template;
+///
+/// #[template]
+/// #[rstest]
+/// #[case(2, 2)]
+/// #[case(4/2, 2)]
+/// fn two_simple_cases(#[case] a: u32, #[case] b: u32) {}
+/// }
+/// }
+///
+///
+/// #[apply(outer::inner::two_simple_cases)]
+/// // Add a case
+/// #[case(9/3, 3)]
+/// // Use fixture with 42 as argument
+/// // Ignore b case values
+/// // add 2 cases with other in 4, 5 for each case
+/// fn lot_of_tests(fix: u32, a: u32, _b: u32, #[values(4, 5)] other: u32) {
+/// assert_eq!(fix, 42);
+/// assert_eq!(a, 2);
+/// assert!([4, 5].contains(other));
+/// }
+/// ```
+///
+
+#[proc_macro_attribute]
+pub fn apply(args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> TokenStream {
+ let template: Path = parse(args).unwrap();
+ let test: ItemFn = parse(input).unwrap();
+ let tokens = quote! {
+ #template! {
+ #test
+ }
+ };
+ tokens.into()
+}
--- /dev/null
+use rstest_test::{
+ assert_in, assert_not_in, sanitize_name, testname, Project, Stringable, TestResults,
+};
+
+use lazy_static::lazy_static;
+
+use rstest::rstest;
+use std::path::{Path, PathBuf};
+use temp_testdir::TempDir;
+
+pub fn resources<O: AsRef<Path>>(name: O) -> PathBuf {
+ Path::new("tests").join("resources").join(name)
+}
+
+fn create_prj(name: &str) -> Project {
+ let prj = ROOT_PROJECT.subproject(name);
+ prj.add_local_dependency("rstest_reuse");
+ prj.add_dependency("rstest", r#""*""#);
+ prj
+}
+
+fn prj(res: impl AsRef<Path>) -> Project {
+ let prj_name = sanitize_name(testname());
+
+ let prj = create_prj(&prj_name);
+ prj.set_code_file(resources(res))
+}
+
+fn run_test(res: impl AsRef<Path>) -> (std::process::Output, String) {
+ let prj = prj(res);
+ (
+ prj.run_tests().unwrap(),
+ prj.get_name().to_owned().to_string(),
+ )
+}
+
+#[test]
+fn simple_example() {
+ let (output, _) = run_test("simple_example.rs");
+
+ TestResults::new()
+ .ok("it_works::case_1")
+ .ok("it_works::case_2")
+ .fail("it_fail::case_1")
+ .fail("it_fail::case_2")
+ .ok("it_fail_but_ok::case_1")
+ .ok("it_fail_but_ok::case_2")
+ .assert(output);
+}
+
+#[test]
+fn use_before_define() {
+ let (output, _) = run_test("use_before_define.rs");
+
+ TestResults::new()
+ .ok("it_works::case_1")
+ .ok("it_works::case_2")
+ .assert(output);
+}
+
+#[rstest]
+#[case::simple("simple_example.rs")]
+#[case::export_not_used("export_not_used.rs")]
+fn not_show_any_warning(#[case] path: &str) {
+ let (output, _) = run_test(path);
+
+ assert_not_in!(output.stderr.str(), "warning:");
+}
+
+#[test]
+fn should_show_warning_if_not_used_template() {
+ let (output, _) = run_test("not_used.rs");
+
+ assert_in!(output.stderr.str(), "warning:");
+}
+
+#[test]
+fn in_mod() {
+ let (output, _) = run_test("in_mod.rs");
+
+ TestResults::new()
+ .ok("sub::it_works::case_1")
+ .ok("sub::it_works::case_2")
+ .fail("sub::it_fail::case_1")
+ .fail("sub::it_fail::case_2")
+ .assert(output);
+}
+
+#[test]
+fn import_from_mod() {
+ let (output, _) = run_test("qualify_template_use.rs");
+
+ TestResults::new()
+ .ok("user::it_works::case_1")
+ .ok("user::it_works::case_2")
+ .ok("qualify::it_works::case_1")
+ .ok("qualify::it_works::case_2")
+ .assert(output);
+}
+
+#[test]
+fn copy_case_attributes_from_template() {
+ let (output, _) = run_test("copy_args_attributes_from_template.rs");
+
+ TestResults::new()
+ .ok("cases::it_works::case_1")
+ .ok("cases::it_works::case_2")
+ .ok("cases::should_not_copy_attributes_if_already_present::case_1")
+ .ok("cases::should_not_copy_attributes_if_already_present::case_2")
+ .ok("cases::add_a_case::case_1")
+ .ok("cases::add_a_case::case_2")
+ .ok("cases::add_a_case::case_3_more")
+ .ok_in("cases::add_values::case_1::_add_some_tests_1")
+ .ok_in("cases::add_values::case_1::_add_some_tests_2")
+ .ok_in("cases::add_values::case_1::_add_some_tests_3")
+ .ok_in("cases::add_values::case_2::_add_some_tests_1")
+ .ok_in("cases::add_values::case_2::_add_some_tests_2")
+ .ok_in("cases::add_values::case_2::_add_some_tests_3")
+ .ok("cases::should_copy_cases_also_from_underscored_attrs::case_1")
+ .ok("cases::should_copy_cases_also_from_underscored_attrs::case_2")
+ .ok_in("values::it_works::cases_1")
+ .ok_in("values::it_works::cases_2")
+ .ok_in("values::add_a_case::case_1_more::cases_1")
+ .ok_in("values::add_a_case::case_1_more::cases_2")
+ .ok_with("values::add_values::a_1", false, 2)
+ .ok_with("values::add_values::a_2", false, 2)
+ .ok_in("values::should_copy_values_also_from_underscored_attrs::_cases_1")
+ .ok_in("values::should_copy_values_also_from_underscored_attrs::_cases_2")
+ .assert(output);
+}
+
+#[test]
+fn deny_docs() {
+ let (output, _) = run_test("deny_docs.rs");
+
+ TestResults::new()
+ .ok("it_works::case_1")
+ .ok("it_works::case_2")
+ .assert(output);
+}
+
+#[test]
+fn enable_export_macros() {
+ let (output, _) = run_test("export_template.rs");
+
+ TestResults::new()
+ .ok("foo::bar::test::case_1")
+ .ok("test_path::case_1")
+ .ok("test_import::case_1")
+ .assert(output);
+}
+
+#[test]
+fn use_same_name_for_more_templates() {
+ let (output, _) = run_test("templates_with_same_name.rs");
+
+ TestResults::new()
+ .ok("inner1::it_works::case_1")
+ .ok("inner1::it_works::case_2")
+ .ok("inner2::it_works::case_1")
+ .ok("inner2::it_works::case_2")
+ .assert(output);
+}
+
+#[test]
+fn no_local_macro_should_not_compile() {
+ let (output, _) = run_test("no_local_macro_should_not_compile.rs");
+
+ assert!(!output.status.success());
+}
+
+#[test]
+fn should_export_main_root() {
+ // Add project with template
+ let _prj_template =
+ create_prj("export_template_root").set_code_file(resources("export_template_root.rs"));
+
+ // Main test project that use template
+ let prj = prj("import_template.rs");
+ prj.add_path_dependency("export_template_root", "../export_template_root");
+
+ let output = prj.run_tests().unwrap();
+ TestResults::new().ok("test::case_1").assert(output);
+}
+
+lazy_static! {
+ static ref ROOT_DIR: TempDir = TempDir::new(std::env::temp_dir().join("rstest_reuse"), false);
+ static ref ROOT_PROJECT: Project = Project::new(ROOT_DIR.as_ref());
+}
--- /dev/null
+use rstest_reuse;
+
+mod cases {
+ use rstest::rstest;
+ use rstest_reuse::*;
+
+ #[template]
+ #[rstest]
+ #[case(2, 2)]
+ #[case(4/2, 2)]
+ fn copy_cases(#[case] a: u32, #[case] b: u32) {}
+
+ #[apply(copy_cases)]
+ fn it_works(a: u32, b: u32) {
+ assert!(a == b);
+ }
+
+ #[apply(copy_cases)]
+ fn should_not_copy_attributes_if_already_present(#[case] a: u32, b: u32) {
+ assert!(a == b);
+ }
+
+ #[apply(copy_cases)]
+ #[case::more(8/4, 2)]
+ fn add_a_case(a: u32, b: u32) {
+ assert!(a == b);
+ }
+
+ #[apply(copy_cases)]
+ fn add_values(a: u32, b: u32, #[values(1, 2, 3)] _add_some_tests: u32) {
+ assert!(a == b);
+ }
+
+ #[apply(copy_cases)]
+ fn should_copy_cases_also_from_underscored_attrs(_a: u32, _b: u32) {}
+}
+
+mod values {
+ use rstest::rstest;
+ use rstest_reuse::*;
+
+ #[template]
+ #[rstest]
+ fn copy_values(#[values(1, 2)] cases: u32) {}
+
+ #[apply(copy_values)]
+ fn it_works(cases: u32) {
+ assert!([1, 2].contains(&cases));
+ }
+
+ #[apply(copy_values)]
+ #[case::more(8/4, 2)]
+ fn add_a_case(#[case] a: u32, #[case] b: u32, cases: u32) {
+ assert!([1, 2].contains(&cases));
+ assert!(a == b);
+ }
+
+ #[apply(copy_values)]
+ fn add_values(#[values(3, 4)] a: u32, cases: u32) {
+ assert!([1, 2].contains(&cases));
+ assert!([3, 4].contains(&a));
+ }
+
+ #[apply(copy_values)]
+ fn should_copy_values_also_from_underscored_attrs(_cases: u32) {}
+}
--- /dev/null
+#![deny(missing_docs)]
+
+use rstest::rstest;
+use rstest_reuse::{self, *};
+
+#[template]
+#[rstest(a, b, case(2, 2), case(4/2, 2))]
+fn two_simple_cases(a: u32, b: u32) {}
+
+#[apply(two_simple_cases)]
+fn it_works(a: u32, b: u32) {
+ assert!(a == b);
+}
--- /dev/null
+mod foo {
+ use rstest_reuse::template;
+
+ #[template]
+ #[export]
+ #[rstest]
+ #[case("bar")]
+ fn not_used(#[case] s: &str) {}
+}
--- /dev/null
+use rstest_reuse;
+
+mod foo {
+ pub(crate) mod bar {
+ use rstest::rstest;
+ use rstest_reuse::{self, *};
+
+ #[template]
+ #[export]
+ #[rstest]
+ #[case("bar")]
+ fn my_template(#[case] s: &str) {}
+
+ #[apply(my_template)]
+ fn test(#[case] s: &str) {
+ assert_eq!("bar", s);
+ }
+ }
+}
+
+use rstest::rstest;
+use rstest_reuse::*;
+
+#[apply(foo::bar::my_template)]
+fn test_path(#[case] s: &str) {
+ assert_eq!("bar", s);
+}
+
+use foo::bar::my_template;
+#[apply(my_template)]
+fn test_import(#[case] s: &str) {
+ assert_eq!("bar", s);
+}
+
+#[template]
+#[export]
+#[rstest]
+#[case("bar")]
+fn root_level(#[case] s: &str) {}
--- /dev/null
+pub use rstest_reuse;
+use rstest_reuse::template;
+
+#[template]
+#[export]
+#[rstest]
+#[case("bar")]
+fn root_level(#[case] s: &str) {}
--- /dev/null
+use export_template_root::root_level;
+use rstest::*;
+use rstest_reuse::apply;
+
+#[apply(root_level)]
+fn test(#[case] s: &str) {
+ assert_eq!("bar", s);
+}
--- /dev/null
+use rstest_reuse;
+
+mod sub {
+ use rstest::rstest;
+ use rstest_reuse::*;
+
+ #[template]
+ #[rstest(a, b, case(2, 2), case(4/2, 2))]
+ fn two_simple_cases(a: u32, b: u32) {}
+
+ #[apply(two_simple_cases)]
+ fn it_works(a: u32, b: u32) {
+ assert!(a == b);
+ }
+
+ #[apply(two_simple_cases)]
+ fn it_fail(a: u32, b: u32) {
+ assert!(a != b);
+ }
+}
--- /dev/null
+use rstest_reuse;
+
+mod foo {
+ use rstest_reuse::{self, *};
+
+ #[template]
+ #[rstest]
+ #[case("bar")]
+ fn my_template(#[case] s: &str) {}
+}
+use rstest::rstest;
+use rstest_reuse::apply;
+
+#[apply(my_template)]
+fn test(#[case] s: &str) {
+ assert_eq!("bar", s);
+}
--- /dev/null
+mod foo {
+ use rstest_reuse::template;
+
+ #[template]
+ #[rstest]
+ #[case("bar")]
+ fn not_used(#[case] s: &str) {}
+}
--- /dev/null
+use rstest_reuse;
+
+mod template {
+ use rstest::rstest;
+ use rstest_reuse::template;
+
+ #[template]
+ #[rstest(a, b, case(2, 2), case(4/2, 2))]
+ fn two_simple_cases(a: u32, b: u32) {}
+}
+
+mod user {
+ use rstest::rstest;
+ use rstest_reuse::apply;
+ use crate::template::two_simple_cases;
+
+ #[apply(two_simple_cases)]
+ fn it_works(a: u32, b: u32) {
+ assert!(a == b);
+ }
+}
+
+mod qualify {
+ use rstest::rstest;
+ use rstest_reuse::apply;
+
+ #[apply(crate::template::two_simple_cases)]
+ fn it_works(a: u32, b: u32) {
+ assert!(a == b);
+ }
+}
--- /dev/null
+use rstest::rstest;
+use rstest_reuse::{self, *};
+
+#[template]
+#[rstest(a, b, case(2, 2), case(4/2, 2))]
+fn two_simple_cases(a: u32, b: u32) {}
+
+#[apply(two_simple_cases)]
+fn it_works(a: u32, b: u32) {
+ assert!(a == b);
+}
+
+#[apply(two_simple_cases)]
+fn it_fail(a: u32, b: u32) {
+ assert!(a != b);
+}
+
+#[apply(two_simple_cases)]
+#[should_panic]
+fn it_fail_but_ok(a: u32, b: u32) {
+ assert!(a != b);
+}
--- /dev/null
+use rstest_reuse;
+
+mod inner1 {
+ use rstest::rstest;
+ use rstest_reuse::*;
+
+ #[template]
+ #[rstest(a, b, case(2, 2), case(4/2, 2))]
+ fn my_template(a: u32, b: u32) {}
+
+ #[apply(my_template)]
+ fn it_works(a: u32, b: u32) {
+ assert!(a == b);
+ }
+}
+
+mod inner2 {
+ use rstest::rstest;
+ use rstest_reuse::*;
+
+ #[template]
+ #[rstest(a, case(2), case(4))]
+ fn my_template(a: u32) {}
+
+ #[apply(my_template)]
+ fn it_works(a: u32) {}
+}
--- /dev/null
+use rstest::rstest;
+use rstest_reuse::{self, *};
+
+#[apply(two_simple_cases)]
+fn it_works(a: u32, b: u32) {
+ assert!(a == b);
+}
+
+#[template]
+#[rstest(a, b, case(2, 2), case(4/2, 2))]
+fn two_simple_cases(a: u32, b: u32) {}