Cleanup: NotNull/Nullable
[idea/community.git] / java / execution / impl / src / com / intellij / execution / testDiscovery / TestDiscoveryConfigurationProducer.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.execution.testDiscovery;
3
4 import com.intellij.codeInsight.TestFrameworks;
5 import com.intellij.execution.*;
6 import com.intellij.execution.actions.ConfigurationContext;
7 import com.intellij.execution.configurations.*;
8 import com.intellij.execution.junit.JavaRunConfigurationProducerBase;
9 import com.intellij.execution.runners.ExecutionEnvironment;
10 import com.intellij.execution.testframework.TestSearchScope;
11 import com.intellij.openapi.module.Module;
12 import com.intellij.openapi.module.ModuleManager;
13 import com.intellij.openapi.module.ModuleUtilCore;
14 import com.intellij.openapi.options.SettingsEditor;
15 import com.intellij.openapi.project.Project;
16 import com.intellij.openapi.util.Pair;
17 import com.intellij.openapi.util.Ref;
18 import com.intellij.openapi.util.registry.Registry;
19 import com.intellij.openapi.util.text.StringUtil;
20 import com.intellij.psi.PsiClass;
21 import com.intellij.psi.PsiElement;
22 import com.intellij.psi.PsiMethod;
23 import com.intellij.psi.util.ClassUtil;
24 import com.intellij.psi.util.PsiTreeUtil;
25 import com.intellij.testIntegration.TestFramework;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28
29 import javax.swing.*;
30 import java.util.*;
31 import java.util.stream.Collectors;
32
33 public abstract class TestDiscoveryConfigurationProducer extends JavaRunConfigurationProducerBase<JavaTestConfigurationWithDiscoverySupport> {
34   protected abstract void setPosition(JavaTestConfigurationBase configuration, PsiLocation<PsiMethod> position);
35   protected abstract Pair<String, String> getPosition(JavaTestConfigurationBase configuration);
36
37   protected void setupDiscoveryConfiguration(JavaTestConfigurationBase configuration, PsiMethod sourceMethod, Module targetModule) {
38     setPosition(configuration, new PsiLocation<>(sourceMethod));
39     Pair<String, String> position = getPosition(configuration);
40     configuration.setName("Tests for " + StringUtil.getShortName(position.first) + "." + position.second);
41     configuration.setModule(targetModule);
42   }
43
44
45   @Override
46   protected boolean setupConfigurationFromContext(final JavaTestConfigurationWithDiscoverySupport configuration,
47                                                   ConfigurationContext configurationContext,
48                                                   Ref<PsiElement> ref) {
49     if (!Registry.is(TestDiscoveryExtension.TEST_DISCOVERY_REGISTRY_KEY)) {
50       return false;
51     }
52     final Location contextLocation = configurationContext.getLocation();
53     assert contextLocation != null;
54     final Location location = JavaExecutionUtil.stepIntoSingleClass(contextLocation);
55     if (location == null) return false;
56     final PsiMethod sourceMethod = getSourceMethod(location);
57     final Pair<String, String> position = getPosition(sourceMethod);
58     if (sourceMethod != null && position != null) {
59       final Project project = configuration.getProject();
60       final TestDiscoveryIndex testDiscoveryIndex = TestDiscoveryIndex.getInstance(project);
61       if (testDiscoveryIndex.getTestsByMethodName(position.first, position.second, configuration.getTestFrameworkId()).isEmpty()) {
62         return false;
63       }
64
65       Module targetModule = getTargetModule(configuration, configurationContext, position, project, testDiscoveryIndex);
66       setupDiscoveryConfiguration(configuration, sourceMethod, targetModule);
67       return true;
68     }
69     return false;
70   }
71
72   private Module getTargetModule(JavaTestConfigurationWithDiscoverySupport configuration,
73                                  ConfigurationContext configurationContext,
74                                  Pair<String, String> position, Project project, TestDiscoveryIndex testDiscoveryIndex) {
75     final RunnerAndConfigurationSettings template =
76       configurationContext.getRunManager().getConfigurationTemplate(getConfigurationFactory());
77     final Module predefinedModule = ((ModuleBasedConfiguration)template.getConfiguration()).getConfigurationModule().getModule();
78     if (predefinedModule != null) {
79       return predefinedModule;
80     }
81
82     //potentially this set won't be big, it reflects modules from where user starts his tests
83     final Collection<String> modules = testDiscoveryIndex.getTestModulesByMethodName(position.first,
84                                                                                      position.second,
85                                                                                      configuration.getTestFrameworkId());
86     if (modules.isEmpty()) return null;
87
88     final List<Module> survivedModules = new ArrayList<>();
89     final ModuleManager moduleManager = ModuleManager.getInstance(project);
90     for (String moduleName : modules) {
91       final Module moduleByName = moduleManager.findModuleByName(moduleName);
92       if (moduleByName != null) {
93         survivedModules.add(moduleByName);
94       }
95     }
96     if (survivedModules.isEmpty()) return null;
97
98     return detectTargetModule(survivedModules, project);
99   }
100
101   public abstract boolean isApplicable(@NotNull Location<PsiMethod> testMethod);
102
103   @NotNull
104   public abstract RunProfileState createProfile(@NotNull Location<PsiMethod>[] testMethods,
105                                                 Module module,
106                                                 RunConfiguration configuration,
107                                                 ExecutionEnvironment environment);
108
109   public RunProfile createProfile(Location<PsiMethod>[] testMethods,
110                                   Module module,
111                                   ConfigurationContext context,
112                                   String configurationName) {
113     RunnerAndConfigurationSettings settings = cloneTemplateConfiguration(context);
114     JavaTestConfigurationBase configuration = (JavaTestConfigurationBase)settings.getConfiguration();
115     configuration.setModule(module);
116     if (module == null) {
117       configuration.setSearchScope(TestSearchScope.WHOLE_PROJECT);
118     }
119     else {
120       configuration.setSearchScope(TestSearchScope.MODULE_WITH_DEPENDENCIES);
121     }
122     configuration.setShortenCommandLine(ShortenCommandLine.MANIFEST);
123     return new MyRunProfile(testMethods, module, configuration, configurationName);
124   }
125
126   public static Module detectTargetModule(Collection<? extends Module> survivedModules, Project project) {
127     ModuleManager moduleManager = ModuleManager.getInstance(project);
128     final Set<Module> allModules = new HashSet<>(Arrays.asList(moduleManager.getModules()));
129     survivedModules
130       .forEach(module -> {
131         final List<Module> dependentModules = ModuleUtilCore.getAllDependentModules(module);
132         dependentModules.add(module);
133         allModules.retainAll(dependentModules);
134       });
135     if (!allModules.isEmpty()) {
136       Module aModule = allModules.iterator().next();
137       for (Module module : survivedModules) {
138         if (allModules.contains(module)) {
139           aModule = module;
140         }
141       }
142       return aModule;
143     }
144     return null;
145   }
146
147   @Override
148   protected Module findModule(JavaTestConfigurationWithDiscoverySupport configuration, Module contextModule) {
149     return null;
150   }
151
152   private static PsiMethod getSourceMethod(Location location) {
153     final PsiElement psiElement = location.getPsiElement();
154     final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(psiElement, PsiMethod.class);
155     if (psiMethod != null) {
156       final PsiClass containingClass = psiMethod.getContainingClass();
157       if (containingClass != null) {
158         final TestFramework testFramework = TestFrameworks.detectFramework(containingClass);
159         if (testFramework != null) {
160           return null;
161         }
162         return psiMethod;
163       }
164     }
165     return null;
166   }
167
168   private static Pair<String, String> getPosition(PsiMethod method) {
169     if (method == null) {
170       return null;
171     }
172     final PsiClass containingClass = method.getContainingClass();
173     if (containingClass == null) {
174       return null;
175     }
176     final String qualifiedName = ClassUtil.getJVMClassName(containingClass);
177     if (qualifiedName != null) {
178       return Pair.create(qualifiedName, method.getName());
179     }
180     return null;
181   }
182
183   @Override
184   public boolean isConfigurationFromContext(JavaTestConfigurationWithDiscoverySupport configuration, ConfigurationContext configurationContext) {
185     final Pair<String, String> position = getPosition(getSourceMethod(configurationContext.getLocation()));
186     return position != null && position.equals(getPosition(configuration));
187   }
188
189   protected static LinkedHashSet<String> collectMethodPatterns(@NotNull Location<PsiMethod>[] testMethods) {
190     return Arrays.stream(testMethods)
191           .map(method -> {
192             Iterator<Location<PsiClass>> ancestors = method.getAncestors(PsiClass.class, true);
193             return JavaExecutionUtil.getRuntimeQualifiedName(ancestors.next().getPsiElement()) + "," + method.getPsiElement().getName();
194           })
195           .collect(Collectors.toCollection(LinkedHashSet::new));
196   }
197
198   private class MyRunProfile implements WrappingRunConfiguration<RunConfiguration>,
199                                         RunConfiguration,
200                                         ConfigurationWithCommandLineShortener,
201                                         RunProfileWithCompileBeforeLaunchOption,
202                                         ModuleRunProfile {
203     private final Location<PsiMethod>[] myTestMethods;
204     private final Module myModule;
205     private final JavaTestConfigurationBase myConfiguration;
206     private final String myConfigurationName;
207
208     MyRunProfile(Location<PsiMethod>[] testMethods, Module module, JavaTestConfigurationBase configuration, String configurationName) {
209       myTestMethods = testMethods;
210       myModule = module;
211       myConfiguration = configuration;
212       myConfigurationName = configurationName;
213     }
214
215     @Nullable
216     @Override
217     public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) {
218       return createProfile(myTestMethods, myModule, myConfiguration, environment);
219     }
220
221     @NotNull
222     @Override
223     public String getName() {
224       return myConfigurationName;
225     }
226
227     @Nullable
228     @Override
229     public Icon getIcon() {
230       return myConfiguration.getIcon();
231     }
232
233     @Nullable
234     @Override
235     public ShortenCommandLine getShortenCommandLine() {
236       return myConfiguration.getShortenCommandLine();
237     }
238
239     @Override
240     public void setShortenCommandLine(@Nullable ShortenCommandLine mode) {
241       myConfiguration.setShortenCommandLine(mode);
242     }
243
244     @Nullable
245     @Override
246     public ConfigurationFactory getFactory() {
247       return myConfiguration.getFactory();
248     }
249
250     @Override
251     public void setName(@NotNull String name) {
252     }
253
254     @NotNull
255     @Override
256     public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
257       return myConfiguration.getConfigurationEditor();
258     }
259
260     @Override
261     public Project getProject() {
262       return myConfiguration.getProject();
263     }
264
265     @Override
266     public RunConfiguration clone() {
267       return new MyRunProfile(myTestMethods, myModule, myConfiguration, myConfigurationName);
268     }
269
270     @Override
271     public RunConfiguration getPeer() {
272       return myConfiguration;
273     }
274
275     @NotNull
276     @Override
277     public Module[] getModules() {
278       return myConfiguration.getModules();
279     }
280   }
281 }