forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathtest_python_server.py
More file actions
162 lines (123 loc) · 5.78 KB
/
test_python_server.py
File metadata and controls
162 lines (123 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
"""Tests for python_server.py, specifically EOF handling to prevent infinite loops."""
import io
from unittest import mock
import pytest
class TestGetHeaders:
"""Tests for the get_headers function."""
def test_get_headers_normal(self):
"""Test get_headers with valid headers."""
# Arrange: Import the module
import python_server
# Create a mock stdin with valid headers
mock_input = b"Content-Length: 100\r\nContent-Type: application/json\r\n\r\n"
mock_stdin = io.BytesIO(mock_input)
# Act
with mock.patch.object(python_server, "STDIN", mock.Mock(buffer=mock_stdin)):
headers = python_server.get_headers()
# Assert
assert headers == {"Content-Length": "100", "Content-Type": "application/json"}
def test_get_headers_eof_raises_error(self):
"""Test that get_headers raises EOFError when stdin is closed (EOF)."""
# Arrange: Import the module
import python_server
# Create a mock stdin that returns empty bytes (EOF)
mock_stdin = io.BytesIO(b"")
# Act & Assert
with mock.patch.object(python_server, "STDIN", mock.Mock(buffer=mock_stdin)), pytest.raises(
EOFError, match="EOF reached while reading headers"
):
python_server.get_headers()
def test_get_headers_eof_mid_headers_raises_error(self):
"""Test that get_headers raises EOFError when EOF occurs mid-headers."""
# Arrange: Import the module
import python_server
# Create a mock stdin with partial headers then EOF
mock_input = b"Content-Length: 100\r\n" # No terminating empty line
mock_stdin = io.BytesIO(mock_input)
# Act & Assert
with mock.patch.object(python_server, "STDIN", mock.Mock(buffer=mock_stdin)), pytest.raises(
EOFError, match="EOF reached while reading headers"
):
python_server.get_headers()
def test_get_headers_empty_line_terminates(self):
"""Test that an empty line (not EOF) properly terminates header reading."""
# Arrange: Import the module
import python_server
# Create a mock stdin with headers followed by empty line
mock_input = b"Content-Length: 50\r\n\r\nsome body content"
mock_stdin = io.BytesIO(mock_input)
# Act
with mock.patch.object(python_server, "STDIN", mock.Mock(buffer=mock_stdin)):
headers = python_server.get_headers()
# Assert
assert headers == {"Content-Length": "50"}
class TestEOFHandling:
"""Tests for EOF handling in various functions that use get_headers."""
def test_custom_input_exits_on_eof(self):
"""Test that custom_input exits gracefully on EOF."""
# Arrange: Import the module
import python_server
# Create a mock stdin that returns empty bytes (EOF)
mock_stdin = io.BytesIO(b"")
mock_stdout = io.BytesIO()
# Act & Assert
with mock.patch.object(
python_server, "STDIN", mock.Mock(buffer=mock_stdin)
), mock.patch.object(python_server, "STDOUT", mock.Mock(buffer=mock_stdout)), pytest.raises(
SystemExit
) as exc_info:
python_server.custom_input("prompt> ")
# Should exit with code 0 (graceful exit)
assert exc_info.value.code == 0
def test_handle_response_exits_on_eof(self):
"""Test that handle_response exits gracefully on EOF."""
# Arrange: Import the module
import python_server
# Create a mock stdin that returns empty bytes (EOF)
mock_stdin = io.BytesIO(b"")
# Act & Assert
with mock.patch.object(python_server, "STDIN", mock.Mock(buffer=mock_stdin)), pytest.raises(
SystemExit
) as exc_info:
python_server.handle_response("test-request-id")
# Should exit with code 0 (graceful exit)
assert exc_info.value.code == 0
class TestMainLoopEOFHandling:
"""Tests that simulate the main loop EOF scenario."""
def test_main_loop_exits_on_eof(self):
"""Test that the main loop pattern exits gracefully on EOF.
This test verifies the fix for GitHub issue #25620 where the server
would spin at 100% CPU instead of exiting when VS Code closes.
"""
# Arrange: Import the module
import python_server
# Create a mock stdin that returns empty bytes (EOF)
mock_stdin = io.BytesIO(b"")
# Simulate what happens in the main loop
with mock.patch.object(python_server, "STDIN", mock.Mock(buffer=mock_stdin)):
try:
python_server.get_headers()
# If we get here without raising EOFError, the fix isn't working
pytest.fail("Expected EOFError to be raised on EOF")
except EOFError:
# This is the expected behavior - the fix is working
pass
def test_readline_eof_vs_empty_line(self):
"""Test that we correctly distinguish between EOF and empty line.
EOF: readline() returns b'' (empty bytes)
Empty line: readline() returns b'\\r\\n' or b'\\n' (newline bytes)
"""
# Test EOF case
eof_stream = io.BytesIO(b"")
result = eof_stream.readline()
assert result == b"", "EOF should return empty bytes"
# Test empty line case
empty_line_stream = io.BytesIO(b"\r\n")
result = empty_line_stream.readline()
assert result == b"\r\n", "Empty line should return newline bytes"
# Test empty line with just newline
empty_line_stream2 = io.BytesIO(b"\n")
result = empty_line_stream2.readline()
assert result == b"\n", "Empty line should return newline bytes"