|
1 | 1 | import shutil
|
| 2 | +import sys |
| 3 | +import time |
2 | 4 |
|
3 | 5 | import pytest
|
4 | 6 |
|
@@ -90,3 +92,89 @@ async def test_stdio_client_nonexistent_command():
|
90 | 92 | or "not found" in error_message.lower()
|
91 | 93 | or "cannot find the file" in error_message.lower() # Windows error message
|
92 | 94 | )
|
| 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