#![cfg(all(feature = "embed", feature = "test-support"))]
use mxsh::ShellBuilder;
use mxsh::ast::Program;
use mxsh::embed::{DeferredWorkKind, StdioConfig, TraceEvent, VariableAttributes};
use mxsh::runtime::testing::{InMemoryRuntime, StringStdioOut};
#[test]
fn runtime_owning_shell_runs_and_collects_trace() {
let stdout = StringStdioOut::new();
let mut shell = ShellBuilder::new()
.env("GREETING", "hello", VariableAttributes::EXPORT)
.stdio(StdioConfig {
stdout: stdout.fd(),
..StdioConfig::default()
})
.build(InMemoryRuntime::new())
.expect("shell should build");
let outcome = shell.run("echo \"$GREETING world\"");
assert_eq!(outcome.status, 0);
assert!(outcome.diagnostics.is_empty());
assert_eq!(stdout.collect(), "hello world\n");
assert_eq!(outcome.trace.len(), 2);
assert!(matches!(
&outcome.trace[0],
TraceEvent::RunStarted {
raw_command: Some(command),
..
} if command == "echo \"$GREETING world\""
));
assert!(matches!(
&outcome.trace[1],
TraceEvent::RunFinished { status: 0, .. }
));
}
#[test]
fn embedded_run_trace_uses_exit_trap_status() {
let mut shell = ShellBuilder::new()
.build(InMemoryRuntime::new())
.expect("shell should build");
let outcome = shell.run("trap 'exit 7' EXIT; exit 3");
assert_eq!(outcome.status, 7);
assert_eq!(outcome.exit_code, Some(7));
assert!(matches!(
outcome.trace.last(),
Some(TraceEvent::RunFinished { status: 7, .. })
));
}
#[test]
fn prepared_run_trace_uses_exit_trap_status() {
let mut shell = ShellBuilder::new()
.build(InMemoryRuntime::new())
.expect("shell should build");
let program = Program::parse("trap 'exit 9' EXIT; exit 2").expect("program should parse");
let prepared = shell.prepare(&program);
let outcome = shell.run_prepared(&prepared);
assert_eq!(outcome.status, 9);
assert_eq!(outcome.exit_code, Some(9));
assert!(matches!(
outcome.trace.last(),
Some(TraceEvent::RunFinished { status: 9, .. })
));
}
#[test]
fn run_outcome_collects_deferred_work_trace_without_manual_sinks() {
let stdout = StringStdioOut::new();
let mut runtime = InMemoryRuntime::new();
runtime.register_command("cat", |_argv, _env, _cwd, stdio| {
let input = stdio.stdin_fd.dup().expect("dup cat stdin").read_all();
let _ = stdio.stdout_fd.write_str(&input);
0
});
let mut shell = ShellBuilder::new()
.stdio(StdioConfig {
stdout: stdout.fd(),
..StdioConfig::default()
})
.build(runtime)
.expect("shell should build");
let outcome = shell.run("unset X; echo ${X:=hi} | cat");
assert_eq!(outcome.status, 0);
assert_eq!(stdout.collect(), "hi\n");
assert!(outcome.diagnostics.is_empty());
assert!(outcome.trace.iter().any(|event| {
matches!(
event,
TraceEvent::DeferredWorkStarted {
kind: DeferredWorkKind::ParameterExpansion,
..
}
)
}));
assert!(outcome.trace.iter().any(|event| {
matches!(
event,
TraceEvent::DeferredWorkFinished {
kind: DeferredWorkKind::ParameterExpansion,
status: 0,
..
}
)
}));
}