Skip to content

Commit d29aac7

Browse files
committed
updates
1 parent 9a866c6 commit d29aac7

File tree

3 files changed

+24
-29
lines changed

3 files changed

+24
-29
lines changed

src/client/extension.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,11 @@ async function activateUnsafe(
123123
// Note standard utils especially experiment and platform code are fundamental to the extension
124124
// and should be available before we activate anything else.Hence register them first.
125125
initializeStandard(ext);
126-
127-
// Register test services and commands early, before any async operations, to prevent race conditions
128-
// where commands are invoked before the extension has fully activated.
126+
127+
// Register test services and commands early to prevent race conditions.
129128
unitTestsRegisterTypes(ext.legacyIOC.serviceManager);
130129
registerTestCommands(activatedServiceContainer);
131-
130+
132131
// We need to activate experiments before initializing components as objects are created or not created based on experiments.
133132
const experimentService = activatedServiceContainer.get<IExperimentService>(IExperimentService);
134133
// This guarantees that all experiment information has loaded & all telemetry will contain experiment info.

src/client/extensionActivation.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import { setExtensionInstallTelemetryProperties } from './telemetry/extensionIns
3333
import { registerTypes as tensorBoardRegisterTypes } from './tensorBoard/serviceRegistry';
3434
import { registerTypes as commonRegisterTerminalTypes } from './terminals/serviceRegistry';
3535
import { ICodeExecutionHelper, ICodeExecutionManager, ITerminalAutoActivation } from './terminals/types';
36-
import { registerTypes as unitTestsRegisterTypes } from './testing/serviceRegistry';
3736

3837
// components
3938
import * as pythonEnvironments from './pythonEnvironments';
@@ -144,7 +143,6 @@ async function activateLegacy(ext: ExtensionState, startupStopWatch: StopWatch):
144143
const { enableProposedApi } = applicationEnv.packageJson;
145144
serviceManager.addSingletonInstance<boolean>(UseProposedApi, enableProposedApi);
146145
// Feature specific registrations.
147-
// Note: unitTestsRegisterTypes is now called earlier in extension.ts before the first await
148146
installerRegisterTypes(serviceManager);
149147
commonRegisterTerminalTypes(serviceManager);
150148
debugConfigurationRegisterTypes(serviceManager);

src/client/testing/main.ts

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { IDisposableRegistry, Product } from '../common/types';
1818
import { IInterpreterService } from '../interpreter/contracts';
1919
import { IServiceContainer } from '../ioc/types';
2020
import { EventName } from '../telemetry/constants';
21-
import { captureTelemetry, sendTelemetryEvent } from '../telemetry/index';
21+
import { sendTelemetryEvent } from '../telemetry/index';
2222
import { selectTestWorkspace } from './common/testUtils';
2323
import { TestSettingsPropertyNames } from './configuration/types';
2424
import { ITestConfigurationService, ITestsHelper } from './common/types';
@@ -43,25 +43,21 @@ export class TestingService implements ITestingService {
4343
}
4444

4545
/**
46-
* Registers test commands early in the extension activation to prevent race conditions.
47-
* This function should be called synchronously before any async operations in the activation flow.
46+
* Registers command handlers but defers service resolution until the commands are actually invoked,
47+
* allowing registration to happen before all services are fully initialized.
4848
*/
4949
export function registerTestCommands(serviceContainer: IServiceContainer): void {
50+
// Resolve only the essential services needed for command registration itself
5051
const disposableRegistry = serviceContainer.get<Disposable[]>(IDisposableRegistry);
5152
const commandManager = serviceContainer.get<ICommandManager>(ICommandManager);
52-
const workspaceService = serviceContainer.get<IWorkspaceService>(IWorkspaceService);
53-
54-
// Get test controller if available
55-
let testController: ITestController | undefined;
56-
if (tests && !!tests.createTestController) {
57-
testController = serviceContainer.get<ITestController>(ITestController);
58-
}
5953

60-
// Helper function to configure tests
54+
// Helper function to configure tests - services are resolved when invoked, not at registration time
6155
const configureTestsHandler = async (resource?: Uri) => {
62-
// Send telemetry for test configuration
6356
sendTelemetryEvent(EventName.UNITTEST_CONFIGURE);
64-
57+
58+
// Resolve services lazily when the command is invoked
59+
const workspaceService = serviceContainer.get<IWorkspaceService>(IWorkspaceService);
60+
6561
let wkspace: Uri | undefined;
6662
if (resource) {
6763
const wkspaceFolder = workspaceService.getWorkspaceFolder(resource);
@@ -84,28 +80,33 @@ export function registerTestCommands(serviceContainer: IServiceContainer): void
8480
};
8581

8682
disposableRegistry.push(
83+
// Command: python.configureTests - prompts user to configure test framework
8784
commandManager.registerCommand(
8885
constants.Commands.Tests_Configure,
8986
(_, _cmdSource: constants.CommandSource = constants.CommandSource.commandPalette, resource?: Uri) => {
90-
// Ignore the exceptions returned.
91-
// This command will be invoked from other places of the extension.
87+
// Invoke configuration handler (errors are ignored as this can be called from multiple places)
9288
configureTestsHandler(resource).ignoreErrors();
9389
traceVerbose('Testing: Trigger refresh after config change');
94-
testController?.refreshTestData(resource, { forceRefresh: true });
90+
// Refresh test data if test controller is available (resolved lazily)
91+
if (tests && !!tests.createTestController) {
92+
const testController = serviceContainer.get<ITestController>(ITestController);
93+
testController?.refreshTestData(resource, { forceRefresh: true });
94+
}
9595
},
9696
),
97+
// Command: python.tests.copilotSetup - Copilot integration for test setup
9798
commandManager.registerCommand(constants.Commands.Tests_CopilotSetup, (resource?: Uri):
9899
| { message: string; command: Command }
99100
| undefined => {
101+
// Resolve services lazily when the command is invoked
102+
const workspaceService = serviceContainer.get<IWorkspaceService>(IWorkspaceService);
100103
const wkspaceFolder =
101104
workspaceService.getWorkspaceFolder(resource) || workspaceService.workspaceFolders?.at(0);
102105
if (!wkspaceFolder) {
103106
return undefined;
104107
}
105108

106-
const configurationService = serviceContainer.get<ITestConfigurationService>(
107-
ITestConfigurationService,
108-
);
109+
const configurationService = serviceContainer.get<ITestConfigurationService>(ITestConfigurationService);
109110
if (configurationService.hasConfiguredTests(wkspaceFolder.uri)) {
110111
return undefined;
111112
}
@@ -119,6 +120,7 @@ export function registerTestCommands(serviceContainer: IServiceContainer): void
119120
},
120121
};
121122
}),
123+
// Command: python.copyTestId - copies test ID to clipboard
122124
commandManager.registerCommand(constants.Commands.CopyTestId, async (testItem: TestItem) => {
123125
writeTestIdToClipboard(testItem);
124126
}),
@@ -163,8 +165,6 @@ export class UnitTestManagementService implements IExtensionActivationService {
163165
this.activatedOnce = true;
164166

165167
this.registerHandlers();
166-
// Note: Commands are now registered early in extensionActivation.ts via registerTestCommands()
167-
// to avoid race conditions where commands are invoked before extension activation completes.
168168

169169
if (!!tests.testResults) {
170170
await this.updateTestUIButtons();
@@ -214,8 +214,6 @@ export class UnitTestManagementService implements IExtensionActivationService {
214214
await Promise.all(changedWorkspaces.map((u) => this.testController?.refreshTestData(u)));
215215
}
216216

217-
218-
219217
private registerHandlers() {
220218
const interpreterService = this.serviceContainer.get<IInterpreterService>(IInterpreterService);
221219
this.disposableRegistry.push(

0 commit comments

Comments
 (0)