Skip to content

Commit 780117a

Browse files
committed
test: report error wpt test results
When a wpt test file is exited for uncaught error, its result should be recorded in the `wptreport.json` and uploaded to wpt.fyi. For instance, `html/webappapis/timers/evil-spec-example.any.js` is exited for uncaught error in Node.js but it shows as "MISSING" at https://wpt.fyi/results/html/webappapis/timers?label=master&label=experimental&product=chrome&product=node.js&aligned.
1 parent 6431c65 commit 780117a

File tree

1 file changed

+65
-28
lines changed

1 file changed

+65
-28
lines changed

test/common/wpt.js

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,39 +58,60 @@ function codeUnitStr(char) {
5858
return 'U+' + char.charCodeAt(0).toString(16);
5959
}
6060

61+
class ReportResult {
62+
constructor(name) {
63+
this.test = name;
64+
this.status = 'OK';
65+
this.subtests = [];
66+
}
67+
68+
addSubtest(name, status, message) {
69+
const subtest = {
70+
status,
71+
// https://github.com/web-platform-tests/wpt/blob/b24eedd/resources/testharness.js#L3722
72+
name: sanitizeUnpairedSurrogates(name),
73+
};
74+
if (message) {
75+
// https://github.com/web-platform-tests/wpt/blob/b24eedd/resources/testharness.js#L4506
76+
subtest.message = sanitizeUnpairedSurrogates(message);
77+
}
78+
this.subtests.push(subtest);
79+
return subtest;
80+
}
81+
82+
finish(status) {
83+
this.status = status ?? 'OK';
84+
}
85+
}
86+
87+
// Generates a report that can be uploaded to wpt.fyi.
88+
// Checkout https://github.com/web-platform-tests/wpt.fyi/tree/main/api#results-creation
89+
// for more details.
6190
class WPTReport {
6291
constructor(path) {
6392
this.filename = `report-${path.replaceAll('/', '-')}.json`;
64-
this.results = [];
93+
/** @type {Map<string, ReportResult>} */
94+
this.results = new Map();
6595
this.time_start = Date.now();
6696
}
6797

68-
addResult(name, status) {
69-
const result = {
70-
test: name,
71-
status,
72-
subtests: [],
73-
addSubtest(name, status, message) {
74-
const subtest = {
75-
status,
76-
// https://github.com/web-platform-tests/wpt/blob/b24eedd/resources/testharness.js#L3722
77-
name: sanitizeUnpairedSurrogates(name),
78-
};
79-
if (message) {
80-
// https://github.com/web-platform-tests/wpt/blob/b24eedd/resources/testharness.js#L4506
81-
subtest.message = sanitizeUnpairedSurrogates(message);
82-
}
83-
this.subtests.push(subtest);
84-
return subtest;
85-
},
86-
};
87-
this.results.push(result);
98+
/**
99+
* Get or create a ReportResult for a test spec.
100+
* @param {WPTTestSpec} spec
101+
*/
102+
getResult(spec) {
103+
const name = `/${spec.getRelativePath()}${spec.variant}`;
104+
if (this.results.has(name)) {
105+
return this.results.get(name);
106+
}
107+
const result = new ReportResult(name);
108+
this.results.set(name, result);
88109
return result;
89110
}
90111

91112
write() {
92113
this.time_end = Date.now();
93-
this.results = this.results.filter((result) => {
114+
const results = Array.from(this.results.values()).filter((result) => {
94115
return result.status === 'SKIP' || result.subtests.length !== 0;
95116
}).map((result) => {
96117
const url = new URL(result.test, 'http://wpt');
@@ -110,7 +131,12 @@ class WPTReport {
110131
os: getOs(),
111132
};
112133

113-
fs.writeFileSync(`out/wpt/${this.filename}`, JSON.stringify(this));
134+
fs.writeFileSync(`out/wpt/${this.filename}`, JSON.stringify({
135+
time_start: this.time_start,
136+
time_end: this.time_end,
137+
run_info: this.run_info,
138+
results: results,
139+
}));
114140
}
115141
}
116142

@@ -642,14 +668,13 @@ class WPTRunner {
642668
this.inProgress.add(spec);
643669
this.workers.set(spec, worker);
644670

645-
let reportResult;
671+
const reportResult = this.report?.getResult(spec);
646672
worker.on('message', (message) => {
647673
switch (message.type) {
648674
case 'result':
649-
reportResult ||= this.report?.addResult(`/${relativePath}${spec.variant}`, 'OK');
650675
return this.resultCallback(spec, message.result, reportResult);
651676
case 'completion':
652-
return this.completionCallback(spec, message.status);
677+
return this.completionCallback(spec, message.status, reportResult);
653678
default:
654679
throw new Error(`Unexpected message from worker: ${message.type}`);
655680
}
@@ -661,6 +686,7 @@ class WPTRunner {
661686
// This can happen normally, for example in timers tests.
662687
return;
663688
}
689+
// Generate a subtest failure for visibility.
664690
this.fail(
665691
spec,
666692
{
@@ -670,7 +696,10 @@ class WPTRunner {
670696
stack: inspect(err),
671697
},
672698
kUncaught,
699+
reportResult,
673700
);
701+
// Mark the whole test as failed in wpt.fyi report.
702+
reportResult?.finish('ERROR');
674703
this.inProgress.delete(spec);
675704
});
676705

@@ -681,6 +710,9 @@ class WPTRunner {
681710
process.on('exit', () => {
682711
for (const spec of this.inProgress) {
683712
this.fail(spec, { name: 'Incomplete' }, kIncomplete);
713+
// Mark the whole test as failed in wpt.fyi report.
714+
const reportResult = this.report?.getResult(spec);
715+
reportResult.finish('ERROR');
684716
}
685717
inspect.defaultOptions.depth = Infinity;
686718
// Sorts the rules to have consistent output
@@ -780,6 +812,7 @@ class WPTRunner {
780812
* in one test file).
781813
* @param {WPTTestSpec} spec
782814
* @param {Test} test The Test object returned by WPT harness
815+
* @param {ReportResult} reportResult The report result object
783816
*/
784817
resultCallback(spec, test, reportResult) {
785818
const status = this.getTestStatus(test.status);
@@ -794,13 +827,17 @@ class WPTRunner {
794827
* Report the status of each WPT test (one per file)
795828
* @param {WPTTestSpec} spec
796829
* @param {object} harnessStatus - The status object returned by WPT harness.
830+
* @param {ReportResult} reportResult The report result object
797831
*/
798-
completionCallback(spec, harnessStatus) {
832+
completionCallback(spec, harnessStatus, reportResult) {
799833
const status = this.getTestStatus(harnessStatus.status);
800834

801835
// Treat it like a test case failure
802836
if (status === kTimeout) {
803-
this.fail(spec, { name: 'WPT testharness timeout' }, kTimeout);
837+
this.fail(spec, { name: 'WPT testharness timeout' }, kTimeout, reportResult);
838+
reportResult?.finish('TIMEOUT');
839+
} else {
840+
reportResult?.finish();
804841
}
805842
this.inProgress.delete(spec);
806843
// Always force termination of the worker. Some tests allocate resources

0 commit comments

Comments
 (0)