diff --git a/src/toolchain/swiftly.ts b/src/toolchain/swiftly.ts index 1e2843cd8..8e64531e6 100644 --- a/src/toolchain/swiftly.ts +++ b/src/toolchain/swiftly.ts @@ -24,7 +24,9 @@ const ListResult = z.object({ toolchains: z.array( z.object({ inUse: z.boolean(), + installed: z.boolean(), isDefault: z.boolean(), + name: z.string(), version: z.discriminatedUnion("type", [ z.object({ major: z.number().optional(), @@ -125,7 +127,8 @@ export class Swiftly { const response = ListResult.parse(JSON.parse(stdout)); return response.toolchains.map(t => t.version.name); } catch (error) { - outputChannel?.appendLine(`Failed to retrieve Swiftly installations: ${error}`); + outputChannel?.appendLine( + `Failed to retrieve Swiftly installations : ${error}`); return []; } } @@ -155,7 +158,7 @@ export class Swiftly { } } - private static isSupported() { + public static isSupported() { return process.platform === "linux" || process.platform === "darwin"; } @@ -242,4 +245,20 @@ export class Swiftly { ); return JSON.parse(swiftlyConfigRaw); } + + public static async isInstalled() { + if (!Swiftly.isSupported()) { + return false; + } + + try { + await Swiftly.version(); + return true; + } catch (error) { + if (error instanceof ExecFileError && "code" in error && error.code === "ENOENT") { + return false; + } + throw error; + } + } } diff --git a/src/ui/ToolchainSelection.ts b/src/ui/ToolchainSelection.ts index 9cff98ebb..231a41872 100644 --- a/src/ui/ToolchainSelection.ts +++ b/src/ui/ToolchainSelection.ts @@ -257,7 +257,7 @@ async function getQuickPickItems( } // Various actions that the user can perform (e.g. to install new toolchains) const actionItems: ActionItem[] = []; - if (process.platform === "linux" || process.platform === "darwin") { + if (Swiftly.isSupported() && !(await Swiftly.isInstalled())) { const platformName = process.platform === "linux" ? "Linux" : "macOS"; actionItems.push({ type: "action", diff --git a/test/unit-tests/toolchain/swiftly.test.ts b/test/unit-tests/toolchain/swiftly.test.ts index fa48bea81..1219c481c 100644 --- a/test/unit-tests/toolchain/swiftly.test.ts +++ b/test/unit-tests/toolchain/swiftly.test.ts @@ -38,7 +38,9 @@ suite("Swiftly Unit Tests", () => { toolchains: [ { inUse: true, + installed: true, isDefault: true, + name: "swift-5.9.0-RELEASE", version: { major: 5, minor: 9, @@ -49,7 +51,9 @@ suite("Swiftly Unit Tests", () => { }, { inUse: false, + installed: true, isDefault: false, + name: "swift-5.8.0-RELEASE", version: { major: 5, minor: 8, @@ -60,7 +64,9 @@ suite("Swiftly Unit Tests", () => { }, { inUse: false, + installed: false, isDefault: false, + name: "swift-DEVELOPMENT-SNAPSHOT-2023-10-15-a", version: { major: 5, minor: 10, @@ -73,10 +79,12 @@ suite("Swiftly Unit Tests", () => { ], }; - mockUtilities.execFile.withArgs("swiftly", ["list", "--format=json"]).resolves({ - stdout: JSON.stringify(jsonOutput), - stderr: "", - }); + mockUtilities.execFile + .withArgs("swiftly", ["list", "--format=json"]) + .resolves({ + stdout: JSON.stringify(jsonOutput), + stderr: "", + }); const result = await Swiftly.listAvailableToolchains(); @@ -102,4 +110,46 @@ suite("Swiftly Unit Tests", () => { expect(mockUtilities.execFile).not.have.been.called; }); }); + + suite("isInstalled", () => { + test("should return false when platform is not supported", async () => { + mockedPlatform.setValue("win32"); + + const result = await Swiftly.isInstalled(); + + expect(result).to.be.false; + expect(mockUtilities.execFile).not.have.been.called; + }); + + test("should return true when swiftly is installed", async () => { + mockedPlatform.setValue("darwin"); + + mockUtilities.execFile.withArgs("swiftly", ["--version"]).resolves({ + stdout: "1.1.0\n", + stderr: "", + }); + + const result = await Swiftly.isInstalled(); + + expect(result).to.be.true; + expect(mockUtilities.execFile).to.have.been.calledWith("swiftly", ["--version"]); + }); + + test("should throw error when swiftly command fails with non-ENOENT error", async () => { + mockedPlatform.setValue("darwin"); + + const otherError = new Error("Other error"); + + mockUtilities.execFile.withArgs("swiftly", ["--version"]).rejects(otherError); + + try { + await Swiftly.isInstalled(); + expect.fail("Should have thrown an error"); + } catch (error) { + expect(error).to.equal(otherError); + } + + expect(mockUtilities.execFile).to.have.been.calledWith("swiftly", ["--version"]); + }); + }); });