From db33e55212b9fe4fecf8e3d6094a7f65048fccfe Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 7 Aug 2023 12:50:34 -0700 Subject: [PATCH 01/12] Revert "revert absolute test-ids (#21742)" This reverts commit dff25d4362389014a560f3dd6aaf61a3d814d2a1. --- pythonFiles/tests/pytestadapter/test_execution.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pythonFiles/tests/pytestadapter/test_execution.py b/pythonFiles/tests/pytestadapter/test_execution.py index 07354b01709b..30f1a7e439d9 100644 --- a/pythonFiles/tests/pytestadapter/test_execution.py +++ b/pythonFiles/tests/pytestadapter/test_execution.py @@ -1,5 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import json import os import shutil From f2eb591ab095d567930ecc7c71fc7a2d313fa309 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 9 Aug 2023 11:29:32 -0700 Subject: [PATCH 02/12] adding functional tests for testing --- .../pytestDiscoveryAdapter.functional.test.ts | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts diff --git a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts new file mode 100644 index 000000000000..d2c8bb9f228b --- /dev/null +++ b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts @@ -0,0 +1,68 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { TestController, WorkspaceFolder, tests } from 'vscode'; +import { PytestTestDiscoveryAdapter } from '../../../../client/testing/testController/pytest/pytestDiscoveryAdapter'; +import { + ITestDiscoveryAdapter, + ITestResultResolver, + ITestServer, +} from '../../../../client/testing/testController/common/types'; +import { PythonResultResolver } from '../../../../client/testing/testController/common/resultResolver'; +import { PythonTestServer } from '../../../../client/testing/testController/common/server'; +import { IWorkspaceService } from '../../../../client/common/application/types'; +import { IPythonExecutionFactory } from '../../../../client/common/process/types'; +import { ITestDebugLauncher } from '../../../../client/testing/common/types'; +import { PYTEST_PROVIDER } from '../../../../client/testing/common/constants'; +import { TestProvider } from '../../../../client/testing/types'; +import { IConfigurationService, ITestOutputChannel } from '../../../../client/common/types'; +import { IServiceContainer } from '../../../../client/ioc/types'; +import { IS_SMOKE_TEST, initialize } from '../../../initialize'; + +suite('pytest test discovery adapter', () => { + let resultResolver: ITestResultResolver; + let discoveryAdapter: ITestDiscoveryAdapter; + let testController: TestController; + let testProvider: TestProvider; + let pythonTestServer: ITestServer; + let pythonExecFactory: IPythonExecutionFactory; + let debugLauncher: ITestDebugLauncher; + let workspace: WorkspaceFolder; + let workspaceService: IWorkspaceService; + let configService: IConfigurationService; + let testOutputChannel: ITestOutputChannel; + let serviceContainer: IServiceContainer; + suiteSetup(async function () { + if (!IS_SMOKE_TEST) { + this.skip(); + } + serviceContainer = (await initialize()).serviceContainer; + }); + + setup(() => { + // create objects that were injected + configService = serviceContainer.get(IConfigurationService); + pythonExecFactory = serviceContainer.get(IPythonExecutionFactory); + debugLauncher = serviceContainer.get(ITestDebugLauncher); + workspaceService = serviceContainer.get(IWorkspaceService); + testOutputChannel = serviceContainer.get(ITestOutputChannel); + + // create objects that were not injected + const workspaces = workspaceService.workspaceFolders || []; + [workspace] = workspaces; // usually this is a for-each loop but we know there will only be one workspace + testProvider = PYTEST_PROVIDER; + testController = tests.createTestController('python-tests', 'Python Tests'); + pythonTestServer = new PythonTestServer(pythonExecFactory, debugLauncher); + resultResolver = new PythonResultResolver(testController, testProvider, workspace.uri); + + discoveryAdapter = new PytestTestDiscoveryAdapter( + pythonTestServer, + configService, + testOutputChannel, + resultResolver, + ); + }); + test('Discovery should call exec with correct basic args', async () => { + discoveryAdapter.discoverTests(workspace.uri, pythonExecFactory); + }); +}); From ef2d4469e6b5a1d519a429f3b57a8cd433efc67c Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 9 Aug 2023 13:18:19 -0700 Subject: [PATCH 03/12] updates --- .../pytestDiscoveryAdapter.functional.test.ts | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts index d2c8bb9f228b..a83043d84c60 100644 --- a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts +++ b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts @@ -1,29 +1,27 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { TestController, WorkspaceFolder, tests } from 'vscode'; +import { WorkspaceFolder } from 'vscode'; +import * as typeMoq from 'typemoq'; import { PytestTestDiscoveryAdapter } from '../../../../client/testing/testController/pytest/pytestDiscoveryAdapter'; import { ITestDiscoveryAdapter, ITestResultResolver, ITestServer, } from '../../../../client/testing/testController/common/types'; -import { PythonResultResolver } from '../../../../client/testing/testController/common/resultResolver'; import { PythonTestServer } from '../../../../client/testing/testController/common/server'; import { IWorkspaceService } from '../../../../client/common/application/types'; import { IPythonExecutionFactory } from '../../../../client/common/process/types'; import { ITestDebugLauncher } from '../../../../client/testing/common/types'; -import { PYTEST_PROVIDER } from '../../../../client/testing/common/constants'; -import { TestProvider } from '../../../../client/testing/types'; import { IConfigurationService, ITestOutputChannel } from '../../../../client/common/types'; import { IServiceContainer } from '../../../../client/ioc/types'; -import { IS_SMOKE_TEST, initialize } from '../../../initialize'; +import { initialize } from '../../../initialize'; -suite('pytest test discovery adapter', () => { - let resultResolver: ITestResultResolver; +suite('Functional Tests: pytest test discovery adapter ejfb', () => { + let resultResolver: typeMoq.IMock; let discoveryAdapter: ITestDiscoveryAdapter; - let testController: TestController; - let testProvider: TestProvider; + // let testController: TestController; + // let testProvider: TestProvider; let pythonTestServer: ITestServer; let pythonExecFactory: IPythonExecutionFactory; let debugLauncher: ITestDebugLauncher; @@ -32,10 +30,10 @@ suite('pytest test discovery adapter', () => { let configService: IConfigurationService; let testOutputChannel: ITestOutputChannel; let serviceContainer: IServiceContainer; - suiteSetup(async function () { - if (!IS_SMOKE_TEST) { - this.skip(); - } + suiteSetup(async () => { + // if (!IS_SMOKE_TEST) { + // this.skip(); + // } serviceContainer = (await initialize()).serviceContainer; }); @@ -50,19 +48,18 @@ suite('pytest test discovery adapter', () => { // create objects that were not injected const workspaces = workspaceService.workspaceFolders || []; [workspace] = workspaces; // usually this is a for-each loop but we know there will only be one workspace - testProvider = PYTEST_PROVIDER; - testController = tests.createTestController('python-tests', 'Python Tests'); + // testProvider = PYTEST_PROVIDER; + // testController = tests.createTestController('python-tests', 'Python Tests'); pythonTestServer = new PythonTestServer(pythonExecFactory, debugLauncher); - resultResolver = new PythonResultResolver(testController, testProvider, workspace.uri); - + resultResolver = typeMoq.Mock.ofType(); discoveryAdapter = new PytestTestDiscoveryAdapter( pythonTestServer, configService, testOutputChannel, - resultResolver, + resultResolver.object, ); }); - test('Discovery should call exec with correct basic args', async () => { + test('ejfb test', async () => { discoveryAdapter.discoverTests(workspace.uri, pythonExecFactory); }); }); From 0f734eb9531edac5d7433d6600ad65dbd7f54342 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 9 Aug 2023 15:56:44 -0700 Subject: [PATCH 04/12] tests working for pytest --- .../pytestDiscoveryAdapter.functional.test.ts | 65 --------- .../pytest/pytestDiscoveryAdapter.test.ts | 126 ++++++++++++++++++ src/testTestingRootWkspc/test_simple.py | 2 + 3 files changed, 128 insertions(+), 65 deletions(-) delete mode 100644 src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts create mode 100644 src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts create mode 100644 src/testTestingRootWkspc/test_simple.py diff --git a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts deleted file mode 100644 index a83043d84c60..000000000000 --- a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.functional.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -import { WorkspaceFolder } from 'vscode'; -import * as typeMoq from 'typemoq'; -import { PytestTestDiscoveryAdapter } from '../../../../client/testing/testController/pytest/pytestDiscoveryAdapter'; -import { - ITestDiscoveryAdapter, - ITestResultResolver, - ITestServer, -} from '../../../../client/testing/testController/common/types'; -import { PythonTestServer } from '../../../../client/testing/testController/common/server'; -import { IWorkspaceService } from '../../../../client/common/application/types'; -import { IPythonExecutionFactory } from '../../../../client/common/process/types'; -import { ITestDebugLauncher } from '../../../../client/testing/common/types'; -import { IConfigurationService, ITestOutputChannel } from '../../../../client/common/types'; -import { IServiceContainer } from '../../../../client/ioc/types'; -import { initialize } from '../../../initialize'; - -suite('Functional Tests: pytest test discovery adapter ejfb', () => { - let resultResolver: typeMoq.IMock; - let discoveryAdapter: ITestDiscoveryAdapter; - // let testController: TestController; - // let testProvider: TestProvider; - let pythonTestServer: ITestServer; - let pythonExecFactory: IPythonExecutionFactory; - let debugLauncher: ITestDebugLauncher; - let workspace: WorkspaceFolder; - let workspaceService: IWorkspaceService; - let configService: IConfigurationService; - let testOutputChannel: ITestOutputChannel; - let serviceContainer: IServiceContainer; - suiteSetup(async () => { - // if (!IS_SMOKE_TEST) { - // this.skip(); - // } - serviceContainer = (await initialize()).serviceContainer; - }); - - setup(() => { - // create objects that were injected - configService = serviceContainer.get(IConfigurationService); - pythonExecFactory = serviceContainer.get(IPythonExecutionFactory); - debugLauncher = serviceContainer.get(ITestDebugLauncher); - workspaceService = serviceContainer.get(IWorkspaceService); - testOutputChannel = serviceContainer.get(ITestOutputChannel); - - // create objects that were not injected - const workspaces = workspaceService.workspaceFolders || []; - [workspace] = workspaces; // usually this is a for-each loop but we know there will only be one workspace - // testProvider = PYTEST_PROVIDER; - // testController = tests.createTestController('python-tests', 'Python Tests'); - pythonTestServer = new PythonTestServer(pythonExecFactory, debugLauncher); - resultResolver = typeMoq.Mock.ofType(); - discoveryAdapter = new PytestTestDiscoveryAdapter( - pythonTestServer, - configService, - testOutputChannel, - resultResolver.object, - ); - }); - test('ejfb test', async () => { - discoveryAdapter.discoverTests(workspace.uri, pythonExecFactory); - }); -}); diff --git a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts new file mode 100644 index 000000000000..8bb4870b7b57 --- /dev/null +++ b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts @@ -0,0 +1,126 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +import { TestRun, Uri } from 'vscode'; +import * as typeMoq from 'typemoq'; +import * as path from 'path'; +import * as assert from 'assert'; +import { PytestTestDiscoveryAdapter } from '../../../../client/testing/testController/pytest/pytestDiscoveryAdapter'; +import { ITestResultResolver, ITestServer } from '../../../../client/testing/testController/common/types'; +import { PythonTestServer } from '../../../../client/testing/testController/common/server'; +import { IPythonExecutionFactory } from '../../../../client/common/process/types'; +import { ITestDebugLauncher } from '../../../../client/testing/common/types'; +import { IConfigurationService, ITestOutputChannel } from '../../../../client/common/types'; +import { IServiceContainer } from '../../../../client/ioc/types'; +import { EXTENSION_ROOT_DIR_FOR_TESTS, initialize } from '../../../initialize'; +import { traceLog } from '../../../../client/logging'; +import { PytestTestExecutionAdapter } from '../../../../client/testing/testController/pytest/pytestExecutionAdapter'; + +suite('Functional Tests: test adapters', () => { + let resultResolver: typeMoq.IMock; + let pythonTestServer: ITestServer; + let pythonExecFactory: IPythonExecutionFactory; + let debugLauncher: ITestDebugLauncher; + let configService: IConfigurationService; + let testOutputChannel: ITestOutputChannel; + let serviceContainer: IServiceContainer; + let workspaceUri: Uri; + const rootPath = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'testTestingRootWkspc'); + suiteSetup(async () => { + // if (!IS_SMOKE_TEST) { + // this.skip(); + // } + console.error('suiteSetup'); + serviceContainer = (await initialize()).serviceContainer; + }); + + setup(async () => { + // create objects that were injected + configService = serviceContainer.get(IConfigurationService); + pythonExecFactory = serviceContainer.get(IPythonExecutionFactory); + debugLauncher = serviceContainer.get(ITestDebugLauncher); + testOutputChannel = serviceContainer.get(ITestOutputChannel); + + // create mock resultResolver object + resultResolver = typeMoq.Mock.ofType(); + + // set workspace to test workspace folder + workspaceUri = Uri.parse(rootPath); + + // create objects that were not injected + pythonTestServer = new PythonTestServer(pythonExecFactory, debugLauncher); + await pythonTestServer.serverReady(); + }); + test('pytest discovery adapter', async () => { + // result resolver and saved data for assertions + let actualData: { status: unknown; error: string | any[]; tests: unknown }; + resultResolver + .setup((x) => x.resolveDiscovery(typeMoq.It.isAny(), typeMoq.It.isAny())) + .returns((data) => { + traceLog(`resolveDiscovery ${data}`); + actualData = data; + return Promise.resolve(); + }); + // run pytest discovery + const discoveryAdapter = new PytestTestDiscoveryAdapter( + pythonTestServer, + configService, + testOutputChannel, + resultResolver.object, + ); + await discoveryAdapter.discoverTests(workspaceUri, pythonExecFactory).finally(() => { + // verification after discovery is complete + resultResolver.verify( + (x) => x.resolveDiscovery(typeMoq.It.isAny(), typeMoq.It.isAny()), + typeMoq.Times.once(), + ); + + // 1. Check the status is "success" + assert.strictEqual(actualData.status, 'success', "Expected status to be 'success'"); + // 2. Confirm no errors + assert.strictEqual(actualData.error.length, 0, "Expected no errors in 'error' field"); + // 3. Confirm tests are found + assert.ok(actualData.tests, 'Expected tests to be present'); + }); + }); + test('pytest execution adapter', async () => { + // result resolver and saved data for assertions + let actualData: { + status: unknown; + error: string | any[]; + result: unknown; + }; + resultResolver + .setup((x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny())) + .returns((data) => { + traceLog(`resolveExecution ${data}`); + actualData = data; + return Promise.resolve(); + }); + // run pytest execution + const executionAdapter = new PytestTestExecutionAdapter( + pythonTestServer, + configService, + testOutputChannel, + resultResolver.object, + ); + const testRun = typeMoq.Mock.ofType(); + testRun.setup((t) => t.token).returns(() => ({ onCancellationRequested: () => undefined } as any)); + await executionAdapter + .runTests(workspaceUri, [`${rootPath}/test_simple.py::test_a`], false, testRun.object, pythonExecFactory) + .finally(() => { + // verification after discovery is complete + resultResolver.verify( + (x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny()), + typeMoq.Times.once(), + ); + + // 1. Check the status is "success" + assert.strictEqual(actualData.status, 'success', "Expected status to be 'success'"); + // 2. Confirm no errors + assert.strictEqual(actualData.error, null, "Expected no errors in 'error' field"); + // 3. Confirm tests are found + assert.ok(actualData.result, 'Expected results to be present'); + }); + }); +}); diff --git a/src/testTestingRootWkspc/test_simple.py b/src/testTestingRootWkspc/test_simple.py new file mode 100644 index 000000000000..33110c7ef8ab --- /dev/null +++ b/src/testTestingRootWkspc/test_simple.py @@ -0,0 +1,2 @@ +def test_a(): + assert 1 == 1 From 3f23d28d1f8bc63f1b02c68c83320f9995875b33 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 10 Aug 2023 15:05:32 -0700 Subject: [PATCH 05/12] some tests working- not execution for unittest --- .../pytest/pytestDiscoveryAdapter.ts | 1 + .../pytest/pytestDiscoveryAdapter.test.ts | 324 +++++++++++++++++- .../test_parameterized_subtest.py | 16 + .../smallWorkspace/test_simple.py | 12 + src/testTestingRootWkspc/test_simple.py | 2 - 5 files changed, 343 insertions(+), 12 deletions(-) create mode 100644 src/testTestingRootWkspc/largeWorkspace/test_parameterized_subtest.py create mode 100644 src/testTestingRootWkspc/smallWorkspace/test_simple.py delete mode 100644 src/testTestingRootWkspc/test_simple.py diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 0c91e3c94bdc..44ab3746dde4 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -98,6 +98,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { deferredExec.resolve({ stdout: '', stderr: '' }); deferred.resolve(); }); + await deferredExec.promise; } } diff --git a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts index 8bb4870b7b57..415fb75b9768 100644 --- a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts +++ b/src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts @@ -15,6 +15,8 @@ import { IServiceContainer } from '../../../../client/ioc/types'; import { EXTENSION_ROOT_DIR_FOR_TESTS, initialize } from '../../../initialize'; import { traceLog } from '../../../../client/logging'; import { PytestTestExecutionAdapter } from '../../../../client/testing/testController/pytest/pytestExecutionAdapter'; +import { UnittestTestDiscoveryAdapter } from '../../../../client/testing/testController/unittest/testDiscoveryAdapter'; +import { UnittestTestExecutionAdapter } from '../../../../client/testing/testController/unittest/testExecutionAdapter'; suite('Functional Tests: test adapters', () => { let resultResolver: typeMoq.IMock; @@ -25,12 +27,22 @@ suite('Functional Tests: test adapters', () => { let testOutputChannel: ITestOutputChannel; let serviceContainer: IServiceContainer; let workspaceUri: Uri; - const rootPath = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'testTestingRootWkspc'); + const rootPathSmallWorkspace = path.join( + EXTENSION_ROOT_DIR_FOR_TESTS, + 'src', + 'testTestingRootWkspc', + 'smallWorkspace', + ); + const rootPathLargeWorkspace = path.join( + EXTENSION_ROOT_DIR_FOR_TESTS, + 'src', + 'testTestingRootWkspc', + 'largeWorkspace', + ); suiteSetup(async () => { // if (!IS_SMOKE_TEST) { // this.skip(); // } - console.error('suiteSetup'); serviceContainer = (await initialize()).serviceContainer; }); @@ -44,16 +56,141 @@ suite('Functional Tests: test adapters', () => { // create mock resultResolver object resultResolver = typeMoq.Mock.ofType(); - // set workspace to test workspace folder - workspaceUri = Uri.parse(rootPath); - // create objects that were not injected pythonTestServer = new PythonTestServer(pythonExecFactory, debugLauncher); await pythonTestServer.serverReady(); }); - test('pytest discovery adapter', async () => { + test('unittest discovery adapter small workspace', async () => { + // result resolver and saved data for assertions + let actualData: { + status: unknown; + error: string | any[]; + tests: unknown; + }; + resultResolver + .setup((x) => x.resolveDiscovery(typeMoq.It.isAny(), typeMoq.It.isAny())) + .returns((data) => { + traceLog(`resolveDiscovery ${data}`); + actualData = data; + return Promise.resolve(); + }); + configService.getSettings(workspaceUri).testing.unittestArgs = ['-s', '.', '-p', '*test*.py']; + // run pytest discovery + const discoveryAdapter = new UnittestTestDiscoveryAdapter( + pythonTestServer, + configService, + testOutputChannel, + resultResolver.object, + ); + + // set workspace to test workspace folder + workspaceUri = Uri.parse(rootPathSmallWorkspace); + + await discoveryAdapter.discoverTests(workspaceUri).finally(() => { + // verification after discovery is complete + resultResolver.verify( + (x) => x.resolveDiscovery(typeMoq.It.isAny(), typeMoq.It.isAny()), + typeMoq.Times.once(), + ); + + // 1. Check the status is "success" + assert.strictEqual(actualData.status, 'success', "Expected status to be 'success'"); + // 2. Confirm no errors + assert.strictEqual(actualData.error, undefined, "Expected no errors in 'error' field"); + // 3. Confirm tests are found + assert.ok(actualData.tests, 'Expected tests to be present'); + }); + }); + + test('unittest discovery adapter large workspace', async () => { + // result resolver and saved data for assertions + let actualData: { + status: unknown; + error: string | any[]; + tests: unknown; + }; + resultResolver + .setup((x) => x.resolveDiscovery(typeMoq.It.isAny(), typeMoq.It.isAny())) + .returns((data) => { + traceLog(`resolveDiscovery ${data}`); + actualData = data; + return Promise.resolve(); + }); + + configService.getSettings(workspaceUri).testing.unittestArgs = ['-s', '.', '-p', '*test*.py']; + // run pytest discovery + const discoveryAdapter = new UnittestTestDiscoveryAdapter( + pythonTestServer, + configService, + testOutputChannel, + resultResolver.object, + ); + + // set workspace to test workspace folder + workspaceUri = Uri.parse(rootPathLargeWorkspace); + + await discoveryAdapter.discoverTests(workspaceUri).finally(() => { + // verification after discovery is complete + resultResolver.verify( + (x) => x.resolveDiscovery(typeMoq.It.isAny(), typeMoq.It.isAny()), + typeMoq.Times.once(), + ); + + // 1. Check the status is "success" + assert.strictEqual(actualData.status, 'success', "Expected status to be 'success'"); + // 2. Confirm no errors + assert.strictEqual(actualData.error, undefined, "Expected no errors in 'error' field"); + // 3. Confirm tests are found + assert.ok(actualData.tests, 'Expected tests to be present'); + }); + }); + test('pytest discovery adapter small workspace', async () => { + // result resolver and saved data for assertions + let actualData: { + status: unknown; + error: string | any[]; + tests: unknown; + }; + resultResolver + .setup((x) => x.resolveDiscovery(typeMoq.It.isAny(), typeMoq.It.isAny())) + .returns((data) => { + traceLog(`resolveDiscovery ${data}`); + actualData = data; + return Promise.resolve(); + }); + // run pytest discovery + const discoveryAdapter = new PytestTestDiscoveryAdapter( + pythonTestServer, + configService, + testOutputChannel, + resultResolver.object, + ); + + // set workspace to test workspace folder + workspaceUri = Uri.parse(rootPathSmallWorkspace); + + await discoveryAdapter.discoverTests(workspaceUri, pythonExecFactory).finally(() => { + // verification after discovery is complete + resultResolver.verify( + (x) => x.resolveDiscovery(typeMoq.It.isAny(), typeMoq.It.isAny()), + typeMoq.Times.once(), + ); + + // 1. Check the status is "success" + assert.strictEqual(actualData.status, 'success', "Expected status to be 'success'"); + // 2. Confirm no errors + assert.strictEqual(actualData.error.length, 0, "Expected no errors in 'error' field"); + // 3. Confirm tests are found + assert.ok(actualData.tests, 'Expected tests to be present'); + }); + }); + test('pytest discovery adapter large workspace', async () => { // result resolver and saved data for assertions - let actualData: { status: unknown; error: string | any[]; tests: unknown }; + let actualData: { + status: unknown; + error: string | any[]; + tests: unknown; + }; resultResolver .setup((x) => x.resolveDiscovery(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns((data) => { @@ -68,6 +205,10 @@ suite('Functional Tests: test adapters', () => { testOutputChannel, resultResolver.object, ); + + // set workspace to test workspace folder + workspaceUri = Uri.parse(rootPathLargeWorkspace); + await discoveryAdapter.discoverTests(workspaceUri, pythonExecFactory).finally(() => { // verification after discovery is complete resultResolver.verify( @@ -83,7 +224,103 @@ suite('Functional Tests: test adapters', () => { assert.ok(actualData.tests, 'Expected tests to be present'); }); }); - test('pytest execution adapter', async () => { + test('unittest execution adapter small workspace', async () => { + // result resolver and saved data for assertions + let actualData: { + status: unknown; + error: string | any[]; + result: unknown; + }; + resultResolver + .setup((x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny())) + .returns((data) => { + traceLog(`resolveExecution ${data}`); + actualData = data; + return Promise.resolve(); + }); + + // set workspace to test workspace folder + workspaceUri = Uri.parse(rootPathSmallWorkspace); + configService.getSettings(workspaceUri).testing.unittestArgs = ['-s', '.', '-p', '*test*.py']; + // run pytest execution + const executionAdapter = new UnittestTestExecutionAdapter( + pythonTestServer, + configService, + testOutputChannel, + resultResolver.object, + ); + const testRun = typeMoq.Mock.ofType(); + testRun + .setup((t) => t.token) + .returns( + () => + ({ + onCancellationRequested: () => undefined, + } as any), + ); + await executionAdapter + .runTests(workspaceUri, ['test_simple.SimpleClass.test_simple_unit'], false, testRun.object) + .finally(() => { + // verification after discovery is complete + resultResolver.verify( + (x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny()), + typeMoq.Times.once(), + ); + + // 1. Check the status is "success" + assert.strictEqual(actualData.status, 'success', "Expected status to be 'success'"); + // 2. Confirm no errors + assert.strictEqual(actualData.error, null, "Expected no errors in 'error' field"); + // 3. Confirm tests are found + assert.ok(actualData.result, 'Expected results to be present'); + }); + }); + test('unittest execution adapter large workspace', async () => { + // result resolver and saved data for assertions + resultResolver + .setup((x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny())) + .returns((data) => { + traceLog(`resolveExecution ${data}`); + // do the following asserts for each time resolveExecution is called, should be called once per test. + // 1. Check the status is "success" + assert.strictEqual(data.status, 'success', "Expected status to be 'success'"); + // 2. Confirm no errors + assert.strictEqual(data.error, null, "Expected no errors in 'error' field"); + // 3. Confirm tests are found + assert.ok(data.result, 'Expected results to be present'); + return Promise.resolve(); + }); + + // set workspace to test workspace folder + workspaceUri = Uri.parse(rootPathSmallWorkspace); + configService.getSettings(workspaceUri).testing.unittestArgs = ['-s', '.', '-p', '*test*.py']; + + // run pytest execution + const executionAdapter = new UnittestTestExecutionAdapter( + pythonTestServer, + configService, + testOutputChannel, + resultResolver.object, + ); + const testRun = typeMoq.Mock.ofType(); + testRun + .setup((t) => t.token) + .returns( + () => + ({ + onCancellationRequested: () => undefined, + } as any), + ); + // ['test_parameterized_subtest.NumbersTest.test_even']; + await executionAdapter.runTests(workspaceUri, [], false, testRun.object).finally(() => { + // verification after discovery is complete + resultResolver.verify( + (x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny()), + typeMoq.Times.exactly(200), + ); + }); + }); + test('pytest execution adapter small workspace', async () => { // result resolver and saved data for assertions let actualData: { status: unknown; @@ -97,6 +334,10 @@ suite('Functional Tests: test adapters', () => { actualData = data; return Promise.resolve(); }); + + // set workspace to test workspace folder + workspaceUri = Uri.parse(rootPathSmallWorkspace); + // run pytest execution const executionAdapter = new PytestTestExecutionAdapter( pythonTestServer, @@ -105,9 +346,22 @@ suite('Functional Tests: test adapters', () => { resultResolver.object, ); const testRun = typeMoq.Mock.ofType(); - testRun.setup((t) => t.token).returns(() => ({ onCancellationRequested: () => undefined } as any)); + testRun + .setup((t) => t.token) + .returns( + () => + ({ + onCancellationRequested: () => undefined, + } as any), + ); await executionAdapter - .runTests(workspaceUri, [`${rootPath}/test_simple.py::test_a`], false, testRun.object, pythonExecFactory) + .runTests( + workspaceUri, + [`${rootPathSmallWorkspace}/test_simple.py::test_a`], + false, + testRun.object, + pythonExecFactory, + ) .finally(() => { // verification after discovery is complete resultResolver.verify( @@ -123,4 +377,54 @@ suite('Functional Tests: test adapters', () => { assert.ok(actualData.result, 'Expected results to be present'); }); }); + test('pytest execution adapter large workspace', async () => { + resultResolver + .setup((x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny())) + .returns((data) => { + traceLog(`resolveExecution ${data}`); + // do the following asserts for each time resolveExecution is called, should be called once per test. + // 1. Check the status is "success" + assert.strictEqual(data.status, 'success', "Expected status to be 'success'"); + // 2. Confirm no errors + assert.strictEqual(data.error, null, "Expected no errors in 'error' field"); + // 3. Confirm tests are found + assert.ok(data.result, 'Expected results to be present'); + return Promise.resolve(); + }); + + // set workspace to test workspace folder + workspaceUri = Uri.parse(rootPathLargeWorkspace); + + // generate list of test_ids + const testIds: string[] = []; + + for (let i = 0; i < 200; i = i + 1) { + const testId = `${rootPathLargeWorkspace}/test_parameterized.py::test_odd_even[${i}]`; + testIds.push(testId); + } + + // run pytest execution + const executionAdapter = new PytestTestExecutionAdapter( + pythonTestServer, + configService, + testOutputChannel, + resultResolver.object, + ); + const testRun = typeMoq.Mock.ofType(); + testRun + .setup((t) => t.token) + .returns( + () => + ({ + onCancellationRequested: () => undefined, + } as any), + ); + await executionAdapter.runTests(workspaceUri, testIds, false, testRun.object, pythonExecFactory).finally(() => { + // resolve execution should be called 200 times since there are 200 tests run. + resultResolver.verify( + (x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny()), + typeMoq.Times.exactly(200), + ); + }); + }); }); diff --git a/src/testTestingRootWkspc/largeWorkspace/test_parameterized_subtest.py b/src/testTestingRootWkspc/largeWorkspace/test_parameterized_subtest.py new file mode 100644 index 000000000000..3e84df0a2d9f --- /dev/null +++ b/src/testTestingRootWkspc/largeWorkspace/test_parameterized_subtest.py @@ -0,0 +1,16 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import pytest +import unittest + + +@pytest.mark.parametrize("num", range(0, 200)) +def test_odd_even(num): + return num % 2 == 0 + + +class NumbersTest(unittest.TestCase): + def test_even(self): + for i in range(0, 200): + with self.subTest(i=i): + self.assertEqual(i % 2, 0) diff --git a/src/testTestingRootWkspc/smallWorkspace/test_simple.py b/src/testTestingRootWkspc/smallWorkspace/test_simple.py new file mode 100644 index 000000000000..6b4f7bd2f8a6 --- /dev/null +++ b/src/testTestingRootWkspc/smallWorkspace/test_simple.py @@ -0,0 +1,12 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import unittest + + +def test_a(): + assert 1 == 1 + + +class SimpleClass(unittest.TestCase): + def test_simple_unit(self): + assert True diff --git a/src/testTestingRootWkspc/test_simple.py b/src/testTestingRootWkspc/test_simple.py deleted file mode 100644 index 33110c7ef8ab..000000000000 --- a/src/testTestingRootWkspc/test_simple.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_a(): - assert 1 == 1 From 32337293030b273f8c0333f47d6629e8d14eff8c Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 10 Aug 2023 15:59:44 -0700 Subject: [PATCH 06/12] all tests working --- .../testingAdapter.test.ts} | 85 ++++++++++--------- 1 file changed, 43 insertions(+), 42 deletions(-) rename src/test/testing/{testController/pytest/pytestDiscoveryAdapter.test.ts => common/testingAdapter.test.ts} (87%) diff --git a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts b/src/test/testing/common/testingAdapter.test.ts similarity index 87% rename from src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts rename to src/test/testing/common/testingAdapter.test.ts index 415fb75b9768..4014e648420e 100644 --- a/src/test/testing/testController/pytest/pytestDiscoveryAdapter.test.ts +++ b/src/test/testing/common/testingAdapter.test.ts @@ -5,18 +5,18 @@ import { TestRun, Uri } from 'vscode'; import * as typeMoq from 'typemoq'; import * as path from 'path'; import * as assert from 'assert'; -import { PytestTestDiscoveryAdapter } from '../../../../client/testing/testController/pytest/pytestDiscoveryAdapter'; -import { ITestResultResolver, ITestServer } from '../../../../client/testing/testController/common/types'; -import { PythonTestServer } from '../../../../client/testing/testController/common/server'; -import { IPythonExecutionFactory } from '../../../../client/common/process/types'; -import { ITestDebugLauncher } from '../../../../client/testing/common/types'; -import { IConfigurationService, ITestOutputChannel } from '../../../../client/common/types'; -import { IServiceContainer } from '../../../../client/ioc/types'; -import { EXTENSION_ROOT_DIR_FOR_TESTS, initialize } from '../../../initialize'; -import { traceLog } from '../../../../client/logging'; -import { PytestTestExecutionAdapter } from '../../../../client/testing/testController/pytest/pytestExecutionAdapter'; -import { UnittestTestDiscoveryAdapter } from '../../../../client/testing/testController/unittest/testDiscoveryAdapter'; -import { UnittestTestExecutionAdapter } from '../../../../client/testing/testController/unittest/testExecutionAdapter'; +import { PytestTestDiscoveryAdapter } from '../../../client/testing/testController/pytest/pytestDiscoveryAdapter'; +import { ITestResultResolver, ITestServer } from '../../../client/testing/testController/common/types'; +import { PythonTestServer } from '../../../client/testing/testController/common/server'; +import { IPythonExecutionFactory } from '../../../client/common/process/types'; +import { ITestDebugLauncher } from '../../../client/testing/common/types'; +import { IConfigurationService, ITestOutputChannel } from '../../../client/common/types'; +import { IServiceContainer } from '../../../client/ioc/types'; +import { EXTENSION_ROOT_DIR_FOR_TESTS, initialize } from '../../initialize'; +import { traceError, traceLog } from '../../../client/logging'; +import { PytestTestExecutionAdapter } from '../../../client/testing/testController/pytest/pytestExecutionAdapter'; +import { UnittestTestDiscoveryAdapter } from '../../../client/testing/testController/unittest/testDiscoveryAdapter'; +import { UnittestTestExecutionAdapter } from '../../../client/testing/testController/unittest/testExecutionAdapter'; suite('Functional Tests: test adapters', () => { let resultResolver: typeMoq.IMock; @@ -74,8 +74,12 @@ suite('Functional Tests: test adapters', () => { actualData = data; return Promise.resolve(); }); + + // set workspace to test workspace folder and set up settings + workspaceUri = Uri.parse(rootPathSmallWorkspace); configService.getSettings(workspaceUri).testing.unittestArgs = ['-s', '.', '-p', '*test*.py']; - // run pytest discovery + + // run unittest discovery const discoveryAdapter = new UnittestTestDiscoveryAdapter( pythonTestServer, configService, @@ -83,9 +87,6 @@ suite('Functional Tests: test adapters', () => { resultResolver.object, ); - // set workspace to test workspace folder - workspaceUri = Uri.parse(rootPathSmallWorkspace); - await discoveryAdapter.discoverTests(workspaceUri).finally(() => { // verification after discovery is complete resultResolver.verify( @@ -117,8 +118,10 @@ suite('Functional Tests: test adapters', () => { return Promise.resolve(); }); + // set settings to work for the given workspace + workspaceUri = Uri.parse(rootPathLargeWorkspace); configService.getSettings(workspaceUri).testing.unittestArgs = ['-s', '.', '-p', '*test*.py']; - // run pytest discovery + // run discovery const discoveryAdapter = new UnittestTestDiscoveryAdapter( pythonTestServer, configService, @@ -126,9 +129,6 @@ suite('Functional Tests: test adapters', () => { resultResolver.object, ); - // set workspace to test workspace folder - workspaceUri = Uri.parse(rootPathLargeWorkspace); - await discoveryAdapter.discoverTests(workspaceUri).finally(() => { // verification after discovery is complete resultResolver.verify( @@ -242,7 +242,7 @@ suite('Functional Tests: test adapters', () => { // set workspace to test workspace folder workspaceUri = Uri.parse(rootPathSmallWorkspace); configService.getSettings(workspaceUri).testing.unittestArgs = ['-s', '.', '-p', '*test*.py']; - // run pytest execution + // run execution const executionAdapter = new UnittestTestExecutionAdapter( pythonTestServer, configService, @@ -261,7 +261,7 @@ suite('Functional Tests: test adapters', () => { await executionAdapter .runTests(workspaceUri, ['test_simple.SimpleClass.test_simple_unit'], false, testRun.object) .finally(() => { - // verification after discovery is complete + // verification after execution is complete resultResolver.verify( (x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny()), typeMoq.Times.once(), @@ -269,9 +269,7 @@ suite('Functional Tests: test adapters', () => { // 1. Check the status is "success" assert.strictEqual(actualData.status, 'success', "Expected status to be 'success'"); - // 2. Confirm no errors - assert.strictEqual(actualData.error, null, "Expected no errors in 'error' field"); - // 3. Confirm tests are found + // 2. Confirm tests are found assert.ok(actualData.result, 'Expected results to be present'); }); }); @@ -280,22 +278,25 @@ suite('Functional Tests: test adapters', () => { resultResolver .setup((x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny())) .returns((data) => { + traceError(`resolveExecution ${data}`); + console.log(`resolveExecution ${data}`); traceLog(`resolveExecution ${data}`); // do the following asserts for each time resolveExecution is called, should be called once per test. - // 1. Check the status is "success" - assert.strictEqual(data.status, 'success', "Expected status to be 'success'"); - // 2. Confirm no errors - assert.strictEqual(data.error, null, "Expected no errors in 'error' field"); - // 3. Confirm tests are found + // 1. Check the status, can be subtest success or failure + assert( + data.status === 'subtest-success' || data.status === 'subtest-failure', + "Expected status to be 'subtest-success' or 'subtest-failure'", + ); + // 2. Confirm tests are found assert.ok(data.result, 'Expected results to be present'); return Promise.resolve(); }); // set workspace to test workspace folder - workspaceUri = Uri.parse(rootPathSmallWorkspace); + workspaceUri = Uri.parse(rootPathLargeWorkspace); configService.getSettings(workspaceUri).testing.unittestArgs = ['-s', '.', '-p', '*test*.py']; - // run pytest execution + // run unittest execution const executionAdapter = new UnittestTestExecutionAdapter( pythonTestServer, configService, @@ -311,14 +312,15 @@ suite('Functional Tests: test adapters', () => { onCancellationRequested: () => undefined, } as any), ); - // ['test_parameterized_subtest.NumbersTest.test_even']; - await executionAdapter.runTests(workspaceUri, [], false, testRun.object).finally(() => { - // verification after discovery is complete - resultResolver.verify( - (x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny()), - typeMoq.Times.exactly(200), - ); - }); + await executionAdapter + .runTests(workspaceUri, ['test_parameterized_subtest.NumbersTest.test_even'], false, testRun.object) + .finally(() => { + // verification after discovery is complete + resultResolver.verify( + (x) => x.resolveExecution(typeMoq.It.isAny(), typeMoq.It.isAny()), + typeMoq.Times.exactly(200), + ); + }); }); test('pytest execution adapter small workspace', async () => { // result resolver and saved data for assertions @@ -397,9 +399,8 @@ suite('Functional Tests: test adapters', () => { // generate list of test_ids const testIds: string[] = []; - for (let i = 0; i < 200; i = i + 1) { - const testId = `${rootPathLargeWorkspace}/test_parameterized.py::test_odd_even[${i}]`; + const testId = `${rootPathLargeWorkspace}/test_parameterized_subtest.py::test_odd_even[${i}]`; testIds.push(testId); } From 4f08c1a1165fef157b59763e47afc7cd970af4d9 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 11 Aug 2023 09:12:44 -0700 Subject: [PATCH 07/12] switch to exit not close --- .../testing/testController/pytest/pytestExecutionAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index 653de36a3e23..4a9a57b16fed 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -175,7 +175,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { this.outputChannel?.append(data); }); - result?.proc?.on('close', () => { + result?.proc?.on('exit', () => { deferredExec.resolve({ stdout: '', stderr: '' }); deferred.resolve(); disposeDataReceiver?.(this.testServer); From 2eb1261b5d7e93bc3f666231479fe141a9f67fc5 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 11 Aug 2023 09:15:22 -0700 Subject: [PATCH 08/12] remove json --- pythonFiles/tests/pytestadapter/test_execution.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pythonFiles/tests/pytestadapter/test_execution.py b/pythonFiles/tests/pytestadapter/test_execution.py index 30f1a7e439d9..07354b01709b 100644 --- a/pythonFiles/tests/pytestadapter/test_execution.py +++ b/pythonFiles/tests/pytestadapter/test_execution.py @@ -1,6 +1,5 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -import json import os import shutil From 08cf571ab9734936adbe07076ef88a03cdac521f Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 11 Aug 2023 10:41:53 -0700 Subject: [PATCH 09/12] rename, fix error on other test --- src/test/linters/lint.functional.test.ts | 5 ++--- src/test/testing/common/testingAdapter.test.ts | 5 +---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/test/linters/lint.functional.test.ts b/src/test/linters/lint.functional.test.ts index a3dc70b7c21e..e02e146cbef1 100644 --- a/src/test/linters/lint.functional.test.ts +++ b/src/test/linters/lint.functional.test.ts @@ -4,7 +4,6 @@ 'use strict'; import * as assert from 'assert'; -import * as childProcess from 'child_process'; import * as fs from 'fs-extra'; import * as os from 'os'; import * as path from 'path'; @@ -781,9 +780,9 @@ suite('Linting Functional Tests', () => { sinon.restore(); }); - const pythonPath = childProcess.execSync(`"${PYTHON_PATH}" -c "import sys;print(sys.executable)"`); + // const pythonPath = childProcess.execSync(`"${PYTHON_PATH}" -c "import sys;print(sys.executable)"`); - console.log(`Testing linter with python ${pythonPath}`); + // console.log(`Testing linter with python ${pythonPath}`); // These are integration tests that mock out everything except // the filesystem and process execution. diff --git a/src/test/testing/common/testingAdapter.test.ts b/src/test/testing/common/testingAdapter.test.ts index 4014e648420e..5c92c7cf3941 100644 --- a/src/test/testing/common/testingAdapter.test.ts +++ b/src/test/testing/common/testingAdapter.test.ts @@ -18,7 +18,7 @@ import { PytestTestExecutionAdapter } from '../../../client/testing/testControll import { UnittestTestDiscoveryAdapter } from '../../../client/testing/testController/unittest/testDiscoveryAdapter'; import { UnittestTestExecutionAdapter } from '../../../client/testing/testController/unittest/testExecutionAdapter'; -suite('Functional Tests: test adapters', () => { +suite('End to End Tests: test adapters', () => { let resultResolver: typeMoq.IMock; let pythonTestServer: ITestServer; let pythonExecFactory: IPythonExecutionFactory; @@ -40,9 +40,6 @@ suite('Functional Tests: test adapters', () => { 'largeWorkspace', ); suiteSetup(async () => { - // if (!IS_SMOKE_TEST) { - // this.skip(); - // } serviceContainer = (await initialize()).serviceContainer; }); From f0df531a89c29ae40f35b1e1f59c5f6e510ba505 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 11 Aug 2023 12:45:39 -0700 Subject: [PATCH 10/12] Revert "Consistent disposal of receivers across adapters (#21759)" This reverts commit b299ec97a74e12fa8f87425973739d19f955838c. --- .../pytest/pytestDiscoveryAdapter.ts | 16 ++++++-------- .../pytest/pytestExecutionAdapter.ts | 22 +++++-------------- .../unittest/testDiscoveryAdapter.ts | 9 +++----- .../unittest/testExecutionAdapter.ts | 13 ++++++----- 4 files changed, 23 insertions(+), 37 deletions(-) diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 44ab3746dde4..f8e3490e854f 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -33,30 +33,27 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { async discoverTests(uri: Uri, executionFactory?: IPythonExecutionFactory): Promise { const settings = this.configSettings.getSettings(uri); - const uuid = this.testServer.createUUID(uri.fsPath); const { pytestArgs } = settings.testing; traceVerbose(pytestArgs); - const dataReceivedDisposable = this.testServer.onDiscoveryDataReceived((e: DataReceivedEvent) => { + const disposable = this.testServer.onDiscoveryDataReceived((e: DataReceivedEvent) => { + // cancelation token ? this.resultResolver?.resolveDiscovery(JSON.parse(e.data)); }); - const disposeDataReceiver = function (testServer: ITestServer) { - testServer.deleteUUID(uuid); - dataReceivedDisposable.dispose(); - }; try { - await this.runPytestDiscovery(uri, uuid, executionFactory); + await this.runPytestDiscovery(uri, executionFactory); } finally { - disposeDataReceiver(this.testServer); + disposable.dispose(); } // this is only a placeholder to handle function overloading until rewrite is finished const discoveryPayload: DiscoveredTestPayload = { cwd: uri.fsPath, status: 'success' }; return discoveryPayload; } - async runPytestDiscovery(uri: Uri, uuid: string, executionFactory?: IPythonExecutionFactory): Promise { + async runPytestDiscovery(uri: Uri, executionFactory?: IPythonExecutionFactory): Promise { const deferred = createDeferred(); const relativePathToPytest = 'pythonFiles'; const fullPluginPath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); + const uuid = this.testServer.createUUID(uri.fsPath); const settings = this.configSettings.getSettings(uri); const { pytestArgs } = settings.testing; const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath; @@ -96,6 +93,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { }); result?.proc?.on('exit', () => { deferredExec.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 4a9a57b16fed..ffc2b705e1a3 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -43,28 +43,19 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { ): Promise { const uuid = this.testServer.createUUID(uri.fsPath); traceVerbose(uri, testIds, debugBool); - const dataReceivedDisposable = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { + const disposedDataReceived = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { if (runInstance) { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } }); - const disposeDataReceiver = function (testServer: ITestServer) { + const dispose = function (testServer: ITestServer) { testServer.deleteUUID(uuid); - dataReceivedDisposable.dispose(); + disposedDataReceived.dispose(); }; runInstance?.token.onCancellationRequested(() => { - disposeDataReceiver(this.testServer); + dispose(this.testServer); }); - await this.runTestsNew( - uri, - testIds, - uuid, - runInstance, - debugBool, - executionFactory, - debugLauncher, - disposeDataReceiver, - ); + await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, executionFactory, debugLauncher); // placeholder until after the rewrite is adopted // TODO: remove after adoption. @@ -84,7 +75,6 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { debugBool?: boolean, executionFactory?: IPythonExecutionFactory, debugLauncher?: ITestDebugLauncher, - disposeDataReceiver?: (testServer: ITestServer) => void, ): Promise { const deferred = createDeferred(); const relativePathToPytest = 'pythonFiles'; @@ -177,8 +167,8 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { result?.proc?.on('exit', () => { deferredExec.resolve({ stdout: '', stderr: '' }); + this.testServer.deleteUUID(uuid); deferred.resolve(); - disposeDataReceiver?.(this.testServer); }); await deferredExec.promise; } diff --git a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts index 1cbad7ef65ef..b49ac3dabd0e 100644 --- a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts @@ -43,16 +43,13 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { outChannel: this.outputChannel, }; - const dataReceivedDisposable = this.testServer.onDiscoveryDataReceived((e: DataReceivedEvent) => { + const disposable = this.testServer.onDiscoveryDataReceived((e: DataReceivedEvent) => { this.resultResolver?.resolveDiscovery(JSON.parse(e.data)); }); - const disposeDataReceiver = function (testServer: ITestServer) { - testServer.deleteUUID(uuid); - dataReceivedDisposable.dispose(); - }; await this.callSendCommand(options, () => { - disposeDataReceiver(this.testServer); + this.testServer.deleteUUID(uuid); + disposable.dispose(); }); // placeholder until after the rewrite is adopted // TODO: remove after adoption. diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index 9af9e593c246..4cd392f93a43 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -42,14 +42,14 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } }); - const disposeDataReceiver = function (testServer: ITestServer) { - testServer.deleteUUID(uuid); + const dispose = function () { disposedDataReceived.dispose(); }; runInstance?.token.onCancellationRequested(() => { - disposeDataReceiver(this.testServer); + this.testServer.deleteUUID(uuid); + dispose(); }); - await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, disposeDataReceiver); + await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, dispose); const executionPayload: ExecutionTestPayload = { cwd: uri.fsPath, status: 'success', error: '' }; return executionPayload; } @@ -60,7 +60,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { uuid: string, runInstance?: TestRun, debugBool?: boolean, - disposeDataReceiver?: (testServer: ITestServer) => void, + dispose?: () => void, ): Promise { const settings = this.configSettings.getSettings(uri); const { unittestArgs } = settings.testing; @@ -84,8 +84,9 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { const runTestIdsPort = await startTestIdServer(testIds); await this.testServer.sendCommand(options, runTestIdsPort.toString(), runInstance, () => { + this.testServer.deleteUUID(uuid); deferred.resolve(); - disposeDataReceiver?.(this.testServer); + dispose?.(); }); // placeholder until after the rewrite is adopted // TODO: remove after adoption. From ee594253e60a33e2c88b265cdefbbd4b11ffbb0e Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 14 Aug 2023 16:35:33 -0700 Subject: [PATCH 11/12] Revert "Revert "Consistent disposal of receivers across adapters (#21759)"" This reverts commit f0df531a89c29ae40f35b1e1f59c5f6e510ba505. --- .../pytest/pytestDiscoveryAdapter.ts | 16 ++++++++------ .../pytest/pytestExecutionAdapter.ts | 22 ++++++++++++++----- .../unittest/testDiscoveryAdapter.ts | 9 +++++--- .../unittest/testExecutionAdapter.ts | 13 +++++------ 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index f8e3490e854f..44ab3746dde4 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -33,27 +33,30 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { async discoverTests(uri: Uri, executionFactory?: IPythonExecutionFactory): Promise { const settings = this.configSettings.getSettings(uri); + const uuid = this.testServer.createUUID(uri.fsPath); const { pytestArgs } = settings.testing; traceVerbose(pytestArgs); - const disposable = this.testServer.onDiscoveryDataReceived((e: DataReceivedEvent) => { - // cancelation token ? + const dataReceivedDisposable = this.testServer.onDiscoveryDataReceived((e: DataReceivedEvent) => { this.resultResolver?.resolveDiscovery(JSON.parse(e.data)); }); + const disposeDataReceiver = function (testServer: ITestServer) { + testServer.deleteUUID(uuid); + dataReceivedDisposable.dispose(); + }; try { - await this.runPytestDiscovery(uri, executionFactory); + await this.runPytestDiscovery(uri, uuid, executionFactory); } finally { - disposable.dispose(); + disposeDataReceiver(this.testServer); } // this is only a placeholder to handle function overloading until rewrite is finished const discoveryPayload: DiscoveredTestPayload = { cwd: uri.fsPath, status: 'success' }; return discoveryPayload; } - async runPytestDiscovery(uri: Uri, executionFactory?: IPythonExecutionFactory): Promise { + async runPytestDiscovery(uri: Uri, uuid: string, executionFactory?: IPythonExecutionFactory): Promise { const deferred = createDeferred(); const relativePathToPytest = 'pythonFiles'; const fullPluginPath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); - const uuid = this.testServer.createUUID(uri.fsPath); const settings = this.configSettings.getSettings(uri); const { pytestArgs } = settings.testing; const cwd = settings.testing.cwd && settings.testing.cwd.length > 0 ? settings.testing.cwd : uri.fsPath; @@ -93,7 +96,6 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { }); result?.proc?.on('exit', () => { deferredExec.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 ffc2b705e1a3..4a9a57b16fed 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -43,19 +43,28 @@ 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 dataReceivedDisposable = this.testServer.onRunDataReceived((e: DataReceivedEvent) => { if (runInstance) { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } }); - const dispose = function (testServer: ITestServer) { + const disposeDataReceiver = function (testServer: ITestServer) { testServer.deleteUUID(uuid); - disposedDataReceived.dispose(); + dataReceivedDisposable.dispose(); }; runInstance?.token.onCancellationRequested(() => { - dispose(this.testServer); + disposeDataReceiver(this.testServer); }); - await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, executionFactory, debugLauncher); + await this.runTestsNew( + uri, + testIds, + uuid, + runInstance, + debugBool, + executionFactory, + debugLauncher, + disposeDataReceiver, + ); // placeholder until after the rewrite is adopted // TODO: remove after adoption. @@ -75,6 +84,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { debugBool?: boolean, executionFactory?: IPythonExecutionFactory, debugLauncher?: ITestDebugLauncher, + disposeDataReceiver?: (testServer: ITestServer) => void, ): Promise { const deferred = createDeferred(); const relativePathToPytest = 'pythonFiles'; @@ -167,8 +177,8 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { result?.proc?.on('exit', () => { deferredExec.resolve({ stdout: '', stderr: '' }); - this.testServer.deleteUUID(uuid); deferred.resolve(); + disposeDataReceiver?.(this.testServer); }); await deferredExec.promise; } diff --git a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts index b49ac3dabd0e..1cbad7ef65ef 100644 --- a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts @@ -43,13 +43,16 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { outChannel: this.outputChannel, }; - const disposable = this.testServer.onDiscoveryDataReceived((e: DataReceivedEvent) => { + const dataReceivedDisposable = this.testServer.onDiscoveryDataReceived((e: DataReceivedEvent) => { this.resultResolver?.resolveDiscovery(JSON.parse(e.data)); }); + const disposeDataReceiver = function (testServer: ITestServer) { + testServer.deleteUUID(uuid); + dataReceivedDisposable.dispose(); + }; await this.callSendCommand(options, () => { - this.testServer.deleteUUID(uuid); - disposable.dispose(); + disposeDataReceiver(this.testServer); }); // placeholder until after the rewrite is adopted // TODO: remove after adoption. diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index 4cd392f93a43..9af9e593c246 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -42,14 +42,14 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } }); - const dispose = function () { + const disposeDataReceiver = function (testServer: ITestServer) { + testServer.deleteUUID(uuid); disposedDataReceived.dispose(); }; runInstance?.token.onCancellationRequested(() => { - this.testServer.deleteUUID(uuid); - dispose(); + disposeDataReceiver(this.testServer); }); - await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, dispose); + await this.runTestsNew(uri, testIds, uuid, runInstance, debugBool, disposeDataReceiver); const executionPayload: ExecutionTestPayload = { cwd: uri.fsPath, status: 'success', error: '' }; return executionPayload; } @@ -60,7 +60,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { uuid: string, runInstance?: TestRun, debugBool?: boolean, - dispose?: () => void, + disposeDataReceiver?: (testServer: ITestServer) => void, ): Promise { const settings = this.configSettings.getSettings(uri); const { unittestArgs } = settings.testing; @@ -84,9 +84,8 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { const runTestIdsPort = await startTestIdServer(testIds); await this.testServer.sendCommand(options, runTestIdsPort.toString(), runInstance, () => { - this.testServer.deleteUUID(uuid); deferred.resolve(); - dispose?.(); + disposeDataReceiver?.(this.testServer); }); // placeholder until after the rewrite is adopted // TODO: remove after adoption. From b4c15f6dd5abb6b105b1065013a319f6f4813b2d Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 14 Aug 2023 16:36:55 -0700 Subject: [PATCH 12/12] remove comment --- src/test/linters/lint.functional.test.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/linters/lint.functional.test.ts b/src/test/linters/lint.functional.test.ts index e02e146cbef1..9887cbc5605a 100644 --- a/src/test/linters/lint.functional.test.ts +++ b/src/test/linters/lint.functional.test.ts @@ -779,11 +779,6 @@ suite('Linting Functional Tests', () => { teardown(() => { sinon.restore(); }); - - // const pythonPath = childProcess.execSync(`"${PYTHON_PATH}" -c "import sys;print(sys.executable)"`); - - // console.log(`Testing linter with python ${pythonPath}`); - // These are integration tests that mock out everything except // the filesystem and process execution.