Skip to content

Latest commit

 

History

History
357 lines (277 loc) · 34.9 KB

test.md

File metadata and controls

357 lines (277 loc) · 34.9 KB

SPEC-017:mcpp 测试规范

项 值
规范编号 SPEC-017
标题 mcpp 测试规范:测试程序、测试协议、记录、存储、启动描述与框架声明;基础能力与可选能力
状态 评审中 v2.2
版本 2.2
最后修改 2026-11-14
对应实现 逐条标注。B1–B4 与 O1、O2、O4–O7 mcpp >= 2026.11.8.1 至 2026.11.10.1(逐条);B5、B6、O3 的运行器一侧、O8 mcpp >= 2026.11.10.1;O9 由框架的工具实现(mtest >= 0.6.0);O10 mcpp >= 2026.11.13.1
取代 SPEC-017 v1.0(test-runner.md);mtest 测试协议版本 1(mtest spec/protocol.md)的规范正文;SPEC-004 中 [test] 的键
相关规范 SPEC-004(清单的平面与命名)、SPEC-006(构建数据库)
相关 issue mcpp#914、mcpp#915、mcpp#917
使用文档 08 —— 测试、50 —— 机器可读输出 §8

用语按 RFC 2119:必须 / 禁止为强制,应当为建议,可以为可选。条款编号有三类:B1–B6 为基础能力,O1–O10 为可选能力,P1–P12 为协议条款(编号与 mtest 协议版本 1 相同,原有引用不变)。

0. 范围与分层

本规范规定测试的三个参与方之间的全部契约。

层 职责 依赖 禁止
客户端(编辑器插件、CI 步骤、代理) 呈现测试树、状态、检查点与输出;发起运行与取消 只依赖本规范:基础能力,以及框架声明的可选能力 依赖某个框架或其工具的名字与私有行为
mcpp(运行器) 拥有本规范;实现 B1–B6 与 O1、O2、O8;汇总框架声明的能力 — 实现会话等属于框架工具的可选能力;引擎中出现框架名或框架特有分支
框架及其工具(mtest、GoogleTest 适配器、脚本适配器、C ABI 写出器) 实现协议的程序一侧;可以附带工具实现可选能力(例如 O9) 本规范 要求运行器或客户端认识自己

基础能力是每个运行器与框架都实现的部分,客户端只凭它就能完整工作。可选能力由实现方在 B6 中声明;客户端只在声明存在时使用它,并在缺失时采用该能力条款规定的回退方式。

1. 原则

  • 规范是契约,框架是实现。 任何实现协议的程序都由同一套规则服务,与其语言或框架无关。
  • 未实现协议的程序是一个用例。 它以退出状态判定(P1.1),并且不得收到协议的变量(P1.2)。
  • 程序的退出状态仍决定其结论。 报告中失败的用例使程序失败,即使它以 0 退出;有 begin 而无 end 的用例失败(P5.4)。
  • 只增不改。 记录、事件与存储的字段只增加;新含义由 feature 宣告(docs/50 §4),已发布名字的含义不变(P9.2)。

2. 基础能力

编号 能力 实现方
B1 测试程序的发现与 [test] 的键 mcpp
B2 协议核心:层级、握手、列出、选择、报告、版本(P1–P5、P8、P9) 程序与运行器
B3 记录:程序记录与列出记录 mcpp
B4 运行存储(P12) 运行器
B5 构建与启动描述 mcpp
B6 框架声明 框架包;mcpp 汇总

B1. 发现与 [test] 的键 已实现(mcpp >= 2026.10.26.2,逐键标注见 docs/04 §2.17)

  1. 一个包的测试程序由 [test] discover 的 glob 决定,缺省 ["tests/**/*.cpp"];以 ! 开头的 glob 移除其匹配的文件;[] 不发现任何程序。
  2. [test] programs:"file"(缺省)时每个文件是一个程序;"directory" 时 glob 固定目录之下同一目录的文件是一个程序,以目录命名;"package" 时包所发现的全部文件是一个程序,以首个 glob 的固定目录命名(mcpp >= 2026.11.13.1)。一个程序中定义 main 的文件不得多于一个,否则被拒绝并写出两者。"directory" 留下直接位于固定目录中的文件时必须说明其数目;不形成任何组时为降级,在 --strict 下为错误(mcpp >= 2026.11.13.1)。
  3. 程序名是其路径相对于首个匹配 glob 的固定目录、去掉扩展名的部分。同名的两个文件被拒绝,并写出两者。
  4. [test] main = { from = "<依赖>/<入口>" } 为每个程序提供起点;[test] start 为每个测试镜像陈述入口符号;windows_code_page、windows_long_paths 与同名目标键含义相同。
  5. [test] split 与 [test] store 分别属于 O1 与 B4;[test] link 规定测试链接其所达("used",缺省)或全部目标文件("objects")。
  6. [test] 中的其他键产生警告,在 --strict 下为错误。

