Repository navigation
Verification Methodology
这一页是本项目最可迁移的部分。它规定「什么算做完了」,适用于任何需要区分「代码写了」和「功能对了」的工程。
大量返工的根源是同一件事:把弱证据当成强证据。
「函数存在」「本次运行命中了」「独立复算一致」是三个不同的证据层级,它们不互相替代。一个功能可以同时满足前两条而仍然是错的。
| 等级 | 含义 | 典型产物 |
|---|---|---|
| 仅资源或符号 | 只有文件、字符串、配置或界面存在 | 目录清单、符号名、截图 |
| 仅静态入口 | 有实现逻辑或部分控制流,缺运行时参数与输出 | 代码走查、类型契约 |
| 单场景运行 | 在一次具体场景下命中并产出结果 | 日志、返回值、产物文件 |
| 上下文闭合 | 触发、入口、输入、核心逻辑、调用者、输出消费者、失败路径全部有据,且无剩余关键问题 | 端到端产物 + 独立复算 |
任一关键字段、失败路径或必要的运行归因缺失,即不评为闭合。
推论:
- 入口命中若缺场景、参数、返回或输出,只证明该入口在该会话可达,不证明功能正确
- 负结果只限定到明确场景,不能推广成「该功能不存在」
- 已纠正的历史错误要单独记录,不要让修正后的条目继续背着旧结论
这些都是从实际误判里总结出来的。
- 键鼠事件发送成功不等于操作生效
- UI 控件返回
true不等于文件真的写出去了 - 进程存活、界面响应、「已断开」文本,任何单独一条都不足以证明状态正常
验收必须看业务产物,不看中间调用的返回值。
这条在 X11 大图传输里有具体后果:只做 flush() 而不 check() 实际请求,会让读取方先看到内容、写入抑制被提前消费,同一份数据被重复录入。
OCR 引擎候选必须先通过 --version 才被用于识别。否则「探测到了」和「实际调用的」可能是两份不同的程序,用户看到的能力状态与真实行为不一致。
dbus.rs 的连接缓存是典型例子:
- 重连判据是
worth_reconnecting - 入缓存判据是
worth_caching - 两者必须严格互补
只按「调用成功」决定是否缓存,会把一条被业务错误拒绝但本身完好的连接扔掉,于是每次失败探测都要重新握手。
这是长截图给出的教训。一张高度完全正确的长图,中间可能少了一整段。
解法是每行唯一编码:给参考内容的每一行画上可解析的行号,成品解码后断言行号严格连续、相邻差全为 1。用尺寸断言永远发现不了漏行。
控件自己 RenderTargetBitmap 出来的图存在亚像素渲染差异,与合成器实际输出的帧不是同一批像素。实测过一次:直接比较有 36 万个像素不同、RGB MAE 约 2.66。
要验证采集链路,必须拿真实的采集缓冲区做比对。
只测「正确输入产生正确输出」不足以证明门禁有效——一个永远返回 true 的检查也能通过所有正向测试。
因此关键门禁要配套负控制:故意构造被篡改的输入,断言检查必须失败。
本项目里的负控制类型包括:错误的输出路径、无关日志、被篡改的传输内容、被篡改的源文件、被篡改的几何、被篡改的 fixture、非规范的输出位置。
同理,布局测试要有「负控制精确失败」的对照——旧的错误 CSS 必须让 fixture 变红,否则无法确认 fixture 真的在检查那件事。
进程与并发相关的代码,正向路径通过没有意义。必须注入:
- 超时
- 输入不被消费
- 输出超量
- 请求合并竞态
- 进程回收顺序
- 分配失败
- 磁盘写入失败
顺序也要断言:子进程 kill/wait 完成后才释放并发许可,否则许可被提前释放会让下一个请求与正在退出的进程重叠。
异步路径的陈旧响应问题无法靠随机测试稳定复现,要用可控 Promise:显式构造「A 慢 / B 快」两种到达顺序,断言旧响应永远不覆盖新结果。
需要这样处理的路径:搜索、分页、新增/删除事件、预览渲染(图片/尺寸/错误/OCR/延迟库)、翻译、朗读、窗口状态查询。
涉及系统资源的测试必须自建隔离环境,不能用开发者当前桌面:
- X11 协议测试新建私有 Xvfb,并验证它确实用了私有环境(故意污染父进程的
DISPLAY/XAUTHORITY后仍通过) - fixture 要保持客户端连接以防显示服务器自动 reset
- 同一测试应在多个独立新建的服务器实例上验证
原生测试默认标记 ignored,只由专用脚本显式运行,避免普通 cargo test 意外访问真实桌面或写入真实剪贴板。
作者不能替独立审查签字。 本项目的实践是:实施方提交实现与自测结果,独立审查方另出报告,两者都保留。
独立审查发现的问题单独列表,不混入原始问题清单——否则无法评估原始审查的覆盖率。
失败记录的价值不低于成功记录。它们防止同一条路被反复重试。
记录失败时要写清边界:
- 是环境失败还是业务失败(例如一次状态文件的共享读异常属于观察失败,不是被观察对象失败)
- 修复覆盖到哪一层(例如某个共享读问题在一处修好了,不等于所有同类采集器都已验收)
- 因果链是否完整(「目标消失」和「某调用导致目标消失」是两个不同强度的结论)
- 有多少证据说多少话,不把「本次未捕获」写成「未调用」
- 不用旧摘要否定新的成功证据,也不用少数成功替代全部验收
- 平台矩阵与安装包构建不能算进「测试通过」的计数
- 一次成功的手动启动只确认编译、进程和服务可用,不构成交互验收
- 未完成项一律保留,不写成全部完成