diff --git a/plugins/maven/src/test/java/org/jetbrains/idea/maven/execution/MavenClasspathsAndSearchScopesTest.java b/plugins/maven/src/test/java/org/jetbrains/idea/maven/execution/MavenClasspathsAndSearchScopesTest.java index b7a65592cb83..8e7caf98107b 100644 --- a/plugins/maven/src/test/java/org/jetbrains/idea/maven/execution/MavenClasspathsAndSearchScopesTest.java +++ b/plugins/maven/src/test/java/org/jetbrains/idea/maven/execution/MavenClasspathsAndSearchScopesTest.java @@ -22,21 +22,17 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.impl.scopes.LibraryRuntimeClasspathScope; import com.intellij.openapi.module.impl.scopes.ModuleWithDependenciesScope; import com.intellij.openapi.roots.*; +import com.intellij.openapi.roots.impl.DirectoryIndex; import com.intellij.openapi.roots.impl.LibraryScopeCache; import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.openapi.vfs.*; import com.intellij.psi.search.DelegatingGlobalSearchScope; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.testFramework.PsiTestUtil; -import com.intellij.util.ArrayUtil; -import com.intellij.util.CommonProcessors; -import com.intellij.util.PathsList; -import com.intellij.util.ReflectionUtil; +import com.intellij.util.*; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.MavenImportingTestCase; import org.jetbrains.idea.maven.importing.ArtifactsDownloadingTestCase; @@ -1041,6 +1037,298 @@ public class MavenClasspathsAndSearchScopesTest extends MavenImportingTestCase { getRepositoryPath() + "/junit/junit/4.0/junit-4.0.jar"); } + public void testDirIndexOrderEntriesTransitiveCompileScope() throws IOException { + List modules = setupDirIndexTestModulesWithScope("compile"); + checkDirIndexTestModulesWithCompileOrRuntimeScope(modules); + } + + public void testDirIndexOrderEntriesTransitiveRuntimeScope() throws IOException { + List modules = setupDirIndexTestModulesWithScope("runtime"); + checkDirIndexTestModulesWithCompileOrRuntimeScope(modules); + } + + // Creates a Maven dependency graph for testing DirectoryIndex#getOrderEntries. + private List setupDirIndexTestModulesWithScope(String scope) throws IOException { + createRepositoryFile("jmock/jmock/1.0/jmock-1.0.jar"); + // Dependency graph: + // m4 + // | + // v + // m1 -> m2 -> m3-| + // |----------> jmock + // v + // m5 -> m6 + // Dependencies are set up to be under the given scope, except that jmock is under a test scope, + // and the m5 -> m6 dep is always under a compile scope. + VirtualFile m1 = createModulePom("m1", "test" + + "m1" + + "1" + + "" + + " " + + " test" + + " m2" + + " 1" + + " " + scope + "" + + " " + + ""); + VirtualFile m2 = createModulePom("m2", "test" + + "m2" + + "1" + + "" + + " " + + " test" + + " m3" + + " 1" + + " " + scope + "" + + " " + + " " + + " jmock" + + " jmock" + + " 1.0" + + " test" + + " " + + " " + + " test" + + " m5" + + " 1" + + " " + scope + "" + + " " + + ""); + VirtualFile m3 = createModulePom("m3", "test" + + "m3" + + "1" + + "" + + " " + + " jmock" + + " jmock" + + " 1.0" + + " test" + + " " + + ""); + VirtualFile m4 = createModulePom("m4", "test" + + "m4" + + "1" + + "" + + " " + + " test" + + " m3" + + " 1" + + " " + scope + "" + + " " + + ""); + // The default setupInWriteAction only creates directories up to m4. + // Create directories for m5 and m6 which we will use for this test. + new WriteCommandAction.Simple(myProject) { + @Override + protected void run() throws Throwable { + createProjectSubDirs("m5/src/main/java", + "m5/src/test/java", + + "m6/src/main/java", + "m6/src/test/java"); + } + }.execute(); + VirtualFile m5 = createModulePom("m5", "test" + + "m5" + + "1" + + "" + + " " + + " test" + + " m6" + + " 1" + + " compile" + + " " + + ""); + VirtualFile m6 = createModulePom("m6", "test" + + "m6" + + "1"); + importProjects(m1, m2, m3, m4, m5, m6); + assertModules("m1", "m2", "m3", "m4", "m5", "m6"); + createOutputDirectories(); + + return Arrays.asList(getModule("m1"), getModule("m2"), getModule("m3"), getModule("m4"), + getModule("m5"), getModule("m6")); + } + + // Checks that the DirectoryIndex#getOrderEntries() returns the expected values + // for the dependency graph set up by setupDirIndexTestModulesWithScope(). + // The result is the same for "compile" and "runtime" scopes. + private void checkDirIndexTestModulesWithCompileOrRuntimeScope(List modules) { + assertEquals(6, modules.size()); + DirectoryIndex index = DirectoryIndex.getInstance(myProject); + VirtualFile m3JavaDir = VfsUtil.findFileByIoFile(new File(getProjectPath(), "m3/src/main/java"), true); + assertNotNull(m3JavaDir); + // Should be: m1 -> m3, m2 -> m3, m3 -> source, and m4 -> m3 + List orderEntries = index.getOrderEntries(index.getInfoForFile(m3JavaDir)); + assertEquals(4, orderEntries.size()); + List ownerModules = orderEntriesToOwnerModules(orderEntries); + List depModules = orderEntriesToDepModules(orderEntries); + assertOrderedElementsAreEqual(ownerModules, + Arrays.asList(modules.get(0), modules.get(1), modules.get(2), modules.get(3))); + assertOrderedElementsAreEqual(depModules, + Arrays.asList(modules.get(2), modules.get(2), null, modules.get(2))); + // m3 -> source + OrderEntry m3E2 = orderEntries.get(2); + assertInstanceOf(m3E2, ModuleSourceOrderEntry.class); + + VirtualFile m6javaDir = VfsUtil.findFileByIoFile(new File(getProjectPath(), "m6/src/main/java"), true); + assertNotNull(m6javaDir); + // Should be m1 -> m6, m2 -> m6, m5 -> m6, m6 -> source + List m6OrderEntries = index.getOrderEntries(index.getInfoForFile(m6javaDir)); + assertEquals(4, m6OrderEntries.size()); + List m6OwnerModules = orderEntriesToOwnerModules(m6OrderEntries); + List m6DepModules = orderEntriesToDepModules(m6OrderEntries); + assertOrderedElementsAreEqual(m6OwnerModules, + Arrays.asList(modules.get(0), modules.get(1), modules.get(4), modules.get(5))); + assertOrderedElementsAreEqual(m6DepModules, + Arrays.asList(modules.get(5), modules.get(5), modules.get(5), null)); + // m6 -> source + OrderEntry m6E3 = m6OrderEntries.get(3); + assertInstanceOf(m6E3, ModuleSourceOrderEntry.class); + + VirtualFile jmockDir = VfsUtil.findFileByIoFile(new File(getRepositoryPath(), "jmock/jmock/1.0/jmock-1.0.jar"), true); + assertNotNull(jmockDir); + VirtualFile jmockJar = JarFileSystem.getInstance().getJarRootForLocalFile(jmockDir); + assertNotNull(jmockJar); + // m2 -> jmock, m3 -> jmock + List jmockOrderEntries = index.getOrderEntries(index.getInfoForFile(jmockJar)); + assertEquals(2, jmockOrderEntries.size()); + OrderEntry jmockE0 = jmockOrderEntries.get(0); + assertEquals(modules.get(1), jmockE0.getOwnerModule()); + assertInstanceOf(jmockE0, LibraryOrderEntry.class); + OrderEntry jmockE1 = jmockOrderEntries.get(1); + assertEquals(modules.get(2), jmockE1.getOwnerModule()); + assertInstanceOf(jmockE1, LibraryOrderEntry.class); + } + + public void testDirIndexOrderEntriesTransitiveTestScope() throws IOException { + // This test is a bit different from the above tests of compile or runtime scope, + // because test scope does not propagate transitive dependencies. + List modules = setupDirIndexTestModulesWithScope("test"); + assertEquals(6, modules.size()); + DirectoryIndex index = DirectoryIndex.getInstance(myProject); + VirtualFile m3JavaDir = VfsUtil.findFileByIoFile(new File(getProjectPath(), "m3/src/main/java"), true); + assertNotNull(m3JavaDir); + // Should be no transitive deps: m2 -> m3, m3 -> source, and m4 -> m3 + List orderEntries = index.getOrderEntries(index.getInfoForFile(m3JavaDir)); + assertEquals(3, orderEntries.size()); + List ownerModules = orderEntriesToOwnerModules(orderEntries); + List depModules = orderEntriesToDepModules(orderEntries); + assertOrderedElementsAreEqual(ownerModules, + Arrays.asList(modules.get(1), modules.get(2), modules.get(3))); + assertOrderedElementsAreEqual(depModules, + Arrays.asList(modules.get(2), null, modules.get(2))); + // m3 -> source + OrderEntry m3E1 = orderEntries.get(1); + assertInstanceOf(m3E1, ModuleSourceOrderEntry.class); + + VirtualFile m6javaDir = VfsUtil.findFileByIoFile(new File(getProjectPath(), "m6/src/main/java"), true); + assertNotNull(m6javaDir); + // Still has some transitive deps because m5 -> m6 is hardcoded to be compile scope + // m2 -> m6, m5 -> m6, m6 -> source + List m6OrderEntries = index.getOrderEntries(index.getInfoForFile(m6javaDir)); + assertEquals(3, m6OrderEntries.size()); + List m6OwnerModules = orderEntriesToOwnerModules(m6OrderEntries); + List m6DepModules = orderEntriesToDepModules(m6OrderEntries); + assertOrderedElementsAreEqual(m6OwnerModules, + Arrays.asList(modules.get(1), modules.get(4), modules.get(5))); + assertOrderedElementsAreEqual(m6DepModules, + Arrays.asList(modules.get(5), modules.get(5), null)); + // m6 -> source + OrderEntry m6E2 = m6OrderEntries.get(2); + assertInstanceOf(m6E2, ModuleSourceOrderEntry.class); + + VirtualFile jmockDir = VfsUtil.findFileByIoFile(new File(getRepositoryPath(), "jmock/jmock/1.0/jmock-1.0.jar"), true); + assertNotNull(jmockDir); + VirtualFile jmockJar = JarFileSystem.getInstance().getJarRootForLocalFile(jmockDir); + assertNotNull(jmockJar); + // m2 -> jmock, m3 -> jmock + List jmockOrderEntries = index.getOrderEntries(index.getInfoForFile(jmockJar)); + assertEquals(2, jmockOrderEntries.size()); + OrderEntry jmockE0 = jmockOrderEntries.get(0); + assertEquals(modules.get(1), jmockE0.getOwnerModule()); + assertInstanceOf(jmockE0, LibraryOrderEntry.class); + OrderEntry jmockE1 = jmockOrderEntries.get(1); + assertEquals(modules.get(2), jmockE1.getOwnerModule()); + assertInstanceOf(jmockE1, LibraryOrderEntry.class); + } + + public void testDirIndexOrderEntriesStartingFromRegularModule() throws IOException { + final List modules = setupDirIndexTestModulesWithScope("compile"); + assertEquals(6, modules.size()); + final Module nonMavenM1 = createModule("nonMavenM1"); + final Module nonMavenM2 = createModule("nonMavenM2"); + + new WriteCommandAction.Simple(myProject) { + @Override + protected void run() throws Throwable { + ModuleRootModificationUtil.addDependency(nonMavenM1, nonMavenM2, DependencyScope.COMPILE, true); + ModuleRootModificationUtil.addDependency(nonMavenM2, modules.get(0), DependencyScope.COMPILE, true); + createProjectSubDirs("nonMavenM1/src/main/java", "nonMavenM1/src/test/java", + "nonMavenM2/src/main/java", "nonMavenM2/src/test/java"); + VirtualFile nonMavenM1JavaDir = VfsUtil.findFileByIoFile(new File(getProjectPath(), "nonMavenM1/src/main/java"), true); + assertNotNull(nonMavenM1JavaDir); + PsiTestUtil.addSourceContentToRoots(nonMavenM1, nonMavenM1JavaDir); + VirtualFile nonMavenM2JavaDir = VfsUtil.findFileByIoFile(new File(getProjectPath(), "nonMavenM2/src/main/java"), true); + assertNotNull(nonMavenM2JavaDir); + PsiTestUtil.addSourceContentToRoots(nonMavenM2, nonMavenM2JavaDir); + } + }.execute().throwException(); + + assertModuleModuleDeps("nonMavenM1", "nonMavenM2"); + assertModuleModuleDeps("nonMavenM2", "m1"); + assertModuleModuleDeps("m1", "m2", "m3", "m5", "m6"); + + DirectoryIndex index = DirectoryIndex.getInstance(myProject); + VirtualFile m3JavaDir = VfsUtil.findFileByIoFile(new File(getProjectPath(), "m3/src/main/java"), true); + assertNotNull(m3JavaDir); + // Should be: m1 -> m3, m2 -> m3, m3 -> source, and m4 -> m3 + // It doesn't trace back to nonMavenM1 and nonMavenM2. + List orderEntries = index.getOrderEntries(index.getInfoForFile(m3JavaDir)); + List ownerModules = orderEntriesToOwnerModules(orderEntries); + List depModules = orderEntriesToDepModules(orderEntries); + assertOrderedElementsAreEqual(ownerModules, + Arrays.asList(modules.get(0), modules.get(1), modules.get(2), modules.get(3))); + assertOrderedElementsAreEqual(depModules, + Arrays.asList(modules.get(2), modules.get(2), null, modules.get(2))); + + VirtualFile m6javaDir = VfsUtil.findFileByIoFile(new File(getProjectPath(), "m6/src/main/java"), true); + assertNotNull(m6javaDir); + // Should be m1 -> m6, m2 -> m6, m5 -> m6, m6 -> source + List m6OrderEntries = index.getOrderEntries(index.getInfoForFile(m6javaDir)); + List m6OwnerModules = orderEntriesToOwnerModules(m6OrderEntries); + List m6DepModules = orderEntriesToDepModules(m6OrderEntries); + assertOrderedElementsAreEqual(m6OwnerModules, + Arrays.asList(modules.get(0), modules.get(1), modules.get(4), modules.get(5))); + assertOrderedElementsAreEqual(m6DepModules, + Arrays.asList(modules.get(5), modules.get(5), modules.get(5), null)); + + VirtualFile nonMavenM2JavaDir = VfsUtil.findFileByIoFile(new File(getProjectPath(), "nonMavenM2/src/main/java"), true); + assertNotNull(nonMavenM2JavaDir); + // Should be nonMavenM1 -> nonMavenM2, nonMavenM2 -> source + List nonMavenM2JavaOrderEntries = index.getOrderEntries(index.getInfoForFile(nonMavenM2JavaDir)); + List nonMavenM2OwnerModules = orderEntriesToOwnerModules(nonMavenM2JavaOrderEntries); + List nonMavenM2DepModules = orderEntriesToDepModules(nonMavenM2JavaOrderEntries); + assertOrderedElementsAreEqual(nonMavenM2OwnerModules, Arrays.asList(nonMavenM1, nonMavenM2)); + assertOrderedElementsAreEqual(nonMavenM2DepModules, Arrays.asList(nonMavenM2, null)); + } + + private static List orderEntriesToOwnerModules(List orderEntries) { + return ContainerUtil.map(orderEntries, new Function() { + @Override + public Module fun(OrderEntry orderEntry) { return orderEntry.getOwnerModule(); } + }); + } + + private static List orderEntriesToDepModules(List orderEntries) { + return ContainerUtil.map(orderEntries, new Function() { + @Override + public Module fun(OrderEntry orderEntry) { + return (orderEntry instanceof ModuleOrderEntry) ? ((ModuleOrderEntry)orderEntry).getModule() : null; + } + }); + } + private void assertAllProductionClasspath(String moduleName, String... paths) throws Exception { assertCompileProductionClasspath(moduleName, paths); assertRuntimeProductionClasspath(moduleName, paths);