Struct MockRuntime

Source
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 of coarsetime. Instead, use SleepProvider and CoarseTimeProvider 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

Source

pub fn mock_task(&self) -> &MockExecutor

Method borrowing MockRuntime::task field. Tasks

Source

pub fn mock_sleep(&self) -> &SimpleMockTimeProvider

Method borrowing MockRuntime::sleep field. Time provider

Source

pub fn mock_net(&self) -> &MockNetProvider

Method borrowing MockRuntime::net field. Net provider

Source§

impl MockRuntime

Source

pub fn new() -> Self

Create a new MockRuntime with default parameters

Source

pub fn builder() -> MockRuntimeBuilder

Return a builder, for creating a MockRuntime with some parameters manually configured

Source

pub fn test_with_various<TC, FUT>(test_case: TC)
where TC: FnMut(MockRuntime) -> FUT, FUT: Future<Output = ()>,

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.

Source

pub fn try_test_with_various<TC, FUT, E>(test_case: TC) -> Result<(), E>
where TC: FnMut(MockRuntime) -> FUT, FUT: Future<Output = 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.

Source

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

See MockExecutor::spawn_identified()

Source

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

See MockExecutor::spawn_join()

Source

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

See progress_until_stalled

Source

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.

Source

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

Source

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.

Source

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

Source

pub fn jump_wallclock(&self, new_wallclock: SystemTime)

Source

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

Source§

type ThreadHandle<T: Send + 'static> = <MockExecutor as Blocking>::ThreadHandle<T>

Future from spawn_blocking
Source§

fn spawn_blocking<F, T>( &self, f: F, ) -> <MockExecutor as Blocking>::ThreadHandle<T>
where F: FnOnce() -> T + Send + 'static, T: Send + 'static,

Spawn a thread for blocking IO or CPU-bound work. Read more
Source§

fn reenter_block_on<F>(&self, future: F) -> F::Output
where F: Future, F::Output: Send + 'static,

Block on a future, from within Blocking::spawn_blocking Read more
§

fn blocking_io<F, T>(&self, f: F) -> impl Future<Output = T>
where F: FnOnce() -> T + Send + 'static, T: Send + 'static,

Perform some blocking IO from an async future Read more
Source§

impl Clone for MockRuntime

Source§

fn clone(&self) -> MockRuntime

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl CoarseTimeProvider for MockRuntime

Source§

fn now_coarse(&self) -> CoarseInstant

Return the CoarseTimeProvider’s view of the current instant. Read more
Source§

impl Debug for MockRuntime

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for MockRuntime

Source§

fn default() -> MockRuntime

Returns the “default value” for a type. Read more
Source§

impl NetStreamProvider<SocketAddr> for MockRuntime

Source§

type Stream = FakeStream

The type for the connections returned by Self::connect().
Source§

type Listener = FakeListener<SocketAddr>

The type for the listeners returned by 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,

Launch a connection connection to a given socket address. Read more
Source§

fn listen<'life0, 'life1, 'async_trait>( &'life0 self, _addr: &'life1 SocketAddr, ) -> Pin<Box<dyn Future<Output = IoResult<Self::Listener>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Open a listener on a given socket address.
Source§

impl NetStreamProvider for MockRuntime

Source§

type Stream = <MockNetProvider as NetStreamProvider>::Stream

The type for the connections returned by Self::connect().
Source§

type Listener = <MockNetProvider as NetStreamProvider>::Listener

The type for the listeners returned by 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,

Launch a connection connection to a given socket address. Read more
Source§

fn listen<'life0, 'life1, 'async_trait>( &'life0 self, addr: &'life1 SocketAddr, ) -> Pin<Box<dyn Future<Output = IoResult<Self::Listener>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Open a listener on a given socket address.
Source§

impl SleepProvider for MockRuntime

Source§

type SleepFuture = <SimpleMockTimeProvider as SleepProvider>::SleepFuture

A future returned by SleepProvider::sleep()
Source§

fn sleep(&self, dur: Duration) -> Self::SleepFuture

Return a future that will be ready after duration has elapsed.
Source§

fn now(&self) -> Instant

Return the SleepProvider’s view of the current instant. Read more
Source§

fn wallclock(&self) -> SystemTime

Return the SleepProvider’s view of the current wall-clock time. Read more
Source§

fn block_advance<T: Into<String>>(&self, reason: T)

Signify that a test running under mock time shouldn’t advance time yet, with a given unique reason string. This is useful for making sure (mock) time doesn’t advance while things that might require some (real-world) time to complete do so, such as spawning a task on another thread. Read more
Source§

