Skip to content

Commit

Permalink
debug: correlate debug sessions to testing, delegate restarts
Browse files Browse the repository at this point in the history
Implements #214486
  • Loading branch information
connor4312 committed Jun 7, 2024
1 parent 4a4ae46 commit 00ce462
Show file tree
Hide file tree
Showing 16 changed files with 154 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export async function activate(context: vscode.ExtensionContext) {
map,
task,
kind === vscode.TestRunProfileKind.Debug
? await runner.debug(currentArgs, req.include)
? await runner.debug(task, currentArgs, req.include)
: await runner.run(currentArgs, req.include),
coverageDir,
cancellationToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export abstract class VSCodeTestRunner {
return new TestOutputScanner(cp, args);
}

public async debug(baseArgs: ReadonlyArray<string>, filter?: ReadonlyArray<vscode.TestItem>) {
public async debug(testRun: vscode.TestRun, baseArgs: ReadonlyArray<string>, filter?: ReadonlyArray<vscode.TestItem>) {
const port = await this.findOpenPort();
const baseConfiguration = vscode.workspace
.getConfiguration('launch', this.repoLocation)
Expand Down Expand Up @@ -95,7 +95,7 @@ export abstract class VSCodeTestRunner {
},
});

vscode.debug.startDebugging(this.repoLocation, { ...baseConfiguration, port });
vscode.debug.startDebugging(this.repoLocation, { ...baseConfiguration, port }, { testRun });

let exited = false;
let rootSession: vscode.DebugSession | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
"../../../src/vscode-dts/vscode.d.ts",
"../../../src/vscode-dts/vscode.proposed.testObserver.d.ts",
"../../../src/vscode-dts/vscode.proposed.attributableCoverage.d.ts",
"../../../src/vscode-dts/vscode.proposed.testRunInDebug.d.ts",
]
}
1 change: 1 addition & 0 deletions src/vs/workbench/api/browser/mainThreadDebugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
compact: options.compact,
compoundRoot: parentSession?.compoundRoot,
saveBeforeRestart: saveBeforeStart,
testRun: options.testRun,