B2. 协议核心

P1. 层级

层级 程序实现的部分 运行器得到的结果
L0 不实现本协议 每个程序一个结果,来自其退出状态
L1 P3、P4、P5 用例列表、用例的选择、每个用例的结果、位置与消息
L2 L1 与 P6(O4) 指标:样本、单位、基准结论、一致性条款
L3 L2 与 P7(O5) 每个用例的资源与工件:调度与按影响选择
  • P1.1 不实现协议的程序是 L0 程序。运行器必须原样运行它,并必须只以退出状态判定:0 为通过,其余为失败。 运行器的配置可以为这类程序陈述一个表示"在本机不适用"的退出状态(mtest 的 skip_exit,mtest >= 0.6.1):程序以它 退出时,其用例为跳过,原因取其输出中最后一行以 SKIP 开头的文字。跳过不得计入通过。
  • P1.2 运行器禁止把协议变量(P2)交给不知其实现协议的程序,除非它接受一个 L0 程序完整运行一次(P2.4)。

P2. 握手

  • P2.1 运行器在程序环境中陈述协议。除 MTEST_PROTOCOL 外,所有变量都是可选的。
变量 值
MTEST_PROTOCOL 运行器使用的协议版本:1
MTEST_MODE list 或 run(缺省 run)
MTEST_REPORT 程序追加事件的文件(P5);缺省时不写事件
MTEST_SELECT 用例标识的文件,每行一个(P4);缺省时为全部用例
MTEST_ISOLATION process(本进程中多个用例,缺省)或 case(运行器为每个用例启动一次程序)
MTEST_TMPDIR 程序可为其用例使用的目录,由运行器删除
MTEST_SEED 用例中随机性的种子,十进制
MTEST_MONITOR、MTEST_MONITOR_SAMPLE 资源用量的测量程度与采样间隔(O7,P10)
MTEST_FIXTURE_<NAME>、MTEST_SERVE 运行夹具的端点与所服务的夹具(O6,P11)
  • P2.2 实现协议的程序禁止把这些变量传给它启动的程序,从而测试启动的测试程序不会被当作运行器的程序。作为运行器启动测试程序的测试自行设定其所需的变量。

  • P2.3 程序以两种方式陈述其实现协议:

    • 宿主声明:构建系统的清单为程序指明协议(mcpp:测试入口所在包的入口目标陈述 test-protocol = 1,B6);
    • 标记:程序文件中出现字节 mtest-protocol=1,其后的字节不是数字。运行器可以读取文件寻找它。

    两种陈述都是由程序的报告确认的声明:不写 program 事件的程序是 L0(P2.4)。

  • P2.4 两者都不知道的运行器仍可以设定 MTEST_MODE=list 与 MTEST_REPORT;首个事件给出层级。没有 program 事件时程序是 L0,且在 list 模式下已完整运行一次。

  • P2.5 框架可以陈述其编写测试的指南(B6 的 test-guide),program 事件可以带 guide(一个 URL)。运行器可以在指出失败时一并指出指南。

  • mcpp 的实现(已实现,mcpp >= 2026.11.8.1)。 程序实现协议的条件是其入口陈述 test-protocol = 1,或其文件或其链接自的目标文件携带标记。mcpp 总是给出 MTEST_PROTOCOL、MTEST_MODE、MTEST_REPORT、MTEST_TMPDIR,适用时给出 MTEST_SELECT、MTEST_SEED;对其他程序,这些变量必须从其环境中移除。每次调用的协议文件位于该调用自己的目录 test-protocol/<pid>-<启动时刻>/<测试>/,调用结束时删除(mcpp >= 2026.11.10.1)。

