feat(app-iced,pages): M100 取证密闭 + harness 参数拆分 + 设置页测试按 section 下放

四件前置事,都是为了设置页正文四条 section 线能并行(不写任何 section 正文):

1. 取证密闭(两条通道各有各的机制,证据分开算)
   - 产品 bin:`cli::ForcedState` 加 `Rest`(静息态),给了 `--screenshot` 且没显式
     `--force-state` 时默认 `Some(Rest)`;交互模式仍是 `None`(`shell::overridden`
     与 `app/view.rs` 的 `model()` 各补一支)。
   - harness:截图窗口开 `window::enable_mouse_passthrough`(winit 的
     `set_cursor_hittest(false)`)⇒ 窗口收不到指针事件 ⇒ 全树控件恒为 Active。
     为什么用通道级而不是逐控件灌 forced:设置页「外观」区的滑块/文本框/裸 button 都不消费
     forced,逐控件做要改十几个组件文件,而新产品组件随时会漏。
   - harness 同时数指针事件并打进日志(`指针事件=N`):让「这张图见过几个鼠标事件」变成
     可读数字,替代「连跑几次 hash 一样」那种恒真的假绿。

2. harness 的外观真状态入口:`--appearance=real` 让设置页走 `appearance: Some(..)`
   (工厂态 + `ULL_APPEARANCE_*`),新入口 `pages::app_with_appearance`。
   `--screen=settings` 之前就有了,但 `appearance` 恒为 None,D30 要的那一屏根本不存在。

3. `pages/harness.rs` 400 行 → 拆 `pages/harness/request.rs`(`#[path]` 范式),
   纯搬移部分 240 行 + 新增密闭代码。拆分等价性:同一 argv 的 PNG sha256 与拆分前一致。

4. `pages/settings/tests.rs` 按 section 下放成 `tests/{general,appearance,java,download}_section.rs`,
   mod 层只留跨 section 断言(每条写清守什么、为什么不能下放)。
   给某组加一条新断言 = 只改它自己那个文件。

顺带:`shell/mod.rs` 在 HEAD 就 399 行、`cli.rs` 394 行,两处都按同一范式把 tests 搬成
`shell/tests.rs` / `cli/tests.rs`(断言一字不动,逐字节可 diff)。

变异验证:把四处实现各改坏一处(默认 Rest、passthrough 默认值、overridden 的 Rest 分支、
view 的 Rest 映射),对应 4 条断言全红(390 passed / 4 failed),恢复后 394 passed。
This commit is contained in:
AskaEth committed 2026-11-03 14:22:40 +08:00
1 parent 2ad808d55c
commit 2061296615
16 files changed
+1152 -469

No files matched your search

+64 -2
View File
@@ -1,8 +1,13 @@
//! `pages::harness` 的断言(M90 r2 从 `harness.rs` 外置:那边曾 402 行,guards 的 file-size 会判红)。
//!
//! 只测**参数解析**与**出图 slug**,不碰 iced 运行时。
//! 只测**参数解析**与**出图 slug**,不碰 iced 运行时——所以「指针穿透真的把窗口的鼠标输入关了」
//! 这类事这里证明不了,只能靠出图时日志里的 `指针事件=N`(见 `harness.rs` 的模块文档)。
use super::*;
use super::request::Request;
use crate::cli::Backend;
use crate::pages::Screen;
use unlucky_design::color::SchemeFlavor;
fn parse(args: &[&str]) -> Result<Request, String> {
Request::parse(args.iter().map(|arg| (*arg).to_owned()))
@@ -15,6 +20,7 @@ fn defaults_are_the_acceptance_frame() {
assert_eq!((request.width, request.height), (1180.0, 760.0));
assert_eq!(request.flavor, SchemeFlavor::Light);
assert!(request.screenshot.is_none());
assert!(!request.real_appearance, "默认画演示行");
}
#[test]
@@ -61,3 +67,59 @@ fn tiny_windows_are_rejected() {
assert!(parse(&["--width=200"]).is_err());
assert!(parse(&["--height=100"]).is_err());
}
/// **密闭不变量(M100)**:截图通道默认开指针穿透;交互模式(没有 `--screenshot`)一律关。
///
/// 抓得住:① 谁把取证默认值改成关(那会让取证图重新受系统指针影响);
/// ② 谁让交互模式也开穿透(用户窗口收不到鼠标 = 产品事故)。
/// 抓不住:穿透**真的**生效了没有——那要看出图日志的 `指针事件=N`,测试替不了。
#[test]
fn capture_is_sealed_by_default_and_interactive_never_is() {
assert!(
parse(&["--screenshot", "o.png"]).unwrap().passthrough,
"截图通道默认必须开指针穿透"
);
assert!(!parse(&[]).unwrap().passthrough, "交互模式不许开指针穿透");
assert!(
!parse(&["--screen=settings"]).unwrap().passthrough,
"交互模式(带页名)同样不许开"
);
// 显式给值在交互模式下是**配置错误**,不是「忽略」:静默忽略会让人以为交互窗口也密闭了
for flags in [
&["--passthrough=on"][..],
&["--passthrough", "off"],
&["--screen=settings", "--passthrough=on"],
] {
assert!(parse(flags).is_err(), "{flags:?} 应该报错");
}
}
/// 对照实验的口子(`--passthrough=off`):只服务「不开穿透时 hover 会不会漏进图」这一条。
#[test]
fn the_control_run_can_turn_the_passthrough_off() {
let shot = parse(&["--screenshot", "o.png", "--passthrough=off"]).unwrap();
assert!(!shot.passthrough, "显式 off 必须尊重");
assert!(shot.screenshot.is_some(), "off 只对截图通道有意义");
}
/// **外观真状态那一屏(M100)**:`--appearance=real` 只在设置页成立,且 slug 要与演示行版分开。
///
/// 抓得住:① 谁把 real 接到别的页(那会出一张「假装接了线」的图);
/// ② 两张图落成同名文件(归档后无法区分哪张是真控件版)。
/// 抓不住:那张图里到底有没有控件——那是像素的事,由两张图的 sha256 不同 + tower 亲自看图判。
#[test]
fn the_real_control_screen_is_settings_only_and_says_so_in_the_slug() {
let real = parse(&["--screen=settings", "--appearance=real"]).unwrap();
assert!(real.real_appearance);
assert_eq!(real.slug(), "settings-real-expanded-light-w1180");
assert_eq!(
parse(&["--screen=settings"]).unwrap().slug(),
"settings-expanded-light-w1180",
"演示行版的 slug 不许变(既有归档脚本按它找图)"
);
assert!(
parse(&["--screen=instances", "--appearance=real"]).is_err(),
"别的页没有外观分组,静默接受会出一张假图"
);
assert!(parse(&["--screen=settings", "--appearance=x"]).is_err());
}