dir_test/lib.rs
1#![allow(clippy::test_attr_in_doctest)]
2#![cfg(not(doctest))]
3//! `dir-test` provides a macro to generate single test cases from files in a
4//! directory.
5//!
6//! ## Usage
7//! Add the following dependency to your `Cargo.toml`.
8//!
9//! ``` toml
10//! [dev-dependencies]
11//! dir-test = "0.4"
12//! ```
13//!
14//! ### Basic Usage
15//! ```rust, no_run
16//! use dir_test::{dir_test, Fixture};
17//!
18//! #[dir_test(
19//! dir: "$CARGO_MANIFEST_DIR/fixtures",
20//! glob: "**/*.txt",
21//! )]
22//! fn mytest(fixture: Fixture<&str>) {
23//! // The file content and the absolute path of the file are available as follows.
24//! let content = fixture.content();
25//! let path = fixture.path();
26//!
27//! // Write your test code here.
28//! // ...
29//! }
30//! ```
31//!
32//! Assuming your crate is as follows, then the above code generates two test
33//! cases `mytest__foo()` and `mytest__fixtures_a_bar()`.
34//!
35//! ```text
36//! my-crate/
37//! ├─ fixtures/
38//! │ ├─ foo.txt
39//! │ ├─ fixtures_a/
40//! │ │ ├─ bar.txt
41//! ├─ src/
42//! │ ├─ ...
43//! │ ├─ lib.rs
44//! ├─ Cargo.toml
45//! ├─ README.md
46//! ```
47//!
48//! 🔽
49//!
50//! ```rust, no_run
51//! #[test]
52//! fn mytest__foo() {
53//! mytest(fixture);
54//! }
55//!
56//! #[test]
57//! fn mytest__fixtures_a_bar() {
58//! mytest(fixture);
59//! }
60//! ```
61//!
62//! **NOTE**: The `dir` argument must be specified in an absolute path because
63//! of the limitation of the current procedural macro system. Consider using
64//! environment variables, `dir-test` crate resolves environment variables
65//! internally.
66//!
67//! ### Custom Loader
68//! You can specify a custom loader function to load the file content from the
69//! file path. The loader will be passed `&'static str` file path and can return
70//! an arbitrary type.
71//! ```rust, no_run
72//! use dir_test::{dir_test, Fixture};
73//!
74//! #[dir_test(
75//! dir: "$CARGO_MANIFEST_DIR/fixtures",
76//! glob: "**/*.txt",
77//! loader: std::fs::read_to_string,
78//! )]
79//! fn test(fixture: Fixture<std::io::Result<String>>) {
80//! let content = fixture.content().unwrap();
81//!
82//! // ...
83//! }
84//! ```
85//!
86//! If the loader function is not specified, the default content type is
87//! `&'static str`.
88//!
89//! ### Custom Test Name
90//! Test names can be customized by specifying the `postfix` argument.
91//! `postfix` is appended to the test name.
92//!
93//! ```rust, no_run
94//! use dir_test::{dir_test, Fixture};
95//!
96//! #[dir_test(
97//! dir: "$CARGO_MANIFEST_DIR/fixtures",
98//! glob: "**/*.txt",
99//! postfix: "custom", // `_custom` is appended to the test name.
100//! )]
101//! fn test(fixture: Fixture<std::io::Result<String>>) {
102//! // ...
103//! }
104//! ```
105//!
106//! ### Test Attributes
107//! Test attributes can specified by the `dir_test_attr` attribute. The
108//! attributes inside `dir_test_atrr` are applied to the all generated test.
109//!
110//! ```rust, no_run
111//! use dir_test::{dir_test, Fixture};
112//!
113//! #[dir_test(
114//! dir: "$CARGO_MANIFEST_DIR/fixtures",
115//! glob: "**/*.txt",
116//! )]
117//! #[dir_test_attr(
118//! #[wasm_bindgen_test]
119//! #[cfg(target_family = "wasm")]
120//! )]
121//! fn wasm_test(fixture: Fixture<std::io::Result<String>>) {
122//! // ...
123//! }
124//! ```
125//!
126//! **NOTE**: The `dir_test_attr` attribute must be specified after the
127//! `dir_test`.
128//!
129//! ### Return Types
130//! Tests may have a return type, allowing for the [`Result<T, E>`] type to be
131//! used in the test. See the relevant book link
132//! [here](https://doc.rust-lang.org/book/ch11-01-writing-tests.html#using-resultt-e-in-tests).
133//!
134//! ```rust, no_run
135//! use dir_test::{dir_test, Fixture};
136//!
137//! #[dir_test(
138//! dir: "$CARGO_MANIFEST_DIR/fixtures",
139//! glob: "**/*.txt",
140//! )]
141//! fn test(fixture: Fixture<&str>) -> std::io::Result<()> {
142//! // ...
143//! }
144//! ```
145
146/// A fixture contains a file content and its absolute path.
147/// Content type is determined by the loader function specified in
148/// `dir_test` macro. If the loader is not specified, the default
149/// content type is `&'static str`.
150pub struct Fixture<T> {
151 content: T,
152 path: &'static str,
153}
154
155impl<T> Fixture<T> {
156 #[doc(hidden)]
157 /// Creates a new fixture from the given content and path.
158 pub fn new(content: T, path: &'static str) -> Self {
159 Self { content, path }
160 }
161
162 /// Returns a reference to the content of the fixture.
163 pub fn content(&self) -> &T {
164 &self.content
165 }
166
167 /// Consumes the fixture and returns the content.
168 pub fn into_content(self) -> T {
169 self.content
170 }
171
172 /// Returns the absolute path of the fixture.
173 pub const fn path(&self) -> &'static str {
174 self.path
175 }
176}
177
178/// A procedural macro to generate test cases from files in a directory.
179pub use dir_test_macros::dir_test;