Skip to content

Commit 6e33f5b

Browse files
authored
Add JUnit 6.1 migration recipes (#1002)
* Add JUnit 6.1 migration recipes - Migrate `org.junit.jupiter.engine.Constants` to `org.junit.jupiter.api.Constants` - Rename `Executions.started()` to `finished()` for `EngineTestKit` - New `MigrateJUnitPioneerToJupiter` recipe for `@DefaultLocale`, `@DefaultTimeZone`, and the system property annotations now native in Jupiter 6.1 - New `RemoveJreOther` recipe that strips deprecated `JRE.OTHER` from `@EnabledOnJre`/`@DisabledOnJre` array values * Update recipes.csv for new JUnit 6.1 recipes * Use Lombok getters and simplify ListUtils check in RemoveJreOther * Remove redundant null/size guard in RemoveJreOther * Remove redundant reference check in stripOtherFromArray
1 parent da4dd54 commit 6e33f5b

10 files changed

Lines changed: 601 additions & 157423 deletions

File tree

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ recipeDependencies {
2929
parserClasspath("org.junit.jupiter:junit-jupiter-params:5.+")
3030
parserClasspath("org.junit.jupiter:junit-jupiter-params:6.+")
3131
parserClasspath("org.junit.jupiter:junit-jupiter-migrationsupport:5.+")
32+
parserClasspath("org.junit-pioneer:junit-pioneer:2.+")
3233
parserClasspath("org.mockito:mockito-all:1.10.19")
3334
parserClasspath("org.mockito:mockito-core:3.+")
3435
parserClasspath("org.mockito:mockito-core:5.+")
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2026 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.testing.junit6;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Getter;
20+
import lombok.Value;
21+
import org.openrewrite.ExecutionContext;
22+
import org.openrewrite.Preconditions;
23+
import org.openrewrite.Recipe;
24+
import org.openrewrite.TreeVisitor;
25+
import org.openrewrite.internal.ListUtils;
26+
import org.openrewrite.java.AnnotationMatcher;
27+
import org.openrewrite.java.JavaIsoVisitor;
28+
import org.openrewrite.java.search.UsesType;
29+
import org.openrewrite.java.tree.Expression;
30+
import org.openrewrite.java.tree.J;
31+
import org.openrewrite.java.tree.TypeUtils;
32+
33+
import java.util.Arrays;
34+
import java.util.List;
35+
36+
@Value
37+
@EqualsAndHashCode(callSuper = false)
38+
public class RemoveJreOther extends Recipe {
39+
40+
private static final String JRE_TYPE = "org.junit.jupiter.api.condition.JRE";
41+
42+
private static final List<AnnotationMatcher> JRE_ANNOTATIONS = Arrays.asList(
43+
new AnnotationMatcher("@org.junit.jupiter.api.condition.EnabledOnJre"),
44+
new AnnotationMatcher("@org.junit.jupiter.api.condition.DisabledOnJre"));
45+
46+
@Getter
47+
final String displayName = "Remove deprecated `JRE.OTHER` from `@EnabledOnJre`/`@DisabledOnJre` arrays";
48+
49+
@Getter
50+
final String description = "JUnit 6.1 deprecated `JRE.OTHER` in favor of `int`/`int[]` annotation attributes. " +
51+
"This recipe removes `JRE.OTHER` entries from `@EnabledOnJre` and `@DisabledOnJre` array " +
52+
"values when other JRE constants remain. Lone `JRE.OTHER` usages are left untouched " +
53+
"because they have no mechanical replacement; review them manually.";
54+
55+
@Override
56+
public TreeVisitor<?, ExecutionContext> getVisitor() {
57+
return Preconditions.check(new UsesType<>(JRE_TYPE, false), new JavaIsoVisitor<ExecutionContext>() {
58+
@Override
59+
public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ctx) {
60+
J.Annotation a = super.visitAnnotation(annotation, ctx);
61+
if (JRE_ANNOTATIONS.stream().noneMatch(m -> m.matches(a)) || a.getArguments() == null) {
62+
return a;
63+
}
64+
return a.withArguments(ListUtils.map(a.getArguments(), arg -> {
65+
if (arg instanceof J.Assignment) {
66+
J.Assignment assignment = (J.Assignment) arg;
67+
if (assignment.getVariable() instanceof J.Identifier &&
68+
"value".equals(((J.Identifier) assignment.getVariable()).getSimpleName())) {
69+
Expression stripped = stripOtherFromArray(assignment.getAssignment());
70+
return stripped == assignment.getAssignment() ? assignment : assignment.withAssignment(stripped);
71+
}
72+
return assignment;
73+
}
74+
return stripOtherFromArray(arg);
75+
}));
76+
}
77+
78+
private Expression stripOtherFromArray(Expression expr) {
79+
if (!(expr instanceof J.NewArray)) {
80+
return expr;
81+
}
82+
J.NewArray array = (J.NewArray) expr;
83+
return array.withInitializer(ListUtils.map(array.getInitializer(), el -> isJreOther(el) ? null : el));
84+
}
85+
86+
private boolean isJreOther(Expression expr) {
87+
if (expr instanceof J.FieldAccess) {
88+
J.FieldAccess fa = (J.FieldAccess) expr;
89+
return "OTHER".equals(fa.getSimpleName()) &&
90+
TypeUtils.isOfClassType(fa.getTarget().getType(), JRE_TYPE);
91+
}
92+
if (expr instanceof J.Identifier) {
93+
J.Identifier id = (J.Identifier) expr;
94+
return "OTHER".equals(id.getSimpleName()) &&
95+
TypeUtils.isOfClassType(id.getType(), JRE_TYPE);
96+
}
97+
return false;
98+
}
99+
});
100+
}
101+
}

0 commit comments

Comments
 (0)