diff --git a/lib/internal/test_runner/coverage.js b/lib/internal/test_runner/coverage.js index 8fa9c872568d1e..5bf1adca08b726 100644 --- a/lib/internal/test_runner/coverage.js +++ b/lib/internal/test_runner/coverage.js @@ -31,13 +31,12 @@ const { join, resolve, relative } = require('path'); const { fileURLToPath, URL } = require('internal/url'); const { kMappings, SourceMap } = require('internal/source_map/source_map'); const { - codes: { - ERR_SOURCE_MAP_CORRUPT, - ERR_SOURCE_MAP_MISSING_SOURCE, - }, + codes: { ERR_SOURCE_MAP_CORRUPT, ERR_SOURCE_MAP_MISSING_SOURCE }, } = require('internal/errors'); const { matchGlobPattern } = require('internal/fs/glob'); -const { constants: { kMockSearchParam } } = require('internal/test_runner/mock/loader'); +const { + constants: { kMockSearchParam }, +} = require('internal/test_runner/mock/loader'); const kCoverageFileRegex = /^coverage-(\d+)-(\d{13})-(\d+)\.json$/; const kIgnoreRegex = /\/\* node:coverage ignore next (?\d+ )?\*\//; @@ -47,8 +46,10 @@ const kStatusRegex = /\/\* node:coverage (?enable|disable) \*\//; class CoverageLine { constructor(line, startOffset, src, length = src?.length) { - const newlineLength = src == null ? 0 : - RegExpPrototypeExec(kLineEndingRegex, src)?.[0].length ?? 0; + const newlineLength = + src == null + ? 0 + : (RegExpPrototypeExec(kLineEndingRegex, src)?.[0].length ?? 0); this.line = line; this.src = src; @@ -60,9 +61,7 @@ class CoverageLine { } class TestCoverage { - constructor(coverageDirectory, - originalCoverageDirectory, - options) { + constructor(coverageDirectory, originalCoverageDirectory, options) { this.coverageDirectory = coverageDirectory; this.originalCoverageDirectory = originalCoverageDirectory; this.options = options; @@ -87,8 +86,7 @@ class TestCoverage { return; } - const linesWithBreaks = - RegExpPrototypeSymbolSplit(kLineSplitRegex, source); + const linesWithBreaks = RegExpPrototypeSymbolSplit(kLineSplitRegex, source); let ignoreCount = 0; let enabled = true; let offset = 0; @@ -179,7 +177,6 @@ class TestCoverage { continue; } - for (let j = 0; j < functions.length; ++j) { const { isBlockCoverage, ranges } = functions[j]; @@ -193,14 +190,18 @@ class TestCoverage { ObjectAssign(range, mapRangeToLines(range, lines)); if (isBlockCoverage) { + // Skip branches where all lines are ignored + if (range.ignoredLines === range.lines.length) { + continue; + } + ArrayPrototypePush(branchReports, { __proto__: null, line: range.lines[0]?.line, count: range.count, }); - if (range.count !== 0 || - range.ignoredLines === range.lines.length) { + if (range.count !== 0) { branchesCovered++; } @@ -297,10 +298,13 @@ class TestCoverage { let dir; try { - mkdirSync(this.originalCoverageDirectory, { __proto__: null, recursive: true }); + mkdirSync(this.originalCoverageDirectory, { + __proto__: null, + recursive: true, + }); dir = opendirSync(this.coverageDirectory); - for (let entry; (entry = dir.readSync()) !== null;) { + for (let entry; (entry = dir.readSync()) !== null; ) { const src = join(this.coverageDirectory, entry.name); const dst = join(this.originalCoverageDirectory, entry.name); copyFileSync(src, dst); @@ -326,7 +330,7 @@ class TestCoverage { try { dir = opendirSync(this.coverageDirectory); - for (let entry; (entry = dir.readSync()) !== null;) { + for (let entry; (entry = dir.readSync()) !== null; ) { if (RegExpPrototypeExec(kCoverageFileRegex, entry.name) === null) { continue; } @@ -344,7 +348,6 @@ class TestCoverage { } } - mapCoverageWithSourceMap(coverage) { const { result } = coverage; const sourceMapCache = coverage['source-map-cache']; @@ -386,18 +389,39 @@ class TestCoverage { const { startOffset, endOffset, count } = ranges[k]; const { lines } = mapRangeToLines(ranges[k], executedLines); - let startEntry = sourceMap - .findEntry(lines[0].line - 1, MathMax(0, startOffset - lines[0].startOffset)); - const endEntry = sourceMap - .findEntry(lines[lines.length - 1].line - 1, (endOffset - lines[lines.length - 1].startOffset) - 1); - if (!startEntry.originalSource && endEntry.originalSource && - lines[0].line === 1 && startOffset === 0 && lines[0].startOffset === 0) { + let startEntry = sourceMap.findEntry( + lines[0].line - 1, + MathMax(0, startOffset - lines[0].startOffset), + ); + const endEntry = sourceMap.findEntry( + lines[lines.length - 1].line - 1, + endOffset - lines[lines.length - 1].startOffset - 1, + ); + if ( + !startEntry.originalSource && + endEntry.originalSource && + lines[0].line === 1 && + startOffset === 0 && + lines[0].startOffset === 0 + ) { // Edge case when the first line is not mappable - const { 2: originalSource, 3: originalLine, 4: originalColumn } = sourceMap[kMappings][0]; - startEntry = { __proto__: null, originalSource, originalLine, originalColumn }; + const { + 2: originalSource, + 3: originalLine, + 4: originalColumn, + } = sourceMap[kMappings][0]; + startEntry = { + __proto__: null, + originalSource, + originalLine, + originalColumn, + }; } - if (!startEntry.originalSource || startEntry.originalSource !== endEntry.originalSource) { + if ( + !startEntry.originalSource || + startEntry.originalSource !== endEntry.originalSource + ) { // The range is not mappable. Skip it. continue; } @@ -413,12 +437,19 @@ class TestCoverage { // The range is not mappable. Skip it. continue; } - for (let l = startEntry.originalLine; l <= endEntry.originalLine; l++) { + for ( + let l = startEntry.originalLine; + l <= endEntry.originalLine; + l++ + ) { mappedLines[l].count = count; } ArrayPrototypePush(newRanges, { - __proto__: null, startOffset: mappedStartOffset, endOffset: mappedEndOffset, count, + __proto__: null, + startOffset: mappedStartOffset, + endOffset: mappedEndOffset, + count, }); } @@ -426,8 +457,17 @@ class TestCoverage { // No mappable ranges. Skip the function. continue; } - const newScript = newResult.get(newUrl) ?? { __proto__: null, url: newUrl, functions: [] }; - ArrayPrototypePush(newScript.functions, { __proto__: null, functionName, ranges: newRanges, isBlockCoverage }); + const newScript = newResult.get(newUrl) ?? { + __proto__: null, + url: newUrl, + functions: [], + }; + ArrayPrototypePush(newScript.functions, { + __proto__: null, + functionName, + ranges: newRanges, + isBlockCoverage, + }); newResult.set(newUrl, newScript); } } @@ -442,7 +482,10 @@ class TestCoverage { // Return -1 if the line is not mappable. return -1; } - return MathMin(mappedLine.startOffset + entry.originalColumn, mappedLine.endOffset); + return MathMin( + mappedLine.startOffset + entry.originalColumn, + mappedLine.endOffset, + ); } mergeCoverage(merged, coverage) { @@ -483,7 +526,8 @@ class TestCoverage { if ( matchGlobPattern(relativePath, excludeGlobs[i]) || matchGlobPattern(absolutePath, excludeGlobs[i]) - ) return true; + ) + return true; } } @@ -493,7 +537,8 @@ class TestCoverage { if ( matchGlobPattern(relativePath, includeGlobs[i]) || matchGlobPattern(absolutePath, includeGlobs[i]) - ) return false; + ) + return false; } return true; } @@ -595,9 +640,11 @@ function mergeCoverageScripts(oldScript, newScript) { for (let j = 0; j < oldScript.functions.length; ++j) { const oldFn = oldScript.functions[j]; - if (newFn.functionName === oldFn.functionName && - newFn.ranges?.[0].startOffset === oldFn.ranges?.[0].startOffset && - newFn.ranges?.[0].endOffset === oldFn.ranges?.[0].endOffset) { + if ( + newFn.functionName === oldFn.functionName && + newFn.ranges?.[0].startOffset === oldFn.ranges?.[0].startOffset && + newFn.ranges?.[0].endOffset === oldFn.ranges?.[0].endOffset + ) { // These are the same functions. found = true; @@ -686,13 +733,17 @@ function mergeCoverageRanges(oldFn, newFn) { } function doesRangeEqualOtherRange(range, otherRange) { - return range.startOffset === otherRange.startOffset && - range.endOffset === otherRange.endOffset; + return ( + range.startOffset === otherRange.startOffset && + range.endOffset === otherRange.endOffset + ); } function doesRangeContainOtherRange(range, otherRange) { - return range.startOffset <= otherRange.startOffset && - range.endOffset >= otherRange.endOffset; + return ( + range.startOffset <= otherRange.startOffset && + range.endOffset >= otherRange.endOffset + ); } module.exports = { setupCoverage, TestCoverage };