Skip to content

Commit e7dfef8

Browse files
authored
fix to get user defined env and use it to set up testing subprocess (#22165)
fixes #21642 and #22166
1 parent 514bce6 commit e7dfef8

13 files changed

+171
-94
lines changed

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
extractJsonPayload,
2424
} from './utils';
2525
import { createDeferred } from '../../../common/utils/async';
26+
import { EnvironmentVariables } from '../../../api/types';
2627

2728
export class PythonTestServer implements ITestServer, Disposable {
2829
private _onDataReceived: EventEmitter<DataReceivedEvent> = new EventEmitter<DataReceivedEvent>();
@@ -165,28 +166,29 @@ export class PythonTestServer implements ITestServer, Disposable {
165166

166167
async sendCommand(
167168
options: TestCommandOptions,
169+
env: EnvironmentVariables,
168170
runTestIdPort?: string,
169171
runInstance?: TestRun,
170172
testIds?: string[],
171173
callback?: () => void,
172174
): Promise<void> {
173175
const { uuid } = options;
174-
176+
// get and edit env vars
177+
const mutableEnv = { ...env };
175178
const pythonPathParts: string[] = process.env.PYTHONPATH?.split(path.delimiter) ?? [];
176179
const pythonPathCommand = [options.cwd, ...pythonPathParts].join(path.delimiter);
180+
mutableEnv.PYTHONPATH = pythonPathCommand;
181+
mutableEnv.TEST_UUID = uuid.toString();
182+
mutableEnv.TEST_PORT = this.getPort().toString();
183+
mutableEnv.RUN_TEST_IDS_PORT = runTestIdPort;
184+
177185
const spawnOptions: SpawnOptions = {
178186
token: options.token,
179187
cwd: options.cwd,
180188
throwOnStdErr: true,
181189
outputChannel: options.outChannel,
182-
extraVariables: {
183-
PYTHONPATH: pythonPathCommand,
184-
TEST_UUID: uuid.toString(),
185-
TEST_PORT: this.getPort().toString(),
186-
},
190+
env: mutableEnv,
187191
};
188-
189-
if (spawnOptions.extraVariables) spawnOptions.extraVariables.RUN_TEST_IDS_PORT = runTestIdPort;
190192
const isRun = runTestIdPort !== undefined;
191193
// Create the Python environment in which to execute the command.
192194
const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import { ITestDebugLauncher, TestDiscoveryOptions } from '../../common/types';
1616
import { IPythonExecutionFactory } from '../../../common/process/types';
1717
import { Deferred } from '../../../common/utils/async';
18+
import { EnvironmentVariables } from '../../../common/variables/types';
1819

1920
export type TestRunInstanceOptions = TestRunOptions & {
2021
exclude?: readonly TestItem[];
@@ -177,6 +178,7 @@ export interface ITestServer {
177178
readonly onDiscoveryDataReceived: Event<DataReceivedEvent>;
178179
sendCommand(
179180
options: TestCommandOptions,
181+
env: EnvironmentVariables,
180182
runTestIdsPort?: string,
181183
runInstance?: TestRun,
182184
testIds?: string[],

src/client/testing/testController/controller.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import { ITestDebugLauncher } from '../common/types';
5050
import { IServiceContainer } from '../../ioc/types';
5151
import { PythonResultResolver } from './common/resultResolver';
5252
import { onDidSaveTextDocument } from '../../common/vscodeApis/workspaceApis';
53+
import { IEnvironmentVariablesProvider } from '../../common/variables/types';
5354

5455
// Types gymnastics to make sure that sendTriggerTelemetry only accepts the correct types.
5556
type EventPropertyType = IEventNamePropertyMapping[EventName.UNITTEST_DISCOVERY_TRIGGER];
@@ -100,6 +101,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc
100101
@inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher,
101102
@inject(ITestOutputChannel) private readonly testOutputChannel: ITestOutputChannel,
102103
@inject(IServiceContainer) private readonly serviceContainer: IServiceContainer,
104+
@inject(IEnvironmentVariablesProvider) private readonly envVarsService: IEnvironmentVariablesProvider,
103105
) {
104106
this.refreshCancellation = new CancellationTokenSource();
105107

@@ -174,12 +176,14 @@ export class PythonTestController implements ITestController, IExtensionSingleAc
174176
this.configSettings,
175177
this.testOutputChannel,
176178
resultResolver,
179+
this.envVarsService,
177180
);
178181
executionAdapter = new UnittestTestExecutionAdapter(
179182
this.pythonTestServer,
180183
this.configSettings,
181184
this.testOutputChannel,
182185
resultResolver,
186+
this.envVarsService,
183187
);
184188
} else {
185189
testProvider = PYTEST_PROVIDER;
@@ -189,12 +193,14 @@ export class PythonTestController implements ITestController, IExtensionSingleAc
189193
this.configSettings,
190194
this.testOutputChannel,
191195
resultResolver,
196+
this.envVarsService,
192197
);
193198
executionAdapter = new PytestTestExecutionAdapter(
194199
this.pythonTestServer,
195200
this.configSettings,
196201
this.testOutputChannel,
197202
resultResolver,
203+
this.envVarsService,
198204
);
199205
}
200206

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
ITestServer,
2020
} from '../common/types';
2121
import { createDiscoveryErrorPayload, createEOTPayload, createTestingDeferred } from '../common/utils';
22+
import { IEnvironmentVariablesProvider } from '../../../common/variables/types';
2223

2324
/**
2425
* Wrapper class for unittest test discovery. This is where we call `runTestCommand`. #this seems incorrectly copied
@@ -29,6 +30,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter {
2930
public configSettings: IConfigurationService,
3031
private readonly outputChannel: ITestOutputChannel,
3132
private readonly resultResolver?: ITestResultResolver,
33+
private readonly envVarsService?: IEnvironmentVariablesProvider,
3234
) {}
3335

3436
async discoverTests(uri: Uri, executionFactory?: IPythonExecutionFactory): Promise<DiscoveredTestPayload> {
@@ -61,18 +63,21 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter {
6163
const { pytestArgs } = settings.testing;
6264
const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath;
6365

66+
// get and edit env vars
67+
const mutableEnv = {
68+
...(await this.envVarsService?.getEnvironmentVariables(uri)),
69+
};
6470
const pythonPathParts: string[] = process.env.PYTHONPATH?.split(path.delimiter) ?? [];
6571
const pythonPathCommand = [fullPluginPath, ...pythonPathParts].join(path.delimiter);
72+
mutableEnv.PYTHONPATH = pythonPathCommand;
73+
mutableEnv.TEST_UUID = uuid.toString();
74+
mutableEnv.TEST_PORT = this.testServer.getPort().toString();
6675

6776
const spawnOptions: SpawnOptions = {
6877
cwd,
6978
throwOnStdErr: true,
70-
extraVariables: {
71-
PYTHONPATH: pythonPathCommand,
72-
TEST_UUID: uuid.toString(),
73-
TEST_PORT: this.testServer.getPort().toString(),
74-
},
7579
outputChannel: this.outputChannel,
80+
env: mutableEnv,
7681
};
7782

7883
// Create the Python environment in which to execute the command.

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

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ import { ITestDebugLauncher, LaunchOptions } from '../../common/types';
2424
import { PYTEST_PROVIDER } from '../../common/constants';
2525
import { EXTENSION_ROOT_DIR } from '../../../common/constants';
2626
import * as utils from '../common/utils';
27+
import { IEnvironmentVariablesProvider } from '../../../common/variables/types';
2728

2829
export class PytestTestExecutionAdapter implements ITestExecutionAdapter {
2930
constructor(
3031
public testServer: ITestServer,
3132
public configSettings: IConfigurationService,
3233
private readonly outputChannel: ITestOutputChannel,
3334
private readonly resultResolver?: ITestResultResolver,
35+
private readonly envVarsService?: IEnvironmentVariablesProvider,
3436
) {}
3537

3638
async runTests(
@@ -46,6 +48,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter {
4648
const deferredTillEOT: Deferred<void> = utils.createTestingDeferred();
4749

4850
const dataReceivedDisposable = this.testServer.onRunDataReceived((e: DataReceivedEvent) => {
51+
runInstance?.token.isCancellationRequested;
4952
if (runInstance) {
5053
const eParsed = JSON.parse(e.data);
5154
this.resultResolver?.resolveExecution(eParsed, runInstance, deferredTillEOT);
@@ -105,20 +108,13 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter {
105108
const settings = this.configSettings.getSettings(uri);
106109
const { pytestArgs } = settings.testing;
107110
const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath;
108-
111+
// get and edit env vars
112+
const mutableEnv = { ...(await this.envVarsService?.getEnvironmentVariables(uri)) };
109113
const pythonPathParts: string[] = process.env.PYTHONPATH?.split(path.delimiter) ?? [];
110114
const pythonPathCommand = [fullPluginPath, ...pythonPathParts].join(path.delimiter);
111-
const spawnOptions: SpawnOptions = {
112-
cwd,
113-
throwOnStdErr: true,
114-
extraVariables: {
115-
PYTHONPATH: pythonPathCommand,
116-
TEST_UUID: uuid.toString(),
117-
TEST_PORT: this.testServer.getPort().toString(),
118-
},
119-
outputChannel: this.outputChannel,
120-
stdinStr: testIds.toString(),
121-
};
115+
mutableEnv.PYTHONPATH = pythonPathCommand;
116+
mutableEnv.TEST_UUID = uuid.toString();
117+
mutableEnv.TEST_PORT = this.testServer.getPort().toString();
122118

123119
// Create the Python environment in which to execute the command.
124120
const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = {
@@ -141,9 +137,17 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter {
141137
testArgs.push('--capture', 'no');
142138
}
143139

140+
// add port with run test ids to env vars
144141
const pytestRunTestIdsPort = await utils.startTestIdServer(testIds);
145-
if (spawnOptions.extraVariables)
146-
spawnOptions.extraVariables.RUN_TEST_IDS_PORT = pytestRunTestIdsPort.toString();
142+
mutableEnv.RUN_TEST_IDS_PORT = pytestRunTestIdsPort.toString();
143+
144+
const spawnOptions: SpawnOptions = {
145+
cwd,
146+
throwOnStdErr: true,
147+
outputChannel: this.outputChannel,
148+
stdinStr: testIds.toString(),
149+
env: mutableEnv,
150+
};
147151

148152
if (debugBool) {
149153
const pytestPort = this.testServer.getPort().toString();

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
TestDiscoveryCommand,
1616
} from '../common/types';
1717
import { Deferred, createDeferred } from '../../../common/utils/async';
18+
import { EnvironmentVariables, IEnvironmentVariablesProvider } from '../../../common/variables/types';
1819

1920
/**
2021
* Wrapper class for unittest test discovery. This is where we call `runTestCommand`.
@@ -25,13 +26,17 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter {
2526
public configSettings: IConfigurationService,
2627
private readonly outputChannel: ITestOutputChannel,
2728
private readonly resultResolver?: ITestResultResolver,
29+
private readonly envVarsService?: IEnvironmentVariablesProvider,
2830
) {}
2931

3032
public async discoverTests(uri: Uri): Promise<DiscoveredTestPayload> {
3133
const settings = this.configSettings.getSettings(uri);
3234
const { unittestArgs } = settings.testing;
3335
const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath;
34-
36+
let env: EnvironmentVariables | undefined = await this.envVarsService?.getEnvironmentVariables(uri);
37+
if (env === undefined) {
38+
env = {} as EnvironmentVariables;
39+
}
3540
const command = buildDiscoveryCommand(unittestArgs);
3641

3742
const uuid = this.testServer.createUUID(uri.fsPath);
@@ -52,7 +57,7 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter {
5257
dataReceivedDisposable.dispose();
5358
};
5459

55-
await this.callSendCommand(options, () => {
60+
await this.callSendCommand(options, env, () => {
5661
disposeDataReceiver?.(this.testServer);
5762
});
5863
await deferredTillEOT.promise;
@@ -66,8 +71,12 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter {
6671
return discoveryPayload;
6772
}
6873

69-
private async callSendCommand(options: TestCommandOptions, callback: () => void): Promise<DiscoveredTestPayload> {
70-
await this.testServer.sendCommand(options, undefined, undefined, [], callback);
74+
private async callSendCommand(
75+
options: TestCommandOptions,
76+
env: EnvironmentVariables,
77+
callback: () => void,
78+
): Promise<DiscoveredTestPayload> {
79+
await this.testServer.sendCommand(options, env, undefined, undefined, [], callback);
7180
const discoveryPayload: DiscoveredTestPayload = { cwd: '', status: 'success' };
7281
return discoveryPayload;
7382
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from '../common/types';
1818
import { traceError, traceInfo, traceLog } from '../../../logging';
1919
import { startTestIdServer } from '../common/utils';
20+
import { EnvironmentVariables, IEnvironmentVariablesProvider } from '../../../common/variables/types';
2021

2122
/**
2223
* Wrapper Class for unittest test execution. This is where we call `runTestCommand`?
@@ -28,6 +29,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter {
2829
public configSettings: IConfigurationService,
2930
private readonly outputChannel: ITestOutputChannel,
3031
private readonly resultResolver?: ITestResultResolver,
32+
private readonly envVarsService?: IEnvironmentVariablesProvider,
3133
) {}
3234

3335
public async runTests(
@@ -78,6 +80,10 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter {
7880
const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath;
7981

8082
const command = buildExecutionCommand(unittestArgs);
83+
let env: EnvironmentVariables | undefined = await this.envVarsService?.getEnvironmentVariables(uri);
84+
if (env === undefined) {
85+
env = {} as EnvironmentVariables;
86+
}
8187

8288
const options: TestCommandOptions = {
8389
workspaceFolder: uri,
@@ -92,7 +98,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter {
9298

9399
const runTestIdsPort = await startTestIdServer(testIds);
94100

95-
await this.testServer.sendCommand(options, runTestIdsPort.toString(), runInstance, testIds, () => {
101+
await this.testServer.sendCommand(options, env, runTestIdsPort.toString(), runInstance, testIds, () => {
96102
deferredTillEOT?.resolve();
97103
});
98104
// placeholder until after the rewrite is adopted

0 commit comments

Comments
 (0)