@@ -5,6 +5,7 @@ const fixtures = require('../common/fixtures');
55const fs = require('fs');
66const fsPromises = fs.promises;
77const path = require('path');
8+ const events = require('events');
89const { inspect } = require('util');
910const { Worker } = require('worker_threads');
1011
@@ -152,21 +153,30 @@ class WPTTestSpec {
152153 this.filename = filename;
153154
154155 this.requires = new Set();
155- this.failReasons = [];
156+ this.failedTests = [];
157+ this.flakyTests = [];
156158 this.skipReasons = [];
157159 for (const item of rules) {
158160 if (item.requires.length) {
159161 for (const req of item.requires) {
160162 this.requires.add(req);
161163 }
162164 }
163- if (item.fail) {
164- this.failReasons.push(item.fail);
165+ if (Array.isArray(item.fail?.expected)) {
166+ this.failedTests.push(...item.fail.expected);
167+ }
168+ if (Array.isArray(item.fail?.flaky)) {
169+ this.failedTests.push(...item.fail.flaky);
170+ this.flakyTests.push(...item.fail.flaky);
165171 }
166172 if (item.skip) {
167173 this.skipReasons.push(item.skip);
168174 }
169175 }
176+
177+ this.failedTests = [...new Set(this.failedTests)];
178+ this.flakyTests = [...new Set(this.flakyTests)];
179+ this.skipReasons = [...new Set(this.skipReasons)];
170180 }
171181
172182 getRelativePath() {
@@ -368,7 +378,7 @@ class WPTRunner {
368378
369379 // TODO(joyeecheung): work with the upstream to port more tests in .html
370380 // to .js.
371- runJsTests() {
381+ async runJsTests() {
372382 let queue = [];
373383
374384 // If the tests are run as `node test/wpt/test-something.js subset.any.js`,
@@ -459,6 +469,8 @@ class WPTRunner {
459469 );
460470 this.inProgress.delete(testFileName);
461471 });
472+
473+ await events.once(worker, 'exit').catch(() => {});
462474 }
463475
464476 process.on('exit', () => {
@@ -469,34 +481,72 @@ class WPTRunner {
469481 }
470482 }
471483 inspect.defaultOptions.depth = Infinity;
472- console.log(this.results);
484+ // Sorts the rules to have consistent output
485+ console.log(JSON.stringify(Object.keys(this.results).sort().reduce(
486+ (obj, key) => {
487+ obj[key] = this.results[key];
488+ return obj;
489+ },
490+ {}
491+ ), null, 2));
473492
474493 const failures = [];
475494 let expectedFailures = 0;
476495 let skipped = 0;
477- for (const key of Object.keys(this.results)) {
478- const item = this.results[key];
479- if (item.fail && item.fail.unexpected) {
496+ for (const [key, item] of Object.entries(this.results)) {
497+ if (item.fail?.unexpected) {
480498 failures.push(key);
481499 }
482- if (item.fail && item.fail .expected) {
500+ if (item.fail? .expected) {
483501 expectedFailures++;
484502 }
485503 if (item.skip) {
486504 skipped++;
487505 }
488506 }
507+
508+ const unexpectedPasses = [];
509+ for (const [key, specMap] of this.specMap) {
510+ // File has no expected failures
511+ if (!specMap.failedTests.length) {
512+ continue;
513+ }
514+
515+ // File was (maybe even conditionally) skipped
516+ if (this.results[key]?.skip) {
517+ continue;
518+ }
519+
520+ // Full check: every expected to fail test is present
521+ if (specMap.failedTests.some((expectedToFail) => {
522+ if (specMap.flakyTests.includes(expectedToFail)) {
523+ return false;
524+ }
525+ return this.results[key]?.fail?.expected?.includes(expectedToFail) !== true;
526+ })) {
527+ unexpectedPasses.push(key);
528+ continue;
529+ }
530+ }
531+
489532 const ran = total - skipped;
490533 const passed = ran - expectedFailures - failures.length;
491534 console.log(`Ran ${ran}/${total} tests, ${skipped} skipped,`,
492535 `${passed} passed, ${expectedFailures} expected failures,`,
493- `${failures.length} unexpected failures`);
536+ `${failures.length} unexpected failures,`,
537+ `${unexpectedPasses.length} unexpected passes`);
494538 if (failures.length > 0) {
495539 const file = path.join('test', 'wpt', 'status', `${this.path}.json`);
496540 throw new Error(
497541 `Found ${failures.length} unexpected failures. ` +
498542 `Consider updating ${file} for these files:\n${failures.join('\n')}`);
499543 }
544+ if (unexpectedPasses.length > 0) {
545+ const file = path.join('test', 'wpt', 'status', `${this.path}.json`);
546+ throw new Error(
547+ `Found ${unexpectedPasses.length} unexpected passes. ` +
548+ `Consider updating ${file} for these files:\n${unexpectedPasses.join('\n')}`);
549+ }
500550 });
501551 }
502552
@@ -577,8 +627,9 @@ class WPTRunner {
577627 if (!result[item.status][key]) {
578628 result[item.status][key] = [];
579629 }
580- if (result[item.status][key].indexOf(item.reason) === -1) {
581- result[item.status][key].push(item.reason);
630+ const hasName = result[item.status][key].includes(item.name);
631+ if (!hasName) {
632+ result[item.status][key].push(item.name);
582633 }
583634 }
584635 }
@@ -589,10 +640,10 @@ class WPTRunner {
589640
590641 fail(filename, test, status) {
591642 const spec = this.specMap.get(filename);
592- const expected = !!( spec.failReasons.length );
643+ const expected = spec.failedTests.includes(test.name );
593644 if (expected) {
594645 console.log(`[EXPECTED_FAILURE][${status.toUpperCase()}] ${test.name}`);
595- console.log(spec.failReasons.join('; ') );
646+ console.log(test.message || status );
596647 } else {
597648 console.log(`[UNEXPECTED_FAILURE][${status.toUpperCase()}] ${test.name}`);
598649 }
@@ -604,6 +655,7 @@ class WPTRunner {
604655 ` ${require.main.filename} ${filename}`;
605656 console.log(`Command: ${command}\n`);
606657 this.addTestResult(filename, {
658+ name: test.name,
607659 expected,
608660 status: kFail,
609661 reason: test.message || status
0 commit comments