use pin_project::pin_project;
use tracing::trace;
use crate::time::MockSleepProvider;
use crate::util::impl_runtime_prelude::*;
#[derive(Clone, Debug, Deftly)]
#[derive_deftly(SomeMockRuntime)]
#[cfg_attr(not(test), deprecated(since = "0.29.0"))]
pub struct MockSleepRuntime<R: Runtime> {
#[deftly(mock(task, net))]
#[deftly(mock(toplevel_where = "R: ToplevelBlockOn"))]
runtime: R,
#[deftly(mock(sleep))]
sleep: MockSleepProvider,
}
impl<R: Runtime> MockSleepRuntime<R> {
pub fn new(runtime: R) -> Self {
let sleep = MockSleepProvider::new(SystemTime::get());
MockSleepRuntime { runtime, sleep }
}
pub fn inner(&self) -> &R {
&self.runtime
}
pub fn mock_sleep(&self) -> &MockSleepProvider {
&self.sleep
}
pub async fn advance(&self, dur: Duration) {
self.sleep.advance(dur).await;
}
pub fn jump_to(&self, new_wallclock: SystemTime) {
self.sleep.jump_to(new_wallclock);
}
pub fn wait_for<F: futures::Future>(&self, fut: F) -> WaitFor<F> {
assert!(
!self.sleep.has_waitfor_waker(),
"attempted to call MockSleepRuntime::wait_for while another WaitFor is active"
);
WaitFor {
sleep: self.sleep.clone(),
fut,
}
}
}
#[pin_project]
pub struct WaitFor<F> {
#[pin]
sleep: MockSleepProvider,
#[pin]
fut: F,
}
use std::pin::Pin;
use std::task::{Context, Poll};
impl<F: Future> Future for WaitFor<F> {
type Output = F::Output;
#[allow(clippy::cognitive_complexity)]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
trace!("waitfor poll");
let mut this = self.project();
this.sleep.register_waitfor_waker(cx.waker().clone());
if let Poll::Ready(r) = this.fut.poll(cx) {
trace!("waitfor done!");
this.sleep.clear_waitfor_waker();
return Poll::Ready(r);
}
trace!("waitfor poll complete");
if this.sleep.should_advance() {
if let Some(duration) = this.sleep.time_until_next_timeout() {
trace!("Advancing by {:?}", duration);
this.sleep.advance_noyield(duration);
} else {
panic!("WaitFor told to advance, but didn't have any duration to advance by");
}
} else {
trace!("waiting for sleepers to advance");
}
Poll::Pending
}
}