target_test_dir/
lib.rs

1//! # Target Test Directories
2//!
3//! This crate provides a convenient proc-macro [macro@with_test_dir] for tests which need a test-specific directory.
4//!
5//! The [macro@with_test_dir] proc-macro inserts a `get_test_dir` macro into the body of a
6//! function. `get_test_dir` creates and returns a test directory based on the name of the test
7//! function in a well known location: `target/test-data/<module-path-and-function-name>`
8//!
9//! These `test-data` directories persist between test runs and are removed and recreated when
10//! beginning a new test run. This facilitates inspecting test data between test runs for both
11//! passing and failing tests.
12//!
13//! ## Example
14//!
15//! Your tests need to depend on this crate in `[dev-dependencies]`:
16//!
17//! ```ignore
18//! [dev-dependencies]
19//! target-test-dir = "…"
20//! ```
21//!
22//! Then in any test which needs a directory, use the [macro@with_test_dir] proc-macro attribute on a
23//! test fn:
24//!
25//! ```
26//! use target_test_dir::with_test_dir;
27//! use std::path::PathBuf;
28//!
29//! #[test]
30//! #[with_test_dir]
31//! fn write_and_read_hello_world() -> std::io::Result<()> {
32//!     let testdir = get_test_dir!();
33//!     let hwpath = testdir.join("hello_world.txt");
34//!     std::fs::write(&hwpath, "Hello World!")?;
35//!
36//!     let bytes = std::fs::read(hwpath)?;
37//!     let output = String::from_utf8(bytes).unwrap();
38//!
39//!     assert_eq!(&output, "Hello World!");
40//!     Ok(())
41//! }
42//! ```
43//!
44//! ## Test Directory Invariants
45//!
46//! The test directories follow these invariants:
47//!
48//! - The `get_test_dir` macro requires `$CARGO_MANIFEST_DIR` to be set due to invoking [get_base_test_dir].
49//! - Each test has a test specific directory in `target/test-data/${TEST_SPECIFIC_NAME}`.
50//! - The `TEST_SPECIFIC_NAME` is the full module + test function rust item path name with `::` replaced with `-`.
51//! - Test process & `test-data` consistency:
52//!   - During a test process run, the first test requiring one of these directories will remove all of `target/test-data` if present.
53//!   - Each test creates its own test-specific directory prior to executing the wrapped test function.
54//!   - These two invariants are designed to guarantee that the contents of that directory should always be due to the most recent run of tests (and should not mix data from different test processes).
55//! - The directories are otherwise not removed by this framework, so that developers can inspect the results for both passing and failing tests.
56//! - They live under `target/` so they should be ignored by Cargo's revision control conventions and cleaned up with `cargo clean`.
57//!
58//! ## Non-test functions
59//!
60//! The macro can be used on non-`#[test]` functions, provided the expectations of
61//! [get_base_test_dir] are met and those functions are only invoked
62//! once per test run. Example:
63//!
64//! ```
65//! use target_test_dir::with_test_dir;
66//! use std::path::PathBuf;
67//!
68//! #[with_test_dir]
69//! fn setup_test_data() -> PathBuf {
70//!     let testdir = get_test_dir!();
71//!
72//!     populate_test_data(&testdir);
73//!     testdir
74//! }
75//!
76//! #[test]
77//! fn test_validator() {
78//!     let testdir = setup_test_data();
79//!     run_validator(testdir);
80//! }
81//!
82//! # fn populate_test_data(_: &std::path::Path) {
83//! #     unimplemented!("stub to demonstrate example");
84//! # }
85//! # fn run_validator(_: PathBuf) {
86//! #     unimplemented!("stub to demonstrate example");
87//! # }
88//! ```
89//!
90//! ## Edge cases
91//!
92//! The `get_test_dir` macro panics if the directory already exists, since the design assumes tests
93//! should always execute against a new empty directory for repeatability.
94//!
95//! This could occur, for example, when re-using a function multiple
96//!
97//! This may occur any time a given `#[with_test_dir]` function is called
98//! multiple times in one test process. One case this occurs is when composing with the
99//! [`test_case`](https://docs.rs/test-case/latest/test_case/) macro which calls the same test
100//! function for each case within a single test process.
101
102mod basedir;
103mod testdir;
104
105pub use self::basedir::get_base_test_dir;
106pub use self::testdir::{get_test_dir, get_test_dir_name, try_get_test_dir};
107/// The `named` proc-macro is re-exported from [function_name] because it is a dependency of [macro@with_test_dir]
108pub use function_name::named;
109pub use target_test_dir_macro::with_test_dir;