Skip to main content

Reply

Struct Reply 

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

A canned reply: stdout/stderr text plus an exit code (or a timed-out run, or a parked-until-cancelled call).

Implementations§

Source§

impl Reply

Source

pub fn ok(stdout: impl Into<String>) -> Self

A successful reply (exit code 0) producing stdout. Pair with with_stderr to also give the successful reply stderr text (e.g. warnings a CLI writes on exit 0).

Source

pub fn fail(code: i32, stderr: impl Into<String>) -> Self

A failing reply with exit code and stderr text. Use with_stderr afterwards to override this stderr text.

Source

pub fn timeout() -> Self

A timed-out reply — drives the timeout path so a test can assert that a command which exceeds its deadline surfaces as ErrorReason::Timeout.

On both the bulk verbs (output_string and friends) and a scripted start this resolves immediately as timed-out, so it asserts the timeout classification without exercising the real deadline race. To model “hangs until the command’s timeout fires,” script pending and set a Command::timeout: the call then parks until the deadline and resolves timed-out — on the bulk verbs and start alike, exactly as the watchdog would drive it for a live child.

Source

pub fn inactivity_timeout() -> Self

An inactivity-timed-out reply — the process stopped because it produced no stdout or stderr for the configured inactivity window.

Like timeout, this resolves immediately and is useful for testing classification. Use pending with Command::inactivity_timeout to exercise the watchdog race itself.

Source

pub fn signalled(signal: Option<i32>) -> Self

A signal-killed reply — the process was terminated by a signal. Drives the Outcome::Signalled path so a test can assert signal-kill handling. Pass Some(n) when the specific signal number matters (e.g. Some(9) for SIGKILL); pass None when only “killed by a signal” matters.

Source

pub fn pending() -> Self

A reply that parks the call until its cancellation token fires or its Command::timeout deadline elapses — the hermetic mirror of a live long-runner that is either cancelled or killed for overrunning its deadline, for testing that an orchestration genuinely cancels (and cleans up) or bounds a hang, not just that it formats a canned error. A firing token resolves with ErrorReason::Cancelled naming the program; a firing deadline resolves as a timed-out run (Outcome::TimedOut), exactly as a timeout reply would.

The token is the matched command’s — set per command (Command::cancel_on) or client-wide (CliClient::default_cancel_on); the deadline is its timeout. Whichever fires first wins (a tie favors cancellation), on both the bulk output_string verb and a scripted start, just as the live runner races its cancel token against its deadline. A pending reply for a command with neither a token nor a timeout parks forever, like a hung child no one can cancel and no deadline bounds — deliberate; pair it with a token, a Command::timeout (or a test timeout) by design.

Source

pub fn dialog(prompt: impl Into<String>, response: impl Into<String>) -> Self

A hermetic interactive dialog reply for a streaming (start) run: on start the scripted child writes prompt to stdout, waits for the caller to answer over take_stdin, then writes response and exits 0 — modeling one prompt/answer turn with no real pipe or pty.

This is the double for the wait_for_output idiom: give prompt no trailing newline so it surfaces as the un-terminated tail wait_for_output matches (a Password: a real child would block on), and give response whatever the child prints next — itself an un-terminated tail (e.g. a next prompt) so a second wait_for_output can see it. Build the command with use_pty (the motivating case) and/or keep_stdin_open; the double hands back a working take_stdin either way.

The response is written the instant stdin is received (not on a timer), so the exchange is deterministic without a paused clock. On the bulk output_string path there is no interactive stdin to answer, so a dialog reply degrades to its prompt immediately followed by response as ordinary output.

use processkit::{Command, ProcessRunner};
use processkit::testing::{Reply, ScriptedRunner};
use std::time::Duration;

let rt = tokio::runtime::Builder::new_current_thread()
    .enable_all().build().unwrap();
rt.block_on(async {
    let runner = ScriptedRunner::new()
        .fallback(Reply::dialog("Password: ", "granted> "));
    let mut run = runner
        .start(&Command::new("login").keep_stdin_open())
        .await
        .expect("start dialog");
    // Wait for the un-terminated prompt, then answer it.
    let prompt = run
        .wait_for_output(|tail| tail.contains("Password:"), Duration::from_secs(1))
        .await
        .expect("prompt");
    assert!(prompt.contains("Password:"));
    run.take_stdin().expect("stdin").write_line("s3cret").await.expect("answer");
    let cont = run
        .wait_for_output(|tail| tail.contains("granted"), Duration::from_secs(1))
        .await
        .expect("continuation");
    assert!(cont.contains("granted"));
});
Source

pub fn lines<I, S>(lines: I) -> Self
where I: IntoIterator<Item = S>, S: Into<String>,

A successful reply whose stdout is lines joined with \n — reads naturally for scripted streaming (startstdout_lines yields exactly these lines), and is equivalent to ok with the joined text for the bulk path.

Source

pub fn with_line_delay(self, delay: Duration) -> Self

On a scripted start, sleep delay before each output frame — so a hermetic streaming test can observe genuinely incremental delivery (deterministic under #[tokio::test(start_paused = true)]). The scripted run “exits” after the last line. Ignored by the bulk output_string path.

Source

pub fn with_stdout(self, stdout: impl Into<String>) -> Self

Attach stdout to a reply — e.g. the CONFLICT … text git merge writes to stdout on a failing reply, so a test can exercise ErrorReason::Exit’s stdout field / ProcessResult::diagnostic.

Source

pub fn with_stderr(self, stderr: impl Into<String>) -> Self

Attach stderr to a reply — e.g. the warnings a CLI like git, a compiler, or a linter writes to stderr even on a successful (exit 0) run, so a test can exercise that stderr without misleadingly reaching for fail.

Source

pub fn not_found() -> Self

A reply that fails as if the program could not be located at all — not installed, not on PATH, or the given path doesn’t resolve — driving ErrorReason::NotFound (is_not_found() == true), the hermetic mirror of a live spawn hitting a missing binary. A rule miss on the real runner instead lands on ErrorReason::Spawn (is_not_found() == false — a double-specific “no rule matched” config error, not a modeled spawn failure), so script this reply explicitly to test a “tool not installed → fallback” branch: .on(["rg", …], Reply::not_found()) (or .fallback(Reply::not_found())) lets a caller’s if err.is_not_found() { try_fallback_tool() } branch run hermetically.

Source

pub fn spawn_error(kind: ErrorKind, message: impl Into<String>) -> Self

A reply that fails at spawn time with a generic OS-level error — permission denied, a busy executable, and so on — as opposed to not_found’s “the program doesn’t exist at all”. Drives ErrorReason::Spawn with an io::Error of the given kind and message, so classifiers built on it (e.g. Error::is_permission_denied) answer on the fake exactly as they would for a live spawn failure.

Trait Implementations§

Source§

impl Clone for Reply

Source§

fn clone(&self) -> Reply

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl Debug for Reply

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Reply

§

impl RefUnwindSafe for Reply

§

impl Send for Reply

§

impl Sync for Reply

§

impl Unpin for Reply

§

impl UnsafeUnpin for Reply

§

impl UnwindSafe for Reply

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> Any for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Source§

fn type_name(&self) -> &'static str

Source§

impl<T> AnySync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

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> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

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

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

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

impl<T> WithSubscriber for T

Source§

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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