diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java index f139bc52c4..f41e41ab6a 100644 --- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java +++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java @@ -11,6 +11,7 @@ * Contributors: * IBM Corporation - initial API and implementation * SAP SE - Support hyperlinks for stack entries with method signature + * Ole Osterhagen - Issue 327 - Attribute "Without test code" is ignored in the launcher *******************************************************************************/ package org.eclipse.jdt.debug.tests; @@ -84,6 +85,7 @@ import org.eclipse.jdt.debug.tests.core.LocalVariableTests; import org.eclipse.jdt.debug.tests.core.ModuleOptionsTests; import org.eclipse.jdt.debug.tests.core.ProcessTests; +import org.eclipse.jdt.debug.tests.core.ResolveRuntimeClasspathTests; import org.eclipse.jdt.debug.tests.core.RuntimeClasspathEntryTests; import org.eclipse.jdt.debug.tests.core.StaticVariableTests; import org.eclipse.jdt.debug.tests.core.StratumTests; @@ -250,6 +252,7 @@ public AutomatedSuite() { addTest(new TestSuite(ClasspathProviderTests.class)); addTest(new TestSuite(BootpathTests.class)); addTest(new TestSuite(EEDefinitionTests.class)); + addTest(new TestSuite(ResolveRuntimeClasspathTests.class)); if (JavaProjectHelper.isJava9Compatible()) { addTest(new TestSuite(ModuleOptionsTests.class)); } diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ResolveRuntimeClasspathTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ResolveRuntimeClasspathTests.java new file mode 100644 index 0000000000..320e38788d --- /dev/null +++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ResolveRuntimeClasspathTests.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2023 Ole Osterhagen 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: + * Ole Osterhagen - Issue 327 - Attribute "Without test code" is ignored in the launcher + *******************************************************************************/ +package org.eclipse.jdt.debug.tests.core; + +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IAccessRule; +import org.eclipse.jdt.core.IClasspathAttribute; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.debug.testplugin.JavaProjectHelper; +import org.eclipse.jdt.debug.tests.AbstractDebugTest; +import org.eclipse.jdt.launching.IRuntimeClasspathEntry; +import org.eclipse.jdt.launching.JavaRuntime; + +/** + * Tests for resolving the runtime classpath + */ +public class ResolveRuntimeClasspathTests extends AbstractDebugTest { + + private IProject projectA; + private IProject projectB; + + public ResolveRuntimeClasspathTests(String name) { + super(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + projectA = root.getProject("projectA"); + projectB = root.getProject("projectB"); + } + + @Override + protected void tearDown() throws Exception { + try { + projectA.delete(true, null); + } catch (CoreException e) { + // ignore + } + try { + projectB.delete(true, null); + } catch (CoreException e) { + // ignore + } + super.tearDown(); + } + + public void testInclusionOfTestCode() throws CoreException { + IJavaProject project = createProjectWithProjectDependency(false); + IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(project); + // same as "excludeTestCode=false" + IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveRuntimeClasspathEntry(unresolved[0], project); + + assertTrue(isOnRuntimeClasspath(resolved, "/projectA/bin/main")); + assertTrue(isOnRuntimeClasspath(resolved, "/projectA/bin/test")); + } + + public void testExclusionOfTestCode() throws CoreException { + IJavaProject project = createProjectWithProjectDependency(false); + IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(project); + IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveRuntimeClasspathEntry(unresolved[0], project, true); + + assertTrue(isOnRuntimeClasspath(resolved, "/projectA/bin/main")); + assertFalse(isOnRuntimeClasspath(resolved, "/projectA/bin/test")); + } + + public void testExclusionOfTestCodeFromDependencyForProject() throws CoreException { + IJavaProject project = createProjectWithProjectDependency(true); + IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(project); + IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveRuntimeClasspathEntry(unresolved[0], project, false); + + assertTrue(isOnRuntimeClasspath(resolved, "/projectA/bin/main")); + // even with "excludeTestCode=false" test code from project A is not accessible + assertFalse(isOnRuntimeClasspath(resolved, "/projectA/bin/test")); + } + + public void testExclusionOfTestCodeFromDependencyForLaunchConfiguration() throws Exception { + IJavaProject project = createProjectWithProjectDependency(true); + IRuntimeClasspathEntry[] unresolved = JavaRuntime.computeUnresolvedRuntimeClasspath(project); + IRuntimeClasspathEntry[] resolved = JavaRuntime.resolveRuntimeClasspathEntry(unresolved[0], createLaunchConfiguration("ResolveRuntimeClasspathTests")); + + assertTrue(isOnRuntimeClasspath(resolved, "/projectA/bin/main")); + assertFalse(isOnRuntimeClasspath(resolved, "/projectA/bin/test")); + } + + private boolean isOnRuntimeClasspath(IRuntimeClasspathEntry[] runtimeClasspathEntries, String path) { + for (IRuntimeClasspathEntry runtimeClasspathEntry : runtimeClasspathEntries) { + if (runtimeClasspathEntry.getPath().equals(new Path(path))) { + return true; + } + } + return false; + } + + /** + * Creates two Java projects A and B. Project A contains a folder with non-test code and a folder with test code. Project B depends on project A. + * + *
    + *
  1. Classpath of project A + * + *
  2. Classpath of project B + * + *
+ * + * @param withoutTestCode + * {@code true} if project B can access test code from project A, otherwise {@code false}. + * @return Java project B + * @throws CoreException + * if this method fails. + */ + private IJavaProject createProjectWithProjectDependency(boolean withoutTestCode) throws CoreException { + // create and configure project A + IJavaProject javaProjectA = JavaProjectHelper.createJavaProject(projectA.getName()); + IFolder mainFolder = createFolders(projectA, "main"); + IFolder testFolder = createFolders(projectA, "test"); + IFolder binMainFolder = createFolders(projectA, "bin/main"); + IFolder binTestFolder = createFolders(projectA, "bin/test"); + JavaProjectHelper.addToClasspath(javaProjectA, JavaCore.newSourceEntry(mainFolder.getFullPath(), new IPath[0], new IPath[0], binMainFolder.getFullPath(), new IClasspathAttribute[0])); + JavaProjectHelper.addToClasspath(javaProjectA, JavaCore.newSourceEntry(testFolder.getFullPath(), new IPath[0], new IPath[0], binTestFolder.getFullPath(), new IClasspathAttribute[] { + JavaCore.newClasspathAttribute(IClasspathAttribute.TEST, "true") })); + + // create and configure project B + IJavaProject javaProjectB = JavaProjectHelper.createJavaProject(projectB.getName()); + JavaProjectHelper.addToClasspath(javaProjectB, JavaCore.newProjectEntry(projectA.getFullPath(), new IAccessRule[0], true, new IClasspathAttribute[] { + JavaCore.newClasspathAttribute(IClasspathAttribute.WITHOUT_TEST_CODE, Boolean.toString(withoutTestCode)) }, false)); + + return javaProjectB; + } + + private IFolder createFolders(IProject project, String path) throws CoreException { + IFolder folder = null; + for (String segment : path.split("/")) { + folder = folder != null ? folder.getFolder(segment) : project.getFolder(segment); + if (!folder.exists()) { + folder.create(true, true, null); + } + } + return folder; + } + +} diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java index cb6d1f80f1..2d48ee0431 100644 --- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java +++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java @@ -12,6 +12,7 @@ * IBM Corporation - initial API and implementation * Frits Jalvingh - Contribution for Bug 459831 - [launching] Support attaching * external annotations to a JRE container + * Ole Osterhagen - Issue 327 - Attribute "Without test code" is ignored in the launcher *******************************************************************************/ package org.eclipse.jdt.launching; @@ -1232,7 +1233,9 @@ public static IRuntimeClasspathEntry[] resolveRuntimeClasspathEntry(IRuntimeClas return new IRuntimeClasspathEntry[0]; } IClasspathAttribute[] attributes = entry.getClasspathEntry().getExtraAttributes(); - IRuntimeClasspathEntry[] entries = resolveOutputLocations(project, entry.getClasspathProperty(), attributes, excludeTestCode); + boolean withoutTestCode = entry.getClasspathEntry().isWithoutTestCode(); + IRuntimeClasspathEntry[] entries = resolveOutputLocations(project, entry.getClasspathProperty(), attributes, excludeTestCode + || withoutTestCode); if (entries != null) { return entries; } @@ -1477,7 +1480,9 @@ public static IRuntimeClasspathEntry[] resolveRuntimeClasspathEntry(IRuntimeClas IJavaProject jp = JavaCore.create(p); if (jp != null && p.isOpen() && jp.exists()) { IClasspathAttribute[] attributes = entry.getClasspathEntry().getExtraAttributes(); - IRuntimeClasspathEntry[] entries = resolveOutputLocations(jp, entry.getClasspathProperty(), attributes, excludeTestCode); + boolean withoutTestCode = entry.getClasspathEntry().isWithoutTestCode(); + IRuntimeClasspathEntry[] entries = resolveOutputLocations(jp, entry.getClasspathProperty(), attributes, excludeTestCode + || withoutTestCode); if (entries != null) { return entries; }