// // PreferenceTests.swift // // Copyright 2026 Brendan Szymanski // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // SPDX-License-Identifier: GPL-3.0-or-later // import Foundation @_spi(SGTKInternal) import Gtk import LuminateCore @_spi(Portico) import Portico import Testing @testable import LuminateUI /// Test view that renders the current server URL preference. private struct PreferenceLabel: View { @Preference(.serverURL) private var serverURL: URL? var body: some View { Label(str: "").label { serverURL?.absoluteString ?? "No server configured" } } } /// Exercises preference mounting, observation, and persistence wiring. @Suite(.serialized) @MainActor struct PreferenceTests { @Test("An unset preference reads nil at mount") func unsetAtMount() { guard Gtk.initCheck() else { return } let preferences = Preferences(store: EphemeralPreferenceStore()) let context = MountContext() let label = AnyView(PreferenceLabel().environment(\.preferences, preferences)) .makeWidget(context) as! Gtk.Label defer { context.registry.teardown() } #expect(label.getText() == "No server configured") } @Test("An un-injected preference resolves through the shared coordinator") func resolvesWithoutInjection() { guard Gtk.initCheck() else { return } Preferences.shared[.serverURL] = URL(string: "http://shared.test") defer { Preferences.shared[.serverURL] = nil } let context = MountContext() let label = AnyView(PreferenceLabel()).makeWidget(context) as! Gtk.Label defer { context.registry.teardown() } #expect(label.getText() == "http://shared.test") } @Test("Writing a preference updates the same mounted label") func liveUpdate() { guard Gtk.initCheck() else { return } let preferences = Preferences(store: EphemeralPreferenceStore()) let context = MountContext() let label = AnyView(PreferenceLabel().environment(\.preferences, preferences)) .makeWidget(context) as! Gtk.Label defer { context.registry.teardown() } let pointer = label.pointer preferences.slot(for: .serverURL).value = URL(string: "http://jellyfin.test:8096") pump { label.getText() == "http://jellyfin.test:8096" } #expect(label.pointer == pointer) #expect(label.getText() == "http://jellyfin.test:8096") } @Test("A preference write reaches the in-memory backend after flush") func persistsToBackend() async throws { let store = EphemeralPreferenceStore() let preferences = Preferences(store: store) preferences.slot(for: .serverURL).value = URL(string: "http://jellyfin.test:8096") try await store.flush() #expect(await store.storedValues["server.url"] == .text("http://jellyfin.test:8096")) } @Test("A seeded store presents its value at mount") func restoresAtMount() { guard Gtk.initCheck() else { return } let store = EphemeralPreferenceStore( initialSnapshot: ["server.url": .text("http://seeded.test")] ) let preferences = Preferences(store: store) let context = MountContext() let label = AnyView(PreferenceLabel().environment(\.preferences, preferences)) .makeWidget(context) as! Gtk.Label defer { context.registry.teardown() } #expect(label.getText() == "http://seeded.test") } @Test("Setting a preference to nil enqueues a deletion") func deletesFromBackend() async throws { let store = EphemeralPreferenceStore( initialSnapshot: ["server.url": .text("http://seeded.test")] ) let preferences = Preferences(store: store) preferences.slot(for: .serverURL).value = nil try await store.flush() #expect(await store.storedValues["server.url"] == nil) } private func pump(until condition: () -> Bool = { false }, turns: Int = 200) { for _ in 0.. Int32