| /******************************************************************************* |
| * Copyright (c) 2011, 2019 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.debug.tests.sourcelookup; |
| |
| import java.nio.file.Files; |
| import java.util.List; |
| |
| import org.eclipse.core.internal.resources.ResourceException; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.debug.core.ILaunch; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.model.ISourceLocator; |
| import org.eclipse.debug.core.model.IStackFrame; |
| import org.eclipse.debug.core.model.IValue; |
| import org.eclipse.debug.core.sourcelookup.ISourceContainer; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.debug.core.IJavaStackFrame; |
| import org.eclipse.jdt.debug.core.IJavaThread; |
| import org.eclipse.jdt.debug.testplugin.JavaProjectHelper; |
| import org.eclipse.jdt.debug.tests.AbstractDebugTest; |
| import org.eclipse.jdt.debug.tests.TestUtil; |
| import org.eclipse.jdt.internal.core.ClassFile; |
| import org.eclipse.jdt.internal.launching.JavaSourceLookupUtil; |
| import org.eclipse.jdt.launching.IRuntimeClasspathEntry; |
| import org.eclipse.jdt.launching.JavaRuntime; |
| import org.eclipse.jdt.launching.sourcelookup.containers.PackageFragmentRootSourceContainer; |
| |
| /** |
| * Tests for finding / showing source from jar files from related projects |
| */ |
| public class JarSourceLookupTests extends AbstractDebugTest { |
| |
| private static final String SAMPLE_JAR_PATH = "/JarProject/lib/sample.jar"; |
| public static final String A_RUN_JAR = "testJar.RunJar"; |
| static IJavaProject fgJarProject = null; |
| |
| String RefPjName = "JarRefProject"; |
| String fJarProject = "JarProject"; |
| |
| /** |
| * Constructor |
| */ |
| public JarSourceLookupTests() { |
| super("JarSourceLookupTests"); |
| } |
| |
| /** |
| * Disposes all source containers after a test, ensures no containers are still holding open Jar references, which can lead to {@link ResourceException}s |
| * when we try to delete / setup following tests |
| * @param containers |
| */ |
| void disposeContainers(ISourceContainer[] containers) { |
| if(containers != null) { |
| for (int i = 0; i < containers.length; i++) { |
| containers[i].dispose(); |
| } |
| } |
| } |
| |
| @Override |
| protected IJavaProject getProjectContext() { |
| return fgJarProject; |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| TestUtil.cleanUp(getName()); |
| IPath testrpath = new Path("testresources"); |
| IProject jarProject = createProjectClone(fJarProject, testrpath.append(fJarProject).toString(), true); |
| |
| IFile jar = jarProject.getFile("lib/sample.jar"); |
| assertTrue("lib/sample.jar is missing in project: " + jarProject.getName(), jar.exists()); |
| |
| fgJarProject = createJavaProjectClone(RefPjName, testrpath.append(RefPjName).toString(), JavaProjectHelper.JAVA_SE_1_7_EE_NAME, true); |
| |
| IProject jarRefProject = fgJarProject.getProject(); |
| IFile cp = jarRefProject.getFile(".classpath"); |
| assertTrue(".classpath is missing in project: " + jarRefProject.getName(), cp.exists()); |
| java.nio.file.Path path = cp.getLocation().toFile().toPath(); |
| List<String> lines = Files.readAllLines(path); |
| boolean foundJar = false; |
| for (String line : lines) { |
| if (line.contains(SAMPLE_JAR_PATH)) { |
| foundJar = true; |
| break; |
| } |
| } |
| assertTrue("The .classpath from project " + jarRefProject + " is unexpected and does not have an entry for " + SAMPLE_JAR_PATH + ": " |
| + new String(Files.readAllBytes(path)), foundJar); |
| waitForBuild(); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| removeAllBreakpoints(); |
| if (fgJarProject.exists()) { |
| IProject project = fgJarProject.getProject(); |
| // Before deleting, let indexer to finish his work to avoid error below (see 516351) |
| TestUtil.waitForJobs(getName(), 100, 3000); |
| try { |
| project.delete(true, null); |
| } |
| catch (ResourceException e) { |
| // Indexer still running on our jars? |
| TestUtil.waitForJobs(getName(), 1000, 5000); |
| if (project.exists()) { |
| project.delete(true, null); |
| } |
| } |
| } |
| super.tearDown(); |
| } |
| |
| /** |
| * Ensures the translation of source containers yields the correct containers |
| * |
| * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=346116 |
| * |
| * @throws Exception |
| */ |
| public void testTranslateContainers() throws Exception { |
| createLaunchConfiguration(fgJarProject, LAUNCHCONFIGURATIONS, A_RUN_JAR); |
| ILaunchConfiguration config = getLaunchConfiguration(fgJarProject, LAUNCHCONFIGURATIONS, A_RUN_JAR); |
| IRuntimeClasspathEntry[] entries = JavaRuntime.computeUnresolvedSourceLookupPath(config); |
| if (JavaRuntime.isModularConfiguration(config)) { |
| // There is no DefaultProjectClasspath entry so 2 entries inside it becomes 2 separate entries now |
| assertEquals("There should be 3 containers returned (JRE and classpath)", 3, entries.length); |
| } else { |
| assertEquals("There should be 2 containers returned (JRE and classpath)", 2, entries.length); |
| } |
| IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveSourceLookupPath(entries, config); |
| ISourceContainer[] containers = JavaSourceLookupUtil.translate(resolved); |
| try { |
| assertTrue("There must be computed containers", containers.length > 0); |
| //the number of containers is M + 2, where M is unknown across JREs, 1 for the project container and 1 for the JAR we are looking for |
| assertTrue("There should be at least 2 containers returned", containers.length >= 2); |
| for (int i = 0; i < containers.length; i++) { |
| ISourceContainer sourceContainer = containers[i]; |
| if ("sample.jar".equals(sourceContainer.getName()) && sourceContainer instanceof PackageFragmentRootSourceContainer) { |
| PackageFragmentRootSourceContainer container = (PackageFragmentRootSourceContainer) sourceContainer; |
| if (SAMPLE_JAR_PATH.equals(container.getPackageFragmentRoot().getPath().toString())) { |
| return; |
| } |
| } |
| } |
| StringBuilder dump = new StringBuilder(); |
| for (ISourceContainer sc : containers) { |
| dump.append(sc.getName()); |
| if (sc instanceof PackageFragmentRootSourceContainer) { |
| PackageFragmentRootSourceContainer pfsc = (PackageFragmentRootSourceContainer) sc; |
| dump.append(" with path: ").append(pfsc.getPath()); |
| } |
| dump.append(", "); |
| } |
| dump.setLength(dump.length() - 2); |
| dump.append(".\n Those containers were resolved from: "); |
| for (IRuntimeClasspathEntry cpe : resolved) { |
| dump.append(cpe); |
| dump.append(", "); |
| } |
| |
| dump.setLength(dump.length() - 2); |
| fail("We did not find a source container that was a PackageFragmentRootSourceContainer " |
| + "and had the name " + SAMPLE_JAR_PATH + ", but found source containers: " + dump); |
| } |
| finally { |
| disposeContainers(containers); |
| } |
| } |
| |
| /** |
| * Tests that the class file is found as source when the lookup is done from a jar from another project |
| * |
| * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=346116 |
| * |
| * @throws Exception |
| */ |
| public void testInspectClassFileFromJar() throws Exception { |
| createLaunchConfiguration(fgJarProject, LAUNCHCONFIGURATIONS, A_RUN_JAR); |
| createLineBreakpoint(21, A_RUN_JAR); |
| ILaunchConfiguration config = getLaunchConfiguration(fgJarProject, LAUNCHCONFIGURATIONS, A_RUN_JAR); |
| IJavaThread thread = null; |
| try { |
| thread = launchToBreakpoint(config); |
| IStackFrame frame = thread.getTopStackFrame(); |
| assertTrue("The found frame should be an IJavaStackFrame", frame instanceof IJavaStackFrame); |
| stepInto((IJavaStackFrame)frame); |
| assertNotNull("the stack frame from the thread cannot be null", frame); |
| IValue value = doEval(thread, "this"); |
| assertNotNull("The evaluation result cannot be null", value); |
| assertEquals("the name of the type being inspected must be a.JarClass", "a.JarClass", value.getReferenceTypeName()); |
| } |
| finally { |
| terminateAndRemove(thread); |
| } |
| } |
| |
| /** |
| * Tests that the class file is found as source when the lookup is done from a jar from another project |
| * |
| * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=346116 |
| * |
| * @throws Exception |
| */ |
| public void testShowClassFileFromJar() throws Exception { |
| createLaunchConfiguration(fgJarProject, LAUNCHCONFIGURATIONS, A_RUN_JAR); |
| createLineBreakpoint(21, A_RUN_JAR); |
| ILaunchConfiguration config = getLaunchConfiguration(fgJarProject, LAUNCHCONFIGURATIONS, A_RUN_JAR); |
| IJavaThread thread = null; |
| try { |
| thread = launchToBreakpoint(config); |
| IStackFrame frame = thread.getTopStackFrame(); |
| assertNotNull("The top stack frame cannot be null", frame); |
| assertTrue("The found frame should be an IJavaStackFrame", frame instanceof IJavaStackFrame); |
| Object source = lookupSource(frame); |
| assertNotNull("We should have found source for the main class testJar.RunJar", source); |
| assertTrue("The found source should be an IFile", source instanceof IFile); |
| assertEquals("We should have found a file named RunJar.java", "RunJar.java", ((IFile) source).getName()); |
| |
| stepInto((IJavaStackFrame)frame); |
| frame = thread.getTopStackFrame(); |
| assertNotNull("The top stack frame cannot be null", frame); |
| |
| source = lookupSource(frame); |
| assertNotNull("We should have found source for the jar class a.JarClass", source); |
| assertTrue("The found source should be a ClassFile", source instanceof ClassFile); |
| assertEquals("we should have found a file named a.JarClass.class", "JarClass.class", ((ClassFile) source).getElementName()); |
| } |
| finally { |
| terminateAndRemove(thread); |
| } |
| } |
| |
| /** |
| * Looks up source for the given frame using its backing {@link ISourceLocator} from its {@link ILaunch} |
| * @param frame the frame to look up source for |
| * @return the source object or <code>null</code> |
| */ |
| Object lookupSource(IStackFrame frame) { |
| ISourceLocator locator = frame.getLaunch().getSourceLocator(); |
| assertNotNull("The default Java source locator cannot be null", locator); |
| return locator.getSourceElement(frame); |
| } |
| } |