blob: b5ff9eba84eb6601fce2eae2fc37790d3535638f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2017 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.core.tests.model;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IOrdinaryClassFile;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
import org.eclipse.jdt.internal.core.search.indexing.IndexRequest;
import junit.framework.Test;
public class ResolveTests9 extends AbstractJavaModelTests {
private static final int MODULE = 1;
private static final int WITHOUT_TEST = 2;
private static final int TEST = 4;
ICompilationUnit wc = null;
static {
// TESTS_NAMES = new String[] { "testModuleInfo_" };
// TESTS_NUMBERS = new int[] { 124 };
// TESTS_RANGE = new int[] { 16, -1 };
}
public static Test suite() {
return buildModelTestSuite(ResolveTests9.class);
}
public ResolveTests9(String name) {
super(name);
}
public ICompilationUnit getWorkingCopy(String path, String source) throws JavaModelException {
return super.getWorkingCopy(path, source, this.wcOwner);
}
public void setUpSuite() throws Exception {
super.setUpSuite();
IJavaProject project = setUpJavaProject("Resolve", "9", true);
String bootModPath = System.getProperty("java.home") + File.separator +"jrt-fs.jar";
IClasspathEntry jrtEntry = JavaCore.newLibraryEntry(new Path(bootModPath), null, null, null, null, false);
IClasspathEntry[] old = project.getRawClasspath();
IClasspathEntry[] newPath = new IClasspathEntry[old.length +1];
System.arraycopy(old, 0, newPath, 0, old.length);
newPath[old.length] = jrtEntry;
project.setRawClasspath(newPath, null);
waitUntilIndexesReady();
}
protected void setUp() throws Exception {
super.setUp();
this.wcOwner = new WorkingCopyOwner(){};
}
public void tearDownSuite() throws Exception {
deleteProject("Resolve");
super.tearDownSuite();
}
protected void tearDown() throws Exception {
if (this.wc != null) {
this.wc.discardWorkingCopy();
}
super.tearDown();
}
void addProjectEntry(IJavaProject thisProject, IJavaProject otherProject, int flags) throws JavaModelException {
addClasspathEntry(thisProject,
JavaCore.newProjectEntry(otherProject.getPath(), null, false, attributes(flags), false));
}
IClasspathAttribute[] attributes(int flags) {
List<IClasspathAttribute> attrs = new ArrayList<>();
if ((flags & MODULE) != 0)
attrs.add(JavaCore.newClasspathAttribute(IClasspathAttribute.MODULE, "true"));
if ((flags & WITHOUT_TEST) != 0)
attrs.add(JavaCore.newClasspathAttribute(IClasspathAttribute.WITHOUT_TEST_CODE, "true"));
if ((flags & TEST) != 0)
attrs.add(JavaCore.newClasspathAttribute(IClasspathAttribute.TEST, "true"));
return attrs.toArray(new IClasspathAttribute[attrs.size()]);
}
void addTestSrc(IJavaProject prj) throws CoreException {
IPath path = prj.getProject().getFullPath();
IClasspathEntry testSrc = JavaCore.newSourceEntry(path.append(new Path("src-test")),
null, null, path.append(new Path("bin-test")),
new IClasspathAttribute[] {JavaCore.newClasspathAttribute(IClasspathAttribute.TEST, "true")});
addClasspathEntry(prj, testSrc);
createFolder(prj.getElementName() + "/src-test");
}
public void testModuleInfo_serviceImplementation_OK() throws CoreException {
IFile modInfo = null;
try {
getWorkingCopy(
"/Resolve/src/test/ITest.java",
"public interface ITest {}\n");
getWorkingCopy(
"/Resolve/src/test/TestClass.java",
"public class TestClass implements ITest {}\n");
this.wc = getWorkingCopy(
"/Resolve/src/module-info.java",
"module com.test {\n" +
" provides p1.Y with ResolveInterface;\n" +
"}\n");
String str = this.wc.getSource();
String selection = "ResolveInterface";
int start = str.indexOf(selection);
int length = selection.length();
IJavaElement[] elements = this.wc.codeSelect(start, length);
assertElementsEqual(
"Unexpected elements",
"ResolveInterface [in ResolveInterface.java [in <default> [in src [in Resolve]]]]",
elements
);
} finally {
if (modInfo != null)
deleteResource(modInfo);
}
}
public void testModuleInfo_serviceInterface_OK() throws CoreException {
IFile modInfo = null;
IFolder testFolder = null;
try {
testFolder = createFolder("/Resolve/src/test");
createFile(
"/Resolve/src/test/ITest.java",
"package test;\n" +
"public interface ITest {}\n");
createFile(
"/Resolve/src/test/TestClass.java",
"package test;\n" +
"public class TestClass implements ITest {}\n");
this.wc = getWorkingCopy(
"/Resolve/src/module-info.java",
"module com.test {\n" +
" provides test.ITest with test.TestClass;\n" +
"}\n");
String str = this.wc.getSource();
String selection = "ITest";
int start = str.indexOf(selection);
int length = selection.length();
IJavaElement[] elements = this.wc.codeSelect(start, length);
assertElementsEqual(
"Unexpected elements",
"ITest [in ITest.java [in test [in src [in Resolve]]]]",
elements
);
} finally {
if (modInfo != null)
deleteResource(modInfo);
if (testFolder != null)
deleteResource(testFolder);
}
}
public void testModuleInfo_serviceInterface_almost_OK() throws CoreException {
IFile modInfo = null;
IFolder testFolder = null;
IndexManager indexManager = JavaModelManager.getIndexManager();
try {
// block the index manager to ensure it's not ready when code select is performed:
indexManager.request(new IndexRequest(new Path("blocker"), indexManager) {
@Override
public boolean execute(IProgressMonitor progress) {
try {
while (!this.isCancelled)
Thread.sleep(1000);
} catch (InterruptedException e) { /* ignore */ }
return false;
}
});
testFolder = createFolder("/Resolve/src/test");
createFile(
"/Resolve/src/test/ITest.java",
"public interface ITest {}\n"); // missing package declaration
createFile(
"/Resolve/src/test/TestClass.java",
"public class TestClass implements ITest {}\n");
this.wc = getWorkingCopy(
"/Resolve/src/module-info.java",
"module com.test {\n" +
" provides test.ITest with test.TestClass;\n" +
"}\n");
String str = this.wc.getSource();
String selection = "ITest";
int start = str.indexOf(selection);
int length = selection.length();
// still, if we get here before indexes are ready,
// then OperationCanceledException will send us into SearchableEnvironment.findTypes(String, ISearchRequestor, int),
// which succeeds:
IJavaElement[] elements = this.wc.codeSelect(start, length);
assertElementsEqual(
"Unexpected elements",
"ITest [in ITest.java [in test [in src [in Resolve]]]]",
elements
);
} finally {
if (modInfo != null)
deleteResource(modInfo);
if (testFolder != null)
deleteResource(testFolder);
indexManager.discardJobs("blocker");
}
}
public void testModuleInfo_serviceInterface_NOK() throws CoreException {
IFile modInfo = null;
IFolder testFolder = null;
try {
testFolder = createFolder("/Resolve/src/test");
createFile(
"/Resolve/src/test/ITest.java",
"public interface ITest {}\n"); // missing package declaration
createFile(
"/Resolve/src/test/TestClass.java",
"public class TestClass implements ITest {}\n");
this.wc = getWorkingCopy(
"/Resolve/src/module-info.java",
"module com.test {\n" +
" provides test.ITest with test.TestClass;\n" +
"}\n");
String str = this.wc.getSource();
String selection = "ITest";
int start = str.indexOf(selection);
int length = selection.length();
waitUntilIndexesReady();
// even after indexes are built we meanwhile (after bug 540541) find the selected type
IJavaElement[] elements = this.wc.codeSelect(start, length);
assertElementsEqual(
"Unexpected elements",
"ITest [in ITest.java [in test [in src [in Resolve]]]]",
elements
);
} finally {
if (modInfo != null)
deleteResource(modInfo);
if (testFolder != null)
deleteResource(testFolder);
}
}
public void testModuleInfo_noReferenceAtKeyword() throws CoreException {
IFile providesFile = createFile("/Resolve/src/provides.java", "public class provides {}");
IFile modInfo = null;
try {
getWorkingCopy(
"/Resolve/src/test/ITest.java",
"public interface ITest {}\n");
getWorkingCopy(
"/Resolve/src/test/TestClass.java",
"public class TestClass implements ITest {}\n");
this.wc = getWorkingCopy(
"/Resolve/src/module-info.java",
"module com.test {\n" +
" provides p1.Y with ResolveInterface;\n" +
"}\n");
String str = this.wc.getSource();
String selection = "provides";
int start = str.indexOf(selection);
int length = selection.length();
IJavaElement[] elements = this.wc.codeSelect(start, length);
assertElementsEqual(
"Unexpected elements",
"",
elements
);
} finally {
deleteResource(providesFile);
if (modInfo != null)
deleteResource(modInfo);
}
}
public void testModuleInfo_referenceAtKeywordInNonKWPosition() throws CoreException {
IFile providesFile = createFile("/Resolve/src/provides.java", "public class provides implements p1.Y {}");
IFile modInfo = null;
try {
getWorkingCopy(
"/Resolve/src/test/ITest.java",
"public interface ITest {}\n");
getWorkingCopy(
"/Resolve/src/test/TestClass.java",
"public class TestClass implements ITest {}\n");
this.wc = getWorkingCopy(
"/Resolve/src/module-info.java",
"module com.test {\n" +
" provides p1.Y with provides;\n" +
"}\n");
String str = this.wc.getSource();
String selection = "provides";
int start = str.lastIndexOf(selection);
int length = selection.length();
IJavaElement[] elements = this.wc.codeSelect(start, length);
assertElementsEqual(
"Unexpected elements",
"provides [in provides.java [in <default> [in src [in Resolve]]]]",
elements
);
} finally {
deleteResource(providesFile);
if (modInfo != null)
deleteResource(modInfo);
}
}
public void testClassFileInModule1() throws CoreException, IOException {
if (!isJRE9) {
System.err.println("Test "+getName()+" requires a JRE 9");
return;
}
IJavaProject javaProject = createJava9Project("Test");
try {
IType type = javaProject.findType("java.util.zip.ZipFile");
IOrdinaryClassFile classFile = type.getClassFile();
String contents = classFile.getBuffer().getContents();
int start = contents.indexOf("this(");
IJavaElement[] selected = classFile.codeSelect(start, 4);
assertElementsEqual(
"Unexpected elements",
"ZipFile(java.io.File, int) [in ZipFile [in ZipFile.class [in java.util.zip [in <module:java.base>]]]]",
selected);
} finally {
deleteProject(javaProject);
}
}
public void testUnnamedNamedConflict() throws CoreException, IOException {
if (!isJRE9) {
System.err.println("Test "+getName()+" requires a JRE 9");
return;
}
IJavaProject mod = null;
IJavaProject test = null;
try {
mod = createJava9Project("mod");
createFolder("mod/src/p1/p2");
createFile("mod/src/p1/p2/C.java", "package p1.p2;\n public class C {}\n");
createFile("mod/src/module-info.java",
"module mod {\n" +
" exports p1.p2;\n" +
"}\n");
mod.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
test = createJava9Project("Test");
IClasspathAttribute[] attributes = { JavaCore.newClasspathAttribute(IClasspathAttribute.MODULE, "true") };
addClasspathEntry(test, JavaCore.newProjectEntry(new Path("/mod"), null, false, attributes, false));
createFolder("Test/src/p1/p2");
createFile("Test/src/p1/p2/C1.java", "package p1.p2;\n public class C1 {}\n");
String source =
"package q;\n" +
"public class Main {\n" +
" p1.p2.C c;\n" +
" p1.p2.C1 c1;\n" +
"}\n";
createFolder("Test/src/q");
createFile("Test/src/q/Main.java", source);
test.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
IMarker[] markers = test.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
sortMarkers(markers);
assertMarkers("Unexpected markers", "The package p1.p2 conflicts with a package accessible from another module: mod", markers);
ICompilationUnit unit = getCompilationUnit("Test/src/q/Main.java");
// test that we can select both types despite the package conflict:
int start = source.indexOf("C c");
IJavaElement[] selected = unit.codeSelect(start, 2);
assertElementsEqual(
"Unexpected elements",
"C [in C.java [in p1.p2 [in src [in mod]]]]",
selected);
start = source.indexOf("C1");
selected = unit.codeSelect(start, 2);
assertElementsEqual(
"Unexpected elements",
"C1 [in C1.java [in p1.p2 [in src [in Test]]]]",
selected);
} finally {
deleteProject(test);
deleteProject(mod);
}
}
public void testBug537934() throws Exception {
if (!isJRE9) {
System.err.println("Test "+getName()+" requires a JRE 9");
return;
}
IJavaProject gui = null;
IJavaProject model = null;
IJavaProject type = null;
IJavaProject logg = null;
try {
// ---- module log:
// - has log4j on the module path
logg = createJava9ProjectWithJREAttributes("com.igorion.log", new String[] {"src"}, attributes(MODULE));
String jarAbsPath = logg.getProject().getLocation()+"/log4j.jar";
createJar(new String[] {
"log4j/Dummy.java",
"package log4j;\n" +
"public class Dummy {}\n"
},
jarAbsPath);
addLibraryEntry(logg, new Path(jarAbsPath), null, null, null, null, attributes(MODULE), false);
createFolder("com.igorion.log/src/com/igorion/log");
createFile("com.igorion.log/src/com/igorion/log/ILog.java",
"package com.igorion.log;\n public interface ILog {}\n");
createFile("com.igorion.log/src/module-info.java",
"module com.igorion.log {\n" +
" requires log4j;\n" +
" exports com.igorion.log;\n" +
"}\n");
logg.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
IMarker[] markers = logg.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
assertMarkers("markers in com.igorion.log",
"Name of automatic module \'log4j\' is unstable, it is derived from the module\'s file name.",
markers);
// ---- module type:
// - has test sources
type = createJava9Project("com.igorion.type");
createFolder("com.igorion.type/src/com/igorion/type");
createFile("com.igorion.type/src/com/igorion/type/IOther.java",
"package com.igorion.type;\n public interface IOther {}\n");
createFile("com.igorion.type/src/module-info.java",
"module com.igorion.type {\n" +
" exports com.igorion.type;\n" +
"}\n");
addTestSrc(type);
type.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
markers = type.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
assertMarkers("markers in com.igorion.type", "", markers);
// ---- module model:
// - has test sources
// - has log4j on the module path
// - has modules log & type on the module path without_test_code
model = createJava9ProjectWithJREAttributes("com.igorion.model", new String[] {"src"}, attributes(MODULE));
createFolder("com.igorion.model/src/com/igorion/model/define");
createFile("com.igorion.model/src/com/igorion/model/IModel.java",
"package com.igorion.model;\n public interface IModel {}\n");
createFile("com.igorion.model/src/com/igorion/model/define/Model.java",
"package com.igorion.model.define;\n" +
"import com.igorion.model.IModel;\n" +
"import java.util.Optional;\n" +
"public class Model {\n" +
" public static synchronized Optional<IModel> instance() { return Optional.empty(); }\n" +
"}\n");
createFile("com.igorion.model/src/module-info.java",
"module com.igorion.model {\n" +
" requires com.igorion.log;\n" +
" exports com.igorion.model;\n" +
" exports com.igorion.model.define;\n" +
"}\n");
addTestSrc(model);
addLibraryEntry(model, new Path(jarAbsPath), null, null, null, null, attributes(MODULE), false);
addProjectEntry(model, logg, MODULE|WITHOUT_TEST);
addProjectEntry(model, type, MODULE|WITHOUT_TEST);
model.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
markers = model.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
assertMarkers("markers in com.igorion.model", "", markers);
// ---- module gui:
// - has log4j on the module path for test code
// - has modules type, model, log on the module path without_test_code (order is significant)
gui = createJava9ProjectWithJREAttributes("com.igorion.gui", new String[] {"src"}, attributes(MODULE));
addTestSrc(gui);
createFolder("com.igorion.gui/src/com/igorion/gui");
String source =
"package com.igorion.gui;\n" +
"import com.igorion.model.IModel;\n" +
"import com.igorion.model.define.Model;\n" +
"import java.util.Optional;\n" +
"public class Reproduce {\n" +
" static void meth() {\n" +
" Optional<IModel> oModel = Model.instance();\n" +
" if (oModel.isPresent())\n" +
" oModel.get();\n" +
" }\n" +
"}\n";
createFile("com.igorion.gui/src/com/igorion/gui/Reproduce.java", source);
createFile("com.igorion.gui/src/module-info.java",
"module com.igorion.gui {\n" +
" requires com.igorion.type;\n" +
" requires com.igorion.model;\n" +
"}\n");
addLibraryEntry(gui, new Path(jarAbsPath), null, null, null, null, attributes(MODULE|TEST), false);
addProjectEntry(gui, type, MODULE|WITHOUT_TEST);
addProjectEntry(gui, model, MODULE|WITHOUT_TEST);
addProjectEntry(gui, logg, MODULE|WITHOUT_TEST);
gui.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
markers = gui.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
assertMarkers("markers in com.igorion.gui", "", markers);
// test that selection finds a fully resolved type Optional<IModel>:
ICompilationUnit unit = getCompilationUnit("com.igorion.gui/src/com/igorion/gui/Reproduce.java");
int start = source.indexOf("get(");
IJavaElement[] selected = unit.codeSelect(start, 3);
assertElementsEqual(
"Unexpected elements",
"get() [in Optional [in Optional.class [in java.util [in <module:java.base>]]]]",
selected);
} finally {
deleteProject(gui);
deleteProject(model);
deleteProject(type);
deleteProject(logg);
}
}
}