--- a/testing/mozbase/mozrunner/mozrunner/application.py
+++ b/testing/mozbase/mozrunner/mozrunner/application.py
@@ -1,48 +1,139 @@
# 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
+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 }
+ '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 B2GContext(object):
- _bindir = None
+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
- 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
@@ -72,69 +163,36 @@ class B2GContext(object):
@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'))
--- a/testing/mozbase/mozrunner/mozrunner/base/__init__.py
+++ b/testing/mozbase/mozrunner/mozrunner/base/__init__.py
@@ -1,3 +1,3 @@
from .runner import BaseRunner
-from .device import DeviceRunner
+from .device import DeviceRunner, FennecRunner
from .browser import GeckoRuntimeRunner
--- a/testing/mozbase/mozrunner/mozrunner/base/device.py
+++ b/testing/mozbase/mozrunner/mozrunner/base/device.py
@@ -9,17 +9,17 @@ import re
import signal
import sys
import tempfile
import time
import mozfile
from .runner import BaseRunner
-from ..devices import Emulator
+from ..devices import BaseEmulator
class DeviceRunner(BaseRunner):
"""
The base runner class used for running gecko on
remote devices (or emulators), such as B2G.
"""
env = { 'MOZ_CRASHREPORTER': '1',
'MOZ_CRASHREPORTER_NO_REPORT': '1',
@@ -60,42 +60,43 @@ class DeviceRunner(BaseRunner):
cmd.extend(['-s', self.app_ctx.dm._deviceSerial])
cmd.append('shell')
for k, v in self._device_env.iteritems():
cmd.append('%s=%s' % (k, v))
cmd.append(self.app_ctx.remote_binary)
return cmd
def start(self, *args, **kwargs):
- if isinstance(self.device, Emulator) and not self.device.connected:
+ if isinstance(self.device, BaseEmulator) and not self.device.connected:
self.device.start()
self.device.connect()
self.device.setup_profile(self.profile)
# TODO: this doesn't work well when the device is running but dropped
# wifi for some reason. It would be good to probe the state of the device
# to see if we have the homescreen running, or something, before waiting here
self.device.wait_for_net()
if not self.device.wait_for_net():
raise Exception("Network did not come up when starting device")
- BaseRunner.start(self, *args, **kwargs)
+ pid = BaseRunner.start(self, *args, **kwargs)
timeout = 10 # seconds
starttime = datetime.datetime.now()
while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout):
if self.is_running():
break
time.sleep(1)
else:
print("timed out waiting for '%s' process to start" % self.app_ctx.remote_process)
if not self.device.wait_for_net():
raise Exception("Failed to get a network connection")
+ return pid
def stop(self, sig=None):
def _wait_for_shutdown(pid, timeout=10):
start_time = datetime.datetime.now()
end_time = datetime.timedelta(seconds=timeout)
while datetime.datetime.now() - start_time < end_time:
if self.is_running() != pid:
return True
@@ -137,22 +138,42 @@ class DeviceRunner(BaseRunner):
msg = "%s with no output" % msg
print(msg % (self.last_test, timeout))
self.check_for_crashes()
def on_finish(self):
self.check_for_crashes()
- def check_for_crashes(self, dump_save_path=None, test_name=None):
+ def check_for_crashes(self, dump_save_path=None, test_name=None, **kwargs):
test_name = test_name or self.last_test
dump_dir = self.device.pull_minidumps()
crashed = BaseRunner.check_for_crashes(
self,
dump_directory=dump_dir,
dump_save_path=dump_save_path,
- test_name=test_name)
+ test_name=test_name,
+ **kwargs)
mozfile.remove(dump_dir)
return crashed
def cleanup(self, *args, **kwargs):
BaseRunner.cleanup(self, *args, **kwargs)
self.device.cleanup()
+
+
+class FennecRunner(DeviceRunner):
+
+ @property
+ def command(self):
+ cmd = [self.app_ctx.adb]
+ if self.app_ctx.dm._deviceSerial:
+ cmd.extend(['-s', self.app_ctx.dm._deviceSerial])
+ cmd.append('shell')
+ app = "%s/org.mozilla.gecko.BrowserApp" % self.app_ctx.remote_process
+ cmd.extend(['am', 'start', '-a', 'android.activity.MAIN', '-n', app])
+ params = ['-no-remote', '-profile', self.app_ctx.remote_profile]
+ cmd.extend(['--es', 'args', '"%s"' % ' '.join(params)])
+ # Append env variables in the form "--es env0 MOZ_CRASHREPORTER=1"
+ for (count, (k, v)) in enumerate(self._device_env.iteritems()):
+ cmd.extend(["--es", "env" + str(count), k + "=" + v])
+
+ return cmd
--- a/testing/mozbase/mozrunner/mozrunner/devices/__init__.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/__init__.py
@@ -1,10 +1,10 @@
# 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 emulator import Emulator
+from emulator import BaseEmulator, Emulator, EmulatorAVD
from base import Device
import emulator_battery
import emulator_geo
import emulator_screen
--- a/testing/mozbase/mozrunner/mozrunner/devices/base.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/base.py
@@ -13,23 +13,24 @@ import tempfile
import time
import traceback
from mozdevice import DMError
from mozprocess import ProcessHandler
class Device(object):
connected = False
+ logcat_proc = None
def __init__(self, app_ctx, logdir=None, serial=None, restore=True):
self.app_ctx = app_ctx
self.dm = self.app_ctx.dm
self.restore = restore
self.serial = serial
- self.logdir = logdir
+ self.logdir = os.path.abspath(os.path.expanduser(logdir))
self.added_files = set()
self.backup_files = set()
@property
def remote_profiles(self):
"""
A list of remote profiles on the device.
"""
--- a/testing/mozbase/mozrunner/mozrunner/devices/emulator.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/emulator.py
@@ -2,93 +2,102 @@
# 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 telnetlib import Telnet
import datetime
import os
import shutil
import subprocess
-import sys
import tempfile
import time
from mozprocess import ProcessHandler
from .base import Device
from .emulator_battery import EmulatorBattery
from .emulator_geo import EmulatorGeo
from .emulator_screen import EmulatorScreen
from ..errors import TimeoutException
+
class ArchContext(object):
- def __init__(self, arch, context, binary=None):
- kernel = os.path.join(context.homedir, 'prebuilts', 'qemu-kernel', '%s', '%s')
- sysdir = os.path.join(context.homedir, 'out', 'target', 'product', '%s')
+ def __init__(self, arch, context, binary=None, avd=None, extra_args=None):
+ homedir = getattr(context,'homedir', '')
+ kernel = os.path.join(homedir, 'prebuilts', 'qemu-kernel', '%s', '%s')
+ sysdir = os.path.join(homedir, 'out', 'target', 'product', '%s')
+ self.extra_args = []
+ self.binary = os.path.join(context.bindir or '', 'emulator')
if arch == 'x86':
- self.binary = os.path.join(context.bindir, 'emulator-x86')
+ self.binary = os.path.join(context.bindir or '', 'emulator-x86')
self.kernel = kernel % ('x86', 'kernel-qemu')
self.sysdir = sysdir % 'generic_x86'
- self.extra_args = []
+ elif avd:
+ self.avd = avd
+ self.extra_args = [
+ '-show-kernel', '-debug',
+ 'init,console,gles,memcheck,adbserver,adbclient,adb,avd_config,socket'
+ ]
else:
- self.binary = os.path.join(context.bindir, 'emulator')
self.kernel = kernel % ('arm', 'kernel-qemu-armv7')
self.sysdir = sysdir % 'generic'
self.extra_args = ['-cpu', 'cortex-a8']
if binary:
self.binary = binary
+ if extra_args:
+ self.extra_args.extend(extra_args)
-class Emulator(Device):
- logcat_proc = None
+
+class SDCard(object):
+ def __init__(self, emulator, size):
+ self.emulator = emulator
+ self.path = self.create_sdcard(size)
+
+ def create_sdcard(self, sdcard_size):
+ """
+ Creates an sdcard partition in the emulator.
+
+ :param sdcard_size: Size of partition to create, e.g '10MB'.
+ """
+ mksdcard = self.emulator.app_ctx.which('mksdcard')
+ path = tempfile.mktemp(prefix='sdcard', dir=self.emulator.tmpdir)
+ sdargs = [mksdcard, '-l', 'mySdCard', sdcard_size, path]
+ sd = subprocess.Popen(sdargs, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ retcode = sd.wait()
+ if retcode:
+ raise Exception('unable to create sdcard: exit code %d: %s'
+ % (retcode, sd.stdout.read()))
+ return path
+
+
+class BaseEmulator(Device):
port = None
proc = None
telnet = None
- def __init__(self, app_ctx, arch, resolution=None, sdcard=None, userdata=None,
- no_window=None, binary=None, **kwargs):
- Device.__init__(self, app_ctx, **kwargs)
-
- self.arch = ArchContext(arch, self.app_ctx, binary=binary)
- self.resolution = resolution or '320x480'
+ def __init__(self, app_ctx, **kwargs):
+ self.arch = ArchContext(kwargs.pop('arch', 'arm'), app_ctx,
+ binary=kwargs.pop('binary', None),
+ avd=kwargs.pop('avd', None))
+ super(BaseEmulator, self).__init__(app_ctx, **kwargs)
self.tmpdir = tempfile.mkdtemp()
- self.sdcard = None
- if sdcard:
- self.sdcard = self.create_sdcard(sdcard)
- self.userdata = tempfile.NamedTemporaryFile(prefix='userdata-qemu', dir=self.tmpdir)
- self.initdata = userdata if userdata else os.path.join(self.arch.sysdir, 'userdata.img')
- self.no_window = no_window
-
+ # These rely on telnet
self.battery = EmulatorBattery(self)
self.geo = EmulatorGeo(self)
self.screen = EmulatorScreen(self)
@property
def args(self):
"""
Arguments to pass into the emulator binary.
"""
- qemu_args = [self.arch.binary,
- '-kernel', self.arch.kernel,
- '-sysdir', self.arch.sysdir,
- '-data', self.userdata.name,
- '-initdata', self.initdata,
- '-wipe-data']
- if self.no_window:
- qemu_args.append('-no-window')
- if self.sdcard:
- qemu_args.extend(['-sdcard', self.sdcard])
- qemu_args.extend(['-memory', '512',
- '-partition-size', '512',
- '-verbose',
- '-skin', self.resolution,
- '-gpu', 'on',
- '-qemu'] + self.arch.extra_args)
- return qemu_args
+ return [self.arch.binary]
def start(self):
"""
Starts a new emulator.
"""
if self.proc:
return
@@ -113,75 +122,56 @@ class Emulator(Device):
self.proc.run()
devices = set(self._get_online_devices())
now = datetime.datetime.now()
while (devices - original_devices) == set([]):
time.sleep(1)
# Sometimes it takes more than 60s to launch emulator, so we
# increase timeout value to 180s. Please see bug 1143380.
- if datetime.datetime.now() - now > datetime.timedelta(seconds=180):
- raise TimeoutException('timed out waiting for emulator to start')
+ if datetime.datetime.now() - now > datetime.timedelta(
+ seconds=180):
+ raise TimeoutException(
+ 'timed out waiting for emulator to start')
devices = set(self._get_online_devices())
devices = devices - original_devices
self.serial = devices.pop()
self.connect()
def _get_online_devices(self):
- return set([d[0] for d in self.dm.devices() if d[1] != 'offline' if d[0].startswith('emulator')])
+ return [d[0] for d in self.dm.devices() if d[1] != 'offline' if
+ d[0].startswith('emulator')]
def connect(self):
"""
Connects to a running device. If no serial was specified in the
constructor, defaults to the first entry in `adb devices`.
"""
if self.connected:
return
- Device.connect(self)
-
- self.port = int(self.serial[self.serial.rindex('-')+1:])
- self.geo.set_default_location()
- self.screen.initialize()
-
- # setup DNS fix for networking
- self.app_ctx.dm.shellCheckOutput(['setprop', 'net.dns1', '10.0.2.3'])
-
- def create_sdcard(self, sdcard_size):
- """
- Creates an sdcard partition in the emulator.
-
- :param sdcard_size: Size of partition to create, e.g '10MB'.
- """
- mksdcard = self.app_ctx.which('mksdcard')
- path = tempfile.mktemp(prefix='sdcard', dir=self.tmpdir)
- sdargs = [mksdcard, '-l', 'mySdCard', sdcard_size, path]
- sd = subprocess.Popen(sdargs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
- retcode = sd.wait()
- if retcode:
- raise Exception('unable to create sdcard: exit code %d: %s'
- % (retcode, sd.stdout.read()))
- return path
+ super(BaseEmulator, self).connect()
+ serial = self.serial or self.dm._deviceSerial
+ self.port = int(serial[serial.rindex('-') + 1:])
def cleanup(self):
"""
- Cleans up and kills the emulator.
+ Cleans up and kills the emulator, if it was started by mozrunner.
"""
- Device.cleanup(self)
+ super(BaseEmulator, self).cleanup()
if self.proc:
self.proc.kill()
self.proc = None
# Remove temporary files
- self.userdata.close()
shutil.rmtree(self.tmpdir)
def _get_telnet_response(self, command=None):
output = []
- assert(self.telnet)
+ assert self.telnet
if command is not None:
self.telnet.write('%s\n' % command)
while True:
line = self.telnet.read_until('\n')
output.append(line.rstrip())
if line.startswith('OK'):
return output
elif line.startswith('KO:'):
@@ -192,8 +182,101 @@ class Emulator(Device):
self.telnet = Telnet('localhost', self.port)
self._get_telnet_response()
return self._get_telnet_response(command)
def __del__(self):
if self.telnet:
self.telnet.write('exit\n')
self.telnet.read_all()
+
+
+class Emulator(BaseEmulator):
+ def __init__(self, app_ctx, arch, resolution=None, sdcard=None, userdata=None,
+ no_window=None, binary=None, **kwargs):
+ super(Emulator, self).__init__(app_ctx, arch=arch, binary=binary, **kwargs)
+
+ # emulator args
+ self.resolution = resolution or '320x480'
+ self._sdcard_size = sdcard
+ self._sdcard = None
+ self.userdata = tempfile.NamedTemporaryFile(prefix='userdata-qemu', dir=self.tmpdir)
+ self.initdata = userdata if userdata else os.path.join(self.arch.sysdir, 'userdata.img')
+ self.no_window = no_window
+
+ @property
+ def sdcard(self):
+ if self._sdcard_size and not self._sdcard:
+ self._sdcard = SDCard(self, self._sdcard_size).path
+ else:
+ return self._sdcard
+
+ @property
+ def args(self):
+ """
+ Arguments to pass into the emulator binary.
+ """
+ qemu_args = super(Emulator, self).args
+ qemu_args.extend([
+ '-kernel', self.arch.kernel,
+ '-sysdir', self.arch.sysdir,
+ '-data', self.userdata.name,
+ '-initdata', self.initdata,
+ '-wipe-data'])
+ if self.no_window:
+ qemu_args.append('-no-window')
+ if self.sdcard:
+ qemu_args.extend(['-sdcard', self.sdcard])
+ qemu_args.extend(['-memory', '512',
+ '-partition-size', '512',
+ '-verbose',
+ '-skin', self.resolution,
+ '-gpu', 'on',
+ '-qemu'] + self.arch.extra_args)
+ return qemu_args
+
+ def connect(self):
+ """
+ Connects to a running device. If no serial was specified in the
+ constructor, defaults to the first entry in `adb devices`.
+ """
+ if self.connected:
+ return
+
+ super(Emulator, self).connect()
+ self.geo.set_default_location()
+ self.screen.initialize()
+
+ # setup DNS fix for networking
+ self.app_ctx.dm.shellCheckOutput(['setprop', 'net.dns1', '10.0.2.3'])
+
+ def cleanup(self):
+ """
+ Cleans up and kills the emulator, if it was started by mozrunner.
+ """
+ super(Emulator, self).cleanup()
+ # Remove temporary files
+ self.userdata.close()
+
+class EmulatorAVD(BaseEmulator):
+ def __init__(self, app_ctx, binary, avd, port=5554, **kwargs):
+ super(EmulatorAVD, self).__init__(app_ctx, binary=binary, avd=avd, **kwargs)
+ self.port = port
+
+ @property
+ def args(self):
+ """
+ Arguments to pass into the emulator binary.
+ """
+ qemu_args = super(EmulatorAVD, self).args
+ qemu_args.extend(['-avd', self.arch.avd,
+ '-port', str(self.port)])
+ qemu_args.extend(self.arch.extra_args)
+ return qemu_args
+
+ def start(self):
+ if self.proc:
+ return
+
+ env = os.environ
+ env['ANDROID_AVD_HOME'] = self.app_ctx.avd_home
+
+ super(EmulatorAVD, self).start()
--- a/testing/mozbase/mozrunner/mozrunner/runners.py
+++ b/testing/mozbase/mozrunner/mozrunner/runners.py
@@ -3,18 +3,18 @@
# You can obtain one at http://mozilla.org/MPL/2.0/.
"""
This module contains a set of shortcut methods that create runners for commonly
used Mozilla applications, such as Firefox or B2G emulator.
"""
from .application import get_app_context
-from .base import DeviceRunner, GeckoRuntimeRunner
-from .devices import Emulator, Device
+from .base import DeviceRunner, GeckoRuntimeRunner, FennecRunner
+from .devices import Emulator, EmulatorAVD, Device
def Runner(*args, **kwargs):
"""
Create a generic GeckoRuntime runner.
:param binary: Path to binary.
:param cmdargs: Arguments to pass into binary.
@@ -87,16 +87,53 @@ def B2GDesktopRunner(*args, **kwargs):
Defaults to False.
:returns: A GeckoRuntimeRunner for b2g desktop.
"""
# There is no difference between a generic and b2g desktop runner,
# but expose a separate entry point for clarity.
return Runner(*args, **kwargs)
+def FennecEmulatorRunner(avd='mozemulator-4.3',
+ adb_path=None,
+ avd_home=None,
+ logdir=None,
+ serial=None,
+ binary=None,
+ app='org.mozilla.fennec',
+ **kwargs):
+ """
+ Create a Fennec emulator runner. This can either start a new emulator
+ (which will use an avd), or connect to an already-running emulator.
+
+ :param avd: name of an AVD available in your environment.
+ Typically obtained via tooltool: either 'mozemulator-4.3' or 'mozemulator-x86'. Defaults to 'mozemulator-4.3'
+ :param avd_home: Path to avd parent directory
+ :param logdir: Path to save logfiles such as logcat and qemu output.
+ :param serial: Serial of emulator to connect to as seen in `adb devices`.
+ Defaults to the first entry in `adb devices`.
+ :param binary: Path to emulator binary.
+ Defaults to None, which causes the device_class to guess based on PATH.
+ :param app: Name of Fennec app (often org.mozilla.fennec_$USER)
+ Defaults to 'org.mozilla.fennec'
+ :returns: A DeviceRunner for Android emulators.
+ """
+ kwargs['app_ctx'] = get_app_context('fennec')(app, adb_path=adb_path,
+ avd_home=avd_home)
+ device_args = { 'app_ctx': kwargs['app_ctx'],
+ 'avd': avd,
+ 'binary': binary,
+ 'serial': serial,
+ 'logdir': logdir
+ }
+ return FennecRunner(device_class=EmulatorAVD,
+ device_args=device_args,
+ **kwargs)
+
+
def B2GEmulatorRunner(arch='arm',
b2g_home=None,
adb_path=None,
logdir=None,
binary=None,
no_window=None,
resolution=None,
sdcard=None,
@@ -163,10 +200,11 @@ def B2GDeviceRunner(b2g_home=None,
runners = {
'default': Runner,
'b2g_desktop': B2GDesktopRunner,
'b2g_emulator': B2GEmulatorRunner,
'b2g_device': B2GDeviceRunner,
'firefox': FirefoxRunner,
'thunderbird': ThunderbirdRunner,
+ 'fennec': FennecEmulatorRunner
}