feat: release Dever 0.1.4 with enforceable project guidance

This commit is contained in:
Antigravity committed 2026-10-09 05:33:57 +00:00
1 parent 201bdee372
commit 89629c2197
86 files changed
+2798 -338

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
use super::{TemporaryProject, process};
use std::fs;
use std::process::Command;
use std::time::{Duration, Instant};
const DESCENDANTS: &str = r#"
/bin/sh -c 'echo $$ > descendant; /bin/sleep 30 & echo $! > grandchild; wait' &
while [ ! -s grandchild ]; do :; done
"#;
// subreaper 是进程级状态;此单例测试顺序覆盖所有真实进程场景。
#[test]
fn case_process_captures_output_times_out_and_reaps_only_owned_descendants() {
let original_reaper = rustix::process::child_subreaper().unwrap();
let mut unrelated = Command::new("/bin/sleep").arg("30").spawn().unwrap();
let result = std::panic::catch_unwind(|| {
for (body, descendants, expected) in [
("printf hidden; printf hidden-error >&2; exit 0", false, None),
(
"printf output; printf diagnostic >&2; exit 7",
false,
Some("exit status: 7"),
),
(
"printf before-timeout; /bin/sleep 30",
false,
Some("timed out after 100 ms"),
),
("exit 0", true, None),
("printf owned-error >&2; exit 7", true, Some("exit status: 7")),
("printf started; wait", true, Some("timed out after 100 ms")),
] {
let root = TemporaryProject::new().unwrap();
let started = Instant::now();
let script = if descendants {
format!("{DESCENDANTS}\n{body}")
} else {
body.to_owned()
};
let result = process::run(
Command::new("/bin/sh")
.arg("-c")
.arg(&script)
.env_clear()
.current_dir(root.path()),
root.path(),
Duration::from_millis(100),
);
assert!(started.elapsed() < Duration::from_secs(5));
match expected {
None => result.unwrap(),
Some(message) => {
let failure = result.unwrap_err();
assert!(failure.message.contains(message), "{failure:?}");
if script.contains("printf output") {
assert_eq!(failure.stdout, b"output");
assert_eq!(failure.stderr, b"diagnostic");
}
if script.contains("printf before-timeout") {
assert_eq!(failure.stdout, b"before-timeout");
}
if script.contains("printf owned-error") {
assert_eq!(failure.stderr, b"owned-error");
}
}
}
for name in ["descendant", "grandchild"] {
let path = root.path().join(name);
if descendants {
let pid = fs::read_to_string(path)
.unwrap()
.trim()
.parse::<u32>()
.unwrap();
assert!(
!std::path::Path::new(&format!("/proc/{pid}")).exists(),
"unreaped {name} {pid}"
);
}
}
assert_eq!(rustix::process::child_subreaper().unwrap(), original_reaper);
}
let root = TemporaryProject::new().unwrap();
let failure = process::run(
Command::new("/bin/sh")
.arg("-c")
.arg("/bin/dd if=/dev/zero bs=131072 count=2 2>/dev/null; printf end >&2; exit 9")
.env_clear(),
root.path(),
Duration::from_secs(5),
)
.unwrap_err();
assert!(failure.message.contains("exit status: 9"));
assert_eq!(failure.stdout.len(), 262_144);
assert_eq!(failure.stderr, b"end");
let root = TemporaryProject::new().unwrap();
let failure = process::run(
&mut Command::new(root.path().join("missing-executable")),
root.path(),
Duration::from_millis(100),
)
.unwrap_err();
assert!(failure.message.contains("cannot run test process"));
assert_eq!(rustix::process::child_subreaper().unwrap(), original_reaper);
});
// 即使断言失败也回收此对照进程,不能让测试自身遗留资源。
let unrelated_running = unrelated.try_wait().unwrap().is_none();
let _ = unrelated.kill();
unrelated.wait().unwrap();
result.unwrap();
assert!(unrelated_running, "case cleanup affected an unrelated child");
}