Skip to content

Commit ba5bd70

Browse files
committed
Implement special migration for float/double-array delta assertion
Cannot be implemented in Refaster due to openrewrite/rewrite-templating#90
1 parent 505985a commit ba5bd70

File tree

3 files changed

+239
-0
lines changed

3 files changed

+239
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package org.philzen.oss.testng;
2+
3+
import org.openrewrite.*;
4+
import org.openrewrite.internal.lang.NonNullApi;
5+
import org.openrewrite.java.JavaParser;
6+
import org.openrewrite.java.JavaTemplate;
7+
import org.openrewrite.java.JavaVisitor;
8+
import org.openrewrite.java.search.UsesMethod;
9+
import org.openrewrite.java.search.UsesType;
10+
import org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor;
11+
import org.openrewrite.java.tree.J;
12+
import org.philzen.oss.utils.JupiterJavaParser;
13+
14+
import java.util.function.Function;
15+
16+
import static org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor.EmbeddingOption.SHORTEN_NAMES;
17+
import static org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor.EmbeddingOption.SIMPLIFY_BOOLEANS;
18+
19+
@NonNullApi
20+
public class MigrateMismatchedAssertions extends Recipe {
21+
22+
@Override
23+
public String getDisplayName() {
24+
return "Replace `Assert#assertEquals(actual[], expected[], delta [, message])` for float and double inputs";
25+
}
26+
27+
@Override
28+
public String getDescription() {
29+
return "Replace `org.testng.Assert#assertEquals(actual[], expected[], delta [, message])` with custom `org.junit.jupiter.api.Assertions#assertAll(() -> {})`.";
30+
}
31+
32+
@Override
33+
public TreeVisitor<?, ExecutionContext> getVisitor() {
34+
JavaVisitor<ExecutionContext> javaVisitor = new AbstractRefasterJavaVisitor() {
35+
36+
final Function<String, JavaTemplate> before = (type) -> JavaTemplate
37+
.builder("org.testng.Assert.assertEquals(#{actual:anyArray(%s)}, #{expected:anyArray(%s)}, #{delta:any(%s)});".replace("%s", type))
38+
.javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath()))
39+
.build();
40+
41+
final Function<String, JavaTemplate> beforeWithMsg = (type) -> JavaTemplate
42+
.builder("org.testng.Assert.assertEquals(#{actual:anyArray(%s)}, #{expected:anyArray(%s)}, #{delta:any(%s)}, #{message:any(java.lang.String)});".replace("%s", type))
43+
.javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath()))
44+
.build();
45+
46+
final JavaTemplate after = JavaTemplate
47+
.builder("Assertions.assertAll(()->{\n Assertions.assertEquals(#{expected:anyArray(float)}.length, #{actual:anyArray(float)}.length, \"Arrays don't have the same size.\");\n for (int i = 0; i < #{actual}.length; i++) {\n Assertions.assertEquals(#{expected}[i], #{actual}[i], #{delta:any(float)});\n }\n});")
48+
.imports("org.junit.jupiter.api.Assertions")
49+
.javaParser(JupiterJavaParser.get()).build();
50+
51+
final JavaTemplate afterWithMsg = JavaTemplate
52+
.builder("Assertions.assertAll(()->{\n Assertions.assertEquals(#{expected:anyArray(float)}.length, #{actual:anyArray(float)}.length, \"Arrays don't have the same size.\");\n for (int i = 0; i < #{actual}.length; i++) {\n Assertions.assertEquals(#{expected}[i], #{actual}[i], #{delta:any(float)}, #{message:any(String)});\n }\n});")
53+
.imports("org.junit.jupiter.api.Assertions")
54+
.javaParser(JupiterJavaParser.get()).build();
55+
56+
@Override
57+
public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) {
58+
JavaTemplate.Matcher matcher;
59+
if ((matcher = before.apply("float").matcher(getCursor())).find()
60+
|| (matcher = before.apply("double").matcher(getCursor())).find())
61+
{
62+
return embed(ctx, after.apply(
63+
getCursor(),
64+
elem.getCoordinates().replace(),
65+
matcher.parameter(1),
66+
matcher.parameter(0),
67+
matcher.parameter(2))
68+
);
69+
}
70+
if ((matcher = beforeWithMsg.apply("float").matcher(getCursor())).find()
71+
|| (matcher = beforeWithMsg.apply("double").matcher(getCursor())).find())
72+
{
73+
return embed(ctx, afterWithMsg.apply(
74+
getCursor(),
75+
elem.getCoordinates().replace(),
76+
matcher.parameter(1),
77+
matcher.parameter(0),
78+
matcher.parameter(2),
79+
matcher.parameter(3)
80+
));
81+
}
82+
return super.visitMethodInvocation(elem, ctx);
83+
}
84+
85+
private J embed(ExecutionContext ctx, J j) {
86+
maybeRemoveImport("org.testng.Assert");
87+
maybeAddImport("org.junit.jupiter.api.Assertions");
88+
return super.embed(j, getCursor(), ctx, SHORTEN_NAMES, SIMPLIFY_BOOLEANS);
89+
}
90+
};
91+
92+
return Preconditions.check(
93+
Preconditions.and(
94+
new UsesType<>("org.testng.Assert", true),
95+
new UsesMethod<>("org.testng.Assert assertEquals(..)")
96+
),
97+
javaVisitor
98+
);
99+
}
100+
}

