OLD | NEW |
---|---|
1 # Copyright (C) 2011 Google Inc. All rights reserved. | 1 # Copyright (C) 2011 Google Inc. All rights reserved. |
2 # | 2 # |
3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
5 # met: | 5 # met: |
6 # | 6 # |
7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
11 # in the documentation and/or other materials provided with the | 11 # in the documentation and/or other materials provided with the |
12 # distribution. | 12 # distribution. |
13 # * Neither the name of Google Inc. nor the names of its | 13 # * Neither the name of Google Inc. nor the names of its |
14 # contributors may be used to endorse or promote products derived from | 14 # contributors may be used to endorse or promote products derived from |
15 # this software without specific prior written permission. | 15 # this software without specific prior written permission. |
16 # | 16 # |
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | 28 |
29 import collections | |
29 import logging | 30 import logging |
30 import os | 31 import os |
31 import StringIO | 32 import StringIO |
32 | 33 |
33 from webkitpy.common.system.executive import ScriptError | 34 from webkitpy.common.system.executive import ScriptError |
34 | 35 |
35 _log = logging.getLogger(__name__) | 36 _log = logging.getLogger(__name__) |
36 | 37 |
37 | 38 |
38 class MockProcess(object): | 39 class MockProcess(object): |
(...skipping 14 matching lines...) Expand all Loading... | |
53 return None | 54 return None |
54 return self.returncode | 55 return self.returncode |
55 | 56 |
56 def communicate(self, *_): | 57 def communicate(self, *_): |
57 return (self.stdout.getvalue(), self.stderr.getvalue()) | 58 return (self.stdout.getvalue(), self.stderr.getvalue()) |
58 | 59 |
59 def kill(self): | 60 def kill(self): |
60 return | 61 return |
61 | 62 |
62 | 63 |
64 MockCall = collections.namedtuple('MockCall', ('args', 'env')) | |
65 | |
66 | |
63 class MockExecutive(object): | 67 class MockExecutive(object): |
64 PIPE = 'MOCK PIPE' | 68 PIPE = 'MOCK PIPE' |
65 STDOUT = 'MOCK STDOUT' | 69 STDOUT = 'MOCK STDOUT' |
66 DEVNULL = 'MOCK_DEVNULL' | 70 DEVNULL = 'MOCK_DEVNULL' |
67 | 71 |
68 @staticmethod | 72 @staticmethod |
69 def ignore_error(error): | 73 def ignore_error(error): |
70 pass | 74 pass |
71 | 75 |
72 def __init__(self, should_log=False, should_throw=False, | 76 def __init__(self, should_log=False, should_throw=False, |
73 output='MOCK output of child process', stderr='', | 77 output='MOCK output of child process', stderr='', |
74 exit_code=0, exception=None, run_command_fn=None): | 78 exit_code=0, exception=None, run_command_fn=None): |
75 self._should_log = should_log | 79 self._should_log = should_log |
76 self._should_throw = should_throw | 80 self._should_throw = should_throw |
77 # FIXME: Once executive wraps os.getpid() we can just use a static pid f or "this" process. | 81 # FIXME: Once executive wraps os.getpid() we can just use a static pid f or "this" process. |
78 self._running_pids = {'test-webkitpy': os.getpid()} | 82 self._running_pids = {'test-webkitpy': os.getpid()} |
79 self._output = output | 83 self._output = output |
80 self._stderr = stderr | 84 self._stderr = stderr |
81 self._exit_code = exit_code | 85 self._exit_code = exit_code |
82 self._exception = exception | 86 self._exception = exception |
83 self._run_command_fn = run_command_fn | 87 self._run_command_fn = run_command_fn |
84 self._proc = None | 88 self._proc = None |
85 self.calls = [] | 89 self.full_calls = [] |
90 | |
91 def _append_call(self, args, env=None): | |
92 if env: | |
93 env = env.copy() | |
94 self.full_calls.append(MockCall( | |
95 args=args, | |
96 env=env.copy() if env is not None else None, | |
97 )) | |
86 | 98 |
87 def check_running_pid(self, pid): | 99 def check_running_pid(self, pid): |
88 return pid in self._running_pids.values() | 100 return pid in self._running_pids.values() |
89 | 101 |
90 def running_pids(self, process_name_filter): | 102 def running_pids(self, process_name_filter): |
91 running_pids = [] | 103 running_pids = [] |
92 for process_name, process_pid in self._running_pids.iteritems(): | 104 for process_name, process_pid in self._running_pids.iteritems(): |
93 if process_name_filter(process_name): | 105 if process_name_filter(process_name): |
94 running_pids.append(process_pid) | 106 running_pids.append(process_pid) |
95 | 107 |
(...skipping 10 matching lines...) Expand all Loading... | |
106 args, | 118 args, |
107 cwd=None, | 119 cwd=None, |
108 input=None, # pylint: disable=redefined-builtin | 120 input=None, # pylint: disable=redefined-builtin |
109 timeout_seconds=False, | 121 timeout_seconds=False, |
110 error_handler=None, | 122 error_handler=None, |
111 return_exit_code=False, | 123 return_exit_code=False, |
112 return_stderr=True, | 124 return_stderr=True, |
113 decode_output=False, | 125 decode_output=False, |
114 env=None, | 126 env=None, |
115 debug_logging=False): | 127 debug_logging=False): |
116 self.calls.append(args) | 128 self._append_call(args, env=env) |
117 | 129 |
118 assert isinstance(args, list) or isinstance(args, tuple) | 130 assert isinstance(args, list) or isinstance(args, tuple) |
119 | 131 |
120 if self._should_log: | 132 if self._should_log: |
121 env_string = '' | 133 env_string = '' |
122 if env: | 134 if env: |
123 env_string = ', env=%s' % env | 135 env_string = ', env=%s' % env |
124 input_string = '' | 136 input_string = '' |
125 if input: | 137 if input: |
126 input_string = ', input=%s' % input | 138 input_string = ', input=%s' % input |
(...skipping 26 matching lines...) Expand all Loading... | |
153 pass | 165 pass |
154 | 166 |
155 def kill_process(self, pid): | 167 def kill_process(self, pid): |
156 pass | 168 pass |
157 | 169 |
158 def interrupt(self, pid): | 170 def interrupt(self, pid): |
159 pass | 171 pass |
160 | 172 |
161 def popen(self, args, cwd=None, env=None, **_): | 173 def popen(self, args, cwd=None, env=None, **_): |
162 assert all(isinstance(arg, basestring) for arg in args) | 174 assert all(isinstance(arg, basestring) for arg in args) |
163 self.calls.append(args) | 175 self._append_call(args, env=env) |
164 if self._should_log: | 176 if self._should_log: |
165 cwd_string = '' | 177 cwd_string = '' |
166 if cwd: | 178 if cwd: |
167 cwd_string = ', cwd=%s' % cwd | 179 cwd_string = ', cwd=%s' % cwd |
168 env_string = '' | 180 env_string = '' |
169 if env: | 181 if env: |
170 env_string = ', env=%s' % env | 182 env_string = ', env=%s' % env |
171 _log.info('MOCK popen: %s%s%s', args, cwd_string, env_string) | 183 _log.info('MOCK popen: %s%s%s', args, cwd_string, env_string) |
172 if not self._proc: | 184 if not self._proc: |
173 self._proc = MockProcess(self._output) | 185 self._proc = MockProcess(self._output) |
174 return self._proc | 186 return self._proc |
175 | 187 |
176 def call(self, args, **_): | 188 def call(self, args, **_): |
177 assert all(isinstance(arg, basestring) for arg in args) | 189 assert all(isinstance(arg, basestring) for arg in args) |
178 self.calls.append(args) | 190 self._append_call(args) |
179 _log.info('Mock call: %s', args) | 191 _log.info('Mock call: %s', args) |
180 | 192 |
181 def run_in_parallel(self, commands): | 193 def run_in_parallel(self, commands): |
182 assert len(commands) | 194 assert len(commands) |
183 | 195 |
184 num_previous_calls = len(self.calls) | 196 num_previous_calls = len(self.full_calls) |
185 command_outputs = [] | 197 command_outputs = [] |
186 for cmd_line, cwd in commands: | 198 for cmd_line, cwd in commands: |
187 assert all(isinstance(arg, basestring) for arg in cmd_line) | 199 assert all(isinstance(arg, basestring) for arg in cmd_line) |
188 command_outputs.append([0, self.run_command(cmd_line, cwd=cwd), '']) | 200 command_outputs.append([0, self.run_command(cmd_line, cwd=cwd), '']) |
189 | 201 |
190 new_calls = self.calls[num_previous_calls:] | 202 new_calls = self.full_calls[num_previous_calls:] |
191 self.calls = self.calls[:num_previous_calls] | 203 self.full_calls = self.full_calls[:num_previous_calls] |
192 self.calls.append(new_calls) | 204 self.full_calls.append(new_calls) |
193 return command_outputs | 205 return command_outputs |
194 | 206 |
195 def map(self, thunk, arglist, processes=None): | 207 def map(self, thunk, arglist, processes=None): |
196 return map(thunk, arglist) | 208 return map(thunk, arglist) |
197 | 209 |
198 def process_dump(self): | 210 def process_dump(self): |
199 return [] | 211 return [] |
200 | 212 |
213 @property | |
214 def calls(self): | |
215 def get_args(v): | |
216 if isinstance(v, list): | |
217 return [get_args(e) for e in v] | |
218 elif isinstance(v, MockCall): | |
219 return v.args | |
220 else: | |
221 return TypeError('Unknown full_calls type: %s' % (type(v).__name __,)) | |
222 return get_args(self.full_calls) | |
Dirk Pranke
2017/05/04 00:05:48
Can't this just be:
@property
def calls(self)
dnj
2017/05/04 00:20:37
No, b/c "run_in_parallel" adds lists of MockCall t
Dirk Pranke
2017/05/04 00:43:46
Ah, I see. That's ugly. Can you add a TODO and/or
dnj
2017/05/04 16:07:54
Done.
| |
223 | |
201 | 224 |
202 def mock_git_commands(vals, strict=False): | 225 def mock_git_commands(vals, strict=False): |
203 def run_fn(args): | 226 def run_fn(args): |
204 sub_command = args[1] | 227 sub_command = args[1] |
205 if strict and sub_command not in vals: | 228 if strict and sub_command not in vals: |
206 raise AssertionError('{} not found in sub-command list {}'.format( | 229 raise AssertionError('{} not found in sub-command list {}'.format( |
207 sub_command, vals)) | 230 sub_command, vals)) |
208 return vals.get(sub_command, '') | 231 return vals.get(sub_command, '') |
209 return MockExecutive(run_command_fn=run_fn) | 232 return MockExecutive(run_command_fn=run_fn) |
OLD | NEW |