Improved JoinAssertThatStatements detection of expressions with side-effects and added pre/post-increment/decrement detection.

This commit is contained in:
Chris Hodges 2019-05-11 13:40:37 +02:00
parent ecb5029154
commit 94c695617a
5 changed files with 60 additions and 31 deletions

View File

@ -81,8 +81,9 @@ You can toggle the various inspections in the Settings/Editor/Inspections in the
assertThat(expected).anotherCondition(); assertThat(expected).anotherCondition();
to: assertThat(expected).someCondition().anotherCondition(); to: assertThat(expected).someCondition().anotherCondition();
``` ```
Joining will work on actual expressions inside assertThat() that are equivalent expressions, Joining will work on actual expressions inside ```assertThat()``` that are equivalent expressions,
except for method calls with known side-effect methods such as ```Iterator.next()``` -- please notify me about others. except for method calls with known side-effect methods such as ```Iterator.next()``` and
pre/post-increment/decrement operations -- please notify me about others.
The comments of the statements will be preserved. When using ```.extracting()``` or similar, the statements will not be merged. The comments of the statements will be preserved. When using ```.extracting()``` or similar, the statements will not be merged.
@ -145,7 +146,7 @@ You can toggle the various inspections in the Settings/Editor/Inspections in the
- AssertThatStringExpression - AssertThatStringExpression
Moves string operations inside assertThat() out. Moves string operations inside ```assertThat()``` out.
``` ```
from: assertThat(stringActual.isEmpty()).isTrue(); from: assertThat(stringActual.isEmpty()).isTrue();
@ -171,7 +172,7 @@ You can toggle the various inspections in the Settings/Editor/Inspections in the
- AssertThatCollectionOrMapExpression - AssertThatCollectionOrMapExpression
Moves collection and map operations inside assertThat() out. Moves collection and map operations inside ```assertThat()``` out.
``` ```
from: assertThat(collection.isEmpty()).isTrue(); from: assertThat(collection.isEmpty()).isTrue();
@ -192,7 +193,7 @@ You can toggle the various inspections in the Settings/Editor/Inspections in the
from: assertThat(map.containsValue(value)).isTrue(); from: assertThat(map.containsValue(value)).isTrue();
to: assertThat(map).containsValue(value); to: assertThat(map).containsValue(value);
``` ```
Analogously with ```isFalse()``` (except for containsAll()). Analogously with ```isFalse()``` (except for ```containsAll()```).
- AssertThatEnumerableIsEmpty - AssertThatEnumerableIsEmpty
@ -279,7 +280,7 @@ You can toggle the various inspections in the Settings/Editor/Inspections in the
- AssertThatJava8Optional - AssertThatJava8Optional
Examines the statement for Java 8 Optional type and whether the statement Examines the statement for Java 8 ```Optional``` type and whether the statement
effectively tries to assert the presence, absence or content and then effectively tries to assert the presence, absence or content and then
replaces the statement by better assertions. replaces the statement by better assertions.
@ -313,7 +314,7 @@ You can toggle the various inspections in the Settings/Editor/Inspections in the
- AssertThatGuavaOptional - AssertThatGuavaOptional
Examines the statement for Google Guava Optional type and whether the statement Examines the statement for Google Guava ```Optional``` type and whether the statement
effectively tries to assert the presence, absence or content and then effectively tries to assert the presence, absence or content and then
replaces the statement by better assertions. replaces the statement by better assertions.
@ -444,9 +445,11 @@ Cajon is written in Kotlin 1.3.
Cajon is probably the only plugin that uses JUnit 5 Jupiter for unit testing so far (or at least the only one that I'm aware of ;) ). Cajon is probably the only plugin that uses JUnit 5 Jupiter for unit testing so far (or at least the only one that I'm aware of ;) ).
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).
## Planned features ## Planned features
- Joining .contains() expressions
- Removing .isPresent().contains() combinations for Optionals
- 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")...
@ -455,11 +458,14 @@ Feel free to use the code (in package de.platon42.intellij.jupiter) for your pro
## Changelog ## Changelog
#### V1.1 (unreleased)
- Improved JoinAssertThatStatements detection of expressions with side-effects and added pre/post-increment/decrement detection.
#### V1.0 (06-May-19) #### V1.0 (06-May-19)
- First release to be considered stable enough for production use. - First release to be considered stable enough for production use.
- Fixed a NPE in AssumeThatInsteadOfReturn inspection quickfix for empty else branches. - Fixed a NPE in AssumeThatInsteadOfReturn inspection quickfix for empty else branches.
- Fixed missing description for AssumeThatInsteadOfReturn inspection. - Fixed missing description for AssumeThatInsteadOfReturn inspection.
- Added new AssertThatCollectionOrMapExpression inspection that tries to pull out methods such as ```isEmpty()``` or ```contains()``` out of an actual assertThat() expression. - Added new AssertThatCollectionOrMapExpression inspection that tries to pull out methods such as ```isEmpty()``` or ```contains()``` out of an actual ```assertThat()``` expression.
#### V0.8 (05-May-19) #### V0.8 (05-May-19)
- Fixed missing description for JoinAssertThatStatements and detection of equivalent expressions (sorry, released it too hastily). - Fixed missing description for JoinAssertThatStatements and detection of equivalent expressions (sorry, released it too hastily).

View File

