| # 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/. |
| |
| from distutils.spawn import find_executable |
| import glob |
| import os |
| import posixpath |
| |
| from mozdevice import DeviceManagerADB, DMError |
| from mozprofile import ( |
| Profile, |
| FirefoxProfile, |
| MetroFirefoxProfile, |
| ThunderbirdProfile |
| ) |
| |
| here = os.path.abspath(os.path.dirname(__file__)) |
| |
| def get_app_context(appname): |
| context_map = { 'default': DefaultContext, |
| 'b2g': B2GContext, |
| 'firefox': FirefoxContext, |
| 'thunderbird': ThunderbirdContext, |
| 'metro': MetroContext } |
| if appname not in context_map: |
| raise KeyError("Application '%s' not supported!" % appname) |
| return context_map[appname] |
| |
| |
| class DefaultContext(object): |
| profile_class = Profile |
| |
| |
| class B2GContext(object): |
| _bindir = None |
| _dm = None |
| _remote_profile = None |
| _remote_settings_db = None |
| profile_class = Profile |
| |
| def __init__(self, b2g_home=None, adb_path=None): |
| self.homedir = b2g_home or os.environ.get('B2G_HOME') |
| |
| if self.homedir is not None and not os.path.isdir(self.homedir): |
| raise OSError('Homedir \'%s\' does not exist!' % self.homedir) |
| |
| self._adb = adb_path |
| self._update_tools = None |
| self._fastboot = None |
| |
| self.remote_binary = '/system/bin/b2g.sh' |
| self.remote_bundles_dir = '/system/b2g/distribution/bundles' |
| self.remote_busybox = '/system/bin/busybox' |
| self.remote_process = '/system/b2g/b2g' |
| self.remote_profiles_ini = '/data/b2g/mozilla/profiles.ini' |
| self.remote_settings_json = '/system/b2g/defaults/settings.json' |
| self.remote_idb_dir = '/data/local/storage/permanent/chrome/idb' |
| self.remote_test_root = '/data/local/tests' |
| self.remote_webapps_dir = '/data/local/webapps' |
| |
| self.remote_backup_files = [ |
| self.remote_settings_json, |
| self.remote_webapps_dir, |
| ] |
| |
| @property |
| def fastboot(self): |
| if self._fastboot is None: |
| self._fastboot = self.which('fastboot') |
| return self._fastboot |
| |
| @property |
| def update_tools(self): |
| if self._update_tools is None and self.homedir is not None: |
| self._update_tools = os.path.join(self.homedir, 'tools', 'update-tools') |
| return self._update_tools |
| |
| @property |
| def adb(self): |
| if not self._adb: |
| paths = [os.environ.get('ADB'), |
| os.environ.get('ADB_PATH'), |
| self.which('adb')] |
| paths = [p for p in paths if p is not None if os.path.isfile(p)] |
| if not paths: |
| raise OSError('Could not find the adb binary, make sure it is on your' \ |
| 'path or set the $ADB_PATH environment variable.') |
| self._adb = paths[0] |
| return self._adb |
| |
| @property |
| def bindir(self): |
| if self._bindir is None and self.homedir is not None: |
| # TODO get this via build configuration |
| path = os.path.join(self.homedir, 'out', 'host', '*', 'bin') |
| paths = glob.glob(path) |
| if paths: |
| self._bindir = paths[0] |
| return self._bindir |
| |
| @property |
| def dm(self): |
| if not self._dm: |
| self._dm = DeviceManagerADB(adbPath=self.adb, autoconnect=False, deviceRoot=self.remote_test_root) |
| return self._dm |
| |
| @property |
| def remote_profile(self): |
| if not self._remote_profile: |
| self._remote_profile = posixpath.join(self.remote_test_root, 'profile') |
| return self._remote_profile |
| |
| @property |
| def remote_settings_db(self): |
| if not self._remote_settings_db: |
| for filename in self.dm.listFiles(self.remote_idb_dir): |
| if filename.endswith('ssegtnti.sqlite'): |
| self._remote_settings_db = posixpath.join(self.remote_idb_dir, filename) |
| break |
| else: |
| raise DMError("Could not find settings db in '%s'!" % self.remote_idb_dir) |
| return self._remote_settings_db |
| |
| def which(self, binary): |
| paths = os.environ.get('PATH', {}).split(os.pathsep) |
| if self.bindir is not None and os.path.abspath(self.bindir) not in paths: |
| paths.insert(0, os.path.abspath(self.bindir)) |
| os.environ['PATH'] = os.pathsep.join(paths) |
| |
| return find_executable(binary) |
| |
| def stop_application(self): |
| self.dm.shellCheckOutput(['stop', 'b2g']) |
| |
| def setup_profile(self, profile): |
| # For some reason user.js in the profile doesn't get picked up. |
| # Manually copy it over to prefs.js. See bug 1009730 for more details. |
| self.dm.moveTree(posixpath.join(self.remote_profile, 'user.js'), |
| posixpath.join(self.remote_profile, 'prefs.js')) |
| |
| if self.dm.fileExists(posixpath.join(self.remote_profile, 'settings.json')): |
| # On devices, settings.json is only read from the profile if |
| # the system location doesn't exist. |
| if self.dm.fileExists(self.remote_settings_json): |
| self.dm.removeFile(self.remote_settings_json) |
| |
| # Delete existing settings db and create a new empty one to force new |
| # settings to be loaded. |
| self.dm.removeFile(self.remote_settings_db) |
| self.dm.shellCheckOutput(['touch', self.remote_settings_db]) |
| |
| # On devices, the webapps are located in /data/local/webapps instead of the profile. |
| # In some cases we may need to replace the existing webapps, in others we may just |
| # need to leave them in the profile. If the system app is present in the profile |
| # webapps, it's a good indication that they should replace the existing ones wholesale. |
| profile_webapps = posixpath.join(self.remote_profile, 'webapps') |
| if self.dm.dirExists(posixpath.join(profile_webapps, 'system.gaiamobile.org')): |
| self.dm.removeDir(self.remote_webapps_dir) |
| self.dm.moveTree(profile_webapps, self.remote_webapps_dir) |
| |
| # On devices extensions are installed in the system dir |
| extension_dir = os.path.join(profile.profile, 'extensions', 'staged') |
| if os.path.isdir(extension_dir): |
| # Copy the extensions to the B2G bundles dir. |
| for filename in os.listdir(extension_dir): |
| path = posixpath.join(self.remote_bundles_dir, filename) |
| if self.dm.fileExists(path): |
| self.dm.removeFile(path) |
| self.dm.pushDir(extension_dir, self.remote_bundles_dir) |
| |
| def cleanup_profile(self): |
| # Delete any bundled extensions |
| extension_dir = posixpath.join(self.remote_profile, 'extensions', 'staged') |
| if self.dm.dirExists(extension_dir): |
| for filename in self.dm.listFiles(extension_dir): |
| try: |
| self.dm.removeDir(posixpath.join(self.remote_bundles_dir, filename)) |
| except DMError: |
| pass |
| |
| if self.dm.fileExists(posixpath.join(self.remote_profile, 'settings.json')): |
| # Force settings.db to be restored to defaults |
| self.dm.removeFile(self.remote_settings_db) |
| self.dm.shellCheckOutput(['touch', self.remote_settings_db]) |
| |
| |
| class FirefoxContext(object): |
| profile_class = FirefoxProfile |
| |
| |
| class ThunderbirdContext(object): |
| profile_class = ThunderbirdProfile |
| |
| |
| class MetroContext(object): |
| profile_class = MetroFirefoxProfile |
| |
| def __init__(self, binary=None): |
| self.binary = binary or os.environ.get('BROWSER_PATH', None) |
| |
| def wrap_command(self, command): |
| immersive_helper_path = os.path.join(os.path.dirname(here), |
| 'resources', |
| 'metrotestharness.exe') |
| command[:0] = [immersive_helper_path, '-firefoxpath'] |
| return command |