diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 46217eab0459..7437a44d6080 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -23,6 +23,7 @@ import { extractJsonPayload, } from './utils'; import { createDeferred } from '../../../common/utils/async'; +import { EnvironmentVariables } from '../../../api/types'; export class PythonTestServer implements ITestServer, Disposable { private _onDataReceived: EventEmitter = new EventEmitter(); @@ -165,28 +166,29 @@ export class PythonTestServer implements ITestServer, Disposable { async sendCommand( options: TestCommandOptions, + env: EnvironmentVariables, runTestIdPort?: string, runInstance?: TestRun, testIds?: string[], callback?: () => void, ): Promise { const { uuid } = options; - + // get and edit env vars + const mutableEnv = { ...env }; const pythonPathParts: string[] = process.env.PYTHONPATH?.split(path.delimiter) ?? []; const pythonPathCommand = [options.cwd, ...pythonPathParts].join(path.delimiter); + mutableEnv.PYTHONPATH = pythonPathCommand; + mutableEnv.TEST_UUID = uuid.toString(); + mutableEnv.TEST_PORT = this.getPort().toString(); + mutableEnv.RUN_TEST_IDS_PORT = runTestIdPort; + const spawnOptions: SpawnOptions = { token: options.token, cwd: options.cwd, throwOnStdErr: true, outputChannel: options.outChannel, - extraVariables: { - PYTHONPATH: pythonPathCommand, - TEST_UUID: uuid.toString(), - TEST_PORT: this.getPort().toString(), - }, + env: mutableEnv, }; - - if (spawnOptions.extraVariables) spawnOptions.extraVariables.RUN_TEST_IDS_PORT = runTestIdPort; const isRun = runTestIdPort !== undefined; // Create the Python environment in which to execute the command. const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index 32e0c4ba8cc6..e51270eb4f9e 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -15,6 +15,7 @@ import { import { ITestDebugLauncher, TestDiscoveryOptions } from '../../common/types'; import { IPythonExecutionFactory } from '../../../common/process/types'; import { Deferred } from '../../../common/utils/async'; +import { EnvironmentVariables } from '../../../common/variables/types'; export type TestRunInstanceOptions = TestRunOptions & { exclude?: readonly TestItem[]; @@ -177,6 +178,7 @@ export interface ITestServer { readonly onDiscoveryDataReceived: Event; sendCommand( options: TestCommandOptions, + env: EnvironmentVariables, runTestIdsPort?: string, runInstance?: TestRun, testIds?: string[], diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index af77ab2b2525..a87017a26a51 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -50,6 +50,7 @@ import { ITestDebugLauncher } from '../common/types'; import { IServiceContainer } from '../../ioc/types'; import { PythonResultResolver } from './common/resultResolver'; import { onDidSaveTextDocument } from '../../common/vscodeApis/workspaceApis'; +import { IEnvironmentVariablesProvider } from '../../common/variables/types'; // Types gymnastics to make sure that sendTriggerTelemetry only accepts the correct types. type EventPropertyType = IEventNamePropertyMapping[EventName.UNITTEST_DISCOVERY_TRIGGER]; @@ -100,6 +101,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc @inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher, @inject(ITestOutputChannel) private readonly testOutputChannel: ITestOutputChannel, @inject(IServiceContainer) private readonly serviceContainer: IServiceContainer, + @inject(IEnvironmentVariablesProvider) private readonly envVarsService: IEnvironmentVariablesProvider, ) { this.refreshCancellation = new CancellationTokenSource(); @@ -174,12 +176,14 @@ export class PythonTestController implements ITestController, IExtensionSingleAc this.configSettings, this.testOutputChannel, resultResolver, + this.envVarsService, ); executionAdapter = new UnittestTestExecutionAdapter( this.pythonTestServer, this.configSettings, this.testOutputChannel, resultResolver, + this.envVarsService, ); } else { testProvider = PYTEST_PROVIDER; @@ -189,12 +193,14 @@ export class PythonTestController implements ITestController, IExtensionSingleAc this.configSettings, this.testOutputChannel, resultResolver, + this.envVarsService, ); executionAdapter = new PytestTestExecutionAdapter( this.pythonTestServer, this.configSettings, this.testOutputChannel, resultResolver, + this.envVarsService, ); } diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index c0e1a310ee4a..4ed2570ba7cc 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -19,6 +19,7 @@ import { ITestServer, } from '../common/types'; import { createDiscoveryErrorPayload, createEOTPayload, createTestingDeferred } from '../common/utils'; +import { IEnvironmentVariablesProvider } from '../../../common/variables/types'; /** * 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 { public configSettings: IConfigurationService, private readonly outputChannel: ITestOutputChannel, private readonly resultResolver?: ITestResultResolver, + private readonly envVarsService?: IEnvironmentVariablesProvider, ) {} async discoverTests(uri: Uri, executionFactory?: IPythonExecutionFactory): Promise { @@ -61,18 +63,21 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const { pytestArgs } = settings.testing; const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath; + // get and edit env vars + const mutableEnv = { + ...(await this.envVarsService?.getEnvironmentVariables(uri)), + }; const pythonPathParts: string[] = process.env.PYTHONPATH?.split(path.delimiter) ?? []; const pythonPathCommand = [fullPluginPath, ...pythonPathParts].join(path.delimiter); + mutableEnv.PYTHONPATH = pythonPathCommand; + mutableEnv.TEST_UUID = uuid.toString(); + mutableEnv.TEST_PORT = this.testServer.getPort().toString(); const spawnOptions: SpawnOptions = { cwd, throwOnStdErr: true, - extraVariables: { - PYTHONPATH: pythonPathCommand, - TEST_UUID: uuid.toString(), - TEST_PORT: this.testServer.getPort().toString(), - }, outputChannel: this.outputChannel, + env: mutableEnv, }; // Create the Python environment in which to execute the command. diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index 8020be17cf90..eb8e9b6f935a 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -24,6 +24,7 @@ import { ITestDebugLauncher, LaunchOptions } from '../../common/types'; import { PYTEST_PROVIDER } from '../../common/constants'; import { EXTENSION_ROOT_DIR } from '../../../common/constants'; import * as utils from '../common/utils'; +import { IEnvironmentVariablesProvider } from '../../../common/variables/types'; export class PytestTestExecutionAdapter implements ITestExecutionAdapter { constructor( @@ -31,6 +32,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { public configSettings: IConfigurationService, private readonly outputChannel: ITestOutputChannel, private readonly resultResolver?: ITestResultResolver, + private readonly envVarsService?: IEnvironmentVariablesProvider, ) {} async runTests( @@ -46,6 +48,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { const deferredTillEOT: Deferred = utils.createTestingDeferred(); const dataReceivedDisposable = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { + runInstance?.token.isCancellationRequested; if (runInstance) { const eParsed = JSON.parse(e.data); this.resultResolver?.resolveExecution(eParsed, runInstance, deferredTillEOT); @@ -105,20 +108,13 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { const settings = this.configSettings.getSettings(uri); const { pytestArgs } = settings.testing; const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath; - + // get and edit env vars + const mutableEnv = { ...(await this.envVarsService?.getEnvironmentVariables(uri)) }; const pythonPathParts: string[] = process.env.PYTHONPATH?.split(path.delimiter) ?? []; const pythonPathCommand = [fullPluginPath, ...pythonPathParts].join(path.delimiter); - const spawnOptions: SpawnOptions = { - cwd, - throwOnStdErr: true, - extraVariables: { - PYTHONPATH: pythonPathCommand, - TEST_UUID: uuid.toString(), - TEST_PORT: this.testServer.getPort().toString(), - }, - outputChannel: this.outputChannel, - stdinStr: testIds.toString(), - }; + mutableEnv.PYTHONPATH = pythonPathCommand; + mutableEnv.TEST_UUID = uuid.toString(); + mutableEnv.TEST_PORT = this.testServer.getPort().toString(); // Create the Python environment in which to execute the command. const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { @@ -141,9 +137,17 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { testArgs.push('--capture', 'no'); } + // add port with run test ids to env vars const pytestRunTestIdsPort = await utils.startTestIdServer(testIds); - if (spawnOptions.extraVariables) - spawnOptions.extraVariables.RUN_TEST_IDS_PORT = pytestRunTestIdsPort.toString(); + mutableEnv.RUN_TEST_IDS_PORT = pytestRunTestIdsPort.toString(); + + const spawnOptions: SpawnOptions = { + cwd, + throwOnStdErr: true, + outputChannel: this.outputChannel, + stdinStr: testIds.toString(), + env: mutableEnv, + }; if (debugBool) { const pytestPort = this.testServer.getPort().toString(); diff --git a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts index 440df4f94dc6..75e29afc9712 100644 --- a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts @@ -15,6 +15,7 @@ import { TestDiscoveryCommand, } from '../common/types'; import { Deferred, createDeferred } from '../../../common/utils/async'; +import { EnvironmentVariables, IEnvironmentVariablesProvider } from '../../../common/variables/types'; /** * Wrapper class for unittest test discovery. This is where we call `runTestCommand`. @@ -25,13 +26,17 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { public configSettings: IConfigurationService, private readonly outputChannel: ITestOutputChannel, private readonly resultResolver?: ITestResultResolver, + private readonly envVarsService?: IEnvironmentVariablesProvider, ) {} public async discoverTests(uri: Uri): Promise { const settings = this.configSettings.getSettings(uri); const { unittestArgs } = settings.testing; const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath; - + let env: EnvironmentVariables | undefined = await this.envVarsService?.getEnvironmentVariables(uri); + if (env === undefined) { + env = {} as EnvironmentVariables; + } const command = buildDiscoveryCommand(unittestArgs); const uuid = this.testServer.createUUID(uri.fsPath); @@ -52,7 +57,7 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { dataReceivedDisposable.dispose(); }; - await this.callSendCommand(options, () => { + await this.callSendCommand(options, env, () => { disposeDataReceiver?.(this.testServer); }); await deferredTillEOT.promise; @@ -66,8 +71,12 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { return discoveryPayload; } - private async callSendCommand(options: TestCommandOptions, callback: () => void): Promise { - await this.testServer.sendCommand(options, undefined, undefined, [], callback); + private async callSendCommand( + options: TestCommandOptions, + env: EnvironmentVariables, + callback: () => void, + ): Promise { + await this.testServer.sendCommand(options, env, undefined, undefined, [], callback); const discoveryPayload: DiscoveredTestPayload = { cwd: '', status: 'success' }; return discoveryPayload; } diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index 9da0872ef601..d90581a93110 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -17,6 +17,7 @@ import { } from '../common/types'; import { traceError, traceInfo, traceLog } from '../../../logging'; import { startTestIdServer } from '../common/utils'; +import { EnvironmentVariables, IEnvironmentVariablesProvider } from '../../../common/variables/types'; /** * Wrapper Class for unittest test execution. This is where we call `runTestCommand`? @@ -28,6 +29,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { public configSettings: IConfigurationService, private readonly outputChannel: ITestOutputChannel, private readonly resultResolver?: ITestResultResolver, + private readonly envVarsService?: IEnvironmentVariablesProvider, ) {} public async runTests( @@ -78,6 +80,10 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath; const command = buildExecutionCommand(unittestArgs); + let env: EnvironmentVariables | undefined = await this.envVarsService?.getEnvironmentVariables(uri); + if (env === undefined) { + env = {} as EnvironmentVariables; + } const options: TestCommandOptions = { workspaceFolder: uri, @@ -92,7 +98,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { const runTestIdsPort = await startTestIdServer(testIds); - await this.testServer.sendCommand(options, runTestIdsPort.toString(), runInstance, testIds, () => { + await this.testServer.sendCommand(options, env, runTestIdsPort.toString(), runInstance, testIds, () => { deferredTillEOT?.resolve(); }); // placeholder until after the rewrite is adopted diff --git a/src/test/testing/common/testingAdapter.test.ts b/src/test/testing/common/testingAdapter.test.ts index 4f46f1cf738c..3b5ef0062a98 100644 --- a/src/test/testing/common/testingAdapter.test.ts +++ b/src/test/testing/common/testingAdapter.test.ts @@ -20,6 +20,7 @@ import { UnittestTestExecutionAdapter } from '../../../client/testing/testContro import { PythonResultResolver } from '../../../client/testing/testController/common/resultResolver'; import { TestProvider } from '../../../client/testing/types'; import { PYTEST_PROVIDER, UNITTEST_PROVIDER } from '../../../client/testing/common/constants'; +import { IEnvironmentVariablesProvider } from '../../../client/common/variables/types'; suite('End to End Tests: test adapters', () => { let resultResolver: ITestResultResolver; @@ -28,6 +29,7 @@ suite('End to End Tests: test adapters', () => { let debugLauncher: ITestDebugLauncher; let configService: IConfigurationService; let serviceContainer: IServiceContainer; + let envVarsService: IEnvironmentVariablesProvider; let workspaceUri: Uri; let testOutputChannel: typeMoq.IMock; let testController: TestController; @@ -67,6 +69,7 @@ suite('End to End Tests: test adapters', () => { pythonExecFactory = serviceContainer.get(IPythonExecutionFactory); debugLauncher = serviceContainer.get(ITestDebugLauncher); testController = serviceContainer.get(ITestController); + envVarsService = serviceContainer.get(IEnvironmentVariablesProvider); // create objects that were not injected pythonTestServer = new PythonTestServer(pythonExecFactory, debugLauncher); @@ -121,6 +124,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); await discoveryAdapter.discoverTests(workspaceUri).finally(() => { @@ -167,6 +171,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); await discoveryAdapter.discoverTests(workspaceUri).finally(() => { @@ -206,6 +211,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); // set workspace to test workspace folder @@ -248,6 +254,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); // set workspace to test workspace folder @@ -301,6 +308,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); const testRun = typeMoq.Mock.ofType(); testRun @@ -353,6 +361,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); const testRun = typeMoq.Mock.ofType(); testRun @@ -403,6 +412,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); const testRun = typeMoq.Mock.ofType(); testRun @@ -467,6 +477,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); const testRun = typeMoq.Mock.ofType(); testRun @@ -524,6 +535,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); const testRun = typeMoq.Mock.ofType(); testRun @@ -579,6 +591,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); // set workspace to test workspace folder @@ -641,6 +654,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); const testRun = typeMoq.Mock.ofType(); testRun @@ -701,6 +715,7 @@ suite('End to End Tests: test adapters', () => { configService, testOutputChannel.object, resultResolver, + envVarsService, ); const testRun = typeMoq.Mock.ofType(); testRun diff --git a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts index 8ba7dd9a6f00..7badb5a0350d 100644 --- a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts +++ b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.unit.test.ts @@ -33,6 +33,7 @@ suite('pytest test discovery adapter', () => { let uri: Uri; let expectedExtraVariables: Record; let mockProc: MockChildProcess; + let deferred2: Deferred; setup(() => { const mockExtensionRootDir = typeMoq.Mock.ofType(); @@ -73,20 +74,25 @@ suite('pytest test discovery adapter', () => { // set up exec service with child process mockProc = new MockChildProcess('', ['']); execService = typeMoq.Mock.ofType(); + execService.setup((p) => ((p as unknown) as any).then).returns(() => undefined); + outputChannel = typeMoq.Mock.ofType(); + const output = new Observable>(() => { /* no op */ }); + deferred2 = createDeferred(); execService .setup((x) => x.execObservable(typeMoq.It.isAny(), typeMoq.It.isAny())) - .returns(() => ({ - proc: mockProc, - out: output, - dispose: () => { - /* no-body */ - }, - })); - execService.setup((p) => ((p as unknown) as any).then).returns(() => undefined); - outputChannel = typeMoq.Mock.ofType(); + .returns(() => { + deferred2.resolve(); + return { + proc: mockProc, + out: output, + dispose: () => { + /* no-body */ + }, + }; + }); }); test('Discovery should call exec with correct basic args', async () => { // set up exec mock @@ -98,24 +104,28 @@ suite('pytest test discovery adapter', () => { deferred.resolve(); return Promise.resolve(execService.object); }); - adapter = new PytestTestDiscoveryAdapter(testServer.object, configService, outputChannel.object); adapter.discoverTests(uri, execFactory.object); // add in await and trigger await deferred.promise; + await deferred2.promise; mockProc.trigger('close'); // verification - const expectedArgs = ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only', '.']; execService.verify( (x) => x.execObservable( - expectedArgs, + typeMoq.It.isAny(), typeMoq.It.is((options) => { - assert.deepEqual(options.extraVariables, expectedExtraVariables); - assert.equal(options.cwd, expectedPath); - assert.equal(options.throwOnStdErr, true); - return true; + try { + assert.deepEqual(options.env, expectedExtraVariables); + assert.equal(options.cwd, expectedPath); + assert.equal(options.throwOnStdErr, true); + return true; + } catch (e) { + console.error(e); + throw e; + } }), ), typeMoq.Times.once(), @@ -147,6 +157,7 @@ suite('pytest test discovery adapter', () => { adapter.discoverTests(uri, execFactory.object); // add in await and trigger await deferred.promise; + await deferred2.promise; mockProc.trigger('close'); // verification @@ -156,7 +167,7 @@ suite('pytest test discovery adapter', () => { x.execObservable( expectedArgs, typeMoq.It.is((options) => { - assert.deepEqual(options.extraVariables, expectedExtraVariables); + assert.deepEqual(options.env, expectedExtraVariables); assert.equal(options.cwd, expectedPathNew); assert.equal(options.throwOnStdErr, true); return true; diff --git a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts index a2e5c810dc86..a097df654360 100644 --- a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts +++ b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts @@ -30,6 +30,7 @@ suite('pytest test execution adapter', () => { let adapter: PytestTestExecutionAdapter; let execService: typeMoq.IMock; let deferred: Deferred; + let deferred4: Deferred; let debugLauncher: typeMoq.IMock; (global as any).EXTENSION_ROOT_DIR = EXTENSION_ROOT_DIR; let myTestPath: string; @@ -59,16 +60,20 @@ suite('pytest test execution adapter', () => { const output = new Observable>(() => { /* no op */ }); + deferred4 = createDeferred(); execService = typeMoq.Mock.ofType(); execService .setup((x) => x.execObservable(typeMoq.It.isAny(), typeMoq.It.isAny())) - .returns(() => ({ - proc: mockProc, - out: output, - dispose: () => { - /* no-body */ - }, - })); + .returns(() => { + deferred4.resolve(); + return { + proc: mockProc, + out: output, + dispose: () => { + /* no-body */ + }, + }; + }); execFactory = typeMoq.Mock.ofType(); utilsStartServerStub = sinon.stub(util, 'startTestIdServer'); debugLauncher = typeMoq.Mock.ofType(); @@ -161,6 +166,7 @@ suite('pytest test execution adapter', () => { await deferred2.promise; await deferred3.promise; + await deferred4.promise; mockProc.trigger('close'); const pathToPythonFiles = path.join(EXTENSION_ROOT_DIR, 'pythonFiles'); @@ -176,10 +182,10 @@ suite('pytest test execution adapter', () => { x.execObservable( expectedArgs, typeMoq.It.is((options) => { - assert.equal(options.extraVariables?.PYTHONPATH, expectedExtraVariables.PYTHONPATH); - assert.equal(options.extraVariables?.TEST_UUID, expectedExtraVariables.TEST_UUID); - assert.equal(options.extraVariables?.TEST_PORT, expectedExtraVariables.TEST_PORT); - assert.equal(options.extraVariables?.RUN_TEST_IDS_PORT, '54321'); + assert.equal(options.env?.PYTHONPATH, expectedExtraVariables.PYTHONPATH); + assert.equal(options.env?.TEST_UUID, expectedExtraVariables.TEST_UUID); + assert.equal(options.env?.TEST_PORT, expectedExtraVariables.TEST_PORT); + assert.equal(options.env?.RUN_TEST_IDS_PORT, '54321'); assert.equal(options.cwd, uri.fsPath); assert.equal(options.throwOnStdErr, true); return true; @@ -227,6 +233,7 @@ suite('pytest test execution adapter', () => { await deferred2.promise; await deferred3.promise; + await deferred4.promise; mockProc.trigger('close'); const pathToPythonFiles = path.join(EXTENSION_ROOT_DIR, 'pythonFiles'); @@ -243,10 +250,10 @@ suite('pytest test execution adapter', () => { x.execObservable( expectedArgs, typeMoq.It.is((options) => { - assert.equal(options.extraVariables?.PYTHONPATH, expectedExtraVariables.PYTHONPATH); - assert.equal(options.extraVariables?.TEST_UUID, expectedExtraVariables.TEST_UUID); - assert.equal(options.extraVariables?.TEST_PORT, expectedExtraVariables.TEST_PORT); - assert.equal(options.extraVariables?.RUN_TEST_IDS_PORT, '54321'); + assert.equal(options.env?.PYTHONPATH, expectedExtraVariables.PYTHONPATH); + assert.equal(options.env?.TEST_UUID, expectedExtraVariables.TEST_UUID); + assert.equal(options.env?.TEST_PORT, expectedExtraVariables.TEST_PORT); + assert.equal(options.env?.RUN_TEST_IDS_PORT, '54321'); assert.equal(options.cwd, newCwd); assert.equal(options.throwOnStdErr, true); return true; diff --git a/src/test/testing/testController/server.unit.test.ts b/src/test/testing/testController/server.unit.test.ts index 02c35e806156..eaf94eca5189 100644 --- a/src/test/testing/testController/server.unit.test.ts +++ b/src/test/testing/testController/server.unit.test.ts @@ -139,7 +139,7 @@ suite('Python Test Server, DataWithPayloadChunks', () => { traceLog('Socket connection error:', error); }); - server.sendCommand(options); + server.sendCommand(options, {}); await deferred.promise; const expectedResult = dataWithPayloadChunks.data; assert.deepStrictEqual(eventData, expectedResult); @@ -176,32 +176,35 @@ suite('Python Test Server, Send command etc', () => { test('sendCommand should add the port to the command being sent and add the correct extra spawn variables', async () => { const deferred2 = createDeferred(); const RUN_TEST_IDS_PORT_CONST = '5678'; + let error = false; + let errorMessage = ''; execService .setup((x) => x.execObservable(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns((_args, options2) => { try { assert.strictEqual( - options2.extraVariables.PYTHONPATH, + options2.env.PYTHONPATH, '/foo/bar', 'Expect python path to exist as extra variable and be set correctly', ); assert.strictEqual( - options2.extraVariables.RUN_TEST_IDS_PORT, + options2.env.RUN_TEST_IDS_PORT, RUN_TEST_IDS_PORT_CONST, 'Expect test id port to be in extra variables and set correctly', ); assert.strictEqual( - options2.extraVariables.TEST_UUID, + options2.env.TEST_UUID, FAKE_UUID, 'Expect test uuid to be in extra variables and set correctly', ); assert.strictEqual( - options2.extraVariables.TEST_PORT, - 12345, + options2.env.TEST_PORT, + '12345', 'Expect server port to be set correctly as a env var', ); } catch (e) { - assert(false, 'Error parsing data, extra variables do not match'); + error = true; + errorMessage = `error occurred, assertion was incorrect, ${e}`; } return typeMoq.Mock.ofType>().object; }); @@ -222,13 +225,20 @@ suite('Python Test Server, Send command etc', () => { cwd: '/foo/bar', uuid: FAKE_UUID, }; - server.sendCommand(options, RUN_TEST_IDS_PORT_CONST); + try { + server.sendCommand(options, {}, RUN_TEST_IDS_PORT_CONST); + } catch (e) { + assert(false, `Error sending command, ${e}`); + } // add in await and trigger await deferred2.promise; mockProc.trigger('close'); const expectedArgs = ['myscript', '-foo', 'foo']; execService.verify((x) => x.execObservable(expectedArgs, typeMoq.It.isAny()), typeMoq.Times.once()); + if (error) { + assert(false, errorMessage); + } }); test('sendCommand should write to an output channel if it is provided as an option', async () => { @@ -260,13 +270,13 @@ suite('Python Test Server, Send command etc', () => { server = new PythonTestServer(execFactory.object, debugLauncher); await server.serverReady(); - server.sendCommand(options); + server.sendCommand(options, {}); // add in await and trigger await deferred.promise; mockProc.trigger('close'); const expected = ['python', 'myscript', '-foo', 'foo'].join(' '); - + assert.equal(output2.length, 1); assert.deepStrictEqual(output2, [expected]); }); @@ -303,7 +313,7 @@ suite('Python Test Server, Send command etc', () => { eventData = JSON.parse(data); }); - server.sendCommand(options); + server.sendCommand(options, {}); await deferred2.promise; await deferred3.promise; assert.notEqual(eventData, undefined); diff --git a/src/test/testing/testController/testCancellationRunAdapters.unit.test.ts b/src/test/testing/testController/testCancellationRunAdapters.unit.test.ts index e85cd2b62834..a0fb4eea8589 100644 --- a/src/test/testing/testController/testCancellationRunAdapters.unit.test.ts +++ b/src/test/testing/testController/testCancellationRunAdapters.unit.test.ts @@ -239,6 +239,7 @@ suite('Execution Flow Run Adapters', () => { typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny(), + typeMoq.It.isAny(), ), ) .returns(() => { @@ -319,6 +320,7 @@ suite('Execution Flow Run Adapters', () => { typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny(), + typeMoq.It.isAny(), ), ) .returns(() => { diff --git a/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts b/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts index dc883afdf441..0eee88120f6a 100644 --- a/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts +++ b/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts @@ -9,6 +9,7 @@ import { IConfigurationService, ITestOutputChannel } from '../../../../client/co import { EXTENSION_ROOT_DIR } from '../../../../client/constants'; import { ITestServer, TestCommandOptions } from '../../../../client/testing/testController/common/types'; import { UnittestTestDiscoveryAdapter } from '../../../../client/testing/testController/unittest/testDiscoveryAdapter'; +import { createDeferred } from '../../../../client/common/utils/async'; suite('Unittest test discovery adapter', () => { let stubConfigSettings: IConfigurationService; @@ -26,10 +27,12 @@ suite('Unittest test discovery adapter', () => { test('DiscoverTests should send the discovery command to the test server with the correct args', async () => { let options: TestCommandOptions | undefined; + const deferred = createDeferred(); const stubTestServer = ({ sendCommand(opt: TestCommandOptions): Promise { delete opt.outChannel; options = opt; + deferred.resolve(); return Promise.resolve(); }, onDiscoveryDataReceived: () => { @@ -44,15 +47,12 @@ suite('Unittest test discovery adapter', () => { const adapter = new UnittestTestDiscoveryAdapter(stubTestServer, stubConfigSettings, outputChannel.object); adapter.discoverTests(uri); - assert.deepStrictEqual(options, { - workspaceFolder: uri, - cwd: uri.fsPath, - command: { - script, - args: ['--udiscovery', '-v', '-s', '.', '-p', 'test*'], - }, - uuid: '123456789', - }); + await deferred.promise; + assert.deepStrictEqual(options?.command?.args, ['--udiscovery', '-v', '-s', '.', '-p', 'test*']); + assert.deepStrictEqual(options.workspaceFolder, uri); + assert.deepStrictEqual(options.cwd, uri.fsPath); + assert.deepStrictEqual(options.command.script, script); + assert.deepStrictEqual(options.uuid, '123456789'); }); test('DiscoverTests should respect settings.testings.cwd when present', async () => { let options: TestCommandOptions | undefined; @@ -62,10 +62,12 @@ suite('Unittest test discovery adapter', () => { }), } as unknown) as IConfigurationService; + const deferred = createDeferred(); const stubTestServer = ({ sendCommand(opt: TestCommandOptions): Promise { delete opt.outChannel; options = opt; + deferred.resolve(); return Promise.resolve(); }, onDiscoveryDataReceived: () => { @@ -80,15 +82,11 @@ suite('Unittest test discovery adapter', () => { const adapter = new UnittestTestDiscoveryAdapter(stubTestServer, stubConfigSettings, outputChannel.object); adapter.discoverTests(uri); - - assert.deepStrictEqual(options, { - workspaceFolder: uri, - cwd: newCwd, - command: { - script, - args: ['--udiscovery', '-v', '-s', '.', '-p', 'test*'], - }, - uuid: '123456789', - }); + await deferred.promise; + assert.deepStrictEqual(options?.command?.args, ['--udiscovery', '-v', '-s', '.', '-p', 'test*']); + assert.deepStrictEqual(options.workspaceFolder, uri); + assert.deepStrictEqual(options.cwd, newCwd); + assert.deepStrictEqual(options.command.script, script); + assert.deepStrictEqual(options.uuid, '123456789'); }); });