Skip to content

Commit

Permalink
add recipe to migrate disables annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
MBoegers committed Mar 2, 2024
1 parent fd907a9 commit d0c0e0f
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 149 deletions.
124 changes: 124 additions & 0 deletions .idea/uiDesigner.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package io.github.mboegers.openrewrite.testngtojupiter;

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.*;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.Space;

import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Value
@EqualsAndHashCode(callSuper = false)
public class MigrateEnabledArgument extends Recipe {

@Override
public String getDisplayName() {
return "Replace TestNG enable Test";
}

@Override
public String getDescription() {
return "Replace @org.testng.annotations.Test's parameter with Juniper @Disabled.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new MigrateEnabledArgumentVisitor();
}

static class MigrateEnabledArgumentVisitor extends JavaIsoVisitor<ExecutionContext> {
private final AnnotationMatcher TESTNG_TEST_MATCHER = new AnnotationMatcher("@org.testng.annotations.Test");

@Override
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext executionContext) {
method = super.visitMethodDeclaration(method, executionContext);

// return early if not @Test annotation with argument absent present
var testNgAnnotation = method.getLeadingAnnotations().stream()
.filter(TESTNG_TEST_MATCHER::matches)
.findAny();
if (testNgAnnotation.isEmpty()) {
return method;
}

var enabledArgument = testNgAnnotation
.map(J.Annotation::getArguments).orElse(List.of())
.stream()
.filter(this::isEnabledExpression)
.map(J.Assignment.class::cast)
.findAny();
if (enabledArgument.isEmpty()) {
return method;
}

// add @Disables if enabled=false
Boolean isEnabled = (Boolean) ((J.Literal)enabledArgument.get().getAssignment().unwrap()).getValue();
if (Boolean.FALSE.equals(isEnabled)) {
var addAnnotationCoordinate = method.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName));
method = JavaTemplate
.builder("@Disabled")
.javaParser(JavaParser.fromJavaVersion().classpath("junit-jupiter-api"))
.imports("org.junit.jupiter.api.Disabled")
.build()
.apply(getCursor(), addAnnotationCoordinate);
maybeAddImport("org.junit.jupiter.api.Disabled", false);
}

// remove argument assigment
doAfterVisit(new RemoveAnnotationAttribute("org.testng.annotations.Test", "enabled").getVisitor());

return method;
}

