extends SceneTree ## Headless smoke test for Studio simulation harness, playtest transition, ## avatar spawn, and clean edit mode restoration (Task f77bf388e8b4). var _failures: int = 0 var _checks: int = 0 const StudioSimulationManagerClass = preload("res://scripts/studio/studio_simulation_manager.gd") const ObjectMotionComponentClass = preload("res://scripts/components/object_motion_component.gd") func _init() -> void: print("--- Running Smoke Test: Studio Simulation Harness & Playtest ---") test_playtest_lifecycle() test_state_rollback_on_stop() if _failures == 0: print("--- PASS: All %d studio playtest checks passed! ---" % _checks) quit(0) else: printerr("--- FAIL: %d failures out of %d checks ---" % [_failures, _checks]) quit(1) func _check(desc: String, cond: bool) -> void: _checks += 1 if cond: print(" ✓ %s" % desc) else: _failures += 1 printerr(" ✗ FAIL: %s" % desc) func _make_mock_core() -> EditorCore: var payload := ChunkDataDecoder.ChunkPayload.new() var layer := ChunkDataDecoder.TileLayerData.new() layer.name = "ground" layer.layer_id = 0 layer.width = 16 layer.height = 16 layer.depth = 1 layer.tiles = PackedInt32Array() layer.tiles.resize(16 * 16) payload.tile_layers.append(layer) var model := ChunkEditorModel.new(payload) var catalog := EditorObjectCatalog.new() catalog.load_catalog() return EditorCore.new(model, catalog) func test_playtest_lifecycle() -> void: print("Testing Simulation transition (Edit -> Play -> Stop)...") var core := _make_mock_core() var world := Node3D.new() root.add_child(world) var sim := StudioSimulationManagerClass.new() world.add_child(sim) sim.setup(core, world) _check("Initial state is EDIT mode", sim.is_editing()) _check("is_playing returns false", not sim.is_playing()) # Start Play sim.start_play(true) _check("State transitioned to PLAYING", sim.is_playing()) _check("Test avatar spawned in world", sim._spawned_avatar != null) # Stop Play sim.stop_simulation() _check("State restored to EDIT mode", sim.is_editing()) _check("Test avatar despawned cleanly", sim._spawned_avatar == null) world.queue_free() func test_state_rollback_on_stop() -> void: print("Testing world object transform snapshot and clean rollback...") var core := _make_mock_core() var world := Node3D.new() root.add_child(world) # Place an initial object at (5, 5, 0) var obj_id := core.place_object("crate_small", Vector3(5, 5, 0)) _check("Placed initial crate at (5, 5, 0)", obj_id != "") var sim := StudioSimulationManagerClass.new() world.add_child(sim) sim.setup(core, world) # Start Play (takes snapshot of (5, 5, 0)) sim.start_play(false) # Simulate movement during gameplay (e.g. physics push or script motion to (20, 30, 10)) var obj = core.model.find_object(obj_id) obj.position = Vector3(20, 30, 10) _check("Object moved during gameplay to (20, 30, 10)", obj.position.is_equal_approx(Vector3(20, 30, 10))) # Stop Play -> triggers snapshot rollback sim.stop_simulation() _check("Object restored exactly to pre-play position (5, 5, 0)", obj.position.is_equal_approx(Vector3(5, 5, 0))) world.queue_free()