123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- /*
- * This file is intended for vendors to implement code needed to integrate
- * testharness.js tests with their own test systems.
- *
- * Typically such integration will attach callbacks when each test is
- * has run, using add_result_callback(callback(test)), or when the whole test
- * file has completed, using add_completion_callback(callback(tests,
- * harness_status)).
- *
- * For more documentation about the callback functions and the
- * parameters they are called with, see testharness.js, or the docs at:
- * https://web-platform-tests.org/writing-tests/testharness-api.html
- */
- (function() {
- let outputDocument = document;
- let didDispatchLoadEvent = false;
- let localPathRegExp = undefined;
- // Setup for Blink JavaScript tests. self.testRunner is expected to be
- // present when tests are run.
- if (self.testRunner) {
- testRunner.dumpAsText();
- testRunner.waitUntilDone();
- testRunner.setPopupBlockingEnabled(false);
- testRunner.setDumpJavaScriptDialogs(false);
- // Some tests intentionally load mixed content in order to test the
- // referrer policy, so WebKitAllowRunningInsecureContent must be set
- // to true or else the load would be blocked.
- const paths = [
- 'service-workers/service-worker/fetch-event-referrer-policy.https.html',
- 'beacon/headers/header-referrer-no-referrer-when-downgrade.https.html',
- 'beacon/headers/header-referrer-strict-origin-when-cross-origin.https.html',
- 'beacon/headers/header-referrer-strict-origin.https.html',
- 'beacon/headers/header-referrer-unsafe-url.https.html',
- ];
- for (const path of paths) {
- if (document.URL.endsWith(path)) {
- testRunner.overridePreference('WebKitAllowRunningInsecureContent', true);
- break;
- }
- }
- }
- if (document.URL.startsWith('file:///')) {
- const index = document.URL.indexOf('/external/wpt');
- if (index >= 0) {
- const localPath = document.URL.substring(
- 'file:///'.length, index + '/external/wpt'.length);
- localPathRegExp = new RegExp(localPath.replace(/(\W)/g, '\\$1'), 'g');
- }
- }
- window.addEventListener('load', loadCallback, {'once': true});
- setup({
- // The default output formats test results into an HTML table, but for
- // the Blink layout test runner, we dump the results as text in the
- // completion callback, so we disable the default output.
- 'output': false,
- // The Blink layout test runner has its own timeout mechanism.
- 'explicit_timeout': true
- });
- add_start_callback(startCallback);
- add_completion_callback(completionCallback);
- /** Loads an automation script if necessary. */
- function loadCallback() {
- didDispatchLoadEvent = true;
- if (isWPTManualTest()) {
- setTimeout(loadAutomationScript, 0);
- }
- }
- /** Checks whether the current path is a manual test in WPT. */
- function isWPTManualTest() {
- // Here we assume that if wptserve is running, then the hostname
- // is web-platform.test.
- const path = location.pathname;
- if (location.hostname == 'web-platform.test' &&
- /.*-manual(\.sub)?(\.https)?(\.tentative)?\.html$/.test(path)) {
- return true;
- }
- // If the file is loaded locally via file://, it must include
- // the wpt directory in the path.
- return /\/external\/wpt\/.*-manual(\.sub)?(\.https)?(\.tentative)?\.html$/.test(path);
- }
- /** Loads the WPT automation script for the current test, if applicable. */
- function loadAutomationScript() {
- const pathAndBase = pathAndBaseNameInWPT();
- if (!pathAndBase) {
- return;
- }
- let automationPath = location.pathname.replace(
- /\/external\/wpt\/.*$/, '/external/wpt_automation');
- if (location.hostname == 'web-platform.test') {
- automationPath = '/wpt_automation';
- }
- // Export importAutomationScript for use by the automation scripts.
- window.importAutomationScript = function(relativePath) {
- const script = document.createElement('script');
- script.src = automationPath + relativePath;
- document.head.appendChild(script);
- };
- let src;
- if (pathAndBase.startsWith('/fullscreen/')) {
- // Fullscreen tests all use the same automation script.
- src = automationPath + '/fullscreen/auto-click.js';
- } else if (pathAndBase.startsWith('/file-system-access/local_')) {
- // local_ File System Access tests all use the same automation script.
- src = automationPath + '/file-system-access/auto-pick-folder.js';
- } else if (pathAndBase.startsWith('/file-system-access/')) {
- // Per-test automation scripts.
- src = automationPath + pathAndBase + '-automation.sub.js';
- } else if (
- pathAndBase.startsWith('/css/') ||
- pathAndBase.startsWith('/pointerevents/') ||
- pathAndBase.startsWith('/uievents/') ||
- pathAndBase.startsWith('/html/') ||
- pathAndBase.startsWith('/input-events/') ||
- pathAndBase.startsWith('/css/selectors/') ||
- pathAndBase.startsWith('/css/cssom-view/') ||
- pathAndBase.startsWith('/css/css-scroll-snap/') ||
- pathAndBase.startsWith('/dom/events/') ||
- pathAndBase.startsWith('/feature-policy/experimental-features/') ||
- pathAndBase.startsWith('/permissions-policy/experimental-features/')) {
- // Per-test automation scripts.
- src = automationPath + pathAndBase + '-automation.js';
- } else {
- return;
- }
- const script = document.createElement('script');
- script.src = src;
- document.head.appendChild(script);
- }
- /**
- * Sets the output document based on the given properties.
- * Usually the output document is the current document, but it could be
- * a separate document in some cases.
- */
- function startCallback(properties) {
- if (properties.output_document) {
- outputDocument = properties.output_document;
- }
- }
- /**
- * Adds results to the page in a manner that allows dumpAsText to produce
- * readable test results.
- */
- function completionCallback(tests, harness_status) {
- const xhtmlNS = 'http://www.w3.org/1999/xhtml';
- // Create element to hold results.
- const resultsElement = outputDocument.createElementNS(xhtmlNS, 'pre');
- resultsElement.style.whiteSpace = 'pre-wrap';
- resultsElement.style.lineHeight = '1.5';
- // Declare result string.
- let resultStr = 'This is a testharness.js-based test.\n';
- // Check harness_status. If it is not 0, tests did not execute
- // correctly, output the error code and message.
- if (harness_status.status != 0) {
- resultStr += `Harness Error. ` +
- `harness_status.status = ${harness_status.status} , ` +
- `harness_status.message = ${harness_status.message}\n`;
- }
- // Output failure metrics if there are many.
- const resultCounts = countResultTypes(tests);
- if (outputDocument.URL.indexOf('://web-platform.test') >= 0 &&
- tests.length >= 50 &&
- (resultCounts[1] || resultCounts[2] || resultCounts[3])) {
- resultStr += failureMetricSummary(resultCounts);
- }
- if (outputDocument.URL.indexOf('/html/dom/reflection') >= 0) {
- resultStr += compactTestOutput(tests);
- } else {
- resultStr += testOutput(tests);
- }
- resultStr += 'Harness: the test ran to completion.\n';
- resultsElement.textContent = resultStr;
- function done() {
- let body = null;
- if (outputDocument.body && outputDocument.body.tagName == 'BODY' &&
- outputDocument.body.namespaceURI == xhtmlNS) {
- body = outputDocument.body;
- }
- // A temporary workaround since |window.self| property lookup starts
- // failing if the frame is detached. |outputDocument| may be an
- // ancestor of |self| so clearing |textContent| may detach |self|.
- // To get around this, cache window.self now and use the cached
- // value.
- // TODO(dcheng): Remove this hack after fixing window/self/frames
- // lookup in https://crbug.com/618672
- const cachedSelf = window.self;
- if (cachedSelf.testRunner) {
- // The following DOM operations may show console messages. We
- // suppress them because they are not related to the running
- // test.
- testRunner.setDumpConsoleMessages(false);
- // Anything in the body isn't part of the output and so should
- // be hidden from the text dump.
- if (body) {
- body.textContent = '';
- }
- }
- // Add the results element to the output document.
- if (!body) {
- // |outputDocument| might be an SVG document.
- if (outputDocument.documentElement) {
- outputDocument.documentElement.remove();
- }
- let html = outputDocument.createElementNS(xhtmlNS, 'html');
- outputDocument.appendChild(html);
- body = outputDocument.createElementNS(xhtmlNS, 'body');
- body.setAttribute('style', 'white-space:pre;');
- html.appendChild(body);
- }
- outputDocument.body.appendChild(resultsElement);
- // IFrames running tests should not complete the harness as the parent
- // page will.
- let shouldCompleteHarness = (window.self == window.top);
- if (cachedSelf.testRunner && shouldCompleteHarness) {
- testRunner.notifyDone();
- }
- }
- if (didDispatchLoadEvent || outputDocument.readyState != 'loading') {
- // This function might not be the last completion callback, and
- // another completion callback might generate more results.
- // So, we don't dump the results immediately.
- setTimeout(done, 0);
- } else {
- // Parsing the test HTML isn't finished yet.
- window.addEventListener('load', done);
- }
- }
- /**
- * Returns a directory part relative to WPT root and a basename part of the
- * current test. e.g.
- * Current test: file:///.../LayoutTests/external/wpt/pointerevents/foobar.html
- * Output: "/pointerevents/foobar"
- */
- function pathAndBaseNameInWPT() {
- const path = location.pathname;
- let matches;
- if (location.hostname == 'web-platform.test') {
- matches = path.match(/^(\/.*)\.html$/);
- return matches ? matches[1] : null;
- }
- matches = path.match(/external\/wpt(\/.*)\.html$/);
- return matches ? matches[1] : null;
- }
- /** Converts the testharness test status into the corresponding string. */
- function convertResult(resultStatus) {
- switch (resultStatus) {
- case 0:
- return 'PASS';
- case 1:
- return 'FAIL';
- case 2:
- return 'TIMEOUT';
- default:
- return 'NOTRUN';
- }
- }
- /**
- * Returns a compact output for reflection test results.
- *
- * The reflection tests contain a large number of tests.
- * This test output merges PASS lines to make baselines smaller.
- */
- function compactTestOutput(tests) {
- let testResults = [];
- for (let i = 0; i < tests.length; ++i) {
- if (tests[i].status == 0) {
- const colon = tests[i].name.indexOf(':');
- if (colon > 0) {
- const prefix = tests[i].name.substring(0, colon + 1);
- let j = i + 1;
- for (; j < tests.length; ++j) {
- if (!tests[j].name.startsWith(prefix) ||
- tests[j].status != 0)
- break;
- }
- const numPasses = j - i;
- if (numPasses > 1) {
- testResults.push(
- `${convertResult(tests[i].status)} ` +
- `${sanitize(prefix)} ${numPasses} tests\n`);
- i = j - 1;
- continue;
- }
- }
- }
- testResults.push(resultLine(tests[i]));
- }
- return testResults.join('');
- }
- function testOutput(tests) {
- let testResults = '';
- window.tests = tests;
- for (let test of tests) {
- testResults += resultLine(test);
- }
- return testResults;
- }
- function resultLine(test) {
- let result = `${convertResult(test.status)} ${sanitize(test.name)}`;
- if (test.message) {
- result += ' ' + sanitize(test.message).trim();
- }
- return result + '\n';
- }
- /** Prepares the given text for display in test results. */
- function sanitize(text) {
- if (!text) {
- return '';
- }
- // Escape null characters, otherwise diff will think the file is binary.
- text = text.replace(/\0/g, '\\0');
- // Escape some special characters to improve readability of the output.
- text = text.replace(/\r/g, '\\r');
- // Replace machine-dependent path with "...".
- if (localPathRegExp) {
- text = text.replace(localPathRegExp, '...');
- }
- return text;
- }
- function countResultTypes(tests) {
- const resultCounts = [0, 0, 0, 0];
- for (let test of tests) {
- resultCounts[test.status]++;
- }
- return resultCounts;
- }
- function failureMetricSummary(resultCounts) {
- const total = resultCounts[0] + resultCounts[1] + resultCounts[2] + resultCounts[3];
- return `Found ${total} tests;` +
- ` ${resultCounts[0]} PASS,` +
- ` ${resultCounts[1]} FAIL,` +
- ` ${resultCounts[2]} TIMEOUT,` +
- ` ${resultCounts[3]} NOTRUN.\n`;
- }
- })();
|