Skip to content

Commit 17f8fa5

Browse files
wenytang-mstrancexpress
authored andcommitted
Use bundled Jupiter version for standalone JUnit Platform jars
JUnit Platform Console Standalone jars expose the platform version in Specification-Version, while the bundled Jupiter engine version is stored in Engine-Version-junit-jupiter. Prefer that Jupiter engine version for junit-platform-console-standalone jars so JUnit 5 and JUnit 6 detection uses the version of the test engine rather than the platform artifact. Fall back to Specification-Version for the existing non-standalone cases and leave annotation lookup/access restriction behavior unchanged. Fixes: #2959
1 parent 5e1a018 commit 17f8fa5

3 files changed

Lines changed: 129 additions & 4 deletions

File tree

org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/util/CoreTestSearchEngine.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ public class CoreTestSearchEngine {
8080
private static final String JUNIT_PLATFORM_SUITE_API_PREFIX= BuildPathSupport.JUNIT_PLATFORM_SUITE_API;
8181
private static final String JUNIT_PLATFORM_COMMONS_PREFIX= BuildPathSupport.JUNIT_PLATFORM_COMMONS;
8282
private static final String JUNIT_JUPITER_API_PREFIX= BuildPathSupport.JUNIT_JUPITER_API;
83+
private static final String JUNIT_PLATFORM_CONSOLE_STANDALONE_PREFIX= "junit-platform-console-standalone"; //$NON-NLS-1$
84+
private static final String ENGINE_VERSION_JUNIT_JUPITER= "Engine-Version-junit-jupiter"; //$NON-NLS-1$
85+
private static final String SPECIFICATION_VERSION= "Specification-Version"; //$NON-NLS-1$
8386
private static final String JAR_EXTENSION= ".jar"; //$NON-NLS-1$
8487

8588
public static boolean isTestOrTestSuite(IType declaringType) throws CoreException {
@@ -198,7 +201,8 @@ private static boolean hasJUnitJupiterTestAnnotation(IJavaProject project, int j
198201
if (type != null) {
199202
// check if we have the right JUnit JUpiter version
200203
Version version = null;
201-
String filename= type.getPath().lastSegment();
204+
PackageFragmentRoot root= (PackageFragmentRoot) type.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
205+
String filename= root != null ? root.getPath().lastSegment() : type.getPath().lastSegment();
202206
boolean isJar = filename.endsWith(JAR_EXTENSION);
203207
// Try matching the library name, this should be enough for many cases.
204208
if (isJar && (filename.startsWith(junitBundlePrefix + "_") || filename.startsWith(junitBundlePrefix + "-"))) { //$NON-NLS-1$ //$NON-NLS-2$
@@ -214,14 +218,13 @@ private static boolean hasJUnitJupiterTestAnnotation(IJavaProject project, int j
214218
}
215219
if (isJar && version == null) {
216220
try {
217-
PackageFragmentRoot root= (PackageFragmentRoot) type.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
218221
Manifest manifest= null;
219222
if (root != null) {
220223
manifest= root.getManifest();
221224
}
222225
if (manifest != null) {
223226
Attributes attributes= manifest.getMainAttributes();
224-
String versionString= attributes.getValue("Specification-Version"); //$NON-NLS-1$
227+
String versionString= getJUnitVersionFromManifest(filename, attributes);
225228
if (versionString != null) {
226229
version= Version.parseVersion(versionString);
227230
}
@@ -233,9 +236,11 @@ private static boolean hasJUnitJupiterTestAnnotation(IJavaProject project, int j
233236
if (version != null && version.getMajor() != junitMajorVersion) {
234237
return false;
235238
}
239+
if (root == null) {
240+
return false;
241+
}
236242
// @Testable/@Suite annotations are not accessible if the JUnit classpath container is set to JUnit 3 or JUnit 4
237243
// (although it may resolve to a JUnit 5 JAR)
238-
IPackageFragmentRoot root= (IPackageFragmentRoot) type.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
239244
IClasspathEntry cpEntry= root.getRawClasspathEntry();
240245
IPath entryPath= cpEntry.getPath();
241246
return !Arrays.asList(disallowedJunitContainerPaths).contains(entryPath);
@@ -247,6 +252,16 @@ private static boolean hasJUnitJupiterTestAnnotation(IJavaProject project, int j
247252
return false;
248253
}
249254

255+
private static String getJUnitVersionFromManifest(String filename, Attributes attributes) {
256+
if (filename.startsWith(JUNIT_PLATFORM_CONSOLE_STANDALONE_PREFIX + "_") || filename.startsWith(JUNIT_PLATFORM_CONSOLE_STANDALONE_PREFIX + "-")) { //$NON-NLS-1$ //$NON-NLS-2$
257+
String versionString= attributes.getValue(ENGINE_VERSION_JUNIT_JUPITER);
258+
if (versionString != null) {
259+
return versionString;
260+
}
261+
}
262+
return attributes.getValue(SPECIFICATION_VERSION);
263+
}
264+
250265
public static boolean isTestImplementor(IType type) throws JavaModelException {
251266
ITypeHierarchy typeHier= type.newSupertypeHierarchy(null);
252267
IType[] superInterfaces= typeHier.getAllInterfaces();

org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/junit/tests/JUnitJUnitTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
JUnit4TestFinderTest16.class,
4545
JUnit5TestFinderJupiterTest.class,
4646
JUnit6TestFinderJupiterTest.class,
47+
JUnitStandaloneDetectionTest.class,
4748

4849
JUnitQuickAssistTest.class,
4950

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 Microsoft Corporation and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Microsoft Corporation - initial API and implementation
13+
*******************************************************************************/
14+
15+
package org.eclipse.jdt.junit.tests;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
19+
import java.io.File;
20+
21+
import org.junit.After;
22+
import org.junit.Test;
23+
24+
import org.eclipse.core.runtime.Path;
25+
26+
import org.eclipse.jdt.core.IJavaProject;
27+
import org.eclipse.jdt.testplugin.JavaProjectHelper;
28+
import org.eclipse.jdt.ui.tests.quickfix.JarUtil;
29+
30+
import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
31+
32+
public class JUnitStandaloneDetectionTest {
33+
34+
private static final String[] JUNIT_JUPITER_API_STUBS= {
35+
"org/junit/jupiter/api/Test.java",
36+
"""
37+
package org.junit.jupiter.api;
38+
39+
public @interface Test {
40+
}
41+
""",
42+
"org/junit/jupiter/api/TestTemplate.java",
43+
"""
44+
package org.junit.jupiter.api;
45+
46+
public @interface TestTemplate {
47+
}
48+
""",
49+
"org/junit/jupiter/api/ClassTemplate.java",
50+
"""
51+
package org.junit.jupiter.api;
52+
53+
public @interface ClassTemplate {
54+
}
55+
""",
56+
"org/junit/platform/commons/annotation/Testable.java",
57+
"""
58+
package org.junit.platform.commons.annotation;
59+
60+
public @interface Testable {
61+
}
62+
"""
63+
};
64+
65+
private IJavaProject javaProject;
66+
67+
@After
68+
public void tearDown() throws Exception {
69+
if (javaProject != null) {
70+
JavaProjectHelper.delete(javaProject);
71+
}
72+
}
73+
74+
@Test
75+
public void testDetectJUnit5FromPlatformConsoleStandaloneJar() throws Exception {
76+
javaProject= createProjectWithStandaloneJar("junit-platform-console-standalone-1.13.4.jar", "1.13.4", "5.13.4");
77+
78+
assertThat(CoreTestSearchEngine.hasJUnit5TestAnnotation(javaProject)).isTrue();
79+
assertThat(CoreTestSearchEngine.hasJUnit6TestAnnotation(javaProject)).isFalse();
80+
}
81+
82+
@Test
83+
public void testDetectJUnit6FromPlatformConsoleStandaloneJar() throws Exception {
84+
javaProject= createProjectWithStandaloneJar("junit-platform-console-standalone-6.0.3.jar", "6.0.3", "6.0.3");
85+
86+
assertThat(CoreTestSearchEngine.hasJUnit5TestAnnotation(javaProject)).isFalse();
87+
assertThat(CoreTestSearchEngine.hasJUnit6TestAnnotation(javaProject)).isTrue();
88+
}
89+
90+
private static IJavaProject createProjectWithStandaloneJar(String jarName, String specificationVersion, String jupiterVersion) throws Exception {
91+
IJavaProject project= JavaProjectHelper.createJavaProject("JUnitStandaloneDetectionTest", "bin");
92+
JavaProjectHelper.addRTJar_17(project, false);
93+
File jar= new File(project.getProject().getLocation().toFile(), jarName);
94+
String rtStubs= JavaProjectHelper.findRtJar(JavaProjectHelper.RT_STUBS17)[0].toOSString();
95+
JarUtil.createJar(JUNIT_JUPITER_API_STUBS, new String[] {
96+
"META-INF/MANIFEST.MF",
97+
"""
98+
Manifest-Version: 1.0
99+
Automatic-Module-Name: org.junit.platform.console.standalone
100+
Multi-Release: true
101+
Specification-Version: %s
102+
Engine-Version-junit-jupiter: %s
103+
104+
""".formatted(specificationVersion, jupiterVersion)
105+
}, jar.getAbsolutePath(), new String[] { rtStubs }, "17", null, null);
106+
JavaProjectHelper.addLibrary(project, Path.fromOSString(jar.getAbsolutePath()));
107+
return project;
108+
}
109+
}

0 commit comments

Comments
 (0)