extends SceneTree const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd") ## Runs the real UI command handlers, a normal clock and disk save. Invoke again ## with -- --read and the same THE_TREE_UPKEEP_UI_SAVE for fresh-process restore. var checks := 0 var failures := 0 var main: Control var runtime: Variant var clock: Variant func _initialize() -> void: call_deferred("_run") func expect(ok: bool, message: String) -> void: checks += 1 if not ok: failures += 1 push_error(message) func _run() -> void: runtime = root.get_node("SimulationRuntime") clock = root.get_node("GameClock") var state: Variant = root.get_node("GameState") clock.reset(1, 6, 0) state.reset() runtime.initialize(clock, state, 424242) clock.set_paused(true) root.size = Vector2i(1440, 900) main = load("res://scenes/main/main.tscn").instantiate() var path := OS.get_environment("THE_TREE_UPKEEP_UI_SAVE") if path.is_empty(): path = "user://tests/upkeep_closed_%d/manual" % OS.get_process_id() main.set("manual_save_path", path) root.add_child(main) await process_frame runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) if "--read" in OS.get_cmdline_user_args(): _press_text("読込") expect(clock.absolute_minute == 1440, "fresh-process UI load restores midnight") else: (main.get("build_buttons")[&"reception"] as Button).button_pressed = true main.call("_on_map_cell_clicked", Vector3i(-6, 0, 0)) expect(runtime.construction_service.get_jobs().size() == 1, "palette and map handler create one construction job") clock.advance_minutes(120) var rooms: Array = runtime.facility_service.get_instances() expect(rooms.size() == 1, "normal clock finishes selected room") if rooms.is_empty(): _finish() return var panel: BusinessManagementPanel = main.get("business_management_panel") panel.selected_facility_id = rooms[0].instance_id panel.refresh() panel.business_open_button.button_pressed = false panel.apply_button.pressed.emit() expect(not rooms[0].operator_open, "real business controls close room while paused") expect("維持費" in panel.explanation_label.text, "business controls explain continued upkeep") clock.advance_minutes(959) expect(runtime.economy_service.get_balance(FacilityUpkeepSettlement.EXPENSE) == 0, "23:59 is not settled early") _press_text("保存") var before: Dictionary = runtime.create_snapshot() _press_text("読込") # Save/load deliberately emit distinct audit events and measured durations. # Compare authoritative clock, ownership and money rather than those events. var after: Dictionary = runtime.create_snapshot() expect(after[&"clock"] == before[&"clock"], "UI disk load restores exact pre-midnight clock") for field: StringName in [&"economy", &"facilities", &"facility_upkeep", &"upkeep_settlement"]: expect(after[&"systems"][&"runtime"][field] == before[&"systems"][&"runtime"][field], "UI disk load preserves " + String(field)) clock.advance_minutes(1) _verify_closed() await _test_ledger_skin() if "--read" not in OS.get_cmdline_user_args(): _press_text("保存") await _capture() _finish() func _test_ledger_skin() -> void: main.call("_set_layer", 0) main.call("_set_ledger_panel_open", true) var ledger: OperatingLedgerPanel = main.get("operating_ledger_panel") var report := ledger.get_report() var cash_label: Label = ledger.get("_cash_label") var closed_label: Label = ledger.get("_closed_label") var before_text := closed_label.text var before_color := cash_label.get_theme_color("font_color") var economy: Dictionary = runtime.economy_service.create_snapshot() main.call("_set_layer", 1) ledger.refresh() expect(cash_label.get_theme_color("font_color") == Color("d8eeea") and closed_label.get_theme_color("font_color") == Color("d8eeea"), "BACK ledger cash and close remain readable after refresh") expect(ledger.get_report() == report and closed_label.text == before_text, "skin preserves selected close and every displayed amount") expect(runtime.economy_service.create_snapshot() == economy, "ledger skin does not post transactions") if DisplayServer.get_name() != "headless": await process_frame;await RenderingServer.frame_post_draw var phase := "read" if "--read" in OS.get_cmdline_user_args() else "write" expect(root.get_texture().get_image().save_png(ProjectSettings.globalize_path("res://artifacts/facility-dimensions-2026-09-29/ledger-skin-back-%s.png" % phase)) == OK, "capture ledger BACK skin") main.call("_set_layer", 0) expect(cash_label.get_theme_color("font_color") == before_color and ledger.get_report() == report, "FRONT restoration retains ledger data") func _verify_closed() -> void: var rooms: Array = runtime.facility_service.get_instances() expect(rooms.size() == 1, "saved room remains present") if rooms.is_empty(): return var room: FacilityInstance = rooms[0] expect(not room.operator_open, "closed policy survives midnight and restore") # 08:00 completion to 00:00 = 960 minutes at the production 3G/day. expect(runtime.economy_service.get_balance(FacilityUpkeepSettlement.EXPENSE) == 2, "closed room incurs 3 * 960 / 1440 = 2G") expect(runtime.upkeep_settlement.get_facility_balance(room.instance_id)[&"fraction_units"] == 0, "whole-gold settlement leaves no fractional remainder") expect(runtime.upkeep_settlement.get_arrears_total() == 0, "upkeep is paid with no arrears") main.get("world_preview").facility_selected.emit(room.instance_id) expect("3G / 日" in (main.get("selected_detail_label") as Label).text, "inspector uses production daily rate") var midnight: Dictionary = runtime.create_snapshot() var count: int = runtime.upkeep_settlement.get_settled_period_count() clock.advance_minutes(1) expect(runtime.upkeep_settlement.get_settled_period_count() == count, "next minute never repeats midnight settlement") # Restore exact midnight for the independent read phase. expect(runtime.restore_snapshot(midnight), "restore verified midnight for disk handoff") func _press_text(text: String) -> void: if text == "読込": var menu := main.find_child("SaveLoadMenu", true, false) as MenuButton expect(menu != null and not menu.disabled, "normal load menu is available") if menu != null and not menu.disabled: menu.get_popup().id_pressed.emit(0) return for button: Node in main.find_children("*", "Button", true, false): if button.text == text: button.pressed.emit() return expect(false, "UI button found: " + text) func _capture() -> void: if DisplayServer.get_name() == "headless": return for frame in 6: await process_frame await RenderingServer.frame_post_draw var suffix := "read" if "--read" in OS.get_cmdline_user_args() else "write" expect(root.get_texture().get_image().save_png("res://artifacts/upkeep-2026-09-29/closed-ui-%s.png" % suffix) == OK, "native rendered evidence saved") func _finish() -> void: 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 print("%s: %d checks (phase %s)" % ["PASS" if failures == 0 else "FAIL", checks, "read" if "--read" in OS.get_cmdline_user_args() else "write"]) quit(failures)