From 41ea88930ee670f624e809a1bfb9d9872984f9cb Mon Sep 17 00:00:00 2001 From: David Thompson Date: Thu, 4 Sep 2025 16:57:17 -0400 Subject: [PATCH] [WIP] Allow using `.launch` files for JUnit Plugin Tests - Take into account Execution Environment when specified; use the requested `java` installation Signed-off-by: David Thompson --- .../JUnitLaunchConfigurationDelegate.java | 7 +- .../internal/JunitLaunchConfiguration.java | 6 +- .../internal/PDEDelegateCommandHandler.java | 67 ++++++++++++++++--- src/extension.ts | 4 +- 4 files changed, 69 insertions(+), 15 deletions(-) diff --git a/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/JUnitLaunchConfigurationDelegate.java b/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/JUnitLaunchConfigurationDelegate.java index dabde0b..4d91232 100644 --- a/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/JUnitLaunchConfigurationDelegate.java +++ b/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/JUnitLaunchConfigurationDelegate.java @@ -21,6 +21,7 @@ import java.util.Map; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.URIUtil; @@ -44,7 +45,9 @@ public class JUnitLaunchConfigurationDelegate extends org.eclipse.pde.launching. public JUnitLaunchArguments getJUnitLaunchArguments(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { loadInstalledBundlesToPDECache(); ILaunch launch = new Launch(configuration, mode, null); - showCommandLine(configuration, mode, launch, monitor); + String commandLine = showCommandLine(configuration, mode, launch, monitor); + ILog.get().error(commandLine); + String javaExec = commandLine.split("\\s+")[0]; String mainTypeName = verifyMainTypeName(configuration); @@ -100,6 +103,7 @@ public JUnitLaunchArguments getJUnitLaunchArguments(ILaunchConfiguration configu launchArguments.programArguments = programArguments.toArray(new String[programArguments.size()]); launchArguments.environment = EclipseApplicationLaunchConfiguration.getEnvironmentVariable(configuration); launchArguments.port = launch.getAttribute(JUnitLaunchConfigurationConstants.ATTR_PORT); + launchArguments.javaExec = javaExec; return launchArguments; } @@ -174,5 +178,6 @@ public static class JUnitLaunchArguments { String[] programArguments; Map environment; String port; + String javaExec; } } diff --git a/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/JunitLaunchConfiguration.java b/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/JunitLaunchConfiguration.java index 914489d..708bb44 100644 --- a/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/JunitLaunchConfiguration.java +++ b/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/JunitLaunchConfiguration.java @@ -72,6 +72,7 @@ class TestInfo { public String testProject = ""; public String jreContainer = "org.eclipse.jdt.launching.JRE_CONTAINER"; public String testBundle = ""; + public String vmArgs = ""; public boolean useUIThread = false; public Map toValueMap() { @@ -84,10 +85,11 @@ public Map toValueMap() { valueMap.put("testProject", testProject); valueMap.put("testBundle", testBundle); valueMap.put("useUIThread", String.valueOf(useUIThread)); + if (Platform.OS_MACOSX.equals(Platform.getOS())) { - valueMap.put("vmArgs", "-XstartOnFirstThread"); + valueMap.put("vmArgs", "-XstartOnFirstThread " + vmArgs); } else { - valueMap.put("vmArgs", ""); + valueMap.put("vmArgs", vmArgs); } return valueMap; } diff --git a/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/PDEDelegateCommandHandler.java b/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/PDEDelegateCommandHandler.java index f44ef54..8f8b340 100644 --- a/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/PDEDelegateCommandHandler.java +++ b/pde/org.eclipse.jdt.ls.importer.pde/src/org/eclipse/jdt/ls/importer/pde/internal/PDEDelegateCommandHandler.java @@ -17,10 +17,13 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; @@ -28,19 +31,23 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.Launch; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.launching.IVMInstall; +import org.eclipse.jdt.launching.IVMInstallType; import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; @@ -57,6 +64,7 @@ public class PDEDelegateCommandHandler implements IDelegateCommandHandler { public static final String RESOLVE_LAUNCH_ARGUMENTS = "java.pde.resolveLaunchArguments"; public static final String RELOAD_TARGET_PLATFORM = "java.pde.reloadTargetPlatform"; public static final String RESOLVE_JUNIT_ARGUMENTS = "java.pde.resolveJUnitArguments"; + public static final String JUNIT_LAUNCH_CONFIG = "org.eclipse.pde.ui.JunitLaunchConfig"; @Override public Object executeCommand(String commandId, List arguments, IProgressMonitor monitor) throws Exception { @@ -92,19 +100,51 @@ private static Object resolveLaunchArguments(String launchFileUri) throws Except String fileName = getSimpleName(file); ILaunchConfiguration configuration = new PDELaunchConfiguration(fileName, file); String configType = configuration.getType().getIdentifier(); - if (!configType.equals(IPDELauncherConstants.ECLIPSE_APPLICATION_LAUNCH_CONFIGURATION_TYPE)) { + + if (configType.equals(JUNIT_LAUNCH_CONFIG)) { + TestInfo testInfo = new TestInfo(); + testInfo.testKind = "org.eclipse.jdt.junit.loader.junit4"; + + List classesUnderTest = configuration.getAttribute("org.eclipse.debug.core.MAPPED_RESOURCE_PATHS", Collections.emptyList()); + if (classesUnderTest.isEmpty()) { + throw new IllegalArgumentException("Expected a test class or suite to test"); + } + testInfo.testMainType = configuration.getAttribute("org.eclipse.jdt.launching.MAIN_TYPE", ""); + testInfo.testProject = configuration.getAttribute("org.eclipse.jdt.launching.PROJECT_ATTR", ""); + testInfo.jreContainer = configuration.getAttribute("org.eclipse.jdt.launching.JRE_CONTAINER", ""); + testInfo.vmArgs = configuration.getAttribute("org.eclipse.jdt.launching.VM_ARGUMENTS", ""); + testInfo.testName = configuration.getAttribute("org.eclipse.jdt.junit.TESTNAME", ""); + testInfo.testBundle = testInfo.testProject; + testInfo.useUIThread = configuration.getAttribute("run_in_ui_thread", true); + JunitLaunchConfiguration launchConfiguration = new JunitLaunchConfiguration(configType, testInfo); + JUnitLaunchConfigurationDelegate delegate = new JUnitLaunchConfigurationDelegate(); + return delegate.getJUnitLaunchArguments(launchConfiguration, "run", new NullProgressMonitor()); + } else if (configType.equals(IPDELauncherConstants.ECLIPSE_APPLICATION_LAUNCH_CONFIGURATION_TYPE)) { + EclipseApplicationLaunchConfiguration pdeLaunchConfiguration = new EclipseApplicationLaunchConfiguration(); + pdeLaunchConfiguration.preLaunchCheck(configuration, (ILaunch) null, new NullProgressMonitor()); + LaunchArguments launchArguments = new LaunchArguments(); + launchArguments.setVmArguments(pdeLaunchConfiguration.getVMArguments(configuration)); + launchArguments.setProgramArguments(pdeLaunchConfiguration.getProgramArguments(configuration)); + launchArguments.setEnvironment(pdeLaunchConfiguration.getEnvironmentVariable(configuration)); + launchArguments.setClasspath(pdeLaunchConfiguration.getClasspath(configuration)); + launchArguments.setWorkspaceLocation(pdeLaunchConfiguration.getWorkspaceLocation()); + + String jreContainer = configuration.getAttribute("org.eclipse.jdt.launching.JRE_CONTAINER", "org.eclipse.jdt.launching.JRE_CONTAINER"); + if (!jreContainer.isEmpty()) { + String[] segments = jreContainer.split("/"); + if (segments.length > 2) { + IExecutionEnvironment ee = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment(segments[2]); + if (ee != null && ee.getDefaultVM() != null) { + launchArguments.setJavaExec(ee.getDefaultVM().getInstallLocation().toPath().resolve("bin").resolve("java").toAbsolutePath().toString()); + } + } + } + + return launchArguments; + } else { throw new UnsupportedOperationException(String.format("PDE plugin doesn't support the launch type '%s'.", configType)); } - - EclipseApplicationLaunchConfiguration pdeLaunchConfiguration = new EclipseApplicationLaunchConfiguration(); - pdeLaunchConfiguration.preLaunchCheck(configuration, (ILaunch) null, new NullProgressMonitor()); - LaunchArguments launchArguments = new LaunchArguments(); - launchArguments.setVmArguments(pdeLaunchConfiguration.getVMArguments(configuration)); - launchArguments.setProgramArguments(pdeLaunchConfiguration.getProgramArguments(configuration)); - launchArguments.setEnvironment(pdeLaunchConfiguration.getEnvironmentVariable(configuration)); - launchArguments.setClasspath(pdeLaunchConfiguration.getClasspath(configuration)); - launchArguments.setWorkspaceLocation(pdeLaunchConfiguration.getWorkspaceLocation()); - return launchArguments; + } catch (URISyntaxException | CoreException e) { throw new Exception("Failed to parse the launch configuration", e); } @@ -285,6 +325,7 @@ private static class LaunchArguments { Map environment; String workspaceLocation; String[] classpath; + String javaExec; public LaunchArguments() { } @@ -308,5 +349,9 @@ public void setWorkspaceLocation(String workspaceLocation) { public void setClasspath(String[] classpath) { this.classpath = classpath; } + + public void setJavaExec(String javaExec) { + this.javaExec = javaExec; + } } } diff --git a/src/extension.ts b/src/extension.ts index 2a91320..1120284 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -127,7 +127,8 @@ async function launchPDEApplication(context: vscode.ExtensionContext, uri: vscod classPaths: launchArguments.classpath, args: launchArguments.programArguments, vmArgs: launchArguments.vmArguments, - env: launchArguments.environment + env: launchArguments.environment, + javaExec: launchArguments.javaExec, }; await persistLaunchConfig(launchConfiguration, workspaceFolder.uri); @@ -290,6 +291,7 @@ interface LaunchArguments { environment; workspaceLocation: string; classpath: string[]; + javaExec: string; } interface JUnitLaunchArguments {