TiXL 手动测试集体系(Manual Test Sets)指南:用 Markdown 组织人机验证流程
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
TiXL 是一款开源的实时动态图形创作软件,其.tests-manual/目录维护着一套完全基于 Markdown 的人工验证测试集(Manual Test Sets)。每份文件定义一个测试集——一组有序的步骤,用来走查一个功能或一条工作流,由人类测试者在 TiXL 编辑器内逐步执行并记录结果。本文以 .tests-manual/README.md 为主干,结合仓库内 70 余个真实测试集与配套的测试运行器规划,系统讲解这套体系的文件布局、frontmatter 字段、步骤书写规范、标签约定与维护流程,帮助贡献者快速上手编写高质量测试集,也帮助读者理解 TiXL 如何在没有自动化 UI 驱动的前提下保障功能回归质量。
一、体系概览:为什么需要"手动测试集"
TiXL 的图形界面操作密集(节点图、时间线、参数窗口等),很多行为难以用纯单元测试覆盖。.tests-manual/采用"人类走查"的方式补位:每个文件定义一套测试集(test set),包含若干步骤(steps),测试者按顺序执行动作并核对预期结果。
从.tests-manual/目录结构看,当前仓库已有大量覆盖各功能域的测试集,例如:
- 基础操作:creating-operators.md(在图中添加运算符)
- 时间线:timeline-editing.md、dopesheet-curve-expand.md
- 撤销重做:undo-redo-graph-edits.md
- 播放控制:playback-transport-controls.md
- 音频路由:audio-graph-routing.md
- 编辑器内部:markdown-renderer.md
- 导出验证:player-export-stripping.md
测试集全部用纯 Markdown 编写,贡献者无需任何工具即可跟读执行;同时 frontmatter 采用结构化字段,为将来编辑器内置的测试运行器(见下文"测试运行器"一节)留好了机器可解析的接口。
二、目录布局与命名规范
按 .tests-manual/README.md 的约定,目录布局如下:
.tests-manual/ ├── README.md (本文档) ├── creating-operators.md (每个测试集一个文件) ├── dopesheet-curve-expand.md └── ...核心规则:
- 一个测试集一个文件,文件即测试集。
- 文件名 = frontmatter 中的
id,使用 kebab-case(小写短横线),例如creating-operators.md的id: creating-operators。 - 测试集保持扁平——暂不设子目录。README 明确提示:若目录规模超过约 30 个测试集,需要重新评估分组策略。当前仓库
.tests-manual/已收录 70+ 个测试集,仍在扁平结构下运作,说明该阈值只是一个前瞻性建议而非硬性限制。
三、文件格式:frontmatter + Markdown 步骤
每个测试集文件由两部分组成:开头的 YAML frontmatter(位于---围栏内)和正文中的步骤序列。以 creating-operators.md 为例:
--- id: creating-operators # kebab-case, unique, matches filename title: Creating Operators # human-readable, shown in runner UI added: 2026-04-19 added-in-version: 4.2 scope: graph-window # broad feature area (free-form tag) tags: [user, smoke, essential] # optional — used by the runner to filter sets prerequisites: # optional — free-text setup requirements - An empty project is open. - The Graph Window is visible. related-help: # optional — relative links into .help/ - ../.help/using/graph-window.md ---正文部分以## Step:开头定义每个步骤,例如:
## Step: Opening the Symbol Browser **Action:** With the Graph Window focused, press `Tab`. **Expected:** - The Symbol Browser opens. - Its search field is focused and empty.字段约定详解
README 对 frontmatter 字段给出了明确约定:
| 字段 | 必填性 | 含义与规则 |
|---|---|---|
id | 必填 | kebab-case、全局唯一,必须与文件名一致 |
title | 必填 | 人类可读标题,未来会显示在运行器 UI 中 |
scope | 推荐 | 宽泛功能域标签(自由文本),如graph-window、timeline、audio、export |
tags | 可选 | 供运行器过滤使用的标签数组,见下文"标签指南" |
added | 新测试集必填 | ISO 日期(YYYY-MM-DD),驱动运行器的 "Recently added"(最近新增)排序;缺少该字段的旧测试集按最旧排序 |
added-in-version | 推荐 | 该测试集首次随 TiXL 发布的major.minor版本,如4.2、4.3 |
prerequisites | 可选 | 自由文本的前置条件列表 |
related-help | 可选 | 指向.help/内文档的相对链接,供测试者深入阅读 |
README 特别说明:added与added-in-version是从 git 历史回溯补写到现有测试集上的("Both were backfilled from git history for existing sets"),因此仓库中既能看到带这两个字段的新测试集,也能看到只有added或两个都没有的早期测试集。
步骤块约定
每个步骤以## Step: <简短的祈使句标题>开头,正文由三个可选/必选块组成:
**Action:**(必写)——给测试者的操作指令。以散文形式书写,像引导新用户那样描述,例如:"With the search results visible, use the cursor up/down keys to highlight[RadialGradient]."。仅在步骤真正是并行选项时才用项目符号(例如"要么点击这里,要么按 Enter")。README 明确反对"每个键一个 bullet"的写法——那读起来像检查清单而不是导览。**Expected:**(必写)——现在时、只写可观察结果。允许用项目符号(因为它们是独立检查项)。禁用 "should probably"(大概应该)这类模糊措辞;如果结果本身就模糊,就把步骤拆开。**Context:**(可选,历史遗留)——一句话交代测试者当前所处位置。旧测试集使用此字段;新测试集应将上下文并入**Action:**的第一句话。运行器为了向后兼容仍会解析它,并在展示时将其合并进 Action 正文。
从真实测试集可以直观看到这三块的组合方式。例如 timeline-editing.md 的"Step: Ripple selection and gap editing":
## Step: Ripple selection and gap editing **Action:** Cut a clip at the playhead (`Ctrl+X`), then press `Ctrl+Shift+A` (or right-click → "Select Following Clips"). **Expected:** - Every clip starting at or after the playhead is selected on all layers — including the right half of the cut you just made. - Dragging the selection right opens a gap at the cut; dragging left closes one.注意其中Ctrl+X、Ctrl+Shift+A这类快捷键直接以反引号内联代码呈现,界面元素(如菜单项 "Select Following Clips")以普通文本或引号标识——这是整套体系"为从未用过 TiXL 的人书写"原则的体现(详见"编写建议"一节)。
关于[ui:...]与[OperatorName]引用语法
实际测试集中出现了两类特殊记号,README 虽未逐字展开,但可以从用例归纳:
[ui:Graph|Graph Window]、[ui:SymbolBrowser|Symbol Browser]、[ui:DopeSheet|dope sheet]、[ui:CurveEditor|curve editor]、[ui:Timeline]等——指向界面元素的引用,显示文本为竖线后的部分。这为将来运行器在步骤卡片中高亮/深链 UI 概念预留了语义。[RadialGradient]、[AudioBus]、[AudioReverb]、[PlayAudioSample]、[Blob]等——运算符(operator)引用。markdown-renderer.md 中的"Clicking an operator reference"步骤证实,点击这类引用会触发回调(在预览场景下向 Console 写入[MarkdownPreview] op ref clicked: RadialGradient),说明该记号确实承载了交互语义。
步骤结果(Step outcomes)
测试者可以给每个步骤记录运行结果:pass/fail/other(other需附带自由文本备注)。README 强调:结果不属于测试定义文件,而是属于某一次"运行(run)"——因此不会把结果写回.tests-manual/*.md中。这与下方"测试运行器"一节的StepResult数据模型完全吻合。
四、标签指南:过滤与分类的标准化词汇
tags字段虽为自由文本,但 README 要求统一使用一套精简的核心词汇,以便运行器提供合理的过滤选项:
| 标签 | 含义 |
|---|---|
smoke | 60 秒内可完成,每次构建后都应运行 |
essential | 某功能的主要快乐路径(happy path) |
edge | 边界情况、回归网络 |
perf | 对性能敏感的观察步骤 |
flaky | 已知偶发不稳定,修复前一直保留该标签 |
真实用例中,creating-operators.md 标注为[user, smoke, essential],undo-redo-graph-edits.md 标注为[user, edge, essential],markdown-renderer.md 标注为[dev, smoke],player-export-stripping.md 标注为[player, export](该测试集还用了player、export这类领域词,说明标签词汇可以在核心词汇之上按需扩展)。
受众:每个测试集恰好带一个
每个测试集还要标注由谁运行,让运行器可以呈现两个干净的分类列表:
user—— 艺术家验证他们在 TiXL 中真正会做的操作:加载工程、设置音频源、录制、导出。用通俗语言书写——以用户屏幕上看到的东西来命名,而不是背后的文件、格式或类。dev—— 贡献者验证编辑器内部机制或编写/构建工作流:创建运算符、图编辑的撤销/重做、构建失败消息、Markdown 渲染器等。这些测试集保留技术细节,因为目标读者需要它们。
判定规则很直接:以"预期由谁来跑"为准——如果一个非编程背景的艺术家能照着做完整套,就归为user。
五、维护流程:何时新增或更新测试集
README 明确指出,这套规则是项目CLAUDE.md中.help/规则的镜像:
- 任何改变用户可见 UI 或行为的 PR,都必须在同一个 PR 中扩展现有测试集或新增一个测试集。
- 功能计划文档(位于 .agentic/Plans/)链接到对应的测试集,而不是重复抄写步骤。例如 Plan_ManualTestRunner.md 直接声明"测试内容的唯一真源是
.tests-manual/",运行器只是这些 Markdown 文件之上的一个薄 UI。 - 过时测试随其覆盖的功能一起删除("Stale tests are removed with the feature they covered")。
六、作者建议(Authoring tips)
README 结尾给出五条直接可用的编写准则:
- 写给从未用过 TiXL 的人——明确写出菜单、按钮、窗口的名字。
- 每个步骤只观察一个变化。如果测试者需要检查两件不相关的事,就拆成两步。
- 避免绝对坐标(如 "click at 200,400"),改用名字(
Graph Window、Parameter Window、[RadialGradient])。 - 能键盘触发就优先键盘触发,鼠标拖拽其次——键盘指令更容易无歧义地描述。
- 如果某步骤依赖前置状态,在
**Context:**里写明——因为一旦运行器支持部分运行,步骤不一定总是自上而下执行。
这些原则在 dopesheet-curve-expand.md 中体现得淋漓尽致:该测试集用 20 个步骤细粒度地验证"Dope 表逐参数曲线展开"的交互,包括 hover 高亮、组件开关(.x .y .z)、拖拽锁轴(U-only / V-only)、切线撤销(Ctrl+Z/Ctrl+Shift+Z)、选区联动等,每一步都只核对一个可观察行为。
七、配套设施:编辑器内手动测试运行器(规划)
虽然运行器尚未以源码形式落地,但 .agentic/Plans/archive/Plan_ManualTestRunner.md 给出了完整的实现蓝图,能帮助我们理解 frontmatter 为何被设计得如此结构化。该计划(日期 2026-04-19,Phase 1 已于 2026-05-01 落地:解析器 + Pick + Run + 内存内 Summary)定义:
- 三个 UI 状态:Pick(勾选测试集、标签过滤、Start Run)、Run(单步骤卡片:标题 + 步骤索引、Context/Action/Expected、Actual Result 备注框、Success/Fail/Other 按钮、
←→导航与Esc放弃)、Summary(按测试集统计N pass / N fail / N other / N skipped,以及导出按钮)。 - 数据模型:
TestSet、TestStep、Outcome { Pending, Pass, Fail, Other, Skipped }、StepResult、RunReport(含EditorVersion、OsVersion),全部仅存于内存,不序列化进工程。 - 解析规则:YAML frontmatter 位于首尾
---围栏之间;正文按^## Step:标题切分;每个步骤正文扫描**Context:**、**Action:**、**Expected:**块,Action/Expected 的 bullet 是标题后紧跟着的-列表项。解析策略宽容:允许缺少 Context,格式错误的步骤只在 Pick UI 发出解析警告而不会让运行器崩溃。 - 导出负载:JSON(完整运行负载到剪贴板)、Markdown(人类可读汇总,便于粘贴到 Discord/Slack)、GitHub Issue URL(预填标题与 urlencoded 正文,无需鉴权)。
其中 JSON 导出示例与 README 的"结果属于 run 而非 test definition"设计一脉相承:
{ "startedUtc": "2026-04-19T12:34:56Z", "finishedUtc": "2026-04-19T12:40:12Z", "editorVersion": "4.x", "os": "Windows 11 10.0.26200", "results": [ { "setId": "creating-operators", "stepIndex": 0, "outcome": "pass", "comment": null, "timestampUtc": "..." }, { "setId": "creating-operators", "stepIndex": 1, "outcome": "fail", "comment": "Symbol browser didn't focus search field", "timestampUtc": "..." } ] }计划的 v1 非目标同样值得注意:不做自动化 UI 驱动(运行器不会替用户按键/点击)、不做 GitHub API 鉴权发帖、不在编辑器内持久化历史运行、步骤不可局部重排(一次运行线性走完所选测试集)。这些边界解释了为什么 Markdown 测试集在可预见的未来仍是"人读人写"的格式。
八、从源码印证:测试集如何挂钩导出流程
以 player-export-stripping.md 为例,可以直观看到测试集期望与编辑器源码日志的对应关系。该测试集要求 Console 中出现三行关键日志:
<project>: stripped 1 unused child operatorsExport copied X files (… MB), skipped Y files (… MB)Skipped optional dependencies: … OpenCvSharpExtern.dll …
在 PlayerExporter.cs 中可以找到对应实现(约 182–187 行):
Log.Info($"Export copied {report.CopiedCount} files ({FormatBytes(report.CopiedBytes)}), " + $"skipped {report.SkippedCount} files ({FormatBytes(report.SkippedBytes)})."); if (dependencyFilter.ExcludedPatterns.Count > 0) { Log.Debug("Skipped optional dependencies: " + string.Join(", ", dependencyFilter.ExcludedPatterns)); }同时在约 295–300 行可以看到按符号剥离未使用子运算符的日志([symbol.Name]: stripped {removedCount} unused child operators与汇总行{package.Name}: stripped {removedChildren} unused child operators),并且 208 行注释明确 "Stripped exports rewrite the symbol files instead of copying them"——这正对应测试集中"检查导出目录下StripTest.t3的 children 列表是否只剩被引用运算符"的验证点。
这类"测试集期望 ↔ 源码日志/行为"的对应关系贯穿整套体系:测试集不是凭空写的验收清单,而是对编辑器真实输出的可执行描述。贡献者在编写新测试集时,可以先定位相关功能的日志与行为源码(如 Editor/UiModel/Exporting/ 目录下的导出实现),把可观察结果如实写进**Expected:**。
九、最佳实践速查与常见误区
综合 README、运行器规划与真实测试集,可提炼出以下实践清单:
写之前:
- 确定受众:
user还是dev?由"谁预期会跑它"决定。 - 检查是否已有覆盖该功能的测试集;功能计划(
.agentic/Plans/)只链接不抄写。 - 为
prerequisites想清楚最少的起步状态(如"An empty project is open")。
写步骤时:
- 标题用祈使句、以"被验证的事物"命名("Opening the Symbol Browser"),因为运行器会把步骤索引拼接成副标题("Step 3/12 — Creating an operator")。
**Action:**用散文、像带新用户一样;**Expected:**用现在时、可观察结果、允许 bullet。- 一次只验证一个变化,模糊就拆步;能键盘就键盘;用名字不用坐标。
- 依赖前置状态时在
**Context:**(或 Action 首句)写清楚。
收尾时:
- 确认
id与文件名一致、kebab-case、唯一。 - 新测试集补上
added(ISO 日期)与added-in-version(major.minor)。 - 按需挂
smoke/essential/edge/perf/flaky标签,且恰好一个受众标签。
常见误区(README 明示):
- 把
**Action:**写成逐按键清单("像检查清单而不是导览")。 - 在
**Expected:**里写 "should probably" 之类的模糊预期。 - 把结果写回测试文件(结果属于 run,不属于 test definition)。
- 在 PR 中改 UI 却不带测试集(违反强制流程)。
结语
TiXL 的.tests-manual/是一套"以人为执行器、以 Markdown 为存储、以 frontmatter 为接口"的轻量回归体系:目录扁平、格式宽容、面向艺术家与开发者双受众,同时为编辑器内运行器预留了完整的数据与解析契约。无论你是要为新功能补一个测试集,还是想理解 TiXL 如何保障图形编辑器这种重交互软件的稳定性,从 .tests-manual/README.md 出发、对照 creating-operators.md 与 timeline-editing.md 等真实用例,都是最直接的路径。
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考