#[cfg(not(test))]
pub(crate) fn now() -> crate::runtime::Instant {
crate::runtime::Instant::now()
}
#[cfg(test)]
pub(crate) fn now() -> crate::runtime::Instant {
BASE.with(|base| *base) + OFFSET.with(std::cell::Cell::get)
}
#[cfg(test)]
pub(crate) fn advance(duration: std::time::Duration) {
poll_pools();
OFFSET.with(|offset| {
offset.set(
offset
.get()
.checked_add(duration)
.expect("advance overflows the test clock"),
);
});
poll_pools();
}
#[cfg(test)]
thread_local! {
static BASE: crate::runtime::Instant = crate::runtime::Instant::now();
static OFFSET: std::cell::Cell<std::time::Duration> = const { std::cell::Cell::new(std::time::Duration::ZERO) };
}
#[cfg(all(test, not(loom)))]
mod tests {
use std::time::Duration;
#[test]
fn frozen_until_advanced() {
let a = super::now();
std::thread::sleep(Duration::from_millis(5));
assert_eq!(a, super::now(), "the test clock moved on its own");
super::advance(Duration::from_secs(3));
assert_eq!(super::now(), a + Duration::from_secs(3));
}
#[test]
fn advances_are_isolated_between_threads() {
let before = super::now();
std::thread::spawn(|| {
let before = super::now();
super::advance(Duration::from_secs(7));
assert_eq!(super::now(), before + Duration::from_secs(7));
})
.join()
.unwrap();
assert_eq!(super::now(), before, "another test thread advanced this clock");
}
}
#[cfg(test)]
thread_local! {
static POOLS: std::cell::RefCell<Vec<crate::cache::PoolWeak>> = const { std::cell::RefCell::new(Vec::new()) };
}
#[cfg(test)]
pub(crate) fn register(pool: &crate::cache::Pool) {
pool.advance_test(now());
POOLS.with(|pools| pools.borrow_mut().push(pool.downgrade()));
}
#[cfg(test)]
fn poll_pools() {
let pools: Vec<_> = POOLS.with(|pools| {
let mut pools = pools.borrow_mut();
pools.retain(|pool| pool.upgrade().is_some());
pools.iter().filter_map(|pool| pool.upgrade()).collect()
});
for pool in pools {
pool.advance_test(now());
}
}