Skip to content

Commit f148139

Browse files
authored
allow pytest tests to handle multiple payloads (#21301)
As part of the switch to allow for dynamic run- the pytest discovery and execution tests are now switched to be take lists of dicts where the dicts are the payloads.
1 parent c213491 commit f148139

File tree

3 files changed

+73
-52
lines changed

3 files changed

+73
-52
lines changed

pythonFiles/tests/pytestadapter/helpers.py

+18-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
3-
4-
import contextlib
53
import io
64
import json
75
import os
86
import pathlib
9-
import random
107
import socket
118
import subprocess
129
import sys
1310
import threading
1411
import uuid
15-
from typing import Any, Dict, List, Optional, Union
12+
from typing import Any, Dict, List, Optional, Tuple
1613

1714
TEST_DATA_PATH = pathlib.Path(__file__).parent / ".data"
1815
from typing_extensions import TypedDict
@@ -70,31 +67,29 @@ def _new_sock() -> socket.socket:
7067
)
7168

7269

73-
def process_rpc_json(data: str) -> Dict[str, Any]:
70+
def process_rpc_message(data: str) -> Tuple[Dict[str, Any], str]:
7471
"""Process the JSON data which comes from the server which runs the pytest discovery."""
7572
str_stream: io.StringIO = io.StringIO(data)
7673

7774
length: int = 0
78-
7975
while True:
8076
line: str = str_stream.readline()
8177
if CONTENT_LENGTH.lower() in line.lower():
8278
length = int(line[len(CONTENT_LENGTH) :])
8379
break
84-
8580
if not line or line.isspace():
8681
raise ValueError("Header does not contain Content-Length")
87-
8882
while True:
8983
line: str = str_stream.readline()
9084
if not line or line.isspace():
9185
break
9286

9387
raw_json: str = str_stream.read(length)
94-
return json.loads(raw_json)
88+
dict_json: Dict[str, Any] = json.loads(raw_json)
89+
return dict_json, str_stream.read()
9590

9691

