pub struct MockRuntime {
task: MockExecutor,
sleep: SimpleMockTimeProvider,
net: MockNetProvider,
}
Expand description
Completely mock runtime, with simulated time
Suitable for test cases that wish to completely control the environment experienced by the code under test.
§Useful properties
The execution order is deterministic.
Time will advance only in a controlled fashion.
Typically, the main task in a test will call
advance_until_stalled
.
Reliable sequencing techniques which can be used in tests include: sleeping for carefully chosen durations; interlocking via intertask channels; or, sequenced control of requests to the code under test.
§Restrictions
The test case must advance the mock time explicitly as desired,
typically by calling one of the MockRuntime::advance_*
methods.
Tests that use this runtime must not interact with the outside world; everything must go through this runtime (and its pieces).
There is no mocking of filesystem access;
the MockRuntime
’s time will disagree with SystemTime
’s
obtained from (for example) std::fs::Metadata
.
§Allowed
-
Inter-future communication facilities from
futures
or other runtime-agnostic crates. -
Fast synchronous operations that will complete “immediately” or “quickly”. E.g.: filesystem calls.
-
std::sync::Mutex
(assuming the use is deadlock-free in a single-threaded executor, as it should be in all of Arti). -
Slower operations that are run synchronously (without futures
await
) provided their completion doesn’t depend on any of the futures we’re running. (These kind of operations are often discouraged in async contexts, because they block the async runtime or its worker threads. But they are often OK in tests.) -
All facilities provided by this
MockExecutor
and its trait impls.
§Not allowed
-
Direct access to the real-world clock (
SystemTime::now
,Instant::now
), including direct use ofcoarsetime
. Instead, useSleepProvider
andCoarseTimeProvider
methods on the runtime. Exception: CPU use measurements. -
Anything that spawns threads and then communicates with those threads using async Rust facilities (futures).
-
Async sockets, or async use of other kernel-based IPC or network mechanisms.
-
Anything provided by a Rust runtime/executor project (eg anything from Tokio), unless it is definitively established that it’s runtime-agnostic.
Fields§
§task: MockExecutor
Tasks
sleep: SimpleMockTimeProvider
Time provider
net: MockNetProvider
Net provider
Implementations§
Source§impl MockRuntime
impl MockRuntime
Sourcepub fn mock_task(&self) -> &MockExecutor
pub fn mock_task(&self) -> &MockExecutor
Method borrowing MockRuntime::task
field.
Tasks
Sourcepub fn mock_sleep(&self) -> &SimpleMockTimeProvider
pub fn mock_sleep(&self) -> &SimpleMockTimeProvider
Method borrowing MockRuntime::sleep
field.
Time provider
Sourcepub fn mock_net(&self) -> &MockNetProvider
pub fn mock_net(&self) -> &MockNetProvider
Method borrowing MockRuntime::net
field.
Net provider
Source§impl MockRuntime
impl MockRuntime
Sourcepub fn builder() -> MockRuntimeBuilder
pub fn builder() -> MockRuntimeBuilder
Return a builder, for creating a MockRuntime
with some parameters manually configured
Sourcepub fn test_with_various<TC, FUT>(test_case: TC)
pub fn test_with_various<TC, FUT>(test_case: TC)
Run a test case with a variety of runtime parameters, to try to find bugs
test_case
is an async closure which receives a MockRuntime
.
It will be run with a number of differently configured executors.
Each run will be preceded by an eprintln!
showing the runtime configuration.
§Variations
The only variation currently implemented is this:
Both FIFO and LIFO scheduling policies are tested, in the hope that this will help discover ordering-dependent bugs.
Sourcepub fn try_test_with_various<TC, FUT, E>(test_case: TC) -> Result<(), E>
pub fn try_test_with_various<TC, FUT, E>(test_case: TC) -> Result<(), E>
Run a faillible test case with a variety of runtime parameters, to try to find bugs
test_case
is an async closure which receives a MockRuntime
.
It will be run with a number of differently configured executors.
This function accepts a fallible closure,
and returns the first Err
to the caller.
See test_with_various()
for more details.
Sourcepub fn spawn_identified(
&self,
desc: impl Display,
fut: impl Future<Output = ()> + Send + 'static,
) -> impl Debug + Clone + Send + 'static
pub fn spawn_identified( &self, desc: impl Display, fut: impl Future<Output = ()> + Send + 'static, ) -> impl Debug + Clone + Send + 'static
Spawn a task and return something to identify it
Sourcepub fn spawn_join<T: Debug + Send + 'static>(
&self,
desc: impl Display,
fut: impl Future<Output = T> + Send + 'static,
) -> impl Future<Output = T>
pub fn spawn_join<T: Debug + Send + 'static>( &self, desc: impl Display, fut: impl Future<Output = T> + Send + 'static, ) -> impl Future<Output = T>
Spawn a task and return its output for further usage
Sourcepub async fn advance_until_stalled(&self)
pub async fn advance_until_stalled(&self)
Run tasks and advance time, until every task except this one is waiting
On return the other tasks won’t be waiting on timeouts, since time will be advanced as needed.
Therefore the other tasks (if any) will be waiting for something that won’t happen by itself, such as a provocation via their APIs from this task.
§Panics
Sourcepub async fn progress_until_stalled(&self)
pub async fn progress_until_stalled(&self)
Run tasks in the current executor until every task except this one is waiting
Calls MockExecutor::progress_until_stalled()
.
§Restriction - no automatic time advance
The mocked time will not be automatically advanced.
Usually
(and especially if the tasks under test are waiting for timeouts or periodic events)
you must use
advance_by()
or
advance_until()
to ensure the simulated time progresses as required.
§Panics
Might malfunction or panic if more than one such call is running at once.
(Ie, you must .await
or drop the returned Future
before calling this method again.)
Must be called and awaited within a future being run by self
.
Sourcepub async fn advance_until(&self, limit: Instant) -> Option<Duration>
pub async fn advance_until(&self, limit: Instant) -> Option<Duration>
Run tasks and advance time up to at most limit
Will return when all other tasks are either:
- Waiting on a timeout that will fire strictly after
limit
, (return value is the time until the earliest such) - Waiting for something else that won’t happen by itself.
(return value is
None
)
Like advance_until_stalled
but stops when the mock time reaches limit
.
§Panics
Panics if the time somehow advances beyond limit
.
(This function won’t do that, but maybe it was beyond limit
on entry,
or another task advanced the clock.)
And, see progress_until_stalled
Sourceasync fn advance_inner<B>(
&self,
body: impl FnMut() -> ControlFlow<B, Duration>,
) -> B
async fn advance_inner<B>( &self, body: impl FnMut() -> ControlFlow<B, Duration>, ) -> B
Advance time, firing events and other tasks - internal implementation
Common code for advance_*
.
body
will called after progress_until_stalled
.
It should examine the simulated time, and the next timeout,
and decide what to do - returning
Break
to break the loop, or
Continue
giving the Duration
by which to advance time and go round again.
Sourcepub async fn advance_by(&self, dur: Duration) -> Option<Duration>
pub async fn advance_by(&self, dur: Duration) -> Option<Duration>
Advances time by dur
, firing time events and other tasks in order
Prefer this to SimpleMockTimeProvider::advance()
;
it works more faithfully.
Specifically, it advances time in successive stages, so that timeouts occur sequentially, in the right order.
§Panics
Can panic if the mock time is advanced by other tasks.
And, see progress_until_stalled
Sourcepub fn jump_wallclock(&self, new_wallclock: SystemTime)
pub fn jump_wallclock(&self, new_wallclock: SystemTime)
Sourcepub fn time_until_next_timeout(&self) -> Option<Duration>
pub fn time_until_next_timeout(&self) -> Option<Duration>
Return the amount of virtual time until the next timeout should elapse.
If there are no more timeouts, return None.
If the next
timeout should elapse right now, return Some(0).
However, if other tasks are proceeding,
typically in that situation those other tasks will wake,
so a Some(0)
return won’t be visible.
In test cases, detect immediate timeouts by detecting
what your task does after the timeout occurs.
Likewise whether this function returns None
or Some(...)
can depend on whether tasks have actually yet polled various futures.
The answer should be correct after
progress_until_stalled
.
Trait Implementations§
Source§impl Blocking for MockRuntime
impl Blocking for MockRuntime
Source§type ThreadHandle<T: Send + 'static> = <MockExecutor as Blocking>::ThreadHandle<T>
type ThreadHandle<T: Send + 'static> = <MockExecutor as Blocking>::ThreadHandle<T>
spawn_blocking
Source§fn spawn_blocking<F, T>(
&self,
f: F,
) -> <MockExecutor as Blocking>::ThreadHandle<T>
fn spawn_blocking<F, T>( &self, f: F, ) -> <MockExecutor as Blocking>::ThreadHandle<T>
Source§fn reenter_block_on<F>(&self, future: F) -> F::Output
fn reenter_block_on<F>(&self, future: F) -> F::Output
Blocking::spawn_blocking
Read moreSource§impl Clone for MockRuntime
impl Clone for MockRuntime
Source§fn clone(&self) -> MockRuntime
fn clone(&self) -> MockRuntime
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read moreSource§impl CoarseTimeProvider for MockRuntime
impl CoarseTimeProvider for MockRuntime
Source§fn now_coarse(&self) -> CoarseInstant
fn now_coarse(&self) -> CoarseInstant
CoarseTimeProvider
’s view of the current instant. Read moreSource§impl Debug for MockRuntime
impl Debug for MockRuntime
Source§impl Default for MockRuntime
impl Default for MockRuntime
Source§fn default() -> MockRuntime
fn default() -> MockRuntime
Source§impl NetStreamProvider<SocketAddr> for MockRuntime
impl NetStreamProvider<SocketAddr> for MockRuntime
Source§type Stream = FakeStream
type Stream = FakeStream
Self::connect()
.Source§type Listener = FakeListener<SocketAddr>
type Listener = FakeListener<SocketAddr>
Self::listen()
.Source§fn connect<'life0, 'life1, 'async_trait>(
&'life0 self,
_addr: &'life1 SocketAddr,
) -> Pin<Box<dyn Future<Output = IoResult<Self::Stream>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn connect<'life0, 'life1, 'async_trait>(
&'life0 self,
_addr: &'life1 SocketAddr,
) -> Pin<Box<dyn Future<Output = IoResult<Self::Stream>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§impl NetStreamProvider for MockRuntime
impl NetStreamProvider for MockRuntime
Source§type Stream = <MockNetProvider as NetStreamProvider>::Stream
type Stream = <MockNetProvider as NetStreamProvider>::Stream
Self::connect()
.Source§type Listener = <MockNetProvider as NetStreamProvider>::Listener
type Listener = <MockNetProvider as NetStreamProvider>::Listener
Self::listen()
.Source§fn connect<'life0, 'life1, 'async_trait>(
&'life0 self,
addr: &'life1 SocketAddr,
) -> Pin<Box<dyn Future<Output = IoResult<Self::Stream>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn connect<'life0, 'life1, 'async_trait>(
&'life0 self,
addr: &'life1 SocketAddr,
) -> Pin<Box<dyn Future<Output = IoResult<Self::Stream>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§impl SleepProvider for MockRuntime
impl SleepProvider for MockRuntime
Source§type SleepFuture = <SimpleMockTimeProvider as SleepProvider>::SleepFuture
type SleepFuture = <SimpleMockTimeProvider as SleepProvider>::SleepFuture
SleepProvider::sleep()
Source§fn sleep(&self, dur: Duration) -> Self::SleepFuture
fn sleep(&self, dur: Duration) -> Self::SleepFuture
duration
has
elapsed.Source§fn wallclock(&self) -> SystemTime
fn wallclock(&self) -> SystemTime
Source§fn block_advance<T: Into<String>>(&self, reason: T)
fn block_advance<T: Into<String>>(&self, reason: T)
Source§fn release_advance<T: Into<String>>(&self, reason: T)
fn release_advance<T: Into<String>>(&self, reason: T)
block_advance
no
longer exists, and it’s fine to move time forward if nothing else is blocking advances. Read moreSource§fn allow_one_advance(&self, dur: Duration)
fn allow_one_advance(&self, dur: Duration)
block_advance
API has been used. Read moreSource§impl Spawn for MockRuntime
impl Spawn for MockRuntime
Source§impl TlsProvider<<MockNetProvider as NetStreamProvider>::Stream> for MockRuntime
impl TlsProvider<<MockNetProvider as NetStreamProvider>::Stream> for MockRuntime
Source§type Connector = <MockNetProvider as TlsProvider<<MockNetProvider as NetStreamProvider>::Stream>>::Connector
type Connector = <MockNetProvider as TlsProvider<<MockNetProvider as NetStreamProvider>::Stream>>::Connector
Source§type TlsStream = <MockNetProvider as TlsProvider<<MockNetProvider as NetStreamProvider>::Stream>>::TlsStream
type TlsStream = <MockNetProvider as TlsProvider<<MockNetProvider as NetStreamProvider>::Stream>>::TlsStream
Source§fn tls_connector(&self) -> Self::Connector
fn tls_connector(&self) -> Self::Connector
Source§fn supports_keying_material_export(&self) -> bool
fn supports_keying_material_export(&self) -> bool
Source§impl ToplevelBlockOn for MockRuntime
impl ToplevelBlockOn for MockRuntime
Source§impl UdpProvider for MockRuntime
impl UdpProvider for MockRuntime
Source§type UdpSocket = <MockNetProvider as UdpProvider>::UdpSocket
type UdpSocket = <MockNetProvider as UdpProvider>::UdpSocket
Self::bind()
Auto Trait Implementations§
impl Freeze for MockRuntime
impl RefUnwindSafe for MockRuntime
impl Send for MockRuntime
impl Sync for MockRuntime
impl Unpin for MockRuntime
impl UnwindSafe for MockRuntime
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more§impl<T> NetStreamProvider<SocketAddr> for T
impl<T> NetStreamProvider<SocketAddr> for T
§type Stream = Stream
type Stream = Stream
Self::connect()
.§type Listener = Listener
type Listener = Listener
Self::listen()
.§impl<R> RuntimeSubstExt for Rwhere
R: Runtime,
impl<R> RuntimeSubstExt for Rwhere
R: Runtime,
§fn with_tcp_provider<T>(
&self,
new_tcp: T,
) -> CompoundRuntime<R, R, R, T, R, R, R>
fn with_tcp_provider<T>( &self, new_tcp: T, ) -> CompoundRuntime<R, R, R, T, R, R, R>
§fn with_sleep_provider<T>(
&self,
new_sleep: T,
) -> CompoundRuntime<R, T, R, R, R, R, R>
fn with_sleep_provider<T>( &self, new_sleep: T, ) -> CompoundRuntime<R, T, R, R, R, R, R>
§fn with_coarse_time_provider<T>(
&self,
new_coarse_time: T,
) -> CompoundRuntime<R, R, T, R, R, R, R>
fn with_coarse_time_provider<T>( &self, new_coarse_time: T, ) -> CompoundRuntime<R, R, T, R, R, R, R>
§impl<T> SleepProviderExt for Twhere
T: SleepProvider,
impl<T> SleepProviderExt for Twhere
T: SleepProvider,
§fn timeout<F>(
&self,
duration: Duration,
future: F,
) -> Timeout<F, Self::SleepFuture> ⓘwhere
F: Future,
fn timeout<F>(
&self,
duration: Duration,
future: F,
) -> Timeout<F, Self::SleepFuture> ⓘwhere
F: Future,
§fn sleep_until_wallclock(
&self,
when: SystemTime,
) -> SleepUntilWallclock<'_, Self>
fn sleep_until_wallclock( &self, when: SystemTime, ) -> SleepUntilWallclock<'_, Self>
when
or later, trying to
recover from clock jumps. Read more§impl<Sp> SpawnExt for Sp
impl<Sp> SpawnExt for Sp
§fn spawn<Fut>(&self, future: Fut) -> Result<(), SpawnError>
fn spawn<Fut>(&self, future: Fut) -> Result<(), SpawnError>
()
to
completion. Read more