Skip to content

Commit 2936f17

Browse files
authored
Only adopt hasSeconds for isEqualTo in AdoptAssertJDurationAssertions (#1215)
* Stop mapping `Duration#getNano` to `hasNanos` in `AdoptAssertJDurationAssertions` `Duration#getNano()` returns only the nanosecond-of-second part, while AssertJ `DurationAssert#hasNanos` compares `toNanos()` of the whole duration. Rewriting `assertThat(d.getNano()).isEqualTo(1)` to `assertThat(d).hasNanos(1)` therefore made a passing assertion fail for any duration of a second or more, so the recipe now leaves `getNano()` assertions alone. * Only convert `isEqualTo` on `Duration#getSeconds` to `hasSeconds` in `AdoptAssertJDurationAssertions` The `getSeconds()` branch did not check which assertion it was replacing, so `assertThat(d.getSeconds()).isGreaterThan(5)` became `assertThat(d).hasSeconds(5)`, turning a comparison into an equality check. The zero handling also only recognised a method call as the `Duration`, so `assertThat(d.getSeconds()).isGreaterThan(0)` on a variable fell through to `hasSeconds(0)`. The recipe now only adopts `hasSeconds` for `isEqualTo`, and turns zero comparisons into `isPositive()`, `isNegative()` or `isZero()` for any `Duration` expression. * Stop converting `DurationAssert` units in `AdoptAssertJDurationAssertions` AssertJ `hasSeconds`, `hasMinutes` and the other unit assertions compare the duration truncated to that unit, so rewriting `hasMillis(5000)` to `hasSeconds(5)` weakened the assertion to also pass for 5999 ms. The conversion also turned a zero argument into `hasDays(0)` and threw an `ArithmeticException` from `Math.toIntExact` for values beyond `int` range. The recipe now leaves the unit assertions as written.
1 parent ad8bea1 commit 2936f17

2 files changed

Lines changed: 96 additions & 146 deletions

File tree

‎src/main/java/org/openrewrite/java/testing/assertj/AdoptAssertJDurationAssertions.java‎

Lines changed: 9 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package org.openrewrite.java.testing.assertj;
1717

1818
import lombok.Getter;
19-
import org.jspecify.annotations.Nullable;
2019
import org.openrewrite.ExecutionContext;
2120
import org.openrewrite.Preconditions;
2221
import org.openrewrite.Recipe;
@@ -28,9 +27,7 @@
2827
import org.openrewrite.java.search.UsesMethod;
2928
import org.openrewrite.java.tree.Expression;
3029
import org.openrewrite.java.tree.J;
31-
import org.openrewrite.java.tree.JavaType;
3230
import org.openrewrite.java.tree.TypeUtils;
33-
import org.openrewrite.staticanalysis.SimplifyDurationCreationUnits;
3431

3532
import java.util.*;
3633

@@ -39,7 +36,6 @@
3936

4037
public class AdoptAssertJDurationAssertions extends Recipe {
4138

42-
private static final String DURATION_ASSERT_HAS_LONG = "org.assertj.core.api.AbstractDurationAssert has*(long)";
4339
private static final String INTEGER_ASSERT_IS_EQUAL_TO = "org.assertj.core.api.AbstractIntegerAssert isEqualTo(..)";
4440
private static final String INTEGER_ASSERT_IS_GREATER_THAN = "org.assertj.core.api.AbstractIntegerAssert isGreaterThan(..)";
4541
private static final String INTEGER_ASSERT_IS_LESS_THAN = "org.assertj.core.api.AbstractIntegerAssert isLessThan(..)";
@@ -48,10 +44,9 @@ public class AdoptAssertJDurationAssertions extends Recipe {
4844
private static final String LONG_ASSERT_IS_EQUAL_TO = "org.assertj.core.api.AbstractLongAssert isEqualTo(..)";
4945

5046
private static final MethodMatcher ASSERT_THAT_MATCHER = new MethodMatcher("org.assertj.core.api.Assertions assertThat(..)");
51-
private static final MethodMatcher GET_NANO_MATCHER = new MethodMatcher("java.time.Duration getNano()");
5247
private static final MethodMatcher GET_SECONDS_MATCHER = new MethodMatcher("java.time.Duration getSeconds()");
5348
private static final MethodMatcher AS_MATCHER = new MethodMatcher("org.assertj.core.api.AbstractObjectAssert as(..)");
54-
private static final MethodMatcher TIME_UNIT_MATCHERS = new MethodMatcher(DURATION_ASSERT_HAS_LONG, true);
49+
private static final MethodMatcher LONG_IS_EQUAL_TO_MATCHER = new MethodMatcher(LONG_ASSERT_IS_EQUAL_TO, true);
5550

5651
private static final List<MethodMatcher> IS_MATCHERS = Arrays.asList(
5752
new MethodMatcher(INTEGER_ASSERT_IS_EQUAL_TO, true),
@@ -65,14 +60,6 @@ public class AdoptAssertJDurationAssertions extends Recipe {
6560

6661
private static final Map<String, String> METHOD_MAP = new HashMap<String, String>() {{
6762
put("getSeconds", "hasSeconds");
68-
put("getNano", "hasNanos");
69-
70-
put("hasNanos", "hasMillis");
71-
put("hasMillis", "hasSeconds");
72-
put("hasSeconds", "hasMinutes");
73-
put("hasMinutes", "hasHours");
74-
put("hasHours", "hasDays");
75-
7663
put("isGreaterThan", "isPositive");
7764
put("isLessThan", "isNegative");
7865
put("isEqualTo", "isZero");
@@ -88,7 +75,6 @@ public class AdoptAssertJDurationAssertions extends Recipe {
8875
public TreeVisitor<?, ExecutionContext> getVisitor() {
8976
return Preconditions.check(
9077
or(
91-
new UsesMethod<>(TIME_UNIT_MATCHERS),
9278
new UsesMethod<>(INTEGER_ASSERT_IS_EQUAL_TO, true),
9379
new UsesMethod<>(INTEGER_ASSERT_IS_GREATER_THAN, true),
9480
new UsesMethod<>(INTEGER_ASSERT_IS_LESS_THAN, true),
@@ -99,9 +85,6 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
9985
@Override
10086
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
10187
J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
102-
if (TIME_UNIT_MATCHERS.matches(mi)) {
103-
return simplifyTimeUnits(mi, ctx);
104-
}
10588
if (IS_MATCHERS.stream().anyMatch(matcher -> matcher.matches(mi))) {
10689
return simplifyMultipleAssertions(mi, ctx);
10790
}
@@ -139,7 +122,7 @@ private J.MethodInvocation simplifyMultipleAssertions(J.MethodInvocation m, Exec
139122
return applyTemplate(ctx, m, formatted_template, assertions, templateParameters.toArray());
140123
}
141124

142-
if (GET_NANO_MATCHER.matches(assertThatArg) || GET_SECONDS_MATCHER.matches(assertThatArg)) {
125+
if (LONG_IS_EQUAL_TO_MATCHER.matches(m) && GET_SECONDS_MATCHER.matches(assertThatArg)) {
143126
Expression assertThatArgSelect = assertThatArg.getSelect();
144127
String methodName = assertThatArg.getSimpleName();
145128
String formatted_template = formatTemplate("assertThat(#{any()}).%s(#{any()});", methodName, asDescription);
@@ -160,55 +143,13 @@ private boolean isZero(Expression isEqualToArg) {
160143
return false;
161144
}
162145

163-
private J.MethodInvocation simplifyTimeUnits(J.MethodInvocation m, ExecutionContext ctx) {
164-
Expression arg = m.getArguments().get(0);
165-
Long argValue = SimplifyDurationCreationUnits.getConstantIntegralValue(arg);
166-
if (argValue == null) {
167-
return m;
168-
}
169-
170-
List<Object> unitInfo = getUnitInfo(m.getSimpleName(), Math.toIntExact(argValue));
171-
String methodName = (String) unitInfo.get(0);
172-
int methodArg = (int) unitInfo.get(1);
173-
if (!m.getSimpleName().equals(methodName)) {
174-
// update method invocation with new name and arg
175-
String template = String.format("#{any()}.%s(%d)", methodName, methodArg);
176-
return applyTemplate(ctx, m, template, null, m.getSelect());
177-
}
178-
179-
return m;
180-
}
181-
182-
private List<Object> getUnitInfo(String name, int argValue) {
183-
final int timeLength;
184-
if ("hasSeconds".equals(name) || "hasMinutes".equals(name)) {
185-
timeLength = 60;
186-
} else if ("hasNanos".equals(name) || "hasMillis".equals(name)) {
187-
timeLength = 1000;
188-
} else if ("hasHours".equals(name)) {
189-
timeLength = 24;
190-
} else {
191-
return Arrays.asList(name, argValue);
192-
}
193-
194-
if (argValue % timeLength == 0) {
195-
String newName = METHOD_MAP.get(name);
196-
return getUnitInfo(newName, argValue / timeLength);
197-
}
198-
// returning name, newArg
199-
return Arrays.asList(name, argValue);
200-
}
201-
202-
private J.MethodInvocation applyTemplate(ExecutionContext ctx, J.MethodInvocation m, String template,
203-
@Nullable String assertions, Object... parameters) {
204-
JavaTemplate.Builder builder = JavaTemplate.builder(template)
146+
private J.MethodInvocation applyTemplate(ExecutionContext ctx, J.MethodInvocation m, String template, String assertions, Object... parameters) {
147+
maybeAddImport(assertions, "assertThat");
148+
J.MethodInvocation invocation = JavaTemplate.builder(template)
205149
.contextSensitive()
206-
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "assertj-core-3"));
207-
if (assertions != null) {
208-
builder.staticImports(assertions + ".assertThat");
209-
maybeAddImport(assertions, "assertThat");
210-
}
211-
J.MethodInvocation invocation = builder.build()
150+
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "assertj-core-3"))
151+
.staticImports(assertions + ".assertThat")
152+
.build()
212153
.apply(getCursor(), m.getCoordinates().replace(), parameters);
213154

214155
// retain whitespace formatting
@@ -223,13 +164,7 @@ private J.MethodInvocation applyTemplate(ExecutionContext ctx, J.MethodInvocatio
223164
}
224165

225166
private boolean checkIfRelatedToDuration(J.MethodInvocation argument) {
226-
if (argument.getSelect() != null) {
227-
if (argument.getSelect() instanceof J.MethodInvocation) {
228-
J.MethodInvocation selectMethod = (J.MethodInvocation) argument.getSelect();
229-
return TypeUtils.isOfType(selectMethod.getType(), JavaType.buildType("java.time.Duration"));
230-
}
231-
}
232-
return false;
167+
return argument.getSelect() != null && TypeUtils.isOfClassType(argument.getSelect().getType(), "java.time.Duration");
233168
}
234169

235170
@SuppressWarnings("ConstantValue")

0 commit comments

Comments
 (0)