P3. 列出

  • P3.1 在 MTEST_MODE=list 下,程序必须先写一个 program 事件,再为将要运行的每个用例写一个 case 事件,最后写一个 done 事件,并以状态 0 退出,不运行任何用例。
  • P3.2 用例标识是 UTF-8 字符串,且稳定:同一用例在每次运行、每个系统上标识相同。程序以 / 连接其套件与用例的名字构成它(例如 fs/a file is read)。同一程序中的标识互不相同。
  • P3.3 case 事件可以给出用例的位置(file、line)、种类(P8)、标签、所覆盖的条款、预期状态,以及调度提示(O5)。未给出种类的用例属于运行器赋予其程序的种类。
  • P3.3 的位置与能力(mtest >= 0.8.0)。 case 事件可以给出用例运行的位置(only、except:平台与架构的名字)、所需能力及程序对其的回答(capabilities:名字到布尔值),以及程序将其列为跳过时的 reason(与预期状态 skip 同在)。位置之外或缺少能力的用例由程序自身跳过并陈述原因,与启动它的运行器无关;运行器可以不启动列为跳过的用例。
  • mcpp 的实现(已实现,mcpp >= 2026.11.13.1)。 mcpp 不解释这些字段:用例由程序跳过,mcpp test 报告其状态与原因(skip <标识> -- only on windows (this host: linux x86_64))。mcpp 自身的端到端用例以这些属性陈述位置与能力(tests/support)。
  • P3.4 只在另一用例运行时才存在的用例(在用例内声明的用例)可以不被列出;它在运行时出现在报告中,其 begin 事件可以给出种类、标签与条款。选中它即以声明它的用例为作用域运行;后者只在其自身语句失败时被报告。

