From 16cbc0242137a6e0bf62b2f4c75b1e09d5911126 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Fri, 18 Sep 2026 07:29:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E6=A0=B7=E6=9C=AC?= =?UTF-8?q?=E7=9A=84**=E6=A0=87=E7=AD=BE=E5=8F=AF=E4=BB=A5=E6=92=92?= =?UTF-8?q?=E8=B0=8E**=20=E2=80=94=E2=80=94=20=E8=A6=86=E7=9B=96=E8=A1=A8?= =?UTF-8?q?=E5=A3=B0=E7=A7=B0=E8=A6=86=E7=9B=96=E7=AE=AD=E5=A4=B4=E5=87=BD?= =?UTF-8?q?=E6=95=B0=EF=BC=8C=E8=80=8C=E9=82=A3=E4=BB=BD=E6=A0=B7=E6=9C=AC?= =?UTF-8?q?=E6=A0=B9=E6=9C=AC=E4=B8=8D=E6=98=AF=E7=AE=AD=E5=A4=B4=E5=87=BD?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 我自己变异出来的(M47),是 M45 的下一层。M45 修的是"样本**少了**要红", 但**名字对不上内容**当时无人管。 ## 一、缺陷:标签是散文,内容没人核 实测:把 `['箭头函数', 'const fooSelfTest = () => {};']` 的**内容**换成 `'function fooSelfTest() {}'`(**标签不动**、样本个数不变) ⇒ 默认跑 **0 条 RED**、`red` 仍是 9(与干净树逐字相同)。 ★ 后果我实测过,而且它**让报警自己少说话**(不只是少覆盖): 在上面那个撒谎标签的前提下,把 `const` 那条正则删掉(⇒ **箭头写法真的认不出来了**), 报警只点名 `const = async (…`,**"箭头函数"一个字都不提**(`grep -c` = **0**)。 ⇒ 覆盖表**声称**覆盖了箭头函数,而报警里那一格**消失了**。 **该说话的地方没说话,而账面上看不出少了什么** —— 我们这一路最怕的那个形状。 ★ 与前面几层的关系(**每修一层,缺口换一层**): · `957ec5e`:发现规则**看不见某种写法**; · `8691940`:样本表**可以静默缩水**(M45); · 本次:样本表**可以撒谎**(M47)—— 表上的名字与内容**没有任何关系**。 ## 二、修法:给每条样本配一条**见证**(结构,不是散文) 每个样本从 `[名字, 内容]` 变成 `[名字, 内容, 见证]`,见证是"这条样本**凭什么**算那种写法"的 **机械**判据,并判两个方向: ① 内容必须满足自己的见证(⇒ **标签与内容不符 = 红**); ② 见证必须**能区分**:至少要能否掉另一条样本(否则 `/./` 这种空见证也算"覆盖")。 ⇒ 把"这条样本是哪一种写法"从**散文**挪进**结构**。 ## 三、变异验证(都已还原) | 变异 | 结果 | |---|---| | **M47** 箭头样本内容换成 function 声明(标签不动) | **红**:`这些样本标签与内容不符:箭头函数 ⇒ 覆盖表在说谎,而报警会因此少说话` | | **M48** 见证退化成 `/./`(对谁都成立) | **红**:`这些见证区分不了任何东西:箭头函数 ⇒ 见证表自己退化成"永远通过"` | | 复合:**M47 + 箭头支持真坏** | 两条红都出:先抓撒谎,再点名 `const = async (…`(⇒ 撒谎不再能掩盖覆盖) | | 回归 **M45** 删样本 | 仍 **1 条报警** | | 回归 **M43** 退化正则 | 仍 **1 条认不出** | ## 四、验证与状态 · 五个自检单独跑仍全 rc=0;默认跑 `checks=459 pass=455 fail=4 skip=0 red=9 … verdict=red`、 `mutants=48 ran=47 skipped=1 on_new_criteria=35 diag=none baseline=7/7✓`; 自检段现为 `…发现规则在 5 种写法上都有对照(要求 5 种,缺一即红;每条样本都带能区分它自己的见证)`。 · 残留全清;`git status` 仅本文件;`HEAD=8691940`。 · pi 的 `cc8beb7`(它插件那例)我已复核;它目录一行未动(那两处实验都在 `/tmp` 做)。 --- client/electron/test/run-all.mjs | 47 ++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/client/electron/test/run-all.mjs b/client/electron/test/run-all.mjs index 0ea5c3e..e3f3e36 100644 --- a/client/electron/test/run-all.mjs +++ b/client/electron/test/run-all.mjs @@ -2364,11 +2364,11 @@ let selfCheckRuns = 0; * ⇒ 样本与生产扫描调的是**同一个** `declaredSelfTests`(不是抄一份)。 */ const FORM_SAMPLES = [ - ['function 声明', 'function fooSelfTest() {}\n'], - ['箭头函数', 'const fooSelfTest = () => {};\n'], - ['async function', 'async function fooSelfTest() {}\n'], - ['名与括号间有空格', 'function fooSelfTest (a) {}\n'], - ['const = async (…', 'const fooSelfTest = async (a) => {};\n'], + ['function 声明', 'function fooSelfTest() {}\n', /^function fooSelfTest\(\)/], + ['箭头函数', 'const fooSelfTest = () => {};\n', /^const fooSelfTest = \(\) =>/], + ['async function', 'async function fooSelfTest() {}\n', /^async function fooSelfTest\(\)/], + ['名与括号间有空格', 'function fooSelfTest (a) {}\n', /^function fooSelfTest \(/], + ['const = async (…', 'const fooSelfTest = async (a) => {};\n', /^const fooSelfTest = async \(/], ]; /* * ★★ **样本表本身也必须被钉住**(我自己变异出来的 M45)。 @@ -2404,6 +2404,40 @@ let selfCheckRuns = 0; console.log(`RED 这些样本不在要求清单里(加了样本却没说是为了守哪种写法):${stray.join('、')}`); reds.push(`(自检)\`FORM_SAMPLES\` 有多余样本未登记进 \`REQUIRED_FORMS\`:${stray.join('、')}`); } + /* + * ★★ **标签不许撒谎**(我自己变异出来的 M47,比 M45 更深一层)。 + * + * 实测:把 `['箭头函数', 'const fooSelfTest = () => {};']` 的**内容**换成 + * `'function fooSelfTest() {}'`(**标签不动**、样本个数不变)⇒ 默认跑 **0 条 RED**、`red` 仍是 9。 + * ⇒ M45 修的是"样本**个数/名字**少了要红",但**名字对不上内容**当时无人管: + * 表上写着覆盖"箭头函数",而那份样本**根本不是箭头函数**。 + * + * ★ 后果我实测过,而且它**让报警自己少说话**(不只是少覆盖): + * 在上面那个撒谎标签的前提下,把 `const` 那条正则删掉(⇒ **箭头写法真的认不出来了**), + * 报警只点名 `const = async (…`,**"箭头函数"一个字都不提**(`grep -c` = **0**)。 + * ⇒ 覆盖表**声称**覆盖了箭头函数,而报警里那一格**消失了**。 + * 这正是我们这一路最怕的形状:**该说话的地方没说话,而账面上看不出少了什么。** + * + * ⇒ 修法:每个样本再带一条**见证**(witness)——"这条样本为什么算那种写法"的**机械**判据: + * ① 样本内容必须满足自己的见证(⇒ 标签与内容不符 = 红); + * ② 见证必须**能区分**:至少要能否掉另一条样本(否则 `/./` 这种空见证也算"覆盖")。 + * ⇒ 标签是**散文**,见证是**结构**;把"这条样本是哪一种写法"从散文挪进结构。 + */ + const liar = FORM_SAMPLES.filter(([, src, w]) => !w.test(src)).map(([name]) => name); + if (liar.length) { + console.log(`RED 这些样本**标签与内容不符**(内容不满足它自己的见证):${liar.join('、')}` + + ' ⇒ 覆盖表在说谎,而报警会因此**少说话**'); + reds.push(`(自检)\`FORM_SAMPLES\` 标签与内容不符:${liar.join('、')}` + + ' —— 声称覆盖了那种写法,而那份样本不是它'); + } + const vacuous = FORM_SAMPLES.filter(([name, , w]) => + !FORM_SAMPLES.some(([other, otherSrc]) => other !== name && !w.test(otherSrc))).map(([n]) => n); + if (vacuous.length) { + console.log(`RED 这些见证**区分不了任何东西**(对别的样本也成立):${vacuous.join('、')}` + + ' ⇒ 见证表自己退化成"永远通过"'); + reds.push(`(自检)\`FORM_SAMPLES\` 的见证对别的样本也成立:${vacuous.join('、')}` + + ' —— 空见证等于没覆盖'); + } const missed = FORM_SAMPLES.filter(([, src]) => !declaredSelfTests(src).includes('fooSelfTest')); if (missed.length) { console.log(`RED 发现规则认不出这些写法(⇒ 那种写法写了自检也不会被接线防线看见):` @@ -2438,7 +2472,8 @@ let selfCheckRuns = 0; console.log(`自检段:${SELFTESTS.length} 个自检已在**默认路径**内跑过` + `(${SELFTESTS.map(([n]) => `--${n}`).join('、')});` + `源码里 ${declared.length} 个 \`*SelfTest\` 函数**全部**在册(无"写了没接线");` - + `发现规则在 ${FORM_SAMPLES.length} 种写法上都有对照(要求 ${REQUIRED_FORMS.length} 种,缺一即红)`); + + `发现规则在 ${FORM_SAMPLES.length} 种写法上都有对照(要求 ${REQUIRED_FORMS.length} 种,缺一即红;` + + '每条样本都带能区分它自己的见证)'); } }