Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public class MultiReleaseTests extends BuilderTests {
private static final String JAVA9_SRC_FOLDER = "src9";
private static final String DEFAULT_SRC_FOLDER = "src";

// static {
// TESTS_NAMES = new String[] { "testMultiReleaseModuleInfoPerRelease" };
// }

public MultiReleaseTests(String name) {
Comment thread
laeubi marked this conversation as resolved.
super(name);
}
Expand Down Expand Up @@ -319,6 +323,92 @@ public String print() {
expectingNoProblems();
}

/**
* Test multi-release compilation with different module-info.java per release.
* This verifies that each source folder with a different release uses its own
* module-info.java for compilation, not a shared module from the project.
* See issue https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4268
*/
public void testMultiReleaseModuleInfoPerRelease() throws JavaModelException, IOException {
// Create modular project with Java 11 as base
IPath projectPath = createMRProject(CompilerOptions.VERSION_11);
IPath defaultSrc = env.getPackageFragmentRootPath(projectPath, DEFAULT_SRC_FOLDER);

// Base module-info requires no extra modules
env.addClass(defaultSrc, "", "module-info",
"""
module MRmodular {
}
"""
);

// Base Test.java - should have errors for both java.desktop and java.xml types
IPath classDefault = env.addClass(defaultSrc, "p", "Test",
"""
package p;
public class Test {
java.awt.Window w11;
org.w3c.dom.Element element11;
}
"""
);

// Java 17 source with module-info requiring java.desktop
IClasspathAttribute[] attributes17 = new IClasspathAttribute[] {
JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "17") };
IPath src17 = env.addPackageFragmentRoot(projectPath, "src17", attributes17);
env.addClass(src17, "", "module-info",
"""
module MRmodular {
requires java.desktop;
}
"""
);
env.addClass(src17, "p", "Test",
"""
package p;
public class Test {
java.awt.Window w17;
org.w3c.dom.Element element17;
}
"""
);

// Java 21 source with module-info requiring java.xml
IClasspathAttribute[] attributes21 = new IClasspathAttribute[] {
JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "21") };
IPath src21 = env.addPackageFragmentRoot(projectPath, "src21", attributes21);
env.addClass(src21, "", "module-info",
"""
module MRmodular {
requires java.xml;
}
"""
);
IPath class21 = env.addClass(src21, "p", "Test",
"""
package p;
public class Test {
java.awt.Window w21;
org.w3c.dom.Element element21;
}
"""
);

fullBuild();
//As our default module descriptor does not import anything both should give an error
expectingSpecificProblemsFor(defaultSrc, new Problem[] { //
new Problem("", "The type java.awt.Window is not accessible", classDefault, 32, 47, 40, IMarker.SEVERITY_ERROR),
new Problem("", "The type org.w3c.dom.Element is not accessible", classDefault, 54, 73, 40, IMarker.SEVERITY_ERROR)
});
//java.desktop includes java.xml so no errors to expect here
expectingNoProblemsFor(src17);
//we only import java.xml so desktop should give an error!
expectingSpecificProblemsFor(src21, new Problem[] { //
new Problem("", "The type java.awt.Window is not accessible", class21, 32, 47, 40, IMarker.SEVERITY_ERROR),
});
}

private IPath whenSetupMRRpoject() throws JavaModelException {
return whenSetupMRRpoject(CompilerOptions.VERSION_1_8);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ private static Class[] getAllTestClasses() {
ReconcilerTests21.class,
ReconcilerStatementsRecoveryTests.class,
ReconcilerMultiReleaseTests.class,
ReconcilerModuleMultiReleaseTests.class,
SelectionMultiReleaseTests.class,

// Copy and move operation tests
CopyMoveElementsTests.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*******************************************************************************
* Copyright (c) 2026 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
*******************************************************************************/
package org.eclipse.jdt.core.tests.model;

import junit.framework.Test;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IProblemRequestor;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;

/**
* Tests that reconciling (problem detection in working copies) of a multi-release
* modular project resolves modules and types as seen from the release specific source
* folder the reconciled unit lives in, honoring a per-release {@code module-info.java}.
*
* See https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4268
*/
public class ReconcilerModuleMultiReleaseTests extends ModifyingResourceTests {

static {
// TESTS_NAMES = new String[] { "testReconcileUsesReleaseModuleInfo21" };
}

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

public static Test suite() {
return buildModelTestSuite(ReconcilerModuleMultiReleaseTests.class);
}

@Override
public void setUpSuite() throws Exception {
super.setUpSuite();
IJavaProject project = createJava9ProjectWithJREAttributes("ReconcilerModuleMR",
new String[] { "src", "src17", "src21" }, null, "21");
IClasspathEntry[] classpath = project.getRawClasspath();
for (int i = 0; i < classpath.length; i++) {
IClasspathEntry entry = classpath[i];
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
if (entry.getPath().toString().endsWith("src17")) {
classpath[i] = JavaCore.newSourceEntry(entry.getPath(), null, null, null,
new IClasspathAttribute[] {
JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "17") });
} else if (entry.getPath().toString().endsWith("src21")) {
classpath[i] = JavaCore.newSourceEntry(entry.getPath(), null, null, null,
new IClasspathAttribute[] {
JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "21") });
}
}
}
project.setRawClasspath(classpath, new NullProgressMonitor());
project.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED);

