GeezerVison is showing github.com — test_runner: count nested tests · nodejs/node@4530582
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/test_runner/harness.js +11Lines changed: 11 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -168,6 +168,17 @@ function setup(root) { | 168 | 168 | __proto__: null, | 169 | 169 | bootstrapComplete: false, | 170 | 170 | 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 | + }, | 171 | 182 | }; | 172 | 183 | root.startTime = hrtime(); | 173 | 184 | return root; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/test_runner/reporter/tap.js +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -113,6 +113,7 @@ function reportDetails(nesting, data = kEmptyObject) { | 113 | 113 | let details = `${_indent} ---\n`; | 114 | 114 |
| 115 | 115 | details += jsToYaml(_indent, 'duration_ms', duration_ms); | | 116 | + details += jsToYaml(_indent, 'type', data.type); | 116 | 117 | details += jsToYaml(_indent, null, error); | 117 | 118 | details += `${_indent} ...\n`; | 118 | 119 | return details; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/test_runner/runner.js +16-17Lines changed: 16 additions & 17 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -10,10 +10,8 @@ const { | 10 | 10 | ArrayPrototypeSome, | 11 | 11 | ArrayPrototypeSort, | 12 | 12 | ArrayPrototypeSplice, | 13 | | - FunctionPrototypeCall, | 14 | 13 | Number, | 15 | 14 | ObjectAssign, | 16 | | - ObjectKeys, | 17 | 15 | PromisePrototypeThen, | 18 | 16 | SafePromiseAll, | 19 | 17 | SafePromiseAllReturnVoid, | @@ -55,8 +53,9 @@ const { YAMLToJs } = require('internal/test_runner/yaml_to_js'); | 55 | 53 | const { TokenKind } = require('internal/test_runner/tap_lexer'); | 56 | 54 |
| 57 | 55 | const { | 58 | | - isSupportedFileType, | | 56 | + countCompletedTest, | 59 | 57 | doesPathMatchFilter, | | 58 | + isSupportedFileType, | 60 | 59 | } = require('internal/test_runner/utils'); | 61 | 60 | const { basename, join, resolve } = require('path'); | 62 | 61 | const { once } = require('events'); | @@ -67,7 +66,7 @@ const { | 67 | 66 |
| 68 | 67 | const kFilterArgs = ['--test', '--experimental-test-coverage', '--watch']; | 69 | 68 | 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']; | 71 | 70 |
| 72 | 71 | const kCanceledTests = new SafeSet() | 73 | 72 | .add(kCancelledByParent).add(kAborted).add(kTestTimeoutFailure); | @@ -151,10 +150,10 @@ function getRunArgs({ path, inspectPort }) { | 151 | 150 |
| 152 | 151 | class FileTest extends Test { | 153 | 152 | #buffer = []; | 154 | | - #counters = { __proto__: null, all: 0, failed: 0, passed: 0, cancelled: 0, skipped: 0, todo: 0, totalFailed: 0 }; | | 153 | + #reportedChildren = 0; | 155 | 154 | failedSubtests = false; | 156 | 155 | #skipReporting() { | 157 | | - return this.#counters.all > 0 && (!this.error || this.error.failureType === kSubtestsFailed); | | 156 | + return this.#reportedChildren > 0 && (!this.error || this.error.failureType === kSubtestsFailed); | 158 | 157 | } | 159 | 158 | #checkNestedComment({ comment }) { | 160 | 159 | const firstSpaceIndex = StringPrototypeIndexOf(comment, ' '); | @@ -204,11 +203,19 @@ class FileTest extends Test { | 204 | 203 | const method = pass ? 'ok' : 'fail'; | 205 | 204 | this.reporter[method](nesting, this.name, testNumber, node.description, diagnostics, directive); | 206 | 205 | if (nesting === 0) { | 207 | | - FunctionPrototypeCall(super.countSubtest, | 208 | | - { finished: true, skipped: skip, isTodo: todo, passed: pass, cancelled }, | 209 | | - this.#counters); | 210 | 206 | this.failedSubtests ||= !pass; | 211 | 207 | } | | 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); | 212 | 219 | break; | 213 | 220 |
| 214 | 221 | } | @@ -233,14 +240,6 @@ class FileTest extends Test { | 233 | 240 | this.reportStarted(); | 234 | 241 | this.#handleReportItem(ast); | 235 | 242 | } | 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 | | - } | 244 | 243 | reportStarted() {} | 245 | 244 | report() { | 246 | 245 | const skipReporting = this.#skipReporting(); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/test_runner/test.js +24-35Lines changed: 24 additions & 35 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -34,6 +34,7 @@ const { MockTracker } = require('internal/test_runner/mock'); | 34 | 34 | const { TestsStream } = require('internal/test_runner/tests_stream'); | 35 | 35 | const { | 36 | 36 | createDeferredCallback, | | 37 | + countCompletedTest, | 37 | 38 | isTestFailureError, | 38 | 39 | parseCommandLine, | 39 | 40 | } = require('internal/test_runner/utils'); | @@ -186,6 +187,7 @@ class Test extends AsyncResource { | 186 | 187 | this.runOnlySubtests = this.only; | 187 | 188 | this.testNumber = 0; | 188 | 189 | this.timeout = kDefaultTimeout; | | 190 | + this.root = this; | 189 | 191 | } else { | 190 | 192 | const nesting = parent.parent === null ? parent.nesting : | 191 | 193 | parent.nesting + 1; | @@ -197,6 +199,7 @@ class Test extends AsyncResource { | 197 | 199 | this.runOnlySubtests = !this.only; | 198 | 200 | this.testNumber = parent.subtests.length + 1; | 199 | 201 | this.timeout = parent.timeout; | | 202 | + this.root = parent.root; | 200 | 203 | } | 201 | 204 |
| 202 | 205 | switch (typeof concurrency) { | @@ -575,31 +578,7 @@ class Test extends AsyncResource { | 575 | 578 | this.postRun(); | 576 | 579 | } | 577 | 580 |
| 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 | | - | 599 | 581 | postRun(pendingSubtestsError) { | 600 | | - const counters = { | 601 | | - __proto__: null, all: 0, failed: 0, passed: 0, cancelled: 0, skipped: 0, todo: 0, totalFailed: 0, | 602 | | - }; | 603 | 582 | // If the test was failed before it even started, then the end time will | 604 | 583 | // be earlier than the start time. Correct that here. | 605 | 584 | if (this.endTime < this.startTime) { | @@ -610,19 +589,22 @@ class Test extends AsyncResource { | 610 | 589 | // The test has run, so recursively cancel any outstanding subtests and | 611 | 590 | // mark this test as failed if any subtests failed. | 612 | 591 | this.pendingSubtests = []; | | 592 | + let failed = 0; | 613 | 593 | for (let i = 0; i < this.subtests.length; i++) { | 614 | 594 | const subtest = this.subtests[i]; | 615 | 595 |
| 616 | 596 | if (!subtest.finished) { | 617 | 597 | subtest.#cancel(pendingSubtestsError); | 618 | 598 | subtest.postRun(pendingSubtestsError); | 619 | 599 | } | 620 | | - subtest.countSubtest(counters); | | 600 | + if (!subtest.passed) { | | 601 | + failed++; | | 602 | + } | 621 | 603 | } | 622 | 604 |
| 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`; | 626 | 608 |
| 627 | 609 | this.fail(new ERR_TEST_FAILURE(msg, kSubtestsFailed)); | 628 | 610 | } | @@ -637,18 +619,19 @@ class Test extends AsyncResource { | 637 | 619 | this.parent.processPendingSubtests(); | 638 | 620 | } else if (!this.reported) { | 639 | 621 | this.reported = true; | 640 | | - this.reporter.plan(this.nesting, kFilename, counters.all); | | 622 | + this.reporter.plan(this.nesting, kFilename, this.root.harness.counters.planned); | 641 | 623 |
| 642 | 624 | for (let i = 0; i < this.diagnostics.length; i++) { | 643 | 625 | this.reporter.diagnostic(this.nesting, kFilename, this.diagnostics[i]); | 644 | 626 | } | 645 | 627 |
| 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}`); | 652 | 635 | this.reporter.diagnostic(this.nesting, kFilename, `duration_ms ${this.#duration()}`); | 653 | 636 |
| 654 | 637 | if (this.harness?.coverage) { | @@ -689,6 +672,7 @@ class Test extends AsyncResource { | 689 | 672 | } | 690 | 673 |
| 691 | 674 | report() { | | 675 | + countCompletedTest(this); | 692 | 676 | if (this.subtests.length > 0) { | 693 | 677 | this.reporter.plan(this.subtests[0].nesting, kFilename, this.subtests.length); | 694 | 678 | } else { | @@ -703,6 +687,10 @@ class Test extends AsyncResource { | 703 | 687 | directive = this.reporter.getTodo(this.message); | 704 | 688 | } | 705 | 689 |
| | 690 | + if (this.reportedType) { | | 691 | + details.type = this.reportedType; | | 692 | + } | | 693 | + | 706 | 694 | if (this.passed) { | 707 | 695 | this.reporter.ok(this.nesting, kFilename, this.testNumber, this.name, details, directive); | 708 | 696 | } else { | @@ -746,6 +734,7 @@ class TestHook extends Test { | 746 | 734 | } | 747 | 735 |
| 748 | 736 | class Suite extends Test { | | 737 | + reportedType = 'suite'; | 749 | 738 | constructor(options) { | 750 | 739 | super(options); | 751 | 740 |
| |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/test_runner/utils.js +25Lines changed: 25 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -222,8 +222,33 @@ function parseCommandLine() { | 222 | 222 | return globalTestOptions; | 223 | 223 | } | 224 | 224 |
| | 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 | + | 225 | 249 | module.exports = { | 226 | 250 | convertStringToRegExp, | | 251 | + countCompletedTest, | 227 | 252 | createDeferredCallback, | 228 | 253 | doesPathMatchFilter, | 229 | 254 | isSupportedFileType, | |
Collapse file Copy file name to clipboardExpand all lines: test/message/test_runner_abort.out +4-3Lines changed: 4 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -260,10 +260,11 @@ not ok 4 - callback abort signal | 260 | 260 | * | 261 | 261 | ... | 262 | 262 | 1..4 | 263 | | -# tests 4 | 264 | | -# pass 0 | | 263 | +# tests 22 | | 264 | +# suites 0 | | 265 | +# pass 8 | 265 | 266 | # fail 0 | 266 | | -# cancelled 4 | | 267 | +# cancelled 14 | 267 | 268 | # skipped 0 | 268 | 269 | # todo 0 | 269 | 270 | # duration_ms * |
Collapse file Copy file name to clipboardExpand all lines: test/message/test_runner_abort_suite.out +6-3Lines changed: 6 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -64,6 +64,7 @@ TAP version 13 | 64 | 64 | not ok 1 - describe timeout signal | 65 | 65 | --- | 66 | 66 | duration_ms: * | | 67 | + type: 'suite' | 67 | 68 | failureType: 'testAborted' | 68 | 69 | error: 'The operation was aborted due to timeout' | 69 | 70 | code: 23 | @@ -78,6 +79,7 @@ not ok 1 - describe timeout signal | 78 | 79 | not ok 2 - describe abort signal | 79 | 80 | --- | 80 | 81 | duration_ms: * | | 82 | + type: 'suite' | 81 | 83 | failureType: 'testAborted' | 82 | 84 | error: 'This operation was aborted' | 83 | 85 | code: 20 | @@ -94,10 +96,11 @@ not ok 2 - describe abort signal | 94 | 96 | * | 95 | 97 | ... | 96 | 98 | 1..2 | 97 | | -# tests 2 | 98 | | -# pass 0 | | 99 | +# tests 9 | | 100 | +# suites 2 | | 101 | +# pass 4 | 99 | 102 | # fail 0 | 100 | | -# cancelled 2 | | 103 | +# cancelled 5 | 101 | 104 | # skipped 0 | 102 | 105 | # todo 0 | 103 | 106 | # duration_ms * |
|