blob: d7c34ea475ba7b678ed07a64d52c49dc30dd66df [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.junit;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.execution.*;
import com.intellij.execution.junit2.info.MethodLocation;
import com.intellij.execution.testframework.SourceScope;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiClassUtil;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@SuppressWarnings({"UtilityClassWithoutPrivateConstructor"})
public class JUnitUtil {
@NonNls public static final String TESTCASE_CLASS = "junit.framework.TestCase";
@NonNls private static final String TEST_INTERFACE = "junit.framework.Test";
@NonNls private static final String TESTSUITE_CLASS = "junit.framework.TestSuite";
@NonNls public static final String TEST_ANNOTATION = "org.junit.Test";
@NonNls public static final String IGNORE_ANNOTATION = "org.junit.Ignore";
@NonNls public static final String RUN_WITH = "org.junit.runner.RunWith";
@NonNls public static final String DATA_POINT = "org.junit.experimental.theories.DataPoint";
@NonNls public static final String SUITE_METHOD_NAME = "suite";
public static final String BEFORE_ANNOTATION_NAME = "org.junit.Before";
public static final String AFTER_ANNOTATION_NAME = "org.junit.After";
public static final String PARAMETRIZED_PARAMETERS_ANNOTATION_NAME = "org.junit.runners.Parameterized.Parameters";
private static final String AFTER_CLASS_ANNOTATION_NAME = "org.junit.AfterClass";
private static final String BEFORE_CLASS_ANNOTATION_NAME = "org.junit.BeforeClass";
private static final Collection<String> CONFIGURATIONS_ANNOTATION_NAME = Collections.unmodifiableList(
Arrays.asList(DATA_POINT, AFTER_ANNOTATION_NAME, BEFORE_ANNOTATION_NAME, AFTER_CLASS_ANNOTATION_NAME, BEFORE_CLASS_ANNOTATION_NAME));
public static final String PARAMETERIZED_CLASS_NAME = "org.junit.runners.Parameterized";
public static boolean isSuiteMethod(@NotNull PsiMethod psiMethod) {
if (!psiMethod.hasModifierProperty(PsiModifier.PUBLIC)) return false;
if (!psiMethod.hasModifierProperty(PsiModifier.STATIC)) return false;
if (psiMethod.isConstructor()) return false;
if (psiMethod.getParameterList().getParametersCount() > 0) return false;
final PsiType returnType = psiMethod.getReturnType();
if (returnType == null || returnType instanceof PsiPrimitiveType) return false;
return returnType.equalsToText(TEST_INTERFACE)||
returnType.equalsToText(TESTSUITE_CLASS) ||
InheritanceUtil.isInheritor(returnType, TEST_INTERFACE);
}
public static boolean isTestMethod(final Location<? extends PsiMethod> location) {
return isTestMethod(location, true);
}
public static boolean isTestMethod(final Location<? extends PsiMethod> location, boolean checkAbstract) {
final PsiMethod psiMethod = location.getPsiElement();
final PsiClass aClass = location instanceof MethodLocation ? ((MethodLocation)location).getContainingClass() : psiMethod.getContainingClass();
if (aClass == null || !isTestClass(aClass, checkAbstract, true)) return false;
if (isTestAnnotated(psiMethod)) return true;
if (psiMethod.isConstructor()) return false;
if (!psiMethod.hasModifierProperty(PsiModifier.PUBLIC)) return false;
if (psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) return false;
if (AnnotationUtil.isAnnotated(psiMethod, CONFIGURATIONS_ANNOTATION_NAME, false)) return false;
if (AnnotationUtil.isAnnotated(aClass, RUN_WITH, true)) return true;
if (psiMethod.getParameterList().getParametersCount() > 0) return false;
if (psiMethod.hasModifierProperty(PsiModifier.STATIC) && SUITE_METHOD_NAME.equals(psiMethod.getName())) return false;
if (!psiMethod.getName().startsWith("test")) return false;
PsiClass testCaseClass = getTestCaseClassOrNull(location);
return testCaseClass != null && psiMethod.getContainingClass().isInheritor(testCaseClass, true);
}
private static boolean isTestCaseInheritor(final PsiClass aClass) {
if (!aClass.isValid()) return false;
Location<PsiClass> location = PsiLocation.fromPsiElement(aClass);
PsiClass testCaseClass = getTestCaseClassOrNull(location);
return testCaseClass != null && aClass.isInheritor(testCaseClass, true);
}
public static boolean isTestClass(final PsiClass psiClass) {
return isTestClass(psiClass, true, true);
}
public static boolean isTestClass(@NotNull PsiClass psiClass, boolean checkAbstract, boolean checkForTestCaseInheritance) {
if (psiClass.getQualifiedName() == null) return false;
if (!PsiClassUtil.isRunnableClass(psiClass, true, checkAbstract)) return false;
if (checkForTestCaseInheritance && isTestCaseInheritor(psiClass)) return true;
final PsiModifierList modifierList = psiClass.getModifierList();
if (modifierList == null) return false;
if (AnnotationUtil.isAnnotated(psiClass, RUN_WITH, true)) return true;
for (final PsiMethod method : psiClass.getAllMethods()) {
ProgressManager.checkCanceled();
if (isSuiteMethod(method)) return true;
if (isTestAnnotated(method)) return true;
}
return false;
}
public static boolean isJUnit3TestClass(final PsiClass clazz) {
return isTestCaseInheritor(clazz);
}
public static boolean isJUnit4TestClass(final PsiClass psiClass) {
return isJUnit4TestClass(psiClass, true);
}
private static boolean isJUnit4TestClass(final PsiClass psiClass, boolean checkAbstract) {
if (!PsiClassUtil.isRunnableClass(psiClass, true, checkAbstract)) return false;
final PsiModifierList modifierList = psiClass.getModifierList();
if (modifierList == null) return false;
if (AnnotationUtil.isAnnotated(psiClass, RUN_WITH, true)) return true;
for (final PsiMethod method : psiClass.getAllMethods()) {
ProgressManager.checkCanceled();
if (isTestAnnotated(method)) return true;
}
return false;
}
public static boolean isTestAnnotated(final PsiMethod method) {
if (AnnotationUtil.isAnnotated(method, TEST_ANNOTATION, false) || JUnitRecognizer.willBeAnnotatedAfterCompilation(method)) {
final PsiAnnotation annotation = AnnotationUtil.findAnnotationInHierarchy(method.getContainingClass(), Collections.singleton(RUN_WITH));
if (annotation != null) {
final PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes();
for (PsiNameValuePair attribute : attributes) {
final PsiAnnotationMemberValue value = attribute.getValue();
if (value instanceof PsiClassObjectAccessExpression ) {
final PsiTypeElement typeElement = ((PsiClassObjectAccessExpression)value).getOperand();
if (typeElement.getType().getCanonicalText().equals(PARAMETERIZED_CLASS_NAME)) {
return false;
}
}
}
}
return true;
}
return false;
}
@Nullable
private static PsiClass getTestCaseClassOrNull(final Location<?> location) {
final Location<PsiClass> ancestorOrSelf = location.getAncestorOrSelf(PsiClass.class);
if (ancestorOrSelf == null) return null;
final PsiClass aClass = ancestorOrSelf.getPsiElement();
Module module = JavaExecutionUtil.findModule(aClass);
if (module == null) return null;
GlobalSearchScope scope = GlobalSearchScope.moduleRuntimeScope(module, true);
return getTestCaseClassOrNull(scope, module.getProject());
}
public static PsiClass getTestCaseClass(final Module module) throws NoJUnitException {
if (module == null) throw new NoJUnitException();
final GlobalSearchScope scope = GlobalSearchScope.moduleRuntimeScope(module, true);
return getTestCaseClass(scope, module.getProject());
}
public static PsiClass getTestCaseClass(final SourceScope scope) throws NoJUnitException {
if (scope == null) throw new NoJUnitException();
return getTestCaseClass(scope.getLibrariesScope(), scope.getProject());
}
private static PsiClass getTestCaseClass(final GlobalSearchScope scope, final Project project) throws NoJUnitException {
PsiClass testCaseClass = getTestCaseClassOrNull(scope, project);
if (testCaseClass == null) throw new NoJUnitException(scope.getDisplayName());
return testCaseClass;
}
@Nullable
private static PsiClass getTestCaseClassOrNull(final GlobalSearchScope scope, final Project project) {
return JavaPsiFacade.getInstance(project).findClass(TESTCASE_CLASS, scope);
}
public static boolean isTestMethodOrConfig(@NotNull PsiMethod psiMethod) {
if (isTestMethod(PsiLocation.fromPsiElement(psiMethod), false)) {
final PsiClass containingClass = psiMethod.getContainingClass();
assert containingClass != null : psiMethod + "; " + psiMethod.getClass() + "; " + psiMethod.getParent();
if (containingClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
final boolean[] foundNonAbstractInheritor = new boolean[1];
ClassInheritorsSearch.search(containingClass).forEach(new Processor<PsiClass>() {
@Override
public boolean process(PsiClass psiClass) {
if (!psiClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
foundNonAbstractInheritor[0] = true;
return false;
}
return true;
}
});
if (foundNonAbstractInheritor[0]) {
return true;
}
} else {
return true;
}
}
final String name = psiMethod.getName();
if (psiMethod.hasModifierProperty(PsiModifier.PUBLIC) && !psiMethod.hasModifierProperty(PsiModifier.ABSTRACT)) {
if (SUITE_METHOD_NAME.equals(name) || "setUp".equals(name) || "tearDown".equals(name)) {
return true;
}
if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
if (AnnotationUtil.isAnnotated(psiMethod, Arrays.asList(BEFORE_CLASS_ANNOTATION_NAME, AFTER_CLASS_ANNOTATION_NAME,
PARAMETRIZED_PARAMETERS_ANNOTATION_NAME))) {
return true;
}
}
else {
if (AnnotationUtil.isAnnotated(psiMethod, Arrays.asList(BEFORE_ANNOTATION_NAME, AFTER_ANNOTATION_NAME))) return true;
}
}
return false;
}
@Nullable
public static PsiMethod findFirstTestMethod(PsiClass clazz) {
PsiMethod testMethod = null;
for (PsiMethod method : clazz.getMethods()) {
if (isTestMethod(MethodLocation.elementInClass(method, clazz)) || isSuiteMethod(method)) {
testMethod = method;
break;
}
}
return testMethod;
}
@Nullable
public static PsiMethod findSuiteMethod(PsiClass clazz) {
final PsiMethod[] suiteMethods = clazz.findMethodsByName(SUITE_METHOD_NAME, false);
for (PsiMethod method : suiteMethods) {
if (isSuiteMethod(method)) return method;
}
return null;
}
public static class TestMethodFilter implements Condition<PsiMethod> {
private final PsiClass myClass;
public TestMethodFilter(final PsiClass aClass) {
myClass = aClass;
}
public boolean value(final PsiMethod method) {
return isTestMethod(MethodLocation.elementInClass(method, myClass));
}
}
public static PsiClass findPsiClass(final String qualifiedName, final Module module, final Project project) {
final GlobalSearchScope scope = module == null ? GlobalSearchScope.projectScope(project) : GlobalSearchScope.moduleWithDependenciesScope(module);
return JavaPsiFacade.getInstance(project).findClass(qualifiedName, scope);
}
public static PsiPackage getContainingPackage(final PsiClass psiClass) {
return JavaDirectoryService.getInstance().getPackage(psiClass.getContainingFile().getContainingDirectory());
}
public static PsiClass getTestClass(final PsiElement element) {
return getTestClass(PsiLocation.fromPsiElement(element));
}
public static PsiClass getTestClass(final Location<?> location) {
for (Iterator<Location<PsiClass>> iterator = location.getAncestors(PsiClass.class, false); iterator.hasNext();) {
final Location<PsiClass> classLocation = iterator.next();
if (isTestClass(classLocation.getPsiElement(), false, true)) return classLocation.getPsiElement();
}
PsiElement element = location.getPsiElement();
if (element instanceof PsiClassOwner) {
PsiClass[] classes = ((PsiClassOwner)element).getClasses();
if (classes.length == 1) return classes[0];
}
return null;
}
public static PsiMethod getTestMethod(final PsiElement element) {
return getTestMethod(element, true);
}
public static PsiMethod getTestMethod(final PsiElement element, boolean checkAbstract) {
final PsiManager manager = element.getManager();
final Location<PsiElement> location = PsiLocation.fromPsiElement(manager.getProject(), element);
for (Iterator<Location<PsiMethod>> iterator = location.getAncestors(PsiMethod.class, false); iterator.hasNext();) {
final Location<? extends PsiMethod> methodLocation = iterator.next();
if (isTestMethod(methodLocation, checkAbstract)) return methodLocation.getPsiElement();
}
return null;
}
public static class NoJUnitException extends CantRunException {
public NoJUnitException() {
super(ExecutionBundle.message("no.junit.error.message"));
}
public NoJUnitException(final String message) {
super(ExecutionBundle.message("no.junit.in.scope.error.message", message));
}
}
}