Bug 1445944 - [mozrunner] Create a base BlinkRuntimeRunner and add a ChromeRunner to the runners list r=rwood
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Thu, 12 Apr 2018 22:29:17 -0400
changeset 467469 df5d226d1df3204b7fb4b975e7075a3a3e7a9775
parent 467468 917baaf08d71cf0e16a53992a33c4500d6029ba9
child 467470 74448840c21fda21bee03f7b278294105b47a067
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrwood
bugs1445944
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1445944 - [mozrunner] Create a base BlinkRuntimeRunner and add a ChromeRunner to the runners list r=rwood This allows consumers to bootstrap Chrome with mozrunner. For now the profile implementation is just an empty class but this will be expanded in a future commit. MozReview-Commit-ID: 1Z14FudH0JJ
testing/mozbase/docs/mozrunner.rst
testing/mozbase/mozrunner/mozrunner/application.py
testing/mozbase/mozrunner/mozrunner/base/__init__.py
testing/mozbase/mozrunner/mozrunner/base/browser.py
testing/mozbase/mozrunner/mozrunner/runners.py
testing/mozbase/mozrunner/tests/conftest.py
testing/mozbase/mozrunner/tests/test_crash.py
--- a/testing/mozbase/docs/mozrunner.rst
+++ b/testing/mozbase/docs/mozrunner.rst
@@ -148,16 +148,22 @@ BaseRunner
    :members:
 
 GeckoRuntimeRunner
 ~~~~~~~~~~~~~~~~~~
 .. autoclass:: mozrunner.base.GeckoRuntimeRunner
    :show-inheritance:
    :members:
 
+BlinkRuntimeRunner
+~~~~~~~~~~~~~~~~~~
+.. autoclass:: mozrunner.base.BlinkRuntimeRunner
+   :show-inheritance:
+   :members:
+
 DeviceRunner
 ~~~~~~~~~~~~
 .. autoclass:: mozrunner.base.DeviceRunner
    :show-inheritance:
    :members:
 
 Device API Documentation
 ------------------------
--- a/testing/mozbase/mozrunner/mozrunner/application.py
+++ b/testing/mozbase/mozrunner/mozrunner/application.py
@@ -15,20 +15,23 @@ from mozprofile import (
     FirefoxProfile,
     ThunderbirdProfile
 )
 
 here = os.path.abspath(os.path.dirname(__file__))
 
 
 def get_app_context(appname):
-    context_map = {'default': DefaultContext,
-                   'firefox': FirefoxContext,
-                   'thunderbird': ThunderbirdContext,
-                   'fennec': FennecContext}
+    context_map = {
+        'chrome': ChromeContext,
+        'default': DefaultContext,
+        'fennec': FennecContext,
+        'firefox': FirefoxContext,
+        'thunderbird': ThunderbirdContext,
+    }
     if appname not in context_map:
         raise KeyError("Application '%s' not supported!" % appname)
     return context_map[appname]
 
 
 class DefaultContext(object):
     profile_class = Profile
 
@@ -126,8 +129,16 @@ class FennecContext(RemoteContext):
 
 
 class FirefoxContext(object):
     profile_class = FirefoxProfile
 
 
 class ThunderbirdContext(object):
     profile_class = ThunderbirdProfile
+
+
+class ChromeProfile(object):
+    """Dummy profile class until a proper one is implemented in mozprofile"""
+
+
+class ChromeContext(object):
+    profile_class = ChromeProfile
--- a/testing/mozbase/mozrunner/mozrunner/base/__init__.py
+++ b/testing/mozbase/mozrunner/mozrunner/base/__init__.py
@@ -1,7 +1,6 @@
+# flake8: noqa
 from __future__ import absolute_import
 
 from .runner import BaseRunner
 from .device import DeviceRunner, FennecRunner
-from .browser import GeckoRuntimeRunner
-
-__all__ = ['BaseRunner', 'DeviceRunner', 'FennecRunner', 'GeckoRuntimeRunner']
+from .browser import GeckoRuntimeRunner, BlinkRuntimeRunner
--- a/testing/mozbase/mozrunner/mozrunner/base/browser.py
+++ b/testing/mozbase/mozrunner/mozrunner/base/browser.py
@@ -72,8 +72,24 @@ class GeckoRuntimeRunner(BaseRunner):
             self.env["MOZ_CRASHREPORTER_DISABLE"] = "1"
         else:
             if not self.show_crash_reporter:
                 # hide the crash reporter window
                 self.env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
             self.env["MOZ_CRASHREPORTER"] = "1"
 
         BaseRunner.start(self, *args, **kwargs)
