mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 05:37:23 -06:00
Testkit: temp_dir starts empty in each process, so a reused process id can't fail a test
This commit is contained in:
@@ -42,7 +42,13 @@ pub use {vectorcraft_doc as doc, vectorcraft_format as format, vectorcraft_rende
|
||||
/// A per-process temporary directory for test output (created on first use). It is made with
|
||||
/// `create_dir` (owner-only on Unix), and a directory that already exists is reused only when it is a
|
||||
/// real directory, not a link, with no access for others and writable by this user: one pre-made in a shared temp folder is refused.
|
||||
/// The first call for a `tag` in a process empties a directory left by an earlier process of the
|
||||
/// same id (Windows reuses ids soon); later calls share it.
|
||||
pub fn temp_dir(tag: &str) -> std::path::PathBuf {
|
||||
static SEEN: std::sync::Mutex<std::collections::BTreeSet<String>> = std::sync::Mutex::new(std::collections::BTreeSet::new());
|
||||
// Held to the end: another thread's first files must not land before the emptying.
|
||||
let mut seen = SEEN.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
let first = seen.insert(tag.to_string());
|
||||
let dir = std::env::temp_dir().join(format!("vectorcraft-testkit-{tag}-{}", std::process::id()));
|
||||
// Unix sets the mode on it; elsewhere it stays as made.
|
||||
#[cfg_attr(not(unix), allow(unused_mut))]
|
||||
@@ -60,6 +66,13 @@ pub fn temp_dir(tag: &str) -> std::path::PathBuf {
|
||||
static PROBES: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
|
||||
let probe = dir.join(format!(".probe-{}", PROBES.fetch_add(1, std::sync::atomic::Ordering::Relaxed)));
|
||||
assert!(std::fs::write(&probe, b"").and_then(|()| std::fs::remove_file(&probe)).is_ok(), "{}: not owned by this user", dir.display());
|
||||
if first {
|
||||
for entry in std::fs::read_dir(&dir).expect("temp dir").flatten() {
|
||||
let path = entry.path();
|
||||
let gone = if path.is_dir() { std::fs::remove_dir_all(&path) } else { std::fs::remove_file(&path) };
|
||||
assert!(gone.is_ok(), "{}: left by an earlier run", path.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
dir
|
||||
}
|
||||
@@ -91,3 +104,20 @@ pub fn catch_quiet<T>(f: impl FnOnce() -> T) -> Result<T, String> {
|
||||
QUIET.with(|q| q.set(false));
|
||||
r.map_err(|p| p.downcast_ref::<String>().cloned().or_else(|| p.downcast_ref::<&str>().map(|s| s.to_string())).unwrap_or_else(|| "panic".into()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn a_temp_dir_starts_empty_in_a_process_and_stays_for_later_calls() {
|
||||
let tag = "fresh-temp-dir";
|
||||
let left = std::env::temp_dir().join(format!("vectorcraft-testkit-{tag}-{}", std::process::id()));
|
||||
std::fs::create_dir_all(left.join("nested")).unwrap();
|
||||
std::fs::write(left.join("left.txt"), b"an earlier process").unwrap();
|
||||
let dir = super::temp_dir(tag);
|
||||
assert_eq!(dir, left);
|
||||
assert_eq!(std::fs::read_dir(&dir).unwrap().count(), 0, "what an earlier process of this id left is gone");
|
||||
std::fs::write(dir.join("mine.txt"), b"this process").unwrap();
|
||||
assert!(super::temp_dir(tag).join("mine.txt").is_file(), "a later call keeps this process's files");
|
||||
std::fs::remove_dir_all(&dir).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user