From 3417d77fd28dda95ba0048fe7604c979e14d5c0e Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 4 Aug 2023 09:22:59 -0700 Subject: [PATCH 1/6] add std out to subprocess to stop overflow --- src/client/testing/testController/common/server.ts | 11 ++++++++++- .../testController/pytest/pytestDiscoveryAdapter.ts | 10 +++++++++- .../testController/pytest/pytestExecutionAdapter.ts | 10 ++++++++++ src/test/mocks/helper.ts | 12 ++++++++++++ src/test/mocks/mockChildProcess.ts | 7 ++++--- 5 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 src/test/mocks/helper.ts diff --git a/src/client/testing/testController/common/server.ts b/src/client/testing/testController/common/server.ts index 564bd82f2ef6..661290bf4988 100644 --- a/src/client/testing/testController/common/server.ts +++ b/src/client/testing/testController/common/server.ts @@ -209,7 +209,16 @@ export class PythonTestServer implements ITestServer, Disposable { runInstance?.token.onCancellationRequested(() => { result?.proc?.kill(); }); - result?.proc?.on('close', () => { + + // Take all output from the subprocess and add it to the test output channel. This will be the pytest output. + // Displays output to user and ensure the subprocess doesn't run into buffer overflow. + result?.proc?.stdout?.on('data', (data) => { + spawnOptions?.outputChannel?.append(data); + }); + result?.proc?.stderr?.on('data', (data) => { + spawnOptions?.outputChannel?.append(data); + }); + result?.proc?.on('exit', () => { traceLog('Exec server closed.', uuid); deferred.resolve({ stdout: '', stderr: '' }); callback?.(); diff --git a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts index 810fae0fa11c..04de62c14322 100644 --- a/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts +++ b/src/client/testing/testController/pytest/pytestDiscoveryAdapter.ts @@ -83,7 +83,15 @@ export class PytestTestDiscoveryAdapter implements ITestDiscoveryAdapter { const execArgs = ['-m', 'pytest', '-p', 'vscode_pytest', '--collect-only'].concat(pytestArgs); const result = execService?.execObservable(execArgs, spawnOptions); - result?.proc?.on('close', () => { + // Take all output from the subprocess and add it to the test output channel. This will be the pytest output. + // Displays output to user and ensure the subprocess doesn't run into buffer overflow. + result?.proc?.stdout?.on('data', (data) => { + spawnOptions.outputChannel?.append(data); + }); + result?.proc?.stderr?.on('data', (data) => { + spawnOptions.outputChannel?.append(data); + }); + result?.proc?.on('exit', () => { deferredExec.resolve({ stdout: '', stderr: '' }); this.testServer.deleteUUID(uuid); deferred.resolve(); diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index b05fa21fc046..f8269017f8b4 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -47,6 +47,7 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { if (runInstance) { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } + disposedDataReceived.dispose(); }); const dispose = function (testServer: ITestServer) { testServer.deleteUUID(uuid); @@ -156,6 +157,15 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { result?.proc?.kill(); }); + // Take all output from the subprocess and add it to the test output channel. This will be the pytest output. + // Displays output to user and ensure the subprocess doesn't run into buffer overflow. + result?.proc?.stdout?.on('data', (data) => { + this.outputChannel?.append(data); + }); + result?.proc?.stderr?.on('data', (data) => { + this.outputChannel?.append(data); + }); + result?.proc?.on('close', () => { deferredExec.resolve({ stdout: '', stderr: '' }); this.testServer.deleteUUID(uuid); diff --git a/src/test/mocks/helper.ts b/src/test/mocks/helper.ts new file mode 100644 index 000000000000..f3b5da4f2fb0 --- /dev/null +++ b/src/test/mocks/helper.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { Readable } from 'stream'; + +export class MyReadableStream extends Readable { + _read(size: unknown): void | null { + // custom reading logic here + console.log(size); + this.push(null); // end the stream + } +} diff --git a/src/test/mocks/mockChildProcess.ts b/src/test/mocks/mockChildProcess.ts index c038c0f845ab..8b565fb7686d 100644 --- a/src/test/mocks/mockChildProcess.ts +++ b/src/test/mocks/mockChildProcess.ts @@ -1,8 +1,9 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Serializable, SendHandle, MessageOptions } from 'child_process'; -import { Writable, Readable, Pipe } from 'stream'; import { EventEmitter } from 'node:events'; +import { Writable, Readable, Pipe } from 'stream'; +import { MyReadableStream } from './helper'; export class MockChildProcess extends EventEmitter { constructor(spawnfile: string, spawnargs: string[]) { @@ -10,8 +11,8 @@ export class MockChildProcess extends EventEmitter { this.spawnfile = spawnfile; this.spawnargs = spawnargs; this.stdin = new Writable(); - this.stdout = new Readable(); - this.stderr = null; + this.stdout = new MyReadableStream(); + this.stderr = new MyReadableStream(); this.channel = null; this.stdio = [this.stdin, this.stdout, this.stdout, this.stderr, null]; this.killed = false; From 68d5ef63cf2078e7aebd1b5690b66fe87e7bc0e9 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 4 Aug 2023 09:27:36 -0700 Subject: [PATCH 2/6] remove for this PR --- .../testing/testController/pytest/pytestExecutionAdapter.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts index f8269017f8b4..115d89b38887 100644 --- a/src/client/testing/testController/pytest/pytestExecutionAdapter.ts +++ b/src/client/testing/testController/pytest/pytestExecutionAdapter.ts @@ -47,7 +47,6 @@ export class PytestTestExecutionAdapter implements ITestExecutionAdapter { if (runInstance) { this.resultResolver?.resolveExecution(JSON.parse(e.data), runInstance); } - disposedDataReceived.dispose(); }); const dispose = function (testServer: ITestServer) { testServer.deleteUUID(uuid); From 77c7996d6e4589ef7227265eca69045fd5cd395f Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Fri, 4 Aug 2023 10:10:04 -0700 Subject: [PATCH 3/6] remove log --- src/test/mocks/helper.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/mocks/helper.ts b/src/test/mocks/helper.ts index f3b5da4f2fb0..857ddc55c3fa 100644 --- a/src/test/mocks/helper.ts +++ b/src/test/mocks/helper.ts @@ -4,9 +4,8 @@ import { Readable } from 'stream'; export class MyReadableStream extends Readable { - _read(size: unknown): void | null { + _read(_size: unknown): void | null { // custom reading logic here - console.log(size); this.push(null); // end the stream } } From d3b7c16c0692e971221adf46c18f0acaa0f74a33 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 7 Aug 2023 08:43:04 -0700 Subject: [PATCH 4/6] change readable name --- src/test/mocks/helper.ts | 2 +- src/test/mocks/mockChildProcess.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/mocks/helper.ts b/src/test/mocks/helper.ts index 857ddc55c3fa..24d7a8290b18 100644 --- a/src/test/mocks/helper.ts +++ b/src/test/mocks/helper.ts @@ -3,7 +3,7 @@ import { Readable } from 'stream'; -export class MyReadableStream extends Readable { +export class FakeReadableStream extends Readable { _read(_size: unknown): void | null { // custom reading logic here this.push(null); // end the stream diff --git a/src/test/mocks/mockChildProcess.ts b/src/test/mocks/mockChildProcess.ts index 8b565fb7686d..a46d66d79ca0 100644 --- a/src/test/mocks/mockChildProcess.ts +++ b/src/test/mocks/mockChildProcess.ts @@ -3,7 +3,7 @@ import { Serializable, SendHandle, MessageOptions } from 'child_process'; import { EventEmitter } from 'node:events'; import { Writable, Readable, Pipe } from 'stream'; -import { MyReadableStream } from './helper'; +import { FakeReadableStream } from './helper'; export class MockChildProcess extends EventEmitter { constructor(spawnfile: string, spawnargs: string[]) { @@ -11,8 +11,8 @@ export class MockChildProcess extends EventEmitter { this.spawnfile = spawnfile; this.spawnargs = spawnargs; this.stdin = new Writable(); - this.stdout = new MyReadableStream(); - this.stderr = new MyReadableStream(); + this.stdout = new FakeReadableStream(); + this.stderr = new FakeReadableStream(); this.channel = null; this.stdio = [this.stdin, this.stdout, this.stdout, this.stderr, null]; this.killed = false; From 32cb3ecd6e12e52be61e30b9730517db718d4699 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 10 Aug 2023 15:11:53 -0700 Subject: [PATCH 5/6] fix ruff formatting --- pythonFiles/tests/pytestadapter/helpers.py | 2 +- pythonFiles/tests/pytestadapter/test_execution.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pythonFiles/tests/pytestadapter/helpers.py b/pythonFiles/tests/pytestadapter/helpers.py index c3e01d52170a..47b4f75d6d60 100644 --- a/pythonFiles/tests/pytestadapter/helpers.py +++ b/pythonFiles/tests/pytestadapter/helpers.py @@ -10,7 +10,7 @@ import sys import threading import uuid -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple TEST_DATA_PATH = pathlib.Path(__file__).parent / ".data" from typing_extensions import TypedDict diff --git a/pythonFiles/tests/pytestadapter/test_execution.py b/pythonFiles/tests/pytestadapter/test_execution.py index ffc84955bf54..5de97f1bcfdb 100644 --- a/pythonFiles/tests/pytestadapter/test_execution.py +++ b/pythonFiles/tests/pytestadapter/test_execution.py @@ -179,6 +179,6 @@ def test_pytest_execution(test_ids, expected_const): or actual_result_dict[key]["outcome"] == "error" ): actual_result_dict[key]["message"] = "ERROR MESSAGE" - if actual_result_dict[key]["traceback"] != None: + if actual_result_dict[key]["traceback"] is not None: actual_result_dict[key]["traceback"] = "TRACEBACK" assert actual_result_dict == expected_const From 094b2b066d3ce1674c679a84df5f4fe3594f5276 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Thu, 10 Aug 2023 16:01:19 -0700 Subject: [PATCH 6/6] remove json --- pythonFiles/tests/pytestadapter/test_execution.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pythonFiles/tests/pytestadapter/test_execution.py b/pythonFiles/tests/pytestadapter/test_execution.py index 30f1a7e439d9..07354b01709b 100644 --- a/pythonFiles/tests/pytestadapter/test_execution.py +++ b/pythonFiles/tests/pytestadapter/test_execution.py @@ -1,6 +1,5 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -import json import os import shutil