From 6dd02613d67fe61570326fb14e1e57838f87f6c0 Mon Sep 17 00:00:00 2001 From: "Jesse L. Zamora" Date: Sat, 6 Dec 2025 13:14:08 +0000 Subject: [PATCH 1/3] Support parsing distribution version using name instead of number - In other words, being able to pass "--distribution-version noble" instead of only "--distribution-version 24.0.4". --- .../PlatformModels/LinuxDistribution.swift | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift b/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift index c82835b..a5a957e 100644 --- a/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift +++ b/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift @@ -28,17 +28,17 @@ public enum LinuxDistribution: Hashable, Sendable { init(version: String) throws { switch version { - case "20.04": - self = .focal - case "22.04": - self = .jammy - case "24.04": - self = .noble + case "20.04": self = .focal + case "22.04": self = .jammy + case "24.04": self = .noble default: - throw GeneratorError.unknownLinuxDistribution( - name: LinuxDistribution.Name.ubuntu.rawValue, - version: version - ) + guard let versionType = Self(rawValue: version) else { + throw GeneratorError.unknownLinuxDistribution( + name: LinuxDistribution.Name.ubuntu.rawValue, + version: version + ) + } + self = versionType } } @@ -101,10 +101,13 @@ public enum LinuxDistribution: Hashable, Sendable { case "12": self = .bookworm case "13": self = .trixie default: - throw GeneratorError.unknownLinuxDistribution( - name: LinuxDistribution.Name.debian.rawValue, - version: version - ) + guard let versionType = Self(rawValue: version) else { + throw GeneratorError.unknownLinuxDistribution( + name: LinuxDistribution.Name.debian.rawValue, + version: version + ) + } + self = versionType } } From 419a660bf75cc57c6257c040e54b297e76ecf132 Mon Sep 17 00:00:00 2001 From: "Jesse L. Zamora" Date: Sat, 6 Dec 2025 17:28:50 +0000 Subject: [PATCH 2/3] Add unit tests LinuxDistribution enum - Only compiled if Testing module is available. - 100% code coverage of LinuxDistribution. --- .../PlatformModels/LinuxDistribution.swift | 12 +- .../LinuxDistributionTests.swift | 183 ++++++++++++++++++ 2 files changed, 187 insertions(+), 8 deletions(-) create mode 100644 Tests/SwiftSDKGeneratorTests/PlatformModels/LinuxDistributionTests.swift diff --git a/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift b/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift index a5a957e..8eef83e 100644 --- a/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift +++ b/Sources/SwiftSDKGenerator/PlatformModels/LinuxDistribution.swift @@ -21,7 +21,7 @@ public enum LinuxDistribution: Hashable, Sendable { case ubi9 } - public enum Ubuntu: String, Sendable { + public enum Ubuntu: String, Sendable, Equatable, CaseIterable { case focal case jammy case noble @@ -59,6 +59,7 @@ public enum LinuxDistribution: Hashable, Sendable { "linux-libc-dev", "zlib1g", "zlib1g-dev", + "libicu-dev", "libcurl4-openssl-dev", ] } @@ -69,28 +70,25 @@ public enum LinuxDistribution: Hashable, Sendable { return commonPackages + [ "libgcc-10-dev", "libicu66", - "libicu-dev", "libstdc++-10-dev", ] case .jammy: return commonPackages + [ "libgcc-12-dev", "libicu70", - "libicu-dev", "libstdc++-12-dev", ] case .noble: return commonPackages + [ "libgcc-13-dev", "libicu74", - "libicu-dev", "libstdc++-13-dev", ] } } } - public enum Debian: String, Sendable { + public enum Debian: String, Sendable, Equatable, CaseIterable { case bullseye case bookworm case trixie @@ -128,6 +126,7 @@ public enum LinuxDistribution: Hashable, Sendable { "linux-libc-dev", "zlib1g", "zlib1g-dev", + "libicu-dev", "libcurl4-openssl-dev", ] } @@ -138,21 +137,18 @@ public enum LinuxDistribution: Hashable, Sendable { return commonPackages + [ "libgcc-10-dev", "libicu67", - "libicu-dev", "libstdc++-10-dev", ] case .bookworm: return commonPackages + [ "libgcc-12-dev", "libicu72", - "libicu-dev", "libstdc++-12-dev", ] case .trixie: return commonPackages + [ "libgcc-14-dev", "libicu76", - "libicu-dev", "libstdc++-14-dev", ] } diff --git a/Tests/SwiftSDKGeneratorTests/PlatformModels/LinuxDistributionTests.swift b/Tests/SwiftSDKGeneratorTests/PlatformModels/LinuxDistributionTests.swift new file mode 100644 index 0000000..798f8c3 --- /dev/null +++ b/Tests/SwiftSDKGeneratorTests/PlatformModels/LinuxDistributionTests.swift @@ -0,0 +1,183 @@ +#if canImport(Testing) + import Testing + @testable import SwiftSDKGenerator + + struct LinuxDistributionTests { + struct UbuntuTests { + @Test(arguments: [ + ("20.04", LinuxDistribution.Ubuntu.focal), + ("focal", LinuxDistribution.Ubuntu.focal), + + ("22.04", LinuxDistribution.Ubuntu.jammy), + ("jammy", LinuxDistribution.Ubuntu.jammy), + + ("24.04", LinuxDistribution.Ubuntu.noble), + ("noble", LinuxDistribution.Ubuntu.noble), + ]) + func validVersionStrings(versionString: String, expectedVersion: LinuxDistribution.Ubuntu) throws { + let version = try LinuxDistribution.Ubuntu(version: versionString) + #expect(version == expectedVersion) + } + + @Test(arguments: [ + "18.04", + "bionic", + "unknown", + "invalid", + ]) func invalidVersionStrings(versionString: String) throws { + #expect(throws: GeneratorError.self) { + let _ = try LinuxDistribution.Ubuntu(version: versionString) + } + } + + @Test(arguments: [ + (LinuxDistribution.Ubuntu.focal, "20.04"), + (LinuxDistribution.Ubuntu.jammy, "22.04"), + (LinuxDistribution.Ubuntu.noble, "24.04"), + ]) + func versionProperty(ubuntuVersion: LinuxDistribution.Ubuntu, expectedVersionString: String) { + #expect(ubuntuVersion.version == expectedVersionString) + } + + @Test(arguments: LinuxDistribution.Ubuntu.allCases) + func requiredPackages(ubuntuVersion: LinuxDistribution.Ubuntu) { + let commonPackages = [ + "libc6", + "libc6-dev", + "libgcc-s1", + "libstdc++6", + "linux-libc-dev", + "zlib1g", + "zlib1g-dev", + "libicu-dev", + "libcurl4-openssl-dev", + ] + + let requiredPackages = ubuntuVersion.requiredPackages + #expect(requiredPackages.starts(with: commonPackages)) + + // Some required packages that change versions between Ubuntu versions + #expect(requiredPackages.contains(where: { $0.matches(regex: "libgcc-\\d{2}-dev") })) + #expect(requiredPackages.contains(where: { $0.matches(regex: "libicu\\d{2}") })) + #expect(requiredPackages.contains(where: { $0.matches(regex: "libstdc\\+\\+-\\d{2}-dev") })) + } + } + + struct DebianTests { + @Test(arguments: [ + ("11", LinuxDistribution.Debian.bullseye), + ("bullseye", LinuxDistribution.Debian.bullseye), + + ("12", LinuxDistribution.Debian.bookworm), + ("bookworm", LinuxDistribution.Debian.bookworm), + + ("13", LinuxDistribution.Debian.trixie), + ("trixie", LinuxDistribution.Debian.trixie), + ]) + func validVersionStrings(versionString: String, expectedVersion: LinuxDistribution.Debian) throws { + let version = try LinuxDistribution.Debian(version: versionString) + #expect(version == expectedVersion) + } + + @Test(arguments: [ + "9", + "sid", + "unknown", + "invalid", + ]) func invalidVersionStrings(versionString: String) throws { + #expect(throws: GeneratorError.self) { + let _ = try LinuxDistribution.Debian(version: versionString) + } + } + + @Test(arguments: [ + (LinuxDistribution.Debian.bullseye, "11"), + (LinuxDistribution.Debian.bookworm, "12"), + (LinuxDistribution.Debian.trixie, "13"), + ]) + func versionProperty(debianVersion: LinuxDistribution.Debian, expectedVersionString: String) { + #expect(debianVersion.version == expectedVersionString) + } + + @Test(arguments: LinuxDistribution.Debian.allCases) + func requiredPackages(debianVersion: LinuxDistribution.Debian) { + let commonPackages = [ + "libc6", + "libc6-dev", + "libgcc-s1", + "libstdc++6", + "linux-libc-dev", + "zlib1g", + "zlib1g-dev", + "libicu-dev", + "libcurl4-openssl-dev", + ] + + let requiredPackages = debianVersion.requiredPackages + #expect(requiredPackages.starts(with: commonPackages)) + + // Some required packages that change versions between Debian versions + #expect(requiredPackages.contains(where: { $0.matches(regex: "libgcc-\\d{2}-dev") })) + #expect(requiredPackages.contains(where: { $0.matches(regex: "libicu\\d{2}") })) + #expect(requiredPackages.contains(where: { $0.matches(regex: "libstdc\\+\\+-\\d{2}-dev") })) + } + } + + @Test(arguments: [ + (LinuxDistribution.Name.rhel, "ubi9", "ubi9", "rhel-ubi9"), + (LinuxDistribution.Name.ubuntu, "22.04", "jammy", "jammy"), + (LinuxDistribution.Name.debian, "12", "bookworm", "bookworm"), + ]) + func distributionProperties( + name: LinuxDistribution.Name, + version: String, + expectedRelease: String, + expectedImageSuffix: String + ) throws { + let distribution = try LinuxDistribution(name: name, version: version) + + #expect(distribution.name == name) + #expect(distribution.release == expectedRelease) + #expect(distribution.swiftDockerImageSuffix == expectedImageSuffix) + #expect(distribution.description.isEmpty == false) + } + + @Test(arguments: [ + ("rhel", LinuxDistribution.Name.rhel), + ("ubuntu", LinuxDistribution.Name.ubuntu), + ("debian", LinuxDistribution.Name.debian), + ]) func validDistributionNames(nameString: String, expectedName: LinuxDistribution.Name, ) throws { + let name = try LinuxDistribution.Name(nameString: nameString) + #expect(name == expectedName) + } + + @Test(arguments: [ + "amazonlinux", + "fedora", + "opensuse", + ]) func invalidDistributionNames(name: String) throws { + #expect(throws: GeneratorError.self) { + let _ = try LinuxDistribution.Name(nameString: name) + } + } + + @Test(arguments: [ + (LinuxDistribution.Name.rhel, "ubi8"), + (LinuxDistribution.Name.ubuntu, "18.04"), + (LinuxDistribution.Name.debian, "9"), + ]) func invalidDistributionVersions( + name: LinuxDistribution.Name, + version: String + ) throws { + #expect(throws: GeneratorError.self) { + let _ = try LinuxDistribution(name: name, version: version) + } + } + } + + extension String { + func matches(regex: String) -> Bool { + return self.range(of: regex, options: .regularExpression) != nil + } + } +#endif From b35403ee2b7ab550b98ff8b6432f715fc1e98afc Mon Sep 17 00:00:00 2001 From: "Jesse L. Zamora" Date: Sun, 7 Dec 2025 01:38:25 +0000 Subject: [PATCH 3/3] Fix soundness + typo in LinuxDistributionTests --- .../PlatformModels/LinuxDistributionTests.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Tests/SwiftSDKGeneratorTests/PlatformModels/LinuxDistributionTests.swift b/Tests/SwiftSDKGeneratorTests/PlatformModels/LinuxDistributionTests.swift index 798f8c3..b07f46f 100644 --- a/Tests/SwiftSDKGeneratorTests/PlatformModels/LinuxDistributionTests.swift +++ b/Tests/SwiftSDKGeneratorTests/PlatformModels/LinuxDistributionTests.swift @@ -1,3 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2022-2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + #if canImport(Testing) import Testing @testable import SwiftSDKGenerator @@ -146,7 +158,7 @@ ("rhel", LinuxDistribution.Name.rhel), ("ubuntu", LinuxDistribution.Name.ubuntu), ("debian", LinuxDistribution.Name.debian), - ]) func validDistributionNames(nameString: String, expectedName: LinuxDistribution.Name, ) throws { + ]) func validDistributionNames(nameString: String, expectedName: LinuxDistribution.Name) throws { let name = try LinuxDistribution.Name(nameString: nameString) #expect(name == expectedName) }