3e6ba7ba3184221069c0e847a63fad80f42fb7f6
[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                     error_handler=None,
121                     ignore_errors=False,
122                     return_exit_code=False,
123                     return_stderr=True,
124                     decode_output=False,
125                     env=None):
126
127         self.calls.append(args)
128
129         assert(isinstance(args, list) or isinstance(args, tuple))
130         if self._should_log:
131             env_string = ""
132             if env:
133                 for key in sorted(env.keys()):
134                     if len(env_string):
135                         env_string += ", "
136                     env_string += "'{}': '{}'".format(key, env[key])
137                 env_string = ", env={" + env_string + "}"
138             input_string = ""
139             if input:
140                 input_string = ", input=%s" % decode_for(input, str)
141             _log.info("MOCK run_command: %s, cwd=%s%s%s" % (args, cwd, env_string, input_string))
142         output = "MOCK output of child process"
143
144         if self._should_throw_when_run.intersection(args):
145             raise ScriptError("Exception for %s" % args, output="MOCK command output")
146
147         if self._should_throw:
148             raise ScriptError("MOCK ScriptError", output=output)
149         return output
150
151     def cpu_count(self):
152         return 2
153
154     def kill_all(self, process_name):
155         pass
156
157     def kill_process(self, pid):
158         pass
159
160     def interrupt(self, pid):
161         pass
162
163     def popen(self, args, cwd=None, env=None, **kwargs):
164         self.calls.append(args)
165         if self._should_log:
166             cwd_string = ""
167             if cwd:
168                 cwd_string = ", cwd=%s" % cwd
169             env_string = ""
170             if env:
171                 env_string = ", env=%s" % env
172             _log.info("MOCK popen: %s%s%s" % (args, cwd_string, env_string))
173         if not self._proc:
174             self._proc = MockProcess()
175         self._proc._is_running = True
176         return self._proc
177
178     def run_in_parallel(self, commands):
179         num_previous_calls = len(self.calls)
180         command_outputs = []
181         for cmd_line, cwd in commands:
182             command_outputs.append([0, self.run_command(cmd_line, cwd=cwd), ''])
183
184         new_calls = self.calls[num_previous_calls:]
185         self.calls = self.calls[:num_previous_calls]
186         self.calls.append(new_calls)
187         return command_outputs
188
189
190 class MockExecutive2(MockExecutive):
191     """MockExecutive2 is like MockExecutive except it doesn't log anything."""
192
193     def __init__(self, output='', exit_code=0, exception=None, run_command_fn=None, stderr=''):
194         self._output = output
195         self._stderr = stderr
196         self._exit_code = exit_code
197         self._exception = exception
198         self._running_pids = {'test-webkitpy': os.getpid()}
199         self._run_command_fn = run_command_fn
200         self.calls = []
201
202     def run_command(self,
203                     args,
204                     cwd=None,
205                     input=None,
206                     error_handler=None,
207                     ignore_errors=False,
208                     return_exit_code=False,
209                     return_stderr=True,
210                     decode_output=False,
211                     env=None):
212         self.calls.append(args)
213         assert(isinstance(args, list) or isinstance(args, tuple))
214
215         if ignore_errors:
216             assert error_handler is None, "don't specify error_handler if ignore_errors is True"
217             error_handler = self.ignore_error
218
219         if self._exception:
220             raise self._exception  # pylint: disable=E0702
221         if self._run_command_fn:
222             return self._run_command_fn(args)
223         if return_exit_code:
224             return self._exit_code
225         if self._exit_code and error_handler:
226             script_error = ScriptError(script_args=args, exit_code=self._exit_code, output=self._output)
227             error_handler(script_error)
228         if return_stderr:
229             return self._output + self._stderr
230         return self._output