140 lines
6.2 KiB
GDScript
140 lines
6.2 KiB
GDScript
extends SceneTree
|
|
|
|
const Adapter = preload("res://campaign/p100_adapter.gd")
|
|
const Controller = preload("res://campaign/fg2_graybox_controller.gd")
|
|
const Fixture = preload("res://campaign/fg2_fixture.gd")
|
|
|
|
const VIEWPORT := Vector2i(844, 390)
|
|
|
|
var failures: Array[String] = []
|
|
var experience
|
|
var parsed_touches := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run_test")
|
|
|
|
|
|
func run_test() -> void:
|
|
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
|
|
await verify_audible_authoritative_flow()
|
|
await verify_receipt_bound_recovery()
|
|
await verify_muted_simulation_identity()
|
|
if is_instance_valid(experience):
|
|
experience.queue_free()
|
|
await settle(60)
|
|
if failures.is_empty():
|
|
print("ABG_FG2_AUDIO_RUNTIME_OK route_receipt=commit wave=forecast damage=impact+guard fallen=casualty outcome=victory recovery=receipt-bound music=loop ambience=loop event_hash=exact mute_result=exact low_power_voices=3 touch=%d fallback=0 direct=0" % parsed_touches)
|
|
quit(0)
|
|
else:
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func verify_audible_authoritative_flow() -> void:
|
|
await mount(Fixture.canonical_market_state(), false, false)
|
|
var expected = Adapter.new()
|
|
expect(bool(expected.configure(Fixture.canonical_market_state()).ok), "expected adapter configures")
|
|
var route_id := Fixture.route_id(expected.snapshot(), 0)
|
|
expect(bool(expected.start_route(route_id).ok), "expected route commits")
|
|
var expected_terminal: Dictionary = expected.run_battle_to_terminal()
|
|
expect(bool(expected_terminal.get("ok", false)), "expected battle reaches terminal")
|
|
await tap("FG2DepartButton")
|
|
expect(experience.screen == Controller.SCREEN_BATTLE, "parsed route commit enters battle")
|
|
expect(int(experience.audio_router.snapshot().asset_play_counts.get("FG2-SFX-COMMIT", 0)) == 1, "route receipt plays one commit")
|
|
var guard := 0
|
|
while not experience.battle_result_visible and guard < 2200:
|
|
experience.advance_battle_ticks(1)
|
|
experience.audio_router.advance(0.10)
|
|
await process_frame
|
|
guard += 1
|
|
expect(guard < 2200 and String(experience.view.battle.outcome) == "victory", "audible canonical battle reaches victory")
|
|
var state: Dictionary = experience.audio_router.snapshot()
|
|
for kind in ["route_committed", "wave_arrived", "damage", "fallen", "outcome"]:
|
|
expect(int(state.event_counts.get(kind, 0)) > 0, "%s reaches audio router" % kind)
|
|
for asset_id in ["FG2-SFX-COMMIT", "FG2-SFX-FORECAST", "FG2-SFX-IMPACT", "FG2-SFX-GUARD", "FG2-SFX-CASUALTY", "FG2-SFX-VICTORY"]:
|
|
expect(int(state.asset_play_counts.get(asset_id, 0)) > 0, "%s plays from the real flow" % asset_id)
|
|
expect(String(experience.view.battle.event_log_hash) == String(expected_terminal.event_log_hash), "audio preserves exact authoritative battle event hash")
|
|
expect(experience.fallback_input_count == 0 and experience.direct_command_count == 0, "audible flow adds no input fallback")
|
|
|
|
|
|
func verify_receipt_bound_recovery() -> void:
|
|
await mount(Fixture.recovery_market_state(), false, false)
|
|
var company_before: int = experience.view.company.size()
|
|
await tap("FG2RecoveryButton")
|
|
var state: Dictionary = experience.audio_router.snapshot()
|
|
expect(experience.screen == Controller.SCREEN_MARKET and experience.view.company.size() == company_before + 1, "successful protected hire changes the Company")
|
|
expect(int(state.event_counts.get("recovery", 0)) == 1, "one successful recovery receipt reaches audio")
|
|
expect(int(state.asset_play_counts.get("FG2-SFX-RECOVERY", 0)) == 1, "successful recovery receipt plays one repair latch")
|
|
|
|
|
|
func verify_muted_simulation_identity() -> void:
|
|
await mount(Fixture.canonical_market_state(), true, true)
|
|
var expected = Adapter.new()
|
|
expect(bool(expected.configure(Fixture.canonical_market_state()).ok), "muted expected adapter configures")
|
|
var route_id := Fixture.route_id(expected.snapshot(), 0)
|
|
expect(bool(expected.start_route(route_id).ok), "muted expected route commits")
|
|
var expected_terminal: Dictionary = expected.run_battle_to_terminal()
|
|
await tap("FG2DepartButton")
|
|
var guard := 0
|
|
while not experience.battle_result_visible and guard < 2200:
|
|
experience.advance_battle_ticks(1)
|
|
experience.audio_router.advance(0.10)
|
|
await process_frame
|
|
guard += 1
|
|
var state: Dictionary = experience.audio_router.snapshot()
|
|
expect(String(experience.view.battle.event_log_hash) == String(expected_terminal.event_log_hash), "mute and low power preserve exact battle event hash")
|
|
expect(state.asset_play_counts.is_empty() and int(state.muted_suppressed) > 0, "mute consumes semantic events without starting assets")
|
|
expect(not bool(state.music_playing) and not bool(state.ambience_playing), "mute keeps continuous beds stopped")
|
|
expect(int(state.active_voice_limit) == 3, "muted low-power runtime keeps the three-voice contract")
|
|
expect(experience.input_touch_count == 1 and experience.fallback_input_count == 0 and experience.direct_command_count == 0, "muted flow still uses parsed touch only")
|
|
|
|
|
|
func mount(snapshot_value: Dictionary, mute_value: bool, power_value: bool) -> void:
|
|
if is_instance_valid(experience):
|
|
experience.queue_free()
|
|
await settle(20)
|
|
experience = Controller.new()
|
|
experience.battle_auto_advance = false
|
|
experience.muted = mute_value
|
|
experience.low_power = power_value
|
|
experience.size = Vector2(VIEWPORT)
|
|
experience.configure_entry(snapshot_value)
|
|
root.add_child(experience)
|
|
await settle(8)
|
|
|
|
|
|
func tap(node_name: String) -> void:
|
|
var control := experience.find_child(node_name, true, false) as Control
|
|
expect(control != null, "%s exists" % node_name)
|
|
if control == null:
|
|
return
|
|
var point := root.get_final_transform() * control.get_global_rect().get_center()
|
|
var down := InputEventScreenTouch.new()
|
|
down.index = 0
|
|
down.position = point
|
|
down.pressed = true
|
|
Input.parse_input_event(down)
|
|
parsed_touches += 1
|
|
await process_frame
|
|
var up := InputEventScreenTouch.new()
|
|
up.index = 0
|
|
up.position = point
|
|
up.pressed = false
|
|
Input.parse_input_event(up)
|
|
await settle(4)
|
|
|
|
|
|
func settle(frames: int) -> void:
|
|
for _index in range(frames):
|
|
await process_frame
|
|
|
|
|
|
func expect(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append("FG-2 audio runtime: %s" % message)
|