12
12
import threading
13
13
import unittest
14
14
import unittest .mock
15
- from contextlib import contextmanager , redirect_stdout , ExitStack
15
+ from contextlib import closing , contextmanager , redirect_stdout , ExitStack
16
16
from pathlib import Path
17
17
from test .support import is_wasi , os_helper , requires_subprocess , SHORT_TIMEOUT
18
18
from test .support .os_helper import temp_dir , TESTFN , unlink
@@ -79,52 +79,14 @@ def get_output(self) -> List[dict]:
79
79
return results
80
80
81
81
82
- class MockDebuggerSocket :
83
- """Mock file-like simulating a connection to a _RemotePdb instance"""
84
-
85
- def __init__ (self , incoming ):
86
- self .incoming = iter (incoming )
87
- self .outgoing = []
88
- self .buffered = bytearray ()
89
-
90
- def write (self , data : bytes ) -> None :
91
- """Simulate write to socket."""
92
- self .buffered += data
93
-
94
- def flush (self ) -> None :
95
- """Ensure each line is valid JSON."""
96
- lines = self .buffered .splitlines (keepends = True )
97
- self .buffered .clear ()
98
- for line in lines :
99
- assert line .endswith (b"\n " )
100
- self .outgoing .append (json .loads (line ))
101
-
102
- def readline (self ) -> bytes :
103
- """Read a line from the prepared input queue."""
104
- # Anything written must be flushed before trying to read,
105
- # since the read will be dependent upon the last write.
106
- assert not self .buffered
107
- try :
108
- item = next (self .incoming )
109
- if not isinstance (item , bytes ):
110
- item = json .dumps (item ).encode ()
111
- return item + b"\n "
112
- except StopIteration :
113
- return b""
114
-
115
- def close (self ) -> None :
116
- """No-op close implementation."""
117
- pass
118
-
119
-
120
82
class PdbClientTestCase (unittest .TestCase ):
121
83
"""Tests for the _PdbClient class."""
122
84
123
85
def do_test (
124
86
self ,
125
87
* ,
126
88
incoming ,
127
- simulate_failure = None ,
89
+ simulate_send_failure = False ,
128
90
expected_outgoing = None ,
129
91
expected_completions = None ,
130
92
expected_exception = None ,
@@ -142,16 +104,6 @@ def do_test(
142
104
expected_state .setdefault ("write_failed" , False )
143
105
messages = [m for source , m in incoming if source == "server" ]
144
106
prompts = [m ["prompt" ] for source , m in incoming if source == "user" ]
145
- sockfile = MockDebuggerSocket (messages )
146
- stdout = io .StringIO ()
147
-
148
- if simulate_failure :
149
- sockfile .write = unittest .mock .Mock ()
150
- sockfile .flush = unittest .mock .Mock ()
151
- if simulate_failure == "write" :
152
- sockfile .write .side_effect = OSError ("write failed" )
153
- elif simulate_failure == "flush" :
154
- sockfile .flush .side_effect = OSError ("flush failed" )
155
107
156
108
input_iter = (m for source , m in incoming if source == "user" )
157
109
completions = []
@@ -181,14 +133,33 @@ def mock_input(prompt):
181
133
return reply
182
134
183
135
with ExitStack () as stack :
136
+ client_sock , server_sock = socket .socketpair ()
137
+ stack .enter_context (closing (client_sock ))
138
+ stack .enter_context (closing (server_sock ))
139
+
140
+ server_sock = unittest .mock .Mock (wraps = server_sock )
141
+
142
+ client_sock .sendall (
143
+ b"" .join (
144
+ (m if isinstance (m , bytes ) else json .dumps (m ).encode ()) + b"\n "
145
+ for m in messages
146
+ )
147
+ )
148
+ client_sock .shutdown (socket .SHUT_WR )
149
+
150
+ if simulate_send_failure :
151
+ client_sock .shutdown (socket .SHUT_RD )
152
+
153
+ stdout = io .StringIO ()
154
+
184
155
input_mock = stack .enter_context (
185
156
unittest .mock .patch ("pdb.input" , side_effect = mock_input )
186
157
)
187
158
stack .enter_context (redirect_stdout (stdout ))
188
159
189
160
client = _PdbClient (
190
161
pid = 0 ,
191
- sockfile = sockfile ,
162
+ server_socket = server_sock ,
192
163
interrupt_script = "/a/b.py" ,
193
164
)
194
165
@@ -199,13 +170,12 @@ def mock_input(prompt):
199
170
200
171
client .cmdloop ()
201
172
202
- actual_outgoing = sockfile .outgoing
203
- if simulate_failure :
204
- actual_outgoing += [
205
- json .loads (msg .args [0 ]) for msg in sockfile .write .mock_calls
206
- ]
173
+ sent_msgs = [msg .args [0 ] for msg in server_sock .sendall .mock_calls ]
174
+ for msg in sent_msgs :
175
+ assert msg .endswith (b"\n " )
176
+ actual_outgoing = [json .loads (msg ) for msg in sent_msgs ]
207
177
208
- self .assertEqual (sockfile . outgoing , expected_outgoing )
178
+ self .assertEqual (actual_outgoing , expected_outgoing )
209
179
self .assertEqual (completions , expected_completions )
210
180
if expected_stdout_substring and not expected_stdout :
211
181
self .assertIn (expected_stdout_substring , stdout .getvalue ())
@@ -478,20 +448,7 @@ def test_write_failing(self):
478
448
self .do_test (
479
449
incoming = incoming ,
480
450
expected_outgoing = [{"signal" : "INT" }],
481
- simulate_failure = "write" ,
482
- expected_state = {"write_failed" : True },
483
- )
484
-
485
- def test_flush_failing (self ):
486
- """Test terminating if flush fails due to a half closed socket."""
487
- incoming = [
488
- ("server" , {"prompt" : "(Pdb) " , "state" : "pdb" }),
489
- ("user" , {"prompt" : "(Pdb) " , "input" : KeyboardInterrupt ()}),
490
- ]
491
- self .do_test (
492
- incoming = incoming ,
493
- expected_outgoing = [{"signal" : "INT" }],
494
- simulate_failure = "flush" ,
451
+ simulate_send_failure = True ,
495
452
expected_state = {"write_failed" : True },
496
453
)
497
454
@@ -622,42 +579,7 @@ def test_write_failure_during_completion(self):
622
579
},
623
580
{"reply" : "xyz" },
624
581
],
625
- simulate_failure = "write" ,
626
- expected_completions = [],
627
- expected_state = {"state" : "interact" , "write_failed" : True },
628
- )
629
-
630
- def test_flush_failure_during_completion (self ):
631
- """Test failing to flush to the socket to request tab completions."""
632
- incoming = [
633
- ("server" , {"prompt" : ">>> " , "state" : "interact" }),
634
- (
635
- "user" ,
636
- {
637
- "prompt" : ">>> " ,
638
- "completion_request" : {
639
- "line" : "xy" ,
640
- "begidx" : 0 ,
641
- "endidx" : 2 ,
642
- },
643
- "input" : "xyz" ,
644
- },
645
- ),
646
- ]
647
- self .do_test (
648
- incoming = incoming ,
649
- expected_outgoing = [
650
- {
651
- "complete" : {
652
- "text" : "xy" ,
653
- "line" : "xy" ,
654
- "begidx" : 0 ,
655
- "endidx" : 2 ,
656
- }
657
- },
658
- {"reply" : "xyz" },
659
- ],
660
- simulate_failure = "flush" ,
582
+ simulate_send_failure = True ,
661
583
expected_completions = [],
662
584
expected_state = {"state" : "interact" , "write_failed" : True },
663
585
)
0 commit comments