diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 17:04:45 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 17:04:45 -0800 |
commit | 2b83cbdb14fcf0307e87b67d46ba17aab4c22a28 (patch) | |
tree | 2641da65fae26fae3fc2cfe06f5ef5d8b80120e8 /testrunner/adb_interface.py | |
parent | edd86fdaa9a52c1f33e27e6e67ffb27fb1521be4 (diff) | |
download | android_development-2b83cbdb14fcf0307e87b67d46ba17aab4c22a28.tar.gz android_development-2b83cbdb14fcf0307e87b67d46ba17aab4c22a28.tar.bz2 android_development-2b83cbdb14fcf0307e87b67d46ba17aab4c22a28.zip |
auto import from //depot/cupcake/@136654
Diffstat (limited to 'testrunner/adb_interface.py')
-rwxr-xr-x | testrunner/adb_interface.py | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/testrunner/adb_interface.py b/testrunner/adb_interface.py new file mode 100755 index 000000000..fb304df9d --- /dev/null +++ b/testrunner/adb_interface.py @@ -0,0 +1,347 @@ +#!/usr/bin/python2.4 +# +# +# Copyright 2008, The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Provides an interface to communicate with the device via the adb command. + +Assumes adb binary is currently on system path. +""" +# Python imports +import os +import string +import time + +# local imports +import am_instrument_parser +import errors +import logger +import run_command + + +class AdbInterface: + """Helper class for communicating with Android device via adb.""" + + # argument to pass to adb, to direct command to specific device + _target_arg = "" + + DEVICE_TRACE_DIR = "/data/test_results/" + + def SetEmulatorTarget(self): + """Direct all future commands to the only running emulator.""" + self._target_arg = "-e" + + def SetDeviceTarget(self): + """Direct all future commands to the only connected USB device.""" + self._target_arg = "-d" + + def SetTargetSerial(self, serial): + """Direct all future commands to Android target with the given serial.""" + self._target_arg = "-s %s" % serial + + def SendCommand(self, command_string, timeout_time=20, retry_count=3): + """Send a command via adb. + + Args: + command_string: adb command to run + timeout_time: number of seconds to wait for command to respond before + retrying + retry_count: number of times to retry command before raising + WaitForResponseTimedOutError + Returns: + string output of command + + Raises: + WaitForResponseTimedOutError if device does not respond to command + """ + adb_cmd = "adb %s %s" % (self._target_arg, command_string) + logger.SilentLog("about to run %s" % adb_cmd) + return run_command.RunCommand(adb_cmd, timeout_time=timeout_time, + retry_count=retry_count) + + def SendShellCommand(self, cmd, timeout_time=20, retry_count=3): + """Send a adb shell command. + + Args: + cmd: adb shell command to run + timeout_time: number of seconds to wait for command to respond before + retrying + retry_count: number of times to retry command before raising + WaitForResponseTimedOutError + + Returns: + string output of command + + Raises: + WaitForResponseTimedOutError: if device does not respond to command + """ + return self.SendCommand("shell %s" % cmd, timeout_time=timeout_time, + retry_count=retry_count) + + def BugReport(self, path): + """Dumps adb bugreport to the file specified by the path. + + Args: + path: Path of the file where adb bugreport is dumped to. + """ + bug_output = self.SendShellCommand("bugreport", timeout_time=60) + bugreport_file = open(path, "w") + bugreport_file.write(bug_output) + bugreport_file.close() + + def Push(self, src, dest): + """Pushes the file src onto the device at dest. + + Args: + src: file path of host file to push + dest: destination absolute file path on device + """ + self.SendCommand("push %s %s" % (src, dest), timeout_time=60) + + def Pull(self, src, dest): + """Pulls the file src on the device onto dest on the host. + + Args: + src: absolute file path of file on device to pull + dest: destination file path on host + + Returns: + True if success and False otherwise. + """ + # Create the base dir if it doesn't exist already + if not os.path.exists(os.path.dirname(dest)): + os.makedirs(os.path.dirname(dest)) + + if self.DoesFileExist(src): + self.SendCommand("pull %s %s" % (src, dest), timeout_time=60) + return True + else: + logger.Log("ADB Pull Failed: Source file %s does not exist." % src) + return False + + def DoesFileExist(self, src): + """Checks if the given path exists on device target. + + Args: + src: file path to be checked. + + Returns: + True if file exists + """ + + output = self.SendShellCommand("ls %s" % src) + error = "No such file or directory" + + if error in output: + return False + return True + + def StartInstrumentationForPackage( + self, package_name, runner_name, timeout_time=60*10, + no_window_animation=False, instrumentation_args={}): + """Run instrumentation test for given package and runner. + + Equivalent to StartInstrumentation, except instrumentation path is + separated into its package and runner components. + """ + instrumentation_path = "%s/%s" % (package_name, runner_name) + return self.StartInstrumentation(self, instrumentation_path, timeout_time, + no_window_animation, instrumentation_args) + + def StartInstrumentation( + self, instrumentation_path, timeout_time=60*10, no_window_animation=False, + profile=False, instrumentation_args={}): + + """Runs an instrumentation class on the target. + + Returns a dictionary containing the key value pairs from the + instrumentations result bundle and a list of TestResults. Also handles the + interpreting of error output from the device and raises the necessary + exceptions. + + Args: + instrumentation_path: string. It should be the fully classified package + name, and instrumentation test runner, separated by "/" + e.g. com.android.globaltimelaunch/.GlobalTimeLaunch + timeout_time: Timeout value for the am command. + no_window_animation: boolean, Whether you want window animations enabled + or disabled + profile: If True, profiling will be turned on for the instrumentation. + instrumentation_args: Dictionary of key value bundle arguments to pass to + instrumentation. + + Returns: + (test_results, inst_finished_bundle) + + test_results: a list of TestResults + inst_finished_bundle (dict): Key/value pairs contained in the bundle that + is passed into ActivityManager.finishInstrumentation(). Included in this + bundle is the return code of the Instrumentation process, any error + codes reported by the activity manager, and any results explicitly added + by the instrumentation code. + + Raises: + WaitForResponseTimedOutError: if timeout occurred while waiting for + response to adb instrument command + DeviceUnresponsiveError: if device system process is not responding + InstrumentationError: if instrumentation failed to run + """ + + command_string = self._BuildInstrumentationCommandPath( + instrumentation_path, no_window_animation=no_window_animation, + profile=profile, raw_mode=True, + instrumentation_args=instrumentation_args) + + (test_results, inst_finished_bundle) = ( + am_instrument_parser.ParseAmInstrumentOutput( + self.SendShellCommand(command_string, timeout_time=timeout_time, + retry_count=2))) + + if "code" not in inst_finished_bundle: + raise errors.InstrumentationError("no test results... device setup " + "correctly?") + + if inst_finished_bundle["code"] == "0": + short_msg_result = "no error message" + if "shortMsg" in inst_finished_bundle: + short_msg_result = inst_finished_bundle["shortMsg"] + logger.Log(short_msg_result) + raise errors.InstrumentationError(short_msg_result) + + if "INSTRUMENTATION_ABORTED" in inst_finished_bundle: + logger.Log("INSTRUMENTATION ABORTED!") + raise errors.DeviceUnresponsiveError + + return (test_results, inst_finished_bundle) + + def StartInstrumentationNoResults( + self, package_name, runner_name, no_window_animation=False, + raw_mode=False, instrumentation_args={}): + """Runs instrumentation and dumps output to stdout. + + Equivalent to StartInstrumentation, but will dump instrumentation + 'normal' output to stdout, instead of parsing return results. Command will + never timeout. + """ + adb_command_string = self.PreviewInstrumentationCommand( + package_name, runner_name, no_window_animation=no_window_animation, + raw_mode=raw_mode, instrumentation_args=instrumentation_args) + logger.Log(adb_command_string) + run_command.RunCommand(adb_command_string, return_output=False) + + def PreviewInstrumentationCommand( + self, package_name, runner_name, no_window_animation=False, + raw_mode=False, instrumentation_args={}): + """Returns a string of adb command that will be executed.""" + inst_command_string = self._BuildInstrumentationCommand( + package_name, runner_name, no_window_animation=no_window_animation, + raw_mode=raw_mode, instrumentation_args=instrumentation_args) + command_string = "adb %s shell %s" % (self._target_arg, inst_command_string) + return command_string + + def _BuildInstrumentationCommand( + self, package, runner_name, no_window_animation=False, profile=False, + raw_mode=True, instrumentation_args={}): + instrumentation_path = "%s/%s" % (package, runner_name) + + return self._BuildInstrumentationCommandPath( + instrumentation_path, no_window_animation=no_window_animation, + profile=profile, raw_mode=raw_mode, + instrumentation_args=instrumentation_args) + + def _BuildInstrumentationCommandPath( + self, instrumentation_path, no_window_animation=False, profile=False, + raw_mode=True, instrumentation_args={}): + command_string = "am instrument" + if no_window_animation: + command_string += " --no_window_animation" + if profile: + self._CreateTraceDir() + command_string += ( + " -p %s/%s.dmtrace" % + (self.DEVICE_TRACE_DIR, instrumentation_path.split(".")[-1])) + + for key, value in instrumentation_args.items(): + command_string += " -e %s %s" % (key, value) + if raw_mode: + command_string += " -r" + command_string += " -w %s" % instrumentation_path + return command_string + + def _CreateTraceDir(self): + ls_response = self.SendShellCommand("ls /data/trace") + if ls_response.strip("#").strip(string.whitespace) != "": + self.SendShellCommand("create /data/trace", "mkdir /data/trace") + self.SendShellCommand("make /data/trace world writeable", + "chmod 777 /data/trace") + + def WaitForDevicePm(self, wait_time=120): + """Waits for targeted device's package manager to be up. + + Args: + wait_time: time in seconds to wait + + Raises: + WaitForResponseTimedOutError if wait_time elapses and pm still does not + respond. + """ + logger.Log("Waiting for device package manager for %s seconds..." + % wait_time) + self.SendCommand("wait-for-device") + # Now the device is there, but may not be running. + # Query the package manager with a basic command + pm_found = False + attempts = 0 + wait_period = 5 + while not pm_found and (attempts*wait_period) < wait_time: + # assume the 'adb shell pm path android' command will always + # return 'package: something' in the success case + output = self.SendShellCommand("pm path android", retry_count=1) + if "package:" in output: + pm_found = True + else: + time.sleep(wait_period) + attempts += 1 + if not pm_found: + raise errors.WaitForResponseTimedOutError + + def Sync(self, retry_count=3): + """Perform a adb sync. + + Blocks until device package manager is responding. + + Args: + retry_count: number of times to retry sync before failing + + Raises: + WaitForResponseTimedOutError if package manager does not respond + """ + output = self.SendCommand("sync", retry_count=retry_count) + if "Read-only file system" in output: + logger.SilentLog(output) + logger.Log("adb sync failed due to read only fs, retrying") + self.SendCommand("remount") + output = self.SendCommand("sync", retry_count=retry_count) + if "No space left on device" in output: + logger.SilentLog(output) + logger.Log("adb sync failed due to no space on device, trying shell" + + " start/stop") + self.SendShellCommand("stop", retry_count=retry_count) + output = self.SendCommand("sync", retry_count=retry_count) + self.SendShellCommand("start", retry_count=retry_count) + + logger.SilentLog(output) + self.WaitForDevicePm() + return output |