172 lines
8.0 KiB
GDScript
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)
|