extends SceneTree const VerticalSliceRoot = preload( "res://game/composition/vertical_slice/vertical_slice_root.gd" ) const MainMenuScreen = preload("res://game/presentation/main_menu/main_menu_screen.gd") const CockpitHud = preload("res://game/presentation/cockpit/cockpit_hud.gd") const CockpitDetailing = preload( "res://game/presentation/cockpit/cockpit_detailing.gd" ) const CockpitPressureBulkhead = preload( "res://game/presentation/cockpit/cockpit_pressure_bulkhead.gd" ) const PauseMenu = preload("res://game/presentation/pause/pause_menu.gd") const ControlsSettingsScreen = preload( "res://game/presentation/settings/controls_settings_screen.gd" ) const ProductionInputAdapter = preload( "res://game/infrastructure/input/production_input_adapter.gd" ) const TEST_COUNT := 64 var _failures: Array[String] = [] func _init() -> void: _run.call_deferred() func _run() -> void: change_scene_to_file( "res://game/composition/vertical_slice/vertical_slice_root.tscn" ) await process_frame await physics_frame var composition := current_scene as VerticalSliceRoot _expect(composition != null, "production composition loads") if composition != null: await _test_vehicle_slice(composition) _expect( int(ProjectSettings.get_setting("physics/common/physics_ticks_per_second")) == 60, "project physics runs at 60 Hz" ) var project_features: PackedStringArray = ProjectSettings.get_setting( "application/config/features" ) _expect("Forward Plus" in project_features, "production project uses Forward+") var exit_code := 0 if _failures.is_empty(): print("Vertical slice foundation integration: %d passed, 0 failed." % TEST_COUNT) else: for failure: String in _failures: push_error(failure) print("Vertical slice foundation integration: %d failed." % _failures.size()) exit_code = 1 Input.action_release(ProductionInputAdapter.ACTION_FORWARD) Input.action_release(ProductionInputAdapter.ACTION_RIGHT) if current_scene != null: current_scene.queue_free() await process_frame await process_frame quit(exit_code) func _test_vehicle_slice(composition: VerticalSliceRoot) -> void: var menu := composition.active_screen() as MainMenuScreen _expect(menu != null, "composition starts on main menu") if menu == null: return var continue_button := menu.get_node("%ContinueButton") as Button _expect(continue_button.disabled, "continue is disabled without save") var new_game_button := menu.get_node("%NewGameButton") as Button new_game_button.pressed.emit() await process_frame await physics_frame var actor = composition.submarine_actor() var camera = composition.camera_controller() var instruments = composition.cockpit_instrument_panel() var detailing: CockpitDetailing = ( instruments.detailing() if instruments != null else null ) var pressure_bulkhead := ( instruments.get_node_or_null("%CurvedPressureBulkhead") as CockpitPressureBulkhead if instruments != null else null ) var analog_gauges := ( instruments.get_node_or_null("Dashboard/AnalogGauges") if instruments != null else null ) var adapter := composition.input_adapter() as ProductionInputAdapter var hud := composition.active_screen() as CockpitHud var sonar_service = composition.sonar_service() var sonar_display = composition.sonar_display() _expect(composition.active_world() != null, "vehicle test world is assembled") _expect(actor != null, "production submarine actor is assembled") _expect(camera != null, "cockpit camera is attached to the submarine") _expect(instruments != null, "physical cockpit instruments are assembled") _expect(detailing != null, "cockpit structural detailing is assembled") _expect(pressure_bulkhead != null, "curved pressure bulkhead is assembled") _expect(analog_gauges != null, "dashboard analog instruments are assembled") _expect(hud != null, "critical HUD overlay is assembled") _expect(sonar_service != null, "production sonar service is assembled") _expect(sonar_display != null, "shared sonar presentation is assembled") _expect(composition.latest_update() != null, "dive session starts with a typed update") if ( actor == null or camera == null or instruments == null or detailing == null or pressure_bulkhead == null or analog_gauges == null or adapter == null or hud == null ): return _expect(instruments.get_node("%WindowFrame") is MeshInstance3D, "cockpit has a physical porthole frame") _expect(instruments.get_node("%Dashboard") is Node3D, "cockpit has a physical dashboard") _expect(instruments.get_node("%SonarScreen") is MeshInstance3D, "cockpit reserves a physical sonar display") _expect(detailing.pressure_hull_rib_count() >= 6, "cockpit exposes reinforced pressure-hull structure") _expect(detailing.pipe_segment_count() >= 13, "cockpit exposes a complete life-support pipe run") _expect(absf(detailing.battery_needle_degrees()) > 100.0, "analog battery gauge follows the dive snapshot") _expect(detailing.textures_are_loaded(), "cockpit loads its overhead and analog gauge textures") _expect(detailing.gauges_are_dashboard_mounted(), "analog gauges are mounted on the dashboard") _expect(detailing.dashboard_control_count() >= 8, "dashboard exposes physical auxiliary controls") _expect(pressure_bulkhead.panel_count() == 12, "porthole bulkhead uses twelve curved pressure panels") _expect(pressure_bulkhead.rectangular_whitebox_is_hidden(), "porthole rectangular whitebox is hidden") _expect(analog_gauges.indicator_count() == 3, "instrument binnacle exposes three inset pilot lights") _expect(analog_gauges.active_indicator_count() == 1, "exactly one pilot light reflects the alert state") _expect("kWh" in instruments.resources_text() and "kW" in instruments.resources_text(), "battery instrument has exact units") _expect("L/min" in instruments.resources_text(), "oxygen instrument has volume and consumption") _expect("bar" in instruments.integrity_text(), "hull instrument has current and certified pressure") _expect(not hud.alert_overlay_visible(), "normal telemetry stays on physical instruments only") _expect( instruments.sonar_texture() == sonar_display.sonar_texture(), "physical and expanded sonar views share one texture" ) adapter.sonar_scan_requested.emit() var first_scan = sonar_service.current_scan() _expect(first_scan != null, "manual sonar command publishes a typed scan") _expect(first_scan.samples.observed_count() > 1000, "reference acquisition captures dense bathymetry") _expect(first_scan.contacts.size() >= 1, "metal calibration target becomes a distinct contact") _expect("RELEVÉ EN COURS" in sonar_display.status_text(), "sonar display reports progressive reveal") for frame in 8: await physics_frame _expect(sonar_display.built_row_count() > 0, "renderer reveals near rows incrementally") adapter.sonar_view_toggle_requested.emit() _expect(sonar_display.is_expanded(), "sonar can expand without pausing the dive") adapter.sonar_view_toggle_requested.emit() _expect(not sonar_display.is_expanded(), "expanded sonar returns to cockpit view") var start_position: Vector3 = actor.global_position var starting_battery: float = composition.latest_update().snapshot.battery.remaining_kwh Input.action_press(ProductionInputAdapter.ACTION_FORWARD) for frame in 35: await physics_frame Input.action_release(ProductionInputAdapter.ACTION_FORWARD) _expect(actor.global_position.distance_to(start_position) > 0.1, "keyboard propulsion moves the submarine") _expect(actor.velocity.length() > 0.1, "propulsion builds inertial velocity") _expect( composition.latest_update().snapshot.battery.remaining_kwh < starting_battery, "propulsion effort drains the domain battery" ) var start_heading: float = actor.rotation.y Input.action_press(ProductionInputAdapter.ACTION_RIGHT) for frame in 24: await physics_frame Input.action_release(ProductionInputAdapter.ACTION_RIGHT) _expect(absf(actor.rotation.y - start_heading) > 0.01, "keyboard steering changes submarine heading") _expect(absf(actor.current_yaw_velocity_rps()) > 0.01, "yaw keeps angular inertia after release") for frame in 90: await physics_frame var settled_heading: float = actor.rotation.y var initial_look_yaw: float = camera.look_yaw_radians() var cockpit_transform: Transform3D = instruments.global_transform var mouse_motion := InputEventMouseMotion.new() mouse_motion.relative = Vector2(40.0, -20.0) adapter._unhandled_input(mouse_motion) await physics_frame _expect(absf(camera.look_yaw_radians() - initial_look_yaw) > 0.01, "mouse rotates the cockpit camera") _expect(absf(actor.rotation.y - settled_heading) < 0.001, "mouse does not steer the submarine") _expect(instruments.global_transform.is_equal_approx(cockpit_transform), "cockpit remains fixed while the player looks around") var paused_sequence: int = composition.latest_update().snapshot.tick_sequence var paused_sonar_progress: float = sonar_service.current_scan().reveal_progress adapter.pause_requested.emit() await process_frame _expect(paused, "pause menu suspends the scene tree") var pause_menu := composition.active_overlay() as PauseMenu _expect(pause_menu != null, "pause overlay is displayed") await process_frame await process_frame _expect( composition.latest_update().snapshot.tick_sequence == paused_sequence, "dive ticks are frozen while paused" ) _expect( is_equal_approx(sonar_service.current_scan().reveal_progress, paused_sonar_progress), "sonar reveal is frozen while paused" ) var controls_button := pause_menu.get_node("%ControlsButton") as Button controls_button.pressed.emit() await process_frame var controls := composition.active_overlay() as ControlsSettingsScreen _expect(controls != null, "controls settings open over the paused dive") _expect(adapter.binding_snapshots().size() == 11, "all gameplay keyboard bindings are exposed") var rebind_results: Array = [] adapter.rebind_result_available.connect( func(result) -> void: rebind_results.append(result) ) adapter.begin_rebind(ProductionInputAdapter.ACTION_FORWARD) adapter._unhandled_input(_pressed_key(KEY_UP)) _expect(not rebind_results.is_empty() and rebind_results.back().accepted, "free key remapping is accepted") _expect( _binding_for(adapter, ProductionInputAdapter.ACTION_FORWARD) == "Up", "binding snapshot reflects the remapped key" ) adapter.begin_rebind(ProductionInputAdapter.ACTION_BACKWARD) adapter._unhandled_input(_pressed_key(KEY_UP)) _expect( not rebind_results.back().accepted and rebind_results.back().conflicting_action_id == ProductionInputAdapter.ACTION_FORWARD, "binding conflict identifies the existing action" ) var swap_result = adapter.resolve_pending_conflict(true) _expect(swap_result.accepted, "conflicting bindings can be swapped") _expect( _binding_for(adapter, ProductionInputAdapter.ACTION_BACKWARD) == "Up", "requested action receives the conflicting key" ) adapter.restore_default_bindings() _expect( _binding_for(adapter, ProductionInputAdapter.ACTION_FORWARD) == "W", "default bindings can be restored" ) adapter.pause_requested.emit() await process_frame _expect(composition.active_overlay() is PauseMenu, "escape from settings returns to pause") adapter.pause_requested.emit() await process_frame _expect(not paused, "second escape resumes the dive") for frame in 3: await physics_frame if composition.latest_update().snapshot.tick_sequence > paused_sequence: break _expect( composition.latest_update().snapshot.tick_sequence > paused_sequence, "fixed dive ticks resume after pause" ) func _pressed_key(keycode: Key) -> InputEventKey: var event := InputEventKey.new() event.physical_keycode = keycode event.pressed = true return event func _binding_for(adapter: ProductionInputAdapter, action_id: StringName) -> String: for binding in adapter.binding_snapshots(): if binding.action_id == action_id: return binding.binding_text return "" func _expect(condition: bool, label: String) -> void: if not condition: _failures.append(label)