test_fork_macros/lib.rs
1// Copyright (C) 2025-2026 Daniel Mueller <deso@posteo.net>
2// SPDX-License-Identifier: (Apache-2.0 OR MIT)
3
4//! The procedural macro powering `test-fork`.
5
6#![cfg_attr(docsrs, feature(doc_cfg))]
7#![doc(hidden)]
8
9use proc_macro::TokenStream;
10
11use syn::parse_macro_input;
12use syn::ItemFn;
13
14#[cfg(all(feature = "unstable", feature = "unsound"))]
15use test_fork_core::try_bench;
16use test_fork_core::try_fork;
17use test_fork_core::try_test;
18
19
20/// A procedural macro for running a test in a separate process.
21///
22/// # Example
23///
24/// Use the attribute for all tests in scope:
25/// ```rust,ignore
26/// use test_fork::test;
27///
28/// #[test]
29/// fn test1() {
30/// assert_eq!(2 + 2, 4);
31/// }
32/// ```
33///
34/// Use it only on a single test:
35/// ```rust,ignore
36/// #[test_fork::test]
37/// fn test2() {
38/// assert_eq!(2 + 3, 5);
39/// }
40/// ```
41#[proc_macro_attribute]
42pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
43 let input_fn = parse_macro_input!(item as ItemFn);
44
45 try_test(attr.into(), input_fn)
46 .unwrap_or_else(syn::Error::into_compile_error)
47 .into()
48}
49
50
51/// A procedural macro for running a benchmark in a separate process.
52///
53/// # Example
54///
55/// Use the attribute for all benchmarks in scope:
56/// ```rust,ignore
57/// use test_fork::bench;
58///
59/// #[bench]
60/// fn bench1(b: &mut Bencher) {
61/// b.iter(|| sleep(Duration::from_millis(1)));
62/// }
63/// ```
64///
65/// Use it only on a single benchmark:
66/// ```rust,ignore
67/// #[test_fork::bench]
68/// fn bench2(b: &mut Bencher) {
69/// b.iter(|| sleep(Duration::from_millis(1)));
70/// }
71#[cfg(all(feature = "unstable", feature = "unsound"))]
72#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", feature = "unsound"))))]
73#[proc_macro_attribute]
74pub fn bench(attr: TokenStream, item: TokenStream) -> TokenStream {
75 let input_fn = parse_macro_input!(item as ItemFn);
76
77 try_bench(attr.into(), input_fn)
78 .unwrap_or_else(syn::Error::into_compile_error)
79 .into()
80}
81
82
83/// A procedural macro for running a test or benchmark in a separate
84/// process.
85///
86/// This attribute is able to cater to both tests and benchmarks, while
87/// #[[macro@test]] is specific to tests and #[[macro@bench]] to
88/// benchmarks.
89///
90/// Contrary to both, this attribute does not in itself make a function
91/// a test/benchmark, so it will *always* have to be combined with an
92/// additional "inner" attribute. However, it can be more convenient for
93/// annotating only a sub-set of tests/benchmarks for running in
94/// separate processes, especially when non-standard attributes are
95/// involved:
96///
97/// # Example
98///
99/// ```rust,ignore
100/// use test_fork::fork;
101///
102/// #[fork]
103/// #[test]
104/// fn test3() {
105/// assert_eq!(2 + 4, 6);
106/// }
107///
108/// #[fork]
109/// #[bench]
110/// fn bench3(b: &mut Bencher) {
111/// b.iter(|| sleep(Duration::from_millis(1)));
112/// }
113/// ```
114#[proc_macro_attribute]
115pub fn fork(attr: TokenStream, item: TokenStream) -> TokenStream {
116 let supports_bench = cfg!(all(feature = "unstable", feature = "unsound"));
117 let input_fn = parse_macro_input!(item as ItemFn);
118
119 try_fork(attr.into(), input_fn, supports_bench)
120 .unwrap_or_else(syn::Error::into_compile_error)
121 .into()
122}