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:
1 parent
2ad808d55c
commit
2061296615
16 files changed
+1152
-469
No files matched your search
@@ -30,6 +30,20 @@
|
||||
//! | (页面上还没有这个分组) | [`ai`] | ai+about 线:先建分组再接线 |
|
||||
//! | 页头 / 侧列 / 滚动区 / 演示卡片 / 空态位 | [`frame`] | 谁都不该改(改了就是改版面) |
|
||||
//!
|
||||
//! ## 测试也按 section 分(M100)
|
||||
//!
|
||||
//! 断言跟着它守的东西走,于是四条 section 线**各改各的测试**:
|
||||
//!
|
||||
//! | 测试文件 | 守什么 | 谁会动它 |
|
||||
//! |---|---|---|
|
||||
//! | `tests/general_section.rs` | General 自己的行 / 图标 / body 现状 | language+appearance 线 |
|
||||
//! | `tests/appearance_section.rs` | Appearance 自己的行 / 图标 / body 现状 | language+appearance 线 |
|
||||
//! | `tests/java_section.rs` | Java 自己的行 / 图标 / body 现状 | java 线 |
|
||||
//! | `tests/download_section.rs` | Downloads 自己的行 / 图标 / body 现状 | download 线 |
|
||||
//! | `tests/mod.rs` | **跨 section**:版面契约、行宽上限、窄窗退化、注册表顺序 / 分组下标 / 「只有一组有真控件」 | 谁都别随手改(每条都写了为什么不能下放) |
|
||||
//!
|
||||
//! 判据:**给某一组加一条新断言 = 只改它自己那个测试文件**。
|
||||
//!
|
||||
//! **加一个分组要动哪里**:① 在它自己的文件里写一个 `pub(super) const GROUP`(名字 / 图标 / 演示行 /
|
||||
//! 可选的真控件渲染器 `body`);② 本文件的 [`GROUPS`] 加一行、数组长度 +1。**不需要**动 `frame.rs`、
|
||||
//! 别的分组文件或 `pages/mod.rs`。
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
//! 设置页的守门测试(M99 拆目录时**原样搬出来**,用例与断言一字未动;断言强度只增不减)。
|
||||
//!
|
||||
//! 站位说明:本文件是 `pages::settings::tests`,所以 `super::super` 就是 `pages`。
|
||||
|
||||
use super::super::layout::{self, frame_metrics, measure_cap_px, PaneKit, SIDE_NAV_WIDTH};
|
||||
use super::*;
|
||||
use crate::pages::appearance::{APPEARANCE_GROUP, GROUP_LABEL};
|
||||
|
||||
#[test]
|
||||
fn side_nav_reuses_the_drawer_width() {
|
||||
// D33:分组导航走 drawer 240(同一容器 rail 88 ⇄ drawer 240),不是 rail 的 88
|
||||
assert_eq!(SIDE_NAV_WIDTH, 240.0);
|
||||
let metrics = frame_metrics(PaneKit::Supporting, 1180.0);
|
||||
assert_eq!(metrics.secondary, Some(240.0));
|
||||
assert_ne!(metrics.secondary, Some(layout::rail_width()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn form_column_never_exceeds_the_measure_cap() {
|
||||
for width in [720.0, 840.0, 1180.0, 1920.0] {
|
||||
let metrics = frame_metrics(PaneKit::Supporting, width);
|
||||
assert!(
|
||||
metrics.column <= measure_cap_px() + f32::EPSILON,
|
||||
"{width} 宽下表单列 {:?} 超过行宽上限",
|
||||
metrics.column
|
||||
);
|
||||
assert!(metrics.column >= layout::measure_floor_px());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_group_has_a_name_icon_and_rows() {
|
||||
assert!(GROUPS.len() >= 3);
|
||||
assert!(SELECTED < GROUPS.len());
|
||||
for group in &GROUPS {
|
||||
assert!(!group.name.is_empty());
|
||||
assert!(!group.icon.icon().path.is_empty(), "{} 缺图标", group.name);
|
||||
assert!(!group.rows.is_empty(), "{} 没有设置行", group.name);
|
||||
for (label, value) in group.rows {
|
||||
assert!(!label.is_empty() && !value.is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn narrow_window_stacks_the_groups_instead_of_the_side_nav() {
|
||||
let narrow = frame_metrics(PaneKit::Supporting, 700.0);
|
||||
assert_eq!(narrow.layout, PageLayout::SingleColumn);
|
||||
assert!(narrow.secondary.is_none(), "窄窗不许留侧列");
|
||||
}
|
||||
|
||||
/// M93:设置页「外观」分组的下标与 `pages::appearance` 声明的常量必须一致。
|
||||
///
|
||||
/// 抓得住:谁把 `GROUPS` 的顺序调了却忘了改 `APPEARANCE_GROUP`(那样真控件会画到别的分组上,
|
||||
/// 而支持列显示的分组名也对不上)。
|
||||
/// 抓不住:控件本身画得对不对(那由截图与 app 侧测试管)。
|
||||
#[test]
|
||||
fn the_appearance_group_is_where_the_module_says_it_is() {
|
||||
assert_eq!(GROUPS[APPEARANCE_GROUP].name, GROUP_LABEL);
|
||||
assert!(SELECTED < GROUPS.len(), "演示下标仍在范围内");
|
||||
}
|
||||
|
||||
/// M99:拆分后**左列顺序**必须还是拆分前那四项(顺序变了就是观感变了,不是「纯搬移」)。
|
||||
///
|
||||
/// 抓得住:① 注册表顺序被调换(`GROUPS` 是四条线各自文件拼出来的,最容易在合并时错位);
|
||||
/// ② 某组被误删/误加(长度不等)。
|
||||
/// 抓不住:组内那些行还在不在、译文对不对(各组自己的文件与 `catalog` 金表各管一段)。
|
||||
#[test]
|
||||
fn the_split_kept_the_left_column_order() {
|
||||
let names: Vec<&str> = GROUPS.iter().map(|group| group.name).collect();
|
||||
assert_eq!(names, vec!["General", "Appearance", "Java", "Downloads"]);
|
||||
}
|
||||
|
||||
/// M99:「外观」那一组之所以能画真控件,是因为**它自己的文件**挂上了 `body`。
|
||||
///
|
||||
/// 抓得住:拆分时把 `body: Some(…)` 搬丢 / 搬到了别的组(那会让真控件消失,退回演示行——
|
||||
/// 而截图对这种「只在有外观状态时出现」的差别不一定抓得住,见报告「没验的部分」)。
|
||||
/// 抓不住:`body` 指向的那个渲染器画得对不对(那是 `pages/appearance` 的事)。
|
||||
#[test]
|
||||
fn only_the_appearance_group_carries_a_real_control_body() {
|
||||
assert!(
|
||||
GROUPS[APPEARANCE_GROUP].body.is_some(),
|
||||
"外观分组必须是接了真控件的那一组"
|
||||
);
|
||||
for (index, group) in GROUPS.iter().enumerate() {
|
||||
if index == APPEARANCE_GROUP {
|
||||
continue;
|
||||
}
|
||||
assert!(
|
||||
group.body.is_none(),
|
||||
"{} 现在应该还是演示行(真控件由它自己那条 section 线接)",
|
||||
group.name
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//! 「Appearance」分组自己的断言(M100 从 `settings/tests.rs` 下放)。
|
||||
//!
|
||||
//! **归 language+appearance 线**:这是设置页唯一接了真控件的一组,真控件实现全在
|
||||
//! `pages/appearance/section.rs`;本文件只守「注册表这一侧的接线」。
|
||||
|
||||
use super::super::appearance;
|
||||
use unlucky_design::icons::IconKind;
|
||||
|
||||
/// 这一组的**演示行**(没有外观运行期状态时才画)。
|
||||
///
|
||||
/// 抓得住:四条线并行合并时把别人的行粘到本文件(四份 `GROUP` 长得几乎一样);
|
||||
/// 漏掉/多一条行;标签与值错位。
|
||||
/// 抓不住:真控件那一屏(那是 `pages/appearance` 的事,靠出图判)。
|
||||
#[test]
|
||||
fn the_appearance_group_shows_density_corner_motion_and_text_scale() {
|
||||
let rows: Vec<(&str, &str)> = appearance::GROUP.rows.to_vec();
|
||||
assert_eq!(
|
||||
rows,
|
||||
vec![
|
||||
("Density", "Comfortable"),
|
||||
("Corner style", "Rounded"),
|
||||
("Motion", "Full"),
|
||||
("Text scale", "100%"),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// 左列图标(`IconKind::Sidebar`)。
|
||||
#[test]
|
||||
fn the_appearance_group_keeps_its_icon() {
|
||||
assert_eq!(appearance::GROUP.icon, IconKind::Sidebar);
|
||||
}
|
||||
|
||||
/// **本组的绊线**:外观分组必须一直是「挂了真控件」的那一组。
|
||||
///
|
||||
/// 抓得住:language+appearance 线把 `body` 换回 `None`(真控件消失、退回演示行,而截图对这种
|
||||
/// 「只在有外观状态时出现」的差别不一定抓得住)。
|
||||
/// 抓不住:`body` 到底指向哪个渲染器——**函数指针相等性断言不了**(rustc
|
||||
/// `unpredictable_function_pointer_comparisons`:fn 地址不保证唯一,比出来的结果没有意义),
|
||||
/// 所以「指向 `pages::appearance::section`」这件事只能由
|
||||
/// `settings/appearance.rs` 那一行源码 + 两张图的 sha256 不同来担保。
|
||||
#[test]
|
||||
fn the_appearance_group_keeps_a_real_control_body() {
|
||||
assert!(
|
||||
appearance::GROUP.body.is_some(),
|
||||
"外观分组必须是挂了真控件的那一组(`body = Some(crate::pages::appearance::section)`)"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//! 「Downloads」分组自己的断言(M100 从 `settings/tests.rs` 下放)。
|
||||
//!
|
||||
//! **归 download 线**:接真控件时改本文件,不必动 `tests/mod.rs`,也不碰别的分组。
|
||||
//!
|
||||
//! 别把它和下载页(`pages/download*`)搞混:那是「装哪个版本」,这一组是「怎么下」的默认值。
|
||||
|
||||
use super::super::download;
|
||||
use unlucky_design::icons::IconKind;
|
||||
|
||||
/// 这一组的行集合(标签 + 值)。
|
||||
///
|
||||
/// 抓得住:① 四条线并行合并时把别人的行粘到本文件(四份 `GROUP` 长得几乎一样,最容易整块贴错);
|
||||
/// ② 漏掉/多出一条行;③ 标签与值错位(`("Concurrency", "8 threads")` 被写成 `"Off"`)。
|
||||
/// 抓不住:值对不对(`Automatic (region)` 是不是本机真选中的源)——那是接线后真数据源的事。
|
||||
#[test]
|
||||
fn the_downloads_group_shows_source_concurrency_and_checksums() {
|
||||
let rows: Vec<(&str, &str)> = download::GROUP.rows.to_vec();
|
||||
assert_eq!(
|
||||
rows,
|
||||
vec![
|
||||
("Preferred source", "Automatic (region)"),
|
||||
("Concurrency", "8 threads"),
|
||||
("Verify checksums", "On"),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// 左列图标(`IconKind::Download`)。与导航轨的「下载」同族,换掉就与 rail 语汇不一致。
|
||||
#[test]
|
||||
fn the_downloads_group_keeps_its_icon() {
|
||||
assert_eq!(download::GROUP.icon, IconKind::Download);
|
||||
}
|
||||
|
||||
/// **绊线**:这一组还没接真控件,画的是演示行。
|
||||
///
|
||||
/// 抓得住:download 线接上了 `body` 却没更新这里的期望(那会让本文件静默变成假绿)。
|
||||
/// 抓不住:界面上真控件长什么样。
|
||||
///
|
||||
/// 接真控件时:把 `is_none()` 改成 `is_some()`,**只改这一行**;再去 `tests/mod.rs` 的
|
||||
/// `only_the_appearance_group_carries_a_real_control_body` 那条绊线上手工确认一次(那条是四组共用的)。
|
||||
#[test]
|
||||
fn the_downloads_group_is_still_demo_rows() {
|
||||
assert!(
|
||||
download::GROUP.body.is_none(),
|
||||
"Downloads 还没接真控件:接了就改本断言"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//! 「General」分组自己的断言(M100 从 `settings/tests.rs` 下放)。
|
||||
//!
|
||||
//! **归 language+appearance 线**:接真控件时改本文件(`GROUP.body` 那条绊线要跟着翻),
|
||||
//! 不必动 `tests/mod.rs`,也不碰别的分组。
|
||||
|
||||
use super::super::general;
|
||||
use unlucky_design::icons::IconKind;
|
||||
|
||||
/// 这一组的行集合(标签 + 值)。改文案就是改观感,所以逐条钉住,而不是「非空就行」。
|
||||
///
|
||||
/// 抓得住:① 四条线并行合并时把别人的行粘到本文件(四份 `GROUP` 长得几乎一样,最容易整块贴错);
|
||||
/// ② 漏掉/多出一条行;③ 标签与值错位(`("Theme", "Follow system")` 被写成 `("Theme", "Instances")`)。
|
||||
/// 抓不住:值对不对(`"Follow system"` 是不是当前系统主题的真相)——那是接线后真数据源的事。
|
||||
#[test]
|
||||
fn the_general_group_shows_language_theme_startup_and_close() {
|
||||
let rows: Vec<(&str, &str)> = general::GROUP.rows.to_vec();
|
||||
assert_eq!(
|
||||
rows,
|
||||
vec![
|
||||
("Language", "English (System)"),
|
||||
("Theme", "Follow system"),
|
||||
("Startup page", "Instances"),
|
||||
("Close button", "Exit to tray"),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// 左列图标(`IconKind::Settings`)。分组图标是左列的语义标签,换掉就是换观感。
|
||||
#[test]
|
||||
fn the_general_group_keeps_its_icon() {
|
||||
assert_eq!(general::GROUP.icon, IconKind::Settings);
|
||||
}
|
||||
|
||||
/// **绊线**:这一组还没接真控件,画的是演示行。
|
||||
///
|
||||
/// 抓得住:language+appearance 线接上了 `body` 却没更新这里的期望(那会让本文件静默变成假绿)。
|
||||
/// 抓不住:界面上真控件长什么样。
|
||||
///
|
||||
/// 接真控件时:把 `is_none()` 改成 `is_some()`,**只改这一行**;再去 `tests/mod.rs` 的
|
||||
/// `only_the_appearance_group_carries_a_real_control_body` 那条绊线上手工确认一次(那条是四组共用的)。
|
||||
#[test]
|
||||
fn the_general_group_is_still_demo_rows() {
|
||||
assert!(
|
||||
general::GROUP.body.is_none(),
|
||||
"General 还没接真控件:接了就改本断言"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//! 「Java」分组自己的断言(M100 从 `settings/tests.rs` 下放)。
|
||||
//!
|
||||
//! **归 java 线**:接真控件时改本文件,不必动 `tests/mod.rs`,也不碰别的分组。
|
||||
//! 判据(M100 交付里正面回答的那条):给 java 加一条新断言 = **只改本文件**。
|
||||
|
||||
use super::super::java;
|
||||
use unlucky_design::icons::IconKind;
|
||||
|
||||
/// 这一组的行集合(标签 + 值)。改文案就是改观感,所以逐条钉住,而不是「非空就行」。
|
||||
///
|
||||
/// 抓得住:① 四条线并行合并时把别人的行粘到本文件(四份 `GROUP` 长得几乎一样,最容易整块贴错);
|
||||
/// ② 漏掉/多出一条行;③ 标签与值错位(`("Default memory", "4 GiB")` 被写成 `"8 threads"`)。
|
||||
/// 抓不住:值对不对(`Temurin 21.0.4` 是不是本机真检测到的运行时)——那是接线后真数据源的事。
|
||||
#[test]
|
||||
fn the_java_group_shows_runtime_memory_and_jvm_arguments() {
|
||||
let rows: Vec<(&str, &str)> = java::GROUP.rows.to_vec();
|
||||
assert_eq!(
|
||||
rows,
|
||||
vec![
|
||||
("Runtime", "Temurin 21.0.4"),
|
||||
("Default memory", "4 GiB"),
|
||||
("JVM arguments", "Automatic"),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// 左列图标必须是 `IconKind::Folder`。
|
||||
///
|
||||
/// 抓得住:谁把它换回 `IconKind::Play`——那是视觉复审 P2-3 判过的(`Play` 是填充字形,
|
||||
/// 和同屏其它描边图标语汇打架;Java 组讲的是运行时安装位置,目录图标语义才对得上)。
|
||||
/// 抓不住:图标画出来的大小/线宽(那是 tokens 层的事)。
|
||||
#[test]
|
||||
fn the_java_group_keeps_the_folder_icon_from_the_visual_review() {
|
||||
assert_eq!(java::GROUP.icon, IconKind::Folder);
|
||||
}
|
||||
|
||||
/// **绊线**:这一组还没接真控件,画的是演示行。
|
||||
///
|
||||
/// 抓得住:java 线接上了 `body` 却没更新这里的期望(那会让本文件静默变成假绿)。
|
||||
/// 抓不住:界面上真控件长什么样。
|
||||
///
|
||||
/// 接真控件时:把 `is_none()` 改成 `is_some()`,**只改这一行**;再去 `tests/mod.rs` 的
|
||||
/// `only_the_appearance_group_carries_a_real_control_body` 那条绊线上手工确认一次(那条是四组共用的)。
|
||||
#[test]
|
||||
fn the_java_group_is_still_demo_rows() {
|
||||
assert!(
|
||||
java::GROUP.body.is_none(),
|
||||
"Java 还没接真控件:接了就改本断言"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
//! 设置页的**跨 section** 守门测试(M100 按 section 下放之后,留在 mod 层的那一份)。
|
||||
//!
|
||||
//! ## 为什么这里只剩「跨 section」的断言
|
||||
//!
|
||||
//! 设置页正文接下来由四条线并行接(java / download / language+appearance / ai+about)。每条线要能
|
||||
//! **只改自己的测试**就跑通,所以「某一组自己的行文案 / 图标 / body 现状」一律下放到
|
||||
//! `tests/<section>_section.rs`;本文件只留**必须四组一起看**的断言——下放到任何单组文件里,
|
||||
//! 都会变成「某组替其它三组说话」,四条线并行时必然互相踩。
|
||||
//!
|
||||
//! 判据(M100 报告里正面回答的那条):**给 java 加一条新断言,改几个文件?** —— 1 个
|
||||
//! (`tests/java_section.rs`)。本文件不动。
|
||||
//!
|
||||
//! ## 站位
|
||||
//!
|
||||
//! 本文件是 `pages::settings::tests`:`super` = `pages::settings`,`super::super` = `pages`。
|
||||
//!
|
||||
//! ## 命名
|
||||
//!
|
||||
//! 子模块带 `_section` 后缀,是为了不和 `use super::*` 带进来的分组模块(`settings::java` 等)
|
||||
//! 撞名——撞名时显式 `mod` 会静默盖掉 glob,读代码的人会以为 `java` 指的是分组的那个。
|
||||
|
||||
mod appearance_section;
|
||||
mod download_section;
|
||||
mod general_section;
|
||||
mod java_section;
|
||||
|
||||
use super::super::layout::{self, frame_metrics, measure_cap_px, PaneKit, SIDE_NAV_WIDTH};
|
||||
use super::*;
|
||||
use crate::pages::appearance::{APPEARANCE_GROUP, GROUP_LABEL};
|
||||
|
||||
// --------------------------------------------------------------- 版面(不属于任何一组)
|
||||
|
||||
/// 守的是**版面契约**:分组导航走 drawer 240(D33),不是 rail 的 88。
|
||||
///
|
||||
/// 不能下放到分组:它不是「某一组」的属性,而是整个 supporting-pane 的几何。
|
||||
#[test]
|
||||
fn side_nav_reuses_the_drawer_width() {
|
||||
assert_eq!(SIDE_NAV_WIDTH, 240.0);
|
||||
let metrics = frame_metrics(PaneKit::Supporting, 1180.0);
|
||||
assert_eq!(metrics.secondary, Some(240.0));
|
||||
assert_ne!(metrics.secondary, Some(layout::rail_width()));
|
||||
}
|
||||
|
||||
/// 守的是**行宽上限**(逐行 67 字符 ≤72)在所有宽度档下都成立。
|
||||
///
|
||||
/// 不能下放:它是四个分组共用的正文列,任何一组把列撑宽都会连带别人。
|
||||
#[test]
|
||||
fn form_column_never_exceeds_the_measure_cap() {
|
||||
for width in [720.0, 840.0, 1180.0, 1920.0] {
|
||||
let metrics = frame_metrics(PaneKit::Supporting, width);
|
||||
assert!(
|
||||
metrics.column <= measure_cap_px() + f32::EPSILON,
|
||||
"{width} 宽下表单列 {:?} 超过行宽上限",
|
||||
metrics.column
|
||||
);
|
||||
assert!(metrics.column >= layout::measure_floor_px());
|
||||
}
|
||||
}
|
||||
|
||||
/// 守的是**窄窗退化**:<720 时侧列消失、分组改为按组堆叠。
|
||||
///
|
||||
/// 不能下放:退化是整页行为,四组一起变(单组文件里没法验证「侧列没了」)。
|
||||
#[test]
|
||||
fn narrow_window_stacks_the_groups_instead_of_the_side_nav() {
|
||||
let narrow = frame_metrics(PaneKit::Supporting, 700.0);
|
||||
assert_eq!(narrow.layout, PageLayout::SingleColumn);
|
||||
assert!(narrow.secondary.is_none(), "窄窗不许留侧列");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------- 注册表(四组一起看)
|
||||
|
||||
/// 守的是**注册表的结构不变量**:每组都有名字、图标、演示行,且演示下标在范围内。
|
||||
///
|
||||
/// 下放不了:它遍历的是 `GROUPS`(四组拼出来的数组),单组文件拿不到其它三组,
|
||||
/// 而「新增一组忘了填 rows」正是这条要抓的。
|
||||
#[test]
|
||||
fn every_group_has_a_name_icon_and_rows() {
|
||||
assert!(GROUPS.len() >= 3);
|
||||
assert!(SELECTED < GROUPS.len());
|
||||
for group in &GROUPS {
|
||||
assert!(!group.name.is_empty());
|
||||
assert!(!group.icon.icon().path.is_empty(), "{} 缺图标", group.name);
|
||||
assert!(!group.rows.is_empty(), "{} 没有设置行", group.name);
|
||||
for (label, value) in group.rows {
|
||||
assert!(!label.is_empty() && !value.is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// M93:设置页「外观」分组的下标与 `pages::appearance` 声明的常量必须一致。
|
||||
///
|
||||
/// 抓得住:谁把 `GROUPS` 的顺序调了却忘了改 `APPEARANCE_GROUP`(那样真控件会画到别的分组上,
|
||||
/// 而支持列显示的分组名也对不上)。
|
||||
/// 抓不住:控件本身画得对不对(那由截图与 app 侧测试管)。
|
||||
///
|
||||
/// 下放不了:它同时读 `GROUPS`(注册表)与 `pages::appearance` 的常量,横跨两处;
|
||||
/// 放进 `appearance_section.rs` 会让「外观那条线」替注册表顺序作证。
|
||||
#[test]
|
||||
fn the_appearance_group_is_where_the_module_says_it_is() {
|
||||
assert_eq!(GROUPS[APPEARANCE_GROUP].name, GROUP_LABEL);
|
||||
assert!(SELECTED < GROUPS.len(), "演示下标仍在范围内");
|
||||
}
|
||||
|
||||
/// M99:拆分后**左列顺序**必须还是拆分前那四项(顺序变了就是观感变了,不是「纯搬移」)。
|
||||
///
|
||||
/// 抓得住:① 注册表顺序被调换(`GROUPS` 是四条线各自文件拼出来的,最容易在合并时错位);
|
||||
/// ② 某组被误删/误加(长度不等)。
|
||||
/// 抓不住:组内那些行还在不在、译文对不对(各组自己的文件与 `catalog` 金表各管一段)。
|
||||
///
|
||||
/// 下放不了:它是一句「四组顺序 = 这四个名字」的断言,任何单组文件都只能看到自己那一个名字。
|
||||
#[test]
|
||||
fn the_split_kept_the_left_column_order() {
|
||||
let names: Vec<&str> = GROUPS.iter().map(|group| group.name).collect();
|
||||
assert_eq!(names, vec!["General", "Appearance", "Java", "Downloads"]);
|
||||
}
|
||||
|
||||
/// M99:「外观」是**当前唯一**接了真控件的那一组。
|
||||
///
|
||||
/// 抓得住:拆分时把 `body: Some(…)` 搬丢 / 搬到了别的组(那会让真控件消失,退回演示行——
|
||||
/// 而截图对这种「只在有外观状态时出现」的差别不一定抓得住,见报告「没验的部分」);
|
||||
/// 也抓「别组偷偷接了 body 却没人知道」。
|
||||
/// 抓不住:`body` 指向的那个渲染器画得对不对(那是 `pages::appearance` 的事)。
|
||||
///
|
||||
/// **为什么不能下放**(任务书点名要求写清):这条断言的全部内容是**四组的 body 现状对比**
|
||||
/// ——「只有第 2 组是 Some,其余三组都是 None」。放进 `appearance_section.rs` 它就得替 java /
|
||||
/// download / general 三组作证(那三条线一接真控件,本文件以外的地方无缘无故变红,且责任方
|
||||
/// 不是改代码的人);拆成四份「我这组是 None」又会丢掉「唯一」这个要点。
|
||||
/// 它是**绊线**:哪条线接上真控件,就必须来本文件手工改这一条(T1:绊线保持字面量,
|
||||
/// 动态取活值等于把绊线剪了)。
|
||||
#[test]
|
||||
fn only_the_appearance_group_carries_a_real_control_body() {
|
||||
assert!(
|
||||
GROUPS[APPEARANCE_GROUP].body.is_some(),
|
||||
"外观分组必须是接了真控件的那一组"
|
||||
);
|
||||
for (index, group) in GROUPS.iter().enumerate() {
|
||||
if index == APPEARANCE_GROUP {
|
||||
continue;
|
||||
}
|
||||
assert!(
|
||||
group.body.is_none(),
|
||||
"{} 现在应该还是演示行(真控件由它自己那条 section 线接)",
|
||||
group.name
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user