##// END OF EJS Templates
tests: fix test-sparse-revlog...
tests: fix test-sparse-revlog This one is not covered by the CIbecause I requires an expensive artifact to be cached. So it goes out of think on regular basis (we should fix that…) The test ouput was affected by e706bb41fdb3 as we filtering now happens sooner, removing for the output.

File last commit:

r50192:402f9f0f default
r50521:da636e7a default
Show More
test-stdio.py
316 lines | 9.2 KiB | text/x-python | PythonLexer
run-tests: stop writing a `python3` symlink pointing to python2...
r48294 #!/usr/bin/env python
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 """
Tests the buffering behavior of stdio streams in `mercurial.utils.procutil`.
"""
import contextlib
Manuel Jacob
tests: proof test-stdio.py against buffer fill-up...
r45628 import errno
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 import os
Gregory Szorc
py3: use pickle directly...
r49725 import pickle
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 import signal
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 import subprocess
import sys
Manuel Jacob
tests: check that procutil.std{out,err}.write() returns correct result...
r45657 import tempfile
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 import unittest
Manuel Jacob
windows: always work around EINVAL in case of broken pipe for stdout / stderr...
r45708 from mercurial import pycompat, util
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583
Manuel Jacob
tests: make subprocess handling reusable for different tests in test-stdio.py
r45638 TEST_BUFFERING_CHILD_SCRIPT = r'''
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 import os
from mercurial import dispatch
from mercurial.utils import procutil
dispatch.initstdio()
Manuel Jacob
tests: generalize common test case code in test-stdio.py
r45589 procutil.{stream}.write(b'aaa')
os.write(procutil.{stream}.fileno(), b'[written aaa]')
procutil.{stream}.write(b'bbb\n')
os.write(procutil.{stream}.fileno(), b'[written bbb\\n]')
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 '''
UNBUFFERED = b'aaa[written aaa]bbb\n[written bbb\\n]'
LINE_BUFFERED = b'[written aaa]aaabbb\n[written bbb\\n]'
FULLY_BUFFERED = b'[written aaa][written bbb\\n]aaabbb\n'
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 TEST_LARGE_WRITE_CHILD_SCRIPT = r'''
Manuel Jacob
procutil: avoid use of deprecated tempfile.mktemp()...
r45664 import os
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 import signal
import sys
from mercurial import dispatch
from mercurial.utils import procutil
signal.signal(signal.SIGINT, lambda *x: None)
dispatch.initstdio()
Manuel Jacob
tests: check that procutil.std{out,err}.write() returns correct result...
r45657 write_result = procutil.{stream}.write(b'x' * 1048576)
Manuel Jacob
procutil: avoid use of deprecated tempfile.mktemp()...
r45664 with os.fdopen(
os.open({write_result_fn!r}, os.O_WRONLY | getattr(os, 'O_TEMPORARY', 0)),
'w',
) as write_result_f:
Manuel Jacob
tests: check that procutil.std{out,err}.write() returns correct result...
r45657 write_result_f.write(str(write_result))
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 '''
Manuel Jacob
windows: always work around EINVAL in case of broken pipe for stdout / stderr...
r45708 TEST_BROKEN_PIPE_CHILD_SCRIPT = r'''
import os
import pickle
from mercurial import dispatch
from mercurial.utils import procutil
dispatch.initstdio()
procutil.stdin.read(1) # wait until parent process closed pipe
try:
procutil.{stream}.write(b'test')
procutil.{stream}.flush()
except EnvironmentError as e:
with os.fdopen(
os.open(
{err_fn!r},
os.O_WRONLY
| getattr(os, 'O_BINARY', 0)
| getattr(os, 'O_TEMPORARY', 0),
),
'wb',
) as err_f:
pickle.dump(e, err_f)
# Exit early to suppress further broken pipe errors at interpreter shutdown.
os._exit(0)
'''
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 @contextlib.contextmanager
def _closing(fds):
try:
yield
finally:
for fd in fds:
try:
os.close(fd)
except EnvironmentError:
pass
Manuel Jacob
tests: make pipes / PTYs non-inheritable in test-stdio.py...
r45707 # In the following, we set the FDs non-inheritable mainly to make it possible
# for tests to close the receiving end of the pipe / PTYs.
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 @contextlib.contextmanager
Manuel Jacob
tests: add tests for when stdout or stderr is connected to `os.devnull`...
r45656 def _devnull():
devnull = os.open(os.devnull, os.O_WRONLY)
Manuel Jacob
tests: make pipes / PTYs non-inheritable in test-stdio.py...
r45707 # We don't have a receiving end, so it's not worth the effort on Python 2
# on Windows to make the FD non-inheritable.
Manuel Jacob
tests: add tests for when stdout or stderr is connected to `os.devnull`...
r45656 with _closing([devnull]):
yield (None, devnull)
@contextlib.contextmanager
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 def _pipes():
rwpair = os.pipe()
with _closing(rwpair):
yield rwpair
@contextlib.contextmanager
def _ptys():
if pycompat.iswindows:
raise unittest.SkipTest("PTYs are not supported on Windows")
import pty
import tty
rwpair = pty.openpty()
with _closing(rwpair):
tty.setraw(rwpair[0])
yield rwpair
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 def _readall(fd, buffer_size, initial_buf=None):
buf = initial_buf or []
Manuel Jacob
tests: proof test-stdio.py against buffer fill-up...
r45628 while True:
try:
s = os.read(fd, buffer_size)
except OSError as e:
if e.errno == errno.EIO:
# If the child-facing PTY got closed, reading from the
# parent-facing PTY raises EIO.
break
raise
if not s:
break
buf.append(s)
return b''.join(buf)
Manuel Jacob
tests: generalize common test case code in test-stdio.py
r45589 class TestStdio(unittest.TestCase):
Manuel Jacob
tests: make subprocess handling reusable for different tests in test-stdio.py
r45638 def _test(
self,
child_script,
stream,
rwpair_generator,
check_output,
python_args=[],
Manuel Jacob
tests: check that procutil.std{out,err}.write() returns correct result...
r45657 post_child_check=None,
Manuel Jacob
windows: always work around EINVAL in case of broken pipe for stdout / stderr...
r45708 stdin_generator=None,
Manuel Jacob
tests: make subprocess handling reusable for different tests in test-stdio.py
r45638 ):
Manuel Jacob
tests: generalize common test case code in test-stdio.py
r45589 assert stream in ('stdout', 'stderr')
Manuel Jacob
windows: always work around EINVAL in case of broken pipe for stdout / stderr...
r45708 if stdin_generator is None:
stdin_generator = open(os.devnull, 'rb')
with rwpair_generator() as (
stream_receiver,
child_stream,
), stdin_generator as child_stdin:
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 proc = subprocess.Popen(
Manuel Jacob
tests: make subprocess handling reusable for different tests in test-stdio.py
r45638 [sys.executable] + python_args + ['-c', child_script],
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 stdin=child_stdin,
Manuel Jacob
tests: generalize common test case code in test-stdio.py
r45589 stdout=child_stream if stream == 'stdout' else None,
stderr=child_stream if stream == 'stderr' else None,
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 )
Manuel Jacob
tests: proof test-stdio.py against buffer fill-up...
r45628 try:
os.close(child_stream)
Manuel Jacob
tests: add tests for when stdout or stderr is connected to `os.devnull`...
r45656 if stream_receiver is not None:
check_output(stream_receiver, proc)
Manuel Jacob
tests: terminate subprocess in test-stdio.py in case of exception...
r45629 except: # re-raises
proc.terminate()
raise
Manuel Jacob
tests: proof test-stdio.py against buffer fill-up...
r45628 finally:
retcode = proc.wait()
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583 self.assertEqual(retcode, 0)
Manuel Jacob
tests: check that procutil.std{out,err}.write() returns correct result...
r45657 if post_child_check is not None:
post_child_check()
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583
Manuel Jacob
tests: make subprocess handling reusable for different tests in test-stdio.py
r45638 def _test_buffering(
self, stream, rwpair_generator, expected_output, python_args=[]
):
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 def check_output(stream_receiver, proc):
Manuel Jacob
tests: make subprocess handling reusable for different tests in test-stdio.py
r45638 self.assertEqual(_readall(stream_receiver, 1024), expected_output)
self._test(
TEST_BUFFERING_CHILD_SCRIPT.format(stream=stream),
stream,
rwpair_generator,
check_output,
python_args,
)
Manuel Jacob
tests: add tests for when stdout or stderr is connected to `os.devnull`...
r45656 def test_buffering_stdout_devnull(self):
self._test_buffering('stdout', _devnull, None)
Manuel Jacob
tests: make names in test-stdio.py more distinctive...
r45630 def test_buffering_stdout_pipes(self):
Manuel Jacob
tests: make subprocess handling reusable for different tests in test-stdio.py
r45638 self._test_buffering('stdout', _pipes, FULLY_BUFFERED)
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583
Manuel Jacob
tests: make names in test-stdio.py more distinctive...
r45630 def test_buffering_stdout_ptys(self):
Manuel Jacob
tests: make subprocess handling reusable for different tests in test-stdio.py
r45638 self._test_buffering('stdout', _ptys, LINE_BUFFERED)
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583
Manuel Jacob
tests: add tests for when stdout or stderr is connected to `os.devnull`...
r45656 def test_buffering_stdout_devnull_unbuffered(self):
self._test_buffering('stdout', _devnull, None, python_args=['-u'])
Manuel Jacob
tests: make names in test-stdio.py more distinctive...
r45630 def test_buffering_stdout_pipes_unbuffered(self):
Manuel Jacob
tests: make subprocess handling reusable for different tests in test-stdio.py
r45638 self._test_buffering('stdout', _pipes, UNBUFFERED, python_args=['-u'])
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583
Manuel Jacob
tests: make names in test-stdio.py more distinctive...
r45630 def test_buffering_stdout_ptys_unbuffered(self):
Manuel Jacob
tests: make subprocess handling reusable for different tests in test-stdio.py
r45638 self._test_buffering('stdout', _ptys, UNBUFFERED, python_args=['-u'])
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 def _test_large_write(self, stream, rwpair_generator, python_args=[]):
def check_output(stream_receiver, proc):
if not pycompat.iswindows:
# On Unix, we can provoke a partial write() by interrupting it
# by a signal handler as soon as a bit of data was written.
# We test that write() is called until all data is written.
buf = [os.read(stream_receiver, 1)]
proc.send_signal(signal.SIGINT)
else:
# On Windows, there doesn't seem to be a way to cause partial
# writes.
buf = []
self.assertEqual(
_readall(stream_receiver, 131072, buf), b'x' * 1048576
)
Manuel Jacob
tests: check that procutil.std{out,err}.write() returns correct result...
r45657 def post_child_check():
Manuel Jacob
tests: remove Python 2 special cases in test-stdio.py
r50192 self.assertEqual(write_result_f.read(), '1048576')
Manuel Jacob
tests: check that procutil.std{out,err}.write() returns correct result...
r45657
Manuel Jacob
procutil: avoid use of deprecated tempfile.mktemp()...
r45664 with tempfile.NamedTemporaryFile('r') as write_result_f:
Manuel Jacob
tests: check that procutil.std{out,err}.write() returns correct result...
r45657 self._test(
TEST_LARGE_WRITE_CHILD_SCRIPT.format(
Manuel Jacob
procutil: avoid use of deprecated tempfile.mktemp()...
r45664 stream=stream, write_result_fn=write_result_f.name
Manuel Jacob
tests: check that procutil.std{out,err}.write() returns correct result...
r45657 ),
stream,
rwpair_generator,
check_output,
python_args,
post_child_check=post_child_check,
)
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655
Manuel Jacob
tests: add tests for when stdout or stderr is connected to `os.devnull`...
r45656 def test_large_write_stdout_devnull(self):
self._test_large_write('stdout', _devnull)
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 def test_large_write_stdout_pipes(self):
self._test_large_write('stdout', _pipes)
def test_large_write_stdout_ptys(self):
self._test_large_write('stdout', _ptys)
Manuel Jacob
tests: add tests for when stdout or stderr is connected to `os.devnull`...
r45656 def test_large_write_stdout_devnull_unbuffered(self):
self._test_large_write('stdout', _devnull, python_args=['-u'])
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 def test_large_write_stdout_pipes_unbuffered(self):
self._test_large_write('stdout', _pipes, python_args=['-u'])
def test_large_write_stdout_ptys_unbuffered(self):
self._test_large_write('stdout', _ptys, python_args=['-u'])
Manuel Jacob
tests: add tests for when stdout or stderr is connected to `os.devnull`...
r45656 def test_large_write_stderr_devnull(self):
self._test_large_write('stderr', _devnull)
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 def test_large_write_stderr_pipes(self):
self._test_large_write('stderr', _pipes)
def test_large_write_stderr_ptys(self):
self._test_large_write('stderr', _ptys)
Manuel Jacob
tests: add tests for when stdout or stderr is connected to `os.devnull`...
r45656 def test_large_write_stderr_devnull_unbuffered(self):
self._test_large_write('stderr', _devnull, python_args=['-u'])
Manuel Jacob
procutil: ensure that procutil.std{out,err}.write() writes all bytes...
r45655 def test_large_write_stderr_pipes_unbuffered(self):
self._test_large_write('stderr', _pipes, python_args=['-u'])
def test_large_write_stderr_ptys_unbuffered(self):
self._test_large_write('stderr', _ptys, python_args=['-u'])
Manuel Jacob
windows: always work around EINVAL in case of broken pipe for stdout / stderr...
r45708 def _test_broken_pipe(self, stream):
assert stream in ('stdout', 'stderr')
def check_output(stream_receiver, proc):
os.close(stream_receiver)
proc.stdin.write(b'x')
proc.stdin.close()
def post_child_check():
Gregory Szorc
py3: use pickle directly...
r49725 err = pickle.load(err_f)
Manuel Jacob
windows: always work around EINVAL in case of broken pipe for stdout / stderr...
r45708 self.assertEqual(err.errno, errno.EPIPE)
self.assertEqual(err.strerror, "Broken pipe")
with tempfile.NamedTemporaryFile('rb') as err_f:
self._test(
TEST_BROKEN_PIPE_CHILD_SCRIPT.format(
stream=stream, err_fn=err_f.name
),
stream,
_pipes,
check_output,
post_child_check=post_child_check,
stdin_generator=util.nullcontextmanager(subprocess.PIPE),
)
def test_broken_pipe_stdout(self):
self._test_broken_pipe('stdout')
def test_broken_pipe_stderr(self):
self._test_broken_pipe('stderr')
Manuel Jacob
tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
r45583
if __name__ == '__main__':
import silenttestrunner
silenttestrunner.main(__name__)