use super::*;
use crate::delegate::records::DelegationRegistry;
fn spawn(home: &std::path::Path, script: &str) -> (Arc<DelegationRegistry>, Arc<LiveChild>) {
let registry = Arc::new(DelegationRegistry::new(&scv_client::Layout::new(home)));
let pending = registry.begin("fake", "session", home, Some(("fake-1", 1)));
let child = LiveChild::spawn(
LiveSpec {
executable: "sh".into(),
args: vec!["-c".into(), script.into()],
cwd: home.to_owned(),
environment: pending.environment.clone(),
max_line_bytes: 1024,
},
Some((Arc::clone(®istry), pending)),
)
.unwrap();
(registry, child)
}
fn zombie(pid: u32) -> bool {
std::fs::read_to_string(format!("/proc/{pid}/stat"))
.ok()
.and_then(|stat| {
let state = stat.rsplit_once(") ")?.1.chars().next()?;
Some(state == 'Z')
})
.unwrap_or(false)
}
async fn settles(registry: &DelegationRegistry, child: &LiveChild) {
tokio::time::timeout(Duration::from_secs(20), async {
while child.is_running() || !registry.list(true).is_empty() {
tokio::time::sleep(Duration::from_millis(20)).await;
}
})
.await
.expect("the exited child was collected and forgotten");
assert!(!zombie(child.pid), "the exited child was left a zombie");
}
#[tokio::test]
async fn a_child_exiting_between_turns_is_collected_and_forgotten() {
let home = tempfile::tempdir().unwrap();
let (registry, child) = spawn(home.path(), "sleep 0.2");
assert!(child.is_running());
assert_eq!(registry.list(true).len(), 1);
settles(®istry, &child).await;
tokio::time::timeout(Duration::from_secs(5), child.close())
.await
.unwrap();
}
#[tokio::test]
async fn killing_an_idle_child_frees_it_at_once() {
let home = tempfile::tempdir().unwrap();
let (registry, child) = spawn(home.path(), "exec sleep 30");
let handle = registry.list(true)[0].record.handle.clone();
registry.kill(&handle).await.unwrap();
settles(®istry, &child).await;
}
#[tokio::test]
async fn close_stops_a_running_child_and_its_record() {
let home = tempfile::tempdir().unwrap();
let (registry, child) = spawn(home.path(), "trap '' TERM; sleep 30");
child.close().await;
assert!(!child.is_running());
assert!(registry.list(true).is_empty());
assert!(!zombie(child.pid));
}