Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / third_party / android_testrunner / adb_interface.py
blob306f18690b2be63b6daad2370ecd0afede158171
1 #!/usr/bin/python2.4
4 # Copyright 2008, 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 """Provides an interface to communicate with the device via the adb command.
20 Assumes adb binary is currently on system path.
21 """
22 # Python imports
23 import os
24 import string
25 import time
27 # local imports
28 import am_instrument_parser
29 import errors
30 import logger
31 import run_command
34 class AdbInterface:
35 """Helper class for communicating with Android device via adb."""
37 DEVICE_TRACE_DIR = "/data/test_results/"
39 def __init__(self, adb_path='adb'):
40 """Constructor.
42 Args:
43 adb_path: Absolute path to the adb binary that should be used. Defaults
44 to the adb in the environment path.
45 """
46 self._adb_path = adb_path
47 # argument to pass to adb, to direct command to specific device
48 self._target_arg = ""
50 def SetEmulatorTarget(self):
51 """Direct all future commands to the only running emulator."""
52 self._target_arg = "-e"
54 def SetDeviceTarget(self):
55 """Direct all future commands to the only connected USB device."""
56 self._target_arg = "-d"
58 def SetTargetSerial(self, serial):
59 """Direct all future commands to Android target with the given serial."""
60 self._target_arg = "-s %s" % serial
62 def SendCommand(self, command_string, timeout_time=20, retry_count=3):
63 """Send a command via adb.
65 Args:
66 command_string: adb command to run
67 timeout_time: number of seconds to wait for command to respond before
68 retrying
69 retry_count: number of times to retry command before raising
70 WaitForResponseTimedOutError
71 Returns:
72 string output of command
74 Raises:
75 WaitForResponseTimedOutError if device does not respond to command within time
76 """
77 adb_cmd = "%s %s %s" % (self._adb_path, self._target_arg, command_string)
78 logger.SilentLog("about to run %s" % adb_cmd)
79 return run_command.RunCommand(adb_cmd, timeout_time=timeout_time,
80 retry_count=retry_count)
82 def SendShellCommand(self, cmd, timeout_time=20, retry_count=3):
83 """Send a adb shell command.
85 Args:
86 cmd: adb shell command to run
87 timeout_time: number of seconds to wait for command to respond before
88 retrying
89 retry_count: number of times to retry command before raising
90 WaitForResponseTimedOutError
92 Returns:
93 string output of command
95 Raises:
96 WaitForResponseTimedOutError: if device does not respond to command
97 """
98 return self.SendCommand("shell %s" % cmd, timeout_time=timeout_time,
99 retry_count=retry_count)
101 def BugReport(self, path):
102 """Dumps adb bugreport to the file specified by the path.
104 Args:
105 path: Path of the file where adb bugreport is dumped to.
107 bug_output = self.SendShellCommand("bugreport", timeout_time=60)
108 bugreport_file = open(path, "w")
109 bugreport_file.write(bug_output)
110 bugreport_file.close()
112 def Push(self, src, dest):
113 """Pushes the file src onto the device at dest.
115 Args:
116 src: file path of host file to push
117 dest: destination absolute file path on device
119 self.SendCommand("push %s %s" % (src, dest), timeout_time=60)
121 def Pull(self, src, dest):
122 """Pulls the file src on the device onto dest on the host.
124 Args:
125 src: absolute file path of file on device to pull
126 dest: destination file path on host
128 Returns:
129 True if success and False otherwise.
131 # Create the base dir if it doesn't exist already
132 if not os.path.exists(os.path.dirname(dest)):
133 os.makedirs(os.path.dirname(dest))
135 if self.DoesFileExist(src):
136 self.SendCommand("pull %s %s" % (src, dest), timeout_time=60)
137 return True
138 else:
139 logger.Log("ADB Pull Failed: Source file %s does not exist." % src)
140 return False
142 def DoesFileExist(self, src):
143 """Checks if the given path exists on device target.
145 Args:
146 src: file path to be checked.
148 Returns:
149 True if file exists
152 output = self.SendShellCommand("ls %s" % src)
153 error = "No such file or directory"
155 if error in output:
156 return False
157 return True
159 def EnableAdbRoot(self):
160 """Enable adb root on device."""
161 output = self.SendCommand("root")
162 if "adbd is already running as root" in output:
163 return True
164 elif "restarting adbd as root" in output:
165 # device will disappear from adb, wait for it to come back
166 self.SendCommand("wait-for-device")
167 return True
168 else:
169 logger.Log("Unrecognized output from adb root: %s" % output)
170 return False
172 def StartInstrumentationForPackage(
173 self, package_name, runner_name, timeout_time=60*10,
174 no_window_animation=False, instrumentation_args={}):
175 """Run instrumentation test for given package and runner.
177 Equivalent to StartInstrumentation, except instrumentation path is
178 separated into its package and runner components.
180 instrumentation_path = "%s/%s" % (package_name, runner_name)
181 return self.StartInstrumentation(instrumentation_path, timeout_time=timeout_time,
182 no_window_animation=no_window_animation,
183 instrumentation_args=instrumentation_args)
185 def StartInstrumentation(
186 self, instrumentation_path, timeout_time=60*10, no_window_animation=False,
187 profile=False, instrumentation_args={}, silent_log=False):
189 """Runs an instrumentation class on the target.
191 Returns a dictionary containing the key value pairs from the
192 instrumentations result bundle and a list of TestResults. Also handles the
193 interpreting of error output from the device and raises the necessary
194 exceptions.
196 Args:
197 instrumentation_path: string. It should be the fully classified package
198 name, and instrumentation test runner, separated by "/"
199 e.g. com.android.globaltimelaunch/.GlobalTimeLaunch
200 timeout_time: Timeout value for the am command.
201 no_window_animation: boolean, Whether you want window animations enabled
202 or disabled
203 profile: If True, profiling will be turned on for the instrumentation.
204 instrumentation_args: Dictionary of key value bundle arguments to pass to
205 instrumentation.
206 silent_log: If True, the invocation of the instrumentation test runner
207 will not be logged.
209 Returns:
210 (test_results, inst_finished_bundle)
212 test_results: a list of TestResults
213 inst_finished_bundle (dict): Key/value pairs contained in the bundle that
214 is passed into ActivityManager.finishInstrumentation(). Included in this
215 bundle is the return code of the Instrumentation process, any error
216 codes reported by the activity manager, and any results explicitly added
217 by the instrumentation code.
219 Raises:
220 WaitForResponseTimedOutError: if timeout occurred while waiting for
221 response to adb instrument command
222 DeviceUnresponsiveError: if device system process is not responding
223 InstrumentationError: if instrumentation failed to run
226 command_string = self._BuildInstrumentationCommandPath(
227 instrumentation_path, no_window_animation=no_window_animation,
228 profile=profile, raw_mode=True,
229 instrumentation_args=instrumentation_args)
230 if silent_log:
231 logger.SilentLog(command_string)
232 else:
233 logger.Log(command_string)
234 (test_results, inst_finished_bundle) = (
235 am_instrument_parser.ParseAmInstrumentOutput(
236 self.SendShellCommand(command_string, timeout_time=timeout_time,
237 retry_count=2)))
238 if "code" not in inst_finished_bundle:
239 logger.Log('No code available. inst_finished_bundle contains: %s '
240 % inst_finished_bundle)
241 raise errors.InstrumentationError("no test results... device setup "
242 "correctly?")
244 if inst_finished_bundle["code"] == "0":
245 long_msg_result = "no error message"
246 if "longMsg" in inst_finished_bundle:
247 long_msg_result = inst_finished_bundle["longMsg"]
248 logger.Log("Error! Test run failed: %s" % long_msg_result)
249 raise errors.InstrumentationError(long_msg_result)
251 if "INSTRUMENTATION_ABORTED" in inst_finished_bundle:
252 logger.Log("INSTRUMENTATION ABORTED!")
253 raise errors.DeviceUnresponsiveError
255 return (test_results, inst_finished_bundle)
257 def StartInstrumentationNoResults(
258 self, package_name, runner_name, no_window_animation=False,
259 raw_mode=False, instrumentation_args={}):
260 """Runs instrumentation and dumps output to stdout.
262 Equivalent to StartInstrumentation, but will dump instrumentation
263 'normal' output to stdout, instead of parsing return results. Command will
264 never timeout.
266 adb_command_string = self.PreviewInstrumentationCommand(
267 package_name, runner_name, no_window_animation=no_window_animation,
268 raw_mode=raw_mode, instrumentation_args=instrumentation_args)
269 logger.Log(adb_command_string)
270 run_command.RunCommand(adb_command_string, return_output=False)
272 def PreviewInstrumentationCommand(
273 self, package_name, runner_name, no_window_animation=False,
274 raw_mode=False, instrumentation_args={}):
275 """Returns a string of adb command that will be executed."""
276 inst_command_string = self._BuildInstrumentationCommand(
277 package_name, runner_name, no_window_animation=no_window_animation,
278 raw_mode=raw_mode, instrumentation_args=instrumentation_args)
279 command_string = "adb %s shell %s" % (self._target_arg, inst_command_string)
280 return command_string
282 def _BuildInstrumentationCommand(
283 self, package, runner_name, no_window_animation=False, profile=False,
284 raw_mode=True, instrumentation_args={}):
285 instrumentation_path = "%s/%s" % (package, runner_name)
287 return self._BuildInstrumentationCommandPath(
288 instrumentation_path, no_window_animation=no_window_animation,
289 profile=profile, raw_mode=raw_mode,
290 instrumentation_args=instrumentation_args)
292 def _BuildInstrumentationCommandPath(
293 self, instrumentation_path, no_window_animation=False, profile=False,
294 raw_mode=True, instrumentation_args={}):
295 command_string = "am instrument"
296 if no_window_animation:
297 command_string += " --no_window_animation"
298 if profile:
299 self._CreateTraceDir()
300 command_string += (
301 " -p %s/%s.dmtrace" %
302 (self.DEVICE_TRACE_DIR, instrumentation_path.split(".")[-1]))
304 for key, value in instrumentation_args.items():
305 command_string += " -e %s '%s'" % (key, value)
306 if raw_mode:
307 command_string += " -r"
308 command_string += " -w %s" % instrumentation_path
309 return command_string
311 def _CreateTraceDir(self):
312 ls_response = self.SendShellCommand("ls /data/trace")
313 if ls_response.strip("#").strip(string.whitespace) != "":
314 self.SendShellCommand("create /data/trace", "mkdir /data/trace")
315 self.SendShellCommand("make /data/trace world writeable",
316 "chmod 777 /data/trace")
318 def WaitForDevicePm(self, wait_time=120):
319 """Waits for targeted device's package manager to be up.
321 Args:
322 wait_time: time in seconds to wait
324 Raises:
325 WaitForResponseTimedOutError if wait_time elapses and pm still does not
326 respond.
328 logger.Log("Waiting for device package manager...")
329 self.SendCommand("wait-for-device", timeout_time=wait_time, retry_count=0)
330 # Now the device is there, but may not be running.
331 # Query the package manager with a basic command
332 try:
333 self._WaitForShellCommandContents("pm path android", "package:",
334 wait_time)
335 except errors.WaitForResponseTimedOutError:
336 raise errors.WaitForResponseTimedOutError(
337 "Package manager did not respond after %s seconds" % wait_time)
339 def WaitForInstrumentation(self, package_name, runner_name, wait_time=120):
340 """Waits for given instrumentation to be present on device
342 Args:
343 wait_time: time in seconds to wait
345 Raises:
346 WaitForResponseTimedOutError if wait_time elapses and instrumentation
347 still not present.
349 instrumentation_path = "%s/%s" % (package_name, runner_name)
350 logger.Log("Waiting for instrumentation to be present")
351 # Query the package manager
352 try:
353 command = "pm list instrumentation | grep %s" % instrumentation_path
354 self._WaitForShellCommandContents(command, "instrumentation:", wait_time,
355 raise_abort=False)
356 except errors.WaitForResponseTimedOutError :
357 logger.Log(
358 "Could not find instrumentation %s on device. Does the "
359 "instrumentation in test's AndroidManifest.xml match definition"
360 "in test_defs.xml?" % instrumentation_path)
361 raise
363 def WaitForProcess(self, name, wait_time=120):
364 """Wait until a process is running on the device.
366 Args:
367 name: the process name as it appears in `ps`
368 wait_time: time in seconds to wait
370 Raises:
371 WaitForResponseTimedOutError if wait_time elapses and the process is
372 still not running
374 logger.Log("Waiting for process %s" % name)
375 self.SendCommand("wait-for-device")
376 self._WaitForShellCommandContents("ps", name, wait_time)
378 def WaitForProcessEnd(self, name, wait_time=120):
379 """Wait until a process is no longer running on the device.
381 Args:
382 name: the process name as it appears in `ps`
383 wait_time: time in seconds to wait
385 Raises:
386 WaitForResponseTimedOutError if wait_time elapses and the process is
387 still running
389 logger.Log("Waiting for process %s to end" % name)
390 self._WaitForShellCommandContents("ps", name, wait_time, invert=True)
392 def _WaitForShellCommandContents(self, command, expected, wait_time,
393 raise_abort=True, invert=False):
394 """Wait until the response to a command contains a given output.
396 Assumes that a only successful execution of "adb shell <command>" contains
397 the substring expected. Assumes that a device is present.
399 Args:
400 command: adb shell command to execute
401 expected: the string that should appear to consider the
402 command successful.
403 wait_time: time in seconds to wait
404 raise_abort: if False, retry when executing the command raises an
405 AbortError, rather than failing.
406 invert: if True, wait until the command output no longer contains the
407 expected contents.
409 Raises:
410 WaitForResponseTimedOutError: If wait_time elapses and the command has not
411 returned an output containing expected yet.
413 # Query the device with the command
414 success = False
415 attempts = 0
416 wait_period = 5
417 while not success and (attempts*wait_period) < wait_time:
418 # assume the command will always contain expected in the success case
419 try:
420 output = self.SendShellCommand(command, retry_count=1,
421 timeout_time=wait_time)
422 if ((not invert and expected in output)
423 or (invert and expected not in output)):
424 success = True
425 except errors.AbortError, e:
426 if raise_abort:
427 raise
428 # ignore otherwise
430 if not success:
431 time.sleep(wait_period)
432 attempts += 1
434 if not success:
435 raise errors.WaitForResponseTimedOutError()
437 def WaitForBootComplete(self, wait_time=120):
438 """Waits for targeted device's bootcomplete flag to be set.
440 Args:
441 wait_time: time in seconds to wait
443 Raises:
444 WaitForResponseTimedOutError if wait_time elapses and pm still does not
445 respond.
447 logger.Log("Waiting for boot complete...")
448 self.SendCommand("wait-for-device")
449 # Now the device is there, but may not be running.
450 # Query the package manager with a basic command
451 boot_complete = False
452 attempts = 0
453 wait_period = 5
454 while not boot_complete and (attempts*wait_period) < wait_time:
455 output = self.SendShellCommand("getprop dev.bootcomplete", retry_count=1)
456 output = output.strip()
457 if output == "1":
458 boot_complete = True
459 else:
460 time.sleep(wait_period)
461 attempts += 1
462 if not boot_complete:
463 raise errors.WaitForResponseTimedOutError(
464 "dev.bootcomplete flag was not set after %s seconds" % wait_time)
466 def Sync(self, retry_count=3, runtime_restart=False):
467 """Perform a adb sync.
469 Blocks until device package manager is responding.
471 Args:
472 retry_count: number of times to retry sync before failing
473 runtime_restart: stop runtime during sync and restart afterwards, useful
474 for syncing system libraries (core, framework etc)
476 Raises:
477 WaitForResponseTimedOutError if package manager does not respond
478 AbortError if unrecoverable error occurred
480 output = ""
481 error = None
482 if runtime_restart:
483 self.SendShellCommand("setprop ro.monkey 1", retry_count=retry_count)
484 # manual rest bootcomplete flag
485 self.SendShellCommand("setprop dev.bootcomplete 0",
486 retry_count=retry_count)
487 self.SendShellCommand("stop", retry_count=retry_count)
489 try:
490 output = self.SendCommand("sync", retry_count=retry_count)
491 except errors.AbortError, e:
492 error = e
493 output = e.msg
494 if "Read-only file system" in output:
495 logger.SilentLog(output)
496 logger.Log("Remounting read-only filesystem")
497 self.SendCommand("remount")
498 output = self.SendCommand("sync", retry_count=retry_count)
499 elif "No space left on device" in output:
500 logger.SilentLog(output)
501 logger.Log("Restarting device runtime")
502 self.SendShellCommand("stop", retry_count=retry_count)
503 output = self.SendCommand("sync", retry_count=retry_count)
504 self.SendShellCommand("start", retry_count=retry_count)
505 elif error is not None:
506 # exception occurred that cannot be recovered from
507 raise error
508 logger.SilentLog(output)
509 if runtime_restart:
510 # start runtime and wait till boot complete flag is set
511 self.SendShellCommand("start", retry_count=retry_count)
512 self.WaitForBootComplete()
513 # press the MENU key, this will disable key guard if runtime is started
514 # with ro.monkey set to 1
515 self.SendShellCommand("input keyevent 82", retry_count=retry_count)
516 else:
517 self.WaitForDevicePm()
518 return output
520 def GetSerialNumber(self):
521 """Returns the serial number of the targeted device."""
522 return self.SendCommand("get-serialno").strip()