private boolean isEnabledExpression(Expression expr) {
return expr instanceof J.Assignment &&
"enabled".equals(((J.Identifier) ((J.Assignment)expr).getVariable()).getSimpleName());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.*;
import org.openrewrite.java.search.FindAnnotations;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaCoordinates;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.function.Predicate;

@Value
@EqualsAndHashCode(callSuper = false)
Expand All @@ -50,112 +49,48 @@ class ReplaceTestAnnotationVisitor extends JavaIsoVisitor<ExecutionContext> {
private static final String TESTNG_TEST_FQN = "org.testng.annotations.Test";
private final AnnotationMatcher TESTNG_TEST_MATCHER = new AnnotationMatcher("@" + TESTNG_TEST_FQN);

private static final String JUPITER_TEST = "org.junit.jupiter.api.Test";
private static final String TEST_ANNOTATION = "@Test";

@Override
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
method = super.visitMethodDeclaration(method, ctx);
var methodAnnotations = method.getLeadingAnnotations();

//return early if no TestNG used
Optional<J.Annotation> testNGAnnotation = method.getLeadingAnnotations().stream().filter(TESTNG_TEST_MATCHER::matches).findAny();
if (testNGAnnotation.isEmpty()) {
//return early if no TestNG used or still has arguments
var testNgAnnotation = methodAnnotations.stream()
.filter(TESTNG_TEST_MATCHER::matches)
.findAny();
if (testNgAnnotation.isEmpty()) {
return method;
}

// transform TestNG @Test to Jupiter
var javaCoordinates = method.getCoordinates().addAnnotation((o1, o2) -> -1);
var testAnnotation = testNGAnnotation.get();
var annotations = method.getLeadingAnnotations();
var arguments = extractArgumentsFrom(testAnnotation);

annotations.add(new AddTestAnnotation(javaCoordinates, getCursor()).visitAnnotation(testAnnotation, ctx));
annotations.add(new TransformEnabled(javaCoordinates, getCursor(), arguments).visitAnnotation(testAnnotation, ctx));

method = method.withLeadingAnnotations(annotations);

// remove org.testng.annotations.Test annotation
maybeRemoveImport(TESTNG_TEST_FQN);
return new RemoveAnnotationVisitor(TESTNG_TEST_MATCHER).visitMethodDeclaration(method, ctx);
}

class AddTestAnnotation extends JavaIsoVisitor<ExecutionContext> {
private static final String JUPITER_TEST = "org.junit.jupiter.api.Test";
private static final String TEST_ANNOTATION = "@Test";

private final JavaCoordinates javaCoordinates;
private final Cursor cursor;

public AddTestAnnotation(JavaCoordinates javaCoordinates, Cursor cursor) {
this.javaCoordinates = javaCoordinates;
this.cursor = cursor;
}

@Override
public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext executionContext) {
J.Annotation testAnnotation = JavaTemplate
.builder(TEST_ANNOTATION)
.javaParser(JavaParser.fromJavaVersion().classpath( "junit-jupiter-api"))
.imports(JUPITER_TEST)
.build()
.apply(cursor, javaCoordinates);
maybeAddImport(JUPITER_TEST, false);

return testAnnotation;
}
}

class TransformEnabled extends JavaIsoVisitor<ExecutionContext> {

private static final String AT_DISABLED = "@Disabled";
private static final String DISABLED_FQN = "org.junit.jupiter.api.Disabled";

private static final String TEST_NG_SKIP_KEY = "enabled";

private final JavaCoordinates javaCoordinates;
private final Map<String, Expression> arguments;

private final Cursor cursor;

public TransformEnabled(JavaCoordinates javaCoordinates, Cursor cursor, Map<String, Expression> arguments) {
this.javaCoordinates = javaCoordinates;
this.arguments = arguments;
this.cursor = cursor;
}

@Override
public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext executionContext) {
Boolean isEnabled = (Boolean) ((J.Literal) arguments.get(TEST_NG_SKIP_KEY)).getValue();

if (isEnabled == null || !isEnabled) {
return annotation;
}

J.Annotation testAnnotation = JavaTemplate
.builder(AT_DISABLED)
.javaParser(JavaParser.fromJavaVersion().classpath( "junit-jupiter-api"))
.imports(DISABLED_FQN)
.build()
.apply(cursor, javaCoordinates);
maybeAddImport(DISABLED_FQN, false);

return testAnnotation;
boolean hasArguments = testNgAnnotation
.map(J.Annotation::getArguments)
.map(as -> !as.isEmpty() && as.stream().noneMatch(J.Empty.class::isInstance))
.orElse(false);
if (hasArguments) {
return method;
}
}
}

private static Map<String, Expression> extractArgumentsFrom(J.Annotation annotation) {
List<Expression> arguments = annotation.getArguments();
if (arguments == null) {
return Map.of();
}
// transform TestNG @Test to Jupiter
var addAnnotationCoordinate = method.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName));
var cursor = getCursor();
method = JavaTemplate
.builder(TEST_ANNOTATION)
.javaParser(JavaParser.fromJavaVersion().classpath( "junit-jupiter-api"))
.imports(JUPITER_TEST)
.build()
.apply(cursor, addAnnotationCoordinate);

// update imports
maybeAddImport(JUPITER_TEST, false);
maybeRemoveImport(TESTNG_TEST_FQN);

Map<String, Expression> values = new HashMap<>();
for (var expr : arguments) {
J.Assignment assigment = (J.Assignment) expr;
J.Identifier variable = (J.Identifier) assigment.getVariable();
Expression initializer = assigment.getAssignment();
//remove old annotation
doAfterVisit(new RemoveAnnotationVisitor(TESTNG_TEST_MATCHER));

values.put(variable.getSimpleName(), initializer);
return method;
}

return Map.copyOf(values);
}
}
2 changes: 1 addition & 1 deletion src/main/resources/META-INF/rewrite/rewrite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ displayName: Migrate Test NG tests to JUnit Jupiter
description: Migrate Test NG annotations and assertions to JUnit Jupiter. It is recommended to also execute org.openrewrite.java.testing.junit5.JUnit5BestPractices to the codebase.
recipeList:
- io.guthub.mboegers.openrewrite.testngtojupiter.MigrateTestAnnotation
- io.guthub.mboegers.openrewrite.testngtojupiter.MigrateEnabledArgument
- io.guthub.mboegers.openrewrite.testngtojupiter.MigrateAssertions
- org.openrewrite.java.testing.junit5.JUnit5BestPractices
---
Loading

0 comments on commit d0c0e0f

Please sign in to comment.