tests/test-stdio.py
author Raphaël Gomès <rgomes@octobus.net>
Thu, 08 Jul 2021 15:55:04 +0200
changeset 47559 53a864a60281
parent 47500 23f5ed6dbcb1
child 48870 df56e6bd37f6
permissions -rwxr-xr-x
encoding: move case-related utils up This will be useful for the next commit that needs this code earlier. Differential Revision: https://phab.mercurial-scm.org/D11024
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
47500
23f5ed6dbcb1 run-tests: stop writing a `python3` symlink pointing to python2
Pierre-Yves David <pierre-yves.david@octobus.net>
parents: 45830
diff changeset
     1
#!/usr/bin/env python
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
     2
"""
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
     3
Tests the buffering behavior of stdio streams in `mercurial.utils.procutil`.
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
     4
"""
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
     5
from __future__ import absolute_import
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
     6
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
     7
import contextlib
45068
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
     8
import errno
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
     9
import os
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    10
import signal
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    11
import subprocess
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    12
import sys
45097
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
    13
import tempfile
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    14
import unittest
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    15
45148
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    16
from mercurial import pycompat, util
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    17
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    18
45147
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    19
if pycompat.ispy3:
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    20
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    21
    def set_noninheritable(fd):
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    22
        # On Python 3, file descriptors are non-inheritable by default.
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    23
        pass
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    24
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    25
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    26
else:
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    27
    if pycompat.iswindows:
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    28
        # unused
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    29
        set_noninheritable = None
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    30
    else:
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    31
        import fcntl
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    32
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    33
        def set_noninheritable(fd):
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    34
            old = fcntl.fcntl(fd, fcntl.F_GETFD)
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    35
            fcntl.fcntl(fd, fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    36
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
    37
45078
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
    38
TEST_BUFFERING_CHILD_SCRIPT = r'''
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    39
import os
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    40
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    41
from mercurial import dispatch
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    42
from mercurial.utils import procutil
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    43
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    44
dispatch.initstdio()
45044
359884685eab tests: generalize common test case code in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45043
diff changeset
    45
procutil.{stream}.write(b'aaa')
359884685eab tests: generalize common test case code in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45043
diff changeset
    46
os.write(procutil.{stream}.fileno(), b'[written aaa]')
359884685eab tests: generalize common test case code in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45043
diff changeset
    47
procutil.{stream}.write(b'bbb\n')
359884685eab tests: generalize common test case code in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45043
diff changeset
    48
os.write(procutil.{stream}.fileno(), b'[written bbb\\n]')
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    49
'''
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    50
UNBUFFERED = b'aaa[written aaa]bbb\n[written bbb\\n]'
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    51
LINE_BUFFERED = b'[written aaa]aaabbb\n[written bbb\\n]'
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    52
FULLY_BUFFERED = b'[written aaa][written bbb\\n]aaabbb\n'
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    53
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
    54
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    55
TEST_LARGE_WRITE_CHILD_SCRIPT = r'''
45104
eb26a9cf7821 procutil: avoid use of deprecated tempfile.mktemp()
Manuel Jacob <me@manueljacob.de>
parents: 45097
diff changeset
    56
import os
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    57
import signal
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    58
import sys
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    59
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    60
from mercurial import dispatch
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    61
from mercurial.utils import procutil
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    62
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    63
signal.signal(signal.SIGINT, lambda *x: None)
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    64
dispatch.initstdio()
45097
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
    65
write_result = procutil.{stream}.write(b'x' * 1048576)
45104
eb26a9cf7821 procutil: avoid use of deprecated tempfile.mktemp()
Manuel Jacob <me@manueljacob.de>
parents: 45097
diff changeset
    66
with os.fdopen(
eb26a9cf7821 procutil: avoid use of deprecated tempfile.mktemp()
Manuel Jacob <me@manueljacob.de>
parents: 45097
diff changeset
    67
    os.open({write_result_fn!r}, os.O_WRONLY | getattr(os, 'O_TEMPORARY', 0)),
eb26a9cf7821 procutil: avoid use of deprecated tempfile.mktemp()
Manuel Jacob <me@manueljacob.de>
parents: 45097
diff changeset
    68
    'w',
eb26a9cf7821 procutil: avoid use of deprecated tempfile.mktemp()
Manuel Jacob <me@manueljacob.de>
parents: 45097
diff changeset
    69
) as write_result_f:
45097
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
    70
    write_result_f.write(str(write_result))
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    71
'''
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    72
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
    73
45148
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    74
TEST_BROKEN_PIPE_CHILD_SCRIPT = r'''
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    75
import os
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    76
import pickle
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    77
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    78
from mercurial import dispatch
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    79
from mercurial.utils import procutil
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    80
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    81
dispatch.initstdio()
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    82
procutil.stdin.read(1)  # wait until parent process closed pipe
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    83
try:
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    84
    procutil.{stream}.write(b'test')
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    85
    procutil.{stream}.flush()
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    86
except EnvironmentError as e:
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    87
    with os.fdopen(
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    88
        os.open(
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    89
            {err_fn!r},
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    90
            os.O_WRONLY
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    91
            | getattr(os, 'O_BINARY', 0)
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    92
            | getattr(os, 'O_TEMPORARY', 0),
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    93
        ),
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    94
        'wb',
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    95
    ) as err_f:
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    96
        pickle.dump(e, err_f)
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    97
# Exit early to suppress further broken pipe errors at interpreter shutdown.
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    98
os._exit(0)
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
    99
'''
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   100
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   101
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   102
@contextlib.contextmanager
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   103
def _closing(fds):
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   104
    try:
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   105
        yield
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   106
    finally:
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   107
        for fd in fds:
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   108
            try:
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   109
                os.close(fd)
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   110
            except EnvironmentError:
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   111
                pass
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   112
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   113
45147
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   114
# In the following, we set the FDs non-inheritable mainly to make it possible
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   115
# for tests to close the receiving end of the pipe / PTYs.
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   116
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   117
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   118
@contextlib.contextmanager
45096
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   119
def _devnull():
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   120
    devnull = os.open(os.devnull, os.O_WRONLY)
45147
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   121
    # We don't have a receiving end, so it's not worth the effort on Python 2
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   122
    # on Windows to make the FD non-inheritable.
45096
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   123
    with _closing([devnull]):
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   124
        yield (None, devnull)
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   125
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   126
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   127
@contextlib.contextmanager
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   128
def _pipes():
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   129
    rwpair = os.pipe()
45147
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   130
    # Pipes are already non-inheritable on Windows.
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   131
    if not pycompat.iswindows:
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   132
        set_noninheritable(rwpair[0])
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   133
        set_noninheritable(rwpair[1])
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   134
    with _closing(rwpair):
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   135
        yield rwpair
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   136
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   137
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   138
@contextlib.contextmanager
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   139
def _ptys():
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   140
    if pycompat.iswindows:
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   141
        raise unittest.SkipTest("PTYs are not supported on Windows")
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   142
    import pty
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   143
    import tty
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   144
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   145
    rwpair = pty.openpty()
45147
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   146
    set_noninheritable(rwpair[0])
c2c862b9b544 tests: make pipes / PTYs non-inheritable in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45104
diff changeset
   147
    set_noninheritable(rwpair[1])
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   148
    with _closing(rwpair):
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   149
        tty.setraw(rwpair[0])
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   150
        yield rwpair
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   151
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   152
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   153
def _readall(fd, buffer_size, initial_buf=None):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   154
    buf = initial_buf or []
45068
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   155
    while True:
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   156
        try:
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   157
            s = os.read(fd, buffer_size)
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   158
        except OSError as e:
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   159
            if e.errno == errno.EIO:
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   160
                # If the child-facing PTY got closed, reading from the
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   161
                # parent-facing PTY raises EIO.
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   162
                break
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   163
            raise
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   164
        if not s:
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   165
            break
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   166
        buf.append(s)
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   167
    return b''.join(buf)
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   168
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   169
45044
359884685eab tests: generalize common test case code in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45043
diff changeset
   170
class TestStdio(unittest.TestCase):
45078
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   171
    def _test(
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   172
        self,
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   173
        child_script,
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   174
        stream,
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   175
        rwpair_generator,
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   176
        check_output,
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   177
        python_args=[],
45097
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   178
        post_child_check=None,
45148
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   179
        stdin_generator=None,
45078
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   180
    ):
45044
359884685eab tests: generalize common test case code in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45043
diff changeset
   181
        assert stream in ('stdout', 'stderr')
45148
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   182
        if stdin_generator is None:
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   183
            stdin_generator = open(os.devnull, 'rb')
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   184
        with rwpair_generator() as (
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   185
            stream_receiver,
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   186
            child_stream,
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   187
        ), stdin_generator as child_stdin:
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   188
            proc = subprocess.Popen(
45078
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   189
                [sys.executable] + python_args + ['-c', child_script],
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   190
                stdin=child_stdin,
45044
359884685eab tests: generalize common test case code in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45043
diff changeset
   191
                stdout=child_stream if stream == 'stdout' else None,
359884685eab tests: generalize common test case code in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45043
diff changeset
   192
                stderr=child_stream if stream == 'stderr' else None,
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   193
            )
45068
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   194
            try:
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   195
                os.close(child_stream)
45096
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   196
                if stream_receiver is not None:
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   197
                    check_output(stream_receiver, proc)
45069
9172fd511999 tests: terminate subprocess in test-stdio.py in case of exception
Manuel Jacob <me@manueljacob.de>
parents: 45068
diff changeset
   198
            except:  # re-raises
9172fd511999 tests: terminate subprocess in test-stdio.py in case of exception
Manuel Jacob <me@manueljacob.de>
parents: 45068
diff changeset
   199
                proc.terminate()
9172fd511999 tests: terminate subprocess in test-stdio.py in case of exception
Manuel Jacob <me@manueljacob.de>
parents: 45068
diff changeset
   200
                raise
45068
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   201
            finally:
8cd18aba5e6c tests: proof test-stdio.py against buffer fill-up
Manuel Jacob <me@manueljacob.de>
parents: 45045
diff changeset
   202
                retcode = proc.wait()
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   203
            self.assertEqual(retcode, 0)
45097
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   204
            if post_child_check is not None:
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   205
                post_child_check()
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   206
45078
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   207
    def _test_buffering(
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   208
        self, stream, rwpair_generator, expected_output, python_args=[]
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   209
    ):
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   210
        def check_output(stream_receiver, proc):
45078
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   211
            self.assertEqual(_readall(stream_receiver, 1024), expected_output)
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   212
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   213
        self._test(
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   214
            TEST_BUFFERING_CHILD_SCRIPT.format(stream=stream),
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   215
            stream,
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   216
            rwpair_generator,
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   217
            check_output,
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   218
            python_args,
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   219
        )
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   220
45096
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   221
    def test_buffering_stdout_devnull(self):
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   222
        self._test_buffering('stdout', _devnull, None)
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   223
45070
bc05c13e246f tests: make names in test-stdio.py more distinctive
Manuel Jacob <me@manueljacob.de>
parents: 45069
diff changeset
   224
    def test_buffering_stdout_pipes(self):
45078
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   225
        self._test_buffering('stdout', _pipes, FULLY_BUFFERED)
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   226
45070
bc05c13e246f tests: make names in test-stdio.py more distinctive
Manuel Jacob <me@manueljacob.de>
parents: 45069
diff changeset
   227
    def test_buffering_stdout_ptys(self):
45078
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   228
        self._test_buffering('stdout', _ptys, LINE_BUFFERED)
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   229
45096
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   230
    def test_buffering_stdout_devnull_unbuffered(self):
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   231
        self._test_buffering('stdout', _devnull, None, python_args=['-u'])
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   232
45070
bc05c13e246f tests: make names in test-stdio.py more distinctive
Manuel Jacob <me@manueljacob.de>
parents: 45069
diff changeset
   233
    def test_buffering_stdout_pipes_unbuffered(self):
45078
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   234
        self._test_buffering('stdout', _pipes, UNBUFFERED, python_args=['-u'])
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   235
45070
bc05c13e246f tests: make names in test-stdio.py more distinctive
Manuel Jacob <me@manueljacob.de>
parents: 45069
diff changeset
   236
    def test_buffering_stdout_ptys_unbuffered(self):
45078
a59aab6078eb tests: make subprocess handling reusable for different tests in test-stdio.py
Manuel Jacob <me@manueljacob.de>
parents: 45077
diff changeset
   237
        self._test_buffering('stdout', _ptys, UNBUFFERED, python_args=['-u'])
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   238
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   239
    if not pycompat.ispy3 and not pycompat.iswindows:
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   240
        # On Python 2 on non-Windows, we manually open stdout in line-buffered
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   241
        # mode if connected to a TTY. We should check if Python was configured
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   242
        # to use unbuffered stdout, but it's hard to do that.
45070
bc05c13e246f tests: make names in test-stdio.py more distinctive
Manuel Jacob <me@manueljacob.de>
parents: 45069
diff changeset
   243
        test_buffering_stdout_ptys_unbuffered = unittest.expectedFailure(
bc05c13e246f tests: make names in test-stdio.py more distinctive
Manuel Jacob <me@manueljacob.de>
parents: 45069
diff changeset
   244
            test_buffering_stdout_ptys_unbuffered
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   245
        )
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   246
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   247
    def _test_large_write(self, stream, rwpair_generator, python_args=[]):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   248
        if not pycompat.ispy3 and pycompat.isdarwin:
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   249
            # Python 2 doesn't always retry on EINTR, but the libc might retry.
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   250
            # So far, it was observed only on macOS that EINTR is raised at the
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   251
            # Python level. As Python 2 support will be dropped soon-ish, we
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   252
            # won't attempt to fix it.
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   253
            raise unittest.SkipTest("raises EINTR on macOS")
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   254
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   255
        def check_output(stream_receiver, proc):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   256
            if not pycompat.iswindows:
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   257
                # On Unix, we can provoke a partial write() by interrupting it
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   258
                # by a signal handler as soon as a bit of data was written.
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   259
                # We test that write() is called until all data is written.
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   260
                buf = [os.read(stream_receiver, 1)]
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   261
                proc.send_signal(signal.SIGINT)
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   262
            else:
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   263
                # On Windows, there doesn't seem to be a way to cause partial
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   264
                # writes.
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   265
                buf = []
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   266
            self.assertEqual(
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   267
                _readall(stream_receiver, 131072, buf), b'x' * 1048576
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   268
            )
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   269
45097
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   270
        def post_child_check():
45104
eb26a9cf7821 procutil: avoid use of deprecated tempfile.mktemp()
Manuel Jacob <me@manueljacob.de>
parents: 45097
diff changeset
   271
            write_result_str = write_result_f.read()
45097
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   272
            if pycompat.ispy3:
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   273
                # On Python 3, we test that the correct number of bytes is
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   274
                # claimed to have been written.
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   275
                expected_write_result_str = '1048576'
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   276
            else:
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   277
                # On Python 2, we only check that the large write does not
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   278
                # crash.
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   279
                expected_write_result_str = 'None'
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   280
            self.assertEqual(write_result_str, expected_write_result_str)
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   281
45104
eb26a9cf7821 procutil: avoid use of deprecated tempfile.mktemp()
Manuel Jacob <me@manueljacob.de>
parents: 45097
diff changeset
   282
        with tempfile.NamedTemporaryFile('r') as write_result_f:
45097
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   283
            self._test(
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   284
                TEST_LARGE_WRITE_CHILD_SCRIPT.format(
45104
eb26a9cf7821 procutil: avoid use of deprecated tempfile.mktemp()
Manuel Jacob <me@manueljacob.de>
parents: 45097
diff changeset
   285
                    stream=stream, write_result_fn=write_result_f.name
45097
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   286
                ),
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   287
                stream,
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   288
                rwpair_generator,
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   289
                check_output,
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   290
                python_args,
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   291
                post_child_check=post_child_check,
dff208398ede tests: check that procutil.std{out,err}.write() returns correct result
Manuel Jacob <me@manueljacob.de>
parents: 45096
diff changeset
   292
            )
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   293
45096
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   294
    def test_large_write_stdout_devnull(self):
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   295
        self._test_large_write('stdout', _devnull)
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   296
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   297
    def test_large_write_stdout_pipes(self):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   298
        self._test_large_write('stdout', _pipes)
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   299
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   300
    def test_large_write_stdout_ptys(self):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   301
        self._test_large_write('stdout', _ptys)
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   302
45096
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   303
    def test_large_write_stdout_devnull_unbuffered(self):
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   304
        self._test_large_write('stdout', _devnull, python_args=['-u'])
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   305
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   306
    def test_large_write_stdout_pipes_unbuffered(self):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   307
        self._test_large_write('stdout', _pipes, python_args=['-u'])
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   308
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   309
    def test_large_write_stdout_ptys_unbuffered(self):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   310
        self._test_large_write('stdout', _ptys, python_args=['-u'])
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   311
45096
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   312
    def test_large_write_stderr_devnull(self):
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   313
        self._test_large_write('stderr', _devnull)
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   314
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   315
    def test_large_write_stderr_pipes(self):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   316
        self._test_large_write('stderr', _pipes)
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   317
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   318
    def test_large_write_stderr_ptys(self):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   319
        self._test_large_write('stderr', _ptys)
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   320
45096
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   321
    def test_large_write_stderr_devnull_unbuffered(self):
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   322
        self._test_large_write('stderr', _devnull, python_args=['-u'])
e9e452eafbfb tests: add tests for when stdout or stderr is connected to `os.devnull`
Manuel Jacob <me@manueljacob.de>
parents: 45095
diff changeset
   323
45095
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   324
    def test_large_write_stderr_pipes_unbuffered(self):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   325
        self._test_large_write('stderr', _pipes, python_args=['-u'])
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   326
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   327
    def test_large_write_stderr_ptys_unbuffered(self):
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   328
        self._test_large_write('stderr', _ptys, python_args=['-u'])
8e04607023e5 procutil: ensure that procutil.std{out,err}.write() writes all bytes
Manuel Jacob <me@manueljacob.de>
parents: 45078
diff changeset
   329
45148
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   330
    def _test_broken_pipe(self, stream):
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   331
        assert stream in ('stdout', 'stderr')
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   332
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   333
        def check_output(stream_receiver, proc):
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   334
            os.close(stream_receiver)
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   335
            proc.stdin.write(b'x')
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   336
            proc.stdin.close()
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   337
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   338
        def post_child_check():
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   339
            err = util.pickle.load(err_f)
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   340
            self.assertEqual(err.errno, errno.EPIPE)
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   341
            self.assertEqual(err.strerror, "Broken pipe")
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   342
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   343
        with tempfile.NamedTemporaryFile('rb') as err_f:
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   344
            self._test(
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   345
                TEST_BROKEN_PIPE_CHILD_SCRIPT.format(
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   346
                    stream=stream, err_fn=err_f.name
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   347
                ),
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   348
                stream,
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   349
                _pipes,
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   350
                check_output,
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   351
                post_child_check=post_child_check,
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   352
                stdin_generator=util.nullcontextmanager(subprocess.PIPE),
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   353
            )
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   354
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   355
    def test_broken_pipe_stdout(self):
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   356
        self._test_broken_pipe('stdout')
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   357
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   358
    def test_broken_pipe_stderr(self):
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   359
        self._test_broken_pipe('stderr')
a37f290a7124 windows: always work around EINVAL in case of broken pipe for stdout / stderr
Manuel Jacob <me@manueljacob.de>
parents: 45147
diff changeset
   360
45038
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   361
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   362
if __name__ == '__main__':
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   363
    import silenttestrunner
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   364
c7d109c400a4 tests: add tests for buffering behavior of mercurial.utils.procutil.stdout
Manuel Jacob <me@manueljacob.de>
parents:
diff changeset
   365
    silenttestrunner.main(__name__)