news 2026/9/10 2:17:07

Continue diff 测试用例三段式格式详解:为 streamDiff 算法编写黄金用例文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Continue diff 测试用例三段式格式详解:为 streamDiff 算法编写黄金用例文件

Continue diff 测试用例三段式格式详解:为 streamDiff 算法编写黄金用例文件

【免费下载链接】continueopen-source coding agent项目地址: https://gitcode.com/GitHub_Trending/co/continue

本篇指南围绕 core/diff/test-examples/README.md 展开,它是 Continue 开源编码智能体代码编辑(diff)子系统中“流式 diff 算法”测试用例(fixture)文件的格式规范。读完本文,你将掌握 Continue 如何用「旧代码 / 新代码 / 期望 diff」三段式文件为streamDiff编写黄金用例,理解displayDiff的渲染约定、期望结果与myersDiff解耦的原因,以及“留空期望段 → 自动回填计算 diff → 人工校正”的用例生成工作流,从而能够独立为仓库新增或修正 diff 测试样本。

背景:测试用例文件在整个 diff 测试体系中的位置

在 Continue 仓库中,代码 diff 相关实现集中在 core/diff 目录,其中包括:

  • streamDiff.ts —— 核心的流式 diff 算法实现,streamDiff(oldLines, newLines)是一个AsyncGenerator<DiffLine>
  • util.ts —— 提供matchLine(模糊行匹配)、streamLinesgenerateLines等辅助函数;
  • streamDiff.vitest.ts —— 单元测试入口,其中面向“真实代码片段”的测试通过读取test-examples目录下的用例文件来驱动;
  • test-examples —— 用例文件目录,目前包含 4 个.diff样本与这份 README 规范。

可见 core/diff/test-examples/README.md 承担着“测试样本的书写规范”角色:它不描述算法本身,而是精确规定如何把一个 diff 测试场景表达成一个可被测试框架自动解析的文本文件。这是 Continue 对“用真实代码样本而非随机字符串验证 diff 正确性”所做的工程化约定。

用例文件格式规范:三段式结构与---分隔符

README 将每个测试用例定义为如下三段式结构:

<CODE BEFORE> --- <CODE AFTER> --- <EXPECTED DIFF>

关键规则如下:

  1. 每个.diff文件必须按顺序包含三段:变更前代码(CODE BEFORE)变更后代码(CODE AFTER)期望的 diff(EXPECTED DIFF)
  2. ---单独成行作为段分隔符(README 称之为 delimiter);
  3. 分隔符两侧的空白(surrounding whitespace)会被修剪;
  4. 期望 diff 段可以通过displayDiff函数生成(具体语义见下文“渲染约定”)。

从仓库实现看,streamDiff.vitest.ts 中的expectDiff(file)正是这一规范的唯一解析与执行方:

const [oldText, newText, expectedDiff] = testFileContents .split("\n---\n") .map((s) => s.replace(/^\n+/, "").trimEnd());

也就是说,解析器先按字面\n---\n切分整个文件得到三段文本,再对每一段去除开头的空行并trimEnd()掉结尾空白——这与 README 中“surrounding whitespace will be trimmed”的说明一一对应。紧接着collectDiffs会把旧文本切行得到oldLines,把新文本经generateLines包装成行流后交给streamDiff,逐行收集真实计算结果。

需要注意的边界语义:解析只在整行恰好是---时切分。因此如果被测代码中确实包含独立的---文本行,需要规避该写法,以免破坏三段切分。

真实用例逐个拆解:四种典型的 diff 场景

当前 test-examples 目录中已有 4 个真实样本,覆盖了从简单修改到复杂重排的不同编辑形态:

1.fastapi.py.diff:最简单的“改函数体 + 改 import”

fastapi.py.diff 演示的是最基础的场景:在from fastapi import FastAPI中追加HTTPException,并把/landing路由的return改成raise HTTPException(status_code=404, ...)。其期望 diff 段如下(已去除-前缀符号列的空格细节,完整内容见文件):

- from fastapi import FastAPI + from fastapi import FastAPI, HTTPException ... + raise HTTPException(status_code=404, detail="Page not found") - return {"message": "Welcome to the landing page"}

注意期望段中每一行都带有+-或空格前缀,+/-分别表示“新行/删除行”,而空格开头表示上下文行(未变化行)——这正是下文displayDiff约定生成的 unified diff 外观。

