Bug 1445944 - [mozrunner] Create a base BlinkRuntimeRunner and add a ChromeRunner to the runners list draft
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Thu, 12 Apr 2018 22:29:17 -0400
changeset 781959 4d85545865877d377c2ad959f4c2996e67e33f4d
parent 781958 d12b4fc2b78a8d8ac0152380c35869db6e168a81
child 781960 71e74aa990f3cdab1273a8baf39caf7f0798d5e2
push id106459
push userahalberstadt@mozilla.com
push dateFri, 13 Apr 2018 22:27:59 +0000
bugs1445944
milestone61.0a1
Bug 1445944 - [mozrunner] Create a base BlinkRuntimeRunner and add a ChromeRunner to the runners list 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 destkop 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