尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

void 编辑器 Diffing Fixture 测试机制全解:从期望文件自愈到 diff 算法回归保障

void 编辑器 Diffing Fixture 测试机制全解:从期望文件自愈到 diff 算法回归保障 void 编辑器 Diffing Fixture 测试机制全解从期望文件自愈到 diff 算法回归保障【免费下载链接】void开源AI代码编辑器Cursor的替代方案。项目地址: https://gitcode.com/GitHub_Trending/void2/void在 void基于 VS Code 的开源 AI 代码编辑器中行级 diffLines Diff算法是编辑器差异视图、合并编辑器Merge Editor与代码评审体验的核心基础。本文以仓库内 diffing fixture 测试说明 为骨架结合 fixtures.test.ts 与 DefaultLinesDiffComputer 的源码实现系统讲解这套黄金样本golden fixtures式回归测试的工作机制、目录约定、期望文件自愈流程以及修改 diff 算法时必须遵循的验证工作流。读完本文你将掌握如何为 diff 算法新增测试用例、理解*.expected.diff.json与*.invalid.diff.json的协作逻辑并能在不破坏既有行为的前提下安全修改 diff 算法。一、什么是 Diffing Fixture 测试Fixture 测试也称为黄金样本测试 / golden file test是 void 编辑器对 diff 计算器Lines Diff Computer进行回归保障的主要手段。其核心思想是为每对输入文件预先固化一份期望 diff 输出算法改动后只需比对实际输出与期望输出是否一致即可快速捕获行为漂移。这套测试位于 src/vs/editor/test/node/diffing 目录包含README.md测试机制的说明文档本文主题fixtures/全部测试样本与期望输出fixtures.test.ts驱动 fixture 测试的 Mocha 测试套件defaultLinesDiffComputer.test.ts针对算法组件的单元测试。二、目录结构与命名约定fixtures/下每个文件夹代表一个测试用例例如trivial、move-1、bracket-aligning、deletion、import-shifting等。每个测试文件夹遵循严格的命名规则1. 输入文件1.*与2.*以1.开头的文件是原始版本original以2.开头的文件是修改后版本modified测试会计算1.*→2.*的 diff。文件扩展名可以是任意类型.txt、.js、.json、.tsx、.tst测试套件通过startsWith(1.)/startsWith(2.)定位输入文件见 fixtures.test.ts。2. 为什么 TypeScript 样本用.tst而不是.ts对于故意包含语法错误、半成品代码或非规范缩进的 diff 样本例如move-1/1.tst中CHEAP_TOKENIZATION_LENGTH_LIMIT常量从2048改成了1024这类真实但非编译通过的代码如果使用.ts扩展名会被 TypeScript 编译器与 linter 扫描并产生报错污染构建。因此约定改用.tst扩展名既让编辑器/编译器将其视为普通文本又能通过文件名一眼识别其 TypeScript 语义。README 原文也明确指出Usetstinstead oftsto avoid compiler/linter errors for typescript diff files.3. 期望输出*.expected.diff.json每个测试文件夹同时包含两类期望文件文件含义advanced.expected.diff.json先进算法DefaultLinesDiffComputer的期望 diff 输出legacy.expected.diff.json旧算法LegacyLinesDiffComputer的期望 diff 输出测试套件会对每一个 fixture 文件夹 × 两种算法分别执行测试见 fixtures.test.ts 的双重循环从而同时守护新旧两条算法路径。部分文件夹还带有*.human.diff.json文件如class-replacement、ts-class、penalize-fragmentation这是人工评审后确认的权威期望结果作为维护者审查机器生成期望文件时的参照基准。三、期望文件的内容结构一个典型的*.expected.diff.json长这样以 trivial/advanced.expected.diff.json 为例输入为空文件 → 一行x{ original: { content: , fileName: ./1.txt }, modified: { content: x, fileName: ./2.txt }, diffs: [ { originalRange: [1,2), modifiedRange: [1,2), innerChanges: [ { originalRange: [1,1 - 1,1 EOL], modifiedRange: [1,1 - 1,2 EOL] } ] } ] }对照 fixtures.test.ts 中的DiffingResult接口可以理解每个字段original/modified记录两侧输入内容与文件名保证期望文件可回溯、可复核diffs顶层差异块IDetailedDiff数组每个块含originalRange/modifiedRange[startLineNumber, endLineNumberExclusive)半开区间以及innerChanges——即块内部的字符级细粒度映射如[1,1 - 1,1 EOL]其中EOL表示终点恰好落在行尾moves可选检测到的代码移动块IMoveInfo每个移动块包含被移动文本的原始/修改区间及其内部变更。当没有任何移动时该字段会被删除见 fixtures.test.ts。以 move-1/advanced.expected.diff.json 为例它识别出原始第[24,28)行被整体移动到修改后的[70,74)位置并记录该移动块内部的 4 个字符改动[26,36 - 26,40 EOL]→[72,36 - 72,40 EOL]这正是 diff 视图里代码块整体搬移并高亮内部变更功能的测试依据。四、期望文件的自愈机制核心工作流README 用三条规则概括了这套机制的灵魂结合 fixtures.test.ts 的源码可以精确还原每一步规则 1缺失的期望文件自动创建如果某个 fixture 目录缺少*.expected.diff.json通常是新加的测试用例测试运行时会用当前算法输出写入期望文件fixtures.test.ts同时写入一个空的*.invalid.diff.json文件fixtures.test.ts抛出错误No expected file! Expected and invalid files were written. Delete the invalid file to make the test pass.这样首次运行即固化基线且保证测试必然失败一次强制开发者主动确认新期望文件的内容。规则 2实际输出与期望不一致时自动更新期望文件如果*.expected.diff.json已存在但实际 diff 与期望不一致先将旧的期望内容备份写入*.invalid.diff.jsonfixtures.test.ts再用实际输出覆盖更新期望文件fixtures.test.ts抛出异常导致测试失败。规则 3存在*.invalid.diff.json时测试必然失败这是整个机制中防止假绿的关键设计。只要目录里残留任何*.invalid.diff.json测试就会失败即使期望文件已被更新若 invalid 文件为空代表新基线尚未确认测试要求开发者手动删除它fixtures.test.ts若 invalid 文件非空代表上次算法变更备份的旧期望测试会先验证当前输出与该 invalid 内容一致若一致则说明发生了可确认的行为回退从 invalid 文件恢复期望文件并删除 invalid若不一致则抛错fixtures.test.ts。三者的协作可以用下面的流程概括运行 fixture 测试 ├─ 无 expected 文件 → 写入 expected 空 invalid → 失败等待确认 ├─ 有 invalid 文件 → 必然失败等待人工清理/确认 └─ 有 expected 且无 invalid ├─ 输出一致 → 通过 └─ 输出不一致 → 旧期望备份为 invalid 更新 expected → 失败这套设计保证了算法变更后即使期望文件被自动改写测试也不会在第二次运行时悄然变绿——必须由开发者审查 diff、删除 invalid 文件回归才算真正完成。五、测试驱动与算法调用链理解 fixture 测试还需要知道它背后调用的算法。测试套件在 fixtures.test.ts 中这样构造输入const diffingAlgo diffingAlgoName legacy ? new LegacyLinesDiffComputer() : new DefaultLinesDiffComputer(); const ignoreTrimWhitespace folder.indexOf(trimws) 0; const diff diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: true });几个值得注意的实现细节输入统一规范化读取的文本会先做\r\n、\r→\n的换行归一化fixtures.test.ts避免平台差异干扰期望文件trimws命名即配置只要文件夹名包含trimws子串如invalid-diff-trimws、issue-202147-trimws就自动启用ignoreTrimWhitespace忽略行首尾空白比较这是 fixture 命名约定驱动测试行为的巧妙范例超时设置为无限fixture 场景规模可控测试目标是确定性输出而非性能始终开启移动检测computeMoves: true确保moves字段被持续验证。ILinesDiffComputerOptions的完整定义位于 linesDiffComputer.tsignoreTrimWhitespace、maxComputationTimeMs、computeMoves以及可选的extendToSubwords。正确性断言对 advanced 算法且非 trimws 场景测试还会额外调用assertDiffCorrectnessfixtures.test.ts把 diff 产生的所有字符级映射构造成一次TextEdit应用到原始文本后必须能精确还原出修改后文本。这是一条比与期望文件一致更强的性质约束从根上杜绝diff 结果自洽但对不齐的缺陷。底层算法实现先进算法 DefaultLinesDiffComputer 的computeDiff会依据输入规模选择不同策略defaultLinesDiffComputer.ts当两侧行数之和小于 1700 时使用动态规划算法DynamicProgrammingDiffing并对完全相同的行按1 Math.log(1 行长度)加权兼顾匹配质量大文件场景切换到Myers 算法MyersDiffAlgorithm以控制复杂度之后依次经过optimizeSequenceDiffs、removeVeryShortMatchingLinesBetweenDiffs等启发式优化heuristicSequenceOptimizations.ts最后通过 computeMovedLines.ts 完成移动块识别。算法组件的单测位于 defaultLinesDiffComputer.test.ts例如直接用LinesSliceCharSequence([hello world], ...)与LinesSliceCharSequence([hallo welt], ...)验证 Myers 算法行为或用getLineRangeMapping验证行区间映射的归一化结果。六、fixture 测试用例家族当前仓库fixtures/下包含约 60 组场景是 diff 算法行为边界的活字典按主题大致可分为类别代表用例覆盖的行为点基础行为trivial、equals、deletion、indentation空文件、完全相等、整段删除、缩进变化代码移动move-1、difficult-move、noisy-move1、false-positive-move、shifting-twice移动块识别、防误报、移动中的噪声容忍括号/结构对齐bracket-aligning、intra-block-align、json-brackets、ws-alignment括号对齐与空白感知类型语言专项ts-class、ts-methods、ts-strings、ts-comments、ts-import-ws-affinity、ts-advanced-bug等数十个ts-*TypeScript 重构场景下的 diff 质量回归缺陷invalid-diff-bug、invalid-diff-trimws、invalid-ranges、issue-131091、issue-185779、issue-201713、issue-204948、issue-214049等历史上修复过的 diff 缺陷防止复发算法质量fuzzy-matching、subword、word-shared-letters、minimal-diff-character、random-match-1/2/3、noise-1/2、penalize-fragmentation模糊匹配、子词切分、碎片化惩罚、噪声鲁棒性以bracket-aligning为例其1.tst与2.tst分别是同一段 Merge Editor 源码在重构前后的版本期望 diff 精确到[24,1 - 28,1 EOL]这样的字符列级别用于守护括号自动对齐这一差异渲染体验。而issue-*系列则把真实用户上报的缺陷固化为回归样本一旦算法回归即可在 CI 中立刻暴露。七、修改 diff 算法时的标准操作流程README 最后一段给出了维护者修改 diff 算法时的明确操作规范这也是贡献者最需要遵循的流程运行 fixture 测试修改算法实现后执行fixtures.test.ts所属测试套件仓库根目录运行测试脚本如scripts/test.sh对应的完整测试流程fixture 测试位于 node 侧测试中审查 diff重点检查被自动更新的*.expected.diff.json文件逐一确认每一处行为变化都是有意且正确的尤其关注innerChanges的字符级映射与moves的移动识别是否合理删除*.invalid.diff.json在所有期望文件确认无误后清理残留的 invalid 文件使测试恢复绿色。只要某次测试运行产生了*.invalid.diff.json就意味着测试保持失败状态——这正是对算法改动必须经过人工确认这一纪律的强制保证。若你的改动被判定为不应保留则只需用 invalid 文件中的旧内容恢复对应的 expected 文件即可。八、相关源码路径速查测试说明src/vs/editor/test/node/diffing/README.mdFixture 驱动测试src/vs/editor/test/node/diffing/fixtures.test.ts算法组件单元测试src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts全部测试样本src/vs/editor/test/node/diffing/fixtures先进算法实现src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts算法接口与选项src/vs/editor/common/diff/linesDiffComputer.ts旧算法实现src/vs/editor/common/diff/legacyLinesDiffComputer.ts启发式优化src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts移动行计算src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts九、总结void 的 Diffing Fixture 测试是一个设计精良的自愈式黄金样本测试体系用1.*/2.*输入文件固化场景用advanced/legacy双算法期望文件守护两条实现路径用*.invalid.diff.json充当强制人工确认的闸门杜绝算法改动在无人审查的情况下悄然通过 CI。对于任何计划修改 diff 算法、新增 diff 场景或排查差异视图缺陷的开发者理解这套机制——尤其是期望文件的自愈流程与trimws命名约定——都是高效工作的前提。【免费下载链接】void开源AI代码编辑器Cursor的替代方案。项目地址: https://gitcode.com/GitHub_Trending/void2/void创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表