Skip to content

Commit b065cb4

Browse files
authored
Merge pull request #827 from XcodesOrg/matt/deleteCrash
fix: crash on delete when version does not exist
2 parents fc82f6d + 7a4282a commit b065cb4

2 files changed

Lines changed: 51 additions & 2 deletions

File tree

‎Xcodes/Backend/AppState.swift‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,7 @@ class AppState: ObservableObject {
685685
try await uninstallXcodeAsync(path: installedXcodePath)
686686
try Task.checkCancellation()
687687
await updateSelectedXcodePathAsync()
688+
await updateInstalledXcodesAsync()
688689
} catch is CancellationError {
689690
} catch {
690691
self.error = error
@@ -881,11 +882,13 @@ class AppState: ObservableObject {
881882
// MARK: - Private
882883

883884
private func uninstallXcodeAsync(path: Path) async throws {
884-
let xcode = InstalledXcode(
885+
guard let xcode = InstalledXcode(
885886
path: path,
886887
contentsAtPath: { path in Current.files.contents(atPath: path) },
887888
loadArchitectures: Current.shell.archs
888-
)!
889+
) else {
890+
throw FileError.fileNotFound(path.string)
891+
}
889892
_ = try XcodeUninstallService(
890893
removeItem: { url in try Current.files.removeItem(at: url) },
891894
trashItem: { url in try Current.files.trashItem(at: url) }

‎XcodesTests/AppStateTests.swift‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,52 @@ class AppStateTests: XCTestCase {
252252
XCTAssertEqual(subject.selectedXcodePath, secondPath.string)
253253
}
254254

255+
func test_Uninstall_MissingXcodePresentsFileNotFoundError() async throws {
256+
let missingPath = try XCTUnwrap(Path("/Applications/Xcode-Missing.app"))
257+
let xcode = Xcode(version: Version("15.0.0")!, installState: .installed(missingPath), selected: false, icon: nil)
258+
let didTryToTrashItem = TestLockedBox(false)
259+
Current.files.contentsAtPath = { _ in nil }
260+
Current.files.trashItem = { _ in
261+
didTryToTrashItem.withValue { $0 = true }
262+
return URL(fileURLWithPath: "\(NSHomeDirectory())/.Trash")
263+
}
264+
265+
subject.uninstall(xcode: xcode)
266+
let uninstallTask = try XCTUnwrap(subject.uninstallTask)
267+
await uninstallTask.value
268+
269+
guard case let .generic(title, message) = subject.presentedAlert else {
270+
return XCTFail("Expected generic uninstall error alert")
271+
}
272+
XCTAssertEqual(title, localizeString("Alert.Uninstall.Error.Title"))
273+
XCTAssertEqual(
274+
message,
275+
String(format: localizeString("Alert.Uninstall.Error.Message.FileNotFound"), missingPath.string)
276+
)
277+
XCTAssertFalse(didTryToTrashItem.read { $0 })
278+
}
279+
280+
func test_Uninstall_RefreshesInstalledXcodeList() async throws {
281+
let installedPath = try XCTUnwrap(Path("/Applications/Xcode-0.0.0.app"))
282+
let version = try XCTUnwrap(Version("0.0.0"))
283+
subject.availableXcodes = [
284+
AvailableXcode(version: version, url: URL(string: "https://apple.com/xcode.xip")!, filename: "mock.xip", releaseDate: nil)
285+
]
286+
subject.allXcodes = [
287+
Xcode(version: version, installState: .installed(installedPath), selected: true, icon: nil)
288+
]
289+
Current.files.installedXcodes = { _ in [] }
290+
Current.shell.xcodeSelectPrintPath = {
291+
ProcessOutput(status: 0, out: "", err: "")
292+
}
293+
294+
subject.uninstall(xcode: subject.allXcodes[0])
295+
let uninstallTask = try XCTUnwrap(subject.uninstallTask)
296+
await uninstallTask.value
297+
298+
XCTAssertEqual(subject.allXcodes[0].installState, .notInstalled)
299+
}
300+
255301
func test_Signout_RemovesCookiesFromDownloadSession() throws {
256302
let session = URLSession(configuration: .ephemeral)
257303
Current.network.session = session

0 commit comments

Comments
 (0)