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
impl Reply
Sourcepub fn ok(stdout: impl Into<String>) -> Self
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).
Sourcepub fn fail(code: i32, stderr: impl Into<String>) -> Self
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.
Sourcepub fn timeout() -> Self
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.
Sourcepub fn inactivity_timeout() -> Self
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.
Sourcepub fn signalled(signal: Option<i32>) -> Self
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.
Sourcepub fn pending() -> Self
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.
Sourcepub fn dialog(prompt: impl Into<String>, response: impl Into<String>) -> Self
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"));
});Sourcepub fn lines<I, S>(lines: I) -> Self
pub fn lines<I, S>(lines: I) -> Self
A successful reply whose stdout is lines joined with \n — reads
naturally for scripted streaming (start → stdout_lines yields
exactly these lines), and is equivalent to ok with the
joined text for the bulk path.
Sourcepub fn with_line_delay(self, delay: Duration) -> Self
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.
Sourcepub fn with_stdout(self, stdout: impl Into<String>) -> Self
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.
Sourcepub fn with_stderr(self, stderr: impl Into<String>) -> Self
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.
Sourcepub fn not_found() -> Self
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.
Sourcepub fn spawn_error(kind: ErrorKind, message: impl Into<String>) -> Self
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.