REGRESSION(r263625): [WPE][GTK] MiniBrowser output no longer includes stderr and...
[WebKit-https.git] / Tools / Scripts / webkitpy / common / system / executive_mock.py
1 # Copyright (C) 2011 Google Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
6 #
7 #    * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 #    * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
12 # distribution.
13 #    * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 import logging
30 import os
31
32 from webkitpy.common.unicode_compatibility import BytesIO, decode_for, encode_if_necessary, unicode
33 from webkitpy.common.system.executive import ScriptError
34
35 _log = logging.getLogger(__name__)
36
37
38 class MockProcess(object):
39     def __init__(self, stdout='MOCK STDOUT\n', stderr=''):
40         self.pid = 42
41         self.stdout = BytesIO(encode_if_necessary(stdout))
42         self.stderr = BytesIO(encode_if_necessary(stderr))
43         self.stdin = BytesIO()
44         self.returncode = 0
45         self._is_running = False
46
47     def wait(self):
48         self._is_running = False
49         return self.returncode
50
51     def communicate(self, input=None):
52         self._is_running = False
53         return (self.stdout, self.stderr)
54
55     def poll(self):
56         if self._is_running:
57             return None
58         return self.returncode
59
60     def __enter__(self):
61         return self
62
63     def __exit__(self, *args):
64         self.wait()
65
66
67 # FIXME: This should be unified with MockExecutive2
68 class MockExecutive(object):
69     PIPE = "MOCK PIPE"
70     STDOUT = "MOCK STDOUT"
71
72     @staticmethod
73     def ignore_error(error):
74         pass
75
76     def __init__(self, should_log=False, should_throw=False, should_throw_when_run=None):
77         self._should_log = should_log
78         self._should_throw = should_throw
79         self._should_throw_when_run = should_throw_when_run or set()
80         # FIXME: Once executive wraps os.getpid() we can just use a static pid for "this" process.
81         self._running_pids = {'test-webkitpy': os.getpid()}
82         self._proc = None
83         self.calls = []
84         self.pid_to_system_pid = {}
85
86     def check_running_pid(self, pid):
87         return pid in self._running_pids.values()
88
89     def running_pids(self, process_name_filter):
90         running_pids = []
91         for process_name, process_pid in self._running_pids.items():
92             if process_name_filter(process_name):
93                 running_pids.append(process_pid)
94
95         _log.info("MOCK running_pids: %s" % running_pids)
96         return running_pids
97
98     def run_and_throw_if_fail(self, args, quiet=False, cwd=None, env=None):
99         if self._should_log:
100             env_string = ""
101             if env:
102                 for key in sorted(env.keys()):
103                     if len(env_string):
104                         env_string += ", "
105                     env_string += "'{}': '{}'".format(key, env[key])
106                 env_string = ", env={" + env_string + "}"
107             _log.info("MOCK run_and_throw_if_fail: %s, cwd=%s%s" % (args, cwd, env_string))
108         if self._should_throw_when_run.intersection(args):
109             raise ScriptError("Exception for %s" % args, output="MOCK command output")
110         return "MOCK output of child process"
111
112     def command_for_printing(self, args):
113         string_args = map(unicode, args)
114         return " ".join(string_args)
115
116     def run_command(self,
117                     args,
118                     cwd=None,
119                     input=None,
120                     stdout=None,
121                     error_handler=None,
122                     ignore_errors=False,
123                     return_exit_code=False,
124                     return_stderr=True,
125                     decode_output=False,
126                     env=None):
127
128         self.calls.append(args)
129
130         assert(isinstance(args, list) or isinstance(args, tuple))
131         if self._should_log:
132             env_string = ""
133             if env:
134                 for key in sorted(env.keys()):
135                     if len(env_string):
136                         env_string += ", "
137                     env_string += "'{}': '{}'".format(key, env[key])
138                 env_string = ", env={" + env_string + "}"
139             input_string = ""
140             if input:
141                 input_string = ", input=%s" % decode_for(input, str)
142             _log.info("MOCK run_command: %s, cwd=%s%s%s" % (args, cwd, env_string, input_string))
143         output = "MOCK output of child process"
144
145         if self._should_throw_when_run.intersection(args):
146             raise ScriptError("Exception for %s" % args, output="MOCK command output")
147
148         if self._should_throw:
149             raise ScriptError("MOCK ScriptError", output=output)
150         return output
151
152     def cpu_count(self):
153         return 2
154
155     def kill_all(self, process_name):
156         pass
157
158     def kill_process(self, pid):
159         pass
160
161     def interrupt(self, pid):
162         pass
163
164     def popen(self, args, cwd=None, env=None, **kwargs):
165         self.calls.append(args)
166         if self._should_log:
167             cwd_string = ""
168             if cwd:
169                 cwd_string = ", cwd=%s" % cwd
170             env_string = ""
171             if env:
172                 env_string = ", env=%s" % env
173             _log.info("MOCK popen: %s%s%s" % (args, cwd_string, env_string))
174         if not self._proc:
175             self._proc = MockProcess()
176         self._proc._is_running = True
177         return self._proc
178
179     def run_in_parallel(self, commands):
180         num_previous_calls = len(self.calls)
181         command_outputs = []
182         for cmd_line, cwd in commands:
183             command_outputs.append([0, self.run_command(cmd_line, cwd=cwd), ''])
184
185         new_calls = self.calls[num_previous_calls:]
186         self.calls = self.calls[:num_previous_calls]
187         self.calls.append(new_calls)
188         return command_outputs
189
190
191 class MockExecutive2(MockExecutive):
192     """MockExecutive2 is like MockExecutive except it doesn't log anything."""
193
194     def __init__(self, output='', exit_code=0, exception=None, run_command_fn=None, stderr=''):
195         self._output = output
196         self._stderr = stderr
197         self._exit_code = exit_code
198         self._exception = exception
199         self._running_pids = {'test-webkitpy': os.getpid()}
200         self._run_command_fn = run_command_fn
201         self.calls = []
202
203     def run_command(self,
204                     args,
205                     cwd=None,
206                     input=None,
207                     error_handler=None,
208                     ignore_errors=False,
209                     return_exit_code=False,
210                     return_stderr=True,
211                     decode_output=False,
212                     env=None):
213         self.calls.append(args)
214         assert(isinstance(args, list) or isinstance(args, tuple))
215
216         if ignore_errors:
217             assert error_handler is None, "don't specify error_handler if ignore_errors is True"
218             error_handler = self.ignore_error
219
220         if self._exception:
221             raise self._exception  # pylint: disable=E0702
222         if self._run_command_fn:
223             return self._run_command_fn(args)
224         if return_exit_code:
225             return self._exit_code
226         if self._exit_code and error_handler:
227             script_error = ScriptError(script_args=args, exit_code=self._exit_code, output=self._output)
228             error_handler(script_error)
229         if return_stderr:
230             return self._output + self._stderr
231         return self._output