fix(app-iced): M95 r4 —— 归属判据回归 / 置信度档 / 运行中页头
代码复审 r3 的四条 p2,修三条、登记一条(P2-4 页脚余量 13px,后置)。 **P2-1(回归修复)**:r3 把归属判据写成「有选中就只认选中」,把**导入崩溃文件**与 `open_session` 这两条路径的诊断一并关掉了——`559ba20` 是**无条件**画的,所以这是 r3 引入的**代码级回归**, 不是既有缺口(今天 iced 侧没有 dispatch 站点,那些还在遗留的 Slint shell 里,故复审未判 p1)。 判据改成三分支(`app/view/logs.rs`): | live_session | selected_session | 报告算数的条件 | |---|---|---| | Some(s) | 任意 | `report.session == s`(只有它在线时日志行是它在追加,别人的报告挂上来就是串场) | | None | Some(s) | `report.session == s` **或** 报告不属于任何已知会话(导入用 `file:<文件名>`,永不在索引里) | | None | None | 全部算数(此时日志行只可能来自 `open_session`/导入,与报告同一次调用发出) | 新增两条测试(都走真实 reducer):`an_imported_crash_file_still_shows_its_findings`、 `a_live_run_says_it_is_running_instead_of_nothing_runs`。 残留(今天不可达,已登记):`open_session(A)` 在 `selected_session` 仍停在上场 B 时会被挡掉—— 真要修得让 presenter 在 `LogLinesChanged` 记下「日志行属于哪一场」。 **P2-2 置信度档(视觉变更)**:`Severity`(借 core 的 `critical/warning/notice` 词汇)→ `Confidence`(`High/Medium/Low`)+ 新 key `LogsConfidenceHigh/Medium/Low`(中英齐,标题明确写 「高/中/低置信度」)。阈值仍是 80/50,但它现在是**档位定义**而不是「严重度代理」。 摘要里的 `findings=rule:high(85)` 同步换档位 slug。DTO 补齐 `severity` 之后应换回真严重度(finding)。 **P2-3 运行中的页头**:session 索引只在**结束**时追加,运行中的那一场不在 `log_sessions` 里 ⇒ 拿不到实例名与结局 ⇒ r3 之前页头在日志滚动时一直说「当前没有运行中的实例」(demo 伪造了身份, 截图照不出来)。`Logs` 新增 `live: bool`(`live_session.is_some()`),`with_status` 三级回退: 有实例+结局 → `实例 · 时间 · 结局`;运行中但没结局 → 新 key `LogsSubtitleLive`(「Running now — logs stream as they arrive」);都没有 → `LogsSubtitleIdle`。 新增 `a_live_run_says_it_is_running_instead_of_nothing_runs`(真实 reducer:只有 `LogAppended(running=true)`,断言副标题不是 Idle 文案且含 `Running now`)。 新增 demo 状态 `ULL_LOGS_FIXTURE=running` 用于出图。 **变异实测**(每条:回退 → 红 → 放回 → 绿):见报告。 验证:`fmt --check` 通过;`clippy -p unlucky-app-iced -p unlucky-i18n --all-targets -D warnings` 干净; `cargo test` **280 + 6 + 23 passed / 0 failed**;行数最大 `pages/mod.rs` 367、`app/view.rs` 324、 `app/view/logs.rs` 337。
This commit is contained in:
1 parent
5ed2bf3a7d
commit
0eb069fa37
9 files changed
+414
-267
No files matched your search
@@ -68,35 +68,32 @@ fn findings_are_grouped_critical_first_and_empty_groups_do_not_exist() {
|
||||
findings,
|
||||
..Logs::default()
|
||||
};
|
||||
let counts: Vec<usize> = GROUPS
|
||||
.iter()
|
||||
.map(|severity| logs.group(*severity).len())
|
||||
.collect();
|
||||
let counts: Vec<usize> = GROUPS.iter().map(|band| logs.group(*band).len()).collect();
|
||||
|
||||
// 抓得住:分组顺序被改成 info 在前(主因会沉底);空组也被画出一个标题(截图上出现 0 条的分区)。
|
||||
// 抓得住:分组顺序被改成低置信度在前(最像主因的沉底);空组也被画出一个标题。
|
||||
// 抓不住:组内顺序(页面刻意不重排 presenter 给的顺序)。
|
||||
assert_eq!(GROUPS[0], Severity::Critical);
|
||||
assert_eq!(GROUPS[0], Confidence::High);
|
||||
assert_eq!(
|
||||
counts.iter().sum::<usize>(),
|
||||
logs.findings.len(),
|
||||
"每条结论必须落进恰好一个分组"
|
||||
);
|
||||
assert!(counts[0] >= 1, "样例至少有主因");
|
||||
assert!(logs.group(Severity::Info).is_empty());
|
||||
assert!(counts[0] >= 1, "样例至少有高置信度结论");
|
||||
assert!(logs.group(Confidence::Low).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summary_is_a_single_deterministic_line_carrying_rule_and_severity() {
|
||||
fn summary_is_a_single_deterministic_line_carrying_rule_and_confidence() {
|
||||
let logs = demo::crash();
|
||||
let summary = logs.summary();
|
||||
|
||||
// 抓得住:摘要里丢了规则 id / 严重度 / 置信度(换 issue 时最关键的三个字段);
|
||||
// 抓得住:摘要里丢了规则 id / 置信度档 / 置信度百分比(换 issue 时最关键的三个字段);
|
||||
// 有人把换行塞进摘要("一行"就不成立了)。
|
||||
// 抓不住:粘贴到 issue 后的可读性(那是人判断的),也不守文案语言(摘要刻意不含界面文案)。
|
||||
assert!(!summary.contains('\n'), "{summary}");
|
||||
assert!(summary.starts_with(SUMMARY_HEADER), "{summary}");
|
||||
assert!(
|
||||
summary.contains("integrity.unsatisfied_link:critical(85)"),
|
||||
summary.contains("integrity.unsatisfied_link:high(85)"),
|
||||
"{summary}"
|
||||
);
|
||||
assert!(summary.contains("lines="), "{summary}");
|
||||
@@ -107,15 +104,21 @@ fn summary_is_a_single_deterministic_line_carrying_rule_and_severity() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn severity_is_derived_from_confidence_with_the_documented_thresholds() {
|
||||
// 抓得住:阈值边界写成 `>` / `>=` 那种 off-by-one(80 与 50 是文档里写死的两个点)。
|
||||
// 抓不住:折算本身对不对——DTO 补上 `severity` 之前这只是**过渡口径**(已报 finding)。
|
||||
assert_eq!(Severity::of_confidence(100), Severity::Critical);
|
||||
assert_eq!(Severity::of_confidence(80), Severity::Critical);
|
||||
assert_eq!(Severity::of_confidence(79), Severity::Warning);
|
||||
assert_eq!(Severity::of_confidence(50), Severity::Warning);
|
||||
assert_eq!(Severity::of_confidence(49), Severity::Info);
|
||||
assert_eq!(Severity::of_confidence(0), Severity::Info);
|
||||
fn the_band_is_the_documented_confidence_band_not_a_severity_impostor() {
|
||||
// 抓得住:阈值边界写成 `>` / `>=` 的 off-by-one(80 / 50 是档位的定义点);
|
||||
// 以及有人把档位名字改回 `critical/warning/notice` 那种**严重度词汇**
|
||||
// (那会让 64 条规则里 12 条进错组,两个方向都有——视觉复审 r3 的 P2-2)。
|
||||
// 抓不住:档位划分本身对不对(那是产品口径;DTO 补上 `severity` 后应换回真严重度,已报 finding)。
|
||||
assert_eq!(Confidence::of(100), Confidence::High);
|
||||
assert_eq!(Confidence::of(80), Confidence::High);
|
||||
assert_eq!(Confidence::of(79), Confidence::Medium);
|
||||
assert_eq!(Confidence::of(50), Confidence::Medium);
|
||||
assert_eq!(Confidence::of(49), Confidence::Low);
|
||||
assert_eq!(Confidence::of(0), Confidence::Low);
|
||||
|
||||
assert_eq!(Confidence::High.slug(), "high");
|
||||
assert_eq!(Confidence::Medium.slug(), "medium");
|
||||
assert_eq!(Confidence::Low.slug(), "low");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -255,9 +258,10 @@ fn every_key_the_page_uses_is_translated_in_both_languages() {
|
||||
Key::LogsClear,
|
||||
Key::LogsOutputTitle,
|
||||
Key::LogsDiagnosisTitle,
|
||||
Key::LogsSeverityCritical,
|
||||
Key::LogsSeverityWarning,
|
||||
Key::LogsSeverityInfo,
|
||||
Key::LogsConfidenceHigh,
|
||||
Key::LogsConfidenceMedium,
|
||||
Key::LogsConfidenceLow,
|
||||
Key::LogsSubtitleLive,
|
||||
Key::LogsConfidence,
|
||||
Key::LogsSuggestedFix,
|
||||
Key::LogsSummaryLabel,
|
||||
|
||||
Reference in new issue
Block a user