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