comparison tests/test-stdio.py @ 45068:8cd18aba5e6c

tests: proof test-stdio.py against buffer fill-up With the previous code, it could in theory happen that the pipe / PTY buffer of the child stdout / stderr fills up and the process never finishes. To prevent that, we read all of the stream before waiting for the end of the process. To ensure that the stream reaches EOF when the child finishes, we must close the parent "copy" of the child stdout / stderr.
author Manuel Jacob <me@manueljacob.de>
date Tue, 07 Jul 2020 11:06:37 +0200
parents 8403cc54bc83
children 9172fd511999
comparison
equal deleted inserted replaced
45067:9a062913bab6 45068:8cd18aba5e6c
3 Tests the buffering behavior of stdio streams in `mercurial.utils.procutil`. 3 Tests the buffering behavior of stdio streams in `mercurial.utils.procutil`.
4 """ 4 """
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 os 9 import os
9 import subprocess 10 import subprocess
10 import sys 11 import sys
11 import unittest 12 import unittest
12 13
60 with _closing(rwpair): 61 with _closing(rwpair):
61 tty.setraw(rwpair[0]) 62 tty.setraw(rwpair[0])
62 yield rwpair 63 yield rwpair
63 64
64 65
66 def _readall(fd, buffer_size):
67 buf = []
68 while True:
69 try:
70 s = os.read(fd, buffer_size)
71 except OSError as e:
72 if e.errno == errno.EIO:
73 # If the child-facing PTY got closed, reading from the
74 # parent-facing PTY raises EIO.
75 break
76 raise
77 if not s:
78 break
79 buf.append(s)
80 return b''.join(buf)
81
82
65 class TestStdio(unittest.TestCase): 83 class TestStdio(unittest.TestCase):
66 def _test(self, stream, rwpair_generator, expected_output, python_args=[]): 84 def _test(self, stream, rwpair_generator, expected_output, python_args=[]):
67 assert stream in ('stdout', 'stderr') 85 assert stream in ('stdout', 'stderr')
68 with rwpair_generator() as (stream_receiver, child_stream), open( 86 with rwpair_generator() as (stream_receiver, child_stream), open(
69 os.devnull, 'rb' 87 os.devnull, 'rb'
74 + ['-c', CHILD_PROCESS.format(stream=stream)], 92 + ['-c', CHILD_PROCESS.format(stream=stream)],
75 stdin=child_stdin, 93 stdin=child_stdin,
76 stdout=child_stream if stream == 'stdout' else None, 94 stdout=child_stream if stream == 'stdout' else None,
77 stderr=child_stream if stream == 'stderr' else None, 95 stderr=child_stream if stream == 'stderr' else None,
78 ) 96 )
79 retcode = proc.wait() 97 try:
98 os.close(child_stream)
99 self.assertEqual(
100 _readall(stream_receiver, 1024), expected_output
101 )
102 finally:
103 retcode = proc.wait()
80 self.assertEqual(retcode, 0) 104 self.assertEqual(retcode, 0)
81 self.assertEqual(os.read(stream_receiver, 1024), expected_output)
82 105
83 def test_stdout_pipes(self): 106 def test_stdout_pipes(self):
84 self._test('stdout', _pipes, FULLY_BUFFERED) 107 self._test('stdout', _pipes, FULLY_BUFFERED)
85 108
86 def test_stdout_ptys(self): 109 def test_stdout_ptys(self):