From 4e65c91d0a1b4dad6caba689c8643fffadc29ceb Mon Sep 17 00:00:00 2001 From: Dave Hunt Date: Tue, 21 Apr 2015 07:40:00 -0400 Subject: [PATCH] Bug 1145680 - [mozbase] Add reboot, move, copy, and info methods to adb.py. r=bclary --- testing/mozbase/docs/mozdevice.rst | 74 +++-- .../mozbase/mozdevice/mozdevice/__init__.py | 1 + testing/mozbase/mozdevice/mozdevice/adb.py | 271 ++++++++++++++---- .../mozdevice/mozdevice/adb_android.py | 67 +---- .../mozbase/mozdevice/mozdevice/adb_b2g.py | 122 ++++++++ 5 files changed, 392 insertions(+), 143 deletions(-) create mode 100644 testing/mozbase/mozdevice/mozdevice/adb_b2g.py diff --git a/testing/mozbase/docs/mozdevice.rst b/testing/mozbase/docs/mozdevice.rst index eb432581595..5e18b56d25b 100644 --- a/testing/mozbase/docs/mozdevice.rst +++ b/testing/mozbase/docs/mozdevice.rst @@ -169,31 +169,39 @@ Device Shell methods 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) +.. automethod:: ADBDevice.clear_logcat +.. automethod:: ADBDevice.get_battery_percentage +.. automethod:: ADBDevice.get_info +.. automethod:: ADBDevice.get_logcat +.. automethod:: ADBDevice.get_prop +.. automethod:: ADBDevice.get_state + +System control methods +++++++++++++++++++++++ +.. automethod:: ADBDevice.is_device_ready +.. automethod:: ADBDevice.reboot 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) +.. automethod:: ADBDevice.chmod +.. automethod:: ADBDevice.cp +.. automethod:: ADBDevice.exists +.. automethod:: ADBDevice.is_dir +.. automethod:: ADBDevice.is_file +.. automethod:: ADBDevice.list_files +.. automethod:: ADBDevice.mkdir +.. automethod:: ADBDevice.mv +.. automethod:: ADBDevice.push +.. automethod:: ADBDevice.rm +.. automethod:: ADBDevice.rmdir .. autoattribute:: ADBDevice.test_root 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) - +.. automethod:: ADBDevice.get_process_list +.. automethod:: ADBDevice.kill +.. automethod:: ADBDevice.pkill +.. automethod:: ADBDevice.process_exist ADBAndroid `````````` @@ -201,24 +209,32 @@ ADBAndroid Informational methods +++++++++++++++++++++ -.. automethod:: ADBAndroid.get_battery_percentage(self, timeout=None) +.. automethod:: ADBAndroid.get_battery_percentage System control methods ++++++++++++++++++++++ -.. automethod:: ADBAndroid.is_device_ready(self, timeout=None) -.. automethod:: ADBAndroid.power_on(self, timeout=None) -.. automethod:: ADBAndroid.reboot(self, timeout=None) +.. automethod:: ADBAndroid.is_device_ready +.. automethod:: ADBAndroid.power_on 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) +.. automethod:: ADBAndroid.install_app +.. automethod:: ADBAndroid.is_app_installed +.. automethod:: ADBAndroid.launch_application +.. automethod:: ADBAndroid.launch_fennec +.. automethod:: ADBAndroid.stop_application +.. automethod:: ADBAndroid.uninstall_app +.. automethod:: ADBAndroid.update_app +ADBB2G +`````` +.. autoclass:: ADBB2G + +Informational methods ++++++++++++++++++++++ +.. automethod:: ADBB2G.get_battery_percentage +.. automethod:: ADBB2G.get_info +.. automethod:: ADBB2G.get_memory_total ADBProcess `````````` diff --git a/testing/mozbase/mozdevice/mozdevice/__init__.py b/testing/mozbase/mozdevice/mozdevice/__init__.py index a33bad2c9fc..a30e7cd56af 100644 --- a/testing/mozbase/mozdevice/mozdevice/__init__.py +++ b/testing/mozbase/mozdevice/mozdevice/__init__.py @@ -4,6 +4,7 @@ from adb import ADBError, ADBRootError, ADBTimeoutError, ADBProcess, ADBCommand, ADBHost, ADBDevice from adb_android import ADBAndroid +from adb_b2g import ADBB2G from devicemanager import DeviceManager, DMError, ZeroconfListener from devicemanagerADB import DeviceManagerADB from devicemanagerSUT import DeviceManagerSUT diff --git a/testing/mozbase/mozdevice/mozdevice/adb.py b/testing/mozbase/mozdevice/mozdevice/adb.py index 02e0cc5cd00..b23f1aabc47 100644 --- a/testing/mozbase/mozdevice/mozdevice/adb.py +++ b/testing/mozbase/mozdevice/mozdevice/adb.py @@ -2,6 +2,7 @@ # 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 abc import os import posixpath import re @@ -12,9 +13,7 @@ import traceback class ADBProcess(object): - """ADBProcess encapsulates the data related to executing the adb process. - - """ + """ADBProcess encapsulates the data related to executing the adb process.""" def __init__(self, args): #: command argument argument list. self.args = args @@ -64,7 +63,6 @@ class ADBError(Exception): device either exited with a non-zero exitcode or when an unexpected error condition has occurred. Generally, ADBErrors can be handled and the device can continue to be used. - """ pass @@ -73,7 +71,6 @@ class ADBRootError(Exception): root but the device does not support it. This error is fatal since there is no recovery possible by the script. You must either root your device or change your scripts to not require running as root. - """ pass @@ -94,7 +91,6 @@ class ADBTimeoutError(Exception): * Rebooting the device manually. * Rebooting the host. - """ pass @@ -117,7 +113,6 @@ class ADBCommand(object): adbcommand = ADBCommand() except NotImplementedError: print "ADBCommand can not be instantiated." - """ def __init__(self, @@ -136,7 +131,6 @@ class ADBCommand(object): :raises: * ADBError * ADBTimeoutError - """ if self.__class__ == ADBCommand: raise NotImplementedError @@ -207,7 +201,6 @@ class ADBCommand(object): It is the caller's responsibilty to clean up by closing the stdout and stderr temporary files. - """ args = [self._adb_path] if self._adb_host: @@ -257,7 +250,6 @@ class ADBCommand(object): :raises: * ADBTimeoutError * ADBError - """ adb_process = None try: @@ -300,7 +292,6 @@ class ADBHost(ADBCommand): adbhost = ADBHost() adbhost.start_server() - """ def __init__(self, adb='adb', @@ -318,7 +309,6 @@ class ADBHost(ADBCommand): :raises: * ADBError * ADBTimeoutError - """ ADBCommand.__init__(self, adb=adb, adb_host=adb_host, adb_port=adb_port, logger_name=logger_name, @@ -353,7 +343,6 @@ class ADBHost(ADBCommand): It is the caller's responsibilty to clean up by closing the stdout and stderr temporary files. - """ return ADBCommand.command(self, cmds, timeout=timeout) @@ -372,7 +361,6 @@ class ADBHost(ADBCommand): :raises: * ADBTimeoutError * ADBError - """ return ADBCommand.command_output(self, cmds, timeout=timeout) @@ -404,7 +392,6 @@ class ADBHost(ADBCommand): while true; do adb -a fork-server server done - """ self.command_output(["start-server"], timeout=timeout) @@ -418,7 +405,6 @@ class ADBHost(ADBCommand): specified, the value set in the ADBHost constructor is used. :raises: * ADBTimeoutError * ADBError - """ self.command_output(["kill-server"], timeout=timeout) @@ -444,7 +430,6 @@ class ADBHost(ADBCommand): [{'device_serial': 'b313b945', 'state': 'device', 'product': 'd2vzw', 'usb': '1-7', 'device': 'd2vzw', 'model': 'SCH_I535' }] - """ # b313b945 device usb:1-7 product:d2vzw model:SCH_I535 device:d2vzw # from Android system/core/adb/transport.c statename() @@ -473,22 +458,12 @@ class ADBHost(ADBCommand): class ADBDevice(ADBCommand): - """ADBDevice provides methods which can be used to interact with - the associated Android-based device. - - Android specific features such as Application management are not - included but are provided via the ADBAndroid interface. - - :: - - from mozdevice import ADBDevice - - adbdevice = ADBDevice() - print adbdevice.list_files("/mnt/sdcard") - if adbdevice.process_exist("org.mozilla.fennec"): - print "Fennec is running" - + """ADBDevice is an abstract base class which provides methods which + can be used to interact with the associated Android or B2G based + device. It must be used via one of the concrete implementations in + :class:`ADBAndroid` or :class:`ADBB2G`. """ + __metaclass__ = abc.ABCMeta def __init__(self, device=None, @@ -532,8 +507,6 @@ class ADBDevice(ADBCommand): :raises: * ADBError * ADBTimeoutError * ValueError - - """ ADBCommand.__init__(self, adb=adb, adb_host=adb_host, adb_port=adb_port, logger_name=logger_name, @@ -589,6 +562,9 @@ class ADBDevice(ADBCommand): except ADBError: self._ls += " -a" + # Do we have cp? + self._have_cp = self.shell_bool("type cp") + self._logger.debug("ADBDevice: %s" % self.__dict__) def _get_device_serial(self, device): @@ -629,7 +605,6 @@ class ADBDevice(ADBCommand): def _escape_command_line(cmd): """Utility function to return escaped and quoted version of command line. - """ quoted_cmd = [] @@ -652,7 +627,6 @@ class ADBDevice(ADBCommand): def _get_exitcode(file_obj): """Get the exitcode from the last line of the file_obj for shell commands. - """ file_obj.seek(0, os.SEEK_END) @@ -790,7 +764,6 @@ class ADBDevice(ADBCommand): It is the caller's responsibilty to clean up by closing the stdout and stderr temporary files. - """ return ADBCommand.command(self, cmds, @@ -813,7 +786,6 @@ class ADBDevice(ADBCommand): :raises: * ADBTimeoutError * ADBError - """ return ADBCommand.command_output(self, cmds, device_serial=self._device_serial, @@ -961,7 +933,6 @@ class ADBDevice(ADBCommand): It is the caller's responsibilty to clean up by closing the stdout and stderr temporary files. - """ if root: ld_library_path='LD_LIBRARY_PATH=/vendor/lib:/system/lib' @@ -1037,7 +1008,6 @@ class ADBDevice(ADBCommand): :raises: * ADBTimeoutError * ADBRootError - """ adb_process = None try: @@ -1071,7 +1041,6 @@ class ADBDevice(ADBCommand): :raises: * ADBTimeoutError * ADBRootError * ADBError - """ adb_process = None try: @@ -1115,7 +1084,6 @@ class ADBDevice(ADBCommand): in the ADBDevice constructor is used. :raises: * ADBTimeoutError * ADBError - """ self.command_output(["logcat", "-c"], timeout=timeout) @@ -1146,7 +1114,6 @@ class ADBDevice(ADBCommand): :returns: list of lines logcat output. :raises: * ADBTimeoutError * ADBError - """ cmds = ["logcat", "-v", format, "-d"] + filter_specs lines = self.command_output(cmds, timeout=timeout).split('\r') @@ -1169,7 +1136,6 @@ class ADBDevice(ADBCommand): :returns: string value of property. :raises: * ADBTimeoutError * ADBError - """ output = self.shell_output('getprop %s' % prop, timeout=timeout) return output @@ -1186,7 +1152,6 @@ class ADBDevice(ADBCommand): :returns: string value of adb get-state. :raises: * ADBTimeoutError * ADBError - """ output = self.command_output(["get-state"], timeout=timeout).strip() return output @@ -1206,7 +1171,6 @@ class ADBDevice(ADBCommand): be found. :raises: * ADBTimeoutError * ADBError - """ ip_regexp = re.compile(r'(\w+)\s+UP\s+([1-9]\d{0,2}\.\d{1,3}\.\d{1,3}\.\d{1,3})') data = self.shell_output('netcfg') @@ -1260,7 +1224,6 @@ class ADBDevice(ADBCommand): :raises: * ADBTimeoutError * ADBRootError * ADBError - """ path = posixpath.normpath(path.strip()) self._logger.debug('chmod: path=%s, recursive=%s, mask=%s, root=%s' % @@ -1306,7 +1269,6 @@ class ADBDevice(ADBCommand): :returns: boolean - True if path exists. :raises: * ADBTimeoutError * ADBRootError - """ path = posixpath.normpath(path) return self.shell_bool('ls -a %s' % path, timeout=timeout, root=root) @@ -1327,7 +1289,6 @@ class ADBDevice(ADBCommand): directory. :raises: * ADBTimeoutError * ADBRootError - """ path = posixpath.normpath(path) return self.shell_bool('ls -a %s/' % path, timeout=timeout, root=root) @@ -1348,7 +1309,6 @@ class ADBDevice(ADBCommand): file. :raises: * ADBTimeoutError * ADBRootError - """ path = posixpath.normpath(path) return ( @@ -1371,7 +1331,6 @@ class ADBDevice(ADBCommand): :returns: list of files/directories contained in the directory. :raises: * ADBTimeoutError * ADBRootError - """ path = posixpath.normpath(path.strip()) data = [] @@ -1407,7 +1366,6 @@ class ADBDevice(ADBCommand): :raises: * ADBTimeoutError * ADBRootError * ADBError - """ path = posixpath.normpath(path) if parents: @@ -1449,7 +1407,6 @@ class ADBDevice(ADBCommand): set in the ADBDevice constructor is used. :raises: * ADBTimeoutError * ADBError - """ self.command_output(["push", os.path.realpath(local), remote], timeout=timeout) @@ -1469,7 +1426,6 @@ class ADBDevice(ADBCommand): set in the ADBDevice constructor is used. :raises: * ADBTimeoutError * ADBError - """ self.command_output(["pull", remote, os.path.realpath(local)], timeout=timeout) @@ -1495,7 +1451,6 @@ class ADBDevice(ADBCommand): :raises: * ADBTimeoutError * ADBRootError * ADBError - """ cmd = "rm" if recursive: @@ -1523,7 +1478,6 @@ class ADBDevice(ADBCommand): :raises: * ADBTimeoutError * ADBRootError * ADBError - """ self.shell_output("rmdir %s" % path, timeout=timeout, root=root) if self.is_dir(path, timeout=timeout, root=root): @@ -1545,7 +1499,6 @@ class ADBDevice(ADBCommand): on the device. :raises: * ADBTimeoutError * ADBError - """ adb_process = None try: @@ -1607,7 +1560,6 @@ class ADBDevice(ADBCommand): :raises: * ADBTimeoutError * ADBRootError * ADBError - """ pid_list = [str(pid) for pid in pids] for attempt in range(attempts): @@ -1655,7 +1607,6 @@ class ADBDevice(ADBCommand): :raises: * ADBTimeoutError * ADBRootError * ADBError - """ procs = self.get_process_list(timeout=timeout) # limit the comparion to the first 75 characters due to a @@ -1688,7 +1639,6 @@ class ADBDevice(ADBCommand): :raises: * ADBTimeoutError * ADBError - """ if not isinstance(process_name, basestring): raise ADBError("Process name %s is not a string" % process_name) @@ -1718,3 +1668,204 @@ class ADBDevice(ADBCommand): if proc_name == app[:75]: return True return False + + def cp(self, source, destination, recursive=False, timeout=None, + root=False): + """Copies a file or directory on the device. + + :param source: string containing the path of the source file or + directory. + :param destination: string containing the path of the destination file + or directory. + :param recursive: optional boolean indicating if a recursive copy is to + be performed. Required if the source is a directory. Defaults to + False. Think cp -R source destination. + :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 ADBDevice constructor is used. + :raises: * ADBTimeoutError + * ADBRootError + * ADBError + """ + source = posixpath.normpath(source) + destination = posixpath.normpath(destination) + if self._have_cp: + r = '-R' if recursive else '' + self.shell_output('cp %s %s %s' % (r, source, destination), + timeout=timeout, root=root) + return + + # Emulate cp behavior depending on if source and destination + # already exists and whether they are a directory or file. + if not self.exists(source, timeout=timeout, root=root): + raise ADBError("cp: can't stat '%s': No such file or directory" % + source) + + if self.is_file(source, timeout=timeout, root=root): + if self.is_dir(destination, timeout=timeout, root=root): + # Copy the source file into the destination directory + destination = posixpath.join(destination, + posixpath.basename(source)) + self.shell_output('dd if=%s of=%s' % (source, destination), + timeout=timeout, root=root) + return + + if self.is_file(destination, timeout=timeout, root=root): + raise ADBError('cp: %s: Not a directory' % destination) + + if not recursive: + raise ADBError("cp: omitting directory '%s'" % source) + + if self.is_dir(destination, timeout=timeout, root=root): + # Copy the source directory into the destination directory. + destination_dir = posixpath.join(destination, + posixpath.basename(source)) + else: + # Copy the contents of the source directory into the + # destination directory. + destination_dir = destination + + try: + # Do not create parent directories since cp does not. + self.mkdir(destination_dir, timeout=timeout, root=root) + except ADBError as e: + if 'File exists' not in e.message: + raise + + for i in self.list_files(source, timeout=timeout, root=root): + self.cp(posixpath.join(source, i), + posixpath.join(destination_dir, i), + recursive=recursive, + timeout=timeout, root=root) + + def mv(self, source, destination, timeout=None, root=False): + """Moves a file or directory on the device. + + :param source: string containing the path of the source file or + directory. + :param destination: string containing the path of the destination file + or directory. + :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 ADBDevice constructor is used. + :raises: * ADBTimeoutError + * ADBRootError + * ADBError + """ + source = posixpath.normpath(source) + destination = posixpath.normpath(destination) + self.shell_output('mv %s %s' % (source, destination), timeout=timeout, + root=root) + + def reboot(self, timeout=None): + """Reboots the device. + + :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 + * ADBError + + 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) + + @abc.abstractmethod + def is_device_ready(self, timeout=None): + """Abstract class that returns True if the device is ready. + + :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 + * ADBError + """ + return + + @abc.abstractmethod + def get_battery_percentage(self, timeout=None): + """Abstract class that returns the battery charge as a percentage. + + :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 ADBDevice constructor is used. + :returns: battery charge as a percentage. + :raises: * ADBTimeoutError + * ADBError + """ + return + + def get_info(self, directive=None, timeout=None): + """ + Returns a dictionary of information strings about the device. + + :param directive: information you want to get. Options are: + - `battery` - battery charge as a percentage + - `disk` - total, free, available bytes on disk + - `id` - unique id of the device + - `os` - name of the os + - `process` - list of running processes (same as ps) + - `systime` - system time of the device + - `uptime` - uptime of the device + + If `directive` is `None`, will return all available information + :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 + * ADBError + """ + directives = ['battery', 'disk', 'id', 'os', 'process', 'systime', + 'uptime'] + + if (directive in directives): + directives = [directive] + + info = {} + if 'battery' in directives: + info['battery'] = self.get_battery_percentage(timeout=timeout) + if 'disk' in directives: + info['disk'] = self.shell_output('df /data /system /sdcard', + timeout=timeout).splitlines() + if 'id' in directives: + info['id'] = self.command_output(['get-serialno'], timeout=timeout) + if 'os' in directives: + info['os'] = self.shell_output('getprop ro.build.display.id', + timeout=timeout) + if 'process' in directives: + ps = self.shell_output('ps', timeout=timeout) + info['process'] = ps.splitlines() + if 'systime' in directives: + info['systime'] = self.shell_output('date', timeout=timeout) + if 'uptime' in directives: + uptime = self.shell_output('uptime', timeout=timeout) + if uptime: + m = re.match('up time: ((\d+) days, )*(\d{2}):(\d{2}):(\d{2})', + uptime) + if m: + uptime = '%d days %d hours %d minutes %d seconds' % tuple( + [int(g or 0) for g in m.groups()[1:]]) + info['uptime'] = uptime + return info diff --git a/testing/mozbase/mozdevice/mozdevice/adb_android.py b/testing/mozbase/mozdevice/mozdevice/adb_android.py index c519299aad6..0ee03c6eeed 100644 --- a/testing/mozbase/mozdevice/mozdevice/adb_android.py +++ b/testing/mozbase/mozdevice/mozdevice/adb_android.py @@ -10,8 +10,19 @@ from adb import ADBDevice, ADBError from distutils.version import StrictVersion -class ADBAndroidMixin(object): - """Mixin to extend ADB with Android-specific functionality""" +class ADBAndroid(ADBDevice): + """ADBAndroid implements :class:`ADBDevice` providing Android-specific + functionality. + + :: + + from mozdevice import ADBAndroid + + adbdevice = ADBAndroid() + print adbdevice.list_files("/mnt/sdcard") + if adbdevice.process_exist("org.mozilla.fennec"): + print "Fennec is running" + """ # Informational methods @@ -27,7 +38,6 @@ class ADBAndroidMixin(object): :returns: battery charge as a percentage. :raises: * ADBTimeoutError * ADBError - """ level = None scale = None @@ -65,7 +75,6 @@ class ADBAndroidMixin(object): set in the ADB constructor is used. :raises: * ADBTimeoutError * ADBError - """ self.command_output(["wait-for-device"], timeout=timeout) pm_error_string = "Error: Could not access the Package Manager" @@ -117,7 +126,6 @@ class ADBAndroidMixin(object): set in the ADB constructor is used. :raises: * ADBTimeoutError * ADBError - """ try: self.shell_output('svc power stayon true', timeout=timeout) @@ -128,30 +136,6 @@ class ADBAndroidMixin(object): 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 - * ADBError - - 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): @@ -167,7 +151,6 @@ class ADBAndroidMixin(object): set in the ADB constructor is used. :raises: * ADBTimeoutError * ADBError - """ data = self.command_output(["install", apk_path], timeout=timeout) if data.find('Success') == -1: @@ -187,7 +170,6 @@ class ADBAndroidMixin(object): set in the ADB constructor is used. :raises: * ADBTimeoutError * ADBError - """ pm_error_string = 'Error: Could not access the Package Manager' data = self.shell_output("pm list package %s" % app_name, timeout=timeout) @@ -219,7 +201,6 @@ class ADBAndroidMixin(object): set in the ADB constructor is used. :raises: * ADBTimeoutError * ADBError - """ # If fail_if_running is True, we throw an exception here. Only one # instance of an application can be running at once on Android, @@ -276,7 +257,6 @@ class ADBAndroidMixin(object): set in the ADB constructor is used. :raises: * ADBTimeoutError * ADBError - """ extras = {} @@ -313,7 +293,6 @@ class ADBAndroidMixin(object): set in the ADB constructor is used. :raises: * ADBTimeoutError * ADBError - """ version = self.shell_output("getprop ro.build.version.release", timeout=timeout, root=root) @@ -352,7 +331,6 @@ class ADBAndroidMixin(object): set in the ADB constructor is used. :raises: * ADBTimeoutError * ADBError - """ if self.is_app_installed(app_name, timeout=timeout): data = self.command_output(["uninstall", app_name], timeout=timeout) @@ -375,27 +353,8 @@ class ADBAndroidMixin(object): set in the ADB constructor is used. :raises: * ADBTimeoutError * ADBError - """ 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 diff --git a/testing/mozbase/mozdevice/mozdevice/adb_b2g.py b/testing/mozbase/mozdevice/mozdevice/adb_b2g.py new file mode 100644 index 00000000000..69facebe3dd --- /dev/null +++ b/testing/mozbase/mozdevice/mozdevice/adb_b2g.py @@ -0,0 +1,122 @@ +# 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 traceback + +import mozfile + +from adb import ADBDevice, ADBError + + +class ADBB2G(ADBDevice): + """ADBB2G implements :class:`ADBDevice` providing B2G-specific + functionality. + + :: + + from mozdevice import ADBB2G + + adbdevice = ADBB2G() + print adbdevice.list_files("/mnt/sdcard") + if adbdevice.process_exist("b2g"): + print "B2G is running" + """ + + def get_battery_percentage(self, timeout=None): + """Returns the battery charge as a percentage. + + :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 ADBDevice constructor is used. + :returns: battery charge as a percentage. + :raises: * ADBTimeoutError + * ADBError + """ + with mozfile.NamedTemporaryFile() as tf: + self.pull('/sys/class/power_supply/battery/capacity', tf.name, + timeout=timeout) + try: + with open(tf.name) as tf2: + return tf2.read().splitlines()[0] + except Exception as e: + raise ADBError(traceback.format_exception_only( + type(e), e)[0].strip()) + + def get_memory_total(self, timeout=None): + """Returns the total memory available with units. + + :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 ADBDevice constructor is used. + :returns: memory total with units. + :raises: * ADBTimeoutError + * ADBError + """ + meminfo = {} + with mozfile.NamedTemporaryFile() as tf: + self.pull('/proc/meminfo', tf.name, timeout=timeout) + try: + with open(tf.name) as tf2: + for line in tf2.read().splitlines(): + key, value = line.split(':') + meminfo[key] = value.strip() + except Exception as e: + raise ADBError(traceback.format_exception_only( + type(e), e)[0].strip()) + return meminfo['MemTotal'] + + def get_info(self, directive=None, timeout=None): + """ + Returns a dictionary of information strings about the device. + + :param directive: information you want to get. Options are: + - `battery` - battery charge as a percentage + - `disk` - total, free, available bytes on disk + - `id` - unique id of the device + - `memtotal` - total memory available on the device + - `os` - name of the os + - `process` - list of running processes (same as ps) + - `systime` - system time of the device + - `uptime` - uptime of the device + + If `directive` is `None`, will return all available information + :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 + * ADBError + """ + info = super(ADBB2G, self).get_info(directive=directive, + timeout=timeout) + + directives = ['memtotal'] + if (directive in directives): + directives = [directive] + + if 'memtotal' in directives: + info['memtotal'] = self.get_memory_total(timeout=timeout) + return info + + def is_device_ready(self, timeout=None): + """Returns True if the device is ready. + + :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 + * ADBError + """ + return self.shell_bool('ls /sbin', timeout=timeout)