extends SceneTree const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd") var checks:=0 var failures:=0 func expect(ok: bool, message: String) -> void: checks+=1 if not ok: failures+=1;push_error(message) func _initialize() -> void: call_deferred("run") func run() -> void: var runtime: Variant=root.get_node("SimulationRuntime") var clock: Variant=root.get_node("GameClock") var state: Variant=root.get_node("GameState") clock.reset(1,6,0);state.reset();runtime.initialize(clock,state,42507);clock.set_paused(true) var paths: Array[String]=FacilityCatalog.FACILITY_PATHS+FacilityCatalog.INFRASTRUCTURE_FACILITY_PATHS+FacilityCatalog.RESCUE_FACILITY_PATHS+FacilityCatalog.ORIGINAL_FACILITY_PATHS paths.erase("res://data/facilities/expansion/premium_room.tres");paths.append("res://tests/fixtures/legacy_premium_room.tres") expect(runtime.definition_registry.rebuild_from_paths(paths)[&"valid"],"load legacy fixture definition") expect(runtime.submit_command(&"build_facility",{&"definition_id":&"reception",&"origin":Vector3i(-6,0,0)}).is_success(),"build fixture reception") clock.advance_minutes(120) expect(runtime.submit_command(&"build_facility",{&"definition_id":&"premium_room",&"origin":Vector3i(-6,2,0)}).is_success(),"build old-size room") clock.advance_minutes(150) paths.erase("res://tests/fixtures/legacy_premium_room.tres");paths.append("res://data/facilities/expansion/premium_room.tres");runtime.definition_registry.rebuild_from_paths(paths) var source: Dictionary=runtime.create_snapshot() var path := "user://saves/migration_ui_%d" % Time.get_ticks_usec() expect(SaveService.save_snapshot(path,source)[&"ok"],"persist disposable old-size game") var main: Control=load("res://scenes/main/main.tscn").instantiate() main.set("manual_save_path",path);root.size=Vector2i(1440,810);root.add_child(main) await process_frame;await process_frame var live: Dictionary=runtime.create_snapshot() var menu: MenuButton=main.find_child("SaveLoadMenu",true,false) expect(menu!=null and menu.get_popup().get_item_count()==3,"load menu exposes ordinary and dimension-migration actions") menu.get_popup().id_pressed.emit(1) var dialog: ConfirmationDialog=main.get("dimension_migration_dialog") expect(dialog!=null and dialog.visible and "上級客室" in dialog.dialog_text and "4×1" in dialog.dialog_text and "未保存" in dialog.dialog_text,"review shows concrete change and unsaved-game consequence") expect(runtime.create_snapshot()==live and SaveService.load_snapshot(path)[&"snapshot"]==source,"review leaves live and disk unchanged") if OS.get_environment("THE_TREE_MIGRATION_CAPTURE")=="1": await process_frame;await RenderingServer.frame_post_draw expect(root.get_texture().get_image().save_png(ProjectSettings.globalize_path("res://artifacts/facility-dimensions-2026-09-29/migration-dialog.png"))==OK,"capture migration review UI") dialog.canceled.emit();dialog.hide() expect(String(main.get("_dimension_migration_digest")).is_empty(),"cancel clears pending migration token") menu.get_popup().id_pressed.emit(1) var changed: Dictionary=source.duplicate(true);changed[&"clock"][&"speed"]=2 SaveService.save_snapshot(path,changed) dialog.confirmed.emit();dialog.hide() expect(runtime.create_snapshot()==live and SaveService.load_snapshot(path)[&"snapshot"]==changed,"changed save rejects confirmation without loading or overwriting") SaveService.save_snapshot(path,source);menu.get_popup().id_pressed.emit(1);dialog.confirmed.emit();dialog.hide() var found:=false for room: FacilityInstance in runtime.facility_service.get_instances(): if room.definition_id==&"premium_room": found=room.occupied_cells.size()==4 expect(found and "修正して読み込みました" in (main.get("build_status_label") as Label).text,"confirmation migrates and loads corrected room through UI") expect(SaveService.load_snapshot(path+SaveService.MIGRATION_BACKUP_SUFFIX)[&"snapshot"]==source,"UI migration retains exact original backup") menu.get_popup().id_pressed.emit(1) expect("旧寸法の施設はありません" in (main.get("build_status_label") as Label).text,"already-current save reports no migration needed") var _audio_cleanup_final: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main) if not bool(_audio_cleanup_final.get(&"passed", false)): push_error("Ordinary fixture audio cleanup failed: %s" % str(_audio_cleanup_final)) quit(96) return await process_frame SaveService.delete_save(path);SaveService.delete_save(path+SaveService.MIGRATION_BACKUP_SUFFIX) print("Dimension migration UI: %d checks, %d failures" % [checks,failures]);quit(failures)