Skip to content

Commit c75fd38

Browse files
Add regression tests for stdio cleanup hanging
NOTE: These tests FAIL without the changes introduced in #559. These tests verify that stdio_client completes cleanup within reasonable time for both slow-terminating and fast-exiting processes, preventing the hanging issues reported in #559.
1 parent 4b4637a commit c75fd38

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

tests/client/test_stdio.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import shutil
2+
import sys
3+
import time
24

35
import pytest
46

@@ -90,3 +92,89 @@ async def test_stdio_client_nonexistent_command():
9092
or "not found" in error_message.lower()
9193
or "cannot find the file" in error_message.lower() # Windows error message
9294
)
95+
96+
97+
@pytest.mark.anyio
98+
async def test_stdio_client_universal_timeout():
99+
"""
100+
Test that stdio_client completes cleanup within reasonable time
101+
even when connected to processes that exit slowly.
102+
"""
103+
104+
# Use a simple sleep command that's available on all platforms
105+
# This simulates a process that takes time to terminate
106+
if sys.platform == "win32":
107+
# Windows: use ping with timeout to simulate a running process
108+
server_params = StdioServerParameters(
109+
command="ping",
110+
args=["127.0.0.1", "-n", "10"], # Ping 10 times, takes ~10 seconds
111+
)
112+
else:
113+
# Unix: use sleep command
114+
server_params = StdioServerParameters(
115+
command="sleep",
116+
args=["10"], # Sleep for 10 seconds
117+
)
118+
119+
start_time = time.time()
120+
121+
try:
122+
async with stdio_client(server_params) as (read_stream, write_stream):
123+
# Immediately exit - this triggers cleanup while process is still running
124+
pass
125+
126+
end_time = time.time()
127+
elapsed = end_time - start_time
128+
129+
# Key assertion: Should complete quickly due to timeout mechanism
130+
assert elapsed < 5.0, (
131+
f"stdio_client cleanup took {elapsed:.1f} seconds, expected < 5.0 seconds. "
132+
f"This suggests the timeout mechanism may not be working properly."
133+
)
134+
135+
except Exception as e:
136+
end_time = time.time()
137+
elapsed = end_time - start_time
138+
print(f"❌ Test failed after {elapsed:.1f} seconds: {e}")
139+
raise
140+
141+
142+
@pytest.mark.anyio
143+
async def test_stdio_client_immediate_completion():
144+
"""
145+
Test that stdio_client doesn't introduce unnecessary delays
146+
when processes exit normally and quickly.
147+
"""
148+
149+
# Use a command that exits immediately
150+
if sys.platform == "win32":
151+
server_params = StdioServerParameters(
152+
command="cmd",
153+
args=["/c", "echo", "hello"], # Windows: echo and exit
154+
)
155+
else:
156+
server_params = StdioServerParameters(
157+
command="echo",
158+
args=["hello"], # Unix: echo and exit
159+
)
160+
161+
start_time = time.time()
162+
163+
try:
164+
async with stdio_client(server_params) as (read_stream, write_stream):
165+
pass
166+
167+
end_time = time.time()
168+
elapsed = end_time - start_time
169+
170+
# Should complete very quickly when process exits normally
171+
assert elapsed < 2.0, (
172+
f"stdio_client took {elapsed:.1f} seconds for fast-exiting process, "
173+
f"expected < 2.0 seconds. Timeout mechanism may be introducing delays."
174+
)
175+
176+
except Exception as e:
177+
end_time = time.time()
178+
elapsed = end_time - start_time
179+
print(f"❌ Test failed after {elapsed:.1f} seconds: {e}")
180+
raise

0 commit comments

Comments
 (0)