Skip to content

Commit 72f7ef8

Browse files
authored
Set up testing rewrite experiment (#21258)
is the beginning of this issue: #21150, in that it will start the process of implementing the setting in the extension
1 parent 4109228 commit 72f7ef8

File tree

7 files changed

+88
-57
lines changed

7 files changed

+88
-57
lines changed

src/client/common/experiments/groups.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ export enum TerminalEnvVarActivation {
1414
export enum ShowFormatterExtensionPrompt {
1515
experiment = 'pythonPromptNewFormatterExt',
1616
}
17+
// Experiment to enable the new testing rewrite.
18+
export enum EnableTestAdapterRewrite {
19+
experiment = 'pythonTestAdapter',
20+
}

src/client/testing/common/debugLauncher.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { getConfigurationsForWorkspace } from '../../debugger/extension/configur
1616
import { getWorkspaceFolder, getWorkspaceFolders } from '../../common/vscodeApis/workspaceApis';
1717
import { showErrorMessage } from '../../common/vscodeApis/windowApis';
1818
import { createDeferred } from '../../common/utils/async';
19+
import { pythonTestAdapterRewriteEnabled } from '../testController/common/utils';
1920

2021
@injectable()
2122
export class DebugLauncher implements ITestDebugLauncher {
@@ -87,6 +88,7 @@ export class DebugLauncher implements ITestDebugLauncher {
8788
path: path.join(EXTENSION_ROOT_DIR, 'pythonFiles'),
8889
include: false,
8990
});
91+
9092
DebugLauncher.applyDefaults(debugConfig!, workspaceFolder, configSettings);
9193

9294
return this.convertConfigToArgs(debugConfig!, workspaceFolder, options);
@@ -171,10 +173,11 @@ export class DebugLauncher implements ITestDebugLauncher {
171173
workspaceFolder: WorkspaceFolder,
172174
options: LaunchOptions,
173175
): Promise<LaunchRequestArguments> {
176+
const pythonTestAdapterRewriteExperiment = pythonTestAdapterRewriteEnabled(this.serviceContainer);
174177
const configArgs = debugConfig as LaunchRequestArguments;
175178
const testArgs =
176179
options.testProvider === 'unittest' ? options.args.filter((item) => item !== '--debug') : options.args;
177-
const script = DebugLauncher.getTestLauncherScript(options.testProvider);
180+
const script = DebugLauncher.getTestLauncherScript(options.testProvider, pythonTestAdapterRewriteExperiment);
178181
const args = script(testArgs);
179182
const [program] = args;
180183
configArgs.program = program;
@@ -199,10 +202,7 @@ export class DebugLauncher implements ITestDebugLauncher {
199202
throw Error(`Invalid debug config "${debugConfig.name}"`);
200203
}
201204
launchArgs.request = 'launch';
202-
203-
// If we are in the pytest rewrite then we must send additional environment variables.
204-
const rewriteTestingEnabled = process.env.ENABLE_PYTHON_TESTING_REWRITE;
205-
if (options.testProvider === 'pytest' && rewriteTestingEnabled) {
205+
if (options.testProvider === 'pytest' && pythonTestAdapterRewriteExperiment) {
206206
if (options.pytestPort && options.pytestUUID) {
207207
launchArgs.env = {
208208
...launchArgs.env,
@@ -226,17 +226,16 @@ export class DebugLauncher implements ITestDebugLauncher {
226226
return launchArgs;
227227
}
228228

229-
private static getTestLauncherScript(testProvider: TestProvider) {
230-
const rewriteTestingEnabled = process.env.ENABLE_PYTHON_TESTING_REWRITE;
229+
private static getTestLauncherScript(testProvider: TestProvider, pythonTestAdapterRewriteExperiment?: boolean) {
231230
switch (testProvider) {
232231
case 'unittest': {
233-
if (rewriteTestingEnabled) {
232+
if (pythonTestAdapterRewriteExperiment) {
234233
return internalScripts.execution_py_testlauncher; // this is the new way to run unittest execution, debugger
235234
}
236235
return internalScripts.visualstudio_py_testlauncher; // old way unittest execution, debugger
237236
}
238237
case 'pytest': {
239-
if (rewriteTestingEnabled) {
238+
if (pythonTestAdapterRewriteExperiment) {
240239
return internalScripts.pytestlauncher; // this is the new way to run pytest execution, debugger
241240
}
242241
return internalScripts.testlauncher; // old way pytest execution, debugger

src/client/testing/testController/common/utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
import * as net from 'net';
44
import { traceLog } from '../../../logging';
55

6+
import { EnableTestAdapterRewrite } from '../../../common/experiments/groups';
7+
import { IExperimentService } from '../../../common/types';
8+
import { IServiceContainer } from '../../../ioc/types';
9+
610
export function fixLogLines(content: string): string {
711
const lines = content.split(/\r?\n/g);
812
return `${lines.join('\r\n')}\r\n`;
@@ -52,6 +56,12 @@ export function jsonRPCContent(headers: Map<string, string>, rawData: string): I
5256
remainingRawData,
5357
};
5458
}
59+
60+
export function pythonTestAdapterRewriteEnabled(serviceContainer: IServiceContainer): boolean {
61+
const experiment = serviceContainer.get<IExperimentService>(IExperimentService);
62+
return experiment.inExperimentSync(EnableTestAdapterRewrite.experiment);
63+
}
64+
5565
export const startServer = (testIds: string): Promise<number> =>
5666
new Promise((resolve, reject) => {
5767
const server = net.createServer((socket: net.Socket) => {

src/client/testing/testController/controller.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import { PytestTestDiscoveryAdapter } from './pytest/pytestDiscoveryAdapter';
4444
import { PytestTestExecutionAdapter } from './pytest/pytestExecutionAdapter';
4545
import { WorkspaceTestAdapter } from './workspaceTestAdapter';
4646
import { ITestDebugLauncher } from '../common/types';
47+
import { pythonTestAdapterRewriteEnabled } from './common/utils';
48+
import { IServiceContainer } from '../../ioc/types';
4749

4850
// Types gymnastics to make sure that sendTriggerTelemetry only accepts the correct types.
4951
type EventPropertyType = IEventNamePropertyMapping[EventName.UNITTEST_DISCOVERY_TRIGGER];
@@ -93,6 +95,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc
9395
@inject(IPythonExecutionFactory) private readonly pythonExecFactory: IPythonExecutionFactory,
9496
@inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher,
9597
@inject(ITestOutputChannel) private readonly testOutputChannel: ITestOutputChannel,
98+
@inject(IServiceContainer) private readonly serviceContainer: IServiceContainer,
9699
) {
97100
this.refreshCancellation = new CancellationTokenSource();
98101

@@ -241,12 +244,11 @@ export class PythonTestController implements ITestController, IExtensionSingleAc
241244
if (uri) {
242245
const settings = this.configSettings.getSettings(uri);
243246
traceVerbose(`Testing: Refreshing test data for ${uri.fsPath}`);
244-
const rewriteTestingEnabled = process.env.ENABLE_PYTHON_TESTING_REWRITE;
245247
if (settings.testing.pytestEnabled) {
246248
// Ensure we send test telemetry if it gets disabled again
247249
this.sendTestDisabledTelemetry = true;
248-
if (rewriteTestingEnabled) {
249-
// ** rewriteTestingEnabled set to true to use NEW test discovery mechanism
250+
// ** experiment to roll out NEW test discovery mechanism
251+
if (pythonTestAdapterRewriteEnabled(this.serviceContainer)) {
250252
const workspace = this.workspaceService.getWorkspaceFolder(uri);
251253
traceVerbose(`Discover tests for workspace name: ${workspace?.name} - uri: ${uri.fsPath}`);
252254
const testAdapter =
@@ -263,8 +265,8 @@ export class PythonTestController implements ITestController, IExtensionSingleAc
263265
} else if (settings.testing.unittestEnabled) {
264266
// ** Ensure we send test telemetry if it gets disabled again
265267
this.sendTestDisabledTelemetry = true;
266-
if (rewriteTestingEnabled) {
267-
// ** rewriteTestingEnabled set to true to use NEW test discovery mechanism
268+
// ** experiment to roll out NEW test discovery mechanism
269+
if (pythonTestAdapterRewriteEnabled(this.serviceContainer)) {
268270
const workspace = this.workspaceService.getWorkspaceFolder(uri);
269271
traceVerbose(`Discover tests for workspace name: ${workspace?.name} - uri: ${uri.fsPath}`);
270272
const testAdapter =
@@ -388,14 +390,13 @@ export class PythonTestController implements ITestController, IExtensionSingleAc
388390

389391
const settings = this.configSettings.getSettings(workspace.uri);
390392
if (testItems.length > 0) {
391-
const rewriteTestingEnabled = process.env.ENABLE_PYTHON_TESTING_REWRITE;
392393
if (settings.testing.pytestEnabled) {
393394
sendTelemetryEvent(EventName.UNITTEST_RUN, undefined, {
394395
tool: 'pytest',
395396
debugging: request.profile?.kind === TestRunProfileKind.Debug,
396397
});
397-
// ** rewriteTestingEnabled set to true to use NEW test discovery mechanism
398-
if (rewriteTestingEnabled) {
398+
// ** experiment to roll out NEW test discovery mechanism
399+
if (pythonTestAdapterRewriteEnabled(this.serviceContainer)) {
399400
const testAdapter =
400401
this.testAdapters.get(workspace.uri) ||
401402
(this.testAdapters.values().next().value as WorkspaceTestAdapter);
@@ -425,8 +426,8 @@ export class PythonTestController implements ITestController, IExtensionSingleAc
425426
tool: 'unittest',
426427
debugging: request.profile?.kind === TestRunProfileKind.Debug,
427428
});
428-
// ** rewriteTestingEnabled set to true to use NEW test discovery mechanism
429-
if (rewriteTestingEnabled) {
429+
// ** experiment to roll out NEW test discovery mechanism
430+
if (pythonTestAdapterRewriteEnabled(this.serviceContainer)) {
430431
const testAdapter =
431432
this.testAdapters.get(workspace.uri) ||
432433
(this.testAdapters.values().next().value as WorkspaceTestAdapter);

src/client/testing/testController/pytest/pytestController.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -285,19 +285,24 @@ export class PytestController implements ITestFrameworkController {
285285

286286
public runTests(testRun: ITestRun, workspace: WorkspaceFolder, token: CancellationToken): Promise<void> {
287287
const settings = this.configService.getSettings(workspace.uri);
288-
return this.runner.runTests(
289-
testRun,
290-
{
291-
workspaceFolder: workspace.uri,
292-
cwd:
293-
settings.testing.cwd && settings.testing.cwd.length > 0
294-
? settings.testing.cwd
295-
: workspace.uri.fsPath,
296-
token,
297-
args: settings.testing.pytestArgs,
298-
},
299-
this.idToRawData,
300-
);
288+
try {
289+
return this.runner.runTests(
290+
testRun,
291+
{
292+
workspaceFolder: workspace.uri,
293+
cwd:
294+
settings.testing.cwd && settings.testing.cwd.length > 0
295+
? settings.testing.cwd
296+
: workspace.uri.fsPath,
297+
token,
298+
args: settings.testing.pytestArgs,
299+
},
300+
this.idToRawData,
301+
);
302+
} catch (ex) {
303+
sendTelemetryEvent(EventName.UNITTEST_RUN_ALL_FAILED, undefined);
304+
throw new Error(`Failed to run tests: ${ex}`);
305+
}
301306
}
302307
}
303308

src/client/testing/testController/unittest/unittestController.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -251,20 +251,25 @@ export class UnittestController implements ITestFrameworkController {
251251
testController?: TestController,
252252
): Promise<void> {
253253
const settings = this.configService.getSettings(workspace.uri);
254-
return this.runner.runTests(
255-
testRun,
256-
{
257-
workspaceFolder: workspace.uri,
258-
cwd:
259-
settings.testing.cwd && settings.testing.cwd.length > 0
260-
? settings.testing.cwd
261-
: workspace.uri.fsPath,
262-
token,
263-
args: settings.testing.unittestArgs,
264-
},
265-
this.idToRawData,
266-
testController,
267-
);
254+
try {
255+
return this.runner.runTests(
256+
testRun,
257+
{
258+
workspaceFolder: workspace.uri,
259+
cwd:
260+
settings.testing.cwd && settings.testing.cwd.length > 0
261+
? settings.testing.cwd
262+
: workspace.uri.fsPath,
263+
token,
264+
args: settings.testing.unittestArgs,
265+
},
266+
this.idToRawData,
267+
testController,
268+
);
269+
} catch (ex) {
270+
sendTelemetryEvent(EventName.UNITTEST_RUN_ALL_FAILED, undefined);
271+
throw new Error(`Failed to run tests: ${ex}`);
272+
}
268273
}
269274
}
270275

src/test/testing/common/debugLauncher.unit.test.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { ITestingSettings } from '../../../client/testing/configuration/types';
2929
import { TestProvider } from '../../../client/testing/types';
3030
import { isOs, OSType } from '../../common';
3131
import { IEnvironmentActivationService } from '../../../client/interpreter/activation/types';
32+
import * as util from '../../../client/testing/testController/common/utils';
3233

3334
use(chaiAsPromised);
3435

@@ -45,6 +46,7 @@ suite('Unit Tests - Debug Launcher', () => {
4546
let getWorkspaceFoldersStub: sinon.SinonStub;
4647
let pathExistsStub: sinon.SinonStub;
4748
let readFileStub: sinon.SinonStub;
49+
let pythonTestAdapterRewriteEnabledStub: sinon.SinonStub;
4850
const envVars = { FOO: 'BAR' };
4951

5052
setup(async () => {
@@ -65,6 +67,8 @@ suite('Unit Tests - Debug Launcher', () => {
6567
getWorkspaceFoldersStub = sinon.stub(workspaceApis, 'getWorkspaceFolders');
6668
pathExistsStub = sinon.stub(fs, 'pathExists');
6769
readFileStub = sinon.stub(fs, 'readFile');
70+
pythonTestAdapterRewriteEnabledStub = sinon.stub(util, 'pythonTestAdapterRewriteEnabled');
71+
pythonTestAdapterRewriteEnabledStub.returns(false);
6872

6973
const appShell = TypeMoq.Mock.ofType<IApplicationShell>(undefined, TypeMoq.MockBehavior.Strict);
7074
appShell.setup((a) => a.showErrorMessage(TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined));
@@ -143,19 +147,22 @@ suite('Unit Tests - Debug Launcher', () => {
143147
uri: Uri.file(folderPath),
144148
};
145149
}
146-
function getTestLauncherScript(testProvider: TestProvider) {
147-
switch (testProvider) {
148-
case 'unittest': {
149-
return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'visualstudio_py_testlauncher.py');
150-
}
151-
case 'pytest': {
152-
return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'testlauncher.py');
153-
}
154-
default: {
155-
throw new Error(`Unknown test provider '${testProvider}'`);
150+
function getTestLauncherScript(testProvider: TestProvider, pythonTestAdapterRewriteExperiment?: boolean) {
151+
if (!pythonTestAdapterRewriteExperiment) {
152+
switch (testProvider) {
153+
case 'unittest': {
154+
return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'visualstudio_py_testlauncher.py');
155+
}
156+
case 'pytest': {
157+
return path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'testlauncher.py');
158+
}
159+
default: {
160+
throw new Error(`Unknown test provider '${testProvider}'`);
161+
}
156162
}
157163
}
158164
}
165+
159166
function getDefaultDebugConfig(): DebugConfiguration {
160167
return {
161168
name: 'Debug Unit Test',
@@ -178,7 +185,7 @@ suite('Unit Tests - Debug Launcher', () => {
178185
expected?: DebugConfiguration,
179186
debugConfigs?: string | DebugConfiguration[],
180187
) {
181-
const testLaunchScript = getTestLauncherScript(testProvider);
188+
const testLaunchScript = getTestLauncherScript(testProvider, false);
182189

183190
const workspaceFolders = [createWorkspaceFolder(options.cwd), createWorkspaceFolder('five/six/seven')];
184191
getWorkspaceFoldersStub.returns(workspaceFolders);

0 commit comments

Comments
 (0)