summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/harness/wptrunner/browsers
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /testing/web-platform/harness/wptrunner/browsers
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/web-platform/harness/wptrunner/browsers')
-rw-r--r--testing/web-platform/harness/wptrunner/browsers/__init__.py33
-rw-r--r--testing/web-platform/harness/wptrunner/browsers/b2g.py243
-rw-r--r--testing/web-platform/harness/wptrunner/browsers/b2g_setup/certtest_app.zipbin0 -> 1237 bytes
-rw-r--r--testing/web-platform/harness/wptrunner/browsers/base.py160
-rw-r--r--testing/web-platform/harness/wptrunner/browsers/chrome.py81
-rw-r--r--testing/web-platform/harness/wptrunner/browsers/firefox.py274
-rw-r--r--testing/web-platform/harness/wptrunner/browsers/server-locations.txt38
-rw-r--r--testing/web-platform/harness/wptrunner/browsers/servo.py80
-rw-r--r--testing/web-platform/harness/wptrunner/browsers/servodriver.py162
9 files changed, 1071 insertions, 0 deletions
diff --git a/testing/web-platform/harness/wptrunner/browsers/__init__.py b/testing/web-platform/harness/wptrunner/browsers/__init__.py
new file mode 100644
index 000000000..ffc5aedc8
--- /dev/null
+++ b/testing/web-platform/harness/wptrunner/browsers/__init__.py
@@ -0,0 +1,33 @@
+# 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/.
+
+"""Subpackage where each product is defined. Each product is created by adding a
+a .py file containing a __wptrunner__ variable in the global scope. This must be
+a dictionary with the fields
+
+"product": Name of the product, assumed to be unique.
+"browser": String indicating the Browser implementation used to launch that
+ product.
+"executor": Dictionary with keys as supported test types and values as the name
+ of the Executor implemantation that will be used to run that test
+ type.
+"browser_kwargs": String naming function that takes product, binary,
+ prefs_root and the wptrunner.run_tests kwargs dict as arguments
+ and returns a dictionary of kwargs to use when creating the
+ Browser class.
+"executor_kwargs": String naming a function that takes http server url and
+ timeout multiplier and returns kwargs to use when creating
+ the executor class.
+"env_options": String naming a funtion of no arguments that returns the
+ arguments passed to the TestEnvironment.
+
+All classes and functions named in the above dict must be imported into the
+module global scope.
+"""
+
+product_list = ["b2g",
+ "chrome",
+ "firefox",
+ "servo",
+ "servodriver"]
diff --git a/testing/web-platform/harness/wptrunner/browsers/b2g.py b/testing/web-platform/harness/wptrunner/browsers/b2g.py
new file mode 100644
index 000000000..bedb00a49
--- /dev/null
+++ b/testing/web-platform/harness/wptrunner/browsers/b2g.py
@@ -0,0 +1,243 @@
+# 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 os
+import tempfile
+import shutil
+import subprocess
+
+import fxos_appgen
+import gaiatest
+import mozdevice
+import moznetwork
+import mozrunner
+from marionette import expected
+from marionette.by import By
+from marionette.wait import Wait
+from mozprofile import FirefoxProfile, Preferences
+
+from .base import get_free_port, BrowserError, Browser, ExecutorBrowser
+from ..executors.executormarionette import MarionetteTestharnessExecutor
+from ..hosts import HostsFile, HostsLine
+from ..environment import hostnames
+
+here = os.path.split(__file__)[0]
+
+__wptrunner__ = {"product": "b2g",
+ "check_args": "check_args",
+ "browser": "B2GBrowser",
+ "executor": {"testharness": "B2GMarionetteTestharnessExecutor"},
+ "browser_kwargs": "browser_kwargs",
+ "executor_kwargs": "executor_kwargs",
+ "env_options": "env_options"}
+
+
+def check_args(**kwargs):
+ pass
+
+
+def browser_kwargs(test_environment, **kwargs):
+ return {"prefs_root": kwargs["prefs_root"],
+ "no_backup": kwargs.get("b2g_no_backup", False)}
+
+
+def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+ **kwargs):
+ timeout_multiplier = kwargs["timeout_multiplier"]
+ if timeout_multiplier is None:
+ timeout_multiplier = 2
+
+ executor_kwargs = {"server_config": server_config,
+ "timeout_multiplier": timeout_multiplier,
+ "close_after_done": False}
+
+ if test_type == "reftest":
+ executor_kwargs["cache_manager"] = cache_manager
+
+ return executor_kwargs
+
+
+def env_options():
+ return {"host": "web-platform.test",
+ "bind_hostname": "false",
+ "test_server_port": False}
+
+
+class B2GBrowser(Browser):
+ used_ports = set()
+ init_timeout = 180
+
+ def __init__(self, logger, prefs_root, no_backup=False):
+ Browser.__init__(self, logger)
+ logger.info("Waiting for device")
+ subprocess.call(["adb", "wait-for-device"])
+ self.device = mozdevice.DeviceManagerADB()
+ self.marionette_port = get_free_port(2828, exclude=self.used_ports)
+ self.used_ports.add(self.marionette_port)
+ self.cert_test_app = None
+ self.runner = None
+ self.prefs_root = prefs_root
+
+ self.no_backup = no_backup
+ self.backup_path = None
+ self.backup_paths = []
+ self.backup_dirs = []
+
+ def setup(self):
+ self.logger.info("Running B2G setup")
+ self.backup_path = tempfile.mkdtemp()
+
+ self.logger.debug("Backing up device to %s" % (self.backup_path,))
+
+ if not self.no_backup:
+ self.backup_dirs = [("/data/local", os.path.join(self.backup_path, "local")),
+ ("/data/b2g/mozilla", os.path.join(self.backup_path, "profile"))]
+
+ self.backup_paths = [("/system/etc/hosts", os.path.join(self.backup_path, "hosts"))]
+
+ for remote, local in self.backup_dirs:
+ self.device.getDirectory(remote, local)
+
+ for remote, local in self.backup_paths:
+ self.device.getFile(remote, local)
+
+ self.setup_hosts()
+
+ def start(self):
+ profile = FirefoxProfile()
+
+ profile.set_preferences({"dom.disable_open_during_load": False,
+ "marionette.defaultPrefs.enabled": True})
+
+ self.logger.debug("Creating device runner")
+ self.runner = mozrunner.B2GDeviceRunner(profile=profile)
+ self.logger.debug("Starting device runner")
+ self.runner.start()
+ self.logger.debug("Device runner started")
+
+ def setup_hosts(self):
+ host_ip = moznetwork.get_ip()
+
+ temp_dir = tempfile.mkdtemp()
+ hosts_path = os.path.join(temp_dir, "hosts")
+ remote_path = "/system/etc/hosts"
+ try:
+ self.device.getFile("/system/etc/hosts", hosts_path)
+
+ with open(hosts_path) as f:
+ hosts_file = HostsFile.from_file(f)
+
+ for canonical_hostname in hostnames:
+ hosts_file.set_host(HostsLine(host_ip, canonical_hostname))
+
+ with open(hosts_path, "w") as f:
+ hosts_file.to_file(f)
+
+ self.logger.info("Installing hosts file")
+
+ self.device.remount()
+ self.device.removeFile(remote_path)
+ self.device.pushFile(hosts_path, remote_path)
+ finally:
+ os.unlink(hosts_path)
+ os.rmdir(temp_dir)
+
+ def load_prefs(self):
+ prefs_path = os.path.join(self.prefs_root, "prefs_general.js")
+ if os.path.exists(prefs_path):
+ preferences = Preferences.read_prefs(prefs_path)
+ else:
+ self.logger.warning("Failed to find base prefs file in %s" % prefs_path)
+ preferences = []
+
+ return preferences
+
+ def stop(self):
+ pass
+
+ def on_output(self):
+ raise NotImplementedError
+
+ def cleanup(self):
+ self.logger.debug("Running browser cleanup steps")
+
+ self.device.remount()
+
+ for remote, local in self.backup_dirs:
+ self.device.removeDir(remote)
+ self.device.pushDir(local, remote)
+
+ for remote, local in self.backup_paths:
+ self.device.removeFile(remote)
+ self.device.pushFile(local, remote)
+
+ shutil.rmtree(self.backup_path)
+ self.device.reboot(wait=True)
+
+ def pid(self):
+ return None
+
+ def is_alive(self):
+ return True
+
+ def executor_browser(self):
+ return B2GExecutorBrowser, {"marionette_port": self.marionette_port}
+
+
+class B2GExecutorBrowser(ExecutorBrowser):
+ # The following methods are called from a different process
+ def __init__(self, *args, **kwargs):
+ ExecutorBrowser.__init__(self, *args, **kwargs)
+
+ import sys, subprocess
+
+ self.device = mozdevice.ADBB2G()
+ self.device.forward("tcp:%s" % self.marionette_port,
+ "tcp:2828")
+ self.executor = None
+ self.marionette = None
+ self.gaia_device = None
+ self.gaia_apps = None
+
+ def after_connect(self, executor):
+ self.executor = executor
+ self.marionette = executor.marionette
+ self.executor.logger.debug("Running browser.after_connect steps")
+
+ self.gaia_apps = gaiatest.GaiaApps(marionette=executor.marionette)
+
+ self.executor.logger.debug("Waiting for homescreen to load")
+
+ # Moved out of gaia_test temporarily
+ self.executor.logger.info("Waiting for B2G to be ready")
+ self.wait_for_homescreen(timeout=60)
+
+ self.install_cert_app()
+ self.use_cert_app()
+
+ def install_cert_app(self):
+ """Install the container app used to run the tests"""
+ if fxos_appgen.is_installed("CertTest App"):
+ self.executor.logger.info("CertTest App is already installed")
+ return
+ self.executor.logger.info("Installing CertTest App")
+ app_path = os.path.join(here, "b2g_setup", "certtest_app.zip")
+ fxos_appgen.install_app("CertTest App", app_path, marionette=self.marionette)
+ self.executor.logger.debug("Install complete")
+
+ def use_cert_app(self):
+ """Start the app used to run the tests"""
+ self.executor.logger.info("Homescreen loaded")
+ self.gaia_apps.launch("CertTest App")
+
+ def wait_for_homescreen(self, timeout):
+ self.executor.logger.info("Waiting for home screen to load")
+ Wait(self.marionette, timeout).until(expected.element_present(
+ By.CSS_SELECTOR, '#homescreen[loading-state=false]'))
+
+
+class B2GMarionetteTestharnessExecutor(MarionetteTestharnessExecutor):
+ def after_connect(self):
+ self.browser.after_connect(self)
+ MarionetteTestharnessExecutor.after_connect(self)
diff --git a/testing/web-platform/harness/wptrunner/browsers/b2g_setup/certtest_app.zip b/testing/web-platform/harness/wptrunner/browsers/b2g_setup/certtest_app.zip
new file mode 100644
index 000000000..f9cbd5300
--- /dev/null
+++ b/testing/web-platform/harness/wptrunner/browsers/b2g_setup/certtest_app.zip
Binary files differ
diff --git a/testing/web-platform/harness/wptrunner/browsers/base.py b/testing/web-platform/harness/wptrunner/browsers/base.py
new file mode 100644
index 000000000..1d3b3d231
--- /dev/null
+++ b/testing/web-platform/harness/wptrunner/browsers/base.py
@@ -0,0 +1,160 @@
+# 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 os
+import platform
+import socket
+from abc import ABCMeta, abstractmethod
+
+from ..wptcommandline import require_arg
+
+here = os.path.split(__file__)[0]
+
+
+def cmd_arg(name, value=None):
+ prefix = "-" if platform.system() == "Windows" else "--"
+ rv = prefix + name
+ if value is not None:
+ rv += "=" + value
+ return rv
+
+
+def get_free_port(start_port, exclude=None):
+ """Get the first port number after start_port (inclusive) that is
+ not currently bound.
+
+ :param start_port: Integer port number at which to start testing.
+ :param exclude: Set of port numbers to skip"""
+ port = start_port
+ while True:
+ if exclude and port in exclude:
+ port += 1
+ continue
+ s = socket.socket()
+ try:
+ s.bind(("127.0.0.1", port))
+ except socket.error:
+ port += 1
+ else:
+ return port
+ finally:
+ s.close()
+
+def browser_command(binary, args, debug_info):
+ if debug_info:
+ if debug_info.requiresEscapedArgs:
+ args = [item.replace("&", "\\&") for item in args]
+ debug_args = [debug_info.path] + debug_info.args
+ else:
+ debug_args = []
+
+ command = [binary] + args
+
+ return debug_args, command
+
+
+class BrowserError(Exception):
+ pass
+
+
+class Browser(object):
+ __metaclass__ = ABCMeta
+
+ process_cls = None
+ init_timeout = 30
+
+ def __init__(self, logger):
+ """Abstract class serving as the basis for Browser implementations.
+
+ The Browser is used in the TestRunnerManager to start and stop the browser
+ process, and to check the state of that process. This class also acts as a
+ context manager, enabling it to do browser-specific setup at the start of
+ the testrun and cleanup after the run is complete.
+
+ :param logger: Structured logger to use for output.
+ """
+ self.logger = logger
+
+ def __enter__(self):
+ self.setup()
+ return self
+
+ def __exit__(self, *args, **kwargs):
+ self.cleanup()
+
+ def setup(self):
+ """Used for browser-specific setup that happens at the start of a test run"""
+ pass
+
+ @abstractmethod
+ def start(self):
+ """Launch the browser object and get it into a state where is is ready to run tests"""
+ pass
+
+ @abstractmethod
+ def stop(self):
+ """Stop the running browser process."""
+ pass
+
+ @abstractmethod
+ def pid(self):
+ """pid of the browser process or None if there is no pid"""
+ pass
+
+ @abstractmethod
+ def is_alive(self):
+ """Boolean indicating whether the browser process is still running"""
+ pass
+
+ def setup_ssl(self, hosts):
+ """Return a certificate to use for tests requiring ssl that will be trusted by the browser"""
+ raise NotImplementedError("ssl testing not supported")
+
+ def cleanup(self):
+ """Browser-specific cleanup that is run after the testrun is finished"""
+ pass
+
+ def executor_browser(self):
+ """Returns the ExecutorBrowser subclass for this Browser subclass and the keyword arguments
+ with which it should be instantiated"""
+ return ExecutorBrowser, {}
+
+ def log_crash(self, process, test):
+ """Return a list of dictionaries containing information about crashes that happend
+ in the browser, or an empty list if no crashes occurred"""
+ self.logger.crash(process, test)
+
+
+class NullBrowser(Browser):
+ def start(self):
+ """No-op browser to use in scenarios where the TestRunnerManager shouldn't
+ actually own the browser process (e.g. Servo where we start one browser
+ per test)"""
+ pass
+
+ def stop(self):
+ pass
+
+ def pid(self):
+ return None
+
+ def is_alive(self):
+ return True
+
+ def on_output(self, line):
+ raise NotImplementedError
+
+
+class ExecutorBrowser(object):
+ def __init__(self, **kwargs):
+ """View of the Browser used by the Executor object.
+ This is needed because the Executor runs in a child process and
+ we can't ship Browser instances between processes on Windows.
+
+ Typically this will have a few product-specific properties set,
+ but in some cases it may have more elaborate methods for setting
+ up the browser from the runner process.
+ """
+ for k, v in kwargs.iteritems():
+ setattr(self, k, v)
diff --git a/testing/web-platform/harness/wptrunner/browsers/chrome.py b/testing/web-platform/harness/wptrunner/browsers/chrome.py
new file mode 100644
index 000000000..184913594
--- /dev/null
+++ b/testing/web-platform/harness/wptrunner/browsers/chrome.py
@@ -0,0 +1,81 @@
+# 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 .base import Browser, ExecutorBrowser, require_arg
+from ..webdriver_server import ChromeDriverServer
+from ..executors import executor_kwargs as base_executor_kwargs
+from ..executors.executorselenium import (SeleniumTestharnessExecutor,
+ SeleniumRefTestExecutor)
+
+
+__wptrunner__ = {"product": "chrome",
+ "check_args": "check_args",
+ "browser": "ChromeBrowser",
+ "executor": {"testharness": "SeleniumTestharnessExecutor",
+ "reftest": "SeleniumRefTestExecutor"},
+ "browser_kwargs": "browser_kwargs",
+ "executor_kwargs": "executor_kwargs",
+ "env_options": "env_options"}
+
+
+def check_args(**kwargs):
+ require_arg(kwargs, "webdriver_binary")
+
+
+def browser_kwargs(**kwargs):
+ return {"binary": kwargs["binary"],
+ "webdriver_binary": kwargs["webdriver_binary"]}
+
+
+def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+ **kwargs):
+ from selenium.webdriver import DesiredCapabilities
+
+ executor_kwargs = base_executor_kwargs(test_type, server_config,
+ cache_manager, **kwargs)
+ executor_kwargs["close_after_done"] = True
+ executor_kwargs["capabilities"] = dict(DesiredCapabilities.CHROME.items())
+ if kwargs["binary"] is not None:
+ executor_kwargs["capabilities"]["chromeOptions"] = {"binary": kwargs["binary"]}
+
+ return executor_kwargs
+
+
+def env_options():
+ return {"host": "web-platform.test",
+ "bind_hostname": "true"}
+
+
+class ChromeBrowser(Browser):
+ """Chrome is backed by chromedriver, which is supplied through
+ ``wptrunner.webdriver.ChromeDriverServer``.
+ """
+
+ def __init__(self, logger, binary, webdriver_binary="chromedriver"):
+ """Creates a new representation of Chrome. The `binary` argument gives
+ the browser binary to use for testing."""
+ Browser.__init__(self, logger)
+ self.binary = binary
+ self.server = ChromeDriverServer(self.logger, binary=webdriver_binary)
+
+ def start(self):
+ self.server.start(block=False)
+
+ def stop(self):
+ self.server.stop()
+
+ def pid(self):
+ return self.server.pid
+
+ def is_alive(self):
+ # TODO(ato): This only indicates the driver is alive,
+ # and doesn't say anything about whether a browser session
+ # is active.
+ return self.server.is_alive()
+
+ def cleanup(self):
+ self.stop()
+
+ def executor_browser(self):
+ return ExecutorBrowser, {"webdriver_url": self.server.url}
diff --git a/testing/web-platform/harness/wptrunner/browsers/firefox.py b/testing/web-platform/harness/wptrunner/browsers/firefox.py
new file mode 100644
index 000000000..183820c5c
--- /dev/null
+++ b/testing/web-platform/harness/wptrunner/browsers/firefox.py
@@ -0,0 +1,274 @@
+# 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 os
+import platform
+import subprocess
+import sys
+
+import mozinfo
+from mozprocess import ProcessHandler
+from mozprofile import FirefoxProfile, Preferences
+from mozprofile.permissions import ServerLocations
+from mozrunner import FirefoxRunner
+from mozrunner.utils import get_stack_fixer_function
+from mozcrash import mozcrash
+
+from .base import (get_free_port,
+ Browser,
+ ExecutorBrowser,
+ require_arg,
+ cmd_arg,
+ browser_command)
+from ..executors import executor_kwargs as base_executor_kwargs
+from ..executors.executormarionette import (MarionetteTestharnessExecutor,
+ MarionetteRefTestExecutor,
+ MarionetteWdspecExecutor)
+from ..environment import hostnames
+
+
+here = os.path.join(os.path.split(__file__)[0])
+
+__wptrunner__ = {"product": "firefox",
+ "check_args": "check_args",
+ "browser": "FirefoxBrowser",
+ "executor": {"testharness": "MarionetteTestharnessExecutor",
+ "reftest": "MarionetteRefTestExecutor",
+ "wdspec": "MarionetteWdspecExecutor"},
+ "browser_kwargs": "browser_kwargs",
+ "executor_kwargs": "executor_kwargs",
+ "env_options": "env_options",
+ "run_info_extras": "run_info_extras",
+ "update_properties": "update_properties"}
+
+
+def check_args(**kwargs):
+ require_arg(kwargs, "binary")
+ if kwargs["ssl_type"] != "none":
+ require_arg(kwargs, "certutil_binary")
+
+
+def browser_kwargs(**kwargs):
+ return {"binary": kwargs["binary"],
+ "prefs_root": kwargs["prefs_root"],
+ "debug_info": kwargs["debug_info"],
+ "symbols_path": kwargs["symbols_path"],
+ "stackwalk_binary": kwargs["stackwalk_binary"],
+ "certutil_binary": kwargs["certutil_binary"],
+ "ca_certificate_path": kwargs["ssl_env"].ca_cert_path(),
+ "e10s": kwargs["gecko_e10s"],
+ "stackfix_dir": kwargs["stackfix_dir"]}
+
+
+def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+ **kwargs):
+ executor_kwargs = base_executor_kwargs(test_type, server_config,
+ cache_manager, **kwargs)
+ executor_kwargs["close_after_done"] = test_type != "reftest"
+ if kwargs["timeout_multiplier"] is None:
+ if test_type == "reftest":
+ if run_info_data["debug"] or run_info_data.get("asan"):
+ executor_kwargs["timeout_multiplier"] = 4
+ else:
+ executor_kwargs["timeout_multiplier"] = 2
+ elif run_info_data["debug"] or run_info_data.get("asan"):
+ executor_kwargs["timeout_multiplier"] = 3
+ if test_type == "wdspec":
+ executor_kwargs["webdriver_binary"] = kwargs.get("webdriver_binary")
+ return executor_kwargs
+
+
+def env_options():
+ return {"host": "127.0.0.1",
+ "external_host": "web-platform.test",
+ "bind_hostname": "false",
+ "certificate_domain": "web-platform.test",
+ "supports_debugger": True}
+
+
+def run_info_extras(**kwargs):
+ return {"e10s": kwargs["gecko_e10s"]}
+
+
+def update_properties():
+ return ["debug", "e10s", "os", "version", "processor", "bits"], {"debug", "e10s"}
+
+
+class FirefoxBrowser(Browser):
+ used_ports = set()
+ init_timeout = 60
+
+ def __init__(self, logger, binary, prefs_root, debug_info=None,
+ symbols_path=None, stackwalk_binary=None, certutil_binary=None,
+ ca_certificate_path=None, e10s=False, stackfix_dir=None):
+ Browser.__init__(self, logger)
+ self.binary = binary
+ self.prefs_root = prefs_root
+ self.marionette_port = None
+ self.runner = None
+ self.debug_info = debug_info
+ self.profile = None
+ self.symbols_path = symbols_path
+ self.stackwalk_binary = stackwalk_binary
+ self.ca_certificate_path = ca_certificate_path
+ self.certutil_binary = certutil_binary
+ self.e10s = e10s
+ if self.symbols_path and stackfix_dir:
+ self.stack_fixer = get_stack_fixer_function(stackfix_dir,
+ self.symbols_path)
+ else:
+ self.stack_fixer = None
+
+ def start(self):
+ self.marionette_port = get_free_port(2828, exclude=self.used_ports)
+ self.used_ports.add(self.marionette_port)
+
+ env = os.environ.copy()
+ env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
+
+ locations = ServerLocations(filename=os.path.join(here, "server-locations.txt"))
+
+ preferences = self.load_prefs()
+
+ self.profile = FirefoxProfile(locations=locations,
+ preferences=preferences)
+ self.profile.set_preferences({"marionette.defaultPrefs.enabled": True,
+ "marionette.defaultPrefs.port": self.marionette_port,
+ "dom.disable_open_during_load": False,
+ "network.dns.localDomains": ",".join(hostnames),
+ "network.proxy.type": 0,
+ "places.history.enabled": False})
+ if self.e10s:
+ self.profile.set_preferences({"browser.tabs.remote.autostart": True})
+
+ # Bug 1262954: winxp + e10s, disable hwaccel
+ if (self.e10s and platform.system() in ("Windows", "Microsoft") and
+ '5.1' in platform.version()):
+ self.profile.set_preferences({"layers.acceleration.disabled": True})
+
+ if self.ca_certificate_path is not None:
+ self.setup_ssl()
+
+ debug_args, cmd = browser_command(self.binary, [cmd_arg("marionette"), "about:blank"],
+ self.debug_info)
+
+ self.runner = FirefoxRunner(profile=self.profile,
+ binary=cmd[0],
+ cmdargs=cmd[1:],
+ env=env,
+ process_class=ProcessHandler,
+ process_args={"processOutputLine": [self.on_output]})
+
+ self.logger.debug("Starting Firefox")
+
+ self.runner.start(debug_args=debug_args, interactive=self.debug_info and self.debug_info.interactive)
+ self.logger.debug("Firefox Started")
+
+ def load_prefs(self):
+ prefs_path = os.path.join(self.prefs_root, "prefs_general.js")
+ if os.path.exists(prefs_path):
+ preferences = Preferences.read_prefs(prefs_path)
+ else:
+ self.logger.warning("Failed to find base prefs file in %s" % prefs_path)
+ preferences = []
+
+ return preferences
+
+ def stop(self):
+ self.logger.debug("Stopping browser")
+ if self.runner is not None:
+ try:
+ self.runner.stop()
+ except OSError:
+ # This can happen on Windows if the process is already dead
+ pass
+
+ def pid(self):
+ if self.runner.process_handler is None:
+ return None
+
+ try:
+ return self.runner.process_handler.pid
+ except AttributeError:
+ return None
+
+ def on_output(self, line):
+ """Write a line of output from the firefox process to the log"""
+ data = line.decode("utf8", "replace")
+ if self.stack_fixer:
+ data = self.stack_fixer(data)
+ self.logger.process_output(self.pid(),
+ data,
+ command=" ".join(self.runner.command))
+
+ def is_alive(self):
+ if self.runner:
+ return self.runner.is_running()
+ return False
+
+ def cleanup(self):
+ self.stop()
+
+ def executor_browser(self):
+ assert self.marionette_port is not None
+ return ExecutorBrowser, {"marionette_port": self.marionette_port}
+
+ def log_crash(self, process, test):
+ dump_dir = os.path.join(self.profile.profile, "minidumps")
+
+ mozcrash.log_crashes(self.logger,
+ dump_dir,
+ symbols_path=self.symbols_path,
+ stackwalk_binary=self.stackwalk_binary,
+ process=process,
+ test=test)
+
+ def setup_ssl(self):
+ """Create a certificate database to use in the test profile. This is configured
+ to trust the CA Certificate that has signed the web-platform.test server
+ certificate."""
+
+ self.logger.info("Setting up ssl")
+
+ # Make sure the certutil libraries from the source tree are loaded when using a
+ # local copy of certutil
+ # TODO: Maybe only set this if certutil won't launch?
+ env = os.environ.copy()
+ certutil_dir = os.path.dirname(self.binary)
+ if mozinfo.isMac:
+ env_var = "DYLD_LIBRARY_PATH"
+ elif mozinfo.isUnix:
+ env_var = "LD_LIBRARY_PATH"
+ else:
+ env_var = "PATH"
+
+
+ env[env_var] = (os.path.pathsep.join([certutil_dir, env[env_var]])
+ if env_var in env else certutil_dir).encode(
+ sys.getfilesystemencoding() or 'utf-8', 'replace')
+
+ def certutil(*args):
+ cmd = [self.certutil_binary] + list(args)
+ self.logger.process_output("certutil",
+ subprocess.check_output(cmd,
+ env=env,
+ stderr=subprocess.STDOUT),
+ " ".join(cmd))
+
+ pw_path = os.path.join(self.profile.profile, ".crtdbpw")
+ with open(pw_path, "w") as f:
+ # Use empty password for certificate db
+ f.write("\n")
+
+ cert_db_path = self.profile.profile
+
+ # Create a new certificate db
+ certutil("-N", "-d", cert_db_path, "-f", pw_path)
+
+ # Add the CA certificate to the database and mark as trusted to issue server certs
+ certutil("-A", "-d", cert_db_path, "-f", pw_path, "-t", "CT,,",
+ "-n", "web-platform-tests", "-i", self.ca_certificate_path)
+
+ # List all certs in the database
+ certutil("-L", "-d", cert_db_path)
diff --git a/testing/web-platform/harness/wptrunner/browsers/server-locations.txt b/testing/web-platform/harness/wptrunner/browsers/server-locations.txt
new file mode 100644
index 000000000..286f12590
--- /dev/null
+++ b/testing/web-platform/harness/wptrunner/browsers/server-locations.txt
@@ -0,0 +1,38 @@
+#
+# 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/.
+
+# See /build/pgo/server-locations.txt for documentation on the format
+
+http://localhost:8000 primary
+
+http://web-platform.test:8000
+http://www.web-platform.test:8000
+http://www1.web-platform.test:8000
+http://www2.web-platform.test:8000
+http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8000
+http://xn--lve-6lad.web-platform.test:8000
+
+http://web-platform.test:8001
+http://www.web-platform.test:8001
+http://www1.web-platform.test:8001
+http://www2.web-platform.test:8001
+http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8001
+http://xn--lve-6lad.web-platform.test:8001
+
+https://web-platform.test:8443
+https://www.web-platform.test:8443
+https://www1.web-platform.test:8443
+https://www2.web-platform.test:8443
+https://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8443
+https://xn--lve-6lad.web-platform.test:8443
+
+# These are actually ws servers, but until mozprofile is
+# fixed we have to pretend that they are http servers
+http://web-platform.test:8888
+http://www.web-platform.test:8888
+http://www1.web-platform.test:8888
+http://www2.web-platform.test:8888
+http://xn--n8j6ds53lwwkrqhv28a.web-platform.test:8888
+http://xn--lve-6lad.web-platform.test:8888
diff --git a/testing/web-platform/harness/wptrunner/browsers/servo.py b/testing/web-platform/harness/wptrunner/browsers/servo.py
new file mode 100644
index 000000000..bc90cefcf
--- /dev/null
+++ b/testing/web-platform/harness/wptrunner/browsers/servo.py
@@ -0,0 +1,80 @@
+# 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 os
+
+from .base import NullBrowser, ExecutorBrowser, require_arg
+from ..executors import executor_kwargs as base_executor_kwargs
+from ..executors.executorservo import ServoTestharnessExecutor, ServoRefTestExecutor
+
+here = os.path.join(os.path.split(__file__)[0])
+
+__wptrunner__ = {"product": "servo",
+ "check_args": "check_args",
+ "browser": "ServoBrowser",
+ "executor": {"testharness": "ServoTestharnessExecutor",
+ "reftest": "ServoRefTestExecutor"},
+ "browser_kwargs": "browser_kwargs",
+ "executor_kwargs": "executor_kwargs",
+ "env_options": "env_options",
+ "run_info_extras": "run_info_extras",
+ "update_properties": "update_properties"}
+
+
+def check_args(**kwargs):
+ require_arg(kwargs, "binary")
+
+
+def browser_kwargs(**kwargs):
+ return {"binary": kwargs["binary"],
+ "debug_info": kwargs["debug_info"],
+ "binary_args": kwargs["binary_args"],
+ "user_stylesheets": kwargs.get("user_stylesheets"),
+ "render_backend": kwargs.get("servo_backend")}
+
+
+def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
+ **kwargs):
+ rv = base_executor_kwargs(test_type, server_config,
+ cache_manager, **kwargs)
+ rv["pause_after_test"] = kwargs["pause_after_test"]
+ return rv
+
+
+def env_options():
+ return {"host": "127.0.0.1",
+ "external_host": "web-platform.test",
+ "bind_hostname": "true",
+ "testharnessreport": "testharnessreport-servo.js",
+ "supports_debugger": True}
+
+
+def run_info_extras(**kwargs):
+ return {"backend": kwargs["servo_backend"]}
+
+
+def update_properties():
+ return ["debug", "os", "version", "processor", "bits", "backend"], None
+
+
+def render_arg(render_backend):
+ return {"cpu": "--cpu", "webrender": "-w"}[render_backend]
+
+
+class ServoBrowser(NullBrowser):
+ def __init__(self, logger, binary, debug_info=None, binary_args=None,
+ user_stylesheets=None, render_backend="cpu"):
+ NullBrowser.__init__(self, logger)
+ self.binary = binary
+ self.debug_info = debug_info
+ self.binary_args = binary_args or []
+ self.user_stylesheets = user_stylesheets or []
+ self.render_backend = render_backend
+
+ def executor_browser(self):
+ return ExecutorBrowser, {"binary": self.binary,
+ "debug_info": self.debug_info,
+ "binary_args": self.binary_args,
+ "user_stylesheets": self.user_stylesheets,
+ "render_backend": self.render_backend}
diff --git a/testing/web-platform/harness/wptrunner/browsers/servodriver.py b/testing/web-platform/harness/wptrunner/browsers/servodriver.py
new file mode 100644
index 000000000..2c05a4dd5
--- /dev/null
+++ b/testing/web-platform/harness/wptrunner/browsers/servodriver.py
@@ -0,0 +1,162 @@
+# 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 os
+import subprocess
+import tempfile
+
+from mozprocess import ProcessHandler
+
+from .base import Browser, require_arg, get_free_port, browser_command, ExecutorBrowser
+from .servo import render_arg
+from ..executors import executor_kwargs as base_executor_kwargs
+from ..executors.executorservodriver import (ServoWebDriverTestharnessExecutor,
+ ServoWebDriverRefTestExecutor)
+
+here = os.path.join(os.path.split(__file__)[0])
+
+__wptrunner__ = {"product": "servodriver",
+ "check_args": "check_args",
+ "browser": "ServoWebDriverBrowser",
+ "executor": {"testharness": "ServoWebDriverTestharnessExecutor",
+ "reftest": "ServoWebDriverRefTestExecutor"},
+ "browser_kwargs": "browser_kwargs",
+ "executor_kwargs": "executor_kwargs",
+ "env_options": "env_options",
+ "run_info_extras": "run_info_extras",
+ "update_properties": "update_properties"}
+
+hosts_text = """127.0.0.1 web-platform.test
+127.0.0.1 www.web-platform.test
+127.0.0.1 www1.web-platform.test
+127.0.0.1 www2.web-platform.test
+127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
+127.0.0.1 xn--lve-6lad.web-platform.test
+"""
+
+
+def check_args(**kwargs):
+ require_arg(kwargs, "binary")
+
+
+def browser_kwargs(**kwargs):
+ return {"binary": kwargs["binary"],
+ "debug_info": kwargs["debug_info"],
+ "user_stylesheets": kwargs.get("user_stylesheets"),
+ "render_backend": kwargs.get("servo_backend")}
+
+
+def executor_kwargs(test_type, server_config, cache_manager, run_info_data, **kwargs):
+ rv = base_executor_kwargs(test_type, server_config,
+ cache_manager, **kwargs)
+ return rv
+
+
+def env_options():
+ return {"host": "127.0.0.1",
+ "external_host": "web-platform.test",
+ "bind_hostname": "true",
+ "testharnessreport": "testharnessreport-servodriver.js",
+ "supports_debugger": True}
+
+
+def run_info_extras(**kwargs):
+ return {"backend": kwargs["servo_backend"]}
+
+
+def update_properties():
+ return ["debug", "os", "version", "processor", "bits", "backend"], None
+
+
+def make_hosts_file():
+ hosts_fd, hosts_path = tempfile.mkstemp()
+ with os.fdopen(hosts_fd, "w") as f:
+ f.write(hosts_text)
+ return hosts_path
+
+
+class ServoWebDriverBrowser(Browser):
+ used_ports = set()
+
+ def __init__(self, logger, binary, debug_info=None, webdriver_host="127.0.0.1",
+ user_stylesheets=None, render_backend="cpu"):
+ Browser.__init__(self, logger)
+ self.binary = binary
+ self.webdriver_host = webdriver_host
+ self.webdriver_port = None
+ self.proc = None
+ self.debug_info = debug_info
+ self.hosts_path = make_hosts_file()
+ self.command = None
+ self.user_stylesheets = user_stylesheets if user_stylesheets else []
+ self.render_backend = render_backend
+
+ def start(self):
+ self.webdriver_port = get_free_port(4444, exclude=self.used_ports)
+ self.used_ports.add(self.webdriver_port)
+
+ env = os.environ.copy()
+ env["HOST_FILE"] = self.hosts_path
+ env["RUST_BACKTRACE"] = "1"
+
+ debug_args, command = browser_command(self.binary,
+ [render_arg(self.render_backend), "--hard-fail",
+ "--webdriver", str(self.webdriver_port),
+ "about:blank"],
+ self.debug_info)
+
+ for stylesheet in self.user_stylesheets:
+ command += ["--user-stylesheet", stylesheet]
+
+ self.command = command
+
+ self.command = debug_args + self.command
+
+ if not self.debug_info or not self.debug_info.interactive:
+ self.proc = ProcessHandler(self.command,
+ processOutputLine=[self.on_output],
+ env=env,
+ storeOutput=False)
+ self.proc.run()
+ else:
+ self.proc = subprocess.Popen(self.command, env=env)
+
+ self.logger.debug("Servo Started")
+
+ def stop(self):
+ self.logger.debug("Stopping browser")
+ if self.proc is not None:
+ try:
+ self.proc.kill()
+ except OSError:
+ # This can happen on Windows if the process is already dead
+ pass
+
+ def pid(self):
+ if self.proc is None:
+ return None
+
+ try:
+ return self.proc.pid
+ except AttributeError:
+ return None
+
+ def on_output(self, line):
+ """Write a line of output from the process to the log"""
+ self.logger.process_output(self.pid(),
+ line.decode("utf8", "replace"),
+ command=" ".join(self.command))
+
+ def is_alive(self):
+ if self.runner:
+ return self.runner.is_running()
+ return False
+
+ def cleanup(self):
+ self.stop()
+
+ def executor_browser(self):
+ assert self.webdriver_port is not None
+ return ExecutorBrowser, {"webdriver_host": self.webdriver_host,
+ "webdriver_port": self.webdriver_port}