From 03c56fa0ccecd6ab97ac2b2f6d09295233de5833 Mon Sep 17 00:00:00 2001 From: Konrad Windszus Date: Fri, 2 Jan 2026 13:17:06 +0100 Subject: [PATCH 1/4] Add Surefire Provider Classpath to Eclipse test classpath Deprecate duplicate constants (defined in org.eclipse.jdt.core.IClasspathAttribute) This closes #2112 --- .../eclipse/m2e/jdt/IClasspathManager.java | 9 +- .../AbstractJavaProjectConfigurator.java | 5 +- .../DefaultClasspathManagerDelegate.java | 8 +- .../jdt/internal/MavenClasspathHelpers.java | 2 +- .../launch/MavenRuntimeClasspathProvider.java | 152 +++++++++++++++++- 5 files changed, 162 insertions(+), 14 deletions(-) diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/IClasspathManager.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/IClasspathManager.java index 2ec933f86f..1079e119f9 100644 --- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/IClasspathManager.java +++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/IClasspathManager.java @@ -16,6 +16,7 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IClasspathAttribute; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IPackageFragmentRoot; @@ -75,8 +76,10 @@ public interface IClasspathManager { * org.eclipse.jdt.core.IClasspathAttribute.TEST, copied here to allow running with older jdt.core version. * * @since 1.9 + * @deprecated use {@link org.eclipse.jdt.core.IClasspathAttribute#TEST} instead */ - String TEST_ATTRIBUTE = "test"; + @Deprecated + String TEST_ATTRIBUTE = IClasspathAttribute.TEST; /** * Name of IClasspathEntry attribute that is to limit the imported code of project by jdt.core. Same as @@ -84,8 +87,10 @@ public interface IClasspathManager { * version. * * @since 1.9 + * @deprecated use {@link org.eclipse.jdt.core.IClasspathAttribute#WITHOUT_TEST_CODE} instead */ - String WITHOUT_TEST_CODE = "without_test_code"; + @Deprecated + String WITHOUT_TEST_CODE = IClasspathAttribute.WITHOUT_TEST_CODE; /** * Maven dependency resolution scope constant indicating test scope. diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java index d5aa3d6ea3..56de1eaa1a 100644 --- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java +++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/AbstractJavaProjectConfigurator.java @@ -66,7 +66,6 @@ import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest; import org.eclipse.m2e.jdt.IClasspathDescriptor; import org.eclipse.m2e.jdt.IClasspathEntryDescriptor; -import org.eclipse.m2e.jdt.IClasspathManager; import org.eclipse.m2e.jdt.IJavaProjectConfigurator; import org.eclipse.m2e.jdt.JreSystemVersion; import org.eclipse.m2e.jdt.MavenJdtPlugin; @@ -528,7 +527,7 @@ protected void addSourceDirs(IClasspathDescriptor classpath, IProject project, L // all source entries are marked as generated (a.k.a. optional) IClasspathEntryDescriptor descriptor = classpath.addSourceEntry(sourceFolder.getFullPath(), outputPath, inclusion, exclusion, true /*generated*/); - descriptor.setClasspathAttribute(IClasspathManager.TEST_ATTRIBUTE, addTestFlag ? "true" : null); + descriptor.setClasspathAttribute(IClasspathAttribute.TEST, addTestFlag ? "true" : null); } else { log.info("Not adding source folder " + sourceFolder.getFullPath() + " because it overlaps with " + enclosing.getPath()); @@ -682,7 +681,7 @@ private void addResourceFolder(IClasspathDescriptor classpath, IPath resourceFol log.info("Adding resource folder " + resourceFolder); IClasspathEntryDescriptor descriptor = classpath.addSourceEntry(resourceFolder, outputPath, DEFAULT_INCLUSIONS, new IPath[] {IPath.fromOSString("**")}, false /*optional*/); - descriptor.setClasspathAttribute(IClasspathManager.TEST_ATTRIBUTE, addTestFlag ? "true" : null); + descriptor.setClasspathAttribute(IClasspathAttribute.TEST, addTestFlag ? "true" : null); descriptor.setClasspathAttribute(IClasspathAttribute.OPTIONAL, "true"); //$NON-NLS-1$ } diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/DefaultClasspathManagerDelegate.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/DefaultClasspathManagerDelegate.java index af86b1f38f..5fea4cae6d 100644 --- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/DefaultClasspathManagerDelegate.java +++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/DefaultClasspathManagerDelegate.java @@ -23,6 +23,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IClasspathAttribute; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.resolver.filter.ArtifactFilter; @@ -48,6 +49,7 @@ * @author igor */ public class DefaultClasspathManagerDelegate implements IClasspathManagerDelegate { + private final IProjectConfigurationManager configurationManager; private final IMavenProjectRegistry projectManager; @@ -139,7 +141,7 @@ void addClasspathEntries(IClasspathDescriptor classpath, IMavenProjectFacade fac File artifactFile = a.getFile(); if(artifactFile != null /*&& artifactFile.canRead()*/) { entry = classpath.addLibraryEntry(IPath.fromOSString(artifactFile.getAbsolutePath())); - entry.setClasspathAttribute(IClasspathManager.TEST_ATTRIBUTE, addTestFlag ? "true" : null); + entry.setClasspathAttribute(IClasspathAttribute.TEST, addTestFlag ? "true" : null); } } @@ -158,8 +160,8 @@ void addClasspathEntries(IClasspathDescriptor classpath, IMavenProjectFacade fac projectTestAttributes.forEach((entryPath, testAttributes) -> { //the classpath definitely has an entry matching the path IClasspathEntryDescriptor descriptor = findClasspathDescriptor(classpath, entryPath); - descriptor.setClasspathAttribute(IClasspathManager.TEST_ATTRIBUTE, (testAttributes.isTest) ? "true" : null); - descriptor.setClasspathAttribute(IClasspathManager.WITHOUT_TEST_CODE, + descriptor.setClasspathAttribute(IClasspathAttribute.TEST, (testAttributes.isTest) ? "true" : null); + descriptor.setClasspathAttribute(IClasspathAttribute.WITHOUT_TEST_CODE, (testAttributes.excludeTestSources) ? "true" : null); }); } diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/MavenClasspathHelpers.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/MavenClasspathHelpers.java index b33cd830b9..9075de7141 100644 --- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/MavenClasspathHelpers.java +++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/MavenClasspathHelpers.java @@ -70,7 +70,7 @@ public static IClasspathEntry newContainerEntry(IPath path, IClasspathAttribute. } public static boolean isTestSource(IClasspathEntry entry) { - return "true".equals(getAttribute(entry, IClasspathManager.TEST_ATTRIBUTE)); + return "true".equals(getAttribute(entry, IClasspathAttribute.TEST)); } public static String getAttribute(IClasspathEntry entry, String key) { diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java index 8618d65765..57d013a03b 100644 --- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java +++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java @@ -14,8 +14,11 @@ package org.eclipse.m2e.jdt.internal.launch; import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; @@ -26,6 +29,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.eclipse.aether.util.version.GenericVersionScheme; +import org.eclipse.aether.version.InvalidVersionSpecificationException; +import org.eclipse.aether.version.Version; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; @@ -49,6 +55,8 @@ import org.eclipse.jdt.launching.StandardClasspathProvider; import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.Mojo; +import org.apache.maven.plugin.MojoExecution; import org.apache.maven.project.MavenProject; import org.eclipse.m2e.core.MavenPlugin; @@ -101,12 +109,30 @@ public class MavenRuntimeClasspathProvider extends StandardClasspathProvider { private static final String PROPERTY_M2E_DISABLE_ADD_MISSING_J_UNIT5_EXECUTION_DEPENDENCIES = "m2e.disableAddMissingJUnit5ExecutionDependencies"; + private static final String GROUP_ID_SUREFIRE_PLUGIN = "org.apache.maven.plugins"; //$NON-NLS-1$ + + private static final String ARTIFACT_ID_SUREFIRE_PLUGIN = "maven-surefire-plugin"; //$NON-NLS-1$ + + /** + * Minimum Surefire version to support adding provider dependencies + * (https://issues.apache.org/jira/browse/SUREFIRE-1564) + */ + private static final Version MIN_SUREFIRE_VERSION; //$NON-NLS-1$ + + private static final String GOAL_TEST = "test"; //$NON-NLS-1$ + private static final Set supportedTypes = new HashSet<>(); static { // not exactly nice, but works with eclipse 3.2, 3.3 and 3.4M3 supportedTypes.add(MavenRuntimeClasspathProvider.JDT_JAVA_APPLICATION); supportedTypes.add(MavenRuntimeClasspathProvider.JDT_JUNIT_TEST); supportedTypes.add(MavenRuntimeClasspathProvider.JDT_TESTNG_TEST); + + try { + MIN_SUREFIRE_VERSION = new GenericVersionScheme().parseVersion("2.22.1"); + } catch(InvalidVersionSpecificationException ex) { + throw new IllegalArgumentException("Could not parse hardcoded version 2.22.1", ex); + } } IMavenProjectRegistry projectManager = MavenPlugin.getMavenProjectRegistry(); @@ -167,11 +193,11 @@ public IRuntimeClasspathEntry[] resolveClasspath(final IRuntimeClasspathEntry[] } else { context = projectFacade.createExecutionContext(); } - return context.execute((ctx, monitor1) -> resolveClasspath0(entries, configuration, monitor1), monitor); + return context.execute((ctx, monitor1) -> resolveClasspath0(entries, configuration, monitor1, ctx), monitor); } IRuntimeClasspathEntry[] resolveClasspath0(IRuntimeClasspathEntry[] entries, ILaunchConfiguration configuration, - IProgressMonitor monitor) throws CoreException { + IProgressMonitor monitor, IMavenExecutionContext context) throws CoreException { IJavaProject javaProject = JavaRuntime.getJavaProject(configuration); boolean isModularConfiguration = JavaRuntime.isModularConfiguration(configuration); @@ -184,7 +210,7 @@ IRuntimeClasspathEntry[] resolveClasspath0(IRuntimeClasspathEntry[] entries, ILa for(IRuntimeClasspathEntry entry : entries) { if(entry.getType() == IRuntimeClasspathEntry.CONTAINER && MavenClasspathHelpers.isMaven2ClasspathContainer(entry.getPath())) { - addMavenClasspathEntries(all, entry, configuration, scope, monitor, isModularConfiguration); + addMavenClasspathEntries(all, entry, configuration, scope, monitor, isModularConfiguration, context); } else if(entry.getType() == IRuntimeClasspathEntry.PROJECT) { if(javaProject.getPath().equals(entry.getPath())) { addProjectEntries(all, entry.getPath(), scope, THIS_PROJECT_CLASSIFIER, configuration, monitor, @@ -207,7 +233,7 @@ private void addStandardClasspathEntries(Set all, IRunti private void addMavenClasspathEntries(Set resolved, IRuntimeClasspathEntry runtimeClasspathEntry, ILaunchConfiguration configuration, int scope, - IProgressMonitor monitor, boolean isModularConfiguration) throws CoreException { + IProgressMonitor monitor, boolean isModularConfiguration, IMavenExecutionContext context) throws CoreException { IJavaProject javaProject = JavaRuntime.getJavaProject(configuration); MavenJdtPlugin plugin = MavenJdtPlugin.getDefault(); IClasspathManager buildpathManager = plugin.getBuildpathManager(); @@ -229,12 +255,128 @@ private void addMavenClasspathEntries(Set resolved, } } - if(scope == IClasspathManager.CLASSPATH_TEST && TESTKIND_ORG_ECLIPSE_JDT_JUNIT_LOADER_JUNIT5 + if(scope == IClasspathManager.CLASSPATH_TEST) { + // TODO: distinguish between junit and testng? + addMavenSurefirePluginProviderDependencies(resolved, configuration, monitor, context, javaProject); + } + } + + /** + * Add Maven Surefire Plugin dependencies to the classpath which are required for the selected provider + * + * @param classpath + * @param facade + * @param maven + * @param monitor + * @throws CoreException + */ + private void addMavenSurefirePluginProviderDependencies(Set resolved, + ILaunchConfiguration configuration, IProgressMonitor monitor, IMavenExecutionContext context, + IJavaProject javaProject) throws CoreException { + + IMavenProjectFacade projectFacade = projectManager.create(javaProject.getProject(), monitor); + if(Boolean.parseBoolean(projectFacade.getMavenProject().getProperties() + .getProperty(PROPERTY_M2E_DISABLE_ADD_MISSING_J_UNIT5_EXECUTION_DEPENDENCIES, "false"))) { //$NON-NLS-1$ + log.debug("Skipping adding Maven Surefire Plugin dependencies as property {} is set to true", + PROPERTY_M2E_DISABLE_ADD_MISSING_J_UNIT5_EXECUTION_DEPENDENCIES); + return; + } + + boolean surefireDependenciesAdded = false; + for(MojoExecution mojoExecution : projectFacade.getMojoExecutions(GROUP_ID_SUREFIRE_PLUGIN, + ARTIFACT_ID_SUREFIRE_PLUGIN, monitor, GOAL_TEST)) { + // only add dependencies if the surefire plugin version is above 2.22.1 (https://issues.apache.org/jira/browse/SUREFIRE-1564) + try { + Version version = new GenericVersionScheme().parseVersion(mojoExecution.getPlugin().getVersion()); + if(version.compareTo(MIN_SUREFIRE_VERSION) < 0) { + log.debug( + "Skipping adding Maven Surefire Plugin dependencies for MojoExecution id {} of plugin {} as its version {} is below 2.22.1", + mojoExecution.getExecutionId(), mojoExecution.getPlugin().getId(), + mojoExecution.getPlugin().getVersion()); + } else { + log.debug("Adding Maven Surefire Plugin dependencies for MojoExecution id {} of plugin {}", + mojoExecution.getExecutionId(), mojoExecution.getPlugin().getId()); + addMavenSurefirePluginProviderDependencies(resolved, mojoExecution, context, monitor); + surefireDependenciesAdded = true; + } + } catch(InvalidVersionSpecificationException ex) { + log.warn("Could not parse Maven Surefire Plugin version " + mojoExecution.getPlugin().getVersion() + + " for MojoExecution id " + mojoExecution.getExecutionId() + ", skipping adding its provider dependencies", + ex); + } + + } + + // use legacy mechanism to add junit5 dependencies for junit5 launches when surefire plugin is not used or below 2.22.1 + if(!surefireDependenciesAdded && TESTKIND_ORG_ECLIPSE_JDT_JUNIT_LOADER_JUNIT5 .equals(configuration.getAttribute(ATTRIBUTE_ORG_ECLIPSE_JDT_JUNIT_TEST_KIND, ""))) { + log.debug( + "Adding missing JUnit5 execution dependencies for JUnit5 launch configuration via legacy method as no suitable Maven Surefire Plugin execution was found"); addMissingJUnit5ExecutionDependencies(resolved, monitor, javaProject); } } + @SuppressWarnings("unchecked") + Boolean addMavenSurefirePluginProviderDependencies(Set resolved, MojoExecution mojoExecution, + IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException { + Mojo mojo = MavenPlugin.getMaven().getConfiguredMojo(context.getSession(), mojoExecution, Mojo.class); + final Class abstractSurefireMojoClass; + try { + abstractSurefireMojoClass = Class.forName("org.apache.maven.plugin.surefire.AbstractSurefireMojo", false, + mojoExecution.getMojoDescriptor().getRealm()); + } catch(ClassNotFoundException ex) { + throw new IllegalStateException("Could not load AbstractSurefireMojo class from surefire plugin realm", ex); + } + Boolean isClasspathModified = false; + try { + // look for surefire test classpath: https://github.com/apache/maven-surefire/blob/18f0c9f9dd850f98377032693fa5ec460f42b65b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java#L1156C34-L1156C49 + Object testClassPath; + try { + // available since Surefire 2.22.1 (https://github.com/apache/maven-surefire/commit/242c0e8a70be5a2a839ab00062c645f0d7b81137) + Method generateTestClasspathMethod = abstractSurefireMojoClass.getDeclaredMethod("generateTestClasspath"); + generateTestClasspathMethod.setAccessible(true); + testClassPath = generateTestClasspathMethod.invoke(mojo); + } catch(NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException e) { + log.warn("Could not access generateTestClasspath() method on AbstractSurefireMojo", e); + return false; + } + // and determine the providers used by this project with the given test classpath: https://github.com/apache/maven-surefire/blob/18f0c9f9dd850f98377032693fa5ec460f42b65b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java#L1156 + try { + Class providerInfoClass = Class.forName("org.apache.maven.surefire.providerapi.ProviderInfo", false, + mojoExecution.getMojoDescriptor().getRealm()); + Class testClassPathClass = Class.forName("org.apache.maven.plugin.surefire.TestClassPath", false, + mojoExecution.getMojoDescriptor().getRealm()); + Method getProviderNameMethod = providerInfoClass.getDeclaredMethod("getProviderName"); + Method getProviderClasspathMethod = providerInfoClass.getDeclaredMethod("getProviderClasspath"); + Method createProvidersMethod = abstractSurefireMojoClass.getDeclaredMethod("createProviders", + testClassPathClass); + createProvidersMethod.setAccessible(true); + Collection providerInfos = (Collection) createProvidersMethod.invoke(mojo, testClassPath); + for(Object providerInfo : providerInfos) { + String providerName = (String) getProviderNameMethod.invoke(providerInfo); + log.debug("Adding Surefire dependencies for provider: {}", providerName); + for(Artifact artifact : ((Collection) getProviderClasspathMethod.invoke(providerInfo))) { + // all returned artifacts are already resolved via the call to "getProviderClasspath()" + File artifactFile = artifact.getFile(); + resolved.add(JavaRuntime.newArchiveRuntimeClasspathEntry(IPath.fromOSString(artifactFile.getAbsolutePath()), + IRuntimeClasspathEntry.USER_CLASSES)); + log.debug("Added Surefire provider dependency: {}:{}:{} for provider {}", artifact.getGroupId(), + artifact.getArtifactId(), artifact.getVersion(), providerName); + isClasspathModified = true; + } + } + } catch(NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException + | ClassNotFoundException e) { + log.warn( + "Could not access createProviders(org.apache.maven.plugin.surefire.TestClassPath) method on AbstractSurefireMojo", + e); + } + } finally { + MavenPlugin.getMaven().releaseMojo(mojo, mojoExecution); + } + return isClasspathModified; + } + private void addMissingJUnit5ExecutionDependencies(Set resolved, IProgressMonitor monitor, IJavaProject javaProject) throws CoreException { IMavenProjectFacade facade = projectManager.create(javaProject.getProject(), monitor); From b906a73b9476a75f3990d487195c9324725c7c08 Mon Sep 17 00:00:00 2001 From: Konrad Windszus Date: Fri, 2 Jan 2026 22:08:56 +0100 Subject: [PATCH 2/4] Add test --- .../junit5TestProject/pom.xml | 47 +++++++++++++ .../src/test/java/testMvn/TestClass.java | 15 +++++ ...UnitTestLaunchConfigConfigurationTest.java | 67 ++++++++++++++++++- 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/pom.xml create mode 100644 org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/src/test/java/testMvn/TestClass.java diff --git a/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/pom.xml b/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/pom.xml new file mode 100644 index 0000000000..5794af4f0d --- /dev/null +++ b/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + + foo.bar + 0.0.1-SNAPSHOT + project.a + + + 3.0.0 + 5.14.1 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${version.surefire} + + + + org.junit.vintage + junit-vintage-engine + ${version.junit.jupiter} + + + + + + + + + org.junit.jupiter + junit-jupiter-api + ${version.junit.jupiter} + test + + + junit + junit + 4.13.2 + test + + + \ No newline at end of file diff --git a/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/src/test/java/testMvn/TestClass.java b/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/src/test/java/testMvn/TestClass.java new file mode 100644 index 0000000000..d603b9ba19 --- /dev/null +++ b/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/src/test/java/testMvn/TestClass.java @@ -0,0 +1,15 @@ +package testMvn; + +import java.io.StringWriter; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; + +public class JUnit4Test { + + @Test + public void test1() { + assertTrue(true); + } + +} diff --git a/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/UnitTestLaunchConfigConfigurationTest.java b/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/UnitTestLaunchConfigConfigurationTest.java index f93dd137a7..26f44c8a8f 100644 --- a/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/UnitTestLaunchConfigConfigurationTest.java +++ b/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/UnitTestLaunchConfigConfigurationTest.java @@ -20,6 +20,7 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; @@ -35,12 +36,15 @@ import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; +import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.m2e.core.MavenPlugin; import org.eclipse.m2e.core.internal.preferences.MavenConfigurationImpl; import org.eclipse.m2e.jdt.internal.UnitTestSupport; import org.eclipse.m2e.jdt.internal.launch.MavenRuntimeClasspathProvider; import org.eclipse.m2e.tests.common.AbstractMavenProjectTestCase; +import org.hamcrest.Description; import org.hamcrest.Matchers; +import org.hamcrest.TypeSafeMatcher; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -55,7 +59,7 @@ public class UnitTestLaunchConfigConfigurationTest extends AbstractMavenProjectT private static final String REPLACED_SUREFIRE_POM_STRING = ""; private static final String REPLACED_FAILSAFE_POM_STRING = ""; private static final String ROOT_PATH = "/projects/surefireFailsafeToTestLaunchSettings"; - private static ILaunchManager LAUNCH_MANAGER = DebugPlugin.getDefault().getLaunchManager(); + private static final ILaunchManager LAUNCH_MANAGER = DebugPlugin.getDefault().getLaunchManager(); /* * XML allows encoding set of control characters: space (U+0020), carriage @@ -102,7 +106,7 @@ public class UnitTestLaunchConfigConfigurationTest extends AbstractMavenProjectT """; // Define the parameters to be used in the test - @Parameters + @Parameters(name = "{0}") public static Collection data() { return List.of(MavenRuntimeClasspathProvider.JDT_TESTNG_TEST, MavenRuntimeClasspathProvider.JDT_JUNIT_TEST); } @@ -364,6 +368,64 @@ public void test_deferred_variable_are_resolved() throws CoreException, IOExcept assertFalse(argLine.contains("@{titi.tata}")); // unresolved property is removed } + @Test + public void testJUnit4TestWithJUnit5Dependency() throws CoreException, IOException, InterruptedException { + assumeTrue("Only relevant for JUnit", MavenRuntimeClasspathProvider.JDT_JUNIT_TEST.equals(testType)); + + // Get launch type + ILaunchConfigurationType type = LAUNCH_MANAGER.getLaunchConfigurationType(testType); + + assumeTrue(testType + " support not available", type != null); + + File pomFile = getTestFile("junit5TestProject/pom.xml"); + + IProject project = importProject(pomFile.getAbsolutePath()); + + // create basic unit test + createDefaultTest(project, type, "testMvn.JUnit4Test"); + + updateProject(project); + waitForJobsToComplete(); + + ILaunchConfiguration[] updatedConfigurations = LAUNCH_MANAGER.getLaunchConfigurations(type); + assertTrue(updatedConfigurations.length == 1); + + ILaunchConfiguration config = updatedConfigurations[0]; + MavenRuntimeClasspathProvider provider = new MavenRuntimeClasspathProvider(); + IRuntimeClasspathEntry[] classpathEntries = provider.computeUnresolvedClasspath(config); // to trigger classpath + // computation + IRuntimeClasspathEntry[] resolvedClasspathEntries = provider.resolveClasspath(classpathEntries, config); + // make sure that vintage engine is on the classpath (as being part of the + // m-surefire-p classpath) + assertThat(Arrays.asList(resolvedClasspathEntries), + Matchers.hasItem(new IRuntimeClasspathEntryMatcherByFilename("junit-vintage-engine-5.14.1.jar"))); + } + + static final class IRuntimeClasspathEntryMatcherByFilename extends TypeSafeMatcher { + + private final String filename; + + public IRuntimeClasspathEntryMatcherByFilename(String filename) { + this.filename = filename; + } + + @Override + public void describeTo(Description description) { + description.appendText("IRuntimeClasspathEntry with location ending with ").appendValue(filename); + } + + @Override + protected void describeMismatchSafely(IRuntimeClasspathEntry item, Description mismatchDescription) { + mismatchDescription.appendText("was IRuntimeClasspathEntry ").appendValue(item) + .appendText(" with location ").appendValue(item.getLocation()); + } + + @Override + protected boolean matchesSafely(IRuntimeClasspathEntry item) { + return item.getLocation() != null && item.getLocation().endsWith(filename); + } + } + private void updateProject(IProject project) throws CoreException, InterruptedException { MavenPlugin.getProjectConfigurationManager().updateProjectConfiguration(project, monitor); waitForJobsToComplete(); @@ -402,4 +464,5 @@ private void mergePomAndPluginConfigIntoProject(IProject project, File pomTempla private File getTestFile(String filename) throws IOException { return new File(FileLocator.toFileURL(getClass().getResource(ROOT_PATH + "/" + filename)).getFile()); } + } From f745fff5db15993899ab12c0d913f38d220b10eb Mon Sep 17 00:00:00 2001 From: Konrad Windszus Date: Thu, 15 Jan 2026 13:39:17 +0100 Subject: [PATCH 3/4] Move tests to dedicated test repo --- m2e-core-tests | 2 +- .../junit5TestProject/pom.xml | 47 -------------- .../src/test/java/testMvn/TestClass.java | 15 ----- ...UnitTestLaunchConfigConfigurationTest.java | 65 +------------------ 4 files changed, 2 insertions(+), 127 deletions(-) delete mode 100644 org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/pom.xml delete mode 100644 org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/src/test/java/testMvn/TestClass.java diff --git a/m2e-core-tests b/m2e-core-tests index 900e448b47..daaf053338 160000 --- a/m2e-core-tests +++ b/m2e-core-tests @@ -1 +1 @@ -Subproject commit 900e448b47f3bc6786faef1966bdaf6641cf4b77 +Subproject commit daaf05333838d21c6fe071c0c69ab2ce51db90ec diff --git a/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/pom.xml b/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/pom.xml deleted file mode 100644 index 5794af4f0d..0000000000 --- a/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - 4.0.0 - - foo.bar - 0.0.1-SNAPSHOT - project.a - - - 3.0.0 - 5.14.1 - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${version.surefire} - - - - org.junit.vintage - junit-vintage-engine - ${version.junit.jupiter} - - - - - - - - - org.junit.jupiter - junit-jupiter-api - ${version.junit.jupiter} - test - - - junit - junit - 4.13.2 - test - - - \ No newline at end of file diff --git a/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/src/test/java/testMvn/TestClass.java b/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/src/test/java/testMvn/TestClass.java deleted file mode 100644 index d603b9ba19..0000000000 --- a/org.eclipse.m2e.jdt.tests/projects/surefireFailsafeToTestLaunchSettings/junit5TestProject/src/test/java/testMvn/TestClass.java +++ /dev/null @@ -1,15 +0,0 @@ -package testMvn; - -import java.io.StringWriter; - -import org.junit.Test; -import static org.junit.Assert.assertTrue; - -public class JUnit4Test { - - @Test - public void test1() { - assertTrue(true); - } - -} diff --git a/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/UnitTestLaunchConfigConfigurationTest.java b/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/UnitTestLaunchConfigConfigurationTest.java index 26f44c8a8f..ca36bcbf88 100644 --- a/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/UnitTestLaunchConfigConfigurationTest.java +++ b/org.eclipse.m2e.jdt.tests/src/org/eclipse/m2e/jdt/tests/UnitTestLaunchConfigConfigurationTest.java @@ -20,7 +20,6 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; @@ -36,15 +35,12 @@ import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; -import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.m2e.core.MavenPlugin; import org.eclipse.m2e.core.internal.preferences.MavenConfigurationImpl; import org.eclipse.m2e.jdt.internal.UnitTestSupport; import org.eclipse.m2e.jdt.internal.launch.MavenRuntimeClasspathProvider; import org.eclipse.m2e.tests.common.AbstractMavenProjectTestCase; -import org.hamcrest.Description; import org.hamcrest.Matchers; -import org.hamcrest.TypeSafeMatcher; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,7 +55,7 @@ public class UnitTestLaunchConfigConfigurationTest extends AbstractMavenProjectT private static final String REPLACED_SUREFIRE_POM_STRING = ""; private static final String REPLACED_FAILSAFE_POM_STRING = ""; private static final String ROOT_PATH = "/projects/surefireFailsafeToTestLaunchSettings"; - private static final ILaunchManager LAUNCH_MANAGER = DebugPlugin.getDefault().getLaunchManager(); + private static ILaunchManager LAUNCH_MANAGER = DebugPlugin.getDefault().getLaunchManager(); /* * XML allows encoding set of control characters: space (U+0020), carriage @@ -368,64 +364,6 @@ public void test_deferred_variable_are_resolved() throws CoreException, IOExcept assertFalse(argLine.contains("@{titi.tata}")); // unresolved property is removed } - @Test - public void testJUnit4TestWithJUnit5Dependency() throws CoreException, IOException, InterruptedException { - assumeTrue("Only relevant for JUnit", MavenRuntimeClasspathProvider.JDT_JUNIT_TEST.equals(testType)); - - // Get launch type - ILaunchConfigurationType type = LAUNCH_MANAGER.getLaunchConfigurationType(testType); - - assumeTrue(testType + " support not available", type != null); - - File pomFile = getTestFile("junit5TestProject/pom.xml"); - - IProject project = importProject(pomFile.getAbsolutePath()); - - // create basic unit test - createDefaultTest(project, type, "testMvn.JUnit4Test"); - - updateProject(project); - waitForJobsToComplete(); - - ILaunchConfiguration[] updatedConfigurations = LAUNCH_MANAGER.getLaunchConfigurations(type); - assertTrue(updatedConfigurations.length == 1); - - ILaunchConfiguration config = updatedConfigurations[0]; - MavenRuntimeClasspathProvider provider = new MavenRuntimeClasspathProvider(); - IRuntimeClasspathEntry[] classpathEntries = provider.computeUnresolvedClasspath(config); // to trigger classpath - // computation - IRuntimeClasspathEntry[] resolvedClasspathEntries = provider.resolveClasspath(classpathEntries, config); - // make sure that vintage engine is on the classpath (as being part of the - // m-surefire-p classpath) - assertThat(Arrays.asList(resolvedClasspathEntries), - Matchers.hasItem(new IRuntimeClasspathEntryMatcherByFilename("junit-vintage-engine-5.14.1.jar"))); - } - - static final class IRuntimeClasspathEntryMatcherByFilename extends TypeSafeMatcher { - - private final String filename; - - public IRuntimeClasspathEntryMatcherByFilename(String filename) { - this.filename = filename; - } - - @Override - public void describeTo(Description description) { - description.appendText("IRuntimeClasspathEntry with location ending with ").appendValue(filename); - } - - @Override - protected void describeMismatchSafely(IRuntimeClasspathEntry item, Description mismatchDescription) { - mismatchDescription.appendText("was IRuntimeClasspathEntry ").appendValue(item) - .appendText(" with location ").appendValue(item.getLocation()); - } - - @Override - protected boolean matchesSafely(IRuntimeClasspathEntry item) { - return item.getLocation() != null && item.getLocation().endsWith(filename); - } - } - private void updateProject(IProject project) throws CoreException, InterruptedException { MavenPlugin.getProjectConfigurationManager().updateProjectConfiguration(project, monitor); waitForJobsToComplete(); @@ -464,5 +402,4 @@ private void mergePomAndPluginConfigIntoProject(IProject project, File pomTempla private File getTestFile(String filename) throws IOException { return new File(FileLocator.toFileURL(getClass().getResource(ROOT_PATH + "/" + filename)).getFile()); } - } From f3adca83577d450f7f5ed2fee2a615e0c80bfded Mon Sep 17 00:00:00 2001 From: Konrad Windszus Date: Thu, 15 Jan 2026 14:10:50 +0100 Subject: [PATCH 4/4] temporarily increase log level to debug CI test failures --- .../jdt/internal/launch/MavenRuntimeClasspathProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java index 57d013a03b..7f3a5cd0f8 100644 --- a/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java +++ b/org.eclipse.m2e.jdt/src/org/eclipse/m2e/jdt/internal/launch/MavenRuntimeClasspathProvider.java @@ -289,12 +289,12 @@ private void addMavenSurefirePluginProviderDependencies(Set