Implemented first version of JoinAssertThatStatements inspection that will try to merge assertThat() statements with the same actual object together, preserving comments.

This commit is contained in:
Chris Hodges 2019-04-28 20:43:42 +02:00
parent 666e373405
commit 941ddfdb5e
15 changed files with 347 additions and 35 deletions

View File

@ -70,8 +70,21 @@ assertThat(array).hasSameSizeAs(collection);
You can toggle the various inspections in the Settings/Editor/Inspections in the AssertJ group. You can toggle the various inspections in the Settings/Editor/Inspections in the AssertJ group.
## Implemented inspections ## Implemented inspections and quickfixes
- JoinAssertThatStatements
```
from: assertThat(expected).someCondition();
assertThat(expected).anotherCondition();
to: assertThat(expected).someCondition().anotherCondition();
```
Joining will work on actual expressions inside assertThat() that are
- the same variable reference
- textually equal binary expressions
- the same method calls (except for known side-effect methods such as ```Iterator.next()``` -- please notify me about others)
The comments of the statements will be preserved. When using ```.extracting()``` or similar, the statements will not be merged.
- AssertThatObjectIsNullOrNotNull - AssertThatObjectIsNullOrNotNull
``` ```
from: assertThat(object).isEqualTo(null); from: assertThat(object).isEqualTo(null);
@ -320,9 +333,8 @@ Cajon is probably the only plugin that uses JUnit 5 Jupiter for unit testing so
The IntelliJ framework actually uses the JUnit 3 TestCase for plugin testing and it took me quite a while to make it work with JUnit 5. The IntelliJ framework actually uses the JUnit 3 TestCase for plugin testing and it took me quite a while to make it work with JUnit 5.
Feel free to use the code (in package de.platon42.intellij.jupiter) for your projects (with attribution). Feel free to use the code (in package de.platon42.intellij.jupiter) for your projects (with attribution).
## TODO ## Planned features
- AssumeThatInsteadOfReturn - AssumeThatInsteadOfReturn
- Join consecutive assertThats
- Extraction with property names to lambda with Java 8 - Extraction with property names to lambda with Java 8
``` ```
from: assertThat(object).extracting("propOne", "propNoGetter", "propTwo.innerProp")... from: assertThat(object).extracting("propOne", "propNoGetter", "propTwo.innerProp")...
@ -333,10 +345,12 @@ Feel free to use the code (in package de.platon42.intellij.jupiter) for your pro
## Changelog ## Changelog
#### V0.7 (unreleased) #### V0.7 (28-Apr-19)
- Another fix for AssertThatGuavaOptional inspection regarding using the same family name for slightly different quick fix executions - Another fix for AssertThatGuavaOptional inspection regarding using the same family name for slightly different quick fix executions
(really, Jetbrains, this sucks for no reason). (really, Jetbrains, this sucks for no reason).
- Extended AssertThatSize inspection to transform ```hasSize()``` into ```hasSameSizeAs()```, if possible. - Extended AssertThatSize inspection to transform ```hasSize()``` into ```hasSameSizeAs()```, if possible.
- Implemented first version of JoinAssertThatStatements inspection that will try to merge ```assertThat()``` statements with the same
actual object together, preserving comments.
#### V0.6 (22-Apr-19) #### V0.6 (22-Apr-19)
- New AssertThatStringExpression inspection that will move ```isEmpty()```, ```equals()```, ```equalsIgnoreCase()```, ```contains()```, - New AssertThatStringExpression inspection that will move ```isEmpty()```, ```equals()```, ```equalsIgnoreCase()```, ```contains()```,

View File

