From 7ef7c78b1b7965eee7f0b810bb5786ec583b8dbb Mon Sep 17 00:00:00 2001 From: WPT Sync Bot Date: Wed, 10 Jun 2020 08:24:06 +0000 Subject: [PATCH] Update web-platform-tests to revision 57727f82763c80c89a94593a7b0960abcf4daa4a --- .../url/url-in-tags-revoke.window.js.ini | 2 +- .../CSS2/floats/hit-test-floats-002.html.ini | 4 - .../CSS2/floats/hit-test-floats-003.html.ini | 4 - .../CSS2/floats/hit-test-floats-005.html.ini | 4 + .../white-space-letter-spacing-001.html.ini | 2 + .../subpixel-transform-changes-001.html.ini | 2 + .../subpixel-transform-changes-002.html.ini | 2 + .../subpixel-transform-changes-003.html.ini | 2 + .../transform-scale-hittest.html.ini | 3 - .../css/cssom-view/CaretPosition-001.html.ini | 4 + ...ryList-addListener-removeListener.html.ini | 3 - .../cssom-view/elementFromPosition.html.ini | 3 - .../reactions/HTMLMediaElement.html.ini | 2 - .../fetch/content-type/response.window.js.ini | 10 +- .../fetch/content-type/script.window.js.ini | 3 + .../http-cache/split-cache.tentative.html.ini | 7 + .../traverse_the_history_3.html.ini} | 2 +- ...ml.ini => traverse_the_history_4.html.ini} | 2 +- ...ross-origin-objects-on-new-window.html.ini | 2 + .../embedded-opener-remove-frame.html.ini | 2 +- .../hash-name-reference.html.ini | 176 + .../networkState_during_progress.html.ini | 2 - .../iframe_sandbox_popups_escaping-2.html.ini | 2 +- .../iframe_sandbox_popups_escaping-3.html.ini | 3 +- ...rame_sandbox_popups_nonescaping-1.html.ini | 2 +- ...rame_sandbox_popups_nonescaping-2.html.ini | 2 +- .../form-double-submit-3.html.ini | 4 + ...le-event-handler-settings-objects.html.ini | 3 - .../iframe-upgrade-request.sub.https.html.ini | 4 + .../webmessaging/with-ports/017.html.ini | 5 - .../webmessaging/without-ports/017.html.ini | 5 - .../webmessaging/without-ports/018.html.ini | 5 + .../semantics/run-a-worker/003.html.ini | 1 + ...d-worker-in-data-url-context.window.js.ini | 5 +- .../url/url-in-tags-revoke.window.js.ini | 2 +- tests/wpt/metadata/MANIFEST.json | 924 +- .../CSS2/floats/hit-test-floats-002.html.ini | 4 - .../CSS2/floats/hit-test-floats-003.html.ini | 4 - .../CSS2/floats/hit-test-floats-005.html.ini | 4 + .../white-space-letter-spacing-001.html.ini | 2 + .../subpixel-transform-changes-001.html.ini | 2 + .../subpixel-transform-changes-002.html.ini | 2 + .../subpixel-transform-changes-003.html.ini | 2 + .../transform-scale-hittest.html.ini | 3 - .../css/cssom-view/CaretPosition-001.html.ini | 4 + ...ryList-addListener-removeListener.html.ini | 3 - .../cssom-view/elementFromPosition.html.ini | 3 - .../reactions/HTMLMediaElement.html.ini | 2 - .../fetch/content-type/response.window.js.ini | 14 +- .../fetch/content-type/script.window.js.ini | 3 + .../http-cache/split-cache.tentative.html.ini | 3 + .../traverse_the_history_3.html.ini | 4 + .../traverse_the_history_4.html.ini | 4 + ...ross-origin-objects-on-new-window.html.ini | 2 + .../embedded-opener-remove-frame.html.ini | 2 +- .../hash-name-reference.html.ini | 3 + .../networkState_during_progress.html.ini | 2 - .../iframe_sandbox_popups_escaping-2.html.ini | 2 +- .../iframe_sandbox_popups_escaping-3.html.ini | 3 +- ...rame_sandbox_popups_nonescaping-1.html.ini | 2 +- ...rame_sandbox_popups_nonescaping-2.html.ini | 2 +- .../form-double-submit-3.html.ini | 4 + .../html5lib_menuitem-element.html.ini | 35 - ...le-event-handler-settings-objects.html.ini | 3 - .../iframe-upgrade-request.sub.https.html.ini | 4 + .../webmessaging/with-ports/017.html.ini | 5 - .../webmessaging/without-ports/017.html.ini | 5 - .../webmessaging/without-ports/018.html.ini | 5 + .../semantics/run-a-worker/003.html.ini | 1 + ...d-worker-in-data-url-context.window.js.ini | 5 +- .../xhr/send-data-readablestream.any.js.ini | 9 - tests/wpt/web-platform-tests/README.md | 2 + .../wpt/web-platform-tests/common/redirect.py | 8 +- .../white-space-letter-spacing-001-ref.html | 21 + .../white-space-letter-spacing-001.html | 41 + .../subpixel-transform-changes-001-ref.html | 16 + .../subpixel-transform-changes-002-ref.html | 22 + .../subpixel-transform-changes-003-ref.html | 25 + .../subpixel-perspective-backface-hidden.html | 2 +- .../subpixel-transform-changes-001.html | 39 + .../subpixel-transform-changes-002.html | 44 + .../subpixel-transform-changes-003.html | 48 + .../split-origin-popup-with-iframe.html | 34 + .../resources/split-origin-popup.html | 5 +- .../http-cache/split-cache.tentative.html | 30 +- .../dedicated-worker.https.html | 14 +- .../html/syntax/parsing/html5lib_blocks.html | 28 + .../syntax/parsing/html5lib_comments01.html | 4 +- .../syntax/parsing/html5lib_entities01.html | 4 +- .../parsing/html5lib_menuitem-element.html | 2 +- .../parsing/html5lib_plain-text-unsafe.html | 4 +- .../html/syntax/parsing/html5lib_tests18.html | 4 +- .../html/syntax/parsing/html5lib_tests25.html | 4 +- .../html/syntax/parsing/html5lib_tests8.html | 4 +- .../syntax/parsing/html5lib_webkit01.html | 4 +- tests/wpt/web-platform-tests/lint.ignore | 3 + .../generic/iframe-upgrade-request.sub.html | 22 + .../iframe-upgrade-request.sub.html.headers | 2 + .../iframe-upgrade-request.sub.https.html | 22 + ...ame-upgrade-request.sub.https.html.headers | 2 + .../generic/resources/referrer.py | 3 + .../resource-timing/SyntheticResponse.py | 33 +- .../resource-timing/resources/TAOResponse.py | 66 +- .../resource-timing/resources/cors-ahem.py | 30 +- .../resource-timing/resources/empty.py | 4 +- .../resource-timing/resources/eventsource.py | 4 +- .../resources/fake_responses.py | 32 +- .../resource-timing/resources/gzip_xml.py | 21 +- .../resources/multi_redirect.py | 45 +- .../resource-timing/resources/preflight.py | 10 +- .../resource-timing/resources/status-code.py | 4 +- .../innerhtml-before-closing-tag.html | 42 + .../innerhtml-on-ordinary-template.html | 47 + tests/wpt/web-platform-tests/tools/.gitignore | 5 + .../tools/ci/ci_tools_integration_test.sh | 8 + tests/wpt/web-platform-tests/tools/pytest.ini | 2 +- .../tools/serve/commands.json | 20 +- .../web-platform-tests/tools/serve/serve.py | 101 +- .../web-platform-tests/tools/serve/wave.py | 124 + .../web-platform-tests/tools/wave/__init__.py | 0 .../tools/wave/config.default.json | 45 + .../tools/wave/configuration_loader.py | 81 + .../tools/wave/data/__init__.py | 0 .../tools/wave/data/client.py | 6 + .../tools/wave/data/exceptions/__init__.py | 0 .../data/exceptions/duplicate_exception.py | 2 + .../data/exceptions/invalid_data_exception.py | 2 + .../data/exceptions/not_found_exception.py | 2 + .../exceptions/permission_denied_exception.py | 2 + .../tools/wave/data/http_polling_client.py | 11 + .../tools/wave/data/session.py | 86 + .../tools/wave/docs/README.md | 4 + .../docs/res/configuration_page_bottom.jpg | Bin 0 -> 126736 bytes ...ration_page_exclude_add_malfunctioning.jpg | Bin 0 -> 139829 bytes ...uration_page_exclude_add_prev_excluded.jpg | Bin 0 -> 139651 bytes .../configuration_page_exclude_add_raw.jpg | Bin 0 -> 129514 bytes .../wave/docs/res/configuration_page_top.jpg | Bin 0 -> 120429 bytes .../tools/wave/docs/res/landing_page.jpg | Bin 0 -> 108212 bytes .../wave/docs/res/overview_page_sessions.jpg | Bin 0 -> 89660 bytes .../res/overview_page_sessions_filtered.jpg | Bin 0 -> 105746 bytes .../overview_page_sessions_pinned_recent.jpg | Bin 0 -> 129519 bytes .../tools/wave/docs/res/overview_page_top.jpg | Bin 0 -> 56339 bytes .../docs/res/results_page_api_results.jpg | Bin 0 -> 127186 bytes .../res/results_page_api_results_export.jpg | Bin 0 -> 129230 bytes .../wave/docs/res/results_page_bottom.jpg | Bin 0 -> 98504 bytes .../docs/res/results_page_last_timed_out.jpg | Bin 0 -> 87467 bytes .../res/results_page_malfunctioning_list.jpg | Bin 0 -> 88697 bytes .../tools/wave/docs/res/results_page_top.jpg | Bin 0 -> 77941 bytes .../tools/wave/docs/rest-api/README.md | 54 + .../wave/docs/rest-api/results-api/config.md | 34 + .../wave/docs/rest-api/results-api/create.md | 65 + .../docs/rest-api/results-api/download.md | 127 + .../wave/docs/rest-api/results-api/import.md | 45 + .../docs/rest-api/results-api/read-compact.md | 59 + .../wave/docs/rest-api/results-api/read.md | 63 + .../wave/docs/rest-api/results-api/view.md | 61 + .../docs/rest-api/sessions-api/control.md | 25 + .../wave/docs/rest-api/sessions-api/create.md | 101 + .../wave/docs/rest-api/sessions-api/delete.md | 25 + .../wave/docs/rest-api/sessions-api/events.md | 34 + .../wave/docs/rest-api/sessions-api/find.md | 29 + .../wave/docs/rest-api/sessions-api/labels.md | 75 + .../docs/rest-api/sessions-api/read-public.md | 30 + .../wave/docs/rest-api/sessions-api/read.md | 84 + .../wave/docs/rest-api/sessions-api/status.md | 48 + .../wave/docs/rest-api/sessions-api/update.md | 102 + .../wave/docs/rest-api/tests-api/read-all.md | 43 + .../rest-api/tests-api/read-available-apis.md | 43 + .../rest-api/tests-api/read-last-completed.md | 47 + .../rest-api/tests-api/read-malfunctioning.md | 30 + .../wave/docs/rest-api/tests-api/read-next.md | 29 + .../docs/rest-api/tests-api/read-session.md | 61 + .../tests-api/update-malfunctioning.md | 56 + .../tools/wave/docs/usage/usage.md | 224 + .../tools/wave/export/css/bulma.min.css | 1 + .../tools/wave/export/css/result.css | 75 + .../tools/wave/export/index.html | 375 + .../tools/wave/export/lib/ui.js | 64 + .../tools/wave/export/lib/utils.js | 40 + .../tools/wave/export/res/wavelogo_2016.jpg | Bin 0 -> 15570 bytes .../tools/wave/network/__init__.py | 0 .../tools/wave/network/api/__init__.py | 0 .../tools/wave/network/api/api_handler.py | 58 + .../wave/network/api/results_api_handler.py | 225 + .../wave/network/api/sessions_api_handler.py | 342 + .../wave/network/api/tests_api_handler.py | 287 + .../tools/wave/network/http_handler.py | 102 + .../tools/wave/network/static_handler.py | 55 + .../tools/wave/requirements.txt | 1 + .../tools/wave/resources/testharnessreport.js | 277 + .../tools/wave/testing/__init__.py | 0 .../tools/wave/testing/event_dispatcher.py | 41 + .../tools/wave/testing/results_manager.py | 628 + .../tools/wave/testing/sessions_manager.py | 442 + .../tools/wave/testing/test_loader.py | 197 + .../tools/wave/testing/tests_manager.py | 369 + .../tools/wave/testing/wpt_report.py | 56 + .../tests/WAVE Local.postman_environment.json | 34 + ...ver REST API Tests.postman_collection.json | 9183 +++++++++++++ .../tools/wave/tests/test_wave.py | 54 + .../wpt/web-platform-tests/tools/wave/tox.ini | 20 + .../tools/wave/utils/__init__.py | 0 .../tools/wave/utils/deserializer.py | 96 + .../tools/wave/utils/serializer.py | 24 + .../tools/wave/utils/user_agent_parser.py | 43 + .../tools/wave/wave_server.py | 114 + .../tools/wave/www/comparison.html | 444 + .../tools/wave/www/configuration.html | 1116 ++ .../tools/wave/www/css/bulma-0.7.5/bulma.css | 10599 ++++++++++++++++ .../wave/www/css/bulma-0.7.5/bulma.css.map | 1 + .../wave/www/css/bulma-0.7.5/bulma.min.css | 1 + .../wave/www/css/fontawesome-5.7.2.min.css | 1 + .../tools/wave/www/css/fontawesome.min.css | 5 + .../tools/wave/www/css/main.css | 101 + .../tools/wave/www/css/result.css | 75 + .../tools/wave/www/css/style.css | 86 + .../tools/wave/www/favicon.ico | Bin 0 -> 90022 bytes .../tools/wave/www/finish.html | 215 + .../tools/wave/www/index.html | 259 + .../tools/wave/www/lib/davidshimjs/LICENSE | 14 + .../tools/wave/www/lib/davidshimjs/qrcode.js | 1533 +++ .../tools/wave/www/lib/keycodes.js | 88 + .../tools/wave/www/lib/ui.js | 97 + .../tools/wave/www/lib/utils.js | 57 + .../tools/wave/www/lib/wave-service.js | 674 + .../tools/wave/www/newsession.html | 257 + .../tools/wave/www/next.html | 45 + .../tools/wave/www/overview.html | 1315 ++ .../tools/wave/www/pause.html | 225 + .../tools/wave/www/res/spinner-solid.svg | 1 + .../tools/wave/www/res/wavelogo_2016.jpg | Bin 0 -> 15570 bytes .../tools/wave/www/results.html | 1545 +++ .../tools/wave/www/submitresult.html | 64 + .../tools/wave/www/webfonts/fa-brands-400.eot | Bin 0 -> 125320 bytes .../tools/wave/www/webfonts/fa-brands-400.svg | 3296 +++++ .../tools/wave/www/webfonts/fa-brands-400.ttf | Bin 0 -> 125016 bytes .../wave/www/webfonts/fa-brands-400.woff | Bin 0 -> 84564 bytes .../wave/www/webfonts/fa-brands-400.woff2 | Bin 0 -> 72112 bytes .../wave/www/webfonts/fa-regular-400.eot | Bin 0 -> 34388 bytes .../wave/www/webfonts/fa-regular-400.svg | 799 ++ .../wave/www/webfonts/fa-regular-400.ttf | Bin 0 -> 34092 bytes .../wave/www/webfonts/fa-regular-400.woff | Bin 0 -> 16812 bytes .../wave/www/webfonts/fa-regular-400.woff2 | Bin 0 -> 13592 bytes .../tools/wave/www/webfonts/fa-solid-900.eot | Bin 0 -> 186512 bytes .../tools/wave/www/webfonts/fa-solid-900.svg | 4516 +++++++ .../tools/wave/www/webfonts/fa-solid-900.ttf | Bin 0 -> 186228 bytes .../tools/wave/www/webfonts/fa-solid-900.woff | Bin 0 -> 96244 bytes .../wave/www/webfonts/fa-solid-900.woff2 | Bin 0 -> 74348 bytes .../wptrunner/formatters/chromium.py | 80 +- .../formatters/tests/test_chromium.py | 74 +- ...tePolicy-cspTests-none-skip.tentative.html | 19 + ...-createPolicy-cspTests-none.tentative.html | 19 + .../web-locks/clientids.tentative.https.html | 1 + .../web-locks/frames.tentative.https.html | 7 + .../query-ordering.tentative.https.html | 1 + .../web-locks/workers.tentative.https.html | 4 + .../webgpu/common/constants.js | 302 - .../common/framework/allowed_characters.js | 7 - .../common/framework/collect_garbage.js | 48 - .../webgpu/common/framework/file_loader.js | 4 +- .../framework/generate_minimal_query_list.js | 106 - .../webgpu/common/framework/id.js | 8 - .../webgpu/common/framework/listing.js | 4 - .../webgpu/common/framework/loader.js | 50 - .../webgpu/common/framework/logger.js | 155 - .../webgpu/common/framework/params.js | 135 - .../webgpu/common/framework/query/compare.js | 20 +- .../framework/query/encode_selectively.js | 14 +- .../webgpu/common/framework/query/query.js | 44 +- .../common/framework/query/separators.js | 15 +- .../common/framework/query/validQueryPart.js | 2 +- .../framework/test_filter/filter_by_group.js | 64 - .../framework/test_filter/filter_one_file.js | 167 - .../common/framework/test_filter/internal.js | 4 - .../framework/test_filter/load_filter.js | 71 - .../test_filter/test_filter_result.js | 4 - .../webgpu/common/framework/test_group.js | 2 +- .../webgpu/common/framework/tree.js | 73 +- .../webgpu/common/framework/url_query.js | 48 - .../common/framework/util/collect_garbage.js | 2 + .../webgpu/common/framework/version.js | 2 +- .../runtime/helper/test_worker-worker.js | 3 +- .../webgpu/common/runtime/wpt.js | 5 +- tests/wpt/web-platform-tests/webgpu/cts.html | 1 + .../webgpu/api/operation/buffers/map.spec.js | 2 +- .../api/operation/buffers/map_oom.spec.js | 18 +- .../copied_texture_clear.spec.js | 3 +- .../resource_init/texture_zero_init_test.js | 21 +- .../api/validation/createBindGroup.spec.js | 7 +- .../validation/createBindGroupLayout.spec.js | 9 +- .../validation/createPipelineLayout.spec.js | 3 +- .../webgpu/api/validation/createView.spec.js | 45 +- .../webgpu/webgpu/capability_info.js | 30 +- .../webgpu/webgpu/gpu_test.js | 40 +- .../webgpu/webgpu/idl/constants/flags.spec.js | 53 + .../webgpu/webgpu/idl/idl_test.js | 27 + .../webgpu/webgpu/listing.js | 8 + .../webgpu/webgpu/util/texture/layout.js | 3 +- .../copyImageBitmapToTexture.spec.js | 2 +- .../webrtc/RTCStats-helper.js | 2 +- 300 files changed, 44918 insertions(+), 1815 deletions(-) delete mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-002.html.ini delete mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-003.html.ini create mode 100644 tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-005.html.ini create mode 100644 tests/wpt/metadata-layout-2020/css/css-text/white-space/white-space-letter-spacing-001.html.ini create mode 100644 tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-001.html.ini create mode 100644 tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-002.html.ini create mode 100644 tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-003.html.ini create mode 100644 tests/wpt/metadata-layout-2020/css/cssom-view/CaretPosition-001.html.ini delete mode 100644 tests/wpt/metadata-layout-2020/custom-elements/reactions/HTMLMediaElement.html.ini create mode 100644 tests/wpt/metadata-layout-2020/fetch/http-cache/split-cache.tentative.html.ini rename tests/wpt/{metadata/html/browsers/history/the-history-interface/traverse_the_history_2.html.ini => metadata-layout-2020/html/browsers/history/the-history-interface/traverse_the_history_3.html.ini} (71%) rename tests/wpt/metadata-layout-2020/html/browsers/history/the-history-interface/{traverse_the_history_2.html.ini => traverse_the_history_4.html.ini} (71%) create mode 100644 tests/wpt/metadata-layout-2020/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html.ini create mode 100644 tests/wpt/metadata-layout-2020/html/semantics/embedded-content/image-maps/image-map-processing-model/hash-name-reference.html.ini delete mode 100644 tests/wpt/metadata-layout-2020/html/semantics/embedded-content/media-elements/networkState_during_progress.html.ini create mode 100644 tests/wpt/metadata-layout-2020/html/semantics/forms/form-submission-0/form-double-submit-3.html.ini create mode 100644 tests/wpt/metadata-layout-2020/referrer-policy/generic/iframe-upgrade-request.sub.https.html.ini delete mode 100644 tests/wpt/metadata-layout-2020/webmessaging/with-ports/017.html.ini delete mode 100644 tests/wpt/metadata-layout-2020/webmessaging/without-ports/017.html.ini create mode 100644 tests/wpt/metadata-layout-2020/webmessaging/without-ports/018.html.ini delete mode 100644 tests/wpt/metadata/css/CSS2/floats/hit-test-floats-002.html.ini delete mode 100644 tests/wpt/metadata/css/CSS2/floats/hit-test-floats-003.html.ini create mode 100644 tests/wpt/metadata/css/CSS2/floats/hit-test-floats-005.html.ini create mode 100644 tests/wpt/metadata/css/css-text/white-space/white-space-letter-spacing-001.html.ini create mode 100644 tests/wpt/metadata/css/css-transforms/subpixel-transform-changes-001.html.ini create mode 100644 tests/wpt/metadata/css/css-transforms/subpixel-transform-changes-002.html.ini create mode 100644 tests/wpt/metadata/css/css-transforms/subpixel-transform-changes-003.html.ini create mode 100644 tests/wpt/metadata/css/cssom-view/CaretPosition-001.html.ini delete mode 100644 tests/wpt/metadata/custom-elements/reactions/HTMLMediaElement.html.ini create mode 100644 tests/wpt/metadata/html/browsers/history/the-history-interface/traverse_the_history_3.html.ini create mode 100644 tests/wpt/metadata/html/browsers/history/the-history-interface/traverse_the_history_4.html.ini create mode 100644 tests/wpt/metadata/html/browsers/origin/cross-origin-objects/cross-origin-objects-on-new-window.html.ini delete mode 100644 tests/wpt/metadata/html/semantics/embedded-content/media-elements/networkState_during_progress.html.ini create mode 100644 tests/wpt/metadata/html/semantics/forms/form-submission-0/form-double-submit-3.html.ini delete mode 100644 tests/wpt/metadata/html/syntax/parsing/html5lib_menuitem-element.html.ini create mode 100644 tests/wpt/metadata/referrer-policy/generic/iframe-upgrade-request.sub.https.html.ini delete mode 100644 tests/wpt/metadata/webmessaging/with-ports/017.html.ini delete mode 100644 tests/wpt/metadata/webmessaging/without-ports/017.html.ini create mode 100644 tests/wpt/metadata/webmessaging/without-ports/018.html.ini delete mode 100644 tests/wpt/metadata/xhr/send-data-readablestream.any.js.ini create mode 100644 tests/wpt/web-platform-tests/css/css-text/white-space/reference/white-space-letter-spacing-001-ref.html create mode 100644 tests/wpt/web-platform-tests/css/css-text/white-space/white-space-letter-spacing-001.html create mode 100644 tests/wpt/web-platform-tests/css/css-transforms/reference/subpixel-transform-changes-001-ref.html create mode 100644 tests/wpt/web-platform-tests/css/css-transforms/reference/subpixel-transform-changes-002-ref.html create mode 100644 tests/wpt/web-platform-tests/css/css-transforms/reference/subpixel-transform-changes-003-ref.html create mode 100644 tests/wpt/web-platform-tests/css/css-transforms/subpixel-transform-changes-001.html create mode 100644 tests/wpt/web-platform-tests/css/css-transforms/subpixel-transform-changes-002.html create mode 100644 tests/wpt/web-platform-tests/css/css-transforms/subpixel-transform-changes-003.html create mode 100644 tests/wpt/web-platform-tests/fetch/http-cache/resources/split-origin-popup-with-iframe.html create mode 100644 tests/wpt/web-platform-tests/html/syntax/parsing/html5lib_blocks.html create mode 100644 tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.html create mode 100644 tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.html.headers create mode 100644 tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.https.html create mode 100644 tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.https.html.headers create mode 100644 tests/wpt/web-platform-tests/referrer-policy/generic/resources/referrer.py create mode 100644 tests/wpt/web-platform-tests/shadow-dom/declarative/innerhtml-before-closing-tag.html create mode 100644 tests/wpt/web-platform-tests/shadow-dom/declarative/innerhtml-on-ordinary-template.html create mode 100644 tests/wpt/web-platform-tests/tools/serve/wave.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/__init__.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/config.default.json create mode 100644 tests/wpt/web-platform-tests/tools/wave/configuration_loader.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/data/__init__.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/data/client.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/data/exceptions/__init__.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/data/exceptions/duplicate_exception.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/data/exceptions/invalid_data_exception.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/data/exceptions/not_found_exception.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/data/exceptions/permission_denied_exception.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/data/http_polling_client.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/data/session.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/README.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/configuration_page_bottom.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/configuration_page_exclude_add_malfunctioning.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/configuration_page_exclude_add_prev_excluded.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/configuration_page_exclude_add_raw.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/configuration_page_top.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/landing_page.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/overview_page_sessions.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/overview_page_sessions_filtered.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/overview_page_sessions_pinned_recent.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/overview_page_top.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/results_page_api_results.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/results_page_api_results_export.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/results_page_bottom.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/results_page_last_timed_out.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/results_page_malfunctioning_list.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/res/results_page_top.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/README.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/results-api/config.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/results-api/create.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/results-api/download.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/results-api/import.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/results-api/read-compact.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/results-api/read.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/results-api/view.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/sessions-api/control.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/sessions-api/create.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/sessions-api/delete.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/sessions-api/events.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/sessions-api/find.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/sessions-api/labels.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/sessions-api/read-public.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/sessions-api/read.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/sessions-api/status.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/sessions-api/update.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/tests-api/read-all.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/tests-api/read-available-apis.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/tests-api/read-last-completed.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/tests-api/read-malfunctioning.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/tests-api/read-next.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/tests-api/read-session.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/rest-api/tests-api/update-malfunctioning.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/docs/usage/usage.md create mode 100644 tests/wpt/web-platform-tests/tools/wave/export/css/bulma.min.css create mode 100644 tests/wpt/web-platform-tests/tools/wave/export/css/result.css create mode 100644 tests/wpt/web-platform-tests/tools/wave/export/index.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/export/lib/ui.js create mode 100644 tests/wpt/web-platform-tests/tools/wave/export/lib/utils.js create mode 100644 tests/wpt/web-platform-tests/tools/wave/export/res/wavelogo_2016.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/network/__init__.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/network/api/__init__.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/network/api/api_handler.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/network/api/results_api_handler.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/network/api/sessions_api_handler.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/network/api/tests_api_handler.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/network/http_handler.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/network/static_handler.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/requirements.txt create mode 100644 tests/wpt/web-platform-tests/tools/wave/resources/testharnessreport.js create mode 100644 tests/wpt/web-platform-tests/tools/wave/testing/__init__.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/testing/event_dispatcher.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/testing/results_manager.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/testing/sessions_manager.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/testing/test_loader.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/testing/tests_manager.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/testing/wpt_report.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/tests/WAVE Local.postman_environment.json create mode 100644 tests/wpt/web-platform-tests/tools/wave/tests/WAVE Server REST API Tests.postman_collection.json create mode 100644 tests/wpt/web-platform-tests/tools/wave/tests/test_wave.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/tox.ini create mode 100644 tests/wpt/web-platform-tests/tools/wave/utils/__init__.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/utils/deserializer.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/utils/serializer.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/utils/user_agent_parser.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/wave_server.py create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/comparison.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/configuration.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/css/bulma-0.7.5/bulma.css create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/css/bulma-0.7.5/bulma.css.map create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/css/bulma-0.7.5/bulma.min.css create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/css/fontawesome-5.7.2.min.css create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/css/fontawesome.min.css create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/css/main.css create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/css/result.css create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/css/style.css create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/favicon.ico create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/finish.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/index.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/lib/davidshimjs/LICENSE create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/lib/davidshimjs/qrcode.js create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/lib/keycodes.js create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/lib/ui.js create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/lib/utils.js create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/lib/wave-service.js create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/newsession.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/next.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/overview.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/pause.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/res/spinner-solid.svg create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/res/wavelogo_2016.jpg create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/results.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/submitresult.html create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-brands-400.eot create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-brands-400.svg create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-brands-400.ttf create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-brands-400.woff create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-brands-400.woff2 create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-regular-400.eot create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-regular-400.svg create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-regular-400.ttf create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-regular-400.woff create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-regular-400.woff2 create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-solid-900.eot create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-solid-900.svg create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-solid-900.ttf create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-solid-900.woff create mode 100644 tests/wpt/web-platform-tests/tools/wave/www/webfonts/fa-solid-900.woff2 create mode 100644 tests/wpt/web-platform-tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none-skip.tentative.html create mode 100644 tests/wpt/web-platform-tests/trusted-types/TrustedTypePolicyFactory-createPolicy-cspTests-none.tentative.html delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/constants.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/allowed_characters.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/collect_garbage.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/generate_minimal_query_list.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/id.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/listing.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/loader.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/logger.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/params.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/test_filter/filter_by_group.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/test_filter/filter_one_file.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/test_filter/internal.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/test_filter/load_filter.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/test_filter/test_filter_result.js delete mode 100644 tests/wpt/web-platform-tests/webgpu/common/framework/url_query.js create mode 100644 tests/wpt/web-platform-tests/webgpu/webgpu/idl/constants/flags.spec.js create mode 100644 tests/wpt/web-platform-tests/webgpu/webgpu/idl/idl_test.js diff --git a/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini b/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini index 3605e8f3fc9..76b44d9e9cf 100644 --- a/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini +++ b/tests/wpt/metadata-layout-2020/FileAPI/url/url-in-tags-revoke.window.js.ini @@ -4,7 +4,7 @@ expected: TIMEOUT [Opening a blob URL in a new window immediately before revoking it works.] - expected: TIMEOUT + expected: FAIL [Fetching a blob URL immediately before revoking it works in an iframe.] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-002.html.ini b/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-002.html.ini deleted file mode 100644 index f64b45fea6b..00000000000 --- a/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-002.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[hit-test-floats-002.html] - [Hit test float] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-003.html.ini b/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-003.html.ini deleted file mode 100644 index f29da48a2a0..00000000000 --- a/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-003.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[hit-test-floats-003.html] - [Miss float below something else] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-005.html.ini b/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-005.html.ini new file mode 100644 index 00000000000..baa9f1a7541 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/CSS2/floats/hit-test-floats-005.html.ini @@ -0,0 +1,4 @@ +[hit-test-floats-005.html] + [Miss clipped float] + expected: FAIL + diff --git a/tests/wpt/metadata-layout-2020/css/css-text/white-space/white-space-letter-spacing-001.html.ini b/tests/wpt/metadata-layout-2020/css/css-text/white-space/white-space-letter-spacing-001.html.ini new file mode 100644 index 00000000000..ea0c28afe9a --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/css-text/white-space/white-space-letter-spacing-001.html.ini @@ -0,0 +1,2 @@ +[white-space-letter-spacing-001.html] + expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-001.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-001.html.ini new file mode 100644 index 00000000000..2e8dcfc89a8 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-001.html.ini @@ -0,0 +1,2 @@ +[subpixel-transform-changes-001.html] + expected: TIMEOUT diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-002.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-002.html.ini new file mode 100644 index 00000000000..13987e51a4f --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-002.html.ini @@ -0,0 +1,2 @@ +[subpixel-transform-changes-002.html] + expected: TIMEOUT diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-003.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-003.html.ini new file mode 100644 index 00000000000..f9bfdc9711e --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/css-transforms/subpixel-transform-changes-003.html.ini @@ -0,0 +1,2 @@ +[subpixel-transform-changes-003.html] + expected: TIMEOUT diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-scale-hittest.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/transform-scale-hittest.html.ini index f8e7e539aae..4a1e8110f6f 100644 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/transform-scale-hittest.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-transforms/transform-scale-hittest.html.ini @@ -2,6 +2,3 @@ [Hit test intersecting scaled box] expected: FAIL - [Hit test within unscaled box] - expected: FAIL - diff --git a/tests/wpt/metadata-layout-2020/css/cssom-view/CaretPosition-001.html.ini b/tests/wpt/metadata-layout-2020/css/cssom-view/CaretPosition-001.html.ini new file mode 100644 index 00000000000..4c79907309b --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/cssom-view/CaretPosition-001.html.ini @@ -0,0 +1,4 @@ +[CaretPosition-001.html] + [Element at (400, 100)] + expected: FAIL + diff --git a/tests/wpt/metadata-layout-2020/css/cssom-view/MediaQueryList-addListener-removeListener.html.ini b/tests/wpt/metadata-layout-2020/css/cssom-view/MediaQueryList-addListener-removeListener.html.ini index c884dc82eab..628b1fab770 100644 --- a/tests/wpt/metadata-layout-2020/css/cssom-view/MediaQueryList-addListener-removeListener.html.ini +++ b/tests/wpt/metadata-layout-2020/css/cssom-view/MediaQueryList-addListener-removeListener.html.ini @@ -2,6 +2,3 @@ [listeners are called when + + diff --git a/tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.html.headers b/tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.html.headers new file mode 100644 index 00000000000..fd9ee0f0328 --- /dev/null +++ b/tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.html.headers @@ -0,0 +1,2 @@ +Content-Security-Policy: upgrade-insecure-requests +Referrer-Policy: origin-when-cross-origin diff --git a/tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.https.html b/tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.https.html new file mode 100644 index 00000000000..243efb76719 --- /dev/null +++ b/tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.https.html @@ -0,0 +1,22 @@ + + + + + + + + diff --git a/tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.https.html.headers b/tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.https.html.headers new file mode 100644 index 00000000000..fd9ee0f0328 --- /dev/null +++ b/tests/wpt/web-platform-tests/referrer-policy/generic/iframe-upgrade-request.sub.https.html.headers @@ -0,0 +1,2 @@ +Content-Security-Policy: upgrade-insecure-requests +Referrer-Policy: origin-when-cross-origin diff --git a/tests/wpt/web-platform-tests/referrer-policy/generic/resources/referrer.py b/tests/wpt/web-platform-tests/referrer-policy/generic/resources/referrer.py new file mode 100644 index 00000000000..ada048dc490 --- /dev/null +++ b/tests/wpt/web-platform-tests/referrer-policy/generic/resources/referrer.py @@ -0,0 +1,3 @@ +def main(request, response): + response_headers = [(b"Access-Control-Allow-Origin", b"*")] + return (200, response_headers, request.headers.get("referer", "")) diff --git a/tests/wpt/web-platform-tests/resource-timing/SyntheticResponse.py b/tests/wpt/web-platform-tests/resource-timing/SyntheticResponse.py index c5158002f18..dc57d8451f9 100644 --- a/tests/wpt/web-platform-tests/resource-timing/SyntheticResponse.py +++ b/tests/wpt/web-platform-tests/resource-timing/SyntheticResponse.py @@ -1,23 +1,28 @@ -import urllib +from six.moves.urllib.parse import unquote + +from wptserve.utils import isomorphic_decode, isomorphic_encode + import sleep def main(request, response): - index = request.request_path.index("?") - args = request.request_path[index+1:].split("&") + index = isomorphic_encode(request.request_path).index(b"?") + args = isomorphic_encode(request.request_path[index+1:]).split(b"&") headers = [] statusSent = False headersSent = False for arg in args: - if arg.startswith("ignored"): + if arg.startswith(b"ignored"): continue - elif arg.endswith("ms"): + elif arg.endswith(b"ms"): sleep.sleep_at_least(float(arg[0:-2])) - elif arg.startswith("redirect:"): - return (302, "WEBPERF MARKETING"), [("Location", urllib.unquote(arg[9:]))], "TEST" - elif arg.startswith("mime:"): - headers.append(("Content-Type", urllib.unquote(arg[5:]))) - elif arg.startswith("send:"): - text = urllib.unquote(arg[5:]) + elif arg.startswith(b"redirect:"): + return (302, u"WEBPERF MARKETING"), [(b"Location", unquote(isomorphic_decode(arg[9:])))], u"TEST" + + elif arg.startswith(b"mime:"): + headers.append((b"Content-Type", unquote(isomorphic_decode(arg[5:])))) + + elif arg.startswith(b"send:"): + text = unquote(isomorphic_decode(arg[5:])) if not statusSent: # Default to a 200 status code. @@ -30,15 +35,15 @@ def main(request, response): headersSent = True response.writer.write_content(text) - elif arg.startswith("status:"): - code = int(urllib.unquote(arg[7:])) + elif arg.startswith(b"status:"): + code = int(unquote(isomorphic_decode(arg[7:]))) response.writer.write_status(code) if code // 100 == 1: # Terminate informational 1XX responses with an empty line. response.writer.end_headers() else: statusSent = True - elif arg == "flush": + elif arg == b"flush": response.writer.flush() # else: diff --git a/tests/wpt/web-platform-tests/resource-timing/resources/TAOResponse.py b/tests/wpt/web-platform-tests/resource-timing/resources/TAOResponse.py index e84912808da..908b68861ae 100644 --- a/tests/wpt/web-platform-tests/resource-timing/resources/TAOResponse.py +++ b/tests/wpt/web-platform-tests/resource-timing/resources/TAOResponse.py @@ -1,51 +1,51 @@ def main(request, response): - origin = request.headers['origin'] - response.headers.set('Access-Control-Allow-Origin', origin) + origin = request.headers[b'origin'] + response.headers.set(b'Access-Control-Allow-Origin', origin) - tao = request.GET.first('tao') + tao = request.GET.first(b'tao') - if tao == 'zero': + if tao == b'zero': # zero TAO value, fail pass - elif tao == 'wildcard': + elif tao == b'wildcard': # wildcard, pass - response.headers.set('Timing-Allow-Origin', '*') - elif tao == 'null': + response.headers.set(b'Timing-Allow-Origin', b'*') + elif tao == b'null': # null, fail unless it's an opaque origin - response.headers.set('Timing-Allow-Origin', 'null') - elif tao == 'Null': + response.headers.set(b'Timing-Allow-Origin', b'null') + elif tao == b'Null': # case-insentive null, fail - response.headers.set('Timing-Allow-Origin', 'Null') - elif tao == 'origin': + response.headers.set(b'Timing-Allow-Origin', b'Null') + elif tao == b'origin': # case-sensitive match for origin, pass - response.headers.set('Timing-Allow-Origin', origin) - elif tao.startswith('origin_port'): + response.headers.set(b'Timing-Allow-Origin', origin) + elif tao.startswith(b'origin_port'): # case-sensitive match for origin and port, pass - origin_parts = origin.split(':') - host = origin_parts[0] + ':' + origin_parts[1] - port = tao.split('origin_port_')[1] - response.headers.set('Timing-Allow-Origin', host + ':' + port) - elif tao == 'space': + origin_parts = origin.split(b':') + host = origin_parts[0] + b':' + origin_parts[1] + port = tao.split(b'origin_port_')[1] + response.headers.set(b'Timing-Allow-Origin', host + b':' + port) + elif tao == b'space': # space separated list of origin and wildcard, fail - response.headers.set('Timing-Allow-Origin', (origin + ' *')) - elif tao == 'multi': + response.headers.set(b'Timing-Allow-Origin', (origin + b' *')) + elif tao == b'multi': # more than one TAO values, separated by comma, pass - response.headers.set('Timing-Allow-Origin', origin) - response.headers.append('Timing-Allow-Origin', '*') - elif tao == 'multi_wildcard': + response.headers.set(b'Timing-Allow-Origin', origin) + response.headers.append(b'Timing-Allow-Origin', b'*') + elif tao == b'multi_wildcard': # multiple wildcards, separated by comma, pass - response.headers.set('Timing-Allow-Origin', '*') - response.headers.append('Timing-Allow-Origin', '*') - elif tao == 'match_origin': + response.headers.set(b'Timing-Allow-Origin', b'*') + response.headers.append(b'Timing-Allow-Origin', b'*') + elif tao == b'match_origin': # contains a match of origin, separated by comma, pass - response.headers.set('Timing-Allow-Origin', origin) - response.headers.append('Timing-Allow-Origin', "fake") - elif tao == 'match_wildcard': + response.headers.set(b'Timing-Allow-Origin', origin) + response.headers.append(b'Timing-Allow-Origin', b"fake") + elif tao == b'match_wildcard': # contains a wildcard, separated by comma, pass - response.headers.set('Timing-Allow-Origin', "fake") - response.headers.append('Timing-Allow-Origin', '*') - elif tao == 'uppercase': + response.headers.set(b'Timing-Allow-Origin', b"fake") + response.headers.append(b'Timing-Allow-Origin', b'*') + elif tao == b'uppercase': # non-case-sensitive match for origin, fail - response.headers.set('Timing-Allow-Origin', origin.upper()) + response.headers.set(b'Timing-Allow-Origin', origin.upper()) else: pass diff --git a/tests/wpt/web-platform-tests/resource-timing/resources/cors-ahem.py b/tests/wpt/web-platform-tests/resource-timing/resources/cors-ahem.py index 1998d47c492..7666b771ac3 100644 --- a/tests/wpt/web-platform-tests/resource-timing/resources/cors-ahem.py +++ b/tests/wpt/web-platform-tests/resource-timing/resources/cors-ahem.py @@ -1,17 +1,19 @@ import os.path -def main(request, response): - etag = "123abc" - if etag == request.headers.get("If-None-Match", None): - response.headers.set("X-HTTP-STATUS", 304) - response.status = (304, "Not Modified") - return "" +from wptserve.utils import isomorphic_decode - response.headers.set("Cache-Control", "public, max-age=86400") - response.headers.set("Content-Type", "font/truetype") - response.headers.set("Access-Control-Allow-Origin", "*") - response.headers.set("Timing-Allow-Origin", "*") - response.headers.set("ETag", etag) - font = "../../fonts/Ahem.ttf" - path = os.path.join(os.path.dirname(__file__), font) - response.content = open(path, "rb").read() +def main(request, response): + etag = b"123abc" + if etag == request.headers.get(b"If-None-Match", None): + response.headers.set(b"X-HTTP-STATUS", 304) + response.status = (304, u"Not Modified") + return u"" + + response.headers.set(b"Cache-Control", b"public, max-age=86400") + response.headers.set(b"Content-Type", b"font/truetype") + response.headers.set(b"Access-Control-Allow-Origin", b"*") + response.headers.set(b"Timing-Allow-Origin", b"*") + response.headers.set(b"ETag", etag) + font = u"../../fonts/Ahem.ttf" + path = os.path.join(os.path.dirname(isomorphic_decode(__file__)), font) + response.content = open(path, u"rb").read() diff --git a/tests/wpt/web-platform-tests/resource-timing/resources/empty.py b/tests/wpt/web-platform-tests/resource-timing/resources/empty.py index e5ccfbe9739..cae83c146e4 100644 --- a/tests/wpt/web-platform-tests/resource-timing/resources/empty.py +++ b/tests/wpt/web-platform-tests/resource-timing/resources/empty.py @@ -1,3 +1,3 @@ def main(request, response): - response.headers.set("Content-Type", "text/plain") - return "" + response.headers.set(b"Content-Type", b"text/plain") + return u"" diff --git a/tests/wpt/web-platform-tests/resource-timing/resources/eventsource.py b/tests/wpt/web-platform-tests/resource-timing/resources/eventsource.py index 5095ea9b34f..e3a2355730a 100644 --- a/tests/wpt/web-platform-tests/resource-timing/resources/eventsource.py +++ b/tests/wpt/web-platform-tests/resource-timing/resources/eventsource.py @@ -1,3 +1,3 @@ def main(request, response): - response.headers.set("Content-Type", "text/event-stream") - return "" + response.headers.set(b"Content-Type", b"text/event-stream") + return u"" diff --git a/tests/wpt/web-platform-tests/resource-timing/resources/fake_responses.py b/tests/wpt/web-platform-tests/resource-timing/resources/fake_responses.py index 289c1793176..359d7cfcff7 100644 --- a/tests/wpt/web-platform-tests/resource-timing/resources/fake_responses.py +++ b/tests/wpt/web-platform-tests/resource-timing/resources/fake_responses.py @@ -1,26 +1,26 @@ # /xhr/resources/conditional.py -- to fake a 304 response def main(request, response): - tag = request.GET.first("tag", None) - redirect = request.GET.first("redirect", None) - match = request.headers.get("If-None-Match", None) - date = request.GET.first("date", "") - modified = request.headers.get("If-Modified-Since", None) - response.headers.set("Access-Control-Allow-Origin", "*"); - response.headers.set("Timing-Allow-Origin", "*"); + tag = request.GET.first(b"tag", None) + redirect = request.GET.first(b"redirect", None) + match = request.headers.get(b"If-None-Match", None) + date = request.GET.first(b"date", b"") + modified = request.headers.get(b"If-Modified-Since", None) + response.headers.set(b"Access-Control-Allow-Origin", b"*"); + response.headers.set(b"Timing-Allow-Origin", b"*"); if tag: - response.headers.set("ETag", '"%s"' % tag) + response.headers.set(b"ETag", b'"%s"' % tag) elif date: - response.headers.set("Last-Modified", date) + response.headers.set(b"Last-Modified", date) if redirect: - response.headers.set("Location", redirect) - response.status = (302, "Moved") - return "" + response.headers.set(b"Location", redirect) + response.status = (302, u"Moved") + return u"" if ((match is not None and match == tag) or (modified is not None and modified == date)): - response.status = (304, "SUPERCOOL") - return "" + response.status = (304, u"SUPERCOOL") + return u"" else: - response.headers.set("Content-Type", "text/plain") - return "MAYBE NOT" + response.headers.set(b"Content-Type", b"text/plain") + return u"MAYBE NOT" diff --git a/tests/wpt/web-platform-tests/resource-timing/resources/gzip_xml.py b/tests/wpt/web-platform-tests/resource-timing/resources/gzip_xml.py index 31a769eb366..2293605ac33 100644 --- a/tests/wpt/web-platform-tests/resource-timing/resources/gzip_xml.py +++ b/tests/wpt/web-platform-tests/resource-timing/resources/gzip_xml.py @@ -1,20 +1,23 @@ import gzip as gzip_module -from cStringIO import StringIO import os +from six import BytesIO + +from wptserve.utils import isomorphic_decode + def main(request, response): - dir_path = os.path.dirname(os.path.realpath(__file__)) - file_path = os.path.join(dir_path, 'resource_timing_test0.xml') - f = open(file_path, 'r') + dir_path = os.path.dirname(os.path.realpath(isomorphic_decode(__file__))) + file_path = os.path.join(dir_path, u'resource_timing_test0.xml') + f = open(file_path, u'rb') output = f.read() - out = StringIO() + out = BytesIO() with gzip_module.GzipFile(fileobj=out, mode="w") as f: - f.write(output) + f.write(output) output = out.getvalue() - headers = [("Content-type", "text/plain"), - ("Content-Encoding", "gzip"), - ("Content-Length", len(output))] + headers = [(b"Content-type", b"text/plain"), + (b"Content-Encoding", b"gzip"), + (b"Content-Length", len(output))] return headers, output diff --git a/tests/wpt/web-platform-tests/resource-timing/resources/multi_redirect.py b/tests/wpt/web-platform-tests/resource-timing/resources/multi_redirect.py index 037d986c723..56f5d6c7715 100644 --- a/tests/wpt/web-platform-tests/resource-timing/resources/multi_redirect.py +++ b/tests/wpt/web-platform-tests/resource-timing/resources/multi_redirect.py @@ -1,3 +1,5 @@ +from wptserve.utils import isomorphic_encode + def main(request, response): """Handler that causes multiple redirections. Redirect chain is as follows: 1. Initial URL containing multi-redirect.py @@ -14,44 +16,43 @@ def main(request, response): Note that |step| is a parameter used internally for the multi-redirect. It's the step we're at in the redirect chain. """ step = 1 - if "step" in request.GET: + if b"step" in request.GET: try: - step = int(request.GET.first("step")) + step = int(request.GET.first(b"step")) except ValueError: pass - origin = request.url_parts.scheme + "://" + request.url_parts.hostname + ":" + str(request.url_parts.port) - page_origin = request.GET.first("page_origin") - cross_origin = request.GET.first("cross_origin") - final_resource = request.GET.first("final_resource") + page_origin = request.GET.first(b"page_origin") + cross_origin = request.GET.first(b"cross_origin") + final_resource = request.GET.first(b"final_resource") - tao_value = "*"; - if "tao_value" in request.GET: - tao_value = request.GET.first("tao_value") + tao_value = b"*" + if b"tao_value" in request.GET: + tao_value = request.GET.first(b"tao_value") tao_steps = 0 - if "tao_steps" in request.GET: - tao_steps = int(request.GET.first("tao_steps")) + if b"tao_steps" in request.GET: + tao_steps = int(request.GET.first(b"tao_steps")) next_tao_steps = tao_steps - 1 - redirect_url_path = "/resource-timing/resources/multi_redirect.py?" - redirect_url_path += "page_origin=" + page_origin - redirect_url_path += "&cross_origin=" + cross_origin - redirect_url_path += "&final_resource=" + final_resource - redirect_url_path += "&tao_value=" + tao_value - redirect_url_path += "&tao_steps=" + str(next_tao_steps) - redirect_url_path += "&step=" + redirect_url_path = b"/resource-timing/resources/multi_redirect.py?" + redirect_url_path += b"page_origin=" + page_origin + redirect_url_path += b"&cross_origin=" + cross_origin + redirect_url_path += b"&final_resource=" + final_resource + redirect_url_path += b"&tao_value=" + tao_value + redirect_url_path += b"&tao_steps=" + isomorphic_encode(str(next_tao_steps)) + redirect_url_path += b"&step=" if tao_steps > 0: - response.headers.set("timing-allow-origin", tao_value) + response.headers.set(b"timing-allow-origin", tao_value) if step == 1: # On the first request, redirect to a cross origin URL - redirect_url = cross_origin + redirect_url_path + "2" + redirect_url = cross_origin + redirect_url_path + b"2" elif step == 2: # On the second request, redirect to a same origin URL - redirect_url = page_origin + redirect_url_path + "3" + redirect_url = page_origin + redirect_url_path + b"3" else: # On the third request, redirect to a static response redirect_url = page_origin + final_resource response.status = 302 - response.headers.set("Location", redirect_url) + response.headers.set(b"Location", redirect_url) diff --git a/tests/wpt/web-platform-tests/resource-timing/resources/preflight.py b/tests/wpt/web-platform-tests/resource-timing/resources/preflight.py index 4fca99d0717..168850e2a88 100644 --- a/tests/wpt/web-platform-tests/resource-timing/resources/preflight.py +++ b/tests/wpt/web-platform-tests/resource-timing/resources/preflight.py @@ -1,8 +1,8 @@ def main(request, response): - response.headers.set("Access-Control-Allow-Origin", "*"); - response.headers.set("Access-Control-Max-Age", "0"); - response.headers.set("Timing-Allow-Origin", "*"); + response.headers.set(b"Access-Control-Allow-Origin", b"*"); + response.headers.set(b"Access-Control-Max-Age", b"0"); + response.headers.set(b"Timing-Allow-Origin", b"*"); # If this script is accessed with the header X-Require-Preflight then the # browser will send a preflight request. Otherwise it won't. - if request.method == 'OPTIONS': - response.headers.set("Access-Control-Allow-Headers", "X-Require-Preflight"); + if request.method == u'OPTIONS': + response.headers.set(b"Access-Control-Allow-Headers", b"X-Require-Preflight"); diff --git a/tests/wpt/web-platform-tests/resource-timing/resources/status-code.py b/tests/wpt/web-platform-tests/resource-timing/resources/status-code.py index 6ddad159524..9bc02bd3467 100644 --- a/tests/wpt/web-platform-tests/resource-timing/resources/status-code.py +++ b/tests/wpt/web-platform-tests/resource-timing/resources/status-code.py @@ -1,4 +1,4 @@ def main(request, response): - status = request.GET.first('status') - response.status = (status, ""); + status = request.GET.first(b'status') + response.status = (status, b""); diff --git a/tests/wpt/web-platform-tests/shadow-dom/declarative/innerhtml-before-closing-tag.html b/tests/wpt/web-platform-tests/shadow-dom/declarative/innerhtml-before-closing-tag.html new file mode 100644 index 00000000000..653c41918d1 --- /dev/null +++ b/tests/wpt/web-platform-tests/shadow-dom/declarative/innerhtml-before-closing-tag.html @@ -0,0 +1,42 @@ + +Declarative Shadow DOM innerHTML + + + + + + + + +
+ +
+ + + diff --git a/tests/wpt/web-platform-tests/shadow-dom/declarative/innerhtml-on-ordinary-template.html b/tests/wpt/web-platform-tests/shadow-dom/declarative/innerhtml-on-ordinary-template.html new file mode 100644 index 00000000000..988acc0ac79 --- /dev/null +++ b/tests/wpt/web-platform-tests/shadow-dom/declarative/innerhtml-on-ordinary-template.html @@ -0,0 +1,47 @@ + +Declarative Shadow DOM innerHTML + + + + + + + + +
+ + + +
+ + + diff --git a/tests/wpt/web-platform-tests/tools/.gitignore b/tests/wpt/web-platform-tests/tools/.gitignore index f888ce9935a..c59f948fdbe 100644 --- a/tests/wpt/web-platform-tests/tools/.gitignore +++ b/tests/wpt/web-platform-tests/tools/.gitignore @@ -10,3 +10,8 @@ coverage.xml *~ \#* runner/MANIFEST.json + +# WAVE +!wave/www/lib +!wave/export/lib +!wave/export/css diff --git a/tests/wpt/web-platform-tests/tools/ci/ci_tools_integration_test.sh b/tests/wpt/web-platform-tests/tools/ci/ci_tools_integration_test.sh index 7c37e7863e0..6929fb8ee0f 100755 --- a/tests/wpt/web-platform-tests/tools/ci/ci_tools_integration_test.sh +++ b/tests/wpt/web-platform-tests/tools/ci/ci_tools_integration_test.sh @@ -8,8 +8,16 @@ cd $WPT_ROOT main() { git fetch --quiet --unshallow https://github.com/web-platform-tests/wpt.git +refs/heads/*:refs/remotes/origin/* pip install --user -U tox codecov + + # wpt commands integration tests cd tools/wpt tox + cd $WPT_ROOT + + # WMAS test runner integration tests + cd tools/wave + tox + cd $WPT_ROOT } main diff --git a/tests/wpt/web-platform-tests/tools/pytest.ini b/tests/wpt/web-platform-tests/tools/pytest.ini index 6bdb4d563f6..140ce236ffd 100644 --- a/tests/wpt/web-platform-tests/tools/pytest.ini +++ b/tests/wpt/web-platform-tests/tools/pytest.ini @@ -1,5 +1,5 @@ [pytest] -norecursedirs = .* {arch} *.egg html5lib third_party pywebsocket six wpt wptrunner +norecursedirs = .* {arch} *.egg html5lib third_party pywebsocket six wave wpt wptrunner xfail_strict = true addopts = --strict-markers markers = diff --git a/tests/wpt/web-platform-tests/tools/serve/commands.json b/tests/wpt/web-platform-tests/tools/serve/commands.json index abcb2970b63..a5457b55a33 100644 --- a/tests/wpt/web-platform-tests/tools/serve/commands.json +++ b/tests/wpt/web-platform-tests/tools/serve/commands.json @@ -1,2 +1,18 @@ -{"serve": {"path": "serve.py", "script": "run", "parser": "get_parser", "help": "Run wptserve server", - "virtualenv": false}} +{ + "serve": { + "path": "serve.py", + "script": "run", + "parser": "get_parser", + "help": "Run wptserve server", + "virtualenv": false + }, + "serve-wave": { + "path": "wave.py", + "script": "run", + "parser": "get_parser", + "help": "Run wptserve server for WAVE", + "virtualenv": true, + "install": ["ua-parser"], + "requirements": ["../wave/requirements.txt"] + } +} diff --git a/tests/wpt/web-platform-tests/tools/serve/serve.py b/tests/wpt/web-platform-tests/tools/serve/serve.py index d98c81eafbb..db15ba4833c 100644 --- a/tests/wpt/web-platform-tests/tools/serve/serve.py +++ b/tests/wpt/web-platform-tests/tools/serve/serve.py @@ -328,7 +328,8 @@ class RoutesBuilder(object): self.forbidden = [("*", "/_certs/*", handlers.ErrorHandler(404)), ("*", "/tools/*", handlers.ErrorHandler(404)), - ("*", "{spec}/tools/*", handlers.ErrorHandler(404))] + ("*", "{spec}/tools/*", handlers.ErrorHandler(404)), + ("*", "/results/", handlers.ErrorHandler(404))] self.extra = [] @@ -384,7 +385,7 @@ class RoutesBuilder(object): self.mountpoint_routes[file_url] = [("GET", file_url, handlers.FileHandler(base_path=base_path, url_base=url_base))] -def build_routes(aliases): +def get_route_builder(aliases, config=None): builder = RoutesBuilder() for alias in aliases: url = alias["url-path"] @@ -396,7 +397,7 @@ def build_routes(aliases): builder.add_mount_point(url, directory) else: builder.add_file_mount_point(url, directory) - return builder.get_routes() + return builder class ServerProc(object): @@ -450,17 +451,16 @@ class ServerProc(object): return self.proc.is_alive() -def check_subdomains(config): +def check_subdomains(config, routes): paths = config.paths bind_address = config.bind_address - aliases = config.aliases host = config.server_host port = get_port() logger.debug("Going to use port %d to check subdomains" % port) wrapper = ServerProc() - wrapper.start(start_http_server, host, port, paths, build_routes(aliases), + wrapper.start(start_http_server, host, port, paths, routes, bind_address, config) url = "http://{}:{}/".format(host, port) @@ -780,45 +780,6 @@ def iter_procs(servers): yield server.proc -def build_config(override_path=None, **kwargs): - rv = ConfigBuilder() - - enable_http2 = kwargs.get("h2") - if enable_http2 is None: - enable_http2 = True - if enable_http2: - rv._default["ports"]["h2"] = [9000] - if kwargs.get("quic_transport"): - rv._default["ports"]["quic-transport"] = [10000] - - if override_path and os.path.exists(override_path): - with open(override_path) as f: - override_obj = json.load(f) - rv.update(override_obj) - - if kwargs.get("config_path"): - other_path = os.path.abspath(os.path.expanduser(kwargs.get("config_path"))) - if os.path.exists(other_path): - with open(other_path) as f: - override_obj = json.load(f) - rv.update(override_obj) - else: - raise ValueError("Config path %s does not exist" % other_path) - - overriding_path_args = [("doc_root", "Document root"), - ("ws_doc_root", "WebSockets document root")] - for key, title in overriding_path_args: - value = kwargs.get(key) - if value is None: - continue - value = os.path.abspath(os.path.expanduser(value)) - if not os.path.exists(value): - raise ValueError("%s path %s does not exist" % (title, value)) - setattr(rv, key, value) - - return rv - - def _make_subdomains_product(s, depth=2): return {u".".join(x) for x in chain(*(product(s, repeat=i) for i in range(1, depth+1)))} @@ -925,6 +886,43 @@ class ConfigBuilder(config.ConfigBuilder): return rv +def build_config(override_path=None, config_cls=ConfigBuilder, **kwargs): + rv = config_cls() + + enable_http2 = kwargs.get("h2") + if enable_http2 is None: + enable_http2 = True + if enable_http2: + rv._default["ports"]["h2"] = [9000] + + if override_path and os.path.exists(override_path): + with open(override_path) as f: + override_obj = json.load(f) + rv.update(override_obj) + + if kwargs.get("config_path"): + other_path = os.path.abspath(os.path.expanduser(kwargs.get("config_path"))) + if os.path.exists(other_path): + with open(other_path) as f: + override_obj = json.load(f) + rv.update(override_obj) + else: + raise ValueError("Config path %s does not exist" % other_path) + + overriding_path_args = [("doc_root", "Document root"), + ("ws_doc_root", "WebSockets document root")] + for key, title in overriding_path_args: + value = kwargs.get(key) + if value is None: + continue + value = os.path.abspath(os.path.expanduser(value)) + if not os.path.exists(value): + raise ValueError("%s path %s does not exist" % (title, value)) + setattr(rv, key, value) + + return rv + + def get_parser(): parser = argparse.ArgumentParser() parser.add_argument("--latency", type=int, @@ -942,13 +940,16 @@ def get_parser(): parser.add_argument("--no-h2", action="store_false", dest="h2", default=None, help="Disable the HTTP/2.0 server") parser.add_argument("--quic-transport", action="store_true", help="Enable QUIC server for WebTransport") + parser.set_defaults(report=False) + parser.set_defaults(is_wave=False) return parser -def run(**kwargs): +def run(config_cls=ConfigBuilder, route_builder=None, **kwargs): received_signal = threading.Event() with build_config(os.path.join(repo_root, "config.json"), + config_cls=config_cls, **kwargs) as config: global logger logger = config.logger @@ -971,8 +972,12 @@ def run(**kwargs): 'local-dir': doc_root, }) + if route_builder is None: + route_builder = get_route_builder + routes = route_builder(config.aliases, config).get_routes() + if config["check_subdomains"]: - check_subdomains(config) + check_subdomains(config, routes) stash_address = None if bind_address: @@ -980,7 +985,7 @@ def run(**kwargs): logger.debug("Going to use port %d for stash" % stash_address[1]) with stash.StashServer(stash_address, authkey=str(uuid.uuid4())): - servers = start(config, build_routes(config["aliases"]), **kwargs) + servers = start(config, routes, **kwargs) signal.signal(signal.SIGTERM, handle_signal) signal.signal(signal.SIGINT, handle_signal) diff --git a/tests/wpt/web-platform-tests/tools/serve/wave.py b/tests/wpt/web-platform-tests/tools/serve/wave.py new file mode 100644 index 00000000000..b13c6ef34de --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/serve/wave.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- + +import subprocess +from manifest import manifest +import localpaths +import logging +import os + +try: + from serve import serve +except ImportError: + import serve + +from tools.wpt import wpt + +global logger +logger = logging.getLogger("wave") + +def get_route_builder_func(report): + def get_route_builder(aliases, config=None): + wave_cfg = None + if config is not None and "wave" in config: + wave_cfg = config["wave"] + builder = serve.get_route_builder(aliases) + logger.debug("Loading manifest ...") + data = load_manifest() + from ..wave.wave_server import WaveServer + wave_server = WaveServer() + wave_server.initialize( + configuration_file_path=os.path.abspath("./config.json"), + reports_enabled=report, + tests=data["items"]) + + class WaveHandler(object): + def __call__(self, request, response): + wave_server.handle_request(request, response) + + web_root = "wave" + if wave_cfg is not None and "web_root" in wave_cfg: + web_root = wave_cfg["web_root"] + if not web_root.startswith("/"): + web_root = "/" + web_root + + wave_handler = WaveHandler() + builder.add_handler("*", web_root + "*", wave_handler) + # serving wave specifc testharnessreport.js + file_path = os.path.join(wpt.localpaths.repo_root, "tools/wave/resources/testharnessreport.js") + builder.add_static( + file_path, + {}, + "text/javascript;charset=utf8", + "/resources/testharnessreport.js") + + return builder + return get_route_builder + +class ConfigBuilder(serve.ConfigBuilder): + _default = serve.ConfigBuilder._default + _default.update({ + "wave": { # wave specific configuration parameters + "results": "./results", + "timeouts": { + "automatic": 60000, + "manual": 300000 + }, + "enable_results_import": False, + "web_root": "/_wave", + "persisting_interval": 20, + "api_titles": [] + } + }) + +def get_parser(): + parser = serve.get_parser() + # Added wave specific arguments + parser.add_argument("--report", action="store_true", dest="report", + help="Flag for enabling the WPTReporting server") + return parser + +def run(venv=None, **kwargs): + if venv is not None: + venv.start() + else: + raise Exception("Missing virtualenv for serve-wave.") + + if kwargs['report'] is True: + if not is_wptreport_installed(): + raise Exception("wptreport is not installed. Please install it from https://github.com/w3c/wptreport") + + serve.run(config_cls=ConfigBuilder, + route_builder=get_route_builder_func(kwargs["report"]), **kwargs) + +# execute wptreport version check +def is_wptreport_installed(): + try: + subprocess.check_output(["wptreport", "--help"]) + return True + except Exception: + return False + + +def load_manifest(): + root = localpaths.repo_root + path = os.path.join(root, "MANIFEST.json") + manifest_file = manifest.load_and_update(root, path, "/", parallel=False) + + supported_types = ["testharness", "manual"] + data = {"items": {}, + "url_base": "/"} + for item_type in supported_types: + data["items"][item_type] = {} + for item_type, path, tests in manifest_file.itertypes(*supported_types): + tests_data = [] + for item in tests: + test_data = [item.url[1:]] + if item_type == "reftest": + test_data.append(item.references) + test_data.append({}) + if item_type != "manual": + test_data[-1]["timeout"] = item.timeout + tests_data.append(test_data) + assert path not in data["items"][item_type] + data["items"][item_type][path] = tests_data + return data diff --git a/tests/wpt/web-platform-tests/tools/wave/__init__.py b/tests/wpt/web-platform-tests/tools/wave/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/wpt/web-platform-tests/tools/wave/config.default.json b/tests/wpt/web-platform-tests/tools/wave/config.default.json new file mode 100644 index 00000000000..8a05b91060a --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/wave/config.default.json @@ -0,0 +1,45 @@ +{ + "browser_host": "web-platform.test", + "alternate_hosts": { + "alt": "not-web-platform.test" + }, + "doc_root": ".", + "ws_doc_root": "./websockets/handlers", + "server_host": null, + "ports": { + "http": [8000, "auto"], + "https": [8443], + "ws": ["auto"], + "wss": ["auto"] + }, + "check_subdomains": true, + "log_level": "debug", + "bind_address": true, + "ssl": { + "type": "pregenerated", + "encrypt_after_connect": false, + "openssl": { + "openssl_binary": "openssl", + "base_path": "_certs", + "force_regenerate": false, + "base_conf_path": null + }, + "pregenerated": { + "host_key_path": "./tools/certs/web-platform.test.key", + "host_cert_path": "./tools/certs/web-platform.test.pem" + }, + "none": {} + }, + "aliases": [], + "wave": { + "results": "./results", + "timeouts": { + "automatic": 60000, + "manual": 300000 + }, + "enable_results_import": false, + "web_root": "/_wave", + "persisting_interval": 20, + "api_titles": [] + } +} diff --git a/tests/wpt/web-platform-tests/tools/wave/configuration_loader.py b/tests/wpt/web-platform-tests/tools/wave/configuration_loader.py new file mode 100644 index 00000000000..a94ebcd60a1 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/wave/configuration_loader.py @@ -0,0 +1,81 @@ +from __future__ import absolute_import +from __future__ import unicode_literals +import json +import os +from io import open + +from tools.wpt import wpt + +DEFAULT_CONFIGURATION_FILE_PATH = os.path.join(wpt.localpaths.repo_root, "./tools/wave/config.default.json") + + +def load(configuration_file_path): + configuration = {} + if configuration_file_path: + configuration = load_configuration_file(configuration_file_path) + default_configuration = load_configuration_file( + DEFAULT_CONFIGURATION_FILE_PATH) + + configuration["wpt_port"] = configuration.get( + "ports", default_configuration["ports"]).get( + "http", default_configuration["ports"]["http"])[0] + configuration["wpt_ssl_port"] = configuration.get( + "ports", default_configuration["ports"]).get( + "https", default_configuration["ports"]["https"])[0] + + web_root = configuration.get( + "wave", default_configuration["wave"]).get( + "web_root", default_configuration["wave"]["web_root"]) + if not web_root.startswith("/"): + web_root = "/" + web_root + if not web_root.endswith("/"): + web_root += "/" + configuration["web_root"] = web_root + + configuration["results_directory_path"] = configuration.get( + "wave", default_configuration["wave"]).get( + "results", default_configuration["wave"]["results"]) + + configuration["timeouts"] = {} + configuration["timeouts"]["automatic"] = configuration.get( + "wave", default_configuration["wave"]).get( + "timeouts", default_configuration["wave"]["timeouts"]).get( + "automatic", default_configuration["wave"]["timeouts"]["automatic"]) + configuration["timeouts"]["manual"] = configuration.get( + "wave", default_configuration["wave"]).get( + "timeouts", default_configuration["wave"]["timeouts"]).get( + "manual", default_configuration["wave"]["timeouts"]["manual"]) + + configuration["hostname"] = configuration.get( + "browser_host", default_configuration["browser_host"]) + + configuration["import_enabled"] = configuration.get( + "wave", default_configuration["wave"]).get( + "enable_results_import", + default_configuration["wave"]["enable_results_import"]) + + configuration["persisting_interval"] = configuration.get( + "wave", default_configuration["wave"]).get( + "persisting_interval", default_configuration["wave"]["persisting_interval"]) + + configuration["tests_directory_path"] = os.getcwd() + + configuration["manifest_file_path"] = os.path.join( + os.getcwd(), "MANIFEST.json") + + configuration["api_titles"] = configuration.get( + "wave", default_configuration["wave"]).get( + "api_titles", default_configuration["wave"]["api_titles"]) + + return configuration + + +def load_configuration_file(path): + if not os.path.isfile(path): + return {} + + configuration = None + with open(path, "r") as configuration_file: + configuration_file_content = configuration_file.read() + configuration = json.loads(configuration_file_content) + return configuration diff --git a/tests/wpt/web-platform-tests/tools/wave/data/__init__.py b/tests/wpt/web-platform-tests/tools/wave/data/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/wpt/web-platform-tests/tools/wave/data/client.py b/tests/wpt/web-platform-tests/tools/wave/data/client.py new file mode 100644 index 00000000000..ab6851ab34b --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/wave/data/client.py @@ -0,0 +1,6 @@ +class Client(object): + def __init__(self, session_token): + self.session_token = session_token + + def send_message(self, message): + raise Exception("Client.send_message(message) not implemented!") diff --git a/tests/wpt/web-platform-tests/tools/wave/data/exceptions/__init__.py b/tests/wpt/web-platform-tests/tools/wave/data/exceptions/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/wpt/web-platform-tests/tools/wave/data/exceptions/duplicate_exception.py b/tests/wpt/web-platform-tests/tools/wave/data/exceptions/duplicate_exception.py new file mode 100644 index 00000000000..2d64ea51bd9 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/wave/data/exceptions/duplicate_exception.py @@ -0,0 +1,2 @@ +class DuplicateException(Exception): + pass diff --git a/tests/wpt/web-platform-tests/tools/wave/data/exceptions/invalid_data_exception.py b/tests/wpt/web-platform-tests/tools/wave/data/exceptions/invalid_data_exception.py new file mode 100644 index 00000000000..50c7e8f3727 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/wave/data/exceptions/invalid_data_exception.py @@ -0,0 +1,2 @@ +class InvalidDataException(Exception): + pass diff --git a/tests/wpt/web-platform-tests/tools/wave/data/exceptions/not_found_exception.py b/tests/wpt/web-platform-tests/tools/wave/data/exceptions/not_found_exception.py new file mode 100644 index 00000000000..0e573506add --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/wave/data/exceptions/not_found_exception.py @@ -0,0 +1,2 @@ +class NotFoundException(Exception): + pass diff --git a/tests/wpt/web-platform-tests/tools/wave/data/exceptions/permission_denied_exception.py b/tests/wpt/web-platform-tests/tools/wave/data/exceptions/permission_denied_exception.py new file mode 100644 index 00000000000..e51660f678a --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/wave/data/exceptions/permission_denied_exception.py @@ -0,0 +1,2 @@ +class PermissionDeniedException(Exception): + pass diff --git a/tests/wpt/web-platform-tests/tools/wave/data/http_polling_client.py b/tests/wpt/web-platform-tests/tools/wave/data/http_polling_client.py new file mode 100644 index 00000000000..740f547c958 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/wave/data/http_polling_client.py @@ -0,0 +1,11 @@ +from .client import Client + + +class HttpPollingClient(Client): + def __init__(self, session_token, event): + super(HttpPollingClient, self).__init__(session_token) + self.event = event + + def send_message(self, message): + self.message = message + self.event.set() diff --git a/tests/wpt/web-platform-tests/tools/wave/data/session.py b/tests/wpt/web-platform-tests/tools/wave/data/session.py new file mode 100644 index 00000000000..df162fcb082 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/wave/data/session.py @@ -0,0 +1,86 @@ +from __future__ import absolute_import +from __future__ import unicode_literals +from ..testing.test_loader import MANUAL, AUTOMATIC + +PAUSED = "paused" +RUNNING = "running" +COMPLETED = "completed" +ABORTED = "aborted" +PENDING = "pending" +UNKNOWN = "unknown" + + +class Session(object): + def __init__( + self, + token=None, + types=None, + user_agent=None, + labels=None, + tests=None, + pending_tests=None, + running_tests=None, + timeouts=None, + status=None, + test_state=None, + last_completed_test=None, + recent_completed_count=None, + date_started=None, + date_finished=None, + is_public=None, + reference_tokens=None, + browser=None, + webhook_urls=None, + expiration_date=None, + malfunctioning_tests=None + ): + if token is None: + token = "" + self.token = token + if types is None: + types = [AUTOMATIC, MANUAL] + self.types = types + if user_agent is None: + user_agent = "" + self.user_agent = user_agent + if labels is None: + labels = [] + self.labels = labels + if tests is None: + tests = {} + self.tests = tests + if pending_tests is None: + pending_tests = {} + self.pending_tests = pending_tests + if running_tests is None: + running_tests = {} + self.running_tests = running_tests + if timeouts is None: + timeouts = {} + self.timeouts = timeouts + if status is None: + status = UNKNOWN + self.status = status + if test_state is None: + test_state = {} + self.test_state = test_state + self.last_completed_test = last_completed_test + if recent_completed_count is None: + recent_completed_count = 0 + self.recent_completed_count = recent_completed_count + self.date_started = date_started + self.date_finished = date_finished + if is_public is None: + is_public = False + self.is_public = is_public + if reference_tokens is None: + reference_tokens = [] + self.reference_tokens = reference_tokens + self.browser = browser + if webhook_urls is None: + webhook_urls = [] + self.webhook_urls = webhook_urls + self.expiration_date = expiration_date + if malfunctioning_tests is None: + malfunctioning_tests = [] + self.malfunctioning_tests = malfunctioning_tests diff --git a/tests/wpt/web-platform-tests/tools/wave/docs/README.md b/tests/wpt/web-platform-tests/tools/wave/docs/README.md new file mode 100644 index 00000000000..6cf7d397f84 --- /dev/null +++ b/tests/wpt/web-platform-tests/tools/wave/docs/README.md @@ -0,0 +1,4 @@ +# WAVE Test Suite Documentation + +- [REST API](./rest-api/README.md) +- [Usage Guide](./usage/usage.md) \ No newline at end of file diff --git a/tests/wpt/web-platform-tests/tools/wave/docs/res/configuration_page_bottom.jpg b/tests/wpt/web-platform-tests/tools/wave/docs/res/configuration_page_bottom.jpg new file mode 100644 index 0000000000000000000000000000000000000000..85d4bdc3fc87d6d0154836c6f8d5eedf4639086b GIT binary patch literal 126736 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