|
5 | 5 |
|
6 | 6 | import * as assert from 'assert'; |
7 | 7 | import * as sinon from 'sinon'; |
8 | | -import { MarkdownString, Range, TestController, TestMessage, TestRunRequest, tests, workspace } from 'vscode'; |
| 8 | +import { MarkdownString, Range, TestController, TestMessage, TestRunRequest, tests, Uri, workspace } from 'vscode'; |
9 | 9 | import { JUnitRunnerResultAnalyzer } from '../../src/runners/junitRunner/JUnitRunnerResultAnalyzer'; |
10 | 10 | import { generateTestItem } from './utils'; |
11 | | -import { TestKind, IRunTestContext } from '../../src/java-test-runner.api'; |
| 11 | +import { TestKind, TestLevel, IRunTestContext } from '../../src/java-test-runner.api'; |
| 12 | +import { dataCache } from '../../src/controller/testItemDataCache'; |
12 | 13 |
|
13 | 14 | // tslint:disable: only-arrow-functions |
14 | 15 | // tslint:disable: no-object-literal-type-assertion |
@@ -543,4 +544,73 @@ org.opentest4j.AssertionFailedError: expected: <1> but was: <2> |
543 | 544 | sinon.assert.calledWith(passedSpy, dummy); |
544 | 545 | }); |
545 | 546 |
|
| 547 | + test("test JUnit 5 @Suite class passed result", () => { |
| 548 | + // Create a class-level test item for the Suite class |
| 549 | + const suiteItem = testController.createTestItem('junit@junit5.suite.MyTestSuite', 'MyTestSuite', Uri.file('/mock/test/MyTestSuite.java')); |
| 550 | + suiteItem.range = new Range(0, 0, 5, 0); |
| 551 | + dataCache.set(suiteItem, { |
| 552 | + jdtHandler: '', |
| 553 | + fullName: 'junit5.suite.MyTestSuite', |
| 554 | + projectName: 'junit', |
| 555 | + testLevel: TestLevel.Class, |
| 556 | + testKind: TestKind.JUnit5, |
| 557 | + }); |
| 558 | + |
| 559 | + // Pre-create child items under the suite so that triggeredTestsMapping |
| 560 | + // can find them and the global createTestItem is never called. |
| 561 | + const classItem = testController.createTestItem('junit@junit5.AppTest', 'AppTest', Uri.file('/mock/test/AppTest.java')); |
| 562 | + classItem.range = new Range(0, 0, 10, 0); |
| 563 | + dataCache.set(classItem, { |
| 564 | + jdtHandler: '', |
| 565 | + fullName: 'junit5.AppTest', |
| 566 | + projectName: 'junit', |
| 567 | + testLevel: TestLevel.Class, |
| 568 | + testKind: TestKind.JUnit5, |
| 569 | + }); |
| 570 | + |
| 571 | + const methodItem = generateTestItem(testController, 'junit@junit5.AppTest#testGetGreeting()', TestKind.JUnit5); |
| 572 | + classItem.children.replace([methodItem]); |
| 573 | + suiteItem.children.replace([classItem]); |
| 574 | + |
| 575 | + const testRunRequest = new TestRunRequest([suiteItem], []); |
| 576 | + const testRun = testController.createTestRun(testRunRequest); |
| 577 | + const startedSpy = sinon.spy(testRun, 'started'); |
| 578 | + const passedSpy = sinon.spy(testRun, 'passed'); |
| 579 | + |
| 580 | + // This is the output format when running a @Suite class with junit-platform-suite engine. |
| 581 | + // The suite container uses [engine:junit-platform-suite]/[suite:...] format. |
| 582 | + // Child tests use [engine:junit-platform-suite]/[suite:...]/[engine:junit-jupiter]/[class:...]/[method:...]. |
| 583 | + // The protocol nests %TESTS/%TESTE for suite → class → method. |
| 584 | + const testRunnerOutput = `%TESTC 2 v2 |
| 585 | +%TSTTREE1,junit5.suite.MyTestSuite,true,1,false,-1,MyTestSuite,,[engine:junit-platform-suite]/[suite:junit5.suite.MyTestSuite] |
| 586 | +%TSTTREE2,junit5.AppTest,true,1,false,1,AppTest,,[engine:junit-platform-suite]/[suite:junit5.suite.MyTestSuite]/[engine:junit-jupiter]/[class:junit5.AppTest] |
| 587 | +%TSTTREE3,testGetGreeting(junit5.AppTest),false,1,false,2,testGetGreeting(),,[engine:junit-platform-suite]/[suite:junit5.suite.MyTestSuite]/[engine:junit-jupiter]/[class:junit5.AppTest]/[method:testGetGreeting()] |
| 588 | +%TESTS 1,junit5.suite.MyTestSuite |
| 589 | +%TESTS 2,junit5.AppTest |
| 590 | +%TESTS 3,testGetGreeting(junit5.AppTest) |
| 591 | +%TESTE 3,testGetGreeting(junit5.AppTest) |
| 592 | +%TESTE 2,junit5.AppTest |
| 593 | +%TESTE 1,junit5.suite.MyTestSuite |
| 594 | +%RUNTIME15`; |
| 595 | + |
| 596 | + const runnerContext: IRunTestContext = { |
| 597 | + isDebug: false, |
| 598 | + kind: TestKind.JUnit5, |
| 599 | + projectName: 'junit', |
| 600 | + testItems: [suiteItem], |
| 601 | + testRun: testRun, |
| 602 | + workspaceFolder: workspace.workspaceFolders?.[0]!, |
| 603 | + }; |
| 604 | + |
| 605 | + const analyzer = new JUnitRunnerResultAnalyzer(runnerContext); |
| 606 | + analyzer.analyzeData(testRunnerOutput); |
| 607 | + |
| 608 | + // Verify the suite item itself started and passed (the core regression in #1828) |
| 609 | + sinon.assert.calledWith(startedSpy, suiteItem); |
| 610 | + sinon.assert.calledWith(passedSpy, suiteItem, sinon.match.number); |
| 611 | + // Verify the method-level child also started and passed |
| 612 | + sinon.assert.calledWith(startedSpy, methodItem); |
| 613 | + sinon.assert.calledWith(passedSpy, methodItem, sinon.match.number); |
| 614 | + }); |
| 615 | + |
546 | 616 | }); |
0 commit comments