Skip to content

Commit 4530582

Browse files
MoLowRafaelGSS
authored andcommitted
test_runner: count nested tests
PR-URL: #47094 Fixes: #46762 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent 7ec87fd commit 4530582

21 files changed

+163
-93
lines changed

lib/internal/test_runner/harness.js

+11
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,17 @@ function setup(root) {
168168
__proto__: null,
169169
bootstrapComplete: false,
170170
coverage: null,
171+
counters: {
172+
__proto__: null,
173+
all: 0,
174+
failed: 0,
175+
passed: 0,
176+
cancelled: 0,
177+
skipped: 0,
178+
todo: 0,
179+
planned: 0,
180+
suites: 0,
181+
},
171182
};
172183
root.startTime = hrtime();
173184
return root;

lib/internal/test_runner/reporter/tap.js

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ function reportDetails(nesting, data = kEmptyObject) {
113113
let details = `${_indent} ---\n`;
114114

115115
details += jsToYaml(_indent, 'duration_ms', duration_ms);
116+
details += jsToYaml(_indent, 'type', data.type);
116117
details += jsToYaml(_indent, null, error);
117118
details += `${_indent} ...\n`;
118119
return details;

lib/internal/test_runner/runner.js

+16-17
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ const {
1010
ArrayPrototypeSome,
1111
ArrayPrototypeSort,
1212
ArrayPrototypeSplice,
13-
FunctionPrototypeCall,
1413
Number,
1514
ObjectAssign,
16-
ObjectKeys,
1715
PromisePrototypeThen,
1816
SafePromiseAll,
1917
SafePromiseAllReturnVoid,
@@ -55,8 +53,9 @@ const { YAMLToJs } = require('internal/test_runner/yaml_to_js');
5553
const { TokenKind } = require('internal/test_runner/tap_lexer');
5654

5755
const {
58-
isSupportedFileType,
56+
countCompletedTest,
5957
doesPathMatchFilter,
58+
isSupportedFileType,
6059
} = require('internal/test_runner/utils');
6160
const { basename, join, resolve } = require('path');
6261
const { once } = require('events');
@@ -67,7 +66,7 @@ const {
6766

6867
const kFilterArgs = ['--test', '--experimental-test-coverage', '--watch'];
6968
const kFilterArgValues = ['--test-reporter', '--test-reporter-destination'];
70-
const kDiagnosticsFilterArgs = ['tests', 'pass', 'fail', 'cancelled', 'skipped', 'todo', 'duration_ms'];
69+
const kDiagnosticsFilterArgs = ['tests', 'suites', 'pass', 'fail', 'cancelled', 'skipped', 'todo', 'duration_ms'];
7170

7271
const kCanceledTests = new SafeSet()
7372
.add(kCancelledByParent).add(kAborted).add(kTestTimeoutFailure);
@@ -151,10 +150,10 @@ function getRunArgs({ path, inspectPort }) {
151150

152151
class FileTest extends Test {
153152
#buffer = [];
154-
#counters = { __proto__: null, all: 0, failed: 0, passed: 0, cancelled: 0, skipped: 0, todo: 0, totalFailed: 0 };
153+
#reportedChildren = 0;
155154
failedSubtests = false;
156155
#skipReporting() {
157-
return this.#counters.all > 0 && (!this.error || this.error.failureType === kSubtestsFailed);
156+
return this.#reportedChildren > 0 && (!this.error || this.error.failureType === kSubtestsFailed);
158157
}
159158
#checkNestedComment({ comment }) {
160159
const firstSpaceIndex = StringPrototypeIndexOf(comment, ' ');
@@ -204,11 +203,19 @@ class FileTest extends Test {
204203
const method = pass ? 'ok' : 'fail';
205204
this.reporter[method](nesting, this.name, testNumber, node.description, diagnostics, directive);
206205
if (nesting === 0) {
207-
FunctionPrototypeCall(super.countSubtest,
208-
{ finished: true, skipped: skip, isTodo: todo, passed: pass, cancelled },
209-
this.#counters);
210206
this.failedSubtests ||= !pass;
211207
}
208+
this.#reportedChildren++;
209+
countCompletedTest({
210+
name: node.description,
211+
finished: true,
212+
skipped: skip,
213+
isTodo: todo,
214+
passed: pass,
215+
cancelled,
216+
nesting,
217+
reportedType: diagnostics.type,
218+
}, this.root.harness);
212219
break;
213220

214221
}
@@ -233,14 +240,6 @@ class FileTest extends Test {
233240
this.reportStarted();
234241
this.#handleReportItem(ast);
235242
}
236-
countSubtest(counters) {
237-
if (this.#counters.all === 0) {
238-
return super.countSubtest(counters);
239-
}
240-
ArrayPrototypeForEach(ObjectKeys(counters), (key) => {
241-
counters[key] += this.#counters[key];
242-
});
243-
}
244243
reportStarted() {}
245244
report() {
246245
const skipReporting = this.#skipReporting();

lib/internal/test_runner/test.js

+24-35
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const { MockTracker } = require('internal/test_runner/mock');
3434
const { TestsStream } = require('internal/test_runner/tests_stream');
3535
const {
3636
createDeferredCallback,
37+
countCompletedTest,
3738
isTestFailureError,
3839
parseCommandLine,
3940
} = require('internal/test_runner/utils');
@@ -186,6 +187,7 @@ class Test extends AsyncResource {
186187
this.runOnlySubtests = this.only;
187188
this.testNumber = 0;
188189
this.timeout = kDefaultTimeout;
190+
this.root = this;
189191
} else {
190192
const nesting = parent.parent === null ? parent.nesting :
191193
parent.nesting + 1;
@@ -197,6 +199,7 @@ class Test extends AsyncResource {
197199
this.runOnlySubtests = !this.only;
198200
this.testNumber = parent.subtests.length + 1;
199201
this.timeout = parent.timeout;
202+
this.root = parent.root;
200203
}
201204

202205
switch (typeof concurrency) {
@@ -575,31 +578,7 @@ class Test extends AsyncResource {
575578
this.postRun();
576579
}
577580

578-
countSubtest(counters) {
579-
// Check SKIP and TODO tests first, as those should not be counted as
580-
// failures.
581-
if (this.skipped) {
582-
counters.skipped++;
583-
} else if (this.isTodo) {
584-
counters.todo++;
585-
} else if (this.cancelled) {
586-
counters.cancelled++;
587-
} else if (!this.passed) {
588-
counters.failed++;
589-
} else {
590-
counters.passed++;
591-
}
592-
593-
if (!this.passed) {
594-
counters.totalFailed++;
595-
}
596-
counters.all++;
597-
}
598-
599581
postRun(pendingSubtestsError) {
600-
const counters = {
601-
__proto__: null, all: 0, failed: 0, passed: 0, cancelled: 0, skipped: 0, todo: 0, totalFailed: 0,
602-
};
603582
// If the test was failed before it even started, then the end time will
604583
// be earlier than the start time. Correct that here.
605584
if (this.endTime < this.startTime) {
@@ -610,19 +589,22 @@ class Test extends AsyncResource {
610589
// The test has run, so recursively cancel any outstanding subtests and
611590
// mark this test as failed if any subtests failed.
612591
this.pendingSubtests = [];
592+
let failed = 0;
613593
for (let i = 0; i < this.subtests.length; i++) {
614594
const subtest = this.subtests[i];
615595

616596
if (!subtest.finished) {
617597
subtest.#cancel(pendingSubtestsError);
618598
subtest.postRun(pendingSubtestsError);
619599
}
620-
subtest.countSubtest(counters);
600+
if (!subtest.passed) {
601+
failed++;
602+
}
621603
}
622604

623-
if ((this.passed || this.parent === null) && counters.totalFailed > 0) {
624-
const subtestString = `subtest${counters.totalFailed > 1 ? 's' : ''}`;
625-
const msg = `${counters.totalFailed} ${subtestString} failed`;
605+
if ((this.passed || this.parent === null) && failed > 0) {
606+
const subtestString = `subtest${failed > 1 ? 's' : ''}`;
607+
const msg = `${failed} ${subtestString} failed`;
626608

627609
this.fail(new ERR_TEST_FAILURE(msg, kSubtestsFailed));
628610
}
@@ -637,18 +619,19 @@ class Test extends AsyncResource {
637619
this.parent.processPendingSubtests();
638620
} else if (!this.reported) {
639621
this.reported = true;
640-
this.reporter.plan(this.nesting, kFilename, counters.all);
622+
this.reporter.plan(this.nesting, kFilename, this.root.harness.counters.planned);
641623

642624
for (let i = 0; i < this.diagnostics.length; i++) {
643625
this.reporter.diagnostic(this.nesting, kFilename, this.diagnostics[i]);
644626
}
645627

646-
this.reporter.diagnostic(this.nesting, kFilename, `tests ${counters.all}`);
647-
this.reporter.diagnostic(this.nesting, kFilename, `pass ${counters.passed}`);
648-
this.reporter.diagnostic(this.nesting, kFilename, `fail ${counters.failed}`);
649-
this.reporter.diagnostic(this.nesting, kFilename, `cancelled ${counters.cancelled}`);
650-
this.reporter.diagnostic(this.nesting, kFilename, `skipped ${counters.skipped}`);
651-
this.reporter.diagnostic(this.nesting, kFilename, `todo ${counters.todo}`);
628+
this.reporter.diagnostic(this.nesting, kFilename, `tests ${this.root.harness.counters.all}`);
629+
this.reporter.diagnostic(this.nesting, kFilename, `suites ${this.root.harness.counters.suites}`);
630+
this.reporter.diagnostic(this.nesting, kFilename, `pass ${this.root.harness.counters.passed}`);
631+
this.reporter.diagnostic(this.nesting, kFilename, `fail ${this.root.harness.counters.failed}`);
632+
this.reporter.diagnostic(this.nesting, kFilename, `cancelled ${this.root.harness.counters.cancelled}`);
633+
this.reporter.diagnostic(this.nesting, kFilename, `skipped ${this.root.harness.counters.skipped}`);
634+
this.reporter.diagnostic(this.nesting, kFilename, `todo ${this.root.harness.counters.todo}`);
652635
this.reporter.diagnostic(this.nesting, kFilename, `duration_ms ${this.#duration()}`);
653636

654637
if (this.harness?.coverage) {
@@ -689,6 +672,7 @@ class Test extends AsyncResource {
689672
}
690673

691674
report() {
675+
countCompletedTest(this);
692676
if (this.subtests.length > 0) {
693677
this.reporter.plan(this.subtests[0].nesting, kFilename, this.subtests.length);
694678
} else {
@@ -703,6 +687,10 @@ class Test extends AsyncResource {
703687
directive = this.reporter.getTodo(this.message);
704688
}
705689

690+
if (this.reportedType) {
691+
details.type = this.reportedType;
692+
}
693+
706694
if (this.passed) {
707695
this.reporter.ok(this.nesting, kFilename, this.testNumber, this.name, details, directive);
708696
} else {
@@ -746,6 +734,7 @@ class TestHook extends Test {
746734
}
747735

748736
class Suite extends Test {
737+
reportedType = 'suite';
749738
constructor(options) {
750739
super(options);
751740

lib/internal/test_runner/utils.js

+25
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,33 @@ function parseCommandLine() {
222222
return globalTestOptions;
223223
}
224224

225+
function countCompletedTest(test, harness = test.root.harness) {
226+
if (test.nesting === 0) {
227+
harness.counters.planned++;
228+
}
229+
if (test.reportedType === 'suite') {
230+
harness.counters.suites++;
231+
return;
232+
}
233+
// Check SKIP and TODO tests first, as those should not be counted as
234+
// failures.
235+
if (test.skipped) {
236+
harness.counters.skipped++;
237+
} else if (test.isTodo) {
238+
harness.counters.todo++;
239+
} else if (test.cancelled) {
240+
harness.counters.cancelled++;
241+
} else if (!test.passed) {
242+
harness.counters.failed++;
243+
} else {
244+
harness.counters.passed++;
245+
}
246+
harness.counters.all++;
247+
}
248+
225249
module.exports = {
226250
convertStringToRegExp,
251+
countCompletedTest,
227252
createDeferredCallback,
228253
doesPathMatchFilter,
229254
isSupportedFileType,

test/message/test_runner_abort.out

+4-3
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,11 @@ not ok 4 - callback abort signal
260260
*
261261
...
262262
1..4
263-
# tests 4
264-
# pass 0
263+
# tests 22
264+
# suites 0
265+
# pass 8
265266
# fail 0
266-
# cancelled 4
267+
# cancelled 14
267268
# skipped 0
268269
# todo 0
269270
# duration_ms *

test/message/test_runner_abort_suite.out

+6-3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ TAP version 13
6464
not ok 1 - describe timeout signal
6565
---
6666
duration_ms: *
67+
type: 'suite'
6768
failureType: 'testAborted'
6869
error: 'The operation was aborted due to timeout'
6970
code: 23
@@ -78,6 +79,7 @@ not ok 1 - describe timeout signal
7879
not ok 2 - describe abort signal
7980
---
8081
duration_ms: *
82+
type: 'suite'
8183
failureType: 'testAborted'
8284
error: 'This operation was aborted'
8385
code: 20
@@ -94,10 +96,11 @@ not ok 2 - describe abort signal
9496
*
9597
...
9698
1..2
97-
# tests 2
98-
# pass 0
99+
# tests 9
100+
# suites 2
101+
# pass 4
99102
# fail 0
100-
# cancelled 2
103+
# cancelled 5
101104
# skipped 0
102105
# todo 0
103106
# duration_ms *

0 commit comments

Comments
 (0)