Skip to content

Commit 3f6ec26

Browse files
committed
Add debug run profile
1 parent 8c6eda0 commit 3f6ec26

File tree

8 files changed

+412
-1
lines changed

8 files changed

+412
-1
lines changed

package.json

+17
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,23 @@
7979
"type": "boolean",
8080
"default": "true",
8181
"description": "Find all tests within open files, without waiting for the file's target to be expanded in the Test Explorer."
82+
},
83+
"bazelbsp.debug.enabled": {
84+
"type": "boolean",
85+
"default": "false",
86+
"markdownDescription": "Enable debugging integration in the Test Explorer. This adds an additional Debug run profile for each test item.\nSet the bazelFlags, profileName, and readyPattern settings in this section to match your repo's required behavior."
87+
},
88+
"bazelbsp.debug.bazelFlags": {
89+
"type": "array",
90+
"description": "Flags to be added when debugging a target. Include any flags needed to ensure Bazel builds and runs the target in debug mode."
91+
},
92+
"bazelbsp.debug.readyPattern": {
93+
"type": "string",
94+
"description": "Regex pattern in the console output that signals that the target is ready for a debugger to connect. Once this is seen, the configured launch configuration will be triggered."
95+
},
96+
"bazelbsp.debug.launchConfigName": {
97+
"type": "string",
98+
"description": "Name of launch configuration that will be executed to begin the DAP debugging session. This must be a valid launch configuration in the launch.json file, workspace, or contributed by another extension."
8299
}
83100
}
84101
}

