fix(app-iced): 复审 r2 三条收口(M237)

- P1-1 判据会让 CI 一亮红:`java_shot.rs` 原来写「没喂图时 `assert!(CI 未设置)`」,
  而 `.github/workflows/ci.yml` 的 test 作业在 ubuntu + windows 上都跑
  `cargo test --workspace --all-targets`,出图步骤只给 `options_shot` 备了 `ULL_DL_*_SHOT`
  (本判据的图不在出图清单里)⇒ `CI=true` 实测 exit 101,一合即红。改成:没喂图**一律响亮跳过**
  (`not_fed()` + CI 时多一句「本仓 CI 不出这张图,跳过是预期」),模块文档写清「要让它守门需要
  跨 scope 加出图步骤 + 注入 `ULL_M237_JAVA_SHOT`,以及那时该怎么改回硬失败」。
  自证:`CI=true` 不喂图 → exit 0(改前 101);无 CI 不喂图 → exit 0;喂图(浅/深)→ 真跑,exit 0。
- P2-1 同类错漏一处:实例列表卡画的是 `MaterialLayer::Card`(`instances.rs:263`),
  但 `list_row` 的 `ListItem` 没传底 ⇒ 行 hover/按下按 `Base::CANVAS` 合成。
  改成 `item.base(frame::BASE)`(同一层,不另立常量),并加进接线守卫。
  像素证据(`--force-state=hover`,1180×760 列表卡矩形 `(112,160)-(448,735)` 内 before/after 差):
  浅色 57923 px `(232,232,233)→(226,226,227)`,深色 57923 px `(32,38,31)→(35,40,35)`
  —— 即 hover 8% 从画布底换到卡底。
- 守卫自身升硬:`base_wiring.rs` 的计数原来按整段文本数,**把接线注释掉仍算数**(r2 实测绿)。
  改成先剔掉整行 `//` 注释再数;变异自证:注释掉 `item.base(frame::BASE)` ⇒ 红
  (`instances.rs: .base(..) 出现 0 次,期望 1 次`,退出码 101),还原后绿。
- 门禁(CARGO_TARGET_DIR=C:/Temp/ull-target/wt-237):fmt 0 / check --all-targets 0 /
  clippy --workspace --all-targets -D warnings 0 / test -p unlucky-app-iced --lib 907 通过 0 失败 /
  test --workspace 3344 通过 0 失败。
This commit is contained in:
AskaEth committed 2026-11-11 07:56:11 +08:00
1 parent 3dcd5fd117
commit ba441351dd
3 files changed
+71 -10

No files matched your search

