@@ -33,182 +33,8 @@ flags.parse();
3333
3434const ROOT_DIR = path . join ( __dirname , '..' ) ;
3535
36- // All non-OK harness statuses. Any non-OK harness status should be investigated
37- // before being added to this list, so that we don't score tests in the wrong
38- // way because of a test or infrastructure issue.
39- const KNOWN_TEST_STATUSES = new Set ( [
40- // ERROR due to duplicate subtest name, fixed in https://github.com/web-platform-tests/wpt/pull/38387
41- '/css/css-color/parsing/color-invalid-color-function.html' ,
42- // TIMEOUT in Safari due to https://webkit.org/b/212201
43- '/css/css-grid/grid-definition/grid-limits-001.html' ,
44- // TIMEOUT in Firefox and Safari, all subtests present
45- '/css/css-scroll-snap/input/keyboard.html' ,
46- // ERROR in Firefox, TIMEOUT in Safari, all subtests failing in Chrome
47- '/css/css-scroll-snap/input/snap-area-overflow-boundary.html' ,
48- // TIMEOUT in Chrome with TIMEOUT subtests
49- '/dom/events/Event-dispatch-click.html' ,
50- // ERROR in Safari but linked bug is fixed
51- '/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-load.html' ,
52- '/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/form-requestsubmit-during-pageshow.html' ,
53- // TIMEOUT in Safari, but just a single subtest
54- '/html/semantics/forms/form-submission-0/form-double-submit-multiple-targets.html' ,
55- // TIMEOUT in Firefox and Safari, but just a single subtest
56- '/html/semantics/forms/form-submission-0/form-double-submit-to-different-origin-frame.html' ,
57- // TIMEOUT in Safari but all passing subtests due to https://bugs.webkit.org/show_bug.cgi?id=235407
58- '/html/semantics/forms/form-submission-target/rel-base-target.html' ,
59- '/html/semantics/forms/form-submission-target/rel-button-target.html' ,
60- '/html/semantics/forms/form-submission-target/rel-form-target.html' ,
61- '/html/semantics/forms/form-submission-target/rel-input-target.html' ,
62- // ERROR in Firefox 95 and Safari 15.2, since fixed
63- '/html/semantics/interactive-elements/the-dialog-element/dialog-showModal.html' ,
64- // ERROR in Chrome 96, since fixed
65- '/html/semantics/interactive-elements/the-dialog-element/modal-dialog-ancestor-is-inert.html' ,
66- // TIMEOUT in Safari, but all subtests present
67- '/html/semantics/forms/textfieldselection/select-event.html' ,
68- '/html/semantics/forms/textfieldselection/selection-start-end.html' ,
69- '/html/semantics/forms/textfieldselection/textfieldselection-setRangeText.html' ,
70- '/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange.html' ,
71- // TIMEOUT in Firefox 98, since fixed
72- '/html/semantics/forms/the-input-element/image-click-form-data.html' ,
73- // TIMEOUT in Safari, but all subtests present
74- '/html/semantics/forms/the-input-element/range-restore-oninput-onchange-event.html' ,
75- // TIMEOUT in STP 137, since fixed
76- '/html/semantics/interactive-elements/the-dialog-element/backdrop-receives-element-events.html' ,
77- // TIMEOUT for one run in Safari but has since run successfully.
78- '/css/css-scroll-snap/snap-at-user-scroll-end.html' ,
79-
80-
81- /**
82- * The tests below have non-OK statuses that have not been investigated as of today.
83- */
84- // interop-2023-contain
85- '/css/css-contain/container-queries/nested-query-containers.html' ,
86- '/css/css-contain/content-visibility/content-visibility-input-image.html' ,
87- '/css/css-contain/content-visibility/content-visibility-031.html' ,
88- '/css/css-contain/content-visibility/content-visibility-auto-state-changed.html' ,
89- '/css/selectors/invalidation/fullscreen-pseudo-class-in-has.html' ,
90- '/css/selectors/invalidation/modal-pseudo-class-in-has.html' ,
91- '/css/selectors/invalidation/user-action-pseudo-classes-in-has.html' ,
92- // interop-2023-modules
93- '/html/semantics/scripting-1/the-script-element/import-assertions/empty-assertion-clause.html' ,
94- '/html/semantics/scripting-1/the-script-element/import-assertions/unsupported-assertion.html' ,
95- '/workers/modules/dedicated-worker-import-blob-url.any.html' ,
96- '/workers/modules/dedicated-worker-import-blob-url.any.worker.html' ,
97- '/workers/modules/dedicated-worker-import-data-url-cross-origin.html' ,
98- '/workers/modules/dedicated-worker-import-data-url.any.html' ,
99- '/workers/modules/dedicated-worker-import-data-url.any.worker.html' ,
100- '/workers/modules/dedicated-worker-import-meta.html' ,
101- '/workers/modules/dedicated-worker-import.any.html' ,
102- '/workers/modules/dedicated-worker-import.any.worker.html' ,
103- '/workers/modules/dedicated-worker-options-credentials.html' ,
104- '/workers/modules/dedicated-worker-parse-error-failure.html' ,
105- '/workers/modules/shared-worker-import-data-url-cross-origin.html' ,
106- '/workers/modules/shared-worker-import-data-url.window.html' ,
107- '/workers/modules/shared-worker-options-credentials.html' ,
108- '/workers/modules/shared-worker-parse-error-failure.html' ,
109- '/import-maps/acquiring/modulepreload-link-header.html' ,
110- '/import-maps/acquiring/modulepreload.html' ,
111- '/workers/modules/shared-worker-import-failure.html' ,
112- '/import-maps/acquiring/dynamic-import.html' ,
113- '/import-maps/acquiring/script-tag-inline.html' ,
114- '/import-maps/acquiring/script-tag.html' ,
115- '/import-maps/bare-specifiers.sub.html' ,
116- // interop-2023-offscreencanvas
117- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.html' ,
118- '/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html' ,
119- '/html/canvas/offscreen/manual/convert-to-blob/offscreencanvas.convert.to.blob.w.html' ,
120- '/html/canvas/offscreen/manual/draw-generic-family/2d.text.draw.generic.family.w.html' ,
121- '/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html' ,
122- '/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.commit.w.html' ,
123- '/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.w.html' ,
124- '/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable.w.html' ,
125- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.html' ,
126- '/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.animated.poster.html' ,
127- '/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.html' ,
128- '/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.copy.html' ,
129- '/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-atop.html' ,
130- '/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.destination-in.html' ,
131- '/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-in.html' ,
132- '/html/canvas/offscreen/compositing/2d.composite.uncovered.pattern.source-out.html' ,
133- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.html' ,
134- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.html' ,
135- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.html' ,
136- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.html' ,
137- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.html' ,
138- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.html' ,
139- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.html' ,
140- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.html' ,
141- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.html' ,
142- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.html' ,
143- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.html' ,
144- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.html' ,
145- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.html' ,
146- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.html' ,
147- '/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.html' ,
148- '/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.html' ,
149- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.html' ,
150- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.html' ,
151- '/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.html' ,
152- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.html' ,
153- '/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.html' ,
154- '/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.html' ,
155- // interop-2023-events
156- '/uievents/mouse/cancel-mousedown-in-subframe.html' ,
157- '/pointerevents/pointerevent_attributes_hoverable_pointers.html?mouse' ,
158- '/pointerevents/pointerevent_attributes_nohover_pointers.html' ,
159- '/pointerevents/pointerevent_disabled_form_control.html?mouse' ,
160- '/html/user-activation/activation-trigger-pointerevent.html?mouse' ,
161- '/pointerevents/pointerevent_movementxy.html?mouse' ,
162- '/pointerevents/pointerevent_pointercapture_in_frame.html?mouse' ,
163- '/uievents/mouse/attributes.html' ,
164- // interop-2022-scrolling
165- '/css/css-scroll-snap/snap-at-user-scroll-end.html' ,
166- // interop-2023-webcodecs
167- '/webcodecs/videoDecoder-codec-specific.https.any.html?av1' ,
168- '/webcodecs/videoDecoder-codec-specific.https.any.html?h264_annexb' ,
169- '/webcodecs/videoDecoder-codec-specific.https.any.html?h264_avc' ,
170- '/webcodecs/videoDecoder-codec-specific.https.any.html?vp8' ,
171- '/webcodecs/videoDecoder-codec-specific.https.any.html?vp9' ,
172- '/webcodecs/videoDecoder-codec-specific.https.any.worker.html?av1' ,
173- '/webcodecs/videoDecoder-codec-specific.https.any.worker.html?h264_annexb' ,
174- '/webcodecs/videoDecoder-codec-specific.https.any.worker.html?h264_avc' ,
175- '/webcodecs/videoDecoder-codec-specific.https.any.worker.html?vp8' ,
176- '/webcodecs/videoDecoder-codec-specific.https.any.worker.html?vp9' ,
177- '/webcodecs/videoDecoder-codec-specific.https.any.worker.html?av1' ,
178- '/webcodecs/videoFrame-construction.any.html' ,
179- '/webcodecs/videoFrame-construction.crossOriginSource.sub.html' ,
180- '/webcodecs/videoFrame-construction.window.html' ,
181- '/webcodecs/videoFrame-serialization.crossAgentCluster.https.html' ,
182- '/webcodecs/videoFrame-serialization.crossAgentCluster.https.html' ,
183- '/webcodecs/temporal-svc-encoding.https.any.html?h264' ,
184- '/webcodecs/temporal-svc-encoding.https.any.html?vp8' ,
185- '/webcodecs/temporal-svc-encoding.https.any.html?vp9' ,
186- '/webcodecs/temporal-svc-encoding.https.any.worker.html?h264' ,
187- '/webcodecs/temporal-svc-encoding.https.any.worker.html?vp8' ,
188- '/webcodecs/temporal-svc-encoding.https.any.worker.html?vp9' ,
189- '/webcodecs/videoFrame-serialization.crossAgentCluster.https.html' ,
190- '/webcodecs/videoFrame-serialization.crossAgentCluster.https.html' ,
191- '/webcodecs/videoFrame-serialization.crossAgentCluster.https.html' ,
192- '/webcodecs/full-cycle-test.https.any.html?av1' ,
193- '/webcodecs/full-cycle-test.https.any.html?h264_annexb' ,
194- '/webcodecs/full-cycle-test.https.any.html?h264_avc' ,
195- '/webcodecs/full-cycle-test.https.any.html?vp9_p0' ,
196- '/webcodecs/full-cycle-test.https.any.html?vp9_p2' ,
197- '/webcodecs/full-cycle-test.https.any.worker.html?av1' ,
198- '/webcodecs/full-cycle-test.https.any.worker.html?h264_annexb' ,
199- '/webcodecs/full-cycle-test.https.any.worker.html?h264_avc' ,
200- '/webcodecs/full-cycle-test.https.any.worker.html?vp9_p0' ,
201- '/webcodecs/full-cycle-test.https.any.worker.html?vp9_p2' ,
202- '/webcodecs/full-cycle-test.https.any.html?vp8' ,
203- '/webcodecs/full-cycle-test.https.any.worker.html?vp8' ,
204- // interop-2023-webcomponents
205- '/shadow-dom/focus/focus-shadowhost-display-none.html' ,
206- '/custom-elements/form-associated/ElementInternals-labels.html' ,
207- '/custom-elements/form-associated/ElementInternals-setFormValue.html' ,
208- '/custom-elements/form-associated/ElementInternals-validation.html' ,
209- '/custom-elements/form-associated/form-disabled-callback.html' ,
210- ] ) ;
211-
36+ const RESULTS_TREE = Symbol ( 'run results tree' ) ;
37+ const COLLECT_NON_OK_TESTS = Symbol ( 'flag for whether to collect non-OK tests for this run' ) ;
21238
21339// Calculate interop score (passing in all browsers) for a category
21440// after tracking the category's scores for each browser.
@@ -277,16 +103,15 @@ function aggregateInteropTestScores(testPassCounts, numBrowsers) {
277103//
278104// 4. Because we round down twice, the score for a category can end up lower
279105// than if we used rational numbers.
280- function scoreRuns ( runs , allTestsSet ) {
106+ function scoreRuns ( runs , allTestsSet , nonOKTests ) {
281107 const scores = [ ] ;
282108 const testPassCounts = new Map ( ) ;
283- const unexpectedNonOKTests = new Set ( ) ;
284109
285110 try {
286111 for ( const run of runs ) {
287112 // Sum of the integer 0-1000 scores for each test.
288113 let score = 0 ;
289- lib . results . walkTests ( run . tree , ( path , test , results ) => {
114+ lib . results . walkTests ( run [ RESULTS_TREE ] , ( path , test , results ) => {
290115 const testname = path + '/' + test ;
291116 if ( ! allTestsSet . has ( testname ) ) {
292117 return ;
@@ -305,8 +130,8 @@ function scoreRuns(runs, allTestsSet) {
305130 testPassCounts . get ( testname ) [ 'subtestTotal' ] = [ ] ;
306131 }
307132 if ( 'subtests' in results ) {
308- if ( results [ 'status' ] != 'OK' && ! KNOWN_TEST_STATUSES . has ( testname ) ) {
309- unexpectedNonOKTests . add ( testname ) ;
133+ if ( results [ 'status' ] != 'OK' && run [ COLLECT_NON_OK_TESTS ] ) {
134+ nonOKTests . add ( testname ) ;
310135 }
311136 subtestTotal = results [ 'subtests' ] . length ;
312137 for ( const subtest of results [ 'subtests' ] ) {
@@ -354,27 +179,19 @@ function scoreRuns(runs, allTestsSet) {
354179 throw e ;
355180 }
356181
357- // Log and tests with unexpected non-OK statuses.
358- if ( unexpectedNonOKTests . size > 0 ) {
359- console . log ( 'Unexpected non-OK status for tests:' ) ;
360- for ( const testname of unexpectedNonOKTests . values ( ) ) {
361- console . log ( testname ) ;
362- }
363- }
364182 // Calculate the interop scores that have been saved and add
365183 // the interop score to the end of the browsers' scores array.
366184 scores . push ( aggregateInteropTestScores ( testPassCounts , runs . length ) ) ;
367185 return scores ;
368186}
369187
370- async function scoreCategory ( category , experimental , products , alignedRuns ,
371- testsSet ) {
188+ async function scoreAlignedRuns ( alignedRuns , testsSet , nonOKTests ) {
372189 // Score the test runs.
373190 const before = Date . now ( ) ;
374191 const dateToScores = new Map ( ) ;
375192 for ( const [ date , runs ] of alignedRuns . entries ( ) ) {
376193 const versions = runs . map ( run => run . browser_version ) ;
377- const scores = scoreRuns ( runs , testsSet ) ;
194+ const scores = scoreRuns ( runs , testsSet , nonOKTests ) ;
378195 dateToScores . set ( date , { versions, scores} ) ;
379196 }
380197 const after = Date . now ( ) ;
@@ -432,22 +249,27 @@ async function main() {
432249 // Load the test result trees into memory; creates a list of recursive tree
433250 // structures: tree = { trees: [...], tests: [...] }. Each 'tree' represents a
434251 // directory, each 'test' is the results from a given test file.
252+ //
253+ // Also set the LAST symbol to true on the last set of aligned runs, to allow
254+ // logging non-OK harness statuses for only those.
435255 console . log ( 'Iterating over all runs, loading test results' ) ;
436256 before = Date . now ( ) ;
257+ let lastRuns = null ;
437258 for ( const runs of alignedRuns . values ( ) ) {
438259 for ( const run of runs ) {
439- // Just in case someone ever adds a 'tree' field to the JSON.
440- if ( run . tree ) {
441- throw new Error ( 'Run JSON contains "tree" field; code needs changed.' ) ;
442- }
443- run . tree = await lib . results . getGitTree ( repo , run ) ;
260+ run [ RESULTS_TREE ] = await lib . results . getGitTree ( repo , run ) ;
444261 }
262+ lastRuns = runs ;
445263 }
446264 after = Date . now ( ) ;
447265 console . log ( `Loading ${ alignedRuns . size } sets of runs took ` +
448266 `${ after - before } ms` ) ;
267+ for ( const run of lastRuns ) {
268+ run [ COLLECT_NON_OK_TESTS ] = true ;
269+ }
449270
450271 const dateToScoresMaps = new Map ( ) ;
272+ const nonOKTests = new Set ( ) ;
451273
452274 // Map from labels to tests (includes)
453275 const labeledTests = new Map ( ) ;
@@ -464,7 +286,12 @@ async function main() {
464286 }
465287 }
466288 }
467- // category is an object with "name" and "labels" props.
289+
290+ // Score each category and add scores to |dateToScores|. For runs that have
291+ // COLLECT_NON_OK_TESTS set (the last ones) any non-OK harness statuses are
292+ // added to |nonOKTests|.
293+ //
294+ // Note: category is an object with "name" and "labels" props.
468295 for ( const category of categories ) {
469296 console . log ( `Scoring runs for ${ category . name } ` ) ;
470297 const testsSet = new Set ( ) ;
@@ -477,14 +304,18 @@ async function main() {
477304 // Keep a unique set of tests associated with the category.
478305 labeledTestsSet . forEach ( test => testsSet . add ( test ) ) ;
479306 }
480- const dateToScores = await scoreCategory ( category , experimental , products ,
481- alignedRuns , testsSet ) ;
307+ const dateToScores = await scoreAlignedRuns ( alignedRuns , testsSet , nonOKTests ) ;
482308 // Store the entire dateToScores for producing the unified CSV later.
483309 dateToScoresMaps . set ( category . name , dateToScores ) ;
484310 }
485311
312+ // Write non-OK harness statuses to a file.
313+ const lines = Array . from ( nonOKTests ) . sort ( ) ;
314+ lines . push ( '' ) ;
315+ await fs . promises . writeFile ( 'non-ok-harness-statuses.txt' , lines . join ( '\n' ) , 'utf-8' ) ;
316+
486317 // TODO: Once the other score CSVs are no longer used, we can push
487- // some of this logic into scoreCategory and simplify things.
318+ // some of this logic into scoreAlignedRuns and simplify things.
488319 let unifiedCsv = 'date' ;
489320 for ( const product of products ) {
490321 const categoryLabels = categories . map ( c => `${ product } -${ c . name } ` ) ;
0 commit comments