suppressDebugStatusbar: options.suppressDebugStatusbar,
suppressDebugToolbar: options.suppressDebugToolbar,
Expand Down
4 changes: 2 additions & 2 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ import { IExtHostTask } from 'vs/workbench/api/common/extHostTask';
import { ExtHostTelemetryLogger, IExtHostTelemetry, isNewAppInstall } from 'vs/workbench/api/common/extHostTelemetry';
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
import { IExtHostTerminalShellIntegration } from 'vs/workbench/api/common/extHostTerminalShellIntegration';
import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
import { IExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors';
import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
Expand Down Expand Up @@ -205,7 +205,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostWebviewPanels = rpcProtocol.set(ExtHostContext.ExtHostWebviewPanels, new ExtHostWebviewPanels(rpcProtocol, extHostWebviews, extHostWorkspace));
const extHostCustomEditors = rpcProtocol.set(ExtHostContext.ExtHostCustomEditors, new ExtHostCustomEditors(rpcProtocol, extHostDocuments, extensionStoragePaths, extHostWebviews, extHostWebviewPanels));
const extHostWebviewViews = rpcProtocol.set(ExtHostContext.ExtHostWebviewViews, new ExtHostWebviewViews(rpcProtocol, extHostWebviews));
const extHostTesting = rpcProtocol.set(ExtHostContext.ExtHostTesting, new ExtHostTesting(rpcProtocol, extHostLogService, extHostCommands, extHostDocumentsAndEditors));
const extHostTesting = rpcProtocol.set(ExtHostContext.ExtHostTesting, accessor.get(IExtHostTesting));
const extHostUriOpeners = rpcProtocol.set(ExtHostContext.ExtHostUriOpeners, new ExtHostUriOpeners(rpcProtocol));
const extHostProfileContentHandlers = rpcProtocol.set(ExtHostContext.ExtHostProfileContentHandlers, new ExtHostProfileContentHandlers(rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService));
Expand Down
2 changes: 2 additions & 0 deletions src/vs/workbench/api/common/extHost.common.services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { ExtHostManagedSockets, IExtHostManagedSockets } from 'vs/workbench/api/
import { ExtHostAuthentication, IExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
import { ExtHostLanguageModels, IExtHostLanguageModels } from 'vs/workbench/api/common/extHostLanguageModels';
import { IExtHostTerminalShellIntegration, ExtHostTerminalShellIntegration } from 'vs/workbench/api/common/extHostTerminalShellIntegration';
import { ExtHostTesting, IExtHostTesting } from 'vs/workbench/api/common/extHostTesting';

registerSingleton(IExtHostLocalizationService, ExtHostLocalizationService, InstantiationType.Delayed);
registerSingleton(ILoggerService, ExtHostLoggerService, InstantiationType.Delayed);
Expand All @@ -40,6 +41,7 @@ registerSingleton(IExtHostAuthentication, ExtHostAuthentication, InstantiationTy
registerSingleton(IExtHostLanguageModels, ExtHostLanguageModels, InstantiationType.Eager);
registerSingleton(IExtHostConfiguration, ExtHostConfiguration, InstantiationType.Eager);
registerSingleton(IExtHostConsumerFileSystem, ExtHostConsumerFileSystem, InstantiationType.Eager);
registerSingleton(IExtHostTesting, ExtHostTesting, InstantiationType.Eager);
registerSingleton(IExtHostDebugService, WorkerExtHostDebugService, InstantiationType.Eager);
registerSingleton(IExtHostDecorations, ExtHostDecorations, InstantiationType.Eager);
registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors, InstantiationType.Eager);
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import { IChatProgressResponseContent } from 'vs/workbench/contrib/chat/common/c
import { IChatFollowup, IChatProgress, IChatResponseErrorDetails, IChatTask, IChatTaskDto, IChatUserActionEvent, ChatAgentVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { IChatRequestVariableValue, IChatVariableData, IChatVariableResolverProgress } from 'vs/workbench/contrib/chat/common/chatVariables';
import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata, ILanguageModelChatSelector, ILanguageModelsChangeEvent } from 'vs/workbench/contrib/chat/common/languageModels';
import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugVisualization, IDebugVisualizationContext, IDebugVisualizationTreeItem, MainThreadDebugVisualization } from 'vs/workbench/contrib/debug/common/debug';
import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugTestRunReference, IDebugVisualization, IDebugVisualizationContext, IDebugVisualizationTreeItem, MainThreadDebugVisualization } from 'vs/workbench/contrib/debug/common/debug';
import * as notebookCommon from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService';
import { ICellExecutionComplete, ICellExecutionStateUpdate } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
Expand Down Expand Up @@ -1566,6 +1566,7 @@ export interface IStartDebuggingOptions {
suppressDebugStatusbar?: boolean;
suppressDebugView?: boolean;
suppressSaveBeforeStart?: boolean;
testRun?: IDebugTestRunReference;
}

export interface MainThreadDebugServiceShape extends IDisposable {
Expand Down
13 changes: 11 additions & 2 deletions src/vs/workbench/api/common/extHostDebugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { ThemeIcon as ThemeIconUtils } from 'vs/base/common/themables';
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import * as Convert from 'vs/workbench/api/common/extHostTypeConverters';
import { coalesce } from 'vs/base/common/arrays';
import { IExtHostTesting } from 'vs/workbench/api/common/extHostTesting';

export const IExtHostDebugService = createDecorator<IExtHostDebugService>('IExtHostDebugService');

Expand Down Expand Up @@ -123,6 +124,7 @@ export abstract class ExtHostDebugServiceBase extends DisposableCls implements I
@IExtHostEditorTabs protected _editorTabs: IExtHostEditorTabs,
@IExtHostVariableResolverProvider private _variableResolver: IExtHostVariableResolverProvider,
@IExtHostCommands private _commands: IExtHostCommands,
@IExtHostTesting private _testing: IExtHostTesting,
) {
super();

Expand Down Expand Up @@ -466,13 +468,19 @@ export abstract class ExtHostDebugServiceBase extends DisposableCls implements I
}

