Skip to content

Commit

Permalink
Add debugger integration test for project with local Bundler settings
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Nov 19, 2024
1 parent 25f00de commit aefa155
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 57 deletions.
8 changes: 4 additions & 4 deletions vscode/src/rubyLsp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ export class RubyLsp {

// Activate the extension. This method should perform all actions necessary to start the extension, such as booting
// all language servers for each existing workspace
async activate() {
await vscode.commands.executeCommand("testing.clearTestResults");

const firstWorkspace = vscode.workspace.workspaceFolders?.[0];
async activate(firstWorkspace = vscode.workspace.workspaceFolders?.[0]) {
if (this.context.extensionMode !== vscode.ExtensionMode.Test) {
await vscode.commands.executeCommand("testing.clearTestResults");
}

// We only activate the first workspace eagerly to avoid running into performance and memory issues. Having too many
// workspaces spawning the Ruby LSP server and indexing can grind the editor to a halt. All other workspaces are
Expand Down
56 changes: 3 additions & 53 deletions vscode/src/test/suite/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ import {
} from "vscode-languageclient/node";
import { after, afterEach, before } from "mocha";

import { Ruby, ManagerIdentifier } from "../../ruby";
import { Ruby } from "../../ruby";
import Client from "../../client";
import { WorkspaceChannel } from "../../workspaceChannel";
import { RUBY_VERSION } from "../rubyVersion";

import { FAKE_TELEMETRY } from "./fakeTelemetry";
import { ensureRubyInstallationPaths } from "./testHelpers";

const [major, minor, _patch] = RUBY_VERSION.split(".");

Expand Down Expand Up @@ -85,58 +86,7 @@ async function launchClient(workspaceUri: vscode.Uri) {
const fakeLogger = new FakeLogger();
const outputChannel = new WorkspaceChannel("fake", fakeLogger as any);

// Ensure that we're activating the correct Ruby version on CI
if (process.env.CI) {
if (os.platform() === "linux") {
await vscode.workspace
.getConfiguration("rubyLsp")
.update(
"rubyVersionManager",
{ identifier: ManagerIdentifier.Chruby },
true,
);

fs.mkdirSync(path.join(os.homedir(), ".rubies"), { recursive: true });
fs.symlinkSync(
`/opt/hostedtoolcache/Ruby/${RUBY_VERSION}/x64`,
path.join(os.homedir(), ".rubies", RUBY_VERSION),
);
} else if (os.platform() === "darwin") {
await vscode.workspace
.getConfiguration("rubyLsp")
.update(
"rubyVersionManager",
{ identifier: ManagerIdentifier.Chruby },
true,
);

fs.mkdirSync(path.join(os.homedir(), ".rubies"), { recursive: true });
fs.symlinkSync(
`/Users/runner/hostedtoolcache/Ruby/${RUBY_VERSION}/arm64`,
path.join(os.homedir(), ".rubies", RUBY_VERSION),
);
} else {
await vscode.workspace
.getConfiguration("rubyLsp")
.update(
"rubyVersionManager",
{ identifier: ManagerIdentifier.RubyInstaller },
true,
);

fs.symlinkSync(
path.join(
"C:",
"hostedtoolcache",
"windows",
"Ruby",
RUBY_VERSION,
"x64",
),
path.join("C:", `Ruby${major}${minor}-${os.arch()}`),
);
}
}
await ensureRubyInstallationPaths();

const ruby = new Ruby(
context,
Expand Down
131 changes: 131 additions & 0 deletions vscode/src/test/suite/rubyLsp.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import path from "path";
import assert from "assert";
import fs from "fs";
import os from "os";

import sinon from "sinon";
import * as vscode from "vscode";
import { beforeEach, afterEach, before, after } from "mocha";

import { RubyLsp } from "../../rubyLsp";
import { RUBY_VERSION } from "../rubyVersion";

import { FAKE_TELEMETRY } from "./fakeTelemetry";
import { ensureRubyInstallationPaths } from "./testHelpers";

suite("Ruby LSP", () => {
const context = {
extensionMode: vscode.ExtensionMode.Test,
subscriptions: [],
workspaceState: {
get: (_name: string) => undefined,
update: (_name: string, _value: any) => Promise.resolve(),
},
extensionUri: vscode.Uri.file(
path.dirname(path.dirname(path.dirname(__dirname))),
),
} as unknown as vscode.ExtensionContext;
let workspacePath: string;
let workspaceUri: vscode.Uri;
let workspaceFolder: vscode.WorkspaceFolder;
const originalSaveBeforeStart = vscode.workspace
.getConfiguration("debug")
.get("saveBeforeStart");

before(async () => {
await vscode.workspace
.getConfiguration("debug")
.update("saveBeforeStart", "none", true);
});

after(async () => {
await vscode.workspace
.getConfiguration("debug")
.update("saveBeforeStart", originalSaveBeforeStart, true);
});

beforeEach(() => {
workspacePath = fs.mkdtempSync(path.join(os.tmpdir(), "ruby-lsp-test-"));
workspaceUri = vscode.Uri.file(workspacePath);
workspaceFolder = {
uri: workspaceUri,
name: path.basename(workspacePath),
index: 0,
};
});

afterEach(() => {
fs.rmSync(workspacePath, { recursive: true, force: true });
});

test("launching debugger in a project with local Bundler settings and composed bundle", async () => {
fs.writeFileSync(path.join(workspacePath, "test.rb"), "1 + 1");
fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION);
fs.writeFileSync(
path.join(workspacePath, "Gemfile"),
'source "https://rubygems.org"\n',
);
fs.writeFileSync(
path.join(workspacePath, "Gemfile.lock"),
[
"GEM",
" remote: https://rubygems.org/",
" specs:",
"",
"PLATFORMS",
" arm64-darwin-23",
" ruby",
"",
"DEPENDENCIES",
"",
"BUNDLED WITH",
" 2.5.16",
].join("\n"),
);
fs.mkdirSync(path.join(workspacePath, ".bundle"));
fs.writeFileSync(
path.join(workspacePath, ".bundle", "config"),
"BUNDLE_PATH: vendor/bundle",
);

await ensureRubyInstallationPaths();

const rubyLsp = new RubyLsp(context, FAKE_TELEMETRY);
await rubyLsp.activate(workspaceFolder);

const stub = sinon.stub(vscode.window, "activeTextEditor").get(() => {
return {
document: {
uri: vscode.Uri.file(path.join(workspacePath, "test.rb")),
},
} as vscode.TextEditor;
});

const getWorkspaceStub = sinon
.stub(vscode.workspace, "getWorkspaceFolder")
.returns(workspaceFolder);

try {
await vscode.debug.startDebugging(workspaceFolder, {
type: "ruby_lsp",
name: "Debug",
request: "launch",
program: `ruby ${path.join(workspacePath, "test.rb")}`,
});
} catch (error: any) {
assert.fail(`Failed to launch debugger: ${error.message}`);
}

// The debugger might take a bit of time to disconnect from the editor. We need to perform cleanup when we receive
// the termination callback or else we try to dispose of the debugger client too early, but we need to wait for that
// so that we can clean up stubs otherwise they leak into other tests.
await new Promise<void>((resolve) => {
vscode.debug.onDidTerminateDebugSession((_session) => {
stub.restore();
getWorkspaceStub.restore();
context.subscriptions.forEach((subscription) => subscription.dispose());
resolve();
});
});
}).timeout(90000);
});
5 changes: 5 additions & 0 deletions vscode/src/test/suite/testController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as assert from "assert";

import * as vscode from "vscode";
import { CodeLens } from "vscode-languageclient/node";
import { afterEach } from "mocha";

import { TestController } from "../../testController";
import { Command } from "../../common";
Expand All @@ -18,6 +19,10 @@ suite("TestController", () => {
},
} as unknown as vscode.ExtensionContext;

afterEach(() => {
context.subscriptions.forEach((subscription) => subscription.dispose());
});

test("createTestItems doesn't break when there's a missing group", () => {
const controller = new TestController(
context,
Expand Down
66 changes: 66 additions & 0 deletions vscode/src/test/suite/testHelpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* eslint-disable no-process-env */

import os from "os";
import fs from "fs";
import path from "path";

import * as vscode from "vscode";

import { ManagerIdentifier } from "../../ruby";
import { RUBY_VERSION } from "../rubyVersion";

export async function ensureRubyInstallationPaths() {
const [major, minor, _patch] = RUBY_VERSION.split(".");
// Ensure that we're activating the correct Ruby version on CI
if (process.env.CI) {
if (os.platform() === "linux") {
await vscode.workspace
.getConfiguration("rubyLsp")
.update(
"rubyVersionManager",
{ identifier: ManagerIdentifier.Chruby },
true,
);

fs.mkdirSync(path.join(os.homedir(), ".rubies"), { recursive: true });
fs.symlinkSync(
`/opt/hostedtoolcache/Ruby/${RUBY_VERSION}/x64`,
path.join(os.homedir(), ".rubies", RUBY_VERSION),
);
} else if (os.platform() === "darwin") {
await vscode.workspace
.getConfiguration("rubyLsp")
.update(
"rubyVersionManager",
{ identifier: ManagerIdentifier.Chruby },
true,
);

fs.mkdirSync(path.join(os.homedir(), ".rubies"), { recursive: true });
fs.symlinkSync(
`/Users/runner/hostedtoolcache/Ruby/${RUBY_VERSION}/arm64`,
path.join(os.homedir(), ".rubies", RUBY_VERSION),
);
} else {
await vscode.workspace
.getConfiguration("rubyLsp")
.update(
"rubyVersionManager",
{ identifier: ManagerIdentifier.RubyInstaller },
true,
);

fs.symlinkSync(
path.join(
"C:",
"hostedtoolcache",
"windows",
"Ruby",
RUBY_VERSION,
"x64",
),
path.join("C:", `Ruby${major}${minor}-${os.arch()}`),
);
}
}
}

0 comments on commit aefa155

Please sign in to comment.