# 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 flow.register(end_ov) # d9 fail-fast 后:未登记层不可进入编排(夹具按 register 先行契约登记) 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() flow.register(other) 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〕") # V6-5 遮挡失败快+遮挡账合而非覆盖(d9) var ms = ModalStackScript.new() var ma := Control.new() var mb := Control.new() var stranger := Control.new() stranger.visible = false # Control 默认 visible=true——先隐,才能断言 fail-fast 未把它照上屏 ms.register(ma) ms.register(mb) ms.open(ma) ms.open(stranger) # 未登记层:push_error + 早退(零副作用) Kit.check_quiet(not stranger.visible and ma.visible, "V6 未登记层 open fail-fast(该层不可见、已开层不变)〔wd9〕") ms.close(stranger) Kit.check_quiet(not stranger.visible and ma.visible, "V6 未登记层 close fail-fast(已开层不受影响)〔wd9〕") ms.open(mb) ms.open(ma) # 开低层隐其上方可见层 Kit.check_quiet(not mb.visible, "V6 开低层隐其上方层(重复 open 前态)〔wd9〕") ms.open(ma) # 同层重复 open:上方已隐,hidden 空表——旧账须保留 Kit.check_quiet(not mb.visible, "V6 重复 open 保持遮挡(V6 在役语义不变)〔wd9〕") ms.close(ma) Kit.check_quiet(mb.visible, "V6 重复 open 不吞遮挡账(close 低层恢复 mb——合并非覆盖)〔wd9〕") ms.close(mb) quit(Kit.finish("modal_flow"))