Skip to content
This repository has been archived by the owner on May 30, 2024. It is now read-only.

Commit

Permalink
Add debugger launch test
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Jan 25, 2024
1 parent 92d0d62 commit f3ca647
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.2"
ruby-version: "3.3"

- name: 📦 Install dependencies
run: yarn --frozen-lockfile
Expand Down
29 changes: 23 additions & 6 deletions src/debugger.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
import path from "path";
import fs from "fs";
import os from "os";
import { ChildProcessWithoutNullStreams, spawn } from "child_process";

import * as vscode from "vscode";

import { LOG_CHANNEL } from "./common";
import { Workspace } from "./workspace";

class TerminalLogger {
append(message: string) {
// eslint-disable-next-line no-console
console.log(message);
}

appendLine(value: string): void {
// eslint-disable-next-line no-console
console.log(value);
}
}

export class Debugger
implements
vscode.DebugAdapterDescriptorFactory,
vscode.DebugConfigurationProvider
{
private debugProcess?: ChildProcessWithoutNullStreams;
private readonly console = vscode.debug.activeDebugConsole;
// eslint-disable-next-line no-process-env
private readonly console = process.env.CI
? new TerminalLogger()
: vscode.debug.activeDebugConsole;

private readonly workspaceResolver: (
uri: vscode.Uri | undefined,
) => Workspace | undefined;
Expand Down Expand Up @@ -135,7 +152,7 @@ export class Debugger
private async attachDebuggee(): Promise<vscode.DebugAdapterDescriptor> {
// When using attach, a process will be launched using Ruby debug and it will create a socket automatically. We have
// to find the available sockets and ask the user which one they want to attach to
const socketsDir = path.join("/", "tmp", "ruby-lsp-debug-sockets");
const socketsDir = path.join(os.tmpdir(), "rlspdebug");
const sockets = fs
.readdirSync(socketsDir)
.map((file) => file)
Expand Down Expand Up @@ -227,7 +244,7 @@ export class Debugger
// If the Ruby debug exits with an exit code > 1, then an error might've occurred. The reason we don't use only
// code zero here is because debug actually exits with 1 if the user cancels the debug session, which is not
// actually an error
this.debugProcess.on("exit", (code) => {
this.debugProcess.on("close", (code) => {
if (code) {
const message = `Debugger exited with status ${code}. Check the output channel for more information.`;
this.console.append(message);
Expand All @@ -241,13 +258,13 @@ export class Debugger
// Generate a socket path so that Ruby debug doesn't have to create one for us. This makes coordination easier since
// we always know the path to the socket
private socketPath(workspaceName: string) {
const socketsDir = path.join("/", "tmp", "ruby-lsp-debug-sockets");
const socketsDir = path.join(os.tmpdir(), "rlspdebug");
if (!fs.existsSync(socketsDir)) {
fs.mkdirSync(socketsDir);
}

let socketIndex = 0;
const prefix = `ruby-debug-${workspaceName}`;
const prefix = `rd-${workspaceName}`;
const existingSockets = fs
.readdirSync(socketsDir)
.map((file) => file)
Expand All @@ -261,6 +278,6 @@ export class Debugger
) + 1;
}

return `${socketsDir}/${prefix}-${socketIndex}.sock`;
return path.join(socketsDir, `${prefix}-${socketIndex}.sock`);
}
}
2 changes: 1 addition & 1 deletion src/test/suite/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ suite("Client", () => {
name: path.basename(tmpPath),
index: 0,
};
fs.writeFileSync(path.join(tmpPath, ".ruby-version"), "3.2.2");
fs.writeFileSync(path.join(tmpPath, ".ruby-version"), "3.3.0");

const context = {
extensionMode: vscode.ExtensionMode.Test,
Expand Down
74 changes: 73 additions & 1 deletion src/test/suite/debugger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import * as os from "os";
import * as vscode from "vscode";

import { Debugger } from "../../debugger";
import { Ruby } from "../../ruby";
import { Ruby, VersionManager } from "../../ruby";
import { Workspace } from "../../workspace";
import { WorkspaceChannel } from "../../workspaceChannel";
import { LOG_CHANNEL, asyncExec } from "../../common";

suite("Debugger", () => {
test("Provide debug configurations returns the default configs", () => {
Expand Down Expand Up @@ -134,4 +136,74 @@ suite("Debugger", () => {
context.subscriptions.forEach((subscription) => subscription.dispose());
fs.rmSync(tmpPath, { recursive: true, force: true });
});

test("Launching the debugger", async () => {
// eslint-disable-next-line no-process-env
if (process.env.CI) {
await vscode.workspace
.getConfiguration("rubyLsp")
.update("rubyVersionManager", VersionManager.None, true, true);
}

// By default, VS Code always saves all open files when launching a debugging session. This is a problem for tests
// because it attempts to save an untitled test file and then we get stuck in the save file dialog with no way of
// closing it. We have to disable that before running this test
const currentSaveBeforeStart = await vscode.workspace
.getConfiguration("debug")
.get("saveBeforeStart");
await vscode.workspace
.getConfiguration("debug")
.update("saveBeforeStart", "none", true, true);

const tmpPath = fs.mkdtempSync(
path.join(os.tmpdir(), "ruby-lsp-test-debugger"),
);
fs.writeFileSync(path.join(tmpPath, "test.rb"), "1 + 1");
fs.writeFileSync(path.join(tmpPath, ".ruby-version"), "3.3.0");
fs.writeFileSync(
path.join(tmpPath, "Gemfile"),
'source "https://rubygems.org"\ngem "debug"',
);

const context = { subscriptions: [] } as unknown as vscode.ExtensionContext;
const outputChannel = new WorkspaceChannel("fake", LOG_CHANNEL);
const workspaceFolder: vscode.WorkspaceFolder = {
uri: vscode.Uri.from({ scheme: "file", path: tmpPath }),
name: path.basename(tmpPath),
index: 0,
};
const ruby = new Ruby(context, workspaceFolder, outputChannel);
await ruby.activateRuby();
await asyncExec("bundle install", { env: ruby.env, cwd: tmpPath });

const debug = new Debugger(context, () => {
return {
ruby,
workspaceFolder,
} as Workspace;
});

try {
await vscode.debug.startDebugging(workspaceFolder, {
type: "ruby_lsp",
name: "Debug",
request: "launch",
program: `ruby ${path.join(tmpPath, "test.rb")}`,
env: ruby.env,
});
} 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
vscode.debug.onDidTerminateDebugSession(async (_session) => {
debug.dispose();
context.subscriptions.forEach((subscription) => subscription.dispose());
fs.rmSync(tmpPath, { recursive: true, force: true });
await vscode.workspace
.getConfiguration("debug")
.update("saveBeforeStart", currentSaveBeforeStart, true, true);
});
}).timeout(30000);
});
2 changes: 1 addition & 1 deletion src/test/suite/ruby.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ suite("Ruby environment activation", () => {
}

const tmpPath = fs.mkdtempSync(path.join(os.tmpdir(), "ruby-lsp-test-"));
fs.writeFileSync(path.join(tmpPath, ".ruby-version"), "3.2.2");
fs.writeFileSync(path.join(tmpPath, ".ruby-version"), "3.3.0");

const context = {
extensionMode: vscode.ExtensionMode.Test,
Expand Down

0 comments on commit f3ca647

Please sign in to comment.