mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 21:05:36 +00:00
Backed out changeset 1fd65e3c2f4a (bug 1012712)
This commit is contained in:
parent
5de1cd29b1
commit
952ae2f289
@ -1,19 +1,11 @@
|
||||
:mod:`mozdevice` --- Interact with remote devices
|
||||
=================================================
|
||||
|
||||
Mozdevice provides several interfaces to interact with a remote device
|
||||
such as an Android- or FirefoxOS-based phone. It allows you to push
|
||||
files to these types of devices, launch processes, and more. There are
|
||||
currently two available interfaces:
|
||||
|
||||
* DeviceManager: an interface to a device that works either via ADB or
|
||||
a custom TCP protocol (the latter requires an agent application
|
||||
running on the device).
|
||||
* ADB: a similar interface that uses Android Debugger Protocol
|
||||
explicitly
|
||||
|
||||
In general, new code should use the ADB abstraction where possible as
|
||||
it is simpler and more reliable.
|
||||
Mozdevice provides an interface to interact with a remote device such
|
||||
as an Android- or FirefoxOS-based phone connected to a
|
||||
host machine. Currently there are two implementations of the interface: one
|
||||
uses a custom TCP-based protocol to communicate with a server running
|
||||
on the device, another uses Android's adb utility.
|
||||
|
||||
.. automodule:: mozdevice
|
||||
|
||||
@ -127,109 +119,3 @@ and DeviceManagerSUT. Here is the interface for DroidADB:
|
||||
.. automethod:: mozdevice.DroidADB.getInstalledApps
|
||||
|
||||
These methods are also found in the DroidSUT class.
|
||||
|
||||
ADB Interface
|
||||
-------------
|
||||
|
||||
The following classes provide a basic interface to interact with the
|
||||
Android Debug Tool (adb) and Android-based devices. It is intended to
|
||||
provide the basis for a replacement for DeviceManager and
|
||||
DeviceManagerADB.
|
||||
|
||||
ADBCommand
|
||||
``````````
|
||||
|
||||
.. autoclass:: mozdevice.ADBCommand
|
||||
|
||||
.. automethod:: ADBCommand.command(self, cmds, timeout=None)
|
||||
.. automethod:: ADBCommand.command_output(self, cmds, timeout=None)
|
||||
|
||||
ADBHost
|
||||
```````
|
||||
.. autoclass:: mozdevice.ADBHost
|
||||
|
||||
.. automethod:: ADBHost.command(self, cmds, timeout=None)
|
||||
.. automethod:: ADBHost.command_output(self, cmds, timeout=None)
|
||||
.. automethod:: ADBHost.start_server(self, timeout=None)
|
||||
.. automethod:: ADBHost.kill_server(self, cmds, timeout=None)
|
||||
.. automethod:: ADBHost.devices(self, timeout=None)
|
||||
|
||||
ADBDevice
|
||||
`````````
|
||||
.. autoclass:: mozdevice.ADBDevice
|
||||
|
||||
Host Command methods
|
||||
++++++++++++++++++++
|
||||
.. automethod:: ADBDevice.command(self, cmds, timeout=None)
|
||||
.. automethod:: ADBDevice.command_output(self, cmds, timeout=None)
|
||||
|
||||
Device Shell methods
|
||||
++++++++++++++++++++
|
||||
.. automethod:: ADBDevice.shell(self, cmd, env=None, cwd=None, timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.shell_bool(self, cmd, env=None, cwd=None, timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.shell_output(self, cmd, env=None, cwd=None, timeout=None, root=False)
|
||||
|
||||
Informational methods
|
||||
+++++++++++++++++++++
|
||||
.. automethod:: ADBDevice.clear_logcat(self, timeout=None)
|
||||
.. automethod:: ADBDevice.get_logcat(self, filterSpecs=["dalvikvm:I", "ConnectivityService:S", "WifiMonitor:S", "WifiStateTracker:S", "wpa_supplicant:S", "NetworkStateTracker:S"], format="time", filter_out_regexps=[], timeout=None)
|
||||
.. automethod:: ADBDevice.get_prop(self, prop, timeout=None)
|
||||
.. automethod:: ADBDevice.get_state(self, timeout=None)
|
||||
|
||||
File management methods
|
||||
+++++++++++++++++++++++
|
||||
.. automethod:: ADBDevice.chmod(self, path, recursive=False, mask="777", timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.exists(self, path, timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.is_dir(self, path, timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.is_file(self, path, timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.list_files(self, path, timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.mkdir(self, path, parents=False, timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.push(self, local, remote, timeout=None)
|
||||
.. automethod:: ADBDevice.rm(self, path, recursive=False, force=False, timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.rmdir(self, path, timeout=None, root=False)
|
||||
|
||||
Process management methods
|
||||
++++++++++++++++++++++++++
|
||||
.. automethod:: ADBDevice.get_process_list(self, timeout=None)
|
||||
.. automethod:: ADBDevice.kill(self, pids, sig=None, attempts=3, wait=5, timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.pkill(self, appname, sig=None, attempts=3, wait=5, timeout=None, root=False)
|
||||
.. automethod:: ADBDevice.process_exist(self, process_name, timeout=None)
|
||||
|
||||
|
||||
ADBAndroid
|
||||
``````````
|
||||
.. autoclass:: ADBAndroid
|
||||
|
||||
System control methods
|
||||
++++++++++++++++++++++
|
||||
.. automethod:: ADBAndroid.is_device_ready(self, timeout=None)
|
||||
.. automethod:: ADBAndroid.power_on(self, timeout=None)
|
||||
.. automethod:: ADBAndroid.reboot(self, timeout=None)
|
||||
|
||||
Application management methods
|
||||
++++++++++++++++++++++++++++++
|
||||
.. automethod:: ADBAndroid.install_app(self, apk_path, timeout=None)
|
||||
.. automethod:: ADBAndroid.is_app_installed(self, app_name, timeout=None)
|
||||
.. automethod:: ADBAndroid.launch_application(self, app_name, activity_name, intent, url=None, extras=None, wait=True, fail_if_running=True, timeout=None)
|
||||
.. automethod:: ADBAndroid.launch_fennec(self, app_name, intent="android.intent.action.VIEW", moz_env=None, extra_args=None, url=None, wait=True, fail_if_running=True, timeout=None)
|
||||
.. automethod:: ADBAndroid.stop_application(self, app_name, timeout=None, root=False)
|
||||
.. automethod:: ADBAndroid.uninstall_app(self, app_name, reboot=False, timeout=None)
|
||||
.. automethod:: ADBAndroid.update_app(self, apk_path, timeout=None)
|
||||
|
||||
|
||||
ADBProcess
|
||||
``````````
|
||||
.. autoclass:: mozdevice.ADBProcess
|
||||
|
||||
ADBError
|
||||
````````
|
||||
.. autoexception:: mozdevice.ADBError
|
||||
|
||||
ADBRootError
|
||||
````````````
|
||||
.. autoexception:: mozdevice.ADBRootError
|
||||
|
||||
ADBTimeoutError
|
||||
```````````````
|
||||
.. autoexception:: mozdevice.ADBTimeoutError
|
||||
|
||||
|
@ -2,8 +2,6 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from adb import ADBError, ADBRootError, ADBTimeoutError, ADBProcess, ADBCommand, ADBHost, ADBDevice
|
||||
from adb_android import ADBAndroid
|
||||
from devicemanager import DeviceManager, DMError, ZeroconfListener
|
||||
from devicemanagerADB import DeviceManagerADB
|
||||
from devicemanagerSUT import DeviceManagerSUT
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,379 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
from adb import ADBDevice, ADBError
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
|
||||
class ADBAndroidMixin(object):
|
||||
"""Mixin to extend ADB with Android-specific functionality"""
|
||||
|
||||
# System control methods
|
||||
|
||||
def is_device_ready(self, timeout=None):
|
||||
"""Checks if a device is ready for testing.
|
||||
|
||||
This method uses the android only package manager to check for
|
||||
readiness.
|
||||
|
||||
:param timeout: optional integer specifying the maximum time
|
||||
in seconds for any spawned adb process to complete before
|
||||
throwing an ADBTimeoutError.
|
||||
This timeout is per adb call. The total time spent
|
||||
may exceed this value. If it is not specified, the value
|
||||
set in the ADB constructor is used.
|
||||
:raises: * ADBTimeoutError - raised if the command takes longer than
|
||||
timeout seconds.
|
||||
* ADBError - raised if the wait-for-device command fails.
|
||||
|
||||
"""
|
||||
self.command_output(["wait-for-device"], timeout=timeout)
|
||||
pm_error_string = "Error: Could not access the Package Manager"
|
||||
pm_list_commands = ["packages", "permission-groups", "permissions",
|
||||
"instrumentation", "features", "libraries"]
|
||||
ready_path = os.path.join(self.test_root, "ready")
|
||||
for attempt in range(self._device_ready_retry_attempts):
|
||||
success = True
|
||||
try:
|
||||
if self.get_state(timeout=timeout) != 'device':
|
||||
success = False
|
||||
else:
|
||||
if self.is_dir(ready_path, timeout=timeout):
|
||||
self.rmdir(ready_path, timeout=timeout)
|
||||
self.mkdir(ready_path, timeout=timeout)
|
||||
self.rmdir(ready_path, timeout=timeout)
|
||||
# Invoke the pm list commands to see if it is up and
|
||||
# running.
|
||||
for pm_list_cmd in pm_list_commands:
|
||||
data = self.shell_output("pm list %s" % pm_list_cmd,
|
||||
timeout=timeout)
|
||||
if pm_error_string in data:
|
||||
success = False
|
||||
break
|
||||
except ADBError, e:
|
||||
success = False
|
||||
data = e.message
|
||||
|
||||
if not success:
|
||||
self._logger.debug('Attempt %s of %s device not ready: %s' % (
|
||||
attempt+1, self._device_ready_retry_attempts,
|
||||
data))
|
||||
time.sleep(self._device_ready_retry_wait)
|
||||
|
||||
return success
|
||||
|
||||
def power_on(self, timeout=None):
|
||||
"""Sets the device's power stayon value.
|
||||
|
||||
:param timeout: optional integer specifying the maximum time in
|
||||
seconds for any spawned adb process to complete before
|
||||
throwing an ADBTimeoutError.
|
||||
This timeout is per adb call. The total time spent
|
||||
may exceed this value. If it is not specified, the value
|
||||
set in the ADB constructor is used.
|
||||
:raises: * ADBTimeoutError - raised if the command takes longer than
|
||||
timeout seconds.
|
||||
* ADBError - raised if the command exits with a
|
||||
non-zero exit code.
|
||||
|
||||
"""
|
||||
try:
|
||||
self.shell_output('svc power stayon true', timeout=timeout)
|
||||
except ADBError, e:
|
||||
# Executing this via adb shell errors, but not interactively.
|
||||
# Any other exitcode is a real error.
|
||||
if 'exitcode: 137' not in e.message:
|
||||
raise
|
||||
self._logger.warning('Unable to set power stayon true: %s' % e)
|
||||
|
||||
def reboot(self, timeout=None):
|
||||
"""Reboots the device.
|
||||
|
||||
This method uses the Android only package manager to determine
|
||||
if the device is ready after the reboot.
|
||||
|
||||
:param timeout: optional integer specifying the maximum time in
|
||||
seconds for any spawned adb process to complete before
|
||||
throwing an ADBTimeoutError.
|
||||
This timeout is per adb call. The total time spent
|
||||
may exceed this value. If it is not specified, the value
|
||||
set in the ADB constructor is used.
|
||||
:raises: * ADBTimeoutError - raised if the command takes longer than
|
||||
timeout seconds.
|
||||
* ADBError - raised if the command exits with a
|
||||
non-zero exit code.
|
||||
|
||||
reboot() reboots the device, issues an adb wait-for-device in order to
|
||||
wait for the device to complete rebooting, then calls is_device_ready()
|
||||
to determine if the device has completed booting.
|
||||
|
||||
"""
|
||||
self.command_output(["reboot"], timeout=timeout)
|
||||
self.command_output(["wait-for-device"], timeout=timeout)
|
||||
return self.is_device_ready(timeout=timeout)
|
||||
|
||||
# Application management methods
|
||||
|
||||
def install_app(self, apk_path, timeout=None):
|
||||
"""Installs an app on the device.
|
||||
|
||||
:param apk_path: string containing the apk file name to be
|
||||
installed.
|
||||
:param timeout: optional integer specifying the maximum time in
|
||||
seconds for any spawned adb process to complete before
|
||||
throwing an ADBTimeoutError.
|
||||
This timeout is per adb call. The total time spent
|
||||
may exceed this value. If it is not specified, the value
|
||||
set in the ADB constructor is used.
|
||||
:raises: * ADBTimeoutError - raised if the command takes longer than
|
||||
timeout seconds.
|
||||
* ADBError - raised if the command exits with a
|
||||
non-zero exit code.
|
||||
|
||||
"""
|
||||
data = self.command_output(["install", apk_path], timeout=timeout)
|
||||
if data.find('Success') == -1:
|
||||
raise ADBError("install failed for %s. Got: %s" %
|
||||
(apk_path, data))
|
||||
|
||||
def is_app_installed(self, app_name, timeout=None):
|
||||
"""Returns True if an app is installed on the device.
|
||||
|
||||
:param app_name: string containing the name of the app to be
|
||||
checked.
|
||||
:param timeout: optional integer specifying the maximum time in
|
||||
seconds for any spawned adb process to complete before
|
||||
throwing an ADBTimeoutError.
|
||||
This timeout is per adb call. The total time spent
|
||||
may exceed this value. If it is not specified, the value
|
||||
set in the ADB constructor is used.
|
||||
:raises: * ADBTimeoutError - raised if the command takes longer than
|
||||
timeout seconds.
|
||||
* ADBError - raised if the command exits with a
|
||||
non-zero exit code.
|
||||
|
||||
"""
|
||||
pm_error_string = 'Error: Could not access the Package Manager'
|
||||
data = self.shell_output("pm list package %s" % app_name, timeout=timeout)
|
||||
if pm_error_string in data:
|
||||
raise ADBError(pm_error_string)
|
||||
if app_name not in data:
|
||||
return False
|
||||
return True
|
||||
|
||||
def launch_application(self, app_name, activity_name, intent, url=None,
|
||||
extras=None, wait=True, fail_if_running=True,
|
||||
timeout=None):
|
||||
"""Launches an Android application
|
||||
|
||||
:param app_name: Name of application (e.g. `com.android.chrome`)
|
||||
:param activity_name: Name of activity to launch (e.g. `.Main`)
|
||||
:param intent: Intent to launch application with
|
||||
:param url: URL to open
|
||||
:param extras: Dictionary of extra arguments for application.
|
||||
:param wait: If True, wait for application to start before
|
||||
returning.
|
||||
:param fail_if_running: Raise an exception if instance of
|
||||
application is already running.
|
||||
:param timeout: optional integer specifying the maximum time in
|
||||
seconds for any spawned adb process to complete before
|
||||
throwing an ADBTimeoutError.
|
||||
This timeout is per adb call. The total time spent
|
||||
may exceed this value. If it is not specified, the value
|
||||
set in the ADB constructor is used.
|
||||
:raises: * ADBTimeoutError - raised if the command takes longer than
|
||||
timeout seconds.
|
||||
* ADBError - raised if the command exits with a
|
||||
non-zero exit code.
|
||||
|
||||
"""
|
||||
# If fail_if_running is True, we throw an exception here. Only one
|
||||
# instance of an application can be running at once on Android,
|
||||
# starting a new instance may not be what we want depending on what
|
||||
# we want to do
|
||||
if fail_if_running and self.process_exist(app_name, timeout=timeout):
|
||||
raise ADBError("Only one instance of an application may be running "
|
||||
"at once")
|
||||
|
||||
acmd = [ "am", "start" ] + \
|
||||
["-W" if wait else '', "-n", "%s/%s" % (app_name, activity_name)]
|
||||
|
||||
if intent:
|
||||
acmd.extend(["-a", intent])
|
||||
|
||||
if extras:
|
||||
for (key, val) in extras.iteritems():
|
||||
if type(val) is int:
|
||||
extra_type_param = "--ei"
|
||||
elif type(val) is bool:
|
||||
extra_type_param = "--ez"
|
||||
else:
|
||||
extra_type_param = "--es"
|
||||
acmd.extend([extra_type_param, str(key), str(val)])
|
||||
|
||||
if url:
|
||||
acmd.extend(["-d", url])
|
||||
|
||||
cmd = self._escape_command_line(acmd)
|
||||
self.shell_output(cmd, timeout=timeout)
|
||||
|
||||
def launch_fennec(self, app_name, intent="android.intent.action.VIEW",
|
||||
moz_env=None, extra_args=None, url=None, wait=True,
|
||||
fail_if_running=True, timeout=None):
|
||||
"""Convenience method to launch Fennec on Android with various
|
||||
debugging arguments
|
||||
|
||||
:param app_name: Name of fennec application (e.g.
|
||||
`org.mozilla.fennec`)
|
||||
:param intent: Intent to launch application.
|
||||
:param moz_env: Mozilla specific environment to pass into
|
||||
application.
|
||||
:param extra_args: Extra arguments to be parsed by fennec.
|
||||
:param url: URL to open
|
||||
:param wait: If True, wait for application to start before
|
||||
returning
|
||||
:param fail_if_running: Raise an exception if instance of
|
||||
application is already running
|
||||
:param timeout: optional integer specifying the maximum time in
|
||||
seconds for any spawned adb process to complete before
|
||||
throwing an ADBTimeoutError.
|
||||
This timeout is per adb call. The total time spent
|
||||
may exceed this value. If it is not specified, the value
|
||||
set in the ADB constructor is used.
|
||||
:raises: * ADBTimeoutError - raised if the command takes longer than
|
||||
timeout seconds.
|
||||
* ADBError - raised if the command exits with a
|
||||
non-zero exit code.
|
||||
|
||||
"""
|
||||
extras = {}
|
||||
|
||||
if moz_env:
|
||||
# moz_env is expected to be a dictionary of environment variables:
|
||||
# Fennec itself will set them when launched
|
||||
for (env_count, (env_key, env_val)) in enumerate(moz_env.iteritems()):
|
||||
extras["env" + str(env_count)] = env_key + "=" + env_val
|
||||
|
||||
# Additional command line arguments that fennec will read and use (e.g.
|
||||
# with a custom profile)
|
||||
if extra_args:
|
||||
extras['args'] = " ".join(extra_args)
|
||||
|
||||
self.launch_application(app_name, ".App", intent, url=url, extras=extras,
|
||||
wait=wait, fail_if_running=fail_if_running,
|
||||
timeout=timeout)
|
||||
|
||||
def stop_application(self, app_name, timeout=None, root=False):
|
||||
"""Stops the specified application
|
||||
|
||||
For Android 3.0+, we use the "am force-stop" to do this, which
|
||||
is reliable and does not require root. For earlier versions of
|
||||
Android, we simply try to manually kill the processes started
|
||||
by the app repeatedly until none is around any more. This is
|
||||
less reliable and does require root.
|
||||
|
||||
:param app_name: Name of application (e.g. `com.android.chrome`)
|
||||
:param timeout: optional integer specifying the maximum time in
|
||||
seconds for any spawned adb process to complete before
|
||||
throwing an ADBTimeoutError.
|
||||
This timeout is per adb call. The total time spent
|
||||
may exceed this value. If it is not specified, the value
|
||||
set in the ADB constructor is used.
|
||||
:raises: * ADBTimeoutError - raised if the command takes longer than
|
||||
timeout seconds.
|
||||
* ADBError - raised if the command exits with a
|
||||
non-zero exit code.
|
||||
|
||||
"""
|
||||
version = self.shell_output("getprop ro.build.version.release",
|
||||
timeout=timeout, root=root)
|
||||
if StrictVersion(version) >= StrictVersion('3.0'):
|
||||
self.shell_output("am force-stop %s" % app_name,
|
||||
timeout=timeout, root=root)
|
||||
else:
|
||||
num_tries = 0
|
||||
max_tries = 5
|
||||
while self.process_exist(app_name, timeout=timeout):
|
||||
if num_tries > max_tries:
|
||||
raise ADBError("Couldn't successfully kill %s after %s "
|
||||
"tries" % (app_name, max_tries))
|
||||
self.pkill(app_name, timeout=timeout, root=root)
|
||||
num_tries += 1
|
||||
|
||||
# sleep for a short duration to make sure there are no
|
||||
# additional processes in the process of being launched
|
||||
# (this is not 100% guaranteed to work since it is inherently
|
||||
# racey, but it's the best we can do)
|
||||
time.sleep(1)
|
||||
|
||||
def uninstall_app(self, app_name, reboot=False, timeout=None):
|
||||
"""Uninstalls an app on the device.
|
||||
|
||||
:param app_name: string containing the name of the app to be
|
||||
uninstalled.
|
||||
:param reboot: boolean flag indicating that the device should
|
||||
be rebooted after the app is uninstalled. No reboot occurs
|
||||
if the app is not installed.
|
||||
:param timeout: optional integer specifying the maximum time in
|
||||
seconds for any spawned adb process to complete before
|
||||
throwing an ADBTimeoutError.
|
||||
This timeout is per adb call. The total time spent
|
||||
may exceed this value. If it is not specified, the value
|
||||
set in the ADB constructor is used.
|
||||
:raises: * ADBTimeoutError - raised if the command takes longer than
|
||||
timeout seconds.
|
||||
* ADBError - raised if the command exits with a
|
||||
non-zero exit code.
|
||||
|
||||
"""
|
||||
if self.is_app_installed(app_name, timeout=timeout):
|
||||
data = self.command_output(["uninstall", app_name], timeout=timeout)
|
||||
if data.find('Success') == -1:
|
||||
self._logger.debug('uninstall_app failed: %s' % data)
|
||||
raise ADBError("uninstall failed for %s. Got: %s" % (app_name, data))
|
||||
if reboot:
|
||||
self.reboot(timeout=timeout)
|
||||
|
||||
def update_app(self, apk_path, timeout=None):
|
||||
"""Updates an app on the device and reboots.
|
||||
|
||||
:param apk_path: string containing the apk file name to be
|
||||
updated.
|
||||
:param timeout: optional integer specifying the maximum time in
|
||||
seconds for any spawned adb process to complete before
|
||||
throwing an ADBTimeoutError.
|
||||
This timeout is per adb call. The total time spent
|
||||
may exceed this value. If it is not specified, the value
|
||||
set in the ADB constructor is used.
|
||||
:raises: * ADBTimeoutError - raised if the command takes longer than
|
||||
timeout seconds.
|
||||
* ADBError - raised if the command exits with a
|
||||
non-zero exit code.
|
||||
|
||||
"""
|
||||
output = self.command_output(["install", "-r", apk_path],
|
||||
timeout=timeout)
|
||||
self.reboot(timeout=timeout)
|
||||
return output
|
||||
|
||||
|
||||
class ADBAndroid(ADBDevice, ADBAndroidMixin):
|
||||
"""ADBAndroid provides all of the methods of :class:`mozdevice.ADB` with
|
||||
Android specific extensions useful for that platform.
|
||||
|
||||
::
|
||||
|
||||
from mozdevice import ADBAndroid as ADBDevice
|
||||
|
||||
adb = ADBDevice(...)
|
||||
|
||||
if adb.is_device_ready():
|
||||
adb.install_app("/tmp/build.apk")
|
||||
adb.launch_fennec("org.mozilla.fennec")
|
||||
|
||||
"""
|
||||
pass
|
Loading…
Reference in New Issue
Block a user