diff --git a/java/execution/impl/src/com/intellij/execution/junit/JUnitUtil.java b/java/execution/impl/src/com/intellij/execution/junit/JUnitUtil.java index 02010166786f..53035b8ef25f 100644 --- a/java/execution/impl/src/com/intellij/execution/junit/JUnitUtil.java +++ b/java/execution/impl/src/com/intellij/execution/junit/JUnitUtil.java @@ -33,7 +33,6 @@ import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.util.*; import com.intellij.testIntegration.JavaTestFramework; import com.intellij.testIntegration.TestFramework; -import com.intellij.util.ArrayUtil; import com.siyeh.ig.psiutils.TestUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -98,7 +97,21 @@ public class JUnitUtil { @NonNls public static final String PARAMETERIZED_CLASS_NAME = "org.junit.runners.Parameterized"; @NonNls public static final String SUITE_CLASS_NAME = "org.junit.runners.Suite"; public static final String JUNIT5_NESTED = "org.junit.jupiter.api.Nested"; - private static final String[] KNOWN_RUNNERS = {"org.junit.runners.Parameterized", "org.junit.runners.BlockJUnit4ClassRunner", "org.junit.runners.JUnit4"}; + private static final String[] RUNNERS_UNAWARE_OF_INNER_CLASSES = { + "org.junit.runners.Parameterized", + "org.junit.runners.BlockJUnit4ClassRunner", + "org.junit.runners.JUnit4", + "org.junit.internal.runners.JUnit38ClassRunner", + "org.junit.internal.runners.JUnit4ClassRunner", + "org.junit.runners.Suite" + }; + + private static final String[] RUNNERS_REQUIRE_ANNOTATION_ON_TEST_METHOD = { + "org.junit.runners.Parameterized", + "org.junit.runners.BlockJUnit4ClassRunner", + "org.junit.runners.JUnit4", + "org.junit.internal.runners.JUnit4ClassRunner" + }; public static boolean isSuiteMethod(@NotNull PsiMethod psiMethod) { if (!psiMethod.hasModifierProperty(PsiModifier.PUBLIC)) return false; @@ -136,7 +149,7 @@ public class JUnitUtil { if (checkClass && checkRunWith) { PsiAnnotation annotation = getRunWithAnnotation(aClass); if (annotation != null) { - return !isRunnerWithRequiredAnnotationOnTestMethod(annotation, KNOWN_RUNNERS); + return !isInheritorOrSelfRunner(annotation, RUNNERS_REQUIRE_ANNOTATION_ON_TEST_METHOD); } } if (psiMethod.getParameterList().getParametersCount() > 0) return false; @@ -225,7 +238,7 @@ public class JUnitUtil { } //default runners do not implicitly run inner classes - if (annotation != null && !isRunnerWithRequiredAnnotationOnTestMethod(annotation, KNOWN_RUNNERS)) { + if (annotation != null && !isInheritorOrSelfRunner(annotation, RUNNERS_UNAWARE_OF_INNER_CLASSES)) { return true; } } @@ -427,15 +440,15 @@ public class JUnitUtil { } public static boolean isParameterized(PsiAnnotation annotation) { - return isRunnerWithRequiredAnnotationOnTestMethod(annotation,"org.junit.runners.Parameterized"); + return isInheritorOrSelfRunner(annotation, "org.junit.runners.Parameterized"); } - private static boolean isRunnerWithRequiredAnnotationOnTestMethod(PsiAnnotation annotation, String... runners) { + public static boolean isInheritorOrSelfRunner(PsiAnnotation annotation, String... runners) { final PsiAnnotationMemberValue value = annotation.findAttributeValue(PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME); if (value instanceof PsiClassObjectAccessExpression) { final PsiTypeElement operand = ((PsiClassObjectAccessExpression)value).getOperand(); final PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(operand.getType()); - return psiClass != null && ArrayUtil.find(runners, psiClass.getQualifiedName()) > -1; + return psiClass != null && Arrays.stream(runners).anyMatch(runner -> InheritanceUtil.isInheritor(psiClass, runner)); } return false; } diff --git a/java/java-impl/src/com/intellij/testIntegration/JavaTestFramework.java b/java/java-impl/src/com/intellij/testIntegration/JavaTestFramework.java index 02467f532e10..e7309ac8fb2b 100644 --- a/java/java-impl/src/com/intellij/testIntegration/JavaTestFramework.java +++ b/java/java-impl/src/com/intellij/testIntegration/JavaTestFramework.java @@ -158,6 +158,13 @@ public abstract class JavaTestFramework implements TestFramework { return false; } + /** + * @return true for junit 3 classes with suite method and for junit 4 tests with @Suite annotation + */ + public boolean isSuiteClass(PsiClass psiClass) { + return false; + } + public boolean isTestMethod(PsiMethod method, PsiClass myClass) { return isTestMethod(method); } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml index 8c2d394f38d8..1619a6c16835 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml @@ -1651,7 +1651,8 @@ level="WARNING" implementationClass="com.siyeh.ig.naming.NewClassNamingConventionInspection"/> - + + diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index 5a68187bfc72..706dfa0f9eb8 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -793,6 +793,7 @@ public.field.accessed.in.synchronized.context.display.name=Non-private field acc string.replaceable.by.string.buffer.display.name=Non-constant String should be StringBuilder junit.test.class.naming.convention.display.name=JUnit test class naming convention junit.test.class.naming.convention.element.description=Test +junit.test.suite.naming.convention.element.description=Test suite method.coupling.display.name=Overly coupled method collections.must.have.initial.capacity.display.name=Collection without initial capacity anonymous.inner.class.display.name=Anonymous inner class diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/TestClassNamingConvention.java b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/TestClassNamingConvention.java index 270600302313..6be2c4bae6d9 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/TestClassNamingConvention.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/TestClassNamingConvention.java @@ -19,6 +19,8 @@ import com.intellij.codeInsight.TestFrameworks; import com.intellij.codeInspection.naming.NamingConvention; import com.intellij.codeInspection.naming.NamingConventionBean; import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiTypeParameter; +import com.intellij.testIntegration.JavaTestFramework; import com.intellij.testIntegration.TestFramework; import com.siyeh.InspectionGadgetsBundle; @@ -35,8 +37,9 @@ public class TestClassNamingConvention extends NamingConvention { @Override public boolean isApplicable(PsiClass member) { + if (member instanceof PsiTypeParameter) return false; TestFramework framework = TestFrameworks.detectFramework(member); - return framework != null && framework.isTestClass(member); + return framework instanceof JavaTestFramework && framework.isTestClass(member) && !((JavaTestFramework)framework).isSuiteClass(member); } @Override diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/TestSuiteNamingConvention.java b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/TestSuiteNamingConvention.java new file mode 100644 index 000000000000..17e946ceac33 --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/TestSuiteNamingConvention.java @@ -0,0 +1,54 @@ +/* + * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * + * 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.siyeh.ig.junit; + +import com.intellij.codeInsight.TestFrameworks; +import com.intellij.codeInspection.naming.NamingConvention; +import com.intellij.codeInspection.naming.NamingConventionBean; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiTypeParameter; +import com.intellij.testIntegration.JavaTestFramework; +import com.intellij.testIntegration.TestFramework; +import com.siyeh.InspectionGadgetsBundle; + +public class TestSuiteNamingConvention extends NamingConvention { + + private static final int DEFAULT_MIN_LENGTH = 8; + private static final int DEFAULT_MAX_LENGTH = 64; + public static final String TEST_SUITE_NAMING_CONVENTION_SHORT_NAME = "TestSuiteNamingConvention"; + + @Override + public NamingConventionBean createDefaultBean() { + return new NamingConventionBean("[A-Z][A-Za-z\\d]*Suite", DEFAULT_MIN_LENGTH, DEFAULT_MAX_LENGTH); + } + + @Override + public boolean isApplicable(PsiClass member) { + if (member instanceof PsiTypeParameter) return false; + TestFramework framework = TestFrameworks.detectFramework(member); + return framework instanceof JavaTestFramework && framework.isTestClass(member) && ((JavaTestFramework)framework).isSuiteClass(member); + } + + @Override + public String getShortName() { + return TEST_SUITE_NAMING_CONVENTION_SHORT_NAME; + } + + @Override + public String getElementDescription() { + return InspectionGadgetsBundle.message("junit.test.suite.naming.convention.element.description"); + } +} \ No newline at end of file diff --git a/plugins/junit/src/com/intellij/execution/junit/JUnit3Framework.java b/plugins/junit/src/com/intellij/execution/junit/JUnit3Framework.java index 3b978dd141e1..f98681ce94a5 100644 --- a/plugins/junit/src/com/intellij/execution/junit/JUnit3Framework.java +++ b/plugins/junit/src/com/intellij/execution/junit/JUnit3Framework.java @@ -50,6 +50,11 @@ public class JUnit3Framework extends JavaTestFramework { return true; } + @Override + public boolean isSuiteClass(PsiClass psiClass) { + return JUnitUtil.findSuiteMethod(psiClass) != null; + } + @Override public boolean isTestMethod(PsiMethod method, PsiClass myClass) { return JUnitUtil.isTestMethod(MethodLocation.elementInClass(method, myClass)); diff --git a/plugins/junit/src/com/intellij/execution/junit/JUnit4Framework.java b/plugins/junit/src/com/intellij/execution/junit/JUnit4Framework.java index 35fb61f25e07..666d4068a156 100644 --- a/plugins/junit/src/com/intellij/execution/junit/JUnit4Framework.java +++ b/plugins/junit/src/com/intellij/execution/junit/JUnit4Framework.java @@ -173,6 +173,12 @@ public class JUnit4Framework extends JavaTestFramework { return new FileTemplateDescriptor("JUnit4 Test Class.java"); } + @Override + public boolean isSuiteClass(PsiClass psiClass) { + PsiAnnotation annotation = JUnitUtil.getRunWithAnnotation(psiClass); + return annotation != null && JUnitUtil.isInheritorOrSelfRunner(annotation, "org.junit.runners.Suite"); + } + @Override public boolean isParameterized(PsiClass clazz) { PsiAnnotation annotation = JUnitUtil.getRunWithAnnotation(clazz);