From c278ee7208a8c54c5be9c39f419f33fede9c3db6 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 14 Jul 2023 13:15:28 -0700 Subject: [PATCH 01/16] current progress stuck --- .../testing/testController/common/server.ts | 29 ++++++++++++++---- .../testing/testController/common/types.ts | 7 ++++- .../pytest/pytestDiscoveryAdapter.ts | 24 ++++++++------- .../pytest/pytestExecutionAdapter.ts | 30 +++++++++++++++---- .../unittest/testDiscoveryAdapter.ts | 18 +++++++---- .../unittest/testExecutionAdapter.ts | 23 ++++++++++---- 6 files changed, 96 insertions(+), 35 deletions(-) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index f854371ffc35..999a2a8b371e 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -3,7 +3,7 @@ import * as net from 'net'; import * as crypto from 'crypto'; -import { Disposable, Event, EventEmitter } from 'vscode'; +import { Disposable, Event, EventEmitter, TestRun } from 'vscode'; import * as path from 'path'; import { ExecutionFactoryCreateWithEnvironmentOptions, @@ -40,6 +40,7 @@ export class PythonTestServer implements ITestServer, Disposable { const rpcHeaders = jsonRPCHeaders(buffer.toString()); const uuid = rpcHeaders.headers.get(JSONRPC_UUID_HEADER); const totalContentLength = rpcHeaders.headers.get('Content-Length'); + traceLog('ABCDEFG::: testing for UUID, if no error then good', uuid); if (!uuid) { traceError('On data received: Error occurred because payload UUID is undefined'); this._onDataReceived.fire({ uuid: '', data: '' }); @@ -120,6 +121,7 @@ export class PythonTestServer implements ITestServer, Disposable { } public deleteUUID(uuid: string): void { + traceLog('ABCDEFG::: DELETE IS OCCURRRING'); this.uuids = this.uuids.filter((u) => u !== uuid); } @@ -140,7 +142,12 @@ export class PythonTestServer implements ITestServer, Disposable { return this._onDataReceived.event; } - async sendCommand(options: TestCommandOptions, runTestIdPort?: string, callback?: () => void): Promise { + async sendCommand( + options: TestCommandOptions, + runTestIdPort?: string, + runInstance?: TestRun, + callback?: () => void, + ): Promise { const { uuid } = options; const pythonPathParts: string[] = process.env.PYTHONPATH?.split(path.delimiter) ?? []; @@ -154,7 +161,7 @@ export class PythonTestServer implements ITestServer, Disposable { }; if (spawnOptions.extraVariables) spawnOptions.extraVariables.RUN_TEST_IDS_PORT = runTestIdPort; - const isRun = !options.testIds; + const isRun = runTestIdPort !== undefined; // Create the Python environment in which to execute the command. const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { allowEnvironmentFetchExceptions: false, @@ -190,12 +197,20 @@ export class PythonTestServer implements ITestServer, Disposable { } else { if (isRun) { // This means it is running the test - traceInfo(`Running unittests with arguments: ${args}\r\n`); + traceInfo(`EJFB Running unittests with arguments: ${args}\r\n`); } else { // This means it is running discovery - traceLog(`Discovering unittest tests with arguments: ${args}\r\n`); + traceLog(`EJFB Discovering unittest tests with arguments: ${args}\r\n`); } - await execService.exec(args, spawnOptions); + const result = await execService.execObservable(args, spawnOptions); + + runInstance?.token.onCancellationRequested(() => { + result?.proc?.kill(); + }); + result?.proc?.on('close', () => { + traceLog('ABCDEFG::: callback on proc close, server.', uuid); + callback?.(); + }); } } catch (ex) { this.uuids = this.uuids.filter((u) => u !== uuid); @@ -206,6 +221,8 @@ export class PythonTestServer implements ITestServer, Disposable { errors: [(ex as Error).message], }), }); + } finally { + traceLog('ABCDEFG::: done with send command, NO deletion :)'); } } } diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index d4e54951bfd7..16c0bd0e3cee 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -174,7 +174,12 @@ export interface ITestServer { readonly onDataReceived: Event; readonly onRunDataReceived: Event; readonly onDiscoveryDataReceived: Event; - sendCommand(options: TestCommandOptions, runTestIdsPort?: string, callback?: () => void): Promise; + sendCommand( + options: TestCommandOptions, + runTestIdsPort?: string, + runInstance?: TestRun, + callback?: () => void, + ): Promise; serverReady(): Promise; getPort(): number; createUUID(cwd: string): string; diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index b83224d4161b..a72101588034 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -10,7 +10,7 @@ import { import { IConfigurationService, ITestOutputChannel } from '../../../common/types'; import { createDeferred } from '../../../common/utils/async'; import { EXTENSION_ROOT_DIR } from '../../../constants'; -import { traceError, traceVerbose } from '../../../logging'; +import { traceError, traceLog, traceVerbose } from '../../../logging'; import { DataReceivedEvent, DiscoveredTestPayload, @@ -78,17 +78,21 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { }; const execService = await executionFactory?.createActivatedEnvironment(creationOptions); // delete UUID following entire discovery finishing. - execService - ?.exec(['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs), spawnOptions) - .then(() => { - this.testServer.deleteUUID(uuid); - return deferred.resolve(); - }) - .catch((err) => { - traceError(`Error while trying to run pytest discovery, \n${err}\r\n\r\n`); + try { + const result = execService?.execObservable( + ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs), + spawnOptions, + ); + result?.proc?.on('close', () => { + traceLog('ABCDEFG::: callback on proc close.'); this.testServer.deleteUUID(uuid); - return deferred.reject(err); + deferred.resolve(); }); + } catch (ex) { + traceError(`Error while trying to run pytest discovery, \n${ex}\r\n\r\n`); + this.testServer.deleteUUID(uuid); + deferred.reject(ex); + } return deferred.promise; } } diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index a75a6089627c..6aa3b6f96e1f 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -47,18 +47,25 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { debugLauncher?: ITestDebugLauncher, ): Promise { const uuid = this.testServer.createUUID(uri.fsPath); + traceLog('ABCDEFG::: create UUID for run', uuid); traceVerbose(uri, testIds, debugBool); - const disposable = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { + const disposedDataReceived = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { if (runInstance) { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } }); + const dispose = function (testServer: ITestServer) { + traceLog('ABCDEFG::: dispose w/ uuid', uuid); + testServer.deleteUUID(uuid); + disposedDataReceived.dispose(); + }; + runInstance?.token.onCancellationRequested(() => { + dispose(this.testServer); + }); try { - await this.runTestsNew(uri, testIds, uuid, debugBool, executionFactory, debugLauncher); + await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, executionFactory, debugLauncher); } finally { - this.testServer.deleteUUID(uuid); - disposable.dispose(); - // confirm with testing that this gets called (it must clean this up) + // dispose(this.testServer); } // placeholder until after the rewrite is adopted // TODO: remove after adoption. @@ -74,6 +81,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { uri: Uri, testIds: string[], uuid: string, + runInstance?: TestRun, debugBool?: boolean, executionFactory?: IPythonExecutionFactory, debugLauncher?: ITestDebugLauncher, @@ -150,7 +158,17 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { const runArgs = [scriptPath, ...testArgs]; traceInfo(`Running pytests with arguments: ${runArgs.join(' ')}\r\n`); - await execService?.exec(runArgs, spawnOptions); + const result = await execService?.execObservable(runArgs, spawnOptions); + + runInstance?.token.onCancellationRequested(() => { + result?.proc?.kill(); + }); + + result?.proc?.on('close', () => { + traceLog('ABCDEFG::: callback on proc close, delete UUID.', uuid); + this.testServer.deleteUUID(uuid); + deferred.resolve(); + }); } } catch (ex) { traceError(`Error while running tests: ${testIds}\r\n${ex}\r\n\r\n`); diff --git a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts index 6deca55117ea..489835d454fe 100644 --- a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts @@ -14,6 +14,7 @@ import { TestCommandOptions, TestDiscoveryCommand, } from '../common/types'; +import { traceLog } from '../../../logging'; /** * Wrapper class for unittest test discovery. This is where we call `runTestCommand`. @@ -34,6 +35,7 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const command = buildDiscoveryCommand(unittestArgs); const uuid = this.testServer.createUUID(uri.fsPath); + traceLog(`ABCDEFG: create UUID ${uuid}`); const options: TestCommandOptions = { workspaceFolder: uri, @@ -46,12 +48,16 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const disposable = this.testServer.onDiscoveryDataReceived((e: DataReceivedEvent) => { this.resultResolver?.resolveDiscovery(JSON.parse(e.data)); }); - try { - await this.callSendCommand(options); - } finally { + + await this.callSendCommand(options, () => { this.testServer.deleteUUID(uuid); disposable.dispose(); - } + }); + + // finally { + // this.testServer.deleteUUID(uuid); + // disposable.dispose(); + // } // placeholder until after the rewrite is adopted // TODO: remove after adoption. const discoveryPayload: DiscoveredTestPayload = { @@ -61,8 +67,8 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { return discoveryPayload; } - private async callSendCommand(options: TestCommandOptions): Promise { - await this.testServer.sendCommand(options); + private async callSendCommand(options: TestCommandOptions, callback: () => void): Promise { + await this.testServer.sendCommand(options, 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 4cab941c2608..42602fc236f8 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -37,17 +37,24 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { runInstance?: TestRun, ): Promise { const uuid = this.testServer.createUUID(uri.fsPath); - const disposable = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { + traceLog('ABCDEFG::: created UUID', uuid); + const disposedDataReceived = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { if (runInstance) { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } }); + const dispose = function () { + traceLog('ABCDEFG::: no dispose of uuid :)'); + disposedDataReceived.dispose(); + }; + runInstance?.token.onCancellationRequested(() => { + this.testServer.deleteUUID(uuid); + dispose(); + }); try { - await this.runTestsNew(uri, testIds, uuid, debugBool); + await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, dispose); } finally { - this.testServer.deleteUUID(uuid); - disposable.dispose(); - // confirm with testing that this gets called (it must clean this up) + // dispose(this.testServer); } const executionPayload: ExecutionTestPayload = { cwd: uri.fsPath, status: 'success', error: '' }; return executionPayload; @@ -57,7 +64,9 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { uri: Uri, testIds: string[], uuid: string, + runInstance?: TestRun, debugBool?: boolean, + dispose?: () => void, ): Promise { const settings = this.configSettings.getSettings(uri); const { unittestArgs } = settings.testing; @@ -80,8 +89,10 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { const runTestIdsPort = await startTestIdServer(testIds); - await this.testServer.sendCommand(options, runTestIdsPort.toString(), () => { + await this.testServer.sendCommand(options, runTestIdsPort.toString(), runInstance, () => { + this.testServer.deleteUUID(uuid); deferred.resolve(); + dispose?.(); }); // placeholder until after the rewrite is adopted // TODO: remove after adoption. From 7cd5217b5914204ef1916023f4025585fa251566 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Tue, 18 Jul 2023 15:30:32 -0700 Subject: [PATCH 02/16] stuck p2 --- .../testing/testController/common/server.ts | 11 +++++++-- .../pytest/pytestDiscoveryAdapter.ts | 24 ++++++++----------- .../pytest/pytestExecutionAdapter.ts | 6 ++++- .../unittest/testExecutionAdapter.ts | 1 + 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 999a2a8b371e..c10d63ebfdda 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -7,6 +7,7 @@ import { Disposable, Event, EventEmitter, TestRun } from 'vscode'; import * as path from 'path'; import { ExecutionFactoryCreateWithEnvironmentOptions, + ExecutionResult, IPythonExecutionFactory, SpawnOptions, } from '../../../common/process/types'; @@ -15,6 +16,7 @@ import { DataReceivedEvent, ITestServer, TestCommandOptions } from './types'; import { ITestDebugLauncher, LaunchOptions } from '../../common/types'; import { UNITTEST_PROVIDER } from '../../common/constants'; import { jsonRPCHeaders, jsonRPCContent, JSONRPC_UUID_HEADER } from './utils'; +import { createDeferred } from '../../../common/utils/async'; export class PythonTestServer implements ITestServer, Disposable { private _onDataReceived: EventEmitter = new EventEmitter(); @@ -30,6 +32,7 @@ export class PythonTestServer implements ITestServer, Disposable { private _onDiscoveryDataReceived: EventEmitter = new EventEmitter(); constructor(private executionFactory: IPythonExecutionFactory, private debugLauncher: ITestDebugLauncher) { + traceLog('ABCDEFG::: testing for UUID, if no error then good'); this.server = net.createServer((socket: net.Socket) => { let buffer: Buffer = Buffer.alloc(0); // Buffer to accumulate received data socket.on('data', (data: Buffer) => { @@ -202,15 +205,19 @@ export class PythonTestServer implements ITestServer, Disposable { // This means it is running discovery traceLog(`EJFB Discovering unittest tests with arguments: ${args}\r\n`); } - const result = await execService.execObservable(args, spawnOptions); + const deferred = createDeferred>(); + + const result = execService.execObservable(args, spawnOptions); runInstance?.token.onCancellationRequested(() => { result?.proc?.kill(); }); result?.proc?.on('close', () => { - traceLog('ABCDEFG::: callback on proc close, server.', uuid); + traceLog('Exec server closed.', uuid); + deferred.resolve({ stdout: '', stderr: '' }); callback?.(); }); + await deferred.promise; } } catch (ex) { this.uuids = this.uuids.filter((u) => u !== uuid); diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index a72101588034..b83224d4161b 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -10,7 +10,7 @@ import { import { IConfigurationService, ITestOutputChannel } from '../../../common/types'; import { createDeferred } from '../../../common/utils/async'; import { EXTENSION_ROOT_DIR } from '../../../constants'; -import { traceError, traceLog, traceVerbose } from '../../../logging'; +import { traceError, traceVerbose } from '../../../logging'; import { DataReceivedEvent, DiscoveredTestPayload, @@ -78,21 +78,17 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { }; const execService = await executionFactory?.createActivatedEnvironment(creationOptions); // delete UUID following entire discovery finishing. - try { - const result = execService?.execObservable( - ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs), - spawnOptions, - ); - result?.proc?.on('close', () => { - traceLog('ABCDEFG::: callback on proc close.'); + execService + ?.exec(['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs), spawnOptions) + .then(() => { + this.testServer.deleteUUID(uuid); + return deferred.resolve(); + }) + .catch((err) => { + traceError(`Error while trying to run pytest discovery, \n${err}\r\n\r\n`); this.testServer.deleteUUID(uuid); - deferred.resolve(); + return deferred.reject(err); }); - } catch (ex) { - traceError(`Error while trying to run pytest discovery, \n${ex}\r\n\r\n`); - this.testServer.deleteUUID(uuid); - deferred.reject(ex); - } return deferred.promise; } } diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index 6aa3b6f96e1f..ccaaeafdcfb2 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -15,6 +15,7 @@ import { } from '../common/types'; import { ExecutionFactoryCreateWithEnvironmentOptions, + ExecutionResult, IPythonExecutionFactory, SpawnOptions, } from '../../../common/process/types'; @@ -158,7 +159,8 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { const runArgs = [scriptPath, ...testArgs]; traceInfo(`Running pytests with arguments: ${runArgs.join(' ')}\r\n`); - const result = await execService?.execObservable(runArgs, spawnOptions); + const deferred2 = createDeferred>(); + const result = execService?.execObservable(runArgs, spawnOptions); runInstance?.token.onCancellationRequested(() => { result?.proc?.kill(); @@ -166,9 +168,11 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { result?.proc?.on('close', () => { traceLog('ABCDEFG::: callback on proc close, delete UUID.', uuid); + deferred2.resolve({ stdout: '', stderr: '' }); this.testServer.deleteUUID(uuid); deferred.resolve(); }); + await deferred2.promise; } } catch (ex) { traceError(`Error while running tests: ${testIds}\r\n${ex}\r\n\r\n`); diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index 42602fc236f8..289d9daa86f3 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -90,6 +90,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { const runTestIdsPort = await startTestIdServer(testIds); await this.testServer.sendCommand(options, runTestIdsPort.toString(), runInstance, () => { + traceLog('ABCDEFG::: resolve deferred, of send command in execution to then call delete UUID'); this.testServer.deleteUUID(uuid); deferred.resolve(); dispose?.(); From 1686268de5ef2d61ae6ebf1d5a603bf7b2a9cffe Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 19 Jul 2023 11:02:12 -0700 Subject: [PATCH 03/16] working now for all 4 --- .../pytest/pytestDiscoveryAdapter.ts | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index b83224d4161b..e9c22b6026b5 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -4,13 +4,14 @@ import * as path from 'path'; import { Uri } from 'vscode'; import { ExecutionFactoryCreateWithEnvironmentOptions, + ExecutionResult, IPythonExecutionFactory, SpawnOptions, } from '../../../common/process/types'; import { IConfigurationService, ITestOutputChannel } from '../../../common/types'; import { createDeferred } from '../../../common/utils/async'; import { EXTENSION_ROOT_DIR } from '../../../constants'; -import { traceError, traceVerbose } from '../../../logging'; +import { traceError, traceLog, traceVerbose } from '../../../logging'; import { DataReceivedEvent, DiscoveredTestPayload, @@ -78,17 +79,27 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { }; const execService = await executionFactory?.createActivatedEnvironment(creationOptions); // delete UUID following entire discovery finishing. - execService - ?.exec(['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs), spawnOptions) - .then(() => { - this.testServer.deleteUUID(uuid); - return deferred.resolve(); - }) - .catch((err) => { - traceError(`Error while trying to run pytest discovery, \n${err}\r\n\r\n`); - this.testServer.deleteUUID(uuid); - return deferred.reject(err); - }); - return deferred.promise; + const deferred2 = createDeferred>(); + const execArgs = ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs); + const result = execService?.execObservable(execArgs, spawnOptions); + + result?.proc?.on('close', () => { + traceLog('ABCDEFG::: callback on proc close, delete UUID.', uuid); + deferred2.resolve({ stdout: '', stderr: '' }); + this.testServer.deleteUUID(uuid); + deferred.resolve(); + }); + await deferred2.promise; + + // .then(() => { + // this.testServer.deleteUUID(uuid); + // return deferred.resolve(); + // }) + // .catch((err) => { + // traceError(`Error while trying to run pytest discovery, \n${err}\r\n\r\n`); + // this.testServer.deleteUUID(uuid); + // return deferred.reject(err); + // }); + // return deferred.promise; } } From ebed320ed996d1c9bc4520ff0c57feac52257c5b Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 19 Jul 2023 11:53:53 -0700 Subject: [PATCH 04/16] pytest work --- .../testing/testController/pytest/pytestDiscoveryAdapter.ts | 4 ++-- .../testing/testController/pytest/pytestExecutionAdapter.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index e9c22b6026b5..e371aa12dd01 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -11,7 +11,7 @@ import { import { IConfigurationService, ITestOutputChannel } from '../../../common/types'; import { createDeferred } from '../../../common/utils/async'; import { EXTENSION_ROOT_DIR } from '../../../constants'; -import { traceError, traceLog, traceVerbose } from '../../../logging'; +import { traceLog, traceVerbose } from '../../../logging'; import { DataReceivedEvent, DiscoveredTestPayload, @@ -49,7 +49,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { return discoveryPayload; } - async runPytestDiscovery(uri: Uri, executionFactory?: IPythonExecutionFactory): Promise { + async runPytestDiscovery(uri: Uri, executionFactory?: IPythonExecutionFactory): Promise { const deferred = createDeferred(); const relativePathToPytest = 'pythonFiles'; const fullPluginPath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index ccaaeafdcfb2..f20ca133efcb 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -152,6 +152,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { traceInfo(`Running DEBUG pytest with arguments: ${testArgs.join(' ')}\r\n`); await debugLauncher!.launchDebugger(launchOptions, () => { deferred.resolve(); + this.testServer.deleteUUID(uuid); }); } else { // combine path to run script with run args From 28c95d5f17d9632537a89845e8c519bd44f6e5e6 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 19 Jul 2023 13:40:06 -0700 Subject: [PATCH 05/16] remove debug logging --- src/client/testing/testController/common/server.ts | 5 ----- .../testing/testController/pytest/pytestDiscoveryAdapter.ts | 1 - .../testing/testController/pytest/pytestExecutionAdapter.ts | 3 --- .../testing/testController/unittest/testDiscoveryAdapter.ts | 2 -- .../testing/testController/unittest/testExecutionAdapter.ts | 3 --- 5 files changed, 14 deletions(-) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index c10d63ebfdda..fb54fdd649e7 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -32,7 +32,6 @@ export class PythonTestServer implements ITestServer, Disposable { private _onDiscoveryDataReceived: EventEmitter = new EventEmitter(); constructor(private executionFactory: IPythonExecutionFactory, private debugLauncher: ITestDebugLauncher) { - traceLog('ABCDEFG::: testing for UUID, if no error then good'); this.server = net.createServer((socket: net.Socket) => { let buffer: Buffer = Buffer.alloc(0); // Buffer to accumulate received data socket.on('data', (data: Buffer) => { @@ -43,7 +42,6 @@ export class PythonTestServer implements ITestServer, Disposable { const rpcHeaders = jsonRPCHeaders(buffer.toString()); const uuid = rpcHeaders.headers.get(JSONRPC_UUID_HEADER); const totalContentLength = rpcHeaders.headers.get('Content-Length'); - traceLog('ABCDEFG::: testing for UUID, if no error then good', uuid); if (!uuid) { traceError('On data received: Error occurred because payload UUID is undefined'); this._onDataReceived.fire({ uuid: '', data: '' }); @@ -124,7 +122,6 @@ export class PythonTestServer implements ITestServer, Disposable { } public deleteUUID(uuid: string): void { - traceLog('ABCDEFG::: DELETE IS OCCURRRING'); this.uuids = this.uuids.filter((u) => u !== uuid); } @@ -228,8 +225,6 @@ export class PythonTestServer implements ITestServer, Disposable { errors: [(ex as Error).message], }), }); - } finally { - traceLog('ABCDEFG::: done with send command, NO deletion :)'); } } } diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index e371aa12dd01..3d43fd639d8b 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -84,7 +84,6 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const result = execService?.execObservable(execArgs, spawnOptions); result?.proc?.on('close', () => { - traceLog('ABCDEFG::: callback on proc close, delete UUID.', uuid); deferred2.resolve({ stdout: '', stderr: '' }); this.testServer.deleteUUID(uuid); deferred.resolve(); diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index f20ca133efcb..7cc718cfce24 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -48,7 +48,6 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { debugLauncher?: ITestDebugLauncher, ): Promise { const uuid = this.testServer.createUUID(uri.fsPath); - traceLog('ABCDEFG::: create UUID for run', uuid); traceVerbose(uri, testIds, debugBool); const disposedDataReceived = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { if (runInstance) { @@ -56,7 +55,6 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { } }); const dispose = function (testServer: ITestServer) { - traceLog('ABCDEFG::: dispose w/ uuid', uuid); testServer.deleteUUID(uuid); disposedDataReceived.dispose(); }; @@ -168,7 +166,6 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { }); result?.proc?.on('close', () => { - traceLog('ABCDEFG::: callback on proc close, delete UUID.', uuid); deferred2.resolve({ stdout: '', stderr: '' }); this.testServer.deleteUUID(uuid); deferred.resolve(); diff --git a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts index 489835d454fe..acf973b7be07 100644 --- a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts @@ -14,7 +14,6 @@ import { TestCommandOptions, TestDiscoveryCommand, } from '../common/types'; -import { traceLog } from '../../../logging'; /** * Wrapper class for unittest test discovery. This is where we call `runTestCommand`. @@ -35,7 +34,6 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const command = buildDiscoveryCommand(unittestArgs); const uuid = this.testServer.createUUID(uri.fsPath); - traceLog(`ABCDEFG: create UUID ${uuid}`); const options: TestCommandOptions = { workspaceFolder: uri, diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index 289d9daa86f3..9492b0fb5b04 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -37,14 +37,12 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { runInstance?: TestRun, ): Promise { const uuid = this.testServer.createUUID(uri.fsPath); - traceLog('ABCDEFG::: created UUID', uuid); const disposedDataReceived = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { if (runInstance) { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } }); const dispose = function () { - traceLog('ABCDEFG::: no dispose of uuid :)'); disposedDataReceived.dispose(); }; runInstance?.token.onCancellationRequested(() => { @@ -90,7 +88,6 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { const runTestIdsPort = await startTestIdServer(testIds); await this.testServer.sendCommand(options, runTestIdsPort.toString(), runInstance, () => { - traceLog('ABCDEFG::: resolve deferred, of send command in execution to then call delete UUID'); this.testServer.deleteUUID(uuid); deferred.resolve(); dispose?.(); From 460af28124e47da89a40860e2208eda827a907ea Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 19 Jul 2023 14:42:58 -0700 Subject: [PATCH 06/16] small cleanup --- .../testing/testController/common/server.ts | 4 ++-- .../pytest/pytestDiscoveryAdapter.ts | 17 +++-------------- .../pytest/pytestExecutionAdapter.ts | 19 +++++-------------- .../unittest/testDiscoveryAdapter.ts | 5 ----- .../unittest/testExecutionAdapter.ts | 6 +----- 5 files changed, 11 insertions(+), 40 deletions(-) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index fb54fdd649e7..564bd82f2ef6 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -197,10 +197,10 @@ export class PythonTestServer implements ITestServer, Disposable { } else { if (isRun) { // This means it is running the test - traceInfo(`EJFB Running unittests with arguments: ${args}\r\n`); + traceInfo(`Running unittests with arguments: ${args}\r\n`); } else { // This means it is running discovery - traceLog(`EJFB Discovering unittest tests with arguments: ${args}\r\n`); + traceLog(`Discovering unittest tests with arguments: ${args}\r\n`); } const deferred = createDeferred>(); diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 3d43fd639d8b..a70fcdc5a16e 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -79,26 +79,15 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { }; const execService = await executionFactory?.createActivatedEnvironment(creationOptions); // delete UUID following entire discovery finishing. - const deferred2 = createDeferred>(); + const deferredExec = createDeferred>(); const execArgs = ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs); const result = execService?.execObservable(execArgs, spawnOptions); result?.proc?.on('close', () => { - deferred2.resolve({ stdout: '', stderr: '' }); + deferredExec.resolve({ stdout: '', stderr: '' }); this.testServer.deleteUUID(uuid); deferred.resolve(); }); - await deferred2.promise; - - // .then(() => { - // this.testServer.deleteUUID(uuid); - // return deferred.resolve(); - // }) - // .catch((err) => { - // traceError(`Error while trying to run pytest discovery, \n${err}\r\n\r\n`); - // this.testServer.deleteUUID(uuid); - // return deferred.reject(err); - // }); - // return deferred.promise; + await deferredExec.promise; } } diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index 7cc718cfce24..576c4fd38d06 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -25,12 +25,6 @@ import { PYTEST_PROVIDER } from '../../common/constants'; import { EXTENSION_ROOT_DIR } from '../../../common/constants'; import { startTestIdServer } from '../common/utils'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -// (global as any).EXTENSION_ROOT_DIR = EXTENSION_ROOT_DIR; -/** - * Wrapper Class for pytest test execution.. - */ - export class PytestTestExecutionAdapter implements ITestExecutionAdapter { constructor( public testServer: ITestServer, @@ -61,11 +55,8 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { runInstance?.token.onCancellationRequested(() => { dispose(this.testServer); }); - try { - await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, executionFactory, debugLauncher); - } finally { - // dispose(this.testServer); - } + await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, executionFactory, debugLauncher); + // placeholder until after the rewrite is adopted // TODO: remove after adoption. const executionPayload: ExecutionTestPayload = { @@ -158,7 +149,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { const runArgs = [scriptPath, ...testArgs]; traceInfo(`Running pytests with arguments: ${runArgs.join(' ')}\r\n`); - const deferred2 = createDeferred>(); + const deferredExec = createDeferred>(); const result = execService?.execObservable(runArgs, spawnOptions); runInstance?.token.onCancellationRequested(() => { @@ -166,11 +157,11 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { }); result?.proc?.on('close', () => { - deferred2.resolve({ stdout: '', stderr: '' }); + deferredExec.resolve({ stdout: '', stderr: '' }); this.testServer.deleteUUID(uuid); deferred.resolve(); }); - await deferred2.promise; + await deferredExec.promise; } } catch (ex) { traceError(`Error while running tests: ${testIds}\r\n${ex}\r\n\r\n`); diff --git a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts index acf973b7be07..b49ac3dabd0e 100644 --- a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts @@ -51,11 +51,6 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { this.testServer.deleteUUID(uuid); disposable.dispose(); }); - - // finally { - // this.testServer.deleteUUID(uuid); - // disposable.dispose(); - // } // placeholder until after the rewrite is adopted // TODO: remove after adoption. const discoveryPayload: DiscoveredTestPayload = { diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index 9492b0fb5b04..4cd392f93a43 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -49,11 +49,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { this.testServer.deleteUUID(uuid); dispose(); }); - try { - await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, dispose); - } finally { - // dispose(this.testServer); - } + await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, dispose); const executionPayload: ExecutionTestPayload = { cwd: uri.fsPath, status: 'success', error: '' }; return executionPayload; } From 91acbd2973ce78e5fc9ef5024d14da7a21c892e9 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 24 Jul 2023 09:49:22 -0700 Subject: [PATCH 07/16] linting --- .../testing/testController/pytest/pytestDiscoveryAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index a70fcdc5a16e..810fae0fa11c 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -11,7 +11,7 @@ import { import { IConfigurationService, ITestOutputChannel } from '../../../common/types'; import { createDeferred } from '../../../common/utils/async'; import { EXTENSION_ROOT_DIR } from '../../../constants'; -import { traceLog, traceVerbose } from '../../../logging'; +import { traceVerbose } from '../../../logging'; import { DataReceivedEvent, DiscoveredTestPayload, From 9d063d04c753cb22a7fa7a021cf38f8509a538f9 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 24 Jul 2023 14:18:02 -0700 Subject: [PATCH 08/16] make testids relative to workspace --- .../pytestadapter/.data/root/tests/pytest.ini | 0 .../pytestadapter/.data/root/tests/test_a.py | 6 ++ .../pytestadapter/.data/root/tests/test_b.py | 6 ++ .../expected_discovery_test_output.py | 56 +++++++++++++++++++ pythonFiles/tests/pytestadapter/helpers.py | 9 ++- .../tests/pytestadapter/test_discovery.py | 54 +++++++++++++++++- pythonFiles/vscode_pytest/__init__.py | 17 +++++- 7 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 pythonFiles/tests/pytestadapter/.data/root/tests/pytest.ini create mode 100644 pythonFiles/tests/pytestadapter/.data/root/tests/test_a.py create mode 100644 pythonFiles/tests/pytestadapter/.data/root/tests/test_b.py diff --git a/pythonFiles/tests/pytestadapter/.data/root/tests/pytest.ini b/pythonFiles/tests/pytestadapter/.data/root/tests/pytest.ini new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pythonFiles/tests/pytestadapter/.data/root/tests/test_a.py b/pythonFiles/tests/pytestadapter/.data/root/tests/test_a.py new file mode 100644 index 000000000000..3ec3dd9626cb --- /dev/null +++ b/pythonFiles/tests/pytestadapter/.data/root/tests/test_a.py @@ -0,0 +1,6 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + + +def test_a_function(): # test_marker--test_a_function + assert True diff --git a/pythonFiles/tests/pytestadapter/.data/root/tests/test_b.py b/pythonFiles/tests/pytestadapter/.data/root/tests/test_b.py new file mode 100644 index 000000000000..0d3148641f85 --- /dev/null +++ b/pythonFiles/tests/pytestadapter/.data/root/tests/test_b.py @@ -0,0 +1,6 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + + +def test_b_function(): # test_marker--test_b_function + assert True diff --git a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py index 91c1453dfc77..ca23f55c0102 100644 --- a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py @@ -668,3 +668,59 @@ ], "id_": TEST_DATA_PATH_STR, } + +tests_path = ( + "/Users/eleanorboyd/vscode-python/pythonFiles/tests/pytestadapter/.data/root/tests" +) +# This is the expected output for the root folder tests. +# └── tests +# └── test_a.py +# └── test_a_function +# └── test_b.py +# └── test_b_function +root_with_config_expected_output = { + "name": "tests", + "path": tests_path, + "type_": "folder", + "children": [ + { + "name": "test_a.py", + "path": os.fspath(os.path.join(tests_path, "test_a.py")), + "type_": "file", + "id_": os.fspath(os.path.join(tests_path, "test_a.py")), + "children": [ + { + "name": "test_a_function", + "path": os.fspath(os.path.join(tests_path, "test_a.py")), + "lineno": find_test_line_number( + "test_a_function", + os.fspath(os.path.join(tests_path, "test_a.py")), + ), + "type_": "test", + "id_": "tests/test_a.py::test_a_function", + "runID": "tests/test_a.py::test_a_function", + } + ], + }, + { + "name": "test_b.py", + "path": os.fspath(os.path.join(tests_path, "test_b.py")), + "type_": "file", + "id_": os.fspath(os.path.join(tests_path, "test_b.py")), + "children": [ + { + "name": "test_b_function", + "path": os.fspath(os.path.join(tests_path, "test_b.py")), + "lineno": find_test_line_number( + "test_b_function", + os.fspath(os.path.join(tests_path, "test_b.py")), + ), + "type_": "test", + "id_": "tests/test_b.py::test_b_function", + "runID": "tests/test_b.py::test_b_function", + } + ], + }, + ], + "id_": tests_path, +} diff --git a/pythonFiles/tests/pytestadapter/helpers.py b/pythonFiles/tests/pytestadapter/helpers.py index c3e01d52170a..5e241bdb77a4 100644 --- a/pythonFiles/tests/pytestadapter/helpers.py +++ b/pythonFiles/tests/pytestadapter/helpers.py @@ -104,6 +104,13 @@ def process_rpc_json(data: str) -> List[Dict[str, Any]]: def runner(args: List[str]) -> Optional[List[Dict[str, Any]]]: + """Run the pytest discovery and return the JSON data from the server.""" + return runner_with_cwd(args, TEST_DATA_PATH) + + +def runner_with_cwd( + args: List[str], path: pathlib.Path +) -> Optional[List[Dict[str, Any]]]: """Run the pytest discovery and return the JSON data from the server.""" process_args: List[str] = [ sys.executable, @@ -134,7 +141,7 @@ def runner(args: List[str]) -> Optional[List[Dict[str, Any]]]: t2 = threading.Thread( target=_run_test_code, - args=(process_args, env, TEST_DATA_PATH, completed), + args=(process_args, env, path, completed), ) t2.start() diff --git a/pythonFiles/tests/pytestadapter/test_discovery.py b/pythonFiles/tests/pytestadapter/test_discovery.py index 5288c7ad769e..c58039787908 100644 --- a/pythonFiles/tests/pytestadapter/test_discovery.py +++ b/pythonFiles/tests/pytestadapter/test_discovery.py @@ -7,7 +7,7 @@ import pytest from . import expected_discovery_test_output -from .helpers import TEST_DATA_PATH, runner +from .helpers import TEST_DATA_PATH, runner, runner_with_cwd def test_import_error(tmp_path): @@ -153,3 +153,55 @@ def test_pytest_collect(file, expected_const): assert actual["status"] == "success" assert actual["cwd"] == os.fspath(TEST_DATA_PATH) assert actual["tests"] == expected_const + + +def test_pytest_root_dir(): + """ + Test to test pytest discovery with the command line arg --rootdir specified to be a subfolder + of the workspace root. Discovery should succeed and testids should be relative to workspace root. + """ + rd = f"--rootdir={TEST_DATA_PATH / 'root' / 'tests'}" + actual = runner_with_cwd( + [ + "--collect-only", + rd, + # "-c", + # "tests/pytest.ini", + ], + TEST_DATA_PATH / "root", + ) + if actual: + actual = actual[0] + assert actual + assert all(item in actual for item in ("status", "cwd", "tests")) + assert actual["status"] == "success" + assert actual["cwd"] == os.fspath(TEST_DATA_PATH / "root") + assert ( + actual["tests"] + == expected_discovery_test_output.root_with_config_expected_output + ) + + +def test_pytest_config_file(): + """ + Test to test pytest discovery with the command line arg -c with a specified config file which + changes the workspace root. Discovery should succeed and testids should be relative to workspace root. + """ + actual = runner_with_cwd( + [ + "--collect-only", + "-c", + "tests/pytest.ini", + ], + TEST_DATA_PATH / "root", + ) + if actual: + actual = actual[0] + assert actual + assert all(item in actual for item in ("status", "cwd", "tests")) + assert actual["status"] == "success" + assert actual["cwd"] == os.fspath(TEST_DATA_PATH / "root") + assert ( + actual["tests"] + == expected_discovery_test_output.root_with_config_expected_output + ) diff --git a/pythonFiles/vscode_pytest/__init__.py b/pythonFiles/vscode_pytest/__init__.py index 1ac287a8410a..297ca53bc93b 100644 --- a/pythonFiles/vscode_pytest/__init__.py +++ b/pythonFiles/vscode_pytest/__init__.py @@ -154,12 +154,21 @@ class testRunResultDict(Dict[str, Dict[str, TestOutcome]]): IS_DISCOVERY = False +RELATIVE_INVOCATION_PATH = "" def pytest_load_initial_conftests(early_config, parser, args): if "--collect-only" in args: global IS_DISCOVERY IS_DISCOVERY = True + invocation_dir = early_config.invocation_params.dir + root = early_config.rootpath + if invocation_dir != root: + try: + global RELATIVE_INVOCATION_PATH + RELATIVE_INVOCATION_PATH = root.relative_to(invocation_dir) + except: + pass collected_tests_so_far = list() @@ -471,13 +480,17 @@ def create_test_node( test_case_loc: str = ( str(test_case.location[1] + 1) if (test_case.location[1] is not None) else "" ) + id = test_case.nodeid + global RELATIVE_INVOCATION_PATH + if RELATIVE_INVOCATION_PATH: + id = str(pathlib.Path(RELATIVE_INVOCATION_PATH) / test_case.nodeid) return { "name": test_case.name, "path": get_node_path(test_case), "lineno": test_case_loc, "type_": "test", - "id_": test_case.nodeid, - "runID": test_case.nodeid, + "id_": id, + "runID": id, } From 85f2f8eb9da89c38a12cd25abbe5e47945cecf1b Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 24 Jul 2023 14:26:06 -0700 Subject: [PATCH 09/16] remove uuid stuff from other PR --- .../testing/testController/common/server.ts | 27 ++---------- .../testing/testController/common/types.ts | 7 +--- .../pytest/pytestDiscoveryAdapter.ts | 27 ++++++------ .../pytest/pytestExecutionAdapter.ts | 41 +++++++------------ .../unittest/testDiscoveryAdapter.ts | 11 ++--- .../unittest/testExecutionAdapter.ts | 21 ++++------ 6 files changed, 48 insertions(+), 86 deletions(-) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 564bd82f2ef6..f854371ffc35 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -3,11 +3,10 @@ import * as net from 'net'; import * as crypto from 'crypto'; -import { Disposable, Event, EventEmitter, TestRun } from 'vscode'; +import { Disposable, Event, EventEmitter } from 'vscode'; import * as path from 'path'; import { ExecutionFactoryCreateWithEnvironmentOptions, - ExecutionResult, IPythonExecutionFactory, SpawnOptions, } from '../../../common/process/types'; @@ -16,7 +15,6 @@ import { DataReceivedEvent, ITestServer, TestCommandOptions } from './types'; import { ITestDebugLauncher, LaunchOptions } from '../../common/types'; import { UNITTEST_PROVIDER } from '../../common/constants'; import { jsonRPCHeaders, jsonRPCContent, JSONRPC_UUID_HEADER } from './utils'; -import { createDeferred } from '../../../common/utils/async'; export class PythonTestServer implements ITestServer, Disposable { private _onDataReceived: EventEmitter = new EventEmitter(); @@ -142,12 +140,7 @@ export class PythonTestServer implements ITestServer, Disposable { return this._onDataReceived.event; } - async sendCommand( - options: TestCommandOptions, - runTestIdPort?: string, - runInstance?: TestRun, - callback?: () => void, - ): Promise { + async sendCommand(options: TestCommandOptions, runTestIdPort?: string, callback?: () => void): Promise { const { uuid } = options; const pythonPathParts: string[] = process.env.PYTHONPATH?.split(path.delimiter) ?? []; @@ -161,7 +154,7 @@ export class PythonTestServer implements ITestServer, Disposable { }; if (spawnOptions.extraVariables) spawnOptions.extraVariables.RUN_TEST_IDS_PORT = runTestIdPort; - const isRun = runTestIdPort !== undefined; + const isRun = !options.testIds; // Create the Python environment in which to execute the command. const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { allowEnvironmentFetchExceptions: false, @@ -202,19 +195,7 @@ export class PythonTestServer implements ITestServer, Disposable { // This means it is running discovery traceLog(`Discovering unittest tests with arguments: ${args}\r\n`); } - const deferred = createDeferred>(); - - const result = execService.execObservable(args, spawnOptions); - - runInstance?.token.onCancellationRequested(() => { - result?.proc?.kill(); - }); - result?.proc?.on('close', () => { - traceLog('Exec server closed.', uuid); - deferred.resolve({ stdout: '', stderr: '' }); - callback?.(); - }); - await deferred.promise; + await execService.exec(args, spawnOptions); } } catch (ex) { this.uuids = this.uuids.filter((u) => u !== uuid); diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index 16c0bd0e3cee..d4e54951bfd7 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -174,12 +174,7 @@ export interface ITestServer { readonly onDataReceived: Event; readonly onRunDataReceived: Event; readonly onDiscoveryDataReceived: Event; - sendCommand( - options: TestCommandOptions, - runTestIdsPort?: string, - runInstance?: TestRun, - callback?: () => void, - ): Promise; + sendCommand(options: TestCommandOptions, runTestIdsPort?: string, callback?: () => void): Promise; serverReady(): Promise; getPort(): number; createUUID(cwd: string): string; diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 810fae0fa11c..b83224d4161b 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -4,14 +4,13 @@ import * as path from 'path'; import { Uri } from 'vscode'; import { ExecutionFactoryCreateWithEnvironmentOptions, - ExecutionResult, IPythonExecutionFactory, SpawnOptions, } from '../../../common/process/types'; import { IConfigurationService, ITestOutputChannel } from '../../../common/types'; import { createDeferred } from '../../../common/utils/async'; import { EXTENSION_ROOT_DIR } from '../../../constants'; -import { traceVerbose } from '../../../logging'; +import { traceError, traceVerbose } from '../../../logging'; import { DataReceivedEvent, DiscoveredTestPayload, @@ -49,7 +48,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { return discoveryPayload; } - async runPytestDiscovery(uri: Uri, executionFactory?: IPythonExecutionFactory): Promise { + async runPytestDiscovery(uri: Uri, executionFactory?: IPythonExecutionFactory): Promise { const deferred = createDeferred(); const relativePathToPytest = 'pythonFiles'; const fullPluginPath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); @@ -79,15 +78,17 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { }; const execService = await executionFactory?.createActivatedEnvironment(creationOptions); // delete UUID following entire discovery finishing. - const deferredExec = createDeferred>(); - const execArgs = ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs); - const result = execService?.execObservable(execArgs, spawnOptions); - - result?.proc?.on('close', () => { - deferredExec.resolve({ stdout: '', stderr: '' }); - this.testServer.deleteUUID(uuid); - deferred.resolve(); - }); - await deferredExec.promise; + execService + ?.exec(['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs), spawnOptions) + .then(() => { + this.testServer.deleteUUID(uuid); + return deferred.resolve(); + }) + .catch((err) => { + traceError(`Error while trying to run pytest discovery, \n${err}\r\n\r\n`); + this.testServer.deleteUUID(uuid); + return deferred.reject(err); + }); + return deferred.promise; } } diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index 576c4fd38d06..a75a6089627c 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -15,7 +15,6 @@ import { } from '../common/types'; import { ExecutionFactoryCreateWithEnvironmentOptions, - ExecutionResult, IPythonExecutionFactory, SpawnOptions, } from '../../../common/process/types'; @@ -25,6 +24,12 @@ import { PYTEST_PROVIDER } from '../../common/constants'; import { EXTENSION_ROOT_DIR } from '../../../common/constants'; import { startTestIdServer } from '../common/utils'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +// (global as any).EXTENSION_ROOT_DIR = EXTENSION_ROOT_DIR; +/** + * Wrapper Class for pytest test execution.. + */ + export class PytestTestExecutionAdapter implements ITestExecutionAdapter { constructor( public testServer: ITestServer, @@ -43,20 +48,18 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { ): Promise { const uuid = this.testServer.createUUID(uri.fsPath); traceVerbose(uri, testIds, debugBool); - const disposedDataReceived = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { + const disposable = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { if (runInstance) { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } }); - const dispose = function (testServer: ITestServer) { - testServer.deleteUUID(uuid); - disposedDataReceived.dispose(); - }; - runInstance?.token.onCancellationRequested(() => { - dispose(this.testServer); - }); - await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, executionFactory, debugLauncher); - + try { + await this.runTestsNew(uri, testIds, uuid, debugBool, executionFactory, debugLauncher); + } finally { + this.testServer.deleteUUID(uuid); + disposable.dispose(); + // confirm with testing that this gets called (it must clean this up) + } // placeholder until after the rewrite is adopted // TODO: remove after adoption. const executionPayload: ExecutionTestPayload = { @@ -71,7 +74,6 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { uri: Uri, testIds: string[], uuid: string, - runInstance?: TestRun, debugBool?: boolean, executionFactory?: IPythonExecutionFactory, debugLauncher?: ITestDebugLauncher, @@ -141,7 +143,6 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { traceInfo(`Running DEBUG pytest with arguments: ${testArgs.join(' ')}\r\n`); await debugLauncher!.launchDebugger(launchOptions, () => { deferred.resolve(); - this.testServer.deleteUUID(uuid); }); } else { // combine path to run script with run args @@ -149,19 +150,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { const runArgs = [scriptPath, ...testArgs]; traceInfo(`Running pytests with arguments: ${runArgs.join(' ')}\r\n`); - const deferredExec = createDeferred>(); - const result = execService?.execObservable(runArgs, spawnOptions); - - runInstance?.token.onCancellationRequested(() => { - result?.proc?.kill(); - }); - - result?.proc?.on('close', () => { - deferredExec.resolve({ stdout: '', stderr: '' }); - this.testServer.deleteUUID(uuid); - deferred.resolve(); - }); - await deferredExec.promise; + await execService?.exec(runArgs, spawnOptions); } } catch (ex) { traceError(`Error while running tests: ${testIds}\r\n${ex}\r\n\r\n`); diff --git a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts index b49ac3dabd0e..6deca55117ea 100644 --- a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts @@ -46,11 +46,12 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const disposable = this.testServer.onDiscoveryDataReceived((e: DataReceivedEvent) => { this.resultResolver?.resolveDiscovery(JSON.parse(e.data)); }); - - await this.callSendCommand(options, () => { + try { + await this.callSendCommand(options); + } finally { this.testServer.deleteUUID(uuid); disposable.dispose(); - }); + } // placeholder until after the rewrite is adopted // TODO: remove after adoption. const discoveryPayload: DiscoveredTestPayload = { @@ -60,8 +61,8 @@ 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): Promise { + await this.testServer.sendCommand(options); 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 4cd392f93a43..4cab941c2608 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -37,19 +37,18 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { runInstance?: TestRun, ): Promise { const uuid = this.testServer.createUUID(uri.fsPath); - const disposedDataReceived = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { + const disposable = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { if (runInstance) { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } }); - const dispose = function () { - disposedDataReceived.dispose(); - }; - runInstance?.token.onCancellationRequested(() => { + try { + await this.runTestsNew(uri, testIds, uuid, debugBool); + } finally { this.testServer.deleteUUID(uuid); - dispose(); - }); - await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, dispose); + disposable.dispose(); + // confirm with testing that this gets called (it must clean this up) + } const executionPayload: ExecutionTestPayload = { cwd: uri.fsPath, status: 'success', error: '' }; return executionPayload; } @@ -58,9 +57,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { uri: Uri, testIds: string[], uuid: string, - runInstance?: TestRun, debugBool?: boolean, - dispose?: () => void, ): Promise { const settings = this.configSettings.getSettings(uri); const { unittestArgs } = settings.testing; @@ -83,10 +80,8 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { const runTestIdsPort = await startTestIdServer(testIds); - await this.testServer.sendCommand(options, runTestIdsPort.toString(), runInstance, () => { - this.testServer.deleteUUID(uuid); + await this.testServer.sendCommand(options, runTestIdsPort.toString(), () => { deferred.resolve(); - dispose?.(); }); // placeholder until after the rewrite is adopted // TODO: remove after adoption. From 3da2ee704a9ab0fc143cdef46f3e0ead07d1e475 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 24 Jul 2023 15:10:25 -0700 Subject: [PATCH 10/16] fix for execution --- .../expected_execution_test_output.py | 13 ++++++ .../tests/pytestadapter/test_discovery.py | 2 - .../tests/pytestadapter/test_execution.py | 42 ++++++++++++++++++- pythonFiles/vscode_pytest/__init__.py | 23 ++++++---- 4 files changed, 68 insertions(+), 12 deletions(-) diff --git a/pythonFiles/tests/pytestadapter/expected_execution_test_output.py b/pythonFiles/tests/pytestadapter/expected_execution_test_output.py index fe1d40a55b43..88661d9fa049 100644 --- a/pythonFiles/tests/pytestadapter/expected_execution_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_execution_test_output.py @@ -395,3 +395,16 @@ "subtest": None, }, } + +# This is the expected output for the root folder with the config file referenced. +# └── test_a.py +# └── test_a_function: success +config_file_pytest_expected_execution_output = { + "tests/test_a.py::test_a_function": { + "test": "tests/test_a.py::test_a_function", + "outcome": "success", + "message": None, + "traceback": None, + "subtest": None, + } +} diff --git a/pythonFiles/tests/pytestadapter/test_discovery.py b/pythonFiles/tests/pytestadapter/test_discovery.py index c58039787908..8d785be27c8b 100644 --- a/pythonFiles/tests/pytestadapter/test_discovery.py +++ b/pythonFiles/tests/pytestadapter/test_discovery.py @@ -165,8 +165,6 @@ def test_pytest_root_dir(): [ "--collect-only", rd, - # "-c", - # "tests/pytest.ini", ], TEST_DATA_PATH / "root", ) diff --git a/pythonFiles/tests/pytestadapter/test_execution.py b/pythonFiles/tests/pytestadapter/test_execution.py index ffc84955bf54..62d94a227a1b 100644 --- a/pythonFiles/tests/pytestadapter/test_execution.py +++ b/pythonFiles/tests/pytestadapter/test_execution.py @@ -1,12 +1,52 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import json import os import shutil import pytest from tests.pytestadapter import expected_execution_test_output -from .helpers import TEST_DATA_PATH, runner +from .helpers import TEST_DATA_PATH, runner, runner_with_cwd + + +def test_config_file(): + """Test pytest execution when a config file is specified.""" + args = ["-c", "tests/pytest.ini", "tests/test_a.py::test_a_function"] + new_cwd = TEST_DATA_PATH / "root" + actual = runner_with_cwd(args, new_cwd) + expected_const = ( + expected_execution_test_output.config_file_pytest_expected_execution_output + ) + assert actual + assert len(actual) == len(expected_const) + actual_result_dict = dict() + for a in actual: + assert all(item in a for item in ("status", "cwd", "result")) + assert a["status"] == "success" + assert a["cwd"] == os.fspath(new_cwd) + actual_result_dict.update(a["result"]) + assert actual_result_dict == expected_const + + +def test_rootdir_specified(): + """Test pytest execution when a --rootdir is specified.""" + rd = f"--rootdir={TEST_DATA_PATH / 'root' / 'tests'}" + args = [rd, "tests/test_a.py::test_a_function"] + new_cwd = TEST_DATA_PATH / "root" + actual = runner_with_cwd(args, new_cwd) + expected_const = ( + expected_execution_test_output.config_file_pytest_expected_execution_output + ) + assert actual + assert len(actual) == len(expected_const) + actual_result_dict = dict() + for a in actual: + assert all(item in a for item in ("status", "cwd", "result")) + assert a["status"] == "success" + assert a["cwd"] == os.fspath(new_cwd) + actual_result_dict.update(a["result"]) + assert actual_result_dict == expected_const def test_syntax_error_execution(tmp_path): diff --git a/pythonFiles/vscode_pytest/__init__.py b/pythonFiles/vscode_pytest/__init__.py index 297ca53bc93b..b935c97310e5 100644 --- a/pythonFiles/vscode_pytest/__init__.py +++ b/pythonFiles/vscode_pytest/__init__.py @@ -87,7 +87,7 @@ def pytest_exception_interact(node, call, report): report_value = "error" if call.excinfo.typename == "AssertionError": report_value = "failure" - node_id = str(node.nodeid) + node_id = get_workspace_node_id(str(node.nodeid)) if node_id not in collected_tests_so_far: collected_tests_so_far.append(node_id) item_result = create_test_outcome( @@ -106,6 +106,14 @@ def pytest_exception_interact(node, call, report): ) +def get_workspace_node_id(testid: str): + id = testid + global RELATIVE_INVOCATION_PATH + if RELATIVE_INVOCATION_PATH: + id = str(pathlib.Path(RELATIVE_INVOCATION_PATH) / testid) + return id + + def pytest_keyboard_interrupt(excinfo): """A pytest hook that is called when a keyboard interrupt is raised. @@ -130,7 +138,7 @@ class TestOutcome(Dict): def create_test_outcome( - test: str, + testid: str, outcome: str, message: Union[str, None], traceback: Union[str, None], @@ -138,7 +146,7 @@ def create_test_outcome( ) -> TestOutcome: """A function that creates a TestOutcome object.""" return TestOutcome( - test=test, + test=testid, outcome=outcome, message=message, traceback=traceback, # TODO: traceback @@ -193,7 +201,7 @@ def pytest_report_teststatus(report, config): elif report.failed: report_value = "failure" message = report.longreprtext - node_id = str(report.nodeid) + node_id = get_workspace_node_id(str(report.nodeid)) if node_id not in collected_tests_so_far: collected_tests_so_far.append(node_id) item_result = create_test_outcome( @@ -222,7 +230,7 @@ def pytest_report_teststatus(report, config): def pytest_runtest_protocol(item, nextitem): skipped = check_skipped_wrapper(item) if skipped: - node_id = str(item.nodeid) + node_id = get_workspace_node_id(str(item.nodeid)) report_value = "skipped" cwd = pathlib.Path.cwd() if node_id not in collected_tests_so_far: @@ -480,10 +488,7 @@ def create_test_node( test_case_loc: str = ( str(test_case.location[1] + 1) if (test_case.location[1] is not None) else "" ) - id = test_case.nodeid - global RELATIVE_INVOCATION_PATH - if RELATIVE_INVOCATION_PATH: - id = str(pathlib.Path(RELATIVE_INVOCATION_PATH) / test_case.nodeid) + id = get_workspace_node_id(test_case.nodeid) return { "name": test_case.name, "path": get_node_path(test_case), From e13f22b0b1742075ade0c4ef9987de8604b80b89 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 24 Jul 2023 15:28:46 -0700 Subject: [PATCH 11/16] remove string casting --- .../tests/pytestadapter/expected_discovery_test_output.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py index ca23f55c0102..97ae41b032b5 100644 --- a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py @@ -694,7 +694,7 @@ "path": os.fspath(os.path.join(tests_path, "test_a.py")), "lineno": find_test_line_number( "test_a_function", - os.fspath(os.path.join(tests_path, "test_a.py")), + os.path.join(tests_path, "test_a.py"), ), "type_": "test", "id_": "tests/test_a.py::test_a_function", @@ -713,7 +713,7 @@ "path": os.fspath(os.path.join(tests_path, "test_b.py")), "lineno": find_test_line_number( "test_b_function", - os.fspath(os.path.join(tests_path, "test_b.py")), + os.path.join(tests_path, "test_b.py"), ), "type_": "test", "id_": "tests/test_b.py::test_b_function", From 932f2d9c27698fbbe44cf8883c8770e435d5380c Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 24 Jul 2023 15:42:00 -0700 Subject: [PATCH 12/16] fix path p2 --- .../tests/pytestadapter/expected_discovery_test_output.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py index 97ae41b032b5..45f82324a8bf 100644 --- a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py @@ -669,9 +669,7 @@ "id_": TEST_DATA_PATH_STR, } -tests_path = ( - "/Users/eleanorboyd/vscode-python/pythonFiles/tests/pytestadapter/.data/root/tests" -) +tests_path = os.fspath(TEST_DATA_PATH / "root" / "tests") # This is the expected output for the root folder tests. # └── tests # └── test_a.py From 48817869b922fbd4957d1eb54efd725ac8956007 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 27 Jul 2023 08:52:40 -0700 Subject: [PATCH 13/16] fixing IDs --- .../expected_discovery_test_output.py | 291 +++++++++++------- .../tests/pytestadapter/test_discovery.py | 144 ++++----- .../tests/pytestadapter/test_execution.py | 6 +- pythonFiles/vscode_pytest/__init__.py | 65 ++-- 4 files changed, 290 insertions(+), 216 deletions(-) diff --git a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py index 45f82324a8bf..acd268571c14 100644 --- a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py @@ -1,5 +1,7 @@ import os +from pythonFiles.vscode_pytest import get_absolute_test_id + from .helpers import TEST_DATA_PATH, find_test_line_number # This file contains the expected output dictionaries for tests discovery and is used in test_discovery.py. @@ -18,7 +20,7 @@ # This is the expected output for the simple_pytest.py file. # └── simple_pytest.py # └── test_function -simple_test_file_path = os.fspath(TEST_DATA_PATH / "simple_pytest.py") +simple_test_file_path = TEST_DATA_PATH / "simple_pytest.py" simple_discovery_pytest_expected_output = { "name": ".data", "path": TEST_DATA_PATH_STR, @@ -26,20 +28,22 @@ "children": [ { "name": "simple_pytest.py", - "path": simple_test_file_path, + "path": os.fspath(simple_test_file_path), "type_": "file", - "id_": simple_test_file_path, + "id_": os.fspath(simple_test_file_path), "children": [ { "name": "test_function", - "path": simple_test_file_path, + "path": os.fspath(simple_test_file_path), "lineno": find_test_line_number( "test_function", simple_test_file_path, ), "type_": "test", "id_": "simple_pytest.py::test_function", - "runID": "simple_pytest.py::test_function", + "runID": get_absolute_test_id( + "simple_pytest.py::test_function", simple_test_file_path + ), } ], } @@ -52,7 +56,7 @@ # ├── TestExample # │ └── test_true_unittest # └── test_true_pytest -unit_pytest_same_file_path = os.fspath(TEST_DATA_PATH / "unittest_pytest_same_file.py") +unit_pytest_same_file_path = TEST_DATA_PATH / "unittest_pytest_same_file.py" unit_pytest_same_file_discovery_expected_output = { "name": ".data", "path": TEST_DATA_PATH_STR, @@ -60,39 +64,45 @@ "children": [ { "name": "unittest_pytest_same_file.py", - "path": unit_pytest_same_file_path, + "path": os.fspath(unit_pytest_same_file_path), "type_": "file", - "id_": unit_pytest_same_file_path, + "id_": os.fspath(unit_pytest_same_file_path), "children": [ { "name": "TestExample", - "path": unit_pytest_same_file_path, + "path": os.fspath(unit_pytest_same_file_path), "type_": "class", "children": [ { "name": "test_true_unittest", - "path": unit_pytest_same_file_path, + "path": os.fspath(unit_pytest_same_file_path), "lineno": find_test_line_number( "test_true_unittest", - unit_pytest_same_file_path, + os.fspath(unit_pytest_same_file_path), ), "type_": "test", "id_": "unittest_pytest_same_file.py::TestExample::test_true_unittest", - "runID": "unittest_pytest_same_file.py::TestExample::test_true_unittest", + "runID": get_absolute_test_id( + "unittest_pytest_same_file.py::TestExample::test_true_unittest", + unit_pytest_same_file_path, + ), } ], "id_": "unittest_pytest_same_file.py::TestExample", }, { "name": "test_true_pytest", - "path": unit_pytest_same_file_path, + "path": os.fspath(unit_pytest_same_file_path), "lineno": find_test_line_number( "test_true_pytest", unit_pytest_same_file_path, ), "type_": "test", "id_": "unittest_pytest_same_file.py::test_true_pytest", - "runID": "unittest_pytest_same_file.py::test_true_pytest", + "runID": get_absolute_test_id( + "unittest_pytest_same_file.py::test_true_pytest", + unit_pytest_same_file_path, + ), }, ], } @@ -124,9 +134,9 @@ # └── test_subtract_positive_numbers # │ └── TestDuplicateFunction # │ └── test_dup_s -unittest_folder_path = os.fspath(TEST_DATA_PATH / "unittest_folder") -test_add_path = os.fspath(TEST_DATA_PATH / "unittest_folder" / "test_add.py") -test_subtract_path = os.fspath(TEST_DATA_PATH / "unittest_folder" / "test_subtract.py") +unittest_folder_path = TEST_DATA_PATH / "unittest_folder" +test_add_path = TEST_DATA_PATH / "unittest_folder" / "test_add.py" +test_subtract_path = TEST_DATA_PATH / "unittest_folder" / "test_subtract.py" unittest_folder_discovery_expected_output = { "name": ".data", "path": TEST_DATA_PATH_STR, @@ -134,61 +144,70 @@ "children": [ { "name": "unittest_folder", - "path": unittest_folder_path, + "path": os.fspath(unittest_folder_path), "type_": "folder", - "id_": unittest_folder_path, + "id_": os.fspath(unittest_folder_path), "children": [ { "name": "test_add.py", - "path": test_add_path, + "path": os.fspath(test_add_path), "type_": "file", - "id_": test_add_path, + "id_": os.fspath(test_add_path), "children": [ { "name": "TestAddFunction", - "path": test_add_path, + "path": os.fspath(test_add_path), "type_": "class", "children": [ { "name": "test_add_negative_numbers", - "path": test_add_path, + "path": os.fspath(test_add_path), "lineno": find_test_line_number( "test_add_negative_numbers", - test_add_path, + os.fspath(test_add_path), ), "type_": "test", "id_": "unittest_folder/test_add.py::TestAddFunction::test_add_negative_numbers", - "runID": "unittest_folder/test_add.py::TestAddFunction::test_add_negative_numbers", + "runID": get_absolute_test_id( + "unittest_folder/test_add.py::TestAddFunction::test_add_negative_numbers", + test_add_path, + ), }, { "name": "test_add_positive_numbers", - "path": test_add_path, + "path": os.fspath(test_add_path), "lineno": find_test_line_number( "test_add_positive_numbers", - test_add_path, + os.fspath(test_add_path), ), "type_": "test", "id_": "unittest_folder/test_add.py::TestAddFunction::test_add_positive_numbers", - "runID": "unittest_folder/test_add.py::TestAddFunction::test_add_positive_numbers", + "runID": get_absolute_test_id( + "unittest_folder/test_add.py::TestAddFunction::test_add_positive_numbers", + test_add_path, + ), }, ], "id_": "unittest_folder/test_add.py::TestAddFunction", }, { "name": "TestDuplicateFunction", - "path": test_add_path, + "path": os.fspath(test_add_path), "type_": "class", "children": [ { "name": "test_dup_a", - "path": test_add_path, + "path": os.fspath(test_add_path), "lineno": find_test_line_number( "test_dup_a", - test_add_path, + os.fspath(test_add_path), ), "type_": "test", "id_": "unittest_folder/test_add.py::TestDuplicateFunction::test_dup_a", - "runID": "unittest_folder/test_add.py::TestDuplicateFunction::test_dup_a", + "runID": get_absolute_test_id( + "unittest_folder/test_add.py::TestDuplicateFunction::test_dup_a", + test_add_path, + ), }, ], "id_": "unittest_folder/test_add.py::TestDuplicateFunction", @@ -197,55 +216,64 @@ }, { "name": "test_subtract.py", - "path": test_subtract_path, + "path": os.fspath(test_subtract_path), "type_": "file", - "id_": test_subtract_path, + "id_": os.fspath(test_subtract_path), "children": [ { "name": "TestSubtractFunction", - "path": test_subtract_path, + "path": os.fspath(test_subtract_path), "type_": "class", "children": [ { "name": "test_subtract_negative_numbers", - "path": test_subtract_path, + "path": os.fspath(test_subtract_path), "lineno": find_test_line_number( "test_subtract_negative_numbers", - test_subtract_path, + os.fspath(test_subtract_path), ), "type_": "test", "id_": "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_negative_numbers", - "runID": "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_negative_numbers", + "runID": get_absolute_test_id( + "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_negative_numbers", + test_subtract_path, + ), }, { "name": "test_subtract_positive_numbers", - "path": test_subtract_path, + "path": os.fspath(test_subtract_path), "lineno": find_test_line_number( "test_subtract_positive_numbers", - test_subtract_path, + os.fspath(test_subtract_path), ), "type_": "test", "id_": "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_positive_numbers", - "runID": "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_positive_numbers", + "runID": get_absolute_test_id( + "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_positive_numbers", + test_subtract_path, + ), }, ], "id_": "unittest_folder/test_subtract.py::TestSubtractFunction", }, { "name": "TestDuplicateFunction", - "path": test_subtract_path, + "path": os.fspath(test_subtract_path), "type_": "class", "children": [ { "name": "test_dup_s", - "path": test_subtract_path, + "path": os.fspath(test_subtract_path), "lineno": find_test_line_number( "test_dup_s", - test_subtract_path, + os.fspath(test_subtract_path), ), "type_": "test", "id_": "unittest_folder/test_subtract.py::TestDuplicateFunction::test_dup_s", - "runID": "unittest_folder/test_subtract.py::TestDuplicateFunction::test_dup_s", + "runID": get_absolute_test_id( + "unittest_folder/test_subtract.py::TestDuplicateFunction::test_dup_s", + test_subtract_path, + ), }, ], "id_": "unittest_folder/test_subtract.py::TestDuplicateFunction", @@ -268,20 +296,23 @@ # └── test_bottom_folder.py # └── test_bottom_function_t # └── test_bottom_function_f -dual_level_nested_folder_path = os.fspath(TEST_DATA_PATH / "dual_level_nested_folder") -test_top_folder_path = os.fspath( +dual_level_nested_folder_path = TEST_DATA_PATH / "dual_level_nested_folder" +test_top_folder_path = ( TEST_DATA_PATH / "dual_level_nested_folder" / "test_top_folder.py" ) -test_nested_folder_one_path = os.fspath( + +test_nested_folder_one_path = ( TEST_DATA_PATH / "dual_level_nested_folder" / "nested_folder_one" ) -test_bottom_folder_path = os.fspath( + +test_bottom_folder_path = ( TEST_DATA_PATH / "dual_level_nested_folder" / "nested_folder_one" / "test_bottom_folder.py" ) + dual_level_nested_folder_expected_output = { "name": ".data", "path": TEST_DATA_PATH_STR, @@ -289,73 +320,85 @@ "children": [ { "name": "dual_level_nested_folder", - "path": dual_level_nested_folder_path, + "path": os.fspath(dual_level_nested_folder_path), "type_": "folder", - "id_": dual_level_nested_folder_path, + "id_": os.fspath(dual_level_nested_folder_path), "children": [ { "name": "test_top_folder.py", - "path": test_top_folder_path, + "path": os.fspath(test_top_folder_path), "type_": "file", - "id_": test_top_folder_path, + "id_": os.fspath(test_top_folder_path), "children": [ { "name": "test_top_function_t", - "path": test_top_folder_path, + "path": os.fspath(test_top_folder_path), "lineno": find_test_line_number( "test_top_function_t", test_top_folder_path, ), "type_": "test", "id_": "dual_level_nested_folder/test_top_folder.py::test_top_function_t", - "runID": "dual_level_nested_folder/test_top_folder.py::test_top_function_t", + "runID": get_absolute_test_id( + "dual_level_nested_folder/test_top_folder.py::test_top_function_t", + test_top_folder_path, + ), }, { "name": "test_top_function_f", - "path": test_top_folder_path, + "path": os.fspath(test_top_folder_path), "lineno": find_test_line_number( "test_top_function_f", test_top_folder_path, ), "type_": "test", "id_": "dual_level_nested_folder/test_top_folder.py::test_top_function_f", - "runID": "dual_level_nested_folder/test_top_folder.py::test_top_function_f", + "runID": get_absolute_test_id( + "dual_level_nested_folder/test_top_folder.py::test_top_function_f", + test_top_folder_path, + ), }, ], }, { "name": "nested_folder_one", - "path": test_nested_folder_one_path, + "path": os.fspath(test_nested_folder_one_path), "type_": "folder", - "id_": test_nested_folder_one_path, + "id_": os.fspath(test_nested_folder_one_path), "children": [ { "name": "test_bottom_folder.py", - "path": test_bottom_folder_path, + "path": os.fspath(test_bottom_folder_path), "type_": "file", - "id_": test_bottom_folder_path, + "id_": os.fspath(test_bottom_folder_path), "children": [ { "name": "test_bottom_function_t", - "path": test_bottom_folder_path, + "path": os.fspath(test_bottom_folder_path), "lineno": find_test_line_number( "test_bottom_function_t", test_bottom_folder_path, ), "type_": "test", "id_": "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", - "runID": "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + "runID": get_absolute_test_id( + "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + test_bottom_folder_path, + ), }, { "name": "test_bottom_function_f", - "path": test_bottom_folder_path, + "path": os.fspath(test_bottom_folder_path), "lineno": find_test_line_number( "test_bottom_function_f", test_bottom_folder_path, ), "type_": "test", "id_": "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", - "runID": "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + "runID": get_absolute_test_id( + "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + test_bottom_folder_path, + ), }, ], } @@ -374,12 +417,10 @@ # └── test_nest.py # └── test_function -folder_a_path = os.fspath(TEST_DATA_PATH / "folder_a") -folder_b_path = os.fspath(TEST_DATA_PATH / "folder_a" / "folder_b") -folder_a_nested_path = os.fspath(TEST_DATA_PATH / "folder_a" / "folder_b" / "folder_a") -test_nest_path = os.fspath( - TEST_DATA_PATH / "folder_a" / "folder_b" / "folder_a" / "test_nest.py" -) +folder_a_path = TEST_DATA_PATH / "folder_a" +folder_b_path = TEST_DATA_PATH / "folder_a" / "folder_b" +folder_a_nested_path = TEST_DATA_PATH / "folder_a" / "folder_b" / "folder_a" +test_nest_path = TEST_DATA_PATH / "folder_a" / "folder_b" / "folder_a" / "test_nest.py" double_nested_folder_expected_output = { "name": ".data", "path": TEST_DATA_PATH_STR, @@ -387,38 +428,41 @@ "children": [ { "name": "folder_a", - "path": folder_a_path, + "path": os.fspath(folder_a_path), "type_": "folder", - "id_": folder_a_path, + "id_": os.fspath(folder_a_path), "children": [ { "name": "folder_b", - "path": folder_b_path, + "path": os.fspath(folder_b_path), "type_": "folder", - "id_": folder_b_path, + "id_": os.fspath(folder_b_path), "children": [ { "name": "folder_a", - "path": folder_a_nested_path, + "path": os.fspath(folder_a_nested_path), "type_": "folder", - "id_": folder_a_nested_path, + "id_": os.fspath(folder_a_nested_path), "children": [ { "name": "test_nest.py", - "path": test_nest_path, + "path": os.fspath(test_nest_path), "type_": "file", - "id_": test_nest_path, + "id_": os.fspath(test_nest_path), "children": [ { "name": "test_function", - "path": test_nest_path, + "path": os.fspath(test_nest_path), "lineno": find_test_line_number( "test_function", test_nest_path, ), "type_": "test", "id_": "folder_a/folder_b/folder_a/test_nest.py::test_function", - "runID": "folder_a/folder_b/folder_a/test_nest.py::test_function", + "runID": get_absolute_test_id( + "folder_a/folder_b/folder_a/test_nest.py::test_function", + test_nest_path, + ), } ], } @@ -438,7 +482,7 @@ # └── [3+5-8] # └── [2+4-6] # └── [6+9-16] -parameterize_tests_path = os.fspath(TEST_DATA_PATH / "parametrize_tests.py") +parameterize_tests_path = TEST_DATA_PATH / "parametrize_tests.py" parametrize_tests_expected_output = { "name": ".data", "path": TEST_DATA_PATH_STR, @@ -446,77 +490,92 @@ "children": [ { "name": "parametrize_tests.py", - "path": parameterize_tests_path, + "path": os.fspath(parameterize_tests_path), "type_": "file", "id_": parameterize_tests_path, "children": [ { "name": "test_adding", - "path": parameterize_tests_path, + "path": os.fspath(parameterize_tests_path), "type_": "function", "id_": "parametrize_tests.py::test_adding", "children": [ { "name": "[3+5-8]", - "path": parameterize_tests_path, + "path": os.fspath(parameterize_tests_path), "lineno": find_test_line_number( "test_adding[3+5-8]", parameterize_tests_path, ), "type_": "test", "id_": "parametrize_tests.py::test_adding[3+5-8]", - "runID": "parametrize_tests.py::test_adding[3+5-8]", + "runID": get_absolute_test_id( + "parametrize_tests.py::test_adding[3+5-8]", + parameterize_tests_path, + ), }, { "name": "[2+4-6]", - "path": parameterize_tests_path, + "path": os.fspath(parameterize_tests_path), "lineno": find_test_line_number( "test_adding[2+4-6]", parameterize_tests_path, ), "type_": "test", "id_": "parametrize_tests.py::test_adding[2+4-6]", - "runID": "parametrize_tests.py::test_adding[2+4-6]", + "runID": get_absolute_test_id( + "parametrize_tests.py::test_adding[2+4-6]", + parameterize_tests_path, + ), }, { "name": "[6+9-16]", - "path": parameterize_tests_path, + "path": os.fspath(parameterize_tests_path), "lineno": find_test_line_number( "test_adding[6+9-16]", parameterize_tests_path, ), "type_": "test", "id_": "parametrize_tests.py::test_adding[6+9-16]", - "runID": "parametrize_tests.py::test_adding[6+9-16]", + "runID": get_absolute_test_id( + "parametrize_tests.py::test_adding[6+9-16]", + parameterize_tests_path, + ), }, ], }, { "name": "test_under_ten", - "path": parameterize_tests_path, + "path": os.fspath(parameterize_tests_path), "type_": "function", "children": [ { "name": "[1]", - "path": parameterize_tests_path, + "path": os.fspath(parameterize_tests_path), "lineno": find_test_line_number( "test_under_ten[1]", parameterize_tests_path, ), "type_": "test", "id_": "parametrize_tests.py::test_under_ten[1]", - "runID": "parametrize_tests.py::test_under_ten[1]", + "runID": get_absolute_test_id( + "parametrize_tests.py::test_under_ten[1]", + parameterize_tests_path, + ), }, { "name": "[2]", - "path": parameterize_tests_path, + "path": os.fspath(parameterize_tests_path), "lineno": find_test_line_number( "test_under_ten[2]", parameterize_tests_path, ), "type_": "test", "id_": "parametrize_tests.py::test_under_ten[2]", - "runID": "parametrize_tests.py::test_under_ten[2]", + "runID": get_absolute_test_id( + "parametrize_tests.py::test_under_ten[2]", + parameterize_tests_path, + ), }, ], "id_": "parametrize_tests.py::test_under_ten", @@ -529,7 +588,7 @@ # This is the expected output for the text_docstring.txt tests. # └── text_docstring.txt -text_docstring_path = os.fspath(TEST_DATA_PATH / "text_docstring.txt") +text_docstring_path = TEST_DATA_PATH / "text_docstring.txt" doctest_pytest_expected_output = { "name": ".data", "path": TEST_DATA_PATH_STR, @@ -537,20 +596,22 @@ "children": [ { "name": "text_docstring.txt", - "path": text_docstring_path, + "path": os.fspath(text_docstring_path), "type_": "file", - "id_": text_docstring_path, + "id_": os.fspath(text_docstring_path), "children": [ { "name": "text_docstring.txt", - "path": text_docstring_path, + "path": os.fspath(text_docstring_path), "lineno": find_test_line_number( "text_docstring.txt", - text_docstring_path, + os.fspath(text_docstring_path), ), "type_": "test", "id_": "text_docstring.txt::text_docstring.txt", - "runID": "text_docstring.txt::text_docstring.txt", + "runID": get_absolute_test_id( + "text_docstring.txt::text_docstring.txt", text_docstring_path + ), } ], } @@ -570,8 +631,8 @@ # └── [1] # └── [2] # └── [3] -param1_path = os.fspath(TEST_DATA_PATH / "param_same_name" / "test_param1.py") -param2_path = os.fspath(TEST_DATA_PATH / "param_same_name" / "test_param2.py") +param1_path = TEST_DATA_PATH / "param_same_name" / "test_param1.py" +param2_path = TEST_DATA_PATH / "param_same_name" / "test_param2.py" param_same_name_expected_output = { "name": ".data", "path": TEST_DATA_PATH_STR, @@ -585,18 +646,18 @@ "children": [ { "name": "test_param1.py", - "path": param1_path, + "path": os.fspath(param1_path), "type_": "file", - "id_": param1_path, + "id_": os.fspath(param1_path), "children": [ { "name": "test_odd_even", - "path": param1_path, + "path": os.fspath(param1_path), "type_": "function", "children": [ { "name": "[a]", - "path": param1_path, + "path": os.fspath(param1_path), "lineno": "6", "type_": "test", "id_": "param_same_name/test_param1.py::test_odd_even[a]", @@ -604,7 +665,7 @@ }, { "name": "[b]", - "path": param1_path, + "path": os.fspath(param1_path), "lineno": "6", "type_": "test", "id_": "param_same_name/test_param1.py::test_odd_even[b]", @@ -612,7 +673,7 @@ }, { "name": "[c]", - "path": param1_path, + "path": os.fspath(param1_path), "lineno": "6", "type_": "test", "id_": "param_same_name/test_param1.py::test_odd_even[c]", @@ -625,18 +686,18 @@ }, { "name": "test_param2.py", - "path": param2_path, + "path": os.fspath(param2_path), "type_": "file", - "id_": param2_path, + "id_": os.fspath(param2_path), "children": [ { "name": "test_odd_even", - "path": param2_path, + "path": os.fspath(param2_path), "type_": "function", "children": [ { "name": "[1]", - "path": param2_path, + "path": os.fspath(param2_path), "lineno": "6", "type_": "test", "id_": "param_same_name/test_param2.py::test_odd_even[1]", @@ -644,7 +705,7 @@ }, { "name": "[2]", - "path": param2_path, + "path": os.fspath(param2_path), "lineno": "6", "type_": "test", "id_": "param_same_name/test_param2.py::test_odd_even[2]", @@ -652,7 +713,7 @@ }, { "name": "[3]", - "path": param2_path, + "path": os.fspath(param2_path), "lineno": "6", "type_": "test", "id_": "param_same_name/test_param2.py::test_odd_even[3]", diff --git a/pythonFiles/tests/pytestadapter/test_discovery.py b/pythonFiles/tests/pytestadapter/test_discovery.py index 8d785be27c8b..8dce27302530 100644 --- a/pythonFiles/tests/pytestadapter/test_discovery.py +++ b/pythonFiles/tests/pytestadapter/test_discovery.py @@ -10,78 +10,78 @@ from .helpers import TEST_DATA_PATH, runner, runner_with_cwd -def test_import_error(tmp_path): - """Test pytest discovery on a file that has a pytest marker but does not import pytest. - - Copies the contents of a .txt file to a .py file in the temporary directory - to then run pytest discovery on. - - The json should still be returned but the errors list should be present. - - Keyword arguments: - tmp_path -- pytest fixture that creates a temporary directory. - """ - # Saving some files as .txt to avoid that file displaying a syntax error for - # the extension as a whole. Instead, rename it before running this test - # in order to test the error handling. - file_path = TEST_DATA_PATH / "error_pytest_import.txt" - temp_dir = tmp_path / "temp_data" - temp_dir.mkdir() - p = temp_dir / "error_pytest_import.py" - shutil.copyfile(file_path, p) - actual_list: Optional[List[Dict[str, Any]]] = runner( - ["--collect-only", os.fspath(p)] - ) - assert actual_list - for actual in actual_list: - assert all(item in actual for item in ("status", "cwd", "error")) - assert actual["status"] == "error" - assert actual["cwd"] == os.fspath(TEST_DATA_PATH) - assert len(actual["error"]) == 2 - - -def test_syntax_error(tmp_path): - """Test pytest discovery on a file that has a syntax error. - - Copies the contents of a .txt file to a .py file in the temporary directory - to then run pytest discovery on. - - The json should still be returned but the errors list should be present. - - Keyword arguments: - tmp_path -- pytest fixture that creates a temporary directory. - """ - # Saving some files as .txt to avoid that file displaying a syntax error for - # the extension as a whole. Instead, rename it before running this test - # in order to test the error handling. - file_path = TEST_DATA_PATH / "error_syntax_discovery.txt" - temp_dir = tmp_path / "temp_data" - temp_dir.mkdir() - p = temp_dir / "error_syntax_discovery.py" - shutil.copyfile(file_path, p) - actual = runner(["--collect-only", os.fspath(p)]) - if actual: - actual = actual[0] - assert actual - assert all(item in actual for item in ("status", "cwd", "error")) - assert actual["status"] == "error" - assert actual["cwd"] == os.fspath(TEST_DATA_PATH) - assert len(actual["error"]) == 2 - - -def test_parameterized_error_collect(): - """Tests pytest discovery on specific file that incorrectly uses parametrize. - - The json should still be returned but the errors list should be present. - """ - file_path_str = "error_parametrize_discovery.py" - actual = runner(["--collect-only", file_path_str]) - if actual: - actual = actual[0] - assert all(item in actual for item in ("status", "cwd", "error")) - assert actual["status"] == "error" - assert actual["cwd"] == os.fspath(TEST_DATA_PATH) - assert len(actual["error"]) == 2 +# def test_import_error(tmp_path): +# """Test pytest discovery on a file that has a pytest marker but does not import pytest. + +# Copies the contents of a .txt file to a .py file in the temporary directory +# to then run pytest discovery on. + +# The json should still be returned but the errors list should be present. + +# Keyword arguments: +# tmp_path -- pytest fixture that creates a temporary directory. +# """ +# # Saving some files as .txt to avoid that file displaying a syntax error for +# # the extension as a whole. Instead, rename it before running this test +# # in order to test the error handling. +# file_path = TEST_DATA_PATH / "error_pytest_import.txt" +# temp_dir = tmp_path / "temp_data" +# temp_dir.mkdir() +# p = temp_dir / "error_pytest_import.py" +# shutil.copyfile(file_path, p) +# actual_list: Optional[List[Dict[str, Any]]] = runner( +# ["--collect-only", os.fspath(p)] +# ) +# assert actual_list +# for actual in actual_list: +# assert all(item in actual for item in ("status", "cwd", "error")) +# assert actual["status"] == "error" +# assert actual["cwd"] == os.fspath(TEST_DATA_PATH) +# assert len(actual["error"]) == 2 + + +# def test_syntax_error(tmp_path): +# """Test pytest discovery on a file that has a syntax error. + +# Copies the contents of a .txt file to a .py file in the temporary directory +# to then run pytest discovery on. + +# The json should still be returned but the errors list should be present. + +# Keyword arguments: +# tmp_path -- pytest fixture that creates a temporary directory. +# """ +# # Saving some files as .txt to avoid that file displaying a syntax error for +# # the extension as a whole. Instead, rename it before running this test +# # in order to test the error handling. +# file_path = TEST_DATA_PATH / "error_syntax_discovery.txt" +# temp_dir = tmp_path / "temp_data" +# temp_dir.mkdir() +# p = temp_dir / "error_syntax_discovery.py" +# shutil.copyfile(file_path, p) +# actual = runner(["--collect-only", os.fspath(p)]) +# if actual: +# actual = actual[0] +# assert actual +# assert all(item in actual for item in ("status", "cwd", "error")) +# assert actual["status"] == "error" +# assert actual["cwd"] == os.fspath(TEST_DATA_PATH) +# assert len(actual["error"]) == 2 + + +# def test_parameterized_error_collect(): +# """Tests pytest discovery on specific file that incorrectly uses parametrize. + +# The json should still be returned but the errors list should be present. +# """ +# file_path_str = "error_parametrize_discovery.py" +# actual = runner(["--collect-only", file_path_str]) +# if actual: +# actual = actual[0] +# assert all(item in actual for item in ("status", "cwd", "error")) +# assert actual["status"] == "error" +# assert actual["cwd"] == os.fspath(TEST_DATA_PATH) +# assert len(actual["error"]) == 2 @pytest.mark.parametrize( diff --git a/pythonFiles/tests/pytestadapter/test_execution.py b/pythonFiles/tests/pytestadapter/test_execution.py index 62d94a227a1b..2be4886c24c1 100644 --- a/pythonFiles/tests/pytestadapter/test_execution.py +++ b/pythonFiles/tests/pytestadapter/test_execution.py @@ -12,7 +12,11 @@ def test_config_file(): """Test pytest execution when a config file is specified.""" - args = ["-c", "tests/pytest.ini", "tests/test_a.py::test_a_function"] + args = [ + "-c", + "tests/pytest.ini", + str(TEST_DATA_PATH / "root" / "tests" / "test_a.py::test_a_function"), + ] new_cwd = TEST_DATA_PATH / "root" actual = runner_with_cwd(args, new_cwd) expected_const = ( diff --git a/pythonFiles/vscode_pytest/__init__.py b/pythonFiles/vscode_pytest/__init__.py index b935c97310e5..87aadcd461a9 100644 --- a/pythonFiles/vscode_pytest/__init__.py +++ b/pythonFiles/vscode_pytest/__init__.py @@ -87,7 +87,7 @@ def pytest_exception_interact(node, call, report): report_value = "error" if call.excinfo.typename == "AssertionError": report_value = "failure" - node_id = get_workspace_node_id(str(node.nodeid)) + node_id = get_absolute_test_id(node.nodeid, get_node_path(node)) if node_id not in collected_tests_so_far: collected_tests_so_far.append(node_id) item_result = create_test_outcome( @@ -106,12 +106,11 @@ def pytest_exception_interact(node, call, report): ) -def get_workspace_node_id(testid: str): - id = testid - global RELATIVE_INVOCATION_PATH - if RELATIVE_INVOCATION_PATH: - id = str(pathlib.Path(RELATIVE_INVOCATION_PATH) / testid) - return id +def get_absolute_test_id(test_id: str, testPath: pathlib.Path) -> str: + split_id = test_id.split("::")[1:] + absolute_test_id = "::".join([str(testPath), *split_id]) + print("absolute path", absolute_test_id) + return absolute_test_id def pytest_keyboard_interrupt(excinfo): @@ -163,20 +162,26 @@ class testRunResultDict(Dict[str, Dict[str, TestOutcome]]): IS_DISCOVERY = False RELATIVE_INVOCATION_PATH = "" +SESSION_CONFIG_ROOT = "" +INVO_DIR = "" +map_id_to_path = dict() def pytest_load_initial_conftests(early_config, parser, args): if "--collect-only" in args: global IS_DISCOVERY IS_DISCOVERY = True - invocation_dir = early_config.invocation_params.dir - root = early_config.rootpath - if invocation_dir != root: - try: - global RELATIVE_INVOCATION_PATH - RELATIVE_INVOCATION_PATH = root.relative_to(invocation_dir) - except: - pass + # global INVO_DIR + # INVO_DIR = early_config.invocation_params.dir + # root = early_config.rootpath + # global SESSION_CONFIG_ROOT + # SESSION_CONFIG_ROOT = root + # # if invocation_dir != root: + # # try: + # # global RELATIVE_INVOCATION_PATH + # # RELATIVE_INVOCATION_PATH = root.relative_to(invocation_dir) + # # except: + # # pass collected_tests_so_far = list() @@ -201,17 +206,20 @@ def pytest_report_teststatus(report, config): elif report.failed: report_value = "failure" message = report.longreprtext - node_id = get_workspace_node_id(str(report.nodeid)) - if node_id not in collected_tests_so_far: - collected_tests_so_far.append(node_id) + node_path = map_id_to_path[report.nodeid] + if not node_path: + node_path = cwd + absolute_node_id = get_absolute_test_id(report.nodeid, node_path) + if absolute_node_id not in collected_tests_so_far: + collected_tests_so_far.append(absolute_node_id) item_result = create_test_outcome( - node_id, + absolute_node_id, report_value, message, traceback, ) collected_test = testRunResultDict() - collected_test[node_id] = item_result + collected_test[absolute_node_id] = item_result execution_post( os.fsdecode(cwd), "success", @@ -228,21 +236,22 @@ def pytest_report_teststatus(report, config): def pytest_runtest_protocol(item, nextitem): + map_id_to_path[item.nodeid] = get_node_path(item) skipped = check_skipped_wrapper(item) if skipped: - node_id = get_workspace_node_id(str(item.nodeid)) + absolute_node_id = get_absolute_test_id(item.nodeid, get_node_path(item)) report_value = "skipped" cwd = pathlib.Path.cwd() - if node_id not in collected_tests_so_far: - collected_tests_so_far.append(node_id) + if absolute_node_id not in collected_tests_so_far: + collected_tests_so_far.append(absolute_node_id) item_result = create_test_outcome( - node_id, + absolute_node_id, report_value, None, None, ) collected_test = testRunResultDict() - collected_test[node_id] = item_result + collected_test[absolute_node_id] = item_result execution_post( os.fsdecode(cwd), "success", @@ -488,14 +497,14 @@ def create_test_node( test_case_loc: str = ( str(test_case.location[1] + 1) if (test_case.location[1] is not None) else "" ) - id = get_workspace_node_id(test_case.nodeid) + absolute_test_id = get_absolute_test_id(test_case.nodeid, get_node_path(test_case)) return { "name": test_case.name, "path": get_node_path(test_case), "lineno": test_case_loc, "type_": "test", - "id_": id, - "runID": id, + "id_": test_case.nodeid, + "runID": absolute_test_id, } From 1b643dc7f98570061a47167ba61354e231107ba3 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 27 Jul 2023 09:11:56 -0700 Subject: [PATCH 14/16] fix discovery tests --- .../expected_discovery_test_output.py | 195 +++++++++++++----- pythonFiles/tests/pytestadapter/helpers.py | 7 + .../tests/pytestadapter/test_discovery.py | 160 +++++++------- pythonFiles/vscode_pytest/__init__.py | 2 +- 4 files changed, 237 insertions(+), 127 deletions(-) diff --git a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py index acd268571c14..2b2c07ab8ea7 100644 --- a/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_discovery_test_output.py @@ -1,8 +1,7 @@ import os -from pythonFiles.vscode_pytest import get_absolute_test_id -from .helpers import TEST_DATA_PATH, find_test_line_number +from .helpers import TEST_DATA_PATH, find_test_line_number, get_absolute_test_id # This file contains the expected output dictionaries for tests discovery and is used in test_discovery.py. @@ -40,7 +39,9 @@ simple_test_file_path, ), "type_": "test", - "id_": "simple_pytest.py::test_function", + "id_": get_absolute_test_id( + "simple_pytest.py::test_function", simple_test_file_path + ), "runID": get_absolute_test_id( "simple_pytest.py::test_function", simple_test_file_path ), @@ -81,7 +82,10 @@ os.fspath(unit_pytest_same_file_path), ), "type_": "test", - "id_": "unittest_pytest_same_file.py::TestExample::test_true_unittest", + "id_": get_absolute_test_id( + "unittest_pytest_same_file.py::TestExample::test_true_unittest", + unit_pytest_same_file_path, + ), "runID": get_absolute_test_id( "unittest_pytest_same_file.py::TestExample::test_true_unittest", unit_pytest_same_file_path, @@ -98,7 +102,10 @@ unit_pytest_same_file_path, ), "type_": "test", - "id_": "unittest_pytest_same_file.py::test_true_pytest", + "id_": get_absolute_test_id( + "unittest_pytest_same_file.py::test_true_pytest", + unit_pytest_same_file_path, + ), "runID": get_absolute_test_id( "unittest_pytest_same_file.py::test_true_pytest", unit_pytest_same_file_path, @@ -167,7 +174,10 @@ os.fspath(test_add_path), ), "type_": "test", - "id_": "unittest_folder/test_add.py::TestAddFunction::test_add_negative_numbers", + "id_": get_absolute_test_id( + "unittest_folder/test_add.py::TestAddFunction::test_add_negative_numbers", + test_add_path, + ), "runID": get_absolute_test_id( "unittest_folder/test_add.py::TestAddFunction::test_add_negative_numbers", test_add_path, @@ -181,7 +191,10 @@ os.fspath(test_add_path), ), "type_": "test", - "id_": "unittest_folder/test_add.py::TestAddFunction::test_add_positive_numbers", + "id_": get_absolute_test_id( + "unittest_folder/test_add.py::TestAddFunction::test_add_positive_numbers", + test_add_path, + ), "runID": get_absolute_test_id( "unittest_folder/test_add.py::TestAddFunction::test_add_positive_numbers", test_add_path, @@ -203,7 +216,10 @@ os.fspath(test_add_path), ), "type_": "test", - "id_": "unittest_folder/test_add.py::TestDuplicateFunction::test_dup_a", + "id_": get_absolute_test_id( + "unittest_folder/test_add.py::TestDuplicateFunction::test_dup_a", + test_add_path, + ), "runID": get_absolute_test_id( "unittest_folder/test_add.py::TestDuplicateFunction::test_dup_a", test_add_path, @@ -233,7 +249,10 @@ os.fspath(test_subtract_path), ), "type_": "test", - "id_": "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_negative_numbers", + "id_": get_absolute_test_id( + "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_negative_numbers", + test_subtract_path, + ), "runID": get_absolute_test_id( "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_negative_numbers", test_subtract_path, @@ -247,7 +266,10 @@ os.fspath(test_subtract_path), ), "type_": "test", - "id_": "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_positive_numbers", + "id_": get_absolute_test_id( + "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_positive_numbers", + test_subtract_path, + ), "runID": get_absolute_test_id( "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_positive_numbers", test_subtract_path, @@ -269,7 +291,10 @@ os.fspath(test_subtract_path), ), "type_": "test", - "id_": "unittest_folder/test_subtract.py::TestDuplicateFunction::test_dup_s", + "id_": get_absolute_test_id( + "unittest_folder/test_subtract.py::TestDuplicateFunction::test_dup_s", + test_subtract_path, + ), "runID": get_absolute_test_id( "unittest_folder/test_subtract.py::TestDuplicateFunction::test_dup_s", test_subtract_path, @@ -338,7 +363,10 @@ test_top_folder_path, ), "type_": "test", - "id_": "dual_level_nested_folder/test_top_folder.py::test_top_function_t", + "id_": get_absolute_test_id( + "dual_level_nested_folder/test_top_folder.py::test_top_function_t", + test_top_folder_path, + ), "runID": get_absolute_test_id( "dual_level_nested_folder/test_top_folder.py::test_top_function_t", test_top_folder_path, @@ -352,7 +380,10 @@ test_top_folder_path, ), "type_": "test", - "id_": "dual_level_nested_folder/test_top_folder.py::test_top_function_f", + "id_": get_absolute_test_id( + "dual_level_nested_folder/test_top_folder.py::test_top_function_f", + test_top_folder_path, + ), "runID": get_absolute_test_id( "dual_level_nested_folder/test_top_folder.py::test_top_function_f", test_top_folder_path, @@ -380,7 +411,10 @@ test_bottom_folder_path, ), "type_": "test", - "id_": "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + "id_": get_absolute_test_id( + "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + test_bottom_folder_path, + ), "runID": get_absolute_test_id( "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", test_bottom_folder_path, @@ -394,7 +428,10 @@ test_bottom_folder_path, ), "type_": "test", - "id_": "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + "id_": get_absolute_test_id( + "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + test_bottom_folder_path, + ), "runID": get_absolute_test_id( "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", test_bottom_folder_path, @@ -458,7 +495,10 @@ test_nest_path, ), "type_": "test", - "id_": "folder_a/folder_b/folder_a/test_nest.py::test_function", + "id_": get_absolute_test_id( + "folder_a/folder_b/folder_a/test_nest.py::test_function", + test_nest_path, + ), "runID": get_absolute_test_id( "folder_a/folder_b/folder_a/test_nest.py::test_function", test_nest_path, @@ -492,7 +532,7 @@ "name": "parametrize_tests.py", "path": os.fspath(parameterize_tests_path), "type_": "file", - "id_": parameterize_tests_path, + "id_": os.fspath(parameterize_tests_path), "children": [ { "name": "test_adding", @@ -508,7 +548,10 @@ parameterize_tests_path, ), "type_": "test", - "id_": "parametrize_tests.py::test_adding[3+5-8]", + "id_": get_absolute_test_id( + "parametrize_tests.py::test_adding[3+5-8]", + parameterize_tests_path, + ), "runID": get_absolute_test_id( "parametrize_tests.py::test_adding[3+5-8]", parameterize_tests_path, @@ -522,7 +565,10 @@ parameterize_tests_path, ), "type_": "test", - "id_": "parametrize_tests.py::test_adding[2+4-6]", + "id_": get_absolute_test_id( + "parametrize_tests.py::test_adding[2+4-6]", + parameterize_tests_path, + ), "runID": get_absolute_test_id( "parametrize_tests.py::test_adding[2+4-6]", parameterize_tests_path, @@ -536,7 +582,10 @@ parameterize_tests_path, ), "type_": "test", - "id_": "parametrize_tests.py::test_adding[6+9-16]", + "id_": get_absolute_test_id( + "parametrize_tests.py::test_adding[6+9-16]", + parameterize_tests_path, + ), "runID": get_absolute_test_id( "parametrize_tests.py::test_adding[6+9-16]", parameterize_tests_path, @@ -557,7 +606,10 @@ parameterize_tests_path, ), "type_": "test", - "id_": "parametrize_tests.py::test_under_ten[1]", + "id_": get_absolute_test_id( + "parametrize_tests.py::test_under_ten[1]", + parameterize_tests_path, + ), "runID": get_absolute_test_id( "parametrize_tests.py::test_under_ten[1]", parameterize_tests_path, @@ -571,7 +623,10 @@ parameterize_tests_path, ), "type_": "test", - "id_": "parametrize_tests.py::test_under_ten[2]", + "id_": get_absolute_test_id( + "parametrize_tests.py::test_under_ten[2]", + parameterize_tests_path, + ), "runID": get_absolute_test_id( "parametrize_tests.py::test_under_ten[2]", parameterize_tests_path, @@ -608,7 +663,9 @@ os.fspath(text_docstring_path), ), "type_": "test", - "id_": "text_docstring.txt::text_docstring.txt", + "id_": get_absolute_test_id( + "text_docstring.txt::text_docstring.txt", text_docstring_path + ), "runID": get_absolute_test_id( "text_docstring.txt::text_docstring.txt", text_docstring_path ), @@ -660,24 +717,42 @@ "path": os.fspath(param1_path), "lineno": "6", "type_": "test", - "id_": "param_same_name/test_param1.py::test_odd_even[a]", - "runID": "param_same_name/test_param1.py::test_odd_even[a]", + "id_": get_absolute_test_id( + "param_same_name/test_param1.py::test_odd_even[a]", + param1_path, + ), + "runID": get_absolute_test_id( + "param_same_name/test_param1.py::test_odd_even[a]", + param1_path, + ), }, { "name": "[b]", "path": os.fspath(param1_path), "lineno": "6", "type_": "test", - "id_": "param_same_name/test_param1.py::test_odd_even[b]", - "runID": "param_same_name/test_param1.py::test_odd_even[b]", + "id_": get_absolute_test_id( + "param_same_name/test_param1.py::test_odd_even[b]", + param1_path, + ), + "runID": get_absolute_test_id( + "param_same_name/test_param1.py::test_odd_even[b]", + param1_path, + ), }, { "name": "[c]", "path": os.fspath(param1_path), "lineno": "6", "type_": "test", - "id_": "param_same_name/test_param1.py::test_odd_even[c]", - "runID": "param_same_name/test_param1.py::test_odd_even[c]", + "id_": get_absolute_test_id( + "param_same_name/test_param1.py::test_odd_even[c]", + param1_path, + ), + "runID": get_absolute_test_id( + "param_same_name/test_param1.py::test_odd_even[c]", + param1_path, + ), }, ], "id_": "param_same_name/test_param1.py::test_odd_even", @@ -700,24 +775,42 @@ "path": os.fspath(param2_path), "lineno": "6", "type_": "test", - "id_": "param_same_name/test_param2.py::test_odd_even[1]", - "runID": "param_same_name/test_param2.py::test_odd_even[1]", + "id_": get_absolute_test_id( + "param_same_name/test_param2.py::test_odd_even[1]", + param2_path, + ), + "runID": get_absolute_test_id( + "param_same_name/test_param2.py::test_odd_even[1]", + param2_path, + ), }, { "name": "[2]", "path": os.fspath(param2_path), "lineno": "6", "type_": "test", - "id_": "param_same_name/test_param2.py::test_odd_even[2]", - "runID": "param_same_name/test_param2.py::test_odd_even[2]", + "id_": get_absolute_test_id( + "param_same_name/test_param2.py::test_odd_even[2]", + param2_path, + ), + "runID": get_absolute_test_id( + "param_same_name/test_param2.py::test_odd_even[2]", + param2_path, + ), }, { "name": "[3]", "path": os.fspath(param2_path), "lineno": "6", "type_": "test", - "id_": "param_same_name/test_param2.py::test_odd_even[3]", - "runID": "param_same_name/test_param2.py::test_odd_even[3]", + "id_": get_absolute_test_id( + "param_same_name/test_param2.py::test_odd_even[3]", + param2_path, + ), + "runID": get_absolute_test_id( + "param_same_name/test_param2.py::test_odd_even[3]", + param2_path, + ), }, ], "id_": "param_same_name/test_param2.py::test_odd_even", @@ -730,7 +823,9 @@ "id_": TEST_DATA_PATH_STR, } -tests_path = os.fspath(TEST_DATA_PATH / "root" / "tests") +tests_path = TEST_DATA_PATH / "root" / "tests" +tests_a_path = TEST_DATA_PATH / "root" / "tests" / "test_a.py" +tests_b_path = TEST_DATA_PATH / "root" / "tests" / "test_b.py" # This is the expected output for the root folder tests. # └── tests # └── test_a.py @@ -739,14 +834,14 @@ # └── test_b_function root_with_config_expected_output = { "name": "tests", - "path": tests_path, + "path": os.fspath(tests_path), "type_": "folder", "children": [ { "name": "test_a.py", - "path": os.fspath(os.path.join(tests_path, "test_a.py")), + "path": os.fspath(tests_a_path), "type_": "file", - "id_": os.fspath(os.path.join(tests_path, "test_a.py")), + "id_": os.fspath(tests_a_path), "children": [ { "name": "test_a_function", @@ -756,16 +851,20 @@ os.path.join(tests_path, "test_a.py"), ), "type_": "test", - "id_": "tests/test_a.py::test_a_function", - "runID": "tests/test_a.py::test_a_function", + "id_": get_absolute_test_id( + "tests/test_a.py::test_a_function", tests_a_path + ), + "runID": get_absolute_test_id( + "tests/test_a.py::test_a_function", tests_a_path + ), } ], }, { "name": "test_b.py", - "path": os.fspath(os.path.join(tests_path, "test_b.py")), + "path": os.fspath(tests_b_path), "type_": "file", - "id_": os.fspath(os.path.join(tests_path, "test_b.py")), + "id_": os.fspath(tests_b_path), "children": [ { "name": "test_b_function", @@ -775,11 +874,15 @@ os.path.join(tests_path, "test_b.py"), ), "type_": "test", - "id_": "tests/test_b.py::test_b_function", - "runID": "tests/test_b.py::test_b_function", + "id_": get_absolute_test_id( + "tests/test_b.py::test_b_function", tests_b_path + ), + "runID": get_absolute_test_id( + "tests/test_b.py::test_b_function", tests_b_path + ), } ], }, ], - "id_": tests_path, + "id_": os.fspath(tests_path), } diff --git a/pythonFiles/tests/pytestadapter/helpers.py b/pythonFiles/tests/pytestadapter/helpers.py index 5e241bdb77a4..28feb6282b92 100644 --- a/pythonFiles/tests/pytestadapter/helpers.py +++ b/pythonFiles/tests/pytestadapter/helpers.py @@ -16,6 +16,13 @@ from typing_extensions import TypedDict +def get_absolute_test_id(test_id: str, testPath: pathlib.Path) -> str: + split_id = test_id.split("::")[1:] + absolute_test_id = "::".join([str(testPath), *split_id]) + print("absolute path", absolute_test_id) + return absolute_test_id + + def create_server( host: str = "127.0.0.1", port: int = 0, diff --git a/pythonFiles/tests/pytestadapter/test_discovery.py b/pythonFiles/tests/pytestadapter/test_discovery.py index 8dce27302530..970923e9771b 100644 --- a/pythonFiles/tests/pytestadapter/test_discovery.py +++ b/pythonFiles/tests/pytestadapter/test_discovery.py @@ -10,91 +10,91 @@ from .helpers import TEST_DATA_PATH, runner, runner_with_cwd -# def test_import_error(tmp_path): -# """Test pytest discovery on a file that has a pytest marker but does not import pytest. - -# Copies the contents of a .txt file to a .py file in the temporary directory -# to then run pytest discovery on. - -# The json should still be returned but the errors list should be present. - -# Keyword arguments: -# tmp_path -- pytest fixture that creates a temporary directory. -# """ -# # Saving some files as .txt to avoid that file displaying a syntax error for -# # the extension as a whole. Instead, rename it before running this test -# # in order to test the error handling. -# file_path = TEST_DATA_PATH / "error_pytest_import.txt" -# temp_dir = tmp_path / "temp_data" -# temp_dir.mkdir() -# p = temp_dir / "error_pytest_import.py" -# shutil.copyfile(file_path, p) -# actual_list: Optional[List[Dict[str, Any]]] = runner( -# ["--collect-only", os.fspath(p)] -# ) -# assert actual_list -# for actual in actual_list: -# assert all(item in actual for item in ("status", "cwd", "error")) -# assert actual["status"] == "error" -# assert actual["cwd"] == os.fspath(TEST_DATA_PATH) -# assert len(actual["error"]) == 2 - - -# def test_syntax_error(tmp_path): -# """Test pytest discovery on a file that has a syntax error. - -# Copies the contents of a .txt file to a .py file in the temporary directory -# to then run pytest discovery on. - -# The json should still be returned but the errors list should be present. - -# Keyword arguments: -# tmp_path -- pytest fixture that creates a temporary directory. -# """ -# # Saving some files as .txt to avoid that file displaying a syntax error for -# # the extension as a whole. Instead, rename it before running this test -# # in order to test the error handling. -# file_path = TEST_DATA_PATH / "error_syntax_discovery.txt" -# temp_dir = tmp_path / "temp_data" -# temp_dir.mkdir() -# p = temp_dir / "error_syntax_discovery.py" -# shutil.copyfile(file_path, p) -# actual = runner(["--collect-only", os.fspath(p)]) -# if actual: -# actual = actual[0] -# assert actual -# assert all(item in actual for item in ("status", "cwd", "error")) -# assert actual["status"] == "error" -# assert actual["cwd"] == os.fspath(TEST_DATA_PATH) -# assert len(actual["error"]) == 2 - - -# def test_parameterized_error_collect(): -# """Tests pytest discovery on specific file that incorrectly uses parametrize. - -# The json should still be returned but the errors list should be present. -# """ -# file_path_str = "error_parametrize_discovery.py" -# actual = runner(["--collect-only", file_path_str]) -# if actual: -# actual = actual[0] -# assert all(item in actual for item in ("status", "cwd", "error")) -# assert actual["status"] == "error" -# assert actual["cwd"] == os.fspath(TEST_DATA_PATH) -# assert len(actual["error"]) == 2 +def test_import_error(tmp_path): + """Test pytest discovery on a file that has a pytest marker but does not import pytest. + + Copies the contents of a .txt file to a .py file in the temporary directory + to then run pytest discovery on. + + The json should still be returned but the errors list should be present. + + Keyword arguments: + tmp_path -- pytest fixture that creates a temporary directory. + """ + # Saving some files as .txt to avoid that file displaying a syntax error for + # the extension as a whole. Instead, rename it before running this test + # in order to test the error handling. + file_path = TEST_DATA_PATH / "error_pytest_import.txt" + temp_dir = tmp_path / "temp_data" + temp_dir.mkdir() + p = temp_dir / "error_pytest_import.py" + shutil.copyfile(file_path, p) + actual_list: Optional[List[Dict[str, Any]]] = runner( + ["--collect-only", os.fspath(p)] + ) + assert actual_list + for actual in actual_list: + assert all(item in actual for item in ("status", "cwd", "error")) + assert actual["status"] == "error" + assert actual["cwd"] == os.fspath(TEST_DATA_PATH) + assert len(actual["error"]) == 2 + + +def test_syntax_error(tmp_path): + """Test pytest discovery on a file that has a syntax error. + + Copies the contents of a .txt file to a .py file in the temporary directory + to then run pytest discovery on. + + The json should still be returned but the errors list should be present. + + Keyword arguments: + tmp_path -- pytest fixture that creates a temporary directory. + """ + # Saving some files as .txt to avoid that file displaying a syntax error for + # the extension as a whole. Instead, rename it before running this test + # in order to test the error handling. + file_path = TEST_DATA_PATH / "error_syntax_discovery.txt" + temp_dir = tmp_path / "temp_data" + temp_dir.mkdir() + p = temp_dir / "error_syntax_discovery.py" + shutil.copyfile(file_path, p) + actual = runner(["--collect-only", os.fspath(p)]) + if actual: + actual = actual[0] + assert actual + assert all(item in actual for item in ("status", "cwd", "error")) + assert actual["status"] == "error" + assert actual["cwd"] == os.fspath(TEST_DATA_PATH) + assert len(actual["error"]) == 2 + + +def test_parameterized_error_collect(): + """Tests pytest discovery on specific file that incorrectly uses parametrize. + + The json should still be returned but the errors list should be present. + """ + file_path_str = "error_parametrize_discovery.py" + actual = runner(["--collect-only", file_path_str]) + if actual: + actual = actual[0] + assert all(item in actual for item in ("status", "cwd", "error")) + assert actual["status"] == "error" + assert actual["cwd"] == os.fspath(TEST_DATA_PATH) + assert len(actual["error"]) == 2 @pytest.mark.parametrize( "file, expected_const", [ - ( - "unittest_skiptest_file_level.py", - expected_discovery_test_output.unittest_skip_file_level_expected_output, - ), - ( - "param_same_name", - expected_discovery_test_output.param_same_name_expected_output, - ), + # ( + # "unittest_skiptest_file_level.py", + # expected_discovery_test_output.unittest_skip_file_level_expected_output, + # ), + # ( + # "param_same_name", + # expected_discovery_test_output.param_same_name_expected_output, + # ), ( "parametrize_tests.py", expected_discovery_test_output.parametrize_tests_expected_output, diff --git a/pythonFiles/vscode_pytest/__init__.py b/pythonFiles/vscode_pytest/__init__.py index 87aadcd461a9..22491aa5674d 100644 --- a/pythonFiles/vscode_pytest/__init__.py +++ b/pythonFiles/vscode_pytest/__init__.py @@ -503,7 +503,7 @@ def create_test_node( "path": get_node_path(test_case), "lineno": test_case_loc, "type_": "test", - "id_": test_case.nodeid, + "id_": absolute_test_id, "runID": absolute_test_id, } From d82419bb8b170781c5745ff89cbbc9b40b28663c Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 27 Jul 2023 14:13:36 -0700 Subject: [PATCH 15/16] tests working --- .../expected_execution_test_output.py | 353 ++++++++++++++---- 1 file changed, 271 insertions(+), 82 deletions(-) diff --git a/pythonFiles/tests/pytestadapter/expected_execution_test_output.py b/pythonFiles/tests/pytestadapter/expected_execution_test_output.py index 88661d9fa049..0a7e737dfc0e 100644 --- a/pythonFiles/tests/pytestadapter/expected_execution_test_output.py +++ b/pythonFiles/tests/pytestadapter/expected_execution_test_output.py @@ -6,6 +6,7 @@ SUCCESS = "success" FAILURE = "failure" TEST_SUBTRACT_FUNCTION_NEGATIVE_NUMBERS_ERROR = "self = \n\n def test_subtract_negative_numbers( # test_marker--test_subtract_negative_numbers\n self,\n ):\n result = subtract(-2, -3)\n> self.assertEqual(result, 100000)\nE AssertionError: 1 != 100000\n\nunittest_folder/test_subtract.py:25: AssertionError" +from .helpers import TEST_DATA_PATH, get_absolute_test_id # This is the expected output for the unittest_folder execute tests # └── unittest_folder @@ -17,30 +18,52 @@ # └── TestSubtractFunction # ├── test_subtract_negative_numbers: failure # └── test_subtract_positive_numbers: success +test_add_path = TEST_DATA_PATH / "unittest_folder" / "test_add.py" +test_subtract_path = TEST_DATA_PATH / "unittest_folder" / "test_subtract.py" uf_execution_expected_output = { - f"{TEST_ADD_FUNCTION}test_add_negative_numbers": { - "test": f"{TEST_ADD_FUNCTION}test_add_negative_numbers", + get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_negative_numbers", test_add_path + ): { + "test": get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_negative_numbers", test_add_path + ), "outcome": SUCCESS, "message": None, "traceback": None, "subtest": None, }, - f"{TEST_ADD_FUNCTION}test_add_positive_numbers": { - "test": f"{TEST_ADD_FUNCTION}test_add_positive_numbers", + get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path + ): { + "test": get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path + ), "outcome": SUCCESS, "message": None, "traceback": None, "subtest": None, }, - f"{TEST_SUBTRACT_FUNCTION}test_subtract_negative_numbers": { - "test": f"{TEST_SUBTRACT_FUNCTION}test_subtract_negative_numbers", + get_absolute_test_id( + f"{TEST_SUBTRACT_FUNCTION}test_subtract_negative_numbers", + test_subtract_path, + ): { + "test": get_absolute_test_id( + f"{TEST_SUBTRACT_FUNCTION}test_subtract_negative_numbers", + test_subtract_path, + ), "outcome": FAILURE, "message": "ERROR MESSAGE", "traceback": None, "subtest": None, }, - f"{TEST_SUBTRACT_FUNCTION}test_subtract_positive_numbers": { - "test": f"{TEST_SUBTRACT_FUNCTION}test_subtract_positive_numbers", + get_absolute_test_id( + f"{TEST_SUBTRACT_FUNCTION}test_subtract_positive_numbers", + test_subtract_path, + ): { + "test": get_absolute_test_id( + f"{TEST_SUBTRACT_FUNCTION}test_subtract_positive_numbers", + test_subtract_path, + ), "outcome": SUCCESS, "message": None, "traceback": None, @@ -55,16 +78,26 @@ # │ └── TestAddFunction # │ ├── test_add_negative_numbers: success # │ └── test_add_positive_numbers: success +test_add_path = TEST_DATA_PATH / "unittest_folder" / "test_add.py" + uf_single_file_expected_output = { - f"{TEST_ADD_FUNCTION}test_add_negative_numbers": { - "test": f"{TEST_ADD_FUNCTION}test_add_negative_numbers", + get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_negative_numbers", test_add_path + ): { + "test": get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_negative_numbers", test_add_path + ), "outcome": SUCCESS, "message": None, "traceback": None, "subtest": None, }, - f"{TEST_ADD_FUNCTION}test_add_positive_numbers": { - "test": f"{TEST_ADD_FUNCTION}test_add_positive_numbers", + get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path + ): { + "test": get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path + ), "outcome": SUCCESS, "message": None, "traceback": None, @@ -72,19 +105,24 @@ }, } + # This is the expected output for the unittest_folder execute only signle method # └── unittest_folder # ├── test_add.py # │ └── TestAddFunction # │ └── test_add_positive_numbers: success uf_single_method_execution_expected_output = { - f"{TEST_ADD_FUNCTION}test_add_positive_numbers": { - "test": f"{TEST_ADD_FUNCTION}test_add_positive_numbers", + get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path + ): { + "test": get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path + ), "outcome": SUCCESS, "message": None, "traceback": None, "subtest": None, - } + }, } # This is the expected output for the unittest_folder tests run where two tests @@ -96,18 +134,28 @@ # └── test_subtract.py # └── TestSubtractFunction # └── test_subtract_positive_numbers: success +test_subtract_path = TEST_DATA_PATH / "unittest_folder" / "test_subtract.py" +test_add_path = TEST_DATA_PATH / "unittest_folder" / "test_add.py" + uf_non_adjacent_tests_execution_expected_output = { - TEST_SUBTRACT_FUNCTION - + "test_subtract_positive_numbers": { - "test": TEST_SUBTRACT_FUNCTION + "test_subtract_positive_numbers", + get_absolute_test_id( + f"{TEST_SUBTRACT_FUNCTION}test_subtract_positive_numbers", test_subtract_path + ): { + "test": get_absolute_test_id( + f"{TEST_SUBTRACT_FUNCTION}test_subtract_positive_numbers", + test_subtract_path, + ), "outcome": SUCCESS, "message": None, "traceback": None, "subtest": None, }, - TEST_ADD_FUNCTION - + "test_add_positive_numbers": { - "test": TEST_ADD_FUNCTION + "test_add_positive_numbers", + get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path + ): { + "test": get_absolute_test_id( + f"{TEST_ADD_FUNCTION}test_add_positive_numbers", test_add_path + ), "outcome": SUCCESS, "message": None, "traceback": None, @@ -115,12 +163,15 @@ }, } + # This is the expected output for the simple_pytest.py file. # └── simple_pytest.py # └── test_function: success +simple_pytest_path = TEST_DATA_PATH / "unittest_folder" / "simple_pytest.py" + simple_execution_pytest_expected_output = { - "simple_pytest.py::test_function": { - "test": "simple_pytest.py::test_function", + get_absolute_test_id("test_function", simple_pytest_path): { + "test": get_absolute_test_id("test_function", simple_pytest_path), "outcome": "success", "message": None, "traceback": None, @@ -128,21 +179,34 @@ } } + # This is the expected output for the unittest_pytest_same_file.py file. # ├── unittest_pytest_same_file.py # ├── TestExample # │ └── test_true_unittest: success # └── test_true_pytest: success +unit_pytest_same_file_path = TEST_DATA_PATH / "unittest_pytest_same_file.py" unit_pytest_same_file_execution_expected_output = { - "unittest_pytest_same_file.py::TestExample::test_true_unittest": { - "test": "unittest_pytest_same_file.py::TestExample::test_true_unittest", + get_absolute_test_id( + "unittest_pytest_same_file.py::TestExample::test_true_unittest", + unit_pytest_same_file_path, + ): { + "test": get_absolute_test_id( + "unittest_pytest_same_file.py::TestExample::test_true_unittest", + unit_pytest_same_file_path, + ), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "unittest_pytest_same_file.py::test_true_pytest": { - "test": "unittest_pytest_same_file.py::test_true_pytest", + get_absolute_test_id( + "unittest_pytest_same_file.py::test_true_pytest", unit_pytest_same_file_path + ): { + "test": get_absolute_test_id( + "unittest_pytest_same_file.py::test_true_pytest", + unit_pytest_same_file_path, + ), "outcome": "success", "message": None, "traceback": None, @@ -154,9 +218,15 @@ # └── error_raise_exception.py # ├── TestSomething # │ └── test_a: failure +error_raised_exception_path = TEST_DATA_PATH / "error_raise_exception.py" error_raised_exception_execution_expected_output = { - "error_raise_exception.py::TestSomething::test_a": { - "test": "error_raise_exception.py::TestSomething::test_a", + get_absolute_test_id( + "error_raise_exception.py::TestSomething::test_a", error_raised_exception_path + ): { + "test": get_absolute_test_id( + "error_raise_exception.py::TestSomething::test_a", + error_raised_exception_path, + ), "outcome": "error", "message": "ERROR MESSAGE", "traceback": "TRACEBACK", @@ -172,44 +242,60 @@ # ├── TestClass # │ └── test_class_function_a: skipped # │ └── test_class_function_b: skipped + +skip_tests_path = TEST_DATA_PATH / "skip_tests.py" skip_tests_execution_expected_output = { - "skip_tests.py::test_something": { - "test": "skip_tests.py::test_something", + get_absolute_test_id("skip_tests.py::test_something", skip_tests_path): { + "test": get_absolute_test_id("skip_tests.py::test_something", skip_tests_path), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "skip_tests.py::test_another_thing": { - "test": "skip_tests.py::test_another_thing", + get_absolute_test_id("skip_tests.py::test_another_thing", skip_tests_path): { + "test": get_absolute_test_id( + "skip_tests.py::test_another_thing", skip_tests_path + ), "outcome": "skipped", "message": None, "traceback": None, "subtest": None, }, - "skip_tests.py::test_decorator_thing": { - "test": "skip_tests.py::test_decorator_thing", + get_absolute_test_id("skip_tests.py::test_decorator_thing", skip_tests_path): { + "test": get_absolute_test_id( + "skip_tests.py::test_decorator_thing", skip_tests_path + ), "outcome": "skipped", "message": None, "traceback": None, "subtest": None, }, - "skip_tests.py::test_decorator_thing_2": { - "test": "skip_tests.py::test_decorator_thing_2", + get_absolute_test_id("skip_tests.py::test_decorator_thing_2", skip_tests_path): { + "test": get_absolute_test_id( + "skip_tests.py::test_decorator_thing_2", skip_tests_path + ), "outcome": "skipped", "message": None, "traceback": None, "subtest": None, }, - "skip_tests.py::TestClass::test_class_function_a": { - "test": "skip_tests.py::TestClass::test_class_function_a", + get_absolute_test_id( + "skip_tests.py::TestClass::test_class_function_a", skip_tests_path + ): { + "test": get_absolute_test_id( + "skip_tests.py::TestClass::test_class_function_a", skip_tests_path + ), "outcome": "skipped", "message": None, "traceback": None, "subtest": None, }, - "skip_tests.py::TestClass::test_class_function_b": { - "test": "skip_tests.py::TestClass::test_class_function_b", + get_absolute_test_id( + "skip_tests.py::TestClass::test_class_function_b", skip_tests_path + ): { + "test": get_absolute_test_id( + "skip_tests.py::TestClass::test_class_function_b", skip_tests_path + ), "outcome": "skipped", "message": None, "traceback": None, @@ -227,30 +313,59 @@ # └── test_bottom_folder.py # └── test_bottom_function_t: success # └── test_bottom_function_f: failure +dual_level_nested_folder_top_path = ( + TEST_DATA_PATH / "dual_level_nested_folder" / "test_top_folder.py" +) +dual_level_nested_folder_bottom_path = ( + TEST_DATA_PATH + / "dual_level_nested_folder" + / "nested_folder_one" + / "test_bottom_folder.py" +) dual_level_nested_folder_execution_expected_output = { - "dual_level_nested_folder/test_top_folder.py::test_top_function_t": { - "test": "dual_level_nested_folder/test_top_folder.py::test_top_function_t", + get_absolute_test_id( + "test_top_folder.py::test_top_function_t", dual_level_nested_folder_top_path + ): { + "test": get_absolute_test_id( + "test_top_folder.py::test_top_function_t", dual_level_nested_folder_top_path + ), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "dual_level_nested_folder/test_top_folder.py::test_top_function_f": { - "test": "dual_level_nested_folder/test_top_folder.py::test_top_function_f", + get_absolute_test_id( + "test_top_folder.py::test_top_function_f", dual_level_nested_folder_top_path + ): { + "test": get_absolute_test_id( + "test_top_folder.py::test_top_function_f", dual_level_nested_folder_top_path + ), "outcome": "failure", "message": "ERROR MESSAGE", "traceback": None, "subtest": None, }, - "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t": { - "test": "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + get_absolute_test_id( + "nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + dual_level_nested_folder_bottom_path, + ): { + "test": get_absolute_test_id( + "nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + dual_level_nested_folder_bottom_path, + ), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f": { - "test": "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + get_absolute_test_id( + "nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + dual_level_nested_folder_bottom_path, + ): { + "test": get_absolute_test_id( + "nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + dual_level_nested_folder_bottom_path, + ), "outcome": "failure", "message": "ERROR MESSAGE", "traceback": None, @@ -264,38 +379,59 @@ # └── folder_a # └── test_nest.py # └── test_function: success + +nested_folder_path = ( + TEST_DATA_PATH / "folder_a" / "folder_b" / "folder_a" / "test_nest.py" +) double_nested_folder_expected_execution_output = { - "folder_a/folder_b/folder_a/test_nest.py::test_function": { - "test": "folder_a/folder_b/folder_a/test_nest.py::test_function", + get_absolute_test_id( + "folder_a/folder_b/folder_a/test_nest.py::test_function", nested_folder_path + ): { + "test": get_absolute_test_id( + "folder_a/folder_b/folder_a/test_nest.py::test_function", nested_folder_path + ), "outcome": "success", "message": None, "traceback": None, "subtest": None, } } - # This is the expected output for the nested_folder tests. # └── parametrize_tests.py # └── test_adding[3+5-8]: success # └── test_adding[2+4-6]: success # └── test_adding[6+9-16]: failure +parametrize_tests_path = TEST_DATA_PATH / "parametrize_tests.py" + parametrize_tests_expected_execution_output = { - "parametrize_tests.py::test_adding[3+5-8]": { - "test": "parametrize_tests.py::test_adding[3+5-8]", + get_absolute_test_id( + "parametrize_tests.py::test_adding[3+5-8]", parametrize_tests_path + ): { + "test": get_absolute_test_id( + "parametrize_tests.py::test_adding[3+5-8]", parametrize_tests_path + ), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "parametrize_tests.py::test_adding[2+4-6]": { - "test": "parametrize_tests.py::test_adding[2+4-6]", + get_absolute_test_id( + "parametrize_tests.py::test_adding[2+4-6]", parametrize_tests_path + ): { + "test": get_absolute_test_id( + "parametrize_tests.py::test_adding[2+4-6]", parametrize_tests_path + ), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "parametrize_tests.py::test_adding[6+9-16]": { - "test": "parametrize_tests.py::test_adding[6+9-16]", + get_absolute_test_id( + "parametrize_tests.py::test_adding[6+9-16]", parametrize_tests_path + ): { + "test": get_absolute_test_id( + "parametrize_tests.py::test_adding[6+9-16]", parametrize_tests_path + ), "outcome": "failure", "message": "ERROR MESSAGE", "traceback": None, @@ -307,8 +443,12 @@ # └── parametrize_tests.py # └── test_adding[3+5-8]: success single_parametrize_tests_expected_execution_output = { - "parametrize_tests.py::test_adding[3+5-8]": { - "test": "parametrize_tests.py::test_adding[3+5-8]", + get_absolute_test_id( + "parametrize_tests.py::test_adding[3+5-8]", parametrize_tests_path + ): { + "test": get_absolute_test_id( + "parametrize_tests.py::test_adding[3+5-8]", parametrize_tests_path + ), "outcome": "success", "message": None, "traceback": None, @@ -319,9 +459,12 @@ # This is the expected output for the single parameterized tests. # └── text_docstring.txt # └── text_docstring: success +doc_test_path = TEST_DATA_PATH / "text_docstring.txt" doctest_pytest_expected_execution_output = { - "text_docstring.txt::text_docstring.txt": { - "test": "text_docstring.txt::text_docstring.txt", + get_absolute_test_id("text_docstring.txt::text_docstring.txt", doc_test_path): { + "test": get_absolute_test_id( + "text_docstring.txt::text_docstring.txt", doc_test_path + ), "outcome": "success", "message": None, "traceback": None, @@ -330,65 +473,110 @@ } # Will run all tests in the cwd that fit the test file naming pattern. +folder_a_path = TEST_DATA_PATH / "folder_a" / "folder_b" / "folder_a" / "test_nest.py" +dual_level_nested_folder_top_path = ( + TEST_DATA_PATH / "dual_level_nested_folder" / "test_top_folder.py" +) +dual_level_nested_folder_bottom_path = ( + TEST_DATA_PATH + / "dual_level_nested_folder" + / "nested_folder_one" + / "test_bottom_folder.py" +) +unittest_folder_add_path = TEST_DATA_PATH / "unittest_folder" / "test_add.py" +unittest_folder_subtract_path = TEST_DATA_PATH / "unittest_folder" / "test_subtract.py" + no_test_ids_pytest_execution_expected_output = { - "folder_a/folder_b/folder_a/test_nest.py::test_function": { - "test": "folder_a/folder_b/folder_a/test_nest.py::test_function", + get_absolute_test_id("test_function", folder_a_path): { + "test": get_absolute_test_id("test_function", folder_a_path), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "dual_level_nested_folder/test_top_folder.py::test_top_function_t": { - "test": "dual_level_nested_folder/test_top_folder.py::test_top_function_t", + get_absolute_test_id("test_top_function_t", dual_level_nested_folder_top_path): { + "test": get_absolute_test_id( + "test_top_function_t", dual_level_nested_folder_top_path + ), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "dual_level_nested_folder/test_top_folder.py::test_top_function_f": { - "test": "dual_level_nested_folder/test_top_folder.py::test_top_function_f", + get_absolute_test_id("test_top_function_f", dual_level_nested_folder_top_path): { + "test": get_absolute_test_id( + "test_top_function_f", dual_level_nested_folder_top_path + ), "outcome": "failure", "message": "ERROR MESSAGE", "traceback": None, "subtest": None, }, - "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t": { - "test": "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_t", + get_absolute_test_id( + "test_bottom_function_t", dual_level_nested_folder_bottom_path + ): { + "test": get_absolute_test_id( + "test_bottom_function_t", dual_level_nested_folder_bottom_path + ), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f": { - "test": "dual_level_nested_folder/nested_folder_one/test_bottom_folder.py::test_bottom_function_f", + get_absolute_test_id( + "test_bottom_function_f", dual_level_nested_folder_bottom_path + ): { + "test": get_absolute_test_id( + "test_bottom_function_f", dual_level_nested_folder_bottom_path + ), "outcome": "failure", "message": "ERROR MESSAGE", "traceback": None, "subtest": None, }, - "unittest_folder/test_add.py::TestAddFunction::test_add_negative_numbers": { - "test": "unittest_folder/test_add.py::TestAddFunction::test_add_negative_numbers", + get_absolute_test_id( + "TestAddFunction::test_add_negative_numbers", unittest_folder_add_path + ): { + "test": get_absolute_test_id( + "TestAddFunction::test_add_negative_numbers", unittest_folder_add_path + ), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "unittest_folder/test_add.py::TestAddFunction::test_add_positive_numbers": { - "test": "unittest_folder/test_add.py::TestAddFunction::test_add_positive_numbers", + get_absolute_test_id( + "TestAddFunction::test_add_positive_numbers", unittest_folder_add_path + ): { + "test": get_absolute_test_id( + "TestAddFunction::test_add_positive_numbers", unittest_folder_add_path + ), "outcome": "success", "message": None, "traceback": None, "subtest": None, }, - "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_negative_numbers": { - "test": "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_negative_numbers", + get_absolute_test_id( + "TestSubtractFunction::test_subtract_negative_numbers", + unittest_folder_subtract_path, + ): { + "test": get_absolute_test_id( + "TestSubtractFunction::test_subtract_negative_numbers", + unittest_folder_subtract_path, + ), "outcome": "failure", "message": "ERROR MESSAGE", "traceback": None, "subtest": None, }, - "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_positive_numbers": { - "test": "unittest_folder/test_subtract.py::TestSubtractFunction::test_subtract_positive_numbers", + get_absolute_test_id( + "TestSubtractFunction::test_subtract_positive_numbers", + unittest_folder_subtract_path, + ): { + "test": get_absolute_test_id( + "TestSubtractFunction::test_subtract_positive_numbers", + unittest_folder_subtract_path, + ), "outcome": "success", "message": None, "traceback": None, @@ -399,9 +587,10 @@ # This is the expected output for the root folder with the config file referenced. # └── test_a.py # └── test_a_function: success +test_add_path = TEST_DATA_PATH / "root" / "tests" / "test_a.py" config_file_pytest_expected_execution_output = { - "tests/test_a.py::test_a_function": { - "test": "tests/test_a.py::test_a_function", + get_absolute_test_id("tests/test_a.py::test_a_function", test_add_path): { + "test": get_absolute_test_id("tests/test_a.py::test_a_function", test_add_path), "outcome": "success", "message": None, "traceback": None, From af1864d2461a193d552c485353ae87e176f5a5b4 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 27 Jul 2023 15:10:30 -0700 Subject: [PATCH 16/16] small fixes --- .../tests/pytestadapter/test_discovery.py | 16 +++++----- pythonFiles/vscode_pytest/__init__.py | 29 ++++++++----------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/pythonFiles/tests/pytestadapter/test_discovery.py b/pythonFiles/tests/pytestadapter/test_discovery.py index 970923e9771b..8d785be27c8b 100644 --- a/pythonFiles/tests/pytestadapter/test_discovery.py +++ b/pythonFiles/tests/pytestadapter/test_discovery.py @@ -87,14 +87,14 @@ def test_parameterized_error_collect(): @pytest.mark.parametrize( "file, expected_const", [ - # ( - # "unittest_skiptest_file_level.py", - # expected_discovery_test_output.unittest_skip_file_level_expected_output, - # ), - # ( - # "param_same_name", - # expected_discovery_test_output.param_same_name_expected_output, - # ), + ( + "unittest_skiptest_file_level.py", + expected_discovery_test_output.unittest_skip_file_level_expected_output, + ), + ( + "param_same_name", + expected_discovery_test_output.param_same_name_expected_output, + ), ( "parametrize_tests.py", expected_discovery_test_output.parametrize_tests_expected_output, diff --git a/pythonFiles/vscode_pytest/__init__.py b/pythonFiles/vscode_pytest/__init__.py index 22491aa5674d..49d429662e3a 100644 --- a/pythonFiles/vscode_pytest/__init__.py +++ b/pythonFiles/vscode_pytest/__init__.py @@ -69,8 +69,7 @@ def pytest_exception_interact(node, call, report): """ # call.excinfo is the captured exception of the call, if it raised as type ExceptionInfo. # call.excinfo.exconly() returns the exception as a string. - # See if it is during discovery or execution. - # if discovery, then add the error to error logs. + # If it is during discovery, then add the error to error logs. if type(report) == pytest.CollectReport: if call.excinfo and call.excinfo.typename != "AssertionError": if report.outcome == "skipped" and "SkipTest" in str(call): @@ -83,7 +82,7 @@ def pytest_exception_interact(node, call, report): report.longreprtext + "\n Check Python Test Logs for more details." ) else: - # if execution, send this data that the given node failed. + # If during execution, send this data that the given node failed. report_value = "error" if call.excinfo.typename == "AssertionError": report_value = "failure" @@ -107,6 +106,15 @@ def pytest_exception_interact(node, call, report): def get_absolute_test_id(test_id: str, testPath: pathlib.Path) -> str: + """A function that returns the absolute test id. This is necessary because testIds are relative to the rootdir. + This does not work for our case since testIds when referenced during run time are relative to the instantiation + location. Absolute paths for testIds are necessary for the test tree ensures configurations that change the rootdir + of pytest are handled correctly. + + Keyword arguments: + test_id -- the pytest id of the test which is relative to the rootdir. + testPath -- the path to the file the test is located in, as a pathlib.Path object. + """ split_id = test_id.split("::")[1:] absolute_test_id = "::".join([str(testPath), *split_id]) print("absolute path", absolute_test_id) @@ -161,9 +169,6 @@ class testRunResultDict(Dict[str, Dict[str, TestOutcome]]): IS_DISCOVERY = False -RELATIVE_INVOCATION_PATH = "" -SESSION_CONFIG_ROOT = "" -INVO_DIR = "" map_id_to_path = dict() @@ -171,17 +176,6 @@ def pytest_load_initial_conftests(early_config, parser, args): if "--collect-only" in args: global IS_DISCOVERY IS_DISCOVERY = True - # global INVO_DIR - # INVO_DIR = early_config.invocation_params.dir - # root = early_config.rootpath - # global SESSION_CONFIG_ROOT - # SESSION_CONFIG_ROOT = root - # # if invocation_dir != root: - # # try: - # # global RELATIVE_INVOCATION_PATH - # # RELATIVE_INVOCATION_PATH = root.relative_to(invocation_dir) - # # except: - # # pass collected_tests_so_far = list() @@ -209,6 +203,7 @@ def pytest_report_teststatus(report, config): node_path = map_id_to_path[report.nodeid] if not node_path: node_path = cwd + # Calculate the absolute test id and use this as the ID moving forward. absolute_node_id = get_absolute_test_id(report.nodeid, node_path) if absolute_node_id not in collected_tests_so_far: collected_tests_so_far.append(absolute_node_id)