+7
View File
@@ -252,6 +252,9 @@ fn list_column<'a>(
.style(frame::scroll_style(ui)); .style(frame::scroll_style(ui));
// 底部内缩:裁切线落在卡片的内边距带里,而不是压在圆角描边上(M102 / 视觉复审 ②)。 // 底部内缩:裁切线落在卡片的内边距带里,而不是压在圆角描边上(M102 / 视觉复审 ②)。
//
// M237:这一层画的是 `MaterialLayer::Card`(= `frame::BASE` 的同一个层),所以卡里的
// 列表行把底传 `frame::BASE`(见 `list_row`)——不另立常量,避免同一层出现两个「真源」。
container(column![head, scroller].height(Length::Fill)) container(column![head, scroller].height(Length::Fill))
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill)
@@ -312,6 +315,10 @@ fn list_row<'a>(
if let Some(state) = forced { if let Some(state) = forced {
item = item.state(state); item = item.state(state);
} }
// M237 复审 P2-1:列表卡画的是 `MaterialLayer::Card`(本文件 `list_column` 的 style),
// 行的 hover / 按下状态层按 `base.role()` 合成(`components/navigation/list_item.rs::palette`),
// 不给底就落回 `Base::CANVAS` —— 卡片里的行抬起时会叠在画布色上,与本轮修的禁用态同一类错。
item = item.base(frame::BASE);
list_item(ui, item) list_item(ui, item)
} }
@@ -41,6 +41,19 @@
//! `ULL_SETTINGS_GROUP=2` 是「Java」组(`settings::java::JAVA_GROUP`);不给就画出 General 组, //! `ULL_SETTINGS_GROUP=2` 是「Java」组(`settings::java::JAVA_GROUP`);不给就画出 General 组,
//! 那张图上没有禁用填充按钮,判据会拿不到样本并**大声报出来**(不是静默通过)。 //! 那张图上没有禁用填充按钮,判据会拿不到样本并**大声报出来**(不是静默通过)。
//! //!
//! ## CI 上这条判据是**跳过**的(复审 P1-1)
//!
//! `.github/workflows/ci.yml` 的 test 作业在 ubuntu 与 windows 上都跑
//! `cargo test --workspace --all-targets`,而**出图步骤只给 `options_shot` 备了四张图**
//! (`ULL_DL_*_SHOT`,Windows 一条)。本判据要的这张 Java 图**不在出图清单里**
//! (`grep -r ULL_M237 .github/` 零命中)⇒ 在 CI 上缺图是**预期**,实现里因此**跳过而不是失败**
//! (见 [`not_fed`]:初版写成「CI 就 panic」,`CI=true` 实测 exit 101,一合就红)。
//!
//! 要让它真的进 CI 守门(跨 scope,塔另排):① ci.yml 加一条 `if: runner.os == 'Windows'` 的
//! 出图步骤(命令见上面的「跑法」);② 把 `ULL_M237_JAVA_SHOT` 写进 `$GITHUB_ENV`;
//! ③ 把 `not_fed` 里那条 `eprintln!` 换成 `assert!(!(cfg!(windows) && in_ci()), …)`
//! ——与 `pages/download/tests/options_shot/feed.rs::not_fed`(`:104-124`)同一口径。
//!
//! ## 抓得住 / 抓不住 //! ## 抓得住 / 抓不住
//! //!
//! - 抓得住:① 页面调用点漏了 `.base(..)`(回落到 `Base::CANVAS`)——卡区里会出现「叠画布」档、 //! - 抓得住:① 页面调用点漏了 `.base(..)`(回落到 `Base::CANVAS`)——卡区里会出现「叠画布」档、
@@ -155,15 +168,40 @@ fn flavor_of(shot: &Shot) -> SchemeFlavor {
candidates[0].0 candidates[0].0
} }
/// 没喂图时的统一裁决:**一律响亮地跳过**,绝不 panic。
///
/// ## 为什么这里不能照 `options_shot/feed.rs` 那样「CI 就硬失败」(复审 P1-1)
///
/// `options_shot` 那条硬失败(`feed.rs:104-124` 的 `!(cfg!(windows) && in_ci())`)成立的前提是
/// `.github/workflows/ci.yml` 的 test 作业**真的**在 Windows 上出那四张图、并把 `ULL_DL_*_SHOT`
/// 写进 `$GITHUB_ENV`(`ci.yml:92-123`)。本判据的图**不在任何出图清单里**
/// (`grep -r ULL_M237 .github/` 零命中),而 `cargo test --workspace --all-targets` 在 ubuntu 与
/// windows 两个 runner 上都跑 ⇒ 照抄那条口径会让**每个 PR 一亮红**:实测
/// `CI=true cargo test -p unlucky-app-iced --lib java_shot` 会 panic(初版就是这么错的)。
///
/// ## 要在 CI 上真的守门(跨 scope,由塔另排)
///
/// 1. `ci.yml` 加一条 `if: runner.os == 'Windows'` 的出图步骤(命令见本文件模块文档的「跑法」);
/// 2. 把 `ULL_M237_JAVA_SHOT` 写进 `$GITHUB_ENV`(与 `ULL_DL_*_SHOT` 同一条路);
/// 3. 那时把本函数末尾那条 `eprintln!` 换成
/// `assert!(!(cfg!(windows) && in_ci()), "守门未喂图:{SHOT_ENV} 没设 …")`
/// —— 与 `options_shot/feed.rs::not_fed` 同一口径:**只有「会出图的平台 + CI」才硬失败**,
/// 别的平台缺图是预期。
fn not_fed() {
let ci = std::env::var_os("CI").is_some_and(|value| !value.is_empty());
eprintln!("[pages] 页面级像素判据未喂图:{SHOT_ENV} 没设,跳过(跑法见本文件模块文档)。");
if ci {
eprintln!(
"[pages] 当前是 CI:本仓 CI 不出这张图(ci.yml 只给 options_shot 注入 ULL_DL_*_SHOT),\
所以这里跳过是**预期**,不是漏跑。让它守门的步骤见 not_fed 的文档。"
);
}
}
#[test] #[test]
fn the_java_card_composites_disabled_controls_over_its_own_base() { fn the_java_card_composites_disabled_controls_over_its_own_base() {
let Some(path) = std::env::var_os(SHOT_ENV) else { let Some(path) = std::env::var_os(SHOT_ENV).filter(|value| !value.is_empty()) else {
// 本机允许不喂图(会大声说一句);CI 里没图就是失败——判据静默空转等于没有判据。 not_fed();
assert!(
std::env::var_os("CI").is_none(),
"守门未喂图:{SHOT_ENV} 没设(跑法见 `java_shot.rs` 的模块文档)"
);
eprintln!("[skip] {SHOT_ENV} 没设:页面级像素判据未执行(本机档)");
return; return;
}; };
+19 -3
View File
@@ -10,6 +10,11 @@
//! 这里退一步,只数「每个文件里出现了几次接线调用」。它证明的是**接线还在**,不是「画对了」; //! 这里退一步,只数「每个文件里出现了几次接线调用」。它证明的是**接线还在**,不是「画对了」;
//! 真正的「叠的是哪一层底」由像素判据与 `interaction::disabled::tests` 两层负责。 //! 真正的「叠的是哪一层底」由像素判据与 `interaction::disabled::tests` 两层负责。
//! //!
//! 清单里有两种接线:**禁用态**(`Button::disabled` / `Switch::disabled` / `Chip::disabled`)与
//! **hover / 选中状态层**(`Text`·`Outlined` 按钮、`ListItem` 行、手写 chip 的 `state_over`)。
//! 像素判据只量到禁用态那一档(`pages/settings/tests/java_shot.rs` 的 Java 卡),
//! 所以 `instances.rs`(列表行,复审 P2-1)这种**只有状态层**的接线只能靠这份计数守。
//!
//! ## 抓得住 / 抓不住 //! ## 抓得住 / 抓不住
//! //!
//! - 抓得住:删掉 / 注释掉某个调用点的 `.base(..)`、或把整个文件的接线回退(数字掉一即红)。 //! - 抓得住:删掉 / 注释掉某个调用点的 `.base(..)`、或把整个文件的接线回退(数字掉一即红)。
@@ -23,7 +28,8 @@
//! 数是 `BASE.role()`(它们不走组件的 `.base(..)`,而是把状态层直接叠在 `frame::BASE` 上)。 //! 数是 `BASE.role()`(它们不走组件的 `.base(..)`,而是把状态层直接叠在 `frame::BASE` 上)。
/// `(文件, 期望的接线次数)`:文件路径相对 `pages/`。 /// `(文件, 期望的接线次数)`:文件路径相对 `pages/`。
const WIRED: [(&str, usize); 8] = [ const WIRED: [(&str, usize); 9] = [
("instances.rs", 1),
("instances/mods.rs", 1), ("instances/mods.rs", 1),
("instances/saves.rs", 2), ("instances/saves.rs", 2),
("instances/launch_args.rs", 5), ("instances/launch_args.rs", 5),
@@ -41,13 +47,23 @@ const ROLE_SITES: [(&str, usize); 3] = [
("settings/logs_row.rs", 1), ("settings/logs_row.rs", 1),
]; ];
/// 数**非注释行**里 `needle` 的出现次数。
///
/// 整行注释(`//` / `///` / `//!`)先剔掉:**把接线注释掉不算接线**,而「注释掉一行」恰恰是
/// 最容易发生的回退。复审 r2 实测过:只按 `source.matches()` 数,注释掉
/// `item.base(frame::BASE)` 之后本判据仍是绿的(注释里还留着 `.base(` 这四个字符)。
fn count(haystack: &str, needle: &str) -> usize { fn count(haystack: &str, needle: &str) -> usize {
haystack.matches(needle).count() haystack
.lines()
.filter(|line| !line.trim_start().starts_with("//"))
.map(|line| line.matches(needle).count())
.sum()
} }
#[test] #[test]
fn every_card_internal_control_site_still_passes_a_base() { fn every_card_internal_control_site_still_passes_a_base() {
let sources: [(&str, &str); 11] = [ let sources: [(&str, &str); 12] = [
("instances.rs", include_str!("../instances.rs")),
("instances/mods.rs", include_str!("../instances/mods.rs")), ("instances/mods.rs", include_str!("../instances/mods.rs")),
("instances/saves.rs", include_str!("../instances/saves.rs")), ("instances/saves.rs", include_str!("../instances/saves.rs")),
( (