Files
gplan-2/game/tests/run_all.gd
T
DKChan 0640b0c06d feat(pool): 迭代20 T2 池结构案D 换代(专长实装批 A段二)
四级档位:TIERS common/excellent/rare/legendary(跨层框架档名映射承袭)——TIER_QUOTAS
70/25/3/0=98 直映射零偏离已裁结构锚(传说=头奖位留空);配额/件值字段物理改名
(旧 rare→excellent、旧 rarest→rare)+凭证四 kind 占位(rap/attune_battery/time_sand/
perfect_insurance,配额暂 0=T4 落路由同批投放)。

概率与上限解耦(案②令牌池+提前退役):curio_cap_* 每 kind 独立字段(99=无上限占位,
实数归调参批)+专长 cap 行字段面;taken 身份维计数(relic:<feat_id>)拿了才计刷出不计;
cap 满在候选视图剔除(牌库本体不动=概率位独立存活);断点 bonus_taken 身份维键随档还原。

曝光槽:三选一=2 格全池+1 格优秀档以上子池(子池独立洗牌同 stream 续消费;子池空回退
全池;池空直通同族兜底);凭证(feat_ticket)不进抽签池只经保底注入;商店货架同 cap 过滤。

潜伏缺陷修正:效果引擎 has 算子 null 安全(敌技伤害载荷无 terrain_kind 事实回落 null 时
判「不在场」而非 String(null) 抛错——候选流变化后 devour 件首次被持有而暴露)。

测试:pool_case_d_test 新套(V6 令牌三态/V7 曝光槽回退/V8 解耦提前退役)入 run_all 20 套;
smoke 三档锚/数值对/池构成/上限带四处断言换代(V14),冒烟 848/0+probe_gate --click 全绿。
2026-10-11 23:18:35 +08:00

68 lines
4.5 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/treasure_target_violation_test.gd", "expect": 1}, # 迭代 7 T1——真视窄域护栏
{"path": "res://tests/unit/data_config/reward_dangling_treasure_test.gd", "expect": 1}, # 迭代 7 T1——秘宝双表校验
{"path": "res://tests/unit/data_config/rng_service_test.gd", "expect": 0},
{"path": "res://tests/unit/core/treasure_items_test.gd", "expect": 0}, # 迭代 7 T7——秘宝结构冻结断言面
{"path": "res://tests/unit/core/breakpoint_resume_test.gd", "expect": 0}, # 迭代 8 卡①/T9——断点往返/续玩确定性/幂等/退出清断点
{"path": "res://tests/unit/core/curio_refresh_paid_test.gd", "expect": 0}, # 2026-10-06——三选一刷新免费双源+GP 付费档(对齐商店 MC-1:价曲线/优先序/停点清零/钱包单口)
{"path": "res://tests/unit/presentation/pacing_queue_test.gd", "expect": 0}, # 迭代 8 卡⑦ AC⑤/T9——排空零分叉/零丢行/延迟双态
{"path": "res://tests/unit/presentation/reward_overlay_test.gd", "expect": 0}, # 迭代 7 hotfix——奖励节点 UI 直验(盲区补漏)
{"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/battle_settlement/skill_unlock_test.gd", "expect": 0}, # 迭代14④ T6——Boss 半血解锁技·容器机制(门谓词/闩不可逆/双面过滤/保底不逼锁定/零扰动)
{"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},
{"path": "res://tests/unit/core/fog_lantern_wiring_test.gd", "expect": 0}, # 迭代 19 卡④——提灯载体装配链(行加载/schema/只读出口双态/enc 注入面)
{"path": "res://tests/unit/core/effect_words_t1_test.gd", "expect": 0}, # 迭代 20 T1——词表扩签封闭性(V1:新词在册/出域四路被拒/adventure 计数器往返/mod_eq 预评估口径)
{"path": "res://tests/unit/core/pool_case_d_test.gd", "expect": 0}, # 迭代 20 T2——池结构案D 行为(V6 令牌三态/V7 曝光槽 2+1 回退/V8 概率上限解耦提前退役)
]
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