//! 真子进程覆盖输出上限、后代持有管道和正常/超时两种回收路径。 use super::Directory; use dever::process::testing::capture; use std::path::Path; use std::time::Duration; #[test] fn test_process_timeout_reaps_only_its_owned_group() { let directory = Directory::new(); let mut unrelated = std::process::Command::new("/bin/sleep") .arg("30") .spawn() .unwrap(); let output = capture( Path::new("/bin/sh"), &[ "-c".into(), "/bin/sh -c 'sleep 30 & echo $!; wait' & echo $!; wait".into(), ], &directory.0, Duration::from_millis(250), ) .unwrap(); assert!(output.timed_out); assert_eq!(output.stdout.lines().count(), 2); for descendant in output.stdout.lines() { let descendant: u32 = descendant.parse().unwrap(); assert!(!Path::new(&format!("/proc/{descendant}")).exists()); } assert!(unrelated.try_wait().unwrap().is_none()); unrelated.kill().unwrap(); unrelated.wait().unwrap(); } #[test] fn test_process_success_reaps_descendants_before_joining_output_readers() { let directory = Directory::new(); let output = capture( Path::new("/bin/sh"), &["-c".into(), "/bin/sh -c 'sleep 30 & echo $! > grandchild; wait' & echo $!; while [ ! -s grandchild ]; do :; done; /bin/cat grandchild".into()], &directory.0, Duration::from_secs(3), ) .unwrap(); assert!(output.status.success()); assert!(!output.timed_out); assert_eq!(output.stdout.lines().count(), 2); for descendant in output.stdout.lines() { let descendant: u32 = descendant.parse().unwrap(); assert!(!Path::new(&format!("/proc/{descendant}")).exists()); } } #[test] fn test_process_noisy_output_stays_bounded_without_disk_capture() { let directory = Directory::new(); let output = capture( Path::new("/bin/sh"), &[ "-c".into(), "echo $$; /bin/cat /dev/zero & /bin/cat /dev/zero >&2 & wait".into(), ], &directory.0, Duration::from_secs(1), ) .unwrap(); assert!(output.timed_out); let marker = "\n[output truncated after 1 MiB]\n"; for stream in [&output.stdout, &output.stderr] { assert_eq!(stream.len(), 1_048_576 + marker.len()); assert!(stream.ends_with(marker)); } let descendant: u32 = output.stdout.lines().next().unwrap().parse().unwrap(); assert!(!Path::new(&format!("/proc/{descendant}")).exists()); assert_eq!(std::fs::read_dir(&directory.0).unwrap().count(), 0); // 启动失败也释放进程监管状态,同一目录可继续执行下一例。 assert!( capture( Path::new("/missing-dever-test"), &[], &directory.0, Duration::from_secs(1) ) .is_err() ); let next = capture( Path::new("/bin/true"), &[], &directory.0, Duration::from_secs(1), ) .unwrap(); assert!(next.status.success()); assert!(!next.timed_out); assert!(next.stdout.is_empty() && next.stderr.is_empty()); }