Fix for multiple JUnit Conversions in batch mode with and without delta creating an exception.

Added new AssertThatObjectExpression inspection for toString() and hashCode() and moved equals() from AssertThatBinaryExpression there.
This commit is contained in:
2019-06-09 21:35:23 +02:00
parent 6795622202
commit 5c455c3ca9
19 changed files with 235 additions and 53 deletions
@@ -5,15 +5,14 @@ import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.*
import com.intellij.psi.util.TypeConversionUtil
import de.platon42.intellij.plugins.cajon.*
import de.platon42.intellij.plugins.cajon.quickfixes.MoveOutMethodCallExpressionQuickFix
import de.platon42.intellij.plugins.cajon.quickfixes.SplitBinaryExpressionMethodCallQuickFix
class AssertThatBinaryExpressionInspection : AbstractAssertJInspection() {
companion object {
private const val DISPLAY_NAME = "Asserting a binary expression"
private const val SPLIT_EXPRESSION_DESCRIPTION_TEMPLATE = "Split %s expression out of assertThat()"
private const val MORE_MEANINGFUL_MESSAGE_TEMPLATE = "Moving %s expression out of assertThat() would be more meaningful"
private const val SPLIT_EXPRESSION_DESCRIPTION_TEMPLATE = "Split binary expression out of assertThat()"
private const val MORE_MEANINGFUL_MESSAGE_TEMPLATE = "Moving binary expression out of assertThat() would be more meaningful"
}
override fun getDisplayName() = DISPLAY_NAME
@@ -33,16 +32,7 @@ class AssertThatBinaryExpressionInspection : AbstractAssertJInspection() {
val expectedCallExpression = statement.findOutmostMethodCall() ?: return
val expectedResult = expectedCallExpression.getAllTheSameExpectedBooleanConstants() ?: return
val assertThatArgument = staticMethodCall.firstArg
if (assertThatArgument is PsiMethodCallExpression && OBJECT_EQUALS.test(assertThatArgument)) {
val replacementMethod = expectedResult.map(MethodNames.IS_EQUAL_TO, MethodNames.IS_NOT_EQUAL_TO)
registerSplitMethod(holder, expectedCallExpression, "${MethodNames.EQUALS}()", replacementMethod) { desc, method ->
MoveOutMethodCallExpressionQuickFix(desc, method)
}
return
}
val binaryExpression = assertThatArgument as? PsiBinaryExpression ?: return
val binaryExpression = staticMethodCall.firstArg as? PsiBinaryExpression ?: return
val leftType = binaryExpression.lOperand.type ?: return
val rightType = binaryExpression.rOperand?.type ?: return
@@ -53,7 +43,7 @@ class AssertThatBinaryExpressionInspection : AbstractAssertJInspection() {
return
} else if (isLeftNull || isRightNull) {
val replacementMethod = expectedResult.map(MethodNames.IS_NULL, MethodNames.IS_NOT_NULL)
registerSplitMethod(holder, expectedCallExpression, "binary", replacementMethod) { desc, method ->
registerSplitMethod(holder, expectedCallExpression, replacementMethod) { desc, method ->
SplitBinaryExpressionMethodCallQuickFix(desc, method, pickRightOperand = isLeftNull, noExpectedExpression = true)
}
return
@@ -75,7 +65,7 @@ class AssertThatBinaryExpressionInspection : AbstractAssertJInspection() {
(isPrimitive || isNumericType).map(TOKEN_TO_ASSERTJ_FOR_PRIMITIVE_MAP, TOKEN_TO_ASSERTJ_FOR_OBJECT_MAPPINGS)
val replacementMethod = mappingToUse[tokenType] ?: return
registerSplitMethod(holder, expectedCallExpression, "binary", replacementMethod) { desc, method ->
registerSplitMethod(holder, expectedCallExpression, replacementMethod) { desc, method ->
SplitBinaryExpressionMethodCallQuickFix(desc, method, pickRightOperand = swapExpectedAndActual)
}
}
@@ -85,13 +75,10 @@ class AssertThatBinaryExpressionInspection : AbstractAssertJInspection() {
private fun registerSplitMethod(
holder: ProblemsHolder,
expression: PsiMethodCallExpression,
type: String,
replacementMethod: String,
quickFixSupplier: (String, String) -> LocalQuickFix
) {
val description = SPLIT_EXPRESSION_DESCRIPTION_TEMPLATE.format(type)
val message = MORE_MEANINGFUL_MESSAGE_TEMPLATE.format(type)
val quickfix = quickFixSupplier(description, replacementMethod)
holder.registerProblem(expression, message, quickfix)
val quickfix = quickFixSupplier(SPLIT_EXPRESSION_DESCRIPTION_TEMPLATE, replacementMethod)
holder.registerProblem(expression, MORE_MEANINGFUL_MESSAGE_TEMPLATE, quickfix)
}
}
@@ -34,11 +34,6 @@ class AssertThatCollectionOrMapExpressionInspection : AbstractAssertJInspection(
Mapping(
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_MAP, "containsValue").parameterCount(1),
MethodNames.CONTAINS_VALUE, MethodNames.DOES_NOT_CONTAIN_VALUE
),
// TODO not quite a collection or map expression, maybe move this out to new inspection together with equals and hashcode.
Mapping(
CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_OBJECT, "toString").parameterCount(0),
MethodNames.HAS_TO_STRING, null
)
)
}
@@ -0,0 +1,59 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.*
import com.siyeh.ig.callMatcher.CallMatcher
import de.platon42.intellij.plugins.cajon.*
import de.platon42.intellij.plugins.cajon.quickfixes.HasHashCodeQuickFix
import de.platon42.intellij.plugins.cajon.quickfixes.MoveOutMethodCallExpressionQuickFix
import de.platon42.intellij.plugins.cajon.quickfixes.RemoveActualOutmostMethodCallQuickFix
class AssertThatObjectExpressionInspection : AbstractAssertJInspection() {
companion object {
private const val DISPLAY_NAME = "Asserting equals(), toString(), or hashCode()"
private const val HASHCODE_MESSAGE_TEMPLATE = "Replacing hashCode() expressions by hasSameHashCode() would be more concise"
private val OBJECT_TO_STRING = CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_OBJECT, "toString").parameterCount(0)
private val OBJECT_HASHCODE = CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_OBJECT, "hashCode").parameterCount(0)
}
override fun getDisplayName() = DISPLAY_NAME
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : JavaElementVisitor() {
override fun visitExpressionStatement(statement: PsiExpressionStatement) {
super.visitExpressionStatement(statement)
if (!statement.hasAssertThat()) {
return
}
val staticMethodCall = statement.findStaticMethodCall() ?: return
val assertThatArgument = staticMethodCall.firstArg as? PsiMethodCallExpression ?: return
val expectedCallExpression = statement.findOutmostMethodCall() ?: return
when {
OBJECT_EQUALS.test(assertThatArgument) -> {
val expectedResult = expectedCallExpression.getAllTheSameExpectedBooleanConstants() ?: return
val replacementMethod = expectedResult.map(MethodNames.IS_EQUAL_TO, MethodNames.IS_NOT_EQUAL_TO)
registerMoveOutMethod(holder, expectedCallExpression, assertThatArgument, replacementMethod) { desc, method ->
MoveOutMethodCallExpressionQuickFix(desc, method)
}
}
OBJECT_TO_STRING.test(assertThatArgument) -> {
staticMethodCall.findFluentCallTo(IS_EQUAL_TO_OBJECT) ?: return
registerMoveOutMethod(holder, expectedCallExpression, assertThatArgument, MethodNames.HAS_TO_STRING) { desc, method ->
RemoveActualOutmostMethodCallQuickFix(desc, method)
}
}
OBJECT_HASHCODE.test(assertThatArgument) -> {
val isEqualTo = staticMethodCall.findFluentCallTo(IS_EQUAL_TO_INT) ?: return
val expectedExpression = isEqualTo.firstArg as? PsiMethodCallExpression ?: return
if (OBJECT_HASHCODE.test(expectedExpression)) {
holder.registerProblem(expectedCallExpression, HASHCODE_MESSAGE_TEMPLATE, HasHashCodeQuickFix())
}
}
}
}
}
}
}
@@ -115,8 +115,7 @@ class AssertThatSizeInspection : AbstractAssertJInspection() {
if (!expression.hasAssertThat()) {
return
}
val isHasSize = HAS_SIZE.test(expression)
if (!(isHasSize)) {
if (!HAS_SIZE.test(expression)) {
return
}
val actualExpression = expression.firstArg
@@ -0,0 +1,37 @@
package de.platon42.intellij.plugins.cajon.quickfixes
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiMethodCallExpression
import de.platon42.intellij.plugins.cajon.*
class HasHashCodeQuickFix :
AbstractCommonQuickFix(HASHCODE_DESCRIPTION) {
companion object {
private const val HASHCODE_DESCRIPTION = "Replace calls to hashCode() with hasSameHashCodeAs()"
}
override fun getFamilyName(): String {
return HASHCODE_DESCRIPTION
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val outmostCallExpression = descriptor.startElement as? PsiMethodCallExpression ?: return
val assertThatMethodCall = outmostCallExpression.findStaticMethodCall() ?: return
val assertExpression = assertThatMethodCall.firstArg as? PsiMethodCallExpression ?: return
val methodsToFix = assertThatMethodCall.gatherAssertionCalls()
assertExpression.replace(assertExpression.qualifierExpression)
methodsToFix
.forEach {
val innerHashCodeObject = (it.firstArg as PsiMethodCallExpression).qualifierExpression
val expectedExpression = createExpectedMethodCall(it, "hasSameHashCodeAs", innerHashCodeObject)
expectedExpression.replaceQualifierFromMethodCall(it)
it.replace(expectedExpression)
}
}
}
@@ -9,7 +9,7 @@ import de.platon42.intellij.plugins.cajon.AssertJClassNames.Companion.GUAVA_ASSE
class ReplaceJUnitDeltaAssertMethodCallQuickFix(description: String, private val replacementMethod: String) : AbstractCommonQuickFix(description) {
companion object {
private const val CONVERT_DESCRIPTION = "Convert JUnit assertions to assertJ"
private const val CONVERT_DESCRIPTION = "Convert JUnit assertions with delta to assertJ"
}
override fun getFamilyName(): String {
+2
View File
@@ -40,6 +40,8 @@
<localInspection groupPath="Java" shortName="AssertThatBinaryExpression" enabledByDefault="true" level="WARNING"
implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatBinaryExpressionInspection"/>
<localInspection groupPath="Java" shortName="AssertThatObjectExpression" enabledByDefault="true" level="WARNING"
implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatObjectExpressionInspection"/>
<localInspection groupPath="Java" shortName="AssertThatStringExpression" enabledByDefault="true" level="WARNING"
implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatStringExpressionInspection"/>
<localInspection groupPath="Java" shortName="AssertThatCollectionOrMapExpression" enabledByDefault="true" level="WARNING"
@@ -4,7 +4,6 @@ Turns a binary expression in the kind of assertThat(actual &lt;operator&gt; expe
into assertThat(actual).is&lt;operator&gt;(expected).
<!-- tooltip end -->
There are over 150 combinations that are found with this inspections.
It also detects assertThat(actual.equals(expected)) assertions.
Also works with constant expressions on the expected side.
Swaps actual and expected when actual is a constant expression (correctly transforming the used operator).
</body>
@@ -0,0 +1,9 @@
<html>
<body>
Tries to simplify Object method calls such as toString, hashCode() and equals().
<!-- tooltip end -->
<br>Turns assertThat(object.toString()).isEqualTo(expression) into assertThat(object).hasToString(expression).
<br>Turns assertThat(object.hashCode()).isEqualTo(otherObject.hashCode()) into assertThat(object).hasSameHashCodeAs(otherObject).
<br>Turns assertThat(object.equals(otherObject)).isEqualTo(true) into assertThat(object).isEqualTo(otherObject) (and variations).
</body>
</html>