fix(app): LaunchEnd 也清 pending 并记失败原因,补早失败回归测试(M43 r3 两条 P2)

P2-1(本轮新引入的回归):`pending` 只在 LaunchStarted 清除,但 `launch::start`
的两条早失败出口(build_request 失败 / spawn 失败——没装 Java、版本 JSON 坏就是
这两条)只发 LaunchEnd → pending 永久残留 → `in_flight` 恒真 → 该实例本会话再也
启动不了,磁贴锁在「启动中」。修:LaunchEnd 分支先 `pending.remove`,早失败与
正常收尾都释放。

P2-2(failed 零 insert):上一轮只在 note 里声称「失败原因进 failed 表」,代码没
落地(唯一 insert 缺失,refresh 的 Failed 分支是死代码,test 6 还靠手动塞表绕过)。
修:LaunchEnd 非 Ok 时 `failed.insert(id, summary)`、Ok 时 remove;refresh_instances
的 Failed 分支从此由真实路径喂数据。

测试(123 → 124,全部走真实入口):
- 新增 `launch_end_before_launch_started_still_releases_the_instance`:mark_pending
  → handle(LaunchEnd Crashed) → pending 空、in_flight 假、refresh 后磁贴是
  Failed{reason}(同时钉住 P2-1 与 P2-2)。
- test 6 重写:Busy 断言改走 `mark_pending`(start_launch 用的同一入口),Failed
  断言改走 `handle(LaunchEnd Crashed)`,不再手动塞 failed 表。
- 所有测试的数据根加 `TempRoot` Drop 守卫,跑完不留临时目录。
This commit is contained in:
AskaEth committed 2026-10-31 22:14:24 +08:00
1 parent 7b2ff62ddc
commit 50b321ec64
3 files changed
+87 -17

No files matched your search

+9 -2
View File
@@ -255,6 +255,14 @@ impl CoreHost {
self.pending.contains(key) || self.launches.iter().any(|live| live.instance == key)
}
/// 标记「已受理、等后台线程回报」(`launch::start` 之后的窗口)。
///
/// 单测也走这个入口而不是直接改 `pending`,保证测的是真实状态流转。
fn mark_pending(&mut self, id: &str) {
self.pending.insert(id.to_string());
self.failed.remove(id);
}
fn start_launch(&mut self, id: &str) {
if self.in_flight(id) {
self.toast(ToastKind::Warn, "这个实例已经在启动/运行中");
@@ -269,8 +277,7 @@ impl CoreHost {
self.toast(ToastKind::Warn, "这个实例已经在启动/运行中");
return;
}
self.pending.insert(instance_id.clone());
self.failed.remove(&instance_id);
self.mark_pending(&instance_id);
if let Some(notice) = game::mode_change_notice(&self.env, &instance_id) {
self.push_notice(notice);
}