target_test_dir_support/basedir.rs
1use once_cell::sync::OnceCell;
2use std::path::{Path, PathBuf};
3
4/// Return a [Path] to a `test-data` directory inside the crate or workspace `target/` directory
5///
6/// This function implicitly assumes the current executable resides within the `target/` directory
7/// which is the case for test binaries. If it cannot find the `target/` directory it will panic.
8///
9/// It prints diagnostic information to `stderr`.
10///
11/// The first time this is called in a process, it will remove any pre-existing `test-data`
12/// directory, then create a new directory. This design aims to leave test data available
13/// for inspection after a test run, while also ensuring all of the contents come from the same
14/// test process run. This function is thread-safe via [OnceCell], which supports the primary use
15/// case of being used for multiple `#[test]` functions which may be invoked concurrently.
16pub fn get_base_test_dir() -> &'static Path {
17 static DIR: OnceCell<PathBuf> = OnceCell::new();
18
19 DIR.get_or_init(|| init_base_test_dir().expect("could not initialize base test data directory"))
20 .as_path()
21}
22
23fn init_base_test_dir() -> std::io::Result<PathBuf> {
24 let pb = get_target_dir()?.join("test-data");
25 if pb.is_dir() {
26 eprintln!("Removing {:?} from previous test run...", pb.display());
27 std::fs::remove_dir_all(&pb)?;
28 }
29 eprintln!("Creating {:?}...", pb.display());
30 std::fs::create_dir(&pb)?;
31 Ok(pb)
32}
33
34/// Attempt to return the crate or workspace `target/` directory
35///
36/// Precondition: the executable path resides within the `target/` directory. This is the case for
37/// standard `cargo test` runs, AFAIK.
38fn get_target_dir() -> std::io::Result<PathBuf> {
39 for candidate in std::env::current_exe()?.ancestors() {
40 if candidate.is_dir() && candidate.file_name().and_then(|os| os.to_str()) == Some("target")
41 {
42 return Ok(candidate.to_path_buf());
43 }
44 }
45
46 Err(std::io::Error::new(
47 std::io::ErrorKind::Other,
48 "Cargo 'target/' directory not found.",
49 ))
50}
51
52#[cfg(test)]
53mod tests;