diff --git a/java/java-analysis-impl/src/com/intellij/openapi/roots/JavaProjectRootsUtil.java b/java/java-analysis-impl/src/com/intellij/openapi/roots/JavaProjectRootsUtil.java index fb3ee078ae7c..03ac166a33cc 100644 --- a/java/java-analysis-impl/src/com/intellij/openapi/roots/JavaProjectRootsUtil.java +++ b/java/java-analysis-impl/src/com/intellij/openapi/roots/JavaProjectRootsUtil.java @@ -54,18 +54,22 @@ public class JavaProjectRootsUtil { public static List getSuitableDestinationSourceRoots(@NotNull Project project) { List roots = new ArrayList(); for (Module module : ModuleManager.getInstance(project).getModules()) { - for (ContentEntry entry : ModuleRootManager.getInstance(module).getContentEntries()) { - for (SourceFolder sourceFolder : entry.getSourceFolders(JavaModuleSourceRootTypes.SOURCES)) { - if (!isForGeneratedSources(sourceFolder)) { - ContainerUtil.addIfNotNull(roots, sourceFolder.getFile()); - } - } - } + collectSuitableDestinationSourceRoots(module, roots); } return roots; } - private static boolean isForGeneratedSources(SourceFolder sourceFolder) { + public static void collectSuitableDestinationSourceRoots(@NotNull Module module, @NotNull List result) { + for (ContentEntry entry : ModuleRootManager.getInstance(module).getContentEntries()) { + for (SourceFolder sourceFolder : entry.getSourceFolders(JavaModuleSourceRootTypes.SOURCES)) { + if (!isForGeneratedSources(sourceFolder)) { + ContainerUtil.addIfNotNull(result, sourceFolder.getFile()); + } + } + } + } + + public static boolean isForGeneratedSources(SourceFolder sourceFolder) { JavaSourceRootProperties properties = sourceFolder.getJpsElement().getProperties(JavaModuleSourceRootTypes.SOURCES); JavaResourceRootProperties resourceProperties = sourceFolder.getJpsElement().getProperties(JavaModuleSourceRootTypes.RESOURCES); return properties != null && properties.isForGeneratedSources() || resourceProperties != null && resourceProperties.isForGeneratedSources(); diff --git a/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestAction.java b/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestAction.java index 0fe0941201ed..9bea740c758c 100644 --- a/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestAction.java +++ b/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestAction.java @@ -22,12 +22,13 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.JavaProjectRootsUtil; import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.SourceFolder; import com.intellij.openapi.roots.TestModuleProperties; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VirtualFile; @@ -39,9 +40,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.JavaSourceRootType; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class CreateTestAction extends PsiElementBaseIntentionAction { @@ -98,6 +100,8 @@ public class CreateTestAction extends PsiElementBaseIntentionAction { @Override public void invoke(final @NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { final Module srcModule = ModuleUtilCore.findModuleForPsiElement(element); + if (srcModule == null) return; + final PsiClass srcClass = getContainingClass(element); if (srcClass == null) return; @@ -106,19 +110,20 @@ public class CreateTestAction extends PsiElementBaseIntentionAction { PsiPackage srcPackage = JavaDirectoryService.getInstance().getPackage(srcDir); final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(); - final HashSet testFolders = new HashSet(); - checkForTestRoots(srcModule, testFolders); - if (testFolders.isEmpty() && !propertiesComponent.getBoolean(CREATE_TEST_IN_THE_SAME_ROOT)) { - if (Messages.showOkCancelDialog(project, "Create test in the same source root?", "No Test Roots Found", Messages.getQuestionIcon()) != - Messages.OK) { - return; + Module testModule = suggestModuleForTests(project, srcModule); + final List testRootUrls = computeTestRoots(testModule); + if (testRootUrls.isEmpty() && computeSuitableTestRootUrls(testModule).isEmpty()) { + testModule = srcModule; + if (!propertiesComponent.getBoolean(CREATE_TEST_IN_THE_SAME_ROOT)) { + if (Messages.showOkCancelDialog(project, "Create test in the same source root?", "No Test Roots Found", Messages.getQuestionIcon()) != + Messages.OK) { + return; + } + propertiesComponent.setValue(CREATE_TEST_IN_THE_SAME_ROOT, true); } - - propertiesComponent.setValue(CREATE_TEST_IN_THE_SAME_ROOT, true); } - final Module targetModule = selectTargetModule(project, srcModule, testFolders); - final CreateTestDialog d = createTestDialog(project, targetModule, srcClass, srcPackage); + final CreateTestDialog d = createTestDialog(project, testModule, srcClass, srcPackage); if (!d.showAndGet()) { return; } @@ -138,44 +143,59 @@ public class CreateTestAction extends PsiElementBaseIntentionAction { }, CodeInsightBundle.message("intention.create.test"), this); } - private static Module selectTargetModule(Project project, Module srcModule, HashSet testFolders) { - Module targetModule = null; - for (VirtualFile testFolder : testFolders) { - Module targetModuleCandidate = ProjectFileIndex.SERVICE.getInstance(project).getModuleForFile(testFolder); - if (targetModuleCandidate == srcModule) { - targetModule = srcModule; - break; - } - else if (targetModule == null || TestModuleProperties.getInstance(targetModule).getProductionModule() != srcModule) { - targetModule = targetModuleCandidate; + @NotNull + private static Module suggestModuleForTests(@NotNull Project project, @NotNull Module productionModule) { + for (Module module : ModuleManager.getInstance(project).getModules()) { + if (productionModule.equals(TestModuleProperties.getInstance(module).getProductionModule())) { + return module; } } - return targetModule == null ? srcModule : targetModule; + return productionModule; } protected CreateTestDialog createTestDialog(Project project, Module srcModule, PsiClass srcClass, PsiPackage srcPackage) { return new CreateTestDialog(project, getText(), srcClass, srcPackage, srcModule); } - protected static void checkForTestRoots(Module srcModule, Set testFolders) { - List sourceRoots = ModuleRootManager.getInstance(srcModule).getSourceRoots(JavaSourceRootType.TEST_SOURCE); - for (VirtualFile sourceRoot : sourceRoots) { - if (!JavaProjectRootsUtil.isInGeneratedCode(sourceRoot, srcModule.getProject())) { - testFolders.add(sourceRoot); - } + static List computeSuitableTestRootUrls(@NotNull Module module) { + return suitableTestSourceFolders(module).map(SourceFolder::getUrl).collect(Collectors.toList()); + } + + static List computeTestRoots(@NotNull Module mainModule) { + if (!computeSuitableTestRootUrls(mainModule).isEmpty()) { + //create test in the same module, if the test source folder doesn't exist yet it will be created + return suitableTestSourceFolders(mainModule) + .map(SourceFolder::getFile) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } - //create test in the same module - if (!testFolders.isEmpty()) return; //suggest to choose from all dependencies modules final HashSet modules = new HashSet(); - ModuleUtilCore.collectModulesDependsOn(srcModule, modules); - for (Module module : modules) { - testFolders.addAll(ModuleRootManager.getInstance(module).getSourceRoots(JavaSourceRootType.TEST_SOURCE)); - } + ModuleUtilCore.collectModulesDependsOn(mainModule, modules); + return modules.stream() + .flatMap(CreateTestAction::suitableTestSourceFolders) + .map(SourceFolder::getFile) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } - @Nullable + private static Stream suitableTestSourceFolders(@NotNull Module module) { + Predicate forGeneratedSources = JavaProjectRootsUtil::isForGeneratedSources; + return Arrays.stream(ModuleRootManager.getInstance(module).getContentEntries()) + .flatMap(entry -> entry.getSourceFolders(JavaSourceRootType.TEST_SOURCE).stream()) + .filter(forGeneratedSources.negate()); + } + + /** + * @deprecated use {@link #computeTestRoots(Module)} instead + */ + @Deprecated + protected static void checkForTestRoots(Module srcModule, Set testFolders) { + testFolders.addAll(computeTestRoots(srcModule)); + } + + @Nullable protected static PsiClass getContainingClass(PsiElement element) { final PsiClass psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class, false); if (psiClass == null) { diff --git a/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestDialog.java b/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestDialog.java index 5c9883fbe4e0..1b447e528706 100644 --- a/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestDialog.java +++ b/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestDialog.java @@ -19,7 +19,6 @@ import com.intellij.CommonBundle; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix; import com.intellij.icons.AllIcons; -import com.intellij.ide.util.PackageUtil; import com.intellij.ide.util.PropertiesComponent; import com.intellij.ide.util.TreeClassChooser; import com.intellij.ide.util.TreeClassChooserFactory; @@ -38,12 +37,14 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.DumbModePermission; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.JavaProjectRootsUtil; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -59,6 +60,7 @@ import com.intellij.testIntegration.TestFramework; import com.intellij.testIntegration.TestIntegrationUtils; import com.intellij.ui.*; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; @@ -510,14 +512,20 @@ public class CreateTestDialog extends DialogWrapper { final VirtualFile selectedRoot = new ReadAction() { protected void run(@NotNull Result result) throws Throwable { - final HashSet testFolders = new HashSet(); - CreateTestAction.checkForTestRoots(myTargetModule, testFolders); + final List testFolders = CreateTestAction.computeTestRoots(myTargetModule); List roots; if (testFolders.isEmpty()) { - roots = ModuleRootManager.getInstance(myTargetModule).getSourceRoots(JavaModuleSourceRootTypes.SOURCES); + roots = new ArrayList<>(); + List urls = CreateTestAction.computeSuitableTestRootUrls(myTargetModule); + for (String url : urls) { + ContainerUtil.addIfNotNull(roots, VfsUtil.createDirectories(VfsUtilCore.urlToPath(url))); + } + if (roots.isEmpty()) { + JavaProjectRootsUtil.collectSuitableDestinationSourceRoots(myTargetModule, roots); + } if (roots.isEmpty()) return; } else { - roots = new ArrayList(testFolders); + roots = new ArrayList<>(testFolders); } if (roots.size() == 1) {