use std::time::Duration;
use crate::common::{poll_until, sleep_secs, sleeper, two_line_echo};
#[cfg(unix)]
fn pid_alive(pid: u32) -> bool {
let probed = unsafe { libc::kill(pid as i32, 0) };
probed == 0 || std::io::Error::last_os_error().raw_os_error() == Some(libc::EPERM)
}
#[cfg(windows)]
fn pid_alive(pid: u32) -> bool {
crate::common::windows_pid_alive(pid)
}
#[cfg(unix)]
fn kill_and_reap(pid: u32) {
unsafe {
libc::kill(pid as i32, libc::SIGKILL);
let mut status = 0i32;
libc::waitpid(pid as i32, &mut status, 0);
}
}
#[cfg(windows)]
fn kill_and_reap(pid: u32) {
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_TERMINATE, TerminateProcess};
let handle = unsafe { OpenProcess(PROCESS_TERMINATE, 0, pid) };
if !handle.is_null() {
unsafe {
TerminateProcess(handle, 1);
CloseHandle(handle);
}
}
}
#[tokio::test]
#[ignore = "spawns a real detached child that outlives its owner handle"]
async fn detached_child_survives_dropping_its_handle() {
let pid = {
let detached = sleep_secs(4)
.spawn_detached()
.expect("spawn a detached child");
detached.pid()
};
tokio::time::sleep(Duration::from_millis(400)).await;
assert!(
pid_alive(pid),
"detached child {pid} must survive dropping its DetachedChild handle"
);
kill_and_reap(pid);
}
#[tokio::test]
#[ignore = "spawns a real contained child to prove kill-on-drop has not regressed"]
async fn contained_start_still_kills_on_drop() {
let running = sleeper().start().await.expect("start a contained child");
let pid = running.pid().expect("the contained child has a pid");
assert!(
pid_alive(pid),
"contained child {pid} should be alive at start"
);
drop(running);
poll_until(
Duration::from_secs(10),
Duration::from_millis(50),
"contained child dies on handle drop",
|| !pid_alive(pid),
)
.await;
}
#[tokio::test]
#[ignore = "spawns a real detached child writing to a file redirect"]
async fn detached_child_can_redirect_stdout_to_a_file() {
let dir = tempfile::tempdir().expect("temp dir");
let log = dir.path().join("detached-out.log");
let pid = two_line_echo()
.stdout_file(&log)
.spawn_detached()
.expect("spawn a detached child with a file redirect")
.pid();
poll_until(
Duration::from_secs(10),
Duration::from_millis(50),
"detached child's stdout reaches the file",
|| {
std::fs::read_to_string(&log)
.map(|s| s.contains("first"))
.unwrap_or(false)
},
)
.await;
kill_and_reap(pid); }