Files
gplan-2/game/tests/unit/presentation/modal_flow_test.gd
T
DKChan 0529e1691c fix(iter37①): T2 呈现/组合根缺陷修——蓄力档分档·庆祝句柄·互斥四口·模态 fail-fast·站桩按 id 解析
- d2 蓄力档 `_intent_charge` 布尔分档(颜色相等判据退役,深浅金同值不再误判);d4 庆祝浮动 tween 句柄 kill 纪律(close 必杀)
- d5/d6/d7/d8 互斥四口:调试钮判据并入 `_sync_sim_button` 单点、组合根两守卫补模拟态/冒险态、`is_running()` 运行态查询面
- d9 ModalStack open/close fail-fast+遮挡账合并非覆盖(重复 open 不再吞旧账);d12 target_id 产发(damage_controller 四处)+站桩两口 id 先名兜底;d17 await 后实例守卫;d18 参数类型注解
- 断言:standee_fx_sweep_test(新增,18 项)/modal_flow_test V6-5(5 项 wd9,V6-1~4 原样同绿)/damage_controller_test target_id/pacing_queue_test 排播 target_id/system_test wd6+wd7
- 代拍板:d18 卡片字面 `lumas_out: Array[Variant] = null` 在 Godot 4.7 解析报错(typed array 不可默认 null),改用 `Variant = null`——唯一合法注法,零行为变化,随代拍板追认单留痕
2026-10-25 03:49:05 +08:00

140 lines
7.4 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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"))