src/bsp/bsp-ext.ts

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export namespace TestParamsDataKind {
1919
export interface BazelTestParamsData {
2020
coverage?: boolean
2121
testFilter?: string
22+
additionalBazelParams?: string
2223
}
2324

2425
export namespace OnBuildPublishOutput {

src/test-info/test-info.ts

+11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {BuildTarget, TestParams, TestResult, StatusCode} from '../bsp/bsp'
44
import {TestParamsDataKind, BazelTestParamsData} from '../bsp/bsp-ext'
55
import {TestCaseStatus, TestRunTracker} from '../test-runner/run-tracker'
66
import {DocumentTestItem, LanguageToolManager} from '../language-tools/manager'
7+
import {getExtensionSetting, SettingName} from '../utils/settings'
78

89
export enum TestItemType {
910
Root,
@@ -113,6 +114,16 @@ export class BuildTargetTestCaseInfo extends TestCaseInfo {
113114
coverage:
114115
currentRun.getRunProfileKind() === vscode.TestRunProfileKind.Coverage,
115116
}
117+
118+
// Includes additional debug-specific flags when necessary.
119+
if (currentRun.getRunProfileKind() === vscode.TestRunProfileKind.Debug) {
120+
const configuredFlags = currentRun.getDebugBazelFlags()
121+
if (configuredFlags && configuredFlags.length > 0) {
122+
// Bazel BSP accepts whitespace separated list of flags.
123+
bazelParams.additionalBazelParams = configuredFlags.join(' ')
124+
}
125+
}
126+
116127
const params = {
117128
targets: [this.target.id],
118129
originId: currentRun.originName,

src/test-runner/run-tracker.ts

+94
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {CoverageTracker} from '../coverage-utils/coverage-tracker'
2222
import {LanguageToolManager} from '../language-tools/manager'
2323
import {TaskEventTracker} from './task-events'
2424
import {ANSI_CODES} from '../utils/utils'
25+
import {getExtensionSetting, SettingName} from '../utils/settings'
2526

2627
export enum TestCaseStatus {
2728
Pending,
@@ -44,6 +45,12 @@ export interface RunTrackerParams {
4445
languageToolManager: LanguageToolManager
4546
}
4647

48+
type DebugInfo = {
49+
debugFlags?: string[]
50+
launchConfig?: vscode.DebugConfiguration
51+
readyPattern?: RegExp
52+
}
53+
4754
export class TestRunTracker implements TaskOriginHandlers {
4855
// All tests that are included in this run. See iterator definition below.
4956
private allTests: Map<TestItemType, TestCaseInfo[]>
@@ -63,6 +70,7 @@ export class TestRunTracker implements TaskOriginHandlers {
6370
private languageToolManager: LanguageToolManager
6471
private pending: Thenable<void>[] = []
6572
private buildTaskTracker: TaskEventTracker = new TaskEventTracker()
73+
private debugInfo: DebugInfo | undefined
6674

6775
constructor(params: RunTrackerParams) {
6876
this.allTests = new Map<TestItemType, TestCaseInfo[]>()
@@ -76,6 +84,7 @@ export class TestRunTracker implements TaskOriginHandlers {
7684
this.languageToolManager = params.languageToolManager
7785

7886
this.prepareCurrentRun()
87+
this.prepareDebugInfo()
7988
}
8089

8190
public get originName(): string {
@@ -228,6 +237,21 @@ export class TestRunTracker implements TaskOriginHandlers {
228237
this.run.appendOutput(params.message)
229238
this.run.appendOutput('\n\r')
230239
}
240+
241+
// During debug runs, watch each message for indication of debug readiness.
242+
// If the message matches the configured pattern, start the debug session.
243+
if (
244+
this.debugInfo?.launchConfig &&
245+
this.debugInfo.readyPattern?.test(params.message)
246+
) {
247+
this.run.appendOutput(
248+
`Starting remote debug session [Launch config: '${this.debugInfo.launchConfig.name}']\r\n`
249+
)
250+
vscode.debug.startDebugging(
251+
vscode.workspace.workspaceFolders?.[0],
252+
this.debugInfo.launchConfig
253+
)
254+
}
231255
}
232256

233257
/**
@@ -254,6 +278,10 @@ export class TestRunTracker implements TaskOriginHandlers {
254278
return this.request.profile?.kind
255279
}
256280

281+
public getDebugBazelFlags(): string[] | undefined {
282+
return this.debugInfo?.debugFlags
283+
}
284+
257285
/**
258286
* Collects and stores the parents and all children to be included in this test run.
259287
* Populates maps to group the test items by their TestItemType and current status.
@@ -284,6 +312,72 @@ export class TestRunTracker implements TaskOriginHandlers {
284312
}
285313
}
286314

315+
/**
316+
* During debug runs, this collects and stores the necessary settings that will be applied through this run.
317+
* In the event that a setting is not found, information will be printed with the test output, but the run will still attempt to proceed.
318+
*/
319+
private prepareDebugInfo() {
320+
if (this.getRunProfileKind() !== vscode.TestRunProfileKind.Debug) {
321+
return
322+
}
323+
324+
// Determine configured launch configuration name.
325+
const configName = getExtensionSetting(SettingName.LAUNCH_CONFIG_NAME)
326+
if (!configName) {
327+
this.run.appendOutput(
328+
'No launch configuration name is configured. Debugger will not connect automatically for this run.\r\n'
329+
)
330+
this.run.appendOutput(
331+
'Check the `bazelbsp.debug.profileName` VS Code setting to ensure it corresponds to a valid launch configuration.\r\n'
332+
)
333+
return
334+
}
335+
336+
// Store the selected launch configuration.
337+
const launchConfigurations = vscode.workspace.getConfiguration('launch')
338+
const configurations =
339+
launchConfigurations.get<any[]>('configurations') || []
340+
const selectedConfig = configurations.find(
341+
config => config.name !== undefined && config.name === configName
342+
)
343+
if (!selectedConfig) {
344+
this.run.appendOutput(
345+
`Unable to find debug profile ${configName}. Debugger will not connect automatically for this run.\r\n`
346+
)
347+
this.run.appendOutput(
348+
'Check the `bazelbsp.debug.profileName` VS Code setting to ensure it corresponds to a valid launch configuration.\r\n'
349+
)
350+
}
351+
352+
// Ensure that matcher pattern is set for the output.
353+
const readyPattern = getExtensionSetting(SettingName.DEBUG_READY_PATTERN)
354+
if (!readyPattern) {
355+
this.run.appendOutput(
356+
'No matcher pattern is set. Debugger will not connect automatically for this run.\r\n'
357+
)
358+
this.run.appendOutput(
359+
'Check the `bazelbsp.debug.readyPattern` VS Code setting to ensure that a pattern is set.\r\n'
360+
)
361+
}
362+
363+
// Ensure that matcher pattern is set for the output.
364+
let debugFlags = getExtensionSetting(SettingName.DEBUG_BAZEL_FLAGS)
365+
if (!debugFlags) {
366+
this.run.appendOutput(
367+
'No additional debug-specific Bazel flags have been found for this run.\r\n'
368+
)
369+
this.run.appendOutput(
370+
'Check the `bazelbsp.debug.bazelFlags` VS Code setting to ensure that necessary flags are set.\r\n'
371+
)
372+
}
373+
374+
this.debugInfo = {
375+
debugFlags: debugFlags,
376+
launchConfig: selectedConfig,
377+
readyPattern: readyPattern ? new RegExp(readyPattern) : undefined,
378+
}
379+
}
380+
287381
/**
288382
* Iterate recursively through all children of the given test item, and collect them in the destination map.
289383
* @param destination Map to be populated with the collected test items, grouped by TestItemType.

src/test-runner/runner.ts

+12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {MessageConnection} from 'vscode-jsonrpc'
1111
import {TestRunTracker} from './run-tracker'
1212
import {RunTrackerFactory} from './run-factory'
1313
import {CoverageTracker} from '../coverage-utils/coverage-tracker'
14+
import {getExtensionSetting, SettingName} from '../utils/settings'
1415

1516
@Injectable()
1617
export class TestRunner implements OnModuleInit, vscode.Disposable {
@@ -53,6 +54,17 @@ export class TestRunner implements OnModuleInit, vscode.Disposable {
5354
this.runProfiles.set(vscode.TestRunProfileKind.Coverage, coverageRunProfile)
5455
coverageRunProfile.loadDetailedCoverage =
5556
this.coverageTracker.loadDetailedCoverage.bind(this.coverageTracker)
57+
58+
// Debug run profile, added only when enabled.
59+
if (getExtensionSetting(SettingName.DEBUG_ENABLED)) {
60+
const debugRunProfile =
61+
this.testCaseStore.testController.createRunProfile(
62+
'Run with Debug',
63+
vscode.TestRunProfileKind.Debug,
64+
this.runHandler.bind(this)
65+
)
66+
this.runProfiles.set(vscode.TestRunProfileKind.Debug, debugRunProfile)
67+
}
5668
}
5769

5870
private async runHandler(

0 commit comments

Comments
 (0)