feat(迭代29 T5): L1 覆盖补洞——4 个零指名 core 模块 + 呈现层两件直接单测
新增六套(run_all 27 → 33 套),全部进 manifest,全绿: · core/board_generator_test(9 条):尺寸与 board_gen 表一致、每个安全格数字恒等于八邻雷数 (全盘逐格复算)、同种子雷集/数字集逐位一致、可推理性度量与清图判定自洽。 · core/minesweeper_rules_test(12 条):装板带初始扩张揭示且未铺满安全格、安全格余量=总数−已揭示、 挖已揭示格拒(target_revealed)、挖非相邻格拒(not_adjacent)、fx_reveal 推进揭示并递减余量、 插旗落旗态与计数、目录外意图被拒。 · core/config_db_resolver_test(8 条):ceil 进度键偏难语义(请求 (1,1) 命中 (1,3) 而非 (1,0))、 行集按表文件行序返回、ceil 行集、难度乘数 clamp 上下限、未登记坐标返回空修饰。 · core/config_db_cross_validator_test(4 条):正向全对齿零错(防误伤)、悬空 skill_id 必报、 buff 挂主角侧状态必报(cast_side)。 · presentation/hud_presenter_test(5 条)+ battle_narrator_test(9 条):文案逐拒绝原因正确、 兜底不吐空串、数值带入、胜负互异、关卡收束态恒定、Boss 名带入。 三屏壳的断言面由 T4 screen_flow_probe 承担(六步流程每步判屏级互斥),不另起第二载体。 变异验证:hud_presenter 的「AP 不足。」改漏句号 → hud_presenter_test 精确见红。 另有一批「写反的断言被实现抓出」的证据(首跑即红、逐条订正后转绿):装板并非零揭示而是带初始扩张、 挖受英雄邻接约束、插旗需 flag_range_free 或相邻且要先给足 AP、技能 type 是 basic 不是 attack、 状态表 id 键是 effect_id 不是 status_id、battle_end_text 在 level_end=true 时恒为「关卡通关!」。 顺带实证本批核心命题:config_db_resolver_test 首跑因诊断回调的 lambda 形参类型写错 (首参是表 id 字符串而非 int)触发 Callable 调用失败——脚本错误不改退出码却让整个解析函数静默 返回 null,套件仍会报「全绿」。正是脚本错误扫描存在的原因。
This commit is contained in:
1 parent
1458d94b0e
commit
d091c98163
7 files changed
+476
No files matched your search
@@ -0,0 +1,44 @@
|
||||
# tests/unit/presentation/battle_narrator_test.gd — 战斗叙述直接单测(迭代 29 T5 补洞:该模块此前零指名)
|
||||
# 断言面 = 纯函数文案:休整/商店/终局三类叙述把数值带进去(玩家读数面由这里出)、
|
||||
# 胜负两态文案互异、未知输入有兜底不吐空串。期望值由注入内容数出来。
|
||||
# 运行:godot --headless --path game --script res://tests/unit/presentation/battle_narrator_test.gd
|
||||
extends SceneTree
|
||||
|
||||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
const NarratorScript := preload("res://presentation/battle_narrator.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_test_numeric_lines_carry_values()
|
||||
_test_end_text_differs_by_result()
|
||||
_test_phase_text_nonempty()
|
||||
quit(Kit.finish("battle_narrator_test"))
|
||||
|
||||
|
||||
func _test_numeric_lines_carry_values() -> void:
|
||||
var rest: String = NarratorScript.rest_line(12, 30, 40)
|
||||
Kit.check("休整叙述带入回复量与当前血量(实际「%s」)" % rest,
|
||||
rest.contains("12") and rest.contains("30") and rest.contains("40"))
|
||||
var shop: String = NarratorScript.shop_line(77)
|
||||
Kit.check("商店叙述带入 GP 数(实际「%s」)" % shop, shop.contains("77"))
|
||||
Kit.check("奖励行与休整行互异(不是同一句套所有场景)",
|
||||
NarratorScript.reward_line() != rest)
|
||||
|
||||
|
||||
func _test_end_text_differs_by_result() -> void:
|
||||
# 关卡收束(level_end=true)恒为「关卡通关!」——单场胜负文案只在非收束态出现
|
||||
var level: String = NarratorScript.battle_end_text("lose", true)
|
||||
Kit.check("关卡收束态恒为「关卡通关!」(实际「%s」)" % level, level == "关卡通关!")
|
||||
var win: String = NarratorScript.battle_end_text("victory", false)
|
||||
var lose: String = NarratorScript.battle_end_text("lose", false)
|
||||
Kit.check("单场胜负文案互异(胜「%s」/负「%s」)" % [win, lose], win != lose)
|
||||
Kit.check("胜利文案非空(实际「%s」)" % win, win != "")
|
||||
var unknown: String = NarratorScript.battle_end_text("something_else", false)
|
||||
Kit.check("未知结果有兜底文案不吐空串(实际「%s」)" % unknown, unknown != "")
|
||||
|
||||
|
||||
func _test_phase_text_nonempty() -> void:
|
||||
var t: String = NarratorScript.phase_text(1, "", false, false, "")
|
||||
Kit.check("相位叙述非空(实际「%s」)" % t, t != "")
|
||||
var boss: String = NarratorScript.phase_text(2, "", false, true, "灰矮人石君王")
|
||||
Kit.check("Boss 在场时叙述带入 Boss 名(实际「%s」)" % boss, boss.contains("灰矮人石君王"))
|
||||
@@ -0,0 +1,47 @@
|
||||
# tests/unit/presentation/hud_presenter_test.gd — HUD 投影直接单测(迭代 29 T5 补洞:该模块此前零指名)
|
||||
# 断言面 = 纯函数投影:动作结果文案按拒绝原因逐类落字(玩家看到的每句提示都由这里出)、
|
||||
# 未知原因有兜底文案(不吐空串)、棋盘变异行投影随表走。期望值由注入内容数出来。
|
||||
# 运行:godot --headless --path game --script res://tests/unit/presentation/hud_presenter_test.gd
|
||||
extends SceneTree
|
||||
|
||||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
const HudScript := preload("res://presentation/hud_presenter.gd")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_test_accepted_text()
|
||||
_test_reject_text_by_reason()
|
||||
_test_unknown_reason_fallback()
|
||||
quit(Kit.finish("hud_presenter_test"))
|
||||
|
||||
|
||||
func _test_accepted_text() -> void:
|
||||
var t: String = HudScript.outcome_text({"accepted": true})
|
||||
Kit.check("受理态文案固定为「执行完成。」(实际「%s」)" % t, t == "执行完成。")
|
||||
|
||||
|
||||
func _test_reject_text_by_reason() -> void:
|
||||
var cases := {
|
||||
"not_adjacent": "不能执行:目标与小人不相邻",
|
||||
"target_revealed": "不能执行:目标已开",
|
||||
"flag_present": "不能插旗:此格已有旗(旗为一次性承诺——相邻时左键直接挖开)",
|
||||
}
|
||||
var bad: Array[String] = []
|
||||
for sub in cases:
|
||||
var got: String = HudScript.outcome_text({
|
||||
"accepted": false, "reject_reason": "board_face", "sub_reason": sub,
|
||||
})
|
||||
if got != String(cases[sub]):
|
||||
bad.append("%s→「%s」" % [sub, got])
|
||||
Kit.check("棋盘面各拒绝原因文案逐类正确(错 %d 项 %s)" % [bad.size(), str(bad)], bad.is_empty())
|
||||
var ap: String = HudScript.outcome_text({"accepted": false, "reject_reason": "resource_face"})
|
||||
Kit.check("资源面拒绝文案为「AP 不足。」(实际「%s」)" % ap, ap == "AP 不足。")
|
||||
|
||||
|
||||
func _test_unknown_reason_fallback() -> void:
|
||||
var t: String = HudScript.outcome_text({"accepted": false, "reject_reason": "brand_new_reason"})
|
||||
Kit.check("未知拒绝原因兜底为「动作被拒。」而非空串(实际「%s」)" % t, t == "动作被拒。")
|
||||
var sub: String = HudScript.outcome_text({
|
||||
"accepted": false, "reject_reason": "board_face", "sub_reason": "brand_new_sub",
|
||||
})
|
||||
Kit.check("棋盘面未知子因兜底带前缀可诊断(实际「%s」)" % sub, sub.begins_with("棋盘面拒绝: "))
|
||||
Reference in new issue
Block a user