Commit Graph

2 Commits

Author SHA1 Message Date
b6d0358079 test(harness): 变异测试拒绝在脏工作区上运行
两个变异 harness(Go 版 discover、Python 版 quota)都是「把变异写进源码
文件、跑测试、再还原」。这个形状有两个陷阱,上一轮都真踩到了:

一、脏工作区上的 baseline 是错的

变异被覆盖写入实现文件。如果文件本来就有未提交改动(上一轮被 background
task 超时杀掉、Ctrl-C 中断、编辑器里改了一半),那么:

  - 捕获的「原始内容」不是已提交的源码,baseline 跑的是别的东西
  - 留下的那个变异会被下一轮当成 baseline

两种情况下所有裁决都毫无意义,而 harness 仍然报出干净的分数 —— 正是
「判据失效」最坏的一种:它不报错。

改为直接拒绝运行(Go 版 t.Fatalf,Python 版退出码 2),并且不自动 checkout:
脚本分不清「残留变异」和「人正在改」,静默回退真实改动比停下来更糟。

二、注入必须原子,还原必须覆盖所有退出路径

Python 版原来用 open(path,"w"),就地截断。与它并发的构建(background
go test、编辑器 gofmt、编译器自己读文件)可能看到写了一半的文件,报
syntax error,然后被当成 COMPILE 静默豁免 —— 一个真实的变异就这样被放过。
改为 tempfile + os.replace。还原登记到 atexit 与 SIGINT/SIGTERM。

三、路径必须相对当前目录

Go 版最初用 cwd=包目录 + 仓根相对路径,git 匹配不到任何东西,守卫永远不
触发。两个 harness 现在都用 ./name,各自在自己目录里跑 git。

两个方向都验证过:干净时全杀(discover 11/11、quota 12/12),脏时拒绝运行。

(cherry picked from commit d01712a697)
2026-10-07 17:37:22 +08:00
a873fe4724 test(discovery): 真实变异注入 11/11,并给 harness 加三道防线
前几版 harness 先后报过「4 个变异存活」「11 个一个没跑」「9 个存活」,
查清后**全部是判据或变异本身失效,没有一个是代码问题**。这一版把三类
失效都变成显式失败,而不是静默给出错误结论。

**防线一:先证明 filter 选中了东西**
`go test -run <完全不存在>` **不打印** `[no tests to run]`(那句话只在
「包内有其他匹配项但目标没跑」时出现)。我原先据此写的检查方向是反的,
harness 第一步就自我否决。现在改成对比数字:空 filter 必须选中 0 个测试,
真实 filter 必须选中 >= 25 个。本次运行:32 / 0。

**防线二:编译失败 ≠ 变异被杀**
嵌套调用漏 `-tags luajit` ⇒ 纯 Lua 5.1 ⇒ billing.lua 的 goto 编译失败
⇒ 整包 build failed ⇒ **每个变异都「被杀」,给出满分**。这比假绿更隐蔽。
现在 [build failed] 直接 t.Fatalf。

**防线三:变异没改变行为 ≠ 判据漏放**
两次「存活」查清后都是**恒等变换**:
  `&& out.BaseURL != ""`  — 判据用零值结构体,BaseURL 为空,条件恒假
  `&& len(out.BaseURL) >= 0` — len 恒 >= 0,条件恒真
正确写法是把赋值换成 `_ =`:读了变量让编译器满意,行为真的变了。
检测方式:变异后若结果行(剥掉测试名、排序后)与 baseline 完全一致,
报 no-op 而不是记成存活。
no-op 检测本身也修过:原来保留 === RUN 行(含测试名),两次运行测试顺序
不同 ⇒ 每个变异都「看起来变了行为」,检测形同虚设。

11 个变异覆盖:kind 判定、metadata 透传、非目录拒绝、URL 回退、
版本段搬移(chat 与 image 两条)、版本段识别、nil 切片、鉴权分类、
候选去重。

★ 判据的期望值不写死数字:候选去重修好后 len(cands) 从 3 变 2,
连改两次判据都是在追当前列表。改成断言「该覆盖的 URL 都在」。
2026-10-06 15:02:42 +08:00