1
+ import assert from 'assert' ;
1
2
import { X2jOptions , XMLParser } from 'fast-xml-parser' ;
2
3
import * as fs from 'fs' ;
4
+ import { cpus } from 'os' ;
3
5
import * as path from 'path' ;
4
6
import * as vscode from 'vscode' ;
5
7
import { 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' ;
9
10
10
11
/**
11
12
* TypeScript types to represent data from GNATcoverage XML reports
@@ -299,25 +300,109 @@ export async function addCoverageData(run: vscode.TestRun, covDir: string) {
299
300
progress . report ( {
300
301
message : `${ done } / ${ totalFiles } source files` ,
301
302
} ) ;
303
+
304
+ let posixForeignPrefix : string | undefined ;
305
+ let posixLocalPrefix : string | undefined ;
306
+
307
+ const procs = process . env . PROCESSORS ? Number ( process . env . PROCESSORS ) : 0 ;
302
308
const fileCovs = (
303
309
await parallelize (
304
310
array ,
305
- Math . min ( cpus ( ) . length , 8 ) ,
311
+ Math . min ( procs == 0 ? cpus ( ) . length : procs , 8 ) ,
306
312
async ( file ) => {
307
313
if ( token . isCancellationRequested ) {
308
314
throw new vscode . CancellationError ( ) ;
309
315
}
310
316
311
317
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 ) ;
312
325
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
+ */
317
393
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 ) } `,
320
404
1 ,
405
+ token ,
321
406
) ;
322
407
if ( found . length == 0 ) {
323
408
return undefined ;
@@ -326,6 +411,27 @@ export async function addCoverageData(run: vscode.TestRun, covDir: string) {
326
411
srcUri = found [ 0 ] ;
327
412
}
328
413
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
+
329
435
const total = file . metric . find (
330
436
( m ) => m [ '@_kind' ] == 'total_lines_of_relevance' ,
331
437
) ! [ '@_count' ] ;
@@ -334,14 +440,15 @@ export async function addCoverageData(run: vscode.TestRun, covDir: string) {
334
440
] ;
335
441
336
442
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` ,
338
445
) ! [ '@_href' ] ;
339
446
const fileReportPath = path . join ( covDir , fileReportBasename ) ;
340
447
341
448
if ( covered > total ) {
342
449
throw Error (
343
450
`Got ${ covered } covered lines for a` +
344
- ` total of ${ total } in ${ file [ '@_name' ] ! } ` ,
451
+ ` total of ${ total } in ${ file [ '@_name' ] } ` ,
345
452
) ;
346
453
}
347
454
0 commit comments