Struct MockExecutor

Source
pub struct MockExecutor { /* private fields */ }
Expand description

Executor for running tests with mocked environment

For test cases which don’t actually wait for anything in the real world.

This is the executor. It implements [Spawn] and ToplevelBlockOn

It will usually be used as part of a MockRuntime.

To run futures, call ToplevelBlockOn::block_on

§Restricted environment

Tests run with this executor must not attempt to block on anything “outside”: every future that anything awaits must (eventually) be woken directly by some other task in the same test case.

(By directly we mean that the Waker::wake call is made by that waking future, before that future itself awaits anything.)

§Panics

The executor will panic if the toplevel future (passed to block_on) doesn’t complete (without externally blocking), but instead waits for something.

The executor will malfunction or panic if reentered. (Eg, if block_on is reentered.)

Implementations§

Source§

impl MockExecutor

Source

pub fn new() -> Self

Make a MockExecutor with default parameters

Source

pub fn with_scheduling(scheduling: SchedulingPolicy) -> Self

Make a MockExecutor with a specific SchedulingPolicy

Source§

impl MockExecutor

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

desc should Display as some kind of short string (ideally without spaces) and will be used in the Debug impl and trace log messages from MockExecutor.

The returned value is an opaque task identifier which is very cheap to clone and which can be used by the caller in debug logging, if it’s desired to correlate with the debug output from MockExecutor. Most callers will want to ignore it.

This method is infallible. (The MockExecutor cannot be shut down.)

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

desc should Display as some kind of short string (ideally without spaces) and will be used in the Debug impl and trace log messages from MockExecutor.

Source§

impl MockExecutor

Source

pub fn progress_until_stalled(&self) -> impl Future<Output = ()>

Run tasks in the current executor until every other task is waiting

§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§

impl MockExecutor

Source

pub fn subthread_spawn<T: Send + 'static>( &self, desc: impl Display, call: impl FnOnce() -> T + Send + 'static, ) -> impl Future<Output = Result<T, Box<dyn Any + Send>>> + Unpin + Send + Sync + 'static

Spawn a “Subthread”, for processing in a sync context

call will be run on a separate thread, called a “Subthread”.

But it will not run simultaneously with the executor, nor with other Subthreads. So Subthreads are somewhat like coroutines.

call must be capable of making progress without waiting for any other Subthreads. call may wait for async futures, using subthread_block_on_future.

Subthreads may be used for cpubound activity, or synchronous IO (such as large volumes of disk activity), provided that the synchronous code will reliably make progress, without waiting (directly or indirectly) for any async task or Subthread - except via subthread_block_on_future.

§Subthreads vs raw std::thread threads

Programs using MockExecutor may use std::thread threads directly. However, this is not recommended. There are severe limitations:

  • Only a Subthread can re-enter the async context from sync code: this must be done with using subthread_block_on_future. (Re-entering the executor with block_on is not allowed.)
  • If async tasks want to suspend waiting for synchronous code, the synchronous code must run on a Subthread. This allows the MockExecutor to know when that synchronous code is still making progress. (This is needed for progress_until_stalled and the facilities which use it, such as MockRuntime::advance_until_stalled.)
  • Subthreads never run in parallel - they only run as scheduled deterministically by the MockExecutor. So using Subthreads eliminates a source of test nonndeterminism. (Execution order is still varied due to explicitly varying the scheduling policy.)
§Panics, abuse, and malfunctions

If call panics and unwinds, spawn_subthread yields Err. The application code should to do something about it if this happens, typically, logging errors, tearing things down, or failing a test case.

If the executor doesn’t run, the subthread will not run either, and will remain stuck. (So, typically, if the thread supposed to run the executor panics, for example because a future or the executor itself panics, all the subthreads will become stuck - effectively, they’ll be leaked.)

spawn_subthread panics if OS thread spawning fails. (Like std::thread::spawn() does.)

MockExecutors will malfunction or panic if any executor invocation method (eg block_on) is called on a Subthread.

Source

pub fn subthread_block_on_future<T: Send + 'static>( &self, fut: impl Future<Output = T>, ) -> T

Call an async Future from a Subthread

Blocks the Subthread, and arranges to run async tasks, including fut, until fut completes.

fut is polled on the executor thread, not on the Subthread. (We may change that in the future, allowing passing a non-Send future.)

§Panics, abuse, and malfunctions

subthread_block_on_future will malfunction or panic if called on a thread that isn’t a Subthread from the same MockExecutor (ie a thread made with spawn_subthread).

If fut itself panics, the executor will panic.

If the executor isn’t running, subthread_block_on_future will hang indefinitely. See spawn_subthread.

Source§

impl MockExecutor

Source

pub fn n_tasks(&self) -> usize

Return the number of tasks running in this executor

One possible use is for a test case to check that task(s) that ought to have exited, have indeed done so.

In the usual case, the answer will be at least 1, because it counts the future passed to block_on (perhaps via MockRuntime::test_with_various).

Source§

impl MockExecutor

Source

pub fn debug_dump(&self)

Dump the executor’s state including backtraces of waiting tasks, to stderr

This is considerably more extensive than simply MockExecutor as Debug.

(This is a convenience method, which wraps MockExecutor::as_debug_dump().

§Backtrace salience (possible spurious traces)

Summary

The technique used to capture backtraces when futures sleep is not 100% exact. It will usually show all the actual sleeping sites, but it might also show other backtraces which were part of the implementation of some complex relevant future.

Details

When a future’s implementation wants to sleep, it needs to record the Waker (from the Context) so that the “other end” can call .wake() on it later, when the future should be woken.

Since Context.waker() gives &Waker, borrowed from the Context, the future must clone the Waker, and it must do so in within the poll() call.

A future which is waiting in a select! will typically show multiple traces, one for each branch. But, if a future sleeps on one thing, and then when polled again later, sleeps on something different, without waking up in between, both backtrace locations will be shown. And, a complicated future contraption might clone the Waker more times. So not every backtrace will necessarily be informative.

§Panics

Panics on write errors.

Source

pub fn as_debug_dump(&self) -> DebugDump<'_>

Dump the executor’s state including backtraces of waiting tasks

This is considerably more extensive than simply MockExecutor as Debug.

Returns an object for formatting with Debug. To simply print the dump to stderr (eg in a test), use .debug_dump().

Backtrace salience (possible spurious traces) - see .debug_dump().

Trait Implementations§

Source§

impl Blocking for MockExecutor

Source§

type ThreadHandle<T: Send + 'static> = Map<Fuse<Receiver<T>>, Box<dyn FnOnce(Result<T, Canceled>) -> T>>

Future from spawn_blocking
Source§

fn spawn_blocking<F, T>(&self, f: F) -> Self::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
Source§

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 MockExecutor

Source§

fn clone(&self) -> MockExecutor

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 Debug for MockExecutor

Source§

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

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

impl Default for MockExecutor

Source§

fn default() -> MockExecutor

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

impl Spawn for MockExecutor

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 ToplevelBlockOn for MockExecutor

Source§

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

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

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§

fn __clone_box(&self, _: Private) -> *mut ()

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<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