97-
def runner(args: List[str]) -> Optional[Dict[str, Any]]:
92+
def runner(args: List[str]) -> Optional[List[Dict[str, Any]]]:
9893
"""Run the pytest discovery and return the JSON data from the server."""
9994
process_args: List[str] = [
10095
sys.executable,
@@ -133,7 +128,19 @@ def runner(args: List[str]) -> Optional[Dict[str, Any]]:
133128
t1.join()
134129
t2.join()
135130

136-
return process_rpc_json(result[0]) if result else None
131+
a = process_rpc_json(result[0])
132+
return a if result else None
133+
134+
135+
def process_rpc_json(data: str) -> List[Dict[str, Any]]:
136+
"""Process the JSON data which comes from the server which runs the pytest discovery."""
137+
json_messages = []
138+
remaining = data
139+
while remaining:
140+
json_data, remaining = process_rpc_message(remaining)
141+
json_messages.append(json_data)
142+
143+
return json_messages
137144

138145

139146
def _listen_on_socket(listener: socket.socket, result: List[str]):

pythonFiles/tests/pytestadapter/test_discovery.py

+26-18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Licensed under the MIT License.
33
import os
44
import shutil
5+
from typing import Any, Dict, List, Optional
56

67
import pytest
78

@@ -28,12 +29,15 @@ def test_syntax_error(tmp_path):
2829
temp_dir.mkdir()
2930
p = temp_dir / "error_syntax_discovery.py"
3031
shutil.copyfile(file_path, p)
31-
actual = runner(["--collect-only", os.fspath(p)])
32-
assert actual
33-
assert all(item in actual for item in ("status", "cwd", "error"))
34-
assert actual["status"] == "error"
35-
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
36-
assert len(actual["error"]) == 2
32+
actual_list: Optional[List[Dict[str, Any]]] = runner(
33+
["--collect-only", os.fspath(p)]
34+
)
35+
assert actual_list
36+
for actual in actual_list:
37+
assert all(item in actual for item in ("status", "cwd", "error"))
38+
assert actual["status"] == "error"
39+
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
40+
assert len(actual["error"]) == 2
3741

3842

3943
def test_parameterized_error_collect():
@@ -42,12 +46,15 @@ def test_parameterized_error_collect():
4246
The json should still be returned but the errors list should be present.
4347
"""
4448
file_path_str = "error_parametrize_discovery.py"
45-
actual = runner(["--collect-only", file_path_str])
46-
assert actual
47-
assert all(item in actual for item in ("status", "cwd", "error"))
48-
assert actual["status"] == "error"
49-
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
50-
assert len(actual["error"]) == 2
49+
actual_list: Optional[List[Dict[str, Any]]] = runner(
50+
["--collect-only", file_path_str]
51+
)
52+
assert actual_list
53+
for actual in actual_list:
54+
assert all(item in actual for item in ("status", "cwd", "error"))
55+
assert actual["status"] == "error"
56+
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
57+
assert len(actual["error"]) == 2
5158

5259

5360
@pytest.mark.parametrize(
@@ -98,14 +105,15 @@ def test_pytest_collect(file, expected_const):
98105
file -- a string with the file or folder to run pytest discovery on.
99106
expected_const -- the expected output from running pytest discovery on the file.
100107
"""
101-
actual = runner(
108+
actual_list: Optional[List[Dict[str, Any]]] = runner(
102109
[
103110
"--collect-only",
104111
os.fspath(TEST_DATA_PATH / file),
105112
]
106113
)
107-
assert actual
108-
assert all(item in actual for item in ("status", "cwd", "tests"))
109-
assert actual["status"] == "success"
110-
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
111-
assert actual["tests"] == expected_const
114+
assert actual_list
115+
for actual in actual_list:
116+
assert all(item in actual for item in ("status", "cwd", "tests"))
117+
assert actual["status"] == "success"
118+
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
119+
assert actual["tests"] == expected_const

pythonFiles/tests/pytestadapter/test_execution.py

+29-23
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Licensed under the MIT License.
33
import os
44
import shutil
5+
from typing import Any, Dict, List, Optional
56

67
import pytest
78
from tests.pytestadapter import expected_execution_test_output
@@ -13,7 +14,7 @@ def test_syntax_error_execution(tmp_path):
1314
"""Test pytest execution on a file that has a syntax error.
1415
1516
Copies the contents of a .txt file to a .py file in the temporary directory
16-
to then run pytest exeuction on.
17+
to then run pytest execution on.
1718
1819
The json should still be returned but the errors list should be present.
1920
@@ -28,25 +29,29 @@ def test_syntax_error_execution(tmp_path):
2829
temp_dir.mkdir()
2930
p = temp_dir / "error_syntax_discovery.py"
3031
shutil.copyfile(file_path, p)
31-
actual = runner(["error_syntax_discover.py::test_function"])
32-
assert actual
33-
assert all(item in actual for item in ("status", "cwd", "error"))
34-
assert actual["status"] == "error"
35-
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
36-
assert len(actual["error"]) == 1
32+
actual_list: Optional[List[Dict[str, Any]]] = runner(
33+
["error_syntax_discover.py::test_function"]
34+
)
35+
assert actual_list
36+
for actual in actual_list:
37+
assert all(item in actual for item in ("status", "cwd", "error"))
38+
assert actual["status"] == "error"
39+
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
40+
assert len(actual["error"]) == 1
3741

3842

3943
def test_bad_id_error_execution():
4044
"""Test pytest discovery with a non-existent test_id.
4145
4246
The json should still be returned but the errors list should be present.
4347
"""
44-
actual = runner(["not/a/real::test_id"])
45-
assert actual
46-
assert all(item in actual for item in ("status", "cwd", "error"))
47-
assert actual["status"] == "error"
48-
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
49-
assert len(actual["error"]) == 1
48+
actual_list: Optional[List[Dict[str, Any]]] = runner(["not/a/real::test_id"])
49+
assert actual_list
50+
for actual in actual_list:
51+
assert all(item in actual for item in ("status", "cwd", "error"))
52+
assert actual["status"] == "error"
53+
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
54+
assert len(actual["error"]) == 1
5055

5156

5257
@pytest.mark.parametrize(
@@ -153,13 +158,14 @@ def test_pytest_execution(test_ids, expected_const):
153158
expected_const -- a dictionary of the expected output from running pytest discovery on the files.
154159
"""
155160
args = test_ids
156-
actual = runner(args)
157-
assert actual
158-
assert all(item in actual for item in ("status", "cwd", "result"))
159-
assert actual["status"] == "success"
160-
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
161-
result_data = actual["result"]
162-
for key in result_data:
163-
if result_data[key]["outcome"] == "failure":
164-
result_data[key]["message"] = "ERROR MESSAGE"
165-
assert result_data == expected_const
161+
actual_list: Optional[List[Dict[str, Any]]] = runner(args)
162+
assert actual_list
163+
for actual in actual_list:
164+
assert all(item in actual for item in ("status", "cwd", "result"))
165+
assert actual["status"] == "success"
166+
assert actual["cwd"] == os.fspath(TEST_DATA_PATH)
167+
result_data = actual["result"]
168+
for key in result_data:
169+
if result_data[key]["outcome"] == "failure":
170+
result_data[key]["message"] = "ERROR MESSAGE"
171+
assert result_data == expected_const

0 commit comments

Comments
 (0)