use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU32, Ordering};
pub fn test_scratch_dir(manifest_dir: &str, tag: &str) -> PathBuf {
static COUNTER: AtomicU32 = AtomicU32::new(0);
let unique = COUNTER.fetch_add(1, Ordering::Relaxed);
let manifest = PathBuf::from(manifest_dir);
let owner = manifest
.file_name()
.map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| "workspace".to_string());
let path = workspace_root(&manifest)
.join("target/test-output")
.join(owner)
.join(format!("{tag}-{}-{unique}", std::process::id()));
let _ = std::fs::remove_dir_all(&path);
std::fs::create_dir_all(&path).expect("a scratch directory under target/test-output");
path
}
fn workspace_root(manifest: &Path) -> PathBuf {
manifest
.ancestors()
.find(|directory| directory.join("Cargo.lock").is_file())
.unwrap_or(manifest)
.to_path_buf()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_scratch_directory_is_empty_under_the_workspace_target() {
let path = test_scratch_dir(env!("CARGO_MANIFEST_DIR"), "scratch");
assert!(path.is_dir());
assert_eq!(std::fs::read_dir(&path).expect("read").count(), 0);
assert!(
path.components().any(|part| part.as_os_str() == "target"),
"the scratch directory belongs under the workspace target: {}",
path.display()
);
let _ = std::fs::remove_dir_all(&path);
}
#[test]
fn two_calls_do_not_share_a_directory() {
let first = test_scratch_dir(env!("CARGO_MANIFEST_DIR"), "scratch");
let second = test_scratch_dir(env!("CARGO_MANIFEST_DIR"), "scratch");
assert_ne!(first, second);
let _ = std::fs::remove_dir_all(&first);
let _ = std::fs::remove_dir_all(&second);
}
}