diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/base.py.orig b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/base.py.orig deleted file mode 100644 index e8228b404dfb..000000000000 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/base.py.orig +++ /dev/null @@ -1,206 +0,0 @@ -import os -import platform -import socket -from abc import ABCMeta, abstractmethod -from copy import deepcopy - -from ..wptcommandline import require_arg # noqa: F401 - -here = os.path.split(__file__)[0] - - -def inherit(super_module, child_globals, product_name): - super_wptrunner = super_module.__wptrunner__ - child_globals["__wptrunner__"] = child_wptrunner = deepcopy(super_wptrunner) - - child_wptrunner["product"] = product_name - - for k in ("check_args", "browser", "browser_kwargs", "executor_kwargs", - "env_extras", "env_options"): - attr = super_wptrunner[k] - child_globals[attr] = getattr(super_module, attr) - - for v in super_module.__wptrunner__["executor"].values(): - child_globals[v] = getattr(super_module, v) - - if "run_info_extras" in super_wptrunner: - attr = super_wptrunner["run_info_extras"] - child_globals[attr] = getattr(super_module, attr) - - -def cmd_arg(name, value=None): - prefix = "-" if platform.system() == "Windows" else "--" - rv = prefix + name - - -def inherit(super_module, child_globals, product_name): - super_wptrunner = super_module.__wptrunner__ - child_globals["__wptrunner__"] = child_wptrunner = deepcopy(super_wptrunner) - - child_wptrunner["product"] = product_name - - for k in ("check_args", "browser", "browser_kwargs", "executor_kwargs", - "env_extras", "env_options"): - attr = super_wptrunner[k] - child_globals[attr] = getattr(super_module, attr) - - for v in super_module.__wptrunner__["executor"].values(): - child_globals[v] = getattr(super_module, v) - - if "run_info_extras" in super_wptrunner: - attr = super_wptrunner["run_info_extras"] - child_globals[attr] = getattr(super_module, attr) - - -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 - - def settings(self, test): - return {} - - @abstractmethod - def start(self, group_metadata, **kwargs): - """Launch the browser object and get it into a state where is is ready to run tests""" - pass - - @abstractmethod - def stop(self, force=False): - """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 check_crash(self, process, test): - """Check if a crash occured and output any useful information to the - log. Returns a boolean indicating whether a crash occured.""" - return False - - -class NullBrowser(Browser): - def __init__(self, logger, **kwargs): - super(NullBrowser, self).__init__(logger) - - def start(self, **kwargs): - """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, force=False): - 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/tests/tools/wptrunner/wptrunner/browsers/fennec.py.orig b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/fennec.py.orig deleted file mode 100644 index e6b07300dc7b..000000000000 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/fennec.py.orig +++ /dev/null @@ -1,217 +0,0 @@ -import os -import tempfile - -import moznetwork -from mozprocess import ProcessHandler -from mozprofile import FirefoxProfile -from mozrunner import FennecEmulatorRunner - -from tools.serve.serve import make_hosts_file - -from .base import (get_free_port, - cmd_arg, - browser_command) -from ..executors.executormarionette import (MarionetteTestharnessExecutor, # noqa: F401 - MarionetteRefTestExecutor) # noqa: F401 -from .firefox import (get_timeout_multiplier, - update_properties, - executor_kwargs, - FirefoxBrowser) - - -__wptrunner__ = {"product": "fennec", - "check_args": "check_args", - "browser": "FennecBrowser", - "executor": {"testharness": "MarionetteTestharnessExecutor", - "reftest": "MarionetteRefTestExecutor"}, - "browser_kwargs": "browser_kwargs", - "executor_kwargs": "executor_kwargs", - "env_extras": "env_extras", - "env_options": "env_options", - "run_info_extras": "run_info_extras", - "update_properties": "update_properties"} - - -def check_args(**kwargs): - pass - - -def browser_kwargs(test_type, run_info_data, config, **kwargs): - return {"package_name": kwargs["package_name"], - "device_serial": kwargs["device_serial"], - "prefs_root": kwargs["prefs_root"], - "extra_prefs": kwargs["extra_prefs"], - "test_type": test_type, - "debug_info": kwargs["debug_info"], - "symbols_path": kwargs["symbols_path"], - "stackwalk_binary": kwargs["stackwalk_binary"], - "certutil_binary": kwargs["certutil_binary"], - "ca_certificate_path": config.ssl_config["ca_cert_path"], - "stackfix_dir": kwargs["stackfix_dir"], - "binary_args": kwargs["binary_args"], - "timeout_multiplier": get_timeout_multiplier(test_type, - run_info_data, - **kwargs), - "leak_check": kwargs["leak_check"], - "stylo_threads": kwargs["stylo_threads"], - "chaos_mode_flags": kwargs["chaos_mode_flags"], - "config": config, - "install_fonts": kwargs["install_fonts"], - "tests_root": config.doc_root} - - -def env_extras(**kwargs): - return [] - - -def run_info_extras(**kwargs): - return {"e10s": False, - "headless": False, - "sw-e10s": False} - - -def env_options(): - # The server host is set to public localhost IP so that resources can be accessed - # from Android emulator - return {"server_host": moznetwork.get_ip(), - "bind_address": False, - "supports_debugger": True} - - -def write_hosts_file(config, device): - new_hosts = make_hosts_file(config, moznetwork.get_ip()) - current_hosts = device.get_file("/etc/hosts") - if new_hosts == current_hosts: - return - hosts_fd, hosts_path = tempfile.mkstemp() - try: - with os.fdopen(hosts_fd, "w") as f: - f.write(new_hosts) - device.remount() - device.push(hosts_path, "/etc/hosts") - finally: - os.remove(hosts_path) - - -class FennecBrowser(FirefoxBrowser): - used_ports = set() - init_timeout = 300 - shutdown_timeout = 60 - - def __init__(self, logger, prefs_root, test_type, package_name=None, - device_serial="emulator-5444", **kwargs): - FirefoxBrowser.__init__(self, logger, None, prefs_root, test_type, **kwargs) - self._package_name = package_name - self.device_serial = device_serial - self.tests_root = kwargs["tests_root"] - self.install_fonts = kwargs["install_fonts"] - - @property - def package_name(self): - """ - Name of app to run on emulator. - """ - if self._package_name is None: - self._package_name = "org.mozilla.fennec" - user = os.getenv("USER") - if user: - self._package_name += "_" + user - return self._package_name - - def start(self, **kwargs): - if self.marionette_port is None: - self.marionette_port = get_free_port(2828, exclude=self.used_ports) - self.used_ports.add(self.marionette_port) - - env = {} - env["MOZ_CRASHREPORTER"] = "1" - env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1" - env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1" - env["STYLO_THREADS"] = str(self.stylo_threads) - if self.chaos_mode_flags is not None: - env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags) - - preferences = self.load_prefs() - - self.profile = FirefoxProfile(preferences=preferences) - self.profile.set_preferences({"marionette.port": self.marionette_port, - "dom.disable_open_during_load": False, - "places.history.enabled": False, - "dom.send_after_paint_to_content": True, - "network.preload": True}) - if self.test_type == "reftest": - self.logger.info("Setting android reftest preferences") - self.profile.set_preferences({"browser.viewport.desktopWidth": 600, - # Disable high DPI - "layout.css.devPixelsPerPx": "1.0", - # Ensure that the full browser element - # appears in the screenshot - "apz.allow_zooming": False, - "android.widget_paints_background": False, - # Ensure that scrollbars are always painted - "ui.scrollbarFadeBeginDelay": 100000}) - - if self.install_fonts: - self.logger.debug("Copying Ahem font to profile") - font_dir = os.path.join(self.profile.profile, "fonts") - if not os.path.exists(font_dir): - os.makedirs(font_dir) - with open(os.path.join(self.tests_root, "fonts", "Ahem.ttf"), "rb") as src: - with open(os.path.join(font_dir, "Ahem.ttf"), "wb") as dest: - dest.write(src.read()) - - if self.leak_check and kwargs.get("check_leaks", True): - self.leak_report_file = os.path.join(self.profile.profile, "runtests_leaks.log") - if os.path.exists(self.leak_report_file): - os.remove(self.leak_report_file) - env["XPCOM_MEM_BLOAT_LOG"] = self.leak_report_file - else: - self.leak_report_file = None - - if self.ca_certificate_path is not None: - self.setup_ssl() - - debug_args, cmd = browser_command(self.package_name, - self.binary_args if self.binary_args else [] + - [cmd_arg("marionette"), "about:blank"], - self.debug_info) - - self.runner = FennecEmulatorRunner(app=self.package_name, - profile=self.profile, - cmdargs=cmd[1:], - env=env, - symbols_path=self.symbols_path, - serial=self.device_serial, - # TODO - choose appropriate log dir - logdir=os.getcwd(), - process_class=ProcessHandler, - process_args={"processOutputLine": [self.on_output]}) - - self.logger.debug("Starting %s" % self.package_name) - # connect to a running emulator - self.runner.device.connect() - - write_hosts_file(self.config, self.runner.device.device) - - self.runner.stop() - self.runner.start(debug_args=debug_args, interactive=self.debug_info and self.debug_info.interactive) - - self.runner.device.device.forward( - local="tcp:{}".format(self.marionette_port), - remote="tcp:{}".format(self.marionette_port)) - - self.logger.debug("%s Started" % self.package_name) - - def stop(self, force=False): - if self.runner is not None: - if (self.runner.device.connected and - len(self.runner.device.device.list_forwards()) > 0): - try: - self.runner.device.device.remove_forwards( - "tcp:{}".format(self.marionette_port)) - except Exception: - self.logger.warning("Failed to remove port forwarding") - # We assume that stopping the runner prompts the - # browser to shut down. This allows the leak log to be written - self.runner.stop() - self.logger.debug("stopped") diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/sauce.py.orig b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/sauce.py.orig deleted file mode 100644 index e9f4c3a92b26..000000000000 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/sauce.py.orig +++ /dev/null @@ -1,252 +0,0 @@ -# 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 glob -import os -import shutil -import subprocess -import tarfile -import tempfile -import time -from cStringIO import StringIO as CStringIO - -import requests - -from .base import Browser, ExecutorBrowser, require_arg -from ..executors import executor_kwargs as base_executor_kwargs -from ..executors.executorselenium import (SeleniumTestharnessExecutor, - SeleniumRefTestExecutor) - -here = os.path.split(__file__)[0] -# Number of seconds to wait between polling operations when detecting status of -# Sauce Connect sub-process. -sc_poll_period = 1 - - -__wptrunner__ = {"product": "sauce", - "check_args": "check_args", - "browser": "SauceBrowser", - "executor": {"testharness": "SeleniumTestharnessExecutor", - "reftest": "SeleniumRefTestExecutor"}, - "browser_kwargs": "browser_kwargs", - "executor_kwargs": "executor_kwargs", - "env_extras": "env_extras", - "env_options": "env_options"} - - -def get_capabilities(**kwargs): - browser_name = kwargs["sauce_browser"] - platform = kwargs["sauce_platform"] - version = kwargs["sauce_version"] - build = kwargs["sauce_build"] - tags = kwargs["sauce_tags"] - tunnel_id = kwargs["sauce_tunnel_id"] - prerun_script = { - "MicrosoftEdge": { - "executable": "sauce-storage:edge-prerun.bat", - "background": False, - }, - "safari": { - "executable": "sauce-storage:safari-prerun.sh", - "background": False, - } - } - capabilities = { - "browserName": browser_name, - "build": build, - "disablePopupHandler": True, - "name": "%s %s on %s" % (browser_name, version, platform), - "platform": platform, - "public": "public", - "selenium-version": "3.3.1", - "tags": tags, - "tunnel-identifier": tunnel_id, - "version": version, - "prerun": prerun_script.get(browser_name) - } - - if browser_name == 'MicrosoftEdge': - capabilities['selenium-version'] = '2.4.8' - - return capabilities - - -def get_sauce_config(**kwargs): - browser_name = kwargs["sauce_browser"] - sauce_user = kwargs["sauce_user"] - sauce_key = kwargs["sauce_key"] - - hub_url = "%s:%s@localhost:4445" % (sauce_user, sauce_key) - data = { - "url": "http://%s/wd/hub" % hub_url, - "browserName": browser_name, - "capabilities": get_capabilities(**kwargs) - } - - return data - - -def check_args(**kwargs): - require_arg(kwargs, "sauce_browser") - require_arg(kwargs, "sauce_platform") - require_arg(kwargs, "sauce_version") - require_arg(kwargs, "sauce_user") - require_arg(kwargs, "sauce_key") - - -def browser_kwargs(test_type, run_info_data, config, **kwargs): - sauce_config = get_sauce_config(**kwargs) - - return {"sauce_config": sauce_config} - - -def executor_kwargs(test_type, server_config, cache_manager, run_info_data, - **kwargs): - executor_kwargs = base_executor_kwargs(test_type, server_config, - cache_manager, run_info_data, **kwargs) - - executor_kwargs["capabilities"] = get_capabilities(**kwargs) - - return executor_kwargs - - -def env_extras(**kwargs): - return [SauceConnect(**kwargs)] - - -def env_options(): - return {"supports_debugger": False} - - -def get_tar(url, dest): - resp = requests.get(url, stream=True) - resp.raise_for_status() - with tarfile.open(fileobj=CStringIO(resp.raw.read())) as f: - f.extractall(path=dest) - - -class SauceConnect(): - - def __init__(self, **kwargs): - self.sauce_user = kwargs["sauce_user"] - self.sauce_key = kwargs["sauce_key"] - self.sauce_tunnel_id = kwargs["sauce_tunnel_id"] - self.sauce_connect_binary = kwargs.get("sauce_connect_binary") - self.sauce_connect_args = kwargs.get("sauce_connect_args") - self.sauce_init_timeout = kwargs.get("sauce_init_timeout") - self.sc_process = None - self.temp_dir = None - self.env_config = None - - def __call__(self, env_options, env_config): - self.env_config = env_config - - return self - - def __enter__(self): - # Because this class implements the context manager protocol, it is - # possible for instances to be provided to the `with` statement - # directly. This class implements the callable protocol so that data - # which is not available during object initialization can be provided - # prior to this moment. Instances must be invoked in preparation for - # the context manager protocol, but this additional constraint is not - # itself part of the protocol. - assert self.env_config is not None, 'The instance has been invoked.' - - if not self.sauce_connect_binary: - self.temp_dir = tempfile.mkdtemp() - get_tar("https://saucelabs.com/downloads/sc-4.4.9-linux.tar.gz", self.temp_dir) - self.sauce_connect_binary = glob.glob(os.path.join(self.temp_dir, "sc-*-linux/bin/sc"))[0] - - self.upload_prerun_exec('edge-prerun.bat') - self.upload_prerun_exec('safari-prerun.sh') - - self.sc_process = subprocess.Popen([ - self.sauce_connect_binary, - "--user=%s" % self.sauce_user, - "--api-key=%s" % self.sauce_key, - "--no-remove-colliding-tunnels", - "--tunnel-identifier=%s" % self.sauce_tunnel_id, - "--metrics-address=0.0.0.0:9876", - "--readyfile=./sauce_is_ready", - "--tunnel-domains", - ",".join(self.env_config.domains_set) - ] + self.sauce_connect_args) - - tot_wait = 0 - while not os.path.exists('./sauce_is_ready') and self.sc_process.poll() is None: - if tot_wait >= self.sauce_init_timeout: - self.quit() - - raise SauceException("Sauce Connect Proxy was not ready after %d seconds" % tot_wait) - - time.sleep(sc_poll_period) - tot_wait += sc_poll_period - - if self.sc_process.returncode is not None: - raise SauceException("Unable to start Sauce Connect Proxy. Process exited with code %s", self.sc_process.returncode) - - def __exit__(self, exc_type, exc_val, exc_tb): - self.env_config = None - self.quit() - if self.temp_dir and os.path.exists(self.temp_dir): - try: - shutil.rmtree(self.temp_dir) - except OSError: - pass - - def upload_prerun_exec(self, file_name): - auth = (self.sauce_user, self.sauce_key) - url = "https://saucelabs.com/rest/v1/storage/%s/%s?overwrite=true" % (self.sauce_user, file_name) - - with open(os.path.join(here, 'sauce_setup', file_name), 'rb') as f: - requests.post(url, data=f, auth=auth) - - def quit(self): - """The Sauce Connect process may be managing an active "tunnel" to the - Sauce Labs service. Issue a request to the process to close any tunnels - and exit. If this does not occur within 5 seconds, force the process to - close.""" - kill_wait = 5 - tot_wait = 0 - self.sc_process.terminate() - - while self.sc_process.poll() is None: - time.sleep(sc_poll_period) - tot_wait += sc_poll_period - - if tot_wait >= kill_wait: - self.sc_process.kill() - break - - -class SauceException(Exception): - pass - - -class SauceBrowser(Browser): - init_timeout = 300 - - def __init__(self, logger, sauce_config): - Browser.__init__(self, logger) - self.sauce_config = sauce_config - - def start(self, **kwargs): - pass - - def stop(self, force=False): - pass - - def pid(self): - return None - - def is_alive(self): - # TODO: Should this check something about the connection? - return True - - def cleanup(self): - pass - - def executor_browser(self): - return ExecutorBrowser, {"webdriver_url": self.sauce_config["url"]} diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py.orig b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py.orig deleted file mode 100644 index f7645fbea9df..000000000000 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py.orig +++ /dev/null @@ -1,882 +0,0 @@ -import json -import os -import threading -import traceback -import urlparse -import uuid - -errors = None -marionette = None -pytestrunner = None - -here = os.path.join(os.path.split(__file__)[0]) - -from .base import (CallbackHandler, - RefTestExecutor, - RefTestImplementation, - TestharnessExecutor, - WdspecExecutor, - WebDriverProtocol, - extra_timeout, - strip_server) -from .protocol import (ActionSequenceProtocolPart, - AssertsProtocolPart, - BaseProtocolPart, - TestharnessProtocolPart, - PrefsProtocolPart, - Protocol, - StorageProtocolPart, - SelectorProtocolPart, - ClickProtocolPart, - SendKeysProtocolPart, - TestDriverProtocolPart, - CoverageProtocolPart) -from ..testrunner import Stop -from ..webdriver_server import GeckoDriverServer - - -def do_delayed_imports(): - global errors, marionette - - # Marionette client used to be called marionette, recently it changed - # to marionette_driver for unfathomable reasons - try: - import marionette - from marionette import errors - except ImportError: - from marionette_driver import marionette, errors - - -class MarionetteBaseProtocolPart(BaseProtocolPart): - def __init__(self, parent): - super(MarionetteBaseProtocolPart, self).__init__(parent) - self.timeout = None - - def setup(self): - self.marionette = self.parent.marionette - - def execute_script(self, script, async=False): - method = self.marionette.execute_async_script if async else self.marionette.execute_script - return method(script, new_sandbox=False, sandbox=None) - - def set_timeout(self, timeout): - """Set the Marionette script timeout. - - :param timeout: Script timeout in seconds - - """ - if timeout != self.timeout: - self.marionette.timeout.script = timeout - self.timeout = timeout - - @property - def current_window(self): - return self.marionette.current_window_handle - - def set_window(self, handle): - self.marionette.switch_to_window(handle) - - def wait(self): - try: - socket_timeout = self.marionette.client.socket_timeout - except AttributeError: - # This can happen if there was a crash - return - if socket_timeout: - try: - self.marionette.timeout.script = socket_timeout / 2 - except IOError: - self.logger.debug("Socket closed") - return - - while True: - try: - self.marionette.execute_async_script("") - except errors.NoSuchWindowException: - # The window closed - break - except errors.ScriptTimeoutException: - self.logger.debug("Script timed out") - pass - except errors.JavascriptException as e: - # This can happen if we navigate, but just keep going - self.logger.debug(e.message) - pass - except IOError: - self.logger.debug("Socket closed") - break - except Exception as e: - self.logger.warning(traceback.format_exc(e)) - break - - -class MarionetteTestharnessProtocolPart(TestharnessProtocolPart): - def __init__(self, parent): - super(MarionetteTestharnessProtocolPart, self).__init__(parent) - self.runner_handle = None - with open(os.path.join(here, "runner.js")) as f: - self.runner_script = f.read() - - def setup(self): - self.marionette = self.parent.marionette - - def load_runner(self, url_protocol): - # Check if we previously had a test window open, and if we did make sure it's closed - if self.runner_handle: - self._close_windows() - url = urlparse.urljoin(self.parent.executor.server_url(url_protocol), - "/testharness_runner.html") - self.logger.debug("Loading %s" % url) - try: - self.dismiss_alert(lambda: self.marionette.navigate(url)) - except Exception as e: - self.logger.critical( - "Loading initial page %s failed. Ensure that the " - "there are no other programs bound to this port and " - "that your firewall rules or network setup does not " - "prevent access.\e%s" % (url, traceback.format_exc(e))) - raise - self.runner_handle = self.marionette.current_window_handle - format_map = {"title": threading.current_thread().name.replace("'", '"')} - self.parent.base.execute_script(self.runner_script % format_map) - - def _close_windows(self): - handles = self.marionette.window_handles - runner_handle = None - try: - handles.remove(self.runner_handle) - runner_handle = self.runner_handle - except ValueError: - # The runner window probably changed id but we can restore it - # This isn't supposed to happen, but marionette ids are not yet stable - # We assume that the first handle returned corresponds to the runner, - # but it hopefully doesn't matter too much if that assumption is - # wrong since we reload the runner in that tab anyway. - runner_handle = handles.pop(0) - self.logger.info("Changing harness_window to %s" % runner_handle) - - for handle in handles: - try: - self.dismiss_alert(lambda: self.marionette.switch_to_window(handle)) - self.marionette.switch_to_window(handle) - self.logger.info("Closing window %s" % handle) - self.marionette.close() - except errors.NoSuchWindowException: - # We might have raced with the previous test to close this - # window, skip it. - pass - self.marionette.switch_to_window(runner_handle) - return runner_handle - - def close_old_windows(self, url_protocol): - runner_handle = self._close_windows() - if runner_handle != self.runner_handle: - self.load_runner(url_protocol) - return self.runner_handle - - def dismiss_alert(self, f): - while True: - try: - f() - except errors.UnexpectedAlertOpen: - alert = self.marionette.switch_to_alert() - try: - alert.dismiss() - except errors.NoAlertPresentException: - pass - else: - break - - def get_test_window(self, window_id, parent): - test_window = None - if window_id: - try: - # Try this, it's in Level 1 but nothing supports it yet - win_s = self.parent.base.execute_script("return window['%s'];" % self.window_id) - win_obj = json.loads(win_s) - test_window = win_obj["window-fcc6-11e5-b4f8-330a88ab9d7f"] - except Exception: - pass - - if test_window is None: - after = self.marionette.window_handles - if len(after) == 2: - test_window = next(iter(set(after) - set([parent]))) - elif after[0] == parent and len(after) > 2: - # Hope the first one here is the test window - test_window = after[1] - else: - raise Exception("unable to find test window") - - assert test_window != parent - return test_window - - -class MarionettePrefsProtocolPart(PrefsProtocolPart): - def setup(self): - self.marionette = self.parent.marionette - - def set(self, name, value): - if value.lower() not in ("true", "false"): - try: - int(value) - except ValueError: - value = "'%s'" % value - else: - value = value.lower() - - self.logger.info("Setting pref %s (%s)" % (name, value)) - - script = """ - let prefInterface = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - let pref = '%s'; - let type = prefInterface.getPrefType(pref); - let value = %s; - switch(type) { - case prefInterface.PREF_STRING: - prefInterface.setCharPref(pref, value); - break; - case prefInterface.PREF_BOOL: - prefInterface.setBoolPref(pref, value); - break; - case prefInterface.PREF_INT: - prefInterface.setIntPref(pref, value); - break; - } - """ % (name, value) - with self.marionette.using_context(self.marionette.CONTEXT_CHROME): - self.marionette.execute_script(script) - - def clear(self, name): - self.logger.info("Clearing pref %s" % (name)) - script = """ - let prefInterface = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - let pref = '%s'; - prefInterface.clearUserPref(pref); - """ % name - with self.marionette.using_context(self.marionette.CONTEXT_CHROME): - self.marionette.execute_script(script) - - def get(self, name): - script = """ - let prefInterface = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - let pref = '%s'; - let type = prefInterface.getPrefType(pref); - switch(type) { - case prefInterface.PREF_STRING: - return prefInterface.getCharPref(pref); - case prefInterface.PREF_BOOL: - return prefInterface.getBoolPref(pref); - case prefInterface.PREF_INT: - return prefInterface.getIntPref(pref); - case prefInterface.PREF_INVALID: - return null; - } - """ % name - with self.marionette.using_context(self.marionette.CONTEXT_CHROME): - self.marionette.execute_script(script) - - -class MarionetteStorageProtocolPart(StorageProtocolPart): - def setup(self): - self.marionette = self.parent.marionette - - def clear_origin(self, url): - self.logger.info("Clearing origin %s" % (url)) - script = """ - let url = '%s'; - let uri = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService) - .newURI(url); - let ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"] - .getService(Ci.nsIScriptSecurityManager); - let principal = ssm.createCodebasePrincipal(uri, {}); - let qms = Components.classes["@mozilla.org/dom/quota-manager-service;1"] - .getService(Components.interfaces.nsIQuotaManagerService); - qms.clearStoragesForPrincipal(principal, "default", null, true); - """ % url - with self.marionette.using_context(self.marionette.CONTEXT_CHROME): - self.marionette.execute_script(script) - - -class MarionetteAssertsProtocolPart(AssertsProtocolPart): - def setup(self): - self.assert_count = {"chrome": 0, "content": 0} - self.chrome_assert_count = 0 - self.marionette = self.parent.marionette - - def get(self): - script = """ - debug = Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2); - if (debug.isDebugBuild) { - return debug.assertionCount; - } - return 0; - """ - - def get_count(context, **kwargs): - try: - context_count = self.marionette.execute_script(script, **kwargs) - if context_count: - self.parent.logger.info("Got %s assert count %s" % (context, context_count)) - test_count = context_count - self.assert_count[context] - self.assert_count[context] = context_count - return test_count - except errors.NoSuchWindowException: - # If the window was already closed - self.parent.logger.warning("Failed to get assertion count; window was closed") - except (errors.MarionetteException, IOError): - # This usually happens if the process crashed - pass - - counts = [] - with self.marionette.using_context(self.marionette.CONTEXT_CHROME): - counts.append(get_count("chrome")) - if self.parent.e10s: - counts.append(get_count("content", sandbox="system")) - - counts = [item for item in counts if item is not None] - - if not counts: - return None - - return sum(counts) - - -class MarionetteSelectorProtocolPart(SelectorProtocolPart): - def setup(self): - self.marionette = self.parent.marionette - - def elements_by_selector(self, selector): - return self.marionette.find_elements("css selector", selector) - - -class MarionetteClickProtocolPart(ClickProtocolPart): - def setup(self): - self.marionette = self.parent.marionette - - def element(self, element): - return element.click() - - -class MarionetteSendKeysProtocolPart(SendKeysProtocolPart): - def setup(self): - self.marionette = self.parent.marionette - - def send_keys(self, element, keys): - return element.send_keys(keys) - - -class MarionetteActionSequenceProtocolPart(ActionSequenceProtocolPart): - def setup(self): - self.marionette = self.parent.marionette - - def send_actions(self, actions): - actions = self.marionette._to_json(actions) - self.logger.info(actions) - self.marionette._send_message("WebDriver:PerformActions", actions) - - -class MarionetteTestDriverProtocolPart(TestDriverProtocolPart): - def setup(self): - self.marionette = self.parent.marionette - - def send_message(self, message_type, status, message=None): - obj = { - "type": "testdriver-%s" % str(message_type), - "status": str(status) - } - if message: - obj["message"] = str(message) - self.parent.base.execute_script("window.postMessage(%s, '*')" % json.dumps(obj)) - - -class MarionetteCoverageProtocolPart(CoverageProtocolPart): - def setup(self): - self.marionette = self.parent.marionette - - if not self.parent.ccov: - self.is_enabled = False - return - - script = """ - ChromeUtils.import("chrome://marionette/content/PerTestCoverageUtils.jsm"); - return PerTestCoverageUtils.enabled; - """ - with self.marionette.using_context(self.marionette.CONTEXT_CHROME): - self.is_enabled = self.marionette.execute_script(script) - - def reset(self): - script = """ - var callback = arguments[arguments.length - 1]; - - ChromeUtils.import("chrome://marionette/content/PerTestCoverageUtils.jsm"); - PerTestCoverageUtils.beforeTest().then(callback, callback); - """ - with self.marionette.using_context(self.marionette.CONTEXT_CHROME): - try: - error = self.marionette.execute_async_script(script) - if error is not None: - raise Exception('Failure while resetting counters: %s' % json.dumps(error)) - except (errors.MarionetteException, IOError): - # This usually happens if the process crashed - pass - - def dump(self): - if len(self.marionette.window_handles): - handle = self.marionette.window_handles[0] - self.marionette.switch_to_window(handle) - - script = """ - var callback = arguments[arguments.length - 1]; - - ChromeUtils.import("chrome://marionette/content/PerTestCoverageUtils.jsm"); - PerTestCoverageUtils.afterTest().then(callback, callback); - """ - with self.marionette.using_context(self.marionette.CONTEXT_CHROME): - try: - error = self.marionette.execute_async_script(script) - if error is not None: - raise Exception('Failure while dumping counters: %s' % json.dumps(error)) - except (errors.MarionetteException, IOError): - # This usually happens if the process crashed - pass - - -class MarionetteProtocol(Protocol): - implements = [MarionetteBaseProtocolPart, - MarionetteTestharnessProtocolPart, - MarionettePrefsProtocolPart, - MarionetteStorageProtocolPart, - MarionetteSelectorProtocolPart, - MarionetteClickProtocolPart, - MarionetteSendKeysProtocolPart, - MarionetteActionSequenceProtocolPart, - MarionetteTestDriverProtocolPart, - MarionetteAssertsProtocolPart, - MarionetteCoverageProtocolPart] - - def __init__(self, executor, browser, capabilities=None, timeout_multiplier=1, e10s=True, ccov=False): - do_delayed_imports() - - super(MarionetteProtocol, self).__init__(executor, browser) - self.marionette = None - self.marionette_port = browser.marionette_port - self.capabilities = capabilities - self.timeout_multiplier = timeout_multiplier - self.runner_handle = None - self.e10s = e10s - self.ccov = ccov - - def connect(self): - self.logger.debug("Connecting to Marionette on port %i" % self.marionette_port) - startup_timeout = marionette.Marionette.DEFAULT_STARTUP_TIMEOUT * self.timeout_multiplier - self.marionette = marionette.Marionette(host='127.0.0.1', - port=self.marionette_port, - socket_timeout=None, - startup_timeout=startup_timeout) - - self.logger.debug("Waiting for Marionette connection") - while True: - try: - self.marionette.raise_for_port() - break - except IOError: - # When running in a debugger wait indefinitely for Firefox to start - if self.executor.debug_info is None: - raise - - self.logger.debug("Starting Marionette session") - self.marionette.start_session(self.capabilities) - self.logger.debug("Marionette session started") - - def after_connect(self): - pass - - def teardown(self): - try: - self.marionette._request_in_app_shutdown() - self.marionette.delete_session(send_request=False) - except Exception: - # This is typically because the session never started - pass - if self.marionette is not None: - del self.marionette - super(MarionetteProtocol, self).teardown() - - @property - def is_alive(self): - try: - self.marionette.current_window_handle - except Exception: - return False - return True - - def on_environment_change(self, old_environment, new_environment): - #Unset all the old prefs - for name in old_environment.get("prefs", {}).iterkeys(): - value = self.executor.original_pref_values[name] - if value is None: - self.prefs.clear(name) - else: - self.prefs.set(name, value) - - for name, value in new_environment.get("prefs", {}).iteritems(): - self.executor.original_pref_values[name] = self.prefs.get(name) - self.prefs.set(name, value) - - -class ExecuteAsyncScriptRun(object): - def __init__(self, logger, func, protocol, url, timeout): - self.logger = logger - self.result = (None, None) - self.protocol = protocol - self.func = func - self.url = url - self.timeout = timeout - self.result_flag = threading.Event() - - def run(self): - index = self.url.rfind("/storage/") - if index != -1: - # Clear storage - self.protocol.storage.clear_origin(self.url) - - timeout = self.timeout - - try: - if timeout is not None: - self.protocol.base.set_timeout(timeout + extra_timeout) - else: - # We just want it to never time out, really, but marionette doesn't - # make that possible. It also seems to time out immediately if the - # timeout is set too high. This works at least. - self.protocol.base.set_timeout(2**28 - 1) - except IOError: - self.logger.error("Lost marionette connection before starting test") - return Stop - - if timeout is not None: - wait_timeout = timeout + 2 * extra_timeout - else: - wait_timeout = None - - timer = threading.Timer(wait_timeout, self._timeout) - timer.start() - - self._run() - - self.result_flag.wait() - timer.cancel() - - if self.result == (None, None): - self.logger.debug("Timed out waiting for a result") - self.result = False, ("EXTERNAL-TIMEOUT", None) - elif self.result[1] is None: - # We didn't get any data back from the test, so check if the - # browser is still responsive - if self.protocol.is_alive: - self.result = False, ("INTERNAL-ERROR", None) - else: - self.result = False, ("CRASH", None) - return self.result - - def _run(self): - try: - self.result = True, self.func(self.protocol, self.url, self.timeout) - except errors.ScriptTimeoutException: - self.logger.debug("Got a marionette timeout") - self.result = False, ("EXTERNAL-TIMEOUT", None) - except IOError: - # This can happen on a crash - # Also, should check after the test if the firefox process is still running - # and otherwise ignore any other result and set it to crash - self.result = False, ("CRASH", None) - except Exception as e: - message = getattr(e, "message", "") - if message: - message += "\n" - message += traceback.format_exc(e) - self.logger.warning(traceback.format_exc()) - self.result = False, ("INTERNAL-ERROR", e) - finally: - self.result_flag.set() - - def _timeout(self): - self.result = False, ("EXTERNAL-TIMEOUT", None) - self.result_flag.set() - - -class MarionetteTestharnessExecutor(TestharnessExecutor): - supports_testdriver = True - - def __init__(self, browser, server_config, timeout_multiplier=1, - close_after_done=True, debug_info=None, capabilities=None, - debug=False, ccov=False, **kwargs): - """Marionette-based executor for testharness.js tests""" - TestharnessExecutor.__init__(self, browser, server_config, - timeout_multiplier=timeout_multiplier, - debug_info=debug_info) - self.protocol = MarionetteProtocol(self, - browser, - capabilities, - timeout_multiplier, - kwargs["e10s"], - ccov) - self.script = open(os.path.join(here, "testharness_webdriver.js")).read() - self.script_resume = open(os.path.join(here, "testharness_webdriver_resume.js")).read() - self.close_after_done = close_after_done - self.window_id = str(uuid.uuid4()) - self.debug = debug - - self.original_pref_values = {} - - if marionette is None: - do_delayed_imports() - - def setup(self, runner): - super(MarionetteTestharnessExecutor, self).setup(runner) - self.protocol.testharness.load_runner(self.last_environment["protocol"]) - - def is_alive(self): - return self.protocol.is_alive - - def on_environment_change(self, new_environment): - self.protocol.on_environment_change(self.last_environment, new_environment) - - if new_environment["protocol"] != self.last_environment["protocol"]: - self.protocol.testharness.load_runner(new_environment["protocol"]) - - def do_test(self, test): - timeout = (test.timeout * self.timeout_multiplier if self.debug_info is None - else None) - - success, data = ExecuteAsyncScriptRun(self.logger, - self.do_testharness, - self.protocol, - self.test_url(test), - timeout).run() - # The format of data depends on whether the test ran to completion or not - # For asserts we only care about the fact that if it didn't complete, the - # status is in the first field. - status = None - if not success: - status = data[0] - - extra = None - if self.debug and (success or status not in ("CRASH", "INTERNAL-ERROR")): - assertion_count = self.protocol.asserts.get() - if assertion_count is not None: - extra = {"assertion_count": assertion_count} - - if success: - return self.convert_result(test, data, extra=extra) - - return (test.result_cls(extra=extra, *data), []) - - def do_testharness(self, protocol, url, timeout): - parent_window = protocol.testharness.close_old_windows(protocol) - - if timeout is not None: - timeout_ms = str(timeout * 1000) - else: - timeout_ms = "null" - - if self.protocol.coverage.is_enabled: - self.protocol.coverage.reset() - - format_map = {"abs_url": url, - "url": strip_server(url), - "window_id": self.window_id, - "timeout_multiplier": self.timeout_multiplier, - "timeout": timeout_ms, - "explicit_timeout": timeout is None} - - script = self.script % format_map - - protocol.base.execute_script(script, async=True) - test_window = protocol.testharness.get_test_window(self.window_id, parent_window) - - handler = CallbackHandler(self.logger, protocol, test_window) - while True: - self.protocol.base.set_window(test_window) - result = protocol.base.execute_script( - self.script_resume % format_map, async=True) - if result is None: - # This can happen if we get an content process crash - return None - done, rv = handler(result) - if done: - break - - if self.protocol.coverage.is_enabled: - self.protocol.coverage.dump() - - return rv - - -class MarionetteRefTestExecutor(RefTestExecutor): - def __init__(self, browser, server_config, timeout_multiplier=1, - screenshot_cache=None, close_after_done=True, - debug_info=None, reftest_internal=False, - reftest_screenshot="unexpected", ccov=False, - group_metadata=None, capabilities=None, debug=False, **kwargs): - """Marionette-based executor for reftests""" - RefTestExecutor.__init__(self, - browser, - server_config, - screenshot_cache=screenshot_cache, - timeout_multiplier=timeout_multiplier, - debug_info=debug_info) - self.protocol = MarionetteProtocol(self, browser, capabilities, - timeout_multiplier, kwargs["e10s"], - ccov) - self.implementation = (InternalRefTestImplementation - if reftest_internal - else RefTestImplementation)(self) - self.implementation_kwargs = ({"screenshot": reftest_screenshot} if - reftest_internal else {}) - - self.close_after_done = close_after_done - self.has_window = False - self.original_pref_values = {} - self.group_metadata = group_metadata - self.debug = debug - - with open(os.path.join(here, "reftest.js")) as f: - self.script = f.read() - with open(os.path.join(here, "reftest-wait_marionette.js")) as f: - self.wait_script = f.read() - - def setup(self, runner): - super(self.__class__, self).setup(runner) - self.implementation.setup(**self.implementation_kwargs) - - def teardown(self): - try: - self.implementation.teardown() - handles = self.protocol.marionette.window_handles - if handles: - self.protocol.marionette.switch_to_window(handles[0]) - super(self.__class__, self).teardown() - except Exception as e: - # Ignore errors during teardown - self.logger.warning("Exception during reftest teardown:\n%s" % - traceback.format_exc(e)) - - def is_alive(self): - return self.protocol.is_alive - - def on_environment_change(self, new_environment): - self.protocol.on_environment_change(self.last_environment, new_environment) - - def do_test(self, test): - if not isinstance(self.implementation, InternalRefTestImplementation): - if self.close_after_done and self.has_window: - self.protocol.marionette.close() - self.protocol.marionette.switch_to_window( - self.protocol.marionette.window_handles[-1]) - self.has_window = False - - if not self.has_window: - self.protocol.base.execute_script(self.script) - self.protocol.base.set_window(self.protocol.marionette.window_handles[-1]) - self.has_window = True - - if self.protocol.coverage.is_enabled: - self.protocol.coverage.reset() - - result = self.implementation.run_test(test) - - if self.protocol.coverage.is_enabled: - self.protocol.coverage.dump() - - if self.debug: - assertion_count = self.protocol.asserts.get() - if "extra" not in result: - result["extra"] = {} - result["extra"]["assertion_count"] = assertion_count - - return self.convert_result(test, result) - - def screenshot(self, test, viewport_size, dpi): - # https://github.com/w3c/wptrunner/issues/166 - assert viewport_size is None - assert dpi is None - - timeout = self.timeout_multiplier * test.timeout if self.debug_info is None else None - - test_url = self.test_url(test) - - return ExecuteAsyncScriptRun(self.logger, - self._screenshot, - self.protocol, - test_url, - timeout).run() - - def _screenshot(self, protocol, url, timeout): - protocol.marionette.navigate(url) - - protocol.base.execute_script(self.wait_script, async=True) - - screenshot = protocol.marionette.screenshot(full=False) - # strip off the data:img/png, part of the url - if screenshot.startswith("data:image/png;base64,"): - screenshot = screenshot.split(",", 1)[1] - - return screenshot - - -class InternalRefTestImplementation(object): - def __init__(self, executor): - self.timeout_multiplier = executor.timeout_multiplier - self.executor = executor - - @property - def logger(self): - return self.executor.logger - - def setup(self, screenshot="unexpected"): - data = {"screenshot": screenshot} - if self.executor.group_metadata is not None: - data["urlCount"] = {urlparse.urljoin(self.executor.server_url(key[0]), key[1]):value - for key, value in self.executor.group_metadata.get("url_count", {}).iteritems() - if value > 1} - self.executor.protocol.marionette.set_context(self.executor.protocol.marionette.CONTEXT_CHROME) - self.executor.protocol.marionette._send_message("reftest:setup", data) - - def run_test(self, test): - references = self.get_references(test) - timeout = (test.timeout * 1000) * self.timeout_multiplier - rv = self.executor.protocol.marionette._send_message("reftest:run", - {"test": self.executor.test_url(test), - "references": references, - "expected": test.expected(), - "timeout": timeout})["value"] - return rv - - def get_references(self, node): - rv = [] - for item, relation in node.references: - rv.append([self.executor.test_url(item), self.get_references(item), relation]) - return rv - - def teardown(self): - try: - self.executor.protocol.marionette._send_message("reftest:teardown", {}) - self.executor.protocol.marionette.set_context(self.executor.protocol.marionette.CONTEXT_CONTENT) - except Exception as e: - # Ignore errors during teardown - self.logger.warning(traceback.format_exc(e)) - - - -class GeckoDriverProtocol(WebDriverProtocol): - server_cls = GeckoDriverServer - - -class MarionetteWdspecExecutor(WdspecExecutor): - protocol_cls = GeckoDriverProtocol