diff --git a/infrastructure/metadata/infrastructure/test262/basic-failure.js.ini b/infrastructure/metadata/infrastructure/test262/basic-failure.js.ini new file mode 100644 index 00000000000000..f09895219ef187 --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/basic-failure.js.ini @@ -0,0 +1,4 @@ +[basic-failure.test262.html] + expected: OK + [Test] + expected: FAIL diff --git a/infrastructure/metadata/infrastructure/test262/basic.js.ini b/infrastructure/metadata/infrastructure/test262/basic.js.ini new file mode 100644 index 00000000000000..390c424933c2bf --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/basic.js.ini @@ -0,0 +1,4 @@ +[basic.test262.html] + expected: OK + [Test] + expected: PASS diff --git a/infrastructure/metadata/infrastructure/test262/error.js.ini b/infrastructure/metadata/infrastructure/test262/error.js.ini new file mode 100644 index 00000000000000..76852fe1949d5c --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/error.js.ini @@ -0,0 +1,4 @@ +[error.test262.html] + expected: OK + [Test] + expected: FAIL diff --git a/infrastructure/metadata/infrastructure/test262/module-error.js.ini b/infrastructure/metadata/infrastructure/test262/module-error.js.ini new file mode 100644 index 00000000000000..242ba0b2e1186a --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/module-error.js.ini @@ -0,0 +1,4 @@ +[module-error.test262-module.html] + expected: ERROR + [Test] + expected: FAIL diff --git a/infrastructure/metadata/infrastructure/test262/module-failure.js.ini b/infrastructure/metadata/infrastructure/test262/module-failure.js.ini new file mode 100644 index 00000000000000..259ff3ed8a942f --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/module-failure.js.ini @@ -0,0 +1,4 @@ +[module-failure.test262-module.html] + expected: OK + [Test] + expected: FAIL diff --git a/infrastructure/metadata/infrastructure/test262/module.js.ini b/infrastructure/metadata/infrastructure/test262/module.js.ini new file mode 100644 index 00000000000000..a615125e6d3458 --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/module.js.ini @@ -0,0 +1,4 @@ +[module.test262-module.html] + expected: OK + [Test] + expected: PASS diff --git a/infrastructure/metadata/infrastructure/test262/negative-error.js.ini b/infrastructure/metadata/infrastructure/test262/negative-error.js.ini new file mode 100644 index 00000000000000..666389bfbe032e --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/negative-error.js.ini @@ -0,0 +1,4 @@ +[negative-error.test262.html] + expected: ERROR + [Test] + expected: FAIL diff --git a/infrastructure/metadata/infrastructure/test262/negative-failure.js.ini b/infrastructure/metadata/infrastructure/test262/negative-failure.js.ini new file mode 100644 index 00000000000000..2d7ad8eb0c3497 --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/negative-failure.js.ini @@ -0,0 +1,4 @@ +[negative-failure.test262.html] + expected: OK + [Test] + expected: FAIL diff --git a/infrastructure/metadata/infrastructure/test262/negative.js.ini b/infrastructure/metadata/infrastructure/test262/negative.js.ini new file mode 100644 index 00000000000000..d2ba995c1925fc --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/negative.js.ini @@ -0,0 +1,4 @@ +[negative.test262.html] + expected: OK + [Test] + expected: PASS diff --git a/infrastructure/metadata/infrastructure/test262/strict-error.js.ini b/infrastructure/metadata/infrastructure/test262/strict-error.js.ini new file mode 100644 index 00000000000000..312ba4e05911de --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/strict-error.js.ini @@ -0,0 +1,4 @@ +[strict-error.test262.strict.html] + expected: ERROR + [Test] + expected: FAIL diff --git a/infrastructure/metadata/infrastructure/test262/strict-failure.js.ini b/infrastructure/metadata/infrastructure/test262/strict-failure.js.ini new file mode 100644 index 00000000000000..646caf613bf8ef --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/strict-failure.js.ini @@ -0,0 +1,4 @@ +[strict-failure.test262.strict.html] + expected: OK + [Test] + expected: FAIL diff --git a/infrastructure/metadata/infrastructure/test262/strict.js.ini b/infrastructure/metadata/infrastructure/test262/strict.js.ini new file mode 100644 index 00000000000000..7006bf6ea90b6e --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/strict.js.ini @@ -0,0 +1,4 @@ +[strict.test262.html] + expected: OK + [Test] + expected: PASS diff --git a/infrastructure/metadata/infrastructure/test262/unexpected-error.js.ini b/infrastructure/metadata/infrastructure/test262/unexpected-error.js.ini new file mode 100644 index 00000000000000..b62ca2ad14ba7b --- /dev/null +++ b/infrastructure/metadata/infrastructure/test262/unexpected-error.js.ini @@ -0,0 +1,4 @@ +[unexpected-error.test262.html] + expected: ERROR + [Test] + expected: FAIL diff --git a/infrastructure/test262/basic-failure.js b/infrastructure/test262/basic-failure.js new file mode 100644 index 00000000000000..613183fd4c7f63 --- /dev/null +++ b/infrastructure/test262/basic-failure.js @@ -0,0 +1,6 @@ +/*--- +description: A basic Test262 smoketest that fails +features: [Test262] +---*/ + +assert.sameValue(1, 2, "One should be two"); diff --git a/infrastructure/test262/basic.js b/infrastructure/test262/basic.js new file mode 100644 index 00000000000000..439a1220da4cc2 --- /dev/null +++ b/infrastructure/test262/basic.js @@ -0,0 +1,6 @@ +/*--- +description: A basic Test262 smoketest +features: [Test262] +---*/ + +assert.sameValue(1, 1, "One should be one"); diff --git a/infrastructure/test262/error.js b/infrastructure/test262/error.js new file mode 100644 index 00000000000000..3b0f7762aadeaa --- /dev/null +++ b/infrastructure/test262/error.js @@ -0,0 +1,5 @@ +/*--- +description: A smoketest that throws a Test262Error +---*/ + +throw new Test262Error("This is a deliberate test failure."); diff --git a/infrastructure/test262/module-error.js b/infrastructure/test262/module-error.js new file mode 100644 index 00000000000000..439371f5922085 --- /dev/null +++ b/infrastructure/test262/module-error.js @@ -0,0 +1,6 @@ +/*--- +description: A module smoketest that throws an unexpected ReferenceError. +flags: [module] +---*/ +import { a } from "./support/module-helper.js"; +foo.bar(); diff --git a/infrastructure/test262/module-failure.js b/infrastructure/test262/module-failure.js new file mode 100644 index 00000000000000..33ecad9eccfed7 --- /dev/null +++ b/infrastructure/test262/module-failure.js @@ -0,0 +1,6 @@ +/*--- +description: A module smoketest that fails an assertion. +flags: [module] +---*/ +import { a } from "./support/module-helper.js"; +assert.sameValue(a, 2, "a should be 2 in module"); diff --git a/infrastructure/test262/module.js b/infrastructure/test262/module.js new file mode 100644 index 00000000000000..8a3642fe4c4d89 --- /dev/null +++ b/infrastructure/test262/module.js @@ -0,0 +1,5 @@ +/*--- +description: A module smoketest +flags: [module] +---*/ +import { a } from "./support/module-helper.js"; diff --git a/infrastructure/test262/negative-error.js b/infrastructure/test262/negative-error.js new file mode 100644 index 00000000000000..2a784d688fe955 --- /dev/null +++ b/infrastructure/test262/negative-error.js @@ -0,0 +1,8 @@ +/*--- +description: A negative test that throws a different error than expected. +negative: + phase: runtime + type: TypeError +---*/ + +throw new RangeError(); diff --git a/infrastructure/test262/negative-failure.js b/infrastructure/test262/negative-failure.js new file mode 100644 index 00000000000000..883a8ad928ac2e --- /dev/null +++ b/infrastructure/test262/negative-failure.js @@ -0,0 +1,6 @@ +/*--- +description: A negative smoketest that is expected to fail because it does not throw an error. +negative: + phase: runtime + type: Test262Error +---*/ diff --git a/infrastructure/test262/negative.js b/infrastructure/test262/negative.js new file mode 100644 index 00000000000000..f7c8bb26242cb4 --- /dev/null +++ b/infrastructure/test262/negative.js @@ -0,0 +1,7 @@ +/*--- +description: A negative smoketest +negative: + phase: runtime + type: Test262Error +---*/ +throw new Test262Error(); diff --git a/infrastructure/test262/strict-error.js b/infrastructure/test262/strict-error.js new file mode 100644 index 00000000000000..7280d62c5d500b --- /dev/null +++ b/infrastructure/test262/strict-error.js @@ -0,0 +1,6 @@ +/*--- +description: A strict mode smoketest that throws an unexpected ReferenceError. +flags: [onlyStrict] +---*/ +"use strict"; +foo.bar(); diff --git a/infrastructure/test262/strict-failure.js b/infrastructure/test262/strict-failure.js new file mode 100644 index 00000000000000..57d302fbedad06 --- /dev/null +++ b/infrastructure/test262/strict-failure.js @@ -0,0 +1,6 @@ +/*--- +description: A strict mode smoketest that fails an assertion. +flags: [onlyStrict] +---*/ +"use strict"; +assert.sameValue(1, 2, "One should be two in strict mode"); diff --git a/infrastructure/test262/strict.js b/infrastructure/test262/strict.js new file mode 100644 index 00000000000000..aec5537b1c163c --- /dev/null +++ b/infrastructure/test262/strict.js @@ -0,0 +1,8 @@ +/*--- +description: A strict-mode smoketest from https://github.com/tc39/test262/blob/main/INTERPRETING.md +negative: + phase: parse + type: SyntaxError +---*/ +$DONOTEVALUATE(); +var a\u2E2F; diff --git a/infrastructure/test262/support/module-helper.js b/infrastructure/test262/support/module-helper.js new file mode 100644 index 00000000000000..cc798ff50da947 --- /dev/null +++ b/infrastructure/test262/support/module-helper.js @@ -0,0 +1 @@ +export const a = 1; diff --git a/infrastructure/test262/unexpected-error.js b/infrastructure/test262/unexpected-error.js new file mode 100644 index 00000000000000..4999a8b9450190 --- /dev/null +++ b/infrastructure/test262/unexpected-error.js @@ -0,0 +1,5 @@ +/*--- +description: A smoketest that throws an unexpected ReferenceError. +---*/ + +foo.bar(); diff --git a/lint.ignore b/lint.ignore index 3feeade3388a7c..331bdb1b7b5095 100644 --- a/lint.ignore +++ b/lint.ignore @@ -841,3 +841,5 @@ PROMISE_REJECTS: wasm/core/js/harness/testharness.js # Legitimate use of test_driver_internal TEST DRIVER INTERNAL: resources/testdriver.js + +*: third_party/test262/* diff --git a/resources/test262/harness-adapter.js b/resources/test262/harness-adapter.js new file mode 100644 index 00000000000000..2de808bd6afe8f --- /dev/null +++ b/resources/test262/harness-adapter.js @@ -0,0 +1,173 @@ +function installAPI(global) { + global.$262 = { + createRealm: function() { + var iframe = global.document.createElement('iframe'); + iframe.style.cssText = 'display: none'; + iframe.src = ''; // iframeSrc; + if (global.document.body === null) { + global.document.body = global.document.createElement('body'); + } + global.document.body.appendChild(iframe); + return installAPI(iframe.contentWindow); + }, + evalScript: function(src) { + var script = global.document.createElement('script'); + script.text = src; + window.__test262_evalScript_error_ = undefined; + global.document.head.appendChild(script); + // Errors in the above appendChild bubble up to the global error handler. + // Our testharnes-client.js stashes them in a global var for rethrowing. + if (window.__test262_evalScript_error_) { + err = window.__test262_evalScript_error_; + window.__test262_evalScript_error_ = undefined; + throw err; + } + }, + detachArrayBuffer: function(buffer) { + if (typeof postMessage !== 'function') { + throw new Error('No method available to detach an ArrayBuffer'); + } else { + postMessage(null, '*', [buffer]); + /* + See + https://html.spec.whatwg.org/multipage/comms.html#dom-window-postmessage + which calls + https://html.spec.whatwg.org/multipage/infrastructure.html#structuredclonewithtransfer + which calls + https://html.spec.whatwg.org/multipage/infrastructure.html#transfer-abstract-op + which calls the DetachArrayBuffer abstract operation + https://tc39.github.io/ecma262/#sec-detacharraybuffer + */ + } + }, + gc: function() { + if (typeof gc !== 'function') { + throw new Error('No method available to invoke a GC'); + } + gc(); + }, + AbstractModuleSource: function() { + throw new Error('AbstractModuleSource not available'); + }, + agent: (function () { + var workers = []; + var i32a = null; + var pendingReports = []; + + // Agents call Atomics.wait on this location to sleep. + var SLEEP_LOC = 0; + // 1 if the started worker is ready, 0 otherwise. + var START_LOC = 1; + // The number of workers that have received the broadcast. + var BROADCAST_LOC = 2; + // Each worker has a count of outstanding reports; worker N uses memory + // location [WORKER_REPORT_LOC + N]. + var WORKER_REPORT_LOC = 3; + + function workerScript(script) { + return ` + var index; + var i32a = null; + var broadcasts = []; + var pendingReceiver = null; + + function handleBroadcast() { + if (pendingReceiver && broadcasts.length > 0) { + pendingReceiver.apply(null, broadcasts.shift()); + pendingReceiver = null; + } + }; + + var onmessage = function({data:msg}) { + switch (msg.kind) { + case 'start': + i32a = msg.i32a; + index = msg.index; + (0, eval)(\`${script}\`); + break; + + case 'broadcast': + Atomics.add(i32a, ${BROADCAST_LOC}, 1); + broadcasts.push([msg.sab, msg.id]); + handleBroadcast(); + break; + } + }; + + var $262 = { + agent: { + receiveBroadcast(receiver) { + pendingReceiver = receiver; + handleBroadcast(); + }, + + report(msg) { + postMessage(String(msg)); + Atomics.add(i32a, ${WORKER_REPORT_LOC} + index, 1); + }, + + sleep(s) { Atomics.wait(i32a, ${SLEEP_LOC}, 0, s); }, + + leaving() {}, + + monotonicNow() { + return performance.now(); + } + } + };`; + } + + var agent = { + start(script) { + if (i32a === null) { + i32a = new Int32Array(new SharedArrayBuffer(256)); + } + var w = new Worker(workerScript(script), {type: 'string'}); + w.index = workers.length; + w.postMessage({kind: 'start', i32a: i32a, index: w.index}); + workers.push(w); + }, + + broadcast(sab, id) { + if (!(sab instanceof SharedArrayBuffer)) { + throw new TypeError('sab must be a SharedArrayBuffer.'); + } + + Atomics.store(i32a, BROADCAST_LOC, 0); + + for (var w of workers) { + w.postMessage({kind: 'broadcast', sab: sab, id: id|0}); + } + + while (Atomics.load(i32a, BROADCAST_LOC) != workers.length) {} + }, + + getReport() { + for (var w of workers) { + while (Atomics.load(i32a, WORKER_REPORT_LOC + w.index) > 0) { + pendingReports.push(w.getMessage()); + Atomics.sub(i32a, WORKER_REPORT_LOC + w.index, 1); + } + } + + return pendingReports.shift() || null; + }, + + sleep(s) { Atomics.wait(i32a, SLEEP_LOC, 0, s); }, + + monotonicNow() { + return performance.now(); + } + }; + return agent; + + })(), + global: global + }; + global.$DONE = function() {} + + return global.$262; +} + +installAPI(window); + diff --git a/resources/test262/testharness-client.js b/resources/test262/testharness-client.js new file mode 100644 index 00000000000000..2014bcfa4a3e7f --- /dev/null +++ b/resources/test262/testharness-client.js @@ -0,0 +1,77 @@ +/* + * Minimalistic testharness-client tailored to run Test262 + * + * Expects parent document to listen for messages using + * test262/testharness.js. + * + */ + + +(function() { + // A placeholder until the real one is loaded from assert.js + function Test262Error(message) { + this.message = message; + } + Test262Error.prototype.name = "Test262Error"; + self.Test262Error = Test262Error; + + // We stash these in case the test overrides them + var Object_prototype_toString = Object.prototype.toString; + var Error_prototype_toString = Error.prototype.toString; + var String_prototype_indexOf = String.prototype.indexOf; + var parentWindow = window.parent; + + var expectedError; + var test_finished = false; + var status = 0; + var message = "OK"; + + window.test262Setup = function() { + } + + function done() { + if (test_finished) { return; } + test_finished = true; + parentWindow.postMessage(message, '*'); + parentWindow.postMessage(status, '*'); + } + window.test262Done = done; + + function on_error(event) { + // This hack ensures that errors thrown inside of a $262.evalScript get + // rethrown in the correct place. + if (event.error && String_prototype_indexOf.call(event.error.message, "Failed to execute 'appendChild' on 'Node'") === 0) { + window.__test262_evalScript_error_ = event.error; + return; + } + + if (expectedError && event.error && + (String_prototype_indexOf.call(event.error.toString(), expectedError) === 0 || + String_prototype_indexOf.call(Error_prototype_toString.call(event.error), expectedError) === 0 || + String_prototype_indexOf.call(event.message, expectedError) === 0)) { + status = 0; // OK + message = "OK"; + } else if (event.error instanceof self.Test262Error) { + status = 1; // FAIL + message = event.error.message; + } else { + status = 2; // ERROR + message = event.message; + } + done(); + } + window.addEventListener("error", on_error); + window.addEventListener("unhandledrejection", function(event) { + on_error({ + message: "Unhandled promise rejection: " + event.reason, + error: event.reason + }); + }); + + window.test262Negative = function(err) { + expectedError = err; + status = 1; + message = "Expected "+err; + window.$DONTEVALUATE = function() {}; + }; +})(); diff --git a/resources/test262/testharness.js b/resources/test262/testharness.js new file mode 100644 index 00000000000000..2554c25bb7d36c --- /dev/null +++ b/resources/test262/testharness.js @@ -0,0 +1,114 @@ +/* + * Minimalistic testharness tailored to run Test262 + * Can be used with or without testharnessreport.js. + * + * Expects actual test to be run in iframe and using + * test262/testharness-client.js. + * + */ +(function() { + var test_finished = false; + var callback; + + var harness_status = { + status: 0, + message: "OK" + }; + + function log(msg) { + document.getElementById('log').innerHTML += ""+msg+"
"; + } + + function report_result() { + log('done'); + log(JSON.stringify(harness_status)); + + if (callback || window.opener) { + var tests = [{ + name: document.title, + status: harness_status.subtest_status + }]; + var stat = { + status: harness_status.status, + message: harness_status.message, + stack: harness_status.stack + }; + var message = { + type: "complete", + tests: tests, + status: stat + }; + if (callback) { + log("callback"); + callback(tests, stat); + } else if (window.opener) { + log("postMessage"); + window.opener.postMessage(message, "*"); + } + return; + } + + // Fall-back support for running without testharnessreport.js included. + var retry = 0; + var result_payload = ["url", "complete", [ + 0, document.title, "", [] + ]]; + function raw_report() { + result_payload[0] = window.__wptrunner_url; + result_payload[2][0] = harness_status.status; + result_payload[2][2] = harness_status.message; + window.__wptrunner_testdriver_callback(result_payload); + window.__wptrunner_process_next_event(); + } + function call_raw_report() { + if (window.__wptrunner_testdriver_callback) { + return raw_report(); + } + retry += 1; + if (retry < 10) { + setTimeout(call_raw_report, 10); + } else { + log("could not communicate result"); + } + } + setTimeout(call_raw_report, 0); + log("raw fallback"); + } + + function done() { + if (test_finished) { return; } + test_finished = true; + report_result(); + } + + window.add_completion_callback = function(cb) { + callback = cb; + }; + window.setup = function() { + }; + + window.addEventListener('message', (event) => { + const iframe = document.getElementById('test262-iframe'); + // Communication protocol is to first receive a string message and then a + // result number. On purpose no objects since test262 mucks with the + // environment a lot (e.g., polluting Object.prototype and such). + if (iframe.contentWindow === event.source) { + if (typeof event.data === 'string') { + harness_status.message = event.data; + } + if (typeof event.data === 'number') { + if (event.data === 1) { // Test262Error, so a subtest failure + harness_status.status = 0; // Overall harness is OK + harness_status.subtest_status = 1; // Subtest is FAIL + } else if (event.data === 2) { // Other error, so a harness error + harness_status.status = 1; // Overall harness is ERROR + harness_status.subtest_status = 1; // Subtest is FAIL + } else { + harness_status.status = event.data; + harness_status.subtest_status = event.data; + } + done(); + } + } + }); +})(); diff --git a/third_party/test262/harness/assert.js b/third_party/test262/harness/assert.js new file mode 100644 index 00000000000000..7ced0cd0b4129f --- /dev/null +++ b/third_party/test262/harness/assert.js @@ -0,0 +1,170 @@ +// Copyright (C) 2017 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: | + Collection of assertion functions used throughout test262 +defines: [assert] +---*/ + + +function assert(mustBeTrue, message) { + if (mustBeTrue === true) { + return; + } + + if (message === undefined) { + message = 'Expected true but got ' + assert._toString(mustBeTrue); + } + throw new Test262Error(message); +} + +assert._isSameValue = function (a, b) { + if (a === b) { + // Handle +/-0 vs. -/+0 + return a !== 0 || 1 / a === 1 / b; + } + + // Handle NaN vs. NaN + return a !== a && b !== b; +}; + +assert.sameValue = function (actual, expected, message) { + try { + if (assert._isSameValue(actual, expected)) { + return; + } + } catch (error) { + throw new Test262Error(message + ' (_isSameValue operation threw) ' + error); + return; + } + + if (message === undefined) { + message = ''; + } else { + message += ' '; + } + + message += 'Expected SameValue(«' + assert._toString(actual) + '», «' + assert._toString(expected) + '») to be true'; + + throw new Test262Error(message); +}; + +assert.notSameValue = function (actual, unexpected, message) { + if (!assert._isSameValue(actual, unexpected)) { + return; + } + + if (message === undefined) { + message = ''; + } else { + message += ' '; + } + + message += 'Expected SameValue(«' + assert._toString(actual) + '», «' + assert._toString(unexpected) + '») to be false'; + + throw new Test262Error(message); +}; + +assert.throws = function (expectedErrorConstructor, func, message) { + var expectedName, actualName; + if (typeof func !== "function") { + throw new Test262Error('assert.throws requires two arguments: the error constructor ' + + 'and a function to run'); + return; + } + if (message === undefined) { + message = ''; + } else { + message += ' '; + } + + try { + func(); + } catch (thrown) { + if (typeof thrown !== 'object' || thrown === null) { + message += 'Thrown value was not an object!'; + throw new Test262Error(message); + } else if (thrown.constructor !== expectedErrorConstructor) { + expectedName = expectedErrorConstructor.name; + actualName = thrown.constructor.name; + if (expectedName === actualName) { + message += 'Expected a ' + expectedName + ' but got a different error constructor with the same name'; + } else { + message += 'Expected a ' + expectedName + ' but got a ' + actualName; + } + throw new Test262Error(message); + } + return; + } + + message += 'Expected a ' + expectedErrorConstructor.name + ' to be thrown but no exception was thrown at all'; + throw new Test262Error(message); +}; + +function isPrimitive(value) { + return !value || (typeof value !== 'object' && typeof value !== 'function'); +} + +assert.compareArray = function (actual, expected, message) { + message = message === undefined ? '' : message; + + if (typeof message === 'symbol') { + message = message.toString(); + } + + if (isPrimitive(actual)) { + assert(false, `Actual argument [${actual}] shouldn't be primitive. ${message}`); + } else if (isPrimitive(expected)) { + assert(false, `Expected argument [${expected}] shouldn't be primitive. ${message}`); + } + var result = compareArray(actual, expected); + if (result) return; + + var format = compareArray.format; + assert(false, `Actual ${format(actual)} and expected ${format(expected)} should have the same contents. ${message}`); +}; + +function compareArray(a, b) { + if (b.length !== a.length) { + return false; + } + for (var i = 0; i < a.length; i++) { + if (!assert._isSameValue(b[i], a[i])) { + return false; + } + } + return true; +} + +compareArray.format = function (arrayLike) { + return `[${Array.prototype.map.call(arrayLike, String).join(', ')}]`; +}; + +assert._formatIdentityFreeValue = function formatIdentityFreeValue(value) { + switch (value === null ? 'null' : typeof value) { + case 'string': + return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`; + case 'bigint': + return `${value}n`; + case 'number': + if (value === 0 && 1 / value === -Infinity) return '-0'; + // falls through + case 'boolean': + case 'undefined': + case 'null': + return String(value); + } +}; + +assert._toString = function (value) { + var basic = assert._formatIdentityFreeValue(value); + if (basic) return basic; + try { + return String(value); + } catch (err) { + if (err.name === 'TypeError') { + return Object.prototype.toString.call(value); + } + throw err; + } +}; diff --git a/third_party/test262/harness/sta.js b/third_party/test262/harness/sta.js new file mode 100644 index 00000000000000..c95ed7a264c550 --- /dev/null +++ b/third_party/test262/harness/sta.js @@ -0,0 +1,27 @@ +// Copyright (c) 2012 Ecma International. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: | + Provides both: + + - An error class to avoid false positives when testing for thrown exceptions + - A function to explicitly throw an exception using the Test262Error class +defines: [Test262Error, $DONOTEVALUATE] +---*/ + + +function Test262Error(message) { + this.message = message || ""; +} + +Test262Error.prototype.toString = function () { + return "Test262Error: " + this.message; +}; + +Test262Error.thrower = function (message) { + throw new Test262Error(message); +}; + +function $DONOTEVALUATE() { + throw "Test262: This statement should not be evaluated."; +} diff --git a/tools/manifest/__init__.py b/tools/manifest/__init__.py index 8c8f189070eaa3..bc0624ae417283 100644 --- a/tools/manifest/__init__.py +++ b/tools/manifest/__init__.py @@ -1 +1 @@ -from . import item, manifest, sourcefile, update # noqa: F401 +from . import item, manifest, sourcefile, test262, update # noqa: F401 diff --git a/tools/manifest/requirements.txt b/tools/manifest/requirements.txt index ca872b12c41955..d12c93a75afe14 100644 --- a/tools/manifest/requirements.txt +++ b/tools/manifest/requirements.txt @@ -1 +1,3 @@ zstandard==0.23.0 +pyyaml==6.0.1 +types-pyyaml==6.0.12.20241230 diff --git a/tools/wptrunner/wptrunner/browsers/chrome.py b/tools/wptrunner/wptrunner/browsers/chrome.py index 71e67811039b2e..c015a7d5ebdb82 100644 --- a/tools/wptrunner/wptrunner/browsers/chrome.py +++ b/tools/wptrunner/wptrunner/browsers/chrome.py @@ -33,7 +33,8 @@ "reftest": "ChromeDriverRefTestExecutor", "print-reftest": "ChromeDriverPrintRefTestExecutor", "wdspec": "WdspecExecutor", - "crashtest": "ChromeDriverCrashTestExecutor"}, + "crashtest": "ChromeDriverCrashTestExecutor", + "test262": "ChromeDriverTestharnessExecutor"}, "browser_kwargs": "browser_kwargs", "executor_kwargs": "executor_kwargs", "env_extras": "env_extras", diff --git a/tools/wptrunner/wptrunner/browsers/edge.py b/tools/wptrunner/wptrunner/browsers/edge.py index 82597c9312a4ad..6145916f626096 100644 --- a/tools/wptrunner/wptrunner/browsers/edge.py +++ b/tools/wptrunner/wptrunner/browsers/edge.py @@ -19,7 +19,8 @@ "reftest": "EdgeDriverRefTestExecutor", "print-reftest": "EdgeDriverPrintRefTestExecutor", "wdspec": "WdspecExecutor", - "crashtest": "WebDriverCrashtestExecutor"}, + "crashtest": "WebDriverCrashtestExecutor", + "test262": "EdgeDriverTestharnessExecutor"}, "browser_kwargs": "browser_kwargs", "executor_kwargs": "executor_kwargs", "env_extras": "env_extras", diff --git a/tools/wptrunner/wptrunner/browsers/firefox.py b/tools/wptrunner/wptrunner/browsers/firefox.py index 81e47197397c84..8bddda22106246 100644 --- a/tools/wptrunner/wptrunner/browsers/firefox.py +++ b/tools/wptrunner/wptrunner/browsers/firefox.py @@ -46,7 +46,8 @@ "testharness": "MarionetteTestharnessExecutor", "reftest": "MarionetteRefTestExecutor", "print-reftest": "MarionettePrintRefTestExecutor", - "wdspec": "MarionetteWdspecExecutor"}, + "wdspec": "MarionetteWdspecExecutor", + "test262": "MarionetteTestharnessExecutor"}, "browser_kwargs": "browser_kwargs", "executor_kwargs": "executor_kwargs", "env_extras": "env_extras", diff --git a/tools/wptrunner/wptrunner/browsers/safari.py b/tools/wptrunner/wptrunner/browsers/safari.py index fc3cc1138b31b6..e43d04b36ae209 100644 --- a/tools/wptrunner/wptrunner/browsers/safari.py +++ b/tools/wptrunner/wptrunner/browsers/safari.py @@ -22,7 +22,8 @@ "executor": {"testharness": "WebDriverTestharnessExecutor", "reftest": "WebDriverRefTestExecutor", "wdspec": "WdspecExecutor", - "crashtest": "WebDriverCrashtestExecutor"}, + "crashtag": "WebDriverCrashtestExecutor", + "test262": "WebDriverTestharnessExecutor"}, "browser_kwargs": "browser_kwargs", "executor_kwargs": "executor_kwargs", "env_extras": "env_extras", diff --git a/tools/wptrunner/wptrunner/browsers/servo.py b/tools/wptrunner/wptrunner/browsers/servo.py index 7a95b08e7ad9ef..96b841f00751ab 100644 --- a/tools/wptrunner/wptrunner/browsers/servo.py +++ b/tools/wptrunner/wptrunner/browsers/servo.py @@ -23,6 +23,7 @@ "testharness": "ServoTestharnessExecutor", "reftest": "ServoRefTestExecutor", "wdspec": "WdspecExecutor", + "test262": "ServoTestharnessExecutor", }, "browser_kwargs": "browser_kwargs", "executor_kwargs": "executor_kwargs", diff --git a/tools/wptrunner/wptrunner/browsers/webkit.py b/tools/wptrunner/wptrunner/browsers/webkit.py index a3e8d1361c3d69..183c685e64b62e 100644 --- a/tools/wptrunner/wptrunner/browsers/webkit.py +++ b/tools/wptrunner/wptrunner/browsers/webkit.py @@ -16,7 +16,8 @@ "executor": {"testharness": "WebDriverTestharnessExecutor", "reftest": "WebDriverRefTestExecutor", "wdspec": "WdspecExecutor", - "crashtest": "WebDriverCrashtestExecutor"}, + "crashtest": "WebDriverCrashtestExecutor", + "test262": "WebDriverTestharnessExecutor"}, "executor_kwargs": "executor_kwargs", "env_extras": "env_extras", "env_options": "env_options", diff --git a/tools/wptrunner/wptrunner/wpttest.py b/tools/wptrunner/wptrunner/wpttest.py index 9616f9c429f309..eb3c98f12ddca6 100644 --- a/tools/wptrunner/wptrunner/wpttest.py +++ b/tools/wptrunner/wptrunner/wpttest.py @@ -10,7 +10,7 @@ from .wptmanifest.parser import atoms atom_reset = atoms["Reset"] -enabled_tests = {"testharness", "reftest", "wdspec", "crashtest", "print-reftest"} +enabled_tests = {"testharness", "reftest", "wdspec", "crashtest", "print-reftest", "test262"} class Result(ABC): @@ -529,6 +529,10 @@ def id(self): return self.url +class Test262Test(TestharnessTest): + test_type = "test262" + + class ReftestTest(Test): """A reftest @@ -764,7 +768,8 @@ def from_manifest(cls, manifest_file, manifest_item, inherit_metadata, test_meta "print-reftest": PrintReftestTest, "testharness": TestharnessTest, "wdspec": WdspecTest, - "crashtest": CrashTest} + "crashtest": CrashTest, + "test262": Test262Test} def from_manifest(manifest_file, manifest_test, inherit_metadata, test_metadata):