Skip to content

Commit c046f31

Browse files
committed
Add debugger integration test for project with local Bundler settings
1 parent 0915b82 commit c046f31

File tree

5 files changed

+209
-57
lines changed

5 files changed

+209
-57
lines changed

vscode/src/rubyLsp.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,10 @@ export class RubyLsp {
119119

120120
// Activate the extension. This method should perform all actions necessary to start the extension, such as booting
121121
// all language servers for each existing workspace
122-
async activate() {
123-
await vscode.commands.executeCommand("testing.clearTestResults");
124-
125-
const firstWorkspace = vscode.workspace.workspaceFolders?.[0];
122+
async activate(firstWorkspace = vscode.workspace.workspaceFolders?.[0]) {
123+
if (this.context.extensionMode !== vscode.ExtensionMode.Test) {
124+
await vscode.commands.executeCommand("testing.clearTestResults");
125+
}
126126

127127
// We only activate the first workspace eagerly to avoid running into performance and memory issues. Having too many
128128
// workspaces spawning the Ruby LSP server and indexing can grind the editor to a halt. All other workspaces are

vscode/src/test/suite/client.test.ts

+3-53
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ import {
2626
} from "vscode-languageclient/node";
2727
import { after, afterEach, before } from "mocha";
2828

29-
import { Ruby, ManagerIdentifier } from "../../ruby";
29+
import { Ruby } from "../../ruby";
3030
import Client from "../../client";
3131
import { WorkspaceChannel } from "../../workspaceChannel";
3232
import { RUBY_VERSION } from "../rubyVersion";
3333

3434
import { FAKE_TELEMETRY } from "./fakeTelemetry";
35+
import { ensureRubyInstallationPaths } from "./testHelpers";
3536

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

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

88-
// Ensure that we're activating the correct Ruby version on CI
89-
if (process.env.CI) {
90-
if (os.platform() === "linux") {
91-
await vscode.workspace
92-
.getConfiguration("rubyLsp")
93-
.update(
94-
"rubyVersionManager",
95-
{ identifier: ManagerIdentifier.Chruby },
96-
true,
97-
);
98-
99-
fs.mkdirSync(path.join(os.homedir(), ".rubies"), { recursive: true });
100-
fs.symlinkSync(
101-
`/opt/hostedtoolcache/Ruby/${RUBY_VERSION}/x64`,
102-
path.join(os.homedir(), ".rubies", RUBY_VERSION),
103-
);
104-
} else if (os.platform() === "darwin") {
105-
await vscode.workspace
106-
.getConfiguration("rubyLsp")
107-
.update(
108-
"rubyVersionManager",
109-
{ identifier: ManagerIdentifier.Chruby },
110-
true,
111-
);
112-
113-
fs.mkdirSync(path.join(os.homedir(), ".rubies"), { recursive: true });
114-
fs.symlinkSync(
115-
`/Users/runner/hostedtoolcache/Ruby/${RUBY_VERSION}/arm64`,
116-
path.join(os.homedir(), ".rubies", RUBY_VERSION),
117-
);
118-
} else {
119-
await vscode.workspace
120-
.getConfiguration("rubyLsp")
121-
.update(
122-
"rubyVersionManager",
123-
{ identifier: ManagerIdentifier.RubyInstaller },
124-
true,
125-
);
126-
127-
fs.symlinkSync(
128-
path.join(
129-
"C:",
130-
"hostedtoolcache",
131-
"windows",
132-
"Ruby",
133-
RUBY_VERSION,
134-
"x64",
135-
),
136-
path.join("C:", `Ruby${major}${minor}-${os.arch()}`),
137-
);
138-
}
139-
}
89+
await ensureRubyInstallationPaths();
14090

14191
const ruby = new Ruby(
14292
context,

vscode/src/test/suite/rubyLsp.test.ts

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import path from "path";
2+
import assert from "assert";
3+
import fs from "fs";
4+
import os from "os";
5+
6+
import sinon from "sinon";
7+
import * as vscode from "vscode";
8+
import { beforeEach, afterEach, before, after } from "mocha";
9+
10+
import { RubyLsp } from "../../rubyLsp";
11+
import { RUBY_VERSION } from "../rubyVersion";
12+
13+
import { FAKE_TELEMETRY } from "./fakeTelemetry";
14+
import { ensureRubyInstallationPaths } from "./testHelpers";
15+
16+
suite("Ruby LSP", () => {
17+
const context = {
18+
extensionMode: vscode.ExtensionMode.Test,
19+
subscriptions: [],
20+
workspaceState: {
21+
get: (_name: string) => undefined,
22+
update: (_name: string, _value: any) => Promise.resolve(),
23+
},
24+
extensionUri: vscode.Uri.file(
25+
path.dirname(path.dirname(path.dirname(__dirname))),
26+
),
27+
} as unknown as vscode.ExtensionContext;
28+
let workspacePath: string;
29+
let workspaceUri: vscode.Uri;
30+
let workspaceFolder: vscode.WorkspaceFolder;
31+
const originalSaveBeforeStart = vscode.workspace
32+
.getConfiguration("debug")
33+
.get("saveBeforeStart");
34+
35+
before(async () => {
36+
await vscode.workspace
37+
.getConfiguration("debug")
38+
.update("saveBeforeStart", "none", true);
39+
});
40+
41+
after(async () => {
42+
await vscode.workspace
43+
.getConfiguration("debug")
44+
.update("saveBeforeStart", originalSaveBeforeStart, true);
45+
});
46+
47+
beforeEach(() => {
48+
workspacePath = fs.mkdtempSync(path.join(os.tmpdir(), "ruby-lsp-test-"));
49+
workspaceUri = vscode.Uri.file(workspacePath);
50+
workspaceFolder = {
51+
uri: workspaceUri,
52+
name: path.basename(workspacePath),
53+
index: 0,
54+
};
55+
});
56+
57+
afterEach(() => {
58+
fs.rmSync(workspacePath, { recursive: true, force: true });
59+
});
60+
61+
test("launching debugger in a project with local Bundler settings and composed bundle", async () => {
62+
fs.writeFileSync(path.join(workspacePath, "test.rb"), "1 + 1");
63+
fs.writeFileSync(path.join(workspacePath, ".ruby-version"), RUBY_VERSION);
64+
fs.writeFileSync(
65+
path.join(workspacePath, "Gemfile"),
66+
'source "https://rubygems.org"\n',
67+
);
68+
fs.writeFileSync(
69+
path.join(workspacePath, "Gemfile.lock"),
70+
[
71+
"GEM",
72+
" remote: https://rubygems.org/",
73+
" specs:",
74+
"",
75+
"PLATFORMS",
76+
" arm64-darwin-23",
77+
" ruby",
78+
"",
79+
"DEPENDENCIES",
80+
"",
81+
"BUNDLED WITH",
82+
" 2.5.16",
83+
].join("\n"),
84+
);
85+
fs.mkdirSync(path.join(workspacePath, ".bundle"));
86+
fs.writeFileSync(
87+
path.join(workspacePath, ".bundle", "config"),
88+
"BUNDLE_PATH: vendor/bundle",
89+
);
90+
91+
await ensureRubyInstallationPaths();
92+
93+
const rubyLsp = new RubyLsp(context, FAKE_TELEMETRY);
94+
await rubyLsp.activate(workspaceFolder);
95+
96+
const stub = sinon.stub(vscode.window, "activeTextEditor").get(() => {
97+
return {
98+
document: {
99+
uri: vscode.Uri.file(path.join(workspacePath, "test.rb")),
100+
},
101+
} as vscode.TextEditor;
102+
});
103+
104+
const getWorkspaceStub = sinon
105+
.stub(vscode.workspace, "getWorkspaceFolder")
106+
.returns(workspaceFolder);
107+
108+
try {
109+
await vscode.debug.startDebugging(workspaceFolder, {
110+
type: "ruby_lsp",
111+
name: "Debug",
112+
request: "launch",
113+
program: `ruby ${path.join(workspacePath, "test.rb")}`,
114+
});
115+
} catch (error: any) {
116+
assert.fail(`Failed to launch debugger: ${error.message}`);
117+
}
118+
119+
// The debugger might take a bit of time to disconnect from the editor. We need to perform cleanup when we receive
120+
// the termination callback or else we try to dispose of the debugger client too early, but we need to wait for that
121+
// so that we can clean up stubs otherwise they leak into other tests.
122+
await new Promise<void>((resolve) => {
123+
vscode.debug.onDidTerminateDebugSession((_session) => {
124+
stub.restore();
125+
getWorkspaceStub.restore();
126+
context.subscriptions.forEach((subscription) => subscription.dispose());
127+
resolve();
128+
});
129+
});
130+
}).timeout(90000);
131+
});

vscode/src/test/suite/testController.test.ts

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as assert from "assert";
22

33
import * as vscode from "vscode";
44
import { CodeLens } from "vscode-languageclient/node";
5+
import { afterEach } from "mocha";
56

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

22+
afterEach(() => {
23+
context.subscriptions.forEach((subscription) => subscription.dispose());
24+
});
25+
2126
test("createTestItems doesn't break when there's a missing group", () => {
2227
const controller = new TestController(
2328
context,

vscode/src/test/suite/testHelpers.ts

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* eslint-disable no-process-env */
2+
3+
import os from "os";
4+
import fs from "fs";
5+
import path from "path";
6+
7+
import * as vscode from "vscode";
8+
9+
import { ManagerIdentifier } from "../../ruby";
10+
import { RUBY_VERSION } from "../rubyVersion";
11+
12+
export async function ensureRubyInstallationPaths() {
13+
const [major, minor, _patch] = RUBY_VERSION.split(".");
14+
// Ensure that we're activating the correct Ruby version on CI
15+
if (process.env.CI) {
16+
if (os.platform() === "linux") {
17+
await vscode.workspace
18+
.getConfiguration("rubyLsp")
19+
.update(
20+
"rubyVersionManager",
21+
{ identifier: ManagerIdentifier.Chruby },
22+
true,
23+
);
24+
25+
fs.mkdirSync(path.join(os.homedir(), ".rubies"), { recursive: true });
26+
fs.symlinkSync(
27+
`/opt/hostedtoolcache/Ruby/${RUBY_VERSION}/x64`,
28+
path.join(os.homedir(), ".rubies", RUBY_VERSION),
29+
);
30+
} else if (os.platform() === "darwin") {
31+
await vscode.workspace
32+
.getConfiguration("rubyLsp")
33+
.update(
34+
"rubyVersionManager",
35+
{ identifier: ManagerIdentifier.Chruby },
36+
true,
37+
);
38+
39+
fs.mkdirSync(path.join(os.homedir(), ".rubies"), { recursive: true });
40+
fs.symlinkSync(
41+
`/Users/runner/hostedtoolcache/Ruby/${RUBY_VERSION}/arm64`,
42+
path.join(os.homedir(), ".rubies", RUBY_VERSION),
43+
);
44+
} else {
45+
await vscode.workspace
46+
.getConfiguration("rubyLsp")
47+
.update(
48+
"rubyVersionManager",
49+
{ identifier: ManagerIdentifier.RubyInstaller },
50+
true,
51+
);
52+
53+
fs.symlinkSync(
54+
path.join(
55+
"C:",
56+
"hostedtoolcache",
57+
"windows",
58+
"Ruby",
59+
RUBY_VERSION,
60+
"x64",
61+
),
62+
path.join("C:", `Ruby${major}${minor}-${os.arch()}`),
63+
);
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)