From 24e3f14ea2374f354763ac9dbc8ea52a7306764b Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Thu, 23 Apr 2026 15:59:03 +0800 Subject: [PATCH] Fix JUnit 5 launch failure with standalone/fat Multi-Release JARs Override preLaunchCheck() to catch false-negative ERR_JUNIT_NOT_ON_PATH errors from upstream Eclipse JDT's CoreTestSearchEngine.findAnnotations(), which fails to find @Testable in standalone JARs like junit-platform-console-standalone-1.13.4.jar. The fallback uses IJavaProject.findType() (the old-style lookup) to verify JUnit is actually on the classpath before suppressing the error. Fixes: https://github.com/redhat-developer/vscode-java/issues/4396 Upstream: https://github.com/eclipse-jdt/eclipse.jdt.ui/issues/2959 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../JUnitLaunchConfigurationDelegate.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfigurationDelegate.java b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfigurationDelegate.java index ddbfb262..aca2304e 100644 --- a/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfigurationDelegate.java +++ b/java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfigurationDelegate.java @@ -31,6 +31,7 @@ import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; @@ -60,11 +61,58 @@ public class JUnitLaunchConfigurationDelegate extends org.eclipse.jdt.junit.laun private static final Set testNameArgs = new HashSet<>( Arrays.asList("-test", "-classNames", "-packageNameFile", "-testNameFile")); + // org.eclipse.jdt.internal.junit.IJUnitStatusConstants.ERR_JUNIT_NOT_ON_PATH + private static final int ERR_JUNIT_NOT_ON_PATH = 20006; + public JUnitLaunchConfigurationDelegate(Argument args) { super(); this.args = args; } + /** + * Workaround for https://github.com/redhat-developer/vscode-java/issues/4396 + * + * Eclipse JDT's CoreTestSearchEngine.hasJUnit5TestAnnotation() was changed to use + * NameLookup.findType() with ACCEPT_ANNOTATIONS + checkRestrictions=true, which fails + * to find @Testable in standalone/fat Multi-Release JARs + * (e.g., junit-platform-console-standalone-1.13.4.jar). + * + * This override calls the superclass preLaunchCheck() normally, but catches the specific + * "JUnit not on path" false negative and falls back to project.findType() which works. + * + * See upstream: https://github.com/eclipse-jdt/eclipse.jdt.ui/issues/2959 + */ + @Override + protected void preLaunchCheck(ILaunchConfiguration configuration, ILaunch launch, + IProgressMonitor monitor) throws CoreException { + try { + super.preLaunchCheck(configuration, launch, monitor); + } catch (CoreException e) { + if (e.getStatus().getCode() == ERR_JUNIT_NOT_ON_PATH) { + final IJavaProject javaProject = getJavaProject(configuration); + if (javaProject != null && hasJUnitOnClasspath(javaProject)) { + // JUnit is actually on the classpath via a standalone/fat JAR, + // suppress the false negative from the upstream check. + return; + } + } + throw e; + } + } + + /** + * Fallback check using the old-style project.findType() which correctly resolves + * types inside standalone/fat Multi-Release JARs. + */ + private boolean hasJUnitOnClasspath(IJavaProject javaProject) { + try { + return javaProject.findType("org.junit.jupiter.api.Test") != null + || javaProject.findType("org.junit.platform.commons.annotation.Testable") != null; + } catch (JavaModelException e) { + return false; + } + } + public Response getJUnitLaunchArguments(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) { final ILaunch launch = new Launch(configuration, mode, null);