From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- .../harness/wptrunner/browsers/__init__.py | 33 +++ .../web-platform/harness/wptrunner/browsers/b2g.py | 243 ++++++++++++++++++ .../wptrunner/browsers/b2g_setup/certtest_app.zip | Bin 0 -> 1237 bytes .../harness/wptrunner/browsers/base.py | 160 ++++++++++++ .../harness/wptrunner/browsers/chrome.py | 81 ++++++ .../harness/wptrunner/browsers/firefox.py | 274 +++++++++++++++++++++ .../wptrunner/browsers/server-locations.txt | 38 +++ .../harness/wptrunner/browsers/servo.py | 80 ++++++ .../harness/wptrunner/browsers/servodriver.py | 162 ++++++++++++ 9 files changed, 1071 insertions(+) create mode 100644 testing/web-platform/harness/wptrunner/browsers/__init__.py create mode 100644 testing/web-platform/harness/wptrunner/browsers/b2g.py create mode 100644 testing/web-platform/harness/wptrunner/browsers/b2g_setup/certtest_app.zip create mode 100644 testing/web-platform/harness/wptrunner/browsers/base.py create mode 100644 testing/web-platform/harness/wptrunner/browsers/chrome.py create mode 100644 testing/web-platform/harness/wptrunner/browsers/firefox.py create mode 100644 testing/web-platform/harness/wptrunner/browsers/server-locations.txt create mode 100644 testing/web-platform/harness/wptrunner/browsers/servo.py create mode 100644 testing/web-platform/harness/wptrunner/browsers/servodriver.py (limited to 'testing/web-platform/harness/wptrunner/browsers') 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 Binary files /dev/null and b/testing/web-platform/harness/wptrunner/browsers/b2g_setup/certtest_app.zip 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} -- cgit v1.2.3