Skip to content

Commit

Permalink
Implement special migration for float/double-array delta assertion
Browse files Browse the repository at this point in the history
Cannot be implemented in Refaster due to
openrewrite/rewrite-templating#90
  • Loading branch information
Philzen committed Jun 14, 2024
1 parent 65935af commit aaecae1
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 0 deletions.
100 changes: 100 additions & 0 deletions src/main/java/org/philzen/oss/testng/MigrateMismatchedAssertions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package org.philzen.oss.testng;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.internal.lang.NonNullApi;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.J;
import org.philzen.oss.utils.Parser;

import java.util.function.Function;

@NonNullApi
public class MigrateMismatchedAssertions extends Recipe {

@Override
public String getDisplayName() {
return "Replace `Assert#assertEquals(actual[], expected[], delta [, message])` for float and double inputs";
}

@Override
public String getDescription() {
return "Replaces `org.testng.Assert#assertEquals(actual[], expected[], delta [, message])` with custom `org.junit.jupiter.api.Assertions#assertAll(() -> {})`.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
JavaVisitor<ExecutionContext> javaVisitor = new JavaVisitor<ExecutionContext>() {

final Function<String, JavaTemplate> before = (type) -> JavaTemplate
.builder("org.testng.Assert.assertEquals(#{actual:anyArray(%s)}, #{expected:anyArray(%s)}, #{delta:any(%s)});".replace("%s", type))
.javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath()))
.build();

final Function<String, JavaTemplate> beforeWithMsg = (type) -> JavaTemplate
.builder("org.testng.Assert.assertEquals(#{actual:anyArray(%s)}, #{expected:anyArray(%s)}, #{delta:any(%s)}, #{message:any(java.lang.String)});".replace("%s", type))
.javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath()))
.build();

final JavaTemplate after = JavaTemplate
.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});")
.imports("org.junit.jupiter.api.Assertions")
.javaParser(Parser.jupiter()).build();

final JavaTemplate afterWithMsg = JavaTemplate
.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});")
.imports("org.junit.jupiter.api.Assertions")
.javaParser(Parser.jupiter()).build();

@Override
public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) {
JavaTemplate.Matcher matcher;
if ((matcher = before.apply("float").matcher(getCursor())).find()
|| (matcher = before.apply("double").matcher(getCursor())).find())
{
imports();
return after.apply(
getCursor(),
elem.getCoordinates().replace(),
matcher.parameter(1),
matcher.parameter(0),
matcher.parameter(2)
);
} else if ((matcher = beforeWithMsg.apply("float").matcher(getCursor())).find()
|| (matcher = beforeWithMsg.apply("double").matcher(getCursor())).find())
{
imports();
return afterWithMsg.apply(
getCursor(),
elem.getCoordinates().replace(),
matcher.parameter(1),
matcher.parameter(0),
matcher.parameter(2),
matcher.parameter(3)
);
}

return super.visitMethodInvocation(elem, ctx);
}

private void imports() {
maybeRemoveImport("org.testng.Assert");
maybeAddImport("org.junit.jupiter.api.Assertions");
}
};

return Preconditions.check(
Preconditions.and(
new UsesType<>("org.testng.Assert", true),
new UsesMethod<>("org.testng.Assert assertEquals(..)")
),
javaVisitor
);
}
}
1 change: 1 addition & 0 deletions src/main/resources/META-INF/rewrite/rewrite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ preconditions:
filePattern: "**/*.java"
recipeList:
- org.philzen.oss.testng.UpdateTestAnnotationToJunit5
- org.philzen.oss.testng.MigrateMismatchedAssertions
- org.openrewrite.java.testing.junit5.AddMissingNested
- io.github.mboegers.openrewrite.testngtojupiter.MigrateAssertionsRecipes
50 changes: 50 additions & 0 deletions src/test/java/org/philzen/oss/research/ApiComparisonTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,31 @@ class ApiComparisonTest {
thisWillFail(() -> Assertions.assertEquals(2d, actual, .999d));
}

