[jvm] IDEA-296557 Convert "Test in production code" inspections to UAST and merge them together

GitOrigin-RevId: a084452c8af9d275285c2aa3591166552fec123b
This commit is contained in:
Bart van Helvert
2022-06-23 12:07:52 +02:00
committed by intellij-monorepo-bot
parent 81c65b8d02
commit f4bd22020b
13 changed files with 102 additions and 166 deletions

View File

@@ -773,6 +773,18 @@ public class InspectionProfileTest extends LightIdeaTestCase {
"</profile>");
}
public void testTestInProductSourceInspections() throws Exception {
checkMergedNoChanges("<profile version=\"1.0\">\n" +
" <option name=\"myName\" value=\"" + PROFILE + "\" />\n" +
" <inspection_tool class=\"TestCaseInProductCode\" enabled=\"false\" level=\"WARNING\" enabled_by_default=\"false\" />\n" +
" <inspection_tool class=\"TestMethodInProductCode\" enabled=\"false\" level=\"WARNING\" enabled_by_default=\"false\" />\n" +
"</profile>");
checkMergedNoChanges("<profile version=\"1.0\">\n" +
" <option name=\"myName\" value=\"" + PROFILE + "\" />\n" +
" <inspection_tool class=\"TestInProductSource\" enabled=\"false\" level=\"WARNING\" enabled_by_default=\"false\" />\n" +
"</profile>");
}
public void testMergedMethodDoesntCallSuperMethodInspections() throws Exception {
checkMergedNoChanges("<profile version=\"1.0\">\n" +
" <option name=\"myName\" value=\"" + PROFILE + "\" />\n" +

View File

@@ -23,6 +23,14 @@
groupPathKey="jvm.inspections.group.name" groupKey="jvm.inspections.test.frameworks.group.name"
key="jvm.inspection.test.failed.line.display.name"
implementationClass="com.intellij.codeInspection.test.TestFailedLineInspection"/>
<localInspection language="UAST" enabledByDefault="false" level="WARNING" shortName="TestInProductSource"
groupBundle="messages.JvmAnalysisBundle" bundle="messages.JvmAnalysisBundle"
groupPathKey="jvm.inspections.group.name" groupKey="jvm.inspections.test.frameworks.group.name"
key="jvm.inspections.junit.test.case.in.product.source.display.name"
implementationClass="com.intellij.codeInspection.test.TestInProductSourceInspection"/>
<inspectionElementsMerger implementation="com.intellij.codeInspection.test.TestInProductSourceInspectionMerger"/>
<!--JUnit framework-->
<localInspection language="UAST" enabledByDefault="false" level="WARNING" shortName="SuperTearDownInFinally"
groupBundle="messages.JvmAnalysisBundle" bundle="messages.JvmAnalysisBundle"
groupPathKey="jvm.inspections.group.name" groupKey="jvm.inspections.test.frameworks.group.name"

View File

@@ -1,6 +1,6 @@
<html>
<body>
Reports methods annotated with the JUnit 4 or JUnit 5 <code>@Test</code> annotation that are located in production source trees.
Reports test classes and test methods that are located in production source trees.
This most likely a mistake and can result in test code being shipped into production.
<!-- tooltip end -->
</body>

View File

@@ -104,6 +104,10 @@ jvm.inspections.unconstructable.test.case.not.public.descriptor=Test class <code
jvm.inspections.unconstructable.test.case.junit3.descriptor=Test class <code>#ref</code> is not constructable because it does not have a 'public' no-arg or single 'String' parameter constructor #loc
jvm.inspections.unconstructable.test.case.junit4.descriptor=Test class <code>#ref</code> is not constructable because it should have exactly one 'public' no-arg constructor #loc
jvm.inspections.junit.test.case.in.product.source.display.name=Test in product source
jvm.inspections.junit.test.case.in.product.source.problem.descriptor=Test case <code>#ref</code> should probably be placed in a test source tree #loc
jvm.inspections.junit.test.method.in.product.source.problem.descriptor=Test method <code>#ref()</code> should probably be placed in a test source tree #loc
jvm.inspections.junit.malformed.declaration.name=JUnit malformed declaration
jvm.inspections.junit.malformed.option.ignore.test.parameter.if.annotated.by=Ignore test parameter if it is annotated by:
jvm.inspections.junit.malformed.method.no.arg.descriptor=Method <code>#ref</code> should be a {0}, {1} and have no parameters

View File

@@ -0,0 +1,43 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.test
import com.intellij.analysis.JvmAnalysisBundle
import com.intellij.codeInspection.AbstractBaseUastLocalInspectionTool
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.registerUProblem
import com.intellij.psi.PsiElementVisitor
import com.intellij.uast.UastHintedVisitorAdapter
import com.siyeh.ig.psiutils.TestUtils
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
class TestInProductSourceInspection : AbstractBaseUastLocalInspectionTool() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor =
UastHintedVisitorAdapter.create(
holder.file.language,
TestInProductSourceVisitor(holder),
arrayOf(UClass::class.java, UMethod::class.java),
directOnly = true
)
}
private class TestInProductSourceVisitor(private val holder: ProblemsHolder) : AbstractUastNonRecursiveVisitor() {
override fun visitClass(node: UClass): Boolean {
val javaClass = node.javaPsi
if (TestUtils.isInTestSourceContent(javaClass) || !TestUtils.isJUnitTestClass(javaClass)) return true
val message = JvmAnalysisBundle.message("jvm.inspections.junit.test.case.in.product.source.problem.descriptor")
holder.registerUProblem(node, message)
return true
}
override fun visitMethod(node: UMethod): Boolean {
val method = node.javaPsi
val containingClass = method.containingClass ?: return true
if (TestUtils.isInTestSourceContent(containingClass) || !TestUtils.isTestMethod(method)) return true
val message = JvmAnalysisBundle.message("jvm.inspections.junit.test.method.in.product.source.problem.descriptor")
holder.registerUProblem(node, message)
return true
}
}

View File

@@ -0,0 +1,18 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.test
import com.intellij.codeInspection.ex.InspectionElementsMerger
class TestInProductSourceInspectionMerger : InspectionElementsMerger() {
override fun getMergedToolName(): String = "TestInProductSource"
override fun getSourceToolNames(): Array<String> = arrayOf(
"TestCaseInProductCode",
"TestMethodInProductCode"
)
override fun getSuppressIds(): Array<String> = arrayOf(
"JUnitTestCaseInProductSource",
"JUnitTestMethodInProductSource"
)
}

View File

@@ -613,8 +613,6 @@ long.literals.ending.with.lowercase.l.display.name='long' literal ending with 'l
overly.complex.arithmetic.expression.display.name=Overly complex arithmetic expression
junit.abstract.test.class.naming.convention.element.description=Abstract test class
unnecessary.parentheses.display.name=Unnecessary parentheses
test.case.in.product.code.display.name=JUnit 'TestCase' in product source
test.method.in.product.code.display.name=JUnit test method in product source
serializable.class.in.secure.context.display.name=Serializable class in secure context
static.variable.naming.convention.element.description='static' field
nested.method.call.display.name=Nested method call
@@ -1067,8 +1065,6 @@ misordered.assert.equals.arguments.problem.descriptor=Arguments to <code>#ref()<
simplifiable.junit.assertion.problem.descriptor=<code>#ref()</code> can be simplified to ''{0}'' #loc
test.method.without.assertion.problem.descriptor=JUnit test method <code>#ref()</code> contains no assertions #loc
test.case.with.no.test.methods.problem.descriptor=Test class <code>#ref</code> has no tests #loc
test.case.in.product.code.problem.descriptor=Test case <code>#ref</code> should probably be placed in a test source tree #loc
test.method.in.product.code.problem.descriptor=Test method <code>#ref()</code> should probably be placed in a test source tree #loc
deserializable.class.in.secure.context.problem.descriptor=Class <code>#ref</code> may be deserialized, compromising security #loc
serializable.class.in.secure.context.problem.descriptor=Class <code>#ref</code> may be serialized, compromising security #loc
serializable.deserializable.class.in.secure.context.problem.descriptor=Class <code>#ref</code> may be serialized and deserialized, compromising security #loc

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2006-2013 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.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.psiutils.TestUtils;
import org.jetbrains.annotations.NotNull;
public class TestMethodInProductCodeInspection extends BaseInspection {
@Override
@NotNull
public String getID() {
return "JUnitTestMethodInProductSource";
}
@Override
@NotNull
protected String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message(
"test.method.in.product.code.problem.descriptor");
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new TestCaseInProductCodeVisitor();
}
private static class TestCaseInProductCodeVisitor
extends BaseInspectionVisitor {
@Override
public void visitMethod(@NotNull PsiMethod method) {
final PsiClass containingClass = method.getContainingClass();
if (TestUtils.isInTestSourceContent(containingClass) ||
!TestUtils.isAnnotatedTestMethod(method)) {
return;
}
registerMethodError(method);
}
}
}

