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