搜索/参考当前的代码和测试,改进变异测试工具

参考当前的代码和测试,改进变异测试工具

修改变异测试工具时使用。先确认当前的代码和测试,保持处理顺序和惰性求值,编写不产生无效变异体的变异算子和能捕获变异体的测试,并按安全的审查、CI和发布步骤推进。

要使用这个AI规则
## 每次修改前,先确认当前的代码、测试和设计记录 将当前源代码树、测试、模式、架构决策和贡献者文档视为规范。当仓库证据与记忆、旧假设或通用框架模式不一致时,不要相信后者。 对于每项功能或缺陷修复: 1. 广泛搜索最相关的单元测试、集成测试、端到端测试、架构测试和自变异测试,包括相关名称、失败情况、无效输入和边界情况。 2. 阅读负责该行为的实现,了解顺序、默认值、失败语义和性能约束。 3. 当代码无法明确说明意图时,阅读所引用的设计记录和上游讨论。 4. 在实现前编写或移植一个范围明确且会失败的测试用例,并将其与证据关联。 5. 记录不明显的机制、版本关卡和有意不兼容行为的原因。 始终一致地使用项目术语。将mutation视为可序列化的变更描述,它可能在另一个进程中应用;将mutator视为独立的适用性检查与转换的组合。 修改前先梳理执行流水线:配置、源代码与覆盖率收集、AST扩充、mutation生成、mutant执行、可选的后续分析和报告,都是具有顺序的职责边界。如果一项变更让已记录的路径、关卡或约定不再正确,应在同一次变更中更新相应文档。 ## 遵守公开API、类型和命名的规则 - 生产代码不得依赖测试或基准测试。将随产品分发的测试辅助工具放入明确的生产侧测试命名空间,并以机械方式强制执行依赖方向。 - 将公共API视为允许列表。把其他内容都标记为内部使用,记录扩展点,并把对允许列表的任何更改归类为兼容性事件。 - 只有从不被模拟的类才能使用硬性的`final`。不要只为模拟一个类而新增接口,同时让需要模拟的服务在技术上保持可替换。 - 以一致方式import函数和常量。对于可能返回`false`的调用,应明确选择失败语义:当失败属于异常情况时使用安全包装器;确实需要false返回值时,使用命名清晰的unsafe alias。 - 优先使用类型化常量、显式override标记和不可变值对象。如果类会向自身属性写入memoized结果,就不要把它设为readonly。 - 只为静态分析无法证明的不变量使用运行时assertion。assertion消息必须指出失败意味着哪种缺陷。 - 让命名表达契约:`find*`返回可为null的值,`get*`保证返回值,命名构造函数表达领域含义,而异常名称应描述领域失败,不要只以通用的`Exception`结尾。 - `Memoized*`只用于每个实例一次且外部不可见的复用。只有调用方能观察到key、失效、TTL或共享backend时,才使用`Cached*`。 - 让纯static类和仅含常量的类不可实例化。依赖注入只用于有状态或执行IO的协作组件。 - 让从上游复制的代码可被识别,保留其归属信息,并记录每一项有意的本地差异。 ## 保持惰性求值,并用参数数组传递子进程命令 - 使用memoized deferred抽象表示开销较大且可复用的值。原始generator只能遍历一次,不得倒回、计数或buffer。 - 区分严格的一对一mapping和一对多的flat mapping。不要让相似的方法名掩盖stream基数差异。 - 只有在记录了所有权和原地修改效果的情况下,才允许在经过测量的hot path上使用按引用传递的mutation。 - 注入clock和filesystem抽象,不要直接调用时间或磁盘primitive。测试必须能够控制时间和filesystem结果。 - 子进程命令应始终表示为字符串参数数组。一个数组元素就是一个参数;绝不要预先拼接shell command,也不要假定会按空白拆分。 - 只保留一个具有类型化getter和lazy factory的显式service registry。使用方不得进行无类型的service lookup,也禁止注册推测出来但无法到达的service。 - 通过clone container来重新bind配置,不要改变共享进程状态。wiring closure变得复杂时,应提取类型化builder。 - 根据每个field的语义merge配置:可为null的override使用null-coalescing;任一来源都可强制开启的flag使用逻辑OR。统一这两种规则会在不易察觉的情况下改变行为。 - 相对输出路径应根据配置文件的位置解析;保留明确允许的stream path,并计算version control base,从而只处理用户有意做出的变更。 ## 保持AST的处理顺序,不改动变异前的AST - 将visitor注册顺序视为算法的一部分。在使用方之前解析名称和父节点,在附加测试前应用排除条件,并在处理每个文件前重置可复用visitor的状态。 - 通过类型化node attribute accessor传递信息。排除操作只把node标记为不适用,绝不在扩充过程中移除或替换node。 - 只为适用node惰性附加高开销的覆盖率查询。保留覆盖率文件发现与逐文件测试位置解析之间的两层惰性。 - 让XML query通过可识别namespace的安全包装器。保留有序的format fallback及其版本依据;清理工作不得移除仍受支持的分支。 - 保持process runner以streaming方式运行。用户测试使用的worker标识符是精确且从1开始的API值。后续分析应由process container chain负责,而不是由scheduler专用分支负责。 - 在调度计算中保持单位语义:enqueue工作所用的时间是时长,不是项目数量。复用worker slot前,释放对已完成container的引用。 - 将特定版本的test runner配置修改保持为彼此独立且可组合的步骤。保留权威的新式coverage配置、用户include filter、支持glob的路径和旧版document shape。 - 通过词法单元位置和node class匹配mutation目标。在mutation前clone原始AST,保留原始词法单元以便维持format的打印,并让原始tree在byte层面保持完全相同。 - 将mutation hash、field顺序和必需的位置attribute视为面向使用方的重新运行契约。重建node时复制每个child和attribute;有效替换可以是scalar、一个node或node list。 ## 编写不会产生无效或等价变异体的变异算子 一项完整的mutator变更应更新实现、所有中央registry和profile map、配置schema、规范测试;如果行为面向用户,还应更新用户文档。 - 将公共mutator表面保持在最小范围:definition、稳定名称、适用性和转换;只有mutator可配置时才增加配置访问。 - definition应说明mutation、category、diff示例,以及用户应如何kill该mutant。 - 在适用性检查中,按照语言真实的大小写规则比较语言标识符;当嵌套context很重要时,向上遍历ancestor。 - 绝不要生成语法错误、必然fatal的错误、等价mutation、无限循环,或已经由其他mutator负责的mutation。 - 可能发生重叠或等价时,应说明理由并增加一个有名称的非mutation用例。 - 构造替换AST node时复制所有child和metadata,避免attribute在无提示的情况下丢失。 - 通过生产环境的traversal harness执行mutator。不要创建会绕过适用性或scope行为的一次性traverser。 - 将缺少预期输出视为明确的“不发生mutation”assertion,并让data set名称可以直接作为规范阅读。 - mutation数量变化时,报告每个mutator的生成、被kill、escaped和timed-out数量。 ## 编写每次结果一致、能捕获变异体的测试 - 为每个具体source class提供一个规范测试,并为每个测试指定明确的coverage target。 - 将执行IO的测试归类为集成测试,将不执行IO的测试归类为非集成测试,并从两个方向强制执行这一边界。 - 让测试保持确定性:使用生成的临时路径,为输出敏感的fixture固定并发度,备份环境变量,并避免硬编码不存在的路径。 - 使用带有描述性用例名称的static data provider。对于参数较多的测试signature,优先使用named argument和default,但不要让使用不同参数子集的测试共享同一个provider。 - 用小型不可变builder或fake替代深层mock graph。让mock expectation断言精确参数,避免mutation通过宽松断言而逃逸。 - 让生产代码的结构便于观察mutation:优先使用early return、拆开的boolean结果和命名常量,而不是无法有效测试的magic literal。 - 避免使用会隐藏缺失依赖的构造函数default参数。如果data provider中的default能让每个用例只说明自身改变的内容,则可以使用。 - 使用项目的visitor harness、command harness和其他能反映生产行为的fixture。应该编写范围明确的单元测试时,不要改用一次性探查脚本。 - escaped mutant表示测试缺失或代码需要重塑。对于确实无法kill或只影响性能的情况,应展开讨论,不要编写牵强的测试。 - 迭代期间运行范围明确的测试。移交前运行仓库format工具、架构和静态分析审查、相关集成测试组及权威的完整测试套件。 ## 整理代码时保留按版本区分的配置和后备处理 - 将版本分界点和fallback ladder连同其保护的行为一起记录。不要仅因熟悉最新形式就简化compatibility matrix。 - 对有顺序的配置mini-language,应按顺序处理key并让后出现的entry优先。false sentinel必须移除该key已注册的全部内容,包括嵌套profile。 - 组合设置时保持merge方向和显式去重。命令行选择可以替换文件选择,但仍应保留global ignore rule。 - 让一个registry成为受支持mutator全集的唯一事实来源,并从中推导自动coverage check。 - 当测试名称filter接近regular expression上限时,应从精确过滤逐步放宽到安全超集,最后省略filter以运行完整测试套件。绝不要放宽到什么都不运行。 - 同时保留配对规则:当空选择有效时,runner报告“未执行任何测试”应视为pass。 - 移除debugger并restart后,不要根据当前已加载的extension推断能力。向restart handler查询跳过了哪个version,并只在需要coverage的child中启用它。 - memory limit调整只能在静态分析后应用于临时child配置,并应通过显式guard以best-effort方式执行。 - 保留进程开销较大的优化前,先进行benchmark。runtime或toolchain版本变化时,重新验证旧的performance workaround。 ## 保护锁文件,只合并CI通过的拉取请求 在无人值守安装中,绝不要绕过已冻结的lockfile。如果依赖解析确实需要更改lockfile,应停止并取得明确授权。精确版本、integrity hash和license compatibility都是supply chain边界。 绝不要直接push到受保护的default branch、绕过branch protection或强制执行administrative merge。采用以下顺序: 1. 创建范围明确的feature branch。 2. 实施最小且连贯的变更,并运行针对性check。 3. 按仓库要求的message convention进行commit。 4. push该branch并创建pull request。 5. 等待所有必需的format、type、unit、integration和end-to-end check通过。 6. 修复失败,不要绕过它们。 7. 通过获准的方法merge,并移除该branch。 不要把refactoring与behavior change混在一起。修复的缺陷必须配有regression test;兼容性破坏必须提供明确的release note和migration guidance;面向用户的行为必须同步用户文档。 让release automation从conventional commit推导metadata。只有在有意重新分类时才使用已记录的override,绝不要手工修改生成的release artifact。 针对不受信任贡献的CI必须在没有secret且只有只读权限的条件下运行。包含secret的preview必须跳过fork,除非获准maintainer通过带有identity gate的command明确触发。 将每个已部署示例登记到build matrix,并增加smoke test来验证预期route、status和content。只有必需check和部署后证据都通过,release或deployment才算完成。