refactor(wpt): unify fixture dispatch and preserve raw response headers

Reconcile the shared HTTP handlers and discovery rules from the split PRs with the complete fixture surface in wpt-misc-fix. Share stash namespaces and XHR dispatch, retain the existing testdriver bridge, preserve raw header values, and carry forward the remaining pipe, template, and dynamic-import regressions.

Validation: 742 Python tests across 74 modules pass; focused Ruff checks and git diff --check pass. With fixed Moli and Chromium binaries, a 27-case WPT comparison against main improves whole-case passes from 7 to 26 and 6 to 23 respectively, with no previously passing case regressing.
This commit is contained in:
ldm0
2026-09-29 12:43:28 +08:00
parent 52a6a52cd1
commit c429911cdf
8 changed files with 848 additions and 833 deletions
@@ -751,6 +751,37 @@ def _common_echo_handler_reference_patterns(directory: str) -> tuple[re.Pattern[
)
@lru_cache(maxsize=None)
def _remote_context_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
references = []
for resource in (
"common/dispatcher/dispatcher.py",
"html/browsers/browsing-the-web/remote-context-helper/resources/executor-window.py",
):
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in references
)
@lru_cache(maxsize=None)
def _iframe_stash_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
resource = "html/semantics/embedded-content/the-iframe-element/stash.py"
relative = posixpath.relpath(resource, directory)
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in ("/" + resource, relative, "./" + relative)
)
@lru_cache(maxsize=None)
def _script_content_type_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
resource = "html/semantics/scripting-1/the-script-element/serve-with-content-type.py"
@@ -764,6 +795,68 @@ def _script_content_type_handler_reference_patterns(directory: str) -> tuple[re.
)
@lru_cache(maxsize=None)
def _document_charset_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
references = []
for name in ("bogus-charset-http.py", "bogus-charset-http-valid-meta.py"):
resource = f"html/syntax/charset/resources/{name}"
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in references
)
@lru_cache(maxsize=None)
def _fetch_content_type_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
resource = "fetch/content-type/resources/content-type.py"
relative = posixpath.relpath(resource, directory)
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in ("/" + resource, relative, "./" + relative)
)
@lru_cache(maxsize=None)
def _fetch_resource_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
names = (
"preflight.py", "clean-stash.py", "inspect-headers.py", "redirect.py",
"redirect-empty-location.py",
)
references = []
for name in names:
resource = "fetch/api/resources/" + name
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
patterns = [
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in references
]
# The Fetch API's shared utils.js defines RESOURCES_DIR. Keep the
# concatenation intact in this match; a bare filename is not sufficient.
patterns.append(re.compile(
r"(?<![\w$.])RESOURCES_DIR\s*\+\s*['\"](?:"
+ "|".join(re.escape(name) for name in names)
+ rf"){WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
))
patterns.append(re.compile(
r"`\$\{\s*RESOURCES_DIR\s*\}(?:"
+ "|".join(re.escape(name) for name in names)
+ rf"){WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
))
return tuple(patterns)
@lru_cache(maxsize=None)
def _navigation_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
resource = (
@@ -829,28 +922,17 @@ def _empty_location_handler_reference_patterns(directory: str) -> tuple[re.Patte
@lru_cache(maxsize=None)
def _remote_context_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
def _xhr_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
references = []
for resource in (
"common/dispatcher/dispatcher.py",
"html/browsers/browsing-the-web/remote-context-helper/resources/executor-window.py",
for name in (
"requri.py", "redirect.py", "headers.py", "inspect-headers.py", "echo-headers.py", "content.py",
"echo-content-type.py", "corsenabled.py", "win-1252-xml.py", "win-1252-html.py",
"access-control-basic-put-allow.py",
"access-control-preflight-request-allow-headers-returns-star.py",
"invalid-utf8-html.py", "shift-jis-html.py", "img-utf8-html.py",
"empty-div-utf8-html.py", "status.py", "last-modified.py",
"bad-chunk-encoding.py", "infinite-redirects.py",
):
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in references
)
@lru_cache(maxsize=None)
def _xhr_url_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
references = []
for name in ("requri.py", "redirect.py"):
resource = f"xhr/resources/{name}"
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
@@ -863,38 +945,82 @@ def _xhr_url_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str]
)
@lru_cache(maxsize=None)
def _script_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
references = []
for name in (
"serve-with-content-type.py", "resources/load-error-events.py",
"json-module/load-error-events.py", "serve-json-then-js.py",
):
resource = "html/semantics/scripting-1/the-script-element/" + name
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in references
)
@lru_cache(maxsize=None)
def _service_worker_script_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
references = []
resources = tuple(
"service-workers/service-worker/resources/" + name
for name in (
"redirect.py", "update-worker.py", "update-worker-from-file.py",
"mime-type-worker.py", "import-mime-type-worker.py", "malformed-worker.py",
"invalid-chunked-encoding.py", "invalid-chunked-encoding-with-flush.py",
"update-during-installation-worker.py",
"import-scripts-version.py", "import-scripts-get.py", "import-scripts-echo.py",
"subdir/import-scripts-echo.py", "scope2/import-scripts-echo.py",
)
) + ("service-workers/service-worker/ServiceWorkerGlobalScope/resources/update-worker.py",)
for resource in resources:
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in references
)
def _supported_wptserve_handler_references(
rel: str | None,
) -> tuple[re.Pattern[str], ...]:
supported: tuple[re.Pattern[str], ...] = ()
if rel is not None:
supported += _service_worker_script_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _fetch_resource_handler_reference_patterns(posixpath.dirname(rel) or ".")
if rel is not None and rel.startswith("fetch/api/"):
supported += _empty_location_handler_reference_patterns(posixpath.dirname(rel))
if rel is not None:
supported += _common_echo_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _remote_context_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _fetch_content_type_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _document_charset_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _iframe_stash_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _remote_context_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _preload_count_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _navigation_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _json_module_handler_reference_patterns(posixpath.dirname(rel) or ".")
if rel is not None:
supported += _script_content_type_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _script_handler_reference_patterns(posixpath.dirname(rel) or ".")
supported += _service_worker_script_handler_reference_patterns(posixpath.dirname(rel) or ".")
if rel is not None and rel.rsplit("/", 1)[0] == "fetch/api/abort":
supported += SUPPORTED_FETCH_ABORT_WPTSERVE_HANDLER_PATTERNS
if rel is not None and rel.rsplit("/", 1)[0] == "fetch/range":
supported += SUPPORTED_FETCH_RANGE_WPTSERVE_HANDLER_PATTERNS
if rel is not None and rel.startswith("fetch/api/"):
supported += _fetch_resource_handler_reference_patterns(posixpath.dirname(rel))
if rel is not None and rel.startswith("wasm/webapi/"):
supported += SUPPORTED_WASM_WEBAPI_WPTSERVE_HANDLER_PATTERNS
if rel is not None:
supported += _script_load_error_handler_reference_patterns(posixpath.dirname(rel) or ".")
if rel is not None and rel.startswith("xhr/"):
supported += SUPPORTED_XHR_DELAY_WPTSERVE_HANDLER_PATTERNS
supported += _xhr_url_handler_reference_patterns(rel.rsplit("/", 1)[0])
supported += _xhr_handler_reference_patterns(rel.rsplit("/", 1)[0])
if rel is not None and rel.startswith(
"html/semantics/scripting-1/the-script-element/module/"
):
@@ -911,9 +1037,7 @@ def _xhr_response_handler_reference_patterns(directory: str) -> tuple[re.Pattern
"inspect-headers.py", "echo-headers.py", "content.py",
"echo-content-type.py", "win-1252-xml.py", "win-1252-html.py",
"invalid-utf8-html.py", "shift-jis-html.py", "img-utf8-html.py",
"empty-div-utf8-html.py", "headers.py", "bad-chunk-encoding.py", "infinite-redirects.py",
"access-control-basic-put-allow.py",
"access-control-preflight-request-allow-headers-returns-star.py"):
"empty-div-utf8-html.py"):
resource = f"xhr/resources/{name}"
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
@@ -1670,109 +1794,3 @@ def explicit_case(wpt_root: Path, case_path: str) -> WptCase:
timeout_multiplier=timeout_multiplier,
test_type="crashtest" if _is_crashtest_path(source_case_path) else "testharness",
)
@lru_cache(maxsize=None)
def _service_worker_script_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
references = []
resources = tuple(
"service-workers/service-worker/resources/" + name
for name in (
"redirect.py", "update-worker.py", "update-worker-from-file.py",
"mime-type-worker.py", "import-mime-type-worker.py", "malformed-worker.py",
"invalid-chunked-encoding.py", "invalid-chunked-encoding-with-flush.py",
"update-during-installation-worker.py",
"import-scripts-version.py", "import-scripts-get.py", "import-scripts-echo.py",
"subdir/import-scripts-echo.py", "scope2/import-scripts-echo.py",
)
) + ("service-workers/service-worker/ServiceWorkerGlobalScope/resources/update-worker.py",)
for resource in resources:
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in references
)
@lru_cache(maxsize=None)
def _fetch_resource_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
names = (
"preflight.py", "clean-stash.py", "inspect-headers.py", "redirect.py",
"redirect-empty-location.py",
)
references = []
for name in names:
resource = "fetch/api/resources/" + name
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
patterns = [
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in references
]
# The Fetch API's shared utils.js defines RESOURCES_DIR. Keep the
# concatenation intact in this match; a bare filename is not sufficient.
patterns.append(re.compile(
r"(?<![\w$.])RESOURCES_DIR\s*\+\s*['\"](?:"
+ "|".join(re.escape(name) for name in names)
+ rf"){WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
))
patterns.append(re.compile(
r"`\$\{\s*RESOURCES_DIR\s*\}(?:"
+ "|".join(re.escape(name) for name in names)
+ rf"){WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
))
return tuple(patterns)
@lru_cache(maxsize=None)
def _fetch_content_type_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
resource = "fetch/content-type/resources/content-type.py"
relative = posixpath.relpath(resource, directory)
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in ("/" + resource, relative, "./" + relative)
)
@lru_cache(maxsize=None)
def _document_charset_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
references = []
for name in ("bogus-charset-http.py", "bogus-charset-http-valid-meta.py"):
resource = f"html/syntax/charset/resources/{name}"
relative = posixpath.relpath(resource, directory)
references.extend(("/" + resource, relative, "./" + relative))
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in references
)
@lru_cache(maxsize=None)
def _iframe_stash_handler_reference_patterns(directory: str) -> tuple[re.Pattern[str], ...]:
resource = "html/semantics/embedded-content/the-iframe-element/stash.py"
relative = posixpath.relpath(resource, directory)
return tuple(
re.compile(
rf"(?<![A-Za-z0-9_./-]){re.escape(reference)}"
rf"{WPTSERVE_HANDLER_TRAILING_BOUNDARY}"
)
for reference in ("/" + resource, relative, "./" + relative)
)
File diff suppressed because it is too large Load Diff
@@ -40,6 +40,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
reports[0]["metadata"]["content_type"],
"application/csp-report",
)
def test_fixture_server_accepts_standard_websocket_echo_endpoint(self) -> None:
with tempfile.TemporaryDirectory() as root:
root_path = Path(root)
@@ -80,6 +81,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
while len(echoed) < 6:
echoed += connection.recv(6 - len(echoed))
self.assertEqual(echoed, b"\x81\x04ping")
def test_fixture_server_serves_raw_asis_header_bytes(self) -> None:
with tempfile.TemporaryDirectory() as root:
root_path = Path(root)
@@ -101,6 +103,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
header = response.headers["X-Custom-Header-Bytes"]
self.assertEqual(header.encode("latin-1"), b"\xe2\x80\xa6")
def test_fixture_server_serves_raw_sidecar_header_bytes(self) -> None:
with tempfile.TemporaryDirectory() as root:
root_path = Path(root)
@@ -122,6 +125,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
header = response.headers["Content-Type"]
self.assertEqual(header.encode("latin-1"), b"\xc3\x97\xc2\xba invalid")
def test_fixture_server_uses_explicit_content_type_header_as_override(self) -> None:
self.assertEqual(
_static_response_header_block(
@@ -134,6 +138,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
_static_response_header_block("application/javascript", [("X-Test", "ok")]),
[("Content-Type", "application/javascript"), ("X-Test", "ok")],
)
def test_fixture_server_detects_explicit_content_length_header(self) -> None:
header_block = _static_response_header_block(
"text/html",
@@ -141,6 +146,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
)
self.assertTrue(_headers_include(header_block, "content-length"))
def test_fixture_server_preserves_explicit_content_length_body_boundary(self) -> None:
with tempfile.TemporaryDirectory() as root:
root_path = Path(root)
@@ -172,12 +178,14 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
self.assertEqual(served, body)
self.assertNotIn(BENCH_TIMEOUT_MULTIPLIER_QUERY.encode("ascii"), served)
def test_fixture_server_maps_legacy_webidl_parser_resource(self) -> None:
self.assertEqual(
_legacy_wpt_resource_alias("/resources/WebIDLParser.js"),
"resources/webidl2/lib/webidl2.js",
)
self.assertIsNone(_legacy_wpt_resource_alias("/resources/testharness.js"))
def test_fixture_server_builds_any_js_window_wrapper(self) -> None:
body = _any_js_window_wrapper(
"/WebCryptoAPI/sign_verify/hmac.https.any.html",
@@ -197,6 +205,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
self.assertIn(b'<script src="hmac_vectors.js"></script>', body)
self.assertIn(b'<div id="log"></div>', body)
self.assertIn(b'<script src="hmac.https.any.js"></script>', body)
def test_fixture_server_builds_window_js_wrapper_without_any_global(self) -> None:
body = _window_js_window_wrapper(
"/WebCryptoAPI/algorithm-discards-context.https.window.html",
@@ -211,6 +220,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
self.assertIn(b'<script src="helper.js"></script>', body)
self.assertIn(b'<script src="algorithm-discards-context.https.window.js"></script>', body)
self.assertNotIn(b"self.GLOBAL", body)
def test_fixture_server_builds_wpt_any_js_query_wrapper(self) -> None:
html = _wpt_any_window_wrapper_html(
"wasm/jsapi/feature.any.js",
@@ -227,6 +237,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
html,
)
self.assertNotIn("moli-wpt-any", html)
def test_fixture_server_builds_any_js_dedicated_worker_wrapper(self) -> None:
html = _wpt_any_dedicated_worker_wrapper_html(
"wasm/jsapi/feature.any.js",
@@ -240,6 +251,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
html,
)
self.assertNotIn("moli-wpt-any", html)
def test_fixture_server_builds_any_js_dedicated_worker_script(self) -> None:
js = _wpt_any_dedicated_worker_wrapper_js(
"wasm/jsapi/feature.any.js",
@@ -255,6 +267,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
self.assertIn('importScripts("/wasm/jsapi/feature.any.js?variant=1");', js)
self.assertTrue(js.rstrip().endswith("done();"))
self.assertNotIn("moli-wpt-any", js)
def test_fixture_server_builds_window_js_wrapper(self) -> None:
html = _wpt_window_js_wrapper_html(
"wasm/serialization/transfer.window.js",
@@ -271,6 +284,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
html,
)
self.assertNotIn("moli-wpt-script", html)
def test_fixture_server_builds_worker_js_wrapper(self) -> None:
html = _wpt_dedicated_worker_js_wrapper_html(
"wasm/create_multiple_memory.worker.js",
@@ -284,6 +298,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
html,
)
self.assertNotIn("moli-wpt-script", html)
def test_any_js_worker_script_path_round_trips_to_source_path(self) -> None:
worker_path = any_js_worker_script_path(
"wasm/jsapi/feature.any.js?variant=1"
@@ -297,6 +312,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
any_js_source_script_path(worker_path),
"wasm/jsapi/feature.any.js?variant=1",
)
def test_any_js_case_path_for_global_replaces_existing_wrapper_query(self) -> None:
self.assertEqual(
any_js_case_path_for_global(
@@ -305,6 +321,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
),
"wasm/jsapi/feature.any.js?variant=1&moli-wpt-any=dedicatedworker",
)
def test_fixture_server_resolves_any_js_meta_scripts_within_wpt_root(self) -> None:
self.assertEqual(
_resolve_wpt_static_script_url(
@@ -326,6 +343,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
"../../../escape.js",
)
)
def test_fixture_server_substitutes_core_sub_template_variables(self) -> None:
body = (
b"http://{{domains[www2]}}:{{ports[http][0]}}/"
@@ -372,6 +390,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
b" ws-url=ws://example.test:12345/socket"
b" wss-url=ws://example.test:12345/socket",
)
def test_fixture_server_substitutes_idna_domain_aliases(self) -> None:
cases = [
("{{domains[天気の良い日]}}", b"xn--n8j6ds53lwwkrqhv28a.localhost"),
@@ -455,6 +474,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
"",
)
)
def test_fixture_server_maps_external_ipv6_domain_location_port_to_remote_port(
self,
) -> None:
@@ -480,6 +500,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
b" http://[2001:db8::1]:34567/d"
b" http://[2001:db8::1]:23456/e",
)
def test_fixture_server_substitutes_get_query_template_variables(self) -> None:
body = (
b"var expected_logs = {{GET[logs]}};"
@@ -497,6 +518,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
b' var timeout = "2";'
b' var missing = "";',
)
def test_fixture_server_substitutes_template_variables_in_sidecar_headers(self) -> None:
with tempfile.TemporaryDirectory() as root:
fixture = Path(root) / "frame-ancestors.sub.html"
@@ -521,6 +543,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
)
],
)
def test_fixture_server_substitution_preserves_non_utf8_bytes(self) -> None:
body = b"\xff{{host}}\xfe{{ports[http][0]}}"
@@ -528,6 +551,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
_substitute_wpt_template_variables(body, port=12345),
b"\xfflocalhost\xfe12345",
)
def test_fixture_server_distinguishes_primary_host_from_request_hostname(
self,
) -> None:
@@ -547,10 +571,12 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
b"host=localhost domain=localhost location=www1.localhost "
b"HTTP_ORIGIN: 'http://' + ORIGINAL_HOST + HTTP_PORT_ELIDED,",
)
def test_host_header_hostname_preserves_ipv6_brackets(self) -> None:
self.assertEqual(_host_header_hostname("[2001:db8::1]:1234"), "[2001:db8::1]")
self.assertEqual(_host_header_hostname("localhost:1234"), "localhost")
self.assertEqual(_host_header_hostname("example.test"), "example.test")
def test_fixture_server_substitutes_ipv6_websocket_template_urls(self) -> None:
body = (
b"ws=ws://{{host}}:{{ports[ws][0]}}/echo"
@@ -566,6 +592,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
b"ws=ws://[2001:db8::1]:12345/echo"
b" wss=ws://[2001:db8::1]:12345/echo",
)
def test_fixture_server_wasm_status_handler_normalizes_status_codes(self) -> None:
self.assertEqual(_wasm_webapi_status_code("status=404"), 404)
self.assertEqual(_wasm_webapi_status_code("status=300"), 300)
@@ -573,6 +600,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
self.assertEqual(_wasm_webapi_status_code("status=700"), 599)
self.assertEqual(_wasm_webapi_status_code("status=not-a-number"), 400)
self.assertEqual(_wasm_webapi_status_code(""), 200)
def test_fixture_server_redirect_handler_models_wasm_origin_probe(self) -> None:
self.assertEqual(
_redirect_fixture_response(
@@ -589,17 +617,20 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
_redirect_fixture_response("status=307&location=/target"),
(307, "/target"),
)
def test_fixture_server_models_csp_resource_py(self) -> None:
body, headers = _content_security_policy_resource_response()
self.assertIn(b"success", body)
self.assertIn(("Access-Control-Allow-Origin", "*"), headers)
def test_fixture_server_models_workers_modules_export_on_load_script_py(self) -> None:
body, headers = _workers_modules_export_on_load_script_response()
self.assertIn(b"export const importedModules", body)
self.assertIn(("Content-Type", "text/javascript"), headers)
self.assertIn(("Access-Control-Allow-Origin", "*"), headers)
def test_fixture_server_preserves_raw_sidecar_header_bytes(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
@@ -614,6 +645,7 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
self.assertEqual(headers, [("Content-Type", "\xc3\x97\xc2\xba invalid")])
for _name, value in headers:
value.encode("latin-1")
def test_fixture_server_substitutes_get_host_info_remote_host_for_loopback(self) -> None:
body = (
b"var REMOTE_HOST = (ORIGINAL_HOST === 'localhost') ? "
@@ -724,4 +756,21 @@ class WptCrossFixtureResourcesTests(WptCrossTestCase):
],
)
def test_fixture_server_substitutes_request_header_or_default(self) -> None:
body = (
b"present={{header_or_default(Referer, missing)}}"
b" absent={{header_or_default(X-Absent, <missing>)}}"
b" empty={{header_or_default(X-Empty, )}}"
)
self.assertEqual(
_substitute_wpt_template_variables(
body,
port=12345,
request_headers={
"referer": "https://example.test/path?a=1&b=2",
},
),
b"present=https://example.test/path?a=1&amp;b=2"
b" absent=&lt;missing&gt; empty=",
)
@@ -13,6 +13,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
)
self.assertEqual(_pipe_trickle_delay_seconds("pipe=trickle(d999)"), 10.0)
self.assertEqual(_pipe_trickle_delay_seconds("notpipe=trickle(d1)"), 0.0)
def test_fixture_server_parses_delay_handler_duration(self) -> None:
self.assertEqual(_wpt_delay_seconds("ms=3000"), 3.0)
self.assertEqual(_wpt_delay_seconds("ms=2.5"), 0.0025)
@@ -21,6 +22,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
self.assertIsNone(_wpt_delay_seconds("ms=invalid"))
self.assertIsNone(_wpt_delay_seconds("ms=-1"))
self.assertIsNone(_wpt_delay_seconds("ms=nan"))
def test_fixture_server_models_xhr_delay_py_methods(self) -> None:
with tempfile.TemporaryDirectory() as root:
root_path = Path(root)
@@ -66,6 +68,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
("YO", 200, b"TEST_DELAY", "text/plain", "*", "YO"),
],
)
def test_fixture_server_models_delayed_module_script_handler(self) -> None:
with tempfile.TemporaryDirectory() as root:
root_path = Path(root)
@@ -111,6 +114,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
("HEAD", 200, b"", "text/javascript"),
],
)
def test_fixture_server_models_common_redirect_opt_in_handler(self) -> None:
with tempfile.TemporaryDirectory() as root:
root_path = Path(root)
@@ -129,6 +133,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
self.assertEqual(response.headers.get("Location"), "/target")
self.assertEqual(response.headers.get("Timing-Allow-Origin"), "*")
connection.close()
def test_fixture_server_drains_xhr_delay_yo_body_before_next_request(
self,
) -> None:
@@ -163,6 +168,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
self.assertIs(connection.sock, first_socket)
finally:
connection.close()
def test_fixture_server_drains_chunked_xhr_delay_body_before_next_request(
self,
) -> None:
@@ -202,6 +208,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
self.assertIs(connection.sock, first_socket)
finally:
connection.close()
def test_fixture_server_parses_wpt_header_pipe(self) -> None:
def applied_pipe_headers(query: str) -> list[tuple[str, str]]:
return _apply_header_operations(
@@ -237,6 +244,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
[("X-Test", "bad Injected: x")],
)
self.assertEqual(applied_pipe_headers("notpipe=header(X,Y)"), [])
def test_fixture_server_parses_wpt_status_pipe(self) -> None:
self.assertEqual(_pipe_response_status("pipe=status(204)&cachebust=1"), 204)
self.assertEqual(
@@ -250,6 +258,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
self.assertIsNone(_pipe_response_status("pipe=status(099)"))
self.assertIsNone(_pipe_response_status("pipe=status(600)"))
self.assertIsNone(_pipe_response_status("notpipe=status(204)"))
def test_fixture_server_parses_wpt_headers_sidecar(self) -> None:
with tempfile.TemporaryDirectory() as root:
fixture = Path(root) / "case.html"
@@ -265,6 +274,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
_sidecar_response_headers(fixture),
[("Referrer-Policy", "no-referrer"), ("X-Test", "ok")],
)
def test_fixture_server_parses_sub_headers_sidecar_for_sub_files(self) -> None:
with tempfile.TemporaryDirectory() as root:
fixture = Path(root) / "worker.sub.js"
@@ -278,6 +288,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
_sidecar_response_headers(fixture),
[("Content-Security-Policy", "connect-src 'none'")],
)
def test_fixture_server_combines_immediate_directory_and_file_headers(self) -> None:
with tempfile.TemporaryDirectory() as root:
root_path = Path(root)
@@ -306,6 +317,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
("X-File", "exact"),
],
)
def test_fixture_server_prefers_file_sub_headers_when_both_exist(self) -> None:
with tempfile.TemporaryDirectory() as root:
fixture = Path(root) / "case.sub.html"
@@ -323,6 +335,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
_sidecar_response_headers(fixture),
[("X-Source", "substituted")],
)
def test_fixture_server_prefers_directory_sub_headers_when_both_exist(self) -> None:
with tempfile.TemporaryDirectory() as root:
root_path = Path(root)
@@ -341,6 +354,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
_sidecar_response_headers(fixture),
[("X-Source", "substituted")],
)
def test_fixture_server_content_type_sidecar_overrides_static_guess(self) -> None:
content_type, headers = _response_content_type_and_extra_headers(
"application/octet-stream",
@@ -352,6 +366,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
self.assertEqual(content_type, "text/javascript; charset=utf-8")
self.assertEqual(headers, [("X-Test", "ok")])
def test_fixture_server_combines_sidecar_and_pipe_headers_for_static_responses(
self,
) -> None:
@@ -390,6 +405,7 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
),
],
)
def test_fixture_server_reads_sub_headers_for_plain_static_resource(self) -> None:
with tempfile.TemporaryDirectory() as root:
fixture = Path(root) / "policy.html"
@@ -692,3 +708,44 @@ class WptCrossFixtureResponsesTests(WptCrossTestCase):
("text/javascript", "nosniff", b"// nothing to see here\np()"),
],
)
def test_fixture_server_models_dynamic_import_redirect_without_cors(self) -> None:
with tempfile.TemporaryDirectory() as root:
root_path = Path(root)
(root_path / "resources").mkdir()
(root_path / "resources" / "testharness.js").write_text(
"// testharness", encoding="utf-8"
)
with WptFixtureServer(root_path) as server:
for method in ("GET", "HEAD"):
for query, status, location in (
("location=%2Ftarget", 302, "/target"),
(
"status=307&location=https%3A%2F%2Fexample.test%2Fx",
307,
"https://example.test/x",
),
("status=invalid&location=%2Ftarget", 302, "/target"),
(
"status=302&status=307&location=%2Fa&location=%2Fb",
302,
"/a",
),
):
with self.subTest(method=method, query=query):
connection = HTTPConnection("127.0.0.1", server.port, timeout=2)
try:
connection.request(
method,
"/html/semantics/scripting-1/the-script-element/module/"
f"dynamic-import/beta/redirect.py?{query}",
)
response = connection.getresponse()
self.assertEqual(response.status, status)
self.assertEqual(response.headers.get("Location"), location)
self.assertIsNone(
response.headers.get("Access-Control-Allow-Origin")
)
self.assertEqual(response.read(), b"")
finally:
connection.close()
@@ -231,7 +231,7 @@ class FetchPreflightFixtureTests(unittest.TestCase):
}
utils = self.root / "fetch/api/resources/utils.js"
utils.parent.mkdir(parents=True)
utils.write_text('var RESOURCES_DIR = "../resources/";')
utils.write_text('var RESOURCES_DIR = "../resources/"; fetch(RESOURCES_DIR + "inspect-headers.py");')
for name, source in cases.items():
path = self.root / f"fetch/api/cors/{name}.any.js"
path.parent.mkdir(parents=True, exist_ok=True)
@@ -119,6 +119,29 @@ class WptPipeFixtureTests(unittest.TestCase):
self.assertEqual(status, 200)
self.assertEqual(headers.get_all("X-Test"), expected)
def test_explicit_cache_control_is_not_overridden_by_the_fixture_default(self) -> None:
for method in ("GET", "HEAD"):
with self.subTest(method=method, source="default"):
status, headers, _ = self.request("", method=method)
self.assertEqual(status, 200)
self.assertEqual(headers.get_all("Cache-Control"), ["no-store"])
(self.root / "module.json.headers").write_text("cAcHe-CoNtRoL: max-age=600\n")
for pipe, expected in (
("", ["max-age=600"]),
("header(Cache-Control,max-age=30)", ["max-age=30"]),
("header(cache-control,public,true)", ["max-age=600", "public"]),
("header(Cache-Control,)", [""]),
):
for method in ("GET", "HEAD"):
with self.subTest(method=method, pipe=pipe):
status, headers, body = self.request(urlencode({"pipe": pipe}), method=method)
self.assertEqual(status, 200)
self.assertEqual(headers.get_all("Cache-Control"), expected)
self.assertEqual(body, b"" if method == "HEAD" else b'{"value": 1}')
(self.root / "module.json.headers").unlink()
_, headers, _ = self.request(urlencode({"pipe": "header(Cache-Control,max-age=60)"}))
self.assertEqual(headers.get_all("Cache-Control"), ["max-age=60"])
def test_only_last_nonempty_pipe_parameter_is_applied(self) -> None:
cases = (
(["unknown", "header(X-Test,last)"], 200, "last"),
@@ -156,6 +179,22 @@ class WptPipeFixtureTests(unittest.TestCase):
self.assertEqual(headers["X-Test"], "before Injected: value")
self.assertIsNone(headers["Injected"])
def test_status_conversion_and_errors_also_work_for_inspection_methods(self) -> None:
for method in ("GET", "HEAD", "POST", "OPTIONS", "CUSTOM"):
for command, expected_status in (
("status(00201)", 201),
("status( +201 )", 201),
("status(invalid)", 500),
("status(201)|unknown", 500),
):
with self.subTest(method=method, command=command):
status, _, body = self.request(
urlencode({"pipe": command}), method=method,
path="/fetch/api/resources/inspect-headers.py",
)
self.assertEqual(status, expected_status)
if method == "HEAD":
self.assertEqual(body, b"")
if __name__ == "__main__":
@@ -12,11 +12,10 @@ from moli_benchmark.wpt_cross.server import WptFixtureServer
RESOURCE = "/xhr/resources/corsenabled.py"
PUT_RESOURCE = "/xhr/resources/access-control-basic-put-allow.py"
STAR_RESOURCE = "/xhr/resources/access-control-preflight-request-allow-headers-returns-star.py"
class XhrCorsFixtureTests(unittest.TestCase):
def setUp(self) -> None:
self.stack = ExitStack()
@@ -100,24 +99,6 @@ class XhrCorsFixtureTests(unittest.TestCase):
status, _, _ = self.request(server, resource=resource)
self.assertEqual(status, 404)
def test_case_selection_recognizes_relative_and_absolute_references(self) -> None:
sources = {
"xhr/absolute.html": f"fetch('{RESOURCE}');",
"xhr/relative.html": "fetch('resources/corsenabled.py');",
"xhr/nested/relative.html": "fetch('../resources/corsenabled.py?delay=0');",
"xhr/unknown.html": "fetch('resources/corsenabled.py'); fetch('unknown.py');",
"xhr/wrong-relative.html": "fetch('corsenabled.py');",
}
for name, script in sources.items():
path = self.root / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text('<script src="/resources/testharness.js"></script><script>'
+ script + '</script>')
selected = enumerate_cases(self.root, dir_prefixes=("xhr",))
self.assertEqual([case.case_path for case in selected], [
"xhr/absolute.html", "xhr/nested/relative.html", "xhr/relative.html",
])
def test_put_preflight_and_upload_preserve_origin_and_bytes(self) -> None:
server = self.stack.enter_context(WptFixtureServer(self.root))
for origin in ("https://caller.test", "null", ""):
@@ -143,7 +124,6 @@ class XhrCorsFixtureTests(unittest.TestCase):
self.assertEqual(headers["Access-Control-Allow-Credentials"], "true")
self.assertIsNone(headers["Access-Control-Allow-Methods"])
def test_put_fixture_missing_origin_matches_upstream_failure(self) -> None:
server = self.stack.enter_context(WptFixtureServer(self.root))
for method in ("OPTIONS", "PUT"):
@@ -152,7 +132,6 @@ class XhrCorsFixtureTests(unittest.TestCase):
self.assertEqual(status, 500)
self.assertIsNone(headers["Access-Control-Allow-Origin"])
def test_put_fixture_reports_other_methods_without_cors_headers(self) -> None:
server = self.stack.enter_context(WptFixtureServer(self.root))
for method in ("GET", "HEAD", "POST", "DELETE", "FOO"):
@@ -164,7 +143,6 @@ class XhrCorsFixtureTests(unittest.TestCase):
self.assertEqual(headers["Content-Length"], str(len(expected)))
self.assertIsNone(headers["Access-Control-Allow-Origin"])
def test_wildcard_preflight_and_actual_request_have_distinct_headers(self) -> None:
server = self.stack.enter_context(WptFixtureServer(self.root))
status, headers, body = self.request(server, "OPTIONS", resource=STAR_RESOURCE)
@@ -184,7 +162,6 @@ class XhrCorsFixtureTests(unittest.TestCase):
self.assertEqual(headers["Content-Type"], "text/plain")
self.assertIsNone(headers["Access-Control-Allow-Headers"])
def test_wildcard_fixture_rejects_missing_or_empty_test_header(self) -> None:
server = self.stack.enter_context(WptFixtureServer(self.root))
for request_headers in ({}, {"X-Test": ""}):
@@ -196,7 +173,6 @@ class XhrCorsFixtureTests(unittest.TestCase):
self.assertEqual(headers["Access-Control-Allow-Origin"], "*")
self.assertIsNone(headers["Content-Type"])
def test_wildcard_fixture_other_methods_have_no_cors_grants(self) -> None:
server = self.stack.enter_context(WptFixtureServer(self.root))
for method in ("HEAD", "POST", "PUT", "DELETE", "FOO"):
@@ -208,7 +184,6 @@ class XhrCorsFixtureTests(unittest.TestCase):
self.assertIsNone(headers["Access-Control-Allow-Origin"])
self.assertIsNone(headers["Content-Type"])
def test_preflight_fixtures_require_exact_resource_paths(self) -> None:
server = self.stack.enter_context(WptFixtureServer(self.root))
for resource in (PUT_RESOURCE, STAR_RESOURCE):
@@ -217,7 +192,6 @@ class XhrCorsFixtureTests(unittest.TestCase):
status, _, _ = self.request(server, resource=wrong)
self.assertEqual(status, 404)
def test_preflight_case_selection_rejects_unsupported_dependencies(self) -> None:
for resource in (PUT_RESOURCE, STAR_RESOURCE):
name = Path(resource).name
@@ -240,3 +214,21 @@ class XhrCorsFixtureTests(unittest.TestCase):
for prefix in ("absolute-", "relative-", "nested/relative-")
)
self.assertEqual([case.case_path for case in selected], expected)
def test_case_selection_recognizes_relative_and_absolute_references(self) -> None:
sources = {
"xhr/absolute.html": f"fetch('{RESOURCE}');",
"xhr/relative.html": "fetch('resources/corsenabled.py');",
"xhr/nested/relative.html": "fetch('../resources/corsenabled.py?delay=0');",
"xhr/unknown.html": "fetch('resources/corsenabled.py'); fetch('unknown.py');",
"xhr/wrong-relative.html": "fetch('corsenabled.py');",
}
for name, script in sources.items():
path = self.root / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text('<script src="/resources/testharness.js"></script><script>'
+ script + '</script>')
selected = enumerate_cases(self.root, dir_prefixes=("xhr",))
self.assertEqual([case.case_path for case in selected], [
"xhr/absolute.html", "xhr/nested/relative.html", "xhr/relative.html",
])
@@ -110,9 +110,6 @@ from moli_benchmark.wpt_cross.scheduler import (
build_run_schedule,
)
from moli_benchmark.wpt_cross.server import (
_nosniff_javascript_response,
_workers_url_encoding_response,
_fetch_status_response,
BENCH_REPORT_BRIDGE,
BENCH_TESTDRIVER_VENDOR_BRIDGE,
BENCH_TIMEOUT_MULTIPLIER_QUERY,
@@ -126,8 +123,10 @@ from moli_benchmark.wpt_cross.server import (
_inject_bench_report_bridge_config,
_host_header_hostname,
_headers_include,
_normalize_harness_case_key,
_inspect_headers_response_headers,
_fetch_status_response,
_normalize_harness_case_key,
_nosniff_javascript_response,
_needs_wpt_template_substitution,
_legacy_wpt_resource_alias,
_pipe_response_header_operations,
@@ -142,6 +141,7 @@ from moli_benchmark.wpt_cross.server import (
_substitute_wpt_template_variables,
_window_js_window_wrapper,
_wasm_webapi_status_code,
_workers_url_encoding_response,
_wpt_delay_seconds,
_wpt_dedicated_worker_js_wrapper_html,
_wpt_any_dedicated_worker_wrapper_html,
@@ -156,8 +156,6 @@ from moli_benchmark.wpt_cross.server import (
clear_current_proxy_env()
class WptCrossTestCase(unittest.TestCase):
def _run_wpt_cross_with_fake_moli(
self,