quick_search/crates/quicksearch-core/src/testutil.rs

77 lines
2.6 KiB
Rust
Raw Normal View History

//! Scratch directories for tests.
//!
//! Public and `#[doc(hidden)]` rather than `#[cfg(test)]`: the `tests/`
//! integration binaries and the GUI crate are separate compilation units, so a
//! test-gated item here would be invisible to them. This is the only reason it
//! is not private.
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
/// Distinguishes directories requested within one process. Two tests running
/// on different threads in the same millisecond would otherwise collide — the
/// hand-rolled helpers this replaces all keyed off a timestamp, which made
/// that rare rather than impossible.
static NEXT: AtomicUsize = AtomicUsize::new(0);
/// A fresh, empty directory under the system temp dir, named for `tag`.
///
/// Not cleaned up on drop, deliberately: when a test fails, the tree it built
/// is most of the evidence. The OS clears the temp dir eventually.
///
/// Panics rather than returning a `Result` — a test that cannot create a
/// directory has nothing left to assert.
#[doc(hidden)]
pub fn scratch_dir(tag: &str) -> PathBuf {
let mut p = std::env::temp_dir();
p.push(format!(
"quicksearch-{}-{}-{}",
tag,
std::process::id(),
NEXT.fetch_add(1, Ordering::Relaxed)
));
std::fs::create_dir_all(&p).expect("create scratch dir");
p
}
/// [`scratch_dir`] canonicalized, for the tests that compare walked paths
/// against the root they were given. On macOS `/tmp` is a symlink to
/// `/private/tmp`, so an uncanonicalized root and a walked path disagree.
#[doc(hidden)]
pub fn scratch_dir_canonical(tag: &str) -> PathBuf {
std::fs::canonicalize(scratch_dir(tag)).expect("canonicalize scratch dir")
}
/// Write `body` to `path`, creating parent directories as needed.
#[doc(hidden)]
pub fn touch(path: &std::path::Path, body: &[u8]) {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).expect("create parent dir");
}
std::fs::write(path, body).expect("write file");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_call_gets_its_own_empty_directory() {
let a = scratch_dir("selftest");
let b = scratch_dir("selftest");
assert_ne!(a, b, "two calls must not collide");
for d in [&a, &b] {
assert!(d.is_dir());
assert_eq!(std::fs::read_dir(d).unwrap().count(), 0, "starts empty");
}
}
#[test]
fn touch_creates_missing_parents() {
let dir = scratch_dir("selftest-touch");
let deep = dir.join("a/b/c.txt");
touch(&deep, b"hi");
assert_eq!(std::fs::read(&deep).unwrap(), b"hi");
}
}