2 * Copyright 2000-2016 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.execution.junit;
18 import com.intellij.codeInsight.AnnotationUtil;
19 import com.intellij.codeInsight.MetaAnnotationUtil;
20 import com.intellij.codeInsight.TestFrameworks;
21 import com.intellij.execution.*;
22 import com.intellij.execution.junit2.info.MethodLocation;
23 import com.intellij.execution.testframework.SourceScope;
24 import com.intellij.openapi.module.Module;
25 import com.intellij.openapi.module.ModuleUtilCore;
26 import com.intellij.openapi.progress.ProgressManager;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.util.Condition;
29 import com.intellij.psi.*;
30 import com.intellij.psi.search.GlobalSearchScope;
31 import com.intellij.psi.search.searches.ClassInheritorsSearch;
32 import com.intellij.psi.util.*;
33 import com.intellij.testIntegration.JavaTestFramework;
34 import com.intellij.testIntegration.TestFramework;
35 import org.jetbrains.annotations.NonNls;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
41 @SuppressWarnings({"UtilityClassWithoutPrivateConstructor"})
42 public class JUnitUtil {
43 @NonNls public static final String TESTCASE_CLASS = "junit.framework.TestCase";
44 @NonNls private static final String TEST_INTERFACE = "junit.framework.Test";
45 @NonNls private static final String TESTSUITE_CLASS = "junit.framework.TestSuite";
46 @NonNls public static final String TEST_ANNOTATION = "org.junit.Test";
47 @NonNls public static final String TEST5_ANNOTATION = "org.junit.jupiter.api.Test";
48 @NonNls public static final String TEST5_FACTORY_ANNOTATION = "org.junit.jupiter.api.TestFactory";
49 @NonNls public static final String IGNORE_ANNOTATION = "org.junit.Ignore";
50 @NonNls public static final String RUN_WITH = "org.junit.runner.RunWith";
51 @NonNls public static final String DATA_POINT = "org.junit.experimental.theories.DataPoint";
52 @NonNls public static final String SUITE_METHOD_NAME = "suite";
54 public static final String BEFORE_ANNOTATION_NAME = "org.junit.Before";
55 public static final String AFTER_ANNOTATION_NAME = "org.junit.After";
57 public static final String BEFORE_EACH_ANNOTATION_NAME = "org.junit.jupiter.api.BeforeEach";
58 public static final String AFTER_EACH_ANNOTATION_NAME = "org.junit.jupiter.api.AfterEach";
60 public static final String PARAMETRIZED_PARAMETERS_ANNOTATION_NAME = "org.junit.runners.Parameterized.Parameters";
61 public static final String PARAMETRIZED_PARAMETER_ANNOTATION_NAME = "org.junit.runners.Parameterized.Parameter";
63 public static final String AFTER_CLASS_ANNOTATION_NAME = "org.junit.AfterClass";
64 public static final String BEFORE_CLASS_ANNOTATION_NAME = "org.junit.BeforeClass";
66 public static final String BEFORE_ALL_ANNOTATION_NAME = "org.junit.jupiter.api.BeforeAll";
67 public static final String AFTER_ALL_ANNOTATION_NAME = "org.junit.jupiter.api.AfterAll";
69 private static final Collection<String> TEST_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(TEST_ANNOTATION,
71 TEST5_FACTORY_ANNOTATION));
72 public static final Collection<String> TEST5_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(TEST5_ANNOTATION, TEST5_FACTORY_ANNOTATION));
74 private static final List<String> INSTANCE_CONFIGS = Arrays.asList(BEFORE_ANNOTATION_NAME, AFTER_ANNOTATION_NAME);
75 private static final List<String> INSTANCE_5_CONFIGS = Arrays.asList(BEFORE_EACH_ANNOTATION_NAME, AFTER_EACH_ANNOTATION_NAME);
77 private static final List<String> STATIC_5_CONFIGS = Arrays.asList(BEFORE_ALL_ANNOTATION_NAME, AFTER_ALL_ANNOTATION_NAME);
79 private static final List<String> STATIC_CONFIGS = Arrays.asList(BEFORE_CLASS_ANNOTATION_NAME, AFTER_CLASS_ANNOTATION_NAME,
80 PARAMETRIZED_PARAMETERS_ANNOTATION_NAME);
81 private static final Collection<String> CONFIGURATIONS_ANNOTATION_NAME = Collections.unmodifiableList(
82 Arrays.asList(DATA_POINT, AFTER_ANNOTATION_NAME, BEFORE_ANNOTATION_NAME, AFTER_CLASS_ANNOTATION_NAME, BEFORE_CLASS_ANNOTATION_NAME,
83 BEFORE_ALL_ANNOTATION_NAME, AFTER_ALL_ANNOTATION_NAME));
85 @NonNls public static final String PARAMETERIZED_CLASS_NAME = "org.junit.runners.Parameterized";
86 @NonNls public static final String SUITE_CLASS_NAME = "org.junit.runners.Suite";
87 public static final String JUNIT5_NESTED = "org.junit.jupiter.api.Nested";
89 public static boolean isSuiteMethod(@NotNull PsiMethod psiMethod) {
90 if (!psiMethod.hasModifierProperty(PsiModifier.PUBLIC)) return false;
91 if (!psiMethod.hasModifierProperty(PsiModifier.STATIC)) return false;
92 if (psiMethod.isConstructor()) return false;
93 if (psiMethod.getParameterList().getParametersCount() > 0) return false;
94 final PsiType returnType = psiMethod.getReturnType();
95 if (returnType == null || returnType instanceof PsiPrimitiveType) return false;
96 return returnType.equalsToText(TEST_INTERFACE)||
97 returnType.equalsToText(TESTSUITE_CLASS) ||
98 InheritanceUtil.isInheritor(returnType, TEST_INTERFACE);
101 public static boolean isTestMethod(final Location<? extends PsiMethod> location) {
102 return isTestMethod(location, true);
105 public static boolean isTestMethod(final Location<? extends PsiMethod> location, boolean checkAbstract) {
106 return isTestMethod(location, checkAbstract, true);
109 public static boolean isTestMethod(final Location<? extends PsiMethod> location, boolean checkAbstract, boolean checkRunWith) {
110 return isTestMethod(location, checkAbstract, checkRunWith, true);
113 public static boolean isTestMethod(final Location<? extends PsiMethod> location, boolean checkAbstract, boolean checkRunWith, boolean checkClass) {
114 final PsiMethod psiMethod = location.getPsiElement();
115 final PsiClass aClass = location instanceof MethodLocation ? ((MethodLocation)location).getContainingClass() : psiMethod.getContainingClass();
116 if (checkClass && (aClass == null || !isTestClass(aClass, checkAbstract, true))) return false;
117 if (isTestAnnotated(psiMethod)) return true;
118 if (psiMethod.isConstructor()) return false;
119 if (!psiMethod.hasModifierProperty(PsiModifier.PUBLIC)) return false;
120 if (psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) return false;
121 if (AnnotationUtil.isAnnotated(psiMethod, CONFIGURATIONS_ANNOTATION_NAME, false)) return false;
122 if (checkClass && checkRunWith) {
123 PsiAnnotation annotation = AnnotationUtil.findAnnotation(aClass, RUN_WITH);
124 if (annotation != null) {
125 return !isParameterized(annotation);
128 if (psiMethod.getParameterList().getParametersCount() > 0) return false;
129 if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) return false;
130 if (!psiMethod.getName().startsWith("test")) return false;
132 PsiClass testCaseClass = getTestCaseClassOrNull(location);
133 if (testCaseClass == null || !psiMethod.getContainingClass().isInheritor(testCaseClass, true)) return false;
135 return PsiType.VOID.equals(psiMethod.getReturnType());
138 public static boolean isTestCaseInheritor(final PsiClass aClass) {
139 if (!aClass.isValid()) return false;
140 Location<PsiClass> location = PsiLocation.fromPsiElement(aClass);
141 PsiClass testCaseClass = getTestCaseClassOrNull(location);
142 return testCaseClass != null && aClass.isInheritor(testCaseClass, true);
145 public static boolean isTestClass(final PsiClass psiClass) {
146 return isTestClass(psiClass, true, true);
149 public static boolean isTestClass(@NotNull PsiClass psiClass, boolean checkAbstract, boolean checkForTestCaseInheritance) {
150 if (psiClass.getQualifiedName() == null) return false;
151 if (isJUnit5(psiClass) && isJUnit5TestClass(psiClass, checkAbstract)) {
154 final PsiClass topLevelClass = PsiTreeUtil.getTopmostParentOfType(psiClass, PsiClass.class);
155 if (topLevelClass != null) {
156 final PsiAnnotation annotation = AnnotationUtil.findAnnotationInHierarchy(topLevelClass, Collections.singleton(RUN_WITH));
157 if (annotation != null) {
158 final PsiAnnotationMemberValue attributeValue = annotation.findAttributeValue("value");
159 if (attributeValue instanceof PsiClassObjectAccessExpression) {
160 final String runnerName = ((PsiClassObjectAccessExpression)attributeValue).getOperand().getType().getCanonicalText();
161 if (!(PARAMETERIZED_CLASS_NAME.equals(runnerName) || SUITE_CLASS_NAME.equals(runnerName))) {
167 if (!PsiClassUtil.isRunnableClass(psiClass, true, checkAbstract)) return false;
169 if (AnnotationUtil.isAnnotated(psiClass, RUN_WITH, true)) return true;
171 if (checkForTestCaseInheritance && isTestCaseInheritor(psiClass)) return true;
173 return CachedValuesManager.getCachedValue(psiClass, () ->
174 CachedValueProvider.Result.create(hasTestOrSuiteMethods(psiClass), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT));
177 private static boolean hasTestOrSuiteMethods(@NotNull PsiClass psiClass) {
178 for (final PsiMethod method : psiClass.getAllMethods()) {
179 if (isSuiteMethod(method)) return true;
180 if (isTestAnnotated(method)) return true;
183 if (isJUnit5(psiClass)) {
184 for (PsiClass innerClass : psiClass.getInnerClasses()) {
185 for (PsiMethod method : innerClass.getAllMethods()) {
186 if (isTestAnnotated(method)) return true;
194 public static boolean isJUnit3TestClass(final PsiClass clazz) {
195 return isTestCaseInheritor(clazz);
198 public static boolean isJUnit4TestClass(final PsiClass psiClass) {
199 return isJUnit4TestClass(psiClass, true);
202 private static boolean isJUnit4TestClass(final PsiClass psiClass, boolean checkAbstract) {
203 final PsiModifierList modifierList = psiClass.getModifierList();
204 if (modifierList == null) return false;
205 if (AnnotationUtil.isAnnotated(psiClass, RUN_WITH, true)) return true;
207 if (!PsiClassUtil.isRunnableClass(psiClass, true, checkAbstract)) return false;
209 for (final PsiMethod method : psiClass.getAllMethods()) {
210 ProgressManager.checkCanceled();
211 if (isTestAnnotated(method)) return true;
217 public static boolean isJUnit5TestClass(final PsiClass psiClass, boolean checkAbstract) {
218 final PsiModifierList modifierList = psiClass.getModifierList();
219 if (modifierList == null) return false;
221 if (psiClass.getContainingClass() != null && AnnotationUtil.isAnnotated(psiClass, JUNIT5_NESTED, false)) {
225 if (!PsiClassUtil.isRunnableClass(psiClass, false, checkAbstract)) return false;
227 Module module = ModuleUtilCore.findModuleForPsiElement(psiClass);
228 if (module != null) {
229 for (final PsiMethod method : psiClass.getAllMethods()) {
230 ProgressManager.checkCanceled();
231 if (MetaAnnotationUtil.isMetaAnnotated(method, TEST5_ANNOTATIONS)) return true;
234 for (PsiClass aClass : psiClass.getInnerClasses()) {
235 if (MetaAnnotationUtil.isMetaAnnotated(aClass, Collections.singleton(JUNIT5_NESTED))) return true;
242 public static boolean isJUnit5(@NotNull PsiElement element) {
243 return isJUnit5(element.getResolveScope(), element.getProject());
246 public static boolean isJUnit5(GlobalSearchScope scope, Project project) {
247 return JavaPsiFacade.getInstance(project).findClass(TEST5_ANNOTATION, scope) != null;
250 public static boolean isTestAnnotated(final PsiMethod method) {
251 if (AnnotationUtil.isAnnotated(method, TEST_ANNOTATION, false) || JUnitRecognizer.willBeAnnotatedAfterCompilation(method)) {
255 return MetaAnnotationUtil.isMetaAnnotated(method, TEST5_ANNOTATIONS);
260 private static PsiClass getTestCaseClassOrNull(final Location<?> location) {
261 final Location<PsiClass> ancestorOrSelf = location.getAncestorOrSelf(PsiClass.class);
262 if (ancestorOrSelf == null) return null;
263 final PsiClass aClass = ancestorOrSelf.getPsiElement();
264 Module module = JavaExecutionUtil.findModule(aClass);
265 if (module == null) return null;
266 GlobalSearchScope scope = GlobalSearchScope.moduleRuntimeScope(module, true);
267 return getTestCaseClassOrNull(scope, module.getProject());
270 public static PsiClass getTestCaseClass(final Module module) throws NoJUnitException {
271 if (module == null) throw new NoJUnitException();
272 final GlobalSearchScope scope = GlobalSearchScope.moduleRuntimeScope(module, true);
273 return getTestCaseClass(scope, module.getProject());
276 public static PsiClass getTestCaseClass(final SourceScope scope) throws NoJUnitException {
277 if (scope == null) throw new NoJUnitException();
278 return getTestCaseClass(scope.getLibrariesScope(), scope.getProject());
281 private static PsiClass getTestCaseClass(final GlobalSearchScope scope, final Project project) throws NoJUnitException {
282 PsiClass testCaseClass = getTestCaseClassOrNull(scope, project);
283 if (testCaseClass == null) throw new NoJUnitException(scope.getDisplayName());
284 return testCaseClass;
288 private static PsiClass getTestCaseClassOrNull(final GlobalSearchScope scope, final Project project) {
289 return JavaPsiFacade.getInstance(project).findClass(TESTCASE_CLASS, scope);
292 public static boolean isTestMethodOrConfig(@NotNull PsiMethod psiMethod) {
293 if (isTestMethod(PsiLocation.fromPsiElement(psiMethod), false)) {
294 final PsiClass containingClass = psiMethod.getContainingClass();
295 assert containingClass != null : psiMethod + "; " + psiMethod.getClass() + "; " + psiMethod.getParent();
296 if (containingClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
297 final boolean[] foundNonAbstractInheritor = new boolean[1];
298 ClassInheritorsSearch.search(containingClass).forEach(psiClass -> {
299 if (!psiClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
300 foundNonAbstractInheritor[0] = true;
305 if (foundNonAbstractInheritor[0]) {
312 final String name = psiMethod.getName();
313 final boolean isPublic = psiMethod.hasModifierProperty(PsiModifier.PUBLIC);
314 if (!psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) {
315 if (isPublic && (SUITE_METHOD_NAME.equals(name) || "setUp".equals(name) || "tearDown".equals(name))) {
319 if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
320 if (AnnotationUtil.isAnnotated(psiMethod, STATIC_CONFIGS)) {
323 if (AnnotationUtil.isAnnotated(psiMethod, STATIC_5_CONFIGS)) {
328 if (AnnotationUtil.isAnnotated(psiMethod, INSTANCE_CONFIGS)) {
332 if (AnnotationUtil.isAnnotated(psiMethod, INSTANCE_5_CONFIGS)) {
341 public static PsiMethod findFirstTestMethod(PsiClass clazz) {
342 PsiMethod testMethod = null;
343 for (PsiMethod method : clazz.getMethods()) {
344 if (isTestMethod(MethodLocation.elementInClass(method, clazz)) || isSuiteMethod(method)) {
353 public static PsiMethod findSuiteMethod(PsiClass clazz) {
354 final PsiMethod[] suiteMethods = clazz.findMethodsByName(SUITE_METHOD_NAME, false);
355 for (PsiMethod method : suiteMethods) {
356 if (isSuiteMethod(method)) return method;
361 public static boolean isParameterized(PsiAnnotation annotation) {
362 final PsiAnnotationMemberValue value = annotation.findAttributeValue(PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME);
363 if (value instanceof PsiClassObjectAccessExpression) {
364 final PsiTypeElement operand = ((PsiClassObjectAccessExpression)value).getOperand();
365 final PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(operand.getType());
366 return psiClass != null && "org.junit.runners.Parameterized".equals(psiClass.getQualifiedName());
371 public static class TestMethodFilter implements Condition<PsiMethod> {
372 private final PsiClass myClass;
373 private final JavaTestFramework framework;
375 public TestMethodFilter(final PsiClass aClass) {
377 TestFramework framework = TestFrameworks.detectFramework(aClass);
378 this.framework = (framework instanceof JavaTestFramework) ? (JavaTestFramework)framework : null;
381 public boolean value(final PsiMethod method) {
382 return framework != null
383 ? framework.isTestMethod(method, myClass)
384 : isTestMethod(MethodLocation.elementInClass(method, myClass));
388 public static PsiClass findPsiClass(final String qualifiedName, final Module module, final Project project) {
389 final GlobalSearchScope scope = module == null ? GlobalSearchScope.projectScope(project) : GlobalSearchScope.moduleWithDependenciesScope(module);
390 return JavaPsiFacade.getInstance(project).findClass(qualifiedName, scope);
393 public static PsiPackage getContainingPackage(@NotNull PsiClass psiClass) {
394 PsiDirectory directory = psiClass.getContainingFile().getContainingDirectory();
395 return directory == null ? null : JavaDirectoryService.getInstance().getPackage(directory);
398 public static PsiClass getTestClass(final PsiElement element) {
399 return getTestClass(PsiLocation.fromPsiElement(element));
402 public static PsiClass getTestClass(final Location<?> location) {
403 for (Iterator<Location<PsiClass>> iterator = location.getAncestors(PsiClass.class, false); iterator.hasNext();) {
404 final Location<PsiClass> classLocation = iterator.next();
405 if (isTestClass(classLocation.getPsiElement(), false, true)) return classLocation.getPsiElement();
407 PsiElement element = location.getPsiElement();
408 if (element instanceof PsiClassOwner) {
409 PsiClass[] classes = ((PsiClassOwner)element).getClasses();
410 if (classes.length == 1) return classes[0];
415 public static PsiMethod getTestMethod(final PsiElement element) {
416 return getTestMethod(element, true);
420 public static PsiMethod getTestMethod(final PsiElement element, boolean checkAbstract) {
421 return getTestMethod(element, checkAbstract, true);
424 public static PsiMethod getTestMethod(final PsiElement element, boolean checkAbstract, boolean checkRunWith) {
425 final PsiManager manager = element.getManager();
426 final Location<PsiElement> location = PsiLocation.fromPsiElement(manager.getProject(), element);
427 for (Iterator<Location<PsiMethod>> iterator = location.getAncestors(PsiMethod.class, false); iterator.hasNext();) {
428 final Location<? extends PsiMethod> methodLocation = iterator.next();
429 if (isTestMethod(methodLocation, checkAbstract, checkRunWith)) return methodLocation.getPsiElement();
434 public static class NoJUnitException extends CantRunException {
435 public NoJUnitException() {
436 super(ExecutionBundle.message("no.junit.error.message"));
439 public NoJUnitException(final String message) {
440 super(ExecutionBundle.message("no.junit.in.scope.error.message", message));