设计目标
四个语料前端针对截然不同的数据(十进制行、区间语句、MPFR 行、TestFloat 向量)报告结果,而 Python 工具链会跨进程汇总这些结果。它们必须对”用例”、“通过”、“跳过”和”分片”的含义达成一致,否则公布的总数将无法相互比较。internal/conformance 一次性确定了这套术语:每个用例一种处置结果、具有固定恒等式的计数器,以及一条分片规则。
数学背景
结果与计数器
一个结果是四元组 (id,δ,π,m),其中处置结果 δ∈{E,D,L,U}(可执行、诊断、遗留、不支持),通过位为 π,消息为 m。对于结果列表 R,定义计数向量
c(R)=r∈R∑⎩⎨⎧eexec+epasseexec+efaileskip+eδδ=E, π,δ=E, ¬π,δ∈{D,L,U},selected(R)=∣R∣,
其中各 e 是 N7 中的单位向量。RunSummary::from_results 精确地计算 c(R) 和 ∣R∣,并存储调用者给出的总数 T。
分片
对于 n≥1 和 0≤i<n,分片 (n,i) 选取序号集合 Si={k∈N:kmodn=i}。
设计决策
四种处置结果,一种失败概念
只有可执行的结果才可能失败。区分诊断、遗留和不支持这三类跳过,使报告能够说明为什么某些行没有运行:诊断行不是测试(不损失任何结论),不支持的行是缺失的功能(损失一项结论),遗留行遵循已废弃的约定。success() 的含义是”没有可执行用例失败”;更严格的判定由调用者在其上叠加(ITL 前端在出现诊断时也判失败,各 CLI 可选择在出现不支持的行时判失败)。
轮转分片
将序号 k 分配给分片 kmodn 无需知道总数,可以在流式处理中决定,并能将相邻的(往往代价相近的)用例分散到各个分片中。校验(try_new)与使用相分离,因此 selects 只是一次比较。
total 由外部提供,merge 取最大值
分片前的用例数由调用者知道,而不是由某个分片的结果列表知道。一次运行的每个分片都报告相同的总数 T,因此无论分成多少部分,merge 中取最大值都返回 T,而求和则会把它计算 n 次。
从 1 开始的位置
SourceLocation 将行号和列号限制为至少为 1,因此格式化后的诊断(file:line:column: message)始终是有效的编辑器位置,即使调用者传入 0 表示”未知”也是如此。
正确性 / 不变式
计数恒等式。 对于每个汇总,selected=executable+skipped,executable=passed+failed,且 skipped=diagnostic+legacy+unsupported。c(R) 的每个加项恰好在每个恒等式的一侧加 1,因此这些恒等式对 from_results 成立;merge 按分量相加计数器,从而保持线性恒等式。
分片划分序号。 每个 k 模 n 恰有一个余数,因此各 Si 互不相交且覆盖 N。在前 N 个序号中,分片 i 分得
∣Si∩{0,…,N−1}∣=⌈nN−i⌉,
因此各分片大小至多相差一。
合并分片得到串行计数。 c 是从(以拼接为运算的)列表到 (N7,+) 的幺半群同态:c(R++R′)=c(R)+c(R′),同样 ∣R++R′∣=∣R∣+∣R′∣。若各分片的结果就是串行运行的结果在 S0,…,Sn−1 上的限制(只要一个用例的结果不依赖于其他用例,这就成立),则
merge(fr(T,R∣S0),…,fr(T,R∣Sn−1)) and fr(T,R)
具有相等的计数器和相等的总数;只有结果列表的顺序不同(按分片分组)。
不可变性。 汇总在构造时和调用 results() 时都会复制结果数组,因此调用者事后无法修改汇总。
被否决的替代方案
- 单一的”跳过”计数器。 这会掩盖非测试与缺失功能之间的区别,而这恰恰是符合性声明必须说明的。
- 连续分片。 这需要事先知道总数,并会把代价高的文件集中到一个分片中。
- 前端公开使用这些类型。 前端改为对它们进行包装,因此本包可以演进而不破坏已发布的 API。
边界
- 不解析语料,不执行,不做 IO,不处理 JSON:这些属于各前端和 internal/runner_cli。
- 不提供计时或性能数据。
- 不检查结果的
passed 位是否与其处置结果一致。
- 内部包:不能在
Luna-Flow/floating 之外导入,不提供稳定性承诺。