Adding Peter Thatcher to the owners file.
[chromium-blink-merge.git] / third_party / android_testrunner / run_command.py
blob6b84156fe0e549b50a8a0c996e754b67e41cd799
1 #!/usr/bin/python2.4
4 # Copyright 2007, The Android Open Source Project
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
18 # System imports
19 import os
20 import signal
21 import subprocess
22 import tempfile
23 import threading
24 import time
26 # local imports
27 import errors
28 import logger
30 _abort_on_error = False
32 def SetAbortOnError(abort=True):
33 """Sets behavior of RunCommand to throw AbortError if command process returns
34 a negative error code"""
35 global _abort_on_error
36 _abort_on_error = abort
38 def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True,
39 stdin_input=None):
40 """Spawn and retry a subprocess to run the given shell command.
42 Args:
43 cmd: shell command to run
44 timeout_time: time in seconds to wait for command to run before aborting.
45 retry_count: number of times to retry command
46 return_output: if True return output of command as string. Otherwise,
47 direct output of command to stdout.
48 stdin_input: data to feed to stdin
49 Returns:
50 output of command
51 """
52 result = None
53 while True:
54 try:
55 result = RunOnce(cmd, timeout_time=timeout_time,
56 return_output=return_output, stdin_input=stdin_input)
57 except errors.WaitForResponseTimedOutError:
58 if retry_count == 0:
59 raise
60 retry_count -= 1
61 logger.Log("No response for %s, retrying" % cmd)
62 else:
63 # Success
64 return result
66 def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None):
67 """Spawns a subprocess to run the given shell command.
69 Args:
70 cmd: shell command to run
71 timeout_time: time in seconds to wait for command to run before aborting.
72 return_output: if True return output of command as string. Otherwise,
73 direct output of command to stdout.
74 stdin_input: data to feed to stdin
75 Returns:
76 output of command
77 Raises:
78 errors.WaitForResponseTimedOutError if command did not complete within
79 timeout_time seconds.
80 errors.AbortError is command returned error code and SetAbortOnError is on.
81 """
82 start_time = time.time()
83 so = []
84 global _abort_on_error, error_occurred
85 error_occurred = False
87 if return_output:
88 output_dest = tempfile.TemporaryFile(bufsize=0)
89 else:
90 # None means direct to stdout
91 output_dest = None
92 if stdin_input:
93 stdin_dest = subprocess.PIPE
94 else:
95 stdin_dest = None
96 pipe = subprocess.Popen(
97 cmd,
98 executable='/bin/bash',
99 stdin=stdin_dest,
100 stdout=output_dest,
101 stderr=subprocess.STDOUT,
102 shell=True, close_fds=True,
103 preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL))
105 def Run():
106 global error_occurred
107 try:
108 pipe.communicate(input=stdin_input)
109 output = None
110 if return_output:
111 output_dest.seek(0)
112 output = output_dest.read()
113 output_dest.close()
114 if output is not None and len(output) > 0:
115 so.append(output)
116 except OSError, e:
117 logger.SilentLog("failed to retrieve stdout from: %s" % cmd)
118 logger.Log(e)
119 so.append("ERROR")
120 error_occurred = True
121 if pipe.returncode:
122 logger.SilentLog("Error: %s returned %d error code" %(cmd,
123 pipe.returncode))
124 error_occurred = True
126 t = threading.Thread(target=Run)
127 t.start()
128 t.join(timeout_time)
129 if t.isAlive():
130 try:
131 pipe.kill()
132 except OSError:
133 # Can't kill a dead process.
134 pass
135 finally:
136 logger.SilentLog("about to raise a timeout for: %s" % cmd)
137 raise errors.WaitForResponseTimedOutError
139 output = "".join(so)
140 if _abort_on_error and error_occurred:
141 raise errors.AbortError(msg=output)
143 return "".join(so)
146 def RunHostCommand(binary, valgrind=False):
147 """Run a command on the host (opt using valgrind).
149 Runs the host binary and returns the exit code.
150 If successfull, the output (stdout and stderr) are discarded,
151 but printed in case of error.
152 The command can be run under valgrind in which case all the
153 output are always discarded.
155 Args:
156 binary: full path of the file to be run.
157 valgrind: If True the command will be run under valgrind.
159 Returns:
160 The command exit code (int)
162 if not valgrind:
163 subproc = subprocess.Popen(binary, stdout=subprocess.PIPE,
164 stderr=subprocess.STDOUT)
165 subproc.wait()
166 if subproc.returncode != 0: # In case of error print the output
167 print subproc.communicate()[0]
168 return subproc.returncode
169 else:
170 # Need the full path to valgrind to avoid other versions on the system.
171 subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck",
172 "--leak-check=yes", "-q", binary],
173 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
174 # Cannot rely on the retcode of valgrind. Instead look for an empty output.
175 valgrind_out = subproc.communicate()[0].strip()
176 if valgrind_out:
177 print valgrind_out
178 return 1
179 else:
180 return 0
183 def HasValgrind():
184 """Check that /usr/bin/valgrind exists.
186 We look for the fullpath to avoid picking up 'alternative' valgrind
187 on the system.
189 Returns:
190 True if a system valgrind was found.
192 return os.path.exists("/usr/bin/valgrind")