diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917..e6441136f 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b6114cae7..cdaec8840 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c +distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d diff --git a/src/main/java/org/openrewrite/java/testing/junit5/AddJupiterDependencies.java b/src/main/java/org/openrewrite/java/testing/junit5/AddJupiterDependencies.java new file mode 100644 index 000000000..928a7646f --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/junit5/AddJupiterDependencies.java @@ -0,0 +1,129 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.junit5; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.*; +import org.openrewrite.gradle.marker.GradleDependencyConfiguration; +import org.openrewrite.gradle.marker.GradleProject; +import org.openrewrite.groovy.GroovyIsoVisitor; +import org.openrewrite.groovy.tree.G; +import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.java.dependencies.AddDependency; +import org.openrewrite.maven.MavenIsoVisitor; +import org.openrewrite.maven.tree.ResolvedDependency; +import org.openrewrite.maven.tree.Scope; +import org.openrewrite.xml.tree.Xml; + +import java.util.List; +import java.util.Optional; + +@Value +@EqualsAndHashCode(callSuper = false) +public class AddJupiterDependencies extends ScanningRecipe { + @Override + public String getDisplayName() { + return "Add JUnit Jupiter dependencies"; + } + + @Override + public String getDescription() { + return "Adds JUnit Jupiter dependencies to a Maven or Gradle project. " + + "Junit Jupiter can be added either with the artifact junit-jupiter, or both of junit-jupiter-api and junit-jupiter-engine. " + + "This adds \"junit-jupiter\" dependency unless \"junit-jupiter-api\" or \"junit-jupiter-engine\" are already present."; + } + + @Override + public AddDependency.Accumulator getInitialValue(ExecutionContext ctx) { + return addJupiterDependency().getInitialValue(ctx); + } + + @Override + public TreeVisitor getScanner(AddDependency.Accumulator acc) { + return addJupiterDependency().getScanner(acc); + } + + @Override + public TreeVisitor getVisitor(AddDependency.Accumulator acc) { + AddJupiterGradle gv = new AddJupiterGradle(acc); + AddJupiterMaven mv = new AddJupiterMaven(acc); + return new TreeVisitor() { + @Override + public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx, Cursor parent) { + if(!(tree instanceof SourceFile)) { + return tree; + } + SourceFile s = (SourceFile) tree; + if(gv.isAcceptable(s, ctx)) { + s = (SourceFile) gv.visitNonNull(s, ctx); + } + if(mv.isAcceptable(s, ctx)) { + s = (SourceFile) mv.visitNonNull(s, ctx); + } + return s; + } + }; + } + + private static AddDependency addJupiterDependency() { + return new AddDependency("org.junit.jupiter", "junit-jupiter", "5.x", null, + "org.junit..*", null, null, null, null, "test", + null, null, null, null); + } + + @Value + @EqualsAndHashCode(callSuper = false) + private static class AddJupiterGradle extends GroovyIsoVisitor { + AddDependency.Accumulator acc; + + @Override + public G.CompilationUnit visitCompilationUnit(G.CompilationUnit t, ExecutionContext ctx) { + Optional maybeGp = t.getMarkers().findFirst(GradleProject.class); + if(!maybeGp.isPresent()) { + return t; + } + GradleProject gp = maybeGp.get(); + GradleDependencyConfiguration trc = gp.getConfiguration("testRuntimeClasspath"); + if(trc == null) { + return t; + } + ResolvedDependency jupiterApi = trc.findResolvedDependency("org.junit.jupiter", "junit-jupiter-api"); + if(jupiterApi == null) { + t = (G.CompilationUnit) addJupiterDependency().getVisitor(acc) + .visitNonNull(t, ctx); + } + + return t; + } + } + + @Value + @EqualsAndHashCode(callSuper = false) + private static class AddJupiterMaven extends MavenIsoVisitor { + AddDependency.Accumulator acc; + @Override + public Xml.Document visitDocument(Xml.Document document, ExecutionContext ctx) { + Xml.Document d = document; + List jupiterApi = getResolutionResult().findDependencies("org.junit.jupiter", "junit-jupiter-api", Scope.Test); + if(jupiterApi.isEmpty()) { + d = (Xml.Document) addJupiterDependency().getVisitor(acc) + .visitNonNull(d, ctx); + } + return d; + } + } +} diff --git a/src/main/java/org/openrewrite/java/testing/junit5/GradleUseJunitJupiter.java b/src/main/java/org/openrewrite/java/testing/junit5/GradleUseJunitJupiter.java new file mode 100644 index 000000000..995408803 --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/junit5/GradleUseJunitJupiter.java @@ -0,0 +1,265 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.junit5; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.gradle.GradleParser; +import org.openrewrite.gradle.marker.GradleProject; +import org.openrewrite.groovy.GroovyIsoVisitor; +import org.openrewrite.groovy.tree.G; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.TypeUtils; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +import static java.util.Objects.requireNonNull; + +@Value +@EqualsAndHashCode(callSuper = false) +public class GradleUseJunitJupiter extends Recipe { + @Override + public String getDisplayName() { + return "Gradle `Test` use JUnit Jupiter"; + } + + @Override + public String getDescription() { + return "By default Gradle's `Test` tasks use JUnit 4. " + + "Gradle `Test` tasks must be configured with `useJUnitPlatform()` to run JUnit Jupiter tests. " + + "This recipe adds the `useJUnitPlatform()` method call to the `Test` task configuration."; + } + + private static final String USE_JUNIT_PLATFORM_PATTERN = "org.gradle.api.tasks.testing.Test useJUnitPlatform()"; + private static final MethodMatcher USE_JUNIT_PLATFORM_MATCHER = new MethodMatcher(USE_JUNIT_PLATFORM_PATTERN); + private static final MethodMatcher USE_JUNIT4_MATCHER = new MethodMatcher("org.gradle.api.tasks.testing.Test useJUnit()"); + private static final MethodMatcher USE_JUNIT4_ALTERNATE_MATCHER = new MethodMatcher("RewriteTestSpec useJUnit()"); + private static final MethodMatcher TEST_DSL_MATCHER = new MethodMatcher("RewriteGradleProject test(..)"); + @Override + public TreeVisitor getVisitor() { + //noinspection NotNullFieldNotInitialized + return new GroovyIsoVisitor() { + + GradleProject gp; + + @Override + public G.CompilationUnit visitCompilationUnit(G.CompilationUnit compilationUnit, ExecutionContext ctx) { + //noinspection DataFlowIssue + gp = compilationUnit.getMarkers().findFirst(GradleProject.class).orElse(null); + if(gp == null) { + return compilationUnit; + } + if(gp.getPlugins().stream().noneMatch(plugin -> plugin.getFullyQualifiedClassName().contains("org.gradle.api.plugins.JavaBasePlugin"))) { + return compilationUnit; + } + if(containsJUnitPlatformInvocation(compilationUnit)) { + return compilationUnit; + } + // If anywhere in the tree there is a useJunit() we can swap it out for useJUnitPlatform() and be done in one step + G.CompilationUnit cu = (G.CompilationUnit) new UpdateExistingUseJunit4() + .visitNonNull(compilationUnit, ctx, requireNonNull(getCursor().getParent())); + if (cu != compilationUnit) { + return cu; + } + // No useJUnit(), but there might already be configuration of a Test task, add useJUnitPlatform() to it + cu = (G.CompilationUnit) new AddJUnitPlatformToExistingTestDsl() + .visitNonNull(cu, ctx, requireNonNull(getCursor().getParent())); + if(cu != compilationUnit) { + return cu; + } + // No existing test task configuration seems to exist, add a whole new one + return (G.CompilationUnit) new AddUseJUnitPlatform() + .visitNonNull(cu, ctx, getCursor().getParent()); + } + }; + } + + private static boolean containsJUnitPlatformInvocation(G.CompilationUnit cu) { + AtomicBoolean found = new AtomicBoolean(false); + new GroovyIsoVisitor() { + @Override + public @Nullable J preVisit(J tree, AtomicBoolean found) { + if(found.get()) { + stopAfterPreVisit(); + return tree; + } + return super.preVisit(tree, found); + } + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation m, AtomicBoolean found) { + // Groovy gradle scripts being weakly type-attributed means we will miss likely-correct changes if we are too strict + if ("useJUnitPlatform".equals(m.getSimpleName()) && (m.getArguments().isEmpty() || m.getArguments().size() == 1 && m.getArguments().get(0) instanceof J.Empty)) { + found.set(true); + return m; + } + return super.visitMethodInvocation(m, found); + } + }.visit(cu, found); + return found.get(); + } + + private static class UpdateExistingUseJunit4 extends GroovyIsoVisitor { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation m = super.visitMethodInvocation(method, ctx); + // Groovy gradle scripts being weakly type-attributed means we will miss changes if we are too strict + if ("useJUnit".equals(m.getSimpleName()) && (m.getArguments().isEmpty() || m.getArguments().size() == 1 && m.getArguments().get(0) instanceof J.Empty)) { + JavaType.Method useJUnitPlatformType = Optional.ofNullable(m.getMethodType()) + .map(JavaType.Method::getDeclaringType) + .flatMap(declaringType -> declaringType.getMethods() + .stream() + .filter(method1 -> method1.getName().equals("useJUnitPlatform")) + .findFirst()) + .orElse(null); + return m.withName(m.getName().withSimpleName("useJUnitPlatform")) + .withMethodType(useJUnitPlatformType); + } + return m; + } + } + + private static class AddUseJUnitPlatform extends GroovyIsoVisitor { + @Override + public G.CompilationUnit visitCompilationUnit(G.CompilationUnit cu, ExecutionContext ctx) { + G.CompilationUnit template = GradleParser.builder() + .build() + .parse("plugins {\n" + + " id 'java'\n" + + "}\n" + + "tasks.withType(Test).configureEach {\n" + + " useJUnitPlatform()\n" + + "}") + .map(G.CompilationUnit.class::cast) + .collect(Collectors.toList()) + .get(0); + J.MethodInvocation configureEachInvocation = (J.MethodInvocation) template.getStatements().get(1); + return cu.withStatements(ListUtils.concat(cu.getStatements(), configureEachInvocation)); + } + } + + private static class AddJUnitPlatformToExistingTestDsl extends GroovyIsoVisitor { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation m = super.visitMethodInvocation(method, ctx); + String mName = m.getSimpleName(); + // A non-exhaustive list of common ways by which the task may already be configured + // test { } + // tasks.withType(Test) { } + // tasks.withType(Test).configureEach { } + // tasks.named("test") { } + // tasks.named("test", Test) { } + switch (mName) { + case "test": + if (!(m.getArguments().size() == 1 && m.getArguments().get(0) instanceof J.Lambda)) { + return m; + } + // Other DSLs may be named "test" so only assume it is test {} if it isn't enclosed in anything else + if(getCursor().getParentTreeCursor().firstEnclosing(J.MethodInvocation.class) != null) { + return m; + } + break; + case "named": + if (m.getArguments().isEmpty()) { + return m; + } + if (!(m.getArguments().get(0) instanceof J.Literal && "test".equals(((J.Literal) m.getArguments().get(0)).getValue()))) { + return m; + } + // The final argument must be a J.Lambda + if (!(m.getArguments().get(m.getArguments().size() - 1) instanceof J.Lambda)) { + return m; + } + break; + case "withType": + if (m.getSelect() == null + || !TypeUtils.isOfClassType(m.getSelect().getType(), "org.gradle.api.tasks.TaskContainer") + || !(m.getArguments().get(0) instanceof J.Identifier && "Test".equals(((J.Identifier) m.getArguments().get(0)).getSimpleName()))) { + return m; + } + break; + case "configureEach": + if(m.getArguments().size() != 1 || !(m.getArguments().get(0) instanceof J.Lambda)) { + return m; + } + if(m.getSelect() == null || !(m.getSelect() instanceof J.MethodInvocation)) { + return m; + } + J.MethodInvocation select = (J.MethodInvocation) m.getSelect(); + if(!"withType".equals(select.getSimpleName()) + || select.getArguments().size() != 1 + || !(select.getArguments().get(0) instanceof J.Identifier) + || !"Test".equals(((J.Identifier) select.getArguments().get(0)).getSimpleName())) { + return m; + } + break; + default: + return m; + } + + return (J.MethodInvocation) new AddJUnitPlatformAsLastStatementInClosure() + .visitNonNull(m, ctx, requireNonNull(getCursor().getParent())); + } + } + + private static class AddJUnitPlatformAsLastStatementInClosure extends GroovyIsoVisitor { + @Override + public J.Lambda visitLambda(J.Lambda l, ExecutionContext ctx) { + if(!(l.getBody() instanceof J.Block)) { + return l; + } + G.CompilationUnit cu = GradleParser.builder() + .build() + .parse("plugins {\n" + + " id 'java'\n" + + "}\n" + + "tasks.withType(Test) {\n" + + " useJUnitPlatform()\n" + + "}") + .map(G.CompilationUnit.class::cast) + .collect(Collectors.toList()) + .get(0); + J.MethodInvocation useJUnitPlatform = Optional.of(cu.getStatements().get(1)) + .map(J.MethodInvocation.class::cast) + .map(J.MethodInvocation::getArguments) + .map(args -> args.get(1)) + .map(J.Lambda.class::cast) + .map(J.Lambda::getBody) + .map(J.Block.class::cast) + .map(J.Block::getStatements) + .map(statements -> statements.get(0)) + .map(J.Return.class::cast) + .map(J.Return::getExpression) + .map(J.MethodInvocation.class::cast) + .orElse(null); + if(useJUnitPlatform == null) { + return l; + } + J.Block b = (J.Block) l.getBody(); + l = l.withBody(b.withStatements(ListUtils.concat(b.getStatements(), useJUnitPlatform))); + return autoFormat(l, ctx, requireNonNull(getCursor().getParent())); + } + } +} diff --git a/src/main/resources/META-INF/rewrite/junit5.yml b/src/main/resources/META-INF/rewrite/junit5.yml index 99f3aa9a2..90cf92e59 100755 --- a/src/main/resources/META-INF/rewrite/junit5.yml +++ b/src/main/resources/META-INF/rewrite/junit5.yml @@ -113,12 +113,7 @@ recipeList: - org.openrewrite.maven.ExcludeDependency: groupId: org.junit.vintage artifactId: junit-vintage-engine - - org.openrewrite.java.dependencies.AddDependency: - groupId: org.junit.jupiter - artifactId: junit-jupiter - version: 5.x - onlyIfUsing: org.junit..* - scope: test + - org.openrewrite.java.testing.junit5.AddJupiterDependencies - org.openrewrite.java.dependencies.AddDependency: groupId: org.junit.jupiter artifactId: junit-jupiter-params @@ -146,6 +141,7 @@ recipeList: groupId: org.apache.maven.plugins artifactId: maven-failsafe-plugin newVersion: 3.1.x + - org.openrewrite.java.testing.junit5.GradleUseJunitJupiter --- type: specs.openrewrite.org/v1beta/recipe diff --git a/src/test/java/org/openrewrite/java/testing/junit5/GradleUseJunitJupiterTest.java b/src/test/java/org/openrewrite/java/testing/junit5/GradleUseJunitJupiterTest.java new file mode 100644 index 000000000..6fc646912 --- /dev/null +++ b/src/test/java/org/openrewrite/java/testing/junit5/GradleUseJunitJupiterTest.java @@ -0,0 +1,243 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * 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 + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * 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.junit5; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.gradle.Assertions.buildGradle; +import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; + +class GradleUseJunitJupiterTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new GradleUseJunitJupiter()) + .beforeRecipe(withToolingApi()); + } + + @DocumentExample + @Test + void addWhenMissing() { + rewriteRun( + //language=groovy + buildGradle( + """ + plugins { + id 'java-library' + } + tasks.named('classes') { } + tasks.withType(JavaCompile) { } + tasks.withType(JavaCompile).configureEach { } + """, + """ + plugins { + id 'java-library' + } + tasks.named('classes') { } + tasks.withType(JavaCompile) { } + tasks.withType(JavaCompile).configureEach { } + tasks.withType(Test).configureEach { + useJUnitPlatform() + } + """ + ) + ); + } + + @Test + void dsl() { + rewriteRun( + //language=groovy + buildGradle( + """ + plugins { + id 'java' + } + test { + } + """, + """ + plugins { + id 'java' + } + test { + useJUnitPlatform() + } + """ + ) + ); + } + + @Test + void dslAlreadyExists() { + rewriteRun( + //language=groovy + buildGradle( + """ + plugins { + id 'java' + } + test { + useJUnit() + } + """, + """ + plugins { + id 'java' + } + test { + useJUnitPlatform() + } + """ + ) + ); + } + + @Test + void tasksWithTypeTest() { + rewriteRun( + //language=groovy + buildGradle( + """ + plugins { + id 'java' + } + tasks.withType(Test) { + } + """, + """ + plugins { + id 'java' + } + tasks.withType(Test) { + useJUnitPlatform() + } + """ + ) + ); + } + + @Test + void tasksWithTypeTestConfigureEach() { + rewriteRun( + //language=groovy + buildGradle( + """ + plugins { + id 'java' + } + tasks.withType(Test).configureEach { + } + """, + """ + plugins { + id 'java' + } + tasks.withType(Test).configureEach { + useJUnitPlatform() + } + """ + ) + ); + } + + @Test + void tasksNamedTest() { + rewriteRun( + //language=groovy + buildGradle( + """ + plugins { + id 'java' + } + tasks.named('test', Test) { + } + """, + """ + plugins { + id 'java' + } + tasks.named('test', Test) { + useJUnitPlatform() + } + """ + ) + ); + } + + @Test + void tasksNamedTestNoType() { + rewriteRun( + //language=groovy + buildGradle( + """ + plugins { + id 'java' + } + tasks.named('test') { + } + """, + """ + plugins { + id 'java' + } + tasks.named('test') { + useJUnitPlatform() + } + """ + ) + ); + } + + @Test + void leaveOtherTestDslAlone() { + rewriteRun( + //language=groovy + buildGradle( + """ + plugins { + id 'java' + } + sourceSets { + test { + java { + srcDir 'src/test/java' + } + } + } + """, + """ + plugins { + id 'java' + } + sourceSets { + test { + java { + srcDir 'src/test/java' + } + } + } + tasks.withType(Test).configureEach { + useJUnitPlatform() + } + """ + ) + ); + } +} diff --git a/src/test/java/org/openrewrite/java/testing/junit5/JUnit5MigrationTest.java b/src/test/java/org/openrewrite/java/testing/junit5/JUnit5MigrationTest.java index 030a1004e..022b61840 100644 --- a/src/test/java/org/openrewrite/java/testing/junit5/JUnit5MigrationTest.java +++ b/src/test/java/org/openrewrite/java/testing/junit5/JUnit5MigrationTest.java @@ -23,9 +23,17 @@ import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import java.util.List; +import java.util.regex.MatchResult; +import java.util.regex.Pattern; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.openrewrite.gradle.Assertions.buildGradle; +import static org.openrewrite.gradle.toolingapi.Assertions.withToolingApi; import static org.openrewrite.java.Assertions.java; import static org.openrewrite.maven.Assertions.pomXml; +@SuppressWarnings({"NewClassNamingConvention", "EqualsWithItself", "deprecation", "LanguageMismatch"}) class JUnit5MigrationTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { @@ -46,7 +54,7 @@ void classReference() { java( """ import org.junit.Test; - + public class Sample { void method() { Class c = Test.class; @@ -55,7 +63,7 @@ void method() { """, """ import org.junit.jupiter.api.Test; - + public class Sample { void method() { Class c = Test.class; @@ -75,10 +83,10 @@ void assertThatReceiver() { """ import org.junit.Assert; import org.junit.Test; - + import static java.util.Arrays.asList; import static org.hamcrest.Matchers.containsInAnyOrder; - + public class SampleTest { @SuppressWarnings("ALL") @Test @@ -90,11 +98,11 @@ public void filterShouldRemoveUnusedConfig() { """, """ import org.junit.jupiter.api.Test; - + import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; - + public class SampleTest { @SuppressWarnings("ALL") @Test @@ -112,8 +120,8 @@ public void filterShouldRemoveUnusedConfig() { @Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/279") void upgradeMavenPluginVersions() { rewriteRun( - //language=xml pomXml( + //language=xml """ 4.0.0 @@ -136,28 +144,15 @@ void upgradeMavenPluginVersions() { """, - """ - - 4.0.0 - com.example.jackson - test-plugins - 1.0.0 - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.2 - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.22.2 - - - - - """ + spec -> spec.after(actual -> { + List list = Pattern.compile("(.*)") + .matcher(actual).results().skip(1).toList(); + assertThat(list) + .hasSize(2) + .extracting(mr -> mr.group(1)) + .allMatch(m -> m.startsWith("3.")); + return actual; + }) ) ); } @@ -239,7 +234,7 @@ void assertEqualsWithArrayArgumentToAssertArrayEquals() { java( """ import org.junit.Assert; - + class MyTest { void test() { Assert.assertEquals(new Object[1], new Object[1]); @@ -248,7 +243,7 @@ void test() { """, """ import org.junit.jupiter.api.Assertions; - + class MyTest { void test() { Assertions.assertArrayEquals(new Object[1], new Object[1]); @@ -269,16 +264,16 @@ void migrateInheritedTestBeforeAfterAnnotations() { import org.junit.After; import org.junit.Before; import org.junit.Test; - + public class AbstractTest { @Before public void before() { } - + @After public void after() { } - + @Test public void test() { } @@ -288,16 +283,16 @@ public void test() { import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - + public class AbstractTest { @BeforeEach public void before() { } - + @AfterEach public void after() { } - + @Test public void test() { } @@ -309,10 +304,10 @@ public void test() { public class A extends AbstractTest { public void before() { } - + public void after() { } - + public void test() { } } @@ -326,11 +321,11 @@ public class A extends AbstractTest { @BeforeEach public void before() { } - + @AfterEach public void after() { } - + @Test public void test() { } @@ -340,4 +335,44 @@ public void test() { ); } + @Test + void noJunitDependencyIfApiAlreadyPresent() { + rewriteRun( + spec -> spec.beforeRecipe(withToolingApi()), + //language=groovy + buildGradle( + """ + plugins { + id 'java-library' + } + repositories { + mavenCentral() + } + dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2' + } + tasks.withType(Test).configureEach { + useJUnitPlatform() + } + """), + //language=xml + pomXml( + """ + + 4.0.0 + dev.ted + testcontainer-migrate + 0.0.1 + + + org.junit.jupiter + junit-jupiter-api + 5.7.2 + test + + + + """) + ); + } }