P4. 选择

  • P4.1 有 MTEST_SELECT 时,程序必须恰好运行标识为该文件某一行的用例,比较前去掉一个行尾 \r,逐字节比较。不指向任何用例的标识以 missing 事件报告,并使运行失败。
  • P4.2 模式属于运行器:提供 fs/* 的运行器按列表(P3)解析它,并传递标识。协议没有模式语法,因而也没有转义。
  • P4.3 排除与按声明选择(mtest >= 0.9.0)。 程序可以在 program 事件中以 selection 列出它理解的选择行:except 时,以 ! 开头的一行把其后标识的用例及其声明的用例排除出选择,只有排除行的选择运行除这些之外的全部用例;optional 时,以 ? 开头的一行选中其后标识的用例,该用例未被声明时不报告 missing。运行器禁止向未列出相应值的程序写这两种行。
  • mcpp 的实现(已实现,mcpp >= 2026.11.8.1)。 mcpp test --cases 以 list 模式运行每个实现协议的程序;--case <模式> 把每个程序中匹配的用例写成 MTEST_SELECT,列出用例之下的名字选中该用例(P3.4)。

P5. 报告

  • P5.1 事件是 JSON 对象,每行一个(NDJSON,UTF-8,每个之后 \n)。程序把它们追加到 MTEST_REPORT,并禁止向该文件写入其他内容。每行整行写出:多线程程序禁止交错写行。
  • P5.2 首个事件为 program。每个事件有字段 e,即其种类。
e 字段 时机
program protocol(1)、level(1–3)、framework、version、isolation,可选 guide 首个
case id、name、file、line、kind、tags、clauses、expect、resources、artifacts,可选 uses list 模式下每个用例一个
component name 与 pools,或 fixture 与 scope(O6) list 模式下每个组件与运行夹具一个
begin id、t(自程序启动起的毫秒),可选 kind、tags、clauses 用例开始
failure id、file、line、message,可选 expected、actual(字符串)、source、fatal(O3) 一个期望不成立
output id、stream(out 或 err)、text 用例捕获的输出
metric id、name、value、unit,可选 samples、better、precision(O4) L2
usage user_ms、sys_ms,可选 peak_bytes、read_bytes、written_bytes(O7) 程序结束时
artifact id、path、kind 用例产生的文件
fixture id、name、ms 用例促成为共享它的用例而构造的对象,及构造耗时
end id、status、ms,可选 reason、checks(O3) 用例结束
missing id P4.1
done status、counts(状态到数目的对象) 末个
  • P5.3 用例的 status 为以下之一:pass、fail、skip(带 reason)、xfail(如预期地失败)、xpass(预期失败却通过;运行失败)、invalid(无法判定的测量;P6.3)。
  • P5.4 运行器必须忽略未知字段与未知事件种类。运行器必须接受没有 done 的报告(程序崩溃):有 begin 而无 end 的用例失败,以程序的退出状态为原因。
  • P5.5 程序的退出状态为零,当且仅当没有用例失败或意外通过,且没有被选用例缺失。

P8. 种类

unit、e2e、cts、bench。种类陈述被测试的是什么;用例所属的集合(冒烟、回归)属于运行器与工程,不属于程序。

P9. 版本

  • P9.1 使用版本 N 的运行器设定 MTEST_PROTOCOL=N。只实现较低版本的程序在其 program 事件中答以其实现的最高版本;运行器随后使用该版本,或把程序当作 L0。
  • P9.2 同一版本内,字段与事件种类只增加。字段只在新版本中改变含义。

B3. 记录 已实现(mcpp >= 2026.11.8.1)

记录的完整字段在 docs/50 §8;本条规定其必需部分与形状。

  1. 程序记录。 mcpp test --message-format json 为每个程序(O1 时为每个部分)写一条:
    • 状态、退出码、用时、protocol;
    • cases:每个用例的 id、status、duration_ms、reason、failures(每项 file、line、message,陈述时 expected、actual);
    • missing;
    • program 与 sources:程序文件及其编译自的源文件,供按变更选择的客户端读取。
  2. 列出记录。 --cases 的记录带 listed:每个用例的 id、file、line,陈述时的 kind、tags。
  3. 一个用例结果在 mcpp 中只有一种形状:cases 中的元素、O2 的 end 记录与 B4 的一行有相同的字段。

B4. 运行存储(P12)已实现(mcpp >= 2026.11.9.1)

  • P12.1 运行器可以把运行保存在存储中,其他运行器与工具以此形式读写:
路径 内容
index.ndjson 每次运行一行:去掉程序与用例的运行记录
runs/<id>/run.json 运行记录:schema(mtest.run/1)、id、started、finished、status、system、arch、machine、commit、set、selection、group、monitor、ms、jobs、counts
runs/<id>/programs.ndjson 每个程序进程一行:name、argv、status、exit、ms、started_ms、worker、select、resources、fixtures,运行器构建它时的 build_started_ms 与 build_ms,usage(带 source:program 或 engine)
runs/<id>/cases.ndjson 每个用例一行:program、id、kind、status、ms、started_ms、reason、failures、metrics、bounds、tags、clauses
runs/<id>/fixtures.ndjson 每个运行夹具一行:name、program、started_ms、ready_ms、ended_ms、status
  • P12.2 运行标识按时间排序。同一 group 的运行(一次 CI 运行的各作业)是一个整体的各部分,读者合并报告。
  • P12.3 一次运行中程序、用例、构建与夹具的时间构成其工作的有向无环图;读者的关键路径是按实测时间加权的最长路径。
  • mcpp 的实现。 存储位于 [test] store(缺省 target/mtest;"none" 不保存)。run.json 的 commit 与 group 在 CI 中取 GITHUB_SHA 与 GITHUB_RUN_ID;分片运行(O10)另写 shard(I/N)。程序进程一行在其为一部分时写 part_of(mcpp >= 2026.11.13.1)。mcpp 在一次构建中构建全部测试程序,不写逐个程序的 build_started_ms。

B5. 构建与启动描述 已实现(mcpp >= 2026.11.10.1)

  1. mcpp test --no-run --message-format json 判断所选测试是否最新,只在需要时构建,并为每个程序写出状态为 built 的程序记录。记录必须带 launch:
    • argv:经由执行宿主或运行器的完整参数向量,含 -- 之后的参数;
    • env:mcpp 交给程序的环境(运行时库目录、运行时环境的变量、MCPP_BIN_<NAME>、工具目录);
    • cwd:程序的工作目录;
    • via:run_under、runner 或空。
  2. 协议的变量(P2.1)与所选用例属于启动者自己,launch 中不含。
  3. 构建状态与构建目录的锁只在 mcpp 一处。启动者(包括 O9 的会话)禁止自行构建测试程序;它请求 B5,并按 launch 启动。
  4. 构建目录的锁只在构建期间被持有,在第一个测试运行之前释放;同一配置的构建或另一次测试必须能与正在运行的测试并行进行(mcpp#915)。
  5. 未改动的工程。 完整路径记录其为包的每个测试所做的计划(test-plan.json,位于 test.ninja 旁)。此后,若构建快路径的检查、包所发现的测试与 ninja 的空运行都确认无事可做,mcpp 按记录给出所选测试,不再规划。该记录是 mcpp 的内部格式,对外只经由本条。

B6. 框架声明 已实现(mcpp >= 2026.11.8.1;test-capabilities、test-session mcpp >= 2026.11.10.1)

  1. 框架在其包的入口目标(kind = "entry")上陈述自己:
键 值
test-protocol 以此为起点的测试程序实现的协议版本(P2.3)
test-main true:作为不定义 main 的测试文件的起点
test-guide 包中的一个文件:编写测试的指南(P2.5)
test-capabilities 框架实现的可选能力名(§3 的"名字"列)
test-session 启动框架会话的命令(O9)
  1. mcpp test --guide(及其 --message-format json 形式的框架记录)必须为图中每个框架写出 package、version、entry、protocol、guide、repo、capabilities 与 session。
  2. 客户端以框架记录与 mcpp 的 feature(§4)判断哪些可选能力可用;未声明的能力视为不可用。

3. 可选能力

编号 名字 能力 实现方 缺失时的回退
O1 parts 一个程序以多个进程运行 mcpp 程序作为一个整体显示与运行
O2 case-stream 用例开始与结束时的实时记录 mcpp 程序结束时一次更新
O3 check-details 检查点的期望、实际、源语句、致命性与计数 框架写出,mcpp 转交 只有位置与消息
O4 metrics 指标、界限、精度(P6) 框架 无指标
O5 scheduling 调度提示:时限、隔离、重试、诊断运行、资源与工件(P3.3、P7) 框架陈述,运行器执行 按程序整体运行
O6 fixtures 组件与运行夹具(P11) 框架与运行器 每个程序自建其所需
O7 usage 资源用量(P10) 框架;运行器补测 无用量
O8 output 每个用例的输出归属 框架写出 output 事件;mcpp 归属与转交 程序的输出整体显示
O9 session 测试会话:运行、加入、取消、状态 框架的工具 客户端以多个并发的 mcpp test 进程运行
O10 measured-schedule 按存储测得的用时排序、划分与分片,重跑失败部分 mcpp 按列出顺序运行,不分片

O1. 各部分 已实现(mcpp >= 2026.11.10.1)

  • [test] split:"file"(缺省)时用例分布在多个文件中的程序每个文件一个进程;"case" 时每个用例一个进程;"none" 时一个进程;"auto"(mcpp >= 2026.11.13.1)按文件划分,并按 O10 把测得长于份额的部分划分为用例组。--split 为一次调用给出该值。
  • 每个进程是程序的一个部分,名为 <程序>/<文件主名>("case" 时为 <程序>/<用例>)。
  • --cases 的记录必须写出 "file" 划分的各部分(parts:name、file);每个部分的运行记录必须写出 part_of。

O2. 用例流 已实现(mcpp >= 2026.11.10.1)

  • 报告是程序逐事件追加的 NDJSON。--message-format json 下,mcpp 必须在程序运行期间读取它,只读完整的行,并在每个用例开始与结束时各写一条 {"case": {...}} 记录,先于其程序的记录:member、test、id、event;begin 带 t_ms;end 带 cases 中一个用例的全部字段。
  • 程序结束后读取其余行,再按 P5.4 收尾。

O3. 检查点细节(框架 mtest >= 0.6.0;运行器 mcpp >= 2026.11.10.1)

  • failure 事件可以带:
    • expected、actual:两侧的值,字符串;
    • source:期望所在的源语句,一行;
    • fatal:true 时该失败结束了用例,其余语句未运行。
  • end 事件可以带 checks:{held, failed},该用例运行的期望中成立与不成立的数目。
  • mcpp 必须把这些字段原样转交到程序记录、O2 的 end 记录与 B4 的 cases.ndjson。

O4. 指标(P6)

  • P6.1 指标属于一个用例,名字在用例内唯一,有值与单位(s、ms、B,比值或计数为 1)。
  • P6.2 基准用例报告其样本。测量是否有效、是否通过,由运行器按工程声明的策略决定,不由程序决定。
  • P6.3 程序可以在知道测量无法判定时报告 invalid。运行器禁止把 invalid 计为通过或失败。
  • P6.4 一致性用例在 clauses 中写出其覆盖的规范条款;运行器由此形成条款 × 实现 × 系统的矩阵。
  • P6.5 用例可以为其指标设界限:bounds,{metric, max, min} 的列表。程序在用例结束时把它们作为期望判定:超过 max、低于 min 或未报告受限的指标,均为该用例的 failure。
  • P6.6 资源用量以标准名的指标报告,各带 precision:exact、process、approx、sampled。标准名为 cpu.user、cpu.sys(ms),mem.peak、mem.delta、mem.rss(B),cpu.load(1),io.read、io.write、net.sent、net.recv(B),alloc.count、alloc.bytes,proc.spawned(1)。
  • mcpp 的实现(mcpp >= 2026.11.10.1)。 metric 事件属于其 id 所指的用例;case 与 begin 事件给出的 kind、tags、bounds 属于该用例;三者进入程序记录与 B4。

O5. 调度提示(P3.3、P7)

  • 用例可以陈述 timeout(秒)、isolation(case 时独占一个进程)、retries(失败后单独重跑的次数)、diagnose(失败后运行一次的变量与时限,其输出被保留,不判定结论)。
  • P7.1 resources 写出用例独占或限量所需之物:serial:<组>、machine、gpu、network、display。运行器禁止同时运行持有同一 serial: 组的两个用例,或 machine 用例与任何其他用例。
  • P7.2 artifacts 写出用例所读之物:target:<名>、file:<路径>、program:<名>;运行器据此按变更选择用例。
  • P7.3 资源可以有容量:组件带来的池陈述同时持有它的用例数;运行器禁止超过容量。
  • 为每个用例启动进程的运行器在用例的 timeout 时结束该进程;整体运行程序的运行器(mcpp test)运行其中每个用例,按报告判定。

O6. 组件与运行夹具(P11)

  • P11.1 组件是若干程序共享的测试部分:它带来的资源池与它服务的运行夹具,各以 component 事件列出。
  • P11.2 运行夹具是整次运行的一个对象。用例在 uses 中写出它使用的夹具;运行器在第一个使用者开始前服务它,在最后一个结束后结束它,并以 MTEST_FIXTURE_<NAME> 把端点交给使用者的程序。
  • P11.3 运行器以 MTEST_SERVE 启动列出该夹具的程序来服务它,输入为管道,不带 MTEST_PROTOCOL。程序不运行用例:准备夹具,向输出写一行 mtest-ready <端点>,在输入结束时结束夹具。
  • P11.4 未就绪的运行夹具是机器的失败:使用它的用例以状态 infra 结束,不运行。

O7. 资源用量(P10)

  • P10.1 运行器在 MTEST_MONITOR 中陈述程度:off、basic(每个用例的处理器时间,及程序结束时的自身用量)、detailed(另加最大驻留集及其增长、读写字节)、trace(另加采样的负载与驻留集)。用例可以要求更高的程度。
  • P10.2 程度不为 off 的程序在 done 之前写一个 usage 事件。
  • P10.3 在 trace 下,程序每 MTEST_MONITOR_SAMPLE 毫秒(缺省 50)采样一次,每个序列报告为一个指标,samples 为序列,value 为其最大样本,精度为 sampled。
  • P10.4 运行器可以从其自己的进程测量不写 usage 的程序,并把结果记作引擎的。
  • mcpp 的实现(mcpp >= 2026.11.10.1)。 usage 属于程序,进入程序记录,并以 source = "program" 进入 B4。

O8. 输出归属(mcpp >= 2026.11.10.1)

  • 用例的输出按以下顺序确定:
    1. 程序为该用例写出的 output 事件(P5.2),按到达顺序连接;
    2. 程序只运行了这一个用例时,程序写到标准输出与标准错误的全部内容;
    3. 其余情况下,用例没有自己的输出,程序的输出整体属于程序记录的 run_output。
  • 运行器禁止按到达时间把一个程序的输出分给其中的多个用例:程序缓冲其输出流,到达的时刻不反映写出的时刻(实测:管道上全缓冲的输出在程序结束时一次到达)。需要逐用例输出的框架写出 output 事件。
  • 用例的输出出现在程序记录的 cases 元素与 B4 之外的记录中,为字符串 output;由 output 事件给出时也出现在 O2 的 end 记录中。
  • 本条不改变协议。

O9. 测试会话(框架的工具;mtest >= 0.6.0 的 mtest serve)

  1. 发现。 客户端从 B6 的框架记录取得 session 命令并启动它。会话以标准输入与标准输出承载 JSON-RPC 2.0,每条消息一行。客户端只依赖本条规定的方法与通知,不依赖实现者。
  2. 方法。
方法 参数 结果
test/plan project、可选 filter 测试程序及其用例(B3 的列出记录)
test/run project、selection(程序与用例标识)、可选 options 立即返回 run 标识
test/add run、selection 把用例加入进行中的运行
test/cancel run,可选 program 与 case 取消整个运行、一个程序或一个用例
test/status 可选 run 各运行与其用例的当前状态
shutdown — 结束会话;进行中的运行先被取消
  1. 通知。 test/build(构建的开始与结束,转述 B5)、test/case(用例的 begin、end、output、failure,字段与 O2、O3、O8 相同)、test/program(程序记录)、test/run(运行结束,带计数)。
  2. 调度。
    • 新请求立即调度,与其他运行共享并发数与资源池(O5、O6);
    • 同一程序正在运行时,新请求另起进程运行,不等待;
    • 用例声明 isolation = "case" 时,每个用例一个进程。
  3. 构建。 会话禁止自行构建:它以 B5 请求 mcpp,并按 launch 启动程序。会话中同一时刻至多一次构建请求;构建期间到达的请求在构建结束后调度。
  4. 取消。
    • 用例独占进程时,运行器结束其进程树(POSIX 上以进程组,Windows 上以作业对象),其状态为 cancelled;
    • 用例与其他用例共用一个进程时,该进程中未结束的用例为 cancelled,已结束的保持原结论,被选中而尚未开始的用例另起进程继续。
    • cancelled 是运行器的状态,不属于 P5.3 的结论集合,不影响 P5.5 的退出状态规则。
  5. 存储。 会话的运行以 B4 的形式保存。

O10. 按测量调度 已实现(mcpp >= 2026.11.13.1)

  1. 估计。 运行之前,运行器从 B4 的存储读取最近的运行(至多 30 次,按 started 由新到旧):每个程序记录名与每个用例标识的最近用时。一个测试的估计是其所选用例用时之和,未选用例时为程序整体的用时。没有测量的程序记录取其最近目录(按 / 逐级向上)中已测程序记录的平均值,各级都没有时取已测程序记录中的最大值;没有测量的用例取同一列表中已测用例的平均值,都没有时取其程序估计按用例数的等分。状态为 built 或 not-run 的记录与跳过的用例不计入。
  2. 顺序。 测试应当按估计从长到短开始;占用 machine 的测试仍排在最后(P7.1)。
  3. 划分。 split = "auto" 时,份额为全部测试的估计之和除以工作线程数与分片数;估计长于份额、且含两个以上列出用例的部分必须划分为 ⌈估计/份额⌉ 组(不多于用例数),每组按最长处理时间优先分配用例,组内保持列出顺序,名为 <部分> (i/k)。 按声明的用例划分(mcpp >= 2026.11.15.1)。 程序的选择理解 except 与 optional(P4.3)时,估计长于份额、且存储中测得两个以上其声明的用例(P3.4:列表不给出这些用例)的列出用例必须按这些用例中最内层者划分为 ⌈估计/份额⌉ 组(不多于其数),名为 <部分> [<用例>] (i/k):前 k−1 组以 ? 行选中所分得的用例,第 k 组选中该用例并以 ! 行排除前 k−1 组的用例。没有测量的声明用例因而只在第 k 组运行一次,已删除或改名的用例不使运行失败。一个用例的估计取其最近一次整体运行的测得用时;没有整体运行时不小于其最内层声明用例的测得用时之和,因为被划分的运行只测得其余下部分。存储中用例的用时按其程序(部分所属的程序)与标识记取。 划分须经测量证明有效。 同一用例声明的用例可能共享由第一个用例填充的状态(缓存、工具链),每一组都要重新付出这部分代价。被划分用例的各组在程序进程一行写 divides(所划分用例的标识)。存储中从未被划分的用例必须划分一次以取得测量;此后仅当其最近一次划分中最长一组的用时不超过其最近一次整体运行用时的五分之四时必须再次划分,否则整体运行;窗口内没有整体运行的记录时整体运行,以重新测得。
  4. 分片。 --shard I/N 把每个测试按估计从长到短(相同时按名字)分配给已分配估计之和最小的分片(相同时取编号最小者),只运行第 I 个分片的测试。分配必须只取决于存储与本次运行的测试列表。分片禁止把本组(GITHUB_RUN_ID,无则为空)的分片运行读作历史,使同一存储上先后启动的各分片读到相同的历史。
  5. 失败重跑。 --failed 读取存储中最近的一次运行:状态为 fail 或 build-fail 的程序记录与状态为 fail 或 xpass 的用例。所选程序为这些记录的 part_of(无则为其名字);每个失败的程序都有失败用例时,只运行这些用例(与 --case 相同);否则整体运行这些程序。最近一次运行没有失败时,运行器不运行任何测试,退出码为 0。

4. 宣告

mcpp 以 feature(docs/50 §4)宣告它实现的条款;客户端按需门控。

feature 宣告的条款 起始版本
test-protocol B2 的运行器一侧、B3 2026.11.8.1
test-parts O1 2026.11.10.1
test-case-stream O2 2026.11.10.1
test-metrics O4、O7 的运行器一侧,B4 的相应字段 2026.11.10.1
test-launch B5 的单个测试形式:mcpp test <测试> --print-launch 2026.10.31.2
test-launch-records B5:--no-run 记录的 launch 2026.11.10.1
test-frameworks B6 的 capabilities 与 session 2026.11.10.1
test-checks O3 的运行器一侧 2026.11.10.1
test-output O8 2026.11.10.1

框架实现的可选能力由 B6 宣告,不由 mcpp 的 feature 宣告。

5. 一致性

  • 一致性套件(mtest 的 conformance)按本规范的条款编号报告:每个检查写出它覆盖的 B、O 或 P 条款。
  • 框架或运行器声明的每项能力都必须通过对应条款的检查;未声明的能力不检查。
  • 实现者在其文档中列出它实现的条款与能力(例如 mtest 的 spec/protocol.md)。

6. 兼容性与迁移

  • 所有新增均为字段、记录种类、清单键与 feature 的增加。case 记录的唯一顶层键是 case,只按已知键分派的客户端会跳过它。
  • 协议条款的编号 P1–P12 不变:引用 mtest 协议版本 1 条款的文档与测试继续有效。mtest 的 spec/protocol.md 改为引用本规范,并列出 mtest 实现的条款与能力。
  • SPEC-017 v1.0 的章节对应:§2 → B2(P2.3、P2.1)与 B6;§3 → B2(P3、P4)与 O1;§4 → B2(P5)、O2、O4、O7;§5 → B3、O2;§6 → B4。
  • SPEC-004 中 [test] 的键由 B1 规定。

变更记录

版本 日期 变更
1.0 2026-11-10 初版(test-runner.md):2026.11.8.1 起的运行器侧行为、各部分、随写随读的用例记录、指标与界限、P12 存储字段(mcpp#917)
2.0 2026-11-10 改为 mcpp 测试规范:合并 SPEC-017 v1.0、mtest 协议版本 1 的规范正文与 SPEC-004 的 [test] 键;分为基础能力 B1–B6 与可选能力 O1–O9;新增 B5 启动描述、B6 的 test-capabilities 与 test-session、O3 检查点细节、O8 输出归属、O9 测试会话(mcpp#914、mcpp#915)
2.1 2026-11-14 P3.3 的位置与能力(only、except、capabilities、列为跳过时的 reason;mtest 0.8.0),及 mcpp 的实现
2.2 2026-11-14 O10 按测量调度(顺序、split = "auto"、--shard、--failed);B4 的 shard 与 part_of
2.3 2026-11-15 P4.3 排除与按声明选择(mtest 0.9.0);O10 按声明的用例划分并以测量判定是否保留(divides),用例的用时按程序与标识记取