Skip to content

Commit f23a6d5

Browse files
committed
test(supervisor): synchronize managed child lifecycle test
Signed-off-by: Evan Lezar <elezar@nvidia.com>
1 parent 98a7639 commit f23a6d5

1 file changed

Lines changed: 96 additions & 11 deletions

File tree

‎crates/openshell-sandbox/src/managed_children.rs‎

Lines changed: 96 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -326,25 +326,34 @@ fn direct_child_pids() -> io::Result<HashSet<i32>> {
326326
Ok(children)
327327
}
328328

329-
#[cfg(test)]
329+
#[cfg(all(test, target_os = "linux"))]
330330
mod tests {
331331
use super::*;
332332
use nix::sys::wait::{Id, WaitPidFlag, WaitStatus, waitid, waitpid};
333333
use nix::unistd::Pid;
334-
use std::process::Command;
334+
use std::process::{Command, Stdio};
335335
use std::sync::mpsc;
336336
use std::time::Duration;
337+
use tokio::process::Command as TokioCommand;
338+
339+
#[test]
340+
fn spawned_child_is_registered_before_returning() {
341+
let pid = 1_000_000_u32;
342+
let child = ManagedChild::spawn(|| Ok::<_, ()>(pid), |pid| Some(*pid)).unwrap();
343+
344+
assert!(is_managed(i32::try_from(child.id().unwrap()).unwrap()));
345+
drop(child);
346+
assert!(!is_managed(i32::try_from(pid).unwrap()));
347+
}
337348

338349
#[test]
339350
fn fast_child_remains_waitable_after_orphan_reap_attempt() {
340-
let mut registry = lock();
341-
let mut child = Command::new("sh")
342-
.args(["-c", "exit 11"])
343-
.spawn()
344-
.expect("spawn fast child");
345-
let child_pid = child.id();
346-
let managed_child = registry.register(child_pid).expect("register fast child");
347-
drop(registry);
351+
let mut child = ManagedChild::spawn(
352+
|| Command::new("sh").args(["-c", "exit 11"]).spawn(),
353+
|child| Some(child.id()),
354+
)
355+
.expect("spawn and register fast child");
356+
let child_pid = child.id().unwrap();
348357
let pid = Pid::from_raw(i32::try_from(child_pid).unwrap());
349358

350359
// Observe the completed child without consuming its status, exactly
@@ -373,11 +382,11 @@ mod tests {
373382
);
374383

375384
let wait = child.wait();
376-
unregister(managed_child);
377385
assert!(
378386
wait.is_ok(),
379387
"the explicit child waiter must retain the exit status"
380388
);
389+
assert!(!is_managed(pid.as_raw()));
381390
}
382391

383392
#[test]
@@ -452,4 +461,80 @@ mod tests {
452461
child.kill().expect("kill child");
453462
child.wait().expect("wait for child");
454463
}
464+
465+
#[tokio::test]
466+
async fn tokio_try_wait_retains_then_releases_management() {
467+
let mut child = ManagedChild::spawn(
468+
|| {
469+
let mut command = TokioCommand::new("sh");
470+
command.args(["-c", "read -r _"]).stdin(Stdio::piped());
471+
command.spawn()
472+
},
473+
tokio::process::Child::id,
474+
)
475+
.expect("spawn managed Tokio child");
476+
let pid = i32::try_from(child.id().unwrap()).unwrap();
477+
let stdin = child.take_stdin().expect("stdin must be piped");
478+
479+
assert!(child.try_wait().expect("observe Tokio child").is_none());
480+
assert!(is_managed(pid), "a running child must remain managed");
481+
482+
drop(stdin);
483+
child.wait().await.expect("wait for Tokio child");
484+
assert!(
485+
!is_managed(pid),
486+
"a waited child must release its managed PID"
487+
);
488+
}
489+
}
490+
491+
#[cfg(test)]
492+
mod cross_platform_tests {
493+
use super::ManagedChild;
494+
use std::process::{Command, Stdio};
495+
496+
#[test]
497+
fn managed_child_retains_valid_pid_on_every_platform() {
498+
let child = ManagedChild::spawn(|| Ok::<_, ()>(1_000_003_u32), |pid| Some(*pid))
499+
.expect("construct managed child");
500+
501+
assert_eq!(child.id(), Some(1_000_003));
502+
}
503+
504+
#[test]
505+
fn standard_child_waits_through_managed_wrapper() {
506+
let executable = std::env::current_exe().expect("current test executable");
507+
let mut child = ManagedChild::spawn(
508+
|| {
509+
Command::new(executable)
510+
.arg("--help")
511+
.stdout(Stdio::null())
512+
.stderr(Stdio::null())
513+
.spawn()
514+
},
515+
|child| Some(child.id()),
516+
)
517+
.expect("spawn test child");
518+
519+
assert!(child.wait().expect("wait for test child").success());
520+
}
521+
522+
#[tokio::test]
523+
async fn tokio_child_waits_through_managed_wrapper() {
524+
let executable = std::env::current_exe().expect("current test executable");
525+
let mut child = ManagedChild::spawn(
526+
|| {
527+
let mut command = tokio::process::Command::new(executable);
528+
command
529+
.arg("--help")
530+
.stdout(Stdio::null())
531+
.stderr(Stdio::null());
532+
command.spawn()
533+
},
534+
tokio::process::Child::id,
535+
)
536+
.expect("spawn test child");
537+
538+
assert!(child.wait().await.expect("wait for test child").success());
539+
}
455540
}

0 commit comments

Comments
 (0)