8.3 KiB
ADR-0004: 测试载体与 CI(Test Carrier & CI)
Status
Accepted(2026-09-12,solo 模式用户授权代决;迭代期可推翻重议)
Date
2026-09-12
Engine Compatibility
| Field | Value |
|---|---|
| Engine | Godot 4.7.2-stable |
| Domain | Core / Scripting(测试与工具链) |
| Knowledge Risk | LOW(--script/探针模式为 4.x 基线;godot-specialist 校验通过,含两条规则升格) |
| References Consulted | docs/engine-reference/godot/(四文件);godotengine/godot#6360(--script 模式不加载 autoload + 不注册全局标识符——官方立场) |
| Post-Cutoff APIs Used | None |
| Verification Required | None(切片 88/88 + 三探针已实证;CI 外部超时在正式 CI 接入时核对) |
ADR Dependencies
| Field | Value |
|---|---|
| Depends On | ADR-0003(core 隔离规则是扫描域的输入) |
| Enables | 全部 GDD AC 的可执行化;正式 CI 接入 |
| Blocks | /gate-check 前置项之一(/test-setup 以本 ADR 为基线) |
| Ordering Note | gdUnit4 复议登记为 D2 OQ1 的关闭形态(推迟,非否决) |
Context
Problem Statement
D2 GDD 定下 --headless --script 冒烟载体(脚本 extends SceneTree、autoload 显式实例化),D2 OQ1 留下"gdUnit4 是否引入"待决;切片新增了场景级 UI 探针形态;项目级静态扫描域散落在各 GDD AC(AC-9/17/21/22/43 等)需要一处统一登记。
Constraints
- headless 全套 < 30s 预算(technical-preferences)
- 致命类用例独立进程协议("调用 → 期望退出码" runner manifest,D2 AC-2b 起)
- 呈现层测试必须有承载(切片教训:已定稿 AC 全覆盖 core、呈现层零测试承载 → 缺陷 ①②④⑤ 漏网)
Requirements
- 双模式边界明确:逻辑冒烟(无 autoload)vs 场景探针(完整项目引导)
- 静态扫描规则统一载体,覆盖 D2/A1–B4 各 AC 登记的扫描项
- CI 可接入且防挂死
Decision
双载体 + 统一静态扫描域;gdUnit4 推迟到正式 CI 期。
1. 逻辑冒烟(--headless --script):脚本 extends SceneTree 以获得 quit(code);ConfigDB/RngService 由脚本显式实例化(引擎事实:--script 模式不加载 autoload 且不注册全局标识符——裸引用 = parse error 编译期即炸,GH #6360 官方确认)。Windows 用 console 版 exe 取 stdout。承载:D2/A1–B4 全部纯逻辑 AC + 致命类独立进程 manifest。
2. 场景探针(headless 跑 .tscn 探针场景):以探针场景为运行目标(godot --headless --path . res://smoke/<probe>.tscn)走正常项目引导——autoload 在位、真实 SceneTree、await process_frame 有效。承载:呈现层场景级测试(切片三探针 ui_probe / ui_click_probe / ui_tune_probe 为先例)。
- 口径修正(specialist 校验):click_probe 走
BoardView.click_cell是"真实翻译出口",绕过 Viewport GUI 管线(无命中测试/悬停/焦点)。表述定为"合成输入走真实翻译出口";真正的端到端输入测试 =Input.parse_input_event()/Viewport.push_input带坐标——若 ADR-0003 的 _draw 迁移执行(Button 命中测试消失),push_input 探针从加分项变为必需项(登记入迁移工作项)。
3. 项目级静态扫描域(统一登记,CI 与冒烟前置双跑):
| # | 规则 | 来源/动机 |
|---|---|---|
| S1 | 玩法代码禁 randomize()、自建 RandomNumberGenerator |
D2 Rule 5(既有) |
| S2 | 禁全局随机函数 randi() / randf() / randf_range()(全局 RNG 绕开 RngService——specialist 补登漏洞) |
D2 Rule 5 补全 |
| S3 | core/ 禁 ConfigDB 外 data 表加载(load( / preload( / ResourceLoader.load;schema class_name 直引豁免) |
D2 Rule 2(既有) |
| S4 | core/ 禁 extends Control/Node2D、禁 .tscn preload |
D2 Rule 7(既有) |
| S5 | core/ 禁裸引用 autoload 全局标识符(ConfigDB / RngService 直接写 = script 模式 parse error)——构造注入 only |
specialist 升格(切片已合规,规则防复发) |
| S6 | core↔core 信号连接只许方法 Callable(禁 lambda 捕获 self);发射者禁持有订阅者反向引用 | ADR-0001(防循环引用/生命周期延长) |
| S7 | B1 编排唯一性:B2/B3/B4 API 符号命中文件 ⊆ B1 文件集;A2/B3/B4 零相互直调 | B1 AC-43(既有) |
| S8 | 结算路径 await 零命中(B1 结算/编排文件集);字面量扫描 = warning 级 + 人工三要素复核(D2 AC-14 口径) |
B1(既有) |
4. CI 防挂死:任何探针/冒烟调用旁必须配外部 wall-clock 超时(命令行 timeout)——引擎自身无死锁探测;探针内 30 帧停滞检测(click_probe 先例)是第一道、外部超时是最后防线。headless 全套 < 30s 预算不变。
5. gdUnit4:推迟到正式 CI 期再议(D2 OQ1 以"deferred"关闭)。它运行在项目模式(autoload 在位),与 SceneTree 冒烟机制正交,引入不废弃既有用例。
Architecture Diagram
提交 → [静态扫描 S1–S8(秒级)] → [逻辑冒烟 --script(独立进程 manifest,<30s)]
→ [场景探针 ×N(headless + 外部超时)] → 全绿放行
Key Interfaces
- 冒烟 runner manifest:条目 = "调用 → 期望退出码"(独立进程、游戏通道)——D2 AC-2b 协议延续
- 探针退出码:0 = PASS;非 0 = 失败类别;停滞 quit(2)
Alternatives Considered
Alternative 1: 立即引入 gdUnit4 并迁移全部冒烟用例
- Pros: 标准 assertions/参数化/报告;编辑器面板。
- Cons: 88 条用例迁移成本即刻发生;切片框架已验证且快(~3s);MVP 期无 CI 基建需求。
- Rejection Reason: 收益在 CI 期才兑现——推迟(非否决),D2 OQ1 关闭形态。
Alternative 2: 仅保留逻辑冒烟、UI 靠人工
- Cons: 切片缺陷 ①②④⑤ 全部漏过既有 AC 的教训直接否定——呈现层无承载 = 呈现层缺陷零防线。
- Rejection Reason: 呈现层契约测试是 code-conventions 硬性要求。
Consequences
Positive
- 双模式边界以引擎事实固化(parse 期炸 vs 完整引导),不再靠约定俗成
- S2/S5/S6 三条 specialist 补登规则堵住静默翻车路径(全局随机绕开、裸引用编译炸、lambda 成环)
- 呈现层探针形态沉淀为项目标配(code-conventions 承载)
Negative
- 静态扫描规则 8 条的维护成本(新规则须挂真实反面案例——code-conventions 纪律)
- "真实 UI 路径"表述收窄后,端到端输入覆盖存在已知缺口(_draw 迁移前可接受)
Risks
- 未被停滞检测覆盖的死锁挂死 CI → 缓解:外部 wall-clock 超时为硬性要求
- 扫描规则漏报(启发式)→ 缓解:行为级硬证据并行(可回放断言、headless 实跑合成判定——D2 AC-9/10 模式)
GDD Requirements Addressed
| GDD System | Requirement | How This ADR Addresses It |
|---|---|---|
| d2-data-config.md | Rule 6 校验双通道;AC-2b 独立进程协议;OQ1 gdUnit4 | 冒烟框架定稿;manifest 延续;OQ1 关闭为 deferred |
| a1–b1 各 GDD | 各自静态扫描 AC(AC-9/17/18/19/21/22/43 模式) | S1–S8 统一登记为项目级域,各 AC 引用本 ADR |
| docs/code-conventions.md | 场景探针标配 / 提交前全量 | 双载体 + CI 链路承载 |
| b1-settlement.md | AC-43 编排唯一性单向扫描 | S7 载体 |
Performance Implications
- CPU: 全套冒烟 ~3s(切片实测),预算 <30s 富余
- Memory: headless dummy server 极低
- Load Time: 探针场景引导一次性
- Network: 不适用
Migration Plan
切片 smoke/ 目录即载体(零迁移);正式 CI 接入时补外部超时包装与 S1–S8 脚本化;/test-setup 以本 ADR 为基线脚手架。
Validation Criteria
- 冒烟 + 探针全绿为提交门(code-conventions"提交前全量")
- 扫描域脚本化后:新增违规案例必须先补扫描规则再修码(缺陷-规范对照表纪律)
<30s预算在 CI 环境复核
Related Decisions
- ADR-0001(S6 规则来源)、ADR-0003(S4/S5 规则来源、探针形态耦合)
- docs/code-conventions.md(缺陷-规范对照表)、各 GDD AC 前言(规范源)