@@ -74,6 +74,11 @@ const UTF8 = { encoding: 'utf-8' };
7474 * fix test failures, removing tests that were expected to fail but now pass
7575 * from the expected-failure files. This option does not add newly failing
7676 * tests to the expected-failure files - this must be done manually.
77+ * @property {number= } maxFailures Whether to stop executing test files after a
78+ * certain number of failures have been reached. Useful for preventing your
79+ * console from becoming overwhelmed.
80+ * @property {boolean= } fullPath Whether to print out the absolute file paths
81+ * of test files in reports.
7782 *
7883 * @param {Options } options Object with the following properties:
7984 * - `polyfillCodeFile: string` - Filename of the Temporal polyfill. Must be a
@@ -107,10 +112,23 @@ const UTF8 = { encoding: 'utf-8' };
107112 * fix test failures, removing tests that were expected to fail but now pass
108113 * from the expected-failure files. This option does not add newly failing
109114 * tests to the expected-failure files - this must be done manually.
115+ * - `maxFailures?: number` - Whether to stop executing test files after a
116+ * certain number of failures have been reached. Useful for preventing your
117+ * console from becoming overwhelmed.
118+ * - `fullPath?: boolean` - Whether to print out the absolute file paths
119+ * of test files in reports.
110120 * @returns {boolean } `true` if all tests completed as expected, `false` if not.
111121 */
112- export default function runTest262 ( { test262Dir, testGlobs, polyfillCodeFile, expectedFailureFiles, timeoutMsecs, updateExpectedFailureFiles } ) {
113-
122+ export default function runTest262 ( {
123+ test262Dir,
124+ testGlobs,
125+ polyfillCodeFile,
126+ expectedFailureFiles,
127+ timeoutMsecs,
128+ updateExpectedFailureFiles,
129+ maxFailures,
130+ fullPath
131+ } ) {
114132 // Default timeout is 2 seconds. Set a longer timeout for running tests under
115133 // a debugger.
116134 timeoutMsecs = parseInt ( timeoutMsecs ) ;
@@ -177,7 +195,7 @@ export default function runTest262({ test262Dir, testGlobs, polyfillCodeFile, ex
177195 function getHelperScript ( includeName ) {
178196 if ( helpersCache . has ( includeName ) ) return helpersCache . get ( includeName ) ;
179197
180- const includeFile = `test262/ harness/ ${ includeName } ` ;
198+ const includeFile = path . join ( test262Dir , ' harness' , includeName ) ;
181199 const includeCode = fs . readFileSync ( includeFile , UTF8 ) ;
182200 const include = new vm . Script ( includeCode , { filename : path . resolve ( includeFile ) } ) ;
183201
@@ -265,9 +283,16 @@ export default function runTest262({ test262Dir, testGlobs, polyfillCodeFile, ex
265283 let passCount = 0 ;
266284 let expectedFailCount = 0 ;
267285 let unexpectedPassCount = 0 ;
286+ let skippedCount = 0 ;
268287
269288 // === The test loop ===
270289 for ( const testFile of testFiles ) {
290+ // Skip test if over the max-failure limit
291+ if ( maxFailures && failures . length >= maxFailures ) {
292+ skippedCount ++ ;
293+ continue ;
294+ }
295+
271296 // Set up the VM context with the polyfill first, as if it were built-in
272297 const testContext = { } ;
273298 vm . createContext ( testContext ) ;
@@ -285,7 +310,7 @@ export default function runTest262({ test262Dir, testGlobs, polyfillCodeFile, ex
285310
286311 // Include a sourceURL so that when tests are run in a debugger they can be
287312 // found using the names listed in the expected-failures-style files.
288- testCode += `\n//# sourceURL=${ testRelPath } ` ;
313+ testCode += `\n//# sourceURL=file:// ${ testFile } ` ;
289314
290315 const frontmatterString = frontmatterMatcher . exec ( testCode ) ?. [ 1 ] ?? '' ;
291316 const frontmatter = yaml . load ( frontmatterString ) ;
@@ -319,7 +344,9 @@ export default function runTest262({ test262Dir, testGlobs, polyfillCodeFile, ex
319344 // what it's supposed to be. This is so that you don't have to wait until the
320345 // end to see if your test failed.
321346 try {
322- vm . runInContext ( testCode , testContext , { timeout : timeoutMsecs } ) ;
347+ const testScript = new vm . Script ( testCode , { filename : testFile } ) ;
348+ testScript . runInContext ( testContext , { timeout : timeoutMsecs } ) ;
349+
323350 if ( ! expectedFailureLists ) {
324351 passCount ++ ;
325352 } else {
@@ -336,7 +363,7 @@ export default function runTest262({ test262Dir, testGlobs, polyfillCodeFile, ex
336363 if ( expectedFailureLists ) {
337364 expectedFailCount ++ ;
338365 } else {
339- failures . push ( { file : testRelPath , error : e } ) ;
366+ failures . push ( { file : fullPath ? path . resolve ( testFile ) : testRelPath , error : e } ) ;
340367 progress . interrupt ( `FAIL: ${ testDisplayName } ` ) ;
341368 }
342369 }
@@ -350,6 +377,32 @@ export default function runTest262({ test262Dir, testGlobs, polyfillCodeFile, ex
350377 progress . tick ( 1 , { test : progressDisplayName } ) ;
351378 }
352379
380+ // === Detect expected-failure entries with missing files ===
381+
382+ const missingTestsMap = new Map ( ) ;
383+ let missingTestsCnt = 0 ;
384+
385+ if ( testGlobs . length === 0 ) {
386+ const testRelPathSet = new Set (
387+ [ ...testFiles ] . map ( ( testFile ) => path . relative ( testSubdirectory , testFile ) )
388+ ) ;
389+
390+ for ( const [ expectedFailureFile , expectedFailureTestsSet ] of expectedFailureLists ) {
391+ const missingTestsSet = new Set ( ) ;
392+
393+ for ( const expectedFailureTest of expectedFailureTestsSet ) {
394+ if ( ! testRelPathSet . has ( expectedFailureTest ) ) {
395+ missingTestsSet . add ( expectedFailureTest ) ;
396+ missingTestsCnt ++ ;
397+ }
398+ }
399+
400+ if ( missingTestsSet . size ) {
401+ missingTestsMap . set ( expectedFailureFile , missingTestsSet ) ;
402+ }
403+ }
404+ }
405+
353406 // === Print results ===
354407
355408 const finish = process . hrtime . bigint ( ) ;
@@ -378,9 +431,24 @@ export default function runTest262({ test262Dir, testGlobs, polyfillCodeFile, ex
378431 }
379432 for ( const [ expectedFailureFile , unexpectedPassesSet ] of unexpectedPasses ) {
380433 if ( updateExpectedFailureFiles ) updateExpectedFailureFile ( expectedFailureFile , unexpectedPassesSet ) ;
381- print ( ` \u2022 ${ expectedFailureFile } :` ) ;
434+ print ( ` \u2022 ${ expectedFailureFile } :` ) ;
382435 for ( const unexpectedPass of unexpectedPassesSet ) {
383- print ( `${ unexpectedPass } ` ) ;
436+ print ( ` \u2022 ${ unexpectedPass } ` ) ;
437+ }
438+ }
439+ }
440+
441+ if ( missingTestsMap . size > 0 ) {
442+ if ( updateExpectedFailureFiles ) {
443+ print ( `\n${ color . yellow . bold ( 'WARNING:' ) } Tests not found; references have been removed from the following expected-failure files:` ) ;
444+ } else {
445+ print ( `\n${ color . yellow . bold ( 'WARNING:' ) } Tests not found; remove references from the following expected-failure files?` ) ;
446+ }
447+ for ( const [ expectedFailureFile , missingTestsSet ] of missingTestsMap ) {
448+ if ( updateExpectedFailureFiles ) updateExpectedFailureFile ( expectedFailureFile , missingTestsSet ) ;
449+ print ( ` \u2022 ${ expectedFailureFile } :` ) ;
450+ for ( const missingTest of missingTestsSet ) {
451+ print ( ` \u2022 ${ missingTest } ` ) ;
384452 }
385453 }
386454 }
@@ -397,16 +465,24 @@ export default function runTest262({ test262Dir, testGlobs, polyfillCodeFile, ex
397465 print ( color . green ( ` ${ passCount } passed` ) ) ;
398466 print ( color . red ( ` ${ failures . length } failed` ) ) ;
399467 print ( color . red ( ` ${ unexpectedPassCount } passed unexpectedly` ) ) ;
468+
400469 if ( expectedFailCount > 0 ) {
401470 print ( color . cyan ( ` ${ expectedFailCount } expected failures` ) ) ;
402471 }
472+ if ( missingTestsCnt > 0 ) {
473+ print ( color . cyan ( ` ${ missingTestsCnt } missing tests` ) ) ;
474+ }
475+ if ( skippedCount > 0 ) {
476+ print ( color . grey ( ` ${ skippedCount } skipped` ) ) ;
477+ }
478+
403479 return ! hasFailures ;
404480}
405481
406- function updateExpectedFailureFile ( fileName , expectedFailuresInFile ) {
482+ function updateExpectedFailureFile ( fileName , linesForRemoval ) {
407483 const linesOnDisk = fs
408484 . readFileSync ( fileName , UTF8 )
409485 . split ( / \r ? \n / g) ;
410- const output = linesOnDisk . filter ( l => ! expectedFailuresInFile . has ( l ) ) ;
486+ const output = linesOnDisk . filter ( l => ! linesForRemoval . has ( l ) ) ;
411487 fs . writeFileSync ( fileName , output . join ( '\n' ) , UTF8 ) ;
412488}
0 commit comments