use super::super::{Change, ChangeKind, Watcher};
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{Duration, Instant};
use tempfile::{TempDir, tempdir};
pub(super) const WAIT_TIMEOUT: Duration = Duration::from_secs(20);
pub(super) const POLL_INTERVAL: Duration = Duration::from_millis(50);
pub(super) fn poll_until<T>(
mut probe: impl FnMut() -> Option<T>,
timeout: Duration,
interval: Duration,
) -> Option<T> {
let deadline = Instant::now() + timeout;
while Instant::now() < deadline {
if let Some(found) = probe() {
return Some(found);
}
std::thread::sleep(interval);
}
probe()
}
pub(crate) fn workspace() -> (TempDir, PathBuf) {
let dir = tempdir().unwrap();
let root = dir.path().canonicalize().unwrap();
(dir, root)
}
pub(super) fn wait_quiet(watcher: &Watcher) {
let probe = watcher.repo_root.join("steps/.arming-probe");
fs::create_dir_all(probe.parent().unwrap()).unwrap();
let armed = poll_until(
|| {
fs::write(&probe, b"x").ok()?;
watcher.tick().iter().any(|c| c.path == probe).then_some(())
},
WAIT_TIMEOUT,
POLL_INTERVAL,
);
assert!(armed.is_some(), "the watcher never armed");
while poll_until(
|| (!watcher.tick().is_empty()).then_some(()),
POLL_INTERVAL * 4,
POLL_INTERVAL,
)
.is_some()
{}
}
pub(super) fn wait_for(watcher: &Watcher, pred: impl Fn(&Change) -> bool) -> Vec<Change> {
wait_for_with(watcher, || {}, pred)
}
pub(super) fn wait_for_with(
watcher: &Watcher,
mut make: impl FnMut(),
pred: impl Fn(&Change) -> bool,
) -> Vec<Change> {
let probe = || {
make();
let changes = watcher.tick();
let found = changes.iter().any(&pred);
found.then_some(changes)
};
poll_until(probe, WAIT_TIMEOUT, POLL_INTERVAL).unwrap_or_default()
}
pub(super) fn expect_touch(root: &Path, target: &Path) {
fs::create_dir_all(target.parent().unwrap()).unwrap();
let watcher = Watcher::new(root).unwrap();
wait_quiet(&watcher);
let changes = wait_for_with(
&watcher,
|| fs::write(target, b"x").unwrap(),
|c| c.path == *target,
);
let hit = changes.iter().find(|c| c.path == *target).expect("event");
assert_eq!(hit.kind, ChangeKind::Touched);
}