171162e3cSSimon Glass# Copyright (c) 2012 The Chromium OS Authors.
271162e3cSSimon Glass# Use of this source code is governed by a BSD-style license that can be
371162e3cSSimon Glass# found in the LICENSE file.
471162e3cSSimon Glass#
571162e3cSSimon Glass# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se>
671162e3cSSimon Glass# Licensed to PSF under a Contributor Agreement.
771162e3cSSimon Glass# See http://www.python.org/2.4/license for licensing details.
871162e3cSSimon Glass
971162e3cSSimon Glass"""Subprocress execution
1071162e3cSSimon Glass
1171162e3cSSimon GlassThis module holds a subclass of subprocess.Popen with our own required
1271162e3cSSimon Glassfeatures, mainly that we get access to the subprocess output while it
1371162e3cSSimon Glassis running rather than just at the end. This makes it easiler to show
1471162e3cSSimon Glassprogress information and filter output in real time.
1571162e3cSSimon Glass"""
1671162e3cSSimon Glass
1771162e3cSSimon Glassimport errno
1871162e3cSSimon Glassimport os
1971162e3cSSimon Glassimport pty
2071162e3cSSimon Glassimport select
2171162e3cSSimon Glassimport subprocess
2271162e3cSSimon Glassimport sys
2371162e3cSSimon Glassimport unittest
2471162e3cSSimon Glass
2571162e3cSSimon Glass
2671162e3cSSimon Glass# Import these here so the caller does not need to import subprocess also.
2771162e3cSSimon GlassPIPE = subprocess.PIPE
2871162e3cSSimon GlassSTDOUT = subprocess.STDOUT
2971162e3cSSimon GlassPIPE_PTY = -3     # Pipe output through a pty
3071162e3cSSimon Glassstay_alive = True
3171162e3cSSimon Glass
3271162e3cSSimon Glass
3371162e3cSSimon Glassclass Popen(subprocess.Popen):
3471162e3cSSimon Glass    """Like subprocess.Popen with ptys and incremental output
3571162e3cSSimon Glass
3671162e3cSSimon Glass    This class deals with running a child process and filtering its output on
3771162e3cSSimon Glass    both stdout and stderr while it is running. We do this so we can monitor
3871162e3cSSimon Glass    progress, and possibly relay the output to the user if requested.
3971162e3cSSimon Glass
4071162e3cSSimon Glass    The class is similar to subprocess.Popen, the equivalent is something like:
4171162e3cSSimon Glass
4271162e3cSSimon Glass        Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
4371162e3cSSimon Glass
4471162e3cSSimon Glass    But this class has many fewer features, and two enhancement:
4571162e3cSSimon Glass
4671162e3cSSimon Glass    1. Rather than getting the output data only at the end, this class sends it
4771162e3cSSimon Glass         to a provided operation as it arrives.
4871162e3cSSimon Glass    2. We use pseudo terminals so that the child will hopefully flush its output
4971162e3cSSimon Glass         to us as soon as it is produced, rather than waiting for the end of a
5071162e3cSSimon Glass         line.
5171162e3cSSimon Glass
5271162e3cSSimon Glass    Use CommunicateFilter() to handle output from the subprocess.
5371162e3cSSimon Glass
5471162e3cSSimon Glass    """
5571162e3cSSimon Glass
5671162e3cSSimon Glass    def __init__(self, args, stdin=None, stdout=PIPE_PTY, stderr=PIPE_PTY,
5771162e3cSSimon Glass                 shell=False, cwd=None, env=None, **kwargs):
5871162e3cSSimon Glass        """Cut-down constructor
5971162e3cSSimon Glass
6071162e3cSSimon Glass        Args:
6171162e3cSSimon Glass            args: Program and arguments for subprocess to execute.
6271162e3cSSimon Glass            stdin: See subprocess.Popen()
6371162e3cSSimon Glass            stdout: See subprocess.Popen(), except that we support the sentinel
6471162e3cSSimon Glass                    value of cros_subprocess.PIPE_PTY.
6571162e3cSSimon Glass            stderr: See subprocess.Popen(), except that we support the sentinel
6671162e3cSSimon Glass                    value of cros_subprocess.PIPE_PTY.
6771162e3cSSimon Glass            shell: See subprocess.Popen()
6871162e3cSSimon Glass            cwd: Working directory to change to for subprocess, or None if none.
6971162e3cSSimon Glass            env: Environment to use for this subprocess, or None to inherit parent.
7071162e3cSSimon Glass            kwargs: No other arguments are supported at the moment.    Passing other
7171162e3cSSimon Glass                    arguments will cause a ValueError to be raised.
7271162e3cSSimon Glass        """
7371162e3cSSimon Glass        stdout_pty = None
7471162e3cSSimon Glass        stderr_pty = None
7571162e3cSSimon Glass
7671162e3cSSimon Glass        if stdout == PIPE_PTY:
7771162e3cSSimon Glass            stdout_pty = pty.openpty()
7871162e3cSSimon Glass            stdout = os.fdopen(stdout_pty[1])
7971162e3cSSimon Glass        if stderr == PIPE_PTY:
8071162e3cSSimon Glass            stderr_pty = pty.openpty()
8171162e3cSSimon Glass            stderr = os.fdopen(stderr_pty[1])
8271162e3cSSimon Glass
8371162e3cSSimon Glass        super(Popen, self).__init__(args, stdin=stdin,
8471162e3cSSimon Glass                stdout=stdout, stderr=stderr, shell=shell, cwd=cwd, env=env,
8571162e3cSSimon Glass                **kwargs)
8671162e3cSSimon Glass
8771162e3cSSimon Glass        # If we're on a PTY, we passed the slave half of the PTY to the subprocess.
8871162e3cSSimon Glass        # We want to use the master half on our end from now on.    Setting this here
8971162e3cSSimon Glass        # does make some assumptions about the implementation of subprocess, but
9071162e3cSSimon Glass        # those assumptions are pretty minor.
9171162e3cSSimon Glass
9271162e3cSSimon Glass        # Note that if stderr is STDOUT, then self.stderr will be set to None by
9371162e3cSSimon Glass        # this constructor.
9471162e3cSSimon Glass        if stdout_pty is not None:
9571162e3cSSimon Glass            self.stdout = os.fdopen(stdout_pty[0])
9671162e3cSSimon Glass        if stderr_pty is not None:
9771162e3cSSimon Glass            self.stderr = os.fdopen(stderr_pty[0])
9871162e3cSSimon Glass
9971162e3cSSimon Glass        # Insist that unit tests exist for other arguments we don't support.
10071162e3cSSimon Glass        if kwargs:
10171162e3cSSimon Glass            raise ValueError("Unit tests do not test extra args - please add tests")
10271162e3cSSimon Glass
10371162e3cSSimon Glass    def CommunicateFilter(self, output):
10471162e3cSSimon Glass        """Interact with process: Read data from stdout and stderr.
10571162e3cSSimon Glass
10671162e3cSSimon Glass        This method runs until end-of-file is reached, then waits for the
10771162e3cSSimon Glass        subprocess to terminate.
10871162e3cSSimon Glass
10971162e3cSSimon Glass        The output function is sent all output from the subprocess and must be
11071162e3cSSimon Glass        defined like this:
11171162e3cSSimon Glass
11271162e3cSSimon Glass            def Output([self,] stream, data)
11371162e3cSSimon Glass            Args:
11471162e3cSSimon Glass                stream: the stream the output was received on, which will be
11571162e3cSSimon Glass                        sys.stdout or sys.stderr.
11671162e3cSSimon Glass                data: a string containing the data
11771162e3cSSimon Glass
11871162e3cSSimon Glass        Note: The data read is buffered in memory, so do not use this
11971162e3cSSimon Glass        method if the data size is large or unlimited.
12071162e3cSSimon Glass
12171162e3cSSimon Glass        Args:
12271162e3cSSimon Glass            output: Function to call with each fragment of output.
12371162e3cSSimon Glass
12471162e3cSSimon Glass        Returns:
12571162e3cSSimon Glass            A tuple (stdout, stderr, combined) which is the data received on
12671162e3cSSimon Glass            stdout, stderr and the combined data (interleaved stdout and stderr).
12771162e3cSSimon Glass
12871162e3cSSimon Glass            Note that the interleaved output will only be sensible if you have
12971162e3cSSimon Glass            set both stdout and stderr to PIPE or PIPE_PTY. Even then it depends on
13071162e3cSSimon Glass            the timing of the output in the subprocess. If a subprocess flips
13171162e3cSSimon Glass            between stdout and stderr quickly in succession, by the time we come to
13271162e3cSSimon Glass            read the output from each we may see several lines in each, and will read
13371162e3cSSimon Glass            all the stdout lines, then all the stderr lines. So the interleaving
13471162e3cSSimon Glass            may not be correct. In this case you might want to pass
13571162e3cSSimon Glass            stderr=cros_subprocess.STDOUT to the constructor.
13671162e3cSSimon Glass
13771162e3cSSimon Glass            This feature is still useful for subprocesses where stderr is
13871162e3cSSimon Glass            rarely used and indicates an error.
13971162e3cSSimon Glass
14071162e3cSSimon Glass            Note also that if you set stderr to STDOUT, then stderr will be empty
14171162e3cSSimon Glass            and the combined output will just be the same as stdout.
14271162e3cSSimon Glass        """
14371162e3cSSimon Glass
14471162e3cSSimon Glass        read_set = []
14571162e3cSSimon Glass        write_set = []
14671162e3cSSimon Glass        stdout = None # Return
14771162e3cSSimon Glass        stderr = None # Return
14871162e3cSSimon Glass
14971162e3cSSimon Glass        if self.stdin:
15071162e3cSSimon Glass            # Flush stdio buffer.    This might block, if the user has
15171162e3cSSimon Glass            # been writing to .stdin in an uncontrolled fashion.
15271162e3cSSimon Glass            self.stdin.flush()
15371162e3cSSimon Glass            if input:
15471162e3cSSimon Glass                write_set.append(self.stdin)
15571162e3cSSimon Glass            else:
15671162e3cSSimon Glass                self.stdin.close()
15771162e3cSSimon Glass        if self.stdout:
15871162e3cSSimon Glass            read_set.append(self.stdout)
15971162e3cSSimon Glass            stdout = []
16071162e3cSSimon Glass        if self.stderr and self.stderr != self.stdout:
16171162e3cSSimon Glass            read_set.append(self.stderr)
16271162e3cSSimon Glass            stderr = []
16371162e3cSSimon Glass        combined = []
16471162e3cSSimon Glass
16571162e3cSSimon Glass        input_offset = 0
16671162e3cSSimon Glass        while read_set or write_set:
16771162e3cSSimon Glass            try:
16871162e3cSSimon Glass                rlist, wlist, _ = select.select(read_set, write_set, [], 0.2)
169*ac3fde93SPaul Burton            except select.error as e:
17071162e3cSSimon Glass                if e.args[0] == errno.EINTR:
17171162e3cSSimon Glass                    continue
17271162e3cSSimon Glass                raise
17371162e3cSSimon Glass
17471162e3cSSimon Glass            if not stay_alive:
17571162e3cSSimon Glass                    self.terminate()
17671162e3cSSimon Glass
17771162e3cSSimon Glass            if self.stdin in wlist:
17871162e3cSSimon Glass                # When select has indicated that the file is writable,
17971162e3cSSimon Glass                # we can write up to PIPE_BUF bytes without risk
18071162e3cSSimon Glass                # blocking.    POSIX defines PIPE_BUF >= 512
18171162e3cSSimon Glass                chunk = input[input_offset : input_offset + 512]
18271162e3cSSimon Glass                bytes_written = os.write(self.stdin.fileno(), chunk)
18371162e3cSSimon Glass                input_offset += bytes_written
18471162e3cSSimon Glass                if input_offset >= len(input):
18571162e3cSSimon Glass                    self.stdin.close()
18671162e3cSSimon Glass                    write_set.remove(self.stdin)
18771162e3cSSimon Glass
18871162e3cSSimon Glass            if self.stdout in rlist:
18971162e3cSSimon Glass                data = ""
19071162e3cSSimon Glass                # We will get an error on read if the pty is closed
19171162e3cSSimon Glass                try:
19271162e3cSSimon Glass                    data = os.read(self.stdout.fileno(), 1024)
19371162e3cSSimon Glass                except OSError:
19471162e3cSSimon Glass                    pass
19571162e3cSSimon Glass                if data == "":
19671162e3cSSimon Glass                    self.stdout.close()
19771162e3cSSimon Glass                    read_set.remove(self.stdout)
19871162e3cSSimon Glass                else:
19971162e3cSSimon Glass                    stdout.append(data)
20071162e3cSSimon Glass                    combined.append(data)
20171162e3cSSimon Glass                    if output:
20271162e3cSSimon Glass                        output(sys.stdout, data)
20371162e3cSSimon Glass            if self.stderr in rlist:
20471162e3cSSimon Glass                data = ""
20571162e3cSSimon Glass                # We will get an error on read if the pty is closed
20671162e3cSSimon Glass                try:
20771162e3cSSimon Glass                    data = os.read(self.stderr.fileno(), 1024)
20871162e3cSSimon Glass                except OSError:
20971162e3cSSimon Glass                    pass
21071162e3cSSimon Glass                if data == "":
21171162e3cSSimon Glass                    self.stderr.close()
21271162e3cSSimon Glass                    read_set.remove(self.stderr)
21371162e3cSSimon Glass                else:
21471162e3cSSimon Glass                    stderr.append(data)
21571162e3cSSimon Glass                    combined.append(data)
21671162e3cSSimon Glass                    if output:
21771162e3cSSimon Glass                        output(sys.stderr, data)
21871162e3cSSimon Glass
21971162e3cSSimon Glass        # All data exchanged.    Translate lists into strings.
22071162e3cSSimon Glass        if stdout is not None:
22171162e3cSSimon Glass            stdout = ''.join(stdout)
22271162e3cSSimon Glass        else:
22371162e3cSSimon Glass            stdout = ''
22471162e3cSSimon Glass        if stderr is not None:
22571162e3cSSimon Glass            stderr = ''.join(stderr)
22671162e3cSSimon Glass        else:
22771162e3cSSimon Glass            stderr = ''
22871162e3cSSimon Glass        combined = ''.join(combined)
22971162e3cSSimon Glass
23071162e3cSSimon Glass        # Translate newlines, if requested.    We cannot let the file
23171162e3cSSimon Glass        # object do the translation: It is based on stdio, which is
23271162e3cSSimon Glass        # impossible to combine with select (unless forcing no
23371162e3cSSimon Glass        # buffering).
23471162e3cSSimon Glass        if self.universal_newlines and hasattr(file, 'newlines'):
23571162e3cSSimon Glass            if stdout:
23671162e3cSSimon Glass                stdout = self._translate_newlines(stdout)
23771162e3cSSimon Glass            if stderr:
23871162e3cSSimon Glass                stderr = self._translate_newlines(stderr)
23971162e3cSSimon Glass
24071162e3cSSimon Glass        self.wait()
24171162e3cSSimon Glass        return (stdout, stderr, combined)
24271162e3cSSimon Glass
24371162e3cSSimon Glass
24471162e3cSSimon Glass# Just being a unittest.TestCase gives us 14 public methods.    Unless we
24571162e3cSSimon Glass# disable this, we can only have 6 tests in a TestCase.    That's not enough.
24671162e3cSSimon Glass#
24771162e3cSSimon Glass# pylint: disable=R0904
24871162e3cSSimon Glass
24971162e3cSSimon Glassclass TestSubprocess(unittest.TestCase):
25071162e3cSSimon Glass    """Our simple unit test for this module"""
25171162e3cSSimon Glass
25271162e3cSSimon Glass    class MyOperation:
25371162e3cSSimon Glass        """Provides a operation that we can pass to Popen"""
25471162e3cSSimon Glass        def __init__(self, input_to_send=None):
25571162e3cSSimon Glass            """Constructor to set up the operation and possible input.
25671162e3cSSimon Glass
25771162e3cSSimon Glass            Args:
25871162e3cSSimon Glass                input_to_send: a text string to send when we first get input. We will
25971162e3cSSimon Glass                    add \r\n to the string.
26071162e3cSSimon Glass            """
26171162e3cSSimon Glass            self.stdout_data = ''
26271162e3cSSimon Glass            self.stderr_data = ''
26371162e3cSSimon Glass            self.combined_data = ''
26471162e3cSSimon Glass            self.stdin_pipe = None
26571162e3cSSimon Glass            self._input_to_send = input_to_send
26671162e3cSSimon Glass            if input_to_send:
26771162e3cSSimon Glass                pipe = os.pipe()
26871162e3cSSimon Glass                self.stdin_read_pipe = pipe[0]
26971162e3cSSimon Glass                self._stdin_write_pipe = os.fdopen(pipe[1], 'w')
27071162e3cSSimon Glass
27171162e3cSSimon Glass        def Output(self, stream, data):
27271162e3cSSimon Glass            """Output handler for Popen. Stores the data for later comparison"""
27371162e3cSSimon Glass            if stream == sys.stdout:
27471162e3cSSimon Glass                self.stdout_data += data
27571162e3cSSimon Glass            if stream == sys.stderr:
27671162e3cSSimon Glass                self.stderr_data += data
27771162e3cSSimon Glass            self.combined_data += data
27871162e3cSSimon Glass
27971162e3cSSimon Glass            # Output the input string if we have one.
28071162e3cSSimon Glass            if self._input_to_send:
28171162e3cSSimon Glass                self._stdin_write_pipe.write(self._input_to_send + '\r\n')
28271162e3cSSimon Glass                self._stdin_write_pipe.flush()
28371162e3cSSimon Glass
28471162e3cSSimon Glass    def _BasicCheck(self, plist, oper):
28571162e3cSSimon Glass        """Basic checks that the output looks sane."""
28671162e3cSSimon Glass        self.assertEqual(plist[0], oper.stdout_data)
28771162e3cSSimon Glass        self.assertEqual(plist[1], oper.stderr_data)
28871162e3cSSimon Glass        self.assertEqual(plist[2], oper.combined_data)
28971162e3cSSimon Glass
29071162e3cSSimon Glass        # The total length of stdout and stderr should equal the combined length
29171162e3cSSimon Glass        self.assertEqual(len(plist[0]) + len(plist[1]), len(plist[2]))
29271162e3cSSimon Glass
29371162e3cSSimon Glass    def test_simple(self):
29471162e3cSSimon Glass        """Simple redirection: Get process list"""
29571162e3cSSimon Glass        oper = TestSubprocess.MyOperation()
29671162e3cSSimon Glass        plist = Popen(['ps']).CommunicateFilter(oper.Output)
29771162e3cSSimon Glass        self._BasicCheck(plist, oper)
29871162e3cSSimon Glass
29971162e3cSSimon Glass    def test_stderr(self):
30071162e3cSSimon Glass        """Check stdout and stderr"""
30171162e3cSSimon Glass        oper = TestSubprocess.MyOperation()
30271162e3cSSimon Glass        cmd = 'echo fred >/dev/stderr && false || echo bad'
30371162e3cSSimon Glass        plist = Popen([cmd], shell=True).CommunicateFilter(oper.Output)
30471162e3cSSimon Glass        self._BasicCheck(plist, oper)
30571162e3cSSimon Glass        self.assertEqual(plist [0], 'bad\r\n')
30671162e3cSSimon Glass        self.assertEqual(plist [1], 'fred\r\n')
30771162e3cSSimon Glass
30871162e3cSSimon Glass    def test_shell(self):
30971162e3cSSimon Glass        """Check with and without shell works"""
31071162e3cSSimon Glass        oper = TestSubprocess.MyOperation()
31171162e3cSSimon Glass        cmd = 'echo test >/dev/stderr'
31271162e3cSSimon Glass        self.assertRaises(OSError, Popen, [cmd], shell=False)
31371162e3cSSimon Glass        plist = Popen([cmd], shell=True).CommunicateFilter(oper.Output)
31471162e3cSSimon Glass        self._BasicCheck(plist, oper)
31571162e3cSSimon Glass        self.assertEqual(len(plist [0]), 0)
31671162e3cSSimon Glass        self.assertEqual(plist [1], 'test\r\n')
31771162e3cSSimon Glass
31871162e3cSSimon Glass    def test_list_args(self):
31971162e3cSSimon Glass        """Check with and without shell works using list arguments"""
32071162e3cSSimon Glass        oper = TestSubprocess.MyOperation()
32171162e3cSSimon Glass        cmd = ['echo', 'test', '>/dev/stderr']
32271162e3cSSimon Glass        plist = Popen(cmd, shell=False).CommunicateFilter(oper.Output)
32371162e3cSSimon Glass        self._BasicCheck(plist, oper)
32471162e3cSSimon Glass        self.assertEqual(plist [0], ' '.join(cmd[1:]) + '\r\n')
32571162e3cSSimon Glass        self.assertEqual(len(plist [1]), 0)
32671162e3cSSimon Glass
32771162e3cSSimon Glass        oper = TestSubprocess.MyOperation()
32871162e3cSSimon Glass
32971162e3cSSimon Glass        # this should be interpreted as 'echo' with the other args dropped
33071162e3cSSimon Glass        cmd = ['echo', 'test', '>/dev/stderr']
33171162e3cSSimon Glass        plist = Popen(cmd, shell=True).CommunicateFilter(oper.Output)
33271162e3cSSimon Glass        self._BasicCheck(plist, oper)
33371162e3cSSimon Glass        self.assertEqual(plist [0], '\r\n')
33471162e3cSSimon Glass
33571162e3cSSimon Glass    def test_cwd(self):
33671162e3cSSimon Glass        """Check we can change directory"""
33771162e3cSSimon Glass        for shell in (False, True):
33871162e3cSSimon Glass            oper = TestSubprocess.MyOperation()
33971162e3cSSimon Glass            plist = Popen('pwd', shell=shell, cwd='/tmp').CommunicateFilter(oper.Output)
34071162e3cSSimon Glass            self._BasicCheck(plist, oper)
34171162e3cSSimon Glass            self.assertEqual(plist [0], '/tmp\r\n')
34271162e3cSSimon Glass
34371162e3cSSimon Glass    def test_env(self):
34471162e3cSSimon Glass        """Check we can change environment"""
34571162e3cSSimon Glass        for add in (False, True):
34671162e3cSSimon Glass            oper = TestSubprocess.MyOperation()
34771162e3cSSimon Glass            env = os.environ
34871162e3cSSimon Glass            if add:
34971162e3cSSimon Glass                env ['FRED'] = 'fred'
35071162e3cSSimon Glass            cmd = 'echo $FRED'
35171162e3cSSimon Glass            plist = Popen(cmd, shell=True, env=env).CommunicateFilter(oper.Output)
35271162e3cSSimon Glass            self._BasicCheck(plist, oper)
35371162e3cSSimon Glass            self.assertEqual(plist [0], add and 'fred\r\n' or '\r\n')
35471162e3cSSimon Glass
35571162e3cSSimon Glass    def test_extra_args(self):
35671162e3cSSimon Glass        """Check we can't add extra arguments"""
35771162e3cSSimon Glass        self.assertRaises(ValueError, Popen, 'true', close_fds=False)
35871162e3cSSimon Glass
35971162e3cSSimon Glass    def test_basic_input(self):
36071162e3cSSimon Glass        """Check that incremental input works
36171162e3cSSimon Glass
36271162e3cSSimon Glass        We set up a subprocess which will prompt for name. When we see this prompt
36371162e3cSSimon Glass        we send the name as input to the process. It should then print the name
36471162e3cSSimon Glass        properly to stdout.
36571162e3cSSimon Glass        """
36671162e3cSSimon Glass        oper = TestSubprocess.MyOperation('Flash')
36771162e3cSSimon Glass        prompt = 'What is your name?: '
36871162e3cSSimon Glass        cmd = 'echo -n "%s"; read name; echo Hello $name' % prompt
36971162e3cSSimon Glass        plist = Popen([cmd], stdin=oper.stdin_read_pipe,
37071162e3cSSimon Glass                shell=True).CommunicateFilter(oper.Output)
37171162e3cSSimon Glass        self._BasicCheck(plist, oper)
37271162e3cSSimon Glass        self.assertEqual(len(plist [1]), 0)
37371162e3cSSimon Glass        self.assertEqual(plist [0], prompt + 'Hello Flash\r\r\n')
37471162e3cSSimon Glass
37571162e3cSSimon Glass    def test_isatty(self):
37671162e3cSSimon Glass        """Check that ptys appear as terminals to the subprocess"""
37771162e3cSSimon Glass        oper = TestSubprocess.MyOperation()
37871162e3cSSimon Glass        cmd = ('if [ -t %d ]; then echo "terminal %d" >&%d; '
37971162e3cSSimon Glass                'else echo "not %d" >&%d; fi;')
38071162e3cSSimon Glass        both_cmds = ''
38171162e3cSSimon Glass        for fd in (1, 2):
38271162e3cSSimon Glass            both_cmds += cmd % (fd, fd, fd, fd, fd)
38371162e3cSSimon Glass        plist = Popen(both_cmds, shell=True).CommunicateFilter(oper.Output)
38471162e3cSSimon Glass        self._BasicCheck(plist, oper)
38571162e3cSSimon Glass        self.assertEqual(plist [0], 'terminal 1\r\n')
38671162e3cSSimon Glass        self.assertEqual(plist [1], 'terminal 2\r\n')
38771162e3cSSimon Glass
38871162e3cSSimon Glass        # Now try with PIPE and make sure it is not a terminal
38971162e3cSSimon Glass        oper = TestSubprocess.MyOperation()
39071162e3cSSimon Glass        plist = Popen(both_cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
39171162e3cSSimon Glass                shell=True).CommunicateFilter(oper.Output)
39271162e3cSSimon Glass        self._BasicCheck(plist, oper)
39371162e3cSSimon Glass        self.assertEqual(plist [0], 'not 1\n')
39471162e3cSSimon Glass        self.assertEqual(plist [1], 'not 2\n')
39571162e3cSSimon Glass
39671162e3cSSimon Glassif __name__ == '__main__':
39771162e3cSSimon Glass    unittest.main()
398