@ -1,7 +1,7 @@
plugins { plugins {
id 'java' id 'java'
id 'org.jetbrains.intellij' version '0.4.8' id 'org.jetbrains.intellij' version '0.4.8'
id 'org.jetbrains.kotlin.jvm' version '1.3.30' id 'org.jetbrains.kotlin.jvm' version '1.3.31'
} }
group 'de.platon42' group 'de.platon42'
@ -40,12 +40,14 @@ intellij {
patchPluginXml { patchPluginXml {
changeNotes """ changeNotes """
<h4>V0.7 (unreleased)</h4> <h4>V0.7 (28-Apr-19)</h4>
<ul> <ul>
<li>Another fix for AssertThatGuavaOptional inspection regarding using the same family name for slightly different quick fix executions <li>Another fix for AssertThatGuavaOptional inspection regarding using the same family name for slightly different quick fix executions
(really, Jetbrains, this sucks for no reason). (really, Jetbrains, this sucks for no reason).
<li>Extended AssertThatSize inspection to transform hasSize() into hasSameSizeAs(), if possible. <li>Extended AssertThatSize inspection to transform hasSize() into hasSameSizeAs(), if possible.
</ul> <li>Implemented first version of JoinAssertThatStatements inspection that will try to merge assertThat() statements with the same
actual object together, preserving comments.
</ul>
<h4>V0.6 (22-Apr-19)</h4> <h4>V0.6 (22-Apr-19)</h4>
<ul> <ul>
<li>New AssertThatStringExpression inspection that will move isEmpty(), equals(), equalsIgnoreCase(), contains(), <li>New AssertThatStringExpression inspection that will move isEmpty(), equals(), equalsIgnoreCase(), contains(),
@ -54,7 +56,7 @@ patchPluginXml {
<li>New AssertThatInvertedBooleanCondition inspection that will remove inverted boolean expressions inside assertThat(). <li>New AssertThatInvertedBooleanCondition inspection that will remove inverted boolean expressions inside assertThat().
<li>Renamed a few inspections to better/shorter names. <li>Renamed a few inspections to better/shorter names.
<li>New AssertThatInstanceOf inspection that moves instanceof expressions out of assertThat(). <li>New AssertThatInstanceOf inspection that moves instanceof expressions out of assertThat().
</ul> </ul>
<p>Full changelog available at <a href="https://github.com/chrisly42/cajon-plugin#changelog">Github project site</a>.</p> <p>Full changelog available at <a href="https://github.com/chrisly42/cajon-plugin#changelog">Github project site</a>.</p>
""" """
} }

View File

@ -0,0 +1,23 @@
package de.platon42.intellij.plugins.cajon
import com.intellij.psi.CommonClassNames
import com.siyeh.ig.callMatcher.CallMatcher
val CORE_ASSERT_THAT_MATCHER = CallMatcher.staticCall(AssertJClassNames.ASSERTIONS_CLASSNAME, MethodNames.ASSERT_THAT)!!
val GUAVA_ASSERT_THAT_MATCHER = CallMatcher.staticCall(AssertJClassNames.GUAVA_ASSERTIONS_CLASSNAME, MethodNames.ASSERT_THAT)!!
val ALL_ASSERT_THAT_MATCHERS = CallMatcher.anyOf(CORE_ASSERT_THAT_MATCHER, GUAVA_ASSERT_THAT_MATCHER)!!
val EXTRACTING_FROM_OBJECT = CallMatcher.instanceCall(AssertJClassNames.ABSTRACT_OBJECT_ASSERT_CLASSNAME, "extracting")!!
val EXTRACTING_FROM_ITERABLE = CallMatcher.instanceCall(AssertJClassNames.ABSTRACT_ITERABLE_ASSERT_CLASSNAME, "extracting")!!
val FLAT_EXTRACTING_FROM_ITERABLE = CallMatcher.instanceCall(AssertJClassNames.ABSTRACT_ITERABLE_ASSERT_CLASSNAME, "flatExtracting")!!
val EXTRACTING_RESULT_OF_FROM_ITERABLE = CallMatcher.instanceCall(AssertJClassNames.ABSTRACT_ITERABLE_ASSERT_CLASSNAME, "extractingResultOf")!!
val EXTRACTING_CALL_MATCHERS = CallMatcher.anyOf(
EXTRACTING_FROM_OBJECT,
EXTRACTING_FROM_ITERABLE,
FLAT_EXTRACTING_FROM_ITERABLE,
EXTRACTING_RESULT_OF_FROM_ITERABLE
)!!
val KNOWN_METHODS_WITH_SIDE_EFFECTS = CallMatcher.anyOf(
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_ITERATOR, "next")
)!!

View File

@ -4,6 +4,7 @@ import com.intellij.psi.*
import com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.codeStyle.JavaCodeStyleManager import com.intellij.psi.codeStyle.JavaCodeStyleManager
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import com.siyeh.ig.callMatcher.CallMatcher
val PsiMethodCallExpression.qualifierExpression: PsiExpression get() = this.methodExpression.qualifierExpression!! val PsiMethodCallExpression.qualifierExpression: PsiExpression get() = this.methodExpression.qualifierExpression!!
val PsiMethodCallExpression.firstArg: PsiExpression get() = this.argumentList.expressions[0]!! val PsiMethodCallExpression.firstArg: PsiExpression get() = this.argumentList.expressions[0]!!
@ -21,6 +22,17 @@ fun PsiElement.findOutmostMethodCall(): PsiMethodCallExpression? {
return PsiTreeUtil.findChildOfType(statement, PsiMethodCallExpression::class.java) return PsiTreeUtil.findChildOfType(statement, PsiMethodCallExpression::class.java)
} }
fun PsiMethodCallExpression.findFluentCallTo(matcher: CallMatcher): PsiMethodCallExpression? {
var currentMethodCall: PsiMethodCallExpression? = this
while (currentMethodCall != null) {
if (matcher.test(currentMethodCall)) {
return currentMethodCall
}
currentMethodCall = PsiTreeUtil.getParentOfType(currentMethodCall, PsiMethodCallExpression::class.java, true, PsiStatement::class.java)
}
return null
}
fun PsiMethodCallExpression.getArg(n: Int): PsiExpression = this.argumentList.expressions[n] fun PsiMethodCallExpression.getArg(n: Int): PsiExpression = this.argumentList.expressions[n]
fun <T> Boolean.map(forTrue: T, forFalse: T) = if (this) forTrue else forFalse fun <T> Boolean.map(forTrue: T, forFalse: T) = if (this) forTrue else forFalse