public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise<boolean> {
const testRunMeta = options.testRun && this._testing.getMetadataForRun(options.testRun);

return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig, {
parentSessionID: options.parentSession ? options.parentSession.id : undefined,
lifecycleManagedByParent: options.lifecycleManagedByParent,
repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate',
noDebug: options.noDebug,
compact: options.compact,
suppressSaveBeforeStart: options.suppressSaveBeforeStart,
testRun: testRunMeta && {
runId: testRunMeta.runId,
taskId: testRunMeta.taskId,
},

// Check debugUI for back-compat, #147264
suppressDebugStatusbar: options.suppressDebugStatusbar ?? (options as any).debugUI?.simple,
Expand Down Expand Up @@ -1247,8 +1255,9 @@ export class WorkerExtHostDebugService extends ExtHostDebugServiceBase {
@IExtHostConfiguration configurationService: IExtHostConfiguration,
@IExtHostEditorTabs editorTabs: IExtHostEditorTabs,
@IExtHostVariableResolverProvider variableResolver: IExtHostVariableResolverProvider,
@IExtHostCommands commands: IExtHostCommands
@IExtHostCommands commands: IExtHostCommands,
@IExtHostTesting testing: IExtHostTesting,
) {
super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver, commands);
super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver, commands, testing);
}
}
69 changes: 54 additions & 15 deletions src/vs/workbench/api/common/extHostTesting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import { MarshalledId } from 'vs/base/common/marshallingIds';
import { isDefined } from 'vs/base/common/types';
import { generateUuid } from 'vs/base/common/uuid';
import { IExtensionDescription, IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { ExtHostTestingShape, ILocationDto, MainContext, MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ExtHostTestItemCollection, TestItemImpl, TestItemRootImpl, toItemFromContext } from 'vs/workbench/api/common/extHostTestItem';
import * as Convert from 'vs/workbench/api/common/extHostTypeConverters';
Expand All @@ -45,7 +46,14 @@ let followupCounter = 0;

const testResultInternalIDs = new WeakMap<vscode.TestRunResult, string>();

export const IExtHostTesting = createDecorator<IExtHostTesting>('IExtHostTesting');
export interface IExtHostTesting extends ExtHostTesting {
readonly _serviceBrand: undefined;
}

export class ExtHostTesting extends Disposable implements ExtHostTestingShape {
declare readonly _serviceBrand: undefined;

private readonly resultsChangedEmitter = this._register(new Emitter<void>());
protected readonly controllers = new Map</* controller ID */ string, ControllerInfo>();
private readonly proxy: MainThreadTestingShape;
Expand All @@ -61,8 +69,8 @@ export class ExtHostTesting extends Disposable implements ExtHostTestingShape {
constructor(
@IExtHostRpcService rpc: IExtHostRpcService,
@ILogService private readonly logService: ILogService,
private readonly commands: ExtHostCommands,
private readonly editors: ExtHostDocumentsAndEditors,
@IExtHostCommands private readonly commands: IExtHostCommands,
@IExtHostDocumentsAndEditors private readonly editors: IExtHostDocumentsAndEditors,
) {
super();
this.proxy = rpc.getProxy(MainContext.MainThreadTesting);
Expand Down Expand Up @@ -111,6 +119,8 @@ export class ExtHostTesting extends Disposable implements ExtHostTestingShape {
});
}

//#region public API

/**
* Implements vscode.test.registerTestProvider
*/
Expand Down Expand Up @@ -236,6 +246,9 @@ export class ExtHostTesting extends Disposable implements ExtHostTestingShape {
return { dispose: () => { this.followupProviders.delete(provider); } };
}

//#endregion

//#region RPC methods
/**
* @inheritdoc
*/
Expand Down Expand Up @@ -412,6 +425,32 @@ export class ExtHostTesting extends Disposable implements ExtHostTestingShape {
return this.commands.executeCommand(command.command, ...(command.arguments || []));
}

/**
* Cancels an ongoing test run.
*/
public $cancelExtensionTestRun(runId: string | undefined) {
if (runId === undefined) {
this.runTracker.cancelAllRuns();
} else {
this.runTracker.cancelRunById(runId);
}
}

//#endregion

public getMetadataForRun(run: vscode.TestRun) {
for (const tracker of this.runTracker.trackers) {
const taskId = tracker.getTaskIdForRun(run);
if (!taskId) {
return undefined;
}

return { taskId, runId: tracker.id };
}

return undefined;
}

private async runControllerTestRequest(req: ICallProfileRunHandler | ICallProfileRunHandler, isContinuous: boolean, token: CancellationToken): Promise<IStartControllerTestsResult> {
const lookup = this.controllers.get(req.controllerId);
if (!lookup) {
Expand Down Expand Up @@ -467,17 +506,6 @@ export class ExtHostTesting extends Disposable implements ExtHostTestingShape {
}
}
}

/**
* Cancels an ongoing test run.
*/
public $cancelExtensionTestRun(runId: string | undefined) {
if (runId === undefined) {
this.runTracker.cancelAllRuns();
} else {
this.runTracker.cancelRunById(runId);
}
}
}

// Deadline after being requested by a user that a test run is forcibly cancelled.
Expand Down Expand Up @@ -543,6 +571,17 @@ class TestRunTracker extends Disposable {
}));
}

