109 lines
5.5 KiB
GDScript
109 lines
5.5 KiB
GDScript
# tests/unit/data_config/level_gen_params_test.gd — 关卡节点图参数档单测(节点图生成器核心批 卡① T1/V2)
|
||
# 覆盖:生产表三行加载+validate 零违例、域校验逐类负向(出口 L1 恒 1/配比合计 1/层深下限/增幅变大/
|
||
# 岔口去路数锁定/键集闭集/键重复)、ConfigDB 访问器按 level_index 读回与缺行空字典。
|
||
# 运行:godot --headless --path game --script res://tests/unit/data_config/level_gen_params_test.gd(expect 0)
|
||
# 协议(tests/README):extends SceneTree + quit(code);负向用例资源直构不碰生产表。
|
||
extends SceneTree
|
||
|
||
const ConfigDBScript := preload("res://core/config_db.gd")
|
||
const LgpScript := preload("res://data/schemas/level_gen_params.gd")
|
||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||
|
||
|
||
func _init() -> void:
|
||
var db = ConfigDBScript.new()
|
||
var load_ok: bool = db.load_tables("res://data/tables", true)
|
||
Kit.check("生产 level_gen_params.tres 随全表加载 + validate 零违例〔fv2〕",
|
||
load_ok and db.diagnostics_count("error") == 0)
|
||
if load_ok:
|
||
_test_production(db)
|
||
_test_accessor()
|
||
_test_negative()
|
||
quit(Kit.finish("level_gen_params_test"))
|
||
|
||
|
||
func _row(L: int, layers: int, bd: float, ratios: Array, entry: int, exit_c: int, floor_n: int) -> Dictionary:
|
||
return {"level_index": L, "layers": layers, "branch_density": bd,
|
||
"battle_ratio": ratios[0], "reward_ratio": ratios[1], "rest_ratio": ratios[2], "shop_ratio": ratios[3],
|
||
"entry_count": entry, "exit_count": exit_c, "battle_floor": floor_n,
|
||
"fork_ways_min": 2, "fork_ways_max": 3}
|
||
|
||
|
||
func _table(rows: Array) -> Variant:
|
||
var t = LgpScript.new()
|
||
var typed: Array[Dictionary] = []
|
||
for r in rows:
|
||
typed.append(r)
|
||
t.rows = typed
|
||
return t
|
||
|
||
|
||
# ---- 生产三行正向(V2)----
|
||
|
||
func _test_production(db) -> void:
|
||
var r1: Dictionary = db.run.level_gen_params.row_by_level_index(1)
|
||
var r2: Dictionary = db.run.level_gen_params.row_by_level_index(2)
|
||
var r3: Dictionary = db.run.level_gen_params.row_by_level_index(3)
|
||
Kit.check("访问器读回 L1 行五旋钮齐备〔fv2〕",
|
||
int(r1.get("layers", 0)) == 6 and is_equal_approx(float(r1.get("branch_density", 0.0)), 0.3)
|
||
and int(r1.get("exit_count", 0)) == 1 and int(r1.get("entry_count", 0)) == 1)
|
||
Kit.check("访问器读回 L2/L3 行层深 9/13 递增〔fv2〕",
|
||
int(r2.get("layers", 0)) == 9 and int(r3.get("layers", 0)) == 13)
|
||
Kit.check("三行出口 = 1/2/3、战斗保底 = 4/6/8 与 §8.1 表对上〔fv2〕",
|
||
int(r1.get("battle_floor", 0)) == 4 and int(r2.get("exit_count", 0)) == 2
|
||
and int(r3.get("exit_count", 0)) == 3 and int(r3.get("battle_floor", 0)) == 8)
|
||
Kit.check("生产表 validate 零错误〔fv2〕", db.run.level_gen_params.validate().is_empty())
|
||
|
||
|
||
# ---- 访问器缺行防御 ----
|
||
|
||
func _test_accessor() -> void:
|
||
var t = _table([_row(1, 6, 0.3, [0.5, 0.2, 0.2, 0.1], 1, 1, 4)])
|
||
Kit.check("row_by_level_index 缺行返回空字典〔fv2〕", t.row_by_level_index(9).is_empty())
|
||
|
||
|
||
# ---- 域校验逐类负向(V2)----
|
||
|
||
func _test_negative() -> void:
|
||
# 合法基线三行
|
||
var base := [_row(1, 6, 0.3, [0.5, 0.2, 0.2, 0.1], 1, 1, 4),
|
||
_row(2, 9, 0.5, [0.5, 0.2, 0.15, 0.15], 2, 2, 6),
|
||
_row(3, 13, 0.7, [0.55, 0.15, 0.15, 0.15], 2, 3, 8)]
|
||
# ① L1 出口改 2 → 出口恒 1 断言红(负样本夹具)
|
||
var bad_l1_exit := [_row(1, 6, 0.3, [0.5, 0.2, 0.2, 0.1], 1, 2, 4), base[1], base[2]]
|
||
# ② 配比合计 0.5 → 合计 1 断言红(负样本夹具)
|
||
var bad_ratio := [_row(1, 6, 0.3, [0.2, 0.1, 0.1, 0.1], 1, 1, 4), base[1], base[2]]
|
||
# ③ 层深低于下限
|
||
var bad_layers := [_row(1, 5, 0.3, [0.5, 0.2, 0.2, 0.1], 1, 1, 4), base[1], base[2]]
|
||
# ④ 增幅未变大(6/9/11:Δ12=3、Δ23=2)
|
||
var bad_growth := [base[0], _row(2, 9, 0.5, [0.5, 0.2, 0.15, 0.15], 2, 2, 6),
|
||
_row(3, 11, 0.7, [0.55, 0.15, 0.15, 0.15], 2, 3, 8)]
|
||
# ⑤ 出口越上界(L3 出口 4)
|
||
var bad_exit := [base[0], base[1], _row(3, 13, 0.7, [0.55, 0.15, 0.15, 0.15], 2, 4, 8)]
|
||
# ⑥ 岔口去路数改 (2,4)
|
||
var bad_fork_row := _row(1, 6, 0.3, [0.5, 0.2, 0.2, 0.1], 1, 1, 4)
|
||
bad_fork_row["fork_ways_max"] = 4
|
||
# ⑦ 键集非闭集(多塞未知键)
|
||
var bad_keys_row: Dictionary = _row(1, 6, 0.3, [0.5, 0.2, 0.2, 0.1], 1, 1, 4)
|
||
bad_keys_row["mystery"] = 1
|
||
# 逐类负向表:[个案标签, 行集, 期望零错误]——每行仍逐案断言,消息带期望/实际与违例明细
|
||
var cases: Array = [
|
||
["合法三行域校验零错误(夹具自证)", base, true],
|
||
["L1 出口=2 被拒(出口 L1 恒 1 已锁)", bad_l1_exit, false],
|
||
["配比合计 0.5 被拒(四类合计 1 已锁)", bad_ratio, false],
|
||
["层深 5<6 被拒(层深下限已锁)", bad_layers, false],
|
||
["层深增幅倒挂被拒(递增且增幅变大)", bad_growth, false],
|
||
["出口 4 越域 [1,3] 被拒", bad_exit, false],
|
||
["岔口去路数 (2,4) 被拒(恒 2/3 已锁)", [bad_fork_row, base[1], base[2]], false],
|
||
["字段键集非 ROW_KEYS 闭集被拒", [bad_keys_row, base[1], base[2]], false],
|
||
["键重复行被拒", [base[0], base[0]], false],
|
||
["战斗保底 -1 被拒(≥0)", [_row(1, 6, 0.3, [0.5, 0.2, 0.2, 0.1], 1, 1, -1), base[1], base[2]], false],
|
||
["空表域校验零错误(合法态)", [], true],
|
||
]
|
||
for c in cases:
|
||
var errs: Array = _table(c[1]).validate()
|
||
var expect_label := "零错误" if c[2] else "有违例"
|
||
var actual_label := "零错误" if errs.is_empty() else "违例 %d 条 %s" % [errs.size(), str(errs)]
|
||
Kit.check("域校验:%s(期望%s,实际%s)〔fv2〕" % [c[0], expect_label, actual_label],
|
||
errs.is_empty() == c[2])
|