T1 数据面:AuxItemTable schema(七字段+retired/deferred 两 additive 标记+真视窄域/board_wide 双承重护栏)+六行 tres(三有效+罗盘 retired 留痕+显影尘/全图卷 deferred)+ConfigDB 加载/访问器+RewardTable 十型扩值(aux_item)+三投放行 5/4/1+aux_none 负占位行(雷格极性字典兜底零兑付)+双表悬空校验(reward_dangling_aux,负占位豁免)+F2 投放维度覆盖(豁免面 plan/info/counter 登记)+两 fatal 夹具(expect=1 复制真表注入违规)+run_all manifest+2 T2 core 骨架:AuxItems 类(api 8 入口+probe 探针;零随机零特判;Used 不可逆;真视恒可用/预读点击级零信息拒绝;3x3 边缘窗口∩板)+action_economy.grant_ap 第 9 API(当轮加量,区别 queue_ap_delta 排队) T3 授予路由+生命周期:_grant_routed aux 分支(正=入库存发「获得」行;负=零兑付 effect 型「道具随雷沉没」行)+use_aux 四步受理(相位 gate→validate→逐 kind 空间先 AP 后→consume;结算明细无道具行)+叠加层三清(装板点/战斗结束)+LevelRunner 关卡域库存持有注入+关卡末 reset+aux_cleared 快照计数扩签 冒烟锚随词表扩值更新:九型→十型封闭/字典 18→22 行;负向字典扫描三处稳健化(aux 不入五型轴+break 改 percent_map 三型齐备+rng 每板新实例——原第一板 break 掩盖 set_master_seed _counter 守卫违例) 闸:冒烟 577/0(净增 1)+单测 11 套全过(含两新夹具)+四探针 PASS
59 lines
2.9 KiB
GDScript
59 lines
2.9 KiB
GDScript
# tests/run_all.gd — 单元测试统一入口(ADR-0004 双载体的逻辑测试调用点,套件 manifest 协议 D2 AC-2b)
|
||
# 用法:godot --headless --path game --script res://tests/run_all.gd (CI 侧包装外部超时:timeout 120s …)
|
||
# 协议:套件清单逐条"套件 → 期望退出码";致命类独立进程(expect=1)。
|
||
# 执行形态(单元测试与契约层批 2026-09-28):逐套件起独立 Godot 子进程实跑
|
||
#(OS.get_executable_path() 解析本引擎二进制——--script 模式无 autoload/全局标识符,
|
||
# 每个测试文件自带显式实例化夹具,见 tests/README 协议要点)。
|
||
# 场景探针(.tscn 载体)由 CI/轻闸单独步骤调用,不经本入口。
|
||
extends SceneTree
|
||
|
||
|
||
func _init() -> void:
|
||
print("[run_all] unit/contract suite entry — %d suites" % _suites().size())
|
||
if DisplayServer.get_name() != "headless":
|
||
push_warning("[run_all] 建议以 --headless 运行(D2 冒烟协议)")
|
||
var failures := 0
|
||
for suite in _suites():
|
||
failures += _run_suite(suite)
|
||
if failures > 0:
|
||
push_error("[run_all] %d 个套件失败" % failures)
|
||
quit(1)
|
||
return
|
||
print("[run_all] all green (%d suites)" % _suites().size())
|
||
quit(0)
|
||
|
||
|
||
# 套件清单:{ "path": String, "expect": int };致命类(期望非 0)独立进程断言退出码。
|
||
func _suites() -> Array:
|
||
return [
|
||
{"path": "res://tests/unit/example_test.gd", "expect": 0},
|
||
{"path": "res://tests/unit/data_config/config_db_test.gd", "expect": 0},
|
||
{"path": "res://tests/unit/data_config/config_db_fatal_oob_test.gd", "expect": 1},
|
||
{"path": "res://tests/unit/data_config/aux_target_violation_test.gd", "expect": 1}, # 迭代 7 T1——真视窄域护栏
|
||
{"path": "res://tests/unit/data_config/reward_dangling_aux_test.gd", "expect": 1}, # 迭代 7 T1——aux 双表校验
|
||
{"path": "res://tests/unit/data_config/rng_service_test.gd", "expect": 0},
|
||
{"path": "res://tests/unit/battle_settlement/damage_math_test.gd", "expect": 0},
|
||
{"path": "res://tests/unit/battle_settlement/damage_controller_test.gd", "expect": 0},
|
||
{"path": "res://tests/unit/contracts/system_contracts_test.gd", "expect": 0},
|
||
{"path": "res://tests/unit/presentation/ui_font_asset_test.gd", "expect": 0},
|
||
{"path": "res://tests/unit/presentation/terrain_tile_sets_test.gd", "expect": 0},
|
||
]
|
||
|
||
|
||
func _run_suite(suite: Dictionary) -> int:
|
||
var path: String = suite.get("path", "")
|
||
var expect: int = int(suite.get("expect", 0))
|
||
if path.is_empty():
|
||
push_error("[run_all] suite missing path")
|
||
return 1
|
||
var args := PackedStringArray(["--headless", "--path", ProjectSettings.globalize_path("res://"), "--script", path])
|
||
var out := PackedStringArray()
|
||
var code: int = OS.execute(OS.get_executable_path(), args, out, true, false)
|
||
var text := "\n".join(out)
|
||
print(text)
|
||
if code != expect:
|
||
push_error("[run_all] FAIL %s — 退出码 %d ≠ 期望 %d" % [path, code, expect])
|
||
return 1
|
||
print("[run_all] PASS %s (exit=%d)" % [path, code])
|
||
return 0
|