View File

@@ -30,6 +30,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.testIntegration.TestFramework;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.junit.JUnitCommonClassNames;
import org.jetbrains.annotations.NonNls;
@@ -38,6 +39,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.intellij.codeInsight.AnnotationUtil.CHECK_HIERARCHY;
@@ -113,15 +115,22 @@ public final class TestUtils {
return method != null && AnnotationUtil.isAnnotated(method, JUnitCommonClassNames.ORG_JUNIT_TEST, CHECK_HIERARCHY);
}
public static boolean isAnnotatedTestMethod(@Nullable PsiMethod method) {
public static boolean isTestMethod(@Nullable PsiMethod method) {
if (method == null) return false;
return TestFrameworks.getInstance().isTestMethod(method);
}
/**
* @param frameworks to check matching with {@link TestFramework#getName}
*/
public static boolean isExecutableTestMethod(@Nullable PsiMethod method, List<String> frameworks) {
if (method == null) return false;
final PsiClass containingClass = method.getContainingClass();
if (containingClass == null) return false;
final TestFramework testFramework = TestFrameworks.detectFramework(containingClass);
if (testFramework == null) return false;
if (testFramework.isTestMethod(method, false)) {
@NonNls final String testFrameworkName = testFramework.getName();
return testFrameworkName.equals("JUnit4") || testFrameworkName.equals("JUnit5");
return ContainerUtil.exists(frameworks, testFramework.getName()::equals);
}
return false;
}

View File

@@ -1388,18 +1388,10 @@
groupKey="group.names.test.frameworks.issues" enabledByDefault="true" level="WARNING"
implementationClass="com.siyeh.ig.testFrameworks.SimplifiableAssertionInspection" cleanupTool="true"/>
<inspectionElementsMerger implementation="com.siyeh.ig.testFrameworks.SimplifiableAssertionMerger"/>
<localInspection groupPath="Java" language="JAVA" suppressId="JUnitTestCaseInProductSource" shortName="TestCaseInProductCode" bundle="messages.InspectionGadgetsBundle"
key="test.case.in.product.code.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.junit.issues" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.junit.TestCaseInProductCodeInspection"/>
<localInspection groupPath="Java" language="JAVA" suppressId="JUnitTestCaseWithNoTests" shortName="TestCaseWithNoTestMethods" bundle="messages.InspectionGadgetsBundle"
key="test.case.with.no.test.methods.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.junit.issues" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.junit.TestCaseWithNoTestMethodsInspection"/>
<localInspection groupPath="Java" language="JAVA" suppressId="JUnitTestMethodInProductSource" shortName="TestMethodInProductCode"
bundle="messages.InspectionGadgetsBundle" key="test.method.in.product.code.display.name"
groupBundle="messages.InspectionsBundle" groupKey="group.names.junit.issues" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.junit.TestMethodInProductCodeInspection"/>
<localInspection groupPath="Java" language="JAVA" suppressId="JUnitTestMethodWithNoAssertions" shortName="TestMethodWithoutAssertion"
bundle="messages.InspectionGadgetsBundle" key="test.method.without.assertion.display.name"
groupBundle="messages.InspectionsBundle" groupKey="group.names.junit.issues" enabledByDefault="false" level="WARNING"

View File

@@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 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.
*/
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.siyeh.ig.junit;
import com.intellij.codeInspection.naming.NamingConvention;
@@ -21,6 +7,8 @@ import com.intellij.psi.PsiMethod;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.psiutils.TestUtils;
import java.util.List;
/**
* @author Bas Leijdekkers
*/
@@ -32,7 +20,7 @@ public class JUnit4MethodNamingConvention extends NamingConvention<PsiMethod> {
@Override
public boolean isApplicable(PsiMethod member) {
return TestUtils.isAnnotatedTestMethod(member);
return TestUtils.isExecutableTestMethod(member, List.of("JUnit4", "JUnit5"));
}
@Override

View File

@@ -1,67 +0,0 @@
/*
* 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 WARRnS 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.psi.PsiClass;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.fixes.MoveClassFix;
import com.siyeh.ig.psiutils.TestUtils;
import org.jetbrains.annotations.NotNull;
public class TestCaseInProductCodeInspection extends BaseInspection {
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
return new MoveClassFix();
}
@Override
@NotNull
public String getID() {
return "JUnitTestCaseInProductSource";
}
@Override
@NotNull
protected String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message(
"test.case.in.product.code.problem.descriptor");
}
@Override
protected boolean buildQuickFixesOnlyForOnTheFlyErrors() {
return true;
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new TestCaseInProductCodeVisitor();
}
private static class TestCaseInProductCodeVisitor extends BaseInspectionVisitor {
@Override
public void visitClass(@NotNull PsiClass aClass) {
if (TestUtils.isInTestSourceContent(aClass) || !TestUtils.isJUnitTestClass(aClass)) {
return;
}
registerClassError(aClass);
}
}
}

View File

@@ -1,8 +0,0 @@
<html>
<body>
Reports JUnit 3 test classes in product source trees.
This most likely indicates a programmer's error and can result in test code being shipped
into production.
<!-- tooltip end -->
</body>
</html>