mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-25 08:55:46 +00:00
Merge pull request #100 from RobotsAndPencils/identical-builds
Improve how identical builds are represented in the UI
This commit is contained in:
commit
9eda0984ac
9 changed files with 286 additions and 75 deletions
|
|
@ -6,10 +6,6 @@ import SwiftSoup
|
|||
import struct XCModel.Xcode
|
||||
|
||||
extension AppState {
|
||||
private var dataSource: DataSource {
|
||||
Current.defaults.string(forKey: "dataSource").flatMap(DataSource.init(rawValue:)) ?? .default
|
||||
}
|
||||
|
||||
func updateIfNeeded() {
|
||||
guard
|
||||
let lastUpdated = Current.defaults.date(forKey: "lastUpdated"),
|
||||
|
|
@ -200,31 +196,6 @@ extension AppState {
|
|||
}
|
||||
return xcodes
|
||||
}
|
||||
.map(filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
/// 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(_ availableXcodes: [AvailableXcode]) -> [AvailableXcode] {
|
||||
var filteredAvailableXcodes: [AvailableXcode] = []
|
||||
for availableXcode in availableXcodes {
|
||||
if availableXcode.version.buildMetadataIdentifiers.isEmpty {
|
||||
filteredAvailableXcodes.append(availableXcode)
|
||||
continue
|
||||
}
|
||||
|
||||
let availableXcodesWithSameBuildMetadataIdentifiers = availableXcodes
|
||||
.filter({ $0.version.buildMetadataIdentifiers == availableXcode.version.buildMetadataIdentifiers })
|
||||
if availableXcodesWithSameBuildMetadataIdentifiers.count > 1,
|
||||
availableXcode.version.prereleaseIdentifiers.isEmpty || availableXcode.version.prereleaseIdentifiers == ["GM"] {
|
||||
filteredAvailableXcodes.append(availableXcode)
|
||||
} else if availableXcodesWithSameBuildMetadataIdentifiers.count == 1 {
|
||||
filteredAvailableXcodes.append(availableXcode)
|
||||
}
|
||||
}
|
||||
return filteredAvailableXcodes
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,12 @@ class AppState: ObservableObject {
|
|||
private var selectPublisher: AnyCancellable?
|
||||
private var uninstallPublisher: AnyCancellable?
|
||||
|
||||
// MARK: -
|
||||
|
||||
var dataSource: DataSource {
|
||||
Current.defaults.string(forKey: "dataSource").flatMap(DataSource.init(rawValue:)) ?? .default
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
init() {
|
||||
|
|
@ -422,29 +428,58 @@ class AppState: ObservableObject {
|
|||
var adjustedAvailableXcodes = availableXcodes
|
||||
|
||||
// First, adjust all of the available Xcodes so that available and installed versions line up and the second part of this function works properly.
|
||||
for installedXcode in installedXcodes {
|
||||
// We can trust that build metadata identifiers are unique for each version of Xcode, so if we have it then it's all we need.
|
||||
// If build metadata matches exactly, replace the available version with the installed version.
|
||||
// This should handle both Xcode Releases versions which can have different prerelease identifiers and Apple versions which rarely have build metadata identifiers.
|
||||
if let index = adjustedAvailableXcodes.map(\.version).firstIndex(where: { $0.buildMetadataIdentifiers == installedXcode.version.buildMetadataIdentifiers }) {
|
||||
adjustedAvailableXcodes[index].version = installedXcode.version
|
||||
}
|
||||
// If an installed version is the same as one that's listed online which doesn't have build metadata, replace it with the installed version
|
||||
// Not all prerelease Apple versions available online include build metadata
|
||||
else if let index = adjustedAvailableXcodes.firstIndex(where: { availableXcode in
|
||||
availableXcode.version.isEquivalent(to: installedXcode.version) &&
|
||||
availableXcode.version.buildMetadataIdentifiers.isEmpty
|
||||
}) {
|
||||
adjustedAvailableXcodes[index].version = installedXcode.version
|
||||
if dataSource == .apple {
|
||||
for installedXcode in installedXcodes {
|
||||
// We can trust that build metadata identifiers are unique for each version of Xcode, so if we have it then it's all we need.
|
||||
// If build metadata matches exactly, replace the available version with the installed version.
|
||||
// This should handle Apple versions from /downloads/more which don't have build metadata identifiers.
|
||||
if let index = adjustedAvailableXcodes.map(\.version).firstIndex(where: { $0.buildMetadataIdentifiers == installedXcode.version.buildMetadataIdentifiers }) {
|
||||
adjustedAvailableXcodes[index].version = installedXcode.version
|
||||
}
|
||||
// If an installed version is the same as one that's listed online which doesn't have build metadata, replace it with the installed version
|
||||
// Not all prerelease Apple versions available online include build metadata
|
||||
else if let index = adjustedAvailableXcodes.firstIndex(where: { availableXcode in
|
||||
availableXcode.version.isEquivalent(to: installedXcode.version) &&
|
||||
availableXcode.version.buildMetadataIdentifiers.isEmpty
|
||||
}) {
|
||||
adjustedAvailableXcodes[index].version = installedXcode.version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map all of the available versions into Xcode values that join available and installed Xcode data for display.
|
||||
var newAllXcodes = adjustedAvailableXcodes
|
||||
.filter { availableXcode in
|
||||
// If we don't have the build identifier, don't attempt to filter prerelease versions with identical build identifiers
|
||||
guard !availableXcode.version.buildMetadataIdentifiers.isEmpty else { return true }
|
||||
|
||||
let availableXcodesWithIdenticalBuildIdentifiers = availableXcodes
|
||||
.filter({ $0.version.buildMetadataIdentifiers == availableXcode.version.buildMetadataIdentifiers })
|
||||
|
||||
// Include this version if there's only one with this build identifier
|
||||
return availableXcodesWithIdenticalBuildIdentifiers.count == 1 ||
|
||||
// Or if there's more than one with this build identifier and this is the release version
|
||||
availableXcodesWithIdenticalBuildIdentifiers.count > 1 && availableXcode.version.prereleaseIdentifiers.isEmpty
|
||||
}
|
||||
.map { availableXcode -> Xcode in
|
||||
let installedXcode = installedXcodes.first(where: { installedXcode in
|
||||
availableXcode.version.isEquivalent(to: installedXcode.version)
|
||||
})
|
||||
|
||||
let identicalBuilds: [Version]
|
||||
let prereleaseAvailableXcodesWithIdenticalBuildIdentifiers = availableXcodes
|
||||
.filter {
|
||||
return $0.version.buildMetadataIdentifiers == availableXcode.version.buildMetadataIdentifiers &&
|
||||
!$0.version.prereleaseIdentifiers.isEmpty &&
|
||||
// If we don't have the build identifier, don't consider this as a potential identical build
|
||||
!$0.version.buildMetadataIdentifiers.isEmpty
|
||||
}
|
||||
// If this is the release version, add the identical builds to it
|
||||
if !prereleaseAvailableXcodesWithIdenticalBuildIdentifiers.isEmpty, availableXcode.version.prereleaseIdentifiers.isEmpty {
|
||||
identicalBuilds = [availableXcode.version] + prereleaseAvailableXcodesWithIdenticalBuildIdentifiers.map(\.version)
|
||||
} else {
|
||||
identicalBuilds = []
|
||||
}
|
||||
|
||||
// If the existing install state is "installing", keep it
|
||||
let existingXcodeInstallState = allXcodes.first { $0.version == availableXcode.version && $0.installState.installing }?.installState
|
||||
|
|
@ -453,6 +488,7 @@ class AppState: ObservableObject {
|
|||
|
||||
return Xcode(
|
||||
version: availableXcode.version,
|
||||
identicalBuilds: identicalBuilds,
|
||||
installState: existingXcodeInstallState ?? defaultXcodeInstallState,
|
||||
selected: installedXcode != nil && selectedXcodePath?.hasPrefix(installedXcode!.path.string) == true,
|
||||
icon: (installedXcode?.path.string).map(NSWorkspace.shared.icon(forFile:)),
|
||||
|
|
|
|||
|
|
@ -50,7 +50,13 @@ public extension Version {
|
|||
}
|
||||
if !prereleaseIdentifiers.isEmpty {
|
||||
base += " " + prereleaseIdentifiers
|
||||
.map { $0.replacingOccurrences(of: "-", with: " ").capitalized.replacingOccurrences(of: "Gm", with: "GM") }
|
||||
.map { identifiers in
|
||||
identifiers
|
||||
.replacingOccurrences(of: "-", with: " ")
|
||||
.capitalized
|
||||
.replacingOccurrences(of: "Gm", with: "GM")
|
||||
.replacingOccurrences(of: "Rc", with: "RC")
|
||||
}
|
||||
.joined(separator: " ")
|
||||
}
|
||||
return base
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ extension Version {
|
|||
versionString += ".\(dp)"
|
||||
}
|
||||
case .gm:
|
||||
versionString += "-GM"
|
||||
break
|
||||
case let .gmSeed(gmSeed):
|
||||
versionString += "-GM.Seed"
|
||||
if gmSeed > 1 {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import struct XCModel.Compilers
|
|||
|
||||
struct Xcode: Identifiable, CustomStringConvertible {
|
||||
let version: Version
|
||||
/// Other Xcode versions that have the same build identifier
|
||||
let identicalBuilds: [Version]
|
||||
var installState: XcodeInstallState
|
||||
let selected: Bool
|
||||
let icon: NSImage?
|
||||
|
|
@ -17,6 +19,7 @@ struct Xcode: Identifiable, CustomStringConvertible {
|
|||
|
||||
init(
|
||||
version: Version,
|
||||
identicalBuilds: [Version] = [],
|
||||
installState: XcodeInstallState,
|
||||
selected: Bool,
|
||||
icon: NSImage?,
|
||||
|
|
@ -27,6 +30,7 @@ struct Xcode: Identifiable, CustomStringConvertible {
|
|||
downloadFileSize: Int64? = nil
|
||||
) {
|
||||
self.version = version
|
||||
self.identicalBuilds = identicalBuilds
|
||||
self.installState = installState
|
||||
self.selected = selected
|
||||
self.icon = icon
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ struct InfoPane: View {
|
|||
@SwiftUI.Environment(\.openURL) var openURL: OpenURLAction
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if let xcode = appState.allXcodes.first(where: { $0.id == selectedXcodeID }) {
|
||||
if let xcode = appState.allXcodes.first(where: { $0.id == selectedXcodeID }) {
|
||||
ScrollView {
|
||||
VStack(spacing: 16) {
|
||||
icon(for: xcode)
|
||||
|
||||
|
|
@ -53,6 +53,7 @@ struct InfoPane: View {
|
|||
Divider()
|
||||
|
||||
releaseNotes(for: xcode)
|
||||
identicalBuilds(for: xcode)
|
||||
compatibility(for: xcode)
|
||||
sdks(for: xcode)
|
||||
compilers(for: xcode)
|
||||
|
|
@ -60,12 +61,13 @@ struct InfoPane: View {
|
|||
|
||||
Spacer()
|
||||
}
|
||||
} else {
|
||||
empty
|
||||
.padding()
|
||||
}
|
||||
.frame(minWidth: 200, maxWidth: .infinity)
|
||||
} else {
|
||||
empty
|
||||
.frame(minWidth: 200, maxWidth: .infinity)
|
||||
}
|
||||
.padding()
|
||||
.frame(minWidth: 200, maxWidth: .infinity)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
|
@ -80,6 +82,34 @@ struct InfoPane: View {
|
|||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func identicalBuilds(for xcode: Xcode) -> some View {
|
||||
if !xcode.identicalBuilds.isEmpty {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text("Identical Builds")
|
||||
Image(systemName: "square.fill.on.square.fill")
|
||||
.foregroundColor(.secondary)
|
||||
.accessibility(hidden: true)
|
||||
.help("Sometimes a prerelease and release version are the exact same build. Xcodes will automatically display these versions together.")
|
||||
}
|
||||
.font(.headline)
|
||||
|
||||
ForEach(xcode.identicalBuilds, id: \.description) { version in
|
||||
Text("• \(version.appleDescription)")
|
||||
.font(.subheadline)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.accessibilityElement()
|
||||
.accessibility(label: Text("Identical Builds"))
|
||||
.accessibility(value: Text(xcode.identicalBuilds.map(\.appleDescription).joined(separator: ", ")))
|
||||
.accessibility(hint: Text("Sometimes a prerelease and release version are the exact same build. Xcodes will automatically display these versions together."))
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func releaseNotes(for xcode: Xcode) -> some View {
|
||||
if let releaseNotesURL = xcode.releaseNotesURL {
|
||||
|
|
@ -183,13 +213,11 @@ struct InfoPane: View {
|
|||
|
||||
@ViewBuilder
|
||||
private var empty: some View {
|
||||
VStack {
|
||||
Spacer()
|
||||
Text("No Xcode Selected")
|
||||
.font(.title)
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
}
|
||||
Text("No Xcode Selected")
|
||||
.font(.title)
|
||||
.foregroundColor(.secondary)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ struct XcodeListView: View {
|
|||
|
||||
var body: some View {
|
||||
List(visibleXcodes, selection: $selectedXcodeID) { xcode in
|
||||
XcodeListViewRow(xcode: xcode, selected: selectedXcodeID == xcode.id)
|
||||
XcodeListViewRow(xcode: xcode, selected: selectedXcodeID == xcode.id, appState: appState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ struct XcodeListView_Previews: PreviewProvider {
|
|||
.environmentObject({ () -> AppState in
|
||||
let a = AppState()
|
||||
a.allXcodes = [
|
||||
Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [Version("12.0.0+1234A")!, Version("12.0.0-RC+1234A")!], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
Xcode(version: Version("12.3.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: nil),
|
||||
Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, icon: nil),
|
||||
Xcode(version: Version("12.1.0")!, installState: .installing(.downloading(progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 })), selected: false, icon: nil),
|
||||
|
|
|
|||
|
|
@ -3,17 +3,28 @@ import SwiftUI
|
|||
import Version
|
||||
|
||||
struct XcodeListViewRow: View {
|
||||
@EnvironmentObject var appState: AppState
|
||||
let xcode: Xcode
|
||||
let selected: Bool
|
||||
let appState: AppState
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
appIconView(for: xcode)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text(verbatim: "\(xcode.description) \(xcode.version.buildMetadataIdentifiersDisplay)")
|
||||
.font(.body)
|
||||
HStack {
|
||||
Text(verbatim: "\(xcode.description) \(xcode.version.buildMetadataIdentifiersDisplay)")
|
||||
.font(.body)
|
||||
|
||||
if !xcode.identicalBuilds.isEmpty {
|
||||
Image(systemName: "square.fill.on.square.fill")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.accessibility(label: Text("Identical Builds"))
|
||||
.accessibility(value: Text(xcode.identicalBuilds.map(\.appleDescription).joined(separator: ", ")))
|
||||
.help("Sometimes a prerelease and release version are the exact same build. Xcodes will automatically display these versions together.")
|
||||
}
|
||||
}
|
||||
|
||||
if case let .installed(path) = xcode.installState {
|
||||
Text(verbatim: path.string)
|
||||
|
|
@ -112,29 +123,39 @@ struct XcodeListViewRow_Previews: PreviewProvider {
|
|||
Group {
|
||||
XcodeListViewRow(
|
||||
xcode: Xcode(version: Version("12.3.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: true, icon: nil),
|
||||
selected: false
|
||||
selected: false,
|
||||
appState: AppState()
|
||||
)
|
||||
|
||||
XcodeListViewRow(
|
||||
xcode: Xcode(version: Version("12.2.0")!, installState: .notInstalled, selected: false, icon: nil),
|
||||
selected: false
|
||||
selected: false,
|
||||
appState: AppState()
|
||||
)
|
||||
|
||||
XcodeListViewRow(
|
||||
xcode: Xcode(version: Version("12.1.0")!, installState: .installing(.downloading(progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 })), selected: false, icon: nil),
|
||||
selected: false
|
||||
selected: false,
|
||||
appState: AppState()
|
||||
)
|
||||
|
||||
XcodeListViewRow(
|
||||
xcode: Xcode(version: Version("12.0.0")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
selected: false
|
||||
selected: false,
|
||||
appState: AppState()
|
||||
)
|
||||
|
||||
XcodeListViewRow(
|
||||
xcode: Xcode(version: Version("12.0.0+1234A")!, installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
selected: false
|
||||
selected: false,
|
||||
appState: AppState()
|
||||
)
|
||||
|
||||
XcodeListViewRow(
|
||||
xcode: Xcode(version: Version("12.0.0+1234A")!, identicalBuilds: [Version("12.0.0-RC+1234A")!], installState: .installed(Path("/Applications/Xcode-12.3.0.app")!), selected: false, icon: nil),
|
||||
selected: false,
|
||||
appState: AppState()
|
||||
)
|
||||
}
|
||||
.environmentObject(AppState())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,14 @@ class AppStateUpdateTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testDeterminesIfInstalledByBuildMetadataAlone() throws {
|
||||
Current.defaults.string = { key in
|
||||
if key == "dataSource" {
|
||||
return "apple"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
subject.allXcodes = [
|
||||
]
|
||||
|
||||
|
|
@ -66,6 +74,14 @@ class AppStateUpdateTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testAdjustedVersionsAreUsedToLookupAvailableXcode() throws {
|
||||
Current.defaults.string = { key in
|
||||
if key == "dataSource" {
|
||||
return "apple"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
subject.allXcodes = [
|
||||
]
|
||||
|
||||
|
|
@ -105,13 +121,141 @@ class AppStateUpdateTests: XCTestCase {
|
|||
XCTAssertEqual(subject.allXcodes.map(\.version), [Version("1.2.3")!, Version("0.0.0+ABC123")!])
|
||||
}
|
||||
|
||||
func testFilterReleasesThatMatchPrereleases() {
|
||||
let result = subject.filterPrereleasesThatMatchReleaseBuildMetadataIdentifiers(
|
||||
[
|
||||
AvailableXcode(version: Version("12.3.0+12C33")!, url: URL(string: "https://apple.com")!, filename: "Xcode_12.3.xip", releaseDate: nil),
|
||||
AvailableXcode(version: Version("12.3.0-RC+12C33")!, url: URL(string: "https://apple.com")!, filename: "Xcode_12.3_RC_1.xip", releaseDate: nil),
|
||||
]
|
||||
|
||||
func testIdenticalBuilds_KeepsReleaseVersion_WithNeitherInstalled() {
|
||||
Current.defaults.string = { key in
|
||||
if key == "dataSource" {
|
||||
return "xcodeReleases"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
subject.allXcodes = [
|
||||
]
|
||||
|
||||
subject.updateAllXcodes(
|
||||
availableXcodes: [
|
||||
AvailableXcode(version: Version("12.4.0+12D4e")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil),
|
||||
AvailableXcode(version: Version("12.4.0-RC+12D4e")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil),
|
||||
],
|
||||
installedXcodes: [
|
||||
],
|
||||
selectedXcodePath: nil
|
||||
)
|
||||
XCTAssertEqual(result.map(\.version), [Version("12.3.0+12C33")])
|
||||
|
||||
XCTAssertEqual(subject.allXcodes.map(\.version), [Version("12.4.0+12D4e")!])
|
||||
XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[Version("12.4.0+12D4e")!, Version("12.4.0-RC+12D4e")!]])
|
||||
}
|
||||
|
||||
func testIdenticalBuilds_DoNotMergeReleaseVersions() {
|
||||
Current.defaults.string = { key in
|
||||
if key == "dataSource" {
|
||||
return "xcodeReleases"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
subject.allXcodes = [
|
||||
]
|
||||
|
||||
subject.updateAllXcodes(
|
||||
availableXcodes: [
|
||||
AvailableXcode(version: Version("3.2.3+10M2262")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil),
|
||||
AvailableXcode(version: Version("3.2.3+10M2262")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil),
|
||||
],
|
||||
installedXcodes: [
|
||||
],
|
||||
selectedXcodePath: nil
|
||||
)
|
||||
|
||||
XCTAssertEqual(subject.allXcodes.map(\.version), [Version("3.2.3+10M2262")!, Version("3.2.3+10M2262")!])
|
||||
XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[], []])
|
||||
}
|
||||
|
||||
func testIdenticalBuilds_KeepsReleaseVersion_WithPrereleaseInstalled() {
|
||||
Current.defaults.string = { key in
|
||||
if key == "dataSource" {
|
||||
return "xcodeReleases"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
subject.allXcodes = [
|
||||
]
|
||||
|
||||
Current.files.contentsAtPath = { path in
|
||||
if path.contains("Info.plist") {
|
||||
return """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.apple.dt.Xcode</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>12.4.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
""".data(using: .utf8)
|
||||
}
|
||||
else if path.contains("version.plist") {
|
||||
return """
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ProductBuildVersion</key>
|
||||
<string>12D4e</string>
|
||||
</dict>
|
||||
</plist>
|
||||
""".data(using: .utf8)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
subject.updateAllXcodes(
|
||||
availableXcodes: [
|
||||
AvailableXcode(version: Version("12.4.0+12D4e")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil),
|
||||
AvailableXcode(version: Version("12.4.0-RC+12D4e")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil),
|
||||
],
|
||||
installedXcodes: [
|
||||
InstalledXcode(path: Path("/Applications/Xcode-12.4.0-RC.app")!)!
|
||||
],
|
||||
selectedXcodePath: nil
|
||||
)
|
||||
|
||||
XCTAssertEqual(subject.allXcodes.map(\.version), [Version("12.4.0+12D4e")!])
|
||||
XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[Version("12.4.0+12D4e")!, Version("12.4.0-RC+12D4e")!]])
|
||||
}
|
||||
|
||||
func testIdenticalBuilds_AppleDataSource_DoNotMergeVersionsWithoutBuildIdentifiers() {
|
||||
Current.defaults.string = { key in
|
||||
if key == "dataSource" {
|
||||
return "apple"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
subject.allXcodes = [
|
||||
]
|
||||
|
||||
subject.updateAllXcodes(
|
||||
availableXcodes: [
|
||||
AvailableXcode(version: Version("12.4.0")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil),
|
||||
AvailableXcode(version: Version("12.3.0-RC")!, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil),
|
||||
],
|
||||
installedXcodes: [
|
||||
],
|
||||
selectedXcodePath: nil
|
||||
)
|
||||
|
||||
XCTAssertEqual(subject.allXcodes.map(\.version), [Version("12.4.0")!, Version("12.3.0-RC")!])
|
||||
XCTAssertEqual(subject.allXcodes.map(\.identicalBuilds), [[], []])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue