From 2faab05dca3967cc5d85f07741247c546a17a5c8 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Wed, 24 Aug 2022 10:39:57 -0700 Subject: [PATCH 01/49] setup file structure --- pythonFiles/{unittestadapter => pytest_adapter}/__init__.py | 0 pythonFiles/pytest_adapter/pytest_discovery.py | 0 pythonFiles/pytest_adapter/pytest_execution.py | 0 pythonFiles/unittest_adapter/__init__.py | 2 ++ .../discovery.py => unittest_adapter/unittest_discovery.py} | 0 .../execution.py => unittest_adapter/unittest_execution.py} | 0 pythonFiles/{unittestadapter => unittest_adapter}/utils.py | 0 .../testing/testController/pytest/pytestDiscoveryAdapter.ts | 0 .../testing/testController/pytest/pytestExecutionAdapter.ts | 0 .../{testDiscoveryAdapter.ts => unittestDiscoveryAdapter.ts} | 0 .../{testExecutionAdapter.ts => unittestExecutionAdapter.ts} | 0 11 files changed, 2 insertions(+) rename pythonFiles/{unittestadapter => pytest_adapter}/__init__.py (100%) create mode 100644 pythonFiles/pytest_adapter/pytest_discovery.py create mode 100644 pythonFiles/pytest_adapter/pytest_execution.py create mode 100644 pythonFiles/unittest_adapter/__init__.py rename pythonFiles/{unittestadapter/discovery.py => unittest_adapter/unittest_discovery.py} (100%) rename pythonFiles/{unittestadapter/execution.py => unittest_adapter/unittest_execution.py} (100%) rename pythonFiles/{unittestadapter => unittest_adapter}/utils.py (100%) create mode 100644 src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts create mode 100644 src/client/testing/testController/pytest/pytestExecutionAdapter.ts rename src/client/testing/testController/unittest/{testDiscoveryAdapter.ts => unittestDiscoveryAdapter.ts} (100%) rename src/client/testing/testController/unittest/{testExecutionAdapter.ts => unittestExecutionAdapter.ts} (100%) diff --git a/pythonFiles/unittestadapter/__init__.py b/pythonFiles/pytest_adapter/__init__.py similarity index 100% rename from pythonFiles/unittestadapter/__init__.py rename to pythonFiles/pytest_adapter/__init__.py diff --git a/pythonFiles/pytest_adapter/pytest_discovery.py b/pythonFiles/pytest_adapter/pytest_discovery.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pythonFiles/pytest_adapter/pytest_execution.py b/pythonFiles/pytest_adapter/pytest_execution.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/pythonFiles/unittest_adapter/__init__.py b/pythonFiles/unittest_adapter/__init__.py new file mode 100644 index 000000000000..5b7f7a925cc0 --- /dev/null +++ b/pythonFiles/unittest_adapter/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. diff --git a/pythonFiles/unittestadapter/discovery.py b/pythonFiles/unittest_adapter/unittest_discovery.py similarity index 100% rename from pythonFiles/unittestadapter/discovery.py rename to pythonFiles/unittest_adapter/unittest_discovery.py diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittest_adapter/unittest_execution.py similarity index 100% rename from pythonFiles/unittestadapter/execution.py rename to pythonFiles/unittest_adapter/unittest_execution.py diff --git a/pythonFiles/unittestadapter/utils.py b/pythonFiles/unittest_adapter/utils.py similarity index 100% rename from pythonFiles/unittestadapter/utils.py rename to pythonFiles/unittest_adapter/utils.py diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/client/testing/testController/unittest/testDiscoveryAdapter.ts b/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts similarity index 100% rename from src/client/testing/testController/unittest/testDiscoveryAdapter.ts rename to src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/unittestExecutionAdapter.ts similarity index 100% rename from src/client/testing/testController/unittest/testExecutionAdapter.ts rename to src/client/testing/testController/unittest/unittestExecutionAdapter.ts From 68a6ff8e65781ee449d75dc1b639d4c91dd97812 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Wed, 24 Aug 2022 13:49:50 -0700 Subject: [PATCH 02/49] reverse dummy commit --- pythonFiles/unittest_adapter/unittest_discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonFiles/unittest_adapter/unittest_discovery.py b/pythonFiles/unittest_adapter/unittest_discovery.py index 0be09e986ca8..5f22a4cc4b92 100644 --- a/pythonFiles/unittest_adapter/unittest_discovery.py +++ b/pythonFiles/unittest_adapter/unittest_discovery.py @@ -17,7 +17,7 @@ from testing_tools import socket_manager # If I use from utils then there will be an import error in test_discovery.py. -from unittestadapter.utils import TestNode, build_test_tree, parse_unittest_args +from unittest_adapter.utils import TestNode, build_test_tree, parse_unittest_args # Add the lib path to sys.path to find the typing_extensions module. sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) From ac8b269db476c69c4d8598b7166bd77e30e01691 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 25 Aug 2022 11:03:38 -0700 Subject: [PATCH 03/49] stash to isolate error --- .../unittest_adapter/unittest_execution.py | 2 +- .../testing/testController/controller.ts | 55 +++++++++----- .../pytest/pytestDiscoveryAdapter.ts | 71 ++++++++++++++++++ .../pytest/pytestExecutionAdapter.ts | 73 +++++++++++++++++++ .../unittest/unittestDiscoveryAdapter.ts | 2 +- .../testDiscoveryAdapter.unit.test.ts | 2 +- .../workspaceTestAdapter.unit.test.ts | 4 +- 7 files changed, 184 insertions(+), 25 deletions(-) diff --git a/pythonFiles/unittest_adapter/unittest_execution.py b/pythonFiles/unittest_adapter/unittest_execution.py index a016ff1af9ec..935e7fbeae57 100644 --- a/pythonFiles/unittest_adapter/unittest_execution.py +++ b/pythonFiles/unittest_adapter/unittest_execution.py @@ -18,7 +18,7 @@ sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) from testing_tools import socket_manager from typing_extensions import NotRequired -from unittestadapter.utils import parse_unittest_args +from unittest_adapter.utils import parse_unittest_args DEFAULT_PORT = "45454" diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index fafdd3fafe7e..3d9a8fa03fd1 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -38,9 +38,12 @@ import { TestRefreshOptions, ITestExecutionAdapter, } from './common/types'; -import { UnittestTestDiscoveryAdapter } from './unittest/testDiscoveryAdapter'; +// TODO: create pytest and add to import +import { UnittestTestDiscoveryAdapter } from './unittest/unittestDiscoveryAdapter'; +import { UnittestTestExecutionAdapter } from './unittest/unittestExecutionAdapter'; +import { PytestTestDiscoveryAdapter } from './pytest/pytestDiscoveryAdapter'; +import { PytestTestExecutionAdapter } from './pytest/pytestExecutionAdapter'; import { WorkspaceTestAdapter } from './workspaceTestAdapter'; -import { UnittestTestExecutionAdapter } from './unittest/testExecutionAdapter'; import { ITestDebugLauncher } from '../common/types'; // Types gymnastics to make sure that sendTriggerTelemetry only accepts the correct types. @@ -156,14 +159,19 @@ export class PythonTestController implements ITestController, IExtensionSingleAc let discoveryAdapter: ITestDiscoveryAdapter; let executionAdapter: ITestExecutionAdapter; let testProvider: TestProvider; - if (settings.testing.unittestEnabled) { + if (settings.testing.pytestEnabled) { + console.log('settings.testing.pytestEnabled = true'); + discoveryAdapter = new PytestTestDiscoveryAdapter(this.pythonTestServer, { ...this.configSettings }); // what is the ... for + executionAdapter = new PytestTestExecutionAdapter(this.pythonTestServer, this.configSettings); + testProvider = PYTEST_PROVIDER; + } else if (settings.testing.unittestEnabled) { + console.log('settings.testing.unittestEnabled = true'); discoveryAdapter = new UnittestTestDiscoveryAdapter(this.pythonTestServer, this.configSettings); executionAdapter = new UnittestTestExecutionAdapter(this.pythonTestServer, this.configSettings); testProvider = UNITTEST_PROVIDER; } else { - // TODO: PYTEST DISCOVERY ADAPTER - // this is a placeholder for now - discoveryAdapter = new UnittestTestDiscoveryAdapter(this.pythonTestServer, { ...this.configSettings }); + // this would be an error because neither is enabled? + discoveryAdapter = new UnittestTestDiscoveryAdapter(this.pythonTestServer, this.configSettings); executionAdapter = new UnittestTestExecutionAdapter(this.pythonTestServer, this.configSettings); testProvider = PYTEST_PROVIDER; } @@ -227,27 +235,29 @@ export class PythonTestController implements ITestController, IExtensionSingleAc if (settings.testing.pytestEnabled) { traceVerbose(`Testing: Refreshing test data for ${uri.fsPath}`); + // pytest needs to be added in the new design + // Ensure we send test telemetry if it gets disabled again this.sendTestDisabledTelemetry = true; await this.pytest.refreshTestData(this.testController, uri, this.refreshCancellation.token); } else if (settings.testing.unittestEnabled) { // TODO: Use new test discovery mechanism - // traceVerbose(`Testing: Refreshing test data for ${uri.fsPath}`); - // const workspace = this.workspaceService.getWorkspaceFolder(uri); - // console.warn(`Discover tests for workspace name: ${workspace?.name} - uri: ${uri.fsPath}`); - // const testAdapter = - // this.testAdapters.get(uri) || (this.testAdapters.values().next().value as WorkspaceTestAdapter); - // testAdapter.discoverTests( - // this.testController, - // this.refreshCancellation.token, - // this.testAdapters.size > 1, - // this.workspaceService.workspaceFile?.fsPath, - // ); - // // Ensure we send test telemetry if it gets disabled again - // this.sendTestDisabledTelemetry = true; + traceVerbose(`Testing: Refreshing test data for ${uri.fsPath}`); + const workspace = this.workspaceService.getWorkspaceFolder(uri); + console.warn(`Discover tests for workspace name: ${workspace?.name} - uri: ${uri.fsPath}`); + const testAdapter = + this.testAdapters.get(uri) || (this.testAdapters.values().next().value as WorkspaceTestAdapter); + testAdapter.discoverTests( + this.testController, + this.refreshCancellation.token, + this.testAdapters.size > 1, + this.workspaceService.workspaceFile?.fsPath, + ); + // Ensure we send test telemetry if it gets disabled again + this.sendTestDisabledTelemetry = true; // comment below 229 to run the new way and uncomment above 212 ~ 227 - await this.unittest.refreshTestData(this.testController, uri, this.refreshCancellation.token); + // await this.unittest.refreshTestData(this.testController, uri, this.refreshCancellation.token); } else { if (this.sendTestDisabledTelemetry) { this.sendTestDisabledTelemetry = false; @@ -293,6 +303,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc const settings = this.configSettings.getSettings(item.uri); if (settings.testing.pytestEnabled) { return this.pytest.resolveChildren(this.testController, item, this.refreshCancellation.token); + // ** check resolve children functionality } if (settings.testing.unittestEnabled) { return this.unittest.resolveChildren(this.testController, item, this.refreshCancellation.token); @@ -360,9 +371,11 @@ export class PythonTestController implements ITestController, IExtensionSingleAc if (testItems.length > 0) { if (settings.testing.pytestEnabled) { sendTelemetryEvent(EventName.UNITTEST_RUN, undefined, { + // seems like this telemetry is named incorrectly? tool: 'pytest', debugging: request.profile?.kind === TestRunProfileKind.Debug, }); + // ** update this to reflect the nwe execution style before return this.pytest.runTests( { includes: testItems, @@ -492,6 +505,8 @@ export class PythonTestController implements ITestController, IExtensionSingleAc ); } + // ** not sure about the telemetry + /** * Send UNITTEST_DISCOVERY_TRIGGER telemetry event only once per trigger type. * diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index e69de29bb2d1..3a3f8fb9a51b 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import * as path from 'path'; +import { Uri } from 'vscode'; +import { IConfigurationService } from '../../../common/types'; +import { createDeferred, Deferred } from '../../../common/utils/async'; +import { EXTENSION_ROOT_DIR } from '../../../constants'; +import { + DataReceivedEvent, + DiscoveredTestPayload, + ITestDiscoveryAdapter, + ITestServer, + TestCommandOptions, + TestDiscoveryCommand, +} from '../common/types'; + +/** + */ +export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { + private deferred: Deferred | undefined; + + private cwd: string | undefined; + + constructor(public testServer: ITestServer, public configSettings: IConfigurationService) { + testServer.onDataReceived(this.onDataReceivedHandler, this); + } + + public onDataReceivedHandler({ cwd, data }: DataReceivedEvent): void { + if (this.deferred && cwd === this.cwd) { + const testData: DiscoveredTestPayload = JSON.parse(data); + + this.deferred.resolve(testData); + this.deferred = undefined; + } + } + + public async discoverTests(uri: Uri): Promise { + if (!this.deferred) { + const settings = this.configSettings.getSettings(uri); + const { unittestArgs } = settings.testing; + + const command = buildDiscoveryCommand(unittestArgs); + + this.cwd = uri.fsPath; + + const options: TestCommandOptions = { + workspaceFolder: uri, + command, + cwd: this.cwd, + }; + + this.deferred = createDeferred(); + + // Send the test command to the server. + // The server will fire an onDataReceived event once it gets a response. + this.testServer.sendCommand(options); + } + + return this.deferred.promise; + } +} + +function buildDiscoveryCommand(args: string[]): TestDiscoveryCommand { + const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'discovery.py'); + + return { + script: discoveryScript, + args: ['--udiscovery', ...args], + }; +} diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index e69de29bb2d1..35d62c50e774 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import * as path from 'path'; +import { Uri } from 'vscode'; +import { IConfigurationService } from '../../../common/types'; +import { createDeferred, Deferred } from '../../../common/utils/async'; +import { EXTENSION_ROOT_DIR } from '../../../constants'; +import { + DataReceivedEvent, + ExecutionTestPayload, + ITestExecutionAdapter, + ITestServer, + TestCommandOptions, + TestExecutionCommand, +} from '../common/types'; + +/** + * Wrapper Class for unittest test execution. This is where we call `runTestCommand`? + */ + +export class PytestTestExecutionAdapter implements ITestExecutionAdapter { + private deferred: Deferred | undefined; + + private cwd: string | undefined; + + constructor(public testServer: ITestServer, public configSettings: IConfigurationService) { + testServer.onDataReceived(this.onDataReceivedHandler, this); + } + + public onDataReceivedHandler({ cwd, data }: DataReceivedEvent): void { + if (this.deferred && cwd === this.cwd) { + const testData: ExecutionTestPayload = JSON.parse(data); + + this.deferred.resolve(testData); + this.deferred = undefined; + } + } + + public async runTests(uri: Uri, testIds: string[], debugBool?: boolean): Promise { + if (!this.deferred) { + const settings = this.configSettings.getSettings(uri); + const { unittestArgs } = settings.testing; + + const command = buildExecutionCommand(unittestArgs); + this.cwd = uri.fsPath; + + const options: TestCommandOptions = { + workspaceFolder: uri, + command, + cwd: this.cwd, + debugBool, + testIds, + }; + + this.deferred = createDeferred(); + + // send test command to server + // server fire onDataReceived event once it gets response + this.testServer.sendCommand(options); + } + return this.deferred.promise; + } +} + +function buildExecutionCommand(args: string[]): TestExecutionCommand { + const executionScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'execution.py'); + + return { + script: executionScript, + args: ['--udiscovery', ...args], + }; +} diff --git a/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts b/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts index f0a5a957807c..13ded842a8b6 100644 --- a/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts @@ -16,7 +16,7 @@ import { } from '../common/types'; /** - * Wrapper class for unittest test discovery. This is where we call `runTestCommand`. + * Wrapper class for unittest test discovery. This is where we call `runTestCommand`. #this seems incorrectly copied */ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { private deferred: Deferred | undefined; diff --git a/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts b/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts index cee4353db09a..9f8dbeabb4b3 100644 --- a/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts +++ b/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts @@ -7,7 +7,7 @@ import { Uri } from 'vscode'; import { IConfigurationService } from '../../../../client/common/types'; import { EXTENSION_ROOT_DIR } from '../../../../client/constants'; import { ITestServer, TestCommandOptions } from '../../../../client/testing/testController/common/types'; -import { UnittestTestDiscoveryAdapter } from '../../../../client/testing/testController/unittest/testDiscoveryAdapter'; +import { UnittestTestDiscoveryAdapter } from '../../../../client/testing/testController/unittest/unittestDiscoveryAdapter'; suite('Unittest test discovery adapter', () => { let stubConfigSettings: IConfigurationService; diff --git a/src/test/testing/testController/workspaceTestAdapter.unit.test.ts b/src/test/testing/testController/workspaceTestAdapter.unit.test.ts index b6be8d6081de..8d875ef60f6c 100644 --- a/src/test/testing/testController/workspaceTestAdapter.unit.test.ts +++ b/src/test/testing/testController/workspaceTestAdapter.unit.test.ts @@ -6,8 +6,8 @@ import * as sinon from 'sinon'; import { TestController, TestItem, Uri } from 'vscode'; import { IConfigurationService } from '../../../client/common/types'; -import { UnittestTestDiscoveryAdapter } from '../../../client/testing/testController/unittest/testDiscoveryAdapter'; -import { UnittestTestExecutionAdapter } from '../../../client/testing/testController/unittest/testExecutionAdapter'; // 7/7 +import { UnittestTestDiscoveryAdapter } from '../../../client/testing/testController/unittest/unittestDiscoveryAdapter'; +import { UnittestTestExecutionAdapter } from '../../../client/testing/testController/unittest/unittestExecutionAdapter'; // 7/7 import { WorkspaceTestAdapter } from '../../../client/testing/testController/workspaceTestAdapter'; import * as Telemetry from '../../../client/telemetry'; import { EventName } from '../../../client/telemetry/constants'; From 48cfa47cf784d9e3462c936031d3bac80d5fb85f Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 7 Sep 2022 14:55:10 -0700 Subject: [PATCH 04/49] fixing merge issues --- .vscode/settings.json | 7 +- .../build/lib/pytest_vscode_integration.py | 21 ++ .../PKG-INFO | 104 ++++++++ .../SOURCES.txt | 11 + .../dependency_links.txt | 1 + .../entry_points.txt | 2 + .../requires.txt | 1 + .../top_level.txt | 1 + pythonFiles/pytest_adapter/conftest.py | 27 +++ .../pytest_adapter/pytest_discovery.py | 144 +++++++++++ pythonFiles/pytest_adapter/pytest_utils.py | 228 ++++++++++++++++++ .../unittest_adapter/unittest_discovery.py | 11 + .../testing/testController/common/server.ts | 50 +++- .../testing/testController/controller.ts | 27 ++- .../pytest/pytestDiscoveryAdapter.ts | 6 +- .../unittest/unittestDiscoveryAdapter.ts | 2 +- .../unittest/unittestExecutionAdapter.ts | 2 +- 17 files changed, 633 insertions(+), 12 deletions(-) create mode 100644 pythonFiles/pytest-vscode-integration/build/lib/pytest_vscode_integration.py create mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/PKG-INFO create mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt create mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/dependency_links.txt create mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/entry_points.txt create mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/requires.txt create mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/top_level.txt create mode 100644 pythonFiles/pytest_adapter/conftest.py create mode 100644 pythonFiles/pytest_adapter/pytest_utils.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 174a850c901e..b46b2e8e40b6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -65,5 +65,10 @@ "--max-line-length=88" ], "typescript.preferences.importModuleSpecifier": "relative", - "debug.javascript.usePreview": false + "debug.javascript.usePreview": false, + "python.testing.pytestArgs": [ + "." + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true } diff --git a/pythonFiles/pytest-vscode-integration/build/lib/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/build/lib/pytest_vscode_integration.py new file mode 100644 index 000000000000..692d49fb0258 --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/build/lib/pytest_vscode_integration.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +import pytest + + +def pytest_addoption(parser): + group = parser.getgroup('vscode-integration') + group.addoption( + '--foo', + action='store', + dest='dest_foo', + default='2022', + help='Set the value for the fixture "bar".' + ) + + parser.addini('HELLO', 'Dummy pytest.ini setting') + + +@pytest.fixture +def bar(request): + return request.config.option.dest_foo diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/PKG-INFO b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/PKG-INFO new file mode 100644 index 000000000000..172aed3ab5bb --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/PKG-INFO @@ -0,0 +1,104 @@ +Metadata-Version: 2.1 +Name: pytest-vscode-integration +Version: 0.1.0 +Summary: used to surface pytest functionality to ports for vscode integration +Home-page: https://github.com/eleanorboyd/pytest-vscode-integration +Author: Eleanor Boyd +Author-email: eleanorboyd@microsoft.com +Maintainer: Eleanor Boyd +Maintainer-email: eleanorboyd@microsoft.com +License: MIT +Classifier: Development Status :: 4 - Beta +Classifier: Framework :: Pytest +Classifier: Intended Audience :: Developers +Classifier: Topic :: Software Development :: Testing +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Operating System :: OS Independent +Classifier: License :: OSI Approved :: MIT License +Requires-Python: >=3.5 +License-File: LICENSE + +========================= +pytest-vscode-integration +========================= + +.. image:: https://img.shields.io/pypi/v/pytest-vscode-integration.svg + :target: https://pypi.org/project/pytest-vscode-integration + :alt: PyPI version + +.. image:: https://img.shields.io/pypi/pyversions/pytest-vscode-integration.svg + :target: https://pypi.org/project/pytest-vscode-integration + :alt: Python versions + +.. image:: https://ci.appveyor.com/api/projects/status/github/eleanorboyd/pytest-vscode-integration?branch=master + :target: https://ci.appveyor.com/project/eleanorboyd/pytest-vscode-integration/branch/master + :alt: See Build Status on AppVeyor + +used to surface pytest functionality to ports for vscode integration + +---- + +This `pytest`_ plugin was generated with `Cookiecutter`_ along with `@hackebrot`_'s `cookiecutter-pytest-plugin`_ template. + + +Features +-------- + +* TODO + + +Requirements +------------ + +* TODO + + +Installation +------------ + +You can install "pytest-vscode-integration" via `pip`_ from `PyPI`_:: + + $ pip install pytest-vscode-integration + + +Usage +----- + +* TODO + +Contributing +------------ +Contributions are very welcome. Tests can be run with `tox`_, please ensure +the coverage at least stays the same before you submit a pull request. + +License +------- + +Distributed under the terms of the `MIT`_ license, "pytest-vscode-integration" is free and open source software + + +Issues +------ + +If you encounter any problems, please `file an issue`_ along with a detailed description. + +.. _`Cookiecutter`: https://github.com/audreyr/cookiecutter +.. _`@hackebrot`: https://github.com/hackebrot +.. _`MIT`: http://opensource.org/licenses/MIT +.. _`BSD-3`: http://opensource.org/licenses/BSD-3-Clause +.. _`GNU GPL v3.0`: http://www.gnu.org/licenses/gpl-3.0.txt +.. _`Apache Software License 2.0`: http://www.apache.org/licenses/LICENSE-2.0 +.. _`cookiecutter-pytest-plugin`: https://github.com/pytest-dev/cookiecutter-pytest-plugin +.. _`file an issue`: https://github.com/eleanorboyd/pytest-vscode-integration/issues +.. _`pytest`: https://github.com/pytest-dev/pytest +.. _`tox`: https://tox.readthedocs.io/en/latest/ +.. _`pip`: https://pypi.org/project/pip/ +.. _`PyPI`: https://pypi.org/project diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt new file mode 100644 index 000000000000..3b06f2a2dcce --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt @@ -0,0 +1,11 @@ +LICENSE +MANIFEST.in +README.rst +pytest_vscode_integration.py +setup.py +pytest_vscode_integration.egg-info/PKG-INFO +pytest_vscode_integration.egg-info/SOURCES.txt +pytest_vscode_integration.egg-info/dependency_links.txt +pytest_vscode_integration.egg-info/entry_points.txt +pytest_vscode_integration.egg-info/requires.txt +pytest_vscode_integration.egg-info/top_level.txt \ No newline at end of file diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/dependency_links.txt b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/dependency_links.txt new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/entry_points.txt b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/entry_points.txt new file mode 100644 index 000000000000..7df59de8857f --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[pytest11] +vscode-integration = pytest_vscode_integration diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/requires.txt b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/requires.txt new file mode 100644 index 000000000000..a1fa3684923e --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/requires.txt @@ -0,0 +1 @@ +pytest>=3.5.0 diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/top_level.txt b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/top_level.txt new file mode 100644 index 000000000000..ea1bb3c56250 --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/top_level.txt @@ -0,0 +1 @@ +pytest_vscode_integration diff --git a/pythonFiles/pytest_adapter/conftest.py b/pythonFiles/pytest_adapter/conftest.py new file mode 100644 index 000000000000..7f9754b50a85 --- /dev/null +++ b/pythonFiles/pytest_adapter/conftest.py @@ -0,0 +1,27 @@ +import sys + +import pytest + +sys.path.append( + "/Users/eleanorboyd/.vscode/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python" +) # +import debugpy + +debugpy.connect(5678) + + +@pytest.hookimpl() +def pytest_sessionstart(session): + print("hello") + + +def pytest_collection_finish(session): + print("end collection") + for item in session.items: + print("**") + parentCur = item.parent + path = str(item.name) + "> " + while parentCur != None: + path += str(parentCur.name) + "> " + parentCur = parentCur.parent + print("P:", path) diff --git a/pythonFiles/pytest_adapter/pytest_discovery.py b/pythonFiles/pytest_adapter/pytest_discovery.py index e69de29bb2d1..34e6a2945cde 100644 --- a/pythonFiles/pytest_adapter/pytest_discovery.py +++ b/pythonFiles/pytest_adapter/pytest_discovery.py @@ -0,0 +1,144 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import argparse +import json +import logging +import os +import pathlib +import sys +import traceback +from typing import List, Literal, Optional, Tuple, TypedDict, Union + +import pytest + +sys.path.append( + "/Users/eleanorboyd/.vscode/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python" +) # +import debugpy + +debugpy.connect(5678) + +# Add the path to pythonFiles to sys.path to find testing_tools.socket_manager. +PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, PYTHON_FILES) + +from pytest_utils import TestNode, parse_unittest_args +from testing_tools import socket_manager + +# Add the lib path to sys.path to find the typing_extensions module. +sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) + +from typing_extensions import NotRequired + +DEFAULT_PORT = "45454" + + +def parse_discovery_cli_args(args: List[str]) -> Tuple[int, Union[str, None]]: + """Parse command-line arguments that should be processed by the script. + + So far this includes the port number that it needs to connect to, and the uuid passed by the TS side. + The port is passed to the discovery.py script when it is executed, and + defaults to DEFAULT_PORT if it can't be parsed. + The uuid should be passed to the discovery.py script when it is executed, and defaults to None if it can't be parsed. + If the arguments appear several times, the value returned by parse_cli_args will be the value of the last argument. + """ + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument("--port", default=DEFAULT_PORT) + arg_parser.add_argument("--uuid") + arg_parser.add_argument("--udiscovery") + parsed_args, _ = arg_parser.parse_known_args(args) + + return int(parsed_args.port), parsed_args.uuid, parsed_args.udiscovery + + +class PayloadDict(TypedDict): + cwd: str + status: Literal["success", "error"] + tests: NotRequired[TestNode] + errors: NotRequired[List[str]] + + +def discover_tests(start_dir: str) -> PayloadDict: + """Returns a dictionary containing details of the discovered tests. + + The returned dict has the following keys: + + - cwd: Absolute path to the test start directory; + - uuid: UUID sent by the caller of the Python script, that needs to be sent back as an integrity check; + - status: Test discovery status, can be "success" or "error"; + - tests: Discoverered tests if any, not present otherwise. Note that the status can be "error" but the payload can still contain tests; + - errors: Discovery errors if any, not present otherwise. + + Payload format for a successful discovery: + { + "status": "success", + "cwd": , + "tests": + } + + Payload format for a successful discovery with no tests: + { + "status": "success", + "cwd": , + } + + Payload format when there are errors: + { + "cwd": + "errors": [list of errors] + "status": "error", + } + """ + cwd = os.path.abspath(start_dir) + payload: PayloadDict = {"cwd": cwd, "status": "success"} + tests = None + errors: List[str] = [] + + try: + # test loading + tests = "" + retcode = pytest.main(["--collect-only", "-q"]) + print("dine") + + except Exception: + errors.append(traceback.format_exc()) + + if tests is not None: + payload["tests"] = tests + + if len(errors): + payload["status"] = "error" + payload["errors"] = errors + + return payload + + +if __name__ == "__main__": + # Get unittest discovery arguments. + argv = sys.argv[1:] + index = argv.index("--udiscovery") + + # start_dir, pattern, top_level_dir = parse_unittest_args(argv[index + 1 :]) + # logging.debug( + # "start_dir, pattern, top_level_dir", start_dir, pattern, top_level_dir + # ) + # logging.debug("hi") + + # Perform test discovery. + port, uuid, start_dir = parse_discovery_cli_args(argv) + + payload = discover_tests(start_dir) + + # Build the request data (it has to be a POST request or the Node side will not process it), and send it. + addr = ("localhost", port) + with socket_manager.SocketManager(addr) as s: + data = json.dumps(payload) + request = f"""POST / HTTP/1.1 +Host: localhost:{port} +Content-Length: {len(data)} +Content-Type: application/json +Request-uuid: {uuid} + +{data}""" + result = s.socket.sendall(request.encode("utf-8")) # type: ignore diff --git a/pythonFiles/pytest_adapter/pytest_utils.py b/pythonFiles/pytest_adapter/pytest_utils.py new file mode 100644 index 000000000000..568ff30ee92d --- /dev/null +++ b/pythonFiles/pytest_adapter/pytest_utils.py @@ -0,0 +1,228 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import argparse +import enum +import inspect +import os +import pathlib +import unittest +from typing import List, Tuple, TypedDict, Union + +# Types + + +# Inherit from str so it's JSON serializable. +class TestNodeTypeEnum(str, enum.Enum): + class_ = "class" + file = "file" + folder = "folder" + test = "test" + + +class TestData(TypedDict): + name: str + path: str + type_: TestNodeTypeEnum + id_: str + + +class TestItem(TestData): + lineno: str + runID: str + + +class TestNode(TestData): + children: "List[TestNode | TestItem]" + + +# Helper functions for data retrieval. + + +def get_test_case(suite): + """Iterate through a unittest test suite and return all test cases.""" + for test in suite: + if isinstance(test, unittest.TestCase): + yield test + else: + for test_case in get_test_case(test): + yield test_case + + +def get_source_line(obj) -> str: + """Get the line number of a test case start line.""" + try: + sourcelines, lineno = inspect.getsourcelines(obj) + except: + try: + # tornado-specific, see https://github.com/microsoft/vscode-python/issues/17285. + sourcelines, lineno = inspect.getsourcelines(obj.orig_method) + except: + return "*" + + # Return the line number of the first line of the test case definition. + for i, v in enumerate(sourcelines): + if v.strip().startswith(("def", "async def")): + return str(lineno + i) + + return "*" + + +# Helper functions for test tree building. + + +def build_test_node(path: str, name: str, type_: TestNodeTypeEnum) -> TestNode: + """Build a test node with no children. A test node can be a folder, a file or a class.""" + ## figure out if we are folder, file, or class + id_gen = path + if type_ == TestNodeTypeEnum.folder or type_ == TestNodeTypeEnum.file: + id_gen = path + else: + # means we have to build test node for class + id_gen = path + "\\" + name + + return {"path": path, "name": name, "type_": type_, "children": [], "id_": id_gen} + + +def get_child_node( + name: str, path: str, type_: TestNodeTypeEnum, root: TestNode +) -> TestNode: + """Find a child node in a test tree given its name and type. If the node doesn't exist, create it.""" + try: + result = next( + node + for node in root["children"] + if node["name"] == name and node["type_"] == type_ + ) + except StopIteration: + result = build_test_node(path, name, type_) + root["children"].append(result) + + return result # type:ignore + + +def build_test_tree( + suite: unittest.TestSuite, test_directory: str +) -> Tuple[Union[TestNode, None], List[str]]: + """Build a test tree from a unittest test suite. + + This function returns the test tree, and any errors found by unittest. + If no tests were discovered, return `None` and a list of errors (if any). + + Test tree structure: + { + "path": , + "type": "folder", + "name": , + "children": [ + { files and folders } + ... + { + "path": , + "name": filename.py, + "type_": "file", + "children": [ + { + "path": , + "name": , + "type_": "class", + "children": [ + { + "path": , + "name": , + "type_": "test", + "lineno": + "id_": , + } + ], + "id_": + } + ], + "id_": + } + ], + "id_": + } + """ + errors = [] + directory_path = pathlib.PurePath(test_directory) + root = build_test_node(test_directory, directory_path.name, TestNodeTypeEnum.folder) + + for test_case in get_test_case(suite): + test_id = test_case.id() + if test_id.startswith("unittest.loader._FailedTest"): + errors.append(str(test_case._exception)) # type: ignore + else: + # Get the static test path components: filename, class name and function name. + components = test_id.split(".") + *folders, filename, class_name, function_name = components + py_filename = f"{filename}.py" + + current_node = root + + # Find/build nodes for the intermediate folders in the test path. + for folder in folders: + current_node = get_child_node( + folder, + os.fsdecode(pathlib.PurePath(current_node["path"], folder)), + TestNodeTypeEnum.folder, + current_node, + ) + + # Find/build file node. + path_components = [test_directory] + folders + [py_filename] + file_path = os.fsdecode(pathlib.PurePath("/".join(path_components))) + current_node = get_child_node( + py_filename, file_path, TestNodeTypeEnum.file, current_node + ) + + # Find/build class node. + current_node = get_child_node( + class_name, file_path, TestNodeTypeEnum.class_, current_node + ) + + # Get test line number. + test_method = getattr(test_case, test_case._testMethodName) + lineno = get_source_line(test_method) + + # Add test node. + test_node: TestItem = { + "name": function_name, + "path": file_path, + "lineno": lineno, + "type_": TestNodeTypeEnum.test, + "id_": file_path + "\\" + class_name + "\\" + function_name, + "runID": test_id, + } # concatenate class name and function test name + current_node["children"].append(test_node) + + if not root["children"]: + root = None + + return root, errors + + +def parse_unittest_args(args: List[str]) -> Tuple[str, str, Union[str, None]]: + """Parse command-line arguments that should be forwarded to unittest to perform discovery. + + Valid unittest arguments are: -v, -s, -p, -t and their long-form counterparts, + however we only care about the last three. + + The returned tuple contains the following items + - start_directory: The directory where to start discovery, defaults to . + - pattern: The pattern to match test files, defaults to test*.py + - top_level_directory: The top-level directory of the project, defaults to None, and unittest will use start_directory behind the scenes. + """ + + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument("--start-directory", "-s", default=".") + arg_parser.add_argument("--pattern", "-p", default="test*.py") + arg_parser.add_argument("--top-level-directory", "-t", default=None) + + parsed_args, _ = arg_parser.parse_known_args(args) + + return ( + parsed_args.start_directory, + parsed_args.pattern, + parsed_args.top_level_directory, + ) diff --git a/pythonFiles/unittest_adapter/unittest_discovery.py b/pythonFiles/unittest_adapter/unittest_discovery.py index 5f22a4cc4b92..cc09637078ee 100644 --- a/pythonFiles/unittest_adapter/unittest_discovery.py +++ b/pythonFiles/unittest_adapter/unittest_discovery.py @@ -3,6 +3,7 @@ import argparse import json +import logging import os import pathlib import sys @@ -10,6 +11,13 @@ import unittest from typing import List, Literal, Optional, Tuple, TypedDict, Union +sys.path.append( + "/Users/eleanorboyd/.vscode/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python" +) # +import debugpy + +debugpy.connect(5678) + # Add the path to pythonFiles to sys.path to find testing_tools.socket_manager. PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, PYTHON_FILES) @@ -114,6 +122,9 @@ def discover_tests( index = argv.index("--udiscovery") start_dir, pattern, top_level_dir = parse_unittest_args(argv[index + 1 :]) + logging.debug( + "start_dir, pattern, top_level_dir", start_dir, pattern, top_level_dir + ) # Perform test discovery. port, uuid = parse_discovery_cli_args(argv[:index]) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index adf5bba1a33c..df5e2c2e7eae 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -71,6 +71,12 @@ export class PythonTestServer implements ITestServer, Disposable { return (this.server.address() as net.AddressInfo).port; } + public createUUID(options: TestCommandOptions): string { + const uuid = crypto.randomUUID(); + this.uuids.set(uuid, options.cwd); + return uuid; + } + public dispose(): void { this.server.close(); this._onDataReceived.dispose(); @@ -80,15 +86,53 @@ export class PythonTestServer implements ITestServer, Disposable { return this._onDataReceived.event; } - async sendCommand(options: TestCommandOptions): Promise { - const uuid = crypto.randomUUID(); + async sendCommandPytest(options: TestCommandOptions, args: string[]): Promise { + const uuid = this.createUUID(options); const spawnOptions: SpawnOptions = { token: options.token, cwd: options.cwd, throwOnStdErr: true, }; - this.uuids.set(uuid, options.cwd); + // Create the Python environment in which to execute the command. + const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { + allowEnvironmentFetchExceptions: false, + resource: options.workspaceFolder, + }; + const execService = await this.executionFactory.createActivatedEnvironment(creationOptions); + + try { + if (options.debugBool) { + const launchOptions: LaunchOptions = { + cwd: options.cwd, + args, + token: options.token, + testProvider: UNITTEST_PROVIDER, + }; + + await this.debugLauncher!.launchDebugger(launchOptions); + } else { + await execService.exec(args, spawnOptions); + } + } catch (ex) { + this.uuids.delete(uuid); + this._onDataReceived.fire({ + cwd: options.cwd, + data: JSON.stringify({ + status: 'error', + errors: [(ex as Error).message], + }), + }); + } + } + + async sendCommand(options: TestCommandOptions): Promise { + const uuid = this.createUUID(options); + const spawnOptions: SpawnOptions = { + token: options.token, + cwd: options.cwd, + throwOnStdErr: true, + }; // Create the Python environment in which to execute the command. const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 3d9a8fa03fd1..273b89adba48 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -144,7 +144,11 @@ export class PythonTestController implements ITestController, IExtensionSingleAc }); return this.refreshTestData(undefined, { forceRefresh: true }); }; +<<<<<<< HEAD +======= + // this.pythonTestServer = new PythonTestServer(this.pythonExecFactory); // old way where debugLauncher did not have to be passed +>>>>>>> 9cca1d959 (pytest is now running - unable to collect output) this.pythonTestServer = new PythonTestServer(this.pythonExecFactory, this.debugLauncher); } @@ -161,7 +165,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc let testProvider: TestProvider; if (settings.testing.pytestEnabled) { console.log('settings.testing.pytestEnabled = true'); - discoveryAdapter = new PytestTestDiscoveryAdapter(this.pythonTestServer, { ...this.configSettings }); // what is the ... for + discoveryAdapter = new PytestTestDiscoveryAdapter(this.pythonTestServer, this.configSettings); // what is the ... for executionAdapter = new PytestTestExecutionAdapter(this.pythonTestServer, this.configSettings); testProvider = PYTEST_PROVIDER; } else if (settings.testing.unittestEnabled) { @@ -235,10 +239,21 @@ export class PythonTestController implements ITestController, IExtensionSingleAc if (settings.testing.pytestEnabled) { traceVerbose(`Testing: Refreshing test data for ${uri.fsPath}`); - // pytest needs to be added in the new design - + // can I move these out of the if statement + const workspace = this.workspaceService.getWorkspaceFolder(uri); + console.warn(`Discover tests for workspace name: ${workspace?.name} - uri: ${uri.fsPath}`); + const testAdapter = + this.testAdapters.get(uri) || (this.testAdapters.values().next().value as WorkspaceTestAdapter); + testAdapter.discoverTests( + this.testController, + this.refreshCancellation.token, + this.testAdapters.size > 1, + this.workspaceService.workspaceFile?.fsPath, + ); // Ensure we send test telemetry if it gets disabled again this.sendTestDisabledTelemetry = true; + // comment below 229 to run the new way and uncomment above 212 ~ 227 + // await this.unittest.refreshTestData(this.testController, uri, this.refreshCancellation.token); await this.pytest.refreshTestData(this.testController, uri, this.refreshCancellation.token); } else if (settings.testing.unittestEnabled) { @@ -322,6 +337,11 @@ export class PythonTestController implements ITestController, IExtensionSingleAc }), ); } + console.log('HERE2'); + this.testController.items.forEach((element) => console.log(element)); + + console.log(this.testController.items); + console.log('size', this.testController.items.size); return Promise.resolve(); } @@ -422,6 +442,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc } if (!settings.testing.pytestEnabled && !settings.testing.unittestEnabled) { + // ** this could be the logic I am looking for unconfiguredWorkspaces.push(workspace); } return Promise.resolve(); diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 3a3f8fb9a51b..4b11b575e635 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -38,9 +38,9 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { public async discoverTests(uri: Uri): Promise { if (!this.deferred) { const settings = this.configSettings.getSettings(uri); - const { unittestArgs } = settings.testing; + const { pytestArgs } = settings.testing; - const command = buildDiscoveryCommand(unittestArgs); + const command = buildDiscoveryCommand(pytestArgs); this.cwd = uri.fsPath; @@ -62,7 +62,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { } function buildDiscoveryCommand(args: string[]): TestDiscoveryCommand { - const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'discovery.py'); + const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pytest_adapter', 'pytest_discovery.py'); return { script: discoveryScript, diff --git a/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts b/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts index 13ded842a8b6..d50d0ce3961b 100644 --- a/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts @@ -63,7 +63,7 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { } function buildDiscoveryCommand(args: string[]): TestDiscoveryCommand { - const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'discovery.py'); + const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittest_adapter', 'unittest_discovery.py'); return { script: discoveryScript, diff --git a/src/client/testing/testController/unittest/unittestExecutionAdapter.ts b/src/client/testing/testController/unittest/unittestExecutionAdapter.ts index d71dea9fea36..2b4e098323a4 100644 --- a/src/client/testing/testController/unittest/unittestExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/unittestExecutionAdapter.ts @@ -64,7 +64,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { } function buildExecutionCommand(args: string[]): TestExecutionCommand { - const executionScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'execution.py'); + const executionScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittest_adapter', 'unittest_execution.py'); return { script: executionScript, From cfe13792e744d3519dee3927b1fa444a6b5c0ea8 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 7 Sep 2022 14:33:30 -0700 Subject: [PATCH 05/49] pushing changes in order to rebase --- .../pytest-vscode-integration/.gitignore | 74 ++++++++++++++++++ pythonFiles/pytest-vscode-integration/LICENSE | 22 ++++++ .../pytest-vscode-integration/MANIFEST.in | 5 ++ .../pytest-vscode-integration/README.rst | 76 +++++++++++++++++++ .../pytest-vscode-integration/appveyor.yml | 40 ++++++++++ .../pytest-vscode-integration/docs/index.md | 3 + .../pytest-vscode-integration/mkdocs.yml | 10 +++ .../pytest_vscode_integration.py | 26 +++++++ .../pytest-vscode-integration/setup.py | 51 +++++++++++++ .../tests/conftest.py | 1 + .../tests/test_vscode_integration.py | 64 ++++++++++++++++ pythonFiles/pytest-vscode-integration/tox.ini | 12 +++ pythonFiles/pytest_adapter/conftest.py | 1 + .../pytest_adapter/pytest_discovery.py | 19 ++++- pythonFiles/testing_tools/run_adapter.py | 4 +- .../testing/testController/controller.ts | 2 +- .../testController/pytest/arguments.ts | 2 +- .../pytest/pytestDiscoveryAdapter.ts | 50 +++++++++--- 18 files changed, 444 insertions(+), 18 deletions(-) create mode 100644 pythonFiles/pytest-vscode-integration/.gitignore create mode 100644 pythonFiles/pytest-vscode-integration/LICENSE create mode 100644 pythonFiles/pytest-vscode-integration/MANIFEST.in create mode 100644 pythonFiles/pytest-vscode-integration/README.rst create mode 100644 pythonFiles/pytest-vscode-integration/appveyor.yml create mode 100644 pythonFiles/pytest-vscode-integration/docs/index.md create mode 100644 pythonFiles/pytest-vscode-integration/mkdocs.yml create mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py create mode 100644 pythonFiles/pytest-vscode-integration/setup.py create mode 100644 pythonFiles/pytest-vscode-integration/tests/conftest.py create mode 100644 pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py create mode 100644 pythonFiles/pytest-vscode-integration/tox.ini diff --git a/pythonFiles/pytest-vscode-integration/.gitignore b/pythonFiles/pytest-vscode-integration/.gitignore new file mode 100644 index 000000000000..6c5886e04363 --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/.gitignore @@ -0,0 +1,74 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ +.pytest_cache + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask instance folder +instance/ + +# Sphinx documentation +docs/_build/ + +# MkDocs documentation +/site/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + diff --git a/pythonFiles/pytest-vscode-integration/LICENSE b/pythonFiles/pytest-vscode-integration/LICENSE new file mode 100644 index 000000000000..544793b47855 --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/LICENSE @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2022 Eleanor Boyd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/pythonFiles/pytest-vscode-integration/MANIFEST.in b/pythonFiles/pytest-vscode-integration/MANIFEST.in new file mode 100644 index 000000000000..1ade348bb74d --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/MANIFEST.in @@ -0,0 +1,5 @@ +include LICENSE +include README.rst + +recursive-exclude * __pycache__ +recursive-exclude * *.py[co] diff --git a/pythonFiles/pytest-vscode-integration/README.rst b/pythonFiles/pytest-vscode-integration/README.rst new file mode 100644 index 000000000000..28a55c3da118 --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/README.rst @@ -0,0 +1,76 @@ +========================= +pytest-vscode-integration +========================= + +.. image:: https://img.shields.io/pypi/v/pytest-vscode-integration.svg + :target: https://pypi.org/project/pytest-vscode-integration + :alt: PyPI version + +.. image:: https://img.shields.io/pypi/pyversions/pytest-vscode-integration.svg + :target: https://pypi.org/project/pytest-vscode-integration + :alt: Python versions + +.. image:: https://ci.appveyor.com/api/projects/status/github/eleanorboyd/pytest-vscode-integration?branch=master + :target: https://ci.appveyor.com/project/eleanorboyd/pytest-vscode-integration/branch/master + :alt: See Build Status on AppVeyor + +used to surface pytest functionality to ports for vscode integration + +---- + +This `pytest`_ plugin was generated with `Cookiecutter`_ along with `@hackebrot`_'s `cookiecutter-pytest-plugin`_ template. + + +Features +-------- + +* TODO + + +Requirements +------------ + +* TODO + + +Installation +------------ + +You can install "pytest-vscode-integration" via `pip`_ from `PyPI`_:: + + $ pip install pytest-vscode-integration + + +Usage +----- + +* TODO + +Contributing +------------ +Contributions are very welcome. Tests can be run with `tox`_, please ensure +the coverage at least stays the same before you submit a pull request. + +License +------- + +Distributed under the terms of the `MIT`_ license, "pytest-vscode-integration" is free and open source software + + +Issues +------ + +If you encounter any problems, please `file an issue`_ along with a detailed description. + +.. _`Cookiecutter`: https://github.com/audreyr/cookiecutter +.. _`@hackebrot`: https://github.com/hackebrot +.. _`MIT`: http://opensource.org/licenses/MIT +.. _`BSD-3`: http://opensource.org/licenses/BSD-3-Clause +.. _`GNU GPL v3.0`: http://www.gnu.org/licenses/gpl-3.0.txt +.. _`Apache Software License 2.0`: http://www.apache.org/licenses/LICENSE-2.0 +.. _`cookiecutter-pytest-plugin`: https://github.com/pytest-dev/cookiecutter-pytest-plugin +.. _`file an issue`: https://github.com/eleanorboyd/pytest-vscode-integration/issues +.. _`pytest`: https://github.com/pytest-dev/pytest +.. _`tox`: https://tox.readthedocs.io/en/latest/ +.. _`pip`: https://pypi.org/project/pip/ +.. _`PyPI`: https://pypi.org/project diff --git a/pythonFiles/pytest-vscode-integration/appveyor.yml b/pythonFiles/pytest-vscode-integration/appveyor.yml new file mode 100644 index 000000000000..61bdd159551a --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/appveyor.yml @@ -0,0 +1,40 @@ +# What Python version is installed where: +# https://www.appveyor.com/docs/build-environment/#python + +environment: + matrix: + - PYTHON: "C:\\Python35" + TOX_ENV: "py35" + + - PYTHON: "C:\\Python36" + TOX_ENV: "py36" + + - PYTHON: "C:\\Python37" + TOX_ENV: "py37" + + - PYTHON: "C:\\Python38" + TOX_ENV: "py38" + +init: + - "%PYTHON%/python -V" + - "%PYTHON%/python -c \"import struct;print( 8 * struct.calcsize(\'P\'))\"" + +install: + - "%PYTHON%/Scripts/easy_install -U pip" + - "%PYTHON%/Scripts/pip install tox" + - "%PYTHON%/Scripts/pip install wheel" + +build: false # Not a C# project, build stuff at the test step instead. + +test_script: + - "%PYTHON%/Scripts/tox -e %TOX_ENV%" + +after_test: + - "%PYTHON%/python setup.py bdist_wheel" + - ps: "ls dist" + +artifacts: + - path: dist\* + +#on_success: +# - TODO: upload the content of dist/*.whl to a public wheelhouse diff --git a/pythonFiles/pytest-vscode-integration/docs/index.md b/pythonFiles/pytest-vscode-integration/docs/index.md new file mode 100644 index 000000000000..6502b106decf --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/docs/index.md @@ -0,0 +1,3 @@ +# Welcome to pytest-vscode-integration + +used to surface pytest functionality to ports for vscode integration diff --git a/pythonFiles/pytest-vscode-integration/mkdocs.yml b/pythonFiles/pytest-vscode-integration/mkdocs.yml new file mode 100644 index 000000000000..e7aeb8d8508d --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/mkdocs.yml @@ -0,0 +1,10 @@ +site_name: pytest-vscode-integration +site_description: used to surface pytest functionality to ports for vscode integration +site_author: Eleanor Boyd + +theme: readthedocs + +repo_url: https://github.com/eleanorboyd/pytest-vscode-integration + +pages: +- Home: index.md diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py new file mode 100644 index 000000000000..11cb14fc379b --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +import pytest + + +def pytest_addoption(parser): + group = parser.getgroup("vscode-integration") + group.addoption( + "--foo", + action="store", + dest="dest_foo", + default="2022", + help='Set the value for the fixture "bar".', + ) + + parser.addini("HELLO", "Dummy pytest.ini setting") + + +@pytest.fixture +def bar(request): + return request.config.option.dest_foo + + +def pytest_runtest_setup(item): + # called for running each test in 'a' directory + print("AAAAA: setting up", item) diff --git a/pythonFiles/pytest-vscode-integration/setup.py b/pythonFiles/pytest-vscode-integration/setup.py new file mode 100644 index 000000000000..9a5b79444162 --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/setup.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import codecs +import os + +from setuptools import setup + + +def read(fname): + file_path = os.path.join(os.path.dirname(__file__), fname) + return codecs.open(file_path, encoding='utf-8').read() + + +setup( + name='pytest-vscode-integration', + version='0.1.0', + author='Eleanor Boyd', + author_email='eleanorboyd@microsoft.com', + maintainer='Eleanor Boyd', + maintainer_email='eleanorboyd@microsoft.com', + license='MIT', + url='https://github.com/eleanorboyd/pytest-vscode-integration', + description='used to surface pytest functionality to ports for vscode integration', + long_description=read('README.rst'), + py_modules=['pytest_vscode_integration'], + python_requires='>=3.5', + install_requires=['pytest>=3.5.0'], + classifiers=[ + 'Development Status :: 4 - Beta', + 'Framework :: Pytest', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Testing', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3 :: Only', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', + 'Operating System :: OS Independent', + 'License :: OSI Approved :: MIT License', + ], + entry_points={ + 'pytest11': [ + 'vscode-integration = pytest_vscode_integration', + ], + }, +) diff --git a/pythonFiles/pytest-vscode-integration/tests/conftest.py b/pythonFiles/pytest-vscode-integration/tests/conftest.py new file mode 100644 index 000000000000..bc711e55fefd --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/tests/conftest.py @@ -0,0 +1 @@ +pytest_plugins = 'pytester' diff --git a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py new file mode 100644 index 000000000000..657faa8e5881 --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + + +def test_bar_fixture(testdir): + """Make sure that pytest accepts our fixture.""" + + # create a temporary pytest test module + testdir.makepyfile(""" + def test_sth(bar): + assert bar == "europython2015" + """) + + # run pytest with the following cmd args + result = testdir.runpytest( + '--foo=europython2015', + '-v' + ) + + # fnmatch_lines does an assertion internally + result.stdout.fnmatch_lines([ + '*::test_sth PASSED*', + ]) + + # make sure that that we get a '0' exit code for the testsuite + assert result.ret == 0 + + +def test_help_message(testdir): + result = testdir.runpytest( + '--help', + ) + # fnmatch_lines does an assertion internally + result.stdout.fnmatch_lines([ + 'vscode-integration:', + '*--foo=DEST_FOO*Set the value for the fixture "bar".', + ]) + + +def test_hello_ini_setting(testdir): + testdir.makeini(""" + [pytest] + HELLO = world + """) + + testdir.makepyfile(""" + import pytest + + @pytest.fixture + def hello(request): + return request.config.getini('HELLO') + + def test_hello_world(hello): + assert hello == 'world' + """) + + result = testdir.runpytest('-v') + + # fnmatch_lines does an assertion internally + result.stdout.fnmatch_lines([ + '*::test_hello_world PASSED*', + ]) + + # make sure that that we get a '0' exit code for the testsuite + assert result.ret == 0 diff --git a/pythonFiles/pytest-vscode-integration/tox.ini b/pythonFiles/pytest-vscode-integration/tox.ini new file mode 100644 index 000000000000..9b037d48e319 --- /dev/null +++ b/pythonFiles/pytest-vscode-integration/tox.ini @@ -0,0 +1,12 @@ +# For more information about tox, see https://tox.readthedocs.io/en/latest/ +[tox] +envlist = py35,py36,py37,py38,pypy3,flake8 + +[testenv] +deps = pytest>=3.0 +commands = pytest {posargs:tests} + +[testenv:flake8] +skip_install = true +deps = flake8 +commands = flake8 pytest_vscode_integration.py setup.py tests diff --git a/pythonFiles/pytest_adapter/conftest.py b/pythonFiles/pytest_adapter/conftest.py index 7f9754b50a85..e562b648576e 100644 --- a/pythonFiles/pytest_adapter/conftest.py +++ b/pythonFiles/pytest_adapter/conftest.py @@ -1,3 +1,4 @@ +import os import sys import pytest diff --git a/pythonFiles/pytest_adapter/pytest_discovery.py b/pythonFiles/pytest_adapter/pytest_discovery.py index 34e6a2945cde..e8089944aa73 100644 --- a/pythonFiles/pytest_adapter/pytest_discovery.py +++ b/pythonFiles/pytest_adapter/pytest_discovery.py @@ -11,6 +11,7 @@ from typing import List, Literal, Optional, Tuple, TypedDict, Union import pytest +import pytest_vscode_integration sys.path.append( "/Users/eleanorboyd/.vscode/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python" @@ -23,12 +24,16 @@ PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, PYTHON_FILES) + from pytest_utils import TestNode, parse_unittest_args from testing_tools import socket_manager # Add the lib path to sys.path to find the typing_extensions module. sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) + +# pytest_plugins = ("myapp.testsupport.myplugin",) + from typing_extensions import NotRequired DEFAULT_PORT = "45454" @@ -98,8 +103,13 @@ def discover_tests(start_dir: str) -> PayloadDict: try: # test loading tests = "" - retcode = pytest.main(["--collect-only", "-q"]) - print("dine") + # retcode = pytest.main(["--collect-only", "-q"]) + # print("dine") + # sys.exit(pytest.main(["-qq"], plugins=[MyPlugin()])) + # python - + os.system("python3 -m pytest --collect-only") + print("HELLO") + os.system("python3 -m pytest --collect-only -p pytest-vscode-integration") except Exception: errors.append(traceback.format_exc()) @@ -114,6 +124,11 @@ def discover_tests(start_dir: str) -> PayloadDict: return payload +class MyPlugin: + def pytest_sessionfinish(self): + print("*** test run reporting finishing") + + if __name__ == "__main__": # Get unittest discovery arguments. argv = sys.argv[1:] diff --git a/pythonFiles/testing_tools/run_adapter.py b/pythonFiles/testing_tools/run_adapter.py index 1eeef194f8f5..b2c6c35608ff 100644 --- a/pythonFiles/testing_tools/run_adapter.py +++ b/pythonFiles/testing_tools/run_adapter.py @@ -14,9 +14,9 @@ ), ) -from testing_tools.adapter.__main__ import parse_args, main - +from testing_tools.adapter.__main__ import main, parse_args if __name__ == "__main__": tool, cmd, subargs, toolargs = parse_args() main(tool, cmd, subargs, toolargs) + print("run adapter hello") diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 273b89adba48..c96e1f9492f0 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -255,7 +255,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc // comment below 229 to run the new way and uncomment above 212 ~ 227 // await this.unittest.refreshTestData(this.testController, uri, this.refreshCancellation.token); - await this.pytest.refreshTestData(this.testController, uri, this.refreshCancellation.token); + // await this.pytest.refreshTestData(this.testController, uri, this.refreshCancellation.token); } else if (settings.testing.unittestEnabled) { // TODO: Use new test discovery mechanism traceVerbose(`Testing: Refreshing test data for ${uri.fsPath}`); diff --git a/src/client/testing/testController/pytest/arguments.ts b/src/client/testing/testController/pytest/arguments.ts index 78b451acdd6b..25b2853539e7 100644 --- a/src/client/testing/testController/pytest/arguments.ts +++ b/src/client/testing/testController/pytest/arguments.ts @@ -263,7 +263,7 @@ export function preparePytestArgumentsForDiscovery(options: TestDiscoveryOptions // Remove unwanted arguments (which happen to be test directories & test specific args). const args = pytestFilterArguments(options.args, TestFilter.discovery); if (options.ignoreCache && args.indexOf('--cache-clear') === -1) { - args.splice(0, 0, '--cache-clear'); + args.splice(0, 0, 'bbbbb--cache-clear'); } if (args.indexOf('-s') === -1) { args.splice(0, 0, '-s'); diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 4b11b575e635..f3b5c1ab2b2c 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. - import * as path from 'path'; import { Uri } from 'vscode'; import { IConfigurationService } from '../../../common/types'; import { createDeferred, Deferred } from '../../../common/utils/async'; import { EXTENSION_ROOT_DIR } from '../../../constants'; + import { DataReceivedEvent, DiscoveredTestPayload, @@ -16,6 +16,7 @@ import { } from '../common/types'; /** + * Wrapper class for unittest test discovery. This is where we call `runTestCommand`. #this seems incorrectly copied */ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { private deferred: Deferred | undefined; @@ -39,33 +40,58 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { if (!this.deferred) { const settings = this.configSettings.getSettings(uri); const { pytestArgs } = settings.testing; + console.debug(pytestArgs); + // const command2 = buildDiscoveryCommand('-m pytest', ['--collect-only', ...pytestArgs]); + // const command = buildDiscoveryCommand('import pytest', []); - const command = buildDiscoveryCommand(pytestArgs); + this.cwd = uri.fsPath; // this.cwd normally = '/Users/eleanorboyd/Documents/testing - tester files/inc_dec_example' + // const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittest_adapter', 'unittest_discovery.py'); - this.cwd = uri.fsPath; - - const options: TestCommandOptions = { + const relativePathToPytest = 'pythonFiles/pytest-vscode-integration'; + const fpath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); + // console.debug('1.2: ', fpath); + const cc = 'sys.path.append('.concat(fpath.toString(), ')'); // 1.2: /Users/eleanorboyd/vscode-python/pythonFiles/pytest-vscode-integration + // console.debug('1.3: ', cc); + let command: TestDiscoveryCommand = buildDiscoveryCommand(cc, []); + const options3: TestCommandOptions = { workspaceFolder: uri, command, - cwd: this.cwd, + cwd: fpath, }; + this.testServer.sendCommand(options3); + + // const options2: TestCommandOptions = { + // workspaceFolder: uri, + // command3, + // cwd: this.cwd, + // }; this.deferred = createDeferred(); + const prom = this.deferred.promise; + const a = await prom; + console.debug('AAAA', a); - // Send the test command to the server. - // The server will fire an onDataReceived event once it gets a response. - this.testServer.sendCommand(options); + // // Send the test command to the server. + // // The server will fire an onDataReceived event once it gets a response. + // this.testServer.sendCommand(options2); + command = buildDiscoveryCommand('import pytest', []); + const options4: TestCommandOptions = { + workspaceFolder: uri, + command, + cwd: fpath, + }; + this.testServer.sendCommand(options4); } return this.deferred.promise; } } -function buildDiscoveryCommand(args: string[]): TestDiscoveryCommand { - const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'pytest_adapter', 'pytest_discovery.py'); +function buildDiscoveryCommand(script: string, args: string[]): TestDiscoveryCommand { + const discoveryScript = script; return { script: discoveryScript, - args: ['--udiscovery', ...args], + args: [...args], }; } From 27c6300ea83e46de00fb5f24f43f32e9f140387e Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 7 Sep 2022 14:39:22 -0700 Subject: [PATCH 06/49] changes to discoverAdapter --- .../testController/pytest/pytestDiscoveryAdapter.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index f3b5c1ab2b2c..b6acf8d2ebae 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -50,22 +50,17 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const relativePathToPytest = 'pythonFiles/pytest-vscode-integration'; const fpath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); // console.debug('1.2: ', fpath); - const cc = 'sys.path.append('.concat(fpath.toString(), ')'); // 1.2: /Users/eleanorboyd/vscode-python/pythonFiles/pytest-vscode-integration + //const cc = 'sys.path.append('.concat(fpath.toString(), ')'); // 1.2: /Users/eleanorboyd/vscode-python/pythonFiles/pytest-vscode-integration // console.debug('1.3: ', cc); - let command: TestDiscoveryCommand = buildDiscoveryCommand(cc, []); + let command: TestDiscoveryCommand = buildDiscoveryCommand('-m pytest --collect-only', []); // as a collection const options3: TestCommandOptions = { workspaceFolder: uri, - command, + 'python -m pytest --collect-only -p: ', // with the port, these args for plugin cwd: fpath, + env: {"PYTHONPATH": fpath}, }; this.testServer.sendCommand(options3); - // const options2: TestCommandOptions = { - // workspaceFolder: uri, - // command3, - // cwd: this.cwd, - // }; - this.deferred = createDeferred(); const prom = this.deferred.promise; const a = await prom; From 72aad5a147cd74f1bef07c3dae2a12dad4e1bd2c Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Sep 2022 10:57:00 -0700 Subject: [PATCH 07/49] extension is calling plugin --- .../pytest_vscode_integration.py | 20 +++ .../common/process/pythonExecutionFactory.ts | 1 + .../testing/testController/common/server.ts | 46 +------ .../testing/testController/common/types.ts | 16 ++- .../testing/testController/controller.ts | 6 +- .../pytest/pytestDiscoveryAdapter.ts | 118 ++++++++++-------- .../testController/workspaceTestAdapter.ts | 8 +- 7 files changed, 113 insertions(+), 102 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index 11cb14fc379b..336d12bbafd8 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -24,3 +24,23 @@ def bar(request): def pytest_runtest_setup(item): # called for running each test in 'a' directory print("AAAAA: setting up", item) + + +def pytest_collection_finish(session): + print("ALERT!! in plugin file file ") + + +def pytest_collectstart(collector): + c = collector + print("collector", c) + print("ALERT!! in plugin collector start") + + +# def pytest_addoption(parser, pluginmanager): +# print("parser xtra info", parser.extra_info) + +# print("pluginmanager", pluginmanager) + + +# def get_config(request): +# print("request,", request.config) diff --git a/src/client/common/process/pythonExecutionFactory.ts b/src/client/common/process/pythonExecutionFactory.ts index 02c42beb1400..3684fe9cb0ed 100644 --- a/src/client/common/process/pythonExecutionFactory.ts +++ b/src/client/common/process/pythonExecutionFactory.ts @@ -92,6 +92,7 @@ export class PythonExecutionFactory implements IPythonExecutionFactory { public async createActivatedEnvironment( options: ExecutionFactoryCreateWithEnvironmentOptions, ): Promise { + console.log('made it here'); const envVars = await this.activationHelper.getActivatedEnvironmentVariables( options.resource, options.interpreter, diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index df5e2c2e7eae..48c0b81972af 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -71,9 +71,9 @@ export class PythonTestServer implements ITestServer, Disposable { return (this.server.address() as net.AddressInfo).port; } - public createUUID(options: TestCommandOptions): string { + public createUUID(cwd: string): string { const uuid = crypto.randomUUID(); - this.uuids.set(uuid, options.cwd); + this.uuids.set(uuid, cwd); return uuid; } @@ -86,48 +86,8 @@ export class PythonTestServer implements ITestServer, Disposable { return this._onDataReceived.event; } - async sendCommandPytest(options: TestCommandOptions, args: string[]): Promise { - const uuid = this.createUUID(options); - const spawnOptions: SpawnOptions = { - token: options.token, - cwd: options.cwd, - throwOnStdErr: true, - }; - - // Create the Python environment in which to execute the command. - const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { - allowEnvironmentFetchExceptions: false, - resource: options.workspaceFolder, - }; - const execService = await this.executionFactory.createActivatedEnvironment(creationOptions); - - try { - if (options.debugBool) { - const launchOptions: LaunchOptions = { - cwd: options.cwd, - args, - token: options.token, - testProvider: UNITTEST_PROVIDER, - }; - - await this.debugLauncher!.launchDebugger(launchOptions); - } else { - await execService.exec(args, spawnOptions); - } - } catch (ex) { - this.uuids.delete(uuid); - this._onDataReceived.fire({ - cwd: options.cwd, - data: JSON.stringify({ - status: 'error', - errors: [(ex as Error).message], - }), - }); - } - } - async sendCommand(options: TestCommandOptions): Promise { - const uuid = this.createUUID(options); + const uuid = this.createUUID(options.cwd); const spawnOptions: SpawnOptions = { token: options.token, cwd: options.cwd, diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index 064307ca8d9a..b61fad1c9167 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -12,6 +12,7 @@ import { Uri, WorkspaceFolder, } from 'vscode'; +import { IPythonExecutionFactory } from '../../../common/process/types'; import { TestDiscoveryOptions } from '../../common/types'; export type TestRunInstanceOptions = TestRunOptions & { @@ -151,6 +152,17 @@ export type TestCommandOptions = { testIds?: string[]; }; +export type TestCommandOptionsPytest = { + workspaceFolder: Uri; + cwd: string; + commandStr: string; + token?: CancellationToken; + outChannel?: OutputChannel; + debugBool?: boolean; + testIds?: string[]; + env: { [key: string]: string | undefined }; +}; + /** * Interface describing the server that will send test commands to the Python side, and process responses. * @@ -161,10 +173,12 @@ export interface ITestServer { readonly onDataReceived: Event; sendCommand(options: TestCommandOptions): Promise; serverReady(): Promise; + getPort(): number; + createUUID(cwd: string): string; } export interface ITestDiscoveryAdapter { - discoverTests(uri: Uri): Promise; + discoverTests(uri: Uri, executionFactory: IPythonExecutionFactory): Promise; } // interface for execution/runner adapter diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index c96e1f9492f0..3030b92f885f 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -144,11 +144,6 @@ export class PythonTestController implements ITestController, IExtensionSingleAc }); return this.refreshTestData(undefined, { forceRefresh: true }); }; -<<<<<<< HEAD - -======= - // this.pythonTestServer = new PythonTestServer(this.pythonExecFactory); // old way where debugLauncher did not have to be passed ->>>>>>> 9cca1d959 (pytest is now running - unable to collect output) this.pythonTestServer = new PythonTestServer(this.pythonExecFactory, this.debugLauncher); } @@ -249,6 +244,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc this.refreshCancellation.token, this.testAdapters.size > 1, this.workspaceService.workspaceFile?.fsPath, + this.pythonExecFactory, ); // Ensure we send test telemetry if it gets disabled again this.sendTestDisabledTelemetry = true; diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index b6acf8d2ebae..0a1220b7d614 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -2,18 +2,16 @@ // Licensed under the MIT License. import * as path from 'path'; import { Uri } from 'vscode'; +import { + ExecutionFactoryCreateWithEnvironmentOptions, + ExecutionResult, + IPythonExecutionFactory, + SpawnOptions, +} from '../../../common/process/types'; import { IConfigurationService } from '../../../common/types'; import { createDeferred, Deferred } from '../../../common/utils/async'; import { EXTENSION_ROOT_DIR } from '../../../constants'; - -import { - DataReceivedEvent, - DiscoveredTestPayload, - ITestDiscoveryAdapter, - ITestServer, - TestCommandOptions, - TestDiscoveryCommand, -} from '../common/types'; +import { DataReceivedEvent, DiscoveredTestPayload, ITestDiscoveryAdapter, ITestServer } from '../common/types'; /** * Wrapper class for unittest test discovery. This is where we call `runTestCommand`. #this seems incorrectly copied @@ -36,57 +34,73 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { } } - public async discoverTests(uri: Uri): Promise { + public async discoverTests(uri: Uri, executionFactory: IPythonExecutionFactory): Promise { if (!this.deferred) { + this.deferred = createDeferred(); + const settings = this.configSettings.getSettings(uri); const { pytestArgs } = settings.testing; - console.debug(pytestArgs); - // const command2 = buildDiscoveryCommand('-m pytest', ['--collect-only', ...pytestArgs]); - // const command = buildDiscoveryCommand('import pytest', []); - - this.cwd = uri.fsPath; // this.cwd normally = '/Users/eleanorboyd/Documents/testing - tester files/inc_dec_example' - // const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittest_adapter', 'unittest_discovery.py'); - - const relativePathToPytest = 'pythonFiles/pytest-vscode-integration'; - const fpath = path.join(EXTENSION_ROOT_DIR, relativePathToPytest); - // console.debug('1.2: ', fpath); - //const cc = 'sys.path.append('.concat(fpath.toString(), ')'); // 1.2: /Users/eleanorboyd/vscode-python/pythonFiles/pytest-vscode-integration - // console.debug('1.3: ', cc); - let command: TestDiscoveryCommand = buildDiscoveryCommand('-m pytest --collect-only', []); // as a collection - const options3: TestCommandOptions = { - workspaceFolder: uri, - 'python -m pytest --collect-only -p: ', // with the port, these args for plugin - cwd: fpath, - env: {"PYTHONPATH": fpath}, - }; - this.testServer.sendCommand(options3); + console.debug(pytestArgs); // do we use pytestArgs anywhere? - this.deferred = createDeferred(); - const prom = this.deferred.promise; - const a = await prom; - console.debug('AAAA', a); - - // // Send the test command to the server. - // // The server will fire an onDataReceived event once it gets a response. - // this.testServer.sendCommand(options2); - command = buildDiscoveryCommand('import pytest', []); - const options4: TestCommandOptions = { - workspaceFolder: uri, - command, - cwd: fpath, - }; - this.testServer.sendCommand(options4); + this.cwd = uri.fsPath; + await this.runPytestDiscovery(uri, executionFactory); } return this.deferred.promise; } -} -function buildDiscoveryCommand(script: string, args: string[]): TestDiscoveryCommand { - const discoveryScript = script; + async runPytestDiscovery(uri: Uri, executionFactory: IPythonExecutionFactory): Promise { + const relativePathToPytest = 'pythonFiles/pytest-vscode-integration'; + 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 pythonPathCommand = `${fullPluginPath}${path.delimiter}`+(process.env.PYTHONPATH ?? ""); + console.debug('pythonp', process.env.PYTHONPATH); + const pythonPathCommand = `${fullPluginPath}${path.delimiter}`.concat(process.env.PYTHONPATH ?? ''); - return { - script: discoveryScript, - args: [...args], - }; + const spawnOptions: SpawnOptions = { + cwd: uri.fsPath, + throwOnStdErr: true, + extraVariables: { + PYTHONPATH: pythonPathCommand, + TEST_UUID: uuid.toString(), + TEST_PORT: this.testServer.getPort().toString(), + }, + }; + + // Create the Python environment in which to execute the command. + const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { + allowEnvironmentFetchExceptions: false, + resource: uri, + }; + const execService = await executionFactory.createActivatedEnvironment(creationOptions); + + try { + const s: ExecutionResult = await execService.exec( + ['-m', 'pytest', '--collect-only', '--foo'].concat(pytestArgs), + spawnOptions, + ); + console.debug('outout', s.stdout); + } catch (ex) { + console.error(ex); + } + + try { + const s: ExecutionResult = await execService.exec(['--version'], spawnOptions); + console.debug('python3 version', s); + } catch (ex) { + console.error(ex); + } + } } + +// function buildDiscoveryCommand(script: string, args: string[]): TestDiscoveryCommand { +// const discoveryScript = script; + +// return { +// script: discoveryScript, +// args: [...args], +// }; +// } diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 0ecab7649745..860f6ec6a6f1 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -14,6 +14,7 @@ import { Uri, Location, } from 'vscode'; +import { IPythonExecutionFactory } from '../../common/process/types'; import { createDeferred, Deferred } from '../../common/utils/async'; import { Testing } from '../../common/utils/localize'; import { traceError } from '../../logging'; @@ -201,6 +202,7 @@ export class WorkspaceTestAdapter { token?: CancellationToken, isMultiroot?: boolean, workspaceFilePath?: string, + executionFactory?: IPythonExecutionFactory, ): Promise { sendTelemetryEvent(EventName.UNITTEST_DISCOVERING, undefined, { tool: this.testProvider }); @@ -216,7 +218,11 @@ export class WorkspaceTestAdapter { let rawTestData; try { - rawTestData = await this.discoveryAdapter.discoverTests(this.workspaceUri); + if (executionFactory !== undefined) { + rawTestData = await this.discoveryAdapter.discoverTests(this.workspaceUri, executionFactory); + } else { + console.log('executionFactory is undefined'); + } deferred.resolve(); } catch (ex) { From f56157b3eebcd905a7f8e74d2fddc3984e0a134e Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Sep 2022 11:30:19 -0700 Subject: [PATCH 08/49] comitting to rebase --- .../pytest_vscode_integration.py | 24 +++++++++++-------- .../pytest/pytestDiscoveryAdapter.ts | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index 336d12bbafd8..a1e4b002d260 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - import pytest @@ -21,19 +20,24 @@ def bar(request): return request.config.option.dest_foo -def pytest_runtest_setup(item): - # called for running each test in 'a' directory - print("AAAAA: setting up", item) +def pytest_configure(config): + print("ALERT!! in plugin configure", config) + print("args", config.args) + print("options T", type(config.option)) + + +# # called for running each test in 'a' directory +# print("AAAAA: setting up", item) def pytest_collection_finish(session): print("ALERT!! in plugin file file ") -def pytest_collectstart(collector): - c = collector - print("collector", c) - print("ALERT!! in plugin collector start") +# def pytest_collectstart(collector): +# c = collector +# print("collector", c) +# print("ALERT!! in plugin collector start") # def pytest_addoption(parser, pluginmanager): @@ -42,5 +46,5 @@ def pytest_collectstart(collector): # print("pluginmanager", pluginmanager) -# def get_config(request): -# print("request,", request.config) +def get_config(request): + print("ABCD request,", request.config) diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 0a1220b7d614..d3e62db8504c 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -79,7 +79,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { try { const s: ExecutionResult = await execService.exec( - ['-m', 'pytest', '--collect-only', '--foo'].concat(pytestArgs), + ['-m', 'pytest', '--collect-only', '--foo', 'FOOIn'].concat(pytestArgs), spawnOptions, ); console.debug('outout', s.stdout); From bf89286b17f3122efc15f0cf653ccbd90a329797 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Sep 2022 11:56:26 -0700 Subject: [PATCH 09/49] remove all files from pytest_adapter.py --- pythonFiles/pytest_adapter/__init__.py | 2 - pythonFiles/pytest_adapter/conftest.py | 28 --- .../pytest_adapter/pytest_discovery.py | 159 ------------ .../pytest_adapter/pytest_execution.py | 0 pythonFiles/pytest_adapter/pytest_utils.py | 228 ------------------ 5 files changed, 417 deletions(-) delete mode 100644 pythonFiles/pytest_adapter/__init__.py delete mode 100644 pythonFiles/pytest_adapter/conftest.py delete mode 100644 pythonFiles/pytest_adapter/pytest_discovery.py delete mode 100644 pythonFiles/pytest_adapter/pytest_execution.py delete mode 100644 pythonFiles/pytest_adapter/pytest_utils.py diff --git a/pythonFiles/pytest_adapter/__init__.py b/pythonFiles/pytest_adapter/__init__.py deleted file mode 100644 index 5b7f7a925cc0..000000000000 --- a/pythonFiles/pytest_adapter/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. diff --git a/pythonFiles/pytest_adapter/conftest.py b/pythonFiles/pytest_adapter/conftest.py deleted file mode 100644 index e562b648576e..000000000000 --- a/pythonFiles/pytest_adapter/conftest.py +++ /dev/null @@ -1,28 +0,0 @@ -import os -import sys - -import pytest - -sys.path.append( - "/Users/eleanorboyd/.vscode/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python" -) # -import debugpy - -debugpy.connect(5678) - - -@pytest.hookimpl() -def pytest_sessionstart(session): - print("hello") - - -def pytest_collection_finish(session): - print("end collection") - for item in session.items: - print("**") - parentCur = item.parent - path = str(item.name) + "> " - while parentCur != None: - path += str(parentCur.name) + "> " - parentCur = parentCur.parent - print("P:", path) diff --git a/pythonFiles/pytest_adapter/pytest_discovery.py b/pythonFiles/pytest_adapter/pytest_discovery.py deleted file mode 100644 index e8089944aa73..000000000000 --- a/pythonFiles/pytest_adapter/pytest_discovery.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -import argparse -import json -import logging -import os -import pathlib -import sys -import traceback -from typing import List, Literal, Optional, Tuple, TypedDict, Union - -import pytest -import pytest_vscode_integration - -sys.path.append( - "/Users/eleanorboyd/.vscode/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python" -) # -import debugpy - -debugpy.connect(5678) - -# Add the path to pythonFiles to sys.path to find testing_tools.socket_manager. -PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0, PYTHON_FILES) - - -from pytest_utils import TestNode, parse_unittest_args -from testing_tools import socket_manager - -# Add the lib path to sys.path to find the typing_extensions module. -sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) - - -# pytest_plugins = ("myapp.testsupport.myplugin",) - -from typing_extensions import NotRequired - -DEFAULT_PORT = "45454" - - -def parse_discovery_cli_args(args: List[str]) -> Tuple[int, Union[str, None]]: - """Parse command-line arguments that should be processed by the script. - - So far this includes the port number that it needs to connect to, and the uuid passed by the TS side. - The port is passed to the discovery.py script when it is executed, and - defaults to DEFAULT_PORT if it can't be parsed. - The uuid should be passed to the discovery.py script when it is executed, and defaults to None if it can't be parsed. - If the arguments appear several times, the value returned by parse_cli_args will be the value of the last argument. - """ - arg_parser = argparse.ArgumentParser() - arg_parser.add_argument("--port", default=DEFAULT_PORT) - arg_parser.add_argument("--uuid") - arg_parser.add_argument("--udiscovery") - parsed_args, _ = arg_parser.parse_known_args(args) - - return int(parsed_args.port), parsed_args.uuid, parsed_args.udiscovery - - -class PayloadDict(TypedDict): - cwd: str - status: Literal["success", "error"] - tests: NotRequired[TestNode] - errors: NotRequired[List[str]] - - -def discover_tests(start_dir: str) -> PayloadDict: - """Returns a dictionary containing details of the discovered tests. - - The returned dict has the following keys: - - - cwd: Absolute path to the test start directory; - - uuid: UUID sent by the caller of the Python script, that needs to be sent back as an integrity check; - - status: Test discovery status, can be "success" or "error"; - - tests: Discoverered tests if any, not present otherwise. Note that the status can be "error" but the payload can still contain tests; - - errors: Discovery errors if any, not present otherwise. - - Payload format for a successful discovery: - { - "status": "success", - "cwd": , - "tests": - } - - Payload format for a successful discovery with no tests: - { - "status": "success", - "cwd": , - } - - Payload format when there are errors: - { - "cwd": - "errors": [list of errors] - "status": "error", - } - """ - cwd = os.path.abspath(start_dir) - payload: PayloadDict = {"cwd": cwd, "status": "success"} - tests = None - errors: List[str] = [] - - try: - # test loading - tests = "" - # retcode = pytest.main(["--collect-only", "-q"]) - # print("dine") - # sys.exit(pytest.main(["-qq"], plugins=[MyPlugin()])) - # python - - os.system("python3 -m pytest --collect-only") - print("HELLO") - os.system("python3 -m pytest --collect-only -p pytest-vscode-integration") - - except Exception: - errors.append(traceback.format_exc()) - - if tests is not None: - payload["tests"] = tests - - if len(errors): - payload["status"] = "error" - payload["errors"] = errors - - return payload - - -class MyPlugin: - def pytest_sessionfinish(self): - print("*** test run reporting finishing") - - -if __name__ == "__main__": - # Get unittest discovery arguments. - argv = sys.argv[1:] - index = argv.index("--udiscovery") - - # start_dir, pattern, top_level_dir = parse_unittest_args(argv[index + 1 :]) - # logging.debug( - # "start_dir, pattern, top_level_dir", start_dir, pattern, top_level_dir - # ) - # logging.debug("hi") - - # Perform test discovery. - port, uuid, start_dir = parse_discovery_cli_args(argv) - - payload = discover_tests(start_dir) - - # Build the request data (it has to be a POST request or the Node side will not process it), and send it. - addr = ("localhost", port) - with socket_manager.SocketManager(addr) as s: - data = json.dumps(payload) - request = f"""POST / HTTP/1.1 -Host: localhost:{port} -Content-Length: {len(data)} -Content-Type: application/json -Request-uuid: {uuid} - -{data}""" - result = s.socket.sendall(request.encode("utf-8")) # type: ignore diff --git a/pythonFiles/pytest_adapter/pytest_execution.py b/pythonFiles/pytest_adapter/pytest_execution.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/pythonFiles/pytest_adapter/pytest_utils.py b/pythonFiles/pytest_adapter/pytest_utils.py deleted file mode 100644 index 568ff30ee92d..000000000000 --- a/pythonFiles/pytest_adapter/pytest_utils.py +++ /dev/null @@ -1,228 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -import argparse -import enum -import inspect -import os -import pathlib -import unittest -from typing import List, Tuple, TypedDict, Union - -# Types - - -# Inherit from str so it's JSON serializable. -class TestNodeTypeEnum(str, enum.Enum): - class_ = "class" - file = "file" - folder = "folder" - test = "test" - - -class TestData(TypedDict): - name: str - path: str - type_: TestNodeTypeEnum - id_: str - - -class TestItem(TestData): - lineno: str - runID: str - - -class TestNode(TestData): - children: "List[TestNode | TestItem]" - - -# Helper functions for data retrieval. - - -def get_test_case(suite): - """Iterate through a unittest test suite and return all test cases.""" - for test in suite: - if isinstance(test, unittest.TestCase): - yield test - else: - for test_case in get_test_case(test): - yield test_case - - -def get_source_line(obj) -> str: - """Get the line number of a test case start line.""" - try: - sourcelines, lineno = inspect.getsourcelines(obj) - except: - try: - # tornado-specific, see https://github.com/microsoft/vscode-python/issues/17285. - sourcelines, lineno = inspect.getsourcelines(obj.orig_method) - except: - return "*" - - # Return the line number of the first line of the test case definition. - for i, v in enumerate(sourcelines): - if v.strip().startswith(("def", "async def")): - return str(lineno + i) - - return "*" - - -# Helper functions for test tree building. - - -def build_test_node(path: str, name: str, type_: TestNodeTypeEnum) -> TestNode: - """Build a test node with no children. A test node can be a folder, a file or a class.""" - ## figure out if we are folder, file, or class - id_gen = path - if type_ == TestNodeTypeEnum.folder or type_ == TestNodeTypeEnum.file: - id_gen = path - else: - # means we have to build test node for class - id_gen = path + "\\" + name - - return {"path": path, "name": name, "type_": type_, "children": [], "id_": id_gen} - - -def get_child_node( - name: str, path: str, type_: TestNodeTypeEnum, root: TestNode -) -> TestNode: - """Find a child node in a test tree given its name and type. If the node doesn't exist, create it.""" - try: - result = next( - node - for node in root["children"] - if node["name"] == name and node["type_"] == type_ - ) - except StopIteration: - result = build_test_node(path, name, type_) - root["children"].append(result) - - return result # type:ignore - - -def build_test_tree( - suite: unittest.TestSuite, test_directory: str -) -> Tuple[Union[TestNode, None], List[str]]: - """Build a test tree from a unittest test suite. - - This function returns the test tree, and any errors found by unittest. - If no tests were discovered, return `None` and a list of errors (if any). - - Test tree structure: - { - "path": , - "type": "folder", - "name": , - "children": [ - { files and folders } - ... - { - "path": , - "name": filename.py, - "type_": "file", - "children": [ - { - "path": , - "name": , - "type_": "class", - "children": [ - { - "path": , - "name": , - "type_": "test", - "lineno": - "id_": , - } - ], - "id_": - } - ], - "id_": - } - ], - "id_": - } - """ - errors = [] - directory_path = pathlib.PurePath(test_directory) - root = build_test_node(test_directory, directory_path.name, TestNodeTypeEnum.folder) - - for test_case in get_test_case(suite): - test_id = test_case.id() - if test_id.startswith("unittest.loader._FailedTest"): - errors.append(str(test_case._exception)) # type: ignore - else: - # Get the static test path components: filename, class name and function name. - components = test_id.split(".") - *folders, filename, class_name, function_name = components - py_filename = f"{filename}.py" - - current_node = root - - # Find/build nodes for the intermediate folders in the test path. - for folder in folders: - current_node = get_child_node( - folder, - os.fsdecode(pathlib.PurePath(current_node["path"], folder)), - TestNodeTypeEnum.folder, - current_node, - ) - - # Find/build file node. - path_components = [test_directory] + folders + [py_filename] - file_path = os.fsdecode(pathlib.PurePath("/".join(path_components))) - current_node = get_child_node( - py_filename, file_path, TestNodeTypeEnum.file, current_node - ) - - # Find/build class node. - current_node = get_child_node( - class_name, file_path, TestNodeTypeEnum.class_, current_node - ) - - # Get test line number. - test_method = getattr(test_case, test_case._testMethodName) - lineno = get_source_line(test_method) - - # Add test node. - test_node: TestItem = { - "name": function_name, - "path": file_path, - "lineno": lineno, - "type_": TestNodeTypeEnum.test, - "id_": file_path + "\\" + class_name + "\\" + function_name, - "runID": test_id, - } # concatenate class name and function test name - current_node["children"].append(test_node) - - if not root["children"]: - root = None - - return root, errors - - -def parse_unittest_args(args: List[str]) -> Tuple[str, str, Union[str, None]]: - """Parse command-line arguments that should be forwarded to unittest to perform discovery. - - Valid unittest arguments are: -v, -s, -p, -t and their long-form counterparts, - however we only care about the last three. - - The returned tuple contains the following items - - start_directory: The directory where to start discovery, defaults to . - - pattern: The pattern to match test files, defaults to test*.py - - top_level_directory: The top-level directory of the project, defaults to None, and unittest will use start_directory behind the scenes. - """ - - arg_parser = argparse.ArgumentParser() - arg_parser.add_argument("--start-directory", "-s", default=".") - arg_parser.add_argument("--pattern", "-p", default="test*.py") - arg_parser.add_argument("--top-level-directory", "-t", default=None) - - parsed_args, _ = arg_parser.parse_known_args(args) - - return ( - parsed_args.start_directory, - parsed_args.pattern, - parsed_args.top_level_directory, - ) From 7ec8c56e19373ffc62a2635267c26499b07b0931 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Fri, 9 Sep 2022 15:08:43 -0700 Subject: [PATCH 10/49] resolve conflicts accept all cur --- .../interpreterSelector/commands/setInterpreter.unit.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts b/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts index 7059fb7ab26f..39be1cac3da6 100644 --- a/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts +++ b/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts @@ -80,6 +80,7 @@ suite('Set Interpreter Command', () => { workspace = TypeMoq.Mock.ofType(); interpreterService = mock(); when(interpreterService.refreshPromise).thenReturn(undefined); + when(interpreterService.triggerRefresh()).thenResolve(); when(interpreterService.triggerRefresh(anything(), anything())).thenResolve(); workspace.setup((w) => w.rootPath).returns(() => 'rootPath'); From 57b6bc6f0e61607e3ea1bb908d2be6f1d6cd0048 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 9 Sep 2022 16:15:21 -0700 Subject: [PATCH 11/49] plugin args working, and tests collected in correct format --- .../pytest_vscode_integration.py | 40 ++++++++++++------- .../pytest/pytestDiscoveryAdapter.ts | 2 +- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index a1e4b002d260..0aafab7a25b4 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -5,25 +5,28 @@ def pytest_addoption(parser): group = parser.getgroup("vscode-integration") group.addoption( - "--foo", + "--port", action="store", - dest="dest_foo", - default="2022", - help='Set the value for the fixture "bar".', + dest="port_arg", + default="500", + help="Get the port value to send back data to.", ) - parser.addini("HELLO", "Dummy pytest.ini setting") +# parser.addini("HELLO", "Dummy pytest.ini setting") -@pytest.fixture -def bar(request): - return request.config.option.dest_foo + +# @pytest.fixture +# def bar(request): +# return request.config.option.dest_foo def pytest_configure(config): - print("ALERT!! in plugin configure", config) - print("args", config.args) - print("options T", type(config.option)) + # print("ALERT!! in plugin configure", config) + # print("args", config.args) + inputArgs = vars(config.option) + port = inputArgs["port_arg"] + print("portValue", port) # # called for running each test in 'a' directory @@ -31,7 +34,16 @@ def pytest_configure(config): def pytest_collection_finish(session): - print("ALERT!! in plugin file file ") + print("end collection") + testsList = [] + for item in session.items: + parentCur = item.parent + path = str(item.name) + while parentCur != session: + path = str(parentCur.name) + "::" + path + parentCur = parentCur.parent + testsList.append(path) + print("final tests collected", testsList) # def pytest_collectstart(collector): @@ -46,5 +58,5 @@ def pytest_collection_finish(session): # print("pluginmanager", pluginmanager) -def get_config(request): - print("ABCD request,", request.config) +# def get_config(request): +# print("ABCD request,", request.config) diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index d3e62db8504c..4fd00a4177fa 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -79,7 +79,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { try { const s: ExecutionResult = await execService.exec( - ['-m', 'pytest', '--collect-only', '--foo', 'FOOIn'].concat(pytestArgs), + ['-m', 'pytest', '--collect-only', '--port', '500'].concat(pytestArgs), spawnOptions, ); console.debug('outout', s.stdout); From ecb2a2faff7a47c84d8642410823e6c4a8b8a85c Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 16 Sep 2022 10:41:43 -0700 Subject: [PATCH 12/49] attempting port --- .../pytest_vscode_integration.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index 0aafab7a25b4..ca8be87fb89b 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -1,6 +1,34 @@ # -*- coding: utf-8 -*- +import json +import os +import sys + import pytest +sys.path.append( + "/Users/eleanorboyd/.vscode/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python" +) # +# import debugpy + +# debugpy.connect(5678) + +# Add the path to pythonFiles to sys.path to find testing_tools.socket_manager. +PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +print("ALERT!! in plugin", PYTHON_FILES) +sys.path.insert(0, PYTHON_FILES) + +from testing_tools import socket_manager + +# If I use from utils then there will be an import error in test_discovery.py. +from unittest_adapter.utils import TestNode, build_test_tree, parse_unittest_args + +# Add the lib path to sys.path to find the typing_extensions module. +sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) + +from typing_extensions import NotRequired + +DEFAULT_PORT = "45454" + def pytest_addoption(parser): group = parser.getgroup("vscode-integration") @@ -44,6 +72,7 @@ def pytest_collection_finish(session): parentCur = parentCur.parent testsList.append(path) print("final tests collected", testsList) + sendPost() # def pytest_collectstart(collector): @@ -60,3 +89,13 @@ def pytest_collection_finish(session): # def get_config(request): # print("ABCD request,", request.config) + + +def sendPost(): + payload: dict = {"status": "success"} + addr = ("localhost", 45454) + print("sending post") + # socket_manager.send_post("Hello from pytest") # type: ignore + with socket_manager.SocketManager(addr) as s: + request = json.dumps(payload) + result = s.socket.sendall(request.encode("utf-8")) # type: ignore From 7fd8315b38d6a76cbd4e5c451ec4ef8ed194d3d5 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 16 Sep 2022 10:48:55 -0700 Subject: [PATCH 13/49] fixing settings --- .vscode/settings.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b46b2e8e40b6..174a850c901e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -65,10 +65,5 @@ "--max-line-length=88" ], "typescript.preferences.importModuleSpecifier": "relative", - "debug.javascript.usePreview": false, - "python.testing.pytestArgs": [ - "." - ], - "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true + "debug.javascript.usePreview": false } From 3e2f1c94ef284f5b48366be472d28e9ea4c182b8 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Mon, 12 Sep 2022 17:07:57 -0700 Subject: [PATCH 14/49] continue --- src/client/interpreter/activation/service.ts | 39 +++++++------------ .../activation/service.unit.test.ts | 7 ++-- 2 files changed, 17 insertions(+), 29 deletions(-) diff --git a/src/client/interpreter/activation/service.ts b/src/client/interpreter/activation/service.ts index e4074b070853..4653251752fb 100644 --- a/src/client/interpreter/activation/service.ts +++ b/src/client/interpreter/activation/service.ts @@ -186,6 +186,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi if (!shellInfo) { return; } + let isPossiblyCondaEnv = false; try { let command: string | undefined; let [args, parse] = internalScripts.printEnvVariables(); @@ -203,23 +204,9 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi command = [...pythonArgv, ...args].map((arg) => arg.toCommandArgumentForPythonExt()).join(' '); } } - if (!command) { - const activationCommands = await this.helper.getEnvironmentActivationShellCommands( - resource, - shellInfo.shellType, - interpreter, - ); - traceVerbose(`Activation Commands received ${activationCommands} for shell ${shellInfo.shell}`); - if (!activationCommands || !Array.isArray(activationCommands) || activationCommands.length === 0) { - return; - } - // Run the activate command collect the environment from it. - const activationCommand = this.fixActivationCommands(activationCommands).join(' && '); - // In order to make sure we know where the environment output is, - // put in a dummy echo we can look for - command = `${activationCommand} && echo '${ENVIRONMENT_PREFIX}' && python ${args.join(' ')}`; - } - + isPossiblyCondaEnv = activationCommands.join(' ').toLowerCase().includes('conda'); + // Run the activate command collect the environment from it. + const activationCommand = this.fixActivationCommands(activationCommands).join(' && '); const processService = await this.processServiceFactory.create(resource); const customEnvVars = await this.envVarsService.getEnvironmentVariables(resource); const hasCustomEnvVars = Object.keys(customEnvVars).length; @@ -231,6 +218,14 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi env[PYTHON_WARNINGS] = 'ignore'; traceVerbose(`${hasCustomEnvVars ? 'Has' : 'No'} Custom Env Vars`); + + // In order to make sure we know where the environment output is, + // put in a dummy echo we can look for + const [args, parse] = internalScripts.printEnvVariables(); + args.forEach((arg, i) => { + args[i] = arg.toCommandArgumentForPythonExt(); + }); + const command = `${activationCommand} && echo '${ENVIRONMENT_PREFIX}' && python ${args.join(' ')}`; traceVerbose(`Activating Environment to capture Environment variables, ${command}`); // Do some wrapping of the call. For two reasons: @@ -248,10 +243,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi result = await processService.shellExec(command, { env, shell: shellInfo.shell, - timeout: - interpreter?.envType === EnvironmentType.Conda - ? CONDA_ENVIRONMENT_TIMEOUT - : ENVIRONMENT_TIMEOUT, + timeout: isPossiblyCondaEnv ? CONDA_ENVIRONMENT_TIMEOUT : ENVIRONMENT_TIMEOUT, maxBuffer: 1000 * 1000, throwOnStdErr: false, }); @@ -297,7 +289,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi } catch (e) { traceError('getActivatedEnvironmentVariables', e); sendTelemetryEvent(EventName.ACTIVATE_ENV_TO_GET_ENV_VARS_FAILED, undefined, { - isPossiblyCondaEnv: interpreter?.envType === EnvironmentType.Conda, + isPossiblyCondaEnv, terminal: shellInfo.shellType, }); @@ -315,9 +307,6 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi @traceDecoratorError('Failed to parse Environment variables') @traceDecoratorVerbose('parseEnvironmentOutput', TraceOptions.None) protected parseEnvironmentOutput(output: string, parse: (out: string) => NodeJS.ProcessEnv | undefined) { - if (output.indexOf(ENVIRONMENT_PREFIX) === -1) { - return parse(output); - } output = output.substring(output.indexOf(ENVIRONMENT_PREFIX) + ENVIRONMENT_PREFIX.length); const js = output.substring(output.indexOf('{')).trim(); return parse(js); diff --git a/src/test/interpreters/activation/service.unit.test.ts b/src/test/interpreters/activation/service.unit.test.ts index d50b2b5d5995..9e705f247ac9 100644 --- a/src/test/interpreters/activation/service.unit.test.ts +++ b/src/test/interpreters/activation/service.unit.test.ts @@ -58,7 +58,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { architecture: Architecture.x64, }; - function initSetup(interpreter: PythonEnvironment | undefined) { + function initSetup() { helper = mock(TerminalHelper); platform = mock(PlatformService); processServiceFactory = mock(ProcessServiceFactory); @@ -71,7 +71,6 @@ suite('Interpreters Activation - Python Environment Variables', () => { onDidChangeInterpreter = new EventEmitter(); when(envVarsService.onDidEnvironmentVariablesChange).thenReturn(onDidChangeEnvVariables.event); when(interpreterService.onDidChangeInterpreter).thenReturn(onDidChangeInterpreter.event); - when(interpreterService.getActiveInterpreter(anything())).thenResolve(interpreter); service = new EnvironmentActivationService( instance(helper), instance(platform), @@ -90,7 +89,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { [undefined, Uri.parse('a')].forEach((resource) => [undefined, pythonInterpreter].forEach((interpreter) => { suite(title(resource, interpreter), () => { - setup(() => initSetup(interpreter)); + setup(initSetup); test('Unknown os will return empty variables', async () => { when(platform.osType).thenReturn(OSType.Unknown); const env = await service.getActivatedEnvironmentVariables(resource); @@ -103,7 +102,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { osTypes.forEach((osType) => { suite(osType.name, () => { - setup(() => initSetup(interpreter)); + setup(initSetup); test('getEnvironmentActivationShellCommands will be invoked', async () => { when(platform.osType).thenReturn(osType.value); when( From 92bc0e0886d794159fd971090e85540ad4ce7009 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 13 Sep 2022 16:22:10 -0700 Subject: [PATCH 15/49] Ensure interpreter quickpick is initialized synchronously (#19828) For https://github.com/microsoft/vscode-python/issues/19101 Before the change item events start coming in, we have to ensure quickpick is ready to receive those events. From c11c5476e220174b0a17e264988cf6055e5b1e28 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 13 Sep 2022 16:25:21 -0700 Subject: [PATCH 16/49] Ensure we start watching environments when activating discovery component (#19827) Ensure we start watchers and create workspace related objects when activating discovery component From d7eaa05757ce6b7084942ff857d14a71d6bd1f8f Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 13 Sep 2022 17:31:24 -0700 Subject: [PATCH 17/49] Ensure an environment is only reported after the final type of environment is known (#19821) For https://github.com/microsoft/vscode-python/issues/19101 From 60de7a106e0a92b764a12c38bb2bca7b4ebeb6e6 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Wed, 14 Sep 2022 13:55:32 -0700 Subject: [PATCH 18/49] Simplify buffer decoder. (#19836) From 212d58a6ec46e572870fbf061ef9b6b1977c017b Mon Sep 17 00:00:00 2001 From: paulacamargo25 Date: Thu, 15 Sep 2022 13:25:12 -0700 Subject: [PATCH 19/49] cherry pick cont --- .../pytest_vscode_integration.py | 282 +++++++++++++----- 1 file changed, 208 insertions(+), 74 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index ca8be87fb89b..fc05cdf78367 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -1,101 +1,235 @@ # -*- coding: utf-8 -*- +import enum import json import os +import pathlib import sys +from dbm.ndbm import library +from typing import KeysView, List, Literal, Optional, Tuple, TypedDict, Union +from unittest import TestCase import pytest -sys.path.append( - "/Users/eleanorboyd/.vscode/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python" -) # -# import debugpy -# debugpy.connect(5678) +# Inherit from str so it's JSON serializable. +class TestNodeTypeEnum(str, enum.Enum): + class_ = "class" + file = "file" + folder = "folder" + test = "test" -# Add the path to pythonFiles to sys.path to find testing_tools.socket_manager. -PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -print("ALERT!! in plugin", PYTHON_FILES) -sys.path.insert(0, PYTHON_FILES) - -from testing_tools import socket_manager - -# If I use from utils then there will be an import error in test_discovery.py. -from unittest_adapter.utils import TestNode, build_test_tree, parse_unittest_args - -# Add the lib path to sys.path to find the typing_extensions module. -sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) -from typing_extensions import NotRequired +class TestData(TypedDict): + name: str + path: str + type_: TestNodeTypeEnum + id_: str -DEFAULT_PORT = "45454" +class TestItem(TestData): + lineno: str + runID: str -def pytest_addoption(parser): - group = parser.getgroup("vscode-integration") - group.addoption( - "--port", - action="store", - dest="port_arg", - default="500", - help="Get the port value to send back data to.", - ) +class TestNode(TestData): + children: "List[TestNode | TestItem]" -# parser.addini("HELLO", "Dummy pytest.ini setting") +# Add the path to pythonFiles to sys.path to find testing_tools.socket_manager. +PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, PYTHON_FILES) -# @pytest.fixture -# def bar(request): -# return request.config.option.dest_foo +# Add the lib path to sys.path to find the typing_extensions module. +sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) +from testing_tools import socket_manager +from typing_extensions import NotRequired +DEFAULT_PORT = "45454" -def pytest_configure(config): - # print("ALERT!! in plugin configure", config) - # print("args", config.args) - inputArgs = vars(config.option) - port = inputArgs["port_arg"] - print("portValue", port) +# session +# test Case +# modules folders1/folders2 (can be in classes) +# test cases -# # called for running each test in 'a' directory -# print("AAAAA: setting up", item) +# module +# class +# test case def pytest_collection_finish(session): - print("end collection") - testsList = [] - for item in session.items: - parentCur = item.parent - path = str(item.name) - while parentCur != session: - path = str(parentCur.name) + "::" + path - parentCur = parentCur.parent - testsList.append(path) - print("final tests collected", testsList) - sendPost() - - -# def pytest_collectstart(collector): -# c = collector -# print("collector", c) -# print("ALERT!! in plugin collector start") - - -# def pytest_addoption(parser, pluginmanager): -# print("parser xtra info", parser.extra_info) - -# print("pluginmanager", pluginmanager) - - -# def get_config(request): -# print("ABCD request,", request.config) - - -def sendPost(): - payload: dict = {"status": "success"} - addr = ("localhost", 45454) - print("sending post") + node, error = build_test_tree(session) + cwd = os.getcwd() + # add error check + sendPost(cwd, node) + + +def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: + errors: List[str] = [] # TODO: how do I check for errors + session_test_node = createSessionTestNode(session) + testNode_file_dict: dict[ + pytest.Module, TestNode + ] = dict() # a dictionary of all files in the session + session_children_dict: dict[ + str, TestNode + ] = dict() # a dictionary of all direct children of the session + testNode_class_dict: dict[ + str, TestNode + ] = dict() # a dictionary of all direct children of the session + # iterate through all the test items in the session + for test_case in session.items: + testNode_test = createTestItem(test_case) + # if the parent object file doesn't already exist + if type(test_case.parent) == pytest.Module: + if test_case.parent not in testNode_file_dict: + testNode_file_dict[test_case.parent] = createFileTestNode( + test_case.parent + ) + testNode_file_dict[test_case.parent].get("children").append( + testNode_test + ) # use set default + else: + # this means its a unittest class + # create class + testNode_class = accessOrCreateGeneral( + test_case.parent.name, testNode_class_dict, test_case.parent.fspath + ) + testNode_class["children"].append(testNode_test) + parent_module = test_case.parent.parent + # create file that wraps class + if parent_module not in testNode_file_dict.keys(): + testNode_file_dict[parent_module] = createFileTestNode(parent_module) + testNode_file_dict[parent_module].get("children").append(testNode_class) + + created_filesfolder_dict: dict[str, TestNode] = {} + for file_module, testNode_file in testNode_file_dict.items(): + name = str(file_module.name) + prev_folder_test_node: TestNode = testNode_file + if "/" in name: + # it is a nested folder structure and so new objects need to be created + nested_folder_list = name.split("/") + path_iterator = ( + str(session.fspath) + + "/" + + "/".join( + nested_folder_list[0:-1] + ) # check to see if windows style (more fancy stuff path lib if windows or posix via API in os module) + ) + for i in range(len(nested_folder_list) - 2, -1, -1): # reverse and slice + folderName = nested_folder_list[i] + folder_test_node = accessOrCreateGeneral( + folderName, + created_filesfolder_dict, + path_iterator, + ) + folder_test_node["children"].append(prev_folder_test_node) + # increase iteration through path + prev_folder_test_node = folder_test_node + path_iterator = str(session.fspath) + "/".join(nested_folder_list[0:i]) + + # the final folder we get to is the highest folder in the path and therefore we add this as a child to the session + if (prev_folder_test_node is not None) and ( + prev_folder_test_node.get("id_") not in session_children_dict + ): + session_children_dict[ + prev_folder_test_node.get("id_") + ] = prev_folder_test_node + session_test_node["children"] = list(session_children_dict.values()) + return session_test_node, errors + + +def createTestItem(test_case) -> TestItem: + return { + "name": test_case.name, + "path": str(test_case.fspath), + "lineno": test_case.location[1] + 1, + "type_": TestNodeTypeEnum.test, + "id_": str(test_case.nodeid), + "runID": test_case.nodeid, # can I use this two times? + } + + +def createSessionTestNode(session) -> TestNode: + return { + "name": session.name, + "path": str(session.fspath), + "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder + "children": [], + "id_": str(session.fspath), + } + + +def createClassTestNode(class_module_name, class_module_path) -> TestNode: + return { + "name": class_module_name, + "path": str(class_module_path), + "type_": TestNodeTypeEnum.class_, + "children": [], + "id_": str(class_module_path), + } + + +def createFileTestNode(file_module) -> TestNode: + return { + "name": str(file_module.fspath.basename), + "path": str(file_module.fspath), + "type_": TestNodeTypeEnum.file, + "id_": str(file_module.fspath), + "children": [], + } + + +def createFolderTestNode(folderName, path_iterator) -> TestNode: + return { + "name": folderName, + "path": str(path_iterator), + "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder + "id_": str(path_iterator), + "children": [], + } + + +def accessOrCreateGeneral(test_node_name, test_node_dict, test_node_path) -> TestNode: + if test_node_name in test_node_dict.keys(): # exists in the dictionary + return test_node_dict[test_node_name] + else: + # create new + temp = createFolderTestNode(test_node_name, test_node_path) + test_node_dict[test_node_name] = temp + return temp + + +# def accessOrCreateClass(class_module, testNode_class_dict) -> TestNode: +# if class_module.name in testNode_class_dict.keys(): # exists in the dictionary +# return testNode_class_dict[class_module.name] +# else: +# # create new +# temp = createClassTestNode(class_module.name, class_module.fspath) +# testNode_class_dict[class_module.name] = temp +# return temp + + +class PayloadDict(TypedDict): + cwd: str + status: Literal["success", "error"] + tests: NotRequired[TestNode] + errors: NotRequired[List[str]] + + +def sendPost(cwd, tests): + payload: PayloadDict = {"cwd": cwd, "status": "success", "tests": tests} + testPort = os.getenv("TEST_PORT", 45454) + testuuid = os.getenv("TEST_UUID") + addr = ("localhost", int(testPort)) + print("sending post", addr, cwd) # socket_manager.send_post("Hello from pytest") # type: ignore with socket_manager.SocketManager(addr) as s: - request = json.dumps(payload) + data = json.dumps(payload) + request = f"""POST / HTTP/1.1 +Host: localhost:{testPort} +Content-Length: {len(data)} +Content-Type: application/json +Request-uuid: {testuuid} + +{data}""" result = s.socket.sendall(request.encode("utf-8")) # type: ignore From dcb05618f25896983bd05eeabcbb03ce32e93e2d Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Fri, 23 Sep 2022 14:57:44 -0700 Subject: [PATCH 20/49] cherry pick --- .../creation/createEnvQuickPick.ts | 54 +++++++++++++++ .../creation/createEnvQuickPick.unit.test.ts | 69 +++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/client/pythonEnvironments/creation/createEnvQuickPick.ts create mode 100644 src/test/pythonEnvironments/creation/createEnvQuickPick.unit.test.ts diff --git a/src/client/pythonEnvironments/creation/createEnvQuickPick.ts b/src/client/pythonEnvironments/creation/createEnvQuickPick.ts new file mode 100644 index 000000000000..de71aa84cd06 --- /dev/null +++ b/src/client/pythonEnvironments/creation/createEnvQuickPick.ts @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License + +import { QuickPickItem } from 'vscode'; +import { CreateEnv } from '../../common/utils/localize'; +import { showQuickPick } from '../../common/vscodeApis/windowApis'; +import { traceError } from '../../logging'; +import { createEnvironment } from './createEnvironment'; +import { CreateEnvironmentOptions, CreateEnvironmentProvider } from './types'; + +interface CreateEnvironmentProviderQuickPickItem extends QuickPickItem { + id: string; +} + +async function showCreateEnvironmentQuickPick( + providers: readonly CreateEnvironmentProvider[], +): Promise { + const items: CreateEnvironmentProviderQuickPickItem[] = providers.map((p) => ({ + label: p.name, + description: p.description, + id: p.id, + })); + const selected = await showQuickPick(items, { + title: CreateEnv.providersQuickPickTitle, + matchOnDescription: true, + ignoreFocusOut: true, + }); + + if (selected) { + const selections = providers.filter((p) => p.id === selected.id); + if (selections.length > 0) { + return selections[0]; + } + } + return undefined; +} + +export async function handleCreateEnvironmentCommand( + providers: readonly CreateEnvironmentProvider[], + options?: CreateEnvironmentOptions, +): Promise { + if (providers.length === 1) { + return createEnvironment(providers[0], options); + } + if (providers.length > 1) { + const provider = await showCreateEnvironmentQuickPick(providers); + if (provider) { + return createEnvironment(provider, options); + } + } else { + traceError('No Environment Creation providers were registered.'); + } + return undefined; +} diff --git a/src/test/pythonEnvironments/creation/createEnvQuickPick.unit.test.ts b/src/test/pythonEnvironments/creation/createEnvQuickPick.unit.test.ts new file mode 100644 index 000000000000..165dff8c6b2b --- /dev/null +++ b/src/test/pythonEnvironments/creation/createEnvQuickPick.unit.test.ts @@ -0,0 +1,69 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { assert } from 'chai'; +import * as sinon from 'sinon'; +import * as typemoq from 'typemoq'; +import * as windowApis from '../../../client/common/vscodeApis/windowApis'; +import * as createEnv from '../../../client/pythonEnvironments/creation/createEnvironment'; +import { handleCreateEnvironmentCommand } from '../../../client/pythonEnvironments/creation/createEnvQuickPick'; +import { CreateEnvironmentProvider } from '../../../client/pythonEnvironments/creation/types'; + +suite('Create Environment Command Handler Tests', () => { + let showQuickPickStub: sinon.SinonStub; + let createEnvironmentStub: sinon.SinonStub; + + setup(() => { + showQuickPickStub = sinon.stub(windowApis, 'showQuickPick'); + createEnvironmentStub = sinon.stub(createEnv, 'createEnvironment'); + }); + + teardown(() => { + sinon.restore(); + }); + + test('No providers registered', async () => { + await handleCreateEnvironmentCommand([]); + + assert.isTrue(showQuickPickStub.notCalled); + assert.isTrue(createEnvironmentStub.notCalled); + }); + + test('Single environment creation provider registered', async () => { + const provider = typemoq.Mock.ofType(); + provider.setup((p) => p.name).returns(() => 'test'); + provider.setup((p) => p.id).returns(() => 'test-id'); + provider.setup((p) => p.description).returns(() => 'test-description'); + + await handleCreateEnvironmentCommand([provider.object]); + + assert.isTrue(showQuickPickStub.notCalled); + createEnvironmentStub.calledOnceWithExactly(provider.object, undefined); + }); + + test('Multiple environment creation providers registered', async () => { + const provider1 = typemoq.Mock.ofType(); + provider1.setup((p) => p.name).returns(() => 'test1'); + provider1.setup((p) => p.id).returns(() => 'test-id1'); + provider1.setup((p) => p.description).returns(() => 'test-description1'); + + const provider2 = typemoq.Mock.ofType(); + provider2.setup((p) => p.name).returns(() => 'test2'); + provider2.setup((p) => p.id).returns(() => 'test-id2'); + provider2.setup((p) => p.description).returns(() => 'test-description2'); + + showQuickPickStub.resolves({ + id: 'test-id2', + label: 'test2', + description: 'test-description2', + }); + + provider1.setup((p) => (p as any).then).returns(() => undefined); + provider2.setup((p) => (p as any).then).returns(() => undefined); + await handleCreateEnvironmentCommand([provider1.object, provider2.object]); + + assert.isTrue(showQuickPickStub.calledOnce); + createEnvironmentStub.calledOnceWithExactly(provider2.object, undefined); + }); +}); From 883dd85e8d5913e107f3a79b0b8c73121120318b Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 27 Sep 2022 14:54:27 -0700 Subject: [PATCH 21/49] Temporarily expose `PYTHONPATH` for Pylance (#19899) Done temporarily on request of Pylance so they can begin testing. From 75bfc972e9f11d56abc5a7c618debc268092228f Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 27 Sep 2022 16:10:33 -0700 Subject: [PATCH 22/49] Only log deprecation warning once for each extension (#19901) For https://github.com/microsoft/vscode-python/issues/19900 From adac2f6ed0095681d9d01ce5593a595257b2b91e Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Mon, 3 Oct 2022 08:41:09 -0700 Subject: [PATCH 23/49] Improve keyword hover information displayed when using Jedi (#19926) For https://github.com/microsoft/vscode/issues/159247 (ref: https://github.com/pappasam/jedi-language-server/issues/227#issuecomment-1263913655) From 69abfcd8449037acd148f2ed08efaa60fb8b3689 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Mon, 3 Oct 2022 11:50:12 -0700 Subject: [PATCH 24/49] cherry --- .../creation/createEnvQuickPick.ts | 54 --------------- .../creation/createEnvQuickPick.unit.test.ts | 69 ------------------- .../venvCreationProvider.unit.test.ts | 12 ++++ 3 files changed, 12 insertions(+), 123 deletions(-) delete mode 100644 src/client/pythonEnvironments/creation/createEnvQuickPick.ts delete mode 100644 src/test/pythonEnvironments/creation/createEnvQuickPick.unit.test.ts diff --git a/src/client/pythonEnvironments/creation/createEnvQuickPick.ts b/src/client/pythonEnvironments/creation/createEnvQuickPick.ts deleted file mode 100644 index de71aa84cd06..000000000000 --- a/src/client/pythonEnvironments/creation/createEnvQuickPick.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License - -import { QuickPickItem } from 'vscode'; -import { CreateEnv } from '../../common/utils/localize'; -import { showQuickPick } from '../../common/vscodeApis/windowApis'; -import { traceError } from '../../logging'; -import { createEnvironment } from './createEnvironment'; -import { CreateEnvironmentOptions, CreateEnvironmentProvider } from './types'; - -interface CreateEnvironmentProviderQuickPickItem extends QuickPickItem { - id: string; -} - -async function showCreateEnvironmentQuickPick( - providers: readonly CreateEnvironmentProvider[], -): Promise { - const items: CreateEnvironmentProviderQuickPickItem[] = providers.map((p) => ({ - label: p.name, - description: p.description, - id: p.id, - })); - const selected = await showQuickPick(items, { - title: CreateEnv.providersQuickPickTitle, - matchOnDescription: true, - ignoreFocusOut: true, - }); - - if (selected) { - const selections = providers.filter((p) => p.id === selected.id); - if (selections.length > 0) { - return selections[0]; - } - } - return undefined; -} - -export async function handleCreateEnvironmentCommand( - providers: readonly CreateEnvironmentProvider[], - options?: CreateEnvironmentOptions, -): Promise { - if (providers.length === 1) { - return createEnvironment(providers[0], options); - } - if (providers.length > 1) { - const provider = await showCreateEnvironmentQuickPick(providers); - if (provider) { - return createEnvironment(provider, options); - } - } else { - traceError('No Environment Creation providers were registered.'); - } - return undefined; -} diff --git a/src/test/pythonEnvironments/creation/createEnvQuickPick.unit.test.ts b/src/test/pythonEnvironments/creation/createEnvQuickPick.unit.test.ts deleted file mode 100644 index 165dff8c6b2b..000000000000 --- a/src/test/pythonEnvironments/creation/createEnvQuickPick.unit.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import { assert } from 'chai'; -import * as sinon from 'sinon'; -import * as typemoq from 'typemoq'; -import * as windowApis from '../../../client/common/vscodeApis/windowApis'; -import * as createEnv from '../../../client/pythonEnvironments/creation/createEnvironment'; -import { handleCreateEnvironmentCommand } from '../../../client/pythonEnvironments/creation/createEnvQuickPick'; -import { CreateEnvironmentProvider } from '../../../client/pythonEnvironments/creation/types'; - -suite('Create Environment Command Handler Tests', () => { - let showQuickPickStub: sinon.SinonStub; - let createEnvironmentStub: sinon.SinonStub; - - setup(() => { - showQuickPickStub = sinon.stub(windowApis, 'showQuickPick'); - createEnvironmentStub = sinon.stub(createEnv, 'createEnvironment'); - }); - - teardown(() => { - sinon.restore(); - }); - - test('No providers registered', async () => { - await handleCreateEnvironmentCommand([]); - - assert.isTrue(showQuickPickStub.notCalled); - assert.isTrue(createEnvironmentStub.notCalled); - }); - - test('Single environment creation provider registered', async () => { - const provider = typemoq.Mock.ofType(); - provider.setup((p) => p.name).returns(() => 'test'); - provider.setup((p) => p.id).returns(() => 'test-id'); - provider.setup((p) => p.description).returns(() => 'test-description'); - - await handleCreateEnvironmentCommand([provider.object]); - - assert.isTrue(showQuickPickStub.notCalled); - createEnvironmentStub.calledOnceWithExactly(provider.object, undefined); - }); - - test('Multiple environment creation providers registered', async () => { - const provider1 = typemoq.Mock.ofType(); - provider1.setup((p) => p.name).returns(() => 'test1'); - provider1.setup((p) => p.id).returns(() => 'test-id1'); - provider1.setup((p) => p.description).returns(() => 'test-description1'); - - const provider2 = typemoq.Mock.ofType(); - provider2.setup((p) => p.name).returns(() => 'test2'); - provider2.setup((p) => p.id).returns(() => 'test-id2'); - provider2.setup((p) => p.description).returns(() => 'test-description2'); - - showQuickPickStub.resolves({ - id: 'test-id2', - label: 'test2', - description: 'test-description2', - }); - - provider1.setup((p) => (p as any).then).returns(() => undefined); - provider2.setup((p) => (p as any).then).returns(() => undefined); - await handleCreateEnvironmentCommand([provider1.object, provider2.object]); - - assert.isTrue(showQuickPickStub.calledOnce); - createEnvironmentStub.calledOnceWithExactly(provider2.object, undefined); - }); -}); diff --git a/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts b/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts index 6b6187ed3e4a..0fe5bb3565dd 100644 --- a/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts +++ b/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts @@ -122,6 +122,18 @@ suite('venv Creation provider tests', () => { ) => task(progressMock.object), ); + progressMock.setup((p) => p.report({ message: CreateEnv.statusStarting })).verifiable(typemoq.Times.once()); + + withProgressStub.callsFake( + ( + _options: ProgressOptions, + task: ( + progress: CreateEnvironmentProgress, + token?: CancellationToken, + ) => Thenable, + ) => task(progressMock.object), + ); + const promise = venvProvider.createEnvironment(); await deferred.promise; assert.isDefined(_next); From 82abb1e250429da372d5b79ebfda9becc4d117d8 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 16 Sep 2022 14:22:50 -0700 Subject: [PATCH 25/49] formatting discovery --- .../pytest_vscode_integration.py | 57 +++++++++++++++++-- .../unittest_adapter/unittest_discovery.py | 4 +- .../testing/testController/controller.ts | 1 + 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index fc05cdf78367..af7436f744f0 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -58,10 +58,59 @@ class TestNode(TestData): def pytest_collection_finish(session): - node, error = build_test_tree(session) - cwd = os.getcwd() - # add error check - sendPost(cwd, node) + print("in config in files") + session.results = dict() + print(session.items) + print("SP", session.path) + parent_list = [] + folder_list = {} + for item in session.items: + parentIt = item.parent + pid = parentIt.nodeid + i = { + "path": item.fspath, + "name": item.name, + "type_": "test", + "lineno": 0, + "id_": item.nodeid, + } + if parentIt not in parent_list: + parent_list.append(parentIt) + + f = { + "path": parentIt.fspath, + "type": "folder", + "name": parentIt.name, + "children": [i], + "id": pid, + } + folder_list.update({pid: f}) + else: + f = folder_list.get(pid) + f.get("children").append(i) # type: ignore + + print("PL", parent_list) + print("FL", folder_list) + # print("end collection") + # testsList = [] + # buildTestTree(session) + # for item in session.items: + # parentCur = item.parent + # path = str(item.name) + # while parentCur != session: + # path = str(parentCur.name) + "::" + path + # parentCur = parentCur.parent + # testsList.append(path) + # print("final tests collected", testsList) + # sendPost() + + +def buildPayload(): + print("building payload") + + +def buildTestTree(session): + print("building test tree") def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: diff --git a/pythonFiles/unittest_adapter/unittest_discovery.py b/pythonFiles/unittest_adapter/unittest_discovery.py index cc09637078ee..2bb5ab311d75 100644 --- a/pythonFiles/unittest_adapter/unittest_discovery.py +++ b/pythonFiles/unittest_adapter/unittest_discovery.py @@ -14,9 +14,9 @@ sys.path.append( "/Users/eleanorboyd/.vscode/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python" ) # -import debugpy +# import debugpy -debugpy.connect(5678) +# debugpy.connect(5678) # Add the path to pythonFiles to sys.path to find testing_tools.socket_manager. PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 3030b92f885f..a72672e3d171 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -264,6 +264,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc this.refreshCancellation.token, this.testAdapters.size > 1, this.workspaceService.workspaceFile?.fsPath, + this.pythonExecFactory, ); // Ensure we send test telemetry if it gets disabled again this.sendTestDisabledTelemetry = true; From 0e96dae97a764502f4ab8275d2c1d79af7040775 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 19 Sep 2022 12:29:21 -0700 Subject: [PATCH 26/49] port posting working --- .../pytest_vscode_integration.py | 78 +++---------------- .../testing/testController/controller.ts | 1 + 2 files changed, 12 insertions(+), 67 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index af7436f744f0..864b9985e3d5 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -58,10 +58,9 @@ class TestNode(TestData): def pytest_collection_finish(session): - print("in config in files") + print("pytest collection finish") session.results = dict() - print(session.items) - print("SP", session.path) + # print("SP", session.path) parent_list = [] folder_list = {} for item in session.items: @@ -88,9 +87,10 @@ def pytest_collection_finish(session): else: f = folder_list.get(pid) f.get("children").append(i) # type: ignore + sendPost() - print("PL", parent_list) - print("FL", folder_list) + # print("PL", parent_list) + # print("FL", folder_list) # print("end collection") # testsList = [] # buildTestTree(session) @@ -208,69 +208,11 @@ def createSessionTestNode(session) -> TestNode: } -def createClassTestNode(class_module_name, class_module_path) -> TestNode: - return { - "name": class_module_name, - "path": str(class_module_path), - "type_": TestNodeTypeEnum.class_, - "children": [], - "id_": str(class_module_path), - } - - -def createFileTestNode(file_module) -> TestNode: - return { - "name": str(file_module.fspath.basename), - "path": str(file_module.fspath), - "type_": TestNodeTypeEnum.file, - "id_": str(file_module.fspath), - "children": [], - } - - -def createFolderTestNode(folderName, path_iterator) -> TestNode: - return { - "name": folderName, - "path": str(path_iterator), - "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder - "id_": str(path_iterator), - "children": [], - } - - -def accessOrCreateGeneral(test_node_name, test_node_dict, test_node_path) -> TestNode: - if test_node_name in test_node_dict.keys(): # exists in the dictionary - return test_node_dict[test_node_name] - else: - # create new - temp = createFolderTestNode(test_node_name, test_node_path) - test_node_dict[test_node_name] = temp - return temp - - -# def accessOrCreateClass(class_module, testNode_class_dict) -> TestNode: -# if class_module.name in testNode_class_dict.keys(): # exists in the dictionary -# return testNode_class_dict[class_module.name] -# else: -# # create new -# temp = createClassTestNode(class_module.name, class_module.fspath) -# testNode_class_dict[class_module.name] = temp -# return temp - - -class PayloadDict(TypedDict): - cwd: str - status: Literal["success", "error"] - tests: NotRequired[TestNode] - errors: NotRequired[List[str]] - - -def sendPost(cwd, tests): - payload: PayloadDict = {"cwd": cwd, "status": "success", "tests": tests} +def sendPost(): + payload: dict = {"status": "success"} testPort = os.getenv("TEST_PORT", 45454) - testuuid = os.getenv("TEST_UUID") addr = ("localhost", int(testPort)) - print("sending post", addr, cwd) + print("sending post", addr) # socket_manager.send_post("Hello from pytest") # type: ignore with socket_manager.SocketManager(addr) as s: data = json.dumps(payload) @@ -278,7 +220,9 @@ def sendPost(cwd, tests): Host: localhost:{testPort} Content-Length: {len(data)} Content-Type: application/json -Request-uuid: {testuuid} +Request-uuid: {12312432423} {data}""" result = s.socket.sendall(request.encode("utf-8")) # type: ignore + # request = json.dumps(payload) + # result = s.socket.sendall(request.encode("utf-8")) # type: ignore diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index a72672e3d171..c6a036c96183 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -151,6 +151,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc traceVerbose('Waiting for test server to start...'); await this.pythonTestServer.serverReady(); traceVerbose('Test server started.'); + console.debug('Test server started'); const workspaces: readonly WorkspaceFolder[] = this.workspaceService.workspaceFolders || []; workspaces.forEach((workspace) => { const settings = this.configSettings.getSettings(workspace.uri); From 597c3f36d770b23a809e8828851a46ffa7cdf89c Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 22 Sep 2022 14:00:03 -0700 Subject: [PATCH 27/49] stashing for testing --- .../pytest_vscode_integration.py | 120 +++++++++++++----- .../pytest/pytestDiscoveryAdapter.ts | 89 ++++++------- .../testController/workspaceTestAdapter.ts | 6 + 3 files changed, 128 insertions(+), 87 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index 864b9985e3d5..0abcadb04714 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -5,12 +5,35 @@ import pathlib import sys from dbm.ndbm import library -from typing import KeysView, List, Literal, Optional, Tuple, TypedDict, Union -from unittest import TestCase +from typing import List, Literal, Optional, Tuple, TypedDict, Union import pytest +# Inherit from str so it's JSON serializable. +class TestNodeTypeEnum(str, enum.Enum): + class_ = "class" + file = "file" + folder = "folder" + test = "test" + + +class TestData(TypedDict): + name: str + path: str + type_: TestNodeTypeEnum + id_: str + + +class TestItem(TestData): + lineno: str + runID: str + + +class TestNode(TestData): + children: "List[TestNode | TestItem]" + + # Inherit from str so it's JSON serializable. class TestNodeTypeEnum(str, enum.Enum): class_ = "class" @@ -39,6 +62,8 @@ class TestNode(TestData): PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, PYTHON_FILES) +from testing_tools import socket_manager + # Add the lib path to sys.path to find the typing_extensions module. sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) from testing_tools import socket_manager @@ -60,34 +85,20 @@ class TestNode(TestData): def pytest_collection_finish(session): print("pytest collection finish") session.results = dict() + folder_list = build_test_tree(session) + print("folder list", folder_list) + session_test_node = { + "name": session.name, + "path": str(session.fspath), + "type": TestNodeTypeEnum.folder, # check if this is a file or a folder + "id": session.nodeid, + "children": folder_list, + } + + cwd = os.getcwd() + print("session, test node", session_test_node) + sendPost(cwd, session_test_node) # print("SP", session.path) - parent_list = [] - folder_list = {} - for item in session.items: - parentIt = item.parent - pid = parentIt.nodeid - i = { - "path": item.fspath, - "name": item.name, - "type_": "test", - "lineno": 0, - "id_": item.nodeid, - } - if parentIt not in parent_list: - parent_list.append(parentIt) - - f = { - "path": parentIt.fspath, - "type": "folder", - "name": parentIt.name, - "children": [i], - "id": pid, - } - folder_list.update({pid: f}) - else: - f = folder_list.get(pid) - f.get("children").append(i) # type: ignore - sendPost() # print("PL", parent_list) # print("FL", folder_list) @@ -109,8 +120,41 @@ def buildPayload(): print("building payload") -def buildTestTree(session): +def build_test_tree(session): print("building test tree") + errors = [] # how do I check for errors + parent_list = [] + folder_list = {} + for item in session.items: + parentIterator = item.parent + parentId = parentIterator.nodeid + currTestItem = { + "path": str(item.fspath), + "name": item.name, + "type_": TestNodeTypeEnum.test, + "id_": item.nodeid, + "lineno": item.location[1], # idk worth a shot + "runID": item.nodeid, # can I use this two times? + } + if parentId not in parent_list: + parent_list.append(parentId) + folder_test_node = { + "name": parentIterator.name, + "path": str(parentIterator.fspath), + "type": TestNodeTypeEnum.folder, # check if this is a file or a folder + "id": parentId, + "children": [currTestItem], + } + folder_list.update({parentId: folder_test_node}) + else: + folder_test_node = folder_list.get(parentId) + folder_test_node.get("children").append(currTestItem) # type: ignore + return list(folder_list.values()) + + +def build_test_node(path: str, name: str, id: str, type_: TestNodeTypeEnum) -> TestNode: + print("building test node") + return {"path": path, "name": name, "type_": type_, "children": [], "id_": id} def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: @@ -208,11 +252,19 @@ def createSessionTestNode(session) -> TestNode: } -def sendPost(): - payload: dict = {"status": "success"} +class PayloadDict(TypedDict): + cwd: str + status: Literal["success", "error"] + tests: NotRequired[TestNode] + errors: NotRequired[List[str]] + + +def sendPost(cwd, tests): + payload: PayloadDict = {"cwd": cwd, "status": "success", "tests": tests} testPort = os.getenv("TEST_PORT", 45454) + testuuid = os.getenv("TEST_UUID") addr = ("localhost", int(testPort)) - print("sending post", addr) + print("sending post", addr, cwd) # socket_manager.send_post("Hello from pytest") # type: ignore with socket_manager.SocketManager(addr) as s: data = json.dumps(payload) @@ -220,7 +272,7 @@ def sendPost(): Host: localhost:{testPort} Content-Length: {len(data)} Content-Type: application/json -Request-uuid: {12312432423} +Request-uuid: {testuuid} {data}""" result = s.socket.sendall(request.encode("utf-8")) # type: ignore diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 4fd00a4177fa..ab28ea59f91b 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -4,7 +4,6 @@ import * as path from 'path'; import { Uri } from 'vscode'; import { ExecutionFactoryCreateWithEnvironmentOptions, - ExecutionResult, IPythonExecutionFactory, SpawnOptions, } from '../../../common/process/types'; @@ -35,65 +34,49 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { } public async discoverTests(uri: Uri, executionFactory: IPythonExecutionFactory): Promise { + const settings = this.configSettings.getSettings(uri); + const { pytestArgs } = settings.testing; + console.debug(pytestArgs); // do we use pytestArgs anywhere? + + this.cwd = uri.fsPath; + return this.runPytestDiscovery(uri, executionFactory); + } + + async runPytestDiscovery(uri: Uri, executionFactory: IPythonExecutionFactory): Promise { if (!this.deferred) { this.deferred = createDeferred(); - + const relativePathToPytest = 'pythonFiles/pytest-vscode-integration'; + 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; - console.debug(pytestArgs); // do we use pytestArgs anywhere? - - this.cwd = uri.fsPath; - await this.runPytestDiscovery(uri, executionFactory); + const pythonPathCommand = `${fullPluginPath}${path.delimiter}`.concat(process.env.PYTHONPATH ?? ''); + + const spawnOptions: SpawnOptions = { + cwd: uri.fsPath, + throwOnStdErr: true, + extraVariables: { + PYTHONPATH: pythonPathCommand, + TEST_UUID: uuid.toString(), + TEST_PORT: this.testServer.getPort().toString(), + }, + }; + + // Create the Python environment in which to execute the command. + const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { + allowEnvironmentFetchExceptions: false, + resource: uri, + }; + const execService = await executionFactory.createActivatedEnvironment(creationOptions); + + try { + execService.exec(['-m', 'pytest', '--collect-only', '--port', '500'].concat(pytestArgs), spawnOptions); + } catch (ex) { + console.error(ex); + } } - return this.deferred.promise; } - - async runPytestDiscovery(uri: Uri, executionFactory: IPythonExecutionFactory): Promise { - const relativePathToPytest = 'pythonFiles/pytest-vscode-integration'; - 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 pythonPathCommand = `${fullPluginPath}${path.delimiter}`+(process.env.PYTHONPATH ?? ""); - console.debug('pythonp', process.env.PYTHONPATH); - const pythonPathCommand = `${fullPluginPath}${path.delimiter}`.concat(process.env.PYTHONPATH ?? ''); - - const spawnOptions: SpawnOptions = { - cwd: uri.fsPath, - throwOnStdErr: true, - extraVariables: { - PYTHONPATH: pythonPathCommand, - TEST_UUID: uuid.toString(), - TEST_PORT: this.testServer.getPort().toString(), - }, - }; - - // Create the Python environment in which to execute the command. - const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { - allowEnvironmentFetchExceptions: false, - resource: uri, - }; - const execService = await executionFactory.createActivatedEnvironment(creationOptions); - - try { - const s: ExecutionResult = await execService.exec( - ['-m', 'pytest', '--collect-only', '--port', '500'].concat(pytestArgs), - spawnOptions, - ); - console.debug('outout', s.stdout); - } catch (ex) { - console.error(ex); - } - - try { - const s: ExecutionResult = await execService.exec(['--version'], spawnOptions); - console.debug('python3 version', s); - } catch (ex) { - console.error(ex); - } - } } // function buildDiscoveryCommand(script: string, args: string[]): TestDiscoveryCommand { diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 860f6ec6a6f1..01f8bf33256a 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -220,6 +220,8 @@ export class WorkspaceTestAdapter { try { if (executionFactory !== undefined) { rawTestData = await this.discoveryAdapter.discoverTests(this.workspaceUri, executionFactory); + console.debug('here'); + console.debug('rawTestData: ', rawTestData); } else { console.log('executionFactory is undefined'); } @@ -345,6 +347,10 @@ function populateTestTree( } // Recursively populate the tree with test data. + for (let i = 0; i < testTreeData.children.length; i = i + 1) { + console.debug('testTreeData.children i= ', i, '', testTreeData.children[i]); + } + testTreeData.children.forEach((child) => { if (!token?.isCancellationRequested) { if (isTestItem(child)) { From 8b1aeaf6c954b66aeeb629b1f74c965816006ce9 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 12 Oct 2022 14:24:38 -0700 Subject: [PATCH 28/49] saving changes --- .../pytest_vscode_integration.py | 195 ++++++++++++------ 1 file changed, 132 insertions(+), 63 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index 0abcadb04714..5e2fdf1683db 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -5,34 +5,14 @@ import pathlib import sys from dbm.ndbm import library -from typing import List, Literal, Optional, Tuple, TypedDict, Union +from typing import KeysView, List, Literal, Optional, Tuple, TypedDict, Union import pytest +sys.path.append("/Users/eleanorboyd/vscode-python/pythonFiles/lib/python") +import debugpy -# Inherit from str so it's JSON serializable. -class TestNodeTypeEnum(str, enum.Enum): - class_ = "class" - file = "file" - folder = "folder" - test = "test" - - -class TestData(TypedDict): - name: str - path: str - type_: TestNodeTypeEnum - id_: str - - -class TestItem(TestData): - lineno: str - runID: str - - -class TestNode(TestData): - children: "List[TestNode | TestItem]" - +debugpy.connect(5678) # Inherit from str so it's JSON serializable. class TestNodeTypeEnum(str, enum.Enum): @@ -83,21 +63,21 @@ class TestNode(TestData): def pytest_collection_finish(session): - print("pytest collection finish") - session.results = dict() - folder_list = build_test_tree(session) - print("folder list", folder_list) - session_test_node = { - "name": session.name, - "path": str(session.fspath), - "type": TestNodeTypeEnum.folder, # check if this is a file or a folder - "id": session.nodeid, - "children": folder_list, - } + # print("pytest collection finish") + # session.results = dict() + node, error = build_test_tree(session) + # print("folder list", folder_list) + # session_test_node = { + # "name": session.name, + # "path": str(session.fspath), + # "type": TestNodeTypeEnum.folder, # check if this is a file or a folder + # "id": session.nodeid, + # "children": folder_list, + # } cwd = os.getcwd() - print("session, test node", session_test_node) - sendPost(cwd, session_test_node) + print("session, test node") + sendPost(cwd, "hello hello") # print("SP", session.path) # print("PL", parent_list) @@ -120,36 +100,125 @@ def buildPayload(): print("building payload") -def build_test_tree(session): +def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: print("building test tree") - errors = [] # how do I check for errors - parent_list = [] - folder_list = {} - for item in session.items: - parentIterator = item.parent - parentId = parentIterator.nodeid - currTestItem = { - "path": str(item.fspath), - "name": item.name, + errors: List[str] = [] # how do I check for errors + session_test_node: TestNode = { + "path": str(session.fspath), + "name": session.name, + "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder + "children": [], + "id_": str(session.fspath), + } + testNode_file_dict: dict[pytest.Module, TestNode] = dict() + session_children_list: "List[TestNode | TestItem]" = [] + session_children_set: set[TestNode] = set() + for test_case in session.items: + # create node for the test case + testNode_test: TestItem = { + "path": str(test_case.fspath), + "name": test_case.name, "type_": TestNodeTypeEnum.test, - "id_": item.nodeid, - "lineno": item.location[1], # idk worth a shot - "runID": item.nodeid, # can I use this two times? + "id_": test_case.nodeid, + "lineno": test_case.location[1], # idk worth a shot + "runID": test_case.nodeid, # can I use this two times? } - if parentId not in parent_list: - parent_list.append(parentId) - folder_test_node = { - "name": parentIterator.name, - "path": str(parentIterator.fspath), - "type": TestNodeTypeEnum.folder, # check if this is a file or a folder - "id": parentId, - "children": [currTestItem], + print("item: ", test_case, type(test_case)) + print("parent: ", test_case.parent, type(test_case.parent)) + # if the parent object doesn't already exist + if ( + test_case.parent not in testNode_file_dict.keys() + and type(test_case.parent) == pytest.Module + ): + # create node for file + ti: TestNode = { + "name": test_case.parent.name, + "path": test_case.parent.fspath, + "type_": TestNodeTypeEnum.file, # check if this is a file or a folder + "id_": test_case.parent.fspath, + "children": [testNode_test], } - folder_list.update({parentId: folder_test_node}) - else: - folder_test_node = folder_list.get(parentId) - folder_test_node.get("children").append(currTestItem) # type: ignore - return list(folder_list.values()) + testNode_file_dict[pytest.Module(test_case.parent)] = ti + elif type(test_case.parent) == pytest.Module: + print("AAA", testNode_file_dict[test_case.parent].keys()) + (testNode_file_dict[test_case.parent]).get("children").append(testNode_test) + created_dict: dict[str, TestNode] = {} + for file_module, testNode_file in testNode_file_dict.items(): + print("TYPE", type(file_module)) + print("parent", file_module.name) + name = str(file_module.name) + prev_folder_test_node: TestNode = testNode_file + if "/" in name: + print("dash") + nested_folder_list = name.split("/") + print("folder list", nested_folder_list) + # update folder_test_node + testNode_file["name"] = nested_folder_list[-1] + path_iterator = str(session.fspath) + "".join(nested_folder_list[0:-1]) + print("len", len(nested_folder_list)) + print("f", file_module) + for i in range(len(nested_folder_list) - 2, -1, -1): + print("folder", nested_folder_list[i]) + print("i", i) + print("path iterator", path_iterator) + folder_test_node: Optional[TestNode] = created_dict.get( + nested_folder_list[i] + ) + if folder_test_node == None: + fn: TestNode = { + "name": nested_folder_list[i], + "path": path_iterator, + "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder + "id_": path_iterator, + "children": [prev_folder_test_node], + } + folder_test_node = fn + created_dict[nested_folder_list[i]] = folder_test_node + else: + folder_test_node["children"].append(prev_folder_test_node) + print("added child", folder_test_node) + prev_folder_test_node = folder_test_node + path_iterator = str(session.fspath) + "".join(nested_folder_list[0:i]) + if (prev_folder_test_node != None) and ( + prev_folder_test_node not in session_children_set + ): + session_children_set.add(prev_folder_test_node) + session_children_list.append(prev_folder_test_node) + session_test_node["children"] = session_children_list + print("STN", session_test_node) + return session_test_node, errors + + +# def build_test_tree(session): +# print("building test tree") +# errors = [] # how do I check for errors +# parent_list = [] +# folder_list = {} +# for item in session.items: +# parentIterator = item.parent +# parentId = parentIterator.nodeid +# currTestItem = { +# "path": str(item.fspath), +# "name": item.name, +# "type_": TestNodeTypeEnum.test, +# "id_": item.nodeid, +# "lineno": item.location[1], # idk worth a shot +# "runID": item.nodeid, # can I use this two times? +# } +# if parentId not in parent_list: +# parent_list.append(parentId) +# folder_test_node = { +# "name": parentIterator.name, +# "path": str(parentIterator.fspath), +# "type": TestNodeTypeEnum.folder, # check if this is a file or a folder +# "id": parentId, +# "children": [currTestItem], +# } +# folder_list.update({parentId: folder_test_node}) +# else: +# folder_test_node = folder_list.get(parentId) +# folder_test_node.get("children").append(currTestItem) # type: ignore +# return list(folder_list.values()) def build_test_node(path: str, name: str, id: str, type_: TestNodeTypeEnum) -> TestNode: From 097c4e063fe242eae1c3c79accb96155c3eab7cf Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 14 Oct 2022 11:18:48 -0700 Subject: [PATCH 29/49] tests showing in explorer panel --- .../pytest_vscode_integration.py | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index 5e2fdf1683db..f7a2f34246ff 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -77,7 +77,7 @@ def pytest_collection_finish(session): cwd = os.getcwd() print("session, test node") - sendPost(cwd, "hello hello") + sendPost(cwd, node) # print("SP", session.path) # print("PL", parent_list) @@ -111,16 +111,14 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: "id_": str(session.fspath), } testNode_file_dict: dict[pytest.Module, TestNode] = dict() - session_children_list: "List[TestNode | TestItem]" = [] - session_children_set: set[TestNode] = set() + session_children_dict: dict[str, TestNode] = dict() for test_case in session.items: - # create node for the test case testNode_test: TestItem = { - "path": str(test_case.fspath), "name": test_case.name, - "type_": TestNodeTypeEnum.test, - "id_": test_case.nodeid, + "path": str(test_case.fspath), "lineno": test_case.location[1], # idk worth a shot + "type_": TestNodeTypeEnum.test, + "id_": str(test_case.nodeid), "runID": test_case.nodeid, # can I use this two times? } print("item: ", test_case, type(test_case)) @@ -133,12 +131,12 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: # create node for file ti: TestNode = { "name": test_case.parent.name, - "path": test_case.parent.fspath, + "path": str(test_case.parent.fspath.basename), "type_": TestNodeTypeEnum.file, # check if this is a file or a folder - "id_": test_case.parent.fspath, + "id_": str(test_case.parent.fspath), "children": [testNode_test], } - testNode_file_dict[pytest.Module(test_case.parent)] = ti + testNode_file_dict[test_case.parent] = ti elif type(test_case.parent) == pytest.Module: print("AAA", testNode_file_dict[test_case.parent].keys()) (testNode_file_dict[test_case.parent]).get("children").append(testNode_test) @@ -149,42 +147,43 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: name = str(file_module.name) prev_folder_test_node: TestNode = testNode_file if "/" in name: - print("dash") + print("this folder is nested") nested_folder_list = name.split("/") print("folder list", nested_folder_list) - # update folder_test_node - testNode_file["name"] = nested_folder_list[-1] - path_iterator = str(session.fspath) + "".join(nested_folder_list[0:-1]) + path_iterator = ( + str(session.fspath) + "/" + "".join(nested_folder_list[0:-1]) + ) # CHECK print("len", len(nested_folder_list)) print("f", file_module) for i in range(len(nested_folder_list) - 2, -1, -1): + folderName = nested_folder_list[i] print("folder", nested_folder_list[i]) print("i", i) print("path iterator", path_iterator) - folder_test_node: Optional[TestNode] = created_dict.get( - nested_folder_list[i] - ) - if folder_test_node == None: - fn: TestNode = { + folder_test_node: TestNode + if folderName in created_dict.keys(): + folder_test_node = created_dict[folderName] + folder_test_node["children"].append(prev_folder_test_node) + print("added child", folder_test_node) + else: + temp: TestNode = { "name": nested_folder_list[i], - "path": path_iterator, + "path": str(path_iterator), "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder - "id_": path_iterator, + "id_": str(path_iterator), "children": [prev_folder_test_node], } - folder_test_node = fn - created_dict[nested_folder_list[i]] = folder_test_node - else: - folder_test_node["children"].append(prev_folder_test_node) - print("added child", folder_test_node) + folder_test_node = temp + created_dict[folderName] = folder_test_node prev_folder_test_node = folder_test_node path_iterator = str(session.fspath) + "".join(nested_folder_list[0:i]) if (prev_folder_test_node != None) and ( - prev_folder_test_node not in session_children_set + prev_folder_test_node.get("id_") not in session_children_dict.keys() ): - session_children_set.add(prev_folder_test_node) - session_children_list.append(prev_folder_test_node) - session_test_node["children"] = session_children_list + session_children_dict[ + prev_folder_test_node.get("id_") + ] = prev_folder_test_node + session_test_node["children"] = list(session_children_dict.values()) print("STN", session_test_node) return session_test_node, errors From 33209e3ec9bf7f7ea3f8d6784676387244894a60 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 14 Oct 2022 11:20:25 -0700 Subject: [PATCH 30/49] remove old code --- .../pytest_vscode_integration.py | 168 +----------------- 1 file changed, 6 insertions(+), 162 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index f7a2f34246ff..cae0bf8568f8 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -7,10 +7,8 @@ from dbm.ndbm import library from typing import KeysView, List, Literal, Optional, Tuple, TypedDict, Union -import pytest - -sys.path.append("/Users/eleanorboyd/vscode-python/pythonFiles/lib/python") import debugpy +import pytest debugpy.connect(5678) @@ -57,43 +55,18 @@ class TestNode(TestData): # modules folders1/folders2 (can be in classes) # test cases -# module -# class -# test case + +# def pytest_configure(config): +# inputArgs = vars(config.option) +# port = inputArgs["port_arg"] +# print("portValue", port) def pytest_collection_finish(session): - # print("pytest collection finish") - # session.results = dict() node, error = build_test_tree(session) - # print("folder list", folder_list) - # session_test_node = { - # "name": session.name, - # "path": str(session.fspath), - # "type": TestNodeTypeEnum.folder, # check if this is a file or a folder - # "id": session.nodeid, - # "children": folder_list, - # } - cwd = os.getcwd() print("session, test node") sendPost(cwd, node) - # print("SP", session.path) - - # print("PL", parent_list) - # print("FL", folder_list) - # print("end collection") - # testsList = [] - # buildTestTree(session) - # for item in session.items: - # parentCur = item.parent - # path = str(item.name) - # while parentCur != session: - # path = str(parentCur.name) + "::" + path - # parentCur = parentCur.parent - # testsList.append(path) - # print("final tests collected", testsList) - # sendPost() def buildPayload(): @@ -188,138 +161,11 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: return session_test_node, errors -# def build_test_tree(session): -# print("building test tree") -# errors = [] # how do I check for errors -# parent_list = [] -# folder_list = {} -# for item in session.items: -# parentIterator = item.parent -# parentId = parentIterator.nodeid -# currTestItem = { -# "path": str(item.fspath), -# "name": item.name, -# "type_": TestNodeTypeEnum.test, -# "id_": item.nodeid, -# "lineno": item.location[1], # idk worth a shot -# "runID": item.nodeid, # can I use this two times? -# } -# if parentId not in parent_list: -# parent_list.append(parentId) -# folder_test_node = { -# "name": parentIterator.name, -# "path": str(parentIterator.fspath), -# "type": TestNodeTypeEnum.folder, # check if this is a file or a folder -# "id": parentId, -# "children": [currTestItem], -# } -# folder_list.update({parentId: folder_test_node}) -# else: -# folder_test_node = folder_list.get(parentId) -# folder_test_node.get("children").append(currTestItem) # type: ignore -# return list(folder_list.values()) - - def build_test_node(path: str, name: str, id: str, type_: TestNodeTypeEnum) -> TestNode: print("building test node") return {"path": path, "name": name, "type_": type_, "children": [], "id_": id} -def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: - errors: List[str] = [] # TODO: how do I check for errors - session_test_node = createSessionTestNode(session) - testNode_file_dict: dict[ - pytest.Module, TestNode - ] = dict() # a dictionary of all files in the session - session_children_dict: dict[ - str, TestNode - ] = dict() # a dictionary of all direct children of the session - testNode_class_dict: dict[ - str, TestNode - ] = dict() # a dictionary of all direct children of the session - # iterate through all the test items in the session - for test_case in session.items: - testNode_test = createTestItem(test_case) - # if the parent object file doesn't already exist - if type(test_case.parent) == pytest.Module: - if test_case.parent not in testNode_file_dict: - testNode_file_dict[test_case.parent] = createFileTestNode( - test_case.parent - ) - testNode_file_dict[test_case.parent].get("children").append( - testNode_test - ) # use set default - else: - # this means its a unittest class - # create class - testNode_class = accessOrCreateGeneral( - test_case.parent.name, testNode_class_dict, test_case.parent.fspath - ) - testNode_class["children"].append(testNode_test) - parent_module = test_case.parent.parent - # create file that wraps class - if parent_module not in testNode_file_dict.keys(): - testNode_file_dict[parent_module] = createFileTestNode(parent_module) - testNode_file_dict[parent_module].get("children").append(testNode_class) - - created_filesfolder_dict: dict[str, TestNode] = {} - for file_module, testNode_file in testNode_file_dict.items(): - name = str(file_module.name) - prev_folder_test_node: TestNode = testNode_file - if "/" in name: - # it is a nested folder structure and so new objects need to be created - nested_folder_list = name.split("/") - path_iterator = ( - str(session.fspath) - + "/" - + "/".join( - nested_folder_list[0:-1] - ) # check to see if windows style (more fancy stuff path lib if windows or posix via API in os module) - ) - for i in range(len(nested_folder_list) - 2, -1, -1): # reverse and slice - folderName = nested_folder_list[i] - folder_test_node = accessOrCreateGeneral( - folderName, - created_filesfolder_dict, - path_iterator, - ) - folder_test_node["children"].append(prev_folder_test_node) - # increase iteration through path - prev_folder_test_node = folder_test_node - path_iterator = str(session.fspath) + "/".join(nested_folder_list[0:i]) - - # the final folder we get to is the highest folder in the path and therefore we add this as a child to the session - if (prev_folder_test_node is not None) and ( - prev_folder_test_node.get("id_") not in session_children_dict - ): - session_children_dict[ - prev_folder_test_node.get("id_") - ] = prev_folder_test_node - session_test_node["children"] = list(session_children_dict.values()) - return session_test_node, errors - - -def createTestItem(test_case) -> TestItem: - return { - "name": test_case.name, - "path": str(test_case.fspath), - "lineno": test_case.location[1] + 1, - "type_": TestNodeTypeEnum.test, - "id_": str(test_case.nodeid), - "runID": test_case.nodeid, # can I use this two times? - } - - -def createSessionTestNode(session) -> TestNode: - return { - "name": session.name, - "path": str(session.fspath), - "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder - "children": [], - "id_": str(session.fspath), - } - - class PayloadDict(TypedDict): cwd: str status: Literal["success", "error"] @@ -344,5 +190,3 @@ def sendPost(cwd, tests): {data}""" result = s.socket.sendall(request.encode("utf-8")) # type: ignore - # request = json.dumps(payload) - # result = s.socket.sendall(request.encode("utf-8")) # type: ignore From c14c828e9e1f7186469557fb095441585daf6534 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 14 Oct 2022 12:03:09 -0700 Subject: [PATCH 31/49] broke apart algo into functions --- .../pytest_vscode_integration.py | 159 ++++++++++-------- .../pytest/pytestDiscoveryAdapter.ts | 11 +- 2 files changed, 90 insertions(+), 80 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index cae0bf8568f8..43b8263ace34 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -6,6 +6,7 @@ import sys from dbm.ndbm import library from typing import KeysView, List, Literal, Optional, Tuple, TypedDict, Union +from unittest import TestCase import debugpy import pytest @@ -40,8 +41,6 @@ class TestNode(TestData): PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, PYTHON_FILES) -from testing_tools import socket_manager - # Add the lib path to sys.path to find the typing_extensions module. sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) from testing_tools import socket_manager @@ -52,8 +51,15 @@ class TestNode(TestData): # session # test Case -# modules folders1/folders2 (can be in classes) -# test cases +# def pytest_addoption(parser): +# group = parser.getgroup("vscode-integration") +# group.addoption( +# "--port", +# action="store", +# dest="port_arg", +# default="500", +# help="Get the port value to send back data to.", +# ) # def pytest_configure(config): @@ -65,91 +71,57 @@ class TestNode(TestData): def pytest_collection_finish(session): node, error = build_test_tree(session) cwd = os.getcwd() - print("session, test node") + # add error check sendPost(cwd, node) -def buildPayload(): - print("building payload") - - def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: - print("building test tree") errors: List[str] = [] # how do I check for errors - session_test_node: TestNode = { - "path": str(session.fspath), - "name": session.name, - "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder - "children": [], - "id_": str(session.fspath), - } - testNode_file_dict: dict[pytest.Module, TestNode] = dict() - session_children_dict: dict[str, TestNode] = dict() + session_test_node = createSessionTestNode(session) + testNode_file_dict: dict[ + pytest.Module, TestNode + ] = dict() # a dictionary of all files in the session + session_children_dict: dict[ + str, TestNode + ] = dict() # a dictionary of all direct children of the session + # iterate through all the test items in the session for test_case in session.items: - testNode_test: TestItem = { - "name": test_case.name, - "path": str(test_case.fspath), - "lineno": test_case.location[1], # idk worth a shot - "type_": TestNodeTypeEnum.test, - "id_": str(test_case.nodeid), - "runID": test_case.nodeid, # can I use this two times? - } - print("item: ", test_case, type(test_case)) - print("parent: ", test_case.parent, type(test_case.parent)) - # if the parent object doesn't already exist + testNode_test = createTestItem(test_case) + # if the parent object file doesn't already exist if ( test_case.parent not in testNode_file_dict.keys() and type(test_case.parent) == pytest.Module ): - # create node for file - ti: TestNode = { - "name": test_case.parent.name, - "path": str(test_case.parent.fspath.basename), - "type_": TestNodeTypeEnum.file, # check if this is a file or a folder - "id_": str(test_case.parent.fspath), - "children": [testNode_test], - } - testNode_file_dict[test_case.parent] = ti - elif type(test_case.parent) == pytest.Module: + testNode_file_dict[test_case.parent] = createFileTestNode( + test_case.parent, testNode_test + ) + elif type(test_case.parent) != pytest.Module: print("AAA", testNode_file_dict[test_case.parent].keys()) (testNode_file_dict[test_case.parent]).get("children").append(testNode_test) - created_dict: dict[str, TestNode] = {} + created_filesfolder_dict: dict[str, TestNode] = {} for file_module, testNode_file in testNode_file_dict.items(): - print("TYPE", type(file_module)) - print("parent", file_module.name) name = str(file_module.name) prev_folder_test_node: TestNode = testNode_file if "/" in name: - print("this folder is nested") + # it is a nested folder structure and so new objects need to be created nested_folder_list = name.split("/") - print("folder list", nested_folder_list) path_iterator = ( str(session.fspath) + "/" + "".join(nested_folder_list[0:-1]) - ) # CHECK - print("len", len(nested_folder_list)) - print("f", file_module) + ) for i in range(len(nested_folder_list) - 2, -1, -1): folderName = nested_folder_list[i] - print("folder", nested_folder_list[i]) - print("i", i) - print("path iterator", path_iterator) - folder_test_node: TestNode - if folderName in created_dict.keys(): - folder_test_node = created_dict[folderName] - folder_test_node["children"].append(prev_folder_test_node) - print("added child", folder_test_node) - else: - temp: TestNode = { - "name": nested_folder_list[i], - "path": str(path_iterator), - "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder - "id_": str(path_iterator), - "children": [prev_folder_test_node], - } - folder_test_node = temp - created_dict[folderName] = folder_test_node + folder_test_node = accessOrCreateFolder( + folderName, + created_filesfolder_dict, + prev_folder_test_node, + path_iterator, + ) + folder_test_node["children"].append(prev_folder_test_node) + # increase iteration through path prev_folder_test_node = folder_test_node path_iterator = str(session.fspath) + "".join(nested_folder_list[0:i]) + + # the final folder we get to is the highest folder in the path and therefore we add this as a child to the session if (prev_folder_test_node != None) and ( prev_folder_test_node.get("id_") not in session_children_dict.keys() ): @@ -157,13 +129,60 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: prev_folder_test_node.get("id_") ] = prev_folder_test_node session_test_node["children"] = list(session_children_dict.values()) - print("STN", session_test_node) return session_test_node, errors -def build_test_node(path: str, name: str, id: str, type_: TestNodeTypeEnum) -> TestNode: - print("building test node") - return {"path": path, "name": name, "type_": type_, "children": [], "id_": id} +def createSessionTestNode(session) -> TestNode: + return { + "path": str(session.fspath), + "name": session.name, + "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder + "children": [], + "id_": str(session.fspath), + } + + +def createTestItem(test_case) -> TestItem: + return { + "name": test_case.name, + "path": str(test_case.fspath), + "lineno": test_case.location[1] + 1, # idk worth a shot + "type_": TestNodeTypeEnum.test, + "id_": str(test_case.nodeid), + "runID": test_case.nodeid, # can I use this two times? + } + + +def createFileTestNode(file_module, testNode_test) -> TestNode: + return { + "name": str(file_module.fspath.basename), + "path": str(file_module.fspath), + "type_": TestNodeTypeEnum.file, + "id_": str(file_module.fspath), + "children": [testNode_test], + } + + +def createFolderTestNode(folderName, path_iterator) -> TestNode: + return { + "name": folderName, + "path": str(path_iterator), + "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder + "id_": str(path_iterator), + "children": [], + } + + +def accessOrCreateFolder( + folderName, created_filesfolder_dict, prev_folder_test_node, path_iterator +) -> TestNode: + if folderName in created_filesfolder_dict.keys(): # exists in the dictionary + return created_filesfolder_dict[folderName] + else: + # create new + temp = createFolderTestNode(folderName, path_iterator) + created_filesfolder_dict[folderName] = temp + return temp class PayloadDict(TypedDict): diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index ab28ea59f91b..8cdd96def619 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -70,7 +70,7 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const execService = await executionFactory.createActivatedEnvironment(creationOptions); try { - execService.exec(['-m', 'pytest', '--collect-only', '--port', '500'].concat(pytestArgs), spawnOptions); + execService.exec(['-m', 'pytest', '--collect-only'].concat(pytestArgs), spawnOptions); } catch (ex) { console.error(ex); } @@ -78,12 +78,3 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { return this.deferred.promise; } } - -// function buildDiscoveryCommand(script: string, args: string[]): TestDiscoveryCommand { -// const discoveryScript = script; - -// return { -// script: discoveryScript, -// args: [...args], -// }; -// } From ea3da6d2a5e92e9ebd64afb5dc96a42c06431030 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 14 Oct 2022 12:51:02 -0700 Subject: [PATCH 32/49] with unittest --- .../pytest_vscode_integration.py | 81 ++++++++++++------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index 43b8263ace34..a08204354757 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -51,23 +51,6 @@ class TestNode(TestData): # session # test Case -# def pytest_addoption(parser): -# group = parser.getgroup("vscode-integration") -# group.addoption( -# "--port", -# action="store", -# dest="port_arg", -# default="500", -# help="Get the port value to send back data to.", -# ) - - -# def pytest_configure(config): -# inputArgs = vars(config.option) -# port = inputArgs["port_arg"] -# print("portValue", port) - - def pytest_collection_finish(session): node, error = build_test_tree(session) cwd = os.getcwd() @@ -76,7 +59,7 @@ def pytest_collection_finish(session): def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: - errors: List[str] = [] # how do I check for errors + errors: List[str] = [] # TODO: how do I check for errors session_test_node = createSessionTestNode(session) testNode_file_dict: dict[ pytest.Module, TestNode @@ -84,20 +67,39 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: session_children_dict: dict[ str, TestNode ] = dict() # a dictionary of all direct children of the session + testNode_class_dict: dict[ + str, TestNode + ] = dict() # a dictionary of all direct children of the session # iterate through all the test items in the session for test_case in session.items: testNode_test = createTestItem(test_case) # if the parent object file doesn't already exist - if ( - test_case.parent not in testNode_file_dict.keys() - and type(test_case.parent) == pytest.Module - ): - testNode_file_dict[test_case.parent] = createFileTestNode( - test_case.parent, testNode_test - ) - elif type(test_case.parent) != pytest.Module: - print("AAA", testNode_file_dict[test_case.parent].keys()) - (testNode_file_dict[test_case.parent]).get("children").append(testNode_test) + if type(test_case.parent) == pytest.Module: + if test_case.parent not in testNode_file_dict.keys(): + testNode_file_dict[test_case.parent] = createFileTestNode( + test_case.parent, testNode_test + ) + else: + (testNode_file_dict[test_case.parent]).get("children").append( + testNode_test + ) + else: + # this means its a unittest class + # create class + testNode_class = accessOrCreateClass(test_case.parent, testNode_class_dict) + testNode_class["children"].append(testNode_test) + parent_module = test_case.parent.parent + # create file that wraps class + if parent_module not in testNode_file_dict.keys(): + testNode_file_dict[parent_module] = createFileTestNode( + parent_module, testNode_class + ) + else: + if testNode_class not in testNode_file_dict[parent_module]["children"]: + (testNode_file_dict[parent_module]).get("children").append( + testNode_class + ) + created_filesfolder_dict: dict[str, TestNode] = {} for file_module, testNode_file in testNode_file_dict.items(): name = str(file_module.name) @@ -113,7 +115,6 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: folder_test_node = accessOrCreateFolder( folderName, created_filesfolder_dict, - prev_folder_test_node, path_iterator, ) folder_test_node["children"].append(prev_folder_test_node) @@ -153,6 +154,16 @@ def createTestItem(test_case) -> TestItem: } +def createClassTestNode(class_module) -> TestNode: + return { + "name": class_module.name, + "path": str(class_module.fspath), + "type_": TestNodeTypeEnum.class_, + "children": [], + "id_": str(class_module._nodeid), + } + + def createFileTestNode(file_module, testNode_test) -> TestNode: return { "name": str(file_module.fspath.basename), @@ -174,7 +185,7 @@ def createFolderTestNode(folderName, path_iterator) -> TestNode: def accessOrCreateFolder( - folderName, created_filesfolder_dict, prev_folder_test_node, path_iterator + folderName, created_filesfolder_dict, path_iterator ) -> TestNode: if folderName in created_filesfolder_dict.keys(): # exists in the dictionary return created_filesfolder_dict[folderName] @@ -185,6 +196,16 @@ def accessOrCreateFolder( return temp +def accessOrCreateClass(class_module, testNode_class_dict) -> TestNode: + if class_module.name in testNode_class_dict.keys(): # exists in the dictionary + return testNode_class_dict[class_module.name] + else: + # create new + temp = createClassTestNode(class_module) + testNode_class_dict[class_module.name] = temp + return temp + + class PayloadDict(TypedDict): cwd: str status: Literal["success", "error"] From 3edc6cea494dce2e030efcfa10b54b9ccc574620 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 14 Oct 2022 14:00:38 -0700 Subject: [PATCH 33/49] fix extra nesting paths --- .../pytest-vscode-integration/pytest_vscode_integration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index a08204354757..cf63ec237159 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -108,7 +108,7 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: # it is a nested folder structure and so new objects need to be created nested_folder_list = name.split("/") path_iterator = ( - str(session.fspath) + "/" + "".join(nested_folder_list[0:-1]) + str(session.fspath) + "/" + "/".join(nested_folder_list[0:-1]) ) for i in range(len(nested_folder_list) - 2, -1, -1): folderName = nested_folder_list[i] @@ -120,7 +120,7 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: folder_test_node["children"].append(prev_folder_test_node) # increase iteration through path prev_folder_test_node = folder_test_node - path_iterator = str(session.fspath) + "".join(nested_folder_list[0:i]) + path_iterator = str(session.fspath) + "/".join(nested_folder_list[0:i]) # the final folder we get to is the highest folder in the path and therefore we add this as a child to the session if (prev_folder_test_node != None) and ( From dbdebabf5e15b817961a19761c57d840f34da29a Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 14 Oct 2022 14:30:28 -0700 Subject: [PATCH 34/49] add general search method --- .../pytest_vscode_integration.py | 87 ++++++++----------- 1 file changed, 38 insertions(+), 49 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index cf63ec237159..21ed8eafe836 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -8,10 +8,8 @@ from typing import KeysView, List, Literal, Optional, Tuple, TypedDict, Union from unittest import TestCase -import debugpy import pytest -debugpy.connect(5678) # Inherit from str so it's JSON serializable. class TestNodeTypeEnum(str, enum.Enum): @@ -77,28 +75,21 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: if type(test_case.parent) == pytest.Module: if test_case.parent not in testNode_file_dict.keys(): testNode_file_dict[test_case.parent] = createFileTestNode( - test_case.parent, testNode_test - ) - else: - (testNode_file_dict[test_case.parent]).get("children").append( - testNode_test + test_case.parent ) + testNode_file_dict[test_case.parent].get("children").append(testNode_test) else: # this means its a unittest class # create class - testNode_class = accessOrCreateClass(test_case.parent, testNode_class_dict) + testNode_class = accessOrCreateGeneral( + test_case.parent.name, testNode_class_dict, test_case.parent.fspath + ) testNode_class["children"].append(testNode_test) parent_module = test_case.parent.parent # create file that wraps class if parent_module not in testNode_file_dict.keys(): - testNode_file_dict[parent_module] = createFileTestNode( - parent_module, testNode_class - ) - else: - if testNode_class not in testNode_file_dict[parent_module]["children"]: - (testNode_file_dict[parent_module]).get("children").append( - testNode_class - ) + testNode_file_dict[parent_module] = createFileTestNode(parent_module) + testNode_file_dict[parent_module].get("children").append(testNode_class) created_filesfolder_dict: dict[str, TestNode] = {} for file_module, testNode_file in testNode_file_dict.items(): @@ -112,7 +103,7 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: ) for i in range(len(nested_folder_list) - 2, -1, -1): folderName = nested_folder_list[i] - folder_test_node = accessOrCreateFolder( + folder_test_node = accessOrCreateGeneral( folderName, created_filesfolder_dict, path_iterator, @@ -133,44 +124,44 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: return session_test_node, errors -def createSessionTestNode(session) -> TestNode: - return { - "path": str(session.fspath), - "name": session.name, - "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder - "children": [], - "id_": str(session.fspath), - } - - def createTestItem(test_case) -> TestItem: return { "name": test_case.name, "path": str(test_case.fspath), - "lineno": test_case.location[1] + 1, # idk worth a shot + "lineno": test_case.location[1] + 1, "type_": TestNodeTypeEnum.test, "id_": str(test_case.nodeid), "runID": test_case.nodeid, # can I use this two times? } -def createClassTestNode(class_module) -> TestNode: +def createSessionTestNode(session) -> TestNode: return { - "name": class_module.name, - "path": str(class_module.fspath), + "name": session.name, + "path": str(session.fspath), + "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder + "children": [], + "id_": str(session.fspath), + } + + +def createClassTestNode(class_module_name, class_module_path) -> TestNode: + return { + "name": class_module_name, + "path": str(class_module_path), "type_": TestNodeTypeEnum.class_, "children": [], - "id_": str(class_module._nodeid), + "id_": str(class_module_path), } -def createFileTestNode(file_module, testNode_test) -> TestNode: +def createFileTestNode(file_module) -> TestNode: return { "name": str(file_module.fspath.basename), "path": str(file_module.fspath), "type_": TestNodeTypeEnum.file, "id_": str(file_module.fspath), - "children": [testNode_test], + "children": [], } @@ -184,26 +175,24 @@ def createFolderTestNode(folderName, path_iterator) -> TestNode: } -def accessOrCreateFolder( - folderName, created_filesfolder_dict, path_iterator -) -> TestNode: - if folderName in created_filesfolder_dict.keys(): # exists in the dictionary - return created_filesfolder_dict[folderName] +def accessOrCreateGeneral(test_node_name, test_node_dict, test_node_path) -> TestNode: + if test_node_name in test_node_dict.keys(): # exists in the dictionary + return test_node_dict[test_node_name] else: # create new - temp = createFolderTestNode(folderName, path_iterator) - created_filesfolder_dict[folderName] = temp + temp = createFolderTestNode(test_node_name, test_node_path) + test_node_dict[test_node_name] = temp return temp -def accessOrCreateClass(class_module, testNode_class_dict) -> TestNode: - if class_module.name in testNode_class_dict.keys(): # exists in the dictionary - return testNode_class_dict[class_module.name] - else: - # create new - temp = createClassTestNode(class_module) - testNode_class_dict[class_module.name] = temp - return temp +# def accessOrCreateClass(class_module, testNode_class_dict) -> TestNode: +# if class_module.name in testNode_class_dict.keys(): # exists in the dictionary +# return testNode_class_dict[class_module.name] +# else: +# # create new +# temp = createClassTestNode(class_module.name, class_module.fspath) +# testNode_class_dict[class_module.name] = temp +# return temp class PayloadDict(TypedDict): From 5284ad9254ed42a9370d47ffa94d37db3def3ef2 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 7 Nov 2022 12:48:24 -0800 Subject: [PATCH 35/49] add example --- .../pytest_vscode_integration.py | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py index 21ed8eafe836..fc05cdf78367 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py @@ -49,6 +49,14 @@ class TestNode(TestData): # session # test Case +# modules folders1/folders2 (can be in classes) +# test cases + +# module +# class +# test case + + def pytest_collection_finish(session): node, error = build_test_tree(session) cwd = os.getcwd() @@ -73,11 +81,13 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: testNode_test = createTestItem(test_case) # if the parent object file doesn't already exist if type(test_case.parent) == pytest.Module: - if test_case.parent not in testNode_file_dict.keys(): + if test_case.parent not in testNode_file_dict: testNode_file_dict[test_case.parent] = createFileTestNode( test_case.parent ) - testNode_file_dict[test_case.parent].get("children").append(testNode_test) + testNode_file_dict[test_case.parent].get("children").append( + testNode_test + ) # use set default else: # this means its a unittest class # create class @@ -99,9 +109,13 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: # it is a nested folder structure and so new objects need to be created nested_folder_list = name.split("/") path_iterator = ( - str(session.fspath) + "/" + "/".join(nested_folder_list[0:-1]) + str(session.fspath) + + "/" + + "/".join( + nested_folder_list[0:-1] + ) # check to see if windows style (more fancy stuff path lib if windows or posix via API in os module) ) - for i in range(len(nested_folder_list) - 2, -1, -1): + for i in range(len(nested_folder_list) - 2, -1, -1): # reverse and slice folderName = nested_folder_list[i] folder_test_node = accessOrCreateGeneral( folderName, @@ -114,8 +128,8 @@ def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: path_iterator = str(session.fspath) + "/".join(nested_folder_list[0:i]) # the final folder we get to is the highest folder in the path and therefore we add this as a child to the session - if (prev_folder_test_node != None) and ( - prev_folder_test_node.get("id_") not in session_children_dict.keys() + if (prev_folder_test_node is not None) and ( + prev_folder_test_node.get("id_") not in session_children_dict ): session_children_dict[ prev_folder_test_node.get("id_") From 400ae9f1f978d5d4631103e9d309ee4ec42c976b Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 7 Nov 2022 14:25:01 -0800 Subject: [PATCH 36/49] undo all changes to src --- .../common/process/pythonExecutionFactory.ts | 1 - src/client/interpreter/activation/service.ts | 39 +++++---- .../testing/testController/common/server.ts | 10 +-- .../testing/testController/common/types.ts | 16 +--- .../testing/testController/controller.ts | 78 +++++------------- .../testController/pytest/arguments.ts | 2 +- .../pytest/pytestDiscoveryAdapter.ts | 80 ------------------- .../pytest/pytestExecutionAdapter.ts | 73 ----------------- .../unittest/unittestDiscoveryAdapter.ts | 4 +- .../unittest/unittestExecutionAdapter.ts | 2 +- .../testController/workspaceTestAdapter.ts | 14 +--- .../commands/setInterpreter.unit.test.ts | 1 - .../activation/service.unit.test.ts | 7 +- .../venvCreationProvider.unit.test.ts | 12 --- .../testDiscoveryAdapter.unit.test.ts | 2 +- .../workspaceTestAdapter.unit.test.ts | 4 +- 16 files changed, 63 insertions(+), 282 deletions(-) diff --git a/src/client/common/process/pythonExecutionFactory.ts b/src/client/common/process/pythonExecutionFactory.ts index 3684fe9cb0ed..02c42beb1400 100644 --- a/src/client/common/process/pythonExecutionFactory.ts +++ b/src/client/common/process/pythonExecutionFactory.ts @@ -92,7 +92,6 @@ export class PythonExecutionFactory implements IPythonExecutionFactory { public async createActivatedEnvironment( options: ExecutionFactoryCreateWithEnvironmentOptions, ): Promise { - console.log('made it here'); const envVars = await this.activationHelper.getActivatedEnvironmentVariables( options.resource, options.interpreter, diff --git a/src/client/interpreter/activation/service.ts b/src/client/interpreter/activation/service.ts index 4653251752fb..e4074b070853 100644 --- a/src/client/interpreter/activation/service.ts +++ b/src/client/interpreter/activation/service.ts @@ -186,7 +186,6 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi if (!shellInfo) { return; } - let isPossiblyCondaEnv = false; try { let command: string | undefined; let [args, parse] = internalScripts.printEnvVariables(); @@ -204,9 +203,23 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi command = [...pythonArgv, ...args].map((arg) => arg.toCommandArgumentForPythonExt()).join(' '); } } - isPossiblyCondaEnv = activationCommands.join(' ').toLowerCase().includes('conda'); - // Run the activate command collect the environment from it. - const activationCommand = this.fixActivationCommands(activationCommands).join(' && '); + if (!command) { + const activationCommands = await this.helper.getEnvironmentActivationShellCommands( + resource, + shellInfo.shellType, + interpreter, + ); + traceVerbose(`Activation Commands received ${activationCommands} for shell ${shellInfo.shell}`); + if (!activationCommands || !Array.isArray(activationCommands) || activationCommands.length === 0) { + return; + } + // Run the activate command collect the environment from it. + const activationCommand = this.fixActivationCommands(activationCommands).join(' && '); + // In order to make sure we know where the environment output is, + // put in a dummy echo we can look for + command = `${activationCommand} && echo '${ENVIRONMENT_PREFIX}' && python ${args.join(' ')}`; + } + const processService = await this.processServiceFactory.create(resource); const customEnvVars = await this.envVarsService.getEnvironmentVariables(resource); const hasCustomEnvVars = Object.keys(customEnvVars).length; @@ -218,14 +231,6 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi env[PYTHON_WARNINGS] = 'ignore'; traceVerbose(`${hasCustomEnvVars ? 'Has' : 'No'} Custom Env Vars`); - - // In order to make sure we know where the environment output is, - // put in a dummy echo we can look for - const [args, parse] = internalScripts.printEnvVariables(); - args.forEach((arg, i) => { - args[i] = arg.toCommandArgumentForPythonExt(); - }); - const command = `${activationCommand} && echo '${ENVIRONMENT_PREFIX}' && python ${args.join(' ')}`; traceVerbose(`Activating Environment to capture Environment variables, ${command}`); // Do some wrapping of the call. For two reasons: @@ -243,7 +248,10 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi result = await processService.shellExec(command, { env, shell: shellInfo.shell, - timeout: isPossiblyCondaEnv ? CONDA_ENVIRONMENT_TIMEOUT : ENVIRONMENT_TIMEOUT, + timeout: + interpreter?.envType === EnvironmentType.Conda + ? CONDA_ENVIRONMENT_TIMEOUT + : ENVIRONMENT_TIMEOUT, maxBuffer: 1000 * 1000, throwOnStdErr: false, }); @@ -289,7 +297,7 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi } catch (e) { traceError('getActivatedEnvironmentVariables', e); sendTelemetryEvent(EventName.ACTIVATE_ENV_TO_GET_ENV_VARS_FAILED, undefined, { - isPossiblyCondaEnv, + isPossiblyCondaEnv: interpreter?.envType === EnvironmentType.Conda, terminal: shellInfo.shellType, }); @@ -307,6 +315,9 @@ export class EnvironmentActivationService implements IEnvironmentActivationServi @traceDecoratorError('Failed to parse Environment variables') @traceDecoratorVerbose('parseEnvironmentOutput', TraceOptions.None) protected parseEnvironmentOutput(output: string, parse: (out: string) => NodeJS.ProcessEnv | undefined) { + if (output.indexOf(ENVIRONMENT_PREFIX) === -1) { + return parse(output); + } output = output.substring(output.indexOf(ENVIRONMENT_PREFIX) + ENVIRONMENT_PREFIX.length); const js = output.substring(output.indexOf('{')).trim(); return parse(js); diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 48c0b81972af..adf5bba1a33c 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -71,12 +71,6 @@ export class PythonTestServer implements ITestServer, Disposable { return (this.server.address() as net.AddressInfo).port; } - public createUUID(cwd: string): string { - const uuid = crypto.randomUUID(); - this.uuids.set(uuid, cwd); - return uuid; - } - public dispose(): void { this.server.close(); this._onDataReceived.dispose(); @@ -87,13 +81,15 @@ export class PythonTestServer implements ITestServer, Disposable { } async sendCommand(options: TestCommandOptions): Promise { - const uuid = this.createUUID(options.cwd); + const uuid = crypto.randomUUID(); const spawnOptions: SpawnOptions = { token: options.token, cwd: options.cwd, throwOnStdErr: true, }; + this.uuids.set(uuid, options.cwd); + // Create the Python environment in which to execute the command. const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { allowEnvironmentFetchExceptions: false, diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index b61fad1c9167..064307ca8d9a 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -12,7 +12,6 @@ import { Uri, WorkspaceFolder, } from 'vscode'; -import { IPythonExecutionFactory } from '../../../common/process/types'; import { TestDiscoveryOptions } from '../../common/types'; export type TestRunInstanceOptions = TestRunOptions & { @@ -152,17 +151,6 @@ export type TestCommandOptions = { testIds?: string[]; }; -export type TestCommandOptionsPytest = { - workspaceFolder: Uri; - cwd: string; - commandStr: string; - token?: CancellationToken; - outChannel?: OutputChannel; - debugBool?: boolean; - testIds?: string[]; - env: { [key: string]: string | undefined }; -}; - /** * Interface describing the server that will send test commands to the Python side, and process responses. * @@ -173,12 +161,10 @@ export interface ITestServer { readonly onDataReceived: Event; sendCommand(options: TestCommandOptions): Promise; serverReady(): Promise; - getPort(): number; - createUUID(cwd: string): string; } export interface ITestDiscoveryAdapter { - discoverTests(uri: Uri, executionFactory: IPythonExecutionFactory): Promise; + discoverTests(uri: Uri): Promise; } // interface for execution/runner adapter diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index c6a036c96183..fafdd3fafe7e 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -38,12 +38,9 @@ import { TestRefreshOptions, ITestExecutionAdapter, } from './common/types'; -// TODO: create pytest and add to import -import { UnittestTestDiscoveryAdapter } from './unittest/unittestDiscoveryAdapter'; -import { UnittestTestExecutionAdapter } from './unittest/unittestExecutionAdapter'; -import { PytestTestDiscoveryAdapter } from './pytest/pytestDiscoveryAdapter'; -import { PytestTestExecutionAdapter } from './pytest/pytestExecutionAdapter'; +import { UnittestTestDiscoveryAdapter } from './unittest/testDiscoveryAdapter'; import { WorkspaceTestAdapter } from './workspaceTestAdapter'; +import { UnittestTestExecutionAdapter } from './unittest/testExecutionAdapter'; import { ITestDebugLauncher } from '../common/types'; // Types gymnastics to make sure that sendTriggerTelemetry only accepts the correct types. @@ -144,6 +141,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc }); return this.refreshTestData(undefined, { forceRefresh: true }); }; + this.pythonTestServer = new PythonTestServer(this.pythonExecFactory, this.debugLauncher); } @@ -151,7 +149,6 @@ export class PythonTestController implements ITestController, IExtensionSingleAc traceVerbose('Waiting for test server to start...'); await this.pythonTestServer.serverReady(); traceVerbose('Test server started.'); - console.debug('Test server started'); const workspaces: readonly WorkspaceFolder[] = this.workspaceService.workspaceFolders || []; workspaces.forEach((workspace) => { const settings = this.configSettings.getSettings(workspace.uri); @@ -159,19 +156,14 @@ export class PythonTestController implements ITestController, IExtensionSingleAc let discoveryAdapter: ITestDiscoveryAdapter; let executionAdapter: ITestExecutionAdapter; let testProvider: TestProvider; - if (settings.testing.pytestEnabled) { - console.log('settings.testing.pytestEnabled = true'); - discoveryAdapter = new PytestTestDiscoveryAdapter(this.pythonTestServer, this.configSettings); // what is the ... for - executionAdapter = new PytestTestExecutionAdapter(this.pythonTestServer, this.configSettings); - testProvider = PYTEST_PROVIDER; - } else if (settings.testing.unittestEnabled) { - console.log('settings.testing.unittestEnabled = true'); + if (settings.testing.unittestEnabled) { discoveryAdapter = new UnittestTestDiscoveryAdapter(this.pythonTestServer, this.configSettings); executionAdapter = new UnittestTestExecutionAdapter(this.pythonTestServer, this.configSettings); testProvider = UNITTEST_PROVIDER; } else { - // this would be an error because neither is enabled? - discoveryAdapter = new UnittestTestDiscoveryAdapter(this.pythonTestServer, this.configSettings); + // TODO: PYTEST DISCOVERY ADAPTER + // this is a placeholder for now + discoveryAdapter = new UnittestTestDiscoveryAdapter(this.pythonTestServer, { ...this.configSettings }); executionAdapter = new UnittestTestExecutionAdapter(this.pythonTestServer, this.configSettings); testProvider = PYTEST_PROVIDER; } @@ -235,42 +227,27 @@ export class PythonTestController implements ITestController, IExtensionSingleAc if (settings.testing.pytestEnabled) { traceVerbose(`Testing: Refreshing test data for ${uri.fsPath}`); - // can I move these out of the if statement - const workspace = this.workspaceService.getWorkspaceFolder(uri); - console.warn(`Discover tests for workspace name: ${workspace?.name} - uri: ${uri.fsPath}`); - const testAdapter = - this.testAdapters.get(uri) || (this.testAdapters.values().next().value as WorkspaceTestAdapter); - testAdapter.discoverTests( - this.testController, - this.refreshCancellation.token, - this.testAdapters.size > 1, - this.workspaceService.workspaceFile?.fsPath, - this.pythonExecFactory, - ); // Ensure we send test telemetry if it gets disabled again this.sendTestDisabledTelemetry = true; - // comment below 229 to run the new way and uncomment above 212 ~ 227 - // await this.unittest.refreshTestData(this.testController, uri, this.refreshCancellation.token); - // await this.pytest.refreshTestData(this.testController, uri, this.refreshCancellation.token); + await this.pytest.refreshTestData(this.testController, uri, this.refreshCancellation.token); } else if (settings.testing.unittestEnabled) { // TODO: Use new test discovery mechanism - traceVerbose(`Testing: Refreshing test data for ${uri.fsPath}`); - const workspace = this.workspaceService.getWorkspaceFolder(uri); - console.warn(`Discover tests for workspace name: ${workspace?.name} - uri: ${uri.fsPath}`); - const testAdapter = - this.testAdapters.get(uri) || (this.testAdapters.values().next().value as WorkspaceTestAdapter); - testAdapter.discoverTests( - this.testController, - this.refreshCancellation.token, - this.testAdapters.size > 1, - this.workspaceService.workspaceFile?.fsPath, - this.pythonExecFactory, - ); - // Ensure we send test telemetry if it gets disabled again - this.sendTestDisabledTelemetry = true; + // traceVerbose(`Testing: Refreshing test data for ${uri.fsPath}`); + // const workspace = this.workspaceService.getWorkspaceFolder(uri); + // console.warn(`Discover tests for workspace name: ${workspace?.name} - uri: ${uri.fsPath}`); + // const testAdapter = + // this.testAdapters.get(uri) || (this.testAdapters.values().next().value as WorkspaceTestAdapter); + // testAdapter.discoverTests( + // this.testController, + // this.refreshCancellation.token, + // this.testAdapters.size > 1, + // this.workspaceService.workspaceFile?.fsPath, + // ); + // // Ensure we send test telemetry if it gets disabled again + // this.sendTestDisabledTelemetry = true; // comment below 229 to run the new way and uncomment above 212 ~ 227 - // await this.unittest.refreshTestData(this.testController, uri, this.refreshCancellation.token); + await this.unittest.refreshTestData(this.testController, uri, this.refreshCancellation.token); } else { if (this.sendTestDisabledTelemetry) { this.sendTestDisabledTelemetry = false; @@ -316,7 +293,6 @@ export class PythonTestController implements ITestController, IExtensionSingleAc const settings = this.configSettings.getSettings(item.uri); if (settings.testing.pytestEnabled) { return this.pytest.resolveChildren(this.testController, item, this.refreshCancellation.token); - // ** check resolve children functionality } if (settings.testing.unittestEnabled) { return this.unittest.resolveChildren(this.testController, item, this.refreshCancellation.token); @@ -335,11 +311,6 @@ export class PythonTestController implements ITestController, IExtensionSingleAc }), ); } - console.log('HERE2'); - this.testController.items.forEach((element) => console.log(element)); - - console.log(this.testController.items); - console.log('size', this.testController.items.size); return Promise.resolve(); } @@ -389,11 +360,9 @@ export class PythonTestController implements ITestController, IExtensionSingleAc if (testItems.length > 0) { if (settings.testing.pytestEnabled) { sendTelemetryEvent(EventName.UNITTEST_RUN, undefined, { - // seems like this telemetry is named incorrectly? tool: 'pytest', debugging: request.profile?.kind === TestRunProfileKind.Debug, }); - // ** update this to reflect the nwe execution style before return this.pytest.runTests( { includes: testItems, @@ -440,7 +409,6 @@ export class PythonTestController implements ITestController, IExtensionSingleAc } if (!settings.testing.pytestEnabled && !settings.testing.unittestEnabled) { - // ** this could be the logic I am looking for unconfiguredWorkspaces.push(workspace); } return Promise.resolve(); @@ -524,8 +492,6 @@ export class PythonTestController implements ITestController, IExtensionSingleAc ); } - // ** not sure about the telemetry - /** * Send UNITTEST_DISCOVERY_TRIGGER telemetry event only once per trigger type. * diff --git a/src/client/testing/testController/pytest/arguments.ts b/src/client/testing/testController/pytest/arguments.ts index 25b2853539e7..78b451acdd6b 100644 --- a/src/client/testing/testController/pytest/arguments.ts +++ b/src/client/testing/testController/pytest/arguments.ts @@ -263,7 +263,7 @@ export function preparePytestArgumentsForDiscovery(options: TestDiscoveryOptions // Remove unwanted arguments (which happen to be test directories & test specific args). const args = pytestFilterArguments(options.args, TestFilter.discovery); if (options.ignoreCache && args.indexOf('--cache-clear') === -1) { - args.splice(0, 0, 'bbbbb--cache-clear'); + args.splice(0, 0, '--cache-clear'); } if (args.indexOf('-s') === -1) { args.splice(0, 0, '-s'); diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 8cdd96def619..e69de29bb2d1 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -import * as path from 'path'; -import { Uri } from 'vscode'; -import { - ExecutionFactoryCreateWithEnvironmentOptions, - IPythonExecutionFactory, - SpawnOptions, -} from '../../../common/process/types'; -import { IConfigurationService } from '../../../common/types'; -import { createDeferred, Deferred } from '../../../common/utils/async'; -import { EXTENSION_ROOT_DIR } from '../../../constants'; -import { DataReceivedEvent, DiscoveredTestPayload, ITestDiscoveryAdapter, ITestServer } from '../common/types'; - -/** - * Wrapper class for unittest test discovery. This is where we call `runTestCommand`. #this seems incorrectly copied - */ -export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { - private deferred: Deferred | undefined; - - private cwd: string | undefined; - - constructor(public testServer: ITestServer, public configSettings: IConfigurationService) { - testServer.onDataReceived(this.onDataReceivedHandler, this); - } - - public onDataReceivedHandler({ cwd, data }: DataReceivedEvent): void { - if (this.deferred && cwd === this.cwd) { - const testData: DiscoveredTestPayload = JSON.parse(data); - - this.deferred.resolve(testData); - this.deferred = undefined; - } - } - - public async discoverTests(uri: Uri, executionFactory: IPythonExecutionFactory): Promise { - const settings = this.configSettings.getSettings(uri); - const { pytestArgs } = settings.testing; - console.debug(pytestArgs); // do we use pytestArgs anywhere? - - this.cwd = uri.fsPath; - return this.runPytestDiscovery(uri, executionFactory); - } - - async runPytestDiscovery(uri: Uri, executionFactory: IPythonExecutionFactory): Promise { - if (!this.deferred) { - this.deferred = createDeferred(); - const relativePathToPytest = 'pythonFiles/pytest-vscode-integration'; - 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 pythonPathCommand = `${fullPluginPath}${path.delimiter}`.concat(process.env.PYTHONPATH ?? ''); - - const spawnOptions: SpawnOptions = { - cwd: uri.fsPath, - throwOnStdErr: true, - extraVariables: { - PYTHONPATH: pythonPathCommand, - TEST_UUID: uuid.toString(), - TEST_PORT: this.testServer.getPort().toString(), - }, - }; - - // Create the Python environment in which to execute the command. - const creationOptions: ExecutionFactoryCreateWithEnvironmentOptions = { - allowEnvironmentFetchExceptions: false, - resource: uri, - }; - const execService = await executionFactory.createActivatedEnvironment(creationOptions); - - try { - execService.exec(['-m', 'pytest', '--collect-only'].concat(pytestArgs), spawnOptions); - } catch (ex) { - console.error(ex); - } - } - return this.deferred.promise; - } -} diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index 35d62c50e774..e69de29bb2d1 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -import * as path from 'path'; -import { Uri } from 'vscode'; -import { IConfigurationService } from '../../../common/types'; -import { createDeferred, Deferred } from '../../../common/utils/async'; -import { EXTENSION_ROOT_DIR } from '../../../constants'; -import { - DataReceivedEvent, - ExecutionTestPayload, - ITestExecutionAdapter, - ITestServer, - TestCommandOptions, - TestExecutionCommand, -} from '../common/types'; - -/** - * Wrapper Class for unittest test execution. This is where we call `runTestCommand`? - */ - -export class PytestTestExecutionAdapter implements ITestExecutionAdapter { - private deferred: Deferred | undefined; - - private cwd: string | undefined; - - constructor(public testServer: ITestServer, public configSettings: IConfigurationService) { - testServer.onDataReceived(this.onDataReceivedHandler, this); - } - - public onDataReceivedHandler({ cwd, data }: DataReceivedEvent): void { - if (this.deferred && cwd === this.cwd) { - const testData: ExecutionTestPayload = JSON.parse(data); - - this.deferred.resolve(testData); - this.deferred = undefined; - } - } - - public async runTests(uri: Uri, testIds: string[], debugBool?: boolean): Promise { - if (!this.deferred) { - const settings = this.configSettings.getSettings(uri); - const { unittestArgs } = settings.testing; - - const command = buildExecutionCommand(unittestArgs); - this.cwd = uri.fsPath; - - const options: TestCommandOptions = { - workspaceFolder: uri, - command, - cwd: this.cwd, - debugBool, - testIds, - }; - - this.deferred = createDeferred(); - - // send test command to server - // server fire onDataReceived event once it gets response - this.testServer.sendCommand(options); - } - return this.deferred.promise; - } -} - -function buildExecutionCommand(args: string[]): TestExecutionCommand { - const executionScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'execution.py'); - - return { - script: executionScript, - args: ['--udiscovery', ...args], - }; -} diff --git a/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts b/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts index d50d0ce3961b..f0a5a957807c 100644 --- a/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts +++ b/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts @@ -16,7 +16,7 @@ import { } from '../common/types'; /** - * Wrapper class for unittest test discovery. This is where we call `runTestCommand`. #this seems incorrectly copied + * Wrapper class for unittest test discovery. This is where we call `runTestCommand`. */ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { private deferred: Deferred | undefined; @@ -63,7 +63,7 @@ export class UnittestTestDiscoveryAdapter implements ITestDiscoveryAdapter { } function buildDiscoveryCommand(args: string[]): TestDiscoveryCommand { - const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittest_adapter', 'unittest_discovery.py'); + const discoveryScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'discovery.py'); return { script: discoveryScript, diff --git a/src/client/testing/testController/unittest/unittestExecutionAdapter.ts b/src/client/testing/testController/unittest/unittestExecutionAdapter.ts index 2b4e098323a4..d71dea9fea36 100644 --- a/src/client/testing/testController/unittest/unittestExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/unittestExecutionAdapter.ts @@ -64,7 +64,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { } function buildExecutionCommand(args: string[]): TestExecutionCommand { - const executionScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittest_adapter', 'unittest_execution.py'); + const executionScript = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'unittestadapter', 'execution.py'); return { script: executionScript, diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 01f8bf33256a..0ecab7649745 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -14,7 +14,6 @@ import { Uri, Location, } from 'vscode'; -import { IPythonExecutionFactory } from '../../common/process/types'; import { createDeferred, Deferred } from '../../common/utils/async'; import { Testing } from '../../common/utils/localize'; import { traceError } from '../../logging'; @@ -202,7 +201,6 @@ export class WorkspaceTestAdapter { token?: CancellationToken, isMultiroot?: boolean, workspaceFilePath?: string, - executionFactory?: IPythonExecutionFactory, ): Promise { sendTelemetryEvent(EventName.UNITTEST_DISCOVERING, undefined, { tool: this.testProvider }); @@ -218,13 +216,7 @@ export class WorkspaceTestAdapter { let rawTestData; try { - if (executionFactory !== undefined) { - rawTestData = await this.discoveryAdapter.discoverTests(this.workspaceUri, executionFactory); - console.debug('here'); - console.debug('rawTestData: ', rawTestData); - } else { - console.log('executionFactory is undefined'); - } + rawTestData = await this.discoveryAdapter.discoverTests(this.workspaceUri); deferred.resolve(); } catch (ex) { @@ -347,10 +339,6 @@ function populateTestTree( } // Recursively populate the tree with test data. - for (let i = 0; i < testTreeData.children.length; i = i + 1) { - console.debug('testTreeData.children i= ', i, '', testTreeData.children[i]); - } - testTreeData.children.forEach((child) => { if (!token?.isCancellationRequested) { if (isTestItem(child)) { diff --git a/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts b/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts index 39be1cac3da6..7059fb7ab26f 100644 --- a/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts +++ b/src/test/configuration/interpreterSelector/commands/setInterpreter.unit.test.ts @@ -80,7 +80,6 @@ suite('Set Interpreter Command', () => { workspace = TypeMoq.Mock.ofType(); interpreterService = mock(); when(interpreterService.refreshPromise).thenReturn(undefined); - when(interpreterService.triggerRefresh()).thenResolve(); when(interpreterService.triggerRefresh(anything(), anything())).thenResolve(); workspace.setup((w) => w.rootPath).returns(() => 'rootPath'); diff --git a/src/test/interpreters/activation/service.unit.test.ts b/src/test/interpreters/activation/service.unit.test.ts index 9e705f247ac9..d50b2b5d5995 100644 --- a/src/test/interpreters/activation/service.unit.test.ts +++ b/src/test/interpreters/activation/service.unit.test.ts @@ -58,7 +58,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { architecture: Architecture.x64, }; - function initSetup() { + function initSetup(interpreter: PythonEnvironment | undefined) { helper = mock(TerminalHelper); platform = mock(PlatformService); processServiceFactory = mock(ProcessServiceFactory); @@ -71,6 +71,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { onDidChangeInterpreter = new EventEmitter(); when(envVarsService.onDidEnvironmentVariablesChange).thenReturn(onDidChangeEnvVariables.event); when(interpreterService.onDidChangeInterpreter).thenReturn(onDidChangeInterpreter.event); + when(interpreterService.getActiveInterpreter(anything())).thenResolve(interpreter); service = new EnvironmentActivationService( instance(helper), instance(platform), @@ -89,7 +90,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { [undefined, Uri.parse('a')].forEach((resource) => [undefined, pythonInterpreter].forEach((interpreter) => { suite(title(resource, interpreter), () => { - setup(initSetup); + setup(() => initSetup(interpreter)); test('Unknown os will return empty variables', async () => { when(platform.osType).thenReturn(OSType.Unknown); const env = await service.getActivatedEnvironmentVariables(resource); @@ -102,7 +103,7 @@ suite('Interpreters Activation - Python Environment Variables', () => { osTypes.forEach((osType) => { suite(osType.name, () => { - setup(initSetup); + setup(() => initSetup(interpreter)); test('getEnvironmentActivationShellCommands will be invoked', async () => { when(platform.osType).thenReturn(osType.value); when( diff --git a/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts b/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts index 0fe5bb3565dd..6b6187ed3e4a 100644 --- a/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts +++ b/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts @@ -122,18 +122,6 @@ suite('venv Creation provider tests', () => { ) => task(progressMock.object), ); - progressMock.setup((p) => p.report({ message: CreateEnv.statusStarting })).verifiable(typemoq.Times.once()); - - withProgressStub.callsFake( - ( - _options: ProgressOptions, - task: ( - progress: CreateEnvironmentProgress, - token?: CancellationToken, - ) => Thenable, - ) => task(progressMock.object), - ); - const promise = venvProvider.createEnvironment(); await deferred.promise; assert.isDefined(_next); diff --git a/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts b/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts index 9f8dbeabb4b3..cee4353db09a 100644 --- a/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts +++ b/src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts @@ -7,7 +7,7 @@ import { Uri } from 'vscode'; import { IConfigurationService } from '../../../../client/common/types'; import { EXTENSION_ROOT_DIR } from '../../../../client/constants'; import { ITestServer, TestCommandOptions } from '../../../../client/testing/testController/common/types'; -import { UnittestTestDiscoveryAdapter } from '../../../../client/testing/testController/unittest/unittestDiscoveryAdapter'; +import { UnittestTestDiscoveryAdapter } from '../../../../client/testing/testController/unittest/testDiscoveryAdapter'; suite('Unittest test discovery adapter', () => { let stubConfigSettings: IConfigurationService; diff --git a/src/test/testing/testController/workspaceTestAdapter.unit.test.ts b/src/test/testing/testController/workspaceTestAdapter.unit.test.ts index 8d875ef60f6c..b6be8d6081de 100644 --- a/src/test/testing/testController/workspaceTestAdapter.unit.test.ts +++ b/src/test/testing/testController/workspaceTestAdapter.unit.test.ts @@ -6,8 +6,8 @@ import * as sinon from 'sinon'; import { TestController, TestItem, Uri } from 'vscode'; import { IConfigurationService } from '../../../client/common/types'; -import { UnittestTestDiscoveryAdapter } from '../../../client/testing/testController/unittest/unittestDiscoveryAdapter'; -import { UnittestTestExecutionAdapter } from '../../../client/testing/testController/unittest/unittestExecutionAdapter'; // 7/7 +import { UnittestTestDiscoveryAdapter } from '../../../client/testing/testController/unittest/testDiscoveryAdapter'; +import { UnittestTestExecutionAdapter } from '../../../client/testing/testController/unittest/testExecutionAdapter'; // 7/7 import { WorkspaceTestAdapter } from '../../../client/testing/testController/workspaceTestAdapter'; import * as Telemetry from '../../../client/telemetry'; import { EventName } from '../../../client/telemetry/constants'; From 7e3b44b788df7c81ed6ec556dcf0892864cc02d2 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 7 Nov 2022 14:26:51 -0800 Subject: [PATCH 37/49] remove non src file --- pythonFiles/testing_tools/run_adapter.py | 4 ++-- pythonFiles/unittest_adapter/unittest_discovery.py | 13 +------------ pythonFiles/unittest_adapter/unittest_execution.py | 2 +- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/pythonFiles/testing_tools/run_adapter.py b/pythonFiles/testing_tools/run_adapter.py index b2c6c35608ff..1eeef194f8f5 100644 --- a/pythonFiles/testing_tools/run_adapter.py +++ b/pythonFiles/testing_tools/run_adapter.py @@ -14,9 +14,9 @@ ), ) -from testing_tools.adapter.__main__ import main, parse_args +from testing_tools.adapter.__main__ import parse_args, main + if __name__ == "__main__": tool, cmd, subargs, toolargs = parse_args() main(tool, cmd, subargs, toolargs) - print("run adapter hello") diff --git a/pythonFiles/unittest_adapter/unittest_discovery.py b/pythonFiles/unittest_adapter/unittest_discovery.py index 2bb5ab311d75..0be09e986ca8 100644 --- a/pythonFiles/unittest_adapter/unittest_discovery.py +++ b/pythonFiles/unittest_adapter/unittest_discovery.py @@ -3,7 +3,6 @@ import argparse import json -import logging import os import pathlib import sys @@ -11,13 +10,6 @@ import unittest from typing import List, Literal, Optional, Tuple, TypedDict, Union -sys.path.append( - "/Users/eleanorboyd/.vscode/extensions/ms-python.python-2022.12.1/pythonFiles/lib/python" -) # -# import debugpy - -# debugpy.connect(5678) - # Add the path to pythonFiles to sys.path to find testing_tools.socket_manager. PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, PYTHON_FILES) @@ -25,7 +17,7 @@ from testing_tools import socket_manager # If I use from utils then there will be an import error in test_discovery.py. -from unittest_adapter.utils import TestNode, build_test_tree, parse_unittest_args +from unittestadapter.utils import TestNode, build_test_tree, parse_unittest_args # Add the lib path to sys.path to find the typing_extensions module. sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) @@ -122,9 +114,6 @@ def discover_tests( index = argv.index("--udiscovery") start_dir, pattern, top_level_dir = parse_unittest_args(argv[index + 1 :]) - logging.debug( - "start_dir, pattern, top_level_dir", start_dir, pattern, top_level_dir - ) # Perform test discovery. port, uuid = parse_discovery_cli_args(argv[:index]) diff --git a/pythonFiles/unittest_adapter/unittest_execution.py b/pythonFiles/unittest_adapter/unittest_execution.py index 935e7fbeae57..a016ff1af9ec 100644 --- a/pythonFiles/unittest_adapter/unittest_execution.py +++ b/pythonFiles/unittest_adapter/unittest_execution.py @@ -18,7 +18,7 @@ sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) from testing_tools import socket_manager from typing_extensions import NotRequired -from unittest_adapter.utils import parse_unittest_args +from unittestadapter.utils import parse_unittest_args DEFAULT_PORT = "45454" From 568bfa92a69f770b815ed0ae6773640cfa998c9d Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 7 Nov 2022 14:29:55 -0800 Subject: [PATCH 38/49] name changes --- pythonFiles/{unittest_adapter => unittestadapter}/__init__.py | 0 .../unittest_discovery.py => unittestadapter/discovery.py} | 0 .../unittest_execution.py => unittestadapter/execution.py} | 0 pythonFiles/{unittest_adapter => unittestadapter}/utils.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename pythonFiles/{unittest_adapter => unittestadapter}/__init__.py (100%) rename pythonFiles/{unittest_adapter/unittest_discovery.py => unittestadapter/discovery.py} (100%) rename pythonFiles/{unittest_adapter/unittest_execution.py => unittestadapter/execution.py} (100%) rename pythonFiles/{unittest_adapter => unittestadapter}/utils.py (100%) diff --git a/pythonFiles/unittest_adapter/__init__.py b/pythonFiles/unittestadapter/__init__.py similarity index 100% rename from pythonFiles/unittest_adapter/__init__.py rename to pythonFiles/unittestadapter/__init__.py diff --git a/pythonFiles/unittest_adapter/unittest_discovery.py b/pythonFiles/unittestadapter/discovery.py similarity index 100% rename from pythonFiles/unittest_adapter/unittest_discovery.py rename to pythonFiles/unittestadapter/discovery.py diff --git a/pythonFiles/unittest_adapter/unittest_execution.py b/pythonFiles/unittestadapter/execution.py similarity index 100% rename from pythonFiles/unittest_adapter/unittest_execution.py rename to pythonFiles/unittestadapter/execution.py diff --git a/pythonFiles/unittest_adapter/utils.py b/pythonFiles/unittestadapter/utils.py similarity index 100% rename from pythonFiles/unittest_adapter/utils.py rename to pythonFiles/unittestadapter/utils.py From cc2bfa69fd34e2bf22ffcca7fe8503dc2de2bf95 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 7 Nov 2022 14:32:52 -0800 Subject: [PATCH 39/49] rename and delete files --- .../testing/testController/pytest/pytestDiscoveryAdapter.ts | 0 .../testing/testController/pytest/pytestExecutionAdapter.ts | 0 .../{unittestDiscoveryAdapter.ts => testDiscoveryAdapter.ts} | 0 .../{unittestExecutionAdapter.ts => testExecutionAdapter.ts} | 0 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts delete mode 100644 src/client/testing/testController/pytest/pytestExecutionAdapter.ts rename src/client/testing/testController/unittest/{unittestDiscoveryAdapter.ts => testDiscoveryAdapter.ts} (100%) rename src/client/testing/testController/unittest/{unittestExecutionAdapter.ts => testExecutionAdapter.ts} (100%) diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts b/src/client/testing/testController/unittest/testDiscoveryAdapter.ts similarity index 100% rename from src/client/testing/testController/unittest/unittestDiscoveryAdapter.ts rename to src/client/testing/testController/unittest/testDiscoveryAdapter.ts diff --git a/src/client/testing/testController/unittest/unittestExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts similarity index 100% rename from src/client/testing/testController/unittest/unittestExecutionAdapter.ts rename to src/client/testing/testController/unittest/testExecutionAdapter.ts From 0354dd9834f6cc3bfe0b27d85dabde73c0dc98d3 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 7 Nov 2022 14:35:19 -0800 Subject: [PATCH 40/49] remove unnecessary files --- .../pytest-vscode-integration/.gitignore | 74 ------ pythonFiles/pytest-vscode-integration/LICENSE | 22 -- .../pytest-vscode-integration/docs/index.md | 3 - .../pytest-vscode-integration/mkdocs.yml | 10 - .../dependency_links.txt | 1 - .../pytest_vscode_integration.py | 235 ------------------ 6 files changed, 345 deletions(-) delete mode 100644 pythonFiles/pytest-vscode-integration/.gitignore delete mode 100644 pythonFiles/pytest-vscode-integration/LICENSE delete mode 100644 pythonFiles/pytest-vscode-integration/docs/index.md delete mode 100644 pythonFiles/pytest-vscode-integration/mkdocs.yml delete mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/dependency_links.txt delete mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py diff --git a/pythonFiles/pytest-vscode-integration/.gitignore b/pythonFiles/pytest-vscode-integration/.gitignore deleted file mode 100644 index 6c5886e04363..000000000000 --- a/pythonFiles/pytest-vscode-integration/.gitignore +++ /dev/null @@ -1,74 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ -.pytest_cache - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask instance folder -instance/ - -# Sphinx documentation -docs/_build/ - -# MkDocs documentation -/site/ - -# PyBuilder -target/ - -# IPython Notebook -.ipynb_checkpoints - -# pyenv -.python-version - diff --git a/pythonFiles/pytest-vscode-integration/LICENSE b/pythonFiles/pytest-vscode-integration/LICENSE deleted file mode 100644 index 544793b47855..000000000000 --- a/pythonFiles/pytest-vscode-integration/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ - -The MIT License (MIT) - -Copyright (c) 2022 Eleanor Boyd - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/pythonFiles/pytest-vscode-integration/docs/index.md b/pythonFiles/pytest-vscode-integration/docs/index.md deleted file mode 100644 index 6502b106decf..000000000000 --- a/pythonFiles/pytest-vscode-integration/docs/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Welcome to pytest-vscode-integration - -used to surface pytest functionality to ports for vscode integration diff --git a/pythonFiles/pytest-vscode-integration/mkdocs.yml b/pythonFiles/pytest-vscode-integration/mkdocs.yml deleted file mode 100644 index e7aeb8d8508d..000000000000 --- a/pythonFiles/pytest-vscode-integration/mkdocs.yml +++ /dev/null @@ -1,10 +0,0 @@ -site_name: pytest-vscode-integration -site_description: used to surface pytest functionality to ports for vscode integration -site_author: Eleanor Boyd - -theme: readthedocs - -repo_url: https://github.com/eleanorboyd/pytest-vscode-integration - -pages: -- Home: index.md diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/dependency_links.txt b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891791f..000000000000 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py deleted file mode 100644 index fc05cdf78367..000000000000 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.py +++ /dev/null @@ -1,235 +0,0 @@ -# -*- coding: utf-8 -*- -import enum -import json -import os -import pathlib -import sys -from dbm.ndbm import library -from typing import KeysView, List, Literal, Optional, Tuple, TypedDict, Union -from unittest import TestCase - -import pytest - - -# Inherit from str so it's JSON serializable. -class TestNodeTypeEnum(str, enum.Enum): - class_ = "class" - file = "file" - folder = "folder" - test = "test" - - -class TestData(TypedDict): - name: str - path: str - type_: TestNodeTypeEnum - id_: str - - -class TestItem(TestData): - lineno: str - runID: str - - -class TestNode(TestData): - children: "List[TestNode | TestItem]" - - -# Add the path to pythonFiles to sys.path to find testing_tools.socket_manager. -PYTHON_FILES = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path.insert(0, PYTHON_FILES) - -# Add the lib path to sys.path to find the typing_extensions module. -sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) -from testing_tools import socket_manager -from typing_extensions import NotRequired - -DEFAULT_PORT = "45454" - -# session -# test Case - -# modules folders1/folders2 (can be in classes) -# test cases - -# module -# class -# test case - - -def pytest_collection_finish(session): - node, error = build_test_tree(session) - cwd = os.getcwd() - # add error check - sendPost(cwd, node) - - -def build_test_tree(session) -> Tuple[Union[TestNode, None], List[str]]: - errors: List[str] = [] # TODO: how do I check for errors - session_test_node = createSessionTestNode(session) - testNode_file_dict: dict[ - pytest.Module, TestNode - ] = dict() # a dictionary of all files in the session - session_children_dict: dict[ - str, TestNode - ] = dict() # a dictionary of all direct children of the session - testNode_class_dict: dict[ - str, TestNode - ] = dict() # a dictionary of all direct children of the session - # iterate through all the test items in the session - for test_case in session.items: - testNode_test = createTestItem(test_case) - # if the parent object file doesn't already exist - if type(test_case.parent) == pytest.Module: - if test_case.parent not in testNode_file_dict: - testNode_file_dict[test_case.parent] = createFileTestNode( - test_case.parent - ) - testNode_file_dict[test_case.parent].get("children").append( - testNode_test - ) # use set default - else: - # this means its a unittest class - # create class - testNode_class = accessOrCreateGeneral( - test_case.parent.name, testNode_class_dict, test_case.parent.fspath - ) - testNode_class["children"].append(testNode_test) - parent_module = test_case.parent.parent - # create file that wraps class - if parent_module not in testNode_file_dict.keys(): - testNode_file_dict[parent_module] = createFileTestNode(parent_module) - testNode_file_dict[parent_module].get("children").append(testNode_class) - - created_filesfolder_dict: dict[str, TestNode] = {} - for file_module, testNode_file in testNode_file_dict.items(): - name = str(file_module.name) - prev_folder_test_node: TestNode = testNode_file - if "/" in name: - # it is a nested folder structure and so new objects need to be created - nested_folder_list = name.split("/") - path_iterator = ( - str(session.fspath) - + "/" - + "/".join( - nested_folder_list[0:-1] - ) # check to see if windows style (more fancy stuff path lib if windows or posix via API in os module) - ) - for i in range(len(nested_folder_list) - 2, -1, -1): # reverse and slice - folderName = nested_folder_list[i] - folder_test_node = accessOrCreateGeneral( - folderName, - created_filesfolder_dict, - path_iterator, - ) - folder_test_node["children"].append(prev_folder_test_node) - # increase iteration through path - prev_folder_test_node = folder_test_node - path_iterator = str(session.fspath) + "/".join(nested_folder_list[0:i]) - - # the final folder we get to is the highest folder in the path and therefore we add this as a child to the session - if (prev_folder_test_node is not None) and ( - prev_folder_test_node.get("id_") not in session_children_dict - ): - session_children_dict[ - prev_folder_test_node.get("id_") - ] = prev_folder_test_node - session_test_node["children"] = list(session_children_dict.values()) - return session_test_node, errors - - -def createTestItem(test_case) -> TestItem: - return { - "name": test_case.name, - "path": str(test_case.fspath), - "lineno": test_case.location[1] + 1, - "type_": TestNodeTypeEnum.test, - "id_": str(test_case.nodeid), - "runID": test_case.nodeid, # can I use this two times? - } - - -def createSessionTestNode(session) -> TestNode: - return { - "name": session.name, - "path": str(session.fspath), - "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder - "children": [], - "id_": str(session.fspath), - } - - -def createClassTestNode(class_module_name, class_module_path) -> TestNode: - return { - "name": class_module_name, - "path": str(class_module_path), - "type_": TestNodeTypeEnum.class_, - "children": [], - "id_": str(class_module_path), - } - - -def createFileTestNode(file_module) -> TestNode: - return { - "name": str(file_module.fspath.basename), - "path": str(file_module.fspath), - "type_": TestNodeTypeEnum.file, - "id_": str(file_module.fspath), - "children": [], - } - - -def createFolderTestNode(folderName, path_iterator) -> TestNode: - return { - "name": folderName, - "path": str(path_iterator), - "type_": TestNodeTypeEnum.folder, # check if this is a file or a folder - "id_": str(path_iterator), - "children": [], - } - - -def accessOrCreateGeneral(test_node_name, test_node_dict, test_node_path) -> TestNode: - if test_node_name in test_node_dict.keys(): # exists in the dictionary - return test_node_dict[test_node_name] - else: - # create new - temp = createFolderTestNode(test_node_name, test_node_path) - test_node_dict[test_node_name] = temp - return temp - - -# def accessOrCreateClass(class_module, testNode_class_dict) -> TestNode: -# if class_module.name in testNode_class_dict.keys(): # exists in the dictionary -# return testNode_class_dict[class_module.name] -# else: -# # create new -# temp = createClassTestNode(class_module.name, class_module.fspath) -# testNode_class_dict[class_module.name] = temp -# return temp - - -class PayloadDict(TypedDict): - cwd: str - status: Literal["success", "error"] - tests: NotRequired[TestNode] - errors: NotRequired[List[str]] - - -def sendPost(cwd, tests): - payload: PayloadDict = {"cwd": cwd, "status": "success", "tests": tests} - testPort = os.getenv("TEST_PORT", 45454) - testuuid = os.getenv("TEST_UUID") - addr = ("localhost", int(testPort)) - print("sending post", addr, cwd) - # socket_manager.send_post("Hello from pytest") # type: ignore - with socket_manager.SocketManager(addr) as s: - data = json.dumps(payload) - request = f"""POST / HTTP/1.1 -Host: localhost:{testPort} -Content-Length: {len(data)} -Content-Type: application/json -Request-uuid: {testuuid} - -{data}""" - result = s.socket.sendall(request.encode("utf-8")) # type: ignore From 988ce2f421aa31073d1e2513080e064f562a50a0 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 7 Nov 2022 14:37:26 -0800 Subject: [PATCH 41/49] remove unneeded files --- .../pytest-vscode-integration/README.rst | 76 ------------- .../build/lib/pytest_vscode_integration.py | 21 ---- .../PKG-INFO | 104 ------------------ 3 files changed, 201 deletions(-) delete mode 100644 pythonFiles/pytest-vscode-integration/README.rst delete mode 100644 pythonFiles/pytest-vscode-integration/build/lib/pytest_vscode_integration.py delete mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/PKG-INFO diff --git a/pythonFiles/pytest-vscode-integration/README.rst b/pythonFiles/pytest-vscode-integration/README.rst deleted file mode 100644 index 28a55c3da118..000000000000 --- a/pythonFiles/pytest-vscode-integration/README.rst +++ /dev/null @@ -1,76 +0,0 @@ -========================= -pytest-vscode-integration -========================= - -.. image:: https://img.shields.io/pypi/v/pytest-vscode-integration.svg - :target: https://pypi.org/project/pytest-vscode-integration - :alt: PyPI version - -.. image:: https://img.shields.io/pypi/pyversions/pytest-vscode-integration.svg - :target: https://pypi.org/project/pytest-vscode-integration - :alt: Python versions - -.. image:: https://ci.appveyor.com/api/projects/status/github/eleanorboyd/pytest-vscode-integration?branch=master - :target: https://ci.appveyor.com/project/eleanorboyd/pytest-vscode-integration/branch/master - :alt: See Build Status on AppVeyor - -used to surface pytest functionality to ports for vscode integration - ----- - -This `pytest`_ plugin was generated with `Cookiecutter`_ along with `@hackebrot`_'s `cookiecutter-pytest-plugin`_ template. - - -Features --------- - -* TODO - - -Requirements ------------- - -* TODO - - -Installation ------------- - -You can install "pytest-vscode-integration" via `pip`_ from `PyPI`_:: - - $ pip install pytest-vscode-integration - - -Usage ------ - -* TODO - -Contributing ------------- -Contributions are very welcome. Tests can be run with `tox`_, please ensure -the coverage at least stays the same before you submit a pull request. - -License -------- - -Distributed under the terms of the `MIT`_ license, "pytest-vscode-integration" is free and open source software - - -Issues ------- - -If you encounter any problems, please `file an issue`_ along with a detailed description. - -.. _`Cookiecutter`: https://github.com/audreyr/cookiecutter -.. _`@hackebrot`: https://github.com/hackebrot -.. _`MIT`: http://opensource.org/licenses/MIT -.. _`BSD-3`: http://opensource.org/licenses/BSD-3-Clause -.. _`GNU GPL v3.0`: http://www.gnu.org/licenses/gpl-3.0.txt -.. _`Apache Software License 2.0`: http://www.apache.org/licenses/LICENSE-2.0 -.. _`cookiecutter-pytest-plugin`: https://github.com/pytest-dev/cookiecutter-pytest-plugin -.. _`file an issue`: https://github.com/eleanorboyd/pytest-vscode-integration/issues -.. _`pytest`: https://github.com/pytest-dev/pytest -.. _`tox`: https://tox.readthedocs.io/en/latest/ -.. _`pip`: https://pypi.org/project/pip/ -.. _`PyPI`: https://pypi.org/project diff --git a/pythonFiles/pytest-vscode-integration/build/lib/pytest_vscode_integration.py b/pythonFiles/pytest-vscode-integration/build/lib/pytest_vscode_integration.py deleted file mode 100644 index 692d49fb0258..000000000000 --- a/pythonFiles/pytest-vscode-integration/build/lib/pytest_vscode_integration.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- - -import pytest - - -def pytest_addoption(parser): - group = parser.getgroup('vscode-integration') - group.addoption( - '--foo', - action='store', - dest='dest_foo', - default='2022', - help='Set the value for the fixture "bar".' - ) - - parser.addini('HELLO', 'Dummy pytest.ini setting') - - -@pytest.fixture -def bar(request): - return request.config.option.dest_foo diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/PKG-INFO b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/PKG-INFO deleted file mode 100644 index 172aed3ab5bb..000000000000 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/PKG-INFO +++ /dev/null @@ -1,104 +0,0 @@ -Metadata-Version: 2.1 -Name: pytest-vscode-integration -Version: 0.1.0 -Summary: used to surface pytest functionality to ports for vscode integration -Home-page: https://github.com/eleanorboyd/pytest-vscode-integration -Author: Eleanor Boyd -Author-email: eleanorboyd@microsoft.com -Maintainer: Eleanor Boyd -Maintainer-email: eleanorboyd@microsoft.com -License: MIT -Classifier: Development Status :: 4 - Beta -Classifier: Framework :: Pytest -Classifier: Intended Audience :: Developers -Classifier: Topic :: Software Development :: Testing -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Operating System :: OS Independent -Classifier: License :: OSI Approved :: MIT License -Requires-Python: >=3.5 -License-File: LICENSE - -========================= -pytest-vscode-integration -========================= - -.. image:: https://img.shields.io/pypi/v/pytest-vscode-integration.svg - :target: https://pypi.org/project/pytest-vscode-integration - :alt: PyPI version - -.. image:: https://img.shields.io/pypi/pyversions/pytest-vscode-integration.svg - :target: https://pypi.org/project/pytest-vscode-integration - :alt: Python versions - -.. image:: https://ci.appveyor.com/api/projects/status/github/eleanorboyd/pytest-vscode-integration?branch=master - :target: https://ci.appveyor.com/project/eleanorboyd/pytest-vscode-integration/branch/master - :alt: See Build Status on AppVeyor - -used to surface pytest functionality to ports for vscode integration - ----- - -This `pytest`_ plugin was generated with `Cookiecutter`_ along with `@hackebrot`_'s `cookiecutter-pytest-plugin`_ template. - - -Features --------- - -* TODO - - -Requirements ------------- - -* TODO - - -Installation ------------- - -You can install "pytest-vscode-integration" via `pip`_ from `PyPI`_:: - - $ pip install pytest-vscode-integration - - -Usage ------ - -* TODO - -Contributing ------------- -Contributions are very welcome. Tests can be run with `tox`_, please ensure -the coverage at least stays the same before you submit a pull request. - -License -------- - -Distributed under the terms of the `MIT`_ license, "pytest-vscode-integration" is free and open source software - - -Issues ------- - -If you encounter any problems, please `file an issue`_ along with a detailed description. - -.. _`Cookiecutter`: https://github.com/audreyr/cookiecutter -.. _`@hackebrot`: https://github.com/hackebrot -.. _`MIT`: http://opensource.org/licenses/MIT -.. _`BSD-3`: http://opensource.org/licenses/BSD-3-Clause -.. _`GNU GPL v3.0`: http://www.gnu.org/licenses/gpl-3.0.txt -.. _`Apache Software License 2.0`: http://www.apache.org/licenses/LICENSE-2.0 -.. _`cookiecutter-pytest-plugin`: https://github.com/pytest-dev/cookiecutter-pytest-plugin -.. _`file an issue`: https://github.com/eleanorboyd/pytest-vscode-integration/issues -.. _`pytest`: https://github.com/pytest-dev/pytest -.. _`tox`: https://tox.readthedocs.io/en/latest/ -.. _`pip`: https://pypi.org/project/pip/ -.. _`PyPI`: https://pypi.org/project From 0e353879a389e95c44a70a8dca34853a8e259ba2 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 7 Nov 2022 15:23:23 -0800 Subject: [PATCH 42/49] fix black formatting --- .../pytest-vscode-integration/setup.py | 62 +++++++++---------- .../tests/conftest.py | 2 +- .../tests/test_vscode_integration.py | 53 +++++++++------- 3 files changed, 63 insertions(+), 54 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/setup.py b/pythonFiles/pytest-vscode-integration/setup.py index 9a5b79444162..02441e2f4e4a 100644 --- a/pythonFiles/pytest-vscode-integration/setup.py +++ b/pythonFiles/pytest-vscode-integration/setup.py @@ -9,43 +9,43 @@ def read(fname): file_path = os.path.join(os.path.dirname(__file__), fname) - return codecs.open(file_path, encoding='utf-8').read() + return codecs.open(file_path, encoding="utf-8").read() setup( - name='pytest-vscode-integration', - version='0.1.0', - author='Eleanor Boyd', - author_email='eleanorboyd@microsoft.com', - maintainer='Eleanor Boyd', - maintainer_email='eleanorboyd@microsoft.com', - license='MIT', - url='https://github.com/eleanorboyd/pytest-vscode-integration', - description='used to surface pytest functionality to ports for vscode integration', - long_description=read('README.rst'), - py_modules=['pytest_vscode_integration'], - python_requires='>=3.5', - install_requires=['pytest>=3.5.0'], + name="pytest-vscode-integration", + version="0.1.0", + author="Eleanor Boyd", + author_email="eleanorboyd@microsoft.com", + maintainer="Eleanor Boyd", + maintainer_email="eleanorboyd@microsoft.com", + license="MIT", + url="https://github.com/eleanorboyd/pytest-vscode-integration", + description="used to surface pytest functionality to ports for vscode integration", + long_description=read("README.rst"), + py_modules=["pytest_vscode_integration"], + python_requires=">=3.5", + install_requires=["pytest>=3.5.0"], classifiers=[ - 'Development Status :: 4 - Beta', - 'Framework :: Pytest', - 'Intended Audience :: Developers', - 'Topic :: Software Development :: Testing', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy', - 'Operating System :: OS Independent', - 'License :: OSI Approved :: MIT License', + "Development Status :: 4 - Beta", + "Framework :: Pytest", + "Intended Audience :: Developers", + "Topic :: Software Development :: Testing", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Operating System :: OS Independent", + "License :: OSI Approved :: MIT License", ], entry_points={ - 'pytest11': [ - 'vscode-integration = pytest_vscode_integration', + "pytest11": [ + "vscode-integration = pytest_vscode_integration", ], }, ) diff --git a/pythonFiles/pytest-vscode-integration/tests/conftest.py b/pythonFiles/pytest-vscode-integration/tests/conftest.py index bc711e55fefd..694d7d58d440 100644 --- a/pythonFiles/pytest-vscode-integration/tests/conftest.py +++ b/pythonFiles/pytest-vscode-integration/tests/conftest.py @@ -1 +1 @@ -pytest_plugins = 'pytester' +pytest_plugins = "pytester" diff --git a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py index 657faa8e5881..dde05e332c18 100644 --- a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py @@ -5,21 +5,22 @@ def test_bar_fixture(testdir): """Make sure that pytest accepts our fixture.""" # create a temporary pytest test module - testdir.makepyfile(""" + testdir.makepyfile( + """ def test_sth(bar): assert bar == "europython2015" - """) + """ + ) # run pytest with the following cmd args - result = testdir.runpytest( - '--foo=europython2015', - '-v' - ) + result = testdir.runpytest("--foo=europython2015", "-v") # fnmatch_lines does an assertion internally - result.stdout.fnmatch_lines([ - '*::test_sth PASSED*', - ]) + result.stdout.fnmatch_lines( + [ + "*::test_sth PASSED*", + ] + ) # make sure that that we get a '0' exit code for the testsuite assert result.ret == 0 @@ -27,22 +28,27 @@ def test_sth(bar): def test_help_message(testdir): result = testdir.runpytest( - '--help', + "--help", ) # fnmatch_lines does an assertion internally - result.stdout.fnmatch_lines([ - 'vscode-integration:', - '*--foo=DEST_FOO*Set the value for the fixture "bar".', - ]) + result.stdout.fnmatch_lines( + [ + "vscode-integration:", + '*--foo=DEST_FOO*Set the value for the fixture "bar".', + ] + ) def test_hello_ini_setting(testdir): - testdir.makeini(""" + testdir.makeini( + """ [pytest] HELLO = world - """) + """ + ) - testdir.makepyfile(""" + testdir.makepyfile( + """ import pytest @pytest.fixture @@ -51,14 +57,17 @@ def hello(request): def test_hello_world(hello): assert hello == 'world' - """) + """ + ) - result = testdir.runpytest('-v') + result = testdir.runpytest("-v") # fnmatch_lines does an assertion internally - result.stdout.fnmatch_lines([ - '*::test_hello_world PASSED*', - ]) + result.stdout.fnmatch_lines( + [ + "*::test_hello_world PASSED*", + ] + ) # make sure that that we get a '0' exit code for the testsuite assert result.ret == 0 From 9b5b53445664cc0246b0c53cd1aea7335b6bc534 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 10 Nov 2022 13:47:49 -0800 Subject: [PATCH 43/49] remove unneeded files --- .../pytest-vscode-integration/MANIFEST.in | 5 --- .../pytest-vscode-integration/appveyor.yml | 40 ------------------- pythonFiles/pytest-vscode-integration/tox.ini | 12 ------ 3 files changed, 57 deletions(-) delete mode 100644 pythonFiles/pytest-vscode-integration/MANIFEST.in delete mode 100644 pythonFiles/pytest-vscode-integration/appveyor.yml delete mode 100644 pythonFiles/pytest-vscode-integration/tox.ini diff --git a/pythonFiles/pytest-vscode-integration/MANIFEST.in b/pythonFiles/pytest-vscode-integration/MANIFEST.in deleted file mode 100644 index 1ade348bb74d..000000000000 --- a/pythonFiles/pytest-vscode-integration/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -include LICENSE -include README.rst - -recursive-exclude * __pycache__ -recursive-exclude * *.py[co] diff --git a/pythonFiles/pytest-vscode-integration/appveyor.yml b/pythonFiles/pytest-vscode-integration/appveyor.yml deleted file mode 100644 index 61bdd159551a..000000000000 --- a/pythonFiles/pytest-vscode-integration/appveyor.yml +++ /dev/null @@ -1,40 +0,0 @@ -# What Python version is installed where: -# https://www.appveyor.com/docs/build-environment/#python - -environment: - matrix: - - PYTHON: "C:\\Python35" - TOX_ENV: "py35" - - - PYTHON: "C:\\Python36" - TOX_ENV: "py36" - - - PYTHON: "C:\\Python37" - TOX_ENV: "py37" - - - PYTHON: "C:\\Python38" - TOX_ENV: "py38" - -init: - - "%PYTHON%/python -V" - - "%PYTHON%/python -c \"import struct;print( 8 * struct.calcsize(\'P\'))\"" - -install: - - "%PYTHON%/Scripts/easy_install -U pip" - - "%PYTHON%/Scripts/pip install tox" - - "%PYTHON%/Scripts/pip install wheel" - -build: false # Not a C# project, build stuff at the test step instead. - -test_script: - - "%PYTHON%/Scripts/tox -e %TOX_ENV%" - -after_test: - - "%PYTHON%/python setup.py bdist_wheel" - - ps: "ls dist" - -artifacts: - - path: dist\* - -#on_success: -# - TODO: upload the content of dist/*.whl to a public wheelhouse diff --git a/pythonFiles/pytest-vscode-integration/tox.ini b/pythonFiles/pytest-vscode-integration/tox.ini deleted file mode 100644 index 9b037d48e319..000000000000 --- a/pythonFiles/pytest-vscode-integration/tox.ini +++ /dev/null @@ -1,12 +0,0 @@ -# For more information about tox, see https://tox.readthedocs.io/en/latest/ -[tox] -envlist = py35,py36,py37,py38,pypy3,flake8 - -[testenv] -deps = pytest>=3.0 -commands = pytest {posargs:tests} - -[testenv:flake8] -skip_install = true -deps = flake8 -commands = flake8 pytest_vscode_integration.py setup.py tests From fa3949de7cf6486e64c8d7ae0b1b476723e479d5 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Mon, 21 Nov 2022 13:46:24 -0800 Subject: [PATCH 44/49] Update pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py Co-authored-by: Brett Cannon --- .../pytest-vscode-integration/tests/test_vscode_integration.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py index dde05e332c18..c5046af30177 100644 --- a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py @@ -3,8 +3,7 @@ def test_bar_fixture(testdir): """Make sure that pytest accepts our fixture.""" - - # create a temporary pytest test module + # Create a temporary pytest test module. testdir.makepyfile( """ def test_sth(bar): From 1dafafc6ceb225780d05b2fc526788a999673e27 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Mon, 21 Nov 2022 13:46:35 -0800 Subject: [PATCH 45/49] Update pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py Co-authored-by: Brett Cannon --- .../pytest-vscode-integration/tests/test_vscode_integration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py index c5046af30177..a59af3b5dcac 100644 --- a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- def test_bar_fixture(testdir): From fdf756b75e56db2324b9bd5e1c6255183c1a2374 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Mon, 21 Nov 2022 13:47:04 -0800 Subject: [PATCH 46/49] Update pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py Co-authored-by: Brett Cannon --- .../pytest-vscode-integration/tests/test_vscode_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py index a59af3b5dcac..eae310cbbc77 100644 --- a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py @@ -21,7 +21,7 @@ def test_sth(bar): ) # make sure that that we get a '0' exit code for the testsuite - assert result.ret == 0 + assert not result.ret def test_help_message(testdir): From cb0202fcfd93348e19dbb58679f535c350a96bc2 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Mon, 21 Nov 2022 14:00:07 -0800 Subject: [PATCH 47/49] Update pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py Co-authored-by: Brett Cannon --- .../tests/test_vscode_integration.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py index eae310cbbc77..106e4bbfa38b 100644 --- a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py @@ -3,12 +3,10 @@ def test_bar_fixture(testdir): """Make sure that pytest accepts our fixture.""" # Create a temporary pytest test module. - testdir.makepyfile( - """ + testdir.makepyfile(""" def test_sth(bar): assert bar == "europython2015" - """ - ) +""") # run pytest with the following cmd args result = testdir.runpytest("--foo=europython2015", "-v") From 519ea9a5738ada20aa83e17174d3ad8140b3f4e8 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 21 Nov 2022 14:02:51 -0800 Subject: [PATCH 48/49] stage 1 changes --- .../SOURCES.txt | 2 +- .../pytest-vscode-integration/setup.py | 51 ------------------- .../tests/test_vscode_integration.py | 2 +- 3 files changed, 2 insertions(+), 53 deletions(-) delete mode 100644 pythonFiles/pytest-vscode-integration/setup.py diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt index 3b06f2a2dcce..e9a83a9c92cf 100644 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt +++ b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt @@ -8,4 +8,4 @@ pytest_vscode_integration.egg-info/SOURCES.txt pytest_vscode_integration.egg-info/dependency_links.txt pytest_vscode_integration.egg-info/entry_points.txt pytest_vscode_integration.egg-info/requires.txt -pytest_vscode_integration.egg-info/top_level.txt \ No newline at end of file +pytest_vscode_integration.egg-info/top_level.txt diff --git a/pythonFiles/pytest-vscode-integration/setup.py b/pythonFiles/pytest-vscode-integration/setup.py deleted file mode 100644 index 02441e2f4e4a..000000000000 --- a/pythonFiles/pytest-vscode-integration/setup.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import codecs -import os - -from setuptools import setup - - -def read(fname): - file_path = os.path.join(os.path.dirname(__file__), fname) - return codecs.open(file_path, encoding="utf-8").read() - - -setup( - name="pytest-vscode-integration", - version="0.1.0", - author="Eleanor Boyd", - author_email="eleanorboyd@microsoft.com", - maintainer="Eleanor Boyd", - maintainer_email="eleanorboyd@microsoft.com", - license="MIT", - url="https://github.com/eleanorboyd/pytest-vscode-integration", - description="used to surface pytest functionality to ports for vscode integration", - long_description=read("README.rst"), - py_modules=["pytest_vscode_integration"], - python_requires=">=3.5", - install_requires=["pytest>=3.5.0"], - classifiers=[ - "Development Status :: 4 - Beta", - "Framework :: Pytest", - "Intended Audience :: Developers", - "Topic :: Software Development :: Testing", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Operating System :: OS Independent", - "License :: OSI Approved :: MIT License", - ], - entry_points={ - "pytest11": [ - "vscode-integration = pytest_vscode_integration", - ], - }, -) diff --git a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py index 106e4bbfa38b..9c9eca265e5f 100644 --- a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py +++ b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py @@ -1,6 +1,6 @@ -def test_bar_fixture(testdir): +def test_plugin_fixture(testdir): """Make sure that pytest accepts our fixture.""" # Create a temporary pytest test module. testdir.makepyfile(""" From 1894faeb7ff70531b93c359cca707b253adafccf Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 18 Jan 2023 13:46:41 -0800 Subject: [PATCH 49/49] remove unneeded files --- .../SOURCES.txt | 11 --- .../requires.txt | 1 - .../top_level.txt | 1 - .../tests/conftest.py | 1 - .../tests/test_vscode_integration.py | 69 ------------------- 5 files changed, 83 deletions(-) delete mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt delete mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/requires.txt delete mode 100644 pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/top_level.txt delete mode 100644 pythonFiles/pytest-vscode-integration/tests/conftest.py delete mode 100644 pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt deleted file mode 100644 index e9a83a9c92cf..000000000000 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/SOURCES.txt +++ /dev/null @@ -1,11 +0,0 @@ -LICENSE -MANIFEST.in -README.rst -pytest_vscode_integration.py -setup.py -pytest_vscode_integration.egg-info/PKG-INFO -pytest_vscode_integration.egg-info/SOURCES.txt -pytest_vscode_integration.egg-info/dependency_links.txt -pytest_vscode_integration.egg-info/entry_points.txt -pytest_vscode_integration.egg-info/requires.txt -pytest_vscode_integration.egg-info/top_level.txt diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/requires.txt b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/requires.txt deleted file mode 100644 index a1fa3684923e..000000000000 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/requires.txt +++ /dev/null @@ -1 +0,0 @@ -pytest>=3.5.0 diff --git a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/top_level.txt b/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/top_level.txt deleted file mode 100644 index ea1bb3c56250..000000000000 --- a/pythonFiles/pytest-vscode-integration/pytest_vscode_integration.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pytest_vscode_integration diff --git a/pythonFiles/pytest-vscode-integration/tests/conftest.py b/pythonFiles/pytest-vscode-integration/tests/conftest.py deleted file mode 100644 index 694d7d58d440..000000000000 --- a/pythonFiles/pytest-vscode-integration/tests/conftest.py +++ /dev/null @@ -1 +0,0 @@ -pytest_plugins = "pytester" diff --git a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py b/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py deleted file mode 100644 index 9c9eca265e5f..000000000000 --- a/pythonFiles/pytest-vscode-integration/tests/test_vscode_integration.py +++ /dev/null @@ -1,69 +0,0 @@ - - -def test_plugin_fixture(testdir): - """Make sure that pytest accepts our fixture.""" - # Create a temporary pytest test module. - testdir.makepyfile(""" - def test_sth(bar): - assert bar == "europython2015" -""") - - # run pytest with the following cmd args - result = testdir.runpytest("--foo=europython2015", "-v") - - # fnmatch_lines does an assertion internally - result.stdout.fnmatch_lines( - [ - "*::test_sth PASSED*", - ] - ) - - # make sure that that we get a '0' exit code for the testsuite - assert not result.ret - - -def test_help_message(testdir): - result = testdir.runpytest( - "--help", - ) - # fnmatch_lines does an assertion internally - result.stdout.fnmatch_lines( - [ - "vscode-integration:", - '*--foo=DEST_FOO*Set the value for the fixture "bar".', - ] - ) - - -def test_hello_ini_setting(testdir): - testdir.makeini( - """ - [pytest] - HELLO = world - """ - ) - - testdir.makepyfile( - """ - import pytest - - @pytest.fixture - def hello(request): - return request.config.getini('HELLO') - - def test_hello_world(hello): - assert hello == 'world' - """ - ) - - result = testdir.runpytest("-v") - - # fnmatch_lines does an assertion internally - result.stdout.fnmatch_lines( - [ - "*::test_hello_world PASSED*", - ] - ) - - # make sure that that we get a '0' exit code for the testsuite - assert result.ret == 0