一致性运行器协议
定义一致性语料与任何想要声明一致性的 XTL 实现之间的契约。
什么是运行器
一致性运行器是一个小程序:
- 遍历
conformance/fixtures/中的 fixture - 对每个 fixture 的
template.xlsx+data.xlsx调用被测实现 - 将实现的输出与 fixture 的
expected.xlsx(或expected/目录)比较 - 以标准格式按 fixture 报告 pass / fail / skip
每个实现提供自己的运行器(因为调用方式与语言相关),但所有运行器都产出可比较的输出。
Fixture 加载
运行器通过枚举 conformance/fixtures/ 的子目录来发现 fixture。每个子目录命名为 <NNN>-<slug>/(例如 001-basic-substitution)。
对每个 fixture,运行器读取:
template.xlsx——输入模板data.xlsx——输入源数据expected.xlsx(单输出场景)或expected/目录下的.xlsx文件(多文件分组场景,包括零输出场景)meta.yaml——fixture 元数据
期望零个输出文件的静态 fixture 使用空的 expected/ 目录。
错误 fixture 省略 expected.xlsx 与 expected/。它们在 meta.yaml 中声明 expected_error;预期结果是实现报告的错误消息包含所声明的文本。
动态 fixture 省略 expected.xlsx 与 expected/。它们在 meta.yaml 中声明 expected_dynamic;预期结果由运行器根据运行器启动时间戳与声明的断言规则计算得到。动态 fixture 仅保留给规范中明确具有时间依赖的行为,例如 TODAY()。
必填的 meta.yaml 字段
description: string # one-line human description
spec_section: string # the spec section this fixture exercises
spec_version: string # minimum XTL version (e.g., "0.1")
tags: [string, ...] # filter tags (e.g., [substitution, repeat, aggregate])
tags 是为 --filter=<tag> CLI 标志提供的 fixture 侧便利字段。标签值不属于一致性契约——运行器必须(MUST)将其作为不透明字符串处理,且不应当(SHOULD NOT)因为某个 fixture 的标签集与另一个 fixture 不同而拒绝它。参考语料使用小写、连字符分隔的词元,但不强制规范分类法。
可选字段:
verified_by: [hand | excel-formulas | manual-script | reference-impl]
expected_warnings: [string, ...] # warnings the impl should emit
expected_error: string # expected error message substring; no expected output is required
expected_error_code: string # optional ADR-0015 stable error code (e.g. "xl3/source/undeclared")
expected_dynamic: string # dynamic assertion kind; no expected output is required
comparison_stage: 1 | 2 # minimum comparison stage for static-output fixtures; default is 1
skip_reason: string # if fixture is currently broken
inputs: # host-supplied runtime inputs (ADR-0010)
- name: region
value: Seoul
inputs 块列出运行器作为运行时输入传递给实现的 name/value 对(依据 ADR-0010 的 __inputs__ 工作表)。运行器必须(MUST)将这些值转发到实现的转换入口点。没有 __inputs__ 工作表的模板会忽略该字段。
阶段门控元数据:
comparison_stage仅适用于静态输出 fixture。其默认为1。仅当 fixture 断言阶段 1 无法观察的工作簿内容(如样式、合并、包零件或二进制媒体)时才使用2。expected_errorfixture 与expected_dynamicfixture 不使用工作簿比较阶段判定通过/失败。运行器仍会报告当前运行阶段,但这些 fixture 保留各自的错误或动态断言规则。expected_dynamic要求为当前定义的utc_today断言种类提供dynamic_cells。静态输出与错误 fixture 省略dynamic_cells。
对于 expected_error fixture,运行器必须(MUST)将其标记为:
pass,当实现报告的错误包含expected_errorfail,当实现成功fail,当实现报告了不同的错误
expected_error 与 expected_dynamic 互斥。
动态断言
动态断言让渲染时行为可测,而无需提交一份会过期的 expected.xlsx。运行器必须(MUST)在执行第一个 fixture 之前捕获一个统一的运行器启动时间戳,并将该时间戳用于本次运行中的所有动态 fixture。这可以避免同一份报告中各 fixture 之间出现跨午夜的差异。
XTL 0.1 定义了一种动态断言种类:
expected_dynamic: utc_today
dynamic_cells:
- sheet: Report
cell: A2
format: YYYY-MM-DD
对于 utc_today,每个所列单元格的预期值是运行器启动时间戳的 UTC 日历日期,按所列的 XTL TEXT() 日期格式格式化。实现的输出必须(MUST)在每个所列工作表/单元格坐标处包含该预期字符串值。
对于 expected_dynamic fixture,运行器必须(MUST)将其标记为:
pass,当实现成功且每个所列的动态单元格都匹配fail,当实现报告了错误fail,当任一所列动态单元格 与计算出的预期值不同
未实现某个已声明的 expected_dynamic 种类的运行器,必须(MUST)将该 fixture 标记为 skip 并附带原因。它们禁止(MUST NOT)将其报告为已通过。
比较阶段
一致性协议有两个比较阶段:
- 阶段 1:单元格值比较。 运行器在通过电子表格库加载
.xlsx文件后,比较工作表名称与非辅助性单元格的值。该阶段有意忽略样式、合并、页面设置、嵌入媒体、缓存值之外的公式以及包结构。对于 XTL 0.1 的引导语料而言已经足够,同时规范 OOXML 比较的细节仍在规定与实现中。 - 阶段 2:规范 OOXML 比较。 运行器在对生成的
.xlsx文件的 OOXML 包进行规范化之后再进行比较。这是完整静态输出一致性的目标,因为它可以捕捉到阶段 1 看不到的版式、样式、合并、工作表结构与包回归。
错误 fixture 与动态 fixture 不是工作簿输出比较。无论比较阶段如何,它们都保留各自的 expected_error 与 expected_dynamic 通过/失败规则。
报告应当(SHOULD)标明每次运行所使用的比较阶段。实现禁止(MUST NOT)凭仅运行阶段 1 而声称达到阶段 2 一致性。静态输出 fixture 可以(MAY)在 meta.yaml 中声明 comparison_stage。当 fixture 声明的比较阶段大于运行器的当前阶段时,运行器必须(MUST)跳过该 fixture。