test(app-iced): 接线守卫改判「代码行」,注释掉不再算接线(M252 r1 P1-1)
复审实测:三处接线守卫都是纯文本 `contains`,把真接线注释掉(行注释,或 `/*` 独占一行的 块注释)之后 943 条用例一条不红;`field.rs` 那处连 rustc 的 unused-variable 兜底也能用 `let _submit` 一起绕掉 —— 功能整条静默失效时,测试与 lint 两条线同时不响。 - `pages/layout/source/code.rs`(新):`code_lines()` 剥 `//` 与跨行/可嵌套 `/* … */`, 字符串与字符字面量整段保留(`"http://x"` 里的 `//` 不剥);`code_line()` / `has_code_line()` 给「某行代码的行首就是那个调用」这条谓词。边界写在模块文档:原始字符串里出现 `"` 是唯一 可能朝松方向错的形态(本仓 src 现无此写法),其余偏差都是假红。 - 三处守卫改用它:`text_field_forwards_the_submit_callback`(行首是 `on_submit_maybe(` 且实参不是 `None`、消息来自 `submit_message`)、`the_rename_field_wires_enter`(回车与 「改名」按钮的行都含 `commit_rename()`,且都不含第二种页面意图 —— 一并关掉 r1 P2-1 「Debug 自证撑不住判据」)、`the_accent_field_wires_enter_to_commit`(含产地那条代码行)。 - `submit_is_wired_only_at_the_two_call_sites` 的 `scan()` 换成同一谓词并跳过测试文件 (守卫自己的 fixture 里也有 `.on_submit(` 样本)。 - T29 顺手收敛 scope 内的同族旧断言:`pages/instances/tests.rs` 的行距/散文接线判据与 `pages/instances/tests/i18n.rs` 的五条取词判据改走 `code_text()`(一行改动、方向不变)。
This commit is contained in:
1 parent
92a3c1bbc8
commit
a17f6fb29f
7 files changed
+464
-45
No files matched your search
@@ -98,27 +98,32 @@ fn list_column_spaces_rows_only_from_the_shared_constant() {
|
||||
let rows_chain = source::statement(file, "let mut rows = column(");
|
||||
|
||||
assert!(
|
||||
body.contains("ROW_SPACING"),
|
||||
"列表行距必须从 ROW_SPACING 取,不许另写:\n{body}"
|
||||
source::code_text(&body).contains("ROW_SPACING"),
|
||||
"列表行距必须从 ROW_SPACING 取(注释里写不算),不许另写:\n{body}"
|
||||
);
|
||||
assert!(
|
||||
body.contains("LIST_BOTTOM_INSET"),
|
||||
"底部内缩必须从 LIST_BOTTOM_INSET 取:\n{body}"
|
||||
source::code_text(&body).contains("LIST_BOTTOM_INSET"),
|
||||
"底部内缩必须从 LIST_BOTTOM_INSET 取(注释里写不算):\n{body}"
|
||||
);
|
||||
|
||||
// ① 行容器的构建链上不许叠间距(`column![head, scroller]` 上的合法间距不在这条链里)
|
||||
// ① 行容器的构建链上不许叠间距(`column![head, scroller]` 上的合法间距不在这条链里)。
|
||||
// 判据走「代码行」(`source::code_text` 剥注释):注释掉的 `.spacing(..)` 等于没有间距。
|
||||
assert!(
|
||||
!rows_chain.contains(".spacing("),
|
||||
!source::code_text(rows_chain).contains(".spacing("),
|
||||
"行容器的构建链上又出现了间距(M102 ② 的页级附加量):\n{rows_chain}"
|
||||
);
|
||||
|
||||
// ② 语句序列:每一处 `rows.spacing(…)` 的实参都必须**顺绑定追回 `ROW_SPACING`**
|
||||
// (直接用它、或 `let spacing = ROW_SPACING` 再中转都算;先 `let extra = Space::X1;`
|
||||
// 再 `rows.spacing(ui.space(extra))` 则红——r4 复审的变异 M4)
|
||||
//
|
||||
// 判据走**代码文本**(`source::code_text` 剥注释):注释掉的 `rows.spacing(..)` 不算「应用了间距」,
|
||||
// 否则把那一行注释掉整条用例会绿着通过(T29 的同族退化)。
|
||||
let body = source::code_text(&body);
|
||||
let applied = source::call_arguments(&body, "rows.spacing(");
|
||||
assert!(
|
||||
!applied.is_empty(),
|
||||
"`list_column()` 里没有把 ROW_SPACING 应用到行容器的调用:\n{body}"
|
||||
"`list_column()` 里没有把 ROW_SPACING 应用到行容器的调用(注释掉也算没有):\n{body}"
|
||||
);
|
||||
for argument in &applied {
|
||||
let ident = argument
|
||||
@@ -155,11 +160,11 @@ fn the_detail_note_goes_through_the_prose_box() {
|
||||
let body = source::function_body(include_str!("../instances/detail.rs"), "fn push_text");
|
||||
|
||||
assert!(
|
||||
body.contains("frame::prose("),
|
||||
"正文段没有走 frame::prose:\n{body}"
|
||||
source::code_text(body).contains("frame::prose("),
|
||||
"正文段没有走 frame::prose(注释里写不算):\n{body}"
|
||||
);
|
||||
assert!(
|
||||
!body.contains("frame::body("),
|
||||
!source::code_text(body).contains("frame::body("),
|
||||
"正文段被塞进了整页块(会失去 72 字符行宽承诺):\n{body}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
use unlucky_i18n::catalog::MISSING;
|
||||
use unlucky_i18n::{Catalog, Key, Lang};
|
||||
|
||||
use crate::pages::layout::source;
|
||||
|
||||
use super::super::Texts;
|
||||
|
||||
/// 中文界面里允许出现的 ASCII 词:**品牌名与技术标识**。
|
||||
@@ -341,24 +343,24 @@ fn every_english_literal_in_the_page_sources_is_whitelisted() {
|
||||
.unwrap_or_else(|| panic!("PAGE_SOURCES 里没有 {name}:拆文件之后忘了登记?"))
|
||||
};
|
||||
assert!(
|
||||
source("instances.rs").contains("Texts::of("),
|
||||
"实例页没有从 Texts 取词"
|
||||
source::code_text(source("instances.rs")).contains("Texts::of("),
|
||||
"实例页没有从 Texts 取词(注释里写不算)"
|
||||
);
|
||||
assert!(
|
||||
source("instances.rs").contains("Header::new(texts.title())"),
|
||||
source::code_text(source("instances.rs")).contains("Header::new(texts.title())"),
|
||||
"页头标题必须是词表里的那条(`Header::new` 里写死字符串 = 中文界面上顶着英文标题)"
|
||||
);
|
||||
assert!(
|
||||
source("instances/text.rs").contains("Key::Instances"),
|
||||
"text.rs 没有引用本族的 key"
|
||||
source::code_text(source("instances/text.rs")).contains("Key::Instances"),
|
||||
"text.rs 没有引用本族的 key(注释里写不算)"
|
||||
);
|
||||
assert!(
|
||||
source("app/view/instances.rs").contains("Key::InstancesStage"),
|
||||
"阶段标签没有走目录"
|
||||
source::code_text(source("app/view/instances.rs")).contains("Key::InstancesStage"),
|
||||
"阶段标签没有走目录(注释里写不算)"
|
||||
);
|
||||
assert!(
|
||||
source("instances/launch_args.rs").contains("texts.launch_title()"),
|
||||
"启动参数区没有走目录"
|
||||
source::code_text(source("instances/launch_args.rs")).contains("texts.launch_title()"),
|
||||
"启动参数区没有走目录(注释里写不算)"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@ use super::super::state::Message as PageMessage;
|
||||
///
|
||||
/// `PageMessage` 只有 `Debug + Clone`(没有 `PartialEq`,那些枚举不在本 mission 的 scope),
|
||||
/// 所以这里比 `Debug` 文本:枚举的 `Debug` 打印变体名与字段,变体不同必然文本不同。
|
||||
///
|
||||
/// 这条只钉「产地生产什么」;**「两条路都用了这个产地」由 [`the_rename_field_wires_enter`] 钉**
|
||||
/// (r1 复审 P2-1:上一版把两件事写进这里,而实际覆盖只在文本型断言里)。
|
||||
#[test]
|
||||
fn enter_takes_the_same_message_as_the_rename_button() {
|
||||
assert_eq!(
|
||||
@@ -33,20 +36,49 @@ fn enter_takes_the_same_message_as_the_rename_button() {
|
||||
);
|
||||
}
|
||||
|
||||
/// `rename_row` 里那枚输入框真的接了 `on_submit`,且接的是 `commit_rename`(与按钮同一个)。
|
||||
/// 回车与「改名」按钮**在源码里**都接 `commit_rename()`,且都没接第二种页面意图。
|
||||
///
|
||||
/// 抓得住(T27 反例):把 `.on_submit(..)` 那一行删掉 → 第一条断言红;把它接成
|
||||
/// `CancelRenameWorld` → 第二条断言红。
|
||||
/// 抓不住:回车键真的到达控件(iced 的键路径,见模块文档)。
|
||||
/// 判据是**代码行**(`source::code_line` 先剥注释),所以下面这些改法都会红:
|
||||
/// - 把 `.on_submit(..)` 那一行删掉,或注释掉(`//` / `/*` 独占一行的块注释);
|
||||
/// - 把它接成别的页面意图(取消 / 重开 / 逐键输入)——按钮那一行同样不许;
|
||||
/// - 让两条路走**不同**的产地(例如按钮仍写 `PageMessage::CommitRenameWorld` 字面量):
|
||||
/// 那时按钮那行不含 `commit_rename()`,第二条断言红。
|
||||
///
|
||||
/// 抓不住:回车键真的到达控件(那是 iced 的 `Widget::update`,见模块文档);同一行里写完的
|
||||
/// 等价接线(本仓构建链一律一行一条)。
|
||||
#[test]
|
||||
fn the_rename_field_wires_enter() {
|
||||
let body = source::function_body(include_str!("../saves/rows.rs"), "fn rename_row");
|
||||
let enter = source::code_line(body, ".on_submit(").unwrap_or_else(|| {
|
||||
panic!("`rename_row` 里没有一行**代码**接 `on_submit`(注释掉也算没有):\n{body}")
|
||||
});
|
||||
assert!(
|
||||
body.contains(".on_submit("),
|
||||
"输入框没接回车(去掉接线这条必须红),实际函数体:\n{body}"
|
||||
enter.contains("commit_rename()"),
|
||||
"回车必须接 `commit_rename()`(与「改名」按钮同一个产地):{enter}"
|
||||
);
|
||||
|
||||
let button = source::code_lines(body)
|
||||
.into_iter()
|
||||
.find(|line| line.contains("saves_rename_confirm()"))
|
||||
.unwrap_or_else(|| panic!("找不到「改名」按钮那一行:\n{body}"));
|
||||
assert!(
|
||||
body.matches("commit_rename()").count() >= 2,
|
||||
"回车与「改名」按钮必须共用 `commit_rename()`(两条路同一个 Message),实际函数体:\n{body}"
|
||||
button.contains("commit_rename()"),
|
||||
"「改名」按钮必须用同一个产地(RenameWorld 只有一处写法):{button}"
|
||||
);
|
||||
|
||||
// P2-1:两条路都只许这一个页面意图——接成别的(取消/重开/逐键输入)就是行为变了。
|
||||
for line in [&enter, &button] {
|
||||
for other in [
|
||||
"CancelRenameWorld",
|
||||
"StartRenameWorld",
|
||||
"RenameWorldChanged",
|
||||
"BackupWorld",
|
||||
"RequestDeleteWorld",
|
||||
] {
|
||||
assert!(
|
||||
!line.contains(other),
|
||||
"回车/按钮接成了别的页面意图({other}):{line}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user