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
10 changes: 10 additions & 0 deletions org.eclipse.jdt.debug.tests/multirelease/src/p/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package p;

public class Main {

public static void main(String[] args) {
System.out.println("Java: "+java.lang.Runtime.version().feature());
new X().greet();
}

}
7 changes: 7 additions & 0 deletions org.eclipse.jdt.debug.tests/multirelease/src/p/X.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package p;

public class X {
public void greet() {
System.out.println("X: 11");
}
}
7 changes: 7 additions & 0 deletions org.eclipse.jdt.debug.tests/multirelease/src/p/Y.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package p;

public class Y {
public void y() {
System.out.println("Y: 11");
}
}
8 changes: 8 additions & 0 deletions org.eclipse.jdt.debug.tests/multirelease/src17/p/X.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package p;

public class X {
public void greet() {
System.out.println("X: 17");
new Y().y();
}
}
7 changes: 7 additions & 0 deletions org.eclipse.jdt.debug.tests/multirelease/src17/p/Z.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package p;

public class Z {
public static void z() {
System.out.println("Z: 17");
}
}
8 changes: 8 additions & 0 deletions org.eclipse.jdt.debug.tests/multirelease/src21/p/Y.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package p;

public class Y {
public void y() {
System.out.println("Y: 21");
Z.z();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
Expand Down Expand Up @@ -111,6 +112,11 @@ public class JavaProjectHelper {
*/
public static final IPath TEST_25_SRC_DIR = new Path("java25");

/**
* path to the multirelease test source
*/
public static final IPath TEST_MR_SRC_DIR = new Path("multirelease");

/**
* path to the compiler error java file
*/
Expand Down Expand Up @@ -323,9 +329,12 @@ public static void delete(IJavaProject jproject) throws CoreException {

/**
* Adds a new source container specified by the container name to the source path of the specified project
*
* @param extra
* optional extra classpath attributes
* @return the package fragment root of the container name
*/
public static IPackageFragmentRoot addSourceContainer(IJavaProject jproject, String containerName) throws CoreException {
public static IPackageFragmentRoot addSourceContainer(IJavaProject jproject, String containerName, IClasspathAttribute... extra) throws CoreException {
IProject project= jproject.getProject();
IContainer container= null;
if (containerName == null || containerName.length() == 0) {
Expand All @@ -339,7 +348,7 @@ public static IPackageFragmentRoot addSourceContainer(IJavaProject jproject, Str
}
IPackageFragmentRoot root= jproject.getPackageFragmentRoot(container);

IClasspathEntry cpe= JavaCore.newSourceEntry(root.getPath());
IClasspathEntry cpe = JavaCore.newSourceEntry(root.getPath(), ClasspathEntry.INCLUDE_ALL, ClasspathEntry.EXCLUDE_NONE, null, extra);
addToClasspath(jproject, cpe);
return root;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ public abstract class AbstractDebugTest extends TestCase implements IEvaluation
public static final String TWENTYFOUR_PROJECT_NAME = "Two_Four";
public static final String TWENTYFIVE_PROJECT_NAME = "Two_Five";
public static final String BOUND_JRE_PROJECT_NAME = "BoundJRE";
public static final String MR_PROJECT_NAME = "MR";
public static final String CLONE_SUFFIX = "Clone";

final String[] LAUNCH_CONFIG_NAMES_1_4 = { "LargeSourceFile", "LotsOfFields",
Expand Down Expand Up @@ -248,6 +249,7 @@ public abstract class AbstractDebugTest extends TestCase implements IEvaluation
private static boolean loadedEE = false;
private static boolean loadedJRE = false;
private static boolean loadedMulti = false;
private static boolean loadedMR;
private static boolean welcomeClosed = false;

/**
Expand Down Expand Up @@ -285,6 +287,8 @@ protected void setUp() throws Exception {
loadedEE = pro.exists();
pro = ResourcesPlugin.getWorkspace().getRoot().getProject(MULTI_OUTPUT_PROJECT_NAME);
loadedMulti = pro.exists();
pro = ResourcesPlugin.getWorkspace().getRoot().getProject(MR_PROJECT_NAME);
loadedMR = pro.exists();
assertWelcomeScreenClosed();
}

Expand Down Expand Up @@ -626,6 +630,40 @@ synchronized void assert21Project() {
}
}

synchronized void assertMRProject() {
IJavaProject jp = null;
ArrayList<ILaunchConfiguration> cfgs = new ArrayList<>(1);
try {
if (!loadedMR) {
jp = createProject(MR_PROJECT_NAME, JavaProjectHelper.TEST_MR_SRC_DIR.toString(), JavaProjectHelper.JAVA_SE_21_EE_NAME, false);
jp.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED);
jp.setOption(JavaCore.COMPILER_COMPLIANCE, "11");
jp.setOption(JavaCore.COMPILER_SOURCE, "11");
jp.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, "11");
IPackageFragmentRoot src17 = JavaProjectHelper.addSourceContainer(jp, "src17", JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "17"));
IPackageFragmentRoot src21 = JavaProjectHelper.addSourceContainer(jp, "src21", JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "21"));
File root = JavaTestPlugin.getDefault().getFileInPlugin(JavaProjectHelper.TEST_MR_SRC_DIR);
JavaProjectHelper.importFilesFromDirectory(new File(root, src17.getPath().lastSegment()), src17.getPath(), null);
JavaProjectHelper.importFilesFromDirectory(new File(root, src21.getPath().lastSegment()), src21.getPath(), null);
cfgs.add(createLaunchConfiguration(jp, "p.Main"));
loadedMR = true;
waitForBuild();
}
} catch (Exception e) {
try {
if (jp != null) {
jp.getProject().delete(true, true, null);
for (int i = 0; i < cfgs.size(); i++) {
cfgs.get(i).delete();
}
}
} catch (CoreException ce) {
// ignore
}
handleProjectCreationException(e, MR_PROJECT_NAME, jp);
}
}

synchronized void assert23Project() {
IJavaProject jp = null;
ArrayList<ILaunchConfiguration> cfgs = new ArrayList<>(1);
Expand Down Expand Up @@ -1017,6 +1055,16 @@ protected IJavaProject get21Project() {
return getJavaProject(TWENTYONE_PROJECT_NAME);
}

/**
* Returns the 'multirelease' project, used for Multirelease tests.
*
* @return the test project
*/
protected IJavaProject getMultireleaseProject() {
assertMRProject();
return getJavaProject(MR_PROJECT_NAME);
}

/**
* Returns the 'Two_Three' project, used for Java 23 tests.
*
Expand Down Expand Up @@ -1117,7 +1165,12 @@ protected IJavaProject createProject(String name, String contentpath, String ee,
IJavaProject jp = JavaProjectHelper.createJavaProject(name, JavaProjectHelper.BIN_DIR);
IPackageFragmentRoot src = JavaProjectHelper.addSourceContainer(jp, JavaProjectHelper.SRC_DIR);
File root = JavaTestPlugin.getDefault().getFileInPlugin(new Path(contentpath));
JavaProjectHelper.importFilesFromDirectory(root, src.getPath(), null);
File srcInRoot = new File(root, src.getPath().lastSegment());
if (srcInRoot.isDirectory()) {
JavaProjectHelper.importFilesFromDirectory(srcInRoot, src.getPath(), null);
} else {
JavaProjectHelper.importFilesFromDirectory(root, src.getPath(), null);
}

// add the EE library
IVMInstall vm = JavaRuntime.getDefaultVMInstall();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
import org.eclipse.jdt.debug.tests.core.LiteralTests17;
import org.eclipse.jdt.debug.tests.core.LocalVariableTests;
import org.eclipse.jdt.debug.tests.core.ModuleOptionsTests;
import org.eclipse.jdt.debug.tests.core.MultiReleaseLaunchTests;
import org.eclipse.jdt.debug.tests.core.ProcessTests;
import org.eclipse.jdt.debug.tests.core.ResolveRuntimeClasspathTests;
import org.eclipse.jdt.debug.tests.core.RuntimeClasspathEntryTests;
Expand Down Expand Up @@ -291,6 +292,7 @@ public AutomatedSuite() {
addTest(new TestSuite(WorkingDirectoryTests.class));
addTest(new TestSuite(EventDispatcherTest.class));
addTest(new TestSuite(SyntheticVariableTests.class));
addTest(new TestSuite(MultiReleaseLaunchTests.class));

// Refactoring tests
//TODO: project rename
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*******************************************************************************
* Copyright (c) 2025 Christoph Läubrich and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.debug.tests.core;

import java.io.File;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;

import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
import org.eclipse.jdt.debug.tests.ui.AbstractDebugUiTests;
import org.eclipse.jdt.internal.launching.DetectVMInstallationsJob;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstall2;
import org.eclipse.jdt.launching.IVMInstallType;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.VMStandin;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.TextConsole;

/**
* <b>IMPORTANT</b> This test requires some different JVM installs to be present (see {@link #JAVA_11}, {@link #JAVA_17}, {@link #JAVA_21})) if such
* JVMs can not be found, the test will fail! One can specify a basedir to search for such jvms with the {@link #JVM_SEARCH_BASE} system property.
*/
public class MultiReleaseLaunchTests extends AbstractDebugUiTests {

private static final String JVM_SEARCH_BASE = "MultiReleaseLaunchTests.rootDir";
private static final RequiredJavaVersion JAVA_11 = new RequiredJavaVersion(11, 16);
private static final RequiredJavaVersion JAVA_17 = new RequiredJavaVersion(17, 20);
private static final RequiredJavaVersion JAVA_21 = new RequiredJavaVersion(21, Integer.MAX_VALUE);

private List<Runnable> disposeVms = new ArrayList<>();

public MultiReleaseLaunchTests(String name) {
super(name);
}

@Override
protected void setUp() throws Exception {
super.setUp();
final Set<File> existingLocations = new HashSet<>();
List<RequiredJavaVersion> requiredJavaVersions = new ArrayList<>(List.of(JAVA_11, JAVA_17, JAVA_21));
removeExistingJavaVersions(requiredJavaVersions, existingLocations);
if (!requiredJavaVersions.isEmpty()) {
final File rootDir = new File(System.getProperty(JVM_SEARCH_BASE, "/opt/tools/java/openjdk/"));
final List<File> locations = new ArrayList<>();
final List<IVMInstallType> types = new ArrayList<>();
DetectVMInstallationsJob.search(rootDir, locations, types, existingLocations, new NullProgressMonitor());
for (int i = 0; i < locations.size(); i++) {
File location = locations.get(i);
IVMInstallType type = types.get(i);
String id = "MultiReleaseLaunchTests-" + UUID.randomUUID() + "-" + i;
VMStandin workingCopy = new VMStandin(type, id);
workingCopy.setInstallLocation(location);
workingCopy.setName(id);
IVMInstall install = workingCopy.convertToRealVM();
if (removeIfMatch(requiredJavaVersions, install)) {
disposeVms.add(() -> type.disposeVMInstall(id));
} else {
type.disposeVMInstall(id);
}
}
}
assertTrue("The following java versions are required by this test but can not be found: "
+ requiredJavaVersions, requiredJavaVersions.isEmpty());
}

@Override
protected void tearDown() throws Exception {
super.tearDown();
disposeVms.forEach(Runnable::run);
}

@Override
protected IJavaProject getProjectContext() {
return getMultireleaseProject();
}

public void testMultiReleaseLaunch() throws Exception {
ILaunchConfiguration config = getLaunchConfiguration("p.Main");
Properties result = launchAndReadResult(config, 11);
assertTrue("Was not launched with a proper Java installation " + result, JAVA_11.matches(result.getProperty("Java")));
assertEquals("X should be executed from Java 11 version: " + result, "11", result.get("X"));
assertNull("Y should not be executed from Java 11 version: " + result, result.get("Y"));
assertNull("Z should not be executed from Java 11 version: " + result, result.get("Z"));
Properties result17 = launchAndReadResult(config, 17);
assertTrue("Was not launched with a proper Java installation " + result17, JAVA_17.matches(result17.getProperty("Java")));
assertEquals("X should be executed from Java 17 version: " + result17, "17", result17.get("X"));
assertEquals("Y should be executed from Java 11 version: " + result17, "11", result17.get("Y"));
assertNull("Z should not be executed from Java 17 version: " + result17, result17.get("Z"));
Properties result21 = launchAndReadResult(config, 21);
assertTrue("Was not launched with a proper Java installation " + result21, JAVA_21.matches(result21.getProperty("Java")));
assertEquals("X should be executed from Java 17 version: " + result21, "17", result21.get("X"));
assertEquals("Y should be executed from Java 21 version: " + result21, "21", result21.get("Y"));
assertEquals("Z should be executed from Java 17 version: " + result21, "17", result21.get("Z"));
}

private Properties launchAndReadResult(ILaunchConfiguration config, int javaVersion) throws Exception {
ILaunchConfigurationWorkingCopy workingCopy = config.getWorkingCopy();
workingCopy.setAttribute("org.eclipse.jdt.launching.JRE_CONTAINER", "org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-"
+ javaVersion + "/");
Properties properties = new Properties();
IJavaDebugTarget target = launchAndTerminate(workingCopy.doSave(), DEFAULT_TIMEOUT);
processUiEvents();
final IConsole console = DebugUITools.getConsole(target.getProcess());
final TextConsole textConsole = (TextConsole) console;
final IDocument consoleDocument = textConsole.getDocument();
String content = consoleDocument.get();
properties.load(new StringReader(content));
DebugPlugin.getDefault().getLaunchManager().removeLaunch(target.getLaunch());
return properties;
}

private static int getJavaVersion(IVMInstall install) {
if (install instanceof IVMInstall2 vm) {
try {
String javaVersion = vm.getJavaVersion().split("\\.")[0]; //$NON-NLS-1$
return Integer.parseInt(javaVersion);
} catch (RuntimeException rte) {
// can't know then...
}
}
return -1;
}

private static void removeExistingJavaVersions(Collection<RequiredJavaVersion> requiredJavaVersions, Set<File> existingLocations) {
IVMInstallType[] installTypes = JavaRuntime.getVMInstallTypes();
for (IVMInstallType installType : installTypes) {
IVMInstall[] vmInstalls = installType.getVMInstalls();
for (IVMInstall install : vmInstalls) {
if (requiredJavaVersions.isEmpty()) {
return;
}
existingLocations.add(install.getInstallLocation());
removeIfMatch(requiredJavaVersions, install);
}
}
}

protected static boolean removeIfMatch(Collection<RequiredJavaVersion> requiredJavaVersions, IVMInstall install) {
int javaVersion = getJavaVersion(install);
for (Iterator<RequiredJavaVersion> iterator = requiredJavaVersions.iterator(); iterator.hasNext();) {
if (iterator.next().matches(javaVersion)) {
iterator.remove();
return true;
}
}
return false;
}

private static record RequiredJavaVersion(int from, int to) {

public boolean matches(int version) {
return (version >= from() && version <= to());
}

public boolean matches(String v) {
return matches(Integer.parseInt(v));
}
}

}
Loading
Loading