diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs index 3206af03a32c..d668c17696e0 100644 --- a/components/webdriver_server/lib.rs +++ b/components/webdriver_server/lib.rs @@ -52,7 +52,7 @@ use util::prefs::{PREFS, PrefValue}; use util::thread::spawn_named; use uuid::Uuid; use webdriver::command::{AddCookieParameters, GetParameters, JavascriptCommandParameters}; -use webdriver::command::{LocatorParameters, Parameters}; +use webdriver::command::{LocatorParameters, Parameters, SwitchToWindowParameters}; use webdriver::command::{SendKeysParameters, SwitchToFrameParameters, TimeoutsParameters}; use webdriver::command::{WebDriverCommand, WebDriverExtensionCommand, WebDriverMessage}; use webdriver::command::WindowSizeParameters; @@ -504,6 +504,20 @@ impl Handler { } } + fn handle_dismiss_alert(&mut self) -> WebDriverResult { + //TODO figure out how to interact with blocking native UI + Ok(WebDriverResponse::Void) + } + + fn handle_switch_to_window(&mut self, parameters: &SwitchToWindowParameters) -> WebDriverResult { + if parameters.handle == self.session.as_ref().unwrap().id.to_string() { + Ok(WebDriverResponse::Void) + } else { + Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, + "Switching to another window is not supported")) + } + } + fn handle_switch_to_frame(&mut self, parameters: &SwitchToFrameParameters) -> WebDriverResult { use webdriver::common::FrameId; let frame_id = match parameters.id { @@ -873,6 +887,7 @@ impl WebDriverHandler for Handler { WebDriverCommand::GetTitle => self.handle_title(), WebDriverCommand::GetWindowHandle => self.handle_window_handle(), WebDriverCommand::GetWindowHandles => self.handle_window_handles(), + WebDriverCommand::SwitchToWindow(ref parameters) => self.handle_switch_to_window(parameters), WebDriverCommand::SwitchToFrame(ref parameters) => self.handle_switch_to_frame(parameters), WebDriverCommand::SwitchToParentFrame => self.handle_switch_to_parent_frame(), WebDriverCommand::FindElement(ref parameters) => self.handle_find_element(parameters), @@ -900,6 +915,7 @@ impl WebDriverHandler for Handler { ServoExtensionCommand::ResetPrefs(ref x) => self.handle_reset_prefs(x), } } + WebDriverCommand::DismissAlert => self.handle_dismiss_alert(), _ => Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, "Command not implemented")) } diff --git a/python/servo/devenv_commands.py b/python/servo/devenv_commands.py index 2fc4a08e434b..a8a20a93fcc4 100644 --- a/python/servo/devenv_commands.py +++ b/python/servo/devenv_commands.py @@ -168,25 +168,28 @@ def fetch(self): print(cargo_path) call(["cargo", "fetch"], env=self.build_env()) - @Command('wpt-upgrade', + @Command('wptrunner-upgrade', description='upgrade wptrunner.', category='devenv') def upgrade_wpt_runner(self): + env = self.build_env() with cd(path.join(self.context.topdir, 'tests', 'wpt', 'harness')): - code = call(["git", "init"], env=self.build_env()) + code = call(["git", "init"], env=env) if code: return code + # No need to report an error if this fails, as it will for the first use + call(["git", "remote", "rm", "upstream"], env=env) code = call( - ["git", "remote", "add", "upstream", "https://github.com/w3c/wptrunner.git"], env=self.build_env()) + ["git", "remote", "add", "upstream", "https://github.com/w3c/wptrunner.git"], env=env) if code: return code - code = call(["git", "fetch", "upstream"], env=self.build_env()) + code = call(["git", "fetch", "upstream"], env=env) if code: return code - code = call(["git", "reset", "--hard", "remotes/upstream/master"], env=self.build_env()) + code = call(["git", "reset", "--hard", "remotes/upstream/master"], env=env) if code: return code - code = call(["rm", "-rf", ".git"], env=self.build_env()) + code = call(["rm", "-rf", ".git"], env=env) if code: return code return 0 diff --git a/tests/wpt/harness/wptrunner/browsers/__init__.py b/tests/wpt/harness/wptrunner/browsers/__init__.py index ffc5aedc8304..2433e42ada1c 100644 --- a/tests/wpt/harness/wptrunner/browsers/__init__.py +++ b/tests/wpt/harness/wptrunner/browsers/__init__.py @@ -28,6 +28,7 @@ product_list = ["b2g", "chrome", + "edge", "firefox", "servo", "servodriver"] diff --git a/tests/wpt/harness/wptrunner/browsers/firefox.py b/tests/wpt/harness/wptrunner/browsers/firefox.py index dcd7a4f1d17d..a3c6f3f5753f 100644 --- a/tests/wpt/harness/wptrunner/browsers/firefox.py +++ b/tests/wpt/harness/wptrunner/browsers/firefox.py @@ -3,6 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. import os +import platform import subprocess import sys @@ -128,10 +129,16 @@ def start(self): 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.dns.localDomains": ",".join(hostnames), + "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() diff --git a/tests/wpt/harness/wptrunner/browsers/servo.py b/tests/wpt/harness/wptrunner/browsers/servo.py index bc90cefcfc9e..4e4bc20128c4 100644 --- a/tests/wpt/harness/wptrunner/browsers/servo.py +++ b/tests/wpt/harness/wptrunner/browsers/servo.py @@ -6,7 +6,7 @@ from .base import NullBrowser, ExecutorBrowser, require_arg from ..executors import executor_kwargs as base_executor_kwargs -from ..executors.executorservo import ServoTestharnessExecutor, ServoRefTestExecutor +from ..executors.executorservo import ServoTestharnessExecutor, ServoRefTestExecutor, ServoWdspecExecutor here = os.path.join(os.path.split(__file__)[0]) @@ -14,7 +14,8 @@ "check_args": "check_args", "browser": "ServoBrowser", "executor": {"testharness": "ServoTestharnessExecutor", - "reftest": "ServoRefTestExecutor"}, + "reftest": "ServoRefTestExecutor", + "wdspec": "ServoWdspecExecutor"}, "browser_kwargs": "browser_kwargs", "executor_kwargs": "executor_kwargs", "env_options": "env_options", diff --git a/tests/wpt/harness/wptrunner/executors/executormarionette.py b/tests/wpt/harness/wptrunner/executors/executormarionette.py index c4b1cba689af..8fc8ebed8130 100644 --- a/tests/wpt/harness/wptrunner/executors/executormarionette.py +++ b/tests/wpt/harness/wptrunner/executors/executormarionette.py @@ -17,11 +17,11 @@ errors = None marionette = None +pytestrunner = None webdriver = None here = os.path.join(os.path.split(__file__)[0]) -from . import pytestrunner from .base import (ExecutorException, Protocol, RefTestExecutor, @@ -41,7 +41,7 @@ def do_delayed_imports(): - global errors, marionette, webdriver + global errors, marionette # Marionette client used to be called marionette, recently it changed # to marionette_driver for unfathomable reasons @@ -51,8 +51,6 @@ def do_delayed_imports(): except ImportError: from marionette_driver import marionette, errors - import webdriver - class MarionetteProtocol(Protocol): def __init__(self, executor, browser): @@ -292,7 +290,7 @@ def is_alive(self): class ExecuteAsyncScriptRun(object): def __init__(self, logger, func, marionette, url, timeout): self.logger = logger - self.result = None + self.result = (None, None) self.marionette = marionette self.func = func self.url = url @@ -323,11 +321,9 @@ def run(self): wait_timeout = None flag = self.result_flag.wait(wait_timeout) - if self.result is None: + if self.result[1] is None: self.logger.debug("Timed out waiting for a result") - assert not flag self.result = False, ("EXTERNAL-TIMEOUT", None) - return self.result def _run(self): @@ -409,7 +405,8 @@ def do_testharness(self, marionette, url, timeout): "timeout": timeout_ms, "explicit_timeout": timeout is None} - return marionette.execute_async_script(script, new_sandbox=False) + rv = marionette.execute_async_script(script, new_sandbox=False) + return rv class MarionetteRefTestExecutor(RefTestExecutor): @@ -487,7 +484,7 @@ def _screenshot(self, marionette, url, timeout): class WdspecRun(object): def __init__(self, func, session, path, timeout): self.func = func - self.result = None + self.result = (None, None) self.session = session self.path = path self.timeout = timeout @@ -504,8 +501,7 @@ def run(self): executor.start() flag = self.result_flag.wait(self.timeout) - if self.result is None: - assert not flag + if self.result[1] is None: self.result = False, ("EXTERNAL-TIMEOUT", None) return self.result @@ -528,6 +524,7 @@ def _run(self): class MarionetteWdspecExecutor(WdspecExecutor): def __init__(self, browser, server_config, webdriver_binary, timeout_multiplier=1, close_after_done=True, debug_info=None): + self.do_delayed_imports() WdspecExecutor.__init__(self, browser, server_config, timeout_multiplier=timeout_multiplier, debug_info=debug_info) @@ -557,3 +554,8 @@ def do_wdspec(self, session, path, timeout): harness_result = ("OK", None) subtest_results = pytestrunner.run(path, session, timeout=timeout) return (harness_result, subtest_results) + + def do_delayed_imports(self): + global pytestrunner, webdriver + from . import pytestrunner + from tools import webdriver diff --git a/tests/wpt/harness/wptrunner/executors/executorselenium.py b/tests/wpt/harness/wptrunner/executors/executorselenium.py index 587c49c7c92f..f5d65f499b0a 100644 --- a/tests/wpt/harness/wptrunner/executors/executorselenium.py +++ b/tests/wpt/harness/wptrunner/executors/executorselenium.py @@ -22,20 +22,21 @@ strip_server) from ..testrunner import Stop - here = os.path.join(os.path.split(__file__)[0]) webdriver = None exceptions = None +RemoteConnection = None extra_timeout = 5 def do_delayed_imports(): global webdriver global exceptions + global RemoteConnection from selenium import webdriver from selenium.common import exceptions - + from selenium.webdriver.remote.remote_connection import RemoteConnection class SeleniumProtocol(Protocol): def __init__(self, executor, browser, capabilities, **kwargs): @@ -53,8 +54,9 @@ def setup(self, runner): session_started = False try: - self.webdriver = webdriver.Remote( - self.url, desired_capabilities=self.capabilities) + self.webdriver = webdriver.Remote(command_executor=RemoteConnection(self.url.strip("/"), + resolve_ip=False), + desired_capabilities=self.capabilities) except: self.logger.warning( "Connecting to Selenium failed:\n%s" % traceback.format_exc()) @@ -231,17 +233,7 @@ def is_alive(self): def do_test(self, test): self.logger.info("Test requires OS-level window focus") - if self.close_after_done and self.has_window: - self.protocol.webdriver.close() - self.protocol.webdriver.switch_to_window( - self.protocol.webdriver.window_handles[-1]) - self.has_window = False - - if not self.has_window: - self.protocol.webdriver.execute_script(self.script) - self.protocol.webdriver.switch_to_window( - self.protocol.webdriver.window_handles[-1]) - self.has_window = True + self.protocol.webdriver.set_window_size(600, 600) result = self.implementation.run_test(test) diff --git a/tests/wpt/harness/wptrunner/executors/executorservo.py b/tests/wpt/harness/wptrunner/executors/executorservo.py index ca684d4ef22c..b0c4d5afea3a 100644 --- a/tests/wpt/harness/wptrunner/executors/executorservo.py +++ b/tests/wpt/harness/wptrunner/executors/executorservo.py @@ -4,11 +4,13 @@ import base64 import hashlib +import httplib import json import os import subprocess import tempfile import threading +import traceback import urlparse import uuid from collections import defaultdict @@ -19,11 +21,19 @@ Protocol, RefTestImplementation, testharness_result_converter, - reftest_result_converter) + reftest_result_converter, + WdspecExecutor) from .process import ProcessTestExecutor from ..browsers.base import browser_command +from ..wpttest import WdspecResult, WdspecSubtestResult +from ..webdriver_server import ServoDriverServer +from .executormarionette import WdspecRun + +pytestrunner = None render_arg = None +webdriver = None +extra_timeout = 5 # seconds def do_delayed_imports(): global render_arg @@ -214,10 +224,7 @@ def screenshot(self, test, viewport_size, dpi): for pref, value in test.environment.get('prefs', {}).iteritems(): command += ["--pref", "%s=%s" % (pref, value)] - if viewport_size: - command += ["--resolution", viewport_size] - else: - command += ["--resolution", "800x600"] + command += ["--resolution", viewport_size or "800x600"] if dpi: command += ["--device-pixel-ratio", dpi] @@ -275,3 +282,84 @@ def on_output(self, line): self.logger.process_output(self.proc.pid, line, " ".join(self.command)) + +class ServoWdspecProtocol(Protocol): + def __init__(self, executor, browser): + self.do_delayed_imports() + Protocol.__init__(self, executor, browser) + self.session = None + self.server = None + + def setup(self, runner): + try: + self.server = ServoDriverServer(self.logger, binary=self.browser.binary, binary_args=self.browser.binary_args, render_backend=self.browser.render_backend) + self.server.start(block=False) + self.logger.info( + "WebDriver HTTP server listening at %s" % self.server.url) + + self.logger.info( + "Establishing new WebDriver session with %s" % self.server.url) + self.session = webdriver.Session( + self.server.host, self.server.port, self.server.base_path) + self.session.start() + except Exception: + self.logger.error(traceback.format_exc()) + self.executor.runner.send_message("init_failed") + else: + self.executor.runner.send_message("init_succeeded") + + def teardown(self): + if self.server is not None: + try: + if self.session.session_id is not None: + self.session.end() + except Exception: + pass + if self.server.is_alive: + self.server.stop() + + @property + def is_alive(self): + conn = httplib.HTTPConnection(self.server.host, self.server.port) + conn.request("HEAD", self.server.base_path + "invalid") + res = conn.getresponse() + return res.status == 404 + + def do_delayed_imports(self): + global pytestrunner, webdriver + from . import pytestrunner + import webdriver + + +class ServoWdspecExecutor(WdspecExecutor): + def __init__(self, browser, server_config, + timeout_multiplier=1, close_after_done=True, debug_info=None, + **kwargs): + WdspecExecutor.__init__(self, browser, server_config, + timeout_multiplier=timeout_multiplier, + debug_info=debug_info) + self.protocol = ServoWdspecProtocol(self, browser) + + def is_alive(self): + return self.protocol.is_alive + + def on_environment_change(self, new_environment): + pass + + def do_test(self, test): + timeout = test.timeout * self.timeout_multiplier + extra_timeout + + success, data = WdspecRun(self.do_wdspec, + self.protocol.session, + test.path, + timeout).run() + + if success: + return self.convert_result(test, data) + + return (test.result_cls(*data), []) + + def do_wdspec(self, session, path, timeout): + harness_result = ("OK", None) + subtest_results = pytestrunner.run(path, session, timeout=timeout) + return (harness_result, subtest_results) diff --git a/tests/wpt/harness/wptrunner/executors/executorservodriver.py b/tests/wpt/harness/wptrunner/executors/executorservodriver.py index fceeb58fad2f..279658d0cd1f 100644 --- a/tests/wpt/harness/wptrunner/executors/executorservodriver.py +++ b/tests/wpt/harness/wptrunner/executors/executorservodriver.py @@ -14,7 +14,6 @@ RefTestImplementation, TestharnessExecutor, strip_server) -from .. import webdriver from ..testrunner import Stop webdriver = None @@ -26,7 +25,7 @@ def do_delayed_imports(): global webdriver - import webdriver + from tools import webdriver class ServoWebDriverProtocol(Protocol): diff --git a/tests/wpt/harness/wptrunner/executors/pytestrunner/fixtures.py b/tests/wpt/harness/wptrunner/executors/pytestrunner/fixtures.py index 77afb4a36842..bda42fe06445 100644 --- a/tests/wpt/harness/wptrunner/executors/pytestrunner/fixtures.py +++ b/tests/wpt/harness/wptrunner/executors/pytestrunner/fixtures.py @@ -3,6 +3,7 @@ # You can obtain one at http://mozilla.org/MPL/2.0/. import pytest +import webdriver """pytest fixtures for use in Python-based WPT tests. @@ -17,7 +18,7 @@ class Session(object): in tests. The session is not created by default to enable testing of session - creation. However, a module-scoped session will be implicitly created + creation. However, a function-scoped session will be implicitly created at the first call to a WebDriver command. This means methods such as `session.send_command` and `session.session_id` are possible to use without having a session. @@ -45,14 +46,62 @@ def setup(request, session): def test_something(setup, session): assert session.url == "https://example.org" - The session is closed when the test module goes out of scope by an - implicit call to `session.end`. + When the test function goes out of scope, any remaining user prompts + and opened windows are closed, and the current browsing context is + switched back to the top-level browsing context. """ def __init__(self, client): self.client = client - @pytest.fixture(scope="module") + @pytest.fixture(scope="function") def session(self, request): - request.addfinalizer(self.client.end) + # finalisers are popped off a stack, + # making their ordering reverse + request.addfinalizer(self.switch_to_top_level_browsing_context) + request.addfinalizer(self.restore_windows) + request.addfinalizer(self.dismiss_user_prompts) + return self.client + + def dismiss_user_prompts(self): + """Dismisses any open user prompts in windows.""" + current_window = self.client.handle + + for window in self.windows(): + self.client.handle = window + try: + self.client.alert.dismiss() + except webdriver.NoSuchAlertException: + pass + + self.client.handle = current_window + + def restore_windows(self): + """Closes superfluous windows opened by the test without ending + the session implicitly by closing the last window. + """ + current_window = self.client.handle + + for window in self.windows(exclude=[current_window]): + self.client.handle = window + if len(self.client.handles) > 1: + self.client.close() + + self.client.handle = current_window + + def switch_to_top_level_browsing_context(self): + """If the current browsing context selected by WebDriver is a + `` or an `