From 03c0c2715d7fd087d370181132fc6d09b0d42011 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Wed, 29 Jun 2022 10:57:36 -0700 Subject: [PATCH 01/49] Adding execution fixes. --- pythonFiles/testing_tools/adapter/__main__.py | 6 ++ pythonFiles/unittestadapter/discovery.py | 6 ++ pythonFiles/unittestadapter/execution.py | 29 ++++---- .../testing/testController/common/types.ts | 23 +++++- .../testing/testController/controller.ts | 34 +++++---- .../unittest/testExecutionAdapter.ts | 71 +++++++++++++++++++ .../testController/workspaceTestAdapter.ts | 10 ++- 7 files changed, 148 insertions(+), 31 deletions(-) create mode 100644 src/client/testing/testController/unittest/testExecutionAdapter.ts diff --git a/pythonFiles/testing_tools/adapter/__main__.py b/pythonFiles/testing_tools/adapter/__main__.py index 218456897df6..d4fb3c4b49a6 100644 --- a/pythonFiles/testing_tools/adapter/__main__.py +++ b/pythonFiles/testing_tools/adapter/__main__.py @@ -9,6 +9,12 @@ from . import pytest, report from .errors import UnsupportedCommandError, UnsupportedToolError +# ##### +# import debugpy + + +# debugpy.connect(("localhost", 5678)) +###### TOOLS = { "pytest": { "_add_subparser": pytest.add_cli_subparser, diff --git a/pythonFiles/unittestadapter/discovery.py b/pythonFiles/unittestadapter/discovery.py index 396f99f7996c..ae1c274d5efb 100644 --- a/pythonFiles/unittestadapter/discovery.py +++ b/pythonFiles/unittestadapter/discovery.py @@ -10,6 +10,12 @@ import unittest from typing import List, Literal, Optional, Tuple, TypedDict, Union +###### +# import debugpy + +# debugpy.connect(("localhost", 5678)) +# debugpy.breakpoint() + # 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) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index e4b8393ef4ef..bb1eee101f39 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -11,10 +11,9 @@ from types import TracebackType from typing import Dict, List, Optional, Tuple, Type, TypeAlias, TypedDict +from discovery import parse_unittest_discovery_args from typing_extensions import NotRequired -from .discovery import parse_unittest_discovery_args - # 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) @@ -139,7 +138,9 @@ def formatResult( "subtest": subtest.id() if subtest else None, } - self.formatted[test_id] = result + self.formatted[test_id] = result # in future: send one by one as we get it. + print("we are adding inside formatResult: ") + print(result) class TestExecutionStatus(str, enum.Enum): @@ -172,7 +173,7 @@ def run_tests( uuid: Optional[str], ) -> PayloadDict: cwd = os.path.abspath(start_dir) - status = TestExecutionStatus.success + status = TestExecutionStatus.error error = None payload: PayloadDict = {"cwd": cwd, "uuid": uuid, "status": status} @@ -191,20 +192,18 @@ def run_tests( "pattern": pattern, "top_level_dir": top_level_dir, } - suite = loader.discover(**{k: v for k, v in args.items() if v is not None}) + suite = loader.discover(start_dir, pattern, top_level_dir) # Run tests. runner = unittest.TextTestRunner(resultclass=UnittestTestResult) result: UnittestTestResult = runner.run(suite) # type: ignore - # Filter tests by id. - filtered_results = {k: v for k, v in result.formatted.items() if k in test_ids} - payload["result"] = filtered_results + payload["result"] = result.formatted # Add a payload entry with the list of test ids for tests that weren't found. - not_found = set(test_ids) - set(filtered_results.keys()) - if not_found: - payload["not_found"] = list(not_found) + # not_found = set(test_ids) - set(filtered_results.keys()) + # if not_found: + # payload["not_found"] = list(not_found) except Exception: status = TestExecutionStatus.error error = traceback.format_exc() @@ -224,7 +223,9 @@ def run_tests( argv = sys.argv[1:] index = argv.index("--udiscovery") - start_dir, pattern, top_level_dir = parse_unittest_discovery_args(argv[index + 1 :]) + start_dir, pattern, top_level_dir = parse_unittest_discovery_args( + argv[index + 1 :] + ) # do we need 225,227? # start_path = pathlib.Path.home() / "Documents" / "Sandbox" / "unittest-subtest" # test_ids = [ @@ -236,8 +237,8 @@ def run_tests( # Perform test execution. port, uuid, test_ids = parse_execution_cli_args(argv[:index]) - run_tests(start_dir, test_ids, pattern, top_level_dir, uuid) - + payload = run_tests(start_dir, test_ids, pattern, top_level_dir, uuid) + # print(payload) # # 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) diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index 7437bcde5524..2843e37d8277 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -136,10 +136,16 @@ export type TestDiscoveryCommand = { args: string[]; }; +// do we need args as string[] for exeuction? 6/27/22 +export type TestExecutionCommand = { + script: string; + args: string[]; +}; + export type TestCommandOptions = { workspaceFolder: Uri; cwd: string; - command: TestDiscoveryCommand; + command: TestDiscoveryCommand | TestExecutionCommand; // 6/27/22 token?: CancellationToken; outChannel?: OutputChannel; }; @@ -159,6 +165,11 @@ export interface ITestDiscoveryAdapter { discoverTests(uri: Uri): Promise; } +// interface for execution/runner adapter 6/27/22 +export interface ITestExecutionAdapter { + runTests(uri: Uri): Promise; +} + // Same types as in pythonFiles/unittestadapter/utils.py export type DiscoveredTestType = 'folder' | 'file' | 'class' | 'test'; @@ -172,6 +183,7 @@ export type DiscoveredTestCommon = { export type DiscoveredTestItem = DiscoveredTestCommon & { lineno: number; + runID: string; }; export type DiscoveredTestNode = DiscoveredTestCommon & { @@ -184,3 +196,12 @@ export type DiscoveredTestPayload = { status: 'success' | 'error'; errors?: string[]; }; + +// payload for exeuctionTest 6/27/22 +export type ExecutionTestPayload = { + cwd: string; + status: 'success' | 'error'; + result?: { [key: string]: { [key: string]: string | null } }; + notFound?: string[]; + error: string; +}; diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 03101f9bd684..1d7e8040568a 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -15,6 +15,7 @@ import { CancellationTokenSource, Uri, EventEmitter, + TestMessage, } from 'vscode'; import { IExtensionSingleActivationService } from '../../activation/types'; import { ICommandManager, IWorkspaceService } from '../../common/application/types'; @@ -214,21 +215,21 @@ export class PythonTestController implements ITestController, IExtensionSingleAc 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; @@ -311,6 +312,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc `Running Tests for Workspace(s): ${workspaces.map((w) => w.uri.fsPath).join(';')}`, true, ); + const dispose = token.onCancellationRequested(() => { runInstance.end(); }); @@ -333,6 +335,9 @@ export class PythonTestController implements ITestController, IExtensionSingleAc const w = this.workspaceService.getWorkspaceFolder(i.uri); if (w?.uri.fsPath === workspace.uri.fsPath) { testItems.push(i); + // trying to add here? test out using below two lines + // const message = new TestMessage('this is intentional'); + // runInstance.passed(i); } }); @@ -355,6 +360,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc ); } if (settings.testing.unittestEnabled) { + // potentially sqeeze in the new exeuction way here? sendTelemetryEvent(EventName.UNITTEST_RUN, undefined, { tool: 'unittest', debugging: request.profile?.kind === TestRunProfileKind.Debug, diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts new file mode 100644 index 000000000000..306582edec78 --- /dev/null +++ b/src/client/testing/testController/unittest/testExecutionAdapter.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, + ExecutionTestPayload, + ITestExecutionAdapter, + ITestServer, + TestCommandOptions, + TestExecutionCommand, +} from '../common/types'; + +/** + * Wrapper Class for unittest test execution. This is where we call `runTestCommand`? + */ + +export class UnittestTestExecutionAdapter 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): 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, + }; + + 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: ['--uexecution', ...args], + }; +} diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index bb9e084886ac..56e3a7dd9b72 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -11,7 +11,13 @@ import { sendTelemetryEvent } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; import { TestProvider } from '../types'; import { createErrorTestItem, DebugTestTag, ErrorTestItemOptions, RunTestTag } from './common/testItemUtilities'; -import { DiscoveredTestItem, DiscoveredTestNode, DiscoveredTestType, ITestDiscoveryAdapter } from './common/types'; +import { + DiscoveredTestItem, + DiscoveredTestNode, + DiscoveredTestType, + ITestDiscoveryAdapter, + // ITestExecutionAdapter, +} from './common/types'; /** * This class exposes a test-provider-agnostic way of discovering tests. @@ -31,7 +37,7 @@ export class WorkspaceTestAdapter { private testProvider: TestProvider, private discoveryAdapter: ITestDiscoveryAdapter, // TODO: Implement test running - // private runningAdapter: ITestRunningAdapter, + // private executionAdapter: ITestExecutionAdapter, private workspaceUri: Uri, ) {} From c07c5acde9b18e297bc9c204e91e757d02d4b030 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Thu, 30 Jun 2022 09:59:12 -0700 Subject: [PATCH 02/49] add map of runIDToTestItem for workspaceTestAdapter instance --- pythonFiles/unittestadapter/utils.py | 2 ++ .../testing/testController/controller.ts | 1 + .../testController/workspaceTestAdapter.ts | 20 +++++++++++++------ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/pythonFiles/unittestadapter/utils.py b/pythonFiles/unittestadapter/utils.py index 44638071520d..4d0342cfaad3 100644 --- a/pythonFiles/unittestadapter/utils.py +++ b/pythonFiles/unittestadapter/utils.py @@ -28,6 +28,7 @@ class TestData(TypedDict): class TestItem(TestData): lineno: str + runID: str class TestNode(TestData): @@ -190,6 +191,7 @@ def build_test_tree( "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) diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 1d7e8040568a..ff07e9d8e9e3 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -337,6 +337,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc testItems.push(i); // trying to add here? test out using below two lines // const message = new TestMessage('this is intentional'); + // runInstance.failed(i, message); // runInstance.passed(i); } }); diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 56e3a7dd9b72..85d7809db7c4 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -33,13 +33,18 @@ export class WorkspaceTestAdapter { private testData: DiscoveredTestNode | undefined; + // potentially a hashmap of runID : testItem? + runIdToTestItem: Map; + constructor( private testProvider: TestProvider, private discoveryAdapter: ITestDiscoveryAdapter, // TODO: Implement test running // private executionAdapter: ITestExecutionAdapter, - private workspaceUri: Uri, - ) {} + private workspaceUri: Uri, // private runIdToTestItem: Map, + ) { + this.runIdToTestItem = new Map(); + } public async discoverTests( testController: TestController, @@ -154,7 +159,7 @@ export class WorkspaceTestAdapter { if (workspaceNode) { updateTestTree(testController, rawTestData.tests, this.testData, workspaceNode, token); } else { - populateTestTree(testController, rawTestData.tests, undefined, token); + populateTestTree(testController, rawTestData.tests, undefined, this, token); } } else { // Delete everything from the test controller. @@ -261,17 +266,18 @@ function updateTestTree( testRoot!.children.add(testItem); // Populate the test tree under the newly created node. - populateTestTree(testController, child, testItem, token); + // populateTestTree(testController, child, testItem, token, this); uncomment later } } } }); } - +// had to switch the order of the original parameter since required param cannot follow optional. function populateTestTree( testController: TestController, testTreeData: DiscoveredTestNode, testRoot: TestItem | undefined, + wstAdapter: WorkspaceTestAdapter, token?: CancellationToken, ): void { // If testRoot is undefined, use the info of the root item of testTreeData to create a test item, and append it to the test controller. @@ -295,6 +301,8 @@ function populateTestTree( testItem.range = range; testItem.tags = [RunTestTag, DebugTestTag]; testRoot!.children.add(testItem); + // add to our map + wstAdapter.runIdToTestItem.set(child.runID, testItem); } else { let node = testController.items.get(child.path); @@ -307,7 +315,7 @@ function populateTestTree( testRoot!.children.add(node); } - populateTestTree(testController, child, node, token); + populateTestTree(testController, child, node, wstAdapter, token); } } }); From 4313014b56a3abd17575ca5bd9b7859041f8bdb5 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Tue, 5 Jul 2022 13:46:59 -0700 Subject: [PATCH 03/49] update UI accordingly using new execution method --- pythonFiles/unittestadapter/execution.py | 29 ++++++---- .../testing/testController/common/types.ts | 10 +++- .../testing/testController/controller.ts | 56 +++++++++++++------ .../unittest/testExecutionAdapter.ts | 2 +- .../testController/workspaceTestAdapter.ts | 44 ++++++++++++++- 5 files changed, 107 insertions(+), 34 deletions(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index bb1eee101f39..f4ae5cb41374 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -12,6 +12,7 @@ from typing import Dict, List, Optional, Tuple, Type, TypeAlias, TypedDict from discovery import parse_unittest_discovery_args +from testing_tools import socket_manager from typing_extensions import NotRequired # Add the path to pythonFiles to sys.path to find testing_tools.socket_manager. @@ -65,6 +66,7 @@ class TestOutcomeEnum(str, enum.Enum): class UnittestTestResult(unittest.TextTestResult): + # formatted: List[Dict[str, Dict[str, str | None]]] = [] formatted: Dict[str, Dict[str, str | None]] = dict() def startTest(self, test: unittest.TestCase): @@ -138,7 +140,10 @@ def formatResult( "subtest": subtest.id() if subtest else None, } - self.formatted[test_id] = result # in future: send one by one as we get it. + self.formatted[test_id] = result # in future: send one by one as we get it.only + # tempResult = {test_id: result} ## + # tempResult = {result} + # self.formatted.append(tempResult) ## print("we are adding inside formatResult: ") print(result) @@ -240,14 +245,14 @@ def run_tests( payload = run_tests(start_dir, test_ids, pattern, top_level_dir, uuid) # print(payload) -# # 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 - -# {data}""" -# result = s.socket.sendall(request.encode("utf-8")) # type: ignore + # # 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 + +{data}""" + result = s.socket.sendall(request.encode("utf-8")) # type: ignore diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index 2843e37d8277..b03a4ac500a1 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -201,7 +201,15 @@ export type DiscoveredTestPayload = { export type ExecutionTestPayload = { cwd: string; status: 'success' | 'error'; - result?: { [key: string]: { [key: string]: string | null } }; + result?: { + [testRunID: string]: { + test?: string; + outcome?: string; + message?: string; + traceback?: string; + subtest?: string; + }; + }; notFound?: string[]; error: string; }; diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index ff07e9d8e9e3..38f3eb63fdca 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -15,7 +15,7 @@ import { CancellationTokenSource, Uri, EventEmitter, - TestMessage, + // TestMessage, } from 'vscode'; import { IExtensionSingleActivationService } from '../../activation/types'; import { ICommandManager, IWorkspaceService } from '../../common/application/types'; @@ -32,9 +32,16 @@ import { PYTEST_PROVIDER, UNITTEST_PROVIDER } from '../common/constants'; import { TestProvider } from '../types'; import { PythonTestServer } from './common/server'; import { DebugTestTag, getNodeByUri, RunTestTag } from './common/testItemUtilities'; -import { ITestController, ITestDiscoveryAdapter, ITestFrameworkController, TestRefreshOptions } from './common/types'; +import { + ITestController, + ITestDiscoveryAdapter, + ITestFrameworkController, + TestRefreshOptions, + ITestExecutionAdapter, +} from './common/types'; import { UnittestTestDiscoveryAdapter } from './unittest/testDiscoveryAdapter'; import { WorkspaceTestAdapter } from './workspaceTestAdapter'; +import { UnittestTestExecutionAdapter } from './unittest/testExecutionAdapter'; // Types gymnastics to make sure that sendTriggerTelemetry only accepts the correct types. type EventPropertyType = IEventNamePropertyMapping[EventName.UNITTEST_DISCOVERY_TRIGGER]; @@ -144,18 +151,26 @@ export class PythonTestController implements ITestController, IExtensionSingleAc const settings = this.configSettings.getSettings(workspace.uri); let discoveryAdapter: ITestDiscoveryAdapter; + let executionAdapter: ITestExecutionAdapter; // added 6/30 let testProvider: TestProvider; if (settings.testing.unittestEnabled) { 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 }); + executionAdapter = new UnittestTestExecutionAdapter(this.pythonTestServer, this.configSettings); testProvider = PYTEST_PROVIDER; } - const workspaceTestAdapter = new WorkspaceTestAdapter(testProvider, discoveryAdapter, workspace.uri); + const workspaceTestAdapter = new WorkspaceTestAdapter( + testProvider, + discoveryAdapter, + executionAdapter, + workspace.uri, + ); this.testAdapters.set(workspace.uri, workspaceTestAdapter); @@ -335,13 +350,18 @@ export class PythonTestController implements ITestController, IExtensionSingleAc const w = this.workspaceService.getWorkspaceFolder(i.uri); if (w?.uri.fsPath === workspace.uri.fsPath) { testItems.push(i); - // trying to add here? test out using below two lines + // trying to add here??? test out using below two lines // const message = new TestMessage('this is intentional'); // runInstance.failed(i, message); // runInstance.passed(i); } }); - + // 6/30 + const testAdapter = + this.testAdapters.get(workspace.uri) || + (this.testAdapters.values().next().value as WorkspaceTestAdapter); + testAdapter.executeTests(this.testController, runInstance, token); + // const settings = this.configSettings.getSettings(workspace.uri); if (testItems.length > 0) { if (settings.testing.pytestEnabled) { @@ -366,17 +386,19 @@ export class PythonTestController implements ITestController, IExtensionSingleAc tool: 'unittest', debugging: request.profile?.kind === TestRunProfileKind.Debug, }); - return this.unittest.runTests( - { - includes: testItems, - excludes: request.exclude ?? [], - runKind: request.profile?.kind ?? TestRunProfileKind.Run, - runInstance, - }, - workspace, - token, - this.testController, - ); + // 6/30 + + // return this.unittest.runTests( + // { + // includes: testItems, + // excludes: request.exclude ?? [], + // runKind: request.profile?.kind ?? TestRunProfileKind.Run, + // runInstance, + // }, + // workspace, + // token, + // this.testController, + // ); } } @@ -388,7 +410,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc ); } finally { runInstance.appendOutput(`Finished running tests!\r\n`); - runInstance.end(); + // runInstance.end(); // dispose.dispose(); if (unconfiguredWorkspaces.length > 0) { diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index 306582edec78..da41dcc9a9b8 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -66,6 +66,6 @@ function buildExecutionCommand(args: string[]): TestExecutionCommand { return { script: executionScript, - args: ['--uexecution', ...args], + args: ['--udiscovery', ...args], }; } diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 85d7809db7c4..a9ef009bac19 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import * as util from 'util'; -import { CancellationToken, Position, Range, TestController, TestItem, Uri } from 'vscode'; +import { CancellationToken, Position, Range, TestController, TestItem, TestMessage, TestRun, Uri } from 'vscode'; import { createDeferred, Deferred } from '../../common/utils/async'; import { Testing } from '../../common/utils/localize'; import { traceError } from '../../logging'; @@ -16,7 +16,7 @@ import { DiscoveredTestNode, DiscoveredTestType, ITestDiscoveryAdapter, - // ITestExecutionAdapter, + ITestExecutionAdapter, } from './common/types'; /** @@ -40,12 +40,50 @@ export class WorkspaceTestAdapter { private testProvider: TestProvider, private discoveryAdapter: ITestDiscoveryAdapter, // TODO: Implement test running - // private executionAdapter: ITestExecutionAdapter, + private executionAdapter: ITestExecutionAdapter, private workspaceUri: Uri, // private runIdToTestItem: Map, ) { this.runIdToTestItem = new Map(); } + public async executeTests( + testController: TestController, + runInstance: TestRun, + token?: CancellationToken, + ): Promise { + const rawTestExecData = await this.executionAdapter.runTests(this.workspaceUri); + // const temp = rawTestExecData.result; + if (rawTestExecData !== undefined && rawTestExecData.result !== undefined) { + for (const keyTemp of Object.keys(rawTestExecData.result)) { + // console.log(keyTemp, rawTestExecData.result[keyTemp]); + // check for result and update the UI accordingly. + if (rawTestExecData.result[keyTemp].outcome === 'failure') { + const message = new TestMessage('this is temporary message'); + const grabTestItem = this.runIdToTestItem.get(keyTemp); + if (grabTestItem !== undefined) { + // runInstance.failed(grabTestItem, message); // choose appropriate one + runInstance.passed(grabTestItem); // choose appropriate one + } + } else if (rawTestExecData.result[keyTemp].outcome === 'success') { + const grabTestItem = this.runIdToTestItem.get(keyTemp); + if (grabTestItem !== undefined) { + const message = new TestMessage('this is temporary message'); + // runInstance.failed(grabTestItem, message); // choose appropriate one + runInstance.passed(grabTestItem); // choose appropriate one + } + } + } + } + + // console.log(temp); + // console.log(temp?.testRunID); + + // console.log(rawTestExecData); + // console.log(token); + // console.log(runInstance); + // console.log(testController); + } + public async discoverTests( testController: TestController, token?: CancellationToken, From d2046888aba6368cf45f0b5a515516eea6f06095 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Tue, 12 Jul 2022 11:09:44 -0700 Subject: [PATCH 04/49] attempt to fix invalid argument --- .../testing/testController/controller.ts | 24 +++---- .../testing/testController/unittest/runner.ts | 1 + .../testController/workspaceTestAdapter.ts | 63 ++++++++++++++++--- .../workspaceTestAdapter.unit.test.ts | 35 +++++++++-- 4 files changed, 98 insertions(+), 25 deletions(-) diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 38f3eb63fdca..0007895699cc 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -361,7 +361,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc this.testAdapters.get(workspace.uri) || (this.testAdapters.values().next().value as WorkspaceTestAdapter); testAdapter.executeTests(this.testController, runInstance, token); - // + const settings = this.configSettings.getSettings(workspace.uri); if (testItems.length > 0) { if (settings.testing.pytestEnabled) { @@ -369,16 +369,16 @@ export class PythonTestController implements ITestController, IExtensionSingleAc tool: 'pytest', debugging: request.profile?.kind === TestRunProfileKind.Debug, }); - return this.pytest.runTests( - { - includes: testItems, - excludes: request.exclude ?? [], - runKind: request.profile?.kind ?? TestRunProfileKind.Run, - runInstance, - }, - workspace, - token, - ); + // return this.pytest.runTests( + // { + // includes: testItems, + // excludes: request.exclude ?? [], + // runKind: request.profile?.kind ?? TestRunProfileKind.Run, + // runInstance, + // }, + // workspace, + // token, + // ); } if (settings.testing.unittestEnabled) { // potentially sqeeze in the new exeuction way here? @@ -386,7 +386,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc tool: 'unittest', debugging: request.profile?.kind === TestRunProfileKind.Debug, }); - // 6/30 + // 6/30 below is old way. // return this.unittest.runTests( // { diff --git a/src/client/testing/testController/unittest/runner.ts b/src/client/testing/testController/unittest/runner.ts index ccc14ae0b4c2..d21fdc12bd60 100644 --- a/src/client/testing/testController/unittest/runner.ts +++ b/src/client/testing/testController/unittest/runner.ts @@ -67,6 +67,7 @@ export class UnittestRunner implements ITestsRunner { nodes.forEach((n) => { if (n.uri) { const fsRunIds = fileToTestCases.get(n.uri.fsPath); + console.log(fsRunIds); // check this if (fsRunIds) { fsRunIds.push(n); } else { diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index a9ef009bac19..37d16ebbc51c 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -4,6 +4,7 @@ import * as path from 'path'; import * as util from 'util'; import { CancellationToken, Position, Range, TestController, TestItem, TestMessage, TestRun, Uri } from 'vscode'; +import { Location } from 'vscode'; // import { createDeferred, Deferred } from '../../common/utils/async'; import { Testing } from '../../common/utils/localize'; import { traceError } from '../../logging'; @@ -36,6 +37,8 @@ export class WorkspaceTestAdapter { // potentially a hashmap of runID : testItem? runIdToTestItem: Map; + runIdToVSid: Map; + constructor( private testProvider: TestProvider, private discoveryAdapter: ITestDiscoveryAdapter, @@ -44,6 +47,7 @@ export class WorkspaceTestAdapter { private workspaceUri: Uri, // private runIdToTestItem: Map, ) { this.runIdToTestItem = new Map(); + this.runIdToVSid = new Map(); } public async executeTests( @@ -52,27 +56,59 @@ export class WorkspaceTestAdapter { token?: CancellationToken, ): Promise { const rawTestExecData = await this.executionAdapter.runTests(this.workspaceUri); - // const temp = rawTestExecData.result; + if (rawTestExecData !== undefined && rawTestExecData.result !== undefined) { for (const keyTemp of Object.keys(rawTestExecData.result)) { - // console.log(keyTemp, rawTestExecData.result[keyTemp]); // check for result and update the UI accordingly. if (rawTestExecData.result[keyTemp].outcome === 'failure') { - const message = new TestMessage('this is temporary message'); + const text = `${rawTestExecData.result[keyTemp].test} failed: ${ + rawTestExecData.result[keyTemp].message ?? rawTestExecData.result[keyTemp].outcome + }\r\n`; + const message = new TestMessage(text); const grabTestItem = this.runIdToTestItem.get(keyTemp); - if (grabTestItem !== undefined) { - // runInstance.failed(grabTestItem, message); // choose appropriate one - runInstance.passed(grabTestItem); // choose appropriate one + const giveMeRealID = grabTestItem?.id; + if (giveMeRealID) { + const grabTestREAL = testController.items.size; + console.log(grabTestREAL); } + let tempArr: TestItem[]; + tempArr = []; + + // fetch inidividual testItem and store into tempArr + testController.items.forEach((i) => + i.children.forEach((z) => + z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), + ), + ); + const grabVSid = this.runIdToVSid.get(keyTemp); + // search through freshly built array of testItem to find the failed test and update UI. + tempArr.forEach((indiItem) => { + if (indiItem.id === grabVSid) { + if (indiItem.uri && indiItem.range) { + message.location = new Location(indiItem.uri, indiItem.range); + runInstance.failed(indiItem, message); + } + } + }); + // if (grabTestItem !== undefined) { + // if (grabTestItem.uri && grabTestItem.range) { + // message.location = new Location(grabTestItem.uri, grabTestItem.range); + // } + // runInstance.failed(grabTestItem, message); // choose appropriate one + // runInstance.appendOutput('you failed'); + // // runInstance.passed(grabTestItem); // choose appropriate one + // } } else if (rawTestExecData.result[keyTemp].outcome === 'success') { const grabTestItem = this.runIdToTestItem.get(keyTemp); if (grabTestItem !== undefined) { const message = new TestMessage('this is temporary message'); // runInstance.failed(grabTestItem, message); // choose appropriate one runInstance.passed(grabTestItem); // choose appropriate one + runInstance.appendOutput('You passed'); } } } + runInstance.end(); } // console.log(temp); @@ -320,7 +356,9 @@ function populateTestTree( ): void { // If testRoot is undefined, use the info of the root item of testTreeData to create a test item, and append it to the test controller. if (!testRoot) { + // const cleanChildPath = testTreeData.path.replace('\\\\', '\\'); // exyts testRoot = testController.createTestItem(testTreeData.path, testTreeData.name, Uri.file(testTreeData.path)); + // testRoot = testController.createTestItem(testTreeData.path, testTreeData.name, Uri.file(cleanChildPath)); testRoot.canResolveChildren = true; testRoot.tags = [RunTestTag, DebugTestTag]; @@ -333,7 +371,16 @@ function populateTestTree( // Try to identify if we fall into TestItem or TestNode? if (isTestItem(child)) { + // warning warning: I think there is a problem with child.path being double dash instead of single dash in legacy code + // maybe thats why uri getting messed up=> highly likely + // const regex = /\\\\/g; + // const cleanChildPath = child.path.replace(regex, '\\'); + // const cleanChildPath = child.path.replace('\\\\', '\\'); const testItem = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); + testItem.tags = [RunTestTag, DebugTestTag]; + // const testItem = testController.createTestItem(child.id_, child.name, Uri.file(cleanChildPath)); + + // const trackerVar = Uri.file(cleanChildPath).fsPath; const range = new Range(new Position(child.lineno - 1, 0), new Position(child.lineno, 0)); testItem.canResolveChildren = false; testItem.range = range; @@ -341,13 +388,15 @@ function populateTestTree( testRoot!.children.add(testItem); // add to our map wstAdapter.runIdToTestItem.set(child.runID, testItem); + wstAdapter.runIdToVSid.set(child.runID, child.id_); } else { let node = testController.items.get(child.path); if (!node) { // replace child.path with child.id_ (unique) + // const cleanChildPath = child.path.replace('\\\\', '\\'); node = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); - + // node = testController.createTestItem(child.id_, child.name, Uri.file(cleanChildPath)); node.canResolveChildren = true; node.tags = [RunTestTag, DebugTestTag]; diff --git a/src/test/testing/testController/workspaceTestAdapter.unit.test.ts b/src/test/testing/testController/workspaceTestAdapter.unit.test.ts index 7b94d73aea6d..b6be8d6081de 100644 --- a/src/test/testing/testController/workspaceTestAdapter.unit.test.ts +++ b/src/test/testing/testController/workspaceTestAdapter.unit.test.ts @@ -7,6 +7,7 @@ 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 { WorkspaceTestAdapter } from '../../../client/testing/testController/workspaceTestAdapter'; import * as Telemetry from '../../../client/telemetry'; import { EventName } from '../../../client/telemetry/constants'; @@ -109,8 +110,13 @@ suite('Workspace test adapter', () => { discoverTestsStub.resolves(); const testDiscoveryAdapter = new UnittestTestDiscoveryAdapter(stubTestServer, stubConfigSettings); - - const workspaceTestAdapter = new WorkspaceTestAdapter('unittest', testDiscoveryAdapter, Uri.parse('foo')); + const testExecutionAdapter = new UnittestTestExecutionAdapter(stubTestServer, stubConfigSettings); // 7/7 + const workspaceTestAdapter = new WorkspaceTestAdapter( + 'unittest', + testDiscoveryAdapter, + testExecutionAdapter, + Uri.parse('foo'), + ); await workspaceTestAdapter.discoverTests(testController); @@ -129,8 +135,13 @@ suite('Workspace test adapter', () => { ); const testDiscoveryAdapter = new UnittestTestDiscoveryAdapter(stubTestServer, stubConfigSettings); - - const workspaceTestAdapter = new WorkspaceTestAdapter('unittest', testDiscoveryAdapter, Uri.parse('foo')); + const testExecutionAdapter = new UnittestTestExecutionAdapter(stubTestServer, stubConfigSettings); // 7/7 + const workspaceTestAdapter = new WorkspaceTestAdapter( + 'unittest', + testDiscoveryAdapter, + testExecutionAdapter, + Uri.parse('foo'), + ); // Try running discovery twice const one = workspaceTestAdapter.discoverTests(testController); @@ -145,8 +156,14 @@ suite('Workspace test adapter', () => { discoverTestsStub.resolves({ status: 'success' }); const testDiscoveryAdapter = new UnittestTestDiscoveryAdapter(stubTestServer, stubConfigSettings); + const testExecutionAdapter = new UnittestTestExecutionAdapter(stubTestServer, stubConfigSettings); - const workspaceTestAdapter = new WorkspaceTestAdapter('unittest', testDiscoveryAdapter, Uri.parse('foo')); + const workspaceTestAdapter = new WorkspaceTestAdapter( + 'unittest', + testDiscoveryAdapter, + testExecutionAdapter, + Uri.parse('foo'), + ); await workspaceTestAdapter.discoverTests(testController); @@ -161,8 +178,14 @@ suite('Workspace test adapter', () => { discoverTestsStub.rejects(new Error('foo')); const testDiscoveryAdapter = new UnittestTestDiscoveryAdapter(stubTestServer, stubConfigSettings); + const testExecutionAdapter = new UnittestTestExecutionAdapter(stubTestServer, stubConfigSettings); - const workspaceTestAdapter = new WorkspaceTestAdapter('unittest', testDiscoveryAdapter, Uri.parse('foo')); + const workspaceTestAdapter = new WorkspaceTestAdapter( + 'unittest', + testDiscoveryAdapter, + testExecutionAdapter, + Uri.parse('foo'), + ); await workspaceTestAdapter.discoverTests(testController); From 221086ecfa71ff0d0303b90b8c3f19578acceed5 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Wed, 13 Jul 2022 19:49:53 -0700 Subject: [PATCH 05/49] fix invalid argument --- .../testing/testController/controller.ts | 14 ++-- .../testController/workspaceTestAdapter.ts | 70 +++++++++++++------ 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 0007895699cc..be4634253ed5 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -356,11 +356,6 @@ export class PythonTestController implements ITestController, IExtensionSingleAc // runInstance.passed(i); } }); - // 6/30 - const testAdapter = - this.testAdapters.get(workspace.uri) || - (this.testAdapters.values().next().value as WorkspaceTestAdapter); - testAdapter.executeTests(this.testController, runInstance, token); const settings = this.configSettings.getSettings(workspace.uri); if (testItems.length > 0) { @@ -386,6 +381,12 @@ export class PythonTestController implements ITestController, IExtensionSingleAc tool: 'unittest', debugging: request.profile?.kind === TestRunProfileKind.Debug, }); + // 6/30 + const testAdapter = + this.testAdapters.get(workspace.uri) || + (this.testAdapters.values().next().value as WorkspaceTestAdapter); + return testAdapter.executeTests(this.testController, runInstance, token); + // 6/30 below is old way. // return this.unittest.runTests( @@ -410,9 +411,8 @@ export class PythonTestController implements ITestController, IExtensionSingleAc ); } finally { runInstance.appendOutput(`Finished running tests!\r\n`); - // runInstance.end(); // + runInstance.end(); // dispose.dispose(); - if (unconfiguredWorkspaces.length > 0) { this.runWithoutConfigurationEvent.fire(unconfiguredWorkspaces); } diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 37d16ebbc51c..6c31946db212 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -30,7 +30,8 @@ import { * and uses them to insert/update/remove items in the `TestController` instance behind the testing UI whenever the `PythonTestController` requests a refresh. */ export class WorkspaceTestAdapter { - private discovering: Deferred | undefined = undefined; + // private discovering: Deferred | undefined = undefined; ???? + private discovering: Deferred | undefined; private testData: DiscoveredTestNode | undefined; @@ -60,32 +61,45 @@ export class WorkspaceTestAdapter { if (rawTestExecData !== undefined && rawTestExecData.result !== undefined) { for (const keyTemp of Object.keys(rawTestExecData.result)) { // check for result and update the UI accordingly. + let tempArr: TestItem[]; + tempArr = []; + + // fetch inidividual testItem and store into tempArr + testController.items.forEach((i) => + i.children.forEach((z) => + z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), + ), + ); if (rawTestExecData.result[keyTemp].outcome === 'failure') { const text = `${rawTestExecData.result[keyTemp].test} failed: ${ rawTestExecData.result[keyTemp].message ?? rawTestExecData.result[keyTemp].outcome }\r\n`; const message = new TestMessage(text); const grabTestItem = this.runIdToTestItem.get(keyTemp); - const giveMeRealID = grabTestItem?.id; - if (giveMeRealID) { - const grabTestREAL = testController.items.size; - console.log(grabTestREAL); - } - let tempArr: TestItem[]; - tempArr = []; - - // fetch inidividual testItem and store into tempArr - testController.items.forEach((i) => - i.children.forEach((z) => - z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), - ), - ); + + // const giveMeRealID = grabTestItem?.id; + // if (giveMeRealID) { + // const grabTestREAL = testController.items.size; + // // console.log(grabTestREAL); + // } + // let tempArr: TestItem[]; + // tempArr = []; + + // // fetch inidividual testItem and store into tempArr + // testController.items.forEach((i) => + // i.children.forEach((z) => + // z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), + // ), + // ); + // note that keyTemp is a runId for unittest library... const grabVSid = this.runIdToVSid.get(keyTemp); // search through freshly built array of testItem to find the failed test and update UI. tempArr.forEach((indiItem) => { if (indiItem.id === grabVSid) { if (indiItem.uri && indiItem.range) { message.location = new Location(indiItem.uri, indiItem.range); + runInstance.started(indiItem); + // console.log('fail'); runInstance.failed(indiItem, message); } } @@ -100,15 +114,26 @@ export class WorkspaceTestAdapter { // } } else if (rawTestExecData.result[keyTemp].outcome === 'success') { const grabTestItem = this.runIdToTestItem.get(keyTemp); + const grabVSid = this.runIdToVSid.get(keyTemp); if (grabTestItem !== undefined) { - const message = new TestMessage('this is temporary message'); - // runInstance.failed(grabTestItem, message); // choose appropriate one - runInstance.passed(grabTestItem); // choose appropriate one - runInstance.appendOutput('You passed'); + // const message = new TestMessage('this is temporary message'); + // // runInstance.failed(grabTestItem, message); // choose appropriate one + tempArr.forEach((indiItem) => { + if (indiItem.id === grabVSid) { + if (indiItem.uri && indiItem.range) { + runInstance.started(grabTestItem); + // console.log('success'); + runInstance.passed(grabTestItem); // choose appropriate one + } + } + }); + // runInstance.started(grabTestItem); + // runInstance.passed(grabTestItem); // choose appropriate one + // runInstance.appendOutput('You passed'); } } } - runInstance.end(); + // runInstance.end(); } // console.log(temp); @@ -381,7 +406,10 @@ function populateTestTree( // const testItem = testController.createTestItem(child.id_, child.name, Uri.file(cleanChildPath)); // const trackerVar = Uri.file(cleanChildPath).fsPath; - const range = new Range(new Position(child.lineno - 1, 0), new Position(child.lineno, 0)); + const range = new Range( + new Position(Number(child.lineno) - 1, 0), + new Position(Number(child.lineno), 0), + ); testItem.canResolveChildren = false; testItem.range = range; testItem.tags = [RunTestTag, DebugTestTag]; From 9d695e22235e19547089b00e4b6663601d5c9207 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Fri, 15 Jul 2022 14:39:43 -0700 Subject: [PATCH 06/49] add traceback to the message --- src/client/common/utils/localize.ts | 5 + .../testing/testController/controller.ts | 92 +++++++++---------- .../testController/workspaceTestAdapter.ts | 86 +++++++++-------- 3 files changed, 91 insertions(+), 92 deletions(-) diff --git a/src/client/common/utils/localize.ts b/src/client/common/utils/localize.ts index 5690e64bcfce..5e667d023a09 100644 --- a/src/client/common/utils/localize.ts +++ b/src/client/common/utils/localize.ts @@ -514,6 +514,11 @@ export namespace Testing { ); export const errorUnittestDiscovery = localize('Testing.errorUnittestDiscovery', 'Unittest test discovery error'); export const seePythonOutput = localize('Testing.seePythonOutput', '(see Output > Python)'); + export const cancelUnittestExecution = localize( + 'Testing.cancelUnittestExecution', + 'Canceled unittest test execution', + ); + export const errorUnittestExecution = localize('Testing.errorUnittestExecution', 'Unittest test execution error'); } export namespace OutdatedDebugger { diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index be4634253ed5..82a9ae95fb48 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -230,21 +230,21 @@ export class PythonTestController implements ITestController, IExtensionSingleAc 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; @@ -350,10 +350,6 @@ export class PythonTestController implements ITestController, IExtensionSingleAc const w = this.workspaceService.getWorkspaceFolder(i.uri); if (w?.uri.fsPath === workspace.uri.fsPath) { testItems.push(i); - // trying to add here??? test out using below two lines - // const message = new TestMessage('this is intentional'); - // runInstance.failed(i, message); - // runInstance.passed(i); } }); @@ -364,16 +360,16 @@ export class PythonTestController implements ITestController, IExtensionSingleAc tool: 'pytest', debugging: request.profile?.kind === TestRunProfileKind.Debug, }); - // return this.pytest.runTests( - // { - // includes: testItems, - // excludes: request.exclude ?? [], - // runKind: request.profile?.kind ?? TestRunProfileKind.Run, - // runInstance, - // }, - // workspace, - // token, - // ); + return this.pytest.runTests( + { + includes: testItems, + excludes: request.exclude ?? [], + runKind: request.profile?.kind ?? TestRunProfileKind.Run, + runInstance, + }, + workspace, + token, + ); } if (settings.testing.unittestEnabled) { // potentially sqeeze in the new exeuction way here? @@ -381,25 +377,25 @@ export class PythonTestController implements ITestController, IExtensionSingleAc tool: 'unittest', debugging: request.profile?.kind === TestRunProfileKind.Debug, }); - // 6/30 - const testAdapter = - this.testAdapters.get(workspace.uri) || - (this.testAdapters.values().next().value as WorkspaceTestAdapter); - return testAdapter.executeTests(this.testController, runInstance, token); - - // 6/30 below is old way. - - // return this.unittest.runTests( - // { - // includes: testItems, - // excludes: request.exclude ?? [], - // runKind: request.profile?.kind ?? TestRunProfileKind.Run, - // runInstance, - // }, - // workspace, - // token, - // this.testController, - // ); + // new execution runner/adapter + // const testAdapter = + // this.testAdapters.get(workspace.uri) || + // (this.testAdapters.values().next().value as WorkspaceTestAdapter); + // return testAdapter.executeTests(this.testController, runInstance, token); + + // below is old way of running unittest execution + + return this.unittest.runTests( + { + includes: testItems, + excludes: request.exclude ?? [], + runKind: request.profile?.kind ?? TestRunProfileKind.Run, + runInstance, + }, + workspace, + token, + this.testController, + ); } } @@ -411,7 +407,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc ); } finally { runInstance.appendOutput(`Finished running tests!\r\n`); - runInstance.end(); // + runInstance.end(); dispose.dispose(); if (unconfiguredWorkspaces.length > 0) { this.runWithoutConfigurationEvent.fire(unconfiguredWorkspaces); diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 6c31946db212..836f962e35e9 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -33,6 +33,8 @@ export class WorkspaceTestAdapter { // private discovering: Deferred | undefined = undefined; ???? private discovering: Deferred | undefined; + private executing: Deferred | undefined; + private testData: DiscoveredTestNode | undefined; // potentially a hashmap of runID : testItem? @@ -56,13 +58,41 @@ export class WorkspaceTestAdapter { runInstance: TestRun, token?: CancellationToken, ): Promise { - const rawTestExecData = await this.executionAdapter.runTests(this.workspaceUri); + if (this.executing) { + return this.executing.promise; + } + + const deferred = createDeferred(); + this.executing = deferred; + + let rawTestExecData; + try { + rawTestExecData = await this.executionAdapter.runTests(this.workspaceUri); + deferred.resolve(); + } catch (ex) { + // handle token and telemetry here + sendTelemetryEvent(EventName.UNITTEST_RUN_ALL_FAILED, undefined); + + const cancel = token?.isCancellationRequested + ? Testing.cancelUnittestExecution + : Testing.errorUnittestExecution; + traceError(`${cancel}\r\n`, ex); + + // Also report on the test view + const message = util.format(`${cancel} ${Testing.seePythonOutput}\r\n`, ex); + const options = buildErrorNodeOptions(this.workspaceUri, message); + const errorNode = createErrorTestItem(testController, options); + testController.items.add(errorNode); + + deferred.reject(ex as Error); + } finally { + this.executing = undefined; + } if (rawTestExecData !== undefined && rawTestExecData.result !== undefined) { for (const keyTemp of Object.keys(rawTestExecData.result)) { // check for result and update the UI accordingly. - let tempArr: TestItem[]; - tempArr = []; + const tempArr: TestItem[] = []; // fetch inidividual testItem and store into tempArr testController.items.forEach((i) => @@ -70,27 +100,18 @@ export class WorkspaceTestAdapter { z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), ), ); + if (rawTestExecData.result[keyTemp].outcome === 'failure') { + const traceback = rawTestExecData.result[keyTemp].traceback + ? rawTestExecData.result[keyTemp] + .traceback!.splitLines({ trim: false, removeEmptyEntries: true }) + .join('\r\n') + : ''; const text = `${rawTestExecData.result[keyTemp].test} failed: ${ rawTestExecData.result[keyTemp].message ?? rawTestExecData.result[keyTemp].outcome - }\r\n`; + }\r\n${traceback}\r\n`; const message = new TestMessage(text); - const grabTestItem = this.runIdToTestItem.get(keyTemp); - // const giveMeRealID = grabTestItem?.id; - // if (giveMeRealID) { - // const grabTestREAL = testController.items.size; - // // console.log(grabTestREAL); - // } - // let tempArr: TestItem[]; - // tempArr = []; - - // // fetch inidividual testItem and store into tempArr - // testController.items.forEach((i) => - // i.children.forEach((z) => - // z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), - // ), - // ); // note that keyTemp is a runId for unittest library... const grabVSid = this.runIdToVSid.get(keyTemp); // search through freshly built array of testItem to find the failed test and update UI. @@ -99,50 +120,27 @@ export class WorkspaceTestAdapter { if (indiItem.uri && indiItem.range) { message.location = new Location(indiItem.uri, indiItem.range); runInstance.started(indiItem); - // console.log('fail'); runInstance.failed(indiItem, message); } } }); - // if (grabTestItem !== undefined) { - // if (grabTestItem.uri && grabTestItem.range) { - // message.location = new Location(grabTestItem.uri, grabTestItem.range); - // } - // runInstance.failed(grabTestItem, message); // choose appropriate one - // runInstance.appendOutput('you failed'); - // // runInstance.passed(grabTestItem); // choose appropriate one - // } } else if (rawTestExecData.result[keyTemp].outcome === 'success') { const grabTestItem = this.runIdToTestItem.get(keyTemp); const grabVSid = this.runIdToVSid.get(keyTemp); if (grabTestItem !== undefined) { - // const message = new TestMessage('this is temporary message'); - // // runInstance.failed(grabTestItem, message); // choose appropriate one tempArr.forEach((indiItem) => { if (indiItem.id === grabVSid) { if (indiItem.uri && indiItem.range) { runInstance.started(grabTestItem); - // console.log('success'); - runInstance.passed(grabTestItem); // choose appropriate one + runInstance.passed(grabTestItem); } } }); - // runInstance.started(grabTestItem); - // runInstance.passed(grabTestItem); // choose appropriate one - // runInstance.appendOutput('You passed'); } } } - // runInstance.end(); } - - // console.log(temp); - // console.log(temp?.testRunID); - - // console.log(rawTestExecData); - // console.log(token); - // console.log(runInstance); - // console.log(testController); + return Promise.resolve(); } public async discoverTests( From b405c7170b48184b89fafd0160fe4ef51163ea5e Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 16 Jul 2022 16:23:56 -0700 Subject: [PATCH 07/49] attempt to fix runID --- pythonFiles/tests/unittestadapter/test_utils.py | 4 ++++ pythonFiles/unittestadapter/execution.py | 8 ++++---- pythonFiles/visualstudio_py_testlauncher.py | 1 + src/client/testing/testController/workspaceTestAdapter.ts | 5 ++++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/pythonFiles/tests/unittestadapter/test_utils.py b/pythonFiles/tests/unittestadapter/test_utils.py index 05c745e05e86..a3bc1dd7693c 100644 --- a/pythonFiles/tests/unittestadapter/test_utils.py +++ b/pythonFiles/tests/unittestadapter/test_utils.py @@ -195,6 +195,7 @@ def test_build_simple_tree() -> None: "type_": TestNodeTypeEnum.test, "lineno": "13", "id_": file_path + "\\" + "TreeOne" + "\\" + "test_one", + "runID": "utils_simple_tree.TreeOne.test_one", }, { "name": "test_two", @@ -202,6 +203,7 @@ def test_build_simple_tree() -> None: "type_": TestNodeTypeEnum.test, "lineno": "16", "id_": file_path + "\\" + "TreeOne" + "\\" + "test_two", + "runID": "utils_simple_tree.TreeOne.test_two", }, ], "id_": file_path + "\\" + "TreeOne", @@ -253,6 +255,7 @@ def test_build_decorated_tree() -> None: "type_": TestNodeTypeEnum.test, "lineno": "24", "id_": file_path + "\\" + "TreeOne" + "\\" + "test_one", + "runID": "utils_decorated_tree.TreeOne.test_one", }, { "name": "test_two", @@ -260,6 +263,7 @@ def test_build_decorated_tree() -> None: "type_": TestNodeTypeEnum.test, "lineno": "28", "id_": file_path + "\\" + "TreeOne" + "\\" + "test_two", + "runID": "utils_decorated_tree.TreeOne.test_two", }, ], "id_": file_path + "\\" + "TreeOne", diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index f4ae5cb41374..8f76c0862eb9 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -144,8 +144,8 @@ def formatResult( # tempResult = {test_id: result} ## # tempResult = {result} # self.formatted.append(tempResult) ## - print("we are adding inside formatResult: ") - print(result) + # print("we are adding inside formatResult: ") + # print(result) class TestExecutionStatus(str, enum.Enum): @@ -218,7 +218,7 @@ def run_tests( payload["status"] = status - print(f"payload: \n{json.dumps(payload, indent=4)}") + # print(f"payload: \n{json.dumps(payload, indent=4)}") return payload @@ -236,7 +236,7 @@ def run_tests( # test_ids = [ # "subfolder.test_two.TestClassTwo.test_two_two", # "test_one.TestClassOne.test_func_one", - # "test_eight.TestClassEight.test_func_eight", + # "test_eight.TestClassEight.test_func_eight" # ] # uuid = "abcd" diff --git a/pythonFiles/visualstudio_py_testlauncher.py b/pythonFiles/visualstudio_py_testlauncher.py index 0b0ef3242f65..096315b5f62b 100644 --- a/pythonFiles/visualstudio_py_testlauncher.py +++ b/pythonFiles/visualstudio_py_testlauncher.py @@ -24,6 +24,7 @@ import sys import traceback import unittest +from types import CodeType, FunctionType try: import thread diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 836f962e35e9..fcab863086e0 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -19,6 +19,7 @@ import { ITestDiscoveryAdapter, ITestExecutionAdapter, } from './common/types'; +import { fixLogLines } from './common/utils'; /** * This class exposes a test-provider-agnostic way of discovering tests. @@ -100,7 +101,7 @@ export class WorkspaceTestAdapter { z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), ), ); - + runInstance.appendOutput('DOEDJODJEODJOPADKASJIKAOJDIOASD'); if (rawTestExecData.result[keyTemp].outcome === 'failure') { const traceback = rawTestExecData.result[keyTemp].traceback ? rawTestExecData.result[keyTemp] @@ -121,6 +122,7 @@ export class WorkspaceTestAdapter { message.location = new Location(indiItem.uri, indiItem.range); runInstance.started(indiItem); runInstance.failed(indiItem, message); + runInstance.appendOutput(fixLogLines(text)); // added 7/15 } } }); @@ -133,6 +135,7 @@ export class WorkspaceTestAdapter { if (indiItem.uri && indiItem.range) { runInstance.started(grabTestItem); runInstance.passed(grabTestItem); + runInstance.appendOutput('Passed here'); } } }); From 6f2d0b08d3800adf4dd010d00f495352634a0ee7 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 16 Jul 2022 16:29:17 -0700 Subject: [PATCH 08/49] remove import Codetypes --- pythonFiles/visualstudio_py_testlauncher.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pythonFiles/visualstudio_py_testlauncher.py b/pythonFiles/visualstudio_py_testlauncher.py index 096315b5f62b..0b0ef3242f65 100644 --- a/pythonFiles/visualstudio_py_testlauncher.py +++ b/pythonFiles/visualstudio_py_testlauncher.py @@ -24,7 +24,6 @@ import sys import traceback import unittest -from types import CodeType, FunctionType try: import thread From fb083d81b5bdd10e7646e91c838db837c4eec216 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 16 Jul 2022 21:44:22 -0700 Subject: [PATCH 09/49] change runtest pattern to non optional --- pythonFiles/unittestadapter/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 8f76c0862eb9..0347b55910c7 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -173,7 +173,7 @@ class PayloadDict(TypedDict): def run_tests( start_dir: str, test_ids: List[str], - pattern: Optional[str], + pattern: str, top_level_dir: Optional[str], uuid: Optional[str], ) -> PayloadDict: From 7719e8fefa563060e40260da0d5b2138fb5d176a Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Mon, 18 Jul 2022 01:15:48 -0700 Subject: [PATCH 10/49] remove unnecessary import comments --- pythonFiles/testing_tools/adapter/__main__.py | 6 ------ pythonFiles/unittestadapter/discovery.py | 6 ------ pythonFiles/unittestadapter/execution.py | 10 ++-------- src/client/testing/testController/common/types.ts | 3 +-- .../testing/testController/workspaceTestAdapter.ts | 2 +- 5 files changed, 4 insertions(+), 23 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/__main__.py b/pythonFiles/testing_tools/adapter/__main__.py index d4fb3c4b49a6..218456897df6 100644 --- a/pythonFiles/testing_tools/adapter/__main__.py +++ b/pythonFiles/testing_tools/adapter/__main__.py @@ -9,12 +9,6 @@ from . import pytest, report from .errors import UnsupportedCommandError, UnsupportedToolError -# ##### -# import debugpy - - -# debugpy.connect(("localhost", 5678)) -###### TOOLS = { "pytest": { "_add_subparser": pytest.add_cli_subparser, diff --git a/pythonFiles/unittestadapter/discovery.py b/pythonFiles/unittestadapter/discovery.py index ae1c274d5efb..396f99f7996c 100644 --- a/pythonFiles/unittestadapter/discovery.py +++ b/pythonFiles/unittestadapter/discovery.py @@ -10,12 +10,6 @@ import unittest from typing import List, Literal, Optional, Tuple, TypedDict, Union -###### -# import debugpy - -# debugpy.connect(("localhost", 5678)) -# debugpy.breakpoint() - # 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) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 0347b55910c7..aeccbafd0235 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -66,7 +66,7 @@ class TestOutcomeEnum(str, enum.Enum): class UnittestTestResult(unittest.TextTestResult): - # formatted: List[Dict[str, Dict[str, str | None]]] = [] + formatted: Dict[str, Dict[str, str | None]] = dict() def startTest(self, test: unittest.TestCase): @@ -140,12 +140,7 @@ def formatResult( "subtest": subtest.id() if subtest else None, } - self.formatted[test_id] = result # in future: send one by one as we get it.only - # tempResult = {test_id: result} ## - # tempResult = {result} - # self.formatted.append(tempResult) ## - # print("we are adding inside formatResult: ") - # print(result) + self.formatted[test_id] = result class TestExecutionStatus(str, enum.Enum): @@ -243,7 +238,6 @@ def run_tests( # Perform test execution. port, uuid, test_ids = parse_execution_cli_args(argv[:index]) payload = run_tests(start_dir, test_ids, pattern, top_level_dir, uuid) - # print(payload) # # 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) diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index b03a4ac500a1..3799d36ad94b 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -136,7 +136,6 @@ export type TestDiscoveryCommand = { args: string[]; }; -// do we need args as string[] for exeuction? 6/27/22 export type TestExecutionCommand = { script: string; args: string[]; @@ -145,7 +144,7 @@ export type TestExecutionCommand = { export type TestCommandOptions = { workspaceFolder: Uri; cwd: string; - command: TestDiscoveryCommand | TestExecutionCommand; // 6/27/22 + command: TestDiscoveryCommand | TestExecutionCommand; token?: CancellationToken; outChannel?: OutputChannel; }; diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index fcab863086e0..460761c9dcde 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -101,7 +101,7 @@ export class WorkspaceTestAdapter { z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), ), ); - runInstance.appendOutput('DOEDJODJEODJOPADKASJIKAOJDIOASD'); + if (rawTestExecData.result[keyTemp].outcome === 'failure') { const traceback = rawTestExecData.result[keyTemp].traceback ? rawTestExecData.result[keyTemp] From e15222cd21767f43a156fe696c860738ff6b3991 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Wed, 20 Jul 2022 22:18:53 -0700 Subject: [PATCH 11/49] add code for unittest execution debugger --- .../common/process/internal/scripts/index.ts | 7 ++++++ src/client/testing/common/debugLauncher.ts | 1 + .../testing/testController/common/server.ts | 25 +++++++++++++++++-- .../testing/testController/common/types.ts | 3 ++- .../testing/testController/controller.ts | 11 ++++++-- .../unittest/testExecutionAdapter.ts | 3 ++- .../testController/workspaceTestAdapter.ts | 5 ++-- 7 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/client/common/process/internal/scripts/index.ts b/src/client/common/process/internal/scripts/index.ts index fc275209a55a..b482c94d814f 100644 --- a/src/client/common/process/internal/scripts/index.ts +++ b/src/client/common/process/internal/scripts/index.ts @@ -138,6 +138,13 @@ export function visualstudio_py_testlauncher(testArgs: string[]): string[] { return [script, ...testArgs]; } +// execution.py 7/19 for new execution +// eslint-disable-next-line camelcase +export function execution_py_testlauncher(testArgs: string[]): string[] { + const script = path.join(SCRIPTS_DIR, 'unittestadapter', 'execution.py'); + return [script, ...testArgs]; +} + // tensorboard_launcher.py export function tensorboardLauncher(args: string[]): string[] { diff --git a/src/client/testing/common/debugLauncher.ts b/src/client/testing/common/debugLauncher.ts index b5213db56ea2..a0b1f8039586 100644 --- a/src/client/testing/common/debugLauncher.ts +++ b/src/client/testing/common/debugLauncher.ts @@ -212,6 +212,7 @@ export class DebugLauncher implements ITestDebugLauncher { switch (testProvider) { case 'unittest': { return internalScripts.visualstudio_py_testlauncher; + // return internalScripts.execution_py_testlauncher; // this is the new way to run unittest execution } case 'pytest': { return internalScripts.testlauncher; diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 9193275a1a21..39b330fee604 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -13,6 +13,8 @@ import { import { traceLog } from '../../../logging'; import { DataReceivedEvent, ITestServer, TestCommandOptions } from './types'; import { DEFAULT_TEST_PORT } from './utils'; +import { ITestDebugLauncher, LaunchOptions } from '../../common/types'; +import { UNITTEST_PROVIDER } from '../../common/constants'; export class PythonTestServer implements ITestServer, Disposable { private _onDataReceived: EventEmitter = new EventEmitter(); @@ -23,7 +25,7 @@ export class PythonTestServer implements ITestServer, Disposable { public port: number; - constructor(private executionFactory: IPythonExecutionFactory) { + constructor(private executionFactory: IPythonExecutionFactory, private debugLauncher?: ITestDebugLauncher) { this.uuids = new Map(); this.port = DEFAULT_TEST_PORT; @@ -97,7 +99,26 @@ export class PythonTestServer implements ITestServer, Disposable { } try { - await execService.exec(args, spawnOptions); + // check if there exist the launchOption provided. + // If there is the launch option provided, that means we need to launch the debugger + // if (launchOptions) { + // } else { + // await execService.exec(args, spawnOptions); + // } + if (options.debugBool) { + const launchOptions: LaunchOptions = { + cwd: options.cwd, + args, + token: options.token, + testProvider: UNITTEST_PROVIDER, + }; + if (this.debugLauncher) { + await this.debugLauncher.launchDebugger(launchOptions); + } + } else { + await execService.exec(args, spawnOptions); + } + // await execService.exec(args, spawnOptions); } catch (ex) { this.uuids.delete(uuid); this._onDataReceived.fire({ diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index 3799d36ad94b..17dc4ca26e2a 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -147,6 +147,7 @@ export type TestCommandOptions = { command: TestDiscoveryCommand | TestExecutionCommand; token?: CancellationToken; outChannel?: OutputChannel; + debugBool?: boolean; }; /** @@ -166,7 +167,7 @@ export interface ITestDiscoveryAdapter { // interface for execution/runner adapter 6/27/22 export interface ITestExecutionAdapter { - runTests(uri: Uri): Promise; + runTests(uri: Uri, debugBool?: boolean): Promise; } // Same types as in pythonFiles/unittestadapter/utils.py diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 82a9ae95fb48..932f62f427bc 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -42,6 +42,7 @@ import { 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. type EventPropertyType = IEventNamePropertyMapping[EventName.UNITTEST_DISCOVERY_TRIGGER]; @@ -86,9 +87,9 @@ export class PythonTestController implements ITestController, IExtensionSingleAc @inject(ITestFrameworkController) @named(PYTEST_PROVIDER) private readonly pytest: ITestFrameworkController, @inject(ITestFrameworkController) @named(UNITTEST_PROVIDER) private readonly unittest: ITestFrameworkController, @inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry, - @inject(IPythonExecutionFactory) private readonly pythonExecFactory: IPythonExecutionFactory, @inject(IInterpreterService) private readonly interpreterService: IInterpreterService, @inject(ICommandManager) private readonly commandManager: ICommandManager, + @inject(IPythonExecutionFactory) private readonly pythonExecFactory: IPythonExecutionFactory, // @inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher, new unittest execution debugger ) { this.refreshCancellation = new CancellationTokenSource(); @@ -142,6 +143,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc }; this.pythonTestServer = new PythonTestServer(this.pythonExecFactory); + // this.pythonTestServer = new PythonTestServer(this.pythonExecFactory, this.debugLauncher); } public async activate(): Promise { @@ -381,7 +383,12 @@ export class PythonTestController implements ITestController, IExtensionSingleAc // const testAdapter = // this.testAdapters.get(workspace.uri) || // (this.testAdapters.values().next().value as WorkspaceTestAdapter); - // return testAdapter.executeTests(this.testController, runInstance, token); + // return testAdapter.executeTests( + // this.testController, + // runInstance, + // token, + // request.profile?.kind === TestRunProfileKind.Debug, + // ); // below is old way of running unittest execution diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index da41dcc9a9b8..264b7b07b554 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -37,7 +37,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { } } - public async runTests(uri: Uri): Promise { + public async runTests(uri: Uri, debugBool?: boolean): Promise { if (!this.deferred) { const settings = this.configSettings.getSettings(uri); const { unittestArgs } = settings.testing; @@ -49,6 +49,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { workspaceFolder: uri, command, cwd: this.cwd, + debugBool, }; this.deferred = createDeferred(); diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 460761c9dcde..3d4acca7e7ca 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -48,7 +48,7 @@ export class WorkspaceTestAdapter { private discoveryAdapter: ITestDiscoveryAdapter, // TODO: Implement test running private executionAdapter: ITestExecutionAdapter, - private workspaceUri: Uri, // private runIdToTestItem: Map, + private workspaceUri: Uri, ) { this.runIdToTestItem = new Map(); this.runIdToVSid = new Map(); @@ -58,6 +58,7 @@ export class WorkspaceTestAdapter { testController: TestController, runInstance: TestRun, token?: CancellationToken, + debugBool?: boolean, ): Promise { if (this.executing) { return this.executing.promise; @@ -68,7 +69,7 @@ export class WorkspaceTestAdapter { let rawTestExecData; try { - rawTestExecData = await this.executionAdapter.runTests(this.workspaceUri); + rawTestExecData = await this.executionAdapter.runTests(this.workspaceUri, debugBool); deferred.resolve(); } catch (ex) { // handle token and telemetry here From 6da14e3f91598a738ac0cac1f5e42b55fed104f4 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Wed, 20 Jul 2022 22:35:25 -0700 Subject: [PATCH 12/49] add noop,traceError to solve mergeConflict --- src/client/testing/testController/controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 932f62f427bc..564f3ffb7e51 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -92,7 +92,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc @inject(IPythonExecutionFactory) private readonly pythonExecFactory: IPythonExecutionFactory, // @inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher, new unittest execution debugger ) { this.refreshCancellation = new CancellationTokenSource(); - + // add to const later => @inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher, new unittest execution debugger this.testController = tests.createTestController('python-tests', 'Python Tests'); this.disposables.push(this.testController); From 090dfc6c54ba1e0cb09e7eca638fe08d7588aee4 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Fri, 22 Jul 2022 16:56:09 -0700 Subject: [PATCH 13/49] fix malfunctioning execution, skipped and expected failure outcome --- src/client/testing/common/debugLauncher.ts | 4 +- .../testing/testController/controller.ts | 95 ++++++++++--------- .../testController/workspaceTestAdapter.ts | 30 +++++- 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/src/client/testing/common/debugLauncher.ts b/src/client/testing/common/debugLauncher.ts index a0b1f8039586..f04e10569112 100644 --- a/src/client/testing/common/debugLauncher.ts +++ b/src/client/testing/common/debugLauncher.ts @@ -211,8 +211,8 @@ export class DebugLauncher implements ITestDebugLauncher { private getTestLauncherScript(testProvider: TestProvider) { switch (testProvider) { case 'unittest': { - return internalScripts.visualstudio_py_testlauncher; - // return internalScripts.execution_py_testlauncher; // this is the new way to run unittest execution + // return internalScripts.visualstudio_py_testlauncher; old way unittest execution n debugger + return internalScripts.execution_py_testlauncher; // this is the new way to run unittest execution } case 'pytest': { return internalScripts.testlauncher; diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 564f3ffb7e51..082a4ee5eb12 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -42,7 +42,7 @@ import { import { UnittestTestDiscoveryAdapter } from './unittest/testDiscoveryAdapter'; import { WorkspaceTestAdapter } from './workspaceTestAdapter'; import { UnittestTestExecutionAdapter } from './unittest/testExecutionAdapter'; -// import { ITestDebugLauncher } from '../common/types'; +import { ITestDebugLauncher } from '../common/types'; // Types gymnastics to make sure that sendTriggerTelemetry only accepts the correct types. type EventPropertyType = IEventNamePropertyMapping[EventName.UNITTEST_DISCOVERY_TRIGGER]; @@ -90,6 +90,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc @inject(IInterpreterService) private readonly interpreterService: IInterpreterService, @inject(ICommandManager) private readonly commandManager: ICommandManager, @inject(IPythonExecutionFactory) private readonly pythonExecFactory: IPythonExecutionFactory, // @inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher, new unittest execution debugger + @inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher, ) { this.refreshCancellation = new CancellationTokenSource(); // add to const later => @inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher, new unittest execution debugger @@ -142,8 +143,8 @@ export class PythonTestController implements ITestController, IExtensionSingleAc return this.refreshTestData(undefined, { forceRefresh: true }); }; - this.pythonTestServer = new PythonTestServer(this.pythonExecFactory); - // this.pythonTestServer = new PythonTestServer(this.pythonExecFactory, this.debugLauncher); + // this.pythonTestServer = new PythonTestServer(this.pythonExecFactory); old one + this.pythonTestServer = new PythonTestServer(this.pythonExecFactory, this.debugLauncher); } public async activate(): Promise { @@ -232,21 +233,21 @@ export class PythonTestController implements ITestController, IExtensionSingleAc 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; @@ -362,16 +363,16 @@ export class PythonTestController implements ITestController, IExtensionSingleAc tool: 'pytest', debugging: request.profile?.kind === TestRunProfileKind.Debug, }); - return this.pytest.runTests( - { - includes: testItems, - excludes: request.exclude ?? [], - runKind: request.profile?.kind ?? TestRunProfileKind.Run, - runInstance, - }, - workspace, - token, - ); + // return this.pytest.runTests( + // { + // includes: testItems, + // excludes: request.exclude ?? [], + // runKind: request.profile?.kind ?? TestRunProfileKind.Run, + // runInstance, + // }, + // workspace, + // token, + // ); } if (settings.testing.unittestEnabled) { // potentially sqeeze in the new exeuction way here? @@ -380,29 +381,29 @@ export class PythonTestController implements ITestController, IExtensionSingleAc debugging: request.profile?.kind === TestRunProfileKind.Debug, }); // new execution runner/adapter - // const testAdapter = - // this.testAdapters.get(workspace.uri) || - // (this.testAdapters.values().next().value as WorkspaceTestAdapter); - // return testAdapter.executeTests( - // this.testController, - // runInstance, - // token, - // request.profile?.kind === TestRunProfileKind.Debug, - // ); + const testAdapter = + this.testAdapters.get(workspace.uri) || + (this.testAdapters.values().next().value as WorkspaceTestAdapter); + return testAdapter.executeTests( + this.testController, + runInstance, + token, + request.profile?.kind === TestRunProfileKind.Debug, + ); // below is old way of running unittest execution - return this.unittest.runTests( - { - includes: testItems, - excludes: request.exclude ?? [], - runKind: request.profile?.kind ?? TestRunProfileKind.Run, - runInstance, - }, - workspace, - token, - this.testController, - ); + // return this.unittest.runTests( + // { + // includes: testItems, + // excludes: request.exclude ?? [], + // runKind: request.profile?.kind ?? TestRunProfileKind.Run, + // runInstance, + // }, + // workspace, + // token, + // this.testController, + // ); } } diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 3d4acca7e7ca..66b35338d5c4 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -97,9 +97,18 @@ export class WorkspaceTestAdapter { const tempArr: TestItem[] = []; // fetch inidividual testItem and store into tempArr + // testController.items.forEach((i) => + // i.children.forEach((z) => + // z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), + // ), + // ); testController.items.forEach((i) => i.children.forEach((z) => - z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), + z.children.forEach((x) => + x.children.forEach((indi) => + indi.children.forEach((eachTestMethod) => tempArr.push(eachTestMethod)), + ), + ), ), ); @@ -127,7 +136,10 @@ export class WorkspaceTestAdapter { } } }); - } else if (rawTestExecData.result[keyTemp].outcome === 'success') { + } else if ( + rawTestExecData.result[keyTemp].outcome === 'success' || + rawTestExecData.result[keyTemp].outcome === 'expected-failure' + ) { const grabTestItem = this.runIdToTestItem.get(keyTemp); const grabVSid = this.runIdToVSid.get(keyTemp); if (grabTestItem !== undefined) { @@ -141,6 +153,20 @@ export class WorkspaceTestAdapter { } }); } + } else if (rawTestExecData.result[keyTemp].outcome === 'skipped') { + const grabTestItem = this.runIdToTestItem.get(keyTemp); + const grabVSid = this.runIdToVSid.get(keyTemp); + if (grabTestItem !== undefined) { + tempArr.forEach((indiItem) => { + if (indiItem.id === grabVSid) { + if (indiItem.uri && indiItem.range) { + runInstance.started(grabTestItem); + runInstance.skipped(grabTestItem); + runInstance.appendOutput('Skipped here'); + } + } + }); + } } } } From 6080872e311eb75684a266c5b02a33661e12d2f2 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 23 Jul 2022 19:26:43 -0700 Subject: [PATCH 14/49] allow cherry pick test runs for efficiency --- pythonFiles/testing_tools/adapter/__main__.py | 5 +++ pythonFiles/unittestadapter/execution.py | 21 +++++++--- .../testing/testController/common/server.ts | 22 ++++++++-- .../testing/testController/common/types.ts | 3 +- .../testing/testController/controller.ts | 1 + .../unittest/testExecutionAdapter.ts | 3 +- .../testController/workspaceTestAdapter.ts | 42 +++++++++++++++---- 7 files changed, 79 insertions(+), 18 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/__main__.py b/pythonFiles/testing_tools/adapter/__main__.py index 218456897df6..2119d12623c5 100644 --- a/pythonFiles/testing_tools/adapter/__main__.py +++ b/pythonFiles/testing_tools/adapter/__main__.py @@ -9,6 +9,11 @@ from . import pytest, report from .errors import UnsupportedCommandError, UnsupportedToolError +########## +# import debugpy + + +# debugpy.connect(("localhost", 5678)) TOOLS = { "pytest": { "_add_subparser": pytest.add_cli_subparser, diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index aeccbafd0235..a509a75a8a59 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -11,10 +11,15 @@ from types import TracebackType from typing import Dict, List, Optional, Tuple, Type, TypeAlias, TypedDict +######## +# import debugpy from discovery import parse_unittest_discovery_args from testing_tools import socket_manager from typing_extensions import NotRequired +# debugpy.connect(("localhost", 5678)) +# debugpy.breakpoint() + # 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) @@ -41,12 +46,12 @@ def parse_execution_cli_args(args: List[str]) -> Tuple[int, str | None, List[str arg_parser = argparse.ArgumentParser() arg_parser.add_argument("--port", default=DEFAULT_PORT) arg_parser.add_argument("--uuid") - arg_parser.add_argument("--testids") + arg_parser.add_argument("--testids", nargs="+") parsed_args, _ = arg_parser.parse_known_args(args) - test_ids: List[str] = parsed_args.testids.split(",") if parsed_args.testids else [] + # test_ids: List[str] = parsed_args.testids.split(",") if parsed_args.testids else [] - return (int(parsed_args.port), parsed_args.uuid, test_ids) + return (int(parsed_args.port), parsed_args.uuid, parsed_args.testids) ErrorType: TypeAlias = ( @@ -196,7 +201,11 @@ def run_tests( # Run tests. runner = unittest.TextTestRunner(resultclass=UnittestTestResult) - result: UnittestTestResult = runner.run(suite) # type: ignore + ### lets try to tailer our own suite so we can figure out running only the ones we want + loader = unittest.TestLoader() + tailor = loader.loadTestsFromNames(test_ids) + # result: UnittestTestResult = runner.run(suite) # type: ignore + result: UnittestTestResult = runner.run(tailor) payload["result"] = result.formatted @@ -236,8 +245,8 @@ def run_tests( # uuid = "abcd" # Perform test execution. - port, uuid, test_ids = parse_execution_cli_args(argv[:index]) - payload = run_tests(start_dir, test_ids, pattern, top_level_dir, uuid) + port, uuid, testids = parse_execution_cli_args(argv[:index]) + payload = run_tests(start_dir, testids, pattern, top_level_dir, uuid) # # 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) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 39b330fee604..c0cee9d20fc9 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -90,9 +90,25 @@ export class PythonTestServer implements ITestServer, Disposable { const execService = await this.executionFactory.createActivatedEnvironment(creationOptions); // Add the generated UUID to the data to be sent (expecting to receive it back). - const args = [options.command.script, '--port', this.port.toString(), '--uuid', uuid].concat( - options.command.args, - ); + // first check if we have testIds passed in (in case of execution) and + // insert appropriate flag and test id array + let args = []; + if (options.testIds) { + args = [ + options.command.script, + '--port', + this.port.toString(), + '--uuid', + uuid, + '--testids', + ...options.testIds, + ].concat(options.command.args); + } else { + // if not case of execution, go with the normal args + args = [options.command.script, '--port', this.port.toString(), '--uuid', uuid].concat( + options.command.args, + ); + } if (options.outChannel) { options.outChannel.appendLine(`python ${args.join(' ')}`); diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index 17dc4ca26e2a..ecdaf8c9ff22 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -148,6 +148,7 @@ export type TestCommandOptions = { token?: CancellationToken; outChannel?: OutputChannel; debugBool?: boolean; + testIds?: string[]; }; /** @@ -167,7 +168,7 @@ export interface ITestDiscoveryAdapter { // interface for execution/runner adapter 6/27/22 export interface ITestExecutionAdapter { - runTests(uri: Uri, debugBool?: boolean): Promise; + runTests(uri: Uri, testIds: string[], debugBool?: boolean): Promise; } // Same types as in pythonFiles/unittestadapter/utils.py diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 082a4ee5eb12..8d401f676d49 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -387,6 +387,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc return testAdapter.executeTests( this.testController, runInstance, + testItems, token, request.profile?.kind === TestRunProfileKind.Debug, ); diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index 264b7b07b554..d71dea9fea36 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -37,7 +37,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { } } - public async runTests(uri: Uri, debugBool?: boolean): Promise { + public async runTests(uri: Uri, testIds: string[], debugBool?: boolean): Promise { if (!this.deferred) { const settings = this.configSettings.getSettings(uri); const { unittestArgs } = settings.testing; @@ -50,6 +50,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { command, cwd: this.cwd, debugBool, + testIds, }; this.deferred = createDeferred(); diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 66b35338d5c4..3b480a696955 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -11,7 +11,13 @@ import { traceError } from '../../logging'; import { sendTelemetryEvent } from '../../telemetry'; import { EventName } from '../../telemetry/constants'; import { TestProvider } from '../types'; -import { createErrorTestItem, DebugTestTag, ErrorTestItemOptions, RunTestTag } from './common/testItemUtilities'; +import { + createErrorTestItem, + DebugTestTag, + ErrorTestItemOptions, + getTestCaseNodes, + RunTestTag, +} from './common/testItemUtilities'; import { DiscoveredTestItem, DiscoveredTestNode, @@ -43,6 +49,8 @@ export class WorkspaceTestAdapter { runIdToVSid: Map; + vsIdToRunId: Map; + constructor( private testProvider: TestProvider, private discoveryAdapter: ITestDiscoveryAdapter, @@ -52,11 +60,13 @@ export class WorkspaceTestAdapter { ) { this.runIdToTestItem = new Map(); this.runIdToVSid = new Map(); + this.vsIdToRunId = new Map(); } public async executeTests( testController: TestController, runInstance: TestRun, + includes: TestItem[], token?: CancellationToken, debugBool?: boolean, ): Promise { @@ -68,8 +78,24 @@ export class WorkspaceTestAdapter { this.executing = deferred; let rawTestExecData; + const testCaseNodes: TestItem[] = []; + const testCaseIds: string[] = []; try { - rawTestExecData = await this.executionAdapter.runTests(this.workspaceUri, debugBool); + // first fetch all the individual test Items that we necessarily want + includes.forEach((t) => { + const nodes = getTestCaseNodes(t); + testCaseNodes.push(...nodes); + }); + // iterate through testItems nodes and fetch their unittest runID to pass in as argument + testCaseNodes.forEach((node) => { + const runId = this.vsIdToRunId.get(node.id); + if (runId) { + testCaseIds.push(runId); + } + }); + + // need to get the testItems runIds so that we can pass in here. + rawTestExecData = await this.executionAdapter.runTests(this.workspaceUri, testCaseIds, debugBool); deferred.resolve(); } catch (ex) { // handle token and telemetry here @@ -97,11 +123,12 @@ export class WorkspaceTestAdapter { const tempArr: TestItem[] = []; // fetch inidividual testItem and store into tempArr - // testController.items.forEach((i) => - // i.children.forEach((z) => - // z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), - // ), - // ); + // one directory above. + testController.items.forEach((i) => + i.children.forEach((z) => + z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), + ), + ); testController.items.forEach((i) => i.children.forEach((z) => z.children.forEach((x) => @@ -445,6 +472,7 @@ function populateTestTree( // add to our map wstAdapter.runIdToTestItem.set(child.runID, testItem); wstAdapter.runIdToVSid.set(child.runID, child.id_); + wstAdapter.vsIdToRunId.set(child.id_, child.runID); } else { let node = testController.items.get(child.path); From 8da4c3e1adaf673e8d9fb036c3af7b6b2d041efc Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Tue, 26 Jul 2022 16:22:04 -0700 Subject: [PATCH 15/49] add subtest update and directory search --- src/client/testing/common/debugLauncher.ts | 2 +- .../testController/workspaceTestAdapter.ts | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/client/testing/common/debugLauncher.ts b/src/client/testing/common/debugLauncher.ts index f04e10569112..2193bbd5d422 100644 --- a/src/client/testing/common/debugLauncher.ts +++ b/src/client/testing/common/debugLauncher.ts @@ -211,7 +211,7 @@ export class DebugLauncher implements ITestDebugLauncher { private getTestLauncherScript(testProvider: TestProvider) { switch (testProvider) { case 'unittest': { - // return internalScripts.visualstudio_py_testlauncher; old way unittest execution n debugger + // return internalScripts.visualstudio_py_testlauncher; // old way unittest execution n debugger return internalScripts.execution_py_testlauncher; // this is the new way to run unittest execution } case 'pytest': { diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 3b480a696955..5a20149429a0 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -138,8 +138,24 @@ export class WorkspaceTestAdapter { ), ), ); + // try to go down one more level + testController.items.forEach((i) => + i.children.forEach((z) => + z.children.forEach((x) => + x.children.forEach((indi) => + indi.children.forEach((eachTestMethod) => + eachTestMethod.children.forEach((moreTests) => tempArr.push(moreTests)), + ), + ), + ), + ), + ); - if (rawTestExecData.result[keyTemp].outcome === 'failure') { + if ( + rawTestExecData.result[keyTemp].outcome === 'failure' || + rawTestExecData.result[keyTemp].outcome === 'subtest-failure' || + rawTestExecData.result[keyTemp].outcome === 'passed-unexpected' + ) { const traceback = rawTestExecData.result[keyTemp].traceback ? rawTestExecData.result[keyTemp] .traceback!.splitLines({ trim: false, removeEmptyEntries: true }) @@ -165,7 +181,8 @@ export class WorkspaceTestAdapter { }); } else if ( rawTestExecData.result[keyTemp].outcome === 'success' || - rawTestExecData.result[keyTemp].outcome === 'expected-failure' + rawTestExecData.result[keyTemp].outcome === 'expected-failure' || + rawTestExecData.result[keyTemp].outcome === 'subtest-passed' ) { const grabTestItem = this.runIdToTestItem.get(keyTemp); const grabVSid = this.runIdToVSid.get(keyTemp); From 79c887418316b16cda68198084c24f6158e5f49c Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Wed, 27 Jul 2022 10:36:27 -0700 Subject: [PATCH 16/49] leave new debugLauncher dormant and try to fix python type --- pythonFiles/unittestadapter/execution.py | 2 +- src/client/testing/common/debugLauncher.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index a509a75a8a59..63cd42bbf758 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -203,7 +203,7 @@ def run_tests( runner = unittest.TextTestRunner(resultclass=UnittestTestResult) ### lets try to tailer our own suite so we can figure out running only the ones we want loader = unittest.TestLoader() - tailor = loader.loadTestsFromNames(test_ids) + tailor: unittest.TestSuite = loader.loadTestsFromNames(test_ids) # result: UnittestTestResult = runner.run(suite) # type: ignore result: UnittestTestResult = runner.run(tailor) diff --git a/src/client/testing/common/debugLauncher.ts b/src/client/testing/common/debugLauncher.ts index 2193bbd5d422..de45e4556f4c 100644 --- a/src/client/testing/common/debugLauncher.ts +++ b/src/client/testing/common/debugLauncher.ts @@ -211,8 +211,8 @@ export class DebugLauncher implements ITestDebugLauncher { private getTestLauncherScript(testProvider: TestProvider) { switch (testProvider) { case 'unittest': { - // return internalScripts.visualstudio_py_testlauncher; // old way unittest execution n debugger - return internalScripts.execution_py_testlauncher; // this is the new way to run unittest execution + return internalScripts.visualstudio_py_testlauncher; // old way unittest execution n debugger + // return internalScripts.execution_py_testlauncher; // this is the new way to run unittest execution } case 'pytest': { return internalScripts.testlauncher; From eeafaef46982d8b753ff11a91f504d369be53353 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Wed, 27 Jul 2022 11:05:16 -0700 Subject: [PATCH 17/49] ignore type for runner.run as in the past --- pythonFiles/unittestadapter/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 63cd42bbf758..413b241b783c 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -205,7 +205,7 @@ def run_tests( loader = unittest.TestLoader() tailor: unittest.TestSuite = loader.loadTestsFromNames(test_ids) # result: UnittestTestResult = runner.run(suite) # type: ignore - result: UnittestTestResult = runner.run(tailor) + result: UnittestTestResult = runner.run(tailor) # type: ignore payload["result"] = result.formatted From 004cf5bdc040987faac8605803a5aee2f4a242ef Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Wed, 27 Jul 2022 13:50:47 -0700 Subject: [PATCH 18/49] cleanup execution.py --- pythonFiles/testing_tools/adapter/__main__.py | 5 ---- pythonFiles/unittestadapter/execution.py | 25 +++---------------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/pythonFiles/testing_tools/adapter/__main__.py b/pythonFiles/testing_tools/adapter/__main__.py index 2119d12623c5..218456897df6 100644 --- a/pythonFiles/testing_tools/adapter/__main__.py +++ b/pythonFiles/testing_tools/adapter/__main__.py @@ -9,11 +9,6 @@ from . import pytest, report from .errors import UnsupportedCommandError, UnsupportedToolError -########## -# import debugpy - - -# debugpy.connect(("localhost", 5678)) TOOLS = { "pytest": { "_add_subparser": pytest.add_cli_subparser, diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 413b241b783c..d9fb8b6b9e08 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -11,20 +11,14 @@ from types import TracebackType from typing import Dict, List, Optional, Tuple, Type, TypeAlias, TypedDict -######## -# import debugpy from discovery import parse_unittest_discovery_args from testing_tools import socket_manager from typing_extensions import NotRequired -# debugpy.connect(("localhost", 5678)) -# debugpy.breakpoint() - # 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 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")) @@ -49,8 +43,6 @@ def parse_execution_cli_args(args: List[str]) -> Tuple[int, str | None, List[str arg_parser.add_argument("--testids", nargs="+") parsed_args, _ = arg_parser.parse_known_args(args) - # test_ids: List[str] = parsed_args.testids.split(",") if parsed_args.testids else [] - return (int(parsed_args.port), parsed_args.uuid, parsed_args.testids) @@ -201,10 +193,9 @@ def run_tests( # Run tests. runner = unittest.TextTestRunner(resultclass=UnittestTestResult) - ### lets try to tailer our own suite so we can figure out running only the ones we want + # lets try to tailer our own suite so we can figure out running only the ones we want loader = unittest.TestLoader() tailor: unittest.TestSuite = loader.loadTestsFromNames(test_ids) - # result: UnittestTestResult = runner.run(suite) # type: ignore result: UnittestTestResult = runner.run(tailor) # type: ignore payload["result"] = result.formatted @@ -232,23 +223,13 @@ def run_tests( argv = sys.argv[1:] index = argv.index("--udiscovery") - start_dir, pattern, top_level_dir = parse_unittest_discovery_args( - argv[index + 1 :] - ) # do we need 225,227? - - # start_path = pathlib.Path.home() / "Documents" / "Sandbox" / "unittest-subtest" - # test_ids = [ - # "subfolder.test_two.TestClassTwo.test_two_two", - # "test_one.TestClassOne.test_func_one", - # "test_eight.TestClassEight.test_func_eight" - # ] - # uuid = "abcd" + start_dir, pattern, top_level_dir = parse_unittest_discovery_args(argv[index + 1 :]) # Perform test execution. port, uuid, testids = parse_execution_cli_args(argv[:index]) payload = run_tests(start_dir, testids, pattern, top_level_dir, uuid) - # # Build the request data (it has to be a POST request or the Node side will not process it), and send it. + # 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) From 275bcde56d66edc1b93fc12969cd13e9190c90f5 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Wed, 27 Jul 2022 15:27:18 -0700 Subject: [PATCH 19/49] typescript side cleanup --- .../common/process/internal/scripts/index.ts | 2 +- src/client/testing/common/debugLauncher.ts | 4 +-- .../testing/testController/common/server.ts | 7 ---- .../testing/testController/common/types.ts | 3 +- .../testing/testController/controller.ts | 9 +++-- .../testing/testController/unittest/runner.ts | 1 - .../testController/workspaceTestAdapter.ts | 36 ++++++++----------- 7 files changed, 23 insertions(+), 39 deletions(-) diff --git a/src/client/common/process/internal/scripts/index.ts b/src/client/common/process/internal/scripts/index.ts index b482c94d814f..e0749cc18c67 100644 --- a/src/client/common/process/internal/scripts/index.ts +++ b/src/client/common/process/internal/scripts/index.ts @@ -138,7 +138,7 @@ export function visualstudio_py_testlauncher(testArgs: string[]): string[] { return [script, ...testArgs]; } -// execution.py 7/19 for new execution +// execution.py // eslint-disable-next-line camelcase export function execution_py_testlauncher(testArgs: string[]): string[] { const script = path.join(SCRIPTS_DIR, 'unittestadapter', 'execution.py'); diff --git a/src/client/testing/common/debugLauncher.ts b/src/client/testing/common/debugLauncher.ts index de45e4556f4c..5c8bfd537f79 100644 --- a/src/client/testing/common/debugLauncher.ts +++ b/src/client/testing/common/debugLauncher.ts @@ -211,8 +211,8 @@ export class DebugLauncher implements ITestDebugLauncher { private getTestLauncherScript(testProvider: TestProvider) { switch (testProvider) { case 'unittest': { - return internalScripts.visualstudio_py_testlauncher; // old way unittest execution n debugger - // return internalScripts.execution_py_testlauncher; // this is the new way to run unittest execution + return internalScripts.visualstudio_py_testlauncher; // old way unittest execution, debugger + // return internalScripts.execution_py_testlauncher; // this is the new way to run unittest execution, debugger } case 'pytest': { return internalScripts.testlauncher; diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index c0cee9d20fc9..85cf2bef987a 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -115,12 +115,6 @@ export class PythonTestServer implements ITestServer, Disposable { } try { - // check if there exist the launchOption provided. - // If there is the launch option provided, that means we need to launch the debugger - // if (launchOptions) { - // } else { - // await execService.exec(args, spawnOptions); - // } if (options.debugBool) { const launchOptions: LaunchOptions = { cwd: options.cwd, @@ -134,7 +128,6 @@ export class PythonTestServer implements ITestServer, Disposable { } else { await execService.exec(args, spawnOptions); } - // await execService.exec(args, spawnOptions); } catch (ex) { this.uuids.delete(uuid); this._onDataReceived.fire({ diff --git a/src/client/testing/testController/common/types.ts b/src/client/testing/testController/common/types.ts index ecdaf8c9ff22..5a7a168b146e 100644 --- a/src/client/testing/testController/common/types.ts +++ b/src/client/testing/testController/common/types.ts @@ -166,7 +166,7 @@ export interface ITestDiscoveryAdapter { discoverTests(uri: Uri): Promise; } -// interface for execution/runner adapter 6/27/22 +// interface for execution/runner adapter export interface ITestExecutionAdapter { runTests(uri: Uri, testIds: string[], debugBool?: boolean): Promise; } @@ -198,7 +198,6 @@ export type DiscoveredTestPayload = { errors?: string[]; }; -// payload for exeuctionTest 6/27/22 export type ExecutionTestPayload = { cwd: string; status: 'success' | 'error'; diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index 8d401f676d49..e33364c2fd78 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -15,7 +15,6 @@ import { CancellationTokenSource, Uri, EventEmitter, - // TestMessage, } from 'vscode'; import { IExtensionSingleActivationService } from '../../activation/types'; import { ICommandManager, IWorkspaceService } from '../../common/application/types'; @@ -89,11 +88,11 @@ export class PythonTestController implements ITestController, IExtensionSingleAc @inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry, @inject(IInterpreterService) private readonly interpreterService: IInterpreterService, @inject(ICommandManager) private readonly commandManager: ICommandManager, - @inject(IPythonExecutionFactory) private readonly pythonExecFactory: IPythonExecutionFactory, // @inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher, new unittest execution debugger + @inject(IPythonExecutionFactory) private readonly pythonExecFactory: IPythonExecutionFactory, @inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher, ) { this.refreshCancellation = new CancellationTokenSource(); - // add to const later => @inject(ITestDebugLauncher) private readonly debugLauncher: ITestDebugLauncher, new unittest execution debugger + this.testController = tests.createTestController('python-tests', 'Python Tests'); this.disposables.push(this.testController); @@ -143,7 +142,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc return this.refreshTestData(undefined, { forceRefresh: true }); }; - // this.pythonTestServer = new PythonTestServer(this.pythonExecFactory); old one + // this.pythonTestServer = new PythonTestServer(this.pythonExecFactory); // old way where debugLauncher did not have to be passed this.pythonTestServer = new PythonTestServer(this.pythonExecFactory, this.debugLauncher); } @@ -154,7 +153,7 @@ export class PythonTestController implements ITestController, IExtensionSingleAc const settings = this.configSettings.getSettings(workspace.uri); let discoveryAdapter: ITestDiscoveryAdapter; - let executionAdapter: ITestExecutionAdapter; // added 6/30 + let executionAdapter: ITestExecutionAdapter; let testProvider: TestProvider; if (settings.testing.unittestEnabled) { discoveryAdapter = new UnittestTestDiscoveryAdapter(this.pythonTestServer, this.configSettings); diff --git a/src/client/testing/testController/unittest/runner.ts b/src/client/testing/testController/unittest/runner.ts index d21fdc12bd60..ccc14ae0b4c2 100644 --- a/src/client/testing/testController/unittest/runner.ts +++ b/src/client/testing/testController/unittest/runner.ts @@ -67,7 +67,6 @@ export class UnittestRunner implements ITestsRunner { nodes.forEach((n) => { if (n.uri) { const fsRunIds = fileToTestCases.get(n.uri.fsPath); - console.log(fsRunIds); // check this if (fsRunIds) { fsRunIds.push(n); } else { diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 5a20149429a0..22f332f7fefd 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -3,8 +3,17 @@ import * as path from 'path'; import * as util from 'util'; -import { CancellationToken, Position, Range, TestController, TestItem, TestMessage, TestRun, Uri } from 'vscode'; -import { Location } from 'vscode'; // +import { + CancellationToken, + Position, + Range, + TestController, + TestItem, + TestMessage, + TestRun, + Uri, + Location, +} from 'vscode'; import { createDeferred, Deferred } from '../../common/utils/async'; import { Testing } from '../../common/utils/localize'; import { traceError } from '../../logging'; @@ -37,14 +46,12 @@ import { fixLogLines } from './common/utils'; * and uses them to insert/update/remove items in the `TestController` instance behind the testing UI whenever the `PythonTestController` requests a refresh. */ export class WorkspaceTestAdapter { - // private discovering: Deferred | undefined = undefined; ???? private discovering: Deferred | undefined; private executing: Deferred | undefined; private testData: DiscoveredTestNode | undefined; - // potentially a hashmap of runID : testItem? runIdToTestItem: Map; runIdToVSid: Map; @@ -54,7 +61,6 @@ export class WorkspaceTestAdapter { constructor( private testProvider: TestProvider, private discoveryAdapter: ITestDiscoveryAdapter, - // TODO: Implement test running private executionAdapter: ITestExecutionAdapter, private workspaceUri: Uri, ) { @@ -175,7 +181,7 @@ export class WorkspaceTestAdapter { message.location = new Location(indiItem.uri, indiItem.range); runInstance.started(indiItem); runInstance.failed(indiItem, message); - runInstance.appendOutput(fixLogLines(text)); // added 7/15 + runInstance.appendOutput(fixLogLines(text)); } } }); @@ -437,7 +443,7 @@ function updateTestTree( testRoot!.children.add(testItem); // Populate the test tree under the newly created node. - // populateTestTree(testController, child, testItem, token, this); uncomment later + // populateTestTree(testController, child, testItem, token, this); // uncomment later } } } @@ -453,9 +459,8 @@ function populateTestTree( ): void { // If testRoot is undefined, use the info of the root item of testTreeData to create a test item, and append it to the test controller. if (!testRoot) { - // const cleanChildPath = testTreeData.path.replace('\\\\', '\\'); // exyts testRoot = testController.createTestItem(testTreeData.path, testTreeData.name, Uri.file(testTreeData.path)); - // testRoot = testController.createTestItem(testTreeData.path, testTreeData.name, Uri.file(cleanChildPath)); + testRoot.canResolveChildren = true; testRoot.tags = [RunTestTag, DebugTestTag]; @@ -465,19 +470,10 @@ function populateTestTree( // Recursively populate the tree with test data. testTreeData.children.forEach((child) => { if (!token?.isCancellationRequested) { - // Try to identify if we fall into TestItem or TestNode? - if (isTestItem(child)) { - // warning warning: I think there is a problem with child.path being double dash instead of single dash in legacy code - // maybe thats why uri getting messed up=> highly likely - // const regex = /\\\\/g; - // const cleanChildPath = child.path.replace(regex, '\\'); - // const cleanChildPath = child.path.replace('\\\\', '\\'); const testItem = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); testItem.tags = [RunTestTag, DebugTestTag]; - // const testItem = testController.createTestItem(child.id_, child.name, Uri.file(cleanChildPath)); - // const trackerVar = Uri.file(cleanChildPath).fsPath; const range = new Range( new Position(Number(child.lineno) - 1, 0), new Position(Number(child.lineno), 0), @@ -494,10 +490,8 @@ function populateTestTree( let node = testController.items.get(child.path); if (!node) { - // replace child.path with child.id_ (unique) - // const cleanChildPath = child.path.replace('\\\\', '\\'); node = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); - // node = testController.createTestItem(child.id_, child.name, Uri.file(cleanChildPath)); + node.canResolveChildren = true; node.tags = [RunTestTag, DebugTestTag]; From 0da5a85cb717d6fefe9b2ab1de8521483c5af88c Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Thu, 28 Jul 2022 11:25:20 -0700 Subject: [PATCH 20/49] move the runInstance starting up before executing --- .../testController/workspaceTestAdapter.ts | 47 +++++-------------- 1 file changed, 11 insertions(+), 36 deletions(-) diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 22f332f7fefd..c1c7ed4b3197 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -94,6 +94,7 @@ export class WorkspaceTestAdapter { }); // iterate through testItems nodes and fetch their unittest runID to pass in as argument testCaseNodes.forEach((node) => { + runInstance.started(node); // do the vscode ui test item start here before runtest const runId = this.vsIdToRunId.get(node.id); if (runId) { testCaseIds.push(runId); @@ -126,36 +127,13 @@ export class WorkspaceTestAdapter { if (rawTestExecData !== undefined && rawTestExecData.result !== undefined) { for (const keyTemp of Object.keys(rawTestExecData.result)) { // check for result and update the UI accordingly. - const tempArr: TestItem[] = []; - - // fetch inidividual testItem and store into tempArr - // one directory above. - testController.items.forEach((i) => - i.children.forEach((z) => - z.children.forEach((x) => x.children.forEach((indi) => tempArr.push(indi))), - ), - ); - testController.items.forEach((i) => - i.children.forEach((z) => - z.children.forEach((x) => - x.children.forEach((indi) => - indi.children.forEach((eachTestMethod) => tempArr.push(eachTestMethod)), - ), - ), - ), - ); - // try to go down one more level - testController.items.forEach((i) => - i.children.forEach((z) => - z.children.forEach((x) => - x.children.forEach((indi) => - indi.children.forEach((eachTestMethod) => - eachTestMethod.children.forEach((moreTests) => tempArr.push(moreTests)), - ), - ), - ), - ), - ); + const testCases: TestItem[] = []; + + // grab leaf level test items + testController.items.forEach((i) => { + const tempArr: TestItem[] = getTestCaseNodes(i); + testCases.push(...tempArr); + }); if ( rawTestExecData.result[keyTemp].outcome === 'failure' || @@ -175,11 +153,10 @@ export class WorkspaceTestAdapter { // note that keyTemp is a runId for unittest library... const grabVSid = this.runIdToVSid.get(keyTemp); // search through freshly built array of testItem to find the failed test and update UI. - tempArr.forEach((indiItem) => { + testCases.forEach((indiItem) => { if (indiItem.id === grabVSid) { if (indiItem.uri && indiItem.range) { message.location = new Location(indiItem.uri, indiItem.range); - runInstance.started(indiItem); runInstance.failed(indiItem, message); runInstance.appendOutput(fixLogLines(text)); } @@ -193,10 +170,9 @@ export class WorkspaceTestAdapter { const grabTestItem = this.runIdToTestItem.get(keyTemp); const grabVSid = this.runIdToVSid.get(keyTemp); if (grabTestItem !== undefined) { - tempArr.forEach((indiItem) => { + testCases.forEach((indiItem) => { if (indiItem.id === grabVSid) { if (indiItem.uri && indiItem.range) { - runInstance.started(grabTestItem); runInstance.passed(grabTestItem); runInstance.appendOutput('Passed here'); } @@ -207,10 +183,9 @@ export class WorkspaceTestAdapter { const grabTestItem = this.runIdToTestItem.get(keyTemp); const grabVSid = this.runIdToVSid.get(keyTemp); if (grabTestItem !== undefined) { - tempArr.forEach((indiItem) => { + testCases.forEach((indiItem) => { if (indiItem.id === grabVSid) { if (indiItem.uri && indiItem.range) { - runInstance.started(grabTestItem); runInstance.skipped(grabTestItem); runInstance.appendOutput('Skipped here'); } From 3e590ec552f858fd4c5f21f7fd1e81acf9355759 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Thu, 28 Jul 2022 11:39:59 -0700 Subject: [PATCH 21/49] remove unnecessary debugLauncher check --- src/client/testing/testController/common/server.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 85cf2bef987a..b50136d5f62a 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -122,9 +122,8 @@ export class PythonTestServer implements ITestServer, Disposable { token: options.token, testProvider: UNITTEST_PROVIDER, }; - if (this.debugLauncher) { - await this.debugLauncher.launchDebugger(launchOptions); - } + + await this.debugLauncher!.launchDebugger(launchOptions); } else { await execService.exec(args, spawnOptions); } From 134b36bb7000edee6979354bf196a774c9e7c239 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Thu, 28 Jul 2022 13:28:57 -0700 Subject: [PATCH 22/49] get rid of TypeAlias --- pythonFiles/unittestadapter/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index d9fb8b6b9e08..5a4076161dba 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -46,7 +46,7 @@ def parse_execution_cli_args(args: List[str]) -> Tuple[int, str | None, List[str return (int(parsed_args.port), parsed_args.uuid, parsed_args.testids) -ErrorType: TypeAlias = ( +ErrorType = ( Tuple[Type[BaseException], BaseException, TracebackType] | Tuple[None, None, None] ) From 50877bf006176f5c8750b7c050d340cf719c5af5 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Thu, 28 Jul 2022 18:16:05 -0700 Subject: [PATCH 23/49] move uuid to header instead of inside payload --- pythonFiles/unittestadapter/discovery.py | 6 +++--- pythonFiles/unittestadapter/execution.py | 4 ++-- src/client/testing/testController/common/server.ts | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pythonFiles/unittestadapter/discovery.py b/pythonFiles/unittestadapter/discovery.py index 396f99f7996c..77bf9dfb3934 100644 --- a/pythonFiles/unittestadapter/discovery.py +++ b/pythonFiles/unittestadapter/discovery.py @@ -72,7 +72,6 @@ def parse_unittest_discovery_args(args: List[str]) -> Tuple[str, str, Union[str, class PayloadDict(TypedDict): cwd: str - uuid: Union[str, None] status: Literal["success", "error"] tests: NotRequired[TestNode] errors: NotRequired[List[str]] @@ -86,7 +85,7 @@ def discover_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; + - uuid: UUID sent by the caller of the Python script, that needs to be sent back as an integrity check; --> now moved to header - 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. @@ -112,7 +111,7 @@ def discover_tests( } """ cwd = os.path.abspath(start_dir) - payload: PayloadDict = {"cwd": cwd, "status": "success", "uuid": uuid} + payload: PayloadDict = {"cwd": cwd, "status": "success"} tests = None errors: List[str] = [] @@ -154,6 +153,7 @@ def discover_tests( Host: localhost:{port} Content-Length: {len(data)} Content-Type: application/json +Requestuuid: {uuid} {data}""" result = s.socket.sendall(request.encode("utf-8")) # type: ignore diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 5a4076161dba..7c05bc0aa38c 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -150,7 +150,6 @@ class TestExecutionStatus(str, enum.Enum): class PayloadDict(TypedDict): cwd: str - uuid: str | None status: TestExecutionStatus result: NotRequired[TestResultTypeAlias] not_found: NotRequired[List[str]] @@ -172,7 +171,7 @@ def run_tests( cwd = os.path.abspath(start_dir) status = TestExecutionStatus.error error = None - payload: PayloadDict = {"cwd": cwd, "uuid": uuid, "status": status} + payload: PayloadDict = {"cwd": cwd, "status": status} try: # If it's a file, split path and file name. @@ -237,6 +236,7 @@ def run_tests( Host: localhost:{port} Content-Length: {len(data)} Content-Type: application/json +Requestuuid: {uuid} {data}""" result = s.socket.sendall(request.encode("utf-8")) # type: ignore diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index b50136d5f62a..8b9d260e7723 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -39,11 +39,11 @@ export class PythonTestServer implements ITestServer, Disposable { } const data = Buffer.concat(buffers).toString(); - + // grab the uuid from the header + const indexRequestuuid = request.rawHeaders.indexOf('Requestuuid'); + const uuid = request.rawHeaders[indexRequestuuid + 1]; response.end(); - const { uuid } = JSON.parse(data); - // Check if the uuid we received exists in the list of active ones. // If yes, process the response, if not, ignore it. const cwd = this.uuids.get(uuid); From 37c56fcf5d7d90fb3236e49bef1316230ec4ee37 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Thu, 28 Jul 2022 19:01:48 -0700 Subject: [PATCH 24/49] fix python test and look server unittest --- pythonFiles/tests/unittestadapter/test_discovery.py | 3 --- src/test/testing/testController/server.unit.test.ts | 7 +++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pythonFiles/tests/unittestadapter/test_discovery.py b/pythonFiles/tests/unittestadapter/test_discovery.py index 7e626b1fc4e2..decbcb7bcc5e 100644 --- a/pythonFiles/tests/unittestadapter/test_discovery.py +++ b/pythonFiles/tests/unittestadapter/test_discovery.py @@ -132,7 +132,6 @@ def test_simple_discovery() -> None: actual = discover_tests(start_dir, pattern, None, uuid) assert actual["status"] == "success" - assert actual["uuid"] == uuid assert is_same_tree(actual.get("tests"), expected) assert "errors" not in actual @@ -148,7 +147,6 @@ def test_empty_discovery() -> None: actual = discover_tests(start_dir, pattern, None, uuid) assert actual["status"] == "success" - assert actual["uuid"] == uuid assert "tests" not in actual assert "errors" not in actual @@ -215,6 +213,5 @@ def test_error_discovery() -> None: actual = discover_tests(start_dir, pattern, None, uuid) assert actual["status"] == "error" - assert actual["uuid"] == uuid assert is_same_tree(expected, actual.get("tests")) assert len(actual.get("errors", [])) == 1 diff --git a/src/test/testing/testController/server.unit.test.ts b/src/test/testing/testController/server.unit.test.ts index 56209fbbf554..5afac4daf8bd 100644 --- a/src/test/testing/testController/server.unit.test.ts +++ b/src/test/testing/testController/server.unit.test.ts @@ -139,12 +139,15 @@ suite('Python Test Server', () => { const request = http.request(requestOptions, (res) => { res.setEncoding('utf8'); }); - const postData = JSON.stringify({ status: 'success', uuid: fakeUuid }); + // const postData = JSON.stringify({ status: 'success', uuid: fakeUuid }); + const postData = JSON.stringify({ status: 'sucess' }); request.write(postData); request.end(); await deferred.promise; - + console.log('**********************************'); + console.log(response); + console.log(postData); assert.deepStrictEqual(response, postData); }); test('If the server receives malformed data, it should display a log message, and not fire an event', async () => { From 1fc0f805c66ac91df6a918e9d29fce0124aa5f8b Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Thu, 28 Jul 2022 21:54:41 -0700 Subject: [PATCH 25/49] test out timeout --- .../testing/testController/server.unit.test.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/test/testing/testController/server.unit.test.ts b/src/test/testing/testController/server.unit.test.ts index 5afac4daf8bd..9e748b7d0b27 100644 --- a/src/test/testing/testController/server.unit.test.ts +++ b/src/test/testing/testController/server.unit.test.ts @@ -139,16 +139,14 @@ suite('Python Test Server', () => { const request = http.request(requestOptions, (res) => { res.setEncoding('utf8'); }); - // const postData = JSON.stringify({ status: 'success', uuid: fakeUuid }); - const postData = JSON.stringify({ status: 'sucess' }); + const postData = JSON.stringify({ status: 'success', uuid: fakeUuid }); request.write(postData); request.end(); - await deferred.promise; - console.log('**********************************'); - console.log(response); - console.log(postData); - assert.deepStrictEqual(response, postData); + // await deferred.promise; + // eslint-disable-next-line prefer-template + const temp = response + 'SEPARATER' + postData; + assert.deepStrictEqual(response, postData, temp); }); test('If the server receives malformed data, it should display a log message, and not fire an event', async () => { const deferred = createDeferred(); @@ -214,7 +212,7 @@ suite('Python Test Server', () => { method: 'POST', port, }; - + // request.hasHeader() const request = http.request(requestOptions, (res) => { res.setEncoding('utf8'); }); From 99dd17bd68d13cbaabdb033445a01656192b7de1 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Thu, 28 Jul 2022 23:17:37 -0700 Subject: [PATCH 26/49] try adding requestuuid to avoid timeout --- src/test/testing/testController/server.unit.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/testing/testController/server.unit.test.ts b/src/test/testing/testController/server.unit.test.ts index 9e748b7d0b27..8e22fb720645 100644 --- a/src/test/testing/testController/server.unit.test.ts +++ b/src/test/testing/testController/server.unit.test.ts @@ -134,16 +134,18 @@ suite('Python Test Server', () => { hostname: 'localhost', method: 'POST', port, + Requestuuid: fakeUuid, }; const request = http.request(requestOptions, (res) => { res.setEncoding('utf8'); }); + const postData = JSON.stringify({ status: 'success', uuid: fakeUuid }); request.write(postData); request.end(); - // await deferred.promise; + await deferred.promise; // eslint-disable-next-line prefer-template const temp = response + 'SEPARATER' + postData; assert.deepStrictEqual(response, postData, temp); From 006204f3404021edfbe3032c650547bec29f1070 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Thu, 28 Jul 2022 23:29:38 -0700 Subject: [PATCH 27/49] try using rawHeaders.push --- src/test/testing/testController/server.unit.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/testing/testController/server.unit.test.ts b/src/test/testing/testController/server.unit.test.ts index 8e22fb720645..263903313417 100644 --- a/src/test/testing/testController/server.unit.test.ts +++ b/src/test/testing/testController/server.unit.test.ts @@ -134,11 +134,12 @@ suite('Python Test Server', () => { hostname: 'localhost', method: 'POST', port, - Requestuuid: fakeUuid, + // Requestuuid: fakeUuid, }; const request = http.request(requestOptions, (res) => { res.setEncoding('utf8'); + res.rawHeaders.push(...['Reuqestuuid', fakeUuid]); }); const postData = JSON.stringify({ status: 'success', uuid: fakeUuid }); From 4c5c398653efdbe96f43f012e319197ab01a4aee Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Fri, 29 Jul 2022 09:38:33 -0700 Subject: [PATCH 28/49] try inserting uuid inside option command --- src/test/testing/testController/server.unit.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/testing/testController/server.unit.test.ts b/src/test/testing/testController/server.unit.test.ts index 263903313417..a092301d3538 100644 --- a/src/test/testing/testController/server.unit.test.ts +++ b/src/test/testing/testController/server.unit.test.ts @@ -113,7 +113,8 @@ suite('Python Test Server', () => { test('If the server receives data, it should fire an event if it is a known uuid', async () => { const deferred = createDeferred(); const options = { - command: { script: 'myscript', args: ['-foo', 'foo'] }, + // command: { script: 'myscript', args: ['-foo', 'foo'] }, + command: { script: 'myscript', args: ['-foo', 'foo', '--uuid', fakeUuid] }, workspaceFolder: Uri.file('/foo/bar'), cwd: '/foo/bar', }; @@ -139,7 +140,7 @@ suite('Python Test Server', () => { const request = http.request(requestOptions, (res) => { res.setEncoding('utf8'); - res.rawHeaders.push(...['Reuqestuuid', fakeUuid]); + // res.rawHeaders.push(...['Reuqestuuid', fakeUuid]); }); const postData = JSON.stringify({ status: 'success', uuid: fakeUuid }); From f5559e9bbd347188f23a0f195c6f50bd1add75d4 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Fri, 29 Jul 2022 14:09:54 -0700 Subject: [PATCH 29/49] fix all server.unit.test.ts problem caused by moving uuid to header --- .../testing/testController/common/server.ts | 6 +++++ .../testController/server.unit.test.ts | 23 +++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 8b9d260e7723..95b1fd5fcdc2 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -46,10 +46,16 @@ export class PythonTestServer implements ITestServer, Disposable { // Check if the uuid we received exists in the list of active ones. // If yes, process the response, if not, ignore it. + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { checkException } = JSON.parse(data); + const cwd = this.uuids.get(uuid); if (cwd) { this._onDataReceived.fire({ cwd, data }); this.uuids.delete(uuid); + } else { + traceLog(`cannot find invalid uuid`); } } catch (ex) { traceLog(`Error processing test server request: ${ex}`); diff --git a/src/test/testing/testController/server.unit.test.ts b/src/test/testing/testController/server.unit.test.ts index a092301d3538..967b3eab9bfa 100644 --- a/src/test/testing/testController/server.unit.test.ts +++ b/src/test/testing/testController/server.unit.test.ts @@ -113,8 +113,7 @@ suite('Python Test Server', () => { test('If the server receives data, it should fire an event if it is a known uuid', async () => { const deferred = createDeferred(); const options = { - // command: { script: 'myscript', args: ['-foo', 'foo'] }, - command: { script: 'myscript', args: ['-foo', 'foo', '--uuid', fakeUuid] }, + command: { script: 'myscript', args: ['-foo', 'foo'] }, workspaceFolder: Uri.file('/foo/bar'), cwd: '/foo/bar', }; @@ -135,12 +134,11 @@ suite('Python Test Server', () => { hostname: 'localhost', method: 'POST', port, - // Requestuuid: fakeUuid, + headers: { Requestuuid: fakeUuid }, }; const request = http.request(requestOptions, (res) => { res.setEncoding('utf8'); - // res.rawHeaders.push(...['Reuqestuuid', fakeUuid]); }); const postData = JSON.stringify({ status: 'success', uuid: fakeUuid }); @@ -148,9 +146,8 @@ suite('Python Test Server', () => { request.end(); await deferred.promise; - // eslint-disable-next-line prefer-template - const temp = response + 'SEPARATER' + postData; - assert.deepStrictEqual(response, postData, temp); + + assert.deepStrictEqual(response, postData); }); test('If the server receives malformed data, it should display a log message, and not fire an event', async () => { const deferred = createDeferred(); @@ -176,6 +173,7 @@ suite('Python Test Server', () => { hostname: 'localhost', method: 'POST', port, + headers: { Requestuuid: fakeUuid }, }; const request = http.request(requestOptions, (res) => { @@ -215,6 +213,7 @@ suite('Python Test Server', () => { hostname: 'localhost', method: 'POST', port, + headers: { Requestuuid: fakeUuid }, }; // request.hasHeader() const request = http.request(requestOptions, (res) => { @@ -253,8 +252,14 @@ suite('Python Test Server', () => { hostname: 'localhost', method: 'POST', port, + headers: { Requestuuid: 'some-other-uuid' }, + }; + const requestOptions2 = { + hostname: 'localhost', + method: 'POST', + port, + headers: { Requestuuid: fakeUuid }, }; - const requestOne = http.request(requestOptions, (res) => { res.setEncoding('utf8'); }); @@ -262,7 +267,7 @@ suite('Python Test Server', () => { requestOne.write(postDataOne); requestOne.end(); - const requestTwo = http.request(requestOptions, (res) => { + const requestTwo = http.request(requestOptions2, (res) => { res.setEncoding('utf8'); }); const postDataTwo = JSON.stringify({ status: 'success', uuid: fakeUuid, payload: 'foo' }); From 6c4f45d2649a43c4ee498468861f90ed5799808d Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Fri, 29 Jul 2022 14:19:09 -0700 Subject: [PATCH 30/49] get rid of unused variable warning --- src/client/testing/testController/common/server.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 95b1fd5fcdc2..9d4f96aa688d 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -44,12 +44,9 @@ export class PythonTestServer implements ITestServer, Disposable { const uuid = request.rawHeaders[indexRequestuuid + 1]; response.end(); + JSON.parse(data); // Check if the uuid we received exists in the list of active ones. // If yes, process the response, if not, ignore it. - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { checkException } = JSON.parse(data); - const cwd = this.uuids.get(uuid); if (cwd) { this._onDataReceived.fire({ cwd, data }); @@ -58,7 +55,7 @@ export class PythonTestServer implements ITestServer, Disposable { traceLog(`cannot find invalid uuid`); } } catch (ex) { - traceLog(`Error processing test server request: ${ex}`); + traceLog(`Error processing test server request: ${ex} observe`); this._onDataReceived.fire({ cwd: '', data: '' }); } }; From 6879a2c99b86246c333d93b74c80c9a2a9d08057 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Fri, 29 Jul 2022 14:40:58 -0700 Subject: [PATCH 31/49] windows python test server --- src/client/testing/testController/common/server.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 9d4f96aa688d..84983b78c2fc 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -51,8 +51,6 @@ export class PythonTestServer implements ITestServer, Disposable { if (cwd) { this._onDataReceived.fire({ cwd, data }); this.uuids.delete(uuid); - } else { - traceLog(`cannot find invalid uuid`); } } catch (ex) { traceLog(`Error processing test server request: ${ex} observe`); From 419a266197b0f51f192f31da6dbe198c34eb6606 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Fri, 29 Jul 2022 15:16:30 -0700 Subject: [PATCH 32/49] pass in debuglauncher to new python test server in all of server.unit.test.ts instances --- .../testing/testController/server.unit.test.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/test/testing/testController/server.unit.test.ts b/src/test/testing/testController/server.unit.test.ts index 967b3eab9bfa..04c83152ca01 100644 --- a/src/test/testing/testController/server.unit.test.ts +++ b/src/test/testing/testController/server.unit.test.ts @@ -10,6 +10,7 @@ import { IPythonExecutionFactory, IPythonExecutionService } from '../../../clien import { createDeferred } from '../../../client/common/utils/async'; import { PythonTestServer } from '../../../client/testing/testController/common/server'; import * as logging from '../../../client/logging'; +import { ITestDebugLauncher } from '../../../client/testing/common/types'; suite('Python Test Server', () => { const fakeUuid = 'fake-uuid'; @@ -21,6 +22,7 @@ suite('Python Test Server', () => { let execArgs: string[]; let v4Stub: sinon.SinonStub; let traceLogStub: sinon.SinonStub; + let debugLauncher: ITestDebugLauncher; setup(() => { sandbox = sinon.createSandbox(); @@ -53,7 +55,7 @@ suite('Python Test Server', () => { cwd: '/foo/bar', }; - server = new PythonTestServer(stubExecutionFactory); + server = new PythonTestServer(stubExecutionFactory, debugLauncher); await server.sendCommand(options); const { port } = server; @@ -75,7 +77,7 @@ suite('Python Test Server', () => { outChannel, }; - server = new PythonTestServer(stubExecutionFactory); + server = new PythonTestServer(stubExecutionFactory, debugLauncher); await server.sendCommand(options); @@ -99,7 +101,7 @@ suite('Python Test Server', () => { cwd: '/foo/bar', }; - server = new PythonTestServer(stubExecutionFactory); + server = new PythonTestServer(stubExecutionFactory, debugLauncher); server.onDataReceived(({ data }) => { eventData = JSON.parse(data); }); @@ -120,7 +122,7 @@ suite('Python Test Server', () => { let response; - server = new PythonTestServer(stubExecutionFactory); + server = new PythonTestServer(stubExecutionFactory, debugLauncher); server.onDataReceived(({ data }) => { response = data; deferred.resolve(); @@ -159,7 +161,7 @@ suite('Python Test Server', () => { let response; - server = new PythonTestServer(stubExecutionFactory); + server = new PythonTestServer(stubExecutionFactory, debugLauncher); server.onDataReceived(({ data }) => { response = data; deferred.resolve(); @@ -199,7 +201,7 @@ suite('Python Test Server', () => { let response; - server = new PythonTestServer(stubExecutionFactory); + server = new PythonTestServer(stubExecutionFactory, debugLauncher); server.onDataReceived(({ data }) => { response = data; deferred.resolve(); @@ -238,7 +240,7 @@ suite('Python Test Server', () => { let response; - server = new PythonTestServer(stubExecutionFactory); + server = new PythonTestServer(stubExecutionFactory, debugLauncher); server.onDataReceived(({ data }) => { response = data; deferred.resolve(); From 6f0a2b43548cbdce6613c4e8bd358ade123bd299 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Fri, 29 Jul 2022 15:54:04 -0700 Subject: [PATCH 33/49] make debugLauncher non optional on server.ts --- src/client/testing/testController/common/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 84983b78c2fc..53cb812f7a4c 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -25,7 +25,7 @@ export class PythonTestServer implements ITestServer, Disposable { public port: number; - constructor(private executionFactory: IPythonExecutionFactory, private debugLauncher?: ITestDebugLauncher) { + constructor(private executionFactory: IPythonExecutionFactory, private debugLauncher: ITestDebugLauncher) { this.uuids = new Map(); this.port = DEFAULT_TEST_PORT; From 2f901d1207f7c5da913e04e7192804cb121b340d Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 30 Jul 2022 11:27:53 -0700 Subject: [PATCH 34/49] fix unnecessary comments on discovery --- pythonFiles/unittestadapter/discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonFiles/unittestadapter/discovery.py b/pythonFiles/unittestadapter/discovery.py index 77bf9dfb3934..02a612628c9f 100644 --- a/pythonFiles/unittestadapter/discovery.py +++ b/pythonFiles/unittestadapter/discovery.py @@ -85,7 +85,7 @@ def discover_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; --> now moved to header + - 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. From 27324717b031887b38c568ada21442b311706a4f Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 30 Jul 2022 11:52:52 -0700 Subject: [PATCH 35/49] rename requestuuid to request-uuid --- pythonFiles/unittestadapter/discovery.py | 2 +- pythonFiles/unittestadapter/execution.py | 2 +- src/client/testing/testController/common/server.ts | 2 +- src/test/testing/testController/server.unit.test.ts | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pythonFiles/unittestadapter/discovery.py b/pythonFiles/unittestadapter/discovery.py index 02a612628c9f..7d8434f2dd01 100644 --- a/pythonFiles/unittestadapter/discovery.py +++ b/pythonFiles/unittestadapter/discovery.py @@ -153,7 +153,7 @@ def discover_tests( Host: localhost:{port} Content-Length: {len(data)} Content-Type: application/json -Requestuuid: {uuid} +Request-uuid: {uuid} {data}""" result = s.socket.sendall(request.encode("utf-8")) # type: ignore diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 7c05bc0aa38c..e8713767c2bf 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -236,7 +236,7 @@ def run_tests( Host: localhost:{port} Content-Length: {len(data)} Content-Type: application/json -Requestuuid: {uuid} +Request-uuid: {uuid} {data}""" result = s.socket.sendall(request.encode("utf-8")) # type: ignore diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 53cb812f7a4c..8e6d2fac3829 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -40,7 +40,7 @@ export class PythonTestServer implements ITestServer, Disposable { const data = Buffer.concat(buffers).toString(); // grab the uuid from the header - const indexRequestuuid = request.rawHeaders.indexOf('Requestuuid'); + const indexRequestuuid = request.rawHeaders.indexOf('Request-uuid'); const uuid = request.rawHeaders[indexRequestuuid + 1]; response.end(); diff --git a/src/test/testing/testController/server.unit.test.ts b/src/test/testing/testController/server.unit.test.ts index 04c83152ca01..125500fd0ab7 100644 --- a/src/test/testing/testController/server.unit.test.ts +++ b/src/test/testing/testController/server.unit.test.ts @@ -136,7 +136,7 @@ suite('Python Test Server', () => { hostname: 'localhost', method: 'POST', port, - headers: { Requestuuid: fakeUuid }, + headers: { 'Request-uuid': fakeUuid }, }; const request = http.request(requestOptions, (res) => { @@ -175,7 +175,7 @@ suite('Python Test Server', () => { hostname: 'localhost', method: 'POST', port, - headers: { Requestuuid: fakeUuid }, + headers: { 'Request-uuid': fakeUuid }, }; const request = http.request(requestOptions, (res) => { @@ -215,7 +215,7 @@ suite('Python Test Server', () => { hostname: 'localhost', method: 'POST', port, - headers: { Requestuuid: fakeUuid }, + headers: { 'Request-uuid': fakeUuid }, }; // request.hasHeader() const request = http.request(requestOptions, (res) => { @@ -254,13 +254,13 @@ suite('Python Test Server', () => { hostname: 'localhost', method: 'POST', port, - headers: { Requestuuid: 'some-other-uuid' }, + headers: { 'Request-uuid': 'some-other-uuid' }, }; const requestOptions2 = { hostname: 'localhost', method: 'POST', port, - headers: { Requestuuid: fakeUuid }, + headers: { 'Request-uuid': fakeUuid }, }; const requestOne = http.request(requestOptions, (res) => { res.setEncoding('utf8'); From 3d1dad72b1b6294d9b6d57e96d8f28519b7eecc8 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 30 Jul 2022 15:25:10 -0700 Subject: [PATCH 36/49] rename parse_unittest_discovery_args and move to utils --- .../tests/unittestadapter/test_discovery.py | 5 ++-- pythonFiles/unittestadapter/discovery.py | 29 ++----------------- pythonFiles/unittestadapter/execution.py | 11 +++---- pythonFiles/unittestadapter/utils.py | 27 +++++++++++++++++ 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/pythonFiles/tests/unittestadapter/test_discovery.py b/pythonFiles/tests/unittestadapter/test_discovery.py index decbcb7bcc5e..30ccb7ef4079 100644 --- a/pythonFiles/tests/unittestadapter/test_discovery.py +++ b/pythonFiles/tests/unittestadapter/test_discovery.py @@ -10,9 +10,8 @@ DEFAULT_PORT, discover_tests, parse_discovery_cli_args, - parse_unittest_discovery_args, ) -from unittestadapter.utils import TestNodeTypeEnum +from unittestadapter.utils import TestNodeTypeEnum, parse_unittest_args from .helpers import TEST_DATA_PATH, is_same_tree @@ -68,7 +67,7 @@ def test_parse_unittest_args(args: List[str], expected: List[str]) -> None: """The parse_unittest_args function should return values for the start_dir, pattern, and top_level_dir arguments when passed as command-line options, and ignore unrecognized arguments. """ - actual = parse_unittest_discovery_args(args) + actual = parse_unittest_args(args) assert actual == expected diff --git a/pythonFiles/unittestadapter/discovery.py b/pythonFiles/unittestadapter/discovery.py index 7d8434f2dd01..49cb1cbd266c 100644 --- a/pythonFiles/unittestadapter/discovery.py +++ b/pythonFiles/unittestadapter/discovery.py @@ -23,6 +23,7 @@ sys.path.insert(0, os.path.join(PYTHON_FILES, "lib", "python")) from typing_extensions import NotRequired +from utils import parse_unittest_args DEFAULT_PORT = "45454" @@ -44,32 +45,6 @@ def parse_discovery_cli_args(args: List[str]) -> Tuple[int, Union[str, None]]: return int(parsed_args.port), parsed_args.uuid -def parse_unittest_discovery_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, - ) - - class PayloadDict(TypedDict): cwd: str status: Literal["success", "error"] @@ -139,7 +114,7 @@ def discover_tests( argv = sys.argv[1:] index = argv.index("--udiscovery") - start_dir, pattern, top_level_dir = parse_unittest_discovery_args(argv[index + 1 :]) + start_dir, pattern, top_level_dir = parse_unittest_args(argv[index + 1 :]) # Perform test discovery. port, uuid = parse_discovery_cli_args(argv[:index]) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index e8713767c2bf..97d4a4103821 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -11,17 +11,14 @@ from types import TracebackType from typing import Dict, List, Optional, Tuple, Type, TypeAlias, TypedDict -from discovery import parse_unittest_discovery_args -from testing_tools import socket_manager -from typing_extensions import NotRequired - # 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 +from utils import parse_unittest_args DEFAULT_PORT = "45454" @@ -222,7 +219,7 @@ def run_tests( argv = sys.argv[1:] index = argv.index("--udiscovery") - start_dir, pattern, top_level_dir = parse_unittest_discovery_args(argv[index + 1 :]) + start_dir, pattern, top_level_dir = parse_unittest_args(argv[index + 1 :]) # Perform test execution. port, uuid, testids = parse_execution_cli_args(argv[:index]) diff --git a/pythonFiles/unittestadapter/utils.py b/pythonFiles/unittestadapter/utils.py index 4d0342cfaad3..568ff30ee92d 100644 --- a/pythonFiles/unittestadapter/utils.py +++ b/pythonFiles/unittestadapter/utils.py @@ -1,6 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import argparse import enum import inspect import os @@ -199,3 +200,29 @@ def build_test_tree( 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 d1da2ed804ea294b2de63d0e29d8568f3fedfb40 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 30 Jul 2022 15:41:19 -0700 Subject: [PATCH 37/49] attempt to fix the moduleNotFound for utils --- pythonFiles/unittestadapter/discovery.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pythonFiles/unittestadapter/discovery.py b/pythonFiles/unittestadapter/discovery.py index 49cb1cbd266c..0be09e986ca8 100644 --- a/pythonFiles/unittestadapter/discovery.py +++ b/pythonFiles/unittestadapter/discovery.py @@ -17,13 +17,12 @@ 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 +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")) from typing_extensions import NotRequired -from utils import parse_unittest_args DEFAULT_PORT = "45454" From a4062ec6262cf727a4ec54b9ee96b899498718f6 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 30 Jul 2022 15:49:22 -0700 Subject: [PATCH 38/49] attempt to fix moduleNotFound2 --- pythonFiles/unittestadapter/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 97d4a4103821..7eaeaad3ac4b 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/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 utils import parse_unittest_args +from unittestadapter.utils import parse_unittest_args DEFAULT_PORT = "45454" From e490446594aea4b0051b4d1a64291c3503931f2a Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 30 Jul 2022 15:56:09 -0700 Subject: [PATCH 39/49] rearrange os.path --- pythonFiles/unittestadapter/execution.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 7eaeaad3ac4b..efba6187f692 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -11,15 +11,15 @@ from types import TracebackType from typing import Dict, List, Optional, Tuple, Type, TypeAlias, TypedDict +from testing_tools import socket_manager +from typing_extensions import NotRequired +from unittestadapter.utils import parse_unittest_args + # 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 -from unittestadapter.utils import parse_unittest_args - DEFAULT_PORT = "45454" From 32154186973b7be527cc94d703f48b8bca48127c Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sat, 30 Jul 2022 15:58:21 -0700 Subject: [PATCH 40/49] undo rearrange os.path --- pythonFiles/unittestadapter/execution.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index efba6187f692..7eaeaad3ac4b 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -11,15 +11,15 @@ from types import TracebackType from typing import Dict, List, Optional, Tuple, Type, TypeAlias, TypedDict -from testing_tools import socket_manager -from typing_extensions import NotRequired -from unittestadapter.utils import parse_unittest_args - # 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 +from unittestadapter.utils import parse_unittest_args + DEFAULT_PORT = "45454" From 0202291a157257cf2dd21f435befe89b913fa095 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sun, 31 Jul 2022 17:27:24 -0700 Subject: [PATCH 41/49] test that were not found mark as skipped --- pythonFiles/unittestadapter/execution.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 7eaeaad3ac4b..137fd5dd12a8 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -197,9 +197,9 @@ def run_tests( payload["result"] = result.formatted # Add a payload entry with the list of test ids for tests that weren't found. - # not_found = set(test_ids) - set(filtered_results.keys()) - # if not_found: - # payload["not_found"] = list(not_found) + not_found = set(test_ids) - set(tailor.keys()) + if not_found: + payload["skipped"] = list(not_found) except Exception: status = TestExecutionStatus.error error = traceback.format_exc() From 4bf9199a69c9038f3cdfd0c6ed41760688a41790 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sun, 31 Jul 2022 17:46:51 -0700 Subject: [PATCH 42/49] fix set subtraction --- pythonFiles/unittestadapter/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 137fd5dd12a8..21248b1472ee 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -197,7 +197,7 @@ def run_tests( payload["result"] = result.formatted # Add a payload entry with the list of test ids for tests that weren't found. - not_found = set(test_ids) - set(tailor.keys()) + not_found = set(tailor) - set(suite) # type: ignore if not_found: payload["skipped"] = list(not_found) except Exception: From 9c4ddfdb44cc2969e6085d2853fdbd776f0c221f Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sun, 31 Jul 2022 17:55:59 -0700 Subject: [PATCH 43/49] remove unnecessary not found which is handled earlier by tailor run and result --- pythonFiles/unittestadapter/execution.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index 21248b1472ee..a016ff1af9ec 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -196,10 +196,6 @@ def run_tests( payload["result"] = result.formatted - # Add a payload entry with the list of test ids for tests that weren't found. - not_found = set(tailor) - set(suite) # type: ignore - if not_found: - payload["skipped"] = list(not_found) except Exception: status = TestExecutionStatus.error error = traceback.format_exc() From 80f7b8878f1dacbe41408f8c2ad0847e435caf5d Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sun, 31 Jul 2022 23:09:37 -0700 Subject: [PATCH 44/49] fix broken discovery refresh --- .../testing/testController/workspaceTestAdapter.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index c1c7ed4b3197..1a6daa41aa95 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -309,7 +309,8 @@ export class WorkspaceTestAdapter { // If the test root for this folder exists: Workspace refresh, update its children. // Otherwise, it is a freshly discovered workspace, and we need to create a new test root and populate the test tree. if (workspaceNode) { - updateTestTree(testController, rawTestData.tests, this.testData, workspaceNode, token); + // updateTestTree(testController, rawTestData.tests, this.testData, workspaceNode, token); + populateTestTree(testController, rawTestData.tests, undefined, this, token); } else { populateTestTree(testController, rawTestData.tests, undefined, this, token); } @@ -382,7 +383,11 @@ function updateTestTree( // Go through the updated tree, update the existing nodes, and create new ones if necessary. updatedData.children.forEach((child) => { if (!token?.isCancellationRequested) { - const root = testController.items.get(child.path); + let root = testController.items.get(child.path); + if (!root) { + // try with id + root = testController.items.get(child.id_); + } if (root) { root.busy = true; // Update existing test node or item. @@ -411,7 +416,7 @@ function updateTestTree( testRoot!.children.add(testItem); } else { - testItem = testController.createTestItem(child.path, child.name, Uri.file(child.path)); + testItem = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); testItem.canResolveChildren = true; testItem.tags = [RunTestTag, DebugTestTag]; From ef716ba6a7561932de1676c9ee8e9d4f78a802ae Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sun, 31 Jul 2022 23:16:01 -0700 Subject: [PATCH 45/49] fix warnings regarding unused --- .../testController/workspaceTestAdapter.ts | 194 +++++++++--------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 1a6daa41aa95..c0af6ecee086 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -332,103 +332,103 @@ function isTestItem(test: DiscoveredTestNode | DiscoveredTestItem): test is Disc return test.type_ === 'test'; } -function deleteTestTree(testController: TestController, root?: TestItem) { - if (root) { - const { children } = root; - - children.forEach((child) => { - deleteTestTree(testController, child); - - const { id } = child; - testController.items.delete(id); - }); - - testController.items.delete(root.id); - } -} - -function updateTestTree( - testController: TestController, - updatedData: DiscoveredTestNode, - localData: DiscoveredTestNode | undefined, - testRoot: TestItem | undefined, - token?: CancellationToken, -): void { - // If testRoot is undefined, use the info of the root item of testTreeData to create a test item, and append it to the test controller. - if (!testRoot) { - testRoot = testController.createTestItem(updatedData.path, updatedData.name, Uri.file(updatedData.path)); - testRoot.canResolveChildren = true; - testRoot.tags = [RunTestTag, DebugTestTag]; - - testController.items.add(testRoot); - } - - // Delete existing items if they don't exist in the updated tree. - if (localData) { - localData.children.forEach((local) => { - if (!token?.isCancellationRequested) { - const exists = updatedData.children.find( - (node) => local.name === node.name && local.path === node.path && local.type_ === node.type_, - ); - - if (!exists) { - // Delete this node and all its children. - const testItem = testController.items.get(local.path); - deleteTestTree(testController, testItem); - } - } - }); - } - - // Go through the updated tree, update the existing nodes, and create new ones if necessary. - updatedData.children.forEach((child) => { - if (!token?.isCancellationRequested) { - let root = testController.items.get(child.path); - if (!root) { - // try with id - root = testController.items.get(child.id_); - } - if (root) { - root.busy = true; - // Update existing test node or item. - if (isTestItem(child)) { - // Update the only property that can be updated. - root.label = child.name; - } else { - const localNode = localData?.children.find( - (node) => child.name === node.name && child.path === node.path && child.type_ === node.type_, - ); - updateTestTree(testController, child, localNode as DiscoveredTestNode, root, token); - } - root.busy = false; - } else { - // Create new test node or item. - let testItem; - if (isTestItem(child)) { - testItem = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); - // testItem = testController.createTestItem(child.uniqueID, child.name, Uri.file(child.path)); - const range = new Range(new Position(child.lineno - 1, 0), new Position(child.lineno, 0)); - - testItem.canResolveChildren = false; - - testItem.tags = [RunTestTag, DebugTestTag]; - testItem.range = range; - - testRoot!.children.add(testItem); - } else { - testItem = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); - testItem.canResolveChildren = true; - testItem.tags = [RunTestTag, DebugTestTag]; - - testRoot!.children.add(testItem); - - // Populate the test tree under the newly created node. - // populateTestTree(testController, child, testItem, token, this); // uncomment later - } - } - } - }); -} +// function deleteTestTree(testController: TestController, root?: TestItem) { +// if (root) { +// const { children } = root; + +// children.forEach((child) => { +// deleteTestTree(testController, child); + +// const { id } = child; +// testController.items.delete(id); +// }); + +// testController.items.delete(root.id); +// } +// } + +// function updateTestTree( +// testController: TestController, +// updatedData: DiscoveredTestNode, +// localData: DiscoveredTestNode | undefined, +// testRoot: TestItem | undefined, +// token?: CancellationToken, +// ): void { +// // If testRoot is undefined, use the info of the root item of testTreeData to create a test item, and append it to the test controller. +// if (!testRoot) { +// testRoot = testController.createTestItem(updatedData.path, updatedData.name, Uri.file(updatedData.path)); +// testRoot.canResolveChildren = true; +// testRoot.tags = [RunTestTag, DebugTestTag]; + +// testController.items.add(testRoot); +// } + +// // Delete existing items if they don't exist in the updated tree. +// if (localData) { +// localData.children.forEach((local) => { +// if (!token?.isCancellationRequested) { +// const exists = updatedData.children.find( +// (node) => local.name === node.name && local.path === node.path && local.type_ === node.type_, +// ); + +// if (!exists) { +// // Delete this node and all its children. +// const testItem = testController.items.get(local.path); +// deleteTestTree(testController, testItem); +// } +// } +// }); +// } + +// // Go through the updated tree, update the existing nodes, and create new ones if necessary. +// updatedData.children.forEach((child) => { +// if (!token?.isCancellationRequested) { +// let root = testController.items.get(child.path); +// if (!root) { +// // try with id +// root = testController.items.get(child.id_); +// } +// if (root) { +// root.busy = true; +// // Update existing test node or item. +// if (isTestItem(child)) { +// // Update the only property that can be updated. +// root.label = child.name; +// } else { +// const localNode = localData?.children.find( +// (node) => child.name === node.name && child.path === node.path && child.type_ === node.type_, +// ); +// updateTestTree(testController, child, localNode as DiscoveredTestNode, root, token); +// } +// root.busy = false; +// } else { +// // Create new test node or item. +// let testItem; +// if (isTestItem(child)) { +// testItem = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); +// // testItem = testController.createTestItem(child.uniqueID, child.name, Uri.file(child.path)); +// const range = new Range(new Position(child.lineno - 1, 0), new Position(child.lineno, 0)); + +// testItem.canResolveChildren = false; + +// testItem.tags = [RunTestTag, DebugTestTag]; +// testItem.range = range; + +// testRoot!.children.add(testItem); +// } else { +// testItem = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); +// testItem.canResolveChildren = true; +// testItem.tags = [RunTestTag, DebugTestTag]; + +// testRoot!.children.add(testItem); + +// // Populate the test tree under the newly created node. +// // populateTestTree(testController, child, testItem, token, this); // uncomment later +// } +// } +// } +// }); +// } // had to switch the order of the original parameter since required param cannot follow optional. function populateTestTree( testController: TestController, From 94794e097e6b80e5733de9ae2441601b2df3c22f Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sun, 31 Jul 2022 23:22:53 -0700 Subject: [PATCH 46/49] remove unused variable --- src/client/testing/testController/workspaceTestAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index c0af6ecee086..bd0f145bceb3 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -50,7 +50,7 @@ export class WorkspaceTestAdapter { private executing: Deferred | undefined; - private testData: DiscoveredTestNode | undefined; + // private testData: DiscoveredTestNode | undefined; runIdToTestItem: Map; From 679c1747e0f203b6d797e0d7dd3e063442c93091 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Sun, 31 Jul 2022 23:29:26 -0700 Subject: [PATCH 47/49] fix testData doesnt exist error --- src/client/testing/testController/workspaceTestAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index bd0f145bceb3..9000b15fe2fd 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -320,7 +320,7 @@ export class WorkspaceTestAdapter { } // Save new test data state. - this.testData = rawTestData.tests; + // this.testData = rawTestData.tests; } sendTelemetryEvent(EventName.UNITTEST_DISCOVERY_DONE, undefined, { tool: this.testProvider, failed: false }); From db8afc809363d7a67dd5ec40b5dc02715e780544 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Mon, 1 Aug 2022 08:47:24 -0700 Subject: [PATCH 48/49] remove unused methods --- .../testController/workspaceTestAdapter.ts | 111 +----------------- 1 file changed, 1 insertion(+), 110 deletions(-) diff --git a/src/client/testing/testController/workspaceTestAdapter.ts b/src/client/testing/testController/workspaceTestAdapter.ts index 9000b15fe2fd..0ecab7649745 100644 --- a/src/client/testing/testController/workspaceTestAdapter.ts +++ b/src/client/testing/testController/workspaceTestAdapter.ts @@ -50,8 +50,6 @@ export class WorkspaceTestAdapter { private executing: Deferred | undefined; - // private testData: DiscoveredTestNode | undefined; - runIdToTestItem: Map; runIdToVSid: Map; @@ -303,24 +301,14 @@ export class WorkspaceTestAdapter { children, }; - const workspaceNode = testController.items.get(rootPath); - if (rawTestData.tests) { // If the test root for this folder exists: Workspace refresh, update its children. // Otherwise, it is a freshly discovered workspace, and we need to create a new test root and populate the test tree. - if (workspaceNode) { - // updateTestTree(testController, rawTestData.tests, this.testData, workspaceNode, token); - populateTestTree(testController, rawTestData.tests, undefined, this, token); - } else { - populateTestTree(testController, rawTestData.tests, undefined, this, token); - } + populateTestTree(testController, rawTestData.tests, undefined, this, token); } else { // Delete everything from the test controller. testController.items.replace([]); } - - // Save new test data state. - // this.testData = rawTestData.tests; } sendTelemetryEvent(EventName.UNITTEST_DISCOVERY_DONE, undefined, { tool: this.testProvider, failed: false }); @@ -332,103 +320,6 @@ function isTestItem(test: DiscoveredTestNode | DiscoveredTestItem): test is Disc return test.type_ === 'test'; } -// function deleteTestTree(testController: TestController, root?: TestItem) { -// if (root) { -// const { children } = root; - -// children.forEach((child) => { -// deleteTestTree(testController, child); - -// const { id } = child; -// testController.items.delete(id); -// }); - -// testController.items.delete(root.id); -// } -// } - -// function updateTestTree( -// testController: TestController, -// updatedData: DiscoveredTestNode, -// localData: DiscoveredTestNode | undefined, -// testRoot: TestItem | undefined, -// token?: CancellationToken, -// ): void { -// // If testRoot is undefined, use the info of the root item of testTreeData to create a test item, and append it to the test controller. -// if (!testRoot) { -// testRoot = testController.createTestItem(updatedData.path, updatedData.name, Uri.file(updatedData.path)); -// testRoot.canResolveChildren = true; -// testRoot.tags = [RunTestTag, DebugTestTag]; - -// testController.items.add(testRoot); -// } - -// // Delete existing items if they don't exist in the updated tree. -// if (localData) { -// localData.children.forEach((local) => { -// if (!token?.isCancellationRequested) { -// const exists = updatedData.children.find( -// (node) => local.name === node.name && local.path === node.path && local.type_ === node.type_, -// ); - -// if (!exists) { -// // Delete this node and all its children. -// const testItem = testController.items.get(local.path); -// deleteTestTree(testController, testItem); -// } -// } -// }); -// } - -// // Go through the updated tree, update the existing nodes, and create new ones if necessary. -// updatedData.children.forEach((child) => { -// if (!token?.isCancellationRequested) { -// let root = testController.items.get(child.path); -// if (!root) { -// // try with id -// root = testController.items.get(child.id_); -// } -// if (root) { -// root.busy = true; -// // Update existing test node or item. -// if (isTestItem(child)) { -// // Update the only property that can be updated. -// root.label = child.name; -// } else { -// const localNode = localData?.children.find( -// (node) => child.name === node.name && child.path === node.path && child.type_ === node.type_, -// ); -// updateTestTree(testController, child, localNode as DiscoveredTestNode, root, token); -// } -// root.busy = false; -// } else { -// // Create new test node or item. -// let testItem; -// if (isTestItem(child)) { -// testItem = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); -// // testItem = testController.createTestItem(child.uniqueID, child.name, Uri.file(child.path)); -// const range = new Range(new Position(child.lineno - 1, 0), new Position(child.lineno, 0)); - -// testItem.canResolveChildren = false; - -// testItem.tags = [RunTestTag, DebugTestTag]; -// testItem.range = range; - -// testRoot!.children.add(testItem); -// } else { -// testItem = testController.createTestItem(child.id_, child.name, Uri.file(child.path)); -// testItem.canResolveChildren = true; -// testItem.tags = [RunTestTag, DebugTestTag]; - -// testRoot!.children.add(testItem); - -// // Populate the test tree under the newly created node. -// // populateTestTree(testController, child, testItem, token, this); // uncomment later -// } -// } -// } -// }); -// } // had to switch the order of the original parameter since required param cannot follow optional. function populateTestTree( testController: TestController, From 581184b33bb14cb65d7bade135683d5a64dac385 Mon Sep 17 00:00:00 2001 From: ksy7588 Date: Mon, 1 Aug 2022 10:27:43 -0700 Subject: [PATCH 49/49] comment new unittest --- .../testing/testController/controller.ts | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/src/client/testing/testController/controller.ts b/src/client/testing/testController/controller.ts index e33364c2fd78..1e4321e9ac7c 100644 --- a/src/client/testing/testController/controller.ts +++ b/src/client/testing/testController/controller.ts @@ -232,21 +232,21 @@ export class PythonTestController implements ITestController, IExtensionSingleAc 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; @@ -362,16 +362,16 @@ export class PythonTestController implements ITestController, IExtensionSingleAc tool: 'pytest', debugging: request.profile?.kind === TestRunProfileKind.Debug, }); - // return this.pytest.runTests( - // { - // includes: testItems, - // excludes: request.exclude ?? [], - // runKind: request.profile?.kind ?? TestRunProfileKind.Run, - // runInstance, - // }, - // workspace, - // token, - // ); + return this.pytest.runTests( + { + includes: testItems, + excludes: request.exclude ?? [], + runKind: request.profile?.kind ?? TestRunProfileKind.Run, + runInstance, + }, + workspace, + token, + ); } if (settings.testing.unittestEnabled) { // potentially sqeeze in the new exeuction way here? @@ -380,30 +380,30 @@ export class PythonTestController implements ITestController, IExtensionSingleAc debugging: request.profile?.kind === TestRunProfileKind.Debug, }); // new execution runner/adapter - const testAdapter = - this.testAdapters.get(workspace.uri) || - (this.testAdapters.values().next().value as WorkspaceTestAdapter); - return testAdapter.executeTests( - this.testController, - runInstance, - testItems, - token, - request.profile?.kind === TestRunProfileKind.Debug, - ); + // const testAdapter = + // this.testAdapters.get(workspace.uri) || + // (this.testAdapters.values().next().value as WorkspaceTestAdapter); + // return testAdapter.executeTests( + // this.testController, + // runInstance, + // testItems, + // token, + // request.profile?.kind === TestRunProfileKind.Debug, + // ); // below is old way of running unittest execution - // return this.unittest.runTests( - // { - // includes: testItems, - // excludes: request.exclude ?? [], - // runKind: request.profile?.kind ?? TestRunProfileKind.Run, - // runInstance, - // }, - // workspace, - // token, - // this.testController, - // ); + return this.unittest.runTests( + { + includes: testItems, + excludes: request.exclude ?? [], + runKind: request.profile?.kind ?? TestRunProfileKind.Run, + runInstance, + }, + workspace, + token, + this.testController, + ); } }