| /******************************************************************************* |
| * Copyright (c) 2000, 2018 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 |
| * Terry Parker <tparker@google.com> - Enable the Java model caches to recover from IO errors - https://bugs.eclipse.org/455042 |
| *******************************************************************************/ |
| package org.eclipse.jdt.core.tests.model; |
| |
| import java.io.IOException; |
| import java.nio.file.FileSystems; |
| import java.nio.file.Files; |
| |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| 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.*; |
| import org.eclipse.jdt.core.tests.util.Util; |
| import org.eclipse.jdt.internal.core.ClasspathEntry; |
| import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner; |
| import org.eclipse.jdt.internal.core.JavaModelManager; |
| import org.eclipse.jdt.internal.core.JavaProject; |
| import org.eclipse.jdt.internal.core.NameLookup; |
| |
| import junit.framework.Test; |
| |
| /** |
| * These test ensure that modifications in Java projects are correctly reported as |
| * IJavaEllementDeltas. |
| */ |
| public class NameLookupTests2 extends ModifyingResourceTests { |
| |
| public NameLookupTests2(String name) { |
| super(name); |
| } |
| |
| // Use this static initializer to specify subset for tests |
| // All specified tests which do not belong to the class are skipped... |
| static { |
| // org.eclipse.jdt.internal.core.search.matching.MatchLocator.PRINT_BUFFER = false; |
| // TESTS_PREFIX = "testArray"; |
| // TESTS_NAMES = new String[] { "testFindBinaryTypeWithDollarName" }; |
| // TESTS_NUMBERS = new int[] { 8 }; |
| // TESTS_RANGE = new int[] { 6, -1 }; |
| } |
| |
| public static Test suite() { |
| return buildModelTestSuite(NameLookupTests2.class); |
| } |
| |
| NameLookup getNameLookup(JavaProject project) throws JavaModelException { |
| return project.newNameLookup((WorkingCopyOwner)null); |
| } |
| public void testAddPackageFragmentRootAndPackageFrament() throws CoreException { |
| try { |
| IJavaProject p1 = createJavaProject("P1", new String[] {"src1"}, "bin"); |
| IJavaProject p2 = createJavaProject("P2", new String[] {}, ""); |
| IClasspathEntry[] classpath = |
| new IClasspathEntry[] { |
| JavaCore.newProjectEntry(new Path("/P1")) |
| }; |
| p2.setRawClasspath(classpath, null); |
| |
| IPackageFragment[] res = getNameLookup((JavaProject)p2).findPackageFragments("p1", false); |
| assertTrue("Should get no package fragment", res == null); |
| |
| IClasspathEntry[] classpath2 = |
| new IClasspathEntry[] { |
| JavaCore.newSourceEntry(new Path("/P1/src1")), |
| JavaCore.newSourceEntry(new Path("/P1/src2")) |
| }; |
| p1.setRawClasspath(classpath2, null); |
| createFolder("/P1/src2/p1"); |
| |
| res = getNameLookup((JavaProject)p2).findPackageFragments("p1", false); |
| assertTrue( |
| "Should get 'p1' package fragment", |
| res != null && |
| res.length == 1 && |
| res[0].getElementName().equals("p1")); |
| |
| } finally { |
| deleteProject("P1"); |
| deleteProject("P2"); |
| } |
| } |
| public void testAddPackageFragment() throws CoreException { |
| try { |
| createJavaProject("P1", new String[] {"src1"}, "bin"); |
| IJavaProject p2 = createJavaProject("P2", new String[] {}, ""); |
| IClasspathEntry[] classpath = |
| new IClasspathEntry[] { |
| JavaCore.newProjectEntry(new Path("/P1")) |
| }; |
| p2.setRawClasspath(classpath, null); |
| |
| IPackageFragment[] res = getNameLookup((JavaProject)p2).findPackageFragments("p1", false); |
| assertTrue("Should get no package fragment", res == null); |
| |
| createFolder("/P1/src1/p1"); |
| |
| res = getNameLookup((JavaProject)p2).findPackageFragments("p1", false); |
| assertTrue( |
| "Should get 'p1' package fragment", |
| res != null && |
| res.length == 1 && |
| res[0].getElementName().equals("p1")); |
| |
| } finally { |
| deleteProject("P1"); |
| deleteProject("P2"); |
| } |
| } |
| /* |
| * Resolve, add pkg, resolve again: new pkg should be accessible |
| * (regression test for bug 37962 Unexpected transient problem during reconcile |
| */ |
| public void testAddPackageFragment2() throws CoreException { |
| try { |
| JavaProject project = (JavaProject)createJavaProject("P", new String[] {"src"}, "bin"); |
| createFolder("/P/src/p1"); |
| |
| IPackageFragment[] pkgs = getNameLookup(project).findPackageFragments("p1", false); |
| assertElementsEqual( |
| "Didn't find p1", |
| "p1 [in src [in P]]", |
| pkgs); |
| |
| createFolder("/P/src/p2"); |
| |
| pkgs = getNameLookup(project).findPackageFragments("p2", false); |
| assertElementsEqual( |
| "Didn't find p2", |
| "p2 [in src [in P]]", |
| pkgs); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| /* |
| * Ensures that a NameLookup can be created with working copies that contain duplicate types |
| * (regression test for bug 63245 findPackageFragment won't return default package) |
| */ |
| public void testDuplicateTypesInWorkingCopies() throws CoreException { |
| // ICompilationUnit[] workingCopies = new ICompilationUnit[3]; |
| this.workingCopies = new ICompilationUnit[3]; |
| try { |
| JavaProject project = (JavaProject)createJavaProject("P"); |
| this.workingCopies[0] = getWorkingCopy( |
| "/P/X.java", |
| "public class X {\n" + |
| "}\n" + |
| "class Other {\n" + |
| "}" |
| ); |
| this.workingCopies[1] = getWorkingCopy( |
| "/P/Y.java", |
| "public class Y {\n" + |
| "}\n" + |
| "class Other {\n" + |
| "}" |
| ); |
| this.workingCopies[2] = getWorkingCopy( |
| "/P/Z.java", |
| "public class Z {\n" + |
| "}\n" + |
| "class Other {\n" + |
| "}" |
| ); |
| NameLookup nameLookup = project.newNameLookup(this.workingCopies); |
| IType type = nameLookup.findType("Other", false, NameLookup.ACCEPT_ALL); // TODO (jerome) should use seekTypes |
| assertTypesEqual( |
| "Unepexted types", |
| "Other\n", |
| new IType[] {type} |
| ); |
| } finally { |
| // discardWorkingCopies(workingCopies); |
| deleteProject("P"); |
| } |
| } |
| /* |
| * Find a default package fragment in a non-default root by its path. |
| * (regression test for bug 63245 findPackageFragment won't return default package) |
| */ |
| public void testFindDefaultPackageFragmentInNonDefaultRoot() throws CoreException { |
| try { |
| JavaProject project = (JavaProject)createJavaProject("P", new String[] {"src"}, "bin"); |
| |
| IPackageFragment pkg = getNameLookup(project).findPackageFragment(new Path("/P/src")); |
| assertElementsEqual( |
| "Didn't find default package", |
| "<default> [in src [in P]]", |
| new IJavaElement[] {pkg}); |
| |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| /* |
| * Creates a package fragment and finds it in a dependent project in a batch operation |
| * (regression test for bug 144776 JavaProject.resetCaches() needs to reset dependent projects |
| */ |
| public void testNameLookupFindPackageFragmentAfterCreation() throws CoreException { |
| try { |
| final IJavaProject p1 = createJavaProject("P1"); |
| final JavaProject p2 = (JavaProject) createJavaProject("P2", new String[] {""}, new String[0], new String[] {"/P1"}, ""); |
| |
| // populate namelookup for p2 |
| getNameLookup(p2); |
| |
| IWorkspaceRunnable runnable = new IWorkspaceRunnable(){ |
| public void run(IProgressMonitor monitor) throws CoreException { |
| p1.getPackageFragmentRoot(p1.getProject()).createPackageFragment("pkg", false/*don't force*/, monitor); |
| IPackageFragment[] pkgs = getNameLookup(p2).findPackageFragments("pkg", false/*exact match*/); |
| assertElementsEqual( |
| "Unexpected package fragments", |
| "pkg [in <project root> [in P1]]", |
| pkgs); |
| } |
| }; |
| JavaCore.run(runnable, null/*no progress*/); |
| } finally { |
| deleteProject("P1"); |
| deleteProject("P2"); |
| } |
| } |
| /* |
| * Ensure that finding a package fragment with a working copy opened returns one element only |
| * (regression test for bug 89624 Open on selection proposes twice the same entry) |
| */ |
| public void testFindPackageFragmentWithWorkingCopy() throws CoreException { |
| this.workingCopies = new ICompilationUnit[1]; |
| try { |
| JavaProject project = (JavaProject)createJavaProject("P"); |
| createFolder("/P/p1"); |
| this.workingCopies[0] = getWorkingCopy( |
| "/P/p1/X.java", |
| "package p1;\n" + |
| "public class X {\n" + |
| "}" |
| ); |
| NameLookup nameLookup = project.newNameLookup(this.workingCopies); |
| IJavaElement[] pkgs = nameLookup.findPackageFragments("p1", false/*not a partial match*/); |
| assertElementsEqual( |
| "Unexpected packages", |
| "p1 [in <project root> [in P]]", |
| pkgs); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| /* |
| * Ensure that a package fragment with a path with a length equals to an external jar path length + 1 |
| * is not found |
| * (regression test for bug 266771 NameLookup.findPackageFragment returns very incorrect package fragments) |
| */ |
| public void testFindPackageFragment2() throws CoreException { |
| try { |
| JavaProject project = (JavaProject)createJavaProject("P", new String[0], new String[] {"JCL_LIB"}, "bin"); |
| NameLookup nameLookup =getNameLookup(project); |
| IPath pathToSearch = new Path(getExternalJCLPathString() + 'a'); |
| IPackageFragment pkg = nameLookup.findPackageFragment(pathToSearch); |
| assertElementEquals( |
| "Unexpected package", |
| "<null>", |
| pkg); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| |
| /* |
| * Ensure that a member type with a name ending with a dollar and a number is found |
| * (regression test for bug 103466 Stack Overflow: Requesting Java AST from selection) |
| */ |
| public void testFindBinaryTypeWithDollarName() throws CoreException, IOException { |
| try { |
| IJavaProject project = createJavaProject("P"); |
| addLibrary(project, "lib.jar", "libsrc.zip", |
| new String[] { |
| "p/X.java", |
| "package p;\n" + |
| "public class X {\n" + |
| " public class $1 {\n" + |
| " public class $2 {\n" + |
| " }\n" + |
| " }\n" + |
| "}" |
| }, |
| "1.4"); |
| IType type = getNameLookup((JavaProject) project).findType("p.X$$1", false, NameLookup.ACCEPT_ALL); |
| assertTypesEqual( |
| "Unexpected type", |
| "p.X$$1\n", |
| new IType[] {type}); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| /* |
| * Ensure that a type with the same simple name as its member type is found |
| * (regression test for bug 102286 Error when trying F4-Type Hierarchy) |
| */ |
| public void testFindBinaryTypeWithSameNameAsMember() throws CoreException, IOException { |
| try { |
| IJavaProject project = createJavaProject("P", new String[] {}, new String[] {"/P/lib"}, new String[] {}, "bin"); |
| createFolder("/P/lib/p"); |
| createFile("/P/lib/p/X.class", ""); |
| createFile("/P/lib/p/X$X.class", ""); |
| IType type = getNameLookup((JavaProject) project).findType("p.X", false, NameLookup.ACCEPT_ALL); |
| assertTypesEqual( |
| "Unexpected type", |
| "p.X\n", |
| new IType[] {type}); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| /* |
| * Ensures that a type in working copy opened in an unrelated project is not found |
| * (regression test for bug 169970 [model] code assist favorites must honour build path of project in context) |
| */ |
| public void testFindTypeWithUnrelatedWorkingCopy() throws Exception { |
| ICompilationUnit workingCopy = null; |
| try { |
| createJavaProject("P1"); |
| createFolder("/P1/p"); |
| createFile( |
| "/P1/p/X.java", |
| "package p;\n" + |
| "public class X {\n" + |
| "}" |
| ); |
| workingCopy = getCompilationUnit("/P1/p/X.java"); |
| workingCopy.becomeWorkingCopy(null); |
| IJavaProject p2 = createJavaProject("P2"); |
| NameLookup nameLookup = ((JavaProject) p2).newNameLookup(DefaultWorkingCopyOwner.PRIMARY); |
| IType type = nameLookup.findType("p.X", false, NameLookup.ACCEPT_ALL); |
| assertNull("Should not find p.X in P2", type); |
| } finally { |
| if (workingCopy != null) |
| workingCopy.discardWorkingCopy(); |
| deleteProject("P1"); |
| deleteProject("P2"); |
| } |
| } |
| /* |
| * A test for bug 162621. Tests that a library jar that is initially invalid but transitions |
| * to being valid becomes visible in name lookup. Previously the jar would stay in the invalid |
| * archive cache until a classpath change was generated, and would not make it into the project's |
| * JavaProjectElementInfo cache without restarting Eclipse or closing and reopening the project. |
| */ |
| public void testTransitionFromInvalidToValidJar() throws CoreException, IOException { |
| String transitioningJar = getExternalPath() + "transitioningJar.jar"; |
| java.nio.file.Path transitioningJarPath = FileSystems.getDefault().getPath(transitioningJar); |
| IPath transitioningIPath = Path.fromOSString(transitioningJar); |
| |
| try { |
| Util.createJar( |
| new String[] { |
| "test1/IResource.java", //$NON-NLS-1$ |
| "package test1;\n" + //$NON-NLS-1$ |
| "public class IResource {\n" + //$NON-NLS-1$ |
| "}" //$NON-NLS-1$ |
| }, |
| new String[] { |
| "META-INF/MANIFEST.MF", |
| "Manifest-Version: 1.0\n" |
| }, |
| transitioningJar, |
| JavaCore.VERSION_1_4); |
| |
| // Set up the project with the invalid jar and allow all of the classpath validation |
| // and delta processing to complete. |
| JavaModelManager.throwIoExceptionsInGetZipFile = true; |
| JavaProject proj = (JavaProject) createJavaProject("P", new String[] {}, new String[] {transitioningJar}, "bin"); |
| JavaModelManager.getJavaModelManager().getJavaModel().refreshExternalArchives(null, null); |
| waitForAutoBuild(); |
| |
| assertTrue("The invalid archive cache should report that the jar is invalid", |
| !JavaModelManager.getJavaModelManager().getArchiveValidity(transitioningIPath).isValid()); |
| IType type = getNameLookup(proj).findType("test1.IResource", false, NameLookup.ACCEPT_CLASSES); |
| assertEquals("Name lookup should fail when the jar is invalid", null, type); |
| |
| // Cause IO exceptions to be thrown on all file operations |
| JavaModelManager.throwIoExceptionsInGetZipFile = false; |
| |
| // Since the timestamp hasn't changed, an external archive refresh isn't going |
| // to update the caches or cause name lookups to work. |
| JavaModelManager.getJavaModelManager().getJavaModel().refreshExternalArchives(null, null); |
| assertTrue("External archive refresh sees no changes, so the invalid archive cache should be unchanged", |
| !JavaModelManager.getJavaModelManager().getArchiveValidity(transitioningIPath).isValid()); |
| type = getNameLookup(proj).findType("test1.IResource", false, NameLookup.ACCEPT_CLASSES); |
| assertEquals("External archive refresh sees no changes, so the project cache should be unchanged", |
| null, type); |
| |
| // Re-validating the jar via validateClasspathEntry() forces eviction from the |
| // invalid archive cache more quickly than waiting for the time-based eviction, |
| // allowing the test to run more quickly. |
| IClasspathEntry transitioningEntry = JavaCore.newLibraryEntry(transitioningIPath, null, null); |
| ClasspathEntry.validateClasspathEntry(proj, transitioningEntry, false, false); |
| |
| assertFalse("The invalid archive cache should no longer report the jar as invalid", |
| !JavaModelManager.getJavaModelManager().getArchiveValidity(transitioningIPath).isValid()); |
| type = getNameLookup(proj).findType("test1.IResource", false, NameLookup.ACCEPT_CLASSES); |
| assertFalse("Name lookup should be able to find types in the valid jar", type == null); |
| } finally { |
| Files.deleteIfExists(transitioningJarPath); |
| deleteProject("P"); |
| } |
| } |
| } |
| |