Commit 44093a32 authored by Luke Zielinski's avatar Luke Zielinski Committed by Commit Bot

Roll internal WPT tools

This rolls up to 4b8a64638b4c06fb38eb562b754eae389b381ec0

Assorted changes, including webkitgtk_minobrowser support and mojojs
improvements

Change-Id: I2a806f1a0040394541fbe52f749f3ab69d3820a8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2453794Reviewed-by: default avatarRobert Ma <robertma@chromium.org>
Commit-Queue: Luke Z <lpz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#814352}
parent 3d03da81
...@@ -22,7 +22,7 @@ Local Modifications: None ...@@ -22,7 +22,7 @@ Local Modifications: None
Name: web-platform-tests - Test Suites for Web Platform specifications Name: web-platform-tests - Test Suites for Web Platform specifications
Short Name: wpt Short Name: wpt
URL: https://github.com/web-platform-tests/wpt/ URL: https://github.com/web-platform-tests/wpt/
Version: 9f6a7e3cfdae54795a0ca58e194b828537f9659e Version: 4b8a64638b4c06fb38eb562b754eae389b381ec0
License: LICENSES FOR W3C TEST SUITES (https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html) License: LICENSES FOR W3C TEST SUITES (https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html)
License File: wpt/wpt/LICENSE.md License File: wpt/wpt/LICENSE.md
Security Critical: no Security Critical: no
......
...@@ -9,7 +9,7 @@ cd $DIR ...@@ -9,7 +9,7 @@ cd $DIR
TARGET_DIR=$DIR/wpt TARGET_DIR=$DIR/wpt
REMOTE_REPO="https://github.com/web-platform-tests/wpt.git" REMOTE_REPO="https://github.com/web-platform-tests/wpt.git"
WPT_HEAD=9f6a7e3cfdae54795a0ca58e194b828537f9659e WPT_HEAD=4b8a64638b4c06fb38eb562b754eae389b381ec0
function clone { function clone {
# Remove existing repo if already exists. # Remove existing repo if already exists.
......
...@@ -581,7 +581,7 @@ class Session(object): ...@@ -581,7 +581,7 @@ class Session(object):
return self.send_session_command("GET", "source") return self.send_session_command("GET", "source")
@command @command
def new_window(self, type_hint=None): def new_window(self, type_hint="tab"):
body = {"type": type_hint} body = {"type": type_hint}
value = self.send_session_command("POST", "window/new", body) value = self.send_session_command("POST", "window/new", body)
......
...@@ -2,6 +2,7 @@ import json ...@@ -2,6 +2,7 @@ import json
import select import select
from six import text_type, PY3 from six import text_type, PY3
from six.moves.collections_abc import Mapping
from six.moves.http_client import HTTPConnection from six.moves.http_client import HTTPConnection
from six.moves.urllib import parse as urlparse from six.moves.urllib import parse as urlparse
...@@ -10,6 +11,56 @@ from . import error ...@@ -10,6 +11,56 @@ from . import error
"""Implements HTTP transport for the WebDriver wire protocol.""" """Implements HTTP transport for the WebDriver wire protocol."""
missing = object()
class ResponseHeaders(Mapping):
"""Read-only dictionary-like API for accessing response headers.
This class:
* Normalizes the header keys it is built with to lowercase (such that
iterating the items will return lowercase header keys).
* Has case-insensitive header lookup.
* Always returns all header values that have the same name, separated by
commas.
It does not ensure header types (e.g. binary vs string).
"""
def __init__(self, items):
self.headers_dict = {}
for key, value in items:
key = key.lower()
if key not in self.headers_dict:
self.headers_dict[key] = []
self.headers_dict[key].append(value)
def __getitem__(self, key):
"""Get all headers of a certain (case-insensitive) name. If there is
more than one, the values are returned comma separated"""
values = self.headers_dict[key.lower()]
if len(values) == 1:
return values[0]
else:
return ", ".join(values)
def get_list(self, key, default=missing):
"""Get all the header values for a particular field name as a list"""
try:
return self.headers_dict[key.lower()]
except KeyError:
if default is not missing:
return default
else:
raise
def __iter__(self):
for item in self.headers_dict:
yield item
def __len__(self):
return len(self.headers_dict)
class Response(object): class Response(object):
""" """
Describes an HTTP response received from a remote end whose Describes an HTTP response received from a remote end whose
...@@ -40,7 +91,7 @@ class Response(object): ...@@ -40,7 +91,7 @@ class Response(object):
def from_http(cls, http_response, decoder=json.JSONDecoder, **kwargs): def from_http(cls, http_response, decoder=json.JSONDecoder, **kwargs):
try: try:
body = json.load(http_response, cls=decoder, **kwargs) body = json.load(http_response, cls=decoder, **kwargs)
headers = dict(http_response.getheaders()) headers = ResponseHeaders(http_response.getheaders())
except ValueError: except ValueError:
raise ValueError("Failed to decode response body as JSON:\n" + raise ValueError("Failed to decode response body as JSON:\n" +
http_response.read()) http_response.read())
......
...@@ -7,7 +7,8 @@ latest_channels = { ...@@ -7,7 +7,8 @@ latest_channels = {
'chrome_android': 'dev', 'chrome_android': 'dev',
'edgechromium': 'dev', 'edgechromium': 'dev',
'safari': 'preview', 'safari': 'preview',
'servo': 'nightly' 'servo': 'nightly',
'webkitgtk_minibrowser': 'nightly'
} }
channel_by_name = { channel_by_name = {
......
...@@ -322,16 +322,21 @@ class Chrome(BrowserSetup): ...@@ -322,16 +322,21 @@ class Chrome(BrowserSetup):
kwargs["binary"] = binary kwargs["binary"] = binary
else: else:
raise WptrunError("Unable to locate Chrome binary") raise WptrunError("Unable to locate Chrome binary")
if kwargs["mojojs_path"]:
kwargs["enable_mojojs"] = True
logger.info("--mojojs-path is provided, enabling MojoJS")
# TODO(Hexcles): Enable this everywhere when Chrome 86 becomes stable. # TODO(Hexcles): Enable this everywhere when Chrome 86 becomes stable.
if browser_channel in self.experimental_channels: elif browser_channel in self.experimental_channels:
try: try:
self.browser.install_mojojs( path = self.browser.install_mojojs(
dest=self.venv.path, dest=self.venv.path,
channel=browser_channel, channel=browser_channel,
browser_binary=kwargs["binary"], browser_binary=kwargs["binary"],
) )
kwargs["mojojs_path"] = path
kwargs["enable_mojojs"] = True kwargs["enable_mojojs"] = True
logger.info("MojoJS enabled") logger.info("MojoJS enabled automatically (mojojs_path: %s)" % path)
except Exception as e: except Exception as e:
logger.error("Cannot enable MojoJS: %s" % e) logger.error("Cannot enable MojoJS: %s" % e)
...@@ -662,18 +667,19 @@ class WebKitGTKMiniBrowser(BrowserSetup): ...@@ -662,18 +667,19 @@ class WebKitGTKMiniBrowser(BrowserSetup):
browser_cls = browser.WebKitGTKMiniBrowser browser_cls = browser.WebKitGTKMiniBrowser
def install(self, channel=None): def install(self, channel=None):
raise NotImplementedError if self.prompt_install(self.name):
return self.browser.install(self.venv.path, channel, self.prompt)
def setup_kwargs(self, kwargs): def setup_kwargs(self, kwargs):
if kwargs["binary"] is None: if kwargs["binary"] is None:
binary = self.browser.find_binary(channel=kwargs["browser_channel"]) binary = self.browser.find_binary(venv_path=self.venv.path, channel=kwargs["browser_channel"])
if binary is None: if binary is None:
raise WptrunError("Unable to find MiniBrowser binary") raise WptrunError("Unable to find MiniBrowser binary")
kwargs["binary"] = binary kwargs["binary"] = binary
if kwargs["webdriver_binary"] is None: if kwargs["webdriver_binary"] is None:
webdriver_binary = self.browser.find_webdriver(channel=kwargs["browser_channel"]) webdriver_binary = self.browser.find_webdriver(venv_path=self.venv.path, channel=kwargs["browser_channel"])
if webdriver_binary is None: if webdriver_binary is None:
raise WptrunError("Unable to find WebKitWebDriver in PATH") raise WptrunError("Unable to find WebKitWebDriver in PATH")
......
...@@ -5,8 +5,11 @@ import shutil ...@@ -5,8 +5,11 @@ import shutil
import stat import stat
import subprocess import subprocess
import tarfile import tarfile
import time
import zipfile import zipfile
from io import BytesIO from io import BytesIO
from socket import error as SocketError # NOQA: N812
from six.moves.urllib.request import urlopen
MYPY = False MYPY = False
if MYPY: if MYPY:
...@@ -100,6 +103,41 @@ def get(url): ...@@ -100,6 +103,41 @@ def get(url):
return resp return resp
def get_download_to_descriptor(fd, url, max_retries=5):
"""Download an URL in chunks and saves it to a file descriptor (truncating it)
It doesn't close the descriptor, but flushes it on success.
It retries the download in case of ECONNRESET up to max_retries.
This function is meant to download big files directly to the disk without
caching the whole file in memory.
"""
if max_retries < 1:
max_retries = 1
wait = 2
for current_retry in range(1, max_retries+1):
try:
logger.info("Downloading %s Try %d/%d" % (url, current_retry, max_retries))
resp = urlopen(url)
# We may come here in a retry, ensure to truncate fd before start writing.
fd.seek(0)
fd.truncate(0)
while True:
chunk = resp.read(16*1024)
if not chunk:
break # Download finished
fd.write(chunk)
fd.flush()
# Success
return
except SocketError as e:
if current_retry < max_retries and e.errno == errno.ECONNRESET:
# Retry
logger.error("Connection reset by peer. Retrying after %ds..." % wait)
time.sleep(wait)
wait *= 2
else:
# Maximum retries or unknown error
raise
def rmtree(path): def rmtree(path):
# This works around two issues: # This works around two issues:
# 1. Cannot delete read-only files owned by us (e.g. files extracted from tarballs) # 1. Cannot delete read-only files owned by us (e.g. files extracted from tarballs)
...@@ -114,3 +152,13 @@ def rmtree(path): ...@@ -114,3 +152,13 @@ def rmtree(path):
raise raise
return shutil.rmtree(path, onerror=handle_remove_readonly) return shutil.rmtree(path, onerror=handle_remove_readonly)
def sha256sum(file_path):
"""Computes the SHA256 hash sum of a file"""
from hashlib import sha256
hash = sha256()
with open(file_path, 'rb') as f:
for chunk in iter(lambda: f.read(4096), b''):
hash.update(chunk)
return hash.hexdigest()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment