1+ import assert from 'assert' ;
12import { X2jOptions , XMLParser } from 'fast-xml-parser' ;
23import * as fs from 'fs' ;
4+ import { cpus } from 'os' ;
35import * as path from 'path' ;
46import * as vscode from 'vscode' ;
57import { CancellationToken } from 'vscode-languageclient' ;
6- import { parallelize , staggerProgress } from './helpers' ;
7- import { cpus } from 'os' ;
8- import { assert } from 'console' ;
8+ import { adaExtState } from './extension' ;
9+ import { getMatchingPrefixes , parallelize , staggerProgress , toPosix } from './helpers' ;
910
1011/**
1112 * TypeScript types to represent data from GNATcoverage XML reports
@@ -299,25 +300,109 @@ export async function addCoverageData(run: vscode.TestRun, covDir: string) {
299300 progress . report ( {
300301 message : `${ done } / ${ totalFiles } source files` ,
301302 } ) ;
303+
304+ let posixForeignPrefix : string | undefined ;
305+ let posixLocalPrefix : string | undefined ;
306+
307+ const procs = process . env . PROCESSORS ? Number ( process . env . PROCESSORS ) : 0 ;
302308 const fileCovs = (
303309 await parallelize (
304310 array ,
305- Math . min ( cpus ( ) . length , 8 ) ,
311+ Math . min ( procs == 0 ? cpus ( ) . length : procs , 8 ) ,
306312 async ( file ) => {
307313 if ( token . isCancellationRequested ) {
308314 throw new vscode . CancellationError ( ) ;
309315 }
310316
311317 assert ( file [ '@_name' ] ) ;
318+ const foreignPath = file [ '@_name' ] ;
319+ /**
320+ * The foreign machine may have a different path
321+ * format, so we normalize to POSIX which is valid on
322+ * both Windows and POSIX OS-es.
323+ */
324+ const posixForeignPath = toPosix ( foreignPath ) ;
312325
313- let srcUri : vscode . Uri ;
314- if ( path . isAbsolute ( file [ '@_name' ] ! ) ) {
315- srcUri = vscode . Uri . file ( file [ '@_name' ] ! ) ;
316- } else {
326+ let srcUri : vscode . Uri | undefined = undefined ;
327+
328+ /**
329+ * The goal here is to find the file in the workspace
330+ * corresponding to the name attribute in the GNATcov
331+ * report.
332+ *
333+ * The name could be a basename (older GNATcov
334+ * versions) or an absolute path (newer GNATcov
335+ * versions).
336+ *
337+ * In the case of a basename, the only course of action
338+ * is to do a file lookup in the workspace.
339+ *
340+ * In the case of an absolute path, the path
341+ * corresponds to the machine where the report was
342+ * created which might be a foreign machine or the
343+ * local host. We can't know in which situation we are
344+ * so it's best to assume that it's a foreign machine.
345+ *
346+ * Then the logic consists of searching for the first
347+ * file by basename, which gives a local absolute path.
348+ * Then by comparing the local absolute path and the
349+ * foreign absolute path, we can find a foreign prefix
350+ * path that should be mapped to the local prefix path
351+ * to compute local absolute paths. Subsequent files
352+ * can use the computed prefixes directly without a
353+ * workspace lookup.
354+ */
355+
356+ if ( path . posix . isAbsolute ( posixForeignPath ) ) {
357+ let localFullPath ;
358+ if ( posixLocalPrefix && posixForeignPrefix ) {
359+ /**
360+ * The prefixes have already been determined, so
361+ * use them directly.
362+ */
363+
364+ if ( posixForeignPath . startsWith ( posixForeignPrefix ) ) {
365+ // Extract the relative path based on the foreign prefix
366+ const posixForeignRelPath = path . relative (
367+ posixForeignPrefix ,
368+ posixForeignPath ,
369+ ) ;
370+
371+ // Resolve the relative path with the local prefix
372+ localFullPath = path . join (
373+ posixLocalPrefix ,
374+ posixForeignRelPath ,
375+ ) ;
376+ }
377+ }
378+
379+ // Fallback to using the input path as is
380+ localFullPath = localFullPath ?? foreignPath ;
381+
382+ if ( fs . existsSync ( localFullPath ) ) {
383+ srcUri = vscode . Uri . file ( localFullPath ) ;
384+ }
385+ }
386+
387+ if ( srcUri === undefined ) {
388+ /**
389+ * If the prefixes haven't been found yet, or
390+ * the last prefixes used were not successful,
391+ * try a workspace lookup of the basename.
392+ */
317393 const found = await vscode . workspace . findFiles (
318- `**/${ file [ '@_name' ] ! } ` ,
319- null ,
394+ `**/${ path . posix . basename ( posixForeignPath ) } ` ,
395+ /**
396+ * Avoid searching in the object dir because we
397+ * might land on gnatcov-instrumented versions
398+ * of the sources.
399+ */
400+ `${ await adaExtState
401+ . getObjectDir ( )
402+ . then ( ( objDir ) => `${ objDir } /**/*` )
403+ . catch ( ( ) => null ) } `,
320404 1 ,
405+ token ,
321406 ) ;
322407 if ( found . length == 0 ) {
323408 return undefined ;
@@ -326,6 +411,27 @@ export async function addCoverageData(run: vscode.TestRun, covDir: string) {
326411 srcUri = found [ 0 ] ;
327412 }
328413
414+ if (
415+ posixForeignPrefix === undefined &&
416+ posixLocalPrefix === undefined &&
417+ path . posix . isAbsolute ( posixForeignPath )
418+ ) {
419+ /**
420+ * If the prefixes haven't been calculated, and the
421+ * foreign path is absolute, let's try to compute
422+ * the prefixes based on the workspace URI that was
423+ * found.
424+ */
425+
426+ const localAbsPath = srcUri . fsPath ;
427+ const posixLocalAbsPath = toPosix ( localAbsPath ) ;
428+
429+ [ posixForeignPrefix , posixLocalPrefix ] = getMatchingPrefixes (
430+ posixForeignPath ,
431+ posixLocalAbsPath ,
432+ ) ;
433+ }
434+
329435 const total = file . metric . find (
330436 ( m ) => m [ '@_kind' ] == 'total_lines_of_relevance' ,
331437 ) ! [ '@_count' ] ;
@@ -334,14 +440,15 @@ export async function addCoverageData(run: vscode.TestRun, covDir: string) {
334440 ] ;
335441
336442 const fileReportBasename = data . coverage_report . sources ! [ 'xi:include' ] . find (
337- ( inc ) => inc [ '@_href' ] == `${ path . basename ( file [ '@_name' ] ! ) } .xml` ,
443+ ( inc ) =>
444+ inc [ '@_href' ] == `${ path . posix . basename ( posixForeignPath ) } .xml` ,
338445 ) ! [ '@_href' ] ;
339446 const fileReportPath = path . join ( covDir , fileReportBasename ) ;
340447
341448 if ( covered > total ) {
342449 throw Error (
343450 `Got ${ covered } covered lines for a` +
344- ` total of ${ total } in ${ file [ '@_name' ] ! } ` ,
451+ ` total of ${ total } in ${ file [ '@_name' ] } ` ,
345452 ) ;
346453 }
347454
0 commit comments