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;