Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ Property | Description
`rubyTestExplorer.logfile` | Write diagnostic logs to the given file.
`rubyTestExplorer.testFramework` | `none`, `auto`, `rspec`, or `minitest`. `auto` by default, which automatically detects the test framework based on the gems listed by Bundler. Can disable the extension functionality with `none` or set the test framework explicitly, if auto-detect isn't working properly.
`rubyTestExplorer.filePattern` | Define the pattern to match test files by, for example `["*_test.rb", "test_*.rb", "*_spec.rb"]`.
`rubyTestExplorer.debugger` | Which debugger to use. `rdebug-ide` or `rdbg`.
`rubyTestExplorer.debuggerHost` | Define the host to connect the debugger to, for example `127.0.0.1`.
`rubyTestExplorer.debuggerPort` | Define the port to connect the debugger to, for example `1234`.
`rubyTestExplorer.debugCommand` | Define how to run rdebug-ide, for example `rdebug-ide` or `bundle exec rdebug-ide`.
`rubyTestExplorer.rdbgCommand` | Define how to run rdbg, for example `rdbg` or `bundle exec rdbg`.
`rubyTestExplorer.rspecCommand` | Define the command to run RSpec tests with, for example `bundle exec rspec`, `spring rspec`, or `rspec`.
`rubyTestExplorer.rspecDirectory` | Define the relative directory of the specs in a given workspace, for example `./spec/`.
`rubyTestExplorer.minitestCommand` | Define how to run Minitest with Rake, for example `./bin/rake`, `bundle exec rake` or `rake`. Must be a Rake command.
Expand Down
20 changes: 20 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,20 @@
},
"scope": "resource"
},
"rubyTestExplorer.debugger": {
"description": "Which debugger to use. `rdebug-ide` or `rdbg`.",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be markdownDescription given the use of backticks, otherwise they won't be rendered nicely

Suggested change
"description": "Which debugger to use. `rdebug-ide` or `rdbg`.",
"markdownDescription": "Which debugger to use. `rdebug-ide` or `rdbg`.",

Copy link

@mikekosulin mikekosulin May 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"type": "string",
"default": "rdebug-ide",
"enum": [
"rdebug-ide",
"rdbg"
],
"enumDescriptions": [
"Use ruby-debug-ide gem.",
"Use debug gem."
],
"scope": "resource"
},
"rubyTestExplorer.debuggerHost": {
"markdownDescription": "The host to connect the debugger to.",
"default": "127.0.0.1",
Expand All @@ -158,6 +172,12 @@
"default": "rdebug-ide",
"type": "string",
"scope": "resource"
},
"rubyTestExplorer.rdbgCommand": {
"markdownDescription": "Define how to run rdbg, for example `rdbg` or `bundle exec rdbg`.",
"default": "rdbg",
"type": "string",
"scope": "resource"
}
}
}
Expand Down
61 changes: 50 additions & 11 deletions src/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,17 @@ export class RubyAdapter implements TestAdapter {
async debug(testsToRun: string[]): Promise<void> {
this.log.info(`Debugging test(s) ${JSON.stringify(testsToRun)} of ${this.workspace.uri.fsPath}`);

const config = vscode.workspace.getConfiguration('rubyTestExplorer', null)

const debuggerConfig = {
name: "Debug Ruby Tests",
type: "Ruby",
request: "attach",
remoteHost: config.get('debuggerHost') || "127.0.0.1",
remotePort: config.get('debuggerPort') || "1234",
remoteWorkspaceRoot: "${workspaceRoot}"
let debuggerConfig: vscode.DebugConfiguration;
try {
debuggerConfig = this.getDebuggerConfig();
} catch (err) {
this.log.error('Failed to get debugger configuration', err);
return;
}

const testRunPromise = this.run(testsToRun, debuggerConfig);

this.log.info('Starting the debug session');
let debugSession: any;
let debugSession: vscode.DebugSession;
try {
await this.testsInstance!.debugCommandStarted()
debugSession = await this.startDebugging(debuggerConfig);
Expand All @@ -103,6 +99,11 @@ export class RubyAdapter implements TestAdapter {
subscription.dispose();
});

if (vscode.workspace.getConfiguration('rubyTestExplorer', null).get('debugger') === 'rdbg') {
// debug gem is configured to stop at load, so we resume the execution on attach.
debugSession.customRequest('continue');
}

await testRunPromise;
}

Expand Down Expand Up @@ -220,6 +221,44 @@ export class RubyAdapter implements TestAdapter {
return vscode.debug.onDidTerminateDebugSession(cb);
}

private getDebuggerConfig(): vscode.DebugConfiguration {
const config = vscode.workspace.getConfiguration('rubyTestExplorer', null)

switch (config.get('debugger')) {
case 'rdebug-ide': {
return {
name: "Debug Ruby Tests",
type: "Ruby",
request: "attach",
remoteHost: config.get('debuggerHost') || "127.0.0.1",
remotePort: config.get('debuggerPort') || "1234",
remoteWorkspaceRoot: "${workspaceRoot}"
}
}
case 'rdbg': {
const debuggerHost = config.get('debuggerHost') || "127.0.0.1";
const debuggerPort = config.get('debuggerPort') || "1234";

// Debugger configuration to attach by using KoichiSasada.vscode-rdbg.
// See: https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg
return {
name: "Debug Ruby Tests",
type: "rdbg",
request: "attach",
localfs: true,
debugPort: `${debuggerHost}:${debuggerPort}`,

// These parameters are used to lanuch the debug server.
debuggerHost,
debuggerPort,
}
}

default:
throw new Error("Invalid debugger");
}
}

/**
* Get the test directory based on the configuration value if there's a configured test framework.
*/
Expand Down
10 changes: 3 additions & 7 deletions src/minitestTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,9 @@ export class MinitestTests extends Tests {
* @return The rdebug-ide command
*/
protected getDebugCommand(debuggerConfig: vscode.DebugConfiguration): string {
let command: string =
(vscode.workspace.getConfiguration('rubyTestExplorer', null).get('debugCommand') as string) ||
'rdebug-ide';

return (
`${command} --host ${debuggerConfig.remoteHost} --port ${debuggerConfig.remotePort}` +
` -- ${process.platform == 'win32' ? '%EXT_DIR%' : '$EXT_DIR'}/debug_minitest.rb`
return this.buildDebugCommand(
debuggerConfig,
`${process.platform == 'win32' ? '%EXT_DIR%' : '$EXT_DIR'}/debug_minitest.rb`
);
}

Expand Down
17 changes: 8 additions & 9 deletions src/rspecTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,10 @@ export class RspecTests extends Tests {
* @return The rdebug-ide command
*/
protected getDebugCommand(debuggerConfig: vscode.DebugConfiguration, args: string): string {
let command: string =
(vscode.workspace.getConfiguration('rubyTestExplorer', null).get('debugCommand') as string) ||
'rdebug-ide';

return (
`${command} --host ${debuggerConfig.remoteHost} --port ${debuggerConfig.remotePort}` +
` -- ${process.platform == 'win32' ? '%EXT_DIR%' : '$EXT_DIR'}/debug_rspec.rb ${args}`
return this.buildDebugCommand(
debuggerConfig,
`${process.platform == 'win32' ? '%EXT_DIR%' : '$EXT_DIR'}/debug_rspec.rb`,
args
);
}
/**
Expand Down Expand Up @@ -192,7 +189,8 @@ export class RspecTests extends Tests {
this.log.info(`Running test file: ${testFile}`);
const spawnArgs: childProcess.SpawnOptions = {
cwd: this.workspace.uri.fsPath,
shell: true
shell: true,
env: this.getProcessEnv()
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a bug that debug fails (due to lack of EXT_DIR environment variable).

};

// Run tests for a given file at once with a single command.
Expand All @@ -217,7 +215,8 @@ export class RspecTests extends Tests {
this.log.info(`Running full test suite.`);
const spawnArgs: childProcess.SpawnOptions = {
cwd: this.workspace.uri.fsPath,
shell: true
shell: true,
env: this.getProcessEnv()
};

let testCommand = this.testCommandWithFormatterAndDebugger(debuggerConfig);
Expand Down
42 changes: 41 additions & 1 deletion src/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ export abstract class Tests {
this.currentChildProcess.stderr!.pipe(split2()).on('data', (data) => {
data = data.toString();
this.log.debug(`[CHILD PROCESS OUTPUT] ${data}`);
if (data.startsWith('Fast Debugger') && this.debugCommandStartedResolver) {
if ((data.startsWith('Fast Debugger') || data.includes("DEBUGGER: wait for debugger connection...")) && this.debugCommandStartedResolver) {
this.debugCommandStartedResolver()
}
});
Expand Down Expand Up @@ -519,6 +519,46 @@ export abstract class Tests {
return this.context.asAbsolutePath('./ruby');
}

/**
* Get the user-configured rdebug-ide command, if there is one.
*
* @param debuggerConfig A VS Code debugger configuration.
* @return The debugger command
*/
protected buildDebugCommand(debuggerConfig: vscode.DebugConfiguration, initFile: string, args?: string): string {
switch (debuggerConfig.type) {
case "Ruby": { // ruby-debug-ide gem
const debugCommand: string =
(vscode.workspace.getConfiguration('rubyTestExplorer', null).get('debugCommand') as string) ||
'rdebug-ide';

let command = `${debugCommand} --host ${debuggerConfig.remoteHost} --port ${debuggerConfig.remotePort} -- ${initFile}`
if (args) {
command += ` ${args}`
}

return command;
}
case "rdbg": { // debug gem
const debugCommand: string =
(vscode.workspace.getConfiguration('rubyTestExplorer', null).get('rdbgCommand') as string) ||
'rdbg';
const host = debuggerConfig.debuggerHost as string;
const port = debuggerConfig.debuggerPort as string;

let command = `${debugCommand} --open --stop-at-load --host ${host} --port ${port} -- ${initFile}`
if (args) {
command += ` ${args}`
}

return command;
}
default:
throw "Unknown debugger"
}

}

/**
* Runs a single test.
*
Expand Down