- CLAUDE.md 硬性红线新增「文件头零工作日志」;code-conventions.md 新增 §1.4.1(含反面案例) - 全仓 .gd 文件头注清偿:删除全部带日期条目、批次叙事段与迁移退役叙述,保留并改写为 现在时的职责、接口契约、GDD 锚内容(main.gd 头注 42→31 行、save_manager.gd 49→45 行) - 纯注释改动零行为变更,按用户令免测试免冒烟
114 lines
6.1 KiB
GDScript
114 lines
6.1 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# 模态编排纯逻辑类单测(ModalFlow 直接 new() 进 run_all;
|
||
# 互斥域谓词/三例外输入锁/演播后排窗挂起/演播完边沿派发/遮挡记账)。
|
||
# 载体零场景零树——纯 RefCounted + 哑 Control。
|
||
# 运行:godot --headless --path game --script res://tests/unit/presentation/modal_flow_test.gd(expect 0)
|
||
extends SceneTree
|
||
|
||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||
const ModalFlowScript := preload("res://presentation/modal_flow.gd") # 探针载体禁全局类名裸引用惯例同源
|
||
const ModalStackScript := preload("res://presentation/modal_stack.gd")
|
||
|
||
|
||
func _init() -> void:
|
||
# 哑层与桩 Callable(纯逻辑类零场景前提的证明面)
|
||
var start := Control.new()
|
||
var inter := Control.new()
|
||
var settlement_holder := {"s": null} # lambda 对局部按值捕获——容器装载
|
||
var rows := {"empty": true}
|
||
var calls := {"flush": 0, "open_sync": 0, "close_sync": 0, "replay": 0}
|
||
var flow = ModalFlowScript.new()
|
||
Kit.check_quiet(flow != null and flow.stack != null, "V6 ModalFlow 直接 new()(stack 内建)〔迭代27 T4〕")
|
||
flow.start_visible_get = func() -> bool: return start.visible
|
||
flow.inter_visible_get = func() -> bool: return inter.visible
|
||
flow.settlement_get = func() -> Variant: return settlement_holder["s"]
|
||
flow.rows_empty_get = func() -> bool: return rows["empty"]
|
||
flow.flush_rows = func() -> void: calls["flush"] += 1
|
||
flow.on_open_sync = func() -> void: calls["open_sync"] += 1
|
||
flow.on_close_sync = func() -> void: calls["close_sync"] += 1
|
||
flow.replay_click = func() -> void: calls["replay"] += 1
|
||
|
||
# V6-1 互斥域登记+遮挡记账
|
||
var a := Control.new() # 普通层
|
||
var dbg := Control.new() # 调试浮窗(non_locking)
|
||
var map := Control.new() # 关卡地图屏(screen_state)
|
||
flow.register(a)
|
||
flow.register(dbg, [ModalStackScript.TAG_NON_LOCKING])
|
||
flow.register(map, [ModalStackScript.TAG_SCREEN_STATE])
|
||
var tag_dump := "tags a=%s dbg=%s map=%s const=%s" % [str(flow.stack._tags.get(a, "MISSING")), str(flow.stack._tags.get(dbg, "MISSING")), str(flow.stack._tags.get(map, "MISSING")), ModalStackScript.TAG_NON_LOCKING]
|
||
print("[DBG] ", tag_dump)
|
||
flow.open(a)
|
||
Kit.check_quiet(a.visible and calls["open_sync"] == 1 and calls["flush"] == 1, "V6 开侧编排(flush→open→sync 单口序)〔迭代27 T4〕")
|
||
# 遮挡记账(登记序=z 序:开低层隐其上方可见层、关闭恢复——低层开启期高层照会不可见的呈现保真)
|
||
flow.open(map)
|
||
Kit.check_quiet(map.visible, "V6 开最高层(上方无可隐)〔迭代27 T4〕")
|
||
flow.open(a)
|
||
Kit.check_quiet(a.visible and not map.visible, "V6 遮挡记账(开低层隐其上方可见层)〔迭代27 T4〕")
|
||
flow.close(a)
|
||
Kit.check_quiet(map.visible and calls["close_sync"] == 1, "V6 关闭恢复(关低层显其上方被隐层+关侧同步)〔迭代27 T4〕")
|
||
|
||
# V6-2 输入锁谓词:互斥域随层登记(non_locking 不计)+ 三例外
|
||
start.visible = false
|
||
inter.visible = false
|
||
settlement_holder["s"] = Object.new()
|
||
flow.open(dbg)
|
||
Kit.check_quiet(not flow.locks_input(), "V6 输入锁:调试浮窗 non_locking 不计(T2 防线)〔迭代27 T4〕")
|
||
flow.close(dbg)
|
||
flow.open(a)
|
||
Kit.check_quiet(flow.locks_input(), "V6 输入锁:普通层开态计锁〔迭代27 T4〕")
|
||
flow.close(a)
|
||
flow.close(map) # 登记册全关(close(a) 经遮挡恢复 map——须一并关账才是「全关」态)
|
||
settlement_holder["s"] = null
|
||
Kit.check_quiet(flow.locks_input(), "V6 输入锁例外三:战斗缺位即锁〔迭代27 T4〕")
|
||
settlement_holder["s"] = Object.new()
|
||
Kit.check_quiet(not flow.locks_input(), "V6 输入锁:战斗在座+登记册全关=不锁〔迭代27 T4〕")
|
||
start.visible = true
|
||
Kit.check_quiet(flow.locks_input(), "V6 输入锁例外一:开始层可见即锁〔迭代27 T4〕")
|
||
start.visible = false
|
||
inter.visible = true
|
||
Kit.check_quiet(flow.locks_input(), "V6 输入锁例外二:过场层可见即锁〔迭代27 T4〕")
|
||
inter.visible = false
|
||
Kit.check_quiet(not flow.locks_input(), "V6 输入锁:全例外解除+登记册全关=不锁〔迭代27 T4〕")
|
||
|
||
# V6-3 角标谓词(状态/调试两屏同款守卫——与旧排除式逐位同值)
|
||
flow.open(dbg)
|
||
Kit.check_quiet(not flow.badge_greyed_battle() and not flow.badge_greyed_map(),
|
||
"V6 角标谓词:浮窗开态两形制均不置灰〔迭代27 T4〕")
|
||
flow.close(dbg)
|
||
flow.open(map)
|
||
Kit.check_quiet(not flow.badge_greyed_map() and flow.badge_greyed_battle(),
|
||
"V6 角标谓词:地图屏 screen_state 地图形制不计/战斗形制计(旧排除式 [debug] 与 [level,debug] 同值面)〔迭代27 T4〕")
|
||
flow.close(map)
|
||
flow.open(a)
|
||
Kit.check_quiet(flow.badge_greyed_battle() and flow.badge_greyed_map(),
|
||
"V6 角标谓词:普通层开态两形制均置灰〔迭代27 T4〕")
|
||
flow.close(a)
|
||
|
||
# V6-4 演播后排窗挂起+边沿派发(T17 语义)
|
||
var end_ov := Control.new()
|
||
end_ov.visible = false
|
||
rows["empty"] = false
|
||
var sync_before1: int = calls["open_sync"]
|
||
flow.open_after_playback(end_ov)
|
||
Kit.check_quiet(flow._deferred == end_ov and not end_ov.visible and calls["open_sync"] == sync_before1,
|
||
"V6 演播后排窗:在播挂起含视觉(不照会上屏——零开侧同步)〔迭代27 T4〕")
|
||
rows["empty"] = true
|
||
var sync_before2: int = calls["open_sync"]
|
||
flow.pump_ended()
|
||
Kit.check_quiet(flow._deferred == null and end_ov.visible and calls["open_sync"] == sync_before2 + 1,
|
||
"V6 演播完边沿:挂起 modal 先开(队列空判定由 rows_empty_get 承载)〔迭代27 T4〕")
|
||
flow.close(end_ov)
|
||
flow.pump_ended()
|
||
Kit.check_quiet(calls["replay"] == 1, "V6 演播完边沿:无挂起→锁期点击重放派发〔迭代27 T4〕")
|
||
# 挂起兜底(他路径开新层先结挂起防永不兑现)
|
||
rows["empty"] = false
|
||
flow.open_after_playback(end_ov)
|
||
rows["empty"] = true
|
||
var other := Control.new()
|
||
var sync_before: int = calls["open_sync"]
|
||
flow.open(other)
|
||
Kit.check_quiet(end_ov.visible and flow._deferred == null and calls["open_sync"] == sync_before + 2,
|
||
"V6 挂起兜底:开新层先结挂起(T17 防永不兑现——挂起层先开+新层后开两拍)〔迭代27 T4〕")
|
||
|
||
quit(Kit.finish("modal_flow"))
|