Files
gplan-2/design/dev/architecture/adr-0004-test-carrier-and-ci.md
T

8.3 KiB
Raw Blame History

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 环境复核
  • ADR-0001(S6 规则来源)、ADR-0003(S4/S5 规则来源、探针形态耦合)
  • docs/code-conventions.md(缺陷-规范对照表)、各 GDD AC 前言(规范源)