createFolder("/ReconcilerModuleMR/src/p");
createFolder("/ReconcilerModuleMR/src17/p");
createFolder("/ReconcilerModuleMR/src21/p");

// base module requires nothing
createFile("/ReconcilerModuleMR/src/module-info.java", """
module MRmodular {
}
""");
// release 17 requires java.desktop (which transitively reads java.xml)
createFile("/ReconcilerModuleMR/src17/module-info.java", """
module MRmodular {
requires java.desktop;
}
""");
// release 21 requires java.xml
createFile("/ReconcilerModuleMR/src21/module-info.java", """
module MRmodular {
requires java.xml;
}
""");
}

@Override
public void tearDownSuite() throws Exception {
deleteProject("ReconcilerModuleMR");
super.tearDownSuite();
}

private void assertReconcileProblems(String path, String source, String expectedProblems) throws Exception {
ProblemRequestor problemRequestor = new ProblemRequestor();
WorkingCopyOwner owner = new WorkingCopyOwner() {
@Override
public IProblemRequestor getProblemRequestor(ICompilationUnit unit) {
return problemRequestor;
}
};
ICompilationUnit wc = getWorkingCopy(path, source, owner, problemRequestor);

Check warning on line 109 in org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerModuleMultiReleaseTests.java

View check run for this annotation

Jenkins - Eclipse JDT / Compiler

Deprecation

NORMAL: The method getWorkingCopy(String, String, WorkingCopyOwner, IProblemRequestor) from the type AbstractJavaModelTests is deprecated
try {
problemRequestor.initialize(source.toCharArray());
wc.reconcile(ICompilationUnit.NO_AST, true/*force problem detection*/, owner, null);
assertProblems("Unexpected problems for " + path, expectedProblems, problemRequestor);
} finally {
wc.discardWorkingCopy();
}
}

// java.xml is required in src21, so org.w3c.dom.Element must be accessible (no problem).
public void testReconcileUsesReleaseModuleInfo21() throws Exception {
assertReconcileProblems("/ReconcilerModuleMR/src21/p/Use.java", """
package p;
public class Use {
org.w3c.dom.Element element;
}
""", "----------\n----------\n");
}

// java.desktop is required in src17 and reads java.xml transitively, so both are accessible.
public void testReconcileUsesReleaseModuleInfo17() throws Exception {
assertReconcileProblems("/ReconcilerModuleMR/src17/p/Use.java", """
package p;
public class Use {
java.awt.Window window;
org.w3c.dom.Element element;
}
""", "----------\n----------\n");
}

// the base module requires nothing, so org.w3c.dom.Element is not accessible here.
public void testReconcileUsesBaseModuleInfo() throws Exception {
assertReconcileProblems("/ReconcilerModuleMR/src/p/Use.java", """
package p;
public class Use {
org.w3c.dom.Element element;
}
""",
"----------\n" +
"1. ERROR in /ReconcilerModuleMR/src/p/Use.java (at line 3)\n" +
" org.w3c.dom.Element element;\n" +
" ^^^^^^^^^^^^^^^^^^^\n" +
"The type org.w3c.dom.Element is not accessible\n" +
"----------\n");
}

private ITypeBinding resolveFieldType(String path, String source) throws Exception {
ICompilationUnit wc = getWorkingCopy(path, source);
try {
ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
parser.setResolveBindings(true);
parser.setSource(wc);
org.eclipse.jdt.core.dom.CompilationUnit ast =
(org.eclipse.jdt.core.dom.CompilationUnit) parser.createAST(null);
TypeDeclaration type = (TypeDeclaration) ast.types().get(0);
FieldDeclaration field = type.getFields()[0];
return field.getType().resolveBinding();
} finally {
wc.discardWorkingCopy();
}
}

// ASTParser.createAST (resolved DOM AST) must resolve org.w3c.dom.Element as seen from src21.
public void testCreateASTUsesReleaseModuleInfo21() throws Exception {
ITypeBinding binding = resolveFieldType("/ReconcilerModuleMR/src21/p/Use.java", """
package p;
public class Use {
org.w3c.dom.Element element;
}
""");
assertNotNull("Type binding should be resolved", binding);
assertEquals("org.w3c.dom.Element", binding.getQualifiedName());
}

// java.desktop reads java.xml transitively, so the binding resolves in src17 as well.
public void testCreateASTUsesReleaseModuleInfo17() throws Exception {
ITypeBinding binding = resolveFieldType("/ReconcilerModuleMR/src17/p/Use.java", """
package p;
public class Use {
org.w3c.dom.Element element;
}
""");
assertNotNull("Type binding should be resolved", binding);
assertEquals("org.w3c.dom.Element", binding.getQualifiedName());
}
}
Loading
Loading