+
+
+class BlinkRuntimeRunner(BaseRunner):
+    """A base runner class for running apps like Google Chrome or Chromium."""
+    def __init__(self, binary, cmdargs=None, **runner_args):
+        super(BlinkRuntimeRunner, self).__init__(**runner_args)
+        self.binary = binary
+        self.cmdargs = cmdargs or []
+
+    @property
+    def command(self):
+        cmd = self.cmdargs[:]
+        return [self.binary] + cmd
+
+    def check_for_crashes(self, *args, **kwargs):
+        raise NotImplementedError
--- a/testing/mozbase/mozrunner/mozrunner/runners.py
+++ b/testing/mozbase/mozrunner/mozrunner/runners.py
@@ -6,17 +6,17 @@
 """
 This module contains a set of shortcut methods that create runners for commonly
 used Mozilla applications, such as Firefox, Firefox for Android or Thunderbird.
 """
 
 from __future__ import absolute_import
 
 from .application import get_app_context
-from .base import GeckoRuntimeRunner, FennecRunner
+from .base import GeckoRuntimeRunner, FennecRunner, BlinkRuntimeRunner
 from .devices import EmulatorAVD
 
 
 def Runner(*args, **kwargs):
     """
     Create a generic GeckoRuntime runner.
 
     :param binary: Path to binary.
@@ -69,16 +69,27 @@ def ThunderbirdRunner(*args, **kwargs):
     :param show_crash_reporter: allow the crash reporter window to pop up.
         Defaults to False.
     :returns: A GeckoRuntimeRunner for Thunderbird.
     """
     kwargs['app_ctx'] = get_app_context('thunderbird')()
     return GeckoRuntimeRunner(*args, **kwargs)
 
 
+def ChromeRunner(*args, **kwargs):
+    """
+    Create a desktop Google Chrome runner.
+
+    :param binary: Path to Chrome binary.
+    :param cmdargs: Arguments to pass into the binary.
+    """
+    kwargs['app_ctx'] = get_app_context('chrome')()
+    return BlinkRuntimeRunner(*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):
@@ -108,13 +119,14 @@ def FennecEmulatorRunner(avd='mozemulato
                    'serial': serial,
                    'logdir': logdir}
     return FennecRunner(device_class=EmulatorAVD,
                         device_args=device_args,
                         **kwargs)
 
 
 runners = {
+    'chrome': ChromeRunner,
     'default': Runner,
     'firefox': FirefoxRunner,
+    'fennec': FennecEmulatorRunner,
     'thunderbird': ThunderbirdRunner,
-    'fennec': FennecEmulatorRunner
 }
--- a/testing/mozbase/mozrunner/tests/conftest.py
+++ b/testing/mozbase/mozrunner/tests/conftest.py
@@ -3,47 +3,56 @@
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import
 
 import os
 import threading
 from time import sleep
 
-import mozprofile
 import mozrunner
 import pytest
 from moztest.selftest import fixtures
 
 
-@pytest.fixture
-def profile():
-    return mozprofile.FirefoxProfile()
-
-
-@pytest.fixture
+@pytest.fixture(scope='session')
 def get_binary():
     if 'BROWSER_PATH' in os.environ:
         os.environ['GECKO_BINARY_PATH'] = os.environ['BROWSER_PATH']
 
     def inner(app):
-        if app != 'firefox':
+        if app not in ('chrome', 'firefox'):
             pytest.xfail(reason="{} support not implemented".format(app))
 
-        binary = fixtures.binary()
+        if app == 'firefox':
+            binary = fixtures.binary()
+        elif app == 'chrome':
+            binary = os.environ.get('CHROME_BINARY_PATH')
+
         if not binary:
             pytest.skip("could not find a {} binary".format(app))
         return binary
     return inner
 
 
-@pytest.fixture
-def runner(profile, get_binary):
-    binary = get_binary('firefox')
-    return mozrunner.FirefoxRunner(binary, profile=profile)
+@pytest.fixture(params=['firefox', 'chrome'])
+def runner(request, get_binary):
+    app = request.param
+    binary = get_binary(app)
+
+    cmdargs = ['--headless']
+    if app == 'chrome':
+        # prevents headless chrome from exiting after loading the page
+        cmdargs.append('--remote-debugging-port=9222')
+        # only needed on Windows, but no harm in specifying it everywhere
+        cmdargs.append('--disable-gpu')
+    runner = mozrunner.runners[app](binary, cmdargs=cmdargs)
+    runner.app = app
+    yield runner
+    runner.stop()
 
 
 class RunnerThread(threading.Thread):
     def __init__(self, runner, start=False, timeout=1):
         threading.Thread.__init__(self)
         self.runner = runner
         self.timeout = timeout
         self.do_start = start
--- a/testing/mozbase/mozrunner/tests/test_crash.py
+++ b/testing/mozbase/mozrunner/tests/test_crash.py
@@ -8,16 +8,19 @@ from __future__ import absolute_import
 from mock import patch
 
 import mozunit
 import pytest
 
 
 @pytest.mark.parametrize('logger', [True, False])
 def test_crash_count_with_or_without_logger(runner, logger):
+    if runner.app == 'chrome':
+        pytest.xfail("crash checking not implemented for ChromeRunner")
+
     if not logger:
         runner.logger = None
         fn = 'check_for_crashes'
     else:
         fn = 'log_crashes'
 
     with patch('mozcrash.{}'.format(fn), return_value=2) as mock:
         assert runner.crashed == 0