2.fastapi-tabs-vs-spaces.py.diff:缩进风格(Tab 与空格)差异的容忍测试

fastapi-tabs-vs-spaces.py.diff 中,变更前代码使用\t缩进,变更后代码改为 4 空格缩进。它被 streamDiff.vitest.ts 中名为 “tabs vs. spaces differences are ignored” 的测试引用,用于验证:在期望 diff 中,仅 tab/空格风格差异的行被算作 changed(输出-/+对),而语义等价的删除-新增不会被误并成多行改动。该用例与源码 util.ts 中“比较前先trimEnd()去除行尾空白”以及基于编辑距离的模糊匹配策略相呼应。

3.add-comments.py.diff:以注释插入为主的大批量新增

add-comments.py.diff 展示了“给每段代码加注释”这种典型 AI 编辑产物:变更后代码成段插入了注释行,同时删除了原本的return {"Hello": "World"}一行并替换成带注释的版本。对应测试名为 “FastAPI comments”(streamDiff.vitest.ts)。它的期望 diff 里可以看到+行与-行在“新增段落 + 被替换单行”上如何编排,对注释插入这类编辑的 hunk 形态很有参考价值。

4.mock-llm.ts.diff:TypeScript 中静态属性改造成 getter/setter

mock-llm.ts.diff 是跨语言样本:把一个Mock extends BaseLLM类中的static Completion = "Test Completion"属性改造成private static _completion+static get/set completion访问器,并同步更新两处引用。它验证了 diff 对“删除一段 + 插入一段结构上相似代码”的多行处理(如把static Completion换成get completion段后,旧行与新行分别以-/+输出)。对应测试名为 “Mock LLM example”(streamDiff.vitest.ts),也是全部样本中唯一非 Python 用例,说明该格式并不绑定特定语言。

期望 diff 的渲染约定:displayDiff做了什么

README 指出:“期望 diff 可以用displayDiff函数生成。”在测试文件 streamDiff.vitest.ts 与 streamDiff.vitest.ts 中定义了渲染规则:

const UNIFIED_DIFF_SYMBOLS = { same: "", new: "+", old: "-", }; function displayDiff(diff: DiffLine[]) { return diff .map(({ type, line }) => `${UNIFIED_DIFF_SYMBOLS[type]} ${line}`) .join("\n"); }

要点:

  • 这里所谓的“diff”指的是streamDiff的输出——一个DiffLine[]数组,每个元素形如{ type, line },其中type取值来自DiffType = "new" | "old" | "same"(定义见 core/index.d.ts);
  • 渲染时按类型加前缀:same行加一个空格、new行加+old行加-,随后用换行拼接——结果就是上节样本中那种标准 unified diff 文本;
  • displayDiff只负责把行序列“符号化”,不负责生成 diff 本身,生成交给被测的streamDiff

为什么“显式期望”而不是直接对比myersDiff输出

README 特别强调了一个设计动机(原文要点):

“我们刻意把期望 diff 写成显式文本,而不是与myersDiff的输出做比较,因为myersDiff的输出可能是不可达的(unattainable),或者并不完全是我们想要的结果。”

这句话背后对应仓库的两个事实:

  1. Continue 的streamDiff在线流式(streaming)算法——它按行异步消费新文件,尽量“所见即所得”地即时产出结果,且在空行、缩进、模糊匹配上有自身策略(见 streamDiff.ts 注释中的不变量与 util.ts 的matchLine启发式),并不是先求出全局最短编辑脚本再回放;
  2. 经典的myers-diff包(在测试中被称为myersDiff)虽然被引入用于对照验证基础场景(例如 streamDiff.vitest.ts 里成对的streamDiffsmyersDiffs断言),但它追求的是确定性的“编辑距离最优解”,与streamDiff面向编辑体验的启发式策略并不总是一致。

因此,如果直接用myersDiff的输出当“期望值”,一旦两者策略分歧,测试就会失败或掩盖真实期望。把期望 diff 固化成仓库内的黄金文本,等于把“streamDiff应该输出成什么样”的最终裁决权交给人审定的显式规格,而非另一个算法。这也解释了为何 README 的表述是 “We make this explicit ... in case the output from that is either unattainable or not exactly what we want.”

用例生成工作流:留空 → 自动回填 → 人工校正

