diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ArtifactsTestUtil.java b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ArtifactsTestUtil.java new file mode 100644 index 00000000000..5f62deb092e --- /dev/null +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ArtifactsTestUtil.java @@ -0,0 +1,126 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.codeInsight.gradle; + +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.packaging.artifacts.Artifact; +import com.intellij.packaging.artifacts.ArtifactManager; +import com.intellij.packaging.artifacts.ArtifactType; +import com.intellij.packaging.artifacts.ModifiableArtifactModel; +import com.intellij.packaging.elements.CompositePackagingElement; +import com.intellij.packaging.elements.PackagingElement; +import com.intellij.packaging.elements.PackagingElementFactory; +import com.intellij.packaging.elements.PackagingElementResolvingContext; +import com.intellij.packaging.impl.elements.ArchivePackagingElement; +import com.intellij.packaging.impl.elements.DirectoryPackagingElement; +import com.intellij.packaging.impl.elements.ManifestFileUtil; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import static org.assertj.core.api.Assertions.*; + +public class ArtifactsTestUtil { + public static String printToString(PackagingElement element, int level) { + StringBuilder builder = new StringBuilder(StringUtil.repeatSymbol(' ', level)); + if (element instanceof ArchivePackagingElement) { + builder.append(((ArchivePackagingElement)element).getArchiveFileName()); + } + else if (element instanceof DirectoryPackagingElement) { + builder.append(((DirectoryPackagingElement)element).getDirectoryName()).append("/"); + } + else { + builder.append(element.toString()); + } + builder.append("\n"); + if (element instanceof CompositePackagingElement) { + for (PackagingElement child : ((CompositePackagingElement)element).getChildren()) { + builder.append(printToString(child, level + 1)); + } + } + return builder.toString(); + } + + public static void assertLayout(PackagingElement element, String expected) { + assertThat(printToString(element, 0)).isEqualTo(adjustMultiLine(expected)); + } + + private static String adjustMultiLine(String expected) { + final List strings = StringUtil.split(StringUtil.trimStart(expected, "\n"), "\n"); + int min = Integer.MAX_VALUE; + for (String s : strings) { + int k = 0; + while (k < s.length() && s.charAt(k) == ' ') { + k++; + } + min = Math.min(min, k); + } + List lines = new ArrayList<>(); + for (String s : strings) { + lines.add(s.substring(min)); + } + return StringUtil.join(lines, "\n") + "\n"; + } + + public static void assertLayout(Project project, String artifactName, String expected) { + assertLayout(findArtifact(project, artifactName).getRootElement(), expected); + } + + public static void assertOutputPath(Project project, String artifactName, String expected) { + assertThat(findArtifact(project, artifactName).getOutputPath()).isEqualTo(expected); + } + + public static void assertOutputFileName(Project project, String artifactName, String expected) { + assertThat(findArtifact(project, artifactName).getRootElement().getName()).isEqualTo(expected); + } + + public static void setOutput(final Project project, final String artifactName, final String outputPath) { + WriteAction.runAndWait(() -> { + final ModifiableArtifactModel model = ArtifactManager.getInstance(project).createModifiableModel(); + model.getOrCreateModifiableArtifact(findArtifact(project, artifactName)).setOutputPath(outputPath); + model.commit(); + }); + } + + public static void addArtifactToLayout(final Project project, final Artifact parent, final Artifact toAdd) { + WriteAction.runAndWait(() -> { + final ModifiableArtifactModel model = ArtifactManager.getInstance(project).createModifiableModel(); + final PackagingElement artifactElement = PackagingElementFactory.getInstance().createArtifactElement(toAdd, project); + model.getOrCreateModifiableArtifact(parent).getRootElement().addOrFindChild(artifactElement); + model.commit(); + }); + } + + public static Artifact findArtifact(Project project, String artifactName) { + final ArtifactManager manager = ArtifactManager.getInstance(project); + final Artifact artifact = manager.findArtifact(artifactName); + assertThat(artifact).describedAs("'" + artifactName + "' artifact not found").isNotNull(); + return artifact; + } + + public static void assertManifest(Artifact artifact, PackagingElementResolvingContext context, @Nullable String mainClass, @Nullable String classpath) { + final CompositePackagingElement rootElement = artifact.getRootElement(); + final ArtifactType type = artifact.getArtifactType(); + assertManifest(rootElement, context, type, mainClass, classpath); + } + + public static void assertManifest(CompositePackagingElement rootElement, + PackagingElementResolvingContext context, + ArtifactType type, + @Nullable String mainClass, @Nullable String classpath) { + final VirtualFile file = ManifestFileUtil.findManifestFile(rootElement, context, type); + assertThat(file).isNotNull(); + final Manifest manifest = ManifestFileUtil.readManifest(file); + assertThat(manifest.getMainAttributes().getValue(Attributes.Name.MAIN_CLASS)).isEqualTo(mainClass); + assertThat(manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH)).isEqualTo(classpath); + } +} + diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemImportingTestCase.java b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemImportingTestCase.java index 91921f3626d..44cebb75416 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemImportingTestCase.java +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemImportingTestCase.java @@ -15,27 +15,61 @@ */ package org.jetbrains.kotlin.idea.codeInsight.gradle; +import com.intellij.find.FindManager; +import com.intellij.find.findUsages.FindUsagesHandler; +import com.intellij.find.findUsages.FindUsagesManager; +import com.intellij.find.findUsages.FindUsagesOptions; +import com.intellij.find.impl.FindManagerImpl; import com.intellij.openapi.application.AccessToken; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.compiler.CompilerPaths; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.externalSystem.importing.ImportSpec; import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder; import com.intellij.openapi.externalSystem.model.DataNode; +import com.intellij.openapi.externalSystem.model.ExternalProjectInfo; import com.intellij.openapi.externalSystem.model.ProjectSystemId; import com.intellij.openapi.externalSystem.model.project.ProjectData; +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; +import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotificationListenerAdapter; import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode; +import com.intellij.openapi.externalSystem.service.notification.ExternalSystemProgressNotificationManager; import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback; import com.intellij.openapi.externalSystem.service.project.ProjectDataManager; +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManagerImpl; import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettings; import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.externalSystem.util.ExternalSystemUtil; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; import com.intellij.openapi.roots.*; +import com.intellij.openapi.roots.libraries.Library; +import com.intellij.openapi.roots.libraries.LibraryTablesRegistrar; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.TestDialog; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFileManager; +import com.intellij.packaging.artifacts.Artifact; +import com.intellij.packaging.artifacts.ArtifactManager; +import com.intellij.psi.PsiElement; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.usageView.UsageInfo; +import com.intellij.util.BooleanFunction; +import com.intellij.util.CommonProcessors; +import com.intellij.util.Function; +import com.intellij.util.PathUtil; import com.intellij.util.containers.ConcurrentWeakKeySoftValueHashMap; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; @@ -48,7 +82,17 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Proxy; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + import org.apache.log4j.Level; +import org.jetbrains.jps.model.java.JavaResourceRootType; +import org.jetbrains.jps.model.java.JavaSourceRootProperties; +import org.jetbrains.jps.model.java.JavaSourceRootType; +import org.jetbrains.jps.model.module.JpsModuleSourceRootType; + +import static com.intellij.testFramework.EdtTestUtil.runInEdtAndGet; // part of com.intellij.openapi.externalSystem.test.ExternalSystemImportingTestCase public abstract class ExternalSystemImportingTestCase extends ExternalSystemTestCase { @@ -58,17 +102,354 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest private Logger logger; - @Override - protected Module getModule(String name) { - AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock(); - try { - Module m = ModuleManager.getInstance(myProject).findModuleByName(name); - assertNotNull("Module " + name + " not found", m); - return m; + protected abstract ExternalProjectSettings getCurrentExternalProjectSettings(); + + protected abstract ProjectSystemId getExternalSystemId(); + + // assertion methods + protected void assertModulesContains(@NotNull Project project, String... expectedNames) { + Module[] actual = ModuleManager.getInstance(project).getModules(); + List actualNames = new ArrayList<>(); + + for (Module m : actual) { + actualNames.add(m.getName()); } - finally { - accessToken.finish(); + + assertContain(actualNames, expectedNames); + } + + protected void assertModulesContains(String... expectedNames) { + assertModulesContains(myProject, expectedNames); + } + + protected void assertModules(@NotNull Project project, String... expectedNames) { + Module[] actual = ModuleManager.getInstance(project).getModules(); + List actualNames = new ArrayList<>(); + + for (Module m : actual) { + actualNames.add(m.getName()); } + + assertUnorderedElementsAreEqual(actualNames, expectedNames); + } + + protected void assertModules(String... expectedNames) { + assertModules(myProject, expectedNames); + } + + protected void assertContentRoots(String moduleName, String... expectedRoots) { + List actual = new ArrayList<>(); + for (ContentEntry e : getContentRoots(moduleName)) { + actual.add(e.getUrl()); + } + + for (int i = 0; i < expectedRoots.length; i++) { + expectedRoots[i] = VfsUtilCore.pathToUrl(expectedRoots[i]); + } + + assertUnorderedPathsAreEqual(actual, Arrays.asList(expectedRoots)); + } + + protected void assertSources(String moduleName, String... expectedSources) { + doAssertContentFolders(moduleName, JavaSourceRootType.SOURCE, expectedSources); + } + + protected void assertGeneratedSources(String moduleName, String... expectedSources) { + assertGeneratedSources(moduleName, JavaSourceRootType.SOURCE, expectedSources); + } + + protected void assertGeneratedTestSources(String moduleName, String... expectedSources) { + assertGeneratedSources(moduleName, JavaSourceRootType.TEST_SOURCE, expectedSources); + } + + private void assertGeneratedSources(String moduleName, JavaSourceRootType type, String... expectedSources) { + final ContentEntry[] contentRoots = getContentRoots(moduleName); + String rootUrl = contentRoots.length > 1 ? ExternalSystemApiUtil.getExternalProjectPath(getModule(moduleName)) : null; + List actual = new ArrayList<>(); + + for (ContentEntry contentRoot : contentRoots) { + rootUrl = VirtualFileManager.extractPath(rootUrl == null ? contentRoot.getUrl() : rootUrl); + for (SourceFolder f : contentRoot.getSourceFolders(type)) { + String folderUrl = VirtualFileManager.extractPath(f.getUrl()); + + if (folderUrl.startsWith(rootUrl)) { + int length = rootUrl.length() + 1; + folderUrl = folderUrl.substring(Math.min(length, folderUrl.length())); + } + + JavaSourceRootProperties properties = f.getJpsElement().getProperties(type); + if (properties != null && properties.isForGeneratedSources()) { + actual.add(folderUrl); + } + } + } + + assertOrderedElementsAreEqual(actual, Arrays.asList(expectedSources)); + } + + protected void assertResources(String moduleName, String... expectedSources) { + doAssertContentFolders(moduleName, JavaResourceRootType.RESOURCE, expectedSources); + } + + protected void assertTestSources(String moduleName, String... expectedSources) { + doAssertContentFolders(moduleName, JavaSourceRootType.TEST_SOURCE, expectedSources); + } + + protected void assertTestResources(String moduleName, String... expectedSources) { + doAssertContentFolders(moduleName, JavaResourceRootType.TEST_RESOURCE, expectedSources); + } + + protected void assertExcludes(String moduleName, String... expectedExcludes) { + ContentEntry contentRoot = getContentRoot(moduleName); + doAssertContentFolders(contentRoot, Arrays.asList(contentRoot.getExcludeFolders()), expectedExcludes); + } + + protected void assertContentRootExcludes(String moduleName, String contentRoot, String... expectedExcudes) { + ContentEntry root = getContentRoot(moduleName, contentRoot); + doAssertContentFolders(root, Arrays.asList(root.getExcludeFolders()), expectedExcudes); + } + + private void doAssertContentFolders(String moduleName, @NotNull JpsModuleSourceRootType rootType, String... expected) { + final ContentEntry[] contentRoots = getContentRoots(moduleName); + final String rootUrl = contentRoots.length > 1 ? ExternalSystemApiUtil.getExternalProjectPath(getModule(moduleName)) : null; + doAssertContentFolders(rootUrl, contentRoots, rootType, expected); + } + + protected static List doAssertContentFolders(@Nullable String rootUrl, + ContentEntry[] contentRoots, + @NotNull JpsModuleSourceRootType rootType, + String... expected) { + List result = new ArrayList<>(); + List actual = new ArrayList<>(); + for (ContentEntry contentRoot : contentRoots) { + for (SourceFolder f : contentRoot.getSourceFolders(rootType)) { + rootUrl = VirtualFileManager.extractPath(rootUrl == null ? contentRoot.getUrl() : rootUrl); + String folderUrl = VirtualFileManager.extractPath(f.getUrl()); + if (folderUrl.startsWith(rootUrl)) { + int length = rootUrl.length() + 1; + folderUrl = folderUrl.substring(Math.min(length, folderUrl.length())); + } + + actual.add(folderUrl); + result.add(f); + } + } + + assertOrderedElementsAreEqual(actual, Arrays.asList(expected)); + return result; + } + + private static void doAssertContentFolders(ContentEntry e, final List folders, String... expected) { + List actual = new ArrayList<>(); + for (ContentFolder f : folders) { + String rootUrl = e.getUrl(); + String folderUrl = f.getUrl(); + + if (folderUrl.startsWith(rootUrl)) { + int length = rootUrl.length() + 1; + folderUrl = folderUrl.substring(Math.min(length, folderUrl.length())); + } + + actual.add(folderUrl); + } + + assertOrderedElementsAreEqual(actual, Arrays.asList(expected)); + } + + protected void assertModuleOutputs(String moduleName, String... outputs) { + String[] outputPaths = ContainerUtil.map2Array(CompilerPaths.getOutputPaths(new Module[]{getModule(moduleName)}), String.class, + s -> getAbsolutePath(s)); + assertUnorderedElementsAreEqual(outputPaths, outputs); + } + + protected void assertModuleOutput(String moduleName, String output, String testOutput) { + CompilerModuleExtension e = getCompilerExtension(moduleName); + + assertFalse(e.isCompilerOutputPathInherited()); + assertEquals(output, getAbsolutePath(e.getCompilerOutputUrl())); + assertEquals(testOutput, getAbsolutePath(e.getCompilerOutputUrlForTests())); + } + + protected void assertModuleInheritedOutput(String moduleName) { + CompilerModuleExtension e = getCompilerExtension(moduleName); + assertTrue(e.isCompilerOutputPathInherited()); + } + + protected static String getAbsolutePath(String path) { + path = VfsUtilCore.urlToPath(path); + path = PathUtil.getCanonicalPath(path); + return FileUtil.toSystemIndependentName(path); + } + + protected void assertProjectOutput(String module) { + assertTrue(getCompilerExtension(module).isCompilerOutputPathInherited()); + } + + protected CompilerModuleExtension getCompilerExtension(String module) { + return CompilerModuleExtension.getInstance(getModule(module)); + } + + protected void assertModuleLibDep(String moduleName, String depName) { + assertModuleLibDep(moduleName, depName, null); + } + + protected void assertModuleLibDep(String moduleName, String depName, String classesPath) { + assertModuleLibDep(moduleName, depName, classesPath, null, null); + } + + protected void assertModuleLibDep(String moduleName, String depName, String classesPath, String sourcePath, String javadocPath) { + LibraryOrderEntry lib = ContainerUtil.getFirstItem(getModuleLibDeps(moduleName, depName)); + final String errorMessage = "Failed to find dependency with name [" + depName + "] in module [" + moduleName + "]\n" + + "Available dependencies: " + collectModuleDepsNames(moduleName, LibraryOrderEntry.class); + assertNotNull(errorMessage, lib); + assertModuleLibDepPath(lib, OrderRootType.CLASSES, classesPath == null ? null : Collections.singletonList(classesPath)); + assertModuleLibDepPath(lib, OrderRootType.SOURCES, sourcePath == null ? null : Collections.singletonList(sourcePath)); + assertModuleLibDepPath(lib, JavadocOrderRootType.getInstance(), javadocPath == null ? null : Collections.singletonList(javadocPath)); + } + + protected void assertModuleLibDep(String moduleName, + String depName, + List classesPaths, + List sourcePaths, + List javadocPaths) { + LibraryOrderEntry lib = ContainerUtil.getFirstItem(getModuleLibDeps(moduleName, depName)); + + assertModuleLibDepPath(lib, OrderRootType.CLASSES, classesPaths); + assertModuleLibDepPath(lib, OrderRootType.SOURCES, sourcePaths); + assertModuleLibDepPath(lib, JavadocOrderRootType.getInstance(), javadocPaths); + } + + private static void assertModuleLibDepPath(LibraryOrderEntry lib, OrderRootType type, List paths) { + assertNotNull(lib); + if (paths == null) return; + assertUnorderedPathsAreEqual(Arrays.asList(lib.getRootUrls(type)), paths); + // also check the library because it may contain slight different set of urls (e.g. with duplicates) + final Library library = lib.getLibrary(); + assertNotNull(library); + assertUnorderedPathsAreEqual(Arrays.asList(library.getUrls(type)), paths); + } + + protected void assertModuleLibDepScope(String moduleName, String depName, DependencyScope... scopes) { + List deps = getModuleLibDeps(moduleName, depName); + assertUnorderedElementsAreEqual(ContainerUtil.map2Array(deps, entry -> entry.getScope()), scopes); + } + + protected List getModuleLibDeps(String moduleName, String depName) { + return getModuleDep(moduleName, depName, LibraryOrderEntry.class); + } + + protected void assertModuleLibDeps(String moduleName, String... expectedDeps) { + assertModuleDeps(moduleName, LibraryOrderEntry.class, expectedDeps); + } + + protected void assertModuleLibDeps(BiPredicate predicate, String moduleName, String... expectedDeps) { + assertModuleDeps(predicate, moduleName, LibraryOrderEntry.class, expectedDeps); + } + + protected void assertExportedDeps(String moduleName, String... expectedDeps) { + final List actual = new ArrayList<>(); + + getRootManager(moduleName).orderEntries().withoutSdk().withoutModuleSourceEntries().exportedOnly().process(new RootPolicy() { + @Override + public Object visitModuleOrderEntry(@NotNull ModuleOrderEntry e, Object value) { + actual.add(e.getModuleName()); + return null; + } + + @Override + public Object visitLibraryOrderEntry(@NotNull LibraryOrderEntry e, Object value) { + actual.add(e.getLibraryName()); + return null; + } + }, null); + + assertOrderedElementsAreEqual(actual, expectedDeps); + } + + protected void assertModuleModuleDeps(String moduleName, String... expectedDeps) { + assertModuleDeps(moduleName, ModuleOrderEntry.class, expectedDeps); + } + + private void assertModuleDeps(String moduleName, Class clazz, String... expectedDeps) { + assertModuleDeps(equalsPredicate(), moduleName, clazz, expectedDeps); + } + + private void assertModuleDeps(BiPredicate predicate, String moduleName, Class clazz, String... expectedDeps) { + assertOrderedElementsAreEqual(predicate, collectModuleDepsNames(moduleName, clazz), expectedDeps); + } + + protected void assertProductionOnTestDependencies(String moduleName, String... expectedDeps) { + assertOrderedElementsAreEqual(collectModuleDepsNames( + moduleName, entry -> entry instanceof ModuleOrderEntry && ((ModuleOrderEntry)entry).isProductionOnTestDependency() + ), expectedDeps); + } + + protected void assertModuleModuleDepScope(String moduleName, String depName, DependencyScope... scopes) { + List deps = getModuleModuleDeps(moduleName, depName); + assertUnorderedElementsAreEqual(ContainerUtil.map2Array(deps, entry -> entry.getScope()), scopes); + } + + protected void assertNoModuleDepForModule(String moduleName, String depName) { + assertEmpty("No dependency '" + depName + "' was expected", collectModuleDeps(moduleName, depName, ModuleOrderEntry.class)); + } + + protected void assertNoLibraryDepForModule(String moduleName, String depName) { + assertEmpty("No dependency '" + depName + "' was expected", collectModuleDeps(moduleName, depName, LibraryOrderEntry.class)); + } + + public void assertProjectLibraries(String... expectedNames) { + List actualNames = new ArrayList<>(); + for (Library each : LibraryTablesRegistrar.getInstance().getLibraryTable(myProject).getLibraries()) { + String name = each.getName(); + actualNames.add(name == null ? "" : name); + } + assertUnorderedElementsAreEqual(actualNames, expectedNames); + } + + protected void assertModuleGroupPath(String moduleName, String... expected) { + String[] path = ModuleManager.getInstance(myProject).getModuleGroupPath(getModule(moduleName)); + + if (expected.length == 0) { + assertNull(path); + } + else { + assertNotNull(path); + assertOrderedElementsAreEqual(Arrays.asList(path), expected); + } + } + + protected void assertArtifacts(String... expectedNames) { + final List actualNames = ContainerUtil.map( + ArtifactManager.getInstance(myProject).getAllArtifactsIncludingInvalid(), + (Function)artifact -> artifact.getName()); + + assertUnorderedElementsAreEqual(actualNames, expectedNames); + } + + //end of assertions + + private ContentEntry getContentRoot(String moduleName) { + ContentEntry[] ee = getContentRoots(moduleName); + List roots = new ArrayList<>(); + for (ContentEntry e : ee) { + roots.add(e.getUrl()); + } + + String message = "Several content roots found: [" + StringUtil.join(roots, ", ") + "]"; + assertEquals(message, 1, ee.length); + + return ee[0]; + } + + private ContentEntry getContentRoot(String moduleName, String path) { + for (ContentEntry e : getContentRoots(moduleName)) { + if (e.getUrl().equals(VfsUtilCore.pathToUrl(path))) return e; + } + throw new AssertionError("content root not found"); + } + + public ContentEntry[] getContentRoots(String moduleName) { + return getRootManager(moduleName).getContentEntries(); } protected void importProject(@NonNls String config) throws IOException { @@ -198,71 +579,161 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest private void doImportProject() { AbstractExternalSystemSettings systemSettings = ExternalSystemApiUtil.getSettings(myProject, getExternalSystemId()); - ExternalProjectSettings projectSettings = getCurrentExternalProjectSettings(); + final ExternalProjectSettings projectSettings = getCurrentExternalProjectSettings(); projectSettings.setExternalProjectPath(getProjectPath()); - @SuppressWarnings("unchecked") Set projects = - ContainerUtilRt.newHashSet(systemSettings.getLinkedProjectsSettings()); + //noinspection unchecked + Set projects = new HashSet<>(systemSettings.getLinkedProjectsSettings()); projects.remove(projectSettings); projects.add(projectSettings); //noinspection unchecked systemSettings.setLinkedProjectsSettings(projects); final Ref> error = Ref.create(); - ExternalSystemUtil.refreshProjects( - new TestImportSpecBuilder(myProject, getExternalSystemId()) - .setCreateEmptyContentRoots(projectSettings.isCreateEmptyContentRootDirectories()) - .use(ProgressExecutionMode.MODAL_SYNC) - .callback(new ExternalProjectRefreshCallback() { - @Override - public void onSuccess(@Nullable DataNode externalProject) { - if (externalProject == null) { - System.err.println("Got null External project after import"); - return; - } - inspectForGradleMemoryLeaks(externalProject); - ServiceManager.getService(ProjectDataManager.class).importData(externalProject, myProject, true); - System.out.println("External project was successfully imported"); - } + ImportSpec importSpec = createImportSpec(); + ExternalProjectRefreshCallback callback = importSpec.getCallback(); + if (callback == null || callback instanceof ImportSpecBuilder.DefaultProjectRefreshCallback) { + importSpec = new TestImportSpecBuilder(importSpec) + .setCreateEmptyContentRoots(projectSettings.isCreateEmptyContentRootDirectories()) + .callback(new ExternalProjectRefreshCallback() { + @Override + public void onSuccess(@Nullable final DataNode externalProject) { + if (externalProject == null) { + System.err.println("Got null External project after import"); + return; + } + ServiceManager.getService(ProjectDataManager.class).importData(externalProject, myProject, true); + System.out.println("External project was successfully imported"); - @Override - public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) { - error.set(Couple.of(errorMessage, errorDetails)); - } - }) - .forceWhenUptodate() - ); + inspectForGradleMemoryLeaks(externalProject); + } + + @Override + public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) { + error.set(Couple.of(errorMessage, errorDetails)); + } + }).build(); + } + + ExternalSystemProgressNotificationManager notificationManager = + ServiceManager.getService(ExternalSystemProgressNotificationManager.class); + ExternalSystemTaskNotificationListenerAdapter listener = new ExternalSystemTaskNotificationListenerAdapter() { + @Override + public void onTaskOutput(@NotNull ExternalSystemTaskId id, @NotNull String text, boolean stdOut) { + if (StringUtil.isEmptyOrSpaces(text)) return; + (stdOut ? System.out : System.err).print(text); + } + }; + notificationManager.addNotificationListener(listener); + try { + ExternalSystemUtil.refreshProjects(importSpec); + } + finally { + notificationManager.removeNotificationListener(listener); + } if (!error.isNull()) { - String failureMsg = "Import failed: " + error.get().first; - if (StringUtil.isNotEmpty(error.get().second)) { - failureMsg += "\nError details: \n" + error.get().second; + handleImportFailure(error.get().first, error.get().second); + } + } + + protected void handleImportFailure(@NotNull String errorMessage, @Nullable String errorDetails) { + String failureMsg = "Import failed: " + errorMessage; + if (StringUtil.isNotEmpty(errorDetails)) { + failureMsg += "\nError details: \n" + errorDetails; + } + fail(failureMsg); + } + + protected ImportSpec createImportSpec() { + ImportSpecBuilder importSpecBuilder = new ImportSpecBuilder(myProject, getExternalSystemId()) + .use(ProgressExecutionMode.MODAL_SYNC) + .forceWhenUptodate(); + return importSpecBuilder.build(); + } + + protected void setupJdkForModules(String... moduleNames) { + for (String each : moduleNames) { + setupJdkForModule(each); + } + } + + @Override + protected Sdk setupJdkForModule(final String moduleName) { + final Sdk sdk = true ? JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk() : createJdk("Java 1.5"); + ModuleRootModificationUtil.setModuleSdk(getModule(moduleName), sdk); + return sdk; + } + + protected static Sdk createJdk(String versionName) { + return IdeaTestUtil.getMockJdk17(versionName); + } + + protected static AtomicInteger configConfirmationForYesAnswer() { + final AtomicInteger counter = new AtomicInteger(); + Messages.setTestDialog(new TestDialog() { + @Override + public int show(@NotNull String message) { + counter.set(counter.get() + 1); + return 0; + } + }); + return counter; + } + + protected static AtomicInteger configConfirmationForNoAnswer() { + final AtomicInteger counter = new AtomicInteger(); + Messages.setTestDialog(new TestDialog() { + @Override + public int show(@NotNull String message) { + counter.set(counter.get() + 1); + return 1; + } + }); + return counter; + } + + protected static Collection findUsages(@NotNull PsiElement element) throws Exception { + return ProgressManager.getInstance().run(new Task.WithResult, Exception>(null, "", false) { + @Override + protected Collection compute(@NotNull ProgressIndicator indicator) { + return runInEdtAndGet(() -> { + FindUsagesManager findUsagesManager = ((FindManagerImpl) FindManager.getInstance(element.getProject())).getFindUsagesManager(); + FindUsagesHandler handler = findUsagesManager.getFindUsagesHandler(element, false); + assertNotNull(handler); + final FindUsagesOptions options = handler.getFindUsagesOptions(); + final CommonProcessors.CollectProcessor processor = new CommonProcessors.CollectProcessor<>(); + for (PsiElement element : handler.getPrimaryElements()) { + handler.processElementUsages(element, processor, options); + } + for (PsiElement element : handler.getSecondaryElements()) { + handler.processElementUsages(element, processor, options); + } + return processor.getResults(); + }); + } + }); + } + + @Nullable + protected SourceFolder findSource(@NotNull String moduleName, @NotNull String sourcePath) { + return findSource(getRootManager(moduleName), sourcePath); + } + + @Nullable + protected SourceFolder findSource(@NotNull ModuleRootModel moduleRootManager, @NotNull String sourcePath) { + ContentEntry[] contentRoots = moduleRootManager.getContentEntries(); + Module module = moduleRootManager.getModule(); + String rootUrl = getAbsolutePath(ExternalSystemApiUtil.getExternalProjectPath(module)); + for (ContentEntry contentRoot : contentRoots) { + for (SourceFolder f : contentRoot.getSourceFolders()) { + String folderPath = getAbsolutePath(f.getUrl()); + String rootPath = getAbsolutePath(rootUrl + "/" + sourcePath); + if (folderPath.equals(rootPath)) return f; } - fail(failureMsg); } + return null; } - protected abstract ExternalProjectSettings getCurrentExternalProjectSettings(); - - protected abstract ProjectSystemId getExternalSystemId(); - - protected void assertModuleModuleDepScope(String moduleName, String depName, DependencyScope... scopes) { - List deps = getModuleModuleDeps(moduleName, depName); - Set actualScopes = new HashSet(); - for (ModuleOrderEntry dep : deps) { - actualScopes.add(dep.getScope()); - } - HashSet expectedScopes = new HashSet(Arrays.asList(scopes)); - assertEquals("Dependency '" + depName + "' for module '" + moduleName + "' has unexpected scope", - expectedScopes, actualScopes); - } - - protected void assertNoModuleDepForModule(String moduleName, String depName) { - assertEmpty("No dependency '" + depName + "' was expected", collectModuleDeps(moduleName, depName, ModuleOrderEntry.class)); - } - - protected void assertNoLibraryDepForModule(String moduleName, String depName) { - assertEmpty("No dependency '" + depName + "' was expected", collectModuleDeps(moduleName, depName, LibraryOrderEntry.class)); - } @NotNull private List getModuleModuleDeps(@NotNull String moduleName, @NotNull String depName) { @@ -273,16 +744,31 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest return ModuleRootManager.getInstance(getModule(module)); } + protected void ignoreData(BooleanFunction> booleanFunction, final boolean ignored) { + final ExternalProjectInfo externalProjectInfo = ProjectDataManagerImpl.getInstance().getExternalProjectData( + myProject, getExternalSystemId(), getCurrentExternalProjectSettings().getExternalProjectPath()); + assertNotNull(externalProjectInfo); + + final DataNode projectDataNode = externalProjectInfo.getExternalProjectStructure(); + assertNotNull(projectDataNode); + + final Collection> nodes = ExternalSystemApiUtil.findAllRecursively(projectDataNode, booleanFunction); + for (DataNode node : nodes) { + node.visit(dataNode -> dataNode.setIgnored(ignored)); + } + ServiceManager.getService(ProjectDataManager.class).importData(projectDataNode, myProject, true); + } + @NotNull private List getModuleDep(@NotNull String moduleName, @NotNull String depName, @NotNull Class clazz) { - List deps = collectModuleDeps(moduleName, depName, clazz); - assertTrue("Dependency '" + - depName + - "' for module '" + - moduleName + - "' not found among: " + - collectModuleDepsNames(moduleName, clazz), - !deps.isEmpty()); + List deps = new ArrayList<>(); + + for (OrderEntry e : getRootManager(moduleName).getOrderEntries()) { + if (clazz.isInstance(e) && e.getPresentableName().equals(depName)) { + deps.add((T)e); + } + } + assertNotNull("Dependency for module \"" + moduleName + "\" not found: " + depName + "\namong: " + collectModuleDepsNames(moduleName, clazz), deps); return deps; } @@ -299,14 +785,19 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest return deps; } - private List collectModuleDepsNames(String moduleName, Class clazz) { - List actual = new ArrayList(); + private List collectModuleDepsNames(String moduleName, Predicate predicate) { + List actual = new ArrayList<>(); for (OrderEntry e : getRootManager(moduleName).getOrderEntries()) { - if (clazz.isInstance(e)) { + if (predicate.test(e)) { actual.add(e.getPresentableName()); } } return actual; } + + private List collectModuleDepsNames(String moduleName, Class clazz) { + return collectModuleDepsNames(moduleName, entry -> clazz.isInstance(entry)); + } + } diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemTestCase.java b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemTestCase.java index 35e57d7482b..02bef0cd2fa 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemTestCase.java +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/ExternalSystemTestCase.java @@ -15,156 +15,199 @@ */ package org.jetbrains.kotlin.idea.codeInsight.gradle; -import com.intellij.compiler.CompilerTestUtil; -import com.intellij.openapi.application.AccessToken; +import com.intellij.compiler.impl.ModuleCompileScope; +import com.intellij.compiler.server.BuildManager; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.compiler.CompileScope; +import com.intellij.openapi.compiler.CompilerMessage; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.module.StdModuleTypes; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectUtil; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; +import com.intellij.openapi.roots.ModuleRootModificationUtil; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.ByteArraySequence; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.openapi.util.io.FileUtilRt; +import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; -import com.intellij.testFramework.EdtTestUtil; -import com.intellij.testFramework.UsefulTestCase; +import com.intellij.packaging.artifacts.Artifact; +import com.intellij.packaging.impl.compiler.ArtifactCompileScope; +import com.intellij.task.ProjectTaskManager; +import com.intellij.testFramework.*; import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Processor; -import com.intellij.util.ThrowableRunnable; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ArrayUtilRt; +import com.intellij.util.ExceptionUtil; +import com.intellij.util.PathUtil; +import com.intellij.util.SmartList; +import com.intellij.util.io.PathKt; +import com.intellij.util.io.TestFileSystemItem; +import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.SystemIndependent; +import org.jetbrains.concurrency.Promise; import org.junit.After; import org.junit.Before; import java.awt.*; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Collection; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.util.List; +import java.util.*; +import java.util.function.BiPredicate; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; -import static com.intellij.testFramework.EdtTestUtilKt.runInEdtAndWait; - -// part of com.intellij.openapi.externalSystem.test.ExternalSystemTestCase +/** + * @author Vladislav.Soroka + */ public abstract class ExternalSystemTestCase extends UsefulTestCase { + private static final BiPredicate EQUALS_PREDICATE = (t, u) -> Objects.equals(t, u); + + private File ourTempDir; + protected IdeaProjectTestFixture myTestFixture; - protected Project myProject; - - private VirtualFile myProjectRoot; - private final List myAllConfigs = new ArrayList(); + protected File myTestDir; + protected VirtualFile myProjectRoot; + protected VirtualFile myProjectConfig; + protected List myAllConfigs = new ArrayList<>(); + protected boolean useProjectTaskManager; @Before @Override public void setUp() throws Exception { super.setUp(); + ensureTempDirCreated(); + + myTestDir = ourTempDir;//TODO was new File(ourTempDir, getTestName(false)); but paths appear to be too long + FileUtil.ensureExists(myTestDir); setUpFixtures(); myProject = myTestFixture.getProject(); - invokeTestRunnable(new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - try { - setUpInWriteAction(); - } - catch (Throwable e) { - try { - tearDown(); - } - catch (Exception e1) { - e1.printStackTrace(); - } - throw new RuntimeException(e); - } - } - }); + EdtTestUtil.runInEdtAndWait(() -> ApplicationManager.getApplication().runWriteAction(() -> { + try { + setUpInWriteAction(); } - }); + catch (Throwable e) { + try { + tearDown(); + } + catch (Exception e1) { + e1.printStackTrace(); + } + throw new RuntimeException(e); + } + })); - List allowedRoots = new ArrayList(); + List allowedRoots = new ArrayList<>(); collectAllowedRoots(allowedRoots); if (!allowedRoots.isEmpty()) { - VfsRootAccess.allowRootAccess(getTestRootDisposable(), ArrayUtil.toStringArray(allowedRoots)); + VfsRootAccess.allowRootAccess(myTestFixture.getTestRootDisposable(), ArrayUtilRt.toStringArray(allowedRoots)); } - - CompilerTestUtil.enableExternalCompiler(); } - protected void collectAllowedRoots(List roots) throws IOException { + protected void collectAllowedRoots(List roots) { } public static Collection collectRootsInside(String root) { - final List roots = ContainerUtil.newSmartList(); + final List roots = new SmartList<>(); roots.add(root); - FileUtil.processFilesRecursively(new File(root), new Processor() { - @Override - public boolean process(File file) { - try { - String path = file.getCanonicalPath(); - if (!FileUtil.isAncestor(path, path, false)) { - roots.add(path); - } + FileUtil.processFilesRecursively(new File(root), file -> { + try { + String path = file.getCanonicalPath(); + if (!FileUtil.isAncestor(path, path, false)) { + roots.add(path); } - catch (IOException ignore) { - } - return true; } + catch (IOException ignore) { + } + return true; }); return roots; } + private void ensureTempDirCreated() throws IOException { + if (ourTempDir != null) return; + + ourTempDir = new File(FileUtil.getTempDirectory(), getTestsTempDir()); + FileUtil.delete(ourTempDir); + FileUtil.ensureExists(ourTempDir); + } + + protected final String getTestsTempDir() { + return ""; + } + protected void setUpFixtures() throws Exception { - myTestFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getName()).getFixture(); + myTestFixture = IdeaTestFixtureFactory.getFixtureFactory().createFixtureBuilder(getName(), useDirectoryBasedStorageFormat()).getFixture(); myTestFixture.setUp(); } - private void setUpInWriteAction() throws Exception { - File projectDir = FileUtil.createTempDirectory("project", "", false); + protected boolean useDirectoryBasedStorageFormat() { + return false; + } + + protected void setUpInWriteAction() throws Exception { + File projectDir = new File(myTestDir, "project"); + FileUtil.ensureExists(projectDir); myProjectRoot = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(projectDir); } @After @Override public void tearDown() throws Exception { - try { - EdtTestUtil.runInEdtAndWait(new ThrowableRunnable() { - @Override - public void run() throws Throwable { - CompilerTestUtil.disableExternalCompiler(myProject); - tearDownFixtures(); - } - }); - myProject = null; - } - finally { - super.tearDown(); - resetClassFields(getClass()); - } + new RunAll( + () -> { + if (myProject != null && !myProject.isDisposed()) { + PathKt.delete(ProjectUtil.getExternalConfigurationDir(myProject)); + } + }, + () -> EdtTestUtil.runInEdtAndWait(() -> tearDownFixtures()), + () -> myProject = null, + () -> PathKt.delete(myTestDir.toPath()), + () -> super.tearDown(), + () -> resetClassFields(getClass()) + ).run(); } - protected void tearDownFixtures() throws Exception { - myTestFixture.tearDown(); + protected void tearDownFixtures() { + if (myTestFixture != null) { + try { + myTestFixture.tearDown(); + } + catch (Exception ignored) { + } + } myTestFixture = null; } - private void resetClassFields(Class aClass) { + private void resetClassFields(final Class aClass) { if (aClass == null) return; - Field[] fields = aClass.getDeclaredFields(); + final Field[] fields = aClass.getDeclaredFields(); for (Field field : fields) { final int modifiers = field.getModifiers(); if ((modifiers & Modifier.FINAL) == 0 @@ -187,7 +230,17 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase { @Override protected void runTest() throws Throwable { try { - super.runTest(); + if (runInWriteAction()) { + try { + WriteAction.runAndWait(() -> super.runTest()); + } + catch (Throwable throwable) { + ExceptionUtil.rethrowAllAsUnchecked(throwable); + } + } + else { + super.runTest(); + } } catch (Exception throwable) { Throwable each = throwable; @@ -203,11 +256,12 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase { } @Override - protected void invokeTestRunnable(@NotNull Runnable runnable) throws Exception { - runInEdtAndWait(() -> { - runnable.run(); - return null; - }); + protected void invokeTestRunnable(@NotNull Runnable runnable) { + runnable.run(); + } + + protected boolean runInWriteAction() { + return false; } protected static String getRoot() { @@ -215,74 +269,356 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase { return ""; } + protected static String getEnvVar() { + if (SystemInfo.isWindows) return "TEMP"; + else if (SystemInfo.isLinux) return "HOME"; + return "TMPDIR"; + } + protected String getProjectPath() { return myProjectRoot.getPath(); } - protected VirtualFile createProjectConfig(@NonNls String config) throws IOException { - return createConfigFile(myProjectRoot, config); + protected String getParentPath() { + return myProjectRoot.getParent().getPath(); } - private VirtualFile createConfigFile(final VirtualFile dir, String config) throws IOException { - final String configFileName = getExternalSystemConfigFileName(); - VirtualFile f = dir.findChild(configFileName); - if (f == null) { - f = new WriteAction() { - @Override - protected void run(@NotNull Result result) throws Throwable { - VirtualFile res = dir.createChildData(null, configFileName); - result.setResult(res); - } - }.execute().getResultObject(); - myAllConfigs.add(f); + @SystemIndependent + protected String path(@NotNull String relativePath) { + return PathUtil.toSystemIndependentName(file(relativePath).getPath()); + } + + protected File file(@NotNull String relativePath) { + return new File(getProjectPath(), relativePath); + } + + protected Module createModule(String name) { + return createModule(name, StdModuleTypes.JAVA); + } + + protected Module createModule(final String name, final ModuleType type) { + try { + return WriteCommandAction.writeCommandAction(myProject).compute(() -> { + VirtualFile f = createProjectSubFile(name + "/" + name + ".iml"); + Module module = ModuleManager.getInstance(myProject).newModule(f.getPath(), type.getId()); + PsiTestUtil.addContentRoot(module, f.getParent()); + return module; + }); } - setFileContent(f, config, true); - return f; + catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected VirtualFile createProjectConfig(@NonNls String config) { + return myProjectConfig = createConfigFile(myProjectRoot, config); + } + + protected VirtualFile createConfigFile(final VirtualFile dir, String config) { + final String configFileName = getExternalSystemConfigFileName(); + VirtualFile configFile; + try { + configFile = WriteAction.computeAndWait(() -> { + VirtualFile file = dir.findChild(configFileName); + return file == null ? dir.createChildData(null, configFileName) : file; + }); + myAllConfigs.add(configFile); + } + catch (IOException e) { + throw new RuntimeException(e); + } + setFileContent(configFile, config, true); + return configFile; } protected abstract String getExternalSystemConfigFileName(); + protected void createStdProjectFolders() throws IOException { + createProjectSubDirs("src/main/java", + "src/main/resources", + "src/test/java", + "src/test/resources"); + } + + protected void createProjectSubDirs(String... relativePaths) throws IOException { + for (String path : relativePaths) { + createProjectSubDir(path); + } + } + + protected VirtualFile createProjectSubDir(String relativePath) throws IOException { + File f = new File(getProjectPath(), relativePath); + FileUtil.ensureExists(f); + return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f); + } + protected VirtualFile createProjectSubFile(String relativePath) throws IOException { File f = new File(getProjectPath(), relativePath); FileUtil.ensureExists(f.getParentFile()); FileUtil.ensureCanCreateFile(f); - boolean created = f.createNewFile(); - if (!created) { + final boolean created = f.createNewFile(); + if (!created && !f.exists()) { throw new AssertionError("Unable to create the project sub file: " + f.getAbsolutePath()); } return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f); } + @NotNull + protected VirtualFile createProjectJarSubFile(String relativePath, Pair... contentEntries) throws IOException { + assertTrue("Use 'jar' extension for JAR files: '" + relativePath + "'", FileUtilRt.extensionEquals(relativePath, "jar")); + File f = new File(getProjectPath(), relativePath); + FileUtil.ensureExists(f.getParentFile()); + FileUtil.ensureCanCreateFile(f); + final boolean created = f.createNewFile(); + if (!created) { + throw new AssertionError("Unable to create the project sub file: " + f.getAbsolutePath()); + } + + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + JarOutputStream target = new JarOutputStream(new FileOutputStream(f), manifest); + for (Pair contentEntry : contentEntries) { + addJarEntry(contentEntry.first.getBytes(), contentEntry.second, target); + } + target.close(); + + final VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(f); + assertNotNull(virtualFile); + final VirtualFile jarFile = JarFileSystem.getInstance().getJarRootForLocalFile(virtualFile); + assertNotNull(jarFile); + return jarFile; + } + + private static void addJarEntry(byte[] bytes, String path, JarOutputStream target) throws IOException { + JarEntry entry = new JarEntry(path.replace("\\", "/")); + target.putNextEntry(entry); + target.write(bytes); + target.close(); + } + protected VirtualFile createProjectSubFile(String relativePath, String content) throws IOException { VirtualFile file = createProjectSubFile(relativePath); setFileContent(file, content, false); return file; } - protected Module getModule(String name) { - AccessToken accessToken = ApplicationManager.getApplication().acquireReadActionLock(); - try { - Module m = ModuleManager.getInstance(myProject).findModuleByName(name); - assertNotNull("Module " + name + " not found", m); - return m; + + protected void compileModules(final String... moduleNames) { + if (useProjectTaskManager) { + Module[] modules = Arrays.stream(moduleNames).map(moduleName -> getModule(moduleName)).toArray(Module[]::new); + build(modules); } - finally { - accessToken.finish(); + else { + compile(createModulesCompileScope(moduleNames)); } } - protected static void setFileContent(final VirtualFile file, final String content, final boolean advanceStamps) throws IOException { - new WriteAction() { - @Override - protected void run(@NotNull Result result) throws Throwable { - if (advanceStamps) { - file.setBinaryContent(content.getBytes(CharsetToolkit.UTF8_CHARSET), -1, file.getTimeStamp() + 4000); - } - else { - file.setBinaryContent(content.getBytes(CharsetToolkit.UTF8_CHARSET), file.getModificationStamp(), file.getTimeStamp()); + protected void buildArtifacts(String... artifactNames) { + if (useProjectTaskManager) { + Artifact[] artifacts = Arrays.stream(artifactNames) + .map(artifactName -> ArtifactsTestUtil.findArtifact(myProject, artifactName)).toArray(Artifact[]::new); + build(artifacts); + } + else { + compile(createArtifactsScope(artifactNames)); + } + } + + private void build(Object[] buildableElements) { + Promise promise; + if (buildableElements instanceof Module[]) { + promise = ProjectTaskManager.getInstance(myProject).build((Module[])buildableElements); + } + else if (buildableElements instanceof Artifact[]) { + promise = ProjectTaskManager.getInstance(myProject).build((Artifact[])buildableElements); + } + else { + throw new AssertionError("Unsupported buildableElements: " + Arrays.toString(buildableElements)); + } + edt(() -> PlatformTestUtil.waitForPromise(promise)); + } + + private void compile(@NotNull CompileScope scope) { + try { + CompilerTester tester = new CompilerTester(myProject, Arrays.asList(scope.getAffectedModules()), null); + try { + List messages = tester.make(scope); + for (CompilerMessage message : messages) { + switch (message.getCategory()) { + case ERROR: + fail("Compilation failed with error: " + message.getMessage()); + break; + case WARNING: + System.out.println("Compilation warning: " + message.getMessage()); + break; + case INFORMATION: + break; + case STATISTICS: + break; + } } } - }.execute().getResultObject(); + finally { + tester.tearDown(); + } + } + catch (Exception e) { + ExceptionUtil.rethrow(e); + } + } + + + private CompileScope createModulesCompileScope(final String[] moduleNames) { + final List modules = new ArrayList<>(); + for (String name : moduleNames) { + modules.add(getModule(name)); + } + return new ModuleCompileScope(myProject, modules.toArray(Module.EMPTY_ARRAY), false); + } + + private CompileScope createArtifactsScope(String[] artifactNames) { + List artifacts = new ArrayList<>(); + for (String name : artifactNames) { + artifacts.add(ArtifactsTestUtil.findArtifact(myProject, name)); + } + return ArtifactCompileScope.createArtifactsScope(myProject, artifacts); + } + + protected Sdk setupJdkForModule(final String moduleName) { + final Sdk sdk = true ? JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk() : createJdk(); + ModuleRootModificationUtil.setModuleSdk(getModule(moduleName), sdk); + return sdk; + } + + protected static Sdk createJdk() { + return IdeaTestUtil.getMockJdk17(); + } + + protected Module getModule(final String name) { + return getModule(myProject, name); + } + + protected Module getModule(Project project, String name) { + Module m = ReadAction.compute(() -> ModuleManager.getInstance(project).findModuleByName(name)); + assertNotNull("Module " + name + " not found", m); + return m; + } + + protected void assertExplodedLayout(String artifactName, String expected) { + assertJarLayout(artifactName + " exploded", expected); + } + + protected void assertJarLayout(String artifactName, String expected) { + ArtifactsTestUtil.assertLayout(myProject, artifactName, expected); + } + + protected void assertArtifactOutputPath(final String artifactName, final String expected) { + ArtifactsTestUtil.assertOutputPath(myProject, artifactName, expected); + } + + protected void assertArtifactOutputFileName(final String artifactName, final String expected) { + ArtifactsTestUtil.assertOutputFileName(myProject, artifactName, expected); + } + + protected void assertArtifactOutput(String artifactName, TestFileSystemItem fs) { + final Artifact artifact = ArtifactsTestUtil.findArtifact(myProject, artifactName); + final String outputFile = artifact.getOutputFilePath(); + assert outputFile != null; + final File file = new File(outputFile); + assert file.exists(); + fs.assertFileEqual(file); + } + + protected static void setFileContent(final VirtualFile file, final String content, final boolean advanceStamps) { + try { + WriteAction.runAndWait(() -> { + if (advanceStamps) { + file.setBinaryContent(content.getBytes(StandardCharsets.UTF_8), -1, file.getTimeStamp() + 4000); + } + else { + file.setBinaryContent(content.getBytes(StandardCharsets.UTF_8), file.getModificationStamp(), file.getTimeStamp()); + } + }); + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected static void assertOrderedElementsAreEqual(Collection actual, Collection expected) { + assertOrderedElementsAreEqual(actual, expected.toArray()); + } + + protected static void assertUnorderedElementsAreEqual(Collection actual, Collection expected) { + assertEquals(new HashSet<>(expected), new HashSet<>(actual)); + } + + protected static void assertUnorderedPathsAreEqual(Collection actual, Collection expected) { + assertEquals(new SetWithToString<>(new THashSet<>(expected, FileUtil.PATH_HASHING_STRATEGY)), + new SetWithToString<>(new THashSet<>(actual, FileUtil.PATH_HASHING_STRATEGY))); + } + + protected static void assertUnorderedElementsAreEqual(T[] actual, T... expected) { + assertUnorderedElementsAreEqual(Arrays.asList(actual), expected); + } + + protected static void assertUnorderedElementsAreEqual(Collection actual, T... expected) { + assertUnorderedElementsAreEqual(actual, Arrays.asList(expected)); + } + + protected static void assertOrderedElementsAreEqual(Collection actual, T... expected) { + assertOrderedElementsAreEqual(equalsPredicate(), actual, expected); + } + + protected static void assertOrderedElementsAreEqual(BiPredicate predicate, Collection actual, T... expected) { + String s = "\nexpected: " + Arrays.asList(expected) + "\nactual: " + new ArrayList<>(actual); + assertEquals(s, expected.length, actual.size()); + + java.util.List actualList = new ArrayList<>(actual); + for (int i = 0; i < expected.length; i++) { + T expectedElement = expected[i]; + U actualElement = actualList.get(i); + assertTrue(s, predicate.test(actualElement, expectedElement)); + } + } + + protected static void assertContain(java.util.List actual, T... expected) { + java.util.List expectedList = Arrays.asList(expected); + assertTrue("expected: " + expectedList + "\n" + "actual: " + actual.toString(), actual.containsAll(expectedList)); + } + + protected static void assertDoNotContain(java.util.List actual, T... expected) { + java.util.List actualCopy = new ArrayList<>(actual); + actualCopy.removeAll(Arrays.asList(expected)); + assertEquals(actual.toString(), actualCopy.size(), actual.size()); + } + + protected boolean ignore() { + printIgnoredMessage(null); + return true; + } + + protected static BiPredicate equalsPredicate() { + //noinspection unchecked + return (BiPredicate)EQUALS_PREDICATE; + } + + public static void deleteBuildSystemDirectory() { + BuildManager buildManager = BuildManager.getInstance(); + if (buildManager == null) return; + Path buildSystemDirectory = buildManager.getBuildSystemDirectory(); + try { + PathKt.delete(buildSystemDirectory); + return; + } + catch (Exception ignore) { + } + try { + FileUtil.delete(buildSystemDirectory.toFile()); + } + catch (Exception e) { + LOG.warn("Unable to remove build system directory.", e); + } } private void printIgnoredMessage(String message) { @@ -293,4 +629,44 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase { toPrint += ": " + getClass().getSimpleName() + "." + getName(); System.out.println(toPrint); } + + private static class SetWithToString extends AbstractSet { + + private final Set myDelegate; + + SetWithToString(@NotNull Set delegate) { + myDelegate = delegate; + } + + @Override + public int size() { + return myDelegate.size(); + } + + @Override + public boolean contains(Object o) { + return myDelegate.contains(o); + } + + @NotNull + @Override + public Iterator iterator() { + return myDelegate.iterator(); + } + + @Override + public boolean containsAll(Collection c) { + return myDelegate.containsAll(c); + } + + @Override + public boolean equals(Object o) { + return myDelegate.equals(o); + } + + @Override + public int hashCode() { + return myDelegate.hashCode(); + } + } } diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleImportingTestCase.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleImportingTestCase.kt index 95a619942d8..6786138e74d 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleImportingTestCase.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleImportingTestCase.kt @@ -15,10 +15,11 @@ */ package org.jetbrains.kotlin.idea.codeInsight.gradle -import com.intellij.compiler.server.BuildManager import com.intellij.openapi.application.PathManager import com.intellij.openapi.application.Result import com.intellij.openapi.application.WriteAction +import com.intellij.openapi.externalSystem.importing.ImportSpec +import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder import com.intellij.openapi.externalSystem.model.ProjectSystemId import com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings @@ -28,19 +29,28 @@ import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.fileEditor.impl.LoadTextUtil import com.intellij.openapi.fileTypes.FileTypeManager import com.intellij.openapi.projectRoots.JavaSdk +import com.intellij.openapi.projectRoots.ProjectJdkTable +import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.TestDialog import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess import com.intellij.testFramework.IdeaTestUtil +import com.intellij.testFramework.RunAll import com.intellij.testFramework.VfsTestUtil +import com.intellij.util.ArrayUtilRt import com.intellij.util.PathUtil +import com.intellij.util.SmartList +import com.intellij.util.ThrowableRunnable import com.intellij.util.containers.ContainerUtil import junit.framework.TestCase +import org.gradle.StartParameter import org.gradle.util.GradleVersion import org.gradle.wrapper.GradleWrapperMain +import org.gradle.wrapper.PathAssembler import org.intellij.lang.annotations.Language import org.jetbrains.annotations.NonNls import org.jetbrains.kotlin.idea.test.KotlinSdkCreationChecker @@ -52,7 +62,9 @@ import org.jetbrains.kotlin.test.RunnerFactoryWithMuteInDatabase import org.jetbrains.plugins.gradle.settings.DistributionType import org.jetbrains.plugins.gradle.settings.GradleProjectSettings import org.jetbrains.plugins.gradle.settings.GradleSettings +import org.jetbrains.plugins.gradle.settings.GradleSystemSettings import org.jetbrains.plugins.gradle.util.GradleConstants +import org.jetbrains.plugins.gradle.util.GradleUtil import org.jetbrains.plugins.groovy.GroovyFileType import org.junit.AfterClass import org.junit.Assume.assumeThat @@ -67,6 +79,8 @@ import java.io.IOException import java.io.StringWriter import java.net.URISyntaxException import java.util.* +import java.util.zip.ZipException +import java.util.zip.ZipFile // part of org.jetbrains.plugins.gradle.importing.GradleImportingTestCase @RunWith(value = JUnitParameterizedWithIdeaConfigurationRunner::class) @@ -75,6 +89,8 @@ abstract class GradleImportingTestCase : ExternalSystemImportingTestCase() { protected var sdkCreationChecker : KotlinSdkCreationChecker? = null + private val removedSdks: MutableList = SmartList() + @JvmField @Rule var name = TestName() @@ -112,6 +128,7 @@ abstract class GradleImportingTestCase : ExternalSystemImportingTestCase() { super.setUp() assumeTrue(isApplicableTest()) assumeThat(gradleVersion, versionMatcherRule.matcher) + removedSdks.clear() runWrite { val jdkTable = getProjectJdkTableSafe() jdkTable.findJdk(GRADLE_JDK_NAME)?.let { @@ -127,47 +144,77 @@ abstract class GradleImportingTestCase : ExternalSystemImportingTestCase() { myProjectSettings = GradleProjectSettings().apply { this.isUseQualifiedModuleNames = false } + System.setProperty(ExternalSystemExecutionSettings.REMOTE_PROCESS_IDLE_TTL_IN_MS_KEY, GRADLE_DAEMON_TTL_MS.toString()) + + val distribution = WriteAction.computeAndWait { configureWrapper() } + + val allowedRoots = ArrayList() + collectAllowedRoots(allowedRoots, distribution) + if (!allowedRoots.isEmpty()) { + VfsRootAccess.allowRootAccess(myTestFixture.testRootDisposable, *ArrayUtilRt.toStringArray(allowedRoots)) + } GradleSettings.getInstance(myProject).gradleVmOptions = "${jvmHeapArgsByGradleVersion(gradleVersion)} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${System.getProperty("user.dir")}" - System.setProperty(ExternalSystemExecutionSettings.REMOTE_PROCESS_IDLE_TTL_IN_MS_KEY, GRADLE_DAEMON_TTL_MS.toString()) - configureWrapper() sdkCreationChecker = KotlinSdkCreationChecker() } override fun tearDown() { - try { - runWrite { - val old = getProjectJdkTableSafe().findJdk(GRADLE_JDK_NAME) - if (old != null) { - SdkConfigurationUtil.removeSdk(old) - } - } - - Messages.setTestDialog(TestDialog.DEFAULT) - FileUtil.delete(BuildManager.getInstance().buildSystemDirectory.toFile()) - sdkCreationChecker?.removeNewKotlinSdk() - } finally { - super.tearDown() + if (myJdkHome == null) { + //super.setUp() wasn't called + return } + RunAll( + ThrowableRunnable { + runWrite { + Arrays.stream(ProjectJdkTable.getInstance().allJdks).forEach { jdk: Sdk -> + ProjectJdkTable.getInstance().removeJdk(jdk) + } + for (sdk in removedSdks) { + SdkConfigurationUtil.addSdk(sdk) + } + removedSdks.clear() + } + }, + ThrowableRunnable { + Messages.setTestDialog(TestDialog.DEFAULT) + deleteBuildSystemDirectory() + // was FileUtil.delete(BuildManager.getInstance().buildSystemDirectory.toFile()) + sdkCreationChecker?.removeNewKotlinSdk() + }, + ThrowableRunnable { + super.tearDown() + } + ).run() } override fun collectAllowedRoots(roots: MutableList) { + super.collectAllowedRoots(roots) roots.add(myJdkHome) roots.addAll(ExternalSystemTestCase.collectRootsInside(myJdkHome)) roots.add(PathManager.getConfigPath()) } + protected open fun collectAllowedRoots( + roots: MutableList, + distribution: PathAssembler.LocalDistribution? + ) { + //Note: could be required to use: + //Environment.getEnvVariable("JAVA_HOME") + roots.add(myJdkHome) + } + override fun getName(): String { return if (name.methodName == null) super.getName() else FileUtil.sanitizeFileName(name.methodName) } override fun getExternalSystemConfigFileName(): String = "build.gradle" - protected fun importProjectUsingSingeModulePerGradleProject() { - myProjectSettings.isResolveModulePerSourceSet = false - importProject() + @Throws(IOException::class) + protected open fun importProjectUsingSingeModulePerGradleProject(config: String? = null) { + currentExternalProjectSettings.isResolveModulePerSourceSet = false + importProject(config) } override fun importProject() { @@ -185,28 +232,43 @@ abstract class GradleImportingTestCase : ExternalSystemImportingTestCase() { super.importProject() } - override fun importProject(@NonNls @Language("Groovy") config: String) { - super.importProject( - """ - allprojects { - repositories { - maven { - url 'https://maven.labs.intellij.net/repo1' - } - } - } + @Throws(IOException::class) + override fun importProject(@NonNls @Language("Groovy") config: String?) { + var config = config + config = injectRepo(config) + super.importProject(config) + } - $config - """.trimIndent() - ) + protected open fun injectRepo(@NonNls @Language("Groovy") config: String?): String { + var config = config ?: "" + config = """allprojects { + repositories { + maven { + url 'https://repo.labs.intellij.net/repo1' + } + }} + $config""" + return config + } + + + override fun createImportSpec(): ImportSpec? { + val importSpecBuilder = ImportSpecBuilder(super.createImportSpec()) + importSpecBuilder.withArguments("--stacktrace") + return importSpecBuilder.build() } override fun getCurrentExternalProjectSettings(): GradleProjectSettings = myProjectSettings override fun getExternalSystemId(): ProjectSystemId = GradleConstants.SYSTEM_ID + @Throws(IOException::class) + protected open fun createSettingsFile(@NonNls @Language("Groovy") content: String?): VirtualFile? { + return createProjectSubFile("settings.gradle", content) + } + @Throws(IOException::class, URISyntaxException::class) - private fun configureWrapper() { + private fun configureWrapper(): PathAssembler.LocalDistribution { val distributionUri = AbstractModelBuilderTest.DistributionLocator().getDistributionFor(GradleVersion.version(gradleVersion)) myProjectSettings.distributionType = DistributionType.DEFAULT_WRAPPED @@ -228,6 +290,27 @@ abstract class GradleImportingTestCase : ExternalSystemImportingTestCase() { properties.store(writer, null) createProjectSubFile("gradle/wrapper/gradle-wrapper.properties", writer.toString()) + + val wrapperConfiguration = + GradleUtil.getWrapperConfiguration(projectPath) + val localDistribution = PathAssembler( + StartParameter.DEFAULT_GRADLE_USER_HOME + ).getDistribution(wrapperConfiguration) + + val zip = localDistribution.zipFile + try { + if (zip.exists()) { + val zipFile = ZipFile(zip) + zipFile.close() + } + } catch (e: ZipException) { + e.printStackTrace() + println("Corrupted file will be removed: " + zip.path) + FileUtil.delete(zip) + } catch (e: IOException) { + e.printStackTrace() + } + return localDistribution } protected open fun testDataDirName(): String = "" @@ -290,7 +373,6 @@ abstract class GradleImportingTestCase : ExternalSystemImportingTestCase() { } } - private fun runWrite(f: () -> Unit) { object : WriteAction() { override fun run(result: Result) { @@ -299,6 +381,10 @@ abstract class GradleImportingTestCase : ExternalSystemImportingTestCase() { }.execute() } + protected open fun enableGradleDebugWithSuspend() { + GradleSystemSettings.getInstance().gradleVmOptions = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" + } + companion object { const val GRADLE_JDK_NAME = "Gradle JDK" private const val GRADLE_DAEMON_TTL_MS = 10000 diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/TestImportSpecBuilder.java b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/TestImportSpecBuilder.java index 414b12011eb..38a0e038627 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/TestImportSpecBuilder.java +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/TestImportSpecBuilder.java @@ -13,10 +13,9 @@ import org.jetbrains.annotations.NotNull; public class TestImportSpecBuilder extends ImportSpecBuilder { public TestImportSpecBuilder( - @NotNull Project project, - @NotNull ProjectSystemId id + @NotNull ImportSpec importSpec ) { - super(project, id); + super(importSpec); } public ImportSpecBuilder setCreateEmptyContentRoots(boolean value) {