r1 复审的两条 P1 都是假护栏:把修复原样回滚(A:frame::body 换回居中 424; C:内缩 X2→X1;D:行距加回 4px)后测试仍全绿,因为断言只喂 frame_metrics() 与常量重算, 既不观察 frame::body,也不引用页面常量。本轮把「摆法」做成可断言的东西: - 新增 `pages/layout/block.rs`:body_width(pane 宽封顶 1440)、body_offset(恒 0 = 贴左)、 prose_width(= metrics.column ≤424)三个纯量 + 单测。frame::body / frame::prose 从它们建盒, frame::body_width 改成 pub use(不另算一份)。 - 新增 `pages/layout/source.rs`(cfg(test)):把某个函数的实现文本切出来。iced 的 Element 没有几何内省,量不到「块摆在哪」,只能断言「照哪个数建的」;仓里已有 no_hardcoded_tokens 扫源码、download/tests 用 include_str! 扫 key 的先例。 - 新护栏(每条都做过变异实测,见报告): · frame_body_is_built_from_the_block_quantities(A 变异红) · frame_prose_is_built_from_the_prose_width(E 变异红) · list_column_spaces_rows_only_from_the_shared_constant(D 变异红) · instances 几何断言改用页面真源 LIST_BOTTOM_INSET.dp() / ROW_SPACING(C 变异红) · the_detail_note_goes_through_the_prose_box(散文接线) - P2:wrap::strip 的「会删原文自带 U+200B」写成限界 + 一条断言;docs 口径从 layout.rs 挪到 layout/block.rs(layout.rs 399 → 389)。 零像素变化:改后 9 张图与 r1 的 m102-after 逐张 cmp 完全相同;2560×760 新出三张证明块宽封顶 1440(112..1551)且仍贴左。
40 lines
2.1 KiB
Rust
40 lines
2.1 KiB
Rust
//! **源码结构断言**的小工具:把某个函数的实现文本切出来(只给测试用)。
|
||
//!
|
||
//! ## 为什么要读源码(而不是量 Element)
|
||
//!
|
||
//! iced 的 `Element` 没有几何内省接口(`size()` 只是 `Size<Length>`,取不到布局结果),
|
||
//! 所以「页面把块摆在哪 / 有没有在行之间叠间距」在单测里**量不到**。M102 r1 的教训正是这个:
|
||
//! 护栏只喂 `frame_metrics()` 与常量重算,把 `frame::body` 换回「居中 424」的实现,
|
||
//! 测试照样全绿——钉住的是公式,不是摆法。
|
||
//!
|
||
//! 本仓已有同一条纪律的先例:`tests/no_hardcoded_tokens.rs` 直接扫 `src/**`,
|
||
//! `pages/download/tests.rs::every_download_key_is_referenced_by_the_page` 用 `include_str!`
|
||
//! 扫页面源码找 key 引用。这里只是把「切出某个函数」这件小事做成一个共用工具,
|
||
//! 让护栏能断言「盒子是不是照那个数建的」。
|
||
//!
|
||
//! ## 边界(它抓不住什么)
|
||
//!
|
||
//! 它读的是**文本**:换个写法(先赋值给局部变量再 `.width(...)`)就可能绕过。
|
||
//! 所以它只配一条**明确的契约**用:`frame::body` / `frame::prose` / `list_column` 这几处的
|
||
//! 写法本身就是契约的一部分(它们各自只有 5~10 行),不是为了抠实现细节。
|
||
//! 真要守像素,还是得靠软件渲染金图(`shot::diff`,绑 Windows + 字体栈,属另一个决定)。
|
||
|
||
/// 把 `source` 里第一个 `signature` 起始、到该函数**顶层**右花括号为止的文本切出来。
|
||
///
|
||
/// 「顶层」= 行首的 `}`(本仓函数都是顶格收尾,内层块都是缩进收尾),所以不会在内层 `}` 提前断。
|
||
///
|
||
/// # Panics
|
||
/// 签名不存在时 panic(护栏自己的前置条件坏了要立刻红,不能静默放行)。
|
||
pub(crate) fn function_body<'a>(source: &'a str, signature: &str) -> &'a str {
|
||
let start = source
|
||
.find(signature)
|
||
.unwrap_or_else(|| panic!("源码里找不到签名 {signature:?}"));
|
||
|
||
let rest = &source[start..];
|
||
let end = rest.find("\n}").map_or(rest.len(), |index| index + 2);
|
||
&rest[..end]
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests;
|