Skip to content

Commit 45d26fd

Browse files
authored
Replace deprecated EnvironmentTestUtils with TestPropertyValues (#52)
1 parent cd9459a commit 45d26fd

File tree

4 files changed

+587
-0
lines changed

4 files changed

+587
-0
lines changed
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (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://www.apache.org/licenses/LICENSE-2.0
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.spring.boot2;
17+
18+
import org.openrewrite.ExecutionContext;
19+
import org.openrewrite.Parser;
20+
import org.openrewrite.Recipe;
21+
import org.openrewrite.TreeVisitor;
22+
import org.openrewrite.internal.lang.Nullable;
23+
import org.openrewrite.java.JavaIsoVisitor;
24+
import org.openrewrite.java.JavaParser;
25+
import org.openrewrite.java.MethodMatcher;
26+
import org.openrewrite.java.search.SemanticallyEqual;
27+
import org.openrewrite.java.tree.Expression;
28+
import org.openrewrite.java.tree.J;
29+
import org.openrewrite.java.tree.Statement;
30+
import org.openrewrite.marker.SearchResult;
31+
32+
import java.util.ArrayList;
33+
import java.util.Collections;
34+
import java.util.List;
35+
import java.util.Optional;
36+
37+
public class ReplaceDeprecatedEnvironmentTestUtils extends Recipe {
38+
39+
public static final String ENV_UTILS_ADD_ENV_FQN = "org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment";
40+
public static final String TEST_PROPERTY_VALUES_FQN = "org.springframework.boot.test.util.TestPropertyValues";
41+
public static final MethodMatcher METHOD_MATCHER = new MethodMatcher("org.springframework.boot.test.util.EnvironmentTestUtils addEnvironment(..)");
42+
43+
@Override
44+
public String getDisplayName() {
45+
return "Replace EnvironmentUtils with TestPropertyValues";
46+
}
47+
48+
@Override
49+
public String getDescription() {
50+
return "Replaces any references to the deprecated org.springframework.boot.test.util.EnvironmentTestUtils" +
51+
" with org.springframework.boot.test.util.TestPropertyValues and the appropriate functionality";
52+
}
53+
54+
@Override
55+
protected TreeVisitor<?, ExecutionContext> getVisitor() {
56+
return new FindEnvironmentTestUtilsVisitor();
57+
}
58+
59+
private static class ReplaceEnvironmentUtilsMarker implements SearchResult {
60+
private final String templateString;
61+
private final List<Expression> parameters;
62+
63+
private ReplaceEnvironmentUtilsMarker(String templateString, List<Expression> parameters) {
64+
this.templateString = templateString;
65+
this.parameters = parameters;
66+
}
67+
68+
69+
@Override
70+
public @Nullable String getDescription() {
71+
return templateString;
72+
}
73+
}
74+
75+
private static class FindEnvironmentTestUtilsVisitor extends JavaIsoVisitor<ExecutionContext> {
76+
77+
@Override
78+
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext executionContext) {
79+
J.MethodDeclaration m = super.visitMethodDeclaration(method, executionContext);
80+
81+
if (m.getBody() == null || m.getBody().getStatements().size() == 0) {
82+
return m;
83+
}
84+
85+
List<Statement> statements = m.getBody().getStatements();
86+
List<Statement> newStatements = new ArrayList<>();
87+
List<J.MethodInvocation> collectedEnvironmentMethods = new ArrayList<>();
88+
boolean requiresRemoval = false;
89+
90+
for (Statement statement : statements) {
91+
if (statement instanceof J.MethodInvocation && isAddEnvironmentMethod((J.MethodInvocation) statement)) {
92+
J.MethodInvocation methodInvocation = (J.MethodInvocation) statement;
93+
if (collectedEnvironmentMethods.isEmpty() || isCollectedContextOrEnvironment(collectedEnvironmentMethods, methodInvocation)) {
94+
collectedEnvironmentMethods.add(methodInvocation);
95+
requiresRemoval = true;
96+
} else {
97+
newStatements.add(coalesceToFluentMethod(collectedEnvironmentMethods));
98+
collectedEnvironmentMethods = new ArrayList<>();
99+
collectedEnvironmentMethods.add(methodInvocation);
100+
}
101+
} else {
102+
if (!collectedEnvironmentMethods.isEmpty()) {
103+
newStatements.add(coalesceToFluentMethod(collectedEnvironmentMethods));
104+
collectedEnvironmentMethods = new ArrayList<>();
105+
}
106+
newStatements.add(statement);
107+
}
108+
}
109+
110+
if (!collectedEnvironmentMethods.isEmpty()) {
111+
newStatements.add(coalesceToFluentMethod(collectedEnvironmentMethods));
112+
}
113+
114+
if (requiresRemoval) {
115+
doAfterVisit(new ReplaceDeprecatedEnvironmentTestUtils.RemoveEnvironmentTestUtilsVisitor());
116+
}
117+
118+
return m.withBody(m.getBody().withStatements(newStatements));
119+
}
120+
121+
private boolean isCollectedContextOrEnvironment(List<J.MethodInvocation> collectedMethods, J.MethodInvocation methodInvocation) {
122+
if (methodInvocation.getArguments().size() == 0
123+
|| collectedMethods.size() == 0
124+
|| collectedMethods.get(0).getArguments().size() == 0) {
125+
return false;
126+
}
127+
J.MethodInvocation collectedMethod = collectedMethods.get(0);
128+
Expression contextOrEnvironmentToCheck = getContextOrEnvironmentArgument(methodInvocation);
129+
Expression collectedContextOrEnvironment = getContextOrEnvironmentArgument(collectedMethod);
130+
131+
return SemanticallyEqual.areEqual(contextOrEnvironmentToCheck, collectedContextOrEnvironment);
132+
}
133+
134+
private Expression getPairArgument(J.MethodInvocation methodInvocation) {
135+
if (methodInvocation.getArguments().size() < 2) {
136+
throw new IllegalArgumentException("getPairArgument requires a method with at least 2 arguments");
137+
}
138+
// for one variant of addEnvironment there are 3 arguments, the others have 2
139+
return methodInvocation.getArguments().get(methodInvocation.getArguments().size() == 3 ? 2 : 1);
140+
}
141+
142+
private Expression getContextOrEnvironmentArgument(J.MethodInvocation methodInvocation) {
143+
if (methodInvocation.getArguments().size() < 2) {
144+
throw new IllegalArgumentException("getContextOrEnvironmentArgument requires a method with at least 2 arguments");
145+
}
146+
// for one variant of addEnvironment there are 3 arguments, the others have 2
147+
return methodInvocation.getArguments().get(methodInvocation.getArguments().size() == 3 ? 1 : 0);
148+
}
149+
150+
private J.MethodInvocation coalesceToFluentMethod(List<J.MethodInvocation> collectedMethods) {
151+
if (collectedMethods.size() == 0) {
152+
throw new IllegalArgumentException("collectedMethods must have at least one element");
153+
}
154+
J.MethodInvocation toReplace = collectedMethods.get(0);
155+
156+
String currentTemplateString = generateTemplateString(collectedMethods);
157+
List<Expression> parameters = generateParameters(collectedMethods);
158+
159+
return toReplace.withMarker(new ReplaceEnvironmentUtilsMarker(currentTemplateString, parameters));
160+
}
161+
162+
private List<Expression> generateParameters(List<J.MethodInvocation> collectedMethods) {
163+
if (collectedMethods.size() == 0) {
164+
throw new IllegalArgumentException("collectedMethods must have at least one element");
165+
}
166+
List<Expression> parameters = new ArrayList<>();
167+
for (J.MethodInvocation collectedMethod : collectedMethods) {
168+
parameters.add(getPairArgument(collectedMethod));
169+
}
170+
parameters.add(getContextOrEnvironmentArgument(collectedMethods.get(0)));
171+
172+
return parameters;
173+
}
174+
175+
private String generateTemplateString(List<J.MethodInvocation> collectedMethods) {
176+
StringBuilder template = new StringBuilder("TestPropertyValues.of(#{})");
177+
for (int i = 1; i < collectedMethods.size(); i++) {
178+
template.append(".and(#{})");
179+
}
180+
template.append(".applyTo(#{})");
181+
return template.toString();
182+
}
183+
184+
private boolean isAddEnvironmentMethod(J.MethodInvocation method) {
185+
return METHOD_MATCHER.matches(method);
186+
}
187+
}
188+
189+
private static class RemoveEnvironmentTestUtilsVisitor extends JavaIsoVisitor<ExecutionContext> {
190+
@Override
191+
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) {
192+
J.MethodInvocation m = super.visitMethodInvocation(method, executionContext);
193+
Optional<ReplaceEnvironmentUtilsMarker> maybeMarker = m.getMarkers().findFirst(ReplaceEnvironmentUtilsMarker.class);
194+
if (maybeMarker.isPresent()) {
195+
ReplaceEnvironmentUtilsMarker marker = maybeMarker.get();
196+
m = m.withTemplate(
197+
template(marker.templateString)
198+
.javaParser(
199+
JavaParser.fromJavaVersion()
200+
.logCompilationWarningsAndErrors(true)
201+
.dependsOn(Collections.singletonList(Parser.Input.fromResource("/TestPropertyValues.java")))
202+
.build()
203+
)
204+
.imports(TEST_PROPERTY_VALUES_FQN)
205+
.build(),
206+
m.getCoordinates().replace(),
207+
marker.parameters.toArray()
208+
);
209+
210+
maybeRemoveImport(ENV_UTILS_ADD_ENV_FQN);
211+
maybeAddImport(TEST_PROPERTY_VALUES_FQN);
212+
}
213+
return m;
214+
}
215+
}
216+
}

