Skip to content

Commit 7513198

Browse files
eleanorjboydkarthiknadig
authored andcommitted
remove EOT from testing communication (#24220)
remove the need for the EOT in the communication design between the extension and the python subprocesses produced to run testing.
1 parent a73eb9c commit 7513198

File tree

18 files changed

+64
-283
lines changed

18 files changed

+64
-283
lines changed

python_files/tests/pytestadapter/helpers.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ def process_data_received(data: str) -> List[Dict[str, Any]]:
7171
7272
This function also:
7373
- Checks that the jsonrpc value is 2.0
74-
- Checks that the last JSON message contains the `eot` token.
7574
"""
7675
json_messages = []
7776
remaining = data
@@ -85,18 +84,14 @@ def process_data_received(data: str) -> List[Dict[str, Any]]:
8584
else:
8685
json_messages.append(json_data["params"])
8786

88-
last_json = json_messages.pop(-1)
89-
if "eot" not in last_json:
90-
raise ValueError("Last JSON messages does not contain 'eot' as its last payload.")
91-
return json_messages # return the list of json messages, only the params part without the EOT token
87+
return json_messages # return the list of json messages
9288

9389

9490
def parse_rpc_message(data: str) -> Tuple[Dict[str, str], str]:
9591
"""Process the JSON data which comes from the server.
9692
9793
A single rpc payload is in the format:
9894
content-length: #LEN# \r\ncontent-type: application/json\r\n\r\n{"jsonrpc": "2.0", "params": ENTIRE_DATA}
99-
with EOT params: "params": {"command_type": "discovery", "eot": true}
10095
10196
returns:
10297
json_data: A single rpc payload of JSON data from the server.

python_files/unittestadapter/discovery.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
# If I use from utils then there will be an import error in test_discovery.py.
1717
from unittestadapter.pvsc_utils import ( # noqa: E402
1818
DiscoveryPayloadDict,
19-
EOTPayloadDict,
2019
VSCodeUnittestError,
2120
build_test_tree,
2221
parse_unittest_args,
@@ -129,7 +128,6 @@ def discover_tests(
129128
# collect args for Django discovery runner.
130129
args = argv[index + 1 :] or []
131130
django_discovery_runner(manage_py_path, args)
132-
# eot payload sent within Django runner.
133131
except Exception as e:
134132
error_msg = f"Error configuring Django test runner: {e}"
135133
print(error_msg, file=sys.stderr)
@@ -139,6 +137,3 @@ def discover_tests(
139137
payload = discover_tests(start_dir, pattern, top_level_dir)
140138
# Post this discovery payload.
141139
send_post_request(payload, test_run_pipe)
142-
# Post EOT token.
143-
eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True}
144-
send_post_request(eot_payload, test_run_pipe)

python_files/unittestadapter/django_test_runner.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from execution import UnittestTestResult # noqa: E402
1414
from pvsc_utils import ( # noqa: E402
1515
DiscoveryPayloadDict,
16-
EOTPayloadDict,
1716
VSCodeUnittestError,
1817
build_test_tree,
1918
send_post_request,
@@ -64,9 +63,6 @@ def run_tests(self, test_labels, **kwargs):
6463

6564
# Send discovery payload.
6665
send_post_request(payload, test_run_pipe)
67-
# Send EOT token.
68-
eot_payload: EOTPayloadDict = {"command_type": "discovery", "eot": True}
69-
send_post_request(eot_payload, test_run_pipe)
7066
return 0 # Skip actual test execution, return 0 as no tests were run.
7167
except Exception as e:
7268
error_msg = (

python_files/unittestadapter/execution.py

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
from unittestadapter.pvsc_utils import ( # noqa: E402
2727
CoveragePayloadDict,
28-
EOTPayloadDict,
2928
ExecutionPayloadDict,
3029
FileCoverageInfo,
3130
TestExecutionStatus,
@@ -60,21 +59,6 @@ def startTest(self, test: unittest.TestCase): # noqa: N802
6059

6160
def stopTestRun(self): # noqa: N802
6261
super().stopTestRun()
63-
# After stopping the test run, send EOT
64-
test_run_pipe = os.getenv("TEST_RUN_PIPE")
65-
if os.getenv("MANAGE_PY_PATH"):
66-
# only send this if it is a Django run
67-
if not test_run_pipe:
68-
print(
69-
"UNITTEST ERROR: TEST_RUN_PIPE is not set at the time of unittest trying to send data. "
70-
f"TEST_RUN_PIPE = {test_run_pipe}\n",
71-
file=sys.stderr,
72-
)
73-
raise VSCodeUnittestError(
74-
"UNITTEST ERROR: TEST_RUN_PIPE is not set at the time of unittest trying to send data. "
75-
)
76-
eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True}
77-
send_post_request(eot_payload, test_run_pipe)
7862

7963
def addError( # noqa: N802
8064
self,
@@ -269,15 +253,8 @@ def run_tests(
269253
return payload
270254

271255

272-
def execute_eot_and_cleanup():
273-
eot_payload: EOTPayloadDict = {"command_type": "execution", "eot": True}
274-
send_post_request(eot_payload, test_run_pipe)
275-
if __socket:
276-
__socket.close()
277-
278-
279256
__socket = None
280-
atexit.register(execute_eot_and_cleanup)
257+
atexit.register(lambda: __socket.close() if __socket else None)
281258

282259

283260
def send_run_data(raw_data, test_run_pipe):
@@ -361,7 +338,6 @@ def send_run_data(raw_data, test_run_pipe):
361338
print("MANAGE_PY_PATH env var set, running Django test suite.")
362339
args = argv[index + 1 :] or []
363340
django_execution_runner(manage_py_path, test_ids, args)
364-
# the django run subprocesses sends the eot payload.
365341
else:
366342
# Perform regular unittest execution.
367343
payload = run_tests(

python_files/unittestadapter/pvsc_utils.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,6 @@ class ExecutionPayloadDict(TypedDict):
7474
error: NotRequired[str]
7575

7676

77-
class EOTPayloadDict(TypedDict):
78-
"""A dictionary that is used to send a end of transmission post request to the server."""
79-
80-
command_type: Literal["discovery", "execution"]
81-
eot: bool
82-
83-
8477
class FileCoverageInfo(TypedDict):
8578
lines_covered: List[int]
8679
lines_missed: List[int]
@@ -314,7 +307,7 @@ def parse_unittest_args(
314307

315308

316309
def send_post_request(
317-
payload: Union[ExecutionPayloadDict, DiscoveryPayloadDict, EOTPayloadDict, CoveragePayloadDict],
310+
payload: Union[ExecutionPayloadDict, DiscoveryPayloadDict, CoveragePayloadDict],
318311
test_run_pipe: Optional[str],
319312
):
320313
"""

python_files/vscode_pytest/__init__.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -455,10 +455,6 @@ def pytest_sessionfinish(session, exitstatus):
455455
)
456456
send_post_request(payload)
457457

458-
command_type = "discovery" if IS_DISCOVERY else "execution"
459-
payload_eot: EOTPayloadDict = {"command_type": command_type, "eot": True}
460-
send_post_request(payload_eot)
461-
462458

463459
def build_test_tree(session: pytest.Session) -> TestNode:
464460
"""Builds a tree made up of testing nodes from the pytest session.
@@ -782,13 +778,6 @@ class CoveragePayloadDict(Dict):
782778
error: str | None # Currently unused need to check
783779

784780

785-
class EOTPayloadDict(TypedDict):
786-
"""A dictionary that is used to send a end of transmission post request to the server."""
787-
788-
command_type: Literal["discovery", "execution"]
789-
eot: bool
790-
791-
792781
def get_node_path(node: Any) -> pathlib.Path:
793782
"""A function that returns the path of a node given the switch to pathlib.Path.
794783
@@ -873,7 +862,7 @@ def default(self, o):
873862

874863

875864
def send_post_request(
876-
payload: ExecutionPayloadDict | DiscoveryPayloadDict | EOTPayloadDict | CoveragePayloadDict,
865+
payload: ExecutionPayloadDict | DiscoveryPayloadDict | CoveragePayloadDict,
877866
cls_encoder=None,
878867
):
879868
"""

src/client/testing/testController/common/resultResolver.ts

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,7 @@ import {
1717
Range,
1818
} from 'vscode';
1919
import * as util from 'util';
20-
import {
21-
CoveragePayload,
22-
DiscoveredTestPayload,
23-
EOTTestPayload,
24-
ExecutionTestPayload,
25-
ITestResultResolver,
26-
} from './types';
20+
import { CoveragePayload, DiscoveredTestPayload, ExecutionTestPayload, ITestResultResolver } from './types';
2721
import { TestProvider } from '../../types';
2822
import { traceError, traceVerbose } from '../../../logging';
2923
import { Testing } from '../../../common/utils/localize';
@@ -32,7 +26,6 @@ import { sendTelemetryEvent } from '../../../telemetry';
3226
import { EventName } from '../../../telemetry/constants';
3327
import { splitLines } from '../../../common/stringUtils';
3428
import { buildErrorNodeOptions, populateTestTree, splitTestNameWithRegex } from './utils';
35-
import { Deferred } from '../../../common/utils/async';
3629

3730
export class PythonResultResolver implements ITestResultResolver {
3831
testController: TestController;
@@ -58,14 +51,8 @@ export class PythonResultResolver implements ITestResultResolver {
5851
this.vsIdToRunId = new Map<string, string>();
5952
}
6053

61-
public resolveDiscovery(
62-
payload: DiscoveredTestPayload | EOTTestPayload,
63-
deferredTillEOT: Deferred<void>,
64-
token?: CancellationToken,
65-
): void {
66-
if ('eot' in payload && payload.eot === true) {
67-
deferredTillEOT.resolve();
68-
} else if (!payload) {
54+
public resolveDiscovery(payload: DiscoveredTestPayload, token?: CancellationToken): void {
55+
if (!payload) {
6956
// No test data is available
7057
} else {
7158
this._resolveDiscovery(payload as DiscoveredTestPayload, token);
@@ -117,16 +104,8 @@ export class PythonResultResolver implements ITestResultResolver {
117104
});
118105
}
119106

120-
public resolveExecution(
121-
payload: ExecutionTestPayload | EOTTestPayload | CoveragePayload,
122-
runInstance: TestRun,
123-
deferredTillEOT: Deferred<void>,
124-
): void {
125-
if ('eot' in payload && payload.eot === true) {
126-
// eot sent once per connection
127-
traceVerbose('EOT received, resolving deferredTillServerClose');
128-
deferredTillEOT.resolve();
129-
} else if ('coverage' in payload) {
107+
public resolveExecution(payload: ExecutionTestPayload | CoveragePayload, runInstance: TestRun): void {
108+
if ('coverage' in payload) {
130109
// coverage data is sent once per connection
131110
traceVerbose('Coverage data received.');
132111
this._resolveCoverage(payload as CoveragePayload, runInstance);

src/client/testing/testController/common/types.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
import { ITestDebugLauncher, TestDiscoveryOptions } from '../../common/types';
1717
import { IPythonExecutionFactory } from '../../../common/process/types';
1818
import { EnvironmentVariables } from '../../../common/variables/types';
19-
import { Deferred } from '../../../common/utils/async';
2019

2120
export type TestRunInstanceOptions = TestRunOptions & {
2221
exclude?: readonly TestItem[];
@@ -198,16 +197,8 @@ export interface ITestResultResolver {
198197
vsIdToRunId: Map<string, string>;
199198
detailedCoverageMap: Map<string, FileCoverageDetail[]>;
200199

201-
resolveDiscovery(
202-
payload: DiscoveredTestPayload | EOTTestPayload,
203-
deferredTillEOT: Deferred<void>,
204-
token?: CancellationToken,
205-
): void;
206-
resolveExecution(
207-
payload: ExecutionTestPayload | EOTTestPayload | CoveragePayload,
208-
runInstance: TestRun,
209-
deferredTillEOT: Deferred<void>,
210-
): void;
200+
resolveDiscovery(payload: DiscoveredTestPayload, token?: CancellationToken): void;
201+
resolveExecution(payload: ExecutionTestPayload | CoveragePayload, runInstance: TestRun): void;
211202
_resolveDiscovery(payload: DiscoveredTestPayload, token?: CancellationToken): void;
212203
_resolveExecution(payload: ExecutionTestPayload, runInstance: TestRun): void;
213204
_resolveCoverage(payload: CoveragePayload, runInstance: TestRun): void;
@@ -259,11 +250,6 @@ export type DiscoveredTestPayload = {
259250
error?: string[];
260251
};
261252

262-
export type EOTTestPayload = {
263-
commandType: 'discovery' | 'execution';
264-
eot: boolean;
265-
};
266-
267253
export type CoveragePayload = {
268254
coverage: boolean;
269255
cwd: string;

src/client/testing/testController/common/utils.ts

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
DiscoveredTestItem,
1717
DiscoveredTestNode,
1818
DiscoveredTestPayload,
19-
EOTTestPayload,
2019
ExecutionTestPayload,
2120
ITestResultResolver,
2221
} from './types';
@@ -193,7 +192,7 @@ export async function startTestIdsNamedPipe(testIds: string[]): Promise<string>
193192
}
194193

195194
interface ExecutionResultMessage extends Message {
196-
params: ExecutionTestPayload | EOTTestPayload;
195+
params: ExecutionTestPayload;
197196
}
198197

199198
/**
@@ -227,7 +226,7 @@ export async function writeTestIdsFile(testIds: string[]): Promise<string> {
227226
}
228227

229228
export async function startRunResultNamedPipe(
230-
dataReceivedCallback: (payload: ExecutionTestPayload | EOTTestPayload) => void,
229+
dataReceivedCallback: (payload: ExecutionTestPayload) => void,
231230
deferredTillServerClose: Deferred<void>,
232231
cancellationToken?: CancellationToken,
233232
): Promise<{ name: string } & Disposable> {
@@ -259,8 +258,7 @@ export async function startRunResultNamedPipe(
259258
}),
260259
reader.listen((data: Message) => {
261260
traceVerbose(`Test Result named pipe ${pipeName} received data`);
262-
// if EOT, call decrement connection count (callback)
263-
dataReceivedCallback((data as ExecutionResultMessage).params as ExecutionTestPayload | EOTTestPayload);
261+
dataReceivedCallback((data as ExecutionResultMessage).params as ExecutionTestPayload);
264262
}),
265263
);
266264
server.serverOnClosePromise().then(() => {
@@ -275,11 +273,11 @@ export async function startRunResultNamedPipe(
275273
}
276274

277275
interface DiscoveryResultMessage extends Message {
278-
params: DiscoveredTestPayload | EOTTestPayload;
276+
params: DiscoveredTestPayload;
279277
}
280278

281279
export async function startDiscoveryNamedPipe(
282-
callback: (payload: DiscoveredTestPayload | EOTTestPayload) => void,
280+
callback: (payload: DiscoveredTestPayload) => void,
283281
cancellationToken?: CancellationToken,
284282
): Promise<{ name: string } & Disposable> {
285283
traceVerbose('Starting Test Discovery named pipe');
@@ -302,10 +300,9 @@ export async function startDiscoveryNamedPipe(
302300
}),
303301
reader.listen((data: Message) => {
304302
traceVerbose(`Test Discovery named pipe ${pipeName} received data`);
305-
callback((data as DiscoveryResultMessage).params as DiscoveredTestPayload | EOTTestPayload);
303+
callback((data as DiscoveryResultMessage).params as DiscoveredTestPayload);
306304
}),
307305
reader.onClose(() => {
308-
callback(createEOTPayload(true));
309306
traceVerbose(`Test Discovery named pipe ${pipeName} closed`);
310307
dispose();
311308
}),
@@ -475,13 +472,6 @@ export function createDiscoveryErrorPayload(
475472
};
476473
}
477474

478-
export function createEOTPayload(executionBool: boolean): EOTTestPayload {
479-
return {
480-
commandType: executionBool ? 'execution' : 'discovery',
481-
eot: true,
482-
} as EOTTestPayload;
483-
}
484-
485475
/**
486476
* Splits a test name into its parent test name and subtest unique section.
487477
*

0 commit comments

Comments
 (0)