Files
aetherbound-guild/runtime/tests/p9_2/horizontal_battle_runtime_test.gd
T

172 lines
8.0 KiB
GDScript

extends SceneTree
const MainScene = preload("res://main.tscn")
var failures: Array[String] = []
var game: Control
var viewport: SubViewport
var capture_root := ""
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
capture_root = OS.get_environment("ABG_P9_2_RUNTIME_CAPTURE_ROOT")
viewport = SubViewport.new()
viewport.size = Vector2i(844, 390)
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
root.add_child(viewport)
game = MainScene.instantiate()
game.auto_boot = false
game.authoritative_auto_advance = false
game.audio_enabled = false
viewport.add_child(game)
await settle(8)
_prepare_p9_1_result()
game.render()
await settle(5)
var continue_button: Control = find_control(game, "ContinueAuthoritativeBattleButton")
_expect(continue_button != null and continue_button.is_visible_in_tree(), "P9.1 result exposes the P9.2 continuation")
if continue_button == null:
_finish()
return
continue_button.pressed.emit()
await settle(4)
_expect(game.authoritative_battle_active, "continuation enters authoritative battle")
if not game.authoritative_battle_active or game.authoritative_battle_presenter.root == null:
_finish()
return
_expect(not game.facade.visible, "legacy facade is hidden during P9.2")
_assert_embedded_chinese_font()
var initial: Dictionary = game.authoritative_battle_service.state.current_snapshot()
_expect(initial.members.ids == ["Hearthguard", "Farstring", "Kindhand"], "P9.1 field order imports")
_expect(initial.members.hp == {"Hearthguard": 72, "Farstring": 46, "Kindhand": 85}, "P9.1 end HP imports")
_assert_runtime_binding("initial")
await capture("01-entry")
var initial_lead_x: int = logical_position("P92Member_Hearthguard")
game.advance_authoritative_ticks(10)
await settle(3)
_expect(logical_position("P92Member_Hearthguard") > initial_lead_x, "Guild line visibly advances from domain position")
_assert_runtime_binding("advance")
await capture("02-advance")
game.advance_authoritative_ticks(14)
await settle(3)
_expect(has_event("contact"), "domain reaches contact")
_assert_runtime_binding("contact")
await capture("03-contact")
game.advance_authoritative_ticks(18)
await settle(3)
_expect(has_event("member_action"), "member profession event reaches runtime")
_expect(has_event("enemy_action"), "enemy event reaches runtime")
_expect(event_ribbon_bound(), "event ribbon retains source-bound event identity")
_assert_runtime_binding("actions")
await capture("04-actions")
for _index in 220:
if game.authoritative_battle_service.state.terminal:
break
game.advance_authoritative_ticks(1)
_expect(game.authoritative_battle_service.state.outcome == "victory", "imported three-member line reaches deterministic victory")
_assert_runtime_binding("victory")
await capture("05-victory")
_verify_bounds(game.authoritative_battle_presenter.root)
_finish()
func _finish() -> void:
if failures.is_empty():
print("ABG_P9_2_HORIZONTAL_BATTLE_RUNTIME_OK viewport=844x390 source=p9_1_result members=3 hp_import=true movement=true contact=true member_events=true enemy_events=true outcome=victory domain_bound=true embedded_font=true")
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func _prepare_p9_1_result() -> void:
game.locale = "zh_CN"
game.muted = true
game.facade.session_state.field_order.assign(["Hearthguard", "Farstring", "Kindhand"])
game.facade.session_state.starting_pair.assign(["Hearthguard", "Farstring"])
game.facade.session_state.front_choice = "Hearthguard"
game.facade.session_state.rear_choice = "Farstring"
game.facade.session_state.added_rear = "Kindhand"
game.facade.session_state.member_hp = {"Hearthguard": 72, "Farstring": 46, "Kindhand": 85}
game.facade.session_state.member_levels = {"Hearthguard": 2, "Farstring": 1, "Kindhand": 1}
game.page = game.AppPage.THIRD_CYCLE_RESULT
func _assert_embedded_chinese_font() -> void:
var battle_root: Control = game.authoritative_battle_presenter.root
var embedded_font: Font = game.facade.UI_FONT
_expect(game.theme != null, "outer runtime root owns the embedded UI theme")
_expect(game.theme == game.facade.theme, "outer runtime inherits the facade UI theme")
_expect(game.theme.default_font == embedded_font, "outer runtime binds the embedded Noto Sans SC font")
_expect(battle_root.get_theme_default_font() == embedded_font, "P9.2 tree inherits the embedded font")
_expect(embedded_font.has_char("裂".unicode_at(0)), "embedded font contains the P9.2 Chinese title glyph")
var title: Label = find_control(battle_root, "P92BattleTitle")
_expect(title != null and title.get_theme_default_font() == embedded_font, "P9.2 title resolves the embedded font")
func _assert_runtime_binding(label: String) -> void:
var snapshot: Dictionary = game.authoritative_battle_service.state.current_snapshot()
var battle_root: Control = game.authoritative_battle_presenter.root
_expect(battle_root != null and battle_root.name == "P92BattleRoot", "%s renders P9.2 root" % label)
_expect(int(battle_root.get_meta("domain_tick", -1)) == int(snapshot.tick), "%s root tick matches domain" % label)
_expect(int(battle_root.get_meta("state_hash", 0)) == game.authoritative_battle_service.state.state_hash(), "%s root hash matches domain" % label)
for member_id in snapshot.members.ids:
var actor: Control = find_control(battle_root, "P92Member_" + String(member_id))
_expect(actor != null, "%s renders member %s" % [label, member_id])
if actor != null:
_expect(int(actor.get_meta("logical_position", -999)) == int(snapshot.members.positions[member_id]), "%s %s position bound" % [label, member_id])
_expect(int(actor.get_meta("hp", -1)) == int(snapshot.members.hp[member_id]), "%s %s HP bound" % [label, member_id])
for enemy_id in snapshot.enemies.ids:
var actor: Control = find_control(battle_root, "P92Enemy_" + String(enemy_id))
_expect(actor != null, "%s renders enemy %s" % [label, enemy_id])
if actor != null:
_expect(int(actor.get_meta("logical_position", -999)) == int(snapshot.enemies.positions[enemy_id]), "%s %s position bound" % [label, enemy_id])
_expect(int(actor.get_meta("hp", -1)) == int(snapshot.enemies.hp[enemy_id]), "%s %s HP bound" % [label, enemy_id])
func logical_position(node_name: String) -> int:
var actor: Control = find_control(game.authoritative_battle_presenter.root, node_name)
return int(actor.get_meta("logical_position", -999)) if actor != null else -999
func has_event(kind: String) -> bool:
for event in game.authoritative_battle_service.state.event_log:
if String(event.get("kind", "")) == kind:
return true
return false
func event_ribbon_bound() -> bool:
var ribbon: Control = find_control(game.authoritative_battle_presenter.root, "P92EventRibbon")
return ribbon != null and int(ribbon.get_meta("event_index", -1)) >= 0 and not String(ribbon.get_meta("event_kind", "")).is_empty()
func _verify_bounds(node: Node) -> void:
if node is Control and node.is_visible_in_tree():
var rect: Rect2 = node.get_global_rect()
if rect.position.x < -0.5 or rect.position.y < -0.5 or rect.end.x > 844.5 or rect.end.y > 390.5:
failures.append("control %s outside 844x390: %s" % [node.name, rect])
for child in node.get_children():
_verify_bounds(child)
func capture(name: String) -> void:
if capture_root.is_empty():
return
DirAccess.make_dir_recursive_absolute(capture_root)
await settle(2)
await RenderingServer.frame_post_draw
var image := viewport.get_texture().get_image()
var error := image.save_png(capture_root.path_join(name + ".png"))
_expect(error == OK, "capture %s succeeds" % name)
func find_control(node: Node, node_name: String) -> Control:
if node.name == node_name and node is Control and not node.is_queued_for_deletion():
return node
for child in node.get_children():
var found: Control = find_control(child, node_name)
if found != null: return found
return null
func settle(frames: int) -> void:
for _frame in frames:
await process_frame
func _expect(condition: bool, message: String) -> void:
if not condition and failures.size() < 100:
failures.append("P9.2 horizontal runtime: %s" % message)