fn release_advance<T: Into<String>>(&self, reason: T)

Signify that the reason to withhold time advancing provided in a call to block_advance no longer exists, and it’s fine to move time forward if nothing else is blocking advances. Read more
Source§

fn allow_one_advance(&self, dur: Duration)

Allow a test running under mock time to advance time by the provided duration, even if the above block_advance API has been used. Read more
Source§

impl Spawn for MockRuntime

Source§

fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError>

Spawns a future that will be run to completion. Read more
§

fn status(&self) -> Result<(), SpawnError>

Determines whether the executor is able to spawn new tasks. Read more
Source§

impl TlsProvider<<MockNetProvider as NetStreamProvider>::Stream> for MockRuntime

Source§

type Connector = <MockNetProvider as TlsProvider<<MockNetProvider as NetStreamProvider>::Stream>>::Connector

The Connector object that this provider can return.
Source§

type TlsStream = <MockNetProvider as TlsProvider<<MockNetProvider as NetStreamProvider>::Stream>>::TlsStream

The type of the stream returned by that connector.
Source§

fn tls_connector(&self) -> Self::Connector

Return a TLS connector for use with this runtime.
Source§

fn supports_keying_material_export(&self) -> bool

Return true iff the keying material exporters (RFC 5705) is supported.
Source§

impl ToplevelBlockOn for MockRuntime

Source§

fn block_on<F: Future>(&self, future: F) -> F::Output

Run future until it is ready, and return its output. Read more
Source§

impl UdpProvider for MockRuntime

Source§

type UdpSocket = <MockNetProvider as UdpProvider>::UdpSocket

The type of Udp Socket returned by Self::bind()
Source§

fn bind<'life0, 'life1, 'async_trait>( &'life0 self, addr: &'life1 SocketAddr, ) -> Pin<Box<dyn Future<Output = IoResult<Self::UdpSocket>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Bind a local port to send and receive packets from

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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

§

type Stream = Stream

The type for the connections returned by Self::connect().
§

type Listener = Listener

The type for the listeners returned by Self::listen().
§

fn connect<'life0, 'life1, 'async_trait>( &'life0 self, addr: &'life1 SocketAddr, ) -> Pin<Box<dyn Future<Output = Result<Stream, Error>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, T: 'async_trait,

Launch a connection connection to a given socket address. Read more
§

fn listen<'life0, 'life1, 'async_trait>( &'life0 self, addr: &'life1 SocketAddr, ) -> Pin<Box<dyn Future<Output = Result<Listener, Error>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, T: 'async_trait,

Open a listener on a given socket address.
§

impl<R> RuntimeSubstExt for R
where R: Runtime,

§

fn with_tcp_provider<T>( &self, new_tcp: T, ) -> CompoundRuntime<R, R, R, T, R, R, R>

Return a new runtime wrapping this runtime, but replacing its TCP NetStreamProvider.
§

fn with_sleep_provider<T>( &self, new_sleep: T, ) -> CompoundRuntime<R, T, R, R, R, R, R>

Return a new runtime wrapping this runtime, but replacing its SleepProvider.
§

fn with_coarse_time_provider<T>( &self, new_coarse_time: T, ) -> CompoundRuntime<R, R, T, R, R, R, R>

Return a new runtime wrapping this runtime, but replacing its CoarseTimeProvider.
§

impl<T> SleepProviderExt for T
where T: SleepProvider,

§

fn timeout<F>( &self, duration: Duration, future: F, ) -> Timeout<F, Self::SleepFuture>
where F: Future,

Wrap a Future with a timeout. Read more
§

fn sleep_until_wallclock( &self, when: SystemTime, ) -> SleepUntilWallclock<'_, Self>

Pause until the wall-clock is at when or later, trying to recover from clock jumps. Read more
§

impl<Sp> SpawnExt for Sp
where Sp: Spawn + ?Sized,

§

fn spawn<Fut>(&self, future: Fut) -> Result<(), SpawnError>
where Fut: Future<Output = ()> + Send + 'static,

Spawns a task that polls the given future with output () to completion. Read more
§

fn spawn_with_handle<Fut>( &self, future: Fut, ) -> Result<RemoteHandle<<Fut as Future>::Output>, SpawnError>
where Fut: Future + Send + 'static, <Fut as Future>::Output: Send,

Spawns a task that polls the given future to completion and returns a future that resolves to the spawned future’s output. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

impl<T> ErasedDestructor for T
where T: 'static,

§

impl<T> MaybeSendSync for T

§

impl<T> Runtime for T

§

impl<T> ToplevelRuntime for T