@ -7,7 +7,7 @@ plugins {
} }
group 'de.platon42' group 'de.platon42'
version '1.0' version '1.1'
repositories { repositories {
mavenCentral() mavenCentral()
@ -42,6 +42,10 @@ intellij {
patchPluginXml { patchPluginXml {
changeNotes """ changeNotes """
<h4>V1.1 (unreleased)</h4>
<ul>
<li>Improved JoinAssertThatStatements detection of expressions with side-effects and added pre/post-increment/decrement detection.
</ul>
<h4>V1.0 (06-May-19)</h4> <h4>V1.0 (06-May-19)</h4>
<ul> <ul>
<li>First release to be considered stable enough for production use. <li>First release to be considered stable enough for production use.
@ -49,17 +53,6 @@ patchPluginXml {
<li>Fixed missing description for AssumeThatInsteadOfReturn inspection. <li>Fixed missing description for AssumeThatInsteadOfReturn inspection.
<li>Added new AssertThatCollectionOrMapExpression inspection that tries to pull out methods such as isEmpty() or contains() out of an actual assertThat() expression. <li>Added new AssertThatCollectionOrMapExpression inspection that tries to pull out methods such as isEmpty() or contains() out of an actual assertThat() expression.
</ul> </ul>
<h4>V0.8 (05-May-19)</h4>
<ul>
<li>Fixed missing description for JoinAssertThatStatements and detection of equivalent expressions (sorry, released it too hastily).
<li>Fixed isEmpty() for enumerables and strings and isNull() for object conversions to be applied only if it is the terminal method call as isEmpty() and isNull() return void.
<li>Heavily reworked inspections for edge cases, such as multiple isEqualTo() calls inside a single statement.
<li>Some inspections could generate bogus code for weird situations, this has been made more fool-proof.
<li>Corrected highlighting for many inspections.
<li>Fixed family names for inspections in batch mode.
<li>Reworded many inspection messages for better understanding.
<li>Added a first version of a new inspection that tries to detect bogus uses of return statements in test methods and replaces them by assumeThat() calls.
</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

@ -35,16 +35,9 @@ class JoinAssertThatStatementsInspection : AbstractAssertJInspection() {
reset = (lastActualExpression == null) reset = (lastActualExpression == null)
actualExpression = assertThatCall.firstArg actualExpression = assertThatCall.firstArg
if (!reset) { if (!reset) {
val isSame = when (actualExpression) { val isSame = equivalenceChecker.expressionsAreEquivalent(actualExpression, lastActualExpression)
is PsiMethodCallExpression -> equivalenceChecker.expressionsAreEquivalent(actualExpression, lastActualExpression) && !hasExpressionWithSideEffects(actualExpression)
// Note: replace with PsiTreeUtil.findChildrenOfAnyType(strict = false) for IDEA >= 2018.1
&& !KNOWN_METHODS_WITH_SIDE_EFFECTS.test(actualExpression)
&& PsiTreeUtil.findChildrenOfAnyType(
actualExpression,
PsiMethodCallExpression::class.java
).none { KNOWN_METHODS_WITH_SIDE_EFFECTS.test(it) }
else -> equivalenceChecker.expressionsAreEquivalent(actualExpression, lastActualExpression)
}
if (isSame) { if (isSame) {
sameCount++ sameCount++
lastStatement = statement lastStatement = statement
@ -78,6 +71,25 @@ class JoinAssertThatStatementsInspection : AbstractAssertJInspection() {
} }
return null return null
} }
private fun hasExpressionWithSideEffects(actualExpression: PsiExpression): Boolean {
var result = false
PsiTreeUtil.processElements(actualExpression) { element ->
val matched = when (element) {
is PsiUnaryExpression -> (element.operationTokenType == JavaTokenType.PLUSPLUS)
|| (element.operationTokenType == JavaTokenType.MINUSMINUS)
is PsiMethodCallExpression -> KNOWN_METHODS_WITH_SIDE_EFFECTS.test(element)
else -> false
}
if (matched) {
result = true
false
} else {
true
}
}
return result
}
} }
} }

View File

@ -48,5 +48,14 @@ public class JoinStatements {
assertThat(iterator.next()).isEqualTo("bar"); assertThat(iterator.next()).isEqualTo("bar");
assertThat(iterator.next().toLowerCase()).isEqualTo("foo"); assertThat(iterator.next().toLowerCase()).isEqualTo("foo");
assertThat(iterator.next().toLowerCase()).isEqualTo("bar"); assertThat(iterator.next().toLowerCase()).isEqualTo("bar");
assertThat(iterator.next() + "bar").isEqualTo("foobar");
assertThat(iterator.next() + "bar").isEqualTo("barbar");
int i = 0;
assertThat(++i).isEqualTo(1);
assertThat(++i).isEqualTo(2);
assertThat(list.get(i++).toLowerCase()).isEqualTo("foo");
assertThat(list.get(i++).toLowerCase()).isEqualTo("foo");
assertThat(list.get(--i)).isEqualTo("foo");
assertThat(list.get(--i)).isEqualTo("foo");
} }
} }

View File

@ -50,5 +50,14 @@ public class JoinStatements {
assertThat(iterator.next()).isEqualTo("bar"); assertThat(iterator.next()).isEqualTo("bar");
assertThat(iterator.next().toLowerCase()).isEqualTo("foo"); assertThat(iterator.next().toLowerCase()).isEqualTo("foo");
assertThat(iterator.next().toLowerCase()).isEqualTo("bar"); assertThat(iterator.next().toLowerCase()).isEqualTo("bar");
assertThat(iterator.next() + "bar").isEqualTo("foobar");
assertThat(iterator.next() + "bar").isEqualTo("barbar");
int i = 0;
assertThat(++i).isEqualTo(1);
assertThat(++i).isEqualTo(2);
assertThat(list.get(i++).toLowerCase()).isEqualTo("foo");
assertThat(list.get(i++).toLowerCase()).isEqualTo("foo");
assertThat(list.get(--i)).isEqualTo("foo");
assertThat(list.get(--i)).isEqualTo("foo");
} }
} }