tests/test-stdio.py
changeset 45095 8e04607023e5
parent 45078 a59aab6078eb
child 45096 e9e452eafbfb
equal deleted inserted replaced
45094:b4c35e439ea5 45095:8e04607023e5
     5 from __future__ import absolute_import
     5 from __future__ import absolute_import
     6 
     6 
     7 import contextlib
     7 import contextlib
     8 import errno
     8 import errno
     9 import os
     9 import os
       
    10 import signal
    10 import subprocess
    11 import subprocess
    11 import sys
    12 import sys
    12 import unittest
    13 import unittest
    13 
    14 
    14 from mercurial import pycompat
    15 from mercurial import pycompat
    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:
    61     with _closing(rwpair):
    75     with _closing(rwpair):
    62         tty.setraw(rwpair[0])
    76         tty.setraw(rwpair[0])
    63         yield rwpair
    77         yield rwpair
    64 
    78 
    65 
    79 
    66 def _readall(fd, buffer_size):
    80 def _readall(fd, buffer_size, initial_buf=None):
    67     buf = []
    81     buf = initial_buf or []
    68     while True:
    82     while True:
    69         try:
    83         try:
    70             s = os.read(fd, buffer_size)
    84             s = os.read(fd, buffer_size)
    71         except OSError as e:
    85         except OSError as e:
    72             if e.errno == errno.EIO:
    86             if e.errno == errno.EIO:
    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__)