src/main/resources/META-INF/rewrite/spring-boot2.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ recipeList:
9292
- org.openrewrite.java.spring.boot2.SpringRunnerToSpringExtension
9393
- org.openrewrite.java.spring.boot2.ConditionalOnBeanAnyNestedCondition
9494
- org.openrewrite.java.spring.boot2.RestTemplateBuilderRequestFactory
95+
- org.openrewrite.java.spring.boot2.ReplaceDeprecatedEnvironmentTestUtils
9596
- org.openrewrite.java.spring.boot2.config.SpringBootConfigurationYaml.2_0
9697
- org.openrewrite.java.spring.boot2.config.SpringBootConfigurationProperties.2_0
9798
- org.openrewrite.java.spring.boot2.config.SpringBootConfigurationYaml.2_1
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (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://www.apache.org/licenses/LICENSE-2.0
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+
17+
package org.springframework.boot.test.util;
18+
19+
import org.springframework.context.ConfigurableApplicationContext;
20+
import org.springframework.core.env.ConfigurableEnvironment;
21+
import org.springframework.core.env.MutablePropertySources;
22+
23+
import java.util.concurrent.Callable;
24+
import java.util.stream.Stream;
25+
26+
public final class TestPropertyValues {
27+
public TestPropertyValues and(String... pairs) { return this; }
28+
29+
private TestPropertyValues and(Stream<Pair> pairs) { return this; }
30+
31+
public void applyTo(ConfigurableApplicationContext context) {}
32+
33+
public void applyTo(ConfigurableEnvironment environment) {}
34+
35+
public void applyTo(ConfigurableEnvironment environment, TestPropertyValues.Type type) {}
36+
37+
public void applyTo(ConfigurableEnvironment environment, TestPropertyValues.Type type, String name) {}
38+
39+
public <T> T applyToSystemProperties(Callable<T> call) { return null; }
40+
41+
private <E extends Throwable> void rethrow(Throwable e) throws E {}
42+
43+
private void addToSources(MutablePropertySources sources, TestPropertyValues.Type type, String name) {}
44+
45+
public static TestPropertyValues of(String... pairs) { return null; }
46+
47+
public static TestPropertyValues of(Iterable<String> pairs) { return null; }
48+
49+
public static TestPropertyValues of(Stream<String> pairs) { return null; }
50+
51+
public static TestPropertyValues empty() { return null; }
52+
53+
public static class Pair {}
54+
55+
public static enum Type {SYSTEM_ENVIRONMENT, MAP}
56+
}

0 commit comments

Comments
 (0)