Skip to content

Commit

Permalink
Flatten Hamcrest assertThat allOf (#375)
Browse files Browse the repository at this point in the history
* Flatten Hamcrest assertThat allOf

For #357

* tried another approach using visitMethodInvocation

* Wrap statements in a block and remove the block after apply

* Add missing license

* Include and test FlattenAllOf followed by migration

* Correctly use || instead of &&

---------

Co-authored-by: AlekSimpson <alek@asu.me>
  • Loading branch information
timtebeek and AlekSimpson authored Jul 20, 2023
1 parent b4db150 commit 244aea7
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 2 deletions.
3 changes: 1 addition & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
@file:Suppress("GradlePackageUpdate")

import java.util.*

plugins {
id("org.openrewrite.build.recipe-library") version "latest.release"
}
Expand Down Expand Up @@ -34,6 +32,7 @@ dependencies {
implementation("org.openrewrite:rewrite-gradle")
implementation("org.openrewrite:rewrite-maven")
implementation("org.openrewrite.recipe:rewrite-java-dependencies:$rewriteVersion")
implementation("org.openrewrite.recipe:rewrite-static-analysis:$rewriteVersion")
runtimeOnly("org.openrewrite:rewrite-java-17")

compileOnly("org.projectlombok:lombok:latest.release")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.testing.hamcrest;

import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.staticanalysis.RemoveUnneededBlock;

import java.util.ArrayList;
import java.util.List;

public class FlattenAllOf extends Recipe {
@Override
public String getDisplayName() {
return "Convert Hamcrest `allOf(Matcher...)` to individual `assertThat` statements";
}

@Override
public String getDescription() {
return "Convert Hamcrest `allOf(Matcher...)` to individual `assertThat` statements for easier migration.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(
new UsesMethod<>("org.hamcrest.Matchers allOf(..)"),
new FlattenAllOfVisitor());
}
}

@SuppressWarnings("NullableProblems")
class FlattenAllOfVisitor extends JavaVisitor<ExecutionContext> {
private final MethodMatcher ASSERT_THAT_MATCHER = new MethodMatcher("org.hamcrest.MatcherAssert assertThat(..)");
private final MethodMatcher ALL_OF_MATCHER = new MethodMatcher("org.hamcrest.Matchers allOf(..)");

@Override
public J visitMethodInvocation(J.MethodInvocation invocation, ExecutionContext ctx) {
J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(invocation, ctx);

List<Expression> arguments = mi.getArguments();
Expression allOf = arguments.get(arguments.size() - 1);
if (!ASSERT_THAT_MATCHER.matches(mi) || !ALL_OF_MATCHER.matches(allOf)) {
return mi;
}

Expression reason = arguments.size() == 3 ? arguments.get(0) : null;
Expression actual = arguments.get(arguments.size() - 2);

// Wrap statements in a block, as JavaTemplate can only return one element
StringBuilder blockTemplate = new StringBuilder();
blockTemplate.append("{");
List<Expression> parameters = new ArrayList<>();
for (Expression matcher : ((J.MethodInvocation) allOf).getArguments()) {
if (reason == null) {
blockTemplate.append("assertThat(#{any(java.lang.Object)}, #{any(org.hamcrest.Matcher)});");
} else {
blockTemplate.append("assertThat(#{any(java.lang.String)}, #{any(java.lang.Object)}, #{any(org.hamcrest.Matcher)});");
parameters.add(reason);
}
parameters.add(actual);
parameters.add(matcher);
}
blockTemplate.append("}");

JavaTemplate template = JavaTemplate.builder(blockTemplate.toString())
.contextSensitive()
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "hamcrest-2.2"))
.staticImports("org.hamcrest.MatcherAssert.assertThat")
.build();

// Remove wrapping block and allOf import after template is applied
doAfterVisit(new RemoveUnneededBlock().getVisitor());
maybeRemoveImport("org.hamcrest.Matchers.allOf");
return template.apply(getCursor(), mi.getCoordinates().replace(), parameters.toArray());
}
}
3 changes: 3 additions & 0 deletions src/main/resources/META-INF/rewrite/hamcrest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ recipeList:
# First remove wrapping `is(Matcher)` calls such that further recipes will match
- org.openrewrite.java.testing.hamcrest.RemoveIsMatcher

# Flatten calls to `MatcherAssert.assertThat(.., allOf(..))` to easier to migrate individual statements
- org.openrewrite.java.testing.hamcrest.FlattenAllOf

# Then remove calls to `MatcherAssert.assertThat(String, boolean)`
- org.openrewrite.java.testing.hamcrest.AssertThatBooleanToAssertJ

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openrewrite.java.testing.hamcrest;

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

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

class FlattenAllOfTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec
.recipe(new FlattenAllOf())
.parser(JavaParser.fromJavaVersion()
.classpathFromResources(new InMemoryExecutionContext(),
"junit-jupiter-api-5.9",
"hamcrest-2.2",
"assertj-core-3.24"));
}

@DocumentExample
@Test
void flattenAllOfStringMatchers() {
rewriteRun(
//language=java
java("""
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasLength;
class ATest {
@Test
void test() {
String str1 = "Hello world!";
String str2 = "Hello world!";
assertThat(str1, allOf(equalTo(str2), hasLength(12)));
}
}
""","""
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasLength;
class ATest {
@Test
void test() {
String str1 = "Hello world!";
String str2 = "Hello world!";
assertThat(str1, equalTo(str2));
assertThat(str1, hasLength(12));
}
}
"""));
}

@Test
void flattenAllOfStringMatchersWithReason() {
rewriteRun(
//language=java
java("""
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasLength;
class ATest {
@Test
void test() {
String str1 = "Hello world!";
String str2 = "Hello world!";
assertThat("str1 and str2 should be equal", str1, allOf(equalTo(str2), hasLength(12)));
}
}
""","""
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasLength;
class ATest {
@Test
void test() {
String str1 = "Hello world!";
String str2 = "Hello world!";
assertThat("str1 and str2 should be equal", str1, equalTo(str2));
assertThat("str1 and str2 should be equal", str1, hasLength(12));
}
}
"""));
}

@Test
void flattenAllOfIntMatchers() {
rewriteRun(
//language=java
java("""
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
class ATest {
@Test
void test() {
int i = 1;
assertThat(i, allOf(equalTo(1), greaterThan(0)));
}
}
""","""
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
class ATest {
@Test
void test() {
int i = 1;
assertThat(i, equalTo(1));
assertThat(i, greaterThan(0));
}
}
"""));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,43 @@ void testEquals() {
}
"""));
}
@DocumentExample
@Test
void flattenAllOfStringMatchersAndConvert() {
rewriteRun(
//language=java
java("""
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasLength;
class ATest {
@Test
void test() {
String str1 = "Hello world!";
String str2 = "Hello world!";
assertThat(str1, allOf(equalTo(str2), hasLength(12)));
}
}
""","""
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class ATest {
@Test
void test() {
String str1 = "Hello world!";
String str2 = "Hello world!";
assertThat(str1).isEqualTo(str2);
assertThat(str1).hasSize(12);
}
}
"""));
}

private static Stream<Arguments> arrayReplacements() {
return Stream.of(
Expand Down

0 comments on commit 244aea7

Please sign in to comment.