266 lines
9.6 KiB
Python
266 lines
9.6 KiB
Python
# 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 abc import ABCMeta, abstractmethod
|
|
from distutils.spawn import find_executable
|
|
import glob
|
|
import os
|
|
import posixpath
|
|
|
|
from mozdevice import DeviceManagerADB, DMError, DroidADB
|
|
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,
|
|
'fennec': FennecContext}
|
|
if appname not in context_map:
|
|
raise KeyError("Application '%s' not supported!" % appname)
|
|
return context_map[appname]
|
|
|
|
|
|
class DefaultContext(object):
|
|
profile_class = Profile
|
|
|
|
|
|
class RemoteContext(object):
|
|
__metaclass__ = ABCMeta
|
|
_dm = None
|
|
_remote_profile = None
|
|
_adb = None
|
|
profile_class = Profile
|
|
dm_class = DeviceManagerADB
|
|
_bindir = None
|
|
remote_test_root = ''
|
|
remote_process = None
|
|
|
|
@property
|
|
def bindir(self):
|
|
if self._bindir is None:
|
|
paths = [find_executable('emulator')]
|
|
paths = [p for p in paths if p is not None if os.path.isfile(p)]
|
|
if not paths:
|
|
self._bindir = ''
|
|
else:
|
|
self._bindir = os.path.dirname(paths[0])
|
|
return self._bindir
|
|
|
|
@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 dm(self):
|
|
if not self._dm:
|
|
self._dm = self.dm_class(adbPath=self.adb, autoconnect=False)
|
|
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
|
|
|
|
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)
|
|
|
|
@abstractmethod
|
|
def stop_application(self):
|
|
""" Run (device manager) command to stop application. """
|
|
pass
|
|
|
|
|
|
class FennecContext(RemoteContext):
|
|
_remote_profiles_ini = None
|
|
_remote_test_root = None
|
|
|
|
def __init__(self, app=None, adb_path=None, avd_home=None):
|
|
self._adb = adb_path
|
|
self.avd_home = avd_home
|
|
self.dm_class = DroidADB
|
|
self.remote_process = app or self.dm._packageName
|
|
|
|
def stop_application(self):
|
|
self.dm.stopApplication(self.remote_process)
|
|
|
|
@property
|
|
def remote_test_root(self):
|
|
if not self._remote_test_root:
|
|
self._remote_test_root = self.dm.getDeviceRoot()
|
|
return self._remote_test_root
|
|
|
|
@property
|
|
def remote_profiles_ini(self):
|
|
if not self._remote_profiles_ini:
|
|
self._remote_profiles_ini = posixpath.join(
|
|
self.dm.getAppRoot(self.remote_process),
|
|
'files', 'mozilla', 'profiles.ini'
|
|
)
|
|
return self._remote_profiles_ini
|
|
|
|
|
|
class B2GContext(RemoteContext):
|
|
_remote_settings_db = None
|
|
|
|
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 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 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 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
|