#![cfg(all(feature = "runtime", feature = "test-support"))]
use std::io;
use std::path::{Path, PathBuf};
use mxsh::runtime::Runtime;
use mxsh::runtime::fd::FileDescriptor;
use mxsh::runtime::process::{
ExternalCommand, ProcessEvent, ProcessHandle, RuntimeSignal, SpawnMode, SpawnStdio,
};
use mxsh::runtime::testing::{DeterministicRuntime, InMemoryRuntime};
fn fork_runtime<R: Runtime + Sized>(runtime: &R) -> R {
runtime.fork().expect("runtime fork should succeed")
}
fn resolve_program<R: Runtime>(runtime: &R, program: &str) -> PathBuf {
runtime
.resolve_command_path(program, "/usr/bin:/bin", Path::new("/"))
.expect("runtime path resolution should succeed")
}
fn run_command<R: Runtime>(runtime: &mut R, command: ExternalCommand) -> i32 {
let child = runtime
.spawn_external_command(&command, SpawnStdio::default(), &[], SpawnMode::Foreground)
.expect("runtime spawn should succeed");
runtime.wait_child(child.handle)
}
fn signal_process<R: Runtime>(runtime: &mut R) {
runtime
.signal_process_group(ProcessHandle::new(1), RuntimeSignal::Continue)
.expect("runtime signaling should succeed");
}
fn claim_and_release_foreground<R: Runtime>(runtime: &mut R) {
let guard = runtime
.claim_foreground(ProcessHandle::new(1), FileDescriptor::STDIN)
.expect("foreground claim should succeed");
runtime
.release_foreground(guard)
.expect("foreground release should succeed");
}
fn exec_is_explicit<R: Runtime>(runtime: &R) -> io::ErrorKind {
runtime
.exec_replace("echo", &["echo".to_string()], &[], Path::new("/"))
.expect_err("in-memory runtime should not support exec")
.kind()
}
#[test]
fn public_runtime_capabilities_are_composable() {
let mut runtime = InMemoryRuntime::new();
runtime.register_command("emit", |_argv, _env, _cwd, _stdio| 7);
let forked = fork_runtime(&runtime);
assert_eq!(resolve_program(&forked, "emit"), PathBuf::from("emit"));
let status = run_command(
&mut runtime,
ExternalCommand {
program: "emit".to_string(),
argv: vec!["emit".to_string()],
env: Vec::new(),
cwd: PathBuf::from("/"),
create_process_group: false,
join_process_group: None,
passed_fds: Vec::new(),
signal_plan: Default::default(),
},
);
assert_eq!(status, 7);
signal_process(&mut runtime);
claim_and_release_foreground(&mut runtime);
assert_eq!(exec_is_explicit(&runtime), io::ErrorKind::Unsupported);
}
#[test]
fn deterministic_runtime_fork_does_not_clone_queued_spawns() {
let mut runtime = DeterministicRuntime::new();
runtime.push_spawn(Some(42), [], [ProcessEvent::Exited(0)]);
let mut forked = runtime.fork().expect("runtime fork should succeed");
let err = forked
.spawn_external_command(
&ExternalCommand {
program: "echo".to_string(),
argv: vec!["echo".to_string()],
env: Vec::new(),
cwd: PathBuf::from("/"),
create_process_group: false,
join_process_group: None,
passed_fds: Vec::new(),
signal_plan: Default::default(),
},
SpawnStdio::default(),
&[],
SpawnMode::Foreground,
)
.expect_err("forked runtime should start with no queued spawns");
assert_eq!(err.kind(), io::ErrorKind::NotFound);
}