@Tag("missing")
@Test void doubleArrayDelta() {
final double[] expected = new double[] {0d, 10d};
final double[] actual = new double[] {1d, 9d};
thisWillPass(() -> Assert.assertEquals(actual, expected, 1d));
// there is no equivalent in Jupiter :/

thisWillFail(() -> Assert.assertEquals(actual, expected, .999d));
// there is no equivalent in Jupiter :/

// possible migration equivalent
thisWillPass(() -> Assertions.assertAll(() -> {
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
for (int i = 0; i < actual.length; i++) {
Assertions.assertEquals(expected[i], actual[i], 1d);
}
}));
thisWillFail(() -> Assertions.assertAll(() -> {
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
for (int i = 0; i < actual.length; i++) {
Assertions.assertEquals(expected[i], actual[i], .999d);
}
}));
}

@Test void floatDelta() {
final float actual = 1f;

Expand All @@ -85,6 +110,31 @@ class ApiComparisonTest {
thisWillFail(() -> Assert.assertEquals(actual, 2f, .999f));
thisWillFail(() -> Assertions.assertEquals(2f, actual, .999f));
}

@Tag("missing")
@Test void floatArrayDelta() {
final double[] expected = new double[] {0d, 10f};
final double[] actual = new double[] {1f, 9f};
thisWillPass(() -> Assert.assertEquals(actual, expected, 1f));
// there is no equivalent in Jupiter :/

thisWillFail(() -> Assert.assertEquals(actual, expected, .999f));
// there is no equivalent in Jupiter :/

// possible migration equivalent
thisWillPass(() -> Assertions.assertAll(() -> {
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
for (int i = 0; i < actual.length; i++) {
Assertions.assertEquals(expected[i], actual[i], 1f);
}
}));
thisWillFail(() -> Assertions.assertAll(() -> {
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
for (int i = 0; i < actual.length; i++) {
Assertions.assertEquals(expected[i], actual[i], .999f);
}
}));
}
}

@Nested class assertNotEquals {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.philzen.oss.testng;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

class MigrateMismatchedAssertionsTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec.recipe(new MigrateMismatchedAssertions());
}

@ValueSource(strings = {"float[]", "double[]"})
@ParameterizedTest
void deltaFunctionForArraysIsMigrated(String type) {
//language=java
rewriteRun(java(
"""
import org.testng.Assert;
class MyTest {
void testMethod() {
%s actual;
%s expected;
Assert.assertEquals(actual, expected, %s);
}
}
""".formatted(type, type, type.equals("float[]") ? "0.1f" : "0.2d"),
"""
import org.junit.jupiter.api.Assertions;
class MyTest {
void testMethod() {
%s actual;
%s expected;
Assertions.assertAll(() -> {
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
for (int i = 0; i < actual.length; i++) {
Assertions.assertEquals(expected[i], actual[i], %s);
}
});
}
}
""".formatted(type, type, type.equals("float[]") ? "0.1f" : "0.2d")
));
}

@ValueSource(strings = {"float[]", "double[]"})
@ParameterizedTest
void deltaFunctionForArraysIsMigratedWithMessage(String type) {
//language=java
rewriteRun(java(
"""
import org.testng.Assert;
class MyTest {
void testMethod() {
%s actual;
%s expected;
Assert.assertEquals(actual, expected, %s, "Those values are way off.");
}
}
""".formatted(type, type, type.equals("float[]") ? "0.1f" : "0.2d"),
"""
import org.junit.jupiter.api.Assertions;
class MyTest {
void testMethod() {
%s actual;
%s expected;
Assertions.assertAll(() -> {
Assertions.assertEquals(expected.length, actual.length, "Arrays don't have the same size.");
for (int i = 0; i < actual.length; i++) {
Assertions.assertEquals(expected[i], actual[i], %s, "Those values are way off.");
}
});
}
}
""".formatted(type, type, type.equals("float[]") ? "0.1f" : "0.2d")
));
}
}

0 comments on commit aaecae1

Please sign in to comment.