junit 5 tags support (IDEA-163481)
[idea/community.git] / plugins / junit / src / com / intellij / execution / ConfigurationUtil.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.execution;
18
19 import com.intellij.execution.junit.JUnitUtil;
20 import com.intellij.execution.junit.TestClassFilter;
21 import com.intellij.openapi.application.AccessToken;
22 import com.intellij.openapi.application.ApplicationManager;
23 import com.intellij.openapi.application.ReadAction;
24 import com.intellij.openapi.application.ReadActionProcessor;
25 import com.intellij.openapi.module.Module;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.util.Ref;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.psi.*;
30 import com.intellij.psi.search.GlobalSearchScope;
31 import com.intellij.psi.search.PsiShortNamesCache;
32 import com.intellij.psi.search.searches.ClassInheritorsSearch;
33 import com.intellij.psi.search.searches.ClassesWithAnnotatedMembersSearch;
34 import com.intellij.psi.util.PsiUtilCore;
35 import com.intellij.util.containers.ContainerUtil;
36 import org.jetbrains.annotations.Nullable;
37
38 import java.util.Set;
39
40 public class ConfigurationUtil {
41   // return true if there is JUnit4 test
42   public static boolean findAllTestClasses(final TestClassFilter testClassFilter,
43                                            @Nullable final Module module,
44                                            final Set<PsiClass> found) {
45     final PsiManager manager = testClassFilter.getPsiManager();
46
47     final Project project = manager.getProject();
48     GlobalSearchScope projectScopeWithoutLibraries = GlobalSearchScope.projectScope(project);
49     final GlobalSearchScope scope = projectScopeWithoutLibraries.intersectWith(testClassFilter.getScope());
50
51     final PsiClass base = testClassFilter.getBase();
52     if (base != null) {
53       ClassInheritorsSearch.search(base, scope, true, true, false).forEach(new ReadActionProcessor<PsiClass>() {
54         @Override
55         public boolean processInReadAction(PsiClass aClass) {
56           if (testClassFilter.isAccepted(aClass)) found.add(aClass);
57           return true;
58         }
59       });
60     }
61
62     // classes having suite() method
63     final PsiMethod[] suiteMethods =
64       ReadAction.compute(() -> PsiShortNamesCache.getInstance(project).getMethodsByName(JUnitUtil.SUITE_METHOD_NAME, scope));
65     for (final PsiMethod method : suiteMethods) {
66       ApplicationManager.getApplication().runReadAction(() -> {
67         final PsiClass containingClass = method.getContainingClass();
68         if (containingClass == null) return;
69         if (containingClass instanceof PsiAnonymousClass) return;
70         if (containingClass.hasModifierProperty(PsiModifier.ABSTRACT)) return;
71         if (containingClass.getContainingClass() != null && !containingClass.hasModifierProperty(PsiModifier.STATIC)) return;
72         if (JUnitUtil.isSuiteMethod(method) && testClassFilter.isAccepted(containingClass)) {
73           found.add(containingClass);
74         }
75       });
76     }
77
78     Set<PsiClass> processed = ContainerUtil.newHashSet();
79     boolean hasJunit4 = addAnnotatedMethodsAnSubclasses(scope, testClassFilter, module, found, processed, JUnitUtil.TEST_ANNOTATION,
80                                                         manager.getProject());
81     hasJunit4 |= addAnnotatedMethodsAnSubclasses(scope, testClassFilter, module, found, processed, JUnitUtil.RUN_WITH, manager.getProject());
82     return hasJunit4;
83   }
84
85   private static boolean addAnnotatedMethodsAnSubclasses(final GlobalSearchScope scope,
86                                                          final TestClassFilter testClassFilter,
87                                                          @Nullable final Module module,
88                                                          final Set<PsiClass> found,
89                                                          final Set<PsiClass> processed,
90                                                          final String annotation,
91                                                          final Project project) {
92     final Ref<Boolean> isJUnit4 = new Ref<>(Boolean.FALSE);
93     // annotated with @Test
94     final PsiClass testAnnotation = ReadAction.compute(() -> JavaPsiFacade.getInstance(project).findClass(annotation, GlobalSearchScope.allScope(project)));
95     if (testAnnotation != null) {
96       //allScope is used to find all abstract test cases which probably have inheritors in the current 'scope'
97       GlobalSearchScope allScope = module == null ? GlobalSearchScope.allScope(project)
98                                                   : module.getModuleRuntimeScope(true);
99       ClassesWithAnnotatedMembersSearch.search(testAnnotation, allScope).forEach(annotated -> {
100         AccessToken token = ReadAction.start();
101         try {
102           if (!processed.add(annotated)) { // don't process the same class twice regardless of it being in the scope
103             return true;
104           }
105           final VirtualFile file = PsiUtilCore.getVirtualFile(annotated);
106           if (file != null && scope.contains(file) && testClassFilter.isAccepted(annotated)) {
107             if (!found.add(annotated)) {
108               return true;
109             }
110             isJUnit4.set(Boolean.TRUE);
111           }
112         }
113         finally {
114           token.finish();
115         }
116         ClassInheritorsSearch.search(annotated, scope, true, true, false).forEach(new ReadActionProcessor<PsiClass>() {
117           @Override
118           public boolean processInReadAction(PsiClass aClass) {
119             if (testClassFilter.isAccepted(aClass)) {
120               found.add(aClass);
121               processed.add(aClass);
122               isJUnit4.set(Boolean.TRUE);
123             }
124             return true;
125           }
126         });
127         return true;
128       });
129     }
130
131     return isJUnit4.get().booleanValue();
132   }
133 }