extends SceneTree const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd") var _finishing := false # Artifact-only UI contract probe. This is a fresh-scene check in one process, # not paired-process gameplay restore acceptance. Keep all proof files. var _checks := 0 var _failures := 0 var _runtime: Variant var _clock: Variant var _main: Control var _staff: StaffManagementPanel var _product: SupplyManagementPanel var _audio: Variant var _cues: Array[StringName] = [] var _output := "user://tests/ui_presentation_contract_%d_%d" % [OS.get_process_id(), Time.get_ticks_usec()] func _initialize() -> void: call_deferred("_run") func _expect(ok: bool, message: String) -> void: _checks += 1 if not ok: _failures += 1 push_error(message) func _raw(name: String, value: Variant) -> void: var f := FileAccess.open(_output.path_join(name + ".var"), FileAccess.WRITE) _expect(f != null, "retain complete typed proof " + name) if f != null: f.store_buffer(var_to_bytes(value)) var j := FileAccess.open(_output.path_join(name + ".json"), FileAccess.WRITE) if j != null: j.store_string(JSON.stringify(value, "\t")) func _mount() -> void: _main = load("res://scenes/main/main.tscn").instantiate() as Control _main.set("audio_settings_path", _output.path_join("audio-settings.cfg")) root.add_child(_main) _runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) await process_frame await process_frame _audio = _main.get("_audio_feedback") _expect(_audio != null, "combined scene owns presentation audio") _audio.playback_enabled = false _audio.cue_requested.connect(_record_cue) _staff = _main.get("staff_management_panel") as StaffManagementPanel _product = _main.get("supply_management_panel") as SupplyManagementPanel _expect(_staff != null and _product != null, "actual normal scene has both panels") func _ui() -> Dictionary: var order: Array[String] = [] for child: Node in _staff.roster_list.get_children(): order.append(String(child.name)) return {&"selection": _main.call("_capture_interface_selection"), &"product_visible": _product.visible, &"staff_visible": _staff.visible, &"roster_children": order, &"staff_selected": _staff.selected_staff_id, &"tab": _product.selected_tab} func _assert_roster(context: String) -> void: var expected: Array[String] = [] for row: Dictionary in _runtime.get_staff_management_report()[&"staff"]: expected.append("HiredStaff_%s" % row[&"staff_id"]) var actual: Array[String] = _ui()[&"roster_children"] _expect(actual == expected, context + " matches authoritative lexical report order") _raw(context + "-roster", {&"expected": expected, &"actual": actual}) func _pure_restore(selection: Variant, context: String) -> void: _cues.clear() var before: Dictionary = _runtime.create_snapshot() _raw(context + "-before", before) _main.call("_apply_loaded_interface", {&"manifest": {"interface_selection": selection}}) var after: Dictionary = _runtime.create_snapshot() _raw(context + "-after", after) _expect(var_to_bytes(before) == var_to_bytes(after), context + " keeps every gameplay field and event byte") _expect(_cues.is_empty(), context + " presentation restoration never replays click, outcome or foreground audio") _raw(context + "-ui", _ui()) func _run() -> void: root.gui_embed_subwindows = true root.size = Vector2i(1280, 720) _runtime = root.get_node("SimulationRuntime") _clock = root.get_node("GameClock") _clock.reset(1, 6, 0) root.get_node("GameState").reset() _runtime.initialize(_clock, root.get_node("GameState"), 420603) _clock.set_paused(true) _expect(DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(_output)) == OK, "create isolated retained fixture directory") await _mount() if _staff == null or _product == null: _finish() return # Deliberately differ from report lexical order: Eri then Akane. for id: StringName in [&"staff_eri", &"staff_akane"]: (_main.get("staff_candidate_buttons")[id] as Button).pressed.emit() _expect(_runtime.staff_service.has_staff(id), "normal paid candidate button hires " + String(id)) _staff.refresh() _assert_roster("existing-hire-order") _staff.select_staff(&"staff_eri") for tab: StringName in [&"purchase", &"kitchen", &"delivery"]: (_main.get("navigation_buttons")[&"product"] as Button).pressed.emit() (_product.tab_buttons[tab] as Button).pressed.emit() var before: Dictionary = _runtime.create_snapshot() var selection: Dictionary = _main.call("_capture_interface_selection") _expect(var_to_bytes(before) == var_to_bytes(_runtime.create_snapshot()), "capture " + String(tab) + " is read-only including every event") _expect(selection.get(&"product_open") is bool and selection.get(&"product_open") and selection.get(&"product_tab") == String(tab), "capture real visible tab " + String(tab)) var slot := _output.path_join(String(tab)) var written: Dictionary = SaveService.save_snapshot(slot, before, {&"interface_selection": selection}) _expect(written.get(&"ok", false), "formal package stores optional presentation " + String(tab)) var inspected: Dictionary = SaveService.inspect_snapshot(slot) _expect(inspected.get(&"ok", false) and inspected.get(&"read_only", false), "read-only formal inspection " + String(tab)) _expect(var_to_bytes(before) == var_to_bytes(_runtime.create_snapshot()), "SaveService direct I/O does not mutate Runtime " + String(tab)) _raw(String(tab) + "-manifest", inspected.get(&"manifest", {})) (_main.get("navigation_buttons")[&"staff"] as Button).pressed.emit() _pure_restore(inspected[&"manifest"].get("interface_selection"), String(tab)) _expect(_product.visible and _product.selected_tab == tab, "success UI entry restores Product + tab " + String(tab)) _expect(not _staff.visible and _staff.selected_staff_id == &"staff_eri", "Product restoration retains hidden selected employee " + String(tab)) # Fresh Main builds roster children from report, with no old Button history. var saved: Dictionary = _main.call("_capture_interface_selection") var remount_cleanup: Dictionary = await _cleanup_fixture_owner(_main, "before-remount") if not bool(remount_cleanup.get(&"passed", false)): _failures += 1 push_error("ordinary Main remount audio cleanup failed") await _mount() _assert_roster("fresh-scene") _pure_restore(saved, "fresh-scene-product") _expect(_product.visible and _product.selected_tab == &"delivery", "fresh scene restores saved delivery page") _expect(_staff.selected_staff_id == &"staff_eri", "fresh scene restores saved employee rather than first roster row") (_main.get("navigation_buttons")[&"staff"] as Button).pressed.emit() var hidden: Dictionary = _main.call("_capture_interface_selection") _pure_restore(hidden, "hidden-product") _expect(not _product.visible and _product.selected_tab == &"delivery" and _staff.visible, "hidden Product tab restores without replacing Staff") # Missing legacy fields and malformed new optional fields are no-ops for # Product, and must never turn presentation errors into gameplay rejection. for row: Array in [ ["legacy", {"version": 1, "staff_open": true}], ["missing", {}], ["wrong-version", {"version": 2, "product_open": true, "product_tab": "delivery"}], ["bool-version", {"version": true, "product_open": true, "product_tab": "delivery"}], ["open-string", {"version": 1, "product_open": "true", "product_tab": "delivery"}], ["tab-array", {"version": 1, "product_open": true, "product_tab": ["delivery"]}], ["unknown-tab", {"version": 1, "product_open": true, "product_tab": "unknown"}], ["conflicting-open", {"version": 1, "staff_open": true, "product_open": true, "product_tab": "delivery"}], ]: var ui_before := _ui() var runtime_before: Dictionary = _runtime.create_snapshot() _main.call("_restore_product_interface_selection", {"interface_selection": row[1]}) _expect(var_to_bytes(ui_before) == var_to_bytes(_ui()), String(row[0]) + " optional metadata leaves whole captured UI unchanged") _expect(var_to_bytes(runtime_before) == var_to_bytes(_runtime.create_snapshot()), String(row[0]) + " optional metadata leaves whole Runtime unchanged") _raw(String(row[0]) + "-optional-proof", {&"before": ui_before, &"after": _ui(), &"metadata": row[1]}) _cues.clear() var intact_ui := _ui() var intact_runtime: Dictionary = _runtime.create_snapshot() _main.call("_on_save_operation_completed", {&"ok": false, &"operation": &"load", &"manifest": {"interface_selection": saved}}) _expect(var_to_bytes(intact_ui) == var_to_bytes(_ui()) and var_to_bytes(intact_runtime) == var_to_bytes(_runtime.create_snapshot()), "failure notification never applies saved presentation or gameplay") _expect(_cues.is_empty(), "failure load notification does not fabricate sound") _raw("failure-notification-ui", _ui()) _finish() func _finish() -> void: if _finishing: return _finishing = true if is_instance_valid(_main): var final_cleanup: Dictionary = await _cleanup_fixture_owner(_main, "final") if not bool(final_cleanup.get(&"passed", false)): _failures += 1 push_error("ordinary Main final audio cleanup failed") var result_file := FileAccess.open(_output.path_join("result.var"), FileAccess.WRITE) _expect(result_file != null, "retain final completion receipt") var result := {&"checks": _checks, &"failures": _failures, &"pid": OS.get_process_id(), &"user_directory": OS.get_user_data_dir(), &"proof_directory": ProjectSettings.globalize_path(_output), &"fresh_process_acceptance": false, &"normal_runtime_load_tested": false} if result_file != null: result_file.store_buffer(var_to_bytes(result)) var result_json := FileAccess.open(_output.path_join("result.json"), FileAccess.WRITE) if result_json != null: result_json.store_string(JSON.stringify(result, "\t")) print("UI presentation audio contract: %d checks, %d failures" % [_checks, _failures]) quit(0 if _failures == 0 else 1) func _record_cue(role: StringName, _correlation: Dictionary) -> void: _cues.append(role) func _cleanup_fixture_owner(owner: Node, phase: String) -> Dictionary: var original_control: String = OS.get_environment("THE_TREE_AUDIO_FIXTURE_CONTROL_PROOF") if original_control.is_empty() or not original_control.is_absolute_path(): return {&"passed": false, &"reason": "fresh absolute owned control proof required"} var phase_control: String = original_control.get_basename() + "-" + phase + ".json" if FileAccess.file_exists(phase_control): return {&"passed": false, &"reason": "ordinary owner control proof overwrite forbidden"} OS.set_environment("THE_TREE_AUDIO_FIXTURE_CONTROL_PROOF", phase_control) var report: Dictionary = await FixtureCleanup.free_owner_and_wait(self, owner) OS.set_environment("THE_TREE_AUDIO_FIXTURE_CONTROL_PROOF", original_control) return report