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