2026-08-05 18:05:04 -04:00
|
|
|
//! Scratch directories for tests.
|
|
|
|
|
//!
|
|
|
|
|
//! Public and `#[doc(hidden)]` rather than `#[cfg(test)]`: the `tests/`
|
2026-08-09 16:25:43 -04:00
|
|
|
//! integration binaries and the GUI crate are separate compilation units, so
|
|
|
|
|
//! a test-gated item here would be invisible to them.
|
2026-08-05 18:05:04 -04:00
|
|
|
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
|
|
2026-08-09 16:25:43 -04:00
|
|
|
/// Distinguishes directories requested within one process; a timestamp alone
|
|
|
|
|
/// lets two tests in the same millisecond collide.
|
2026-08-05 18:05:04 -04:00
|
|
|
static NEXT: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
|
|
2026-08-09 18:36:47 -04:00
|
|
|
/// The compressed body [`crate::db::repo::set_content_done`] wants, for tests
|
|
|
|
|
/// that only care that a sidecar row gets written.
|
|
|
|
|
///
|
|
|
|
|
/// Production callers compress a whole batch through one
|
|
|
|
|
/// [`crate::db::repo::DocEncoder`] before taking the connection lock; a test
|
|
|
|
|
/// writing one row has nothing to amortize and wants the one-liner.
|
|
|
|
|
pub fn zstd_of(text: &str) -> Option<Vec<u8>> {
|
|
|
|
|
crate::db::repo::encode_one(text, true).expect("zstd encode")
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-05 18:05:04 -04:00
|
|
|
/// A fresh, empty directory under the system temp dir, named for `tag`.
|
2026-08-09 16:25:43 -04:00
|
|
|
/// Not cleaned up on drop: when a test fails, the tree it built is most of
|
|
|
|
|
/// the evidence. Panics — a test that cannot create a directory has nothing
|
|
|
|
|
/// left to assert.
|
2026-08-05 18:05:04 -04:00
|
|
|
#[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");
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-09 16:25:43 -04:00
|
|
|
/// Power-of-two bucket, so memory-map sizes group by what allocated them
|
|
|
|
|
/// rather than by their exact size. Shared by the memory probes.
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
|
pub fn size_class(bytes: u64) -> String {
|
|
|
|
|
let mib = bytes as f64 / (1024.0 * 1024.0);
|
|
|
|
|
if mib < 1.0 {
|
|
|
|
|
"< 1 MiB".to_string()
|
|
|
|
|
} else {
|
|
|
|
|
let bucket = 1u64 << (63 - (bytes / (1024 * 1024)).leading_zeros() as u64);
|
|
|
|
|
format!("~{} MiB", bucket)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
|
pub fn mib(bytes: u64) -> String {
|
|
|
|
|
format!("{:.1} MiB", bytes as f64 / (1024.0 * 1024.0))
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-05 18:05:04 -04:00
|
|
|
#[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");
|
|
|
|
|
}
|
|
|
|
|
}
|