T6 探针基建收口段: 三族样板收单点(probe_ui_util树寻址contains/begins_with参数化+isolate_save七份隔离样板/battle_driver五份战斗驱动)+level_autoplay双战斗循环合并_decide_act+test_kit收13套件_check样板(断言数逐套件零丢失对照表在报告)+BAD_DIR夹具共享+smoke_all五件(:953公有面clamped_pool/_battle_st七袋/check_silent并入quiet参/_pct转发删/load改preload);探针小件=main句柄Node类型注/魔法等待常量化/画布常数同源锚注/死参verbose删;红线守住=ui_input case④b与click循环体零触碰/拍板点三件未退役
This commit is contained in:
1 parent
0691a188fd
commit
315e46038c
32 files changed
+587
-764
No files matched your search
@@ -7,27 +7,12 @@
|
||||
extends SceneTree
|
||||
|
||||
const ConfigDBScript := preload("res://core/config_db.gd")
|
||||
const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:BAD 表目录夹具共享(copy_tables)
|
||||
|
||||
const SRC_DIR := "res://data/tables"
|
||||
const BAD_DIR := "user://aux_fixture_target_violation"
|
||||
|
||||
|
||||
func _copy_tables() -> bool:
|
||||
DirAccess.make_dir_recursive_absolute(BAD_DIR)
|
||||
var dir := DirAccess.open(SRC_DIR)
|
||||
if dir == null:
|
||||
return false
|
||||
dir.list_dir_begin()
|
||||
var f := dir.get_next()
|
||||
while f != "":
|
||||
if not dir.current_is_dir():
|
||||
var err := dir.copy(SRC_DIR + "/" + f, BAD_DIR + "/" + f)
|
||||
if err != OK:
|
||||
return false
|
||||
f = dir.get_next()
|
||||
return true
|
||||
|
||||
|
||||
func _inject_violation() -> bool:
|
||||
var path := BAD_DIR + "/aux_items.tres"
|
||||
var f := FileAccess.open(path, FileAccess.READ)
|
||||
@@ -58,7 +43,7 @@ func _inject_violation() -> bool:
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
if not _copy_tables() or not _inject_violation():
|
||||
if not Kit.copy_tables(SRC_DIR, BAD_DIR) or not _inject_violation():
|
||||
push_error("[aux_target_violation_test] 夹具目录准备失败")
|
||||
quit(2)
|
||||
return
|
||||
|
||||
@@ -5,16 +5,14 @@
|
||||
extends SceneTree
|
||||
|
||||
const ConfigDBScript := preload("res://core/config_db.gd")
|
||||
|
||||
var _failures := 0
|
||||
var _checks := 0
|
||||
const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:_check/汇总退出样板单点
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
var db = ConfigDBScript.new()
|
||||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||||
_check("全部表加载 + 校验通过(含跨表护栏零 error 诊断)", load_ok and db.diagnostics_count("error") == 0)
|
||||
_check("is_ready 通道随加载置位", db.is_ready)
|
||||
Kit.check("全部表加载 + 校验通过(含跨表护栏零 error 诊断)", load_ok and db.diagnostics_count("error") == 0)
|
||||
Kit.check("is_ready 通道随加载置位", db.is_ready)
|
||||
if not load_ok:
|
||||
for d in db.diagnostics():
|
||||
if d.severity == "error":
|
||||
@@ -25,21 +23,7 @@ func _init() -> void:
|
||||
_test_shop_accessors(db)
|
||||
_test_meta_accessors(db)
|
||||
_test_manifest_dir_sync(db)
|
||||
if _failures > 0:
|
||||
push_error("[config_db_test] %d/%d 项失败" % [_failures, _checks])
|
||||
quit(1)
|
||||
return
|
||||
print("[config_db_test] all green (%d checks)" % _checks)
|
||||
quit(0)
|
||||
|
||||
|
||||
func _check(desc: String, ok: bool) -> void:
|
||||
_checks += 1
|
||||
if ok:
|
||||
print(" [PASS] " + desc)
|
||||
else:
|
||||
_failures += 1
|
||||
push_error(" [FAIL] " + desc)
|
||||
quit(Kit.finish("config_db_test"))
|
||||
|
||||
|
||||
# ---- 进度坐标 ceil 解析(R1:向上下取整保证偏难;越最大行 clamp 取最大值 + 警告去重)----
|
||||
@@ -67,34 +51,34 @@ func _test_progress_resolution(db) -> void:
|
||||
# 精确命中:请求 = 表内现存 (L,D) → 原行返回(字段逐位相等)
|
||||
var probe_row: Dictionary = bg_rows[0]
|
||||
var got: Dictionary = db.resolve_board_gen(int(probe_row.level_index), int(probe_row.node_depth))
|
||||
_check("进度解析精确命中返回原行(board_gen 首行)",
|
||||
Kit.check("进度解析精确命中返回原行(board_gen 首行)",
|
||||
int(got.get("level_index", -99)) == int(probe_row.level_index)
|
||||
and int(got.get("node_depth", -99)) == int(probe_row.node_depth)
|
||||
and String(got.get("board_type", "")) == String(probe_row.get("board_type", "")))
|
||||
|
||||
# enemy 行集同通道:同 (L,D) 精确行存在(关卡节点地图 前置——battle 节点双表精确行契约)
|
||||
var en_got: Dictionary = db.resolve_enemy_rowset(int(probe_row.level_index), int(probe_row.node_depth))
|
||||
_check("enemy 行集同坐标解析非空(双表精确行契约)", not en_got.is_empty())
|
||||
Kit.check("enemy 行集同坐标解析非空(双表精确行契约)", not en_got.is_empty())
|
||||
|
||||
# 深度 clamp:请求深度超最大行 → 取该关最大深度行 + 警告诊断一次(去重)
|
||||
db.reset_diagnostics()
|
||||
var deep: Dictionary = db.resolve_board_gen(min_l, int(min_depths[-1]) + 1)
|
||||
_check("深度越最大行 clamp 取该关最大深度行",
|
||||
Kit.check("深度越最大行 clamp 取该关最大深度行",
|
||||
int(deep.get("level_index", -99)) == min_l and int(deep.get("node_depth", -99)) == int(min_depths[-1]))
|
||||
_check("clamp 解析发警告诊断(resolve_depth_clamp)", db.diagnostics_count("warning") >= 1)
|
||||
Kit.check("clamp 解析发警告诊断(resolve_depth_clamp)", db.diagnostics_count("warning") >= 1)
|
||||
var found_clamp := false
|
||||
for d in db.diagnostics():
|
||||
if d.category == "resolve_depth_clamp":
|
||||
found_clamp = true
|
||||
_check("警告类别 = resolve_depth_clamp", found_clamp)
|
||||
Kit.check("警告类别 = resolve_depth_clamp", found_clamp)
|
||||
var warn_before: int = db.diagnostics_count("warning")
|
||||
db.resolve_board_gen(min_l, int(min_depths[-1]) + 1)
|
||||
_check("同键重复请求警告去重(四元组注册表)", db.diagnostics_count("warning") == warn_before)
|
||||
Kit.check("同键重复请求警告去重(四元组注册表)", db.diagnostics_count("warning") == warn_before)
|
||||
|
||||
# 关卡 clamp:请求关卡超最大行 → 取最大关卡行
|
||||
db.reset_diagnostics()
|
||||
var high: Dictionary = db.resolve_board_gen(max_l + 1, 0)
|
||||
_check("关卡越最大行 clamp 取最大关卡行", int(high.get("level_index", -99)) == max_l)
|
||||
Kit.check("关卡越最大行 clamp 取最大关卡行", int(high.get("level_index", -99)) == max_l)
|
||||
|
||||
|
||||
# ---- 变异池门控(unlock_level 闸 + ∅ board_types 通配)----
|
||||
@@ -114,12 +98,12 @@ func _test_variant_pool_gate(db) -> void:
|
||||
var tag: String = String(gated_row.board_types[0])
|
||||
var before: Array = db.variant_pool(u - 1, tag)
|
||||
var after: Array = db.variant_pool(u, tag)
|
||||
_check("变异池 unlock 前不含未解锁行(level=unlock−1)", not _rows_contain(before, gated_row))
|
||||
_check("变异池 unlock 当级含该行", _rows_contain(after, gated_row))
|
||||
Kit.check("变异池 unlock 前不含未解锁行(level=unlock−1)", not _rows_contain(before, gated_row))
|
||||
Kit.check("变异池 unlock 当级含该行", _rows_contain(after, gated_row))
|
||||
if not wildcard_row.is_empty():
|
||||
var u2: int = int(wildcard_row.unlock_level)
|
||||
var any_pool: Array = db.variant_pool(u2, "nonexistent_board_type_tag")
|
||||
_check("∅ board_types 行通配(任一 board_type 命中)", _rows_contain(any_pool, wildcard_row))
|
||||
Kit.check("∅ board_types 行通配(任一 board_type 命中)", _rows_contain(any_pool, wildcard_row))
|
||||
|
||||
|
||||
func _rows_contain(pool: Array, row: Dictionary) -> bool:
|
||||
@@ -134,26 +118,26 @@ func _rows_contain(pool: Array, row: Dictionary) -> bool:
|
||||
func _test_shop_accessors(db) -> void:
|
||||
var products: Array = db.shop_products()
|
||||
if products.is_empty():
|
||||
_check("ShopTable products 行族非空(表已落籍前提)", false)
|
||||
Kit.check("ShopTable products 行族非空(表已落籍前提)", false)
|
||||
return
|
||||
_check("ShopTable products 行族非空", true)
|
||||
Kit.check("ShopTable products 行族非空", true)
|
||||
var first: Dictionary = products[0]
|
||||
var got: Dictionary = db.shop_product_row(String(first.product_id))
|
||||
_check("shop_product_row 按 product_id 精确命中首行", got == first)
|
||||
_check("shop_product_row 未知 id 返回空字典(容错通道)", db.shop_product_row("no_such_product_id__").is_empty())
|
||||
Kit.check("shop_product_row 按 product_id 精确命中首行", got == first)
|
||||
Kit.check("shop_product_row 未知 id 返回空字典(容错通道)", db.shop_product_row("no_such_product_id__").is_empty())
|
||||
var tier_rows: Array = db._shop_rows("gp_drops")
|
||||
if not tier_rows.is_empty():
|
||||
var tier0: String = String(tier_rows[0].tier)
|
||||
_check("shop_gp_drop_row 按 tier 命中", not db.shop_gp_drop_row(tier0).is_empty())
|
||||
Kit.check("shop_gp_drop_row 按 tier 命中", not db.shop_gp_drop_row(tier0).is_empty())
|
||||
var bonus_rows: Array = db._shop_rows("level_gp_bonus")
|
||||
if not bonus_rows.is_empty():
|
||||
var blv: int = int(bonus_rows[0].level_index)
|
||||
_check("shop_level_gp_bonus 按关卡序号命中行值", db.shop_level_gp_bonus(blv) == int(bonus_rows[0].bonus))
|
||||
_check("shop_level_gp_bonus 未登记关卡返回 0(兜底通道)", db.shop_level_gp_bonus(-1) == 0)
|
||||
Kit.check("shop_level_gp_bonus 按关卡序号命中行值", db.shop_level_gp_bonus(blv) == int(bonus_rows[0].bonus))
|
||||
Kit.check("shop_level_gp_bonus 未登记关卡返回 0(兜底通道)", db.shop_level_gp_bonus(-1) == 0)
|
||||
var price_rows: Array = db._shop_rows("price_level_bonus")
|
||||
if not price_rows.is_empty():
|
||||
var plv: int = int(price_rows[0].level_index)
|
||||
_check("shop_price_level_bonus 按关卡序号命中行值", db.shop_price_level_bonus(plv) == int(price_rows[0].bonus))
|
||||
Kit.check("shop_price_level_bonus 按关卡序号命中行值", db.shop_price_level_bonus(plv) == int(price_rows[0].bonus))
|
||||
|
||||
|
||||
# ---- MetaTable 消费面 ----
|
||||
@@ -161,19 +145,19 @@ func _test_shop_accessors(db) -> void:
|
||||
func _test_meta_accessors(db) -> void:
|
||||
var forge_rows: Array = db._meta_rows("forge_products")
|
||||
if forge_rows.is_empty():
|
||||
_check("MetaTable forge_products 行族非空", false)
|
||||
Kit.check("MetaTable forge_products 行族非空", false)
|
||||
else:
|
||||
var attr0: String = String(forge_rows[0].attr)
|
||||
_check("meta_forge_row 按 attr 命中首行", db.meta_forge_row(attr0) == forge_rows[0])
|
||||
_check("meta_forge_row 未知 attr 返回空字典", db.meta_forge_row("no_such_attr__").is_empty())
|
||||
Kit.check("meta_forge_row 按 attr 命中首行", db.meta_forge_row(attr0) == forge_rows[0])
|
||||
Kit.check("meta_forge_row 未知 attr 返回空字典", db.meta_forge_row("no_such_attr__").is_empty())
|
||||
var key_rows: Array = db._meta_rows("boss_keystone")
|
||||
if not key_rows.is_empty():
|
||||
var klv: int = int(key_rows[0].level_index)
|
||||
_check("meta_boss_keystone_row 按关卡序号命中", db.meta_boss_keystone_row(klv) == key_rows[0])
|
||||
Kit.check("meta_boss_keystone_row 按关卡序号命中", db.meta_boss_keystone_row(klv) == key_rows[0])
|
||||
var conv_rows: Array = db._meta_rows("conversion")
|
||||
if not conv_rows.is_empty():
|
||||
var cattr: String = String(conv_rows[0].attr)
|
||||
_check("meta_conversion_row 按 attr 命中", db.meta_conversion_row(cattr) == conv_rows[0])
|
||||
Kit.check("meta_conversion_row 按 attr 命中", db.meta_conversion_row(cattr) == conv_rows[0])
|
||||
|
||||
|
||||
# ---- TABLE_MANIFEST ↔ data/tables 目录同步(各系统→D2 单点契约的登记面)----
|
||||
@@ -195,4 +179,4 @@ func _test_manifest_dir_sync(db) -> void:
|
||||
for key in manifest.keys():
|
||||
if not disk_files.has(String(manifest[key])):
|
||||
synced = false
|
||||
_check("TABLE_MANIFEST 与 data/tables 目录逐文件同步(无缺登/无悬登)", synced)
|
||||
Kit.check("TABLE_MANIFEST 与 data/tables 目录逐文件同步(无缺登/无悬登)", synced)
|
||||
@@ -7,26 +7,12 @@
|
||||
extends SceneTree
|
||||
|
||||
const ConfigDBScript := preload("res://core/config_db.gd")
|
||||
const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:BAD 表目录夹具共享(copy_tables)
|
||||
|
||||
const SRC_DIR := "res://data/tables"
|
||||
const BAD_DIR := "user://aux_fixture_dangling_aux"
|
||||
|
||||
|
||||
func _copy_tables() -> bool:
|
||||
DirAccess.make_dir_recursive_absolute(BAD_DIR)
|
||||
var dir := DirAccess.open(SRC_DIR)
|
||||
if dir == null:
|
||||
return false
|
||||
dir.list_dir_begin()
|
||||
var f := dir.get_next()
|
||||
while f != "":
|
||||
if not dir.current_is_dir():
|
||||
if dir.copy(SRC_DIR + "/" + f, BAD_DIR + "/" + f) != OK:
|
||||
return false
|
||||
f = dir.get_next()
|
||||
return true
|
||||
|
||||
|
||||
func _inject_violation() -> bool:
|
||||
var path := BAD_DIR + "/reward.tres"
|
||||
var f := FileAccess.open(path, FileAccess.READ)
|
||||
@@ -50,7 +36,7 @@ func _inject_violation() -> bool:
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
if not _copy_tables() or not _inject_violation():
|
||||
if not Kit.copy_tables(SRC_DIR, BAD_DIR) or not _inject_violation():
|
||||
push_error("[reward_dangling_aux_test] 夹具目录准备失败")
|
||||
quit(2)
|
||||
return
|
||||
|
||||
@@ -6,33 +6,17 @@
|
||||
extends SceneTree
|
||||
|
||||
const RngServiceScript := preload("res://core/rng_service.gd")
|
||||
const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:_check/汇总退出样板单点
|
||||
|
||||
const TEST_SEED := 20260928 # 夹具种子(任意固定值;语义 = 可复现性,非玩法数值)
|
||||
|
||||
var _failures := 0
|
||||
var _checks := 0
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_test_derive_determinism()
|
||||
_test_counter_ledger()
|
||||
_test_stream_draw_determinism()
|
||||
_test_negative_seed_normalization()
|
||||
if _failures > 0:
|
||||
push_error("[rng_service_test] %d/%d 项失败" % [_failures, _checks])
|
||||
quit(1)
|
||||
return
|
||||
print("[rng_service_test] all green (%d checks)" % _checks)
|
||||
quit(0)
|
||||
|
||||
|
||||
func _check(desc: String, ok: bool) -> void:
|
||||
_checks += 1
|
||||
if ok:
|
||||
print(" [PASS] " + desc)
|
||||
else:
|
||||
_failures += 1
|
||||
push_error(" [FAIL] " + desc)
|
||||
quit(Kit.finish("rng_service_test"))
|
||||
|
||||
|
||||
func _fresh_service():
|
||||
@@ -49,11 +33,11 @@ func _test_derive_determinism() -> void:
|
||||
for scope in range(8):
|
||||
if a.derive_at(scope) != b.derive_at(scope):
|
||||
same = false
|
||||
_check("同种子两实例 derive_at(0..7) 逐位一致", same)
|
||||
Kit.check("同种子两实例 derive_at(0..7) 逐位一致", same)
|
||||
var a2 = _fresh_service()
|
||||
var before: int = a2.counter()
|
||||
a2.derive_at(0)
|
||||
_check("derive_at 为纯函数不推计数器", a2.counter() == before)
|
||||
Kit.check("derive_at 为纯函数不推计数器", a2.counter() == before)
|
||||
|
||||
|
||||
# 全局取数计数器:唯一账本——open_stream 逐次 +1(scope 单调)
|
||||
@@ -64,13 +48,13 @@ func _test_counter_ledger() -> void:
|
||||
var c1: int = rng.counter()
|
||||
rng.open_stream("armor_roll")
|
||||
var c2: int = rng.counter()
|
||||
_check("open_stream 逐次推进全局计数器(+1/次)", c1 == c0 + 1 and c2 == c0 + 2)
|
||||
Kit.check("open_stream 逐次推进全局计数器(+1/次)", c1 == c0 + 1 and c2 == c0 + 2)
|
||||
# 用途标签只进事件日志不参与派生——同 scope 同派生种子(ADR-0007 裁丙口径)
|
||||
var rng_x = _fresh_service()
|
||||
var rng_y = _fresh_service()
|
||||
var s1: int = int(rng_x.open_stream("board_gen").seed)
|
||||
var s2: int = int(rng_y.open_stream("reward_roll").seed)
|
||||
_check("同 scope 异用途标签派生种子一致(标签不参与派生)", s1 == s2)
|
||||
Kit.check("同 scope 异用途标签派生种子一致(标签不参与派生)", s1 == s2)
|
||||
|
||||
|
||||
# 同种子两实例同标签流的实际取数序列逐位一致(回放确定性契约——存档快照=主种子+计数器的前提)
|
||||
@@ -84,7 +68,7 @@ func _test_stream_draw_determinism() -> void:
|
||||
var rb: int = b.open_stream("armor_roll").randi_range(1, faces)
|
||||
if ra != rb:
|
||||
same = false
|
||||
_check("同种子两实例同标签流取数 12 次逐位一致", same)
|
||||
Kit.check("同种子两实例同标签流取数 12 次逐位一致", same)
|
||||
|
||||
|
||||
# 负输入 abs 归一化(警告通道)——|−s| 与 s 同派生
|
||||
@@ -92,4 +76,4 @@ func _test_negative_seed_normalization() -> void:
|
||||
var pos = _fresh_service()
|
||||
var neg = RngServiceScript.new()
|
||||
neg.set_master_seed(-TEST_SEED)
|
||||
_check("负种子 abs 归一化后派生与正种子一致", pos.derive_at(0) == neg.derive_at(0))
|
||||
Kit.check("负种子 abs 归一化后派生与正种子一致", pos.derive_at(0) == neg.derive_at(0))
|
||||
Reference in new issue
Block a user