Skip to content

Commit 0f6606c

Browse files
Switch to using named pipes for test communication (#22390)
Resolves #22177. Switches to using named pipes as communication between extension and test run subprocesses. --------- Co-authored-by: eleanorjboyd <[email protected]>
1 parent fc49f2b commit 0f6606c

35 files changed

+2020
-2373
lines changed

build/test-requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ torch-tb-profiler
1919

2020
# extension build tests
2121
freezegun
22+
23+
# testing custom pytest plugin require the use of named pipes
24+
namedpipe; platform_system == "Windows"

python_files/testing_tools/process_json_util.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
# Licensed under the MIT License.
33
import io
44
import json
5-
from typing import List
5+
from typing import List, Dict
66

77
CONTENT_LENGTH: str = "Content-Length:"
88

99

10-
def process_rpc_json(data: str) -> List[str]:
10+
def process_rpc_json(data: str) -> Dict[str, List[str]]:
1111
"""Process the JSON data which comes from the server."""
1212
str_stream: io.StringIO = io.StringIO(data)
1313

@@ -22,7 +22,7 @@ def process_rpc_json(data: str) -> List[str]:
2222
if not line or line.isspace():
2323
raise ValueError("Header does not contain Content-Length")
2424

25-
while True:
25+
while True: # keep reading until the number of bytes is the CONTENT_LENGTH
2626
line: str = str_stream.readline()
2727
if not line or line.isspace():
2828
break

python_files/testing_tools/socket_manager.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,76 @@
44
import socket
55
import sys
66

7+
# set the socket before it gets blocked or overwritten by a user tests
8+
_SOCKET = socket.socket
9+
10+
11+
class PipeManager:
12+
def __init__(self, name):
13+
self.name = name
14+
15+
def __enter__(self):
16+
return self.connect()
17+
18+
def __exit__(self, *_):
19+
self.close()
20+
21+
def connect(self):
22+
if sys.platform == "win32":
23+
self._writer = open(self.name, "wt", encoding="utf-8")
24+
# reader created in read method
25+
else:
26+
self._socket = _SOCKET(socket.AF_UNIX, socket.SOCK_STREAM)
27+
self._socket.connect(self.name)
28+
return self
29+
30+
def close(self):
31+
if sys.platform == "win32":
32+
self._writer.close()
33+
else:
34+
# add exception catch
35+
self._socket.close()
36+
37+
def write(self, data: str):
38+
if sys.platform == "win32":
39+
try:
40+
# for windows, is should only use \n\n
41+
request = (
42+
f"""content-length: {len(data)}\ncontent-type: application/json\n\n{data}"""
43+
)
44+
self._writer.write(request)
45+
self._writer.flush()
46+
except Exception as e:
47+
print("error attempting to write to pipe", e)
48+
raise (e)
49+
else:
50+
# must include the carriage-return defined (as \r\n) for unix systems
51+
request = (
52+
f"""content-length: {len(data)}\r\ncontent-type: application/json\r\n\r\n{data}"""
53+
)
54+
self._socket.send(request.encode("utf-8"))
55+
56+
def read(self, bufsize=1024) -> str:
57+
"""Read data from the socket.
58+
59+
Args:
60+
bufsize (int): Number of bytes to read from the socket.
61+
62+
Returns:
63+
data (str): Data received from the socket.
64+
"""
65+
if sys.platform == "win32":
66+
# returns a string automatically from read
67+
if not hasattr(self, "_reader"):
68+
self._reader = open(self.name, "rt", encoding="utf-8")
69+
return self._reader.read(bufsize)
70+
else:
71+
# receive bytes and convert to string
72+
while True:
73+
part: bytes = self._socket.recv(bufsize)
74+
data: str = part.decode("utf-8")
75+
return data
76+
777

878
class SocketManager(object):
979
"""Create a socket and connect to the given address.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
# pytest.ini is specified here so the root directory of the tests is kept at .data instead of referencing
5+
# the parent python_files/pyproject.toml for test_discovery.py and test_execution.py for pytest-adapter tests.

0 commit comments

Comments
 (0)