macrotest/lib.rs
1#![crate_type = "lib"]
2
3//! ####   Test harness for macro expansion.
4//!
5//! Similar to [trybuild], but allows you to write tests on how macros are expanded.
6//!
7//! *Minimal Supported Rust Version: 1.76*
8//!
9//! <br>
10//!
11//! # Macro expansion tests
12//!
13//! A minimal `macrotest` setup looks like this:
14//!
15//! ```rust
16//! # /*
17//! #[test]
18//! # */
19//! pub fn pass() {
20//! macrotest::expand("tests/expand/*.rs");
21//! // Alternatively,
22//! macrotest::expand_without_refresh("tests/expand/*.rs");
23//! }
24//! ```
25//!
26//! The test can be run with `cargo test`. This test will invoke the [`cargo expand`] command
27//! on each of the source files that matches the glob pattern and will compare the expansion result
28//! with the corresponding `*.expanded.rs` file.
29//!
30//! If a `*.expanded.rs` file doesn't exists and it's not explicitly expected to (see [`expand_without_refresh`]),
31//! it will be created (this is how you update your tests).
32//!
33//! Possible test outcomes are:
34//! - **Pass**: expansion succeeded and the result is the same as in the `.expanded.rs` file
35//! - **Fail**: expansion was different from the `.expanded.rs` file content
36//! - **Refresh**: `.expanded.rs` didn't exist and has been created
37//! - **Refresh-fail**: `.expanded.rs` is expected to be present, but not exists. See [`expand_without_refresh`].
38//!
39//! *Note:* when working with multiple expansion test files, it is recommended to
40//! specify wildcard (*.rs) instead of doing a multiple calls to `expand` functions for individual files.
41//! Usage of wildcards for multiple files will group them under a single temporary crate for which
42//! dependencies will be built a single time. In contrast, calling `expand` functions for each
43//! source file will create multiple temporary crates and that will reduce performance as depdendencies
44//! will be build for each of the temporary crates.
45//!
46//! ## Passing additional arguments to `cargo expand`
47//!
48//! It's possible to specify additional arguments for [`cargo expand`] command.
49//!
50//! In order to do so, use the following functions with `_args` suffix:
51//! - [`expand_args`]
52//! - [`expand_without_refresh_args`]
53//!
54//! Example:
55//!
56//! ```rust
57//! pub fn pass() {
58//! macrotest::expand_args("tests/expand/*.rs", &["--features", "my-feature"]);
59//! // Or
60//! macrotest::expand_without_refresh_args("tests/expand/*.rs", &["--features", "my-feature"]);
61//! }
62//! ```
63//!
64//! The `_args` functions will result in the following [`cargo expand`] command being run:
65//!
66//! ```bash
67//! cargo expand --bin <test-name> --theme none --features my-feature
68//! ```
69//!
70//! # Workflow
71//!
72//! First of all, the [`cargo expand`] tool must be present. You can install it via cargo:
73//!
74//! ```bash
75//! cargo install --locked cargo-expand
76//! ```
77//!
78//! (In CI, you'll want to pin to a particular version,
79//! since
80//! [cargo expand's output is not stable across versions](https://github.com/dtolnay/cargo-expand/issues/179).
81//! Look up the
82//! [current version](https://crates.io/crates/cargo-expand)
83//! and do something like `cargo install --locked --version 1.0.81 cargo-expand`.)
84//!
85//! ## Setting up a test project
86//!
87//! In your crate that provides procedural or declarative macros, under the `tests` directory,
88//! create an `expand` directory and populate it with different expansion test cases as
89//! rust source files.
90//!
91//! Then create a `tests.rs` file that will run the tests:
92//!
93//! ```rust
94//! # /*
95//! #[test]
96//! # */
97//! pub fn pass() {
98//! macrotest::expand("tests/expand/*.rs");
99//! // Or:
100//! macrotest::expand_without_refresh("tests/expand/*.rs");
101//! }
102//! ```
103//!
104//! And then you can run `cargo test`, which will
105//!
106//! 1. Expand macros in source files that match glob pattern
107//! 1. In case if [`expand`] function is used:
108//! - On the first run, generate the `*.expanded.rs` files for each of the test cases under
109//! the `expand` directory
110//! - On subsequent runs, compare test cases' expansion result with the
111//! content of the respective `*.expanded.rs` files
112//! 1. In case if [`expand_without_refresh`] is used:
113//! - On each run, it will compare test cases' expansion result with the content of the
114//! respective `*.expanded.rs` files.
115//! - If one or more `*.expanded.rs` files is not found, the test will fail.
116//!
117//! ## Updating `.expanded.rs`
118//!
119//! This applicable only to tests that are using [`expand`] or [`expand_args`] function.
120//!
121//! Run tests with the environment variable `MACROTEST=overwrite` or remove the `*.expanded.rs`
122//! files and re-run the corresponding tests. Files will be created automatically; hand-writing
123//! them is not recommended.
124//!
125//! [`expand_without_refresh`]: expand/fn.expand_without_refresh.html
126//! [`expand_without_refresh_args`]: expand/fn.expand_without_refresh_args.html
127//! [`expand`]: expand/fn.expand.html
128//! [`expand_args`]: expand/fn.expand_args.html
129//! [trybuild]: https://github.com/dtolnay/trybuild
130//! [`cargo expand`]: https://github.com/dtolnay/cargo-expand
131
132#![allow(clippy::lines_filter_map_ok)] // https://github.com/rust-lang/rust-clippy/issues/14127
133
134#[macro_use]
135mod path;
136
137mod cargo;
138mod dependencies;
139mod error;
140mod expand;
141mod features;
142mod manifest;
143mod message;
144mod rustflags;
145
146pub use expand::expand;
147pub use expand::expand_args;
148pub use expand::expand_without_refresh;
149pub use expand::expand_without_refresh_args;