src/test/java/org/philzen/oss/research/ApiComparisonTest.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,31 @@ public class ApiComparisonTest {
5555
thisWillFail(() -> Assertions.assertEquals(2d, actual, .999d));
5656
}
5757

58+
@Tag("missing")
59+
@Test void doubleArrayDelta() {
60+
final double[] expected = new double[] {0d, 10d};
61+
final double[] actual = new double[] {1d, 9d};
62+
thisWillPass(() -> Assert.assertEquals(actual, expected, 1d));
63+
// there is no equivalent in Jupiter :/
64+
65+
thisWillFail(() -> Assert.assertEquals(actual, expected, .999d));
66+
// there is no equivalent in Jupiter :/
67+
68+
// possible migration equivalent
69+
thisWillPass(() -> Assertions.assertAll(() -> {
70+
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
71+
for (int i = 0; i < actual.length; i++) {
72+
Assertions.assertEquals(expected[i], actual[i], 1d);
73+
}
74+
}));
75+
thisWillFail(() -> Assertions.assertAll(() -> {
76+
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
77+
for (int i = 0; i < actual.length; i++) {
78+
Assertions.assertEquals(expected[i], actual[i], .999d);
79+
}
80+
}));
81+
}
82+
5883
@Test void floatDelta() {
5984
final float actual = 1f;
6085

@@ -64,6 +89,31 @@ public class ApiComparisonTest {
6489
thisWillFail(() -> Assert.assertEquals(actual, 2f, .999f));
6590
thisWillFail(() -> Assertions.assertEquals(2f, actual, .999f));
6691
}
92+
93+
@Tag("missing")
94+
@Test void floatArrayDelta() {
95+
final double[] expected = new double[] {0d, 10f};
96+
final double[] actual = new double[] {1f, 9f};
97+
thisWillPass(() -> Assert.assertEquals(actual, expected, 1f));
98+
// there is no equivalent in Jupiter :/
99+
100+
thisWillFail(() -> Assert.assertEquals(actual, expected, .999f));
101+
// there is no equivalent in Jupiter :/
102+
103+
// possible migration equivalent
104+
thisWillPass(() -> Assertions.assertAll(() -> {
105+
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
106+
for (int i = 0; i < actual.length; i++) {
107+
Assertions.assertEquals(expected[i], actual[i], 1f);
108+
}
109+
}));
110+
thisWillFail(() -> Assertions.assertAll(() -> {
111+
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
112+
for (int i = 0; i < actual.length; i++) {
113+
Assertions.assertEquals(expected[i], actual[i], .999f);
114+
}
115+
}));
116+
}
67117
}
68118

69119
@Nested class assertNotEquals {
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package org.philzen.oss.testng;
2+
3+
import org.junit.jupiter.params.ParameterizedTest;
4+
import org.junit.jupiter.params.provider.ValueSource;
5+
import org.openrewrite.test.RecipeSpec;
6+
import org.openrewrite.test.RewriteTest;
7+
8+
import static org.openrewrite.java.Assertions.java;
9+
10+
class MigrateMismatchedAssertionsTest implements RewriteTest {
11+
@Override
12+
public void defaults(RecipeSpec spec) {
13+
spec.recipe(new MigrateMismatchedAssertions());
14+
}
15+
16+
@ValueSource(strings = {"float[]", "double[]"})
17+
@ParameterizedTest
18+
void deltaFunctionForArraysIsMigrated(String type) {
19+
//language=java
20+
rewriteRun(java(
21+
"""
22+
import org.testng.Assert;
23+
24+
class MyTest {
25+
void testMethod() {
26+
%s actual;
27+
%s expected;
28+
29+
Assert.assertEquals(actual, expected, %s);
30+
}
31+
}
32+
""".formatted(type, type, type.equals("float[]") ? "0.1f" : "0.2d"),
33+
"""
34+
import org.junit.jupiter.api.Assertions;
35+
36+
class MyTest {
37+
void testMethod() {
38+
%s actual;
39+
%s expected;
40+
41+
Assertions.assertAll(() -> {
42+
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
43+
for (int i = 0; i < actual.length; i++) {
44+
Assertions.assertEquals(expected[i], actual[i], %s);
45+
}
46+
});
47+
}
48+
}
49+
""".formatted(type, type, type.equals("float[]") ? "0.1f" : "0.2d")
50+
));
51+
}
52+
53+
@ValueSource(strings = {"float[]", "double[]"})
54+
@ParameterizedTest
55+
void deltaFunctionForArraysIsMigratedWithMessage(String type) {
56+
//language=java
57+
rewriteRun(java(
58+
"""
59+
import org.testng.Assert;
60+
61+
class MyTest {
62+
void testMethod() {
63+
%s actual;
64+
%s expected;
65+
66+
Assert.assertEquals(actual, expected, %s, "Those values are way off.");
67+
}
68+
}
69+
""".formatted(type, type, type.equals("float[]") ? "0.1f" : "0.2d"),
70+
"""
71+
import org.junit.jupiter.api.Assertions;
72+
73+
class MyTest {
74+
void testMethod() {
75+
%s actual;
76+
%s expected;
77+
78+
Assertions.assertAll(() -> {
79+
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
80+
for (int i = 0; i < actual.length; i++) {
81+
Assertions.assertEquals(expected[i], actual[i], %s, "Those values are way off.");
82+
}
83+
});
84+
}
85+
}
86+
""".formatted(type, type, type.equals("float[]") ? "0.1f" : "0.2d")
87+
));
88+
}
89+
}

0 commit comments

Comments
 (0)