202 lines
9.2 KiB
GDScript
202 lines
9.2 KiB
GDScript
extends SceneTree
|
|
|
|
const Controller = preload("res://p9_10/runtime_controller.gd")
|
|
const BossService = preload("res://p9_10/boss_service.gd")
|
|
const BossBattleService = preload("res://p9_10/boss_battle_service.gd")
|
|
const Fixture = preload("res://tests/p9_10/runtime_fixture.gd")
|
|
|
|
var failures: Array[String] = []
|
|
var experience
|
|
var capture_root := ""
|
|
var captures := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run_test")
|
|
|
|
|
|
func run_test() -> void:
|
|
capture_root = OS.get_environment("ABG_P9_10_LAYOUT_CAPTURE_ROOT")
|
|
if not capture_root.is_empty():
|
|
DirAccess.make_dir_recursive_absolute(capture_root)
|
|
var market := Fixture.p99_market_four()
|
|
var muster := Fixture.p910_boss_muster()
|
|
await fixture("01-market-small-zh", market, Vector2i(760, 360), "zh_CN", 1.0, false, false)
|
|
await battle_fixture("02-battle-reference-en130", market, Vector2i(844, 390), "en", 1.3, false, false)
|
|
await fixture("03-muster-large-reduced", muster, Vector2i(1280, 720), "zh_CN", 1.0, true, false)
|
|
await boss_battle_fixture("04-boss-battle-low-power", muster, Vector2i(844, 390), "en", 1.0, false, true)
|
|
await fixture("05-boss-recovery-zh130", boss_recovery_state(muster), Vector2i(844, 390), "zh_CN", 1.3, true, false)
|
|
var result := boss_result_state(muster)
|
|
await fixture("06-contract-result-en", result, Vector2i(844, 390), "en", 1.0, false, false)
|
|
var closed = BossService.new()
|
|
expect(closed.restore_snapshot(result), "persistent fixture restores the pending result")
|
|
expect(closed.close_contract("p910-layout-close").ok, "persistent fixture closes exactly once")
|
|
await fixture("07-persistent-rg02-large", closed.state.snapshot(), Vector2i(1280, 720), "zh_CN", 1.0, false, false)
|
|
await fixture("08-market-reference-muted", market, Vector2i(844, 390), "en", 1.0, false, false)
|
|
await fixture("09-portrait-guidance", market, Vector2i(390, 844), "zh_CN", 1.0, false, false, true)
|
|
if is_instance_valid(experience):
|
|
experience.queue_free()
|
|
for _index in 45:
|
|
await process_frame
|
|
if failures.is_empty():
|
|
print("ABG_P9_10_LAYOUT_ACCESSIBILITY_OK fixtures=9 states=market,battle,muster,boss_battle,recovery,contract_result,persistent,muted,rotate viewports=760x360,844x390,1280x720,390x844 locales=zh_CN,en text_130=true reduced_motion=true muted=true low_power=true controls=48 cards=0 captures=%d" % captures)
|
|
quit(0)
|
|
else:
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func fixture(label: String, snapshot: Dictionary, viewport: Vector2i, locale: String, text_scale: float, reduced: bool, low_power: bool, portrait := false) -> void:
|
|
await mount(snapshot, viewport, locale, text_scale, reduced, low_power)
|
|
verify_current(label, viewport, portrait)
|
|
await capture(label)
|
|
|
|
|
|
func battle_fixture(label: String, snapshot: Dictionary, viewport: Vector2i, locale: String, text_scale: float, reduced: bool, low_power: bool) -> void:
|
|
await mount(snapshot, viewport, locale, text_scale, reduced, low_power)
|
|
experience.tap_route("P910-R4-Q0")
|
|
experience.input_guard_until_frame = -1
|
|
experience.tap_route("P910-R4-Q0")
|
|
for _index in 28:
|
|
experience.advance_battle_ticks(1)
|
|
await settle_frames()
|
|
expect(experience.battle_active and experience.find_child("P97BattleActor_P99-D1", true, false) != null, "%s shows automatic Company work" % label)
|
|
verify_current(label, viewport, false)
|
|
verify_battle_names(label)
|
|
await capture(label)
|
|
|
|
|
|
func boss_battle_fixture(label: String, snapshot: Dictionary, viewport: Vector2i, locale: String, text_scale: float, reduced: bool, low_power: bool) -> void:
|
|
await mount(snapshot, viewport, locale, text_scale, reduced, low_power)
|
|
experience.tap_boss_plan("break_spindle")
|
|
experience.input_guard_until_frame = -1
|
|
experience.tap_boss_plan("break_spindle")
|
|
for _index in 72:
|
|
experience.advance_battle_ticks(1)
|
|
await settle_frames()
|
|
expect(experience.boss_battle_active and experience.find_child("P910BossBattleActor", true, false) != null, "%s shows the moving Gale Loom battle" % label)
|
|
expect(int(experience.presented_event_counts.get("boss_rule", 0)) > 0, "%s shows gust/spindle consequences" % label)
|
|
verify_current(label, viewport, false)
|
|
await capture(label)
|
|
|
|
|
|
func mount(snapshot: Dictionary, viewport: Vector2i, locale: String, text_scale: float, reduced: bool, low_power: bool) -> void:
|
|
if is_instance_valid(experience):
|
|
experience.queue_free()
|
|
await settle_frames()
|
|
root.size = viewport
|
|
root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS
|
|
root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND
|
|
root.content_scale_size = viewport
|
|
experience = Controller.new()
|
|
experience.persistence_enabled = false
|
|
experience.battle_auto_advance = false
|
|
experience.locale = locale
|
|
experience.text_scale = text_scale
|
|
experience.reduced_motion = reduced
|
|
experience.muted = true
|
|
experience.low_power = low_power
|
|
experience.size = Vector2(viewport)
|
|
experience.configure_entry(snapshot)
|
|
root.add_child(experience)
|
|
await settle_frames()
|
|
|
|
|
|
func verify_current(label: String, viewport: Vector2i, portrait: bool) -> void:
|
|
var bounds := Rect2(Vector2.ZERO, Vector2(viewport))
|
|
if portrait:
|
|
expect(experience.find_child("P97RotateNotice", true, false) != null, "%s shows landscape guidance" % label)
|
|
return
|
|
expect(experience.find_child("P97RotateNotice", true, false) == null, "%s renders the game world" % label)
|
|
expect(float(experience.get_meta("visible_world_ratio", 0.0)) >= 0.80, "%s keeps a world-first composition" % label)
|
|
expect(experience.find_children("*", "Panel", true, false).is_empty(), "%s adds no app panels" % label)
|
|
for node in experience.find_children("*", "Button", true, false):
|
|
var button := node as Button
|
|
expect(button.size.x >= 48 and button.size.y >= 48, "%s target %s is at least 48px" % [label, button.name])
|
|
expect(bounds.encloses(button.get_global_rect()), "%s target %s stays inside the viewport" % [label, button.name])
|
|
for node in experience.find_children("*", "Control", true, false):
|
|
var lowered := String(node.name).to_lower()
|
|
expect("card" not in lowered and "modal" not in lowered and "dialog" not in lowered, "%s has no card/modal surface" % label)
|
|
|
|
|
|
func verify_battle_names(label: String) -> void:
|
|
var names: Array[Control] = []
|
|
for node in experience.find_children("P97BattleName_*", "Label", true, false):
|
|
var name_label := node as Control
|
|
if name_label != null and name_label.visible:
|
|
names.append(name_label)
|
|
for left_index in names.size():
|
|
for right_index in range(left_index + 1, names.size()):
|
|
var overlap := names[left_index].get_global_rect().intersection(names[right_index].get_global_rect())
|
|
expect(overlap.get_area() <= 1.0, "%s battle labels stay separated" % label)
|
|
|
|
|
|
func boss_recovery_state(muster: Dictionary) -> Dictionary:
|
|
var service = BossService.new()
|
|
expect(service.restore_snapshot(single_survivor_muster(muster)), "recovery fixture restores")
|
|
expect(service.commit_boss_plan("p910-layout-defeat-plan", "hold_line").ok, "recovery fixture commits its plan")
|
|
var battle = BossBattleService.new()
|
|
expect(battle.start_from_contract(service.state).ok, "recovery fixture starts")
|
|
var guard := 0
|
|
while not battle.state.terminal and guard < 1900:
|
|
battle.advance_ticks(1)
|
|
guard += 1
|
|
expect(guard < 1900 and battle.state.outcome == "defeat", "recovery fixture reaches a natural defeat")
|
|
expect(service.settle_boss("p910-layout-defeat-settle", battle.state.snapshot()).ok, "recovery fixture settles")
|
|
return service.state.snapshot()
|
|
|
|
|
|
func boss_result_state(muster: Dictionary) -> Dictionary:
|
|
var service = BossService.new()
|
|
expect(service.restore_snapshot(muster), "result fixture restores")
|
|
expect(service.commit_boss_plan("p910-layout-win-plan", "break_spindle").ok, "result fixture commits its plan")
|
|
var battle = BossBattleService.new()
|
|
expect(battle.start_from_contract(service.state).ok, "result fixture starts")
|
|
var guard := 0
|
|
while not battle.state.terminal and guard < 1900:
|
|
battle.advance_ticks(1)
|
|
guard += 1
|
|
expect(guard < 1900 and battle.state.outcome == "victory", "result fixture reaches victory")
|
|
expect(service.settle_boss("p910-layout-win-settle", battle.state.snapshot()).ok, "result fixture settles")
|
|
return service.state.snapshot()
|
|
|
|
|
|
func single_survivor_muster(muster: Dictionary) -> Dictionary:
|
|
var snapshot := muster.duplicate(true)
|
|
for index in snapshot.deployed_recruit_ids.size():
|
|
var recruit_id: String = snapshot.deployed_recruit_ids[index]
|
|
if index > 0:
|
|
snapshot.member_condition[recruit_id] = 0
|
|
snapshot.boss_muster.member_hp[recruit_id] = 0
|
|
snapshot.coin = maxi(32, int(snapshot.coin))
|
|
snapshot.boss_muster.coin = snapshot.coin
|
|
var authority: Dictionary = snapshot.boss_muster.duplicate(true)
|
|
authority.erase("muster_hash")
|
|
snapshot.boss_muster = authority.duplicate(true)
|
|
snapshot.boss_muster["muster_hash"] = JSON.stringify(authority).sha256_text()
|
|
return snapshot
|
|
|
|
|
|
func capture(label: String) -> void:
|
|
if capture_root.is_empty():
|
|
return
|
|
await RenderingServer.frame_post_draw
|
|
var error := root.get_texture().get_image().save_png(capture_root.path_join(label + ".png"))
|
|
expect(error == OK, "%s capture writes" % label)
|
|
if error == OK:
|
|
captures += 1
|
|
await process_frame
|
|
|
|
|
|
func settle_frames() -> void:
|
|
await process_frame
|
|
await process_frame
|
|
await process_frame
|
|
await process_frame
|
|
|
|
|
|
func expect(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append("P9.10 layout: %s" % message)
|