mirror of
https://github.com/servo/servo.git
synced 2025-09-01 02:28:21 +01:00
Update web-platform-tests to revision 5084587f6b05bf99ad09e7844be66dcc61070cdf
This commit is contained in:
parent
6d42d2f1e8
commit
7d1071a6a4
408 changed files with 8968 additions and 2608 deletions
7
tests/wpt/web-platform-tests/tools/ci/ci_taskcluster.sh
Executable file
7
tests/wpt/web-platform-tests/tools/ci/ci_taskcluster.sh
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ $1 == "firefox" ]; then
|
||||
./wpt run firefox --log-tbpl=- --log-tbpl-level=debug --log-wptreport=../artifacts/wpt_report.json --this-chunk=$3 --total-chunks=$4 --test-type=$2 -y --install-browser --no-pause --no-restart-on-unexpected
|
||||
elif [ $1 == "chrome" ]; then
|
||||
./wpt run chrome --log-tbpl=- --log-tbpl-level=debug --log-wptreport=../artifacts/wpt_report.json --this-chunk=$3 --total-chunks=$4 --test-type=$2 -y --no-pause --no-restart-on-unexpected
|
||||
fi
|
|
@ -3,5 +3,7 @@
|
|||
"virtualenv": false},
|
||||
"check-stability": {"path": "check_stability.py", "script": "run", "parser": "get_parser", "parse_known": true, "help": "Check test stability",
|
||||
"virtualenv": true, "install": ["requests"], "requirements": ["../wptrunner/requirements.txt"]},
|
||||
"make-hosts-file": {"path": "make_hosts_file.py", "script": "run", "parser": "create_parser", "help": "Output a hosts file to stdout", "virtualenv": false}
|
||||
"make-hosts-file": {"path": "make_hosts_file.py", "script": "run", "parser": "create_parser", "help": "Output a hosts file to stdout", "virtualenv": false},
|
||||
"make-tasks": {"path": "taskgraph.py", "script": "run", "parser": "get_parser", "parse_known": true, "help": "Generate taskcluster.yml file containing the run tasks",
|
||||
"virtualenv": true, "install": ["pyyaml"]}
|
||||
}
|
||||
|
|
123
tests/wpt/web-platform-tests/tools/ci/taskgraph.py
Normal file
123
tests/wpt/web-platform-tests/tools/ci/taskgraph.py
Normal file
|
@ -0,0 +1,123 @@
|
|||
import argparse
|
||||
import copy
|
||||
import os
|
||||
import six
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
here = os.path.dirname(__file__)
|
||||
wpt_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir))
|
||||
|
||||
|
||||
task_template = {
|
||||
"provisionerId": "{{ taskcluster.docker.provisionerId }}",
|
||||
"workerType": "{{ taskcluster.docker.workerType }}",
|
||||
"extra": {
|
||||
"github": {
|
||||
"events": ["push"],
|
||||
"branches": ["master"],
|
||||
},
|
||||
},
|
||||
"payload": {
|
||||
"maxRunTime": 5400,
|
||||
"image": "harjgam/web-platform-tests:0.6",
|
||||
"command":[
|
||||
"/bin/bash",
|
||||
"--login",
|
||||
"-c",
|
||||
""">-
|
||||
~/start.sh &&
|
||||
cd /home/test/web-platform-tests &&
|
||||
git fetch {{event.head.repo.url}} &&
|
||||
git config advice.detachedHead false &&
|
||||
git checkout {{event.head.sha}} &&
|
||||
%(command)s"""],
|
||||
"artifacts": {
|
||||
"public/results": {
|
||||
"path": "/home/test/artifacts",
|
||||
"type": "directory"
|
||||
}
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"name": "wpt-%(browser_name)s-%(suite)s-%(chunk)s",
|
||||
"description": "",
|
||||
"owner": "{{ event.head.user.email }}",
|
||||
"source": "{{ event.head.repo.url }}",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
file_template = {
|
||||
"version": 0,
|
||||
"tasks": [],
|
||||
"allowPullRequests": "collaborators"
|
||||
}
|
||||
|
||||
suites = {
|
||||
"testharness": {"chunks": 12},
|
||||
"reftest": {"chunks": 6},
|
||||
"wdspec": {"chunks": 1}
|
||||
}
|
||||
|
||||
browsers = {
|
||||
"firefox": {"name": "firefox-nightly"},
|
||||
"chrome": {"name": "chrome-dev"}
|
||||
}
|
||||
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--dest",
|
||||
action="store",
|
||||
default=wpt_root,
|
||||
help="Directory to write the .taskcluster.yml file to")
|
||||
return parser
|
||||
|
||||
|
||||
def fill(template, data):
|
||||
rv = {}
|
||||
for key, value in template.iteritems():
|
||||
rv[key] = fill_inner(value, data)
|
||||
return rv
|
||||
|
||||
|
||||
def fill_inner(value, data):
|
||||
if isinstance(value, six.string_types):
|
||||
return value % data
|
||||
elif isinstance(value, dict):
|
||||
return fill(value, data)
|
||||
elif isinstance(value, list):
|
||||
return [fill_inner(item, data) for item in value]
|
||||
elif isinstance(value, (float,) + six.integer_types):
|
||||
return value
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
|
||||
def run(venv, *args, **kwargs):
|
||||
if not os.path.isdir(kwargs["dest"]):
|
||||
raise ValueError("Invalid directory %s" % kwargs["dest"])
|
||||
|
||||
task_config = copy.deepcopy(file_template)
|
||||
for browser, browser_props in browsers.iteritems():
|
||||
for suite, suite_props in suites.iteritems():
|
||||
total_chunks = suite_props.get("chunks", 1)
|
||||
for chunk in six.moves.xrange(1, total_chunks + 1):
|
||||
data = {
|
||||
"suite": suite,
|
||||
"chunk": chunk,
|
||||
"browser_name": browser_props["name"],
|
||||
"command": ("./tools/ci/ci_taskcluster.sh %s %s %s %s" %
|
||||
(browser, suite, chunk, total_chunks))
|
||||
}
|
||||
|
||||
task_data = fill(task_template, data)
|
||||
task_config["tasks"].append(task_data)
|
||||
|
||||
with open(os.path.join(kwargs["dest"], ".taskcluster.yml"), "w") as f:
|
||||
f.write("""# GENERATED FILE DO NOT EDIT
|
||||
# To regenerate this file run ./wpt make-tasks
|
||||
""")
|
||||
yaml.dump(task_config, f)
|
4
tests/wpt/web-platform-tests/tools/docker/.bashrc
Normal file
4
tests/wpt/web-platform-tests/tools/docker/.bashrc
Normal file
|
@ -0,0 +1,4 @@
|
|||
function xvfb_start() {
|
||||
GEOMETRY="$SCREEN_WIDTH""x""$SCREEN_HEIGHT""x""$SCREEN_DEPTH"
|
||||
xvfb-run --server-args="-screen 0 $GEOMETRY -ac +extension RANDR" $@
|
||||
}
|
76
tests/wpt/web-platform-tests/tools/docker/Dockerfile
Normal file
76
tests/wpt/web-platform-tests/tools/docker/Dockerfile
Normal file
|
@ -0,0 +1,76 @@
|
|||
FROM ubuntu:16.04
|
||||
|
||||
# No interactive frontend during docker build
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
DEBCONF_NONINTERACTIVE_SEEN=true
|
||||
|
||||
# General requirements not in the base image
|
||||
RUN apt-get -qqy update \
|
||||
&& apt-get -qqy install \
|
||||
bzip2 \
|
||||
ca-certificates \
|
||||
dbus-x11 \
|
||||
gdebi \
|
||||
git \
|
||||
locales \
|
||||
pulseaudio \
|
||||
python \
|
||||
python-pip \
|
||||
tzdata \
|
||||
sudo \
|
||||
unzip \
|
||||
wget \
|
||||
xvfb
|
||||
|
||||
# Installing just the deps of firefox and chrome is moderately tricky, so
|
||||
# just install the default versions of them, and some extra deps we happen
|
||||
# to know that chrome requires
|
||||
|
||||
RUN apt-get -qqy install \
|
||||
firefox \
|
||||
libnss3-tools \
|
||||
fonts-liberation \
|
||||
indicator-application \
|
||||
libappindicator1 \
|
||||
libappindicator3-1 \
|
||||
libdbusmenu-gtk3-4 \
|
||||
libindicator3-7 \
|
||||
libindicator7
|
||||
|
||||
RUN pip install --upgrade pip
|
||||
RUN pip install virtualenv
|
||||
|
||||
ENV TZ "UTC"
|
||||
RUN echo "${TZ}" > /etc/timezone \
|
||||
&& dpkg-reconfigure --frontend noninteractive tzdata
|
||||
|
||||
RUN useradd test \
|
||||
--shell /bin/bash \
|
||||
--create-home \
|
||||
&& usermod -a -G sudo test \
|
||||
&& echo 'ALL ALL = (ALL) NOPASSWD: ALL' >> /etc/sudoers \
|
||||
&& echo 'test:secret' | chpasswd
|
||||
|
||||
ENV SCREEN_WIDTH 1280
|
||||
ENV SCREEN_HEIGHT 1024
|
||||
ENV SCREEN_DEPTH 24
|
||||
ENV DISPLAY :99.0
|
||||
|
||||
USER test
|
||||
|
||||
WORKDIR /home/test
|
||||
|
||||
COPY .bashrc /home/test/.bashrc
|
||||
|
||||
COPY start.sh /home/test/start.sh
|
||||
|
||||
# Remove information on how to use sudo on login
|
||||
RUN sudo echo ""
|
||||
|
||||
RUN git clone --depth=1 https://github.com/w3c/web-platform-tests.git
|
||||
|
||||
RUN mkdir -p /home/test/.fonts && \
|
||||
cp web-platform-tests/fonts/Ahem.ttf ~/.fonts && \
|
||||
fc-cache -f -v
|
||||
|
||||
RUN mkdir -p /home/test/artifacts
|
14
tests/wpt/web-platform-tests/tools/docker/start.sh
Executable file
14
tests/wpt/web-platform-tests/tools/docker/start.sh
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd web-platform-tests
|
||||
git pull --depth=1
|
||||
|
||||
sudo sh -c './wpt make-hosts-file >> /etc/hosts'
|
||||
|
||||
# Install Chome dev
|
||||
deb_archive=google-chrome-unstable_current_amd64.deb
|
||||
wget https://dl.google.com/linux/direct/$deb_archive
|
||||
|
||||
sudo gdebi -n $deb_archive
|
||||
|
||||
sudo Xvfb $DISPLAY -screen 0 ${SCREEN_WIDTH}x${SCREEN_HEIGHT}x${SCREEN_DEPTH} &
|
|
@ -18,7 +18,7 @@ from .. import localpaths
|
|||
from ..gitignore.gitignore import PathFilter
|
||||
from ..wpt import testfiles
|
||||
|
||||
from manifest.sourcefile import SourceFile, js_meta_re, python_meta_re, space_chars
|
||||
from manifest.sourcefile import SourceFile, js_meta_re, python_meta_re, space_chars, get_any_variants, get_default_any_variants
|
||||
from six import binary_type, iteritems, itervalues
|
||||
from six.moves import range
|
||||
from six.moves.urllib.parse import urlsplit, urljoin
|
||||
|
@ -616,6 +616,31 @@ broken_js_metadata = re.compile(b"//\s*META:")
|
|||
broken_python_metadata = re.compile(b"#\s*META:")
|
||||
|
||||
|
||||
def check_global_metadata(value):
|
||||
global_values = {item.strip() for item in value.split(b",") if item.strip()}
|
||||
|
||||
included_variants = set.union(get_default_any_variants(),
|
||||
*(get_any_variants(v) for v in global_values if not v.startswith(b"!")))
|
||||
|
||||
for global_value in global_values:
|
||||
if global_value.startswith(b"!"):
|
||||
excluded_value = global_value[1:]
|
||||
if not get_any_variants(excluded_value):
|
||||
yield ("UNKNOWN-GLOBAL-METADATA", "Unexpected value for global metadata")
|
||||
|
||||
elif excluded_value in global_values:
|
||||
yield ("BROKEN-GLOBAL-METADATA", "Cannot specify both %s and %s" % (global_value, excluded_value))
|
||||
|
||||
else:
|
||||
excluded_variants = get_any_variants(excluded_value)
|
||||
if not (excluded_variants & included_variants):
|
||||
yield ("BROKEN-GLOBAL-METADATA", "Cannot exclude %s if it is not included" % (excluded_value,))
|
||||
|
||||
else:
|
||||
if not get_any_variants(global_value):
|
||||
yield ("UNKNOWN-GLOBAL-METADATA", "Unexpected value for global metadata")
|
||||
|
||||
|
||||
def check_script_metadata(repo_root, path, f):
|
||||
if path.endswith((".worker.js", ".any.js")):
|
||||
meta_re = js_meta_re
|
||||
|
@ -634,7 +659,9 @@ def check_script_metadata(repo_root, path, f):
|
|||
m = meta_re.match(line)
|
||||
if m:
|
||||
key, value = m.groups()
|
||||
if key == b"timeout":
|
||||
if key == b"global":
|
||||
errors.extend((kind, message, path, idx + 1) for (kind, message) in check_global_metadata(value))
|
||||
elif key == b"timeout":
|
||||
if value != b"long":
|
||||
errors.append(("UNKNOWN-TIMEOUT-METADATA", "Unexpected value for timeout metadata", path, idx + 1))
|
||||
elif key == b"script":
|
||||
|
|
|
@ -559,6 +559,39 @@ def test_script_metadata(filename, input, error):
|
|||
assert errors == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("globals,error", [
|
||||
(b"", None),
|
||||
(b"default", None),
|
||||
(b"!default", None),
|
||||
(b"window", None),
|
||||
(b"!window", None),
|
||||
(b"!dedicatedworker", None),
|
||||
(b"window, !window", "BROKEN-GLOBAL-METADATA"),
|
||||
(b"!serviceworker", "BROKEN-GLOBAL-METADATA"),
|
||||
(b"serviceworker, !serviceworker", "BROKEN-GLOBAL-METADATA"),
|
||||
(b"worker, !dedicatedworker", None),
|
||||
(b"worker, !serviceworker", None),
|
||||
(b"!worker", None),
|
||||
(b"foo", "UNKNOWN-GLOBAL-METADATA"),
|
||||
(b"!foo", "UNKNOWN-GLOBAL-METADATA"),
|
||||
])
|
||||
def test_script_globals_metadata(globals, error):
|
||||
filename = "foo.any.js"
|
||||
input = b"""// META: global=%s\n""" % globals
|
||||
errors = check_file_contents("", filename, six.BytesIO(input))
|
||||
check_errors(errors)
|
||||
|
||||
if error is not None:
|
||||
errors = [(k, f, l) for (k, _, f, l) in errors]
|
||||
assert errors == [
|
||||
(error,
|
||||
filename,
|
||||
1),
|
||||
]
|
||||
else:
|
||||
assert errors == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("input,error", [
|
||||
(b"""#META: timeout=long\n""", None),
|
||||
(b"""# META: timeout=long\n""", None),
|
||||
|
|
|
@ -49,6 +49,87 @@ def read_script_metadata(f, regexp):
|
|||
yield (m.groups()[0], m.groups()[1])
|
||||
|
||||
|
||||
_any_variants = {
|
||||
b"default": {"longhand": {b"window", b"dedicatedworker"}},
|
||||
b"window": {"suffix": ".any.html"},
|
||||
b"serviceworker": {"force_https": True},
|
||||
b"sharedworker": {},
|
||||
b"dedicatedworker": {"suffix": ".any.worker.html"},
|
||||
b"worker": {"longhand": {b"dedicatedworker", b"sharedworker", b"serviceworker"}}
|
||||
}
|
||||
|
||||
|
||||
def get_any_variants(item):
|
||||
"""
|
||||
Returns a set of variants (bytestrings) defined by the given keyword.
|
||||
"""
|
||||
assert isinstance(item, binary_type), item
|
||||
assert not item.startswith(b"!"), item
|
||||
|
||||
variant = _any_variants.get(item, None)
|
||||
if variant is None:
|
||||
return set()
|
||||
|
||||
return variant.get("longhand", {item})
|
||||
|
||||
|
||||
def get_default_any_variants():
|
||||
"""
|
||||
Returns a set of variants (bytestrings) that will be used by default.
|
||||
"""
|
||||
return set(_any_variants[b"default"]["longhand"])
|
||||
|
||||
|
||||
def parse_variants(value):
|
||||
"""
|
||||
Returns a set of variants (bytestrings) defined by a comma-separated value.
|
||||
"""
|
||||
assert isinstance(value, binary_type), value
|
||||
|
||||
globals = get_default_any_variants()
|
||||
|
||||
for item in value.split(b","):
|
||||
item = item.strip()
|
||||
if item.startswith(b"!"):
|
||||
globals -= get_any_variants(item[1:])
|
||||
else:
|
||||
globals |= get_any_variants(item)
|
||||
|
||||
return globals
|
||||
|
||||
|
||||
def global_suffixes(value):
|
||||
"""
|
||||
Yields the relevant filename suffixes (strings) for the variants defined by
|
||||
the given comma-separated value.
|
||||
"""
|
||||
assert isinstance(value, binary_type), value
|
||||
|
||||
rv = set()
|
||||
|
||||
global_types = parse_variants(value)
|
||||
for global_type in global_types:
|
||||
variant = _any_variants[global_type]
|
||||
suffix = variant.get("suffix", ".any.%s.html" % global_type.decode("utf-8"))
|
||||
if variant.get("force_https", False):
|
||||
suffix = ".https" + suffix
|
||||
rv.add(suffix)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
def global_variant_url(url, suffix):
|
||||
"""
|
||||
Returns a url created from the given url and suffix (all strings).
|
||||
"""
|
||||
url = url.replace(".any.", ".")
|
||||
# If the url must be loaded over https, ensure that it will have
|
||||
# the form .https.any.js
|
||||
if ".https." in url and suffix.startswith(".https."):
|
||||
url = url.replace(".https.", ".")
|
||||
return replace_end(url, ".js", suffix)
|
||||
|
||||
|
||||
class SourceFile(object):
|
||||
parsers = {"html":lambda x:html5lib.parse(x, treebuilder="etree"),
|
||||
"xhtml":lambda x:ElementTree.parse(x, XMLParser.XMLParser()),
|
||||
|
@ -370,11 +451,18 @@ class SourceFile(object):
|
|||
@cached_property
|
||||
def test_variants(self):
|
||||
rv = []
|
||||
for element in self.variant_nodes:
|
||||
if "content" in element.attrib:
|
||||
variant = element.attrib["content"]
|
||||
assert variant == "" or variant[0] in ["#", "?"]
|
||||
rv.append(variant)
|
||||
if self.ext == ".js":
|
||||
for (key, value) in self.script_metadata:
|
||||
if key == b"variant":
|
||||
rv.append(value.decode("utf-8"))
|
||||
else:
|
||||
for element in self.variant_nodes:
|
||||
if "content" in element.attrib:
|
||||
variant = element.attrib["content"]
|
||||
rv.append(variant)
|
||||
|
||||
for variant in rv:
|
||||
assert variant == "" or variant[0] in ["#", "?"], variant
|
||||
|
||||
if not rv:
|
||||
rv = [""]
|
||||
|
@ -510,22 +598,34 @@ class SourceFile(object):
|
|||
rv = VisualTest.item_type, [VisualTest(self, self.url)]
|
||||
|
||||
elif self.name_is_multi_global:
|
||||
rv = TestharnessTest.item_type, [
|
||||
TestharnessTest(self, replace_end(self.url, ".any.js", ".any.html"),
|
||||
timeout=self.timeout),
|
||||
TestharnessTest(self, replace_end(self.url, ".any.js", ".any.worker.html"),
|
||||
timeout=self.timeout),
|
||||
globals = b""
|
||||
for (key, value) in self.script_metadata:
|
||||
if key == b"global":
|
||||
globals = value
|
||||
break
|
||||
|
||||
tests = [
|
||||
TestharnessTest(self, global_variant_url(self.url, suffix) + variant, timeout=self.timeout)
|
||||
for suffix in sorted(global_suffixes(globals))
|
||||
for variant in self.test_variants
|
||||
]
|
||||
rv = TestharnessTest.item_type, tests
|
||||
|
||||
elif self.name_is_worker:
|
||||
rv = (TestharnessTest.item_type,
|
||||
[TestharnessTest(self, replace_end(self.url, ".worker.js", ".worker.html"),
|
||||
timeout=self.timeout)])
|
||||
test_url = replace_end(self.url, ".worker.js", ".worker.html")
|
||||
tests = [
|
||||
TestharnessTest(self, test_url + variant, timeout=self.timeout)
|
||||
for variant in self.test_variants
|
||||
]
|
||||
rv = TestharnessTest.item_type, tests
|
||||
|
||||
elif self.name_is_window:
|
||||
rv = (TestharnessTest.item_type,
|
||||
[TestharnessTest(self, replace_end(self.url, ".window.js", ".window.html"),
|
||||
timeout=self.timeout)])
|
||||
test_url = replace_end(self.url, ".window.js", ".window.html")
|
||||
tests = [
|
||||
TestharnessTest(self, test_url + variant, timeout=self.timeout)
|
||||
for variant in self.test_variants
|
||||
]
|
||||
rv = TestharnessTest.item_type, tests
|
||||
|
||||
elif self.name_is_webdriver:
|
||||
rv = WebdriverSpecTest.item_type, [WebdriverSpecTest(self, self.url,
|
||||
|
|
|
@ -3,6 +3,7 @@ import os
|
|||
import pytest
|
||||
|
||||
from six import BytesIO
|
||||
from ...lint.lint import check_global_metadata
|
||||
from ..sourcefile import SourceFile, read_script_metadata, js_meta_re, python_meta_re
|
||||
|
||||
def create(filename, contents=b""):
|
||||
|
@ -201,6 +202,66 @@ test()"""
|
|||
assert item.timeout == "long"
|
||||
|
||||
|
||||
def test_worker_with_variants():
|
||||
contents = b"""// META: variant=
|
||||
// META: variant=?wss
|
||||
test()"""
|
||||
|
||||
s = create("html/test.worker.js", contents=contents)
|
||||
assert not s.name_is_non_test
|
||||
assert not s.name_is_manual
|
||||
assert not s.name_is_visual
|
||||
assert not s.name_is_multi_global
|
||||
assert s.name_is_worker
|
||||
assert not s.name_is_window
|
||||
assert not s.name_is_reference
|
||||
|
||||
assert not s.content_is_testharness
|
||||
|
||||
item_type, items = s.manifest_items()
|
||||
assert item_type == "testharness"
|
||||
|
||||
expected_urls = [
|
||||
"/html/test.worker.html" + suffix
|
||||
for suffix in ["", "?wss"]
|
||||
]
|
||||
assert len(items) == len(expected_urls)
|
||||
|
||||
for item, url in zip(items, expected_urls):
|
||||
assert item.url == url
|
||||
assert item.timeout is None
|
||||
|
||||
|
||||
def test_window_with_variants():
|
||||
contents = b"""// META: variant=
|
||||
// META: variant=?wss
|
||||
test()"""
|
||||
|
||||
s = create("html/test.window.js", contents=contents)
|
||||
assert not s.name_is_non_test
|
||||
assert not s.name_is_manual
|
||||
assert not s.name_is_visual
|
||||
assert not s.name_is_multi_global
|
||||
assert not s.name_is_worker
|
||||
assert s.name_is_window
|
||||
assert not s.name_is_reference
|
||||
|
||||
assert not s.content_is_testharness
|
||||
|
||||
item_type, items = s.manifest_items()
|
||||
assert item_type == "testharness"
|
||||
|
||||
expected_urls = [
|
||||
"/html/test.window.html" + suffix
|
||||
for suffix in ["", "?wss"]
|
||||
]
|
||||
assert len(items) == len(expected_urls)
|
||||
|
||||
for item, url in zip(items, expected_urls):
|
||||
assert item.url == url
|
||||
assert item.timeout is None
|
||||
|
||||
|
||||
def test_python_long_timeout():
|
||||
contents = b"""# META: timeout=long
|
||||
|
||||
|
@ -263,6 +324,99 @@ test()"""
|
|||
assert item.timeout == "long"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("input,expected", [
|
||||
(b"", {"dedicatedworker", "window"}),
|
||||
(b"default", {"dedicatedworker", "window"}),
|
||||
(b"!default", {}),
|
||||
(b"!default,window", {"window"}),
|
||||
(b"window,!default", {}),
|
||||
(b"!default,dedicatedworker", {"dedicatedworker"}),
|
||||
(b"dedicatedworker,!default", {}),
|
||||
(b"!default,worker", {"dedicatedworker", "serviceworker", "sharedworker"}),
|
||||
(b"worker,!default", {"serviceworker", "sharedworker"}),
|
||||
(b"!dedicatedworker", {"window"}),
|
||||
(b"!worker", {"window"}),
|
||||
(b"!window", {"dedicatedworker"}),
|
||||
(b"!window,worker", {"dedicatedworker", "serviceworker", "sharedworker"}),
|
||||
(b"worker,!dedicatedworker", {"serviceworker", "sharedworker", "window"}),
|
||||
(b"!dedicatedworker,worker", {"dedicatedworker", "serviceworker", "sharedworker", "window"}),
|
||||
(b"worker,!sharedworker", {"dedicatedworker", "serviceworker", "window"}),
|
||||
(b"!sharedworker,worker", {"dedicatedworker", "serviceworker", "sharedworker", "window"}),
|
||||
(b"sharedworker", {"dedicatedworker", "sharedworker", "window"}),
|
||||
(b"sharedworker,serviceworker", {"dedicatedworker", "serviceworker", "sharedworker", "window"}),
|
||||
])
|
||||
def test_multi_global_with_custom_globals(input, expected):
|
||||
contents = b"""// META: global=%s
|
||||
test()""" % input
|
||||
|
||||
assert list(check_global_metadata(input)) == []
|
||||
|
||||
s = create("html/test.any.js", contents=contents)
|
||||
assert not s.name_is_non_test
|
||||
assert not s.name_is_manual
|
||||
assert not s.name_is_visual
|
||||
assert s.name_is_multi_global
|
||||
assert not s.name_is_worker
|
||||
assert not s.name_is_reference
|
||||
|
||||
assert not s.content_is_testharness
|
||||
|
||||
item_type, items = s.manifest_items()
|
||||
assert item_type == "testharness"
|
||||
|
||||
urls = {
|
||||
"dedicatedworker": "/html/test.any.worker.html",
|
||||
"serviceworker": "/html/test.https.any.serviceworker.html",
|
||||
"sharedworker": "/html/test.any.sharedworker.html",
|
||||
"window": "/html/test.any.html",
|
||||
}
|
||||
|
||||
expected_urls = sorted(urls[ty] for ty in expected)
|
||||
assert len(items) == len(expected_urls)
|
||||
|
||||
for item, url in zip(items, expected_urls):
|
||||
assert item.url == url
|
||||
assert item.timeout is None
|
||||
|
||||
|
||||
def test_multi_global_with_variants():
|
||||
contents = b"""// META: global=window,worker
|
||||
// META: variant=
|
||||
// META: variant=?wss
|
||||
test()"""
|
||||
|
||||
s = create("html/test.any.js", contents=contents)
|
||||
assert not s.name_is_non_test
|
||||
assert not s.name_is_manual
|
||||
assert not s.name_is_visual
|
||||
assert s.name_is_multi_global
|
||||
assert not s.name_is_worker
|
||||
assert not s.name_is_reference
|
||||
|
||||
assert not s.content_is_testharness
|
||||
|
||||
item_type, items = s.manifest_items()
|
||||
assert item_type == "testharness"
|
||||
|
||||
urls = {
|
||||
"dedicatedworker": "/html/test.any.worker.html",
|
||||
"serviceworker": "/html/test.https.any.serviceworker.html",
|
||||
"sharedworker": "/html/test.any.sharedworker.html",
|
||||
"window": "/html/test.any.html",
|
||||
}
|
||||
|
||||
expected_urls = sorted(
|
||||
urls[ty] + suffix
|
||||
for ty in ["dedicatedworker", "serviceworker", "sharedworker", "window"]
|
||||
for suffix in ["", "?wss"]
|
||||
)
|
||||
assert len(items) == len(expected_urls)
|
||||
|
||||
for item, url in zip(items, expected_urls):
|
||||
assert item.url == url
|
||||
assert item.timeout is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("input,expected", [
|
||||
(b"""//META: foo=bar\n""", [(b"foo", b"bar")]),
|
||||
(b"""// META: foo=bar\n""", [(b"foo", b"bar")]),
|
||||
|
|
|
@ -20,13 +20,13 @@ from multiprocessing import Process, Event
|
|||
from localpaths import repo_root
|
||||
|
||||
import sslutils
|
||||
from manifest.sourcefile import read_script_metadata, js_meta_re
|
||||
from manifest.sourcefile import read_script_metadata, js_meta_re, parse_variants
|
||||
from wptserve import server as wptserve, handlers
|
||||
from wptserve import stash
|
||||
from wptserve import config
|
||||
from wptserve.logger import set_logger
|
||||
from wptserve.handlers import filesystem_path, wrap_pipeline
|
||||
from wptserve.utils import get_port
|
||||
from wptserve.utils import get_port, HTTPException
|
||||
from mod_pywebsocket import standalone as pywebsocket
|
||||
|
||||
def replace_end(s, old, new):
|
||||
|
@ -56,9 +56,14 @@ class WrapperHandler(object):
|
|||
for header_name, header_value in self.headers:
|
||||
response.headers.set(header_name, header_value)
|
||||
|
||||
self.check_exposure(request)
|
||||
|
||||
path = self._get_path(request.url_parts.path, True)
|
||||
query = request.url_parts.query
|
||||
if query:
|
||||
query = "?" + query
|
||||
meta = "\n".join(self._get_meta(request))
|
||||
response.content = self.wrapper % {"meta": meta, "path": path}
|
||||
response.content = self.wrapper % {"meta": meta, "path": path, "query": query}
|
||||
wrap_pipeline(path, request, response)
|
||||
|
||||
def _get_path(self, path, resource_path):
|
||||
|
@ -85,18 +90,27 @@ class WrapperHandler(object):
|
|||
path = replace_end(path, src, dest)
|
||||
return path
|
||||
|
||||
def _get_meta(self, request):
|
||||
"""Get an iterator over strings to inject into the wrapper document
|
||||
based on //META comments in the associated js file.
|
||||
def _get_metadata(self, request):
|
||||
"""Get an iterator over script metadata based on //META comments in the
|
||||
associated js file.
|
||||
|
||||
:param request: The Request being processed.
|
||||
"""
|
||||
path = self._get_path(filesystem_path(self.base_path, request, self.url_base), False)
|
||||
with open(path, "rb") as f:
|
||||
for key, value in read_script_metadata(f, js_meta_re):
|
||||
replacement = self._meta_replacement(key, value)
|
||||
if replacement:
|
||||
yield replacement
|
||||
yield key, value
|
||||
|
||||
def _get_meta(self, request):
|
||||
"""Get an iterator over strings to inject into the wrapper document
|
||||
based on //META comments in the associated js file.
|
||||
|
||||
:param request: The Request being processed.
|
||||
"""
|
||||
for key, value in self._get_metadata(request):
|
||||
replacement = self._meta_replacement(key, value)
|
||||
if replacement:
|
||||
yield replacement
|
||||
|
||||
@abc.abstractproperty
|
||||
def path_replace(self):
|
||||
|
@ -118,8 +132,27 @@ class WrapperHandler(object):
|
|||
# a specific metadata key: value pair.
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def check_exposure(self, request):
|
||||
# Raise an exception if this handler shouldn't be exposed after all.
|
||||
pass
|
||||
|
||||
|
||||
class HtmlWrapperHandler(WrapperHandler):
|
||||
global_type = None
|
||||
|
||||
def check_exposure(self, request):
|
||||
if self.global_type:
|
||||
globals = b""
|
||||
for (key, value) in self._get_metadata(request):
|
||||
if key == b"global":
|
||||
globals = value
|
||||
break
|
||||
|
||||
if self.global_type not in parse_variants(globals):
|
||||
raise HTTPException(404, "This test cannot be loaded in %s mode" %
|
||||
self.global_type)
|
||||
|
||||
def _meta_replacement(self, key, value):
|
||||
if key == b"timeout":
|
||||
if value == b"long":
|
||||
|
@ -131,6 +164,7 @@ class HtmlWrapperHandler(WrapperHandler):
|
|||
|
||||
|
||||
class WorkersHandler(HtmlWrapperHandler):
|
||||
global_type = b"dedicatedworker"
|
||||
path_replace = [(".any.worker.html", ".any.js", ".any.worker.js"),
|
||||
(".worker.html", ".worker.js")]
|
||||
wrapper = """<!doctype html>
|
||||
|
@ -140,7 +174,7 @@ class WorkersHandler(HtmlWrapperHandler):
|
|||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id=log></div>
|
||||
<script>
|
||||
fetch_tests_from_worker(new Worker("%(path)s"));
|
||||
fetch_tests_from_worker(new Worker("%(path)s%(query)s"));
|
||||
</script>
|
||||
"""
|
||||
|
||||
|
@ -158,6 +192,7 @@ class WindowHandler(HtmlWrapperHandler):
|
|||
|
||||
|
||||
class AnyHtmlHandler(HtmlWrapperHandler):
|
||||
global_type = b"window"
|
||||
path_replace = [(".any.html", ".any.js")]
|
||||
wrapper = """<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
|
@ -175,6 +210,42 @@ self.GLOBAL = {
|
|||
"""
|
||||
|
||||
|
||||
class SharedWorkersHandler(HtmlWrapperHandler):
|
||||
global_type = b"sharedworker"
|
||||
path_replace = [(".any.sharedworker.html", ".any.js", ".any.worker.js")]
|
||||
wrapper = """<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
%(meta)s
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id=log></div>
|
||||
<script>
|
||||
fetch_tests_from_worker(new SharedWorker("%(path)s%(query)s"));
|
||||
</script>
|
||||
"""
|
||||
|
||||
|
||||
class ServiceWorkersHandler(HtmlWrapperHandler):
|
||||
global_type = b"serviceworker"
|
||||
path_replace = [(".https.any.serviceworker.html", ".any.js", ".any.worker.js")]
|
||||
wrapper = """<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
%(meta)s
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id=log></div>
|
||||
<script>
|
||||
(async function() {
|
||||
const scope = 'does/not/exist';
|
||||
let reg = await navigator.serviceWorker.getRegistration(scope);
|
||||
if (reg) await reg.unregister();
|
||||
reg = await navigator.serviceWorker.register("%(path)s%(query)s", {scope});
|
||||
fetch_tests_from_worker(reg.installing);
|
||||
})();
|
||||
</script>
|
||||
"""
|
||||
|
||||
|
||||
class AnyWorkerHandler(WrapperHandler):
|
||||
headers = [('Content-Type', 'text/javascript')]
|
||||
path_replace = [(".any.worker.js", ".any.js")]
|
||||
|
@ -189,8 +260,6 @@ done();
|
|||
"""
|
||||
|
||||
def _meta_replacement(self, key, value):
|
||||
if key == b"timeout":
|
||||
return None
|
||||
if key == b"script":
|
||||
attribute = value.decode('utf-8').replace("\\", "\\\\").replace('"', '\\"')
|
||||
return 'importScripts("%s")' % attribute
|
||||
|
@ -243,6 +312,8 @@ class RoutesBuilder(object):
|
|||
("GET", "*.worker.html", WorkersHandler),
|
||||
("GET", "*.window.html", WindowHandler),
|
||||
("GET", "*.any.html", AnyHtmlHandler),
|
||||
("GET", "*.any.sharedworker.html", SharedWorkersHandler),
|
||||
("GET", "*.https.any.serviceworker.html", ServiceWorkersHandler),
|
||||
("GET", "*.any.worker.js", AnyWorkerHandler),
|
||||
("GET", "*.asis", handlers.AsIsHandler),
|
||||
("*", "*.py", handlers.PythonScriptHandler),
|
||||
|
|
|
@ -457,7 +457,8 @@ def run(venv, **kwargs):
|
|||
|
||||
def run_single(venv, **kwargs):
|
||||
from wptrunner import wptrunner
|
||||
return wptrunner.start(**kwargs)
|
||||
wptrunner.start(**kwargs)
|
||||
return
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
@ -153,10 +153,10 @@ class TestExecutor(object):
|
|||
:param test: The test to run"""
|
||||
if test.environment != self.last_environment:
|
||||
self.on_environment_change(test.environment)
|
||||
|
||||
try:
|
||||
result = self.do_test(test)
|
||||
except Exception as e:
|
||||
self.logger.warning(traceback.format_exc(e))
|
||||
result = self.result_from_exception(test, e)
|
||||
|
||||
if result is Stop:
|
||||
|
@ -547,6 +547,7 @@ class CallbackHandler(object):
|
|||
def _send_message(self, message_type, status, message=None):
|
||||
self.protocol.testdriver.send_message(message_type, status, message=message)
|
||||
|
||||
|
||||
class ClickAction(object):
|
||||
def __init__(self, logger, protocol):
|
||||
self.logger = logger
|
||||
|
@ -562,6 +563,7 @@ class ClickAction(object):
|
|||
self.logger.debug("Clicking element: %s" % selector)
|
||||
self.protocol.click.element(elements[0])
|
||||
|
||||
|
||||
class SendKeysAction(object):
|
||||
def __init__(self, logger, protocol):
|
||||
self.logger = logger
|
||||
|
|
|
@ -475,7 +475,8 @@ class ExecuteAsyncScriptRun(object):
|
|||
if message:
|
||||
message += "\n"
|
||||
message += traceback.format_exc(e)
|
||||
self.result = False, ("INTERNAL-ERROR", e)
|
||||
self.logger.warning(message)
|
||||
self.result = False, ("INTERNAL-ERROR", None)
|
||||
|
||||
finally:
|
||||
self.result_flag.set()
|
||||
|
@ -551,6 +552,9 @@ class MarionetteTestharnessExecutor(TestharnessExecutor):
|
|||
while True:
|
||||
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
|
||||
|
|
|
@ -594,7 +594,8 @@ class TestRunnerManager(threading.Thread):
|
|||
((subtest_unexpected or is_unexpected) and
|
||||
self.restart_on_unexpected))
|
||||
|
||||
if (self.pause_after_test or
|
||||
if (not file_result.status == "CRASH" and
|
||||
self.pause_after_test or
|
||||
(self.pause_on_unexpected and (subtest_unexpected or is_unexpected))):
|
||||
self.logger.info("Pausing until the browser exits")
|
||||
self.send_message("wait")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue