Skip to content

Commit 2d1ecbf

Browse files
MoLowRafaelGSS
authored andcommitted
test_runner: calculate executed lines using source map
PR-URL: #53315 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
1 parent 34f2098 commit 2d1ecbf

5 files changed

Lines changed: 78 additions & 10 deletions

File tree

‎lib/internal/test_runner/coverage.js‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ const kLineSplitRegex = /(?<=\r?\n)/u;
3636
constkStatusRegex=/\/\*node:coverage(?<status>enable|disable)\*\//;
3737

3838
classCoverageLine{
39-
constructor(line,src,startOffset){
40-
constnewlineLength=
39+
constructor(line,startOffset,src,length=src?.length){
40+
constnewlineLength=src==null ? 0 :
4141
RegExpPrototypeExec(kLineEndingRegex,src)?.[0].length??0;
4242

4343
this.line=line;
4444
this.src=src;
4545
this.startOffset=startOffset;
46-
this.endOffset=startOffset+src.length-newlineLength;
46+
this.endOffset=startOffset+length-newlineLength;
4747
this.ignore=false;
4848
this.count=this.startOffset===this.endOffset ? 1 : 0;
4949
}
@@ -83,7 +83,7 @@ class TestCoverage {
8383

8484
constlines=ArrayPrototypeMap(linesWithBreaks,(line,i)=>{
8585
conststartOffset=offset;
86-
constcoverageLine=newCoverageLine(i+1,line,startOffset);
86+
constcoverageLine=newCoverageLine(i+1,startOffset,line);
8787

8888
offset+=line.length;
8989

@@ -335,8 +335,13 @@ class TestCoverage {
335335
newResult.set(url,script);
336336
continue;
337337
}
338-
constoriginalLines=this.getLines(url);
339338
const{ data, lineLengths }=sourceMapCache[url];
339+
letoffset=0;
340+
constexecutedLines=ArrayPrototypeMap(lineLengths,(length,i)=>{
341+
constcoverageLine=newCoverageLine(i+1,offset,null,length);
342+
offset+=length;
343+
returncoverageLine;
344+
});
340345
if(data.sourcesContent!=null){
341346
for(letj=0;j<data.sources.length;++j){
342347
this.getLines(data.sources[j],data.sourcesContent[j]);
@@ -353,7 +358,7 @@ class TestCoverage {
353358
constnewRanges=[];
354359
for(letk=0;k<ranges.length;++k){
355360
const{ startOffset, endOffset, count }=ranges[k];
356-
const{ lines }=mapRangeToLines(ranges[k],originalLines);
361+
const{ lines }=mapRangeToLines(ranges[k],executedLines);
357362

358363
letstartEntry=sourceMap
359364
.findEntry(lines[0].line-1,MathMax(0,startOffset-lines[0].startOffset));
Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1-
constsource=`
1+
constsources={
2+
// Virtual file. Dosen't exist on disk
3+
"virtual.js": `
24
import { test } from 'node:test';
35
test('test', async () => {});
4-
`;
6+
`,
7+
// file with source map. this emulates the behavior of tsx
8+
"sum.test.ts": `\
9+
import{describe,it}from"node:test";import assert from"node:assert";import{sum}from"./sum.ts";describe("sum",()=>{it("should sum two numbers",()=>{assert.deepStrictEqual(sum(1,2),3)});it("should error out if one is not a number",()=>{assert.throws(()=>sum(1,"b"),Error)})});
10+
11+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IkFBQUEsT0FBUyxTQUFVLE9BQVUsWUFDN0IsT0FBTyxXQUFZLGNBQ25CLE9BQVMsUUFBVyxRQUVwQixTQUFTLE1BQU8sSUFBTSxDQUNwQixHQUFHLHlCQUEwQixJQUFNLENBQy9CLE9BQU8sZ0JBQWdCLElBQUksRUFBRSxDQUFDLEVBQUcsQ0FBQyxDQUN0QyxDQUFDLEVBRUQsR0FBRywwQ0FBMkMsSUFBTSxDQUNsRCxPQUFPLE9BQU8sSUFBTSxJQUFJLEVBQUcsR0FBRyxFQUFHLEtBQUssQ0FDeEMsQ0FBQyxDQUNILENBQUMiLCJuYW1lcyI6W10sImlnbm9yZUxpc3QiOltdLCJzb3VyY2VzIjpbIi4vc3VtLnRlc3QudHMiXSwic291cmNlc0NvbnRlbnQiOltudWxsXX0=`,
12+
// file with source map. this emulates the behavior of tsx
13+
"sum.ts": `\
14+
var __defProp=Object.defineProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:true});function sum(...n){if(!n.every(num=>typeof num==="number"))throw new Error("Not a number");return n.reduce((acc,cur)=>acc+cur)}__name(sum,"sum");export{sum};
15+
16+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6ImtIQUFPLFNBQVMsT0FBUSxFQUFHLENBQ3pCLEdBQUksQ0FBQyxFQUFFLE1BQU8sS0FBUSxPQUFPLE1BQVEsUUFBUSxFQUFHLE1BQU0sSUFBSSxNQUFNLGNBQWMsRUFDOUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxJQUFLLE1BQVEsSUFBTSxHQUFHLENBQ3pDLENBSGdCIiwibmFtZXMiOltdLCJpZ25vcmVMaXN0IjpbXSwic291cmNlcyI6WyIuL3N1bS50cyJdLCJzb3VyY2VzQ29udGVudCI6W251bGxdfQ==`,
17+
};
518

619
exportasyncfunctionload(url,context,nextLoad){
7-
if(url.endsWith('virtual.js')){
8-
return{format: "module", source,shortCircuit: true};
20+
constfile=url.split('/').at(-1);
21+
if(sources[file]!==undefined){
22+
return{format: "module",source: sources[file],shortCircuit: true};
923
}
1024
returnnextLoad(url,context);
1125
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import{describe,it}from'node:test'
2+
importassertfrom'node:assert'
3+
import{sum}from'./sum'
4+
5+
describe('sum',()=>{
6+
it('should sum two numbers',()=>{
7+
assert.deepStrictEqual(sum(1,2),3)
8+
})
9+
10+
it('should error out if one is not a number',()=>{
11+
assert.throws(()=>sum(1,'b'),Error)
12+
})
13+
})
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
exportfunctionsum(...n){
2+
if(!n.every((num)=>typeofnum==='number'))thrownewError('Not a number')
3+
returnn.reduce((acc,cur)=>acc+cur)
4+
}

‎test/parallel/test-runner-coverage.js‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,35 @@ test('coverage with ESM hook - source irrelevant', skipIfNoInspector, () => {
303303
assert(result.stdout.toString().includes(report));
304304
assert.strictEqual(result.status,0);
305305
});
306+
307+
test('coverage with ESM hook - source transpiled',skipIfNoInspector,()=>{
308+
letreport=[
309+
'# start of coverage report',
310+
'# ------------------------------------------------------------------',
311+
'# file | line % | branch % | funcs % | uncovered lines',
312+
'# ------------------------------------------------------------------',
313+
'# hooks.mjs | 100.00 | 100.00 | 100.00 | ',
314+
'# register-hooks.js | 100.00 | 100.00 | 100.00 | ',
315+
'# sum.test.ts | 100.00 | 100.00 | 100.00 | ',
316+
'# sum.ts | 100.00 | 100.00 | 100.00 | ',
317+
'# ------------------------------------------------------------------',
318+
'# all files | 100.00 | 100.00 | 100.00 |',
319+
'# ------------------------------------------------------------------',
320+
'# end of coverage report',
321+
].join('\n');
322+
323+
if(common.isWindows){
324+
report=report.replaceAll('/','\\');
325+
}
326+
327+
constfixture=fixtures.path('test-runner','coverage-loader');
328+
constargs=[
329+
'--import','./register-hooks.js','--test','--experimental-test-coverage',
330+
'--test-reporter','tap','sum.test.ts',
331+
];
332+
constresult=spawnSync(process.execPath,args,{cwd: fixture});
333+
334+
assert.strictEqual(result.stderr.toString(),'');
335+
assert(result.stdout.toString().includes(report));
336+
assert.strictEqual(result.status,0);
337+
});

0 commit comments

Comments
 (0)