@@ -8,7 +8,9 @@ import type {
88 CoverageOptions ,
99 CoverageProvider ,
1010} from '../types/coverage' ;
11- import { logger } from '../utils' ;
11+ import { logger , type TraceSpan } from '../utils' ;
12+
13+ const traceNoop : TraceSpan = async ( _name , _cat , fn ) => fn ( ) ;
1214
1315export const getIncludedFiles = async (
1416 coverage : CoverageOptions ,
@@ -179,6 +181,7 @@ export async function generateCoverage(
179181 context : RstestContext ,
180182 coverageMap : CoverageMap ,
181183 coverageProvider : CoverageProvider ,
184+ traceSpan : TraceSpan = traceNoop ,
182185) : Promise < void > {
183186 const {
184187 rootPath,
@@ -188,51 +191,64 @@ export async function generateCoverage(
188191 try {
189192 const finalCoverageMap = coverageMap ;
190193
191- const rawDistPathRoot = context . normalizedConfig . output ?. distPath ?. root ;
192- const distPathRoot = rawDistPathRoot ? normalize ( rawDistPathRoot ) : '' ;
193- const normalizedRootPath = normalize ( rootPath ) ;
194- const setupCoverageExcludes = getSetupCoverageExcludes ( context ) ;
195- const absDistPathRoot = distPathRoot
196- ? normalize (
197- isAbsolute ( distPathRoot )
198- ? distPathRoot
199- : `${ normalizedRootPath } /${ distPathRoot } ` ,
200- )
201- : '' ;
202- finalCoverageMap . filter ( ( filePath ) => {
203- const normalizedFile = normalize ( filePath ) ;
204- const fileRelativeToRoot = normalize (
205- relative ( normalizedRootPath , normalizedFile ) ,
206- ) ;
207- if (
208- ( distPathRoot && isSameOrSubPath ( fileRelativeToRoot , distPathRoot ) ) ||
209- ( absDistPathRoot && isSameOrSubPath ( normalizedFile , absDistPathRoot ) )
210- ) {
211- return false ;
212- }
213- if ( isRuntimeSentinelCoverageFile ( normalizedFile ) ) {
214- return false ;
215- }
216- // Keep setupFiles/globalSetup out of the final report for every provider.
217- // Istanbul already excludes them before instrumentation; V8 needs this
218- // post-collection pruning so both providers converge on the same output.
219- if (
220- shouldExcludeSetupCoverageFile (
221- normalizedFile ,
222- normalizedRootPath ,
223- setupCoverageExcludes ,
224- )
225- ) {
226- return false ;
227- }
228- if ( ! coverage . allowExternal ) {
229- return isSameOrSubPath ( normalizedFile , normalize ( rootPath ) ) ;
230- }
231- return true ;
232- } ) ;
194+ await traceSpan (
195+ 'coverage:filter-files' ,
196+ 'coverage' ,
197+ ( ) => {
198+ const rawDistPathRoot = context . normalizedConfig . output ?. distPath ?. root ;
199+ const distPathRoot = rawDistPathRoot ? normalize ( rawDistPathRoot ) : '' ;
200+ const normalizedRootPath = normalize ( rootPath ) ;
201+ const setupCoverageExcludes = getSetupCoverageExcludes ( context ) ;
202+ const absDistPathRoot = distPathRoot
203+ ? normalize (
204+ isAbsolute ( distPathRoot )
205+ ? distPathRoot
206+ : `${ normalizedRootPath } /${ distPathRoot } ` ,
207+ )
208+ : '' ;
209+ finalCoverageMap . filter ( ( filePath ) => {
210+ const normalizedFile = normalize ( filePath ) ;
211+ const fileRelativeToRoot = normalize (
212+ relative ( normalizedRootPath , normalizedFile ) ,
213+ ) ;
214+ if (
215+ ( distPathRoot &&
216+ isSameOrSubPath ( fileRelativeToRoot , distPathRoot ) ) ||
217+ ( absDistPathRoot &&
218+ isSameOrSubPath ( normalizedFile , absDistPathRoot ) )
219+ ) {
220+ return false ;
221+ }
222+ if ( isRuntimeSentinelCoverageFile ( normalizedFile ) ) {
223+ return false ;
224+ }
225+ // Keep setupFiles/globalSetup out of the final report for every provider.
226+ // Istanbul already excludes them before instrumentation; V8 needs this
227+ // post-collection pruning so both providers converge on the same output.
228+ if (
229+ shouldExcludeSetupCoverageFile (
230+ normalizedFile ,
231+ normalizedRootPath ,
232+ setupCoverageExcludes ,
233+ )
234+ ) {
235+ return false ;
236+ }
237+ if ( ! coverage . allowExternal ) {
238+ return isSameOrSubPath ( normalizedFile , normalizedRootPath ) ;
239+ }
240+ return true ;
241+ } ) ;
242+ } ,
243+ { allowExternal : coverage . allowExternal } ,
244+ ) ;
233245
234246 if ( coverage . include ?. length ) {
235- const coveredFilesSet = new Set ( finalCoverageMap . files ( ) . map ( normalize ) ) ;
247+ const coveredFilesSet = await traceSpan (
248+ 'coverage:collect-covered-files' ,
249+ 'coverage' ,
250+ ( ) => new Set ( finalCoverageMap . files ( ) . map ( normalize ) ) ,
251+ ) ;
236252
237253 let isTimeout = false ;
238254
@@ -247,14 +263,23 @@ export async function generateCoverage(
247263 // intermediate data be GC'd before the next one starts.
248264 const allFiles : string [ ] = [ ] ;
249265 for ( const p of projects ) {
250- const includedFiles = filterChangedFiles (
251- filterExternalFiles (
252- await getIncludedFiles ( coverage , p . rootPath ) ,
253- p . rootPath ,
254- coverage . allowExternal ,
255- ) ,
256- context . changedCoverageFilters ,
257- p . rootPath ,
266+ const includedFiles = await traceSpan (
267+ 'coverage:collect-included-files' ,
268+ 'coverage' ,
269+ async ( ) =>
270+ filterChangedFiles (
271+ filterExternalFiles (
272+ await getIncludedFiles ( coverage , p . rootPath ) ,
273+ p . rootPath ,
274+ coverage . allowExternal ,
275+ ) ,
276+ context . changedCoverageFilters ,
277+ p . rootPath ,
278+ ) ,
279+ {
280+ project : p . environmentName ,
281+ changedOnly : Boolean ( context . changedCoverageFilters ?. length ) ,
282+ } ,
258283 ) ;
259284 allFiles . push ( ...includedFiles ) ;
260285
@@ -263,11 +288,21 @@ export async function generateCoverage(
263288 ) ;
264289
265290 if ( uncoveredFiles . length ) {
266- await generateCoverageForUntestedFiles (
267- p . environmentName ,
268- uncoveredFiles ,
269- finalCoverageMap ,
270- coverageProvider ,
291+ await traceSpan (
292+ 'coverage:generate-untested-files' ,
293+ 'coverage' ,
294+ ( ) =>
295+ generateCoverageForUntestedFiles (
296+ p . environmentName ,
297+ uncoveredFiles ,
298+ finalCoverageMap ,
299+ coverageProvider ,
300+ traceSpan ,
301+ ) ,
302+ {
303+ project : p . environmentName ,
304+ fileCount : uncoveredFiles . length ,
305+ } ,
271306 ) ;
272307 }
273308 }
@@ -280,26 +315,53 @@ export async function generateCoverage(
280315
281316 // should be better to filter files before swc coverage is processed
282317 const allFilesSet = new Set ( allFiles . map ( normalize ) ) ;
283- finalCoverageMap . filter ( ( file ) => allFilesSet . has ( normalize ( file ) ) ) ;
318+ await traceSpan (
319+ 'coverage:filter-included-files' ,
320+ 'coverage' ,
321+ ( ) => {
322+ finalCoverageMap . filter ( ( file ) => allFilesSet . has ( normalize ( file ) ) ) ;
323+ } ,
324+ { fileCount : allFilesSet . size } ,
325+ ) ;
284326 } else if ( context . changedCoverageFilters ?. length ) {
285- finalCoverageMap . filter (
286- ( file ) =>
287- filterChangedFiles ( [ file ] , context . changedCoverageFilters , rootPath )
288- . length > 0 ,
327+ await traceSpan (
328+ 'coverage:filter-changed-files' ,
329+ 'coverage' ,
330+ ( ) => {
331+ finalCoverageMap . filter (
332+ ( file ) =>
333+ filterChangedFiles (
334+ [ file ] ,
335+ context . changedCoverageFilters ,
336+ rootPath ,
337+ ) . length > 0 ,
338+ ) ;
339+ } ,
340+ { filterCount : context . changedCoverageFilters . length } ,
289341 ) ;
290342 }
291343
292344 // Generate coverage reports
293- await coverageProvider . generateReports ( finalCoverageMap , coverage ) ;
345+ await traceSpan ( 'coverage:generate-reports' , 'coverage' , ( ) =>
346+ coverageProvider . generateReports ( finalCoverageMap , coverage ) ,
347+ ) ;
294348
295349 if ( coverage . thresholds ) {
296- const { checkThresholds } = await import ( '../coverage/checkThresholds' ) ;
297- const thresholdResult = checkThresholds ( {
298- coverageMap : finalCoverageMap ,
299- coverageProvider,
300- rootPath,
301- thresholds : coverage . thresholds ,
302- } ) ;
350+ const { thresholds } = coverage ;
351+ const thresholdResult = await traceSpan (
352+ 'coverage:check-thresholds' ,
353+ 'coverage' ,
354+ async ( ) => {
355+ const { checkThresholds } =
356+ await import ( '../coverage/checkThresholds' ) ;
357+ return checkThresholds ( {
358+ coverageMap : finalCoverageMap ,
359+ coverageProvider,
360+ rootPath,
361+ thresholds,
362+ } ) ;
363+ } ,
364+ ) ;
303365 if ( ! thresholdResult . success ) {
304366 logger . log ( '' ) ;
305367 logger . stderr ( thresholdResult . message ) ;
@@ -317,6 +379,7 @@ async function generateCoverageForUntestedFiles(
317379 uncoveredFiles : string [ ] ,
318380 coverageMap : CoverageMap ,
319381 coverageProvider : CoverageProvider ,
382+ traceSpan : TraceSpan = traceNoop ,
320383) : Promise < void > {
321384 if ( ! coverageProvider . generateCoverageForUntestedFiles ) {
322385 logger . warn (
@@ -334,10 +397,21 @@ async function generateCoverageForUntestedFiles(
334397 const batchSize = 25 ;
335398
336399 for ( let index = 0 ; index < uncoveredFiles . length ; index += batchSize ) {
337- const coverages = await coverageProvider . generateCoverageForUntestedFiles ( {
338- environmentName,
339- files : uncoveredFiles . slice ( index , index + batchSize ) ,
340- } ) ;
400+ const files = uncoveredFiles . slice ( index , index + batchSize ) ;
401+ const coverages = await traceSpan (
402+ 'coverage:generate-untested-files-batch' ,
403+ 'coverage' ,
404+ ( ) =>
405+ coverageProvider . generateCoverageForUntestedFiles ! ( {
406+ environmentName,
407+ files,
408+ } ) ,
409+ {
410+ environmentName,
411+ batchIndex : Math . floor ( index / batchSize ) ,
412+ fileCount : files . length ,
413+ } ,
414+ ) ;
341415
342416 coverages . forEach ( ( coverageData ) => {
343417 coverageMap . addFileCoverage ( coverageData ) ;
0 commit comments