27 os.write(procutil.{stream}.fileno(), b'[written bbb\\n]') |
28 os.write(procutil.{stream}.fileno(), b'[written bbb\\n]') |
28 ''' |
29 ''' |
29 UNBUFFERED = b'aaa[written aaa]bbb\n[written bbb\\n]' |
30 UNBUFFERED = b'aaa[written aaa]bbb\n[written bbb\\n]' |
30 LINE_BUFFERED = b'[written aaa]aaabbb\n[written bbb\\n]' |
31 LINE_BUFFERED = b'[written aaa]aaabbb\n[written bbb\\n]' |
31 FULLY_BUFFERED = b'[written aaa][written bbb\\n]aaabbb\n' |
32 FULLY_BUFFERED = b'[written aaa][written bbb\\n]aaabbb\n' |
|
33 |
|
34 |
|
35 TEST_LARGE_WRITE_CHILD_SCRIPT = r''' |
|
36 import signal |
|
37 import sys |
|
38 |
|
39 from mercurial import dispatch |
|
40 from mercurial.utils import procutil |
|
41 |
|
42 signal.signal(signal.SIGINT, lambda *x: None) |
|
43 dispatch.initstdio() |
|
44 procutil.{stream}.write(b'x' * 1048576) |
|
45 ''' |
32 |
46 |
33 |
47 |
34 @contextlib.contextmanager |
48 @contextlib.contextmanager |
35 def _closing(fds): |
49 def _closing(fds): |
36 try: |
50 try: |
99 stdout=child_stream if stream == 'stdout' else None, |
113 stdout=child_stream if stream == 'stdout' else None, |
100 stderr=child_stream if stream == 'stderr' else None, |
114 stderr=child_stream if stream == 'stderr' else None, |
101 ) |
115 ) |
102 try: |
116 try: |
103 os.close(child_stream) |
117 os.close(child_stream) |
104 check_output(stream_receiver) |
118 check_output(stream_receiver, proc) |
105 except: # re-raises |
119 except: # re-raises |
106 proc.terminate() |
120 proc.terminate() |
107 raise |
121 raise |
108 finally: |
122 finally: |
109 retcode = proc.wait() |
123 retcode = proc.wait() |
110 self.assertEqual(retcode, 0) |
124 self.assertEqual(retcode, 0) |
111 |
125 |
112 def _test_buffering( |
126 def _test_buffering( |
113 self, stream, rwpair_generator, expected_output, python_args=[] |
127 self, stream, rwpair_generator, expected_output, python_args=[] |
114 ): |
128 ): |
115 def check_output(stream_receiver): |
129 def check_output(stream_receiver, proc): |
116 self.assertEqual(_readall(stream_receiver, 1024), expected_output) |
130 self.assertEqual(_readall(stream_receiver, 1024), expected_output) |
117 |
131 |
118 self._test( |
132 self._test( |
119 TEST_BUFFERING_CHILD_SCRIPT.format(stream=stream), |
133 TEST_BUFFERING_CHILD_SCRIPT.format(stream=stream), |
120 stream, |
134 stream, |
141 # to use unbuffered stdout, but it's hard to do that. |
155 # to use unbuffered stdout, but it's hard to do that. |
142 test_buffering_stdout_ptys_unbuffered = unittest.expectedFailure( |
156 test_buffering_stdout_ptys_unbuffered = unittest.expectedFailure( |
143 test_buffering_stdout_ptys_unbuffered |
157 test_buffering_stdout_ptys_unbuffered |
144 ) |
158 ) |
145 |
159 |
|
160 def _test_large_write(self, stream, rwpair_generator, python_args=[]): |
|
161 if not pycompat.ispy3 and pycompat.isdarwin: |
|
162 # Python 2 doesn't always retry on EINTR, but the libc might retry. |
|
163 # So far, it was observed only on macOS that EINTR is raised at the |
|
164 # Python level. As Python 2 support will be dropped soon-ish, we |
|
165 # won't attempt to fix it. |
|
166 raise unittest.SkipTest("raises EINTR on macOS") |
|
167 |
|
168 def check_output(stream_receiver, proc): |
|
169 if not pycompat.iswindows: |
|
170 # On Unix, we can provoke a partial write() by interrupting it |
|
171 # by a signal handler as soon as a bit of data was written. |
|
172 # We test that write() is called until all data is written. |
|
173 buf = [os.read(stream_receiver, 1)] |
|
174 proc.send_signal(signal.SIGINT) |
|
175 else: |
|
176 # On Windows, there doesn't seem to be a way to cause partial |
|
177 # writes. |
|
178 buf = [] |
|
179 self.assertEqual( |
|
180 _readall(stream_receiver, 131072, buf), b'x' * 1048576 |
|
181 ) |
|
182 |
|
183 self._test( |
|
184 TEST_LARGE_WRITE_CHILD_SCRIPT.format(stream=stream), |
|
185 stream, |
|
186 rwpair_generator, |
|
187 check_output, |
|
188 python_args, |
|
189 ) |
|
190 |
|
191 def test_large_write_stdout_pipes(self): |
|
192 self._test_large_write('stdout', _pipes) |
|
193 |
|
194 def test_large_write_stdout_ptys(self): |
|
195 self._test_large_write('stdout', _ptys) |
|
196 |
|
197 def test_large_write_stdout_pipes_unbuffered(self): |
|
198 self._test_large_write('stdout', _pipes, python_args=['-u']) |
|
199 |
|
200 def test_large_write_stdout_ptys_unbuffered(self): |
|
201 self._test_large_write('stdout', _ptys, python_args=['-u']) |
|
202 |
|
203 def test_large_write_stderr_pipes(self): |
|
204 self._test_large_write('stderr', _pipes) |
|
205 |
|
206 def test_large_write_stderr_ptys(self): |
|
207 self._test_large_write('stderr', _ptys) |
|
208 |
|
209 def test_large_write_stderr_pipes_unbuffered(self): |
|
210 self._test_large_write('stderr', _pipes, python_args=['-u']) |
|
211 |
|
212 def test_large_write_stderr_ptys_unbuffered(self): |
|
213 self._test_large_write('stderr', _ptys, python_args=['-u']) |
|
214 |
146 |
215 |
147 if __name__ == '__main__': |
216 if __name__ == '__main__': |
148 import silenttestrunner |
217 import silenttestrunner |
149 |
218 |
150 silenttestrunner.main(__name__) |
219 silenttestrunner.main(__name__) |