From 0daa2eed73fb2af844b14165c7929ea33545c968 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 4 Sep 2024 11:36:37 -0700 Subject: [PATCH 1/7] switch to using temp file for test_ids --- python_files/unittestadapter/execution.py | 109 +++++++----------- .../vscode_pytest/run_pytest_script.py | 70 ++++------- .../testing/testController/common/utils.ts | 29 +++++ .../pytest/pytestExecutionAdapter.ts | 8 +- .../unittest/testExecutionAdapter.ts | 6 +- 5 files changed, 102 insertions(+), 120 deletions(-) diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index 4bc668bf71b6..1c2b87b42683 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -306,70 +306,47 @@ def send_run_data(raw_data, test_run_pipe): if not test_run_pipe: print("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.") raise VSCodeUnittestError("Error[vscode-unittest]: TEST_RUN_PIPE env var is not set.") - test_ids_from_buffer = [] - raw_json = None - try: - with socket_manager.PipeManager(run_test_ids_pipe) as sock: - buffer: str = "" - while True: - # Receive the data from the client - data: str = sock.read() - if not data: - break - - # Append the received data to the buffer - buffer += data - - try: - # Try to parse the buffer as JSON - raw_json = process_json_util.process_rpc_json(buffer) - # Clear the buffer as complete JSON object is received - buffer = "" - print("Received JSON data in run") - break - except json.JSONDecodeError: - # JSON decoding error, the complete JSON object is not yet received - continue - except OSError as e: - msg = f"Error: Could not connect to RUN_TEST_IDS_PIPE: {e}" - print(msg) - raise VSCodeUnittestError(msg) from e - + test_ids = [] try: - if raw_json and "params" in raw_json and raw_json["params"]: - test_ids_from_buffer = raw_json["params"] - # Check to see if we are running django tests. - if manage_py_path := os.environ.get("MANAGE_PY_PATH"): - args = argv[index + 1 :] or [] - django_execution_runner(manage_py_path, test_ids_from_buffer, args) - # the django run subprocesses sends the eot payload. - else: - # Perform test execution. - payload = run_tests( - start_dir, - test_ids_from_buffer, - pattern, - top_level_dir, - verbosity, - failfast, - locals_, - ) - eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} - send_post_request(eot_payload, test_run_pipe) - else: - # No test ids received from buffer - cwd = os.path.abspath(start_dir) # noqa: PTH100 - status = TestExecutionStatus.error - payload: ExecutionPayloadDict = { - "cwd": cwd, - "status": status, - "error": "No test ids received from buffer", - "result": None, - } - send_post_request(payload, test_run_pipe) - eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} - send_post_request(eot_payload, test_run_pipe) - except json.JSONDecodeError as exc: - msg = "Error: Could not parse test ids from stdin" - print(msg) - raise VSCodeUnittestError(msg) from exc + # Read the test ids from the file, attempt to delete file afterwords. + ids_path = pathlib.Path(run_test_ids_pipe) + test_ids = ids_path.read_text(encoding="utf-8").splitlines() + print("Received test ids from temp file.") + try: + ids_path.unlink() + except Exception as e: + print("Error[vscode-pytest]: unable to delete temp file" + str(e)) + + except Exception as e: + # No test ids received from buffer, return error payload + cwd = os.path.abspath(start_dir) # noqa: PTH100 + status = TestExecutionStatus.error + payload: ExecutionPayloadDict = { + "cwd": cwd, + "status": status, + "error": "No test ids read from temp file," + str(e), + "result": None, + } + send_post_request(payload, test_run_pipe) + eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} + send_post_request(eot_payload, test_run_pipe) + + # If no error occurred, we will have test ids to run. + if manage_py_path := os.environ.get("MANAGE_PY_PATH"): + print("MANAGE_PY_PATH env var set, running Django test suite.") + args = argv[index + 1 :] or [] + django_execution_runner(manage_py_path, test_ids, args) + # the django run subprocesses sends the eot payload. + else: + # Perform regular unittest execution. + payload = run_tests( + start_dir, + test_ids, + pattern, + top_level_dir, + verbosity, + failfast, + locals_, + ) + eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} + send_post_request(eot_payload, test_run_pipe) diff --git a/python_files/vscode_pytest/run_pytest_script.py b/python_files/vscode_pytest/run_pytest_script.py index 515e04d1b84d..0054a8b2298b 100644 --- a/python_files/vscode_pytest/run_pytest_script.py +++ b/python_files/vscode_pytest/run_pytest_script.py @@ -22,6 +22,13 @@ socket_manager, ) + +def run_without_test_ids(args): + print("Running pytest with no test ids as args. Args being used: ", args) + arg_array = ["-p", "vscode_pytest", *args] + pytest.main(arg_array) + + # This script handles running pytest via pytest.main(). It is called via run in the # pytest execution adapter and gets the test_ids to run via stdin and the rest of the # args through sys.argv. It then runs pytest.main() with the args and test_ids. @@ -34,52 +41,21 @@ # Get the rest of the args to run with pytest. args = sys.argv[1:] run_test_ids_pipe = os.environ.get("RUN_TEST_IDS_PIPE") - if not run_test_ids_pipe: - print("Error[vscode-pytest]: RUN_TEST_IDS_PIPE env var is not set.") - raw_json = {} - try: - socket_name = os.environ.get("RUN_TEST_IDS_PIPE") - with socket_manager.PipeManager(socket_name) as sock: - buffer = "" - while True: - # Receive the data from the client as a string - data = sock.read(3000) - if not data: - break - - # Append the received data to the buffer - buffer += data - - try: - # Try to parse the buffer as JSON - raw_json = process_json_util.process_rpc_json(buffer) - # Clear the buffer as complete JSON object is received - buffer = "" - print("Received JSON data in run script") - break - except json.JSONDecodeError: - # JSON decoding error, the complete JSON object is not yet received - continue - except UnicodeDecodeError: - continue - except OSError as e: - print(f"Error: Could not connect to runTestIdsPort: {e}") - print("Error: Could not connect to runTestIdsPort") - try: - test_ids_from_buffer = raw_json.get("params") - if test_ids_from_buffer: - arg_array = ["-p", "vscode_pytest", *args, *test_ids_from_buffer] + if run_test_ids_pipe: + try: + # Read the test ids from the file, delete file, and run pytest. + ids_path = pathlib.Path(run_test_ids_pipe) + ids = ids_path.read_text(encoding="utf-8").splitlines() + try: + ids_path.unlink() + except Exception as e: + print("Error[vscode-pytest]: unable to delete temp file" + str(e)) + arg_array = ["-p", "vscode_pytest", *args, *ids] print("Running pytest with args: " + str(arg_array)) pytest.main(arg_array) - else: - print( - "Error: No test ids received from stdin, could be an error or a run request without ids provided.", - ) - print("Running pytest with no test ids as args. Args being used: ", args) - arg_array = ["-p", "vscode_pytest", *args] - pytest.main(arg_array) - except json.JSONDecodeError: - print( - "Error: Could not parse test ids from stdin. Raw json received from socket: \n", - raw_json, - ) + except Exception as e: + print("Error[vscode-pytest]: unable to read testIds from temp file" + str(e)) + run_without_test_ids(args) + else: + print("Error[vscode-pytest]: RUN_TEST_IDS_PIPE env var is not set.") + run_without_test_ids(args) diff --git a/src/client/testing/testController/common/utils.ts b/src/client/testing/testController/common/utils.ts index 759fb0713de4..0b624d33ce40 100644 --- a/src/client/testing/testController/common/utils.ts +++ b/src/client/testing/testController/common/utils.ts @@ -3,6 +3,8 @@ import * as net from 'net'; import * as path from 'path'; import * as fs from 'fs'; +import * as os from 'os'; +import * as crypto from 'crypto'; import { CancellationToken, Position, TestController, TestItem, Uri, Range, Disposable } from 'vscode'; import { Message } from 'vscode-jsonrpc'; import { traceError, traceInfo, traceLog, traceVerbose } from '../../../logging'; @@ -193,6 +195,33 @@ interface ExecutionResultMessage extends Message { params: ExecutionTestPayload | EOTTestPayload; } +/** + * Writes an array of test IDs to a temporary file. + * + * @param testIds - The array of test IDs to write. + * @returns A promise that resolves to the file name of the temporary file. + */ +export async function writeTestIdsFile(testIds: string[], cwd?: string): Promise { + // temp file name in format of test-ids-.txt + const randomSuffix = crypto.randomBytes(10).toString('hex'); + const tempName = `test-ids-${randomSuffix}.txt`; + // create temp file + let tempFileName: string; + try { + traceLog('Attempting to use temp directory for test ids file, file name:', tempName); + tempFileName = path.join(os.tmpdir(), tempName); + } catch (error) { + // Handle the error when accessing the temp directory + traceError('Error accessing temp directory:', error, ' Attempt to use current working directory instead.'); + tempFileName = path.join(cwd || process.cwd(), tempName); + traceLog('New temp file:', tempFileName); + } + // write test ids to file + await fs.promises.writeFile(tempFileName, testIds.join('\n')); + // return file name + return tempFileName; +} + export async function startRunResultNamedPipe( dataReceivedCallback: (payload: ExecutionTestPayload | EOTTestPayload) => void, deferredTillServerClose: Deferred, diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index 5099efde179c..4b57c0413d6e 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -142,9 +142,9 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { testArgs = utils.addValueIfKeyNotExist(testArgs, '--capture', 'no'); } - // add port with run test ids to env vars - const testIdsPipeName = await utils.startTestIdsNamedPipe(testIds); - mutableEnv.RUN_TEST_IDS_PIPE = testIdsPipeName; + // create a file with the test ids and set the environment variable to the file name + const testIdsFileName = await utils.writeTestIdsFile(testIds, cwd); + mutableEnv.RUN_TEST_IDS_PIPE = testIdsFileName; traceInfo(`All environment variables set for pytest execution: ${JSON.stringify(mutableEnv)}`); const spawnOptions: SpawnOptions = { @@ -162,7 +162,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { args: testArgs, token: runInstance?.token, testProvider: PYTEST_PROVIDER, - runTestIdsPort: testIdsPipeName, + runTestIdsPort: testIdsFileName, pytestPort: resultNamedPipeName, }; traceInfo(`Running DEBUG pytest with arguments: ${testArgs} for workspace ${uri.fsPath} \r\n`); diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index 4746c3101752..97085b3d3834 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -137,8 +137,8 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { traceLog(`Running UNITTEST execution for the following test ids: ${testIds}`); // create named pipe server to send test ids - const testIdsPipeName = await utils.startTestIdsNamedPipe(testIds); - mutableEnv.RUN_TEST_IDS_PIPE = testIdsPipeName; + const testIdsFileName = await utils.writeTestIdsFile(testIds, cwd); + mutableEnv.RUN_TEST_IDS_PIPE = testIdsFileName; traceInfo(`All environment variables set for pytest execution: ${JSON.stringify(mutableEnv)}`); const spawnOptions: SpawnOptions = { @@ -167,7 +167,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { args, token: options.token, testProvider: UNITTEST_PROVIDER, - runTestIdsPort: testIdsPipeName, + runTestIdsPort: testIdsFileName, pytestPort: resultNamedPipeName, // change this from pytest }; traceInfo(`Running DEBUG unittest for workspace ${options.cwd} with arguments: ${args}\r\n`); From 21cff745c45cd2b9f2550c38d0a5c7ecf3b2b748 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 4 Sep 2024 12:01:19 -0700 Subject: [PATCH 2/7] unittest fix and fallback to extension dir --- .../testing/testController/common/utils.ts | 7 ++++--- .../pytest/pytestExecutionAdapter.ts | 2 +- .../unittest/testExecutionAdapter.ts | 2 +- .../pytest/pytestExecutionAdapter.unit.test.ts | 16 ++++++++-------- .../unittest/testExecutionAdapter.unit.test.ts | 14 +++++++------- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/client/testing/testController/common/utils.ts b/src/client/testing/testController/common/utils.ts index 0b624d33ce40..4b724355b218 100644 --- a/src/client/testing/testController/common/utils.ts +++ b/src/client/testing/testController/common/utils.ts @@ -22,6 +22,7 @@ import { } from './types'; import { Deferred, createDeferred } from '../../../common/utils/async'; import { createNamedPipeServer, generateRandomPipeName } from '../../../common/pipes/namedPipes'; +import { EXTENSION_ROOT_DIR } from '../../../constants'; export function fixLogLines(content: string): string { const lines = content.split(/\r?\n/g); @@ -201,7 +202,7 @@ interface ExecutionResultMessage extends Message { * @param testIds - The array of test IDs to write. * @returns A promise that resolves to the file name of the temporary file. */ -export async function writeTestIdsFile(testIds: string[], cwd?: string): Promise { +export async function writeTestIdsFile(testIds: string[]): Promise { // temp file name in format of test-ids-.txt const randomSuffix = crypto.randomBytes(10).toString('hex'); const tempName = `test-ids-${randomSuffix}.txt`; @@ -212,8 +213,8 @@ export async function writeTestIdsFile(testIds: string[], cwd?: string): Promise tempFileName = path.join(os.tmpdir(), tempName); } catch (error) { // Handle the error when accessing the temp directory - traceError('Error accessing temp directory:', error, ' Attempt to use current working directory instead.'); - tempFileName = path.join(cwd || process.cwd(), tempName); + traceError('Error accessing temp directory:', error, ' Attempt to use extension root dir instead'); + tempFileName = path.join(EXTENSION_ROOT_DIR, '.temp', tempName); traceLog('New temp file:', tempFileName); } // write test ids to file diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index 4b57c0413d6e..9d48003525d6 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -143,7 +143,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { } // create a file with the test ids and set the environment variable to the file name - const testIdsFileName = await utils.writeTestIdsFile(testIds, cwd); + const testIdsFileName = await utils.writeTestIdsFile(testIds); mutableEnv.RUN_TEST_IDS_PIPE = testIdsFileName; traceInfo(`All environment variables set for pytest execution: ${JSON.stringify(mutableEnv)}`); diff --git a/src/client/testing/testController/unittest/testExecutionAdapter.ts b/src/client/testing/testController/unittest/testExecutionAdapter.ts index 97085b3d3834..b3e134a30dd6 100644 --- a/src/client/testing/testController/unittest/testExecutionAdapter.ts +++ b/src/client/testing/testController/unittest/testExecutionAdapter.ts @@ -137,7 +137,7 @@ export class UnittestTestExecutionAdapter implements ITestExecutionAdapter { traceLog(`Running UNITTEST execution for the following test ids: ${testIds}`); // create named pipe server to send test ids - const testIdsFileName = await utils.writeTestIdsFile(testIds, cwd); + const testIdsFileName = await utils.writeTestIdsFile(testIds); mutableEnv.RUN_TEST_IDS_PIPE = testIdsFileName; traceInfo(`All environment variables set for pytest execution: ${JSON.stringify(mutableEnv)}`); diff --git a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts index 911cca4a284f..040734601a09 100644 --- a/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts +++ b/src/test/testing/testController/pytest/pytestExecutionAdapter.unit.test.ts @@ -33,7 +33,7 @@ suite('pytest test execution adapter', () => { (global as any).EXTENSION_ROOT_DIR = EXTENSION_ROOT_DIR; let myTestPath: string; let mockProc: MockChildProcess; - let utilsStartTestIdsNamedPipeStub: sinon.SinonStub; + let utilsWriteTestIdsFileStub: sinon.SinonStub; let utilsStartRunResultNamedPipeStub: sinon.SinonStub; setup(() => { configService = ({ @@ -65,7 +65,7 @@ suite('pytest test execution adapter', () => { execFactory = typeMoq.Mock.ofType(); // added - utilsStartTestIdsNamedPipeStub = sinon.stub(util, 'startTestIdsNamedPipe'); + utilsWriteTestIdsFileStub = sinon.stub(util, 'writeTestIdsFile'); debugLauncher = typeMoq.Mock.ofType(); execFactory .setup((x) => x.createActivatedEnvironment(typeMoq.It.isAny())) @@ -95,7 +95,7 @@ suite('pytest test execution adapter', () => { teardown(() => { sinon.restore(); }); - test('startTestIdServer called with correct testIds', async () => { + test('WriteTestIdsFile called with correct testIds', async () => { const deferred2 = createDeferred(); const deferred3 = createDeferred(); execFactory = typeMoq.Mock.ofType(); @@ -105,7 +105,7 @@ suite('pytest test execution adapter', () => { deferred2.resolve(); return Promise.resolve(execService.object); }); - utilsStartTestIdsNamedPipeStub.callsFake(() => { + utilsWriteTestIdsFileStub.callsFake(() => { deferred3.resolve(); return Promise.resolve({ name: 'mockName', @@ -129,7 +129,7 @@ suite('pytest test execution adapter', () => { mockProc.trigger('close'); // assert - sinon.assert.calledWithExactly(utilsStartTestIdsNamedPipeStub, testIds); + sinon.assert.calledWithExactly(utilsWriteTestIdsFileStub, testIds); }); test('pytest execution called with correct args', async () => { const deferred2 = createDeferred(); @@ -141,7 +141,7 @@ suite('pytest test execution adapter', () => { deferred2.resolve(); return Promise.resolve(execService.object); }); - utilsStartTestIdsNamedPipeStub.callsFake(() => { + utilsWriteTestIdsFileStub.callsFake(() => { deferred3.resolve(); return Promise.resolve('testIdPipe-mockName'); }); @@ -192,7 +192,7 @@ suite('pytest test execution adapter', () => { deferred2.resolve(); return Promise.resolve(execService.object); }); - utilsStartTestIdsNamedPipeStub.callsFake(() => { + utilsWriteTestIdsFileStub.callsFake(() => { deferred3.resolve(); return Promise.resolve('testIdPipe-mockName'); }); @@ -243,7 +243,7 @@ suite('pytest test execution adapter', () => { test('Debug launched correctly for pytest', async () => { const deferred3 = createDeferred(); const deferredEOT = createDeferred(); - utilsStartTestIdsNamedPipeStub.callsFake(() => { + utilsWriteTestIdsFileStub.callsFake(() => { deferred3.resolve(); return Promise.resolve('testIdPipe-mockName'); }); diff --git a/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts b/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts index 6881524af20c..0cb64a8c75cd 100644 --- a/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts +++ b/src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts @@ -33,7 +33,7 @@ suite('Unittest test execution adapter', () => { (global as any).EXTENSION_ROOT_DIR = EXTENSION_ROOT_DIR; let myTestPath: string; let mockProc: MockChildProcess; - let utilsStartTestIdsNamedPipeStub: sinon.SinonStub; + let utilsWriteTestIdsFileStub: sinon.SinonStub; let utilsStartRunResultNamedPipeStub: sinon.SinonStub; setup(() => { configService = ({ @@ -65,7 +65,7 @@ suite('Unittest test execution adapter', () => { execFactory = typeMoq.Mock.ofType(); // added - utilsStartTestIdsNamedPipeStub = sinon.stub(util, 'startTestIdsNamedPipe'); + utilsWriteTestIdsFileStub = sinon.stub(util, 'writeTestIdsFile'); debugLauncher = typeMoq.Mock.ofType(); execFactory .setup((x) => x.createActivatedEnvironment(typeMoq.It.isAny())) @@ -105,7 +105,7 @@ suite('Unittest test execution adapter', () => { deferred2.resolve(); return Promise.resolve(execService.object); }); - utilsStartTestIdsNamedPipeStub.callsFake(() => { + utilsWriteTestIdsFileStub.callsFake(() => { deferred3.resolve(); return Promise.resolve({ name: 'mockName', @@ -129,7 +129,7 @@ suite('Unittest test execution adapter', () => { mockProc.trigger('close'); // assert - sinon.assert.calledWithExactly(utilsStartTestIdsNamedPipeStub, testIds); + sinon.assert.calledWithExactly(utilsWriteTestIdsFileStub, testIds); }); test('unittest execution called with correct args', async () => { const deferred2 = createDeferred(); @@ -141,7 +141,7 @@ suite('Unittest test execution adapter', () => { deferred2.resolve(); return Promise.resolve(execService.object); }); - utilsStartTestIdsNamedPipeStub.callsFake(() => { + utilsWriteTestIdsFileStub.callsFake(() => { deferred3.resolve(); return Promise.resolve('testIdPipe-mockName'); }); @@ -191,7 +191,7 @@ suite('Unittest test execution adapter', () => { deferred2.resolve(); return Promise.resolve(execService.object); }); - utilsStartTestIdsNamedPipeStub.callsFake(() => { + utilsWriteTestIdsFileStub.callsFake(() => { deferred3.resolve(); return Promise.resolve('testIdPipe-mockName'); }); @@ -242,7 +242,7 @@ suite('Unittest test execution adapter', () => { test('Debug launched correctly for unittest', async () => { const deferred3 = createDeferred(); const deferredEOT = createDeferred(); - utilsStartTestIdsNamedPipeStub.callsFake(() => { + utilsWriteTestIdsFileStub.callsFake(() => { deferred3.resolve(); return Promise.resolve('testIdPipe-mockName'); }); From 9afc101c3604050f3b4d72ea150f486d78f04f84 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Wed, 4 Sep 2024 14:22:10 -0700 Subject: [PATCH 3/7] fix formatting and unittest --- python_files/unittestadapter/execution.py | 2 -- python_files/vscode_pytest/run_pytest_script.py | 5 ----- .../testCancellationRunAdapters.unit.test.ts | 8 ++++---- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index 1c2b87b42683..434931e1d5b0 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -3,7 +3,6 @@ import atexit import enum -import json import os import pathlib import sys @@ -24,7 +23,6 @@ from django_handler import django_execution_runner # noqa: E402 -from testing_tools import process_json_util, socket_manager # noqa: E402 from unittestadapter.pvsc_utils import ( # noqa: E402 EOTPayloadDict, ExecutionPayloadDict, diff --git a/python_files/vscode_pytest/run_pytest_script.py b/python_files/vscode_pytest/run_pytest_script.py index 0054a8b2298b..107ce190e342 100644 --- a/python_files/vscode_pytest/run_pytest_script.py +++ b/python_files/vscode_pytest/run_pytest_script.py @@ -1,6 +1,5 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -import json import os import pathlib import sys @@ -17,10 +16,6 @@ script_dir = pathlib.Path(__file__).parent.parent sys.path.append(os.fspath(script_dir)) sys.path.append(os.fspath(script_dir / "lib" / "python")) -from testing_tools import ( # noqa: E402 - process_json_util, - socket_manager, -) def run_without_test_ids(args): diff --git a/src/test/testing/testController/testCancellationRunAdapters.unit.test.ts b/src/test/testing/testController/testCancellationRunAdapters.unit.test.ts index fc120ef1f526..563735e6a467 100644 --- a/src/test/testing/testController/testCancellationRunAdapters.unit.test.ts +++ b/src/test/testing/testController/testCancellationRunAdapters.unit.test.ts @@ -28,7 +28,7 @@ suite('Execution Flow Run Adapters', () => { (global as any).EXTENSION_ROOT_DIR = EXTENSION_ROOT_DIR; let myTestPath: string; let mockProc: MockChildProcess; - let utilsStartTestIdsNamedPipe: sinon.SinonStub; + let utilsWriteTestIdsFileStub: sinon.SinonStub; let utilsStartRunResultNamedPipe: sinon.SinonStub; let serverDisposeStub: sinon.SinonStub; @@ -47,7 +47,7 @@ suite('Execution Flow Run Adapters', () => { execFactoryStub = typeMoq.Mock.ofType(); // mocked utility functions that handle pipe related functions - utilsStartTestIdsNamedPipe = sinon.stub(util, 'startTestIdsNamedPipe'); + utilsWriteTestIdsFileStub = sinon.stub(util, 'writeTestIdsFile'); utilsStartRunResultNamedPipe = sinon.stub(util, 'startRunResultNamedPipe'); serverDisposeStub = sinon.stub(); @@ -87,7 +87,7 @@ suite('Execution Flow Run Adapters', () => { // test ids named pipe mocking const deferredStartTestIdsNamedPipe = createDeferred(); - utilsStartTestIdsNamedPipe.callsFake(() => { + utilsWriteTestIdsFileStub.callsFake(() => { deferredStartTestIdsNamedPipe.resolve(); return Promise.resolve('named-pipe'); }); @@ -165,7 +165,7 @@ suite('Execution Flow Run Adapters', () => { // test ids named pipe mocking const deferredStartTestIdsNamedPipe = createDeferred(); - utilsStartTestIdsNamedPipe.callsFake(() => { + utilsWriteTestIdsFileStub.callsFake(() => { deferredStartTestIdsNamedPipe.resolve(); return Promise.resolve('named-pipe'); }); From a63d23f09bd30462c6cc1f464466e5a7a7e29165 Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Thu, 5 Sep 2024 08:27:21 -0700 Subject: [PATCH 4/7] Apply suggestions from code review Co-authored-by: Karthik Nadig --- python_files/unittestadapter/execution.py | 2 +- python_files/vscode_pytest/run_pytest_script.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index 434931e1d5b0..18ca8e217529 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -317,7 +317,7 @@ def send_run_data(raw_data, test_run_pipe): except Exception as e: # No test ids received from buffer, return error payload - cwd = os.path.abspath(start_dir) # noqa: PTH100 + cwd = pathlib.Path(start_dir).absolute(start_dir) # noqa: PTH100 status = TestExecutionStatus.error payload: ExecutionPayloadDict = { "cwd": cwd, diff --git a/python_files/vscode_pytest/run_pytest_script.py b/python_files/vscode_pytest/run_pytest_script.py index 107ce190e342..99dd0f8eba2e 100644 --- a/python_files/vscode_pytest/run_pytest_script.py +++ b/python_files/vscode_pytest/run_pytest_script.py @@ -18,8 +18,7 @@ sys.path.append(os.fspath(script_dir / "lib" / "python")) -def run_without_test_ids(args): - print("Running pytest with no test ids as args. Args being used: ", args) +def run_pytest(args): arg_array = ["-p", "vscode_pytest", *args] pytest.main(arg_array) From 97657993cfa961845a619932bb29cbe1ec68749a Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 5 Sep 2024 10:09:23 -0700 Subject: [PATCH 5/7] fixing comments on PR --- python_files/unittestadapter/execution.py | 14 +++++++++----- python_files/vscode_pytest/run_pytest_script.py | 4 ++-- src/client/testing/testController/common/utils.ts | 3 +++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index 18ca8e217529..e5d25dcc17e9 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -267,8 +267,15 @@ def run_tests( return payload +def execute_eot_and_cleanup(): + eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} + send_post_request(eot_payload, test_run_pipe) + if __socket: + __socket.close() + + __socket = None -atexit.register(lambda: __socket.close() if __socket else None) +atexit.register(execute_eot_and_cleanup) def send_run_data(raw_data, test_run_pipe): @@ -326,8 +333,7 @@ def send_run_data(raw_data, test_run_pipe): "result": None, } send_post_request(payload, test_run_pipe) - eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} - send_post_request(eot_payload, test_run_pipe) + # If no error occurred, we will have test ids to run. if manage_py_path := os.environ.get("MANAGE_PY_PATH"): @@ -346,5 +352,3 @@ def send_run_data(raw_data, test_run_pipe): failfast, locals_, ) - eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True} - send_post_request(eot_payload, test_run_pipe) diff --git a/python_files/vscode_pytest/run_pytest_script.py b/python_files/vscode_pytest/run_pytest_script.py index 99dd0f8eba2e..79e039607c4b 100644 --- a/python_files/vscode_pytest/run_pytest_script.py +++ b/python_files/vscode_pytest/run_pytest_script.py @@ -49,7 +49,7 @@ def run_pytest(args): pytest.main(arg_array) except Exception as e: print("Error[vscode-pytest]: unable to read testIds from temp file" + str(e)) - run_without_test_ids(args) + run_pytest(args) else: print("Error[vscode-pytest]: RUN_TEST_IDS_PIPE env var is not set.") - run_without_test_ids(args) + run_pytest(args) diff --git a/src/client/testing/testController/common/utils.ts b/src/client/testing/testController/common/utils.ts index 4b724355b218..cf82a2ebd1c1 100644 --- a/src/client/testing/testController/common/utils.ts +++ b/src/client/testing/testController/common/utils.ts @@ -214,6 +214,9 @@ export async function writeTestIdsFile(testIds: string[]): Promise { } catch (error) { // Handle the error when accessing the temp directory traceError('Error accessing temp directory:', error, ' Attempt to use extension root dir instead'); + // Make new temp directory in extension root dir + const tempDir = path.join(EXTENSION_ROOT_DIR, '.temp'); + await fs.promises.mkdir(tempDir, { recursive: true }); tempFileName = path.join(EXTENSION_ROOT_DIR, '.temp', tempName); traceLog('New temp file:', tempFileName); } From 6df8d26c21fb2fd28accfc546087f9002bf050c2 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 5 Sep 2024 10:13:52 -0700 Subject: [PATCH 6/7] linting --- python_files/unittestadapter/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index e5d25dcc17e9..200cb6f96660 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -324,7 +324,7 @@ def send_run_data(raw_data, test_run_pipe): except Exception as e: # No test ids received from buffer, return error payload - cwd = pathlib.Path(start_dir).absolute(start_dir) # noqa: PTH100 + cwd = pathlib.Path(start_dir).absolute(start_dir) status = TestExecutionStatus.error payload: ExecutionPayloadDict = { "cwd": cwd, From e6aa596d2f93c310bb8e70b59f5358875cb78ecc Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 5 Sep 2024 10:57:55 -0700 Subject: [PATCH 7/7] ruff check --- python_files/tests/unittestadapter/conftest.py | 6 ------ python_files/unittestadapter/execution.py | 9 ++++----- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/python_files/tests/unittestadapter/conftest.py b/python_files/tests/unittestadapter/conftest.py index 19af85d1e095..5b7f7a925cc0 100644 --- a/python_files/tests/unittestadapter/conftest.py +++ b/python_files/tests/unittestadapter/conftest.py @@ -1,8 +1,2 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. - -import sys - -# Ignore the contents of this folder for Python 2 tests. -if sys.version_info[0] < 3: - collect_ignore_glob = ["*.py"] diff --git a/python_files/unittestadapter/execution.py b/python_files/unittestadapter/execution.py index 200cb6f96660..8e4b2462e681 100644 --- a/python_files/unittestadapter/execution.py +++ b/python_files/unittestadapter/execution.py @@ -324,17 +324,16 @@ def send_run_data(raw_data, test_run_pipe): except Exception as e: # No test ids received from buffer, return error payload - cwd = pathlib.Path(start_dir).absolute(start_dir) - status = TestExecutionStatus.error + cwd = pathlib.Path(start_dir).absolute() + status: TestExecutionStatus = TestExecutionStatus.error payload: ExecutionPayloadDict = { - "cwd": cwd, + "cwd": str(cwd), "status": status, - "error": "No test ids read from temp file," + str(e), "result": None, + "error": "No test ids read from temp file," + str(e), } send_post_request(payload, test_run_pipe) - # If no error occurred, we will have test ids to run. if manage_py_path := os.environ.get("MANAGE_PY_PATH"): print("MANAGE_PY_PATH env var set, running Django test suite.")