test(app-iced): M79 r2 —— probe 复跑暴露的两处修正(分隔线判据 / 可见性警告)

真跑 `navigation::probe` 才发现的两件事,都只影响测试代码:

1. **Secondary 分隔线判据错了**:原来按 `SurfaceVariant` 的**精确值**找块,
   但 1px 的线画在分数像素上,真实像素是「线色 ⊗ 页面底色」的混合值
   (实测 `(221,233,245)`,不是令牌原值)→ 找到 **0 段**,断言假红。
   改成:先找「与页面底色不同、横跨 >150px 的那一行」,再用**该行自己的颜色**数连通段
   (`probe/scan.rs` 新增 `longest_h_line` / `line_segments` / `differs`)。
   现在:r1 基线图 → 实测 **2 段**(红);r2 新图 → **1 段**(绿)。
2. `matches` 原来被写成 `differs` 的正向委托(语义反了),以及 `pub(super) fn shot` 暴露了
   更私有的 `Shot`(`private_interfaces` 警告,`-D warnings` 会红):前者改回
   `!differs(..)`,后者把 `shot`/`shot_dir` 收回私有(子模块 `probe::long` 仍可见)。

其他:`probe/long.rs` 的 `filter(..).next()` → `find(..)`(clippy::filter_next)。

复跑(真实输出):
- `ULL_NAV_DIR=target/m79/r2 cargo test -p unlucky-app-iced navigation::probe` → **5 passed**
- `cargo test -p unlucky-app-iced` → **197 passed**(+ gate 6 passed,1 ignored)
- `cargo clippy -p unlucky-app-iced --all-targets -- -D warnings` → 干净
This commit is contained in:
AskaEth committed 2026-11-02 11:56:04 +08:00
1 parent a36f542f28
commit 55a775fca7
3 files changed
+65 -15

No files matched your search

@@ -40,7 +40,7 @@ mod scan;
use scan::{rgb, ui, BBox, Rgb, Shot};
/// `ULL_NAV_DIR` → 截图目录(相对路径按**工作区根**解释:`cargo test` 的 cwd 是 crate 目录)。
pub(super) fn shot_dir() -> Option<PathBuf> {
fn shot_dir() -> Option<PathBuf> {
let raw = std::env::var_os("ULL_NAV_DIR")?;
let path = PathBuf::from(raw);
if path.is_absolute() || path.exists() {
@@ -65,7 +65,7 @@ macro_rules! require_dir {
};
}
pub(super) fn shot(dir: &Path, part: &str, flavor: SchemeFlavor) -> Shot {
fn shot(dir: &Path, part: &str, flavor: SchemeFlavor) -> Shot {
let slug = match flavor {
SchemeFlavor::Light => "light",
SchemeFlavor::Dark => "dark",
@@ -199,13 +199,16 @@ fn tabs_indicator_tracks_the_selected_tab() {
measured.push((bar.width(), ink));
}
// 6.6:Secondary 的分隔线必须是**一条连续直线**(逐 tab 分段画会在相邻段之间留 1px 亮缝,
// 视觉 r1 P2-2 实测 `25..101 | 103..191 | 193..265`)
let divider = rgb(ui.color(Role::SurfaceVariant));
let segments = shot
.blocks(divider)
.into_iter()
.filter(|b| b.height() <= 2 && b.width() >= 20)
.count();
// 视觉 r1 P2-2 实测 `25..101 | 103..191 | 193..265`)。
//
// 判法:1px 的线画在分数像素上 → 真实像素是「线色 ⊗ 页面底色」的**混合值**(实测
// `(221,233,245)`,不是 `SurfaceVariant` 的原值),所以不能按精确色找块。改成:
// 先找「与页面底色不同、横跨 >150px 的那一行」,再用**该行自己的颜色**数连通段。
let surface = rgb(ui.color(Role::Surface));
let divider = shot
.longest_h_line(surface, 150)
.expect("Secondary 那一行应有一条长横线");
let segments = shot.line_segments(divider.0, divider.1, divider.2);
assert_eq!(
segments, 1,
"Secondary 分隔线应是一条直线,实测 {segments} 段(分段画会留缝)"
@@ -55,8 +55,7 @@ fn long_text_stays_single_line() {
let bar = shot
.blocks(rgb(ui.color(Role::Surface)))
.into_iter()
.filter(|b| b.width() > 300 && (54..=58).contains(&b.height()))
.next()
.find(|b| b.width() > 300 && (54..=58).contains(&b.height()))
.expect("长文案块应有一条 56 高的页头");
assert!(
(55..=57).contains(&bar.height()),
@@ -40,10 +40,13 @@ pub(super) fn rgb(color: iced::Color) -> Rgb {
}
pub(super) fn matches(pixel: Rgb, expected: Rgb) -> bool {
pixel
.iter()
.zip(expected)
.all(|(a, b)| a.abs_diff(b) <= TOLERANCE)
// **每个通道**都在容差内;`differs` 是它的否定(任一通道超差即「不同」)
!differs(pixel, expected, TOLERANCE)
}
/// 两个颜色在**每个通道**上都不超过 `tol`。
pub(super) fn differs(pixel: Rgb, expected: Rgb, tol: u8) -> bool {
pixel.iter().zip(expected).any(|(a, b)| a.abs_diff(b) > tol)
}
/// 一个外接框(闭区间)。
@@ -178,6 +181,51 @@ impl Shot {
span
}
/// 全图里**第一行**横跨 ≥ `min_len` 的「与底色不同」的连续像素:返回 `(y, x0, x1)`。
///
/// 用于找 1px 的分隔线:它的颜色是混合值,不能按精确色找。
pub(super) fn longest_h_line(
&self,
background: Rgb,
min_len: usize,
) -> Option<(usize, usize, usize)> {
for y in 0..self.height {
let mut run = 0;
let mut start = 0;
for x in 0..self.width {
if differs(self.at(x, y), background, 6) {
if run == 0 {
start = x;
}
run += 1;
if run >= min_len {
return Some((y, start, x));
}
} else {
run = 0;
}
}
}
None
}
/// `y` 行 `[x0, x1]` 上「按该行中点的颜色」数的连通段数(分隔线断没断)。
pub(super) fn line_segments(&self, y: usize, x0: usize, x1: usize) -> usize {
let line = self.at((x0 + x1) / 2, y);
let mut segments = 0;
let mut in_run = false;
for x in x0..=x1 {
let on = matches(self.at(x, y), line);
if on && !in_run {
segments += 1;
in_run = true;
} else if !on {
in_run = false;
}
}
segments
}
/// 矩形内「与底色不同」的像素的左右范围(量文本墨迹用)。
///
/// 为什么不按精确文字色找:小字号抗锯齿后几乎没有像素落在**精确**色上(容差 3 时实测