fix(core): 老版本 Java 按版本距离选 + 启动看门狗有界等待 + 结束归属(M214 D4/D6/D10)
D4:`pick` 的排序键从「来源序」改成「版本距离 → 来源序」,全不满足时选最接近要求的那个 (托管 25 与 JAVA_HOME/扫盘 17 之间选 17,而不是按 rank 选托管)。新增 `select::decide`: 不符且**没有人显式指定 Java** 时 `ull launch` 直接报 `java_mismatch` 不 spawn(`--java` / 实例 `javaPath` 是豁免,只告警)——1.12.2 配 Java 17/25 必然起不来,警告没有信息量。 `JavaMismatch` + `check` 拆到 `mismatch.rs` 并加 `advice()`(mapping.rs 408→282 行)。 D6:`LaunchSession::finish` 的等进程改成**有界**。判据在 `startup_watch.rs`(纯逻辑): ①窗口从未出现且**连续 N 秒没有任何日志**(还在打日志就说明还在加载,慢加载不被判死); ②日志出现关闭标记后 N 秒仍未退出(实机「关窗后 java.exe 仍存活」的形状)。 到点只**停止等待、绝不杀进程**:写 `session_end(still_running)` + 索引 + `launcher.log`, 返回 `LaunchError::Watchdog`,CLI 翻成 `launch_timeout`。两个时长可配 (默认 300s/60s;`ULL_LAUNCH_STARTUP_TIMEOUT_SECS` / `ULL_LAUNCH_SHUTDOWN_GRACE_SECS`, 生效值每次启动写进 launcher.log 与人读输出)。 D10:把「被终止」与「应用错误」分开。①我们自己杀的:`tree_kill` **送达即登记** `kill_registry`(pid 登记簿,取走语义、上限 64),覆盖 GUI 从别的线程 `kill_tree` 的路径, `finish` 据此判 `killed`;②外部强杀:Windows 上「退出码 1 + 零错误输出 + 无崩溃/JVM 关键字」 判 `killed` 并标 `terminated_externally`(任务管理器与 `taskkill /F` 都是 `TerminateProcess(h, 1)`,应用真崩给的是 NTSTATUS 异常码)。 顺带:`SessionHandle` 拆 `session_handle.rs`(launch_session.rs 422→343 行)、 `NativesReport::to_json` 落 core(cli/launch.rs 腾出空间,仍 399 行)。 用例:select 距离/取舍、startup_watch 状态机、watchdog_cases(4 条真进程)、 exit_cases(外部终止与「我们杀的」区分)、pipeline 跨线程 kill。
This commit is contained in:
1 parent
5959a27add
commit
ff78fe2cae
23 files changed
+1708
-250
No files matched your search
@@ -77,15 +77,16 @@ fn echo_script() -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn long_running_script() -> String {
|
||||
fn long_running_script(dir: &std::path::Path) -> String {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
"ping -n 30 127.0.0.1 >nul".to_string()
|
||||
}
|
||||
let (file, body) = (
|
||||
dir.join("linger.bat"),
|
||||
"@echo off\r\nping -n 30 127.0.0.1 >nul\r\n",
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
"sleep 30".to_string()
|
||||
}
|
||||
let (file, body) = (dir.join("linger.sh"), "#!/bin/sh\nsleep 30\n");
|
||||
std::fs::write(&file, body).expect("写长跑脚本");
|
||||
file.display().to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -191,7 +192,7 @@ fn pipeline_stop_records_killed_exit() {
|
||||
let sessions = temp_sessions_root("killed");
|
||||
let run_dir = temp_dir("killed-run");
|
||||
let launcher = launcher(&sessions, None);
|
||||
let (request, _) = shell_request(&long_running_script(), &run_dir);
|
||||
let (request, _) = shell_request(&long_running_script(&run_dir), &run_dir);
|
||||
|
||||
let mut session = launcher.spawn(&request).expect("spawn 应成功");
|
||||
assert!(session.pid() > 0);
|
||||
@@ -211,6 +212,34 @@ fn pipeline_stop_records_killed_exit() {
|
||||
assert_eq!(index[0].exit_type.as_deref(), Some("killed"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_kill_from_another_thread_is_recorded_as_killed() {
|
||||
// D10:GUI 的 `host::launch::stop(pid)` 是从别的线程直接 `kill_tree`,不经过
|
||||
// `LaunchSession::stop()`。这条路以前会记成 `application_error`(Windows 退出码 1)。
|
||||
let sessions = temp_sessions_root("external-kill");
|
||||
let run_dir = temp_dir("external-kill-run");
|
||||
let launcher = launcher(&sessions, None);
|
||||
let (request, _) = shell_request(&long_running_script(&run_dir), &run_dir);
|
||||
|
||||
let session = launcher.spawn(&request).expect("spawn 应成功");
|
||||
let pid = session.pid();
|
||||
// `kill_tree` 的返回值不代表「进程还在」:温和阶段可能已经关掉了它(taskkill 报 128)。
|
||||
let _ = unlucky_core::launch::tree_kill::kill_tree(pid, Duration::from_millis(200));
|
||||
let handle = session.finish().expect("收尾应成功");
|
||||
|
||||
assert_eq!(
|
||||
handle.exit.exit_type,
|
||||
ExitType::Killed,
|
||||
"我们杀的要判成 killed:{}",
|
||||
handle.exit.summary()
|
||||
);
|
||||
assert!(
|
||||
!handle.exit.terminated_externally,
|
||||
"有登记作据就不该只是「疑似」:{}",
|
||||
handle.exit.summary()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pipeline_assembly_failure_still_writes_evidence() {
|
||||
let sessions = temp_sessions_root("assembly-fail");
|
||||
|
||||
Reference in new issue
Block a user