View File

@ -4,9 +4,6 @@ import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiExpression import com.intellij.psi.PsiExpression
import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiMethodCallExpression
import com.siyeh.ig.callMatcher.CallMatcher
val CORE_ASSERT_THAT_MATCHER = CallMatcher.staticCall(AssertJClassNames.ASSERTIONS_CLASSNAME, MethodNames.ASSERT_THAT)!!
fun createAssertThat(context: PsiElement, actualExpression: PsiExpression): PsiMethodCallExpression { fun createAssertThat(context: PsiElement, actualExpression: PsiExpression): PsiMethodCallExpression {
return createAssertThat(context, AssertJClassNames.ASSERTIONS_CLASSNAME, actualExpression) return createAssertThat(context, AssertJClassNames.ASSERTIONS_CLASSNAME, actualExpression)

View File

@ -0,0 +1,87 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
import de.platon42.intellij.plugins.cajon.*
import de.platon42.intellij.plugins.cajon.quickfixes.JoinStatementsQuickFix
class JoinAssertThatStatementsInspection : AbstractAssertJInspection() {
companion object {
private const val DISPLAY_NAME = "Joining multiple assertThat() statements with same actual expression"
private const val CAN_BE_JOINED_DESCRIPTION = "Multiple assertThat() statements can be joined together"
}
override fun getDisplayName() = DISPLAY_NAME
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : JavaElementVisitor() {
override fun visitCodeBlock(block: PsiCodeBlock?) {
super.visitCodeBlock(block)
val statements = block?.statements ?: return
var lastActualExpression: PsiExpression? = null
var sameCount = 0
var firstStatement: PsiStatement? = null
var lastStatement: PsiStatement? = null
for (statement in statements) {
val assertThatCall = isLegitAssertThatCall(statement)
var reset = true
var actualExpression: PsiExpression? = null
if (assertThatCall != null) {
reset = (lastActualExpression == null)
actualExpression = assertThatCall.firstArg
if (!reset) {
val isSame = when (actualExpression) {
is PsiReferenceExpression -> (actualExpression.qualifierExpression == (lastActualExpression as? PsiReferenceExpression)?.qualifierExpression)
is PsiMethodCallExpression -> (actualExpression.text == (lastActualExpression as? PsiMethodCallExpression)?.text)
&& !KNOWN_METHODS_WITH_SIDE_EFFECTS.test(actualExpression)
is PsiPolyadicExpression -> (actualExpression.text == (lastActualExpression as? PsiPolyadicExpression)?.text)
else -> false
}
if (isSame) {
sameCount++
lastStatement = statement
} else {
reset = true
}
}
}
if (reset) {
if (sameCount > 1) {
registerProblem(firstStatement, lastStatement)
}
firstStatement = statement
lastStatement = null
lastActualExpression = actualExpression
sameCount = 1
}
}
if (sameCount > 1) {
registerProblem(firstStatement, lastStatement)
}
}
private fun registerProblem(firstStatement: PsiStatement?, lastStatement: PsiStatement?) {
val problemDescriptor = holder.manager.createProblemDescriptor(
firstStatement!!,
lastStatement!!,
CAN_BE_JOINED_DESCRIPTION,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
isOnTheFly,
JoinStatementsQuickFix()
)
holder.registerProblem(problemDescriptor)
}
private fun isLegitAssertThatCall(statement: PsiStatement?): PsiMethodCallExpression? {
if ((statement is PsiExpressionStatement) && (statement.expression is PsiMethodCallExpression)) {
val assertThatCall = PsiTreeUtil.findChildrenOfType(statement, PsiMethodCallExpression::class.java).find { ALL_ASSERT_THAT_MATCHERS.test(it) }
return assertThatCall?.takeIf { it.findFluentCallTo(EXTRACTING_CALL_MATCHERS) == null }
}
return null
}
}
}
}

View File

@ -0,0 +1,54 @@
package de.platon42.intellij.plugins.cajon.quickfixes
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.util.PsiTreeUtil
import de.platon42.intellij.plugins.cajon.ALL_ASSERT_THAT_MATCHERS
class JoinStatementsQuickFix : AbstractCommonQuickFix(JOIN_STATEMENTS_MESSAGE) {
companion object {
private const val JOIN_STATEMENTS_MESSAGE = "Join assertThat() statements"
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val firstStatement = descriptor.startElement as PsiExpressionStatement
val lastStatement = descriptor.endElement as PsiExpressionStatement
do {
val commentsToKeep = ArrayList<PsiComment>()
val stuffToDelete = ArrayList<PsiElement>()
var previousStatement = lastStatement.prevSibling ?: throw IllegalStateException("Internal error")
while (previousStatement !is PsiExpressionStatement) {
if (previousStatement is PsiComment) {
commentsToKeep.add(previousStatement.copy() as PsiComment)
}
stuffToDelete.add(previousStatement)
previousStatement = previousStatement.prevSibling ?: throw IllegalStateException("Internal error")
}
stuffToDelete.forEach { if (it.isValid) it.delete() }
val statementComments = PsiTreeUtil.getChildrenOfAnyType(previousStatement, PsiComment::class.java)
commentsToKeep.addAll(statementComments)
val assertThatCallOfCursorStatement =
PsiTreeUtil.findChildrenOfType(lastStatement, PsiMethodCallExpression::class.java).find { ALL_ASSERT_THAT_MATCHERS.test(it) }
?: throw IllegalStateException("Internal error")
val lastElementBeforeConcat = assertThatCallOfCursorStatement.parent
commentsToKeep.forEach {
lastElementBeforeConcat.addAfter(it, lastElementBeforeConcat.firstChild)
val newLineNode =
PsiParserFacade.SERVICE.getInstance(project).createWhiteSpaceFromText("\n\t")
lastElementBeforeConcat.addAfter(newLineNode, lastElementBeforeConcat.firstChild)
}
val newLeaf = previousStatement.firstChild
assertThatCallOfCursorStatement.replace(newLeaf)
previousStatement.delete()
} while (previousStatement !== firstStatement)
val codeBlock = PsiTreeUtil.getParentOfType(lastStatement, PsiCodeBlock::class.java) ?: return
CodeStyleManager.getInstance(project).reformat(codeBlock)
}
}

View File

@ -5,7 +5,7 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.JavaPsiFacade import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiInstanceOfExpression import com.intellij.psi.PsiInstanceOfExpression
import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiParenthesizedExpression import com.intellij.psi.util.PsiUtil
import de.platon42.intellij.plugins.cajon.createExpectedMethodCall import de.platon42.intellij.plugins.cajon.createExpectedMethodCall
import de.platon42.intellij.plugins.cajon.findOutmostMethodCall import de.platon42.intellij.plugins.cajon.findOutmostMethodCall
import de.platon42.intellij.plugins.cajon.firstArg import de.platon42.intellij.plugins.cajon.firstArg
@ -21,11 +21,7 @@ class RemoveInstanceOfExpressionQuickFix(description: String, private val replac
val factory = JavaPsiFacade.getElementFactory(project) val factory = JavaPsiFacade.getElementFactory(project)
val classObjectAccess = factory.createExpressionFromText("${expectedClass.type.canonicalText}.class", null) val classObjectAccess = factory.createExpressionFromText("${expectedClass.type.canonicalText}.class", null)
var operand = assertExpression.operand val operand = PsiUtil.deparenthesizeExpression(assertExpression.operand) ?: return
while (operand is PsiParenthesizedExpression) {
operand = operand.expression ?: return
}
assertExpression.replace(operand) assertExpression.replace(operand)
val oldExpectedExpression = element.findOutmostMethodCall() ?: return val oldExpectedExpression = element.findOutmostMethodCall() ?: return

View File

@ -3,8 +3,8 @@ package de.platon42.intellij.plugins.cajon.quickfixes
import com.intellij.codeInspection.ProblemDescriptor import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiParenthesizedExpression
import com.intellij.psi.PsiUnaryExpression import com.intellij.psi.PsiUnaryExpression
import com.intellij.psi.util.PsiUtil
import de.platon42.intellij.plugins.cajon.createExpectedMethodCall import de.platon42.intellij.plugins.cajon.createExpectedMethodCall
import de.platon42.intellij.plugins.cajon.findOutmostMethodCall import de.platon42.intellij.plugins.cajon.findOutmostMethodCall
import de.platon42.intellij.plugins.cajon.firstArg import de.platon42.intellij.plugins.cajon.firstArg
@ -16,10 +16,7 @@ class RemoveUnaryExpressionQuickFix(description: String, private val replacement
val element = descriptor.startElement val element = descriptor.startElement
val methodCallExpression = element as? PsiMethodCallExpression ?: return val methodCallExpression = element as? PsiMethodCallExpression ?: return
val assertExpression = methodCallExpression.firstArg as? PsiUnaryExpression ?: return val assertExpression = methodCallExpression.firstArg as? PsiUnaryExpression ?: return
var operand = assertExpression.operand ?: return val operand = PsiUtil.skipParenthesizedExprDown(assertExpression.operand) ?: return
while (operand is PsiParenthesizedExpression) {
operand = operand.expression ?: return
}
assertExpression.replace(operand) assertExpression.replace(operand)
val oldExpectedExpression = element.findOutmostMethodCall() ?: return val oldExpectedExpression = element.findOutmostMethodCall() ?: return

View File

@ -10,19 +10,11 @@ import com.intellij.psi.util.PsiTypesUtil
import com.intellij.util.ArrayUtil import com.intellij.util.ArrayUtil
import com.intellij.util.ProcessingContext import com.intellij.util.ProcessingContext
import com.siyeh.ig.callMatcher.CallMatcher import com.siyeh.ig.callMatcher.CallMatcher
import de.platon42.intellij.plugins.cajon.AssertJClassNames import de.platon42.intellij.plugins.cajon.*
import de.platon42.intellij.plugins.cajon.CORE_ASSERT_THAT_MATCHER
import de.platon42.intellij.plugins.cajon.firstArg
class ExtractorReferenceContributor : PsiReferenceContributor() { class ExtractorReferenceContributor : PsiReferenceContributor() {
companion object { companion object {
private val EXTRACTING_FROM_OBJECT = CallMatcher.instanceCall(AssertJClassNames.ABSTRACT_OBJECT_ASSERT_CLASSNAME, "extracting")
private val EXTRACTING_FROM_ITERABLE = CallMatcher.instanceCall(AssertJClassNames.ABSTRACT_ITERABLE_ASSERT_CLASSNAME, "extracting")
private val FLAT_EXTRACTING_FROM_ITERABLE = CallMatcher.instanceCall(AssertJClassNames.ABSTRACT_ITERABLE_ASSERT_CLASSNAME, "flatExtracting")
private val EXTRACTING_RESULT_OF_FROM_ITERABLE = CallMatcher.instanceCall(AssertJClassNames.ABSTRACT_ITERABLE_ASSERT_CLASSNAME, "extractingResultOf")
private val BY_NAME = CallMatcher.staticCall(AssertJClassNames.EXTRACTORS_CLASSNAME, "byName") private val BY_NAME = CallMatcher.staticCall(AssertJClassNames.EXTRACTORS_CLASSNAME, "byName")
private val RESULT_OF = CallMatcher.staticCall(AssertJClassNames.EXTRACTORS_CLASSNAME, "resultOf") private val RESULT_OF = CallMatcher.staticCall(AssertJClassNames.EXTRACTORS_CLASSNAME, "resultOf")
.parameterTypes(CommonClassNames.JAVA_LANG_STRING)!! .parameterTypes(CommonClassNames.JAVA_LANG_STRING)!!

View File

@ -43,6 +43,9 @@
<localInspection groupPath="Java" shortName="AssertThatStringExpression" enabledByDefault="true" level="WARNING" <localInspection groupPath="Java" shortName="AssertThatStringExpression" enabledByDefault="true" level="WARNING"
implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatStringExpressionInspection"/> implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatStringExpressionInspection"/>
<localInspection groupPath="Java" shortName="JoinAssertThatStatements" enabledByDefault="true" level="WARNING"
implementationClass="de.platon42.intellij.plugins.cajon.inspections.JoinAssertThatStatementsInspection"/>
<localInspection groupPath="Java" shortName="AssertThatJava8Optional" enabledByDefault="true" level="WARNING" <localInspection groupPath="Java" shortName="AssertThatJava8Optional" enabledByDefault="true" level="WARNING"
implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatJava8OptionalInspection"/> implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatJava8OptionalInspection"/>
<localInspection groupPath="Java" shortName="AssertThatGuavaOptional" enabledByDefault="true" level="WARNING" <localInspection groupPath="Java" shortName="AssertThatGuavaOptional" enabledByDefault="true" level="WARNING"

View File

@ -4,10 +4,7 @@ import org.assertj.core.api.ListAssert;
import org.assertj.core.data.Offset; import org.assertj.core.data.Offset;
import org.assertj.core.extractor.Extractors; import org.assertj.core.extractor.Extractors;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.Offset.offset; import static org.assertj.core.data.Offset.offset;
@ -38,6 +35,39 @@ public class Playground {
assertThat(new Long[1]).as("etc").hasSameSizeAs(new Long[2]); assertThat(new Long[1]).as("etc").hasSameSizeAs(new Long[2]);
} }
private void joinStatements() {
List<String> list = new ArrayList<>();
assertThat(list).as("foo").hasSize(2);
assertThat(list).as("bar").contains("barbar"); // comment to keep
assertThat(list).as("etc").contains("etcetc");
// moar!
assertThat(list).doesNotContain("foobar");
assertThat("narf").isNotEqualTo("puit");
assertThat(list).as("bar").contains("barbar");
assertThat(list).as("foo").hasSize(2);
assertThat(list).as("evil").extracting(String::length).contains(2);
assertThat(list).as("bar").contains("barbar");
assertThat("narf").isNotEqualTo("puit");
assertThat(list).as("foo").hasSize(2);
if (true) {
assertThat(list).doesNotContain("narf");
assertThat(list).as("bar").contains("barbar");
}
assertThat(list.get(0)).isNotEmpty();
assertThat(list.get(0)).hasSize(3);
assertThat(list.get(0)).isEqualTo("bar");
assertThat(list.get(0) + "foo").isEqualTo("bar");
assertThat(list.get(0) + "foo").doesNotStartWith("foo");
Iterator<String> iterator = list.iterator();
assertThat(iterator.next()).isEqualTo("foo");
assertThat(iterator.next()).isEqualTo("bar");
}
private void sizeOfArray() { private void sizeOfArray() {
assertThat(new String[1].length).isLessThanOrEqualTo(1); assertThat(new String[1].length).isLessThanOrEqualTo(1);
assertThat(new String[1]).hasSameSizeAs(new Object()); assertThat(new String[1]).hasSameSizeAs(new Object());

View File

@ -0,0 +1,21 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
import de.platon42.intellij.jupiter.MyFixture
import de.platon42.intellij.jupiter.TestDataSubPath
import de.platon42.intellij.plugins.cajon.AbstractCajonTest
import org.junit.jupiter.api.Test
internal class JoinAssertThatStatementsInspectionTest : AbstractCajonTest() {
@Test
@TestDataSubPath("inspections/JoinStatements")
internal fun assertThat_size_of_array_or_collection_can_be_simplified(@MyFixture myFixture: JavaCodeInsightTestFixture) {
runTest {
myFixture.enableInspections(JoinAssertThatStatementsInspection::class.java)
myFixture.configureByFile("JoinStatementsBefore.java")
executeQuickFixes(myFixture, Regex.fromLiteral("Join assertThat() statements"), 6)
myFixture.checkResultByFile("JoinStatementsAfter.java")
}
}
}

View File

@ -0,0 +1,40 @@
import java.util.*;
import static org.assertj.core.api.Assertions.assertThat;
public class JoinStatements {
private void joinStatements() {
List<String> list = new ArrayList<>();
// the future is always born in pain
/* tricky */
assertThat(list).as("foo").hasSize(2)
/* do another */
/* do one */.as("bar").contains("barbar")
// comment to keep
.doesNotContain("barbara") // another comment to keep
.doesNotContain("wrzlbrmpft")
/* and a multi line comment
after the statement */
// across two lines
.as("etc")/* what a nasty comment */.contains("etcetc")
// moar!
.doesNotContain("foobar");
assertThat("narf").isNotEqualTo("puit").as("bar").contains("barbar").as("foo").hasSize(2);
assertThat(list).as("evil").extracting(String::length).contains(2);
assertThat(list).as("bar").contains("barbar");
assertThat("narf").isNotEqualTo("puit").as("foo").hasSize(2);
if (true) {
assertThat(list).doesNotContain("narf").as("bar").contains("barbar");
}
assertThat(list.get(0)).isNotEmpty().hasSize(3).isEqualTo("bar");
assertThat(list.get(0) + "foo").isEqualTo("bar").doesNotStartWith("foo");
Iterator<String> iterator = list.iterator();
assertThat(iterator.next()).isEqualTo("foo");
assertThat(iterator.next()).isEqualTo("bar");
}
}

View File

@ -0,0 +1,44 @@
import java.util.*;
import static org.assertj.core.api.Assertions.assertThat;
public class JoinStatements {
private void joinStatements() {
List<String> list = new ArrayList<>();
// the future is always born in pain
/* tricky */assertThat(list).as("foo").hasSize(2); /* do one */ /* do another */
assertThat(list).as("bar").contains("barbar"); // comment to keep
assertThat(list).doesNotContain("barbara") // another comment to keep
.doesNotContain("wrzlbrmpft") // across two lines
; /* and a multi line comment
after the statement */
assertThat(list).as("etc")/* what a nasty comment */.contains("etcetc");
// moar!
assertThat(list).doesNotContain("foobar");
assertThat("narf").isNotEqualTo("puit");
assertThat(list).as("bar").contains("barbar");
assertThat(list).as("foo").hasSize(2);
assertThat(list).as("evil").extracting(String::length).contains(2);
assertThat(list).as("bar").contains("barbar");
assertThat("narf").isNotEqualTo("puit");
assertThat(list).as("foo").hasSize(2);
if (true) {
assertThat(list).doesNotContain("narf");
assertThat(list).as("bar").contains("barbar");
}
assertThat(list.get(0)).isNotEmpty();
assertThat(list.get(0)).hasSize(3);
assertThat(list.get(0)).isEqualTo("bar");
assertThat(list.get(0) + "foo").isEqualTo("bar");
assertThat(list.get(0) + "foo").doesNotStartWith("foo");
Iterator<String> iterator = list.iterator();
assertThat(iterator.next()).isEqualTo("foo");
assertThat(iterator.next()).isEqualTo("bar");
}
}