@@ -58,46 +58,66 @@ function codeUnitStr(char) {
5858return '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.
6190class WPTReport {
6291constructor ( path ) {
6392this . filename = `report-${ path . replaceAll ( '/' , '-' ) } .json` ;
64- this . results = [ ] ;
93+ /** @type {Map<string, ReportResult> } */
94+ this . results = new Map ( ) ;
6595this . 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 ) ;
88109return result ;
89110}
90111
91112write ( ) {
92113this . time_end = Date . now ( ) ;
93- this . results = this . results . filter ( ( result ) => {
94- return result . status === 'SKIP' || result . subtests . length !== 0 ;
95- } ) . map ( ( result ) => {
96- const url = new URL ( result . test , 'http://wpt' ) ;
97- url . pathname = url . pathname . replace ( / \. j s $ / , '.html' ) ;
98- result . test = url . href . slice ( url . origin . length ) ;
99- return result ;
100- } ) ;
114+ const results = Array . from ( this . results . values ( ) )
115+ . map ( ( result ) => {
116+ const url = new URL ( result . test , 'http://wpt' ) ;
117+ url . pathname = url . pathname . replace ( / \. j s $ / , '.html' ) ;
118+ result . test = url . href . slice ( url . origin . length ) ;
119+ return result ;
120+ } ) ;
101121
102122/**
103123 * Return required and some optional properties
@@ -110,7 +130,12 @@ class WPTReport {
110130os : getOs ( ) ,
111131} ;
112132
113- fs . writeFileSync ( `out/wpt/${ this . filename } ` , JSON . stringify ( this ) ) ;
133+ fs . writeFileSync ( `out/wpt/${ this . filename } ` , JSON . stringify ( {
134+ time_start : this . time_start ,
135+ time_end : this . time_end ,
136+ run_info : this . run_info ,
137+ results : results ,
138+ } ) ) ;
114139}
115140}
116141
@@ -642,14 +667,13 @@ class WPTRunner {
642667this . inProgress . add ( spec ) ;
643668this . workers . set ( spec , worker ) ;
644669
645- let reportResult ;
670+ const reportResult = this . report ?. getResult ( spec ) ;
646671worker . on ( 'message' , ( message ) => {
647672switch ( message . type ) {
648673case 'result' :
649- reportResult ||= this . report ?. addResult ( `/${ relativePath } ${ spec . variant } ` , 'OK' ) ;
650674return this . resultCallback ( spec , message . result , reportResult ) ;
651675case 'completion' :
652- return this . completionCallback ( spec , message . status ) ;
676+ return this . completionCallback ( spec , message . status , reportResult ) ;
653677default :
654678throw new Error ( `Unexpected message from worker: ${ message . type } ` ) ;
655679}
@@ -661,6 +685,8 @@ class WPTRunner {
661685// This can happen normally, for example in timers tests.
662686return ;
663687}
688+ // Generate a subtest failure for visibility.
689+ // No need to record this synthetic failure with wpt.fyi.
664690this . fail (
665691spec ,
666692{
@@ -671,6 +697,8 @@ class WPTRunner {
671697} ,
672698kUncaught ,
673699) ;
700+ // Mark the whole test as failed in wpt.fyi report.
701+ reportResult ?. finish ( 'ERROR' ) ;
674702this . inProgress . delete ( spec ) ;
675703} ) ;
676704
@@ -680,7 +708,11 @@ class WPTRunner {
680708
681709process . on ( 'exit' , ( ) => {
682710for ( const spec of this . inProgress ) {
711+ // No need to record this synthetic failure with wpt.fyi.
683712this . 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}
685717inspect . 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 */
784817resultCallback ( spec , test , reportResult ) {
785818const status = this . getTestStatus ( test . status ) ;
@@ -794,13 +827,19 @@ 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 ) {
799833const status = this . getTestStatus ( harnessStatus . status ) ;
800834
801835// Treat it like a test case failure
802836if ( status === kTimeout ) {
837+ // No need to record this synthetic failure with wpt.fyi.
803838this . fail ( spec , { name : 'WPT testharness timeout' } , kTimeout ) ;
839+ // Mark the whole test as TIMEOUT in wpt.fyi report.
840+ reportResult ?. finish ( 'TIMEOUT' ) ;
841+ } else {
842+ reportResult ?. finish ( ) ;
804843}
805844this . inProgress . delete ( spec ) ;
806845// Always force termination of the worker. Some tests allocate resources
0 commit comments