Skip to content
Merged
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 src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export namespace Context {
export namespace JUnitTestPart {
export const CLASS: string = 'class:';
export const NESTED_CLASS: string = 'nested-class:';
export const SUITE: string = 'suite:';
export const METHOD: string = 'method:';
export const TEST_FACTORY: string = 'test-factory:';
// Property id is for jqwik
Expand Down
7 changes: 6 additions & 1 deletion src/runners/junitRunner/JUnitRunnerResultAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export class JUnitRunnerResultAnalyzer extends RunnerResultAnalyzer {
}

protected getTestId(message: string): string {
if (message.includes('engine:junit5') || message.includes('engine:junit-jupiter') || message.includes('engine:jqwik')) {
if (message.includes('engine:junit5') || message.includes('engine:junit-jupiter') || message.includes('engine:jqwik') || message.includes('engine:junit-platform-suite')) {
return this.getTestIdForJunit5Method(message);
} else {
return this.getTestIdForNonJunit5Method(message);
Expand All @@ -218,6 +218,11 @@ export class JUnitRunnerResultAnalyzer extends RunnerResultAnalyzer {

if (part.startsWith(JUnitTestPart.CLASS)) {
className = part.substring(JUnitTestPart.CLASS.length);
} else if (part.startsWith(JUnitTestPart.SUITE)) {
// Only use suite name as className if no class: part has been seen yet
if (!className) {
className = part.substring(JUnitTestPart.SUITE.length);
}
} else if (part.startsWith(JUnitTestPart.METHOD)) {
const rawMethodName: string = part.substring(JUnitTestPart.METHOD.length);
// If the method name exists then we want to include the '#' qualifier.
Expand Down
74 changes: 72 additions & 2 deletions test/suite/JUnitAnalyzer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

import * as assert from 'assert';
import * as sinon from 'sinon';
import { MarkdownString, Range, TestController, TestMessage, TestRunRequest, tests, workspace } from 'vscode';
import { MarkdownString, Range, TestController, TestMessage, TestRunRequest, tests, Uri, workspace } from 'vscode';
import { JUnitRunnerResultAnalyzer } from '../../src/runners/junitRunner/JUnitRunnerResultAnalyzer';
import { generateTestItem } from './utils';
import { TestKind, IRunTestContext } from '../../src/java-test-runner.api';
import { TestKind, TestLevel, IRunTestContext } from '../../src/java-test-runner.api';
import { dataCache } from '../../src/controller/testItemDataCache';

// tslint:disable: only-arrow-functions
// tslint:disable: no-object-literal-type-assertion
Expand Down Expand Up @@ -543,4 +544,73 @@ org.opentest4j.AssertionFailedError: expected: <1> but was: <2>
sinon.assert.calledWith(passedSpy, dummy);
});

test("test JUnit 5 @Suite class passed result", () => {
// Create a class-level test item for the Suite class
const suiteItem = testController.createTestItem('junit@junit5.suite.MyTestSuite', 'MyTestSuite', Uri.file('/mock/test/MyTestSuite.java'));
suiteItem.range = new Range(0, 0, 5, 0);
dataCache.set(suiteItem, {
jdtHandler: '',
fullName: 'junit5.suite.MyTestSuite',
projectName: 'junit',
testLevel: TestLevel.Class,
testKind: TestKind.JUnit5,
});

// Pre-create child items under the suite so that triggeredTestsMapping
// can find them and the global createTestItem is never called.
const classItem = testController.createTestItem('junit@junit5.AppTest', 'AppTest', Uri.file('/mock/test/AppTest.java'));
classItem.range = new Range(0, 0, 10, 0);
dataCache.set(classItem, {
jdtHandler: '',
fullName: 'junit5.AppTest',
projectName: 'junit',
testLevel: TestLevel.Class,
testKind: TestKind.JUnit5,
});

const methodItem = generateTestItem(testController, 'junit@junit5.AppTest#testGetGreeting()', TestKind.JUnit5);
classItem.children.replace([methodItem]);
suiteItem.children.replace([classItem]);

const testRunRequest = new TestRunRequest([suiteItem], []);
const testRun = testController.createTestRun(testRunRequest);
const startedSpy = sinon.spy(testRun, 'started');
const passedSpy = sinon.spy(testRun, 'passed');

// This is the output format when running a @Suite class with junit-platform-suite engine.
// The suite container uses [engine:junit-platform-suite]/[suite:...] format.
// Child tests use [engine:junit-platform-suite]/[suite:...]/[engine:junit-jupiter]/[class:...]/[method:...].
// The protocol nests %TESTS/%TESTE for suite → class → method.
const testRunnerOutput = `%TESTC 2 v2
%TSTTREE1,junit5.suite.MyTestSuite,true,1,false,-1,MyTestSuite,,[engine:junit-platform-suite]/[suite:junit5.suite.MyTestSuite]
%TSTTREE2,junit5.AppTest,true,1,false,1,AppTest,,[engine:junit-platform-suite]/[suite:junit5.suite.MyTestSuite]/[engine:junit-jupiter]/[class:junit5.AppTest]
%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()]
%TESTS 1,junit5.suite.MyTestSuite
%TESTS 2,junit5.AppTest
%TESTS 3,testGetGreeting(junit5.AppTest)
%TESTE 3,testGetGreeting(junit5.AppTest)
%TESTE 2,junit5.AppTest
%TESTE 1,junit5.suite.MyTestSuite
%RUNTIME15`;

const runnerContext: IRunTestContext = {
isDebug: false,
kind: TestKind.JUnit5,
projectName: 'junit',
testItems: [suiteItem],
testRun: testRun,
workspaceFolder: workspace.workspaceFolders?.[0]!,
};

const analyzer = new JUnitRunnerResultAnalyzer(runnerContext);
analyzer.analyzeData(testRunnerOutput);

// Verify the suite item itself started and passed (the core regression in #1828)
sinon.assert.calledWith(startedSpy, suiteItem);
sinon.assert.calledWith(passedSpy, suiteItem, sinon.match.number);
// Verify the method-level child also started and passed
sinon.assert.calledWith(startedSpy, methodItem);
sinon.assert.calledWith(passedSpy, methodItem, sinon.match.number);
});

});
6 changes: 6 additions & 0 deletions test/test-projects/junit/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.jqwik</groupId>
<artifactId>jqwik</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package junit5.suite;

import org.junit.platform.suite.api.SelectPackages;
import org.junit.platform.suite.api.Suite;

@Suite
@SelectPackages("junit5")
public class MyTestSuite {
}