@@ -236,6 +236,7 @@ class StatusRule { |
236 | 236 | this.requires = value.requires || []; |
237 | 237 | this.fail = value.fail; |
238 | 238 | this.skip = value.skip; |
| 239 | + this.skipTests = value.skipTests; |
239 | 240 | if (pattern) { |
240 | 241 | this.pattern = this.transformPattern(pattern); |
241 | 242 | } |
@@ -312,6 +313,7 @@ class WPTTestSpec { |
312 | 313 | this.failedTests = []; |
313 | 314 | this.flakyTests = []; |
314 | 315 | this.skipReasons = []; |
| 316 | + this.skippedTests = []; |
315 | 317 | for (const item of rules) { |
316 | 318 | if (item.requires.length) { |
317 | 319 | for (const req of item.requires) { |
@@ -328,6 +330,9 @@ class WPTTestSpec { |
328 | 330 | if (item.skip) { |
329 | 331 | this.skipReasons.push(item.skip); |
330 | 332 | } |
| 333 | + if (Array.isArray(item.skipTests)) { |
| 334 | + this.skippedTests.push(...item.skipTests); |
| 335 | + } |
331 | 336 | } |
332 | 337 |
|
333 | 338 | this.failedTests = [...new Set(this.failedTests)]; |
@@ -347,6 +352,22 @@ class WPTTestSpec { |
347 | 352 | return meta.variant?.map((variant) => new WPTTestSpec(mod, filename, rules, variant)) || [spec]; |
348 | 353 | } |
349 | 354 |
|
| 355 | + /** |
| 356 | + * Check if a subtest should be skipped by name. |
| 357 | + * @param {string} name |
| 358 | + * @returns {boolean} |
| 359 | + */ |
| 360 | + isSkippedTest(name) { |
| 361 | + for (const matcher of this.skippedTests) { |
| 362 | + if (typeof matcher === 'string') { |
| 363 | + if (name === matcher) return true; |
| 364 | + } else if (matcher.test(name)) { |
| 365 | + return true; |
| 366 | + } |
| 367 | + } |
| 368 | + return false; |
| 369 | + } |
| 370 | + |
350 | 371 | getRelativePath() { |
351 | 372 | return path.join(this.module, this.filename); |
352 | 373 | } |
@@ -684,6 +705,7 @@ class WPTRunner { |
684 | 705 | }, |
685 | 706 | scriptsToRun, |
686 | 707 | needsGc: !!meta.script?.find((script) => script === '/common/gc.js'), |
| 708 | + skippedTests: spec.skippedTests, |
687 | 709 | }, |
688 | 710 | }); |
689 | 711 | this.inProgress.add(spec); |
@@ -694,6 +716,8 @@ class WPTRunner { |
694 | 716 | switch (message.type) { |
695 | 717 | case 'result': |
696 | 718 | return this.resultCallback(spec, message.result, reportResult); |
| 719 | + case 'skip': |
| 720 | + return this.skipTest(spec, { name: message.name }, reportResult); |
697 | 721 | case 'completion': |
698 | 722 | return this.completionCallback(spec, message.status, reportResult); |
699 | 723 | default: |
@@ -751,6 +775,7 @@ class WPTRunner { |
751 | 775 | const failures = []; |
752 | 776 | let expectedFailures = 0; |
753 | 777 | let skipped = 0; |
| 778 | + let skippedTests = 0; |
754 | 779 | for (const [key, item] of Object.entries(this.results)) { |
755 | 780 | if (item.fail?.unexpected) { |
756 | 781 | failures.push(key); |
@@ -761,6 +786,9 @@ class WPTRunner { |
761 | 786 | if (item.skip) { |
762 | 787 | skipped++; |
763 | 788 | } |
| 789 | + if (item.skipTests) { |
| 790 | + skippedTests += item.skipTests.length; |
| 791 | + } |
764 | 792 | } |
765 | 793 |
|
766 | 794 | const unexpectedPasses = []; |
@@ -801,7 +829,8 @@ class WPTRunner { |
801 | 829 | console.log(`Ran ${ran}/${total} tests, ${skipped} skipped,`, |
802 | 830 | `${passed} passed, ${expectedFailures} expected failures,`, |
803 | 831 | `${failures.length} unexpected failures,`, |
804 | | - `${unexpectedPasses.length} unexpected passes`); |
| 832 | + `${unexpectedPasses.length} unexpected passes` + |
| 833 | + (skippedTests ? `, ${skippedTests} subtests skipped` : '')); |
805 | 834 | if (failures.length > 0) { |
806 | 835 | const file = path.join('test', 'wpt', 'status', `${this.path}.json`); |
807 | 836 | throw new Error( |
@@ -890,8 +919,16 @@ class WPTRunner { |
890 | 919 | let result = this.results[spec.filename]; |
891 | 920 | result ||= this.results[spec.filename] = {}; |
892 | 921 | if (item.status === kSkip) { |
893 | | - // { filename: { skip: 'reason' } } |
894 | | - result[kSkip] = item.reason; |
| 922 | + if (item.name) { |
| 923 | + // Subtest-level skip: { filename: { skipTests: [ ... ] } } |
| 924 | + result.skipTests ||= []; |
| 925 | + if (!result.skipTests.includes(item.name)) { |
| 926 | + result.skipTests.push(item.name); |
| 927 | + } |
| 928 | + } else { |
| 929 | + // File-level skip: { filename: { skip: 'reason' } } |
| 930 | + result[kSkip] = item.reason; |
| 931 | + } |
895 | 932 | } else { |
896 | 933 | // { filename: { fail: { expected: [ ... ], |
897 | 934 | // unexpected: [ ... ] } }} |
@@ -910,6 +947,15 @@ class WPTRunner { |
910 | 947 | reportResult?.addSubtest(test.name, 'PASS'); |
911 | 948 | } |
912 | 949 |
|
| 950 | + skipTest(spec, test, reportResult) { |
| 951 | + console.log(`[SKIP] ${test.name}`); |
| 952 | + reportResult?.addSubtest(test.name, 'NOTRUN'); |
| 953 | + this.addTestResult(spec, { |
| 954 | + name: test.name, |
| 955 | + status: kSkip, |
| 956 | + }); |
| 957 | + } |
| 958 | + |
913 | 959 | fail(spec, test, status, reportResult) { |
914 | 960 | const expected = spec.failedTests.includes(test.name); |
915 | 961 | if (expected) { |
|