Bug 1318644 - Auto-detect application type if binary has been specified.
MozReview-Commit-ID: 15etyogBI54
--- a/testing/marionette/client/marionette_driver/geckoinstance.py
+++ b/testing/marionette/client/marionette_driver/geckoinstance.py
@@ -4,16 +4,18 @@
import os
import sys
import tempfile
import time
from copy import deepcopy
+import mozversion
+
from mozprofile import Profile
from mozrunner import Runner, FennecEmulatorRunner
class GeckoInstance(object):
required_prefs = {
"browser.sessionstore.resume_from_crash": False,
"browser.shell.checkDefaultBrowser": False,
@@ -42,17 +44,17 @@ class GeckoInstance(object):
"startup.homepage_welcome_url": "",
"startup.homepage_welcome_url.additional": "",
"toolkit.telemetry.enabled": False,
# Until Bug 1238095 is fixed, we have to enable CPOWs in order
# for Marionette tests to work properly.
"dom.ipc.cpows.forbid-unsafe-from-browser": False,
}
- def __init__(self, host, port, bin, profile=None, addons=None,
+ def __init__(self, host=None, port=None, bin=None, profile=None, addons=None,
app_args=None, symbols_path=None, gecko_log=None, prefs=None,
workspace=None, verbose=0):
self.runner_class = Runner
self.app_args = app_args or []
self.runner = None
self.symbols_path = symbols_path
self.binary = bin
@@ -128,16 +130,30 @@ class GeckoInstance(object):
os.path.basename(self.profile_path),
time.time()
)
if self.workspace:
profile_args["path_to"] = os.path.join(self.workspace,
profile_name)
self.profile = Profile.clone(**profile_args)
+ @classmethod
+ def create(cls, app=None, *args, **kwargs):
+ try:
+ if not app:
+ app_id = mozversion.get_version(binary=kwargs.get('bin'))['application_id']
+ app = app_ids[app_id]
+
+ instance_class = apps[app]
+ except KeyError:
+ msg = 'Application "{0}" unknown (should be one of {1})'
+ raise NotImplementedError(msg.format(app, apps.keys()))
+
+ return instance_class(*args, **kwargs)
+
def start(self):
self._update_profile()
self.runner = self.runner_class(**self._get_runner_args())
self.runner.start()
def _get_runner_args(self):
process_args = {
'processOutputLine': [NullOutput()],
@@ -313,11 +329,16 @@ class DesktopInstance(GeckoInstance):
class NullOutput(object):
def __call__(self, line):
pass
apps = {
+ 'fennec': FennecInstance,
'fxdesktop': DesktopInstance,
- 'fennec': FennecInstance,
}
+
+app_ids = {
+ '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'fennec',
+ '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'fxdesktop',
+}
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -1,33 +1,31 @@
# 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 ConfigParser
import base64
import datetime
import json
import os
import socket
import sys
import time
import traceback
import warnings
from contextlib import contextmanager
-from decorators import do_process_check
-from keys import Keys
+import errors
+import transport
-import errors
-import geckoinstance
-import transport
-from timeout import Timeouts
-
+from .decorators import do_process_check
+from .geckoinstance import GeckoInstance
+from .keys import Keys
+from .timeout import Timeouts
WEBELEMENT_KEY = "ELEMENT"
W3C_WEBELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf"
class HTMLElement(object):
"""Represents a DOM Element."""
@@ -579,50 +577,27 @@ class Marionette(object):
self.chrome_window = None
self.baseurl = baseurl
self._test_name = None
self.socket_timeout = socket_timeout
self.crashed = 0
startup_timeout = startup_timeout or self.DEFAULT_STARTUP_TIMEOUT
if self.bin:
- self.instance = self._create_instance(app, instance_args)
+ if not Marionette.is_port_available(self.port, host=self.host):
+ ex_msg = "{0}:{1} is unavailable.".format(self.host, self.port)
+ raise errors.MarionetteException(message=ex_msg)
+
+ self.instance = GeckoInstance.create(
+ app, host=self.host, port=self.port, bin=self.bin, **instance_args)
self.instance.start()
self.raise_for_port(timeout=startup_timeout)
self.timeout = Timeouts(self)
- def _create_instance(self, app, instance_args):
- if not Marionette.is_port_available(self.port, host=self.host):
- ex_msg = "{0}:{1} is unavailable.".format(self.host, self.port)
- raise errors.MarionetteException(message=ex_msg)
- if app:
- # select instance class for the given app
- try:
- instance_class = geckoinstance.apps[app]
- except KeyError:
- msg = 'Application "{0}" unknown (should be one of {1})'
- raise NotImplementedError(
- msg.format(app, geckoinstance.apps.keys()))
- else:
- try:
- if not isinstance(self.bin, basestring):
- raise TypeError("bin must be a string if app is not specified")
- config = ConfigParser.RawConfigParser()
- config.read(os.path.join(os.path.dirname(self.bin),
- 'application.ini'))
- app = config.get('App', 'Name')
- instance_class = geckoinstance.apps[app.lower()]
- except (ConfigParser.NoOptionError,
- ConfigParser.NoSectionError,
- KeyError):
- instance_class = geckoinstance.GeckoInstance
- return instance_class(host=self.host, port=self.port, bin=self.bin,
- **instance_args)
-
@property
def profile_path(self):
if self.instance and self.instance.profile:
return self.instance.profile.profile
def cleanup(self):
if self.session:
try:
--- a/testing/marionette/client/requirements.txt
+++ b/testing/marionette/client/requirements.txt
@@ -1,1 +1,2 @@
mozrunner >= 6.13
+mozversion >= 1.1
--- a/testing/marionette/harness/marionette/runner/base.py
+++ b/testing/marionette/harness/marionette/runner/base.py
@@ -1,36 +1,36 @@
# 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 argparse import ArgumentParser
-
-from copy import deepcopy
import json
-import mozinfo
-import moznetwork
import os
import random
import re
import socket
import sys
import time
import traceback
import unittest
import warnings
+
+from argparse import ArgumentParser
+from copy import deepcopy
+
+import mozinfo
+import moznetwork
import mozprofile
-
+import mozversion
from manifestparser import TestManifest
from manifestparser.filters import tags
from marionette_driver.marionette import Marionette
from moztest.adapters.unit import StructuredTestRunner, StructuredTestResult
from moztest.results import TestResultCollection, TestResult, relevant_line
-import mozversion
import httpd
here = os.path.abspath(os.path.dirname(__file__))
def update_mozinfo(path=None):
@@ -501,35 +501,39 @@ class BaseMarionetteTestRunner(object):
symbols_path=None,
shuffle=False, shuffle_seed=random.randint(0, sys.maxint), this_chunk=1,
total_chunks=1,
server_root=None, gecko_log=None, result_callbacks=None,
prefs=None, test_tags=None,
socket_timeout=BaseMarionetteArguments.socket_timeout_default,
startup_timeout=None, addons=None, workspace=None,
verbose=0, e10s=True, emulator=False, **kwargs):
+
+ self._appinfo = None
+ self._appName = None
+ self._capabilities = None
+ self._filename_pattern = None
+ self._version_info = {}
+
self.extra_kwargs = kwargs
self.test_kwargs = deepcopy(kwargs)
self.address = address
self.app = app
self.app_args = app_args or []
self.bin = binary
self.emulator = emulator
self.profile = profile
self.addons = addons
self.logger = logger
self.httpd = None
self.marionette = None
self.logdir = logdir
self.repeat = repeat
self.symbols_path = symbols_path
self.socket_timeout = socket_timeout
- self._capabilities = None
- self._appinfo = None
- self._appName = None
self.shuffle = shuffle
self.shuffle_seed = shuffle_seed
self.server_root = server_root
self.this_chunk = this_chunk
self.total_chunks = total_chunks
self.mixin_run_tests = []
self.manifest_skipped_tests = []
self.tests = []
@@ -538,17 +542,16 @@ class BaseMarionetteTestRunner(object):
self.test_tags = test_tags
self.startup_timeout = startup_timeout
self.workspace = workspace
# If no workspace is set, default location for gecko.log is .
# and default location for profile is TMP
self.workspace_path = workspace or os.getcwd()
self.verbose = verbose
self.e10s = e10s
- self._filename_pattern = None
def gather_debug(test, status):
rv = {}
marionette = test._marionette_weakref()
# In the event we're gathering debug without starting a session,
# skip marionette commands
if marionette.session is not None:
@@ -671,16 +674,27 @@ class BaseMarionetteTestRunner(object):
"""Set binary and reset parts of runner accordingly.
Intended use: to change binary between calls to run_tests
"""
self._bin = path
self.tests = []
self.cleanup()
+ @property
+ def version_info(self):
+ if not self._version_info:
+ try:
+ # TODO: Get version_info in Fennec case
+ self._version_info = mozversion.get_version(binary=self.bin)
+ except Exception:
+ self.logger.warning("Failed to retrieve version information for {}".format(
+ self.bin))
+ return self._version_info
+
def reset_test_stats(self):
self.passed = 0
self.failed = 0
self.crashed = 0
self.unexpected_successes = 0
self.todo = 0
self.skipped = 0
self.failures = []
@@ -820,25 +834,20 @@ class BaseMarionetteTestRunner(object):
device_info = None
if self.marionette.instance and self.emulator:
try:
device_info = self.marionette.instance.runner.device.dm.getInfo()
except Exception:
self.logger.warning('Could not get device info.')
- # TODO: Get version_info in Fennec case
- version_info = None
- if self.bin:
- version_info = mozversion.get_version(binary=self.bin)
-
self.logger.info("running with e10s: {}".format(self.e10s))
self.logger.suite_start(self.tests,
- version_info=version_info,
+ version_info=self.version_info,
device_info=device_info)
self._log_skipped_tests()
interrupted = None
try:
counter = self.repeat
while counter >= 0:
new file mode 100644
--- /dev/null
+++ b/testing/marionette/harness/marionette/tests/unit/test_geckoinstance.py
@@ -0,0 +1,27 @@
+# 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 marionette.marionette_test import MarionetteTestCase
+from marionette_driver.geckoinstance import apps, GeckoInstance
+
+
+class TestGeckoInstance(MarionetteTestCase):
+
+ def test_create(self):
+ """Test that the correct gecko instance is determined."""
+ for app in apps:
+ # If app has been specified we directly return the appropriate instance class
+ self.assertEqual(type(GeckoInstance.create(app=app, bin="n/a")),
+ apps[app])
+
+ # When using mozversion to detect the instance class, it should never return the
+ # base GeckoInstance class.
+ self.assertNotEqual(GeckoInstance.create(bin=self.marionette.bin),
+ GeckoInstance)
+
+ # Unknown applications and binaries should fail
+ self.assertRaises(NotImplementedError, GeckoInstance.create,
+ app="n/a", binary=self.marionette.bin)
+ self.assertRaises(IOError, GeckoInstance.create,
+ bin="n/a")
--- a/testing/marionette/harness/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/harness/marionette/tests/unit/unit-tests.ini
@@ -1,9 +1,10 @@
[test_marionette.py]
+[test_geckoinstance.py]
[test_data_driven.py]
[test_session.py]
[test_capabilities.py]
[test_accessibility.py]
[test_expectedfail.py]
expected = fail
[test_import_script.py]
[test_click.py]