185 lines
8.0 KiB
GDScript
185 lines
8.0 KiB
GDScript
extends SceneTree
|
|
|
|
var failures: Array[String] = []
|
|
|
|
|
|
func _init() -> void:
|
|
_run.call_deferred()
|
|
|
|
|
|
func _run() -> void:
|
|
var ui_scene: PackedScene = load("res://scenes/ui/main_ui.tscn")
|
|
var player_scene: PackedScene = load("res://scenes/characters/player.tscn")
|
|
if ui_scene == null or player_scene == null:
|
|
failures.append("Debug UI test scenes should load")
|
|
_finish()
|
|
return
|
|
|
|
var ui := ui_scene.instantiate()
|
|
var player := player_scene.instantiate()
|
|
root.add_child(player)
|
|
root.add_child(ui)
|
|
await process_frame
|
|
|
|
for node_path: String in [
|
|
"DebugPanel",
|
|
"DebugPanel/StateAxesLabel",
|
|
"DebugPanel/ActionLabel",
|
|
"DebugPanel/AnchorLabel",
|
|
"DebugPanel/EffectsLabel",
|
|
"DebugPanel/DefenseLabel",
|
|
"DebugPanel/PatternsLabel",
|
|
"DebugPanel/HitLogLabel",
|
|
"DebugPanel/CalibrationLabel",
|
|
]:
|
|
_expect(ui.has_node(node_path), "MainUI should expose %s" % node_path)
|
|
|
|
if ui.has_method("bind_debug_actor"):
|
|
ui.call("bind_debug_actor", player)
|
|
else:
|
|
failures.append("MainUI should expose bind_debug_actor(actor)")
|
|
|
|
var state_machine: Node = player.get_node("StateMachine")
|
|
state_machine.call("set_ground_state", &"Airborne")
|
|
state_machine.call("set_action_phase", &"Startup")
|
|
state_machine.call("set_life_state", &"Hitstun")
|
|
state_machine.call("set_defense_state", &"Parrying")
|
|
|
|
var action_controller: Node = player.get_node("ActionController")
|
|
action_controller.set("current_action", load("res://resources/actions/ground_attack_left_1.tres"))
|
|
action_controller.set("startup_stretch_seconds", 0.125)
|
|
|
|
var effect_container: Node = player.get_node("EffectContainer")
|
|
if _method_arg_count(effect_container, "add_effect") >= 2:
|
|
effect_container.call("add_effect", load("res://resources/effects/effect_root.tres"), &"debug_test")
|
|
effect_container.call("add_effect", load("res://resources/effects/effect_temporary_invincible.tres"), &"debug_defense")
|
|
else:
|
|
failures.append("EffectContainer.add_effect should accept a source argument for debug UI")
|
|
effect_container.call("add_effect", load("res://resources/effects/effect_root.tres"))
|
|
|
|
if ui.has_method("refresh_debug_panel"):
|
|
ui.call("refresh_debug_panel")
|
|
else:
|
|
failures.append("MainUI should expose refresh_debug_panel()")
|
|
|
|
_expect_label_contains(ui, "DebugPanel/StateAxesLabel", "Airborne", "Debug state axes should include GroundState")
|
|
_expect_label_contains(ui, "DebugPanel/StateAxesLabel", "Startup", "Debug state axes should include ActionPhaseState")
|
|
_expect_label_contains(ui, "DebugPanel/StateAxesLabel", "Hitstun", "Debug state axes should include LifeState")
|
|
_expect_label_contains(ui, "DebugPanel/StateAxesLabel", "Parrying", "Debug state axes should include DefenseState")
|
|
_expect_label_contains(ui, "DebugPanel/ActionLabel", "ground_attack_left_1", "Debug action label should show current action id")
|
|
_expect_label_contains(ui, "DebugPanel/AnchorLabel", "0.125", "Debug anchor label should show Startup stretch seconds")
|
|
_expect_label_contains(ui, "DebugPanel/EffectsLabel", "effect_root", "Debug effects label should show active effect ids")
|
|
_expect_label_contains(ui, "DebugPanel/EffectsLabel", "1.5s", "Debug effects label should show active effect remaining seconds")
|
|
_expect_label_contains(ui, "DebugPanel/EffectsLabel", "x1", "Debug effects label should show active effect stacks")
|
|
_expect_label_contains(ui, "DebugPanel/EffectsLabel", "debug_test", "Debug effects label should show active effect source")
|
|
_expect_label_contains(ui, "DebugPanel/DefenseLabel", "Baseline", "Debug defense label should identify baseline defense")
|
|
_expect_label_contains(ui, "DebugPanel/DefenseLabel", "Invincible", "Debug defense label should show synthesized defense state")
|
|
_expect_label_contains(ui, "DebugPanel/DefenseLabel", "damage 0.00", "Debug defense label should show baseline damage multiplier")
|
|
_expect_label_contains(ui, "DebugPanel/DefenseLabel", "interrupt false", "Debug defense label should show baseline interrupt result")
|
|
_expect_label_contains(ui, "DebugPanel/PatternsLabel", "A->ground_attack_left_1", "Debug patterns label should show exported legal patterns")
|
|
_expect_label_contains(ui, "DebugPanel/PatternsLabel", "S->block_start", "Debug patterns label should show standalone S block")
|
|
_expect_label_not_contains(ui, "DebugPanel/PatternsLabel", "SS->", "Debug patterns label should not show removed S S combo")
|
|
_expect_label_contains(ui, "DebugPanel/CalibrationLabel", "Diff", "Debug calibration label should show rhythm diff calibration data")
|
|
|
|
var bus := root.get_node_or_null("EventBus")
|
|
if bus != null and bus.has_signal("hit_confirmed"):
|
|
bus.emit_signal("hit_confirmed", {
|
|
"damage": 7,
|
|
"hit_type": &"melee",
|
|
"action": load("res://resources/actions/ground_attack_left_1.tres"),
|
|
"defense": {"defense_state": &"Parrying"},
|
|
"interrupts": false,
|
|
})
|
|
else:
|
|
failures.append("EventBus should expose hit_confirmed(result) for debug hit logs")
|
|
_expect_label_contains(ui, "DebugPanel/HitLogLabel", "ground_attack_left_1", "Debug hit log should show hit action id")
|
|
_expect_label_contains(ui, "DebugPanel/HitLogLabel", "dmg 7", "Debug hit log should show resolved damage")
|
|
_expect_label_contains(ui, "DebugPanel/HitLogLabel", "Parrying", "Debug hit log should show effective defense")
|
|
_expect_label_contains(ui, "DebugPanel/HitLogLabel", "interrupt false", "Debug hit log should show interrupt result")
|
|
if bus != null and bus.has_signal("hit_confirmed"):
|
|
for index: int in range(4):
|
|
bus.emit_signal("hit_confirmed", {
|
|
"damage": index + 1,
|
|
"hit_type": &"melee",
|
|
"action": load("res://resources/actions/ground_attack_left_1.tres"),
|
|
"defense": {"defense_state": &"Vulnerable"},
|
|
"interrupts": true,
|
|
})
|
|
if ui.has_method("get_hit_log_entries"):
|
|
var entries: Array = ui.call("get_hit_log_entries")
|
|
_expect_int(entries.size(), 5, "Debug UI should retain full hit history beyond the compact label")
|
|
else:
|
|
failures.append("MainUI should expose get_hit_log_entries()")
|
|
if ui.has_method("export_hit_log"):
|
|
_expect(ui.call("export_hit_log").contains("ground_attack_left_1"), "Debug UI should export hit history text")
|
|
else:
|
|
failures.append("MainUI should expose export_hit_log()")
|
|
|
|
ui.free()
|
|
player.free()
|
|
await _check_main_scene_binds_debug_actor()
|
|
_finish()
|
|
|
|
|
|
func _expect_label_contains(root_node: Node, path: String, text: String, label: String) -> void:
|
|
var node := root_node.get_node_or_null(path) as Label
|
|
if node == null:
|
|
failures.append("%s: missing %s" % [label, path])
|
|
return
|
|
if not node.text.contains(text):
|
|
failures.append("%s: expected '%s' to contain '%s'" % [label, node.text, text])
|
|
|
|
|
|
func _expect_label_not_contains(root_node: Node, path: String, text: String, label: String) -> void:
|
|
var node := root_node.get_node_or_null(path) as Label
|
|
if node == null:
|
|
failures.append("%s: missing %s" % [label, path])
|
|
return
|
|
if node.text.contains(text):
|
|
failures.append("%s: expected '%s' not to contain '%s'" % [label, node.text, text])
|
|
|
|
|
|
func _method_arg_count(object: Object, method_name: String) -> int:
|
|
for method: Dictionary in object.get_method_list():
|
|
if str(method.get("name", "")) != method_name:
|
|
continue
|
|
var args = method.get("args", [])
|
|
return args.size() if args is Array else 0
|
|
return 0
|
|
|
|
|
|
func _check_main_scene_binds_debug_actor() -> void:
|
|
var main_scene: PackedScene = load("res://scenes/main/main.tscn")
|
|
if main_scene == null:
|
|
failures.append("main.tscn should load")
|
|
return
|
|
var main := main_scene.instantiate()
|
|
root.add_child(main)
|
|
await process_frame
|
|
var player: Node = main.call("get_player")
|
|
var ui: Node = main.get_node("UI")
|
|
player.get_node("StateMachine").call("set_action_phase", &"Startup")
|
|
ui.call("refresh_debug_panel")
|
|
_expect_label_contains(ui, "DebugPanel/StateAxesLabel", "Startup", "Main should bind Player to UI DebugPanel")
|
|
main.free()
|
|
|
|
|
|
func _expect(condition: bool, label: String) -> void:
|
|
if not condition:
|
|
failures.append(label)
|
|
|
|
|
|
func _expect_int(actual: int, expected: int, label: String) -> void:
|
|
if actual != expected:
|
|
failures.append("%s: expected %d, got %d" % [label, expected, actual])
|
|
|
|
|
|
func _finish() -> void:
|
|
if failures.is_empty():
|
|
print("PASS debug ui")
|
|
quit(0)
|
|
else:
|
|
for failure: String in failures:
|
|
push_error(failure)
|
|
quit(1)
|