extends SceneTree const IO := preload("res://scripts/stage_scene_io.gd") const STAGE_SCENE := preload("res://scenes/sandbox_stage.tscn") const FIXTURE_PATH := "user://cli_fixture.scene" var _checks := 0 var _failures := 0 func _initialize() -> void: call_deferred("_run") func _run() -> void: _cleanup() _test_parser() await _test_load_into_edit() await _test_play_mode() await _test_missing_scene_error() await _test_play_without_scene() await _test_recovery_skipped() _cleanup() print("----------------------------------------") if _failures == 0: print("RESULT: ALL PASSED (%d assertions)" % _checks) quit(0) else: print("RESULT: %d FAILURE(S) out of %d assertions" % [_failures, _checks]) quit(1) func _expect(condition: bool, message: String) -> void: _checks += 1 if condition: print("PASS: " + message) else: _failures += 1 push_error("CLI scene test failed: " + message) func _cleanup() -> void: if FileAccess.file_exists(FIXTURE_PATH): DirAccess.remove_absolute(FIXTURE_PATH) if FileAccess.file_exists("user://sandbox_autosave.scene"): DirAccess.remove_absolute("user://sandbox_autosave.scene") func _write_fixture() -> void: var fixture := { "version": "1.0", "scene_name": "CLI Fixture", "camera": {"position": {"x": 0.0, "y": -400.0}, "zoom": 0.5}, "objects": [{ "file_id": "terrain_1", "type": "terrain", "position": {"x": 20.0, "y": 30.0}, "rotation": 0.0, "asset_id": "ground", "scale": {"x": 1.0, "y": 1.0}, }], "event_rules": [], "metadata": {}, } IO.write_file_safe(FIXTURE_PATH, fixture) func _make_stage() -> Node2D: var stage: Node2D = STAGE_SCENE.instantiate() root.add_child(stage) return stage func _test_parser() -> void: var empty: Dictionary = CliSceneLauncher.parse([]) _expect(String(empty["scene_path"]) == "" and not bool(empty["play"]) and String(empty["error"]) == "", "empty args parse clean") var full: Dictionary = CliSceneLauncher.parse(["--play", "--scene", "res://x.scene"]) _expect(String(full["scene_path"]) == "res://x.scene" and bool(full["play"]) and String(full["error"]) == "", "--play --scene sets both") var eq: Dictionary = CliSceneLauncher.parse(["--scene=res://x.scene"]) _expect(String(eq["scene_path"]) == "res://x.scene" and not bool(eq["play"]), "--scene= form sets path") var positional: Dictionary = CliSceneLauncher.parse(["/abs/x.scene", "--play"]) _expect(String(positional["scene_path"]) == "/abs/x.scene" and bool(positional["play"]), "bare positional plus --play") var play_only: Dictionary = CliSceneLauncher.parse(["--play"]) _expect(not String(play_only["error"]).is_empty(), "--play alone is an error") var missing_value: Dictionary = CliSceneLauncher.parse(["--scene"]) _expect(not String(missing_value["error"]).is_empty(), "--scene without value is an error") var unknown: Dictionary = CliSceneLauncher.parse(["--bogus"]) _expect(not String(unknown["error"]).is_empty(), "unknown option is an error") var two_pos: Dictionary = CliSceneLauncher.parse(["a.scene", "b.scene"]) _expect(not String(two_pos["error"]).is_empty(), "two positionals are an error") var flag_and_pos: Dictionary = CliSceneLauncher.parse(["--scene", "a.scene", "b.scene"]) _expect(not String(flag_and_pos["error"]).is_empty(), "--scene plus positional is an error") func _test_load_into_edit() -> void: _write_fixture() var stage: Node2D = _make_stage() await process_frame stage._handle_cli_startup(["--scene", FIXTURE_PATH]) _expect(stage._current_scene_path == FIXTURE_PATH, "load adopts the scene path") _expect(stage._world.get_child_count() == 1, "load reconstructs one object") _expect(not stage._dirty, "load clears dirty") _expect(stage._recent_scenes.size() > 0 and stage._recent_scenes[0] == FIXTURE_PATH, "load updates Open Recent") _expect("cli_fixture.scene" in stage.get_window().title, "title shows the loaded scene file") _expect(not FileAccess.file_exists(stage.AUTOSAVE_PATH), "clean load leaves no autosave") stage.queue_free() await process_frame func _test_play_mode() -> void: _write_fixture() var stage: Node2D = _make_stage() await process_frame stage._handle_cli_startup(["--play", "--scene", FIXTURE_PATH]) _expect(stage.current_mode == stage.StageMode.PLAY, "--play enters PLAY mode") _expect(not FileAccess.file_exists(stage.AUTOSAVE_PATH), "CLI PLAY suppresses the autosave write") stage.queue_free() await process_frame func _test_missing_scene_error() -> void: var stage: Node2D = _make_stage() await process_frame stage._handle_cli_startup(["--scene", "user://definitely_missing_cli.scene"]) _expect(stage._cli_error_dialog != null and stage._cli_error_dialog.visible, "missing scene shows the error dialog") _expect(not stage._cli_error_dialog.dialog_text.is_empty(), "missing scene error has dialog text") _expect(stage._world.get_child_count() == 0, "missing scene leaves a blank stage") _expect(stage.current_mode == stage.StageMode.EDIT, "missing scene stays in EDIT") stage._cli_error_dialog.hide() stage.queue_free() await process_frame func _test_play_without_scene() -> void: var stage: Node2D = _make_stage() await process_frame stage._handle_cli_startup(["--play"]) _expect(stage._cli_error_dialog != null and stage._cli_error_dialog.visible, "--play without scene shows the error dialog") _expect(stage._cli_error_dialog.dialog_text == "--play requires --scene .", "--play error text matches the parser contract") _expect(stage.current_mode == stage.StageMode.EDIT, "--play without scene never enters PLAY") stage._cli_error_dialog.hide() stage.queue_free() await process_frame func _test_recovery_skipped() -> void: var stage: Node2D = _make_stage() await process_frame _write_fixture() var autosave := { "version": "1.0", "scene_name": "Stale Autosave", "objects": [], "event_rules": [], "metadata": {"autosave": true, "source_path": "", "modified_unix": Time.get_unix_time_from_system()}, } IO.write_file_safe(stage.AUTOSAVE_PATH, autosave) _expect(FileAccess.file_exists(stage.AUTOSAVE_PATH), "autosave fixture pre-written") stage._handle_cli_startup(["--scene", FIXTURE_PATH]) _expect(not FileAccess.file_exists(stage.AUTOSAVE_PATH), "explicit --scene retires the autosave") var ui: Node = stage.get_node(NodePath("UI")) _expect(ui.get_node_or_null(NodePath("AutosaveRecoveryDialog")) == null, "no recovery dialog is created for explicit --scene") _expect(stage._current_scene_path == FIXTURE_PATH, "explicit --scene loads the requested scene") stage.queue_free() await process_frame