diff --git a/FrameworkTests/HostIconTests.swift b/FrameworkTests/HostIconTests.swift index 0f3a84be..87dabe40 100644 --- a/FrameworkTests/HostIconTests.swift +++ b/FrameworkTests/HostIconTests.swift @@ -62,7 +62,7 @@ struct HostIconTests { #expect( catalog.entry(matching: try #require(DetectedDistribution(id: "custom", likeIDs: ["debian"])))?.id == "debian") - #expect(catalog.selectableEntries.prefix(2).map(\.id) == ["macos", "windows"]) + #expect(catalog.selectableEntries.map(\.id) == ["bsd", "debian", "linux", "macos", "ubuntu", "windows"]) #expect(Set(catalog.entries.map(\.id)).count == catalog.entries.count) } @@ -75,11 +75,45 @@ struct HostIconTests { {"id":"ubuntu","title":"Ubuntu","asset":"LinuxDistro-ubuntu","ids":["ubuntu"],"hex":"E95420"}, {"id":"debian","title":"Debian","asset":"LinuxDistro-debian","ids":["debian"],"hex":"A81D33"}, {"id":"macos","title":"macOS","asset":"HostOS-macos","ids":["macos"],"hex":"000000"}, - {"id":"windows","title":"Windows","asset":"HostOS-windows","ids":["windows"],"hex":"0078D4"} + {"id":"windows","title":"Windows","asset":"HostOS-windows","ids":["windows"],"hex":"0078D4"}, + {"id":"linux","title":"Linux","asset":"HostOS-linux","ids":["linux"],"hex":"FCC624"}, + {"id":"bsd","title":"BSD","asset":"HostOS-bsd","ids":["bsd","dragonfly"],"hex":"AB2B28"} ]} """.utf8)) } + @Test func genericArtworkDoesNotReplaceExactMatchesOrManualChoices() throws { + let catalog = try fixtureCatalog() + let unknown = try #require(DetectedDistribution(id: "new-linux")) + #expect(catalog.entry(matching: unknown)?.id == "linux") + #expect(catalog.entry(matching: try #require(DetectedDistribution(id: "dragonfly")))?.id == "bsd") + #expect( + catalog.entry( + matching: try #require(DetectedDistribution(id: "custom", likeIDs: ["missing", "debian", "ubuntu"])))? + .id + == "debian") + #expect( + catalog.entry(matching: try #require(DetectedDistribution(id: "ubuntu", likeIDs: ["debian"])))?.id + == "ubuntu") + #expect(catalog.resolvedEntry(overrideID: "server", distribution: unknown) == nil) + #expect(catalog.resolvedEntry(overrideID: "future-icon", distribution: unknown) == nil) + #expect(catalog.resolvedEntry(overrideID: nil, distribution: nil) == nil) + } + + @Test func alphabeticalOrderIgnoresCaseAndUsesStableIDsForTies() throws { + var catalog = try fixtureCatalog() + let template = try #require(catalog.entries.first) + catalog.entries = [("z", "alpha"), ("b", "Beta"), ("a", "ALPHA"), ("w", "Windows")].map { id, title in + var entry = template + entry.id = id + entry.title = title + return entry + } + #expect(catalog.selectableEntries.map(\.id) == ["a", "z", "b", "w"]) + catalog.entries.reverse() + #expect(catalog.selectableEntries.map(\.id) == ["a", "z", "b", "w"]) + } + @Test func legacyDomainJSONDefaultsToAutomaticAndUnknownIDsSurvive() throws { let host = Host(name: "Host", host: "example.invalid") let data = try JSONEncoder().encode(host) @@ -98,17 +132,19 @@ struct HostIconTests { let store = HostPersistence(context: source.mainContext) let remote = HostPersistence(context: destination.mainContext) let library = HostLibraryModel(persistence: store) - var host = Host(name: "Host", host: "example.invalid") + var host = Host( + name: "Host", host: "example.invalid", detectedDistribution: DetectedDistribution(id: "dragonfly")) #expect(library.saveHost(host)) host = try #require(library.host(host.id)) let journal = VaultSyncJournal(context: source.mainContext) - for choice: String? in ["windows", "macos", "future-icon", nil] { + for choice: String? in ["windows", "macos", "freebsd", "bsd", "linux", "future-icon", nil] { host.iconOverrideID = choice #expect(library.saveHost(host)) let envelope = try #require(try journal.envelope(for: .init(kind: .host, id: host.id))) #expect(envelope.formatVersion == SyncRecordFormats.vault.writingVersion(for: "host")) _ = try remote.applyRemote([VaultSyncCodec.decode(VaultSyncCodec.encode(envelope))]) #expect(try remote.load().hosts.first?.iconOverrideID == choice) + #expect(try remote.load().hosts.first?.detectedDistribution?.id == "dragonfly") } } diff --git a/FrameworkTests/RemoteSystemInfoTests.swift b/FrameworkTests/RemoteSystemInfoTests.swift index a960a733..1bc88ab9 100644 --- a/FrameworkTests/RemoteSystemInfoTests.swift +++ b/FrameworkTests/RemoteSystemInfoTests.swift @@ -5,8 +5,9 @@ import Testing struct RemoteSystemInfoTests { @Test func distributionDetectionDistinguishesUnknownLinuxFromANonLinuxHost() throws { #expect(RemoteSystemInfo().distributionDetection == .unavailable) - #expect(RemoteSystemInfo(kernelName: "Linux").distributionDetection == .unavailable) - #expect(RemoteSystemInfo(kernelName: "FreeBSD").distributionDetection == .resolved(nil)) + #expect( + RemoteSystemInfo(kernelName: "Linux").distributionDetection == .resolved(DetectedDistribution(id: "linux"))) + #expect(RemoteSystemInfo(kernelName: "SunOS").distributionDetection == .resolved(nil)) let detected = try #require(DetectedDistribution(id: "Ubuntu", likeIDs: ["Debian"])) #expect( @@ -17,4 +18,15 @@ struct RemoteSystemInfoTests { ).distributionDetection == .resolved(detected) ) } + + @Test(arguments: ["FreeBSD", "OpenBSD", "NetBSD", "DragonFly"]) + func bsdDetectionUsesKernelRatherThanLinuxMetadata(kernel: String) { + for spelling in [kernel, kernel.lowercased(), kernel.uppercased()] { + let info = RemoteSystemInfo(distributionID: "ubuntu", kernelName: spelling) + #expect(info.distributionDetection == .resolved(DetectedDistribution(id: kernel.lowercased()))) + #expect(info.distributionID == "ubuntu") + } + #expect( + RemoteSystemInfo(kernelName: kernel).distributionDetection == .resolved(DetectedDistribution(id: kernel))) + } } diff --git a/FrameworkTests/TailscaleIntegrationTests.swift b/FrameworkTests/TailscaleIntegrationTests.swift index 48d2f37b..54891fac 100644 --- a/FrameworkTests/TailscaleIntegrationTests.swift +++ b/FrameworkTests/TailscaleIntegrationTests.swift @@ -332,9 +332,13 @@ struct TailscaleIntegrationTests { #expect(destinationLibrary.group(group.id)?.tailscale?.exitNodeID == "exit-node-stable-id") #expect(discovered.name == "Renamed") destinationLibrary.recordRemoteSystemInfo(RemoteSystemInfo(kernelName: "Linux"), for: discovered.id) - #expect(destinationLibrary.host(discovered.id)?.detectedDistribution?.id == "ubuntu") + #expect(destinationLibrary.host(discovered.id)?.detectedDistribution?.id == "linux") destinationLibrary.recordRemoteSystemInfo(RemoteSystemInfo(kernelName: "FreeBSD"), for: discovered.id) destinationLibrary.replaceTailscalePeers([peer()], groupID: group.id) + #expect(destinationLibrary.host(discovered.id)?.detectedDistribution?.id == "freebsd") + destinationLibrary.recordRemoteSystemInfo(RemoteSystemInfo(), for: discovered.id) + #expect(destinationLibrary.host(discovered.id)?.detectedDistribution?.id == "freebsd") + destinationLibrary.recordRemoteSystemInfo(RemoteSystemInfo(kernelName: "SunOS"), for: discovered.id) #expect(destinationLibrary.host(discovered.id)?.detectedDistribution == nil) } diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-bsd.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-bsd.imageset/Contents.json new file mode 100644 index 00000000..30f204f8 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-bsd.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "HostOS-bsd.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-bsd.imageset/HostOS-bsd.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-bsd.imageset/HostOS-bsd.svg new file mode 100644 index 00000000..bd6fd8eb --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-bsd.imageset/HostOS-bsd.svg @@ -0,0 +1 @@ +BSD \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-freebsd.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-freebsd.imageset/Contents.json new file mode 100644 index 00000000..f32bf8ff --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-freebsd.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "HostOS-freebsd.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-freebsd.imageset/HostOS-freebsd.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-freebsd.imageset/HostOS-freebsd.svg new file mode 100644 index 00000000..8febe9d4 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-freebsd.imageset/HostOS-freebsd.svg @@ -0,0 +1 @@ +FreeBSD \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-linux.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-linux.imageset/Contents.json new file mode 100644 index 00000000..42edeb83 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-linux.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "HostOS-linux.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-linux.imageset/HostOS-linux.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-linux.imageset/HostOS-linux.svg new file mode 100644 index 00000000..381d3d8a --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-linux.imageset/HostOS-linux.svg @@ -0,0 +1 @@ +Linux \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-netbsd.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-netbsd.imageset/Contents.json new file mode 100644 index 00000000..317b835a --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-netbsd.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "HostOS-netbsd.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-netbsd.imageset/HostOS-netbsd.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-netbsd.imageset/HostOS-netbsd.svg new file mode 100644 index 00000000..511f066e --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-netbsd.imageset/HostOS-netbsd.svg @@ -0,0 +1 @@ +NetBSD \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-openbsd.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-openbsd.imageset/Contents.json new file mode 100644 index 00000000..722d8635 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-openbsd.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "HostOS-openbsd.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-openbsd.imageset/HostOS-openbsd.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-openbsd.imageset/HostOS-openbsd.svg new file mode 100644 index 00000000..e78fbc7e --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/HostOS-openbsd.imageset/HostOS-openbsd.svg @@ -0,0 +1 @@ +OpenBSD \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-artixlinux.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-artixlinux.imageset/Contents.json new file mode 100644 index 00000000..20aab9eb --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-artixlinux.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-artixlinux.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-artixlinux.imageset/LinuxDistro-artixlinux.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-artixlinux.imageset/LinuxDistro-artixlinux.svg new file mode 100644 index 00000000..4b653ddf --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-artixlinux.imageset/LinuxDistro-artixlinux.svg @@ -0,0 +1 @@ +Artix Linux \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-asahilinux.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-asahilinux.imageset/Contents.json new file mode 100644 index 00000000..4cbf3a6b --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-asahilinux.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-asahilinux.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-asahilinux.imageset/LinuxDistro-asahilinux.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-asahilinux.imageset/LinuxDistro-asahilinux.svg new file mode 100644 index 00000000..01e7ceec --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-asahilinux.imageset/LinuxDistro-asahilinux.svg @@ -0,0 +1 @@ +Asahi Linux \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-cachyos.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-cachyos.imageset/Contents.json new file mode 100644 index 00000000..47513f6f --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-cachyos.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-cachyos.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-cachyos.imageset/LinuxDistro-cachyos.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-cachyos.imageset/LinuxDistro-cachyos.svg new file mode 100644 index 00000000..66e9e6c2 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-cachyos.imageset/LinuxDistro-cachyos.svg @@ -0,0 +1 @@ +CachyOS \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-devuan.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-devuan.imageset/Contents.json new file mode 100644 index 00000000..268fe5e0 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-devuan.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-devuan.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-devuan.imageset/LinuxDistro-devuan.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-devuan.imageset/LinuxDistro-devuan.svg new file mode 100644 index 00000000..001980b6 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-devuan.imageset/LinuxDistro-devuan.svg @@ -0,0 +1 @@ +Devuan \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-garudalinux.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-garudalinux.imageset/Contents.json new file mode 100644 index 00000000..3262e0b4 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-garudalinux.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-garudalinux.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-garudalinux.imageset/LinuxDistro-garudalinux.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-garudalinux.imageset/LinuxDistro-garudalinux.svg new file mode 100644 index 00000000..d0f7d8ba --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-garudalinux.imageset/LinuxDistro-garudalinux.svg @@ -0,0 +1 @@ +Garuda Linux \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-mxlinux.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-mxlinux.imageset/Contents.json new file mode 100644 index 00000000..3732bd3f --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-mxlinux.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-mxlinux.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-mxlinux.imageset/LinuxDistro-mxlinux.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-mxlinux.imageset/LinuxDistro-mxlinux.svg new file mode 100644 index 00000000..7805a528 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-mxlinux.imageset/LinuxDistro-mxlinux.svg @@ -0,0 +1 @@ +MX Linux \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-nobaralinux.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-nobaralinux.imageset/Contents.json new file mode 100644 index 00000000..2ee7e716 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-nobaralinux.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-nobaralinux.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-nobaralinux.imageset/LinuxDistro-nobaralinux.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-nobaralinux.imageset/LinuxDistro-nobaralinux.svg new file mode 100644 index 00000000..6ce9e46e --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-nobaralinux.imageset/LinuxDistro-nobaralinux.svg @@ -0,0 +1 @@ +Nobara Linux \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-openwrt.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-openwrt.imageset/Contents.json new file mode 100644 index 00000000..976c5d26 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-openwrt.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-openwrt.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-openwrt.imageset/LinuxDistro-openwrt.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-openwrt.imageset/LinuxDistro-openwrt.svg new file mode 100644 index 00000000..71a93b5c --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-openwrt.imageset/LinuxDistro-openwrt.svg @@ -0,0 +1 @@ +OpenWrt \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-parrotsecurity.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-parrotsecurity.imageset/Contents.json new file mode 100644 index 00000000..bc88008c --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-parrotsecurity.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-parrotsecurity.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-parrotsecurity.imageset/LinuxDistro-parrotsecurity.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-parrotsecurity.imageset/LinuxDistro-parrotsecurity.svg new file mode 100644 index 00000000..8eefdbd0 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-parrotsecurity.imageset/LinuxDistro-parrotsecurity.svg @@ -0,0 +1 @@ +Parrot Security \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-qubesos.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-qubesos.imageset/Contents.json new file mode 100644 index 00000000..478bf09c --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-qubesos.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-qubesos.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-qubesos.imageset/LinuxDistro-qubesos.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-qubesos.imageset/LinuxDistro-qubesos.svg new file mode 100644 index 00000000..d7031d07 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-qubesos.imageset/LinuxDistro-qubesos.svg @@ -0,0 +1 @@ +Qubes OS \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-slackware.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-slackware.imageset/Contents.json new file mode 100644 index 00000000..0cc60c7b --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-slackware.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-slackware.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-slackware.imageset/LinuxDistro-slackware.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-slackware.imageset/LinuxDistro-slackware.svg new file mode 100644 index 00000000..611bf809 --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-slackware.imageset/LinuxDistro-slackware.svg @@ -0,0 +1 @@ +Slackware \ No newline at end of file diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-zorin.imageset/Contents.json b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-zorin.imageset/Contents.json new file mode 100644 index 00000000..e859203f --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-zorin.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images": [ + { + "filename": "LinuxDistro-zorin.svg", + "idiom": "universal" + } + ], + "info": { + "author": "xcode", + "version": 1 + }, + "properties": { + "preserves-vector-representation": true, + "template-rendering-intent": "template" + } +} diff --git a/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-zorin.imageset/LinuxDistro-zorin.svg b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-zorin.imageset/LinuxDistro-zorin.svg new file mode 100644 index 00000000..e867f61b --- /dev/null +++ b/Frameworks/Resources/Assets.xcassets/LinuxDistributions/LinuxDistro-zorin.imageset/LinuxDistro-zorin.svg @@ -0,0 +1 @@ +Zorin \ No newline at end of file diff --git a/Frameworks/Resources/HostIcons.json b/Frameworks/Resources/HostIcons.json index 39d243bb..5f5ce6ac 100644 --- a/Frameworks/Resources/HostIcons.json +++ b/Frameworks/Resources/HostIcons.json @@ -40,6 +40,45 @@ "sourceURL": "https://www.archlinux.org/art", "title": "Arch Linux" }, + { + "asset": "LinuxDistro-artixlinux", + "hex": "10A0CC", + "id": "artixlinux", + "ids": [ + "artix" + ], + "license": null, + "package": "simple-icons", + "slug": "artixlinux", + "sourceURL": "https://gitea.artixlinux.org/artix/artwork/src/commit/256432e3d06b3e9024bfd6912768e80281ea3746/icons/logo-gray.svg", + "title": "Artix Linux" + }, + { + "asset": "LinuxDistro-asahilinux", + "hex": "A61200", + "id": "asahilinux", + "ids": [ + "asahi" + ], + "license": null, + "package": "simple-icons", + "slug": "asahilinux", + "sourceURL": "https://github.com/AsahiLinux/artwork/blob/292637c9658c1491ddc1128fb6134aec01d904dd/logos/svg/AsahiLinux_logomark_mono.svg", + "title": "Asahi Linux" + }, + { + "asset": "LinuxDistro-cachyos", + "hex": "00AA88", + "id": "cachyos", + "ids": [ + "cachyos" + ], + "license": null, + "package": "simple-icons", + "slug": "cachyos", + "sourceURL": "https://commons.wikimedia.org/wiki/File:CachyOS_Logo.svg", + "title": "CachyOS" + }, { "asset": "LinuxDistro-centos", "hex": "262577", @@ -79,6 +118,19 @@ "sourceURL": "https://commons.wikimedia.org/wiki/File:Deepin_logo.svg", "title": "deepin" }, + { + "asset": "LinuxDistro-devuan", + "hex": "004489", + "id": "devuan", + "ids": [ + "devuan" + ], + "license": "CC-BY-SA-4.0", + "package": "simple-icons", + "slug": "devuan", + "sourceURL": "https://git.devuan.org/devuan/documentation/src/commit/f4931e70e17f043c2824d591bba7c7f70ed583b6/art/graphics/logo/devuan-emblem.svg", + "title": "Devuan" + }, { "asset": "LinuxDistro-elementary", "hex": "64BAFF", @@ -118,6 +170,19 @@ "sourceURL": "https://docs.fedoraproject.org/en-US/project/brand/", "title": "Fedora" }, + { + "asset": "LinuxDistro-garudalinux", + "hex": "8839EF", + "id": "garudalinux", + "ids": [ + "garuda" + ], + "license": "GPL-3.0-only", + "package": "simple-icons", + "slug": "garudalinux", + "sourceURL": "https://gitlab.com/garuda-linux/themes-and-settings/artwork/garuda-icons/-/blob/aab26625fe01479ebc3a252103fca723baca9e88/usr/share/icons/garuda/distributor-logo-garuda-outline.svg", + "title": "Garuda Linux" + }, { "asset": "LinuxDistro-gentoo", "hex": "54487A", @@ -196,6 +261,19 @@ "sourceURL": "https://manjaro.org", "title": "Manjaro" }, + { + "asset": "LinuxDistro-mxlinux", + "hex": "000000", + "id": "mxlinux", + "ids": [ + "mx" + ], + "license": "GPL-3.0-only", + "package": "simple-icons", + "slug": "mxlinux", + "sourceURL": "https://mxlinux.org/art/", + "title": "MX Linux" + }, { "asset": "LinuxDistro-nixos", "hex": "5277C3", @@ -209,6 +287,19 @@ "sourceURL": "https://brand.nixos.org/logos/nixos-logomark-black-flat-minimal.svg", "title": "NixOS" }, + { + "asset": "LinuxDistro-nobaralinux", + "hex": "000000", + "id": "nobaralinux", + "ids": [ + "nobara" + ], + "license": null, + "package": "simple-icons", + "slug": "nobaralinux", + "sourceURL": "https://wiki.nobaraproject.org", + "title": "Nobara Linux" + }, { "asset": "LinuxDistro-opensuse", "hex": "73BA25", @@ -225,6 +316,32 @@ "sourceURL": "https://github.com/openSUSE/artwork/blob/33e94aa76837c09f03d1712705949b71a246a53b/logos/buttons/button-colour.svg", "title": "openSUSE" }, + { + "asset": "LinuxDistro-openwrt", + "hex": "00B5E2", + "id": "openwrt", + "ids": [ + "openwrt" + ], + "license": null, + "package": "simple-icons", + "slug": "openwrt", + "sourceURL": "https://openwrt.org/docs/guide-graphic-designer/openwrt-logo", + "title": "OpenWrt" + }, + { + "asset": "LinuxDistro-parrotsecurity", + "hex": "15E0ED", + "id": "parrotsecurity", + "ids": [ + "parrot" + ], + "license": null, + "package": "simple-icons", + "slug": "parrotsecurity", + "sourceURL": "https://gitlab.com/parrotsec/project/documentation/-/blob/d1d426b9cb3ea0efd16a2b34056c1ebb21bb9af9/static/img/parrot-logo.svg", + "title": "Parrot Security" + }, { "asset": "LinuxDistro-popos", "hex": "48B9C7", @@ -239,6 +356,19 @@ "sourceURL": "https://github.com/system76/brand/blob/7a31740b54f929b62a165baa61dfb0b5164261e8/Pop_OS%20branding/Pop_icon.svg", "title": "Pop!_OS" }, + { + "asset": "LinuxDistro-qubesos", + "hex": "3874D8", + "id": "qubesos", + "ids": [ + "qubes" + ], + "license": null, + "package": "simple-icons", + "slug": "qubesos", + "sourceURL": "https://github.com/QubesOS/qubes-attachment/blob/ed7e552eb8a5fca4e099361d137793d3551b3968/icons/qubes-logo-home.svg", + "title": "Qubes OS" + }, { "asset": "LinuxDistro-raspberrypi", "hex": "A22846", @@ -280,6 +410,19 @@ "sourceURL": "https://github.com/rocky-linux/branding/blob/94e97dd30b87d909cc4f6a6838a2926f77f9ac47/logo/src/icon-black.svg", "title": "Rocky Linux" }, + { + "asset": "LinuxDistro-slackware", + "hex": "000000", + "id": "slackware", + "ids": [ + "slackware" + ], + "license": null, + "package": "simple-icons", + "slug": "slackware", + "sourceURL": "https://en.wikipedia.org/wiki/Slackware", + "title": "Slackware" + }, { "asset": "LinuxDistro-solus", "hex": "5294E2", @@ -361,6 +504,19 @@ "sourceURL": "https://commons.wikimedia.org/wiki/File:Xubuntu_Icon.svg", "title": "Xubuntu" }, + { + "asset": "LinuxDistro-zorin", + "hex": "15A6F0", + "id": "zorin", + "ids": [ + "zorin" + ], + "license": null, + "package": "simple-icons", + "slug": "zorin", + "sourceURL": "https://zorin.com/press", + "title": "Zorin" + }, { "asset": "HostOS-macos", "hex": "000000", @@ -374,6 +530,72 @@ "sourceURL": "https://www.apple.com", "title": "macOS" }, + { + "asset": "HostOS-bsd", + "hex": "AB2B28", + "id": "bsd", + "ids": [ + "bsd", + "dragonfly" + ], + "license": null, + "package": "simple-icons", + "slug": "bsd", + "sourceURL": "https://freebsdfoundation.org/about-us/about-the-foundation/project", + "title": "BSD" + }, + { + "asset": "HostOS-freebsd", + "hex": "AB2B28", + "id": "freebsd", + "ids": [ + "freebsd" + ], + "license": null, + "package": "simple-icons", + "slug": "freebsd", + "sourceURL": "https://www.freebsdfoundation.org/about/project/", + "title": "FreeBSD" + }, + { + "asset": "HostOS-openbsd", + "hex": "F2CA30", + "id": "openbsd", + "ids": [ + "openbsd" + ], + "license": null, + "package": "simple-icons", + "slug": "openbsd", + "sourceURL": "https://en.wikipedia.org/wiki/OpenBSD", + "title": "OpenBSD" + }, + { + "asset": "HostOS-netbsd", + "hex": "FF6600", + "id": "netbsd", + "ids": [ + "netbsd" + ], + "license": null, + "package": "simple-icons", + "slug": "netbsd", + "sourceURL": "https://www.netbsd.org", + "title": "NetBSD" + }, + { + "asset": "HostOS-linux", + "hex": "FCC624", + "id": "linux", + "ids": [ + "linux" + ], + "license": null, + "package": "simple-icons", + "slug": "linux", + "sourceURL": "https://www.linuxfoundation.org/the-linux-mark/", + "title": "Linux" + }, { "asset": "HostOS-windows", "colorSource": "Termind presentation choice", diff --git a/Frameworks/TermindDomain/Sessions/RemoteSystemInfo.swift b/Frameworks/TermindDomain/Sessions/RemoteSystemInfo.swift index 5d48df39..9f99681e 100644 --- a/Frameworks/TermindDomain/Sessions/RemoteSystemInfo.swift +++ b/Frameworks/TermindDomain/Sessions/RemoteSystemInfo.swift @@ -138,13 +138,14 @@ public nonisolated enum RemoteDistributionDetection: Hashable, Sendable { extension RemoteSystemInfo { public var distributionDetection: RemoteDistributionDetection { guard let kernelName else { return .unavailable } - if kernelName.caseInsensitiveCompare("Darwin") == .orderedSame { + let kernel = kernelName.lowercased() + if kernel == "darwin" { return .resolved(DetectedDistribution(id: "macos")) } - guard kernelName.caseInsensitiveCompare("Linux") == .orderedSame else { return .resolved(nil) } - guard let distributionID, - let distribution = DetectedDistribution(id: distributionID, likeIDs: distributionLikeIDs) - else { return .unavailable } - return .resolved(distribution) + if ["freebsd", "openbsd", "netbsd", "dragonfly"].contains(kernel) { + return .resolved(DetectedDistribution(id: kernel)) + } + guard kernel == "linux" else { return .resolved(nil) } + return .resolved(DetectedDistribution(id: distributionID ?? "linux", likeIDs: distributionLikeIDs)) } } diff --git a/Frameworks/TermindPresentation/HostIconCatalog.swift b/Frameworks/TermindPresentation/HostIconCatalog.swift index 6f73a2b5..d702a44b 100644 --- a/Frameworks/TermindPresentation/HostIconCatalog.swift +++ b/Frameworks/TermindPresentation/HostIconCatalog.swift @@ -37,10 +37,11 @@ public nonisolated struct HostIconCatalog: Codable, Sendable { } public var selectableEntries: [HostIconEntry] { - let platforms = ["macos", "windows"].compactMap { id in entries.first { $0.id == id } } - return platforms - + entries.filter { $0.id != "macos" && $0.id != "windows" } - .sorted { $0.title.localizedStandardCompare($1.title) == .orderedAscending } + entries.sorted { + let comparison = $0.title.compare( + $1.title, options: [.caseInsensitive], locale: Locale(identifier: "en_US")) + return comparison == .orderedSame ? $0.id < $1.id : comparison == .orderedAscending + } } public func entry(matching distribution: DetectedDistribution) -> HostIconEntry? { @@ -49,6 +50,8 @@ public nonisolated struct HostIconCatalog: Codable, Sendable { uniquingKeysWith: { first, _ in first } ) if let exact = byID[distribution.id] { return exact } - return distribution.likeIDs.lazy.compactMap { byID[$0] }.first + if let related = distribution.likeIDs.lazy.compactMap({ byID[$0] }).first { return related } + // Detection only persists supported OS families; BSD and macOS match above. + return byID["linux"] } } diff --git a/Scripts/linux-distribution-icons.json b/Scripts/linux-distribution-icons.json index abdfa096..87947b25 100644 --- a/Scripts/linux-distribution-icons.json +++ b/Scripts/linux-distribution-icons.json @@ -2,32 +2,44 @@ { "title": "AlmaLinux", "slug": "almalinux", "ids": ["almalinux", "alma"] }, { "title": "Alpine Linux", "slug": "alpinelinux", "ids": ["alpine"] }, { "title": "Arch Linux", "slug": "archlinux", "ids": ["arch"] }, + { "title": "Artix Linux", "slug": "artixlinux", "ids": ["artix"] }, + { "title": "Asahi Linux", "slug": "asahilinux", "ids": ["asahi"] }, + { "title": "CachyOS", "slug": "cachyos", "ids": ["cachyos"] }, { "title": "CentOS", "slug": "centos", "ids": ["centos"] }, { "title": "Debian", "slug": "debian", "ids": ["debian"] }, { "title": "deepin", "slug": "deepin", "ids": ["deepin"] }, + { "title": "Devuan", "slug": "devuan", "ids": ["devuan"] }, { "title": "elementary", "slug": "elementary", "ids": ["elementary"] }, { "title": "EndeavourOS", "slug": "endeavouros", "ids": ["endeavouros"] }, { "title": "Fedora", "slug": "fedora", "ids": ["fedora"] }, + { "title": "Garuda Linux", "slug": "garudalinux", "ids": ["garuda"] }, { "title": "Gentoo", "slug": "gentoo", "ids": ["gentoo"] }, { "title": "Kali Linux", "slug": "kalilinux", "ids": ["kali"] }, { "title": "Kubuntu", "slug": "kubuntu", "ids": ["kubuntu"] }, { "title": "Linux Mint", "slug": "linuxmint", "ids": ["linuxmint"] }, { "title": "Lubuntu", "slug": "lubuntu", "ids": ["lubuntu"] }, { "title": "Manjaro", "slug": "manjaro", "ids": ["manjaro"] }, + { "title": "MX Linux", "slug": "mxlinux", "ids": ["mx"] }, { "title": "NixOS", "slug": "nixos", "ids": ["nixos"] }, + { "title": "Nobara Linux", "slug": "nobaralinux", "ids": ["nobara"] }, { "title": "openSUSE", "slug": "opensuse", "ids": ["opensuse", "opensuse-leap", "opensuse-tumbleweed", "opensuse-microos"] }, + { "title": "OpenWrt", "slug": "openwrt", "ids": ["openwrt"] }, + { "title": "Parrot Security", "slug": "parrotsecurity", "ids": ["parrot"] }, { "title": "Pop!_OS", "slug": "popos", "ids": ["pop", "popos"] }, + { "title": "Qubes OS", "slug": "qubesos", "ids": ["qubes"] }, { "title": "Raspberry Pi", "slug": "raspberrypi", "ids": ["raspbian", "raspberrypios"] }, { "title": "Red Hat", "slug": "redhat", "ids": ["rhel", "redhat"] }, { "title": "Rocky Linux", "slug": "rockylinux", "ids": ["rocky"] }, + { "title": "Slackware", "slug": "slackware", "ids": ["slackware"] }, { "title": "Solus", "slug": "solus", "ids": ["solus"] }, { "title": "SUSE", "slug": "suse", "ids": ["suse", "sles", "sled"] }, { "title": "Ubuntu", "slug": "ubuntu", "ids": ["ubuntu"] }, { "title": "Ubuntu MATE", "slug": "ubuntumate", "ids": ["ubuntu-mate", "ubuntu-mate-desktop"] }, { "title": "Void Linux", "slug": "voidlinux", "ids": ["void"] }, - { "title": "Xubuntu", "slug": "xubuntu", "ids": ["xubuntu"] } + { "title": "Xubuntu", "slug": "xubuntu", "ids": ["xubuntu"] }, + { "title": "Zorin", "slug": "zorin", "ids": ["zorin"] } ] diff --git a/Scripts/update-host-icons.py b/Scripts/update-host-icons.py index 13443ae1..4bb19717 100644 --- a/Scripts/update-host-icons.py +++ b/Scripts/update-host-icons.py @@ -72,6 +72,14 @@ def main(): metadata = {entry["title"]: entry for entry in json.loads(read(simple, "data/simple-icons.json"))} requested = json.loads((ROOT / "Scripts/linux-distribution-icons.json").read_text()) requested.append({"title": "Apple", "slug": "apple", "ids": ["macos"], "id": "macos"}) + for title, slug, ids in [ + ("BSD", "bsd", ["bsd", "dragonfly"]), + ("FreeBSD", "freebsd", ["freebsd"]), + ("OpenBSD", "openbsd", ["openbsd"]), + ("NetBSD", "netbsd", ["netbsd"]), + ("Linux", "linux", ["linux"]), + ]: + requested.append({"title": title, "slug": slug, "ids": ids, "id": slug}) with tempfile.TemporaryDirectory(prefix="termind-host-icons-") as temporary: stage = Path(temporary) assets = stage / "assets" @@ -83,7 +91,7 @@ def main(): for item in requested: info = metadata[item["title"]] is_mac = item.get("id") == "macos" - asset = "HostOS-macos" if is_mac else "LinuxDistro-" + item["slug"] + asset = "HostOS-" + item["id"] if "id" in item else "LinuxDistro-" + item["slug"] write_icon(assets, asset, read(simple, "icons/" + item["slug"] + ".svg")) entries.append({ "id": item.get("id", item["slug"]), "title": "macOS" if is_mac else item["title"], diff --git a/TFITests/MobileAppTests.swift b/TFITests/MobileAppTests.swift index 7c8d856f..c4eed964 100644 --- a/TFITests/MobileAppTests.swift +++ b/TFITests/MobileAppTests.swift @@ -1505,7 +1505,8 @@ private actor MobileTestSFTPClient: SFTPClientProtocol { @Suite struct WorkspaceHostIconTests { @Test @MainActor func operatingSystemArtworkIsBundledOnMobile() { - #expect(HostIconCatalog.bundled.selectableEntries.prefix(2).map(\.id) == ["macos", "windows"]) + #expect(HostIconCatalog.bundled.selectableEntries.first?.title == "AlmaLinux") + #expect(HostIconCatalog.bundled.selectableEntries.last?.title == "Zorin") for entry in HostIconCatalog.bundled.entries { #expect(UIImage(named: entry.asset) != nil, Comment(rawValue: entry.asset)) } diff --git a/TFITests/MobileHostIntegrationRenderingTests.swift b/TFITests/MobileHostIntegrationRenderingTests.swift index df104c7f..39fed0be 100644 --- a/TFITests/MobileHostIntegrationRenderingTests.swift +++ b/TFITests/MobileHostIntegrationRenderingTests.swift @@ -41,12 +41,13 @@ struct MobileHostIntegrationRenderingTests { descendants(controller.view).compactMap { $0 as? UIButton }.first { $0.showsMenuAsPrimaryAction }) let actions = try await menuActions(in: picker, expectedCount: HostIconCatalog.bundled.entries.count + 2) #expect( - actions.prefix(4).map(\.title) == [ + actions.prefix(2).map(\.title) == [ AppLocalization.string("Automatic", locale: Locale(identifier: language)), - AppLocalization.string("Server", locale: Locale(identifier: language)), "macOS", "Windows", + AppLocalization.string("Server", locale: Locale(identifier: language)), ]) + #expect(actions.dropFirst(2).map(\.title) == HostIconCatalog.bundled.selectableEntries.map(\.title)) #expect(actions.first?.state == .on) - picker.sendAction(actions[3]) + picker.sendAction(try #require(actions.first { $0.title == "Windows" })) #expect(draft.host.iconOverrideID == "windows") #expect(draft.host.detectedDistribution?.id == "macos") for style in [UIUserInterfaceStyle.light, .dark] { diff --git a/TFITests/MobileWorkspaceSessionTests.swift b/TFITests/MobileWorkspaceSessionTests.swift index 1afda920..1aba8f1e 100644 --- a/TFITests/MobileWorkspaceSessionTests.swift +++ b/TFITests/MobileWorkspaceSessionTests.swift @@ -44,6 +44,11 @@ struct MobileWorkspaceSessionTests { try await Task.sleep(for: .milliseconds(10)) } #expect(workspace.activitySession.iconAsset == "LinuxDistro-ubuntu") + workspace.receiveShellEvent(.systemInfo(RemoteSystemInfo(kernelName: "DragonFly"))) + #expect(workspace.activitySession.iconAsset == "HostOS-bsd") + #expect(app.library.host(host.id)?.detectedDistribution?.id == "dragonfly") + workspace.receiveShellEvent(.systemInfo(RemoteSystemInfo(distributionID: "custom-linux", kernelName: "Linux"))) + #expect(workspace.activitySession.iconAsset == "HostOS-linux") #expect(workspace.status == originalStatus) #expect(session.openWorkspace(id) === workspace) } diff --git a/TFMTests/HostIconPresentationTests.swift b/TFMTests/HostIconPresentationTests.swift index e3ce48c2..07a6d1fd 100644 --- a/TFMTests/HostIconPresentationTests.swift +++ b/TFMTests/HostIconPresentationTests.swift @@ -7,9 +7,23 @@ import Testing @Suite @MainActor struct HostIconPresentationTests { + @Test(arguments: [ + ("artix", "Artix Linux"), ("asahi", "Asahi Linux"), ("cachyos", "CachyOS"), + ("devuan", "Devuan"), ("garuda", "Garuda Linux"), ("mx", "MX Linux"), + ("nobara", "Nobara Linux"), ("openwrt", "OpenWrt"), ("parrot", "Parrot Security"), + ("qubes", "Qubes OS"), ("slackware", "Slackware"), ("zorin", "Zorin"), + ("freebsd", "FreeBSD"), ("openbsd", "OpenBSD"), ("netbsd", "NetBSD"), ("dragonfly", "BSD"), + ("linux", "Linux"), ("unlisted-linux", "Linux"), + ]) + func expandedCatalogMatchesSystemIdentifiers(id: String, title: String) throws { + let distribution = try #require(DetectedDistribution(id: id)) + #expect(HostIconCatalog.entry(for: distribution)?.title == title) + } + @Test func bundledArtworkCanBeLoadedAndManualSelectionWins() throws { let catalog = HostIconCatalog.bundled - #expect(catalog.selectableEntries.prefix(2).map(\.id) == ["macos", "windows"]) + #expect(catalog.selectableEntries.first?.title == "AlmaLinux") + #expect(catalog.selectableEntries.last?.title == "Zorin") for entry in catalog.entries { #expect(NSImage(named: entry.asset) != nil, Comment(rawValue: entry.asset)) } diff --git a/TFMTests/ShellIntegrationBootstrapTests.swift b/TFMTests/ShellIntegrationBootstrapTests.swift index da30d0fa..3050aab1 100644 --- a/TFMTests/ShellIntegrationBootstrapTests.swift +++ b/TFMTests/ShellIntegrationBootstrapTests.swift @@ -727,6 +727,17 @@ struct ShellIntegrationBootstrapTests { #expect(completed.systemInfo?.architecture == "arm64") } + @Test(arguments: ["FreeBSD", "OpenBSD", "NetBSD", "DragonFly"], ["||", "|||"]) + func bsdKernelOnlyRecordsRetainTheirSystemIdentity(kernel: String, prefix: String) throws { + var bootstrap = RemoteShellIntegrationBootstrap(token: "BSD") + let marker = "__TRMD_BSD_SYSTEM__\(prefix)\(kernel)|14.1|amd64__\r\n__TRMD_BSD_SHELL__/bin/zsh__\r\n" + _ = try #require(bootstrap.receive(Data(marker.utf8)).command) + let completed = bootstrap.receive(Data("__TRMD_BSD_DONE__\r\n".utf8)) + #expect(completed.integrationInstalled) + #expect(completed.systemInfo?.distributionDetection == .resolved(DetectedDistribution(id: kernel))) + #expect(completed.systemInfo?.distributionID == nil) + } + @Test func existingProbeReadsDarwinOnTheLocalMac() throws { var bootstrap = RemoteShellIntegrationBootstrap(token: "LOCALMAC") var script = Data("export SHELL=/bin/zsh\n".utf8)