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
@@ -210,22 +210,34 @@ fn submit_is_opt_in() {
|
||||
/// 回归(T27 反例 ①):`text_field` 必须把 `Field` 上的提交回调**转发**给 `text_input`。
|
||||
///
|
||||
/// iced 的 `Element` 没有内省接口,所以这条守的是**源码结构**(本仓既有同一条纪律,见
|
||||
/// `pages::layout::source` 的模块文档)。两条断言合起来才等于「转发」:消息只从
|
||||
/// `Field::submit_message()` 取(唯一读取口),且它被交给 `on_submit_maybe`。
|
||||
/// `pages::layout::source` 的模块文档)。三条断言合起来才等于「转发」:有一**行代码**在调
|
||||
/// `on_submit_maybe`、它的实参不是写死的 `None`、消息确实来自 `Field::submit_message`(唯一读取口)。
|
||||
///
|
||||
/// 抓得住:删掉 `.on_submit_maybe(..)` 那一行 / 改成 `on_submit_maybe(None)` / 把
|
||||
/// `Field::on_submit` 存着不读(变异实测见 M252 报告)——本条立刻红。
|
||||
/// 抓不住:`text_input` 内部的键处理(那是 iced 的实现,不在这里)。
|
||||
/// **判据是「代码行」,不是「文本里还有那个字符串」**(M252 r1 的 P1-1):断言走
|
||||
/// `source::code_line`,注释先被剥掉。所以下面这些改法都会红:
|
||||
/// - 删掉 `.on_submit_maybe(..)` 那一行;
|
||||
/// - 把它**注释掉**(`//` 行注释,或 `/*` 独占一行的块注释);
|
||||
/// - 改成 `.on_submit_maybe(None)` ——**即使同时**把 `let submit` 写成 `let _submit` 把 rustc 的
|
||||
/// unused-variable 兜底也绕掉(复审实测过:那样测试与 lint 两条线会同时静默)。
|
||||
///
|
||||
/// 抓不住:`text_input` 内部的键处理(iced 的实现);等价重构(把值先赋给别的名字再传)——
|
||||
/// 那要同时改 needle,见 `pages::layout::source::code` 的边界一节。
|
||||
#[test]
|
||||
fn text_field_forwards_the_submit_callback() {
|
||||
let body = crate::pages::layout::source::function_body(FIELD_SOURCE, "pub fn text_field");
|
||||
let call =
|
||||
crate::pages::layout::source::code_line(body, ".on_submit_maybe(").unwrap_or_else(|| {
|
||||
panic!(
|
||||
"`text_field` 里没有一行**代码**在调 `on_submit_maybe`(注释掉也算没有):\n{body}"
|
||||
)
|
||||
});
|
||||
assert!(
|
||||
body.contains("submit_message("),
|
||||
"提交消息必须从 `Field::submit_message()` 取(唯一读取口),实际函数体:\n{body}"
|
||||
!call.contains("None"),
|
||||
"提交消息被写死成 `None`(回车什么都不发,且功能静默失效):{call}"
|
||||
);
|
||||
assert!(
|
||||
body.contains(".on_submit_maybe("),
|
||||
"取到之后必须交给 `text_input` 的 `on_submit_maybe`,实际函数体:\n{body}"
|
||||
crate::pages::layout::source::code_text(body).contains("submit_message("),
|
||||
"提交消息必须从 `Field::submit_message()` 取(唯一读取口;注释里写不算),实际函数体:\n{body}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -236,7 +248,8 @@ fn text_field_forwards_the_submit_callback() {
|
||||
/// 这条不靠「我看了一遍所有调用点」:白名单对不上就红;新增一处接线必须来这里登记,
|
||||
/// 这份摩擦是故意的。
|
||||
///
|
||||
/// 抓得住:任何一处**新增/删除**接在构建链上的 `.on_submit(..)`(本仓的链一律一行一条)。
|
||||
/// 抓得住:任何一处**新增/删除/注释掉**接在构建链上的 `.on_submit(..)`(本仓的链一律一行一条;
|
||||
/// 注释先被 [`scan`] 剥掉,所以 `/*` 独占一行的块注释也算删除)。
|
||||
/// 抓不住:写在别处的同名文本(注释/字符串里的例子)、写成同一行的接线,也不管那两处接的
|
||||
/// 消息对不对(由 `pages` 侧各自的用例钉)。
|
||||
#[test]
|
||||
@@ -255,10 +268,13 @@ fn submit_is_wired_only_at_the_two_call_sites() {
|
||||
);
|
||||
}
|
||||
|
||||
/// 递归找 `dir` 下**有一行以 `.on_submit(` 开头**的 `.rs`,记下相对 `root` 的路径(`/` 分隔)。
|
||||
/// 递归找 `dir` 下**有一行代码以 `.on_submit(` 开头**的 `.rs`,记下相对 `root` 的路径(`/` 分隔)。
|
||||
///
|
||||
/// 为什么按「行首」而不是整篇 `contains`:注释与字符串里出现的 `.on_submit(` 例子(包括本文件
|
||||
/// 自己)会假红。代价是**写成同一行的接线扫不到**——本仓的构建链一律一行一条,这条取舍记在上面。
|
||||
/// 谓词与三处接线守卫同一个(`source::has_code_line`):注释与字符串里出现的 `.on_submit(`
|
||||
/// 例子不算接线。代价是**写成同一行的接线扫不到**——本仓的构建链一律一行一条。
|
||||
///
|
||||
/// **测试文件跳过**([`is_test_path`]):守卫自己的 fixture 里有 `.on_submit(` 的样本(本文件、
|
||||
/// `pages/layout/source/code/tests.rs`),那不是调用点;测试也不会去接生产 UI。
|
||||
fn scan(root: &std::path::Path, dir: &std::path::Path, found: &mut Vec<String>) {
|
||||
let Ok(entries) = std::fs::read_dir(dir) else {
|
||||
return;
|
||||
@@ -267,17 +283,26 @@ fn scan(root: &std::path::Path, dir: &std::path::Path, found: &mut Vec<String>)
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
scan(root, &path, found);
|
||||
} else if path.extension().is_some_and(|ext| ext == "rs") {
|
||||
} else if path.extension().is_some_and(|ext| ext == "rs") && !is_test_path(&path) {
|
||||
let Ok(text) = std::fs::read_to_string(&path) else {
|
||||
continue;
|
||||
};
|
||||
if text
|
||||
.lines()
|
||||
.any(|line| line.trim_start().starts_with(".on_submit("))
|
||||
{
|
||||
if crate::pages::layout::source::has_code_line(&text, ".on_submit(") {
|
||||
let rel = path.strip_prefix(root).unwrap_or(&path);
|
||||
found.push(rel.to_string_lossy().replace('\\', "/"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 这个路径是不是**测试模块**的文件:任一组件叫 `tests` / `tests.rs`,或文件名以 `_tests.rs` 收尾。
|
||||
///
|
||||
/// 本仓的测试文件就这几种形状(`*/tests.rs`、`*/tests/*.rs`、`*_tests.rs`),全部由
|
||||
/// `#[cfg(test)]` 或 `tests/` 目标挂载;生产文件的 inline `mod tests` 不在这里判(那些文件本身
|
||||
/// 也可能是真调用点,例如 `section/accent.rs`)。
|
||||
fn is_test_path(path: &std::path::Path) -> bool {
|
||||
path.components().any(|component| {
|
||||
let name = component.as_os_str().to_string_lossy();
|
||||
name == "tests" || name == "tests.rs" || name.ends_with("_tests.rs")
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user