summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozrunner/mozrunner/application.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mozbase/mozrunner/mozrunner/application.py')
-rw-r--r--testing/mozbase/mozrunner/mozrunner/application.py265
1 files changed, 265 insertions, 0 deletions
diff --git a/testing/mozbase/mozrunner/mozrunner/application.py b/testing/mozbase/mozrunner/mozrunner/application.py
new file mode 100644
index 000000000..6734487ae
--- /dev/null
+++ b/testing/mozbase/mozrunner/mozrunner/application.py
@@ -0,0 +1,265 @@
+# 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