Skip to content

Commit a72ce70

Browse files
committed
Rework the layer that does the filtering of releases
Add release mocking to the toolchain downloader with a mocked set of releases
1 parent 354321d commit a72ce70

File tree

3 files changed

+113
-72
lines changed

3 files changed

+113
-72
lines changed

Sources/SwiftlyCore/HTTPClient.swift

Lines changed: 41 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,7 @@ extension Components.Schemas.SwiftlyReleasePlatformArtifacts {
5353
public protocol HTTPRequestExecutor {
5454
func execute(_ request: HTTPClientRequest, timeout: TimeAmount) async throws -> HTTPClientResponse
5555
func getCurrentSwiftlyRelease() async throws -> Components.Schemas.SwiftlyRelease
56-
func getReleaseToolchains(
57-
platform: PlatformDefinition,
58-
arch a: Components.Schemas.Architecture?,
59-
limit: Int?,
60-
filter: ((ToolchainVersion.StableRelease) -> Bool)?
61-
) async throws -> [ToolchainVersion.StableRelease]
56+
func getReleaseToolchains() async throws -> [Components.Schemas.Release]
6257
}
6358

6459
internal struct SwiftlyUserAgentMiddleware: ClientMiddleware {
@@ -135,14 +130,7 @@ internal class HTTPRequestExecutorImpl: HTTPRequestExecutor {
135130
return try response.ok.body.json
136131
}
137132

138-
public func getReleaseToolchains(
139-
platform: PlatformDefinition,
140-
arch a: Components.Schemas.Architecture? = nil,
141-
limit: Int? = nil,
142-
filter: ((ToolchainVersion.StableRelease) -> Bool)? = nil
143-
) async throws -> [ToolchainVersion.StableRelease] {
144-
let arch = a ?? cpuArch
145-
133+
public func getReleaseToolchains() async throws -> [Components.Schemas.Release] {
146134
let config = AsyncHTTPClientTransport.Configuration(client: self.httpClient, timeout: .seconds(30))
147135
let swiftlyUserAgent = SwiftlyUserAgentMiddleware()
148136

@@ -154,40 +142,7 @@ internal class HTTPRequestExecutorImpl: HTTPRequestExecutor {
154142

155143
let response = try await client.listReleases()
156144

157-
var swiftOrgFiltered: [ToolchainVersion.StableRelease] = try response.ok.body.json.compactMap { swiftOrgRelease in
158-
if platform.name != PlatformDefinition.macOS.name {
159-
// If the platform isn't xcode then verify that there is an offering for this platform name and arch
160-
guard let swiftOrgPlatform = swiftOrgRelease.platforms.first(where: { $0.matches(platform) }) else {
161-
return nil
162-
}
163-
164-
guard case let archs = swiftOrgPlatform.archs, archs.contains(arch) else {
165-
return nil
166-
}
167-
}
168-
169-
guard let version = try? ToolchainVersion(parsing: swiftOrgRelease.stableName),
170-
case let .stable(release) = version
171-
else {
172-
throw SwiftlyError(message: "error parsing swift.org release version: \(swiftOrgRelease.stableName)")
173-
}
174-
175-
if let filter {
176-
guard filter(release) else {
177-
return nil
178-
}
179-
}
180-
181-
return release
182-
}
183-
184-
swiftOrgFiltered.sort(by: >)
185-
186-
return if let limit = limit {
187-
Array(swiftOrgFiltered.prefix(limit))
188-
} else {
189-
swiftOrgFiltered
190-
}
145+
return try response.ok.body.json
191146
}
192147
}
193148

@@ -355,7 +310,44 @@ public struct SwiftlyHTTPClient {
355310
limit: Int? = nil,
356311
filter: ((ToolchainVersion.StableRelease) -> Bool)? = nil
357312
) async throws -> [ToolchainVersion.StableRelease] {
358-
try await SwiftlyCore.httpRequestExecutor.getReleaseToolchains(platform: platform, arch: a, limit: limit, filter: filter)
313+
let arch = a ?? cpuArch
314+
315+
let releases = try await SwiftlyCore.httpRequestExecutor.getReleaseToolchains()
316+
317+
var swiftOrgFiltered: [ToolchainVersion.StableRelease] = try releases.compactMap { swiftOrgRelease in
318+
if platform.name != PlatformDefinition.macOS.name {
319+
// If the platform isn't xcode then verify that there is an offering for this platform name and arch
320+
guard let swiftOrgPlatform = swiftOrgRelease.platforms.first(where: { $0.matches(platform) }) else {
321+
return nil
322+
}
323+
324+
guard case let archs = swiftOrgPlatform.archs, archs.contains(arch) else {
325+
return nil
326+
}
327+
}
328+
329+
guard let version = try? ToolchainVersion(parsing: swiftOrgRelease.stableName),
330+
case let .stable(release) = version
331+
else {
332+
throw SwiftlyError(message: "error parsing swift.org release version: \(swiftOrgRelease.stableName)")
333+
}
334+
335+
if let filter {
336+
guard filter(release) else {
337+
return nil
338+
}
339+
}
340+
341+
return release
342+
}
343+
344+
swiftOrgFiltered.sort(by: >)
345+
346+
return if let limit = limit {
347+
Array(swiftOrgFiltered.prefix(limit))
348+
} else {
349+
swiftOrgFiltered
350+
}
359351
}
360352

361353
public struct SnapshotBranchNotFoundError: LocalizedError {

Tests/SwiftlyTests/InstallTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ final class InstallTests: SwiftlyTests {
3030
}
3131

3232
// As of writing this, 5.8.0 is the latest stable release. Assert it is at least that new.
33+
print("RELEASE IS \(release)")
3334
XCTAssertTrue(release >= ToolchainVersion.StableRelease(major: 5, minor: 8, patch: 0))
3435

3536
try await validateInstalledToolchains([installedToolchain], description: "install latest")

Tests/SwiftlyTests/SwiftlyTests.swift

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ struct SwiftlyTestError: LocalizedError {
1717
let message: String
1818
}
1919

20-
class SwiftlyTests: XCTestCase {
21-
override class func tearDown() {
20+
public class SwiftlyTests: XCTestCase {
21+
override public class func tearDown() {
2222
#if os(Linux)
2323
let deleteTestGPGKeys = Process()
2424
deleteTestGPGKeys.executableURL = URL(fileURLWithPath: "/usr/bin/env")
@@ -36,13 +36,13 @@ class SwiftlyTests: XCTestCase {
3636
}
3737

3838
// Below are some constants that can be used to write test cases.
39-
static let oldStable = ToolchainVersion(major: 5, minor: 6, patch: 0)
40-
static let oldStableNewPatch = ToolchainVersion(major: 5, minor: 6, patch: 3)
41-
static let newStable = ToolchainVersion(major: 5, minor: 7, patch: 0)
42-
static let oldMainSnapshot = ToolchainVersion(snapshotBranch: .main, date: "2022-09-10")
43-
static let newMainSnapshot = ToolchainVersion(snapshotBranch: .main, date: "2022-10-22")
44-
static let oldReleaseSnapshot = ToolchainVersion(snapshotBranch: .release(major: 5, minor: 7), date: "2022-08-27")
45-
static let newReleaseSnapshot = ToolchainVersion(snapshotBranch: .release(major: 5, minor: 7), date: "2022-08-30")
39+
public static let oldStable = ToolchainVersion(major: 5, minor: 6, patch: 0)
40+
public static let oldStableNewPatch = ToolchainVersion(major: 5, minor: 6, patch: 3)
41+
public static let newStable = ToolchainVersion(major: 5, minor: 7, patch: 0)
42+
public static let oldMainSnapshot = ToolchainVersion(snapshotBranch: .main, date: "2022-09-10")
43+
public static let newMainSnapshot = ToolchainVersion(snapshotBranch: .main, date: "2022-10-22")
44+
public static let oldReleaseSnapshot = ToolchainVersion(snapshotBranch: .release(major: 5, minor: 7), date: "2022-08-27")
45+
public static let newReleaseSnapshot = ToolchainVersion(snapshotBranch: .release(major: 5, minor: 7), date: "2022-08-30")
4646

4747
static let allToolchains: Set<ToolchainVersion> = [
4848
oldStable,
@@ -468,12 +468,7 @@ private struct MockHTTPRequestExecutor: HTTPRequestExecutor {
468468
throw SwiftlyTestError(message: "Mocking of fetching the current swiftly release is not implemented in MockHTTPRequestExecutor.")
469469
}
470470

471-
public func getReleaseToolchains(
472-
platform _: PlatformDefinition,
473-
arch _: Components.Schemas.Architecture?,
474-
limit _: Int?,
475-
filter _: ((ToolchainVersion.StableRelease) -> Bool)?
476-
) async throws -> [ToolchainVersion.StableRelease] {
471+
public func getReleaseToolchains() async throws -> [Components.Schemas.Release] {
477472
throw SwiftlyTestError(message: "Mocking of fetching the release toolchains is not implemented in MockHTTPRequestExecutor.")
478473
}
479474
}
@@ -494,13 +489,31 @@ public class MockToolchainDownloader: HTTPRequestExecutor {
494489

495490
private let latestSwiftlyVersion: SwiftlyVersion
496491

497-
public init(executables: [String]? = nil, latestSwiftlyVersion: SwiftlyVersion = SwiftlyCore.version, delegate: HTTPRequestExecutor) {
492+
private let releaseToolchains: [ToolchainVersion.StableRelease]
493+
494+
public init(
495+
executables: [String]? = nil,
496+
latestSwiftlyVersion: SwiftlyVersion = SwiftlyCore.version,
497+
releaseToolchains: [ToolchainVersion.StableRelease] = [
498+
SwiftlyTests.oldStable.asStableRelease!,
499+
SwiftlyTests.newStable.asStableRelease!,
500+
SwiftlyTests.oldStableNewPatch.asStableRelease!,
501+
ToolchainVersion.StableRelease(major: 5, minor: 7, patch: 4), // Some tests look for a patch in the 5.7.x series larger than 5.0.3
502+
ToolchainVersion.StableRelease(major: 5, minor: 9, patch: 0), // Some tests try to update from 5.9.0
503+
ToolchainVersion.StableRelease(major: 5, minor: 9, patch: 1),
504+
ToolchainVersion.StableRelease(major: 6, minor: 0, patch: 0), // Some tests check for a release larger than 5.8.0 to be present
505+
ToolchainVersion.StableRelease(major: 6, minor: 0, patch: 1), // Some tests try to update from 6.0.0
506+
ToolchainVersion.StableRelease(major: 6, minor: 0, patch: 2), // Some tests try to update from 6.0.1
507+
],
508+
delegate: HTTPRequestExecutor
509+
) {
498510
self.executables = executables ?? ["swift"]
499511
#if os(Linux)
500512
self.signatures = [:]
501513
#endif
502514
self.delegate = delegate
503515
self.latestSwiftlyVersion = latestSwiftlyVersion
516+
self.releaseToolchains = releaseToolchains
504517
}
505518

506519
public func getCurrentSwiftlyRelease() async throws -> Components.Schemas.SwiftlyRelease {
@@ -515,13 +528,48 @@ public class MockToolchainDownloader: HTTPRequestExecutor {
515528
return release
516529
}
517530

518-
public func getReleaseToolchains(
519-
platform _: PlatformDefinition,
520-
arch _: Components.Schemas.Architecture?,
521-
limit _: Int?,
522-
filter _: ((ToolchainVersion.StableRelease) -> Bool)?
523-
) async throws -> [ToolchainVersion.StableRelease] {
524-
throw SwiftlyTestError(message: "Mocking of the fetching of release toolchains is not implemented by the MockToolchainDownloader")
531+
public func getReleaseToolchains() async throws -> [Components.Schemas.Release] {
532+
let currentPlatform = try await Swiftly.currentPlatform.detectPlatform(disableConfirmation: true, platform: nil)
533+
534+
let platformName = switch currentPlatform {
535+
case PlatformDefinition.ubuntu2004:
536+
"Ubuntu 20.04"
537+
case PlatformDefinition.amazonlinux2:
538+
"Amazon Linux 2"
539+
case PlatformDefinition.ubuntu2204:
540+
"Ubuntu 22.04"
541+
case PlatformDefinition.rhel9:
542+
"Red Hat Universal Base Image 9"
543+
case PlatformDefinition(name: "ubuntu2404", nameFull: "ubuntu24.04", namePretty: "Ubuntu 24.04"):
544+
"Ubuntu 24.04"
545+
case PlatformDefinition(name: "debian12", nameFull: "debian12", namePretty: "Debian 12"):
546+
"Debian 12"
547+
case PlatformDefinition(name: "fedora39", nameFull: "fedora39", namePretty: "Fedora 39"):
548+
"Fedora 39"
549+
case PlatformDefinition.macOS:
550+
"Xcode" // NOTE: this is not actually a platform that gets added in the swift.org API for macos/xcode
551+
default:
552+
String?.none
553+
}
554+
555+
guard let platformName else {
556+
throw SwiftlyTestError(message: "Could not detect the current platform in test")
557+
}
558+
559+
return self.releaseToolchains.map { releaseToolchain in
560+
Components.Schemas.Release(
561+
name: String(describing: releaseToolchain),
562+
date: "",
563+
platforms: [.init(
564+
name: platformName,
565+
platform: .init(value1: .linux, value2: "Linux"),
566+
archs: [cpuArch]
567+
)],
568+
tag: "",
569+
xcode: "",
570+
xcodeRelease: true
571+
)
572+
}
525573
}
526574

527575
public func execute(_ request: HTTPClientRequest, timeout: TimeAmount) async throws -> HTTPClientResponse {

0 commit comments

Comments
 (0)