对于很难手写出正确 unified diff 的新场景,README 给出了官方推荐的迭代流程(结合 streamDiff.vitest.ts 的实现可完整还原):

  1. 新建样本:在 core/diff/test-examples 下新建<名字>.diff文件,先写好前两段(<CODE BEFORE><CODE AFTER>),把<EXPECTED DIFF>段留空;
  2. 注册测试:在streamDiff.vitest.ts的 describe 块中仿照现有写法添加一行,例如test("我的场景", async () => { await expectDiff("我的场景文件名"); });
  3. 运行测试:在仓库 core 目录下执行 vitest(如npx vitest run diff/streamDiff.vitest.ts)。当检测到期望段为空时,expectDiff会打印 “Expected diff was empty. Writing computed diff to the test file”,并把displayDiff(streamDiffs)计算出的文本连同前两段一起回写进该.diff文件,随后抛出Error("Expected diff is empty")使测试失败(这正是为了让自动回填的中间产物不会被误当成通过);
  4. 人工校正:检查回填的 diff 是否符合你对“理想输出”的预期——必要时手动调整增删行的顺序、范围与归属(例如把多余的整段重排修正成更小的局部修改),然后再运行测试直到通过。

这个工作流把“最容易出错的期望值书写”变成了“先让被测实现给出初稿、再由人来审定”,与 README 中“It is up to you to correct this to the expected diff”的说明完全一致。

编写新用例的实操建议

结合格式规范与streamDiff的实现特征,扩展测试样本时可以遵循以下检查清单:

  • 尽量使用真实代码片段:样本是fastapi.pymock-llm.ts这类可读片段,比随机短串更能暴露注释插入、括号块移动、缩进风格切换等真实编辑问题;
  • 关注空白与缩进:需要明确该场景是“应当容忍的缩进变化”还是“应当显式展示的修改”。例如 util.ts 中,仅当行内容长度足够(trim().length > 8)或后续行匹配持续宽松时,才允许把纯缩进差异当作 perfect match 保留旧行——写期望段时要与这些启发式保持一致;
  • 利用现有四类样本定位差异:修改 import(如fastapi.py)、插入注释(如add-comments.py)、改变缩进风格(如fastapi-tabs-vs-spaces.py)、结构重构替换(如mock-llm.ts),新场景可以先对照最接近的样本起步;
  • 三段的空行处理:由于解析会对每段做“去开头空行 +trimEnd()”,段首多余空行可省略,但段内有意保留的空行必须保留;
  • 每次改动后重新生成:若只是调整了新旧代码,直接清空期望段并重复上述“自动回填”流程即可,避免手写与算法输出偏差导致的无谓返工。

小结

core/diff/test-examples/README.md 用极简篇幅定义了一套高可用的 diff 测试用例格式:三段文本 +---分隔 + 显式期望 diff。配合 streamDiff.vitest.ts 中的解析与回填逻辑、streamDiff.ts 与 util.ts 中的算法实现,以及 core/index.d.ts 中DiffLine/DiffType的类型约定,你可以完整理解并复现 Continue 对“流式编辑 diff”正确性的验证方式。无论你是想给 Continue 提交新的 diff 回归用例,还是在自己的项目中借鉴“黄金用例 + 自动回填”的测试设计,这份规范与样本都提供了可直接照做的模板。

【免费下载链接】continueopen-source coding agent项目地址: https://gitcode.com/GitHub_Trending/co/continue

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 2:16:35

CANN/GE ACL张量格式设置API

aclSetTensorFormat 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、Tensor…

作者头像 李华
网站建设 2026/9/10 2:16:21

常见硬件故障排查:自己动手修电脑

080 常见硬件故障排查:自己动手修电脑 电脑出问题了,你的第一反应是找维修店?别急。很多常见的硬件问题,你自己就能排查和解决,省几百块维修费。 今天我们就来学一些基本的硬件故障排查方法。 排查原则 在动手之前,记住三个原则: 从简单到复杂:先检查最简单的可能…

作者头像 李华
网站建设 2026/9/10 2:15:45

DuckDB 1.5.0深度解读:查询引擎、Arrow集成与性能突破

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 2:15:18

CANN/GE ACL矩阵向量乘法API

aclblasGemvEx 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、TensorFlow …

作者头像 李华
网站建设 2026/9/10 2:14:55

从Apriori到FP-Growth:关联规则挖掘的原理、实践与性能优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 2:14:51

Manus恢复独立运营 创始团队回归:AI Agent产品战略转向解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华