Skip to content

Commit e78fd8c

Browse files
committed
test_runner: apply filtering when tests begin
This commit updates the way filtering is applied to tests and suites. After this change, filters are applied just before the test/suite is started. The results are the same, but this allows us to eventually move away from the --test-only flag except when process level isolation is used. PR-URL: #54832 Reviewed-By: Chemi Atlow <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Moshe Atlow <[email protected]>
1 parent 9f5977f commit e78fd8c

File tree

2 files changed

+62
-40
lines changed

2 files changed

+62
-40
lines changed

lib/internal/test_runner/harness.js

+5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ testResources.set(reporterScope.asyncId(), reporterScope);
3636

3737
function createTestTree(rootTestOptions, globalOptions) {
3838
const buildPhaseDeferred = createDeferredPromise();
39+
const isFilteringByName = globalOptions.testNamePatterns ||
40+
globalOptions.testSkipPatterns;
41+
const isFilteringByOnly = globalOptions.only;
3942
const harness = {
4043
__proto__: null,
4144
buildPromise: buildPhaseDeferred.promise,
@@ -62,6 +65,8 @@ function createTestTree(rootTestOptions, globalOptions) {
6265
shouldColorizeTestFiles: shouldColorizeTestFiles(globalOptions.destinations),
6366
teardown: null,
6467
snapshotManager: null,
68+
isFilteringByName,
69+
isFilteringByOnly,
6570
async waitForBuildPhase() {
6671
if (harness.buildSuites.length > 0) {
6772
await SafePromiseAllReturnVoid(harness.buildSuites);

lib/internal/test_runner/test.js

+57-40
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,10 @@ class Test extends AsyncResource {
395395
this.parent = parent;
396396
this.testNumber = 0;
397397
this.outputSubtestCount = 0;
398-
this.filteredSubtestCount = 0;
398+
this.diagnostics = [];
399399
this.filtered = false;
400+
this.filteredByName = false;
401+
this.hasOnlyTests = false;
400402

401403
if (parent === null) {
402404
this.root = this;
@@ -413,26 +415,48 @@ class Test extends AsyncResource {
413415
} else {
414416
const nesting = parent.parent === null ? parent.nesting :
415417
parent.nesting + 1;
418+
const { config, isFilteringByName, isFilteringByOnly } = parent.root.harness;
416419

417420
this.root = parent.root;
418421
this.harness = null;
419-
this.config = this.root.harness.config;
422+
this.config = config;
420423
this.concurrency = parent.concurrency;
421424
this.nesting = nesting;
422-
this.only = only ?? (parent.only && !parent.runOnlySubtests);
425+
this.only = only;
423426
this.reporter = parent.reporter;
424427
this.runOnlySubtests = false;
425428
this.childNumber = parent.subtests.length + 1;
426429
this.timeout = parent.timeout;
427430
this.entryFile = parent.entryFile;
428431

429-
if (this.willBeFiltered()) {
430-
this.filtered = true;
431-
this.parent.filteredSubtestCount++;
432+
if (isFilteringByName) {
433+
this.filteredByName = this.willBeFilteredByName();
434+
if (!this.filteredByName) {
435+
for (let t = this.parent; t !== null && t.filteredByName; t = t.parent) {
436+
t.filteredByName = false;
437+
}
438+
}
432439
}
433440

434-
if (this.config.only && only === false) {
435-
fn = noop;
441+
if (isFilteringByOnly) {
442+
if (this.only) {
443+
// If filtering impacts the tests within a suite, then the suite only
444+
// runs those tests. If filtering does not impact the tests within a
445+
// suite, then all tests are run.
446+
this.parent.runOnlySubtests = true;
447+
448+
if (this.parent === this.root || this.parent.startTime === null) {
449+
for (let t = this.parent; t !== null && !t.hasOnlyTests; t = t.parent) {
450+
t.hasOnlyTests = true;
451+
}
452+
}
453+
} else if (this.only === false) {
454+
fn = noop;
455+
}
456+
} else if (only || this.parent.runOnlySubtests) {
457+
const warning =
458+
"'only' and 'runOnly' require the --test-only command-line option.";
459+
this.diagnostic(warning);
436460
}
437461
}
438462

@@ -491,7 +515,6 @@ class Test extends AsyncResource {
491515
this.endTime = null;
492516
this.passed = false;
493517
this.error = null;
494-
this.diagnostics = [];
495518
this.message = typeof skip === 'string' ? skip :
496519
typeof todo === 'string' ? todo : null;
497520
this.activeSubtests = 0;
@@ -509,12 +532,6 @@ class Test extends AsyncResource {
509532
ownAfterEachCount: 0,
510533
};
511534

512-
if (!this.config.only && (only || this.parent?.runOnlySubtests)) {
513-
const warning =
514-
"'only' and 'runOnly' require the --test-only command-line option.";
515-
this.diagnostic(warning);
516-
}
517-
518535
if (loc === undefined) {
519536
this.loc = undefined;
520537
} else {
@@ -542,9 +559,27 @@ class Test extends AsyncResource {
542559
}
543560
}
544561

545-
willBeFiltered() {
546-
if (this.config.only && !this.only) return true;
562+
applyFilters() {
563+
if (this.error) {
564+
// Never filter out errors.
565+
return;
566+
}
547567

568+
if (this.filteredByName) {
569+
this.filtered = true;
570+
return;
571+
}
572+
573+
if (this.root.harness.isFilteringByOnly && !this.only && !this.hasOnlyTests) {
574+
if (this.parent.runOnlySubtests ||
575+
this.parent.hasOnlyTests ||
576+
this.only === false) {
577+
this.filtered = true;
578+
}
579+
}
580+
}
581+
582+
willBeFilteredByName() {
548583
const { testNamePatterns, testSkipPatterns } = this.config;
549584

550585
if (testNamePatterns && !testMatchesPattern(this, testNamePatterns)) {
@@ -757,12 +792,10 @@ class Test extends AsyncResource {
757792
ArrayPrototypePush(this.diagnostics, message);
758793
}
759794

760-
get shouldFilter() {
761-
return this.filtered && this.parent?.filteredSubtestCount > 0;
762-
}
763-
764795
start() {
765-
if (this.shouldFilter) {
796+
this.applyFilters();
797+
798+
if (this.filtered) {
766799
noopTestStream ??= new TestsStream();
767800
this.reporter = noopTestStream;
768801
this.run = this.filteredRun;
@@ -970,7 +1003,7 @@ class Test extends AsyncResource {
9701003
this.mock?.reset();
9711004

9721005
if (this.parent !== null) {
973-
if (!this.shouldFilter) {
1006+
if (!this.filtered) {
9741007
const report = this.getReportDetails();
9751008
report.details.passed = this.passed;
9761009
this.testNumber ||= ++this.parent.outputSubtestCount;
@@ -1159,7 +1192,7 @@ class TestHook extends Test {
11591192
getRunArgs() {
11601193
return this.#args;
11611194
}
1162-
willBeFiltered() {
1195+
willBeFilteredByName() {
11631196
return false;
11641197
}
11651198
postRun() {
@@ -1192,7 +1225,6 @@ class Suite extends Test {
11921225
this.fn = options.fn || this.fn;
11931226
this.skipped = false;
11941227
}
1195-
this.runOnlySubtests = this.config.only;
11961228

11971229
try {
11981230
const { ctx, args } = this.getRunArgs();
@@ -1216,21 +1248,6 @@ class Suite extends Test {
12161248

12171249
postBuild() {
12181250
this.buildPhaseFinished = true;
1219-
if (this.filtered &&
1220-
(this.filteredSubtestCount !== this.subtests.length || this.error)) {
1221-
// A suite can transition from filtered to unfiltered based on the
1222-
// tests that it contains - in case of children matching patterns.
1223-
this.filtered = false;
1224-
this.parent.filteredSubtestCount--;
1225-
} else if (
1226-
this.config.only &&
1227-
this.config.testNamePatterns == null &&
1228-
this.config.testSkipPatterns == null &&
1229-
this.filteredSubtestCount === this.subtests.length
1230-
) {
1231-
// If no subtests are marked as "only", run them all
1232-
this.filteredSubtestCount = 0;
1233-
}
12341251
}
12351252

12361253
getRunArgs() {

0 commit comments

Comments
 (0)