Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 15 additions & 13 deletions Sources/XcodesKit/XcodeList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,30 +165,32 @@ extension XcodeList {
}
return xcodes
}
.map(filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers)
.map(XcodeList.filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers)
}

/// Xcode Releases may have multiple releases with the same build metadata when a build doesn't change between candidate and final releases.
/// For example, 12.3 RC and 12.3 are both build 12C33
/// We don't care about that difference, so only keep the final release (GM or Release, in XCModel terms).
/// The downside of this is that a user could technically have both releases installed, and so they won't both be shown in the list, but I think most users wouldn't do this.
func filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers(_ xcodes: [Xcode]) -> [Xcode] {
/// This may not preserve order.
static func filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers(_ xcodes: [Xcode]) -> [Xcode] {

let xcodesByBuildMetadataIdentifiers =
Dictionary(grouping: xcodes, by: { $0.version.buildMetadataIdentifiers })

var filteredXcodes: [Xcode] = []
for xcode in xcodes {
if xcode.version.buildMetadataIdentifiers.isEmpty {
filteredXcodes.append(xcode)
for (buildMetadataIdentifiers, xcodes) in xcodesByBuildMetadataIdentifiers {
if buildMetadataIdentifiers.isEmpty || xcodes.count == 1 {
filteredXcodes.append(contentsOf: xcodes)
continue
}

let xcodesWithSameBuildMetadataIdentifiers = xcodes
.filter({ $0.version.buildMetadataIdentifiers == xcode.version.buildMetadataIdentifiers })
if xcodesWithSameBuildMetadataIdentifiers.count > 1,
xcode.version.prereleaseIdentifiers.isEmpty || xcode.version.prereleaseIdentifiers == ["GM"] {
filteredXcodes.append(xcode)
} else if xcodesWithSameBuildMetadataIdentifiers.count == 1 {
filteredXcodes.append(xcode)
}
// Use the final release if there is one, otherwise just (arbitrarily) pick the first.
let finalRelease = xcodes.first(where: {
$0.version.prereleaseIdentifiers.isEmpty || $0.version.prereleaseIdentifiers == ["GM"] })
filteredXcodes.append(finalRelease ?? xcodes.first!)
}

return filteredXcodes
}
}
61 changes: 61 additions & 0 deletions Tests/XcodesKitTests/XcodeListTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import XCTest
import Version
@testable import XcodesKit

class XcodeListTests: XCTestCase {

func xcodeFromVersion(version: Version?) -> Xcode
{
return Xcode(version: version!,
url: URL(fileURLWithPath: "https://developer.apple.com/Xcode_example.app"),
filename: "Xcode_example.app",
releaseDate: nil)
}

let versions = [

// Single version
Version(major: 1, minor: 1, patch: 1, prereleaseIdentifiers: [], buildMetadataIdentifiers: ["1.1.1.build1"]),

// 2 versions with the same build, one is beta
Version(major: 1, minor: 2, patch: 1, prereleaseIdentifiers: [], buildMetadataIdentifiers: ["1.2.1.build1"]),
Version(major: 1, minor: 2, patch: 1, prereleaseIdentifiers: ["beta"], buildMetadataIdentifiers: ["1.2.1.build1"]),

// 2 versions with the same build, one is beta (other GM)
Version(major: 1, minor: 3, patch: 1, prereleaseIdentifiers: ["GM"], buildMetadataIdentifiers: ["1.3.1.build1"]),
Version(major: 1, minor: 3, patch: 1, prereleaseIdentifiers: ["beta"], buildMetadataIdentifiers: ["1.3.1.build1"]),

// 2 versions with no buildMetaIdentifiers.
Version(major: 1, minor: 4, patch: 1, prereleaseIdentifiers: [], buildMetadataIdentifiers: []),
Version(major: 1, minor: 4, patch: 2, prereleaseIdentifiers: [], buildMetadataIdentifiers: []),
]

let filteredVersionsExpected = [
// Single version
Version(major: 1, minor: 1, patch: 1, prereleaseIdentifiers: [], buildMetadataIdentifiers: ["1.1.1.build1"]),

// 2 versions with the same build, one is beta
Version(major: 1, minor: 2, patch: 1, prereleaseIdentifiers: [], buildMetadataIdentifiers: ["1.2.1.build1"]),
//Version(major: 1, minor: 2, patch: 1, prereleaseIdentifiers: ["beta"], buildMetadataIdentifiers: ["1.2.1.build1"]),

// 2 versions with the same build, one is beta (other GM)
Version(major: 1, minor: 3, patch: 1, prereleaseIdentifiers: ["GM"], buildMetadataIdentifiers: ["1.3.1.build1"]),
//Version(major: 1, minor: 3, patch: 1, prereleaseIdentifiers: ["beta"], buildMetadataIdentifiers: ["1.3.1.build1"]),

// 2 versions with no buildMetaIdentifiers.
Version(major: 1, minor: 4, patch: 1, prereleaseIdentifiers: [], buildMetadataIdentifiers: []),
Version(major: 1, minor: 4, patch: 2, prereleaseIdentifiers: [], buildMetadataIdentifiers: []),
]

var filteredVersions : [Version] = []

override func setUpWithError() throws {
let xcodes = versions.map(xcodeFromVersion)
let filteredXcodes = XcodeList.filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers(xcodes)
filteredVersions = filteredXcodes.map { $0.version }
}

func test_filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers() {
XCTAssertEqual(filteredVersions.sorted(), filteredVersionsExpected.sorted())
}
}