@@ -8,6 +8,7 @@ const path = require('path');
88const events = require ( 'events' ) ;
99const os = require ( 'os' ) ;
1010const { inspect } = require ( 'util' ) ;
11+ const { pathToFileURL } = require ( 'url' ) ;
1112const { Worker } = require ( 'worker_threads' ) ;
1213
1314const workerPath = path . join ( __dirname , 'wpt/worker.js' ) ;
@@ -194,6 +195,24 @@ class ResourceLoader {
194195 fixtures . path ( 'wpt' , base , url ) ;
195196 }
196197
198+ /**
199+ * Map a URL that a test would have fetched from the WPT server (an
200+ * absolute path, or a path relative to the test file) to a file: URL
201+ * into the fixtures directory. URLs that already have a scheme (data:,
202+ * blob:, http:, ...) are returned unchanged.
203+ * @param {string } from the path of the file loading this resource,
204+ * relative to the WPT folder.
205+ * @param {string|URL } url the url of the resource being loaded.
206+ * @returns {string }
207+ */
208+ mapServerURL ( from , url ) {
209+ url = `${ url } ` ;
210+ if ( / ^ [ a - z A - Z ] [ a - z A - Z 0 - 9 + . - ] * : | ^ \/ \/ / . test ( url ) ) {
211+ return url ;
212+ }
213+ return pathToFileURL ( this . toRealFilePath ( from , url ) ) . href ;
214+ }
215+
197216 /**
198217 * Load a resource in test/fixtures/wpt specified with a URL
199218 * @param {string } from the path of the file loading this resource,
@@ -389,13 +408,15 @@ class WPTTestSpec {
389408 * @returns {{ script?: string[]; variant?: string[]; [key: string]: string } } parsed META tags of a spec file
390409 */
391410 getMeta ( ) {
392- const matches = this . getContent ( ) . match ( / \/ \/ M E T A : .+ / g) ;
411+ // Like upstream, tolerate missing whitespace around "META:".
412+ // Refs: https://github.com/web-platform-tests/wpt/blob/master/tools/manifest/sourcefile.py
413+ const matches = this . getContent ( ) . match ( / \/ \/ \s * M E T A : \s * .+ / g) ;
393414 if ( ! matches ) {
394415 return { } ;
395416 }
396417 const result = { } ;
397418 for ( const match of matches ) {
398- const parts = match . match ( / \/ \/ M E T A : ( [ ^ = ] + ?) = ( .+ ) / ) ;
419+ const parts = match . match ( / \/ \/ \s * M E T A : \s * ( [ ^ = ] + ?) = ( .+ ) / ) ;
399420 const key = parts [ 1 ] ;
400421 const value = parts [ 2 ] ;
401422 if ( key === 'script' || key === 'variant' ) {
@@ -570,7 +591,11 @@ class WPTRunner {
570591 this . resource = new ResourceLoader ( path ) ;
571592 this . concurrency = concurrency ;
572593
573- this . flags = [ ] ;
594+ // Since we need to prepare the Web Worker APIs
595+ // in the harness that runs on all WPT workers,
596+ // we enable the API globally. This has no practical
597+ // effect on the non-web-worker tests, however.
598+ this . flags = [ '--experimental-web-worker' ] ;
574599 this . globalThisInitScripts = [ ] ;
575600 this . initScript = null ;
576601
@@ -596,7 +621,7 @@ class WPTRunner {
596621 * @param {string[] } flags
597622 */
598623 setFlags ( flags ) {
599- this . flags = flags ;
624+ this . flags = this . flags . concat ( flags ) ;
600625 }
601626
602627 /**
@@ -679,23 +704,41 @@ class WPTRunner {
679704 const absolutePath = spec . getAbsolutePath ( ) ;
680705 const relativePath = spec . getRelativePath ( ) ;
681706 const harnessPath = fixtures . path ( 'wpt' , 'resources' , 'testharness.js' ) ;
682-
683- // Scripts specified with the `// META: script=` header
684- const scriptsToRun = meta . script ?. map ( ( script ) => {
707+ // *.worker.js tests are dedicated worker tests by definition.
708+ // Multi-global (*.any.js) tests whose global scopes include a
709+ // dedicated worker but not a window also run inside an actual Web
710+ // Worker, like the .any.worker.html variant generated by the WPT
711+ // server does. Refs:
712+ // https://web-platform-tests.org/writing-tests/testharness.html#multi-global-tests
713+ const isAnyTest = / \. a n y \. j s $ / . test ( spec . filename ) ;
714+ const globalScopes = isAnyTest ?
715+ ( meta . global ?. split ( ',' ) . map ( ( s ) => s . trim ( ) ) ??
716+ [ 'window' , 'dedicatedworker' ] ) : [ ] ;
717+ const isWebWorkerTest = / \. w o r k e r \. j s $ / . test ( spec . filename ) ||
718+ ( isAnyTest && ! globalScopes . includes ( 'window' ) &&
719+ ( globalScopes . includes ( 'worker' ) ||
720+ globalScopes . includes ( 'dedicatedworker' ) ) ) ;
721+
722+ // Scripts specified with the `// META: script=` header. For tests
723+ // that run inside a Web Worker they are imported by the worker
724+ // instead.
725+ const scriptsToRun = isWebWorkerTest ? [ ] : meta . script ?. map ( ( script ) => {
685726 const obj = {
686727 filename : this . resource . toRealFilePath ( relativePath , script ) ,
687728 code : this . resource . read ( relativePath , script ) ,
688729 } ;
689730 this . scriptsModifier ?. ( obj ) ;
690731 return obj ;
691732 } ) ?? [ ] ;
692- // The actual test
693- const obj = {
694- code : content ,
695- filename : absolutePath ,
696- } ;
697- this . scriptsModifier ?. ( obj ) ;
698- scriptsToRun . push ( obj ) ;
733+ if ( ! isWebWorkerTest ) {
734+ // The actual test
735+ const obj = {
736+ code : content ,
737+ filename : absolutePath ,
738+ } ;
739+ this . scriptsModifier ?. ( obj ) ;
740+ scriptsToRun . push ( obj ) ;
741+ }
699742
700743 run ( async ( ) => {
701744 const worker = new Worker ( workerPath , {
@@ -710,6 +753,15 @@ class WPTRunner {
710753 filename : harnessPath ,
711754 } ,
712755 scriptsToRun,
756+ // Set when the test runs inside an actual Web Worker.
757+ webWorker : isWebWorkerTest ? {
758+ path : absolutePath ,
759+ isAnyTest,
760+ scripts : meta . script ?. map (
761+ ( script ) => this . resource . toRealFilePath ( relativePath , script ) ,
762+ ) ?? [ ] ,
763+ skippedTests : spec . skippedTests ,
764+ } : undefined ,
713765 needsGc : ! ! meta . script ?. find ( ( script ) => script === '/common/gc.js' ) ,
714766 skippedTests : spec . skippedTests ,
715767 } ,
0 commit comments