/** Gets the task ID from a test run object. */
public getTaskIdForRun(run: vscode.TestRun) {
for (const [taskId, { run: r }] of this.tasks) {
if (r === run) {
return taskId;
}
}

return undefined;
}

/** Requests cancellation of the run. On the second call, forces cancellation. */
public cancel() {
if (this.state === TestRunTrackerState.Running) {
Expand Down
4 changes: 3 additions & 1 deletion src/vs/workbench/api/node/extHostDebugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/c
import type * as vscode from 'vscode';
import { ExtHostConfigProvider, IExtHostConfiguration } from '../common/extHostConfiguration';
import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { IExtHostTesting } from 'vs/workbench/api/common/extHostTesting';

export class ExtHostDebugService extends ExtHostDebugServiceBase {

Expand All @@ -44,8 +45,9 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
@IExtHostEditorTabs editorTabs: IExtHostEditorTabs,
@IExtHostVariableResolverProvider variableResolver: IExtHostVariableResolverProvider,
@IExtHostCommands commands: IExtHostCommands,
@IExtHostTesting testing: IExtHostTesting,
) {
super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver, commands);
super(extHostRpcService, workspaceService, extensionService, configurationService, editorTabs, variableResolver, commands, testing);
}

protected override createDebugAdapter(adapter: IAdapterDescriptor, session: ExtHostDebugSession): AbstractDebugAdapter | undefined {
Expand Down
17 changes: 17 additions & 0 deletions src/vs/workbench/contrib/debug/browser/debugService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { ViewModel } from 'vs/workbench/contrib/debug/common/debugViewModel';
import { Debugger } from 'vs/workbench/contrib/debug/common/debugger';
import { DisassemblyViewInput } from 'vs/workbench/contrib/debug/common/disassemblyViewInput';
import { VIEWLET_ID as EXPLORER_VIEWLET_ID } from 'vs/workbench/contrib/files/common/files';
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
Expand Down Expand Up @@ -112,6 +113,7 @@ export class DebugService implements IDebugService {
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IWorkspaceTrustRequestService private readonly workspaceTrustRequestService: IWorkspaceTrustRequestService,
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
@ITestService private readonly testService: ITestService,
) {
this.breakpointsToSendOnResourceSaved = new Set<URI>();

Expand Down Expand Up @@ -839,6 +841,21 @@ export class DebugService implements IDebugService {
}
};

// For debug sessions spawned by test runs, cancel the test run and stop
// the session, then start the test run again; tests have no notion of restarts.
if (session.correlatedTestRun) {
if (!session.correlatedTestRun.completedAt) {
this.testService.cancelTestRun(session.correlatedTestRun.id);
await Event.toPromise(session.correlatedTestRun.onComplete);
// todo@connor4312 is there any reason to wait for the debug session to
// terminate? I don't think so, test extension should already handle any
// state conflicts...
}

this.testService.runResolvedTests(session.correlatedTestRun.request);
return;
}

if (session.capabilities.supportsRestartRequest) {
const taskResult = await runTasks();
if (taskResult === TaskRunResult.Success) {
Expand Down
Loading

0 comments on commit 00ce462

Please sign in to comment.