66 with os.fdopen( |
66 with os.fdopen( |
67 os.open({write_result_fn!r}, os.O_WRONLY | getattr(os, 'O_TEMPORARY', 0)), |
67 os.open({write_result_fn!r}, os.O_WRONLY | getattr(os, 'O_TEMPORARY', 0)), |
68 'w', |
68 'w', |
69 ) as write_result_f: |
69 ) as write_result_f: |
70 write_result_f.write(str(write_result)) |
70 write_result_f.write(str(write_result)) |
|
71 ''' |
|
72 |
|
73 |
|
74 TEST_BROKEN_PIPE_CHILD_SCRIPT = r''' |
|
75 import os |
|
76 import pickle |
|
77 |
|
78 from mercurial import dispatch |
|
79 from mercurial.utils import procutil |
|
80 |
|
81 dispatch.initstdio() |
|
82 procutil.stdin.read(1) # wait until parent process closed pipe |
|
83 try: |
|
84 procutil.{stream}.write(b'test') |
|
85 procutil.{stream}.flush() |
|
86 except EnvironmentError as e: |
|
87 with os.fdopen( |
|
88 os.open( |
|
89 {err_fn!r}, |
|
90 os.O_WRONLY |
|
91 | getattr(os, 'O_BINARY', 0) |
|
92 | getattr(os, 'O_TEMPORARY', 0), |
|
93 ), |
|
94 'wb', |
|
95 ) as err_f: |
|
96 pickle.dump(e, err_f) |
|
97 # Exit early to suppress further broken pipe errors at interpreter shutdown. |
|
98 os._exit(0) |
71 ''' |
99 ''' |
72 |
100 |
73 |
101 |
74 @contextlib.contextmanager |
102 @contextlib.contextmanager |
75 def _closing(fds): |
103 def _closing(fds): |
146 stream, |
174 stream, |
147 rwpair_generator, |
175 rwpair_generator, |
148 check_output, |
176 check_output, |
149 python_args=[], |
177 python_args=[], |
150 post_child_check=None, |
178 post_child_check=None, |
|
179 stdin_generator=None, |
151 ): |
180 ): |
152 assert stream in ('stdout', 'stderr') |
181 assert stream in ('stdout', 'stderr') |
153 with rwpair_generator() as (stream_receiver, child_stream), open( |
182 if stdin_generator is None: |
154 os.devnull, 'rb' |
183 stdin_generator = open(os.devnull, 'rb') |
155 ) as child_stdin: |
184 with rwpair_generator() as ( |
|
185 stream_receiver, |
|
186 child_stream, |
|
187 ), stdin_generator as child_stdin: |
156 proc = subprocess.Popen( |
188 proc = subprocess.Popen( |
157 [sys.executable] + python_args + ['-c', child_script], |
189 [sys.executable] + python_args + ['-c', child_script], |
158 stdin=child_stdin, |
190 stdin=child_stdin, |
159 stdout=child_stream if stream == 'stdout' else None, |
191 stdout=child_stream if stream == 'stdout' else None, |
160 stderr=child_stream if stream == 'stderr' else None, |
192 stderr=child_stream if stream == 'stderr' else None, |
293 self._test_large_write('stderr', _pipes, python_args=['-u']) |
325 self._test_large_write('stderr', _pipes, python_args=['-u']) |
294 |
326 |
295 def test_large_write_stderr_ptys_unbuffered(self): |
327 def test_large_write_stderr_ptys_unbuffered(self): |
296 self._test_large_write('stderr', _ptys, python_args=['-u']) |
328 self._test_large_write('stderr', _ptys, python_args=['-u']) |
297 |
329 |
|
330 def _test_broken_pipe(self, stream): |
|
331 assert stream in ('stdout', 'stderr') |
|
332 |
|
333 def check_output(stream_receiver, proc): |
|
334 os.close(stream_receiver) |
|
335 proc.stdin.write(b'x') |
|
336 proc.stdin.close() |
|
337 |
|
338 def post_child_check(): |
|
339 err = util.pickle.load(err_f) |
|
340 self.assertEqual(err.errno, errno.EPIPE) |
|
341 self.assertEqual(err.strerror, "Broken pipe") |
|
342 |
|
343 with tempfile.NamedTemporaryFile('rb') as err_f: |
|
344 self._test( |
|
345 TEST_BROKEN_PIPE_CHILD_SCRIPT.format( |
|
346 stream=stream, err_fn=err_f.name |
|
347 ), |
|
348 stream, |
|
349 _pipes, |
|
350 check_output, |
|
351 post_child_check=post_child_check, |
|
352 stdin_generator=util.nullcontextmanager(subprocess.PIPE), |
|
353 ) |
|
354 |
|
355 def test_broken_pipe_stdout(self): |
|
356 self._test_broken_pipe('stdout') |
|
357 |
|
358 def test_broken_pipe_stderr(self): |
|
359 self._test_broken_pipe('stderr') |
|
360 |
298 |
361 |
299 if __name__ == '__main__': |
362 if __name__ == '__main__': |
300 import silenttestrunner |
363 import silenttestrunner |
301 |
364 |
302 silenttestrunner.main(__name__) |
365 silenttestrunner.main(__name__) |