Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/internal/fs/glob.js
Original file line number Diff line number Diff line change
Expand Up @@ -947,5 +947,6 @@ function matchGlobPattern(path, pattern, windows = isWindows) {
module.exports = {
__proto__: null,
Glob,
createMatcher,
matchGlobPattern,
};
46 changes: 28 additions & 18 deletions lib/internal/test_runner/coverage.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const {
ERR_SOURCE_MAP_MISSING_SOURCE,
},
} = require('internal/errors');
const { matchGlobPattern } = require('internal/fs/glob');
const { createMatcher } = require('internal/fs/glob');
const { constants: { kMockSearchParam } } = require('internal/test_runner/mock/loader');

const kCoverageFileRegex = /^coverage-(\d+)-(\d{13})-(\d+)\.json$/;
Expand Down Expand Up @@ -85,6 +85,9 @@ class TestCoverage {

#sourceLines = new SafeMap();
#typeScriptLines = new SafeSet();
#skipCache = new SafeMap();
Comment thread
Han5991 marked this conversation as resolved.
#excludeMatchers = null;
#includeMatchers = null;

getLines(fileUrl, source) {
// Split the file source into lines. Make sure the lines maintain their
Expand Down Expand Up @@ -534,35 +537,42 @@ class TestCoverage {
}

shouldSkipFileCoverage(url) {
const cached = this.#skipCache.get(url);
if (cached !== undefined) return cached;
const result = this.#computeShouldSkipFileCoverage(url);
this.#skipCache.set(url, result);
return result;
}

#computeShouldSkipFileCoverage(url) {
// This check filters out core modules, which start with 'node:' in
// coverage reports, as well as any invalid coverages which have been
// observed on Windows.
if (!StringPrototypeStartsWith(url, 'file:')) return true;

const absolutePath = fileURLToPath(url);
const relativePath = relative(this.options.cwd, absolutePath);
const {
coverageExcludeGlobs: excludeGlobs,
coverageIncludeGlobs: includeGlobs,
} = this.options;
// The exclude/include globs are fixed for the lifetime of this
// TestCoverage instance, so compile each glob to a matcher once and reuse
// it for every file. Building a fresh Minimatch per call (the previous
// behavior) dominated the coverage report time, scaling with
// files * globs.
this.#excludeMatchers ??= ArrayPrototypeMap(
this.options.coverageExcludeGlobs ?? [], (pattern) => createMatcher(pattern));
this.#includeMatchers ??= ArrayPrototypeMap(
this.options.coverageIncludeGlobs ?? [], (pattern) => createMatcher(pattern));

// This check filters out files that match the exclude globs.
if (excludeGlobs?.length > 0) {
for (let i = 0; i < excludeGlobs.length; ++i) {
if (
matchGlobPattern(relativePath, excludeGlobs[i]) ||
matchGlobPattern(absolutePath, excludeGlobs[i])
) return true;
}
for (let i = 0; i < this.#excludeMatchers.length; ++i) {
const matcher = this.#excludeMatchers[i];
if (matcher.match(relativePath) || matcher.match(absolutePath)) return true;
}

// This check filters out files that do not match the include globs.
if (includeGlobs?.length > 0) {
for (let i = 0; i < includeGlobs.length; ++i) {
if (
matchGlobPattern(relativePath, includeGlobs[i]) ||
matchGlobPattern(absolutePath, includeGlobs[i])
) return false;
if (this.#includeMatchers.length > 0) {
for (let i = 0; i < this.#includeMatchers.length; ++i) {
const matcher = this.#includeMatchers[i];
if (matcher.match(relativePath) || matcher.match(absolutePath)) return false;
}
return true;
}
Expand Down
Loading