|
| 1 | +/******************************************************************************* |
| 2 | + * Copyright (c) 2025 Christoph Läubrich 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 | + * Christoph Läubrich - initial API and implementation |
| 13 | + *******************************************************************************/ |
| 14 | +package org.eclipse.jdt.debug.tests.core; |
| 15 | + |
| 16 | +import java.io.File; |
| 17 | +import java.io.StringReader; |
| 18 | +import java.util.ArrayList; |
| 19 | +import java.util.Collection; |
| 20 | +import java.util.HashSet; |
| 21 | +import java.util.Iterator; |
| 22 | +import java.util.List; |
| 23 | +import java.util.Properties; |
| 24 | +import java.util.Set; |
| 25 | +import java.util.UUID; |
| 26 | + |
| 27 | +import org.eclipse.core.runtime.NullProgressMonitor; |
| 28 | +import org.eclipse.debug.core.DebugPlugin; |
| 29 | +import org.eclipse.debug.core.ILaunchConfiguration; |
| 30 | +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| 31 | +import org.eclipse.debug.ui.DebugUITools; |
| 32 | +import org.eclipse.jdt.core.IJavaProject; |
| 33 | +import org.eclipse.jdt.debug.core.IJavaDebugTarget; |
| 34 | +import org.eclipse.jdt.debug.tests.ui.AbstractDebugUiTests; |
| 35 | +import org.eclipse.jdt.internal.launching.DetectVMInstallationsJob; |
| 36 | +import org.eclipse.jdt.launching.IVMInstall; |
| 37 | +import org.eclipse.jdt.launching.IVMInstall2; |
| 38 | +import org.eclipse.jdt.launching.IVMInstallType; |
| 39 | +import org.eclipse.jdt.launching.JavaRuntime; |
| 40 | +import org.eclipse.jdt.launching.VMStandin; |
| 41 | +import org.eclipse.jface.text.IDocument; |
| 42 | +import org.eclipse.ui.console.IConsole; |
| 43 | +import org.eclipse.ui.console.TextConsole; |
| 44 | + |
| 45 | +/** |
| 46 | + * <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 |
| 47 | + * 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. |
| 48 | + */ |
| 49 | +public class MultiReleaseLaunchTests extends AbstractDebugUiTests { |
| 50 | + |
| 51 | + private static final String JVM_SEARCH_BASE = "MultiReleaseLaunchTests.rootDir"; |
| 52 | + private static final RequiredJavaVersion JAVA_11 = new RequiredJavaVersion(11, 16); |
| 53 | + private static final RequiredJavaVersion JAVA_17 = new RequiredJavaVersion(17, 20); |
| 54 | + private static final RequiredJavaVersion JAVA_21 = new RequiredJavaVersion(21, Integer.MAX_VALUE); |
| 55 | + |
| 56 | + private List<Runnable> disposeVms = new ArrayList<>(); |
| 57 | + |
| 58 | + public MultiReleaseLaunchTests(String name) { |
| 59 | + super(name); |
| 60 | + } |
| 61 | + |
| 62 | + @Override |
| 63 | + protected void setUp() throws Exception { |
| 64 | + super.setUp(); |
| 65 | + final Set<File> existingLocations = new HashSet<>(); |
| 66 | + List<RequiredJavaVersion> requiredJavaVersions = new ArrayList<>(List.of(JAVA_11, JAVA_17, JAVA_21)); |
| 67 | + removeExistingJavaVersions(requiredJavaVersions, existingLocations); |
| 68 | + if (!requiredJavaVersions.isEmpty()) { |
| 69 | + final File rootDir = new File(System.getProperty(JVM_SEARCH_BASE, "/opt/tools/java/openjdk/")); |
| 70 | + final List<File> locations = new ArrayList<>(); |
| 71 | + final List<IVMInstallType> types = new ArrayList<>(); |
| 72 | + DetectVMInstallationsJob.search(rootDir, locations, types, existingLocations, new NullProgressMonitor()); |
| 73 | + for (int i = 0; i < locations.size(); i++) { |
| 74 | + File location = locations.get(i); |
| 75 | + IVMInstallType type = types.get(i); |
| 76 | + String id = "MultiReleaseLaunchTests-" + UUID.randomUUID() + "-" + i; |
| 77 | + VMStandin workingCopy = new VMStandin(type, id); |
| 78 | + workingCopy.setInstallLocation(location); |
| 79 | + workingCopy.setName(id); |
| 80 | + IVMInstall install = workingCopy.convertToRealVM(); |
| 81 | + if (removeIfMatch(requiredJavaVersions, install)) { |
| 82 | + disposeVms.add(() -> type.disposeVMInstall(id)); |
| 83 | + } else { |
| 84 | + type.disposeVMInstall(id); |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | + assertTrue("The following java versions are required by this test but can not be found: " |
| 89 | + + requiredJavaVersions, requiredJavaVersions.isEmpty()); |
| 90 | + } |
| 91 | + |
| 92 | + @Override |
| 93 | + protected void tearDown() throws Exception { |
| 94 | + super.tearDown(); |
| 95 | + disposeVms.forEach(Runnable::run); |
| 96 | + } |
| 97 | + |
| 98 | + @Override |
| 99 | + protected IJavaProject getProjectContext() { |
| 100 | + return getMultireleaseProject(); |
| 101 | + } |
| 102 | + |
| 103 | + public void testMultiReleaseLaunch() throws Exception { |
| 104 | + ILaunchConfiguration config = getLaunchConfiguration("p.Main"); |
| 105 | + Properties result = launchAndReadResult(config, 11); |
| 106 | + assertTrue("Was not launched with a proper Java installation " + result, JAVA_11.matches(result.getProperty("Java"))); |
| 107 | + assertEquals("X should be executed from Java 11 version: " + result, "11", result.get("X")); |
| 108 | + assertNull("Y should not be executed from Java 11 version: " + result, result.get("Y")); |
| 109 | + assertNull("Z should not be executed from Java 11 version: " + result, result.get("Z")); |
| 110 | + Properties result17 = launchAndReadResult(config, 17); |
| 111 | + assertTrue("Was not launched with a proper Java installation " + result17, JAVA_17.matches(result17.getProperty("Java"))); |
| 112 | + assertEquals("X should be executed from Java 17 version: " + result17, "17", result17.get("X")); |
| 113 | + assertEquals("Y should be executed from Java 11 version: " + result17, "11", result17.get("Y")); |
| 114 | + assertNull("Z should not be executed from Java 17 version: " + result17, result17.get("Z")); |
| 115 | + Properties result21 = launchAndReadResult(config, 21); |
| 116 | + assertTrue("Was not launched with a proper Java installation " + result21, JAVA_21.matches(result21.getProperty("Java"))); |
| 117 | + assertEquals("X should be executed from Java 17 version: " + result21, "17", result21.get("X")); |
| 118 | + assertEquals("Y should be executed from Java 21 version: " + result21, "21", result21.get("Y")); |
| 119 | + assertEquals("Z should be executed from Java 17 version: " + result21, "17", result21.get("Z")); |
| 120 | + } |
| 121 | + |
| 122 | + private Properties launchAndReadResult(ILaunchConfiguration config, int javaVersion) throws Exception { |
| 123 | + ILaunchConfigurationWorkingCopy workingCopy = config.getWorkingCopy(); |
| 124 | + workingCopy.setAttribute("org.eclipse.jdt.launching.JRE_CONTAINER", "org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-" |
| 125 | + + javaVersion + "/"); |
| 126 | + Properties properties = new Properties(); |
| 127 | + IJavaDebugTarget target = launchAndTerminate(workingCopy.doSave(), DEFAULT_TIMEOUT); |
| 128 | + processUiEvents(); |
| 129 | + final IConsole console = DebugUITools.getConsole(target.getProcess()); |
| 130 | + final TextConsole textConsole = (TextConsole) console; |
| 131 | + final IDocument consoleDocument = textConsole.getDocument(); |
| 132 | + String content = consoleDocument.get(); |
| 133 | + properties.load(new StringReader(content)); |
| 134 | + DebugPlugin.getDefault().getLaunchManager().removeLaunch(target.getLaunch()); |
| 135 | + return properties; |
| 136 | + } |
| 137 | + |
| 138 | + private static int getJavaVersion(IVMInstall install) { |
| 139 | + if (install instanceof IVMInstall2 vm) { |
| 140 | + try { |
| 141 | + String javaVersion = vm.getJavaVersion().split("\\.")[0]; //$NON-NLS-1$ |
| 142 | + return Integer.parseInt(javaVersion); |
| 143 | + } catch (RuntimeException rte) { |
| 144 | + // can't know then... |
| 145 | + } |
| 146 | + } |
| 147 | + return -1; |
| 148 | + } |
| 149 | + |
| 150 | + private static void removeExistingJavaVersions(Collection<RequiredJavaVersion> requiredJavaVersions, Set<File> existingLocations) { |
| 151 | + IVMInstallType[] installTypes = JavaRuntime.getVMInstallTypes(); |
| 152 | + for (IVMInstallType installType : installTypes) { |
| 153 | + IVMInstall[] vmInstalls = installType.getVMInstalls(); |
| 154 | + for (IVMInstall install : vmInstalls) { |
| 155 | + if (requiredJavaVersions.isEmpty()) { |
| 156 | + return; |
| 157 | + } |
| 158 | + existingLocations.add(install.getInstallLocation()); |
| 159 | + removeIfMatch(requiredJavaVersions, install); |
| 160 | + } |
| 161 | + } |
| 162 | + } |
| 163 | + |
| 164 | + protected static boolean removeIfMatch(Collection<RequiredJavaVersion> requiredJavaVersions, IVMInstall install) { |
| 165 | + int javaVersion = getJavaVersion(install); |
| 166 | + for (Iterator<RequiredJavaVersion> iterator = requiredJavaVersions.iterator(); iterator.hasNext();) { |
| 167 | + if (iterator.next().matches(javaVersion)) { |
| 168 | + iterator.remove(); |
| 169 | + return true; |
| 170 | + } |
| 171 | + } |
| 172 | + return false; |
| 173 | + } |
| 174 | + |
| 175 | + private static record RequiredJavaVersion(int from, int to) { |
| 176 | + |
| 177 | + public boolean matches(int version) { |
| 178 | + return (version >= from() && version <= to()); |
| 179 | + } |
| 180 | + |
| 181 | + public boolean matches(String v) { |
| 182 | + return matches(Integer.parseInt(v)); |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | +} |
0 commit comments