test_helpers_async/
lib.rs

1//
2//! Some setup and teardown macro helpers to mimic [Jest's setup and teardown](https://jestjs.io/docs/setup-teardown)
3//! functionality. Also includes a `skip` macro that mimics the [skip](https://jestjs.io/docs/api#testskipname-fn)
4//! functionality in Jest.
5//!
6//! There are currently five macros provided: `after_all`,
7//! `after_each`, `before_all`, `before_each`, and `skip`. I would like to implement `only` to
8//! match [Jest's only](https://jestjs.io/docs/api#testonlyname-fn-timeout) functionality. I'm
9//! unsure of a great way to do that currently, however.
10//!
11//! ## Getting Started
12//! Using these macros is fairly simple. The four after/before functions all require a function
13//! with the same name as the attribute and are only valid when applied to a mod. They are all used
14//! like in the below example. Replace `before_each` with whichever method you want to use. The
15//! code in the matching function will be inserted into every fn in the containing mod that has an
16//! attribute with the word "test" in it. This is to allow for use with not just normal `#[test]`
17//! attributes, but also other flavors like `#[tokio::test]` and `#[test_case(0)]`.
18//! ```
19//! #[cfg(test)]
20//! use test_env_helpers::*;
21//!
22//! #[before_each]
23//! #[cfg(test)]
24//! mod my_tests{
25//!     fn before_each(){println!("I'm in every test!")}
26//!     #[test]
27//!     fn test_1(){}
28//!     #[test]
29//!     fn test_2(){}
30//!     #[test]
31//!     fn test_3(){}
32//! }
33//! ```
34//!
35//! The `skip` macro is valid on either a mod or an individual test and will remove the mod or test
36//! it is applied to. You can use it to skip tests that aren't working correctly or that you don't
37//! want to run for some reason.
38//!
39//! ```
40//! #[cfg(test)]
41//! use test_env_helpers::*;
42//!
43//! #[cfg(test)]
44//! mod my_tests{
45//!     #[skip]
46//!     #[test]
47//!     fn broken_test(){panic!("I'm hella broke")}
48//!     #[skip]
49//!     mod broken_mod{
50//!         #[test]
51//!         fn i_will_not_be_run(){panic!("I get skipped too")}
52//!     }
53//!     #[test]
54//!     fn test_2(){}
55//!     #[test]
56//!     fn test_3(){}
57//! }
58//! ```
59
60extern crate proc_macro;
61mod utils;
62
63use crate::utils::traverse_use_item;
64
65use proc_macro::TokenStream;
66use quote::quote;
67use syn::Stmt;
68use syn::Item;
69use syn::parse_quote;
70use syn::parse_macro_input;
71
72/// Will run the code in the matching `after_all` function exactly once when all of the tests have
73/// run. This works by counting the number of `#[test]` attributes and decrementing a counter at
74/// the beginning of every test. Once the counter reaches 0, it will run the code in `after_all`.
75/// It uses [std::sync::Once](https://doc.rust-lang.org/std/sync/struct.Once.html) internally
76/// to ensure that the code is run at maximum one time.
77///
78/// ```
79/// #[cfg(test)]
80/// use test_env_helpers::*;
81///
82/// #[after_all]
83/// #[cfg(test)]
84/// mod my_tests{
85///     fn after_all(){println!("I only get run once at the very end")}
86///     #[test]
87///     fn test_1(){}
88///     #[test]
89///     fn test_2(){}
90///     #[test]
91///     fn test_3(){}
92/// }
93/// ```
94#[proc_macro_attribute]
95pub fn after_all_async(_metadata: TokenStream, input: TokenStream) -> TokenStream {
96    let input: Item = match parse_macro_input!(input as Item) {
97        Item::Mod(mut m) => {
98            let (brace, items) = m.content.unwrap();
99            let (after_all_fn, everything_else): (Vec<Item>, Vec<Item>) =
100                items.into_iter().partition(|t| match t {
101                    Item::Fn(f) => f.sig.ident == "after_all_async",
102                    _ => false,
103                });
104            let after_all_fn_block = if after_all_fn.len() != 1 {
105                panic!("The `after_all` macro attribute requires a single function named `after_all` in the body of the module it is called on.")
106            } else {
107                match after_all_fn.into_iter().next().unwrap() {
108                    Item::Fn(f) => f.block,
109                    _ => unreachable!(),
110                }
111            };
112            let after_all_if: Stmt = parse_quote! {
113                if REMAINING_TESTS.fetch_sub(1, Ordering::SeqCst) == 1 {
114                    AFTER_ALL.call_once(|| {
115                        #after_all_fn_block
116                    });
117                }
118            };
119            let resume: Stmt = parse_quote! {
120                if let Err(err) = result {
121                    panic::resume_unwind(err);
122                }
123            };
124            let mut count: usize = 0;
125            let mut has_once: bool = false;
126            let mut has_atomic_usize: bool = false;
127            let mut has_ordering: bool = false;
128            let mut has_panic: bool = false;
129
130            let mut e: Vec<Item> = everything_else
131                .into_iter()
132                .map(|t| match t {
133                    Item::Fn(mut f) => {
134                        let test_count = f
135                            .attrs
136                            .iter()
137                            .filter(|attr| {
138                                attr.path
139                                    .segments
140                                    .iter()
141                                    .any(|segment| segment.ident.to_string().contains("test"))
142                            })
143                            .count();
144                        if test_count > 0 {
145                            count += test_count;
146                            f.block.stmts.push(after_all_if.clone());
147                            Item::Fn(f)
148                        } else {
149                            Item::Fn(f)
150                        }
151                    }
152                    Item::Use(use_stmt) => {
153                        if traverse_use_item(&use_stmt.tree, vec!["std", "sync", "Once"]).is_some()
154                        {
155                            has_once = true;
156                        }
157                        if traverse_use_item(
158                            &use_stmt.tree,
159                            vec!["std", "sync", "atomic", "AtomicUsize"],
160                        )
161                        .is_some()
162                        {
163                            has_atomic_usize = true;
164                        }
165                        if traverse_use_item(
166                            &use_stmt.tree,
167                            vec!["std", "sync", "atomic", "Ordering"],
168                        )
169                        .is_some()
170                        {
171                            has_ordering = true;
172                        }
173                        if traverse_use_item(
174                            &use_stmt.tree,
175                            vec!["std", "panic"],
176                        )
177                        .is_some() {
178                            has_panic = true;
179                        }
180                        Item::Use(use_stmt)
181                    }
182                    el => el,
183                })
184                .collect();
185
186            let use_once: Item = parse_quote!(
187                use std::sync::Once;
188            );
189            let use_atomic_usize: Item = parse_quote!(
190                use std::sync::atomic::AtomicUsize;
191            );
192            let use_ordering: Item = parse_quote!(
193                use std::sync::atomic::Ordering;
194            );
195            let use_panic: Item = parse_quote!(
196                use std::panic;
197            );
198            let static_once: Item = parse_quote!(
199                static AFTER_ALL: Once = Once::new();
200            );
201            let static_count: Item = parse_quote!(
202                static REMAINING_TESTS: AtomicUsize = AtomicUsize::new(#count);
203            );
204
205            let mut once_content = vec![];
206
207            if !has_once {
208                once_content.push(use_once);
209            }
210            if !has_atomic_usize {
211                once_content.push(use_atomic_usize);
212            }
213            if !has_ordering {
214                once_content.push(use_ordering);
215            }
216            if !has_panic {
217                once_content.push(use_panic);
218            }
219            once_content.append(&mut vec![static_once, static_count]);
220            once_content.append(&mut e);
221
222            m.content = Some((brace, once_content));
223            Item::Mod(m)
224        }
225        _ => {
226            panic!("The `after_all` macro attribute is only valid when called on a module.")
227        }
228    };
229    TokenStream::from(quote! (#input))
230}
231
232
233/// Will run the code in the matching `after_all` function exactly once when all of the tests have
234/// run. This works by counting the number of `#[test]` attributes and decrementing a counter at
235/// the beginning of every test. Once the counter reaches 0, it will run the code in `after_all`.
236/// It uses [std::sync::Once](https://doc.rust-lang.org/std/sync/struct.Once.html) internally
237/// to ensure that the code is run at maximum one time.
238///
239/// ```
240/// #[cfg(test)]
241/// use test_env_helpers::*;
242///
243/// #[after_all]
244/// #[cfg(test)]
245/// mod my_tests{
246///     fn after_all(){println!("I only get run once at the very end")}
247///     #[test]
248///     fn test_1(){}
249///     #[test]
250///     fn test_2(){}
251///     #[test]
252///     fn test_3(){}
253/// }
254/// ```
255#[proc_macro_attribute]
256pub fn after_all(_metadata: TokenStream, input: TokenStream) -> TokenStream {
257    let input: Item = match parse_macro_input!(input as Item) {
258        Item::Mod(mut m) => {
259            let (brace, items) = m.content.unwrap();
260            let (after_all_fn, everything_else): (Vec<Item>, Vec<Item>) =
261                items.into_iter().partition(|t| match t {
262                    Item::Fn(f) => f.sig.ident == "after_all",
263                    _ => false,
264                });
265            let after_all_fn_block = if after_all_fn.len() != 1 {
266                panic!("The `after_all` macro attribute requires a single function named `after_all` in the body of the module it is called on.")
267            } else {
268                match after_all_fn.into_iter().next().unwrap() {
269                    Item::Fn(f) => f.block,
270                    _ => unreachable!(),
271                }
272            };
273            let after_all_if: Stmt = parse_quote! {
274                if REMAINING_TESTS.fetch_sub(1, Ordering::SeqCst) == 1 {
275                    AFTER_ALL.call_once(|| {
276                        #after_all_fn_block
277                    });
278                }
279            };
280            let resume: Stmt = parse_quote! {
281                if let Err(err) = result {
282                    panic::resume_unwind(err);
283                }
284            };
285            let mut count: usize = 0;
286            let mut has_once: bool = false;
287            let mut has_atomic_usize: bool = false;
288            let mut has_ordering: bool = false;
289            let mut has_panic: bool = false;
290
291            let mut e: Vec<Item> = everything_else
292                .into_iter()
293                .map(|t| match t {
294                    Item::Fn(mut f) => {
295                        let test_count = f
296                            .attrs
297                            .iter()
298                            .filter(|attr| {
299                                attr.path
300                                    .segments
301                                    .iter()
302                                    .any(|segment| segment.ident.to_string().contains("test"))
303                            })
304                            .count();
305                        if test_count > 0 {
306                            count += test_count;
307                            let block = f.block.clone();
308                            let catch_unwind: Stmt = parse_quote! {
309                                let result = panic::catch_unwind(|| {
310                                    #block
311                                });
312                            };
313                            f.block.stmts = vec![catch_unwind, after_all_if.clone(), resume.clone()];
314                            Item::Fn(f)
315                        } else {
316                            Item::Fn(f)
317                        }
318                    }
319                    Item::Use(use_stmt) => {
320                        if traverse_use_item(&use_stmt.tree, vec!["std", "sync", "Once"]).is_some()
321                        {
322                            has_once = true;
323                        }
324                        if traverse_use_item(
325                            &use_stmt.tree,
326                            vec!["std", "sync", "atomic", "AtomicUsize"],
327                        )
328                        .is_some()
329                        {
330                            has_atomic_usize = true;
331                        }
332                        if traverse_use_item(
333                            &use_stmt.tree,
334                            vec!["std", "sync", "atomic", "Ordering"],
335                        )
336                        .is_some()
337                        {
338                            has_ordering = true;
339                        }
340                        if traverse_use_item(
341                            &use_stmt.tree,
342                            vec!["std", "panic"],
343                        )
344                        .is_some() {
345                            has_panic = true;
346                        }
347                        Item::Use(use_stmt)
348                    }
349                    el => el,
350                })
351                .collect();
352
353            let use_once: Item = parse_quote!(
354                use std::sync::Once;
355            );
356            let use_atomic_usize: Item = parse_quote!(
357                use std::sync::atomic::AtomicUsize;
358            );
359            let use_ordering: Item = parse_quote!(
360                use std::sync::atomic::Ordering;
361            );
362            let use_panic: Item = parse_quote!(
363                use std::panic;
364            );
365            let static_once: Item = parse_quote!(
366                static AFTER_ALL: Once = Once::new();
367            );
368            let static_count: Item = parse_quote!(
369                static REMAINING_TESTS: AtomicUsize = AtomicUsize::new(#count);
370            );
371
372            let mut once_content = vec![];
373
374            if !has_once {
375                once_content.push(use_once);
376            }
377            if !has_atomic_usize {
378                once_content.push(use_atomic_usize);
379            }
380            if !has_ordering {
381                once_content.push(use_ordering);
382            }
383            if !has_panic {
384                once_content.push(use_panic);
385            }
386            once_content.append(&mut vec![static_once, static_count]);
387            once_content.append(&mut e);
388
389            m.content = Some((brace, once_content));
390            Item::Mod(m)
391        }
392        _ => {
393            panic!("The `after_all` macro attribute is only valid when called on a module.")
394        }
395    };
396    TokenStream::from(quote! (#input))
397}
398
399// /// Will run the code in the matching `after_all` function exactly once when all of the tests have
400// /// run. This works by counting the number of `#[test]` attributes and decrementing a counter at
401// /// the beginning of every test. Once the counter reaches 0, it will run the code in `after_all`.
402// /// It uses [std::sync::Once](https://doc.rust-lang.org/std/sync/struct.Once.html) internally
403// /// to ensure that the code is run at maximum one time.
404// ///
405// /// ```
406// /// #[cfg(test)]
407// /// use test_env_helpers::*;
408// ///
409// /// #[after_all]
410// /// #[cfg(test)]
411// /// mod my_tests{
412// ///     fn after_all(){println!("I only get run once at the very end")}
413// ///     #[test]
414// ///     fn test_1(){}
415// ///     #[test]
416// ///     fn test_2(){}
417// ///     #[test]
418// ///     fn test_3(){}
419// /// }
420// /// ```
421// #[proc_macro_attribute]
422// pub fn after_all_async(_metadata: TokenStream, input: TokenStream) -> TokenStream {
423//     let input: Item = match parse_macro_input!(input as Item) {
424//         Item::Mod(mut m) => {
425//             let (brace, items) = m.content.unwrap();
426//             let (after_all_fn, everything_else): (Vec<Item>, Vec<Item>) =
427//                 items.into_iter().partition(|t| match t {
428//                     Item::Fn(f) => f.sig.ident == "after_all_async",
429//                     _ => false,
430//                 });
431//             let after_all_fn_block = if after_all_fn.len() != 1 {
432//                 panic!("The `after_all_async` macro attribute requires a single function named `after_all_async` in the body of the module it is called on.")
433//             } else {
434//                 match after_all_fn.into_iter().next().unwrap() {
435//                     Item::Fn(f) => f.block,
436//                     _ => unreachable!(),
437//                 }
438//             };
439//             let after_all_if: Stmt = parse_quote! {
440//                 if REMAINING_TESTS.fetch_sub(1, Ordering::SeqCst) == 1 {
441//                     AFTER_ALL.call_once(|| {
442//                         #after_all_fn_block
443//                     });
444//                 }
445//             };
446//             let resume: Stmt = parse_quote! {
447//                 if let Err(err) = result {
448//                     panic::resume_unwind(err);
449//                 }
450//             };
451//             let mut count: usize = 0;
452//             let mut has_once: bool = false;
453//             let mut has_atomic_usize: bool = false;
454//             let mut has_ordering: bool = false;
455//             let mut has_panic: bool = false;
456//             let mut has_future_ext: bool = false;
457
458//             let mut e: Vec<Item> = everything_else
459//                 .into_iter()
460//                 .map(|t| match t {
461//                     Item::Fn(mut f) => {
462//                         let test_count = f
463//                             .attrs
464//                             .iter()
465//                             .filter(|attr| {
466//                                 attr.path
467//                                     .segments
468//                                     .iter()
469//                                     .any(|segment| segment.ident.to_string().contains("test"))
470//                             })
471//                             .count();
472//                         if test_count > 0 {
473//                             count += test_count;
474//                             let block = f.block.clone();
475//                             let catch_unwind: Stmt = parse_quote! {
476//                                 let run = async {
477//                                     #block
478//                                 };
479//                                 let result = run.catch_unwind().await;
480//                             };
481//                             f.block.stmts = vec![catch_unwind, after_all_if.clone(), resume.clone()];
482//                             Item::Fn(f)
483//                         } else {
484//                             Item::Fn(f)
485//                         }
486//                     }
487//                     Item::Use(use_stmt) => {
488//                         if traverse_use_item(&use_stmt.tree, vec!["std", "sync", "Once"]).is_some()
489//                         {
490//                             has_once = true;
491//                         }
492//                         if traverse_use_item(
493//                             &use_stmt.tree,
494//                             vec!["std", "sync", "atomic", "AtomicUsize"],
495//                         )
496//                         .is_some()
497//                         {
498//                             has_atomic_usize = true;
499//                         }
500//                         if traverse_use_item(
501//                             &use_stmt.tree,
502//                             vec!["std", "sync", "atomic", "Ordering"],
503//                         )
504//                         .is_some()
505//                         {
506//                             has_ordering = true;
507//                         }
508//                         if traverse_use_item(
509//                             &use_stmt.tree,
510//                             vec!["std", "panic"],
511//                         )
512//                         .is_some() {
513//                             has_panic = true;
514//                         }
515//                         Item::Use(use_stmt)
516//                     }
517//                     el => el,
518//                 })
519//                 .collect();
520
521//             let use_once: Item = parse_quote!(
522//                 use std::sync::Once;
523//             );
524//             let use_atomic_usize: Item = parse_quote!(
525//                 use std::sync::atomic::AtomicUsize;
526//             );
527//             let use_ordering: Item = parse_quote!(
528//                 use std::sync::atomic::Ordering;
529//             );
530//             let use_panic: Item = parse_quote!(
531//                 use std::panic;
532//             );
533//             let use_future_ext: Item = parse_quote!(
534//                 use futures::FutureExt;
535//             );
536//             let static_once: Item = parse_quote!(
537//                 static AFTER_ALL: Once = Once::new();
538//             );
539//             let static_count: Item = parse_quote!(
540//                 static REMAINING_TESTS: AtomicUsize = AtomicUsize::new(#count);
541//             );
542
543//             let mut once_content = vec![];
544
545//             if !has_once {
546//                 once_content.push(use_once);
547//             }
548//             if !has_atomic_usize {
549//                 once_content.push(use_atomic_usize);
550//             }
551//             if !has_ordering {
552//                 once_content.push(use_ordering);
553//             }
554//             if !has_panic {
555//                 once_content.push(use_panic);
556//             }
557//             if !has_future_ext {
558//                 once_content.push(use_future_ext);
559//             }
560//             once_content.append(&mut vec![static_once, static_count]);
561//             once_content.append(&mut e);
562
563//             m.content = Some((brace, once_content));
564//             Item::Mod(m)
565//         }
566//         _ => {
567//             panic!("The `after_all_async` macro attribute is only valid when called on a module.")
568//         }
569//     };
570//     TokenStream::from(quote! (#input))
571// }
572
573/// Will run the code in the matching `after_each` function at the end of every `#[test]` function.
574/// Useful if you want to cleanup after a test or reset some external state. If the test panics,
575/// this code will not be run. If you need something that is infallible, you should use
576/// `before_each` instead.
577/// ```
578/// #[cfg(test)]
579/// use test_env_helpers::*;
580///
581/// #[after_each]
582/// #[cfg(test)]
583/// mod my_tests{
584///     fn after_each(){println!("I get run at the very end of each function")}
585///     #[test]
586///     fn test_1(){}
587///     #[test]
588///     fn test_2(){}
589///     #[test]
590///     fn test_3(){}
591/// }
592/// ```
593#[proc_macro_attribute]
594pub fn after_each(_metadata: TokenStream, input: TokenStream) -> TokenStream {
595    let input: Item = match parse_macro_input!(input as Item) {
596        Item::Mod(mut m) => {
597            let (brace, items) = m.content.unwrap();
598            let (after_each_fn, everything_else): (Vec<Item>, Vec<Item>) =
599                items.into_iter().partition(|t| match t {
600                    Item::Fn(f) => f.sig.ident == "after_each",
601                    _ => false,
602                });
603            let after_each_fn_block = if after_each_fn.len() != 1 {
604                panic!("The `after_each` macro attribute requires a single function named `after_each` in the body of the module it is called on.")
605            } else {
606                match after_each_fn.into_iter().next().unwrap() {
607                    Item::Fn(f) => f.block,
608                    _ => unreachable!(),
609                }
610            };
611
612            let e: Vec<Item> = everything_else
613                .into_iter()
614                .map(|t| match t {
615                    Item::Fn(mut f) => {
616                        if f.attrs.iter().any(|attr| {
617                            attr.path
618                                .segments
619                                .iter()
620                                .any(|segment| segment.ident.to_string().contains("test"))
621                        }) {
622                            f.block.stmts.append(&mut after_each_fn_block.stmts.clone());
623                            Item::Fn(f)
624                        } else {
625                            Item::Fn(f)
626                        }
627                    }
628                    e => e,
629                })
630                .collect();
631            m.content = Some((brace, e));
632            Item::Mod(m)
633        }
634
635        _ => {
636            panic!("The `after_each` macro attribute is only valid when called on a module.")
637        }
638    };
639    TokenStream::from(quote! {#input})
640}
641
642/// Will run the code in the matching `before_all` function exactly once at the very beginning of a
643/// test run. It uses [std::sync::Once](https://doc.rust-lang.org/std/sync/struct.Once.html) internally
644/// to ensure that the code is run at maximum one time. Useful for setting up some external state
645/// that will be reused in multiple tests.
646/// ```
647/// #[cfg(test)]
648/// use test_env_helpers::*;
649///
650/// #[before_all]
651/// #[cfg(test)]
652/// mod my_tests{
653///     fn before_all(){println!("I get run at the very beginning of the test suite")}
654///     #[test]
655///     fn test_1(){}
656///     #[test]
657///     fn test_2(){}
658///     #[test]
659///     fn test_3(){}
660/// }
661/// ```
662#[proc_macro_attribute]
663pub fn before_all(_metadata: TokenStream, input: TokenStream) -> TokenStream {
664    let input: Item = match parse_macro_input!(input as Item) {
665        Item::Mod(mut m) => {
666            let (brace, items) = m.content.unwrap();
667            let (before_all_fn, everything_else): (Vec<Item>, Vec<Item>) =
668                items.into_iter().partition(|t| match t {
669                    Item::Fn(f) => f.sig.ident == "before_all",
670                    _ => false,
671                });
672            let before_all_fn_block = if before_all_fn.len() != 1 {
673                panic!("The `before_all` macro attribute requires a single function named `before_all` in the body of the module it is called on.")
674            } else {
675                match before_all_fn.into_iter().next().unwrap() {
676                    Item::Fn(f) => f.block,
677                    _ => unreachable!(),
678                }
679            };
680            let q: Stmt = parse_quote! {
681                BEFORE_ALL.call_once(|| {
682                    #before_all_fn_block
683                });
684            };
685
686            let mut has_once: bool = false;
687            let mut e: Vec<Item> = everything_else
688                .into_iter()
689                .map(|t| match t {
690                    Item::Fn(mut f) => {
691                        if f.attrs.iter().any(|attr| {
692                            attr.path
693                                .segments
694                                .iter()
695                                .any(|segment| segment.ident.to_string().contains("test"))
696                        }) {
697                            let mut stmts = vec![q.clone()];
698                            stmts.append(&mut f.block.stmts);
699                            f.block.stmts = stmts;
700                            Item::Fn(f)
701                        } else {
702                            Item::Fn(f)
703                        }
704                    }
705                    Item::Use(use_stmt) => {
706                        if traverse_use_item(&use_stmt.tree, vec!["std", "sync", "Once"]).is_some()
707                        {
708                            has_once = true;
709                        }
710                        Item::Use(use_stmt)
711                    }
712                    e => e,
713                })
714                .collect();
715            let use_once: Item = parse_quote!(
716                use std::sync::Once;
717            );
718            let static_once: Item = parse_quote!(
719                static BEFORE_ALL: Once = Once::new();
720            );
721
722            let mut once_content = vec![];
723            if !has_once {
724                once_content.push(use_once);
725            }
726            once_content.push(static_once);
727            once_content.append(&mut e);
728
729            m.content = Some((brace, once_content));
730            Item::Mod(m)
731        }
732
733        _ => {
734            panic!("The `before_all` macro attribute is only valid when called on a module.")
735        }
736    };
737    TokenStream::from(quote! (#input))
738}
739
740/// Will run the code in the matching `before_each` function at the beginning of every test. Useful
741/// to reset state to ensure that a test has a clean slate.
742/// ```
743/// #[cfg(test)]
744/// use test_env_helpers::*;
745///
746/// #[before_each]
747/// #[cfg(test)]
748/// mod my_tests{
749///     fn before_each(){println!("I get run at the very beginning of every test")}
750///     #[test]
751///     fn test_1(){}
752///     #[test]
753///     fn test_2(){}
754///     #[test]
755///     fn test_3(){}
756/// }
757/// ```
758///
759/// Can be used to reduce the amount of boilerplate setup code that needs to be copied into each test.
760/// For example, if you need to ensure that tests in a single test suite are not run in parallel, this can
761/// easily be done with a [Mutex](https://doc.rust-lang.org/std/sync/struct.Mutex.html).
762/// However, remembering to copy and paste the code to acquire a lock on the `Mutex` in every test
763/// is tedious and error prone.
764/// ```
765/// #[cfg(test)]
766/// mod without_before_each{
767///     lazy_static! {
768///         static ref MTX: Mutex<()> = Mutex::new(());
769///     }
770///     #[test]
771///     fn test_1(){let _m = MTX.lock();}
772///     #[test]
773///     fn test_2(){let _m = MTX.lock();}
774///     #[test]
775///     fn test_3(){let _m = MTX.lock();}
776/// }
777/// ```
778/// Using `before_each` removes the need to copy and paste so much and makes making changes easier
779/// because they only need to be made in a single location instead of once for every test.
780/// ```
781/// #[cfg(test)]
782/// use test_env_helpers::*;
783///
784/// #[before_each]
785/// #[cfg(test)]
786/// mod with_before_each{
787///     lazy_static! {
788///         static ref MTX: Mutex<()> = Mutex::new(());
789///     }
790///     fn before_each(){let _m = MTX.lock();}
791///     #[test]
792///     fn test_1(){}
793///     #[test]
794///     fn test_2(){}
795///     #[test]
796///     fn test_3(){}
797/// }
798/// ```
799#[proc_macro_attribute]
800pub fn before_each(_metadata: TokenStream, input: TokenStream) -> TokenStream {
801    let input: Item = match parse_macro_input!(input as Item) {
802        Item::Mod(mut m) => {
803            let (brace, items) = m.content.unwrap();
804            let (before_each_fn, everything_else): (Vec<Item>, Vec<Item>) =
805                items.into_iter().partition(|t| match t {
806                    Item::Fn(f) => f.sig.ident == "before_each",
807                    _ => false,
808                });
809            let before_each_fn_block = if before_each_fn.len() != 1 {
810                panic!("The `before_each` macro attribute requires a single function named `before_each` in the body of the module it is called on.")
811            } else {
812                match before_each_fn.into_iter().next().unwrap() {
813                    Item::Fn(f) => f.block,
814                    _ => unreachable!(),
815                }
816            };
817
818            let e: Vec<Item> = everything_else
819                .into_iter()
820                .map(|t| match t {
821                    Item::Fn(mut f) => {
822                        if f.attrs.iter().any(|attr| {
823                            attr.path
824                                .segments
825                                .iter()
826                                .any(|segment| segment.ident.to_string().contains("test"))
827                        }) {
828                            let mut b = before_each_fn_block.stmts.clone();
829                            b.append(&mut f.block.stmts);
830                            f.block.stmts = b;
831                            Item::Fn(f)
832                        } else {
833                            Item::Fn(f)
834                        }
835                    }
836                    e => e,
837                })
838                .collect();
839            m.content = Some((brace, e));
840            Item::Mod(m)
841        }
842
843        _ => {
844            panic!("The `before_each` macro attribute is only valid when called on a module.")
845        }
846    };
847    TokenStream::from(quote! {#input})
848}
849
850/// Will skip running the code it is applied on. You can use it to skip tests that aren't working
851/// correctly or that you don't want to run for some reason. There are no checks to make sure it's
852/// applied to a `#[test]` or mod. It will remove whatever it is applied to from the final AST.
853///
854/// ```
855/// #[cfg(test)]
856/// use test_env_helpers::*;
857///
858/// #[cfg(test)]
859/// mod my_tests{
860///     #[skip]
861///     #[test]
862///     fn broken_test(){panic!("I'm hella broke")}
863///     #[skip]
864///     mod broken_mod{
865///         #[test]
866///         fn i_will_not_be_run(){panic!("I get skipped too")}
867///     }
868///     #[test]
869///     fn test_2(){}
870///     #[test]
871///     fn test_3(){}
872/// }
873/// ```
874#[proc_macro_attribute]
875pub fn skip(_metadata: TokenStream, _input: TokenStream) -> TokenStream {
876    TokenStream::from(quote! {})
877}