Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dependencies in JVMTestSuite dependency block #5000

Merged
merged 32 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d08d561
add test to reproduce behavior of adding a dependency inside a nested…
MBoegers Feb 7, 2025
121fc70
Merge branch 'main' into fix/792-adddependency-nested
MBoegers Feb 7, 2025
a764edb
simplify tests
MBoegers Feb 7, 2025
a1604f9
add test to reproduce behavior of adding a dependency inside a nested…
MBoegers Feb 7, 2025
cf4c8b5
simplify tests
MBoegers Feb 7, 2025
ac5174d
add `org.junit.vintage:junit-vintage-engine` to rewrite-test to fix I…
MBoegers Feb 10, 2025
106b9e9
Merge remote-tracking branch 'origin/fix/792-adddependency-nested' in…
MBoegers Feb 10, 2025
27b0555
rename test to addToJVMTestSuite
MBoegers Feb 10, 2025
38f585e
fix tests to fail and revert movement
MBoegers Feb 10, 2025
d9d6b29
First version adding dependencies
MBoegers Feb 11, 2025
1bf4ac8
add support for suites without dependencies
MBoegers Feb 11, 2025
3217aa9
Merge branch 'main' into fix/792-adddependency-nested
timtebeek Feb 11, 2025
6f5b7a8
Apply typo fixes from review
MBoegers Feb 12, 2025
e619404
Merge branch 'main' into fix/792-adddependency-nested
MBoegers Feb 12, 2025
9ac2869
Merge branch 'main' into fix/792-adddependency-nested
timtebeek Feb 13, 2025
82105a8
Update rewrite-gradle/src/main/java/org/openrewrite/gradle/AddDepende…
timtebeek Feb 13, 2025
a1731f4
Merge branch 'main' into fix/792-adddependency-nested
MBoegers Feb 14, 2025
e673d4c
align tests with review comments
MBoegers Feb 14, 2025
10d54e7
add disables tests to document open issues with the `AddDependency` r…
MBoegers Feb 14, 2025
ce1f12b
Merge branch 'main' into fix/792-adddependency-nested
MBoegers Feb 17, 2025
c6d80d5
update tests to meet recipe configuration expectations
MBoegers Feb 17, 2025
074fb91
move JVM plugin configuration resolution to search recipe
MBoegers Feb 17, 2025
4b36d78
Use `FindJMVTestSuites` in `AddDependency` and handle adding of depen…
MBoegers Feb 17, 2025
a3438b6
add missing license information
MBoegers Feb 17, 2025
192c32c
Apply code suggestions
timtebeek Feb 17, 2025
ae34bf3
Light polish on FindJMVTestSuites
timtebeek Feb 17, 2025
b70239a
Light polish on AddDependency
timtebeek Feb 17, 2025
586b87d
Fix typo in recipe name
timtebeek Feb 17, 2025
f4ecc97
Fix data table row insertion issue
timtebeek Feb 17, 2025
55e0b74
Merge branch 'main' into fix/792-adddependency-nested
timtebeek Feb 17, 2025
4e6b153
Merge branch 'main' into fix/792-adddependency-nested
MBoegers Feb 18, 2025
7a03f76
use `StringUtils#isBlank`
MBoegers Feb 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 111 additions & 11 deletions rewrite-gradle/src/main/java/org/openrewrite/gradle/AddDependency.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@
import org.openrewrite.java.marker.JavaProject;
import org.openrewrite.java.marker.JavaSourceSet;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaSourceFile;
import org.openrewrite.java.tree.Statement;
import org.openrewrite.maven.table.MavenMetadataFailures;
import org.openrewrite.semver.Semver;

import java.util.*;
import java.util.function.Predicate;

import static java.util.Collections.*;
import static java.util.Collections.singletonList;
import static java.util.Objects.requireNonNull;

@Value
Expand All @@ -56,25 +59,25 @@ public class AddDependency extends ScanningRecipe<AddDependency.Scanned> {

@Option(displayName = "Version",
description = "An exact version number or node-style semver selector used to select the version number. " +
"You can also use `latest.release` for the latest available version and `latest.patch` if " +
"the current version is a valid semantic version. For more details, you can look at the documentation " +
"page of [version selectors](https://docs.openrewrite.org/reference/dependency-version-selectors).",
"You can also use `latest.release` for the latest available version and `latest.patch` if " +
"the current version is a valid semantic version. For more details, you can look at the documentation " +
"page of [version selectors](https://docs.openrewrite.org/reference/dependency-version-selectors).",
example = "29.X",
required = false)
@Nullable
String version;

@Option(displayName = "Version pattern",
description = "Allows version selection to be extended beyond the original Node Semver semantics. So for example, " +
"Setting 'version' to \"25-29\" can be paired with a metadata pattern of \"-jre\" to select Guava 29.0-jre",
"Setting 'version' to \"25-29\" can be paired with a metadata pattern of \"-jre\" to select Guava 29.0-jre",
example = "-jre",
required = false)
@Nullable
String versionPattern;

@Option(displayName = "Configuration",
description = "A configuration to use when it is not what can be inferred from usage. Most of the time this will be left empty, but " +
"is used when adding a new as of yet unused dependency.",
"is used when adding a new as of yet unused dependency.",
example = "implementation",
required = false)
@Nullable
Expand Down Expand Up @@ -103,7 +106,7 @@ public class AddDependency extends ScanningRecipe<AddDependency.Scanned> {

@Option(displayName = "Family pattern",
description = "A pattern, applied to groupIds, used to determine which other dependencies should have aligned version numbers. " +
"Accepts '*' as a wildcard character.",
"Accepts '*' as a wildcard character.",
example = "com.fasterxml.jackson*",
required = false)
@Nullable
Expand Down Expand Up @@ -143,6 +146,7 @@ public Validated<Object> validate() {
public static class Scanned {
boolean usingType;
Map<JavaProject, Set<String>> configurationsByProject = new HashMap<>();
Map<JavaProject, Set<String>> customJvmTestSuitesWithDependencies = new HashMap<>();
}

@Override
Expand All @@ -156,16 +160,75 @@ public TreeVisitor<?, ExecutionContext> getScanner(Scanned acc) {

@Nullable
UsesType<ExecutionContext> usesType = null;

private boolean usesType(SourceFile sourceFile, ExecutionContext ctx) {
if(onlyIfUsing == null) {
if (onlyIfUsing == null) {
return true;
}
if(usesType == null) {
if (usesType == null) {
usesType = new UsesType<>(onlyIfUsing, true);
}
return usesType.isAcceptable(sourceFile, ctx) && usesType.visit(sourceFile, ctx) != sourceFile;
}

private Set<String> jvmTestSuites(SourceFile sourceFile, ExecutionContext ctx) {
HashSet<String> customJVMSuites = new HashSet<>();
new GroovyIsoVisitor<ExecutionContext>() {

private boolean isJVMTestSuitesBlock(J.MethodInvocation methodInvocation) {
if ("suites".equals(methodInvocation.getSimpleName())) {
if (getCursor().getParent() != null) {
J.MethodInvocation parentBlock = getCursor().getParent().firstEnclosing(J.MethodInvocation.class);
return parentBlock != null && "testing".equals(parentBlock.getSimpleName());
}
}
return false;
}

boolean definesDependencies(J.MethodInvocation suite) {
for (Expression suiteDefinition : suite.getArguments()) {
if (suiteDefinition instanceof J.Lambda) {
for (Statement statement : ((J.Block) ((J.Lambda) suiteDefinition).getBody()).getStatements()) {
if (statement instanceof J.Return) {
Expression expression = ((J.Return) statement).getExpression();
if (expression instanceof J.MethodInvocation) {
if ("dependencies".equals(((J.MethodInvocation) expression).getSimpleName())) {
return true;
}
}
}
}
}
}
return false;
}

@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
if (isJVMTestSuitesBlock(method)) {
for (Expression suiteDefinition : method.getArguments()) {
if (suiteDefinition instanceof J.Lambda) {
for (Statement statement : ((J.Block) ((J.Lambda) suiteDefinition).getBody()).getStatements()) {
if (statement instanceof J.Return) {
Expression expression = ((J.Return) statement).getExpression();
if (expression instanceof J.MethodInvocation) {
J.MethodInvocation suite = (J.MethodInvocation) expression;
if (definesDependencies(suite)) {
customJVMSuites.add(suite.getSimpleName());
}
}
}
}
}
}
return method;
}
return super.visitMethodInvocation(method, ctx);
}
}.visit(sourceFile, ctx);
return customJVMSuites;
}

@Override
public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
if (!(tree instanceof SourceFile)) {
Expand All @@ -176,6 +239,9 @@ private boolean usesType(SourceFile sourceFile, ExecutionContext ctx) {
if (usesType(sourceFile, ctx)) {
acc.usingType = true;
}

acc.customJvmTestSuitesWithDependencies.computeIfAbsent(javaProject, k -> jvmTestSuites(sourceFile, ctx));

Set<String> configurations = acc.configurationsByProject.computeIfAbsent(javaProject, ignored -> new HashSet<>());
sourceFile.getMarkers().findFirst(JavaSourceSet.class).ifPresent(sourceSet ->
configurations.add("main".equals(sourceSet.getName()) ? "implementation" : sourceSet.getName() + "Implementation"));
Expand Down Expand Up @@ -248,8 +314,13 @@ public TreeVisitor<?, ExecutionContext> getVisitor(Scanned acc) {

G.CompilationUnit g = (G.CompilationUnit) s;
for (String resolvedConfiguration : resolvedConfigurations) {
g = (G.CompilationUnit) new AddDependencyVisitor(groupId, artifactId, version, versionPattern, resolvedConfiguration,
classifier, extension, metadataFailures, this::isTopLevel).visitNonNull(g, ctx);
if (!targetsCustomJVMTestSuite(resolvedConfiguration, acc.customJvmTestSuitesWithDependencies.get(jp))) {
g = (G.CompilationUnit) new AddDependencyVisitor(groupId, artifactId, version, versionPattern, resolvedConfiguration,
classifier, extension, metadataFailures, this::isTopLevel).visitNonNull(g, ctx);
} else {
g = (G.CompilationUnit) new AddDependencyVisitor(groupId, artifactId, version, versionPattern, "implementation",
classifier, extension, metadataFailures, isMatchingJVMTestSuite(resolvedConfiguration)).visitNonNull(g, ctx);
}
}

return g;
Expand All @@ -258,7 +329,36 @@ public TreeVisitor<?, ExecutionContext> getVisitor(Scanned acc) {
private boolean isTopLevel(Cursor cursor) {
return cursor.getParentOrThrow().firstEnclosing(J.MethodInvocation.class) == null;
}

private Predicate<Cursor> isMatchingJVMTestSuite(String resolvedConfiguration) {
return cursor -> {
String sourceSet = resolvedConfiguration.substring(0, resolvedConfiguration.length() - 14);
J.MethodInvocation methodInvocation = cursor.getParentOrThrow().firstEnclosing(J.MethodInvocation.class);
return methodInvocation != null && sourceSet.equals(methodInvocation.getSimpleName());
};
}

private final Set<String> gradleStandardConfigurations = new HashSet<>(Arrays.asList(
"api",
"implementation",
"compileOnly",
"compileOnlyApi",
"runtimeOnly",
"testImplementation",
"testCompileOnly",
"testRuntimeOnly"));

boolean targetsCustomJVMTestSuite(String configuration, Set<String> customJvmTestSuites) {
if (gradleStandardConfigurations.contains(configuration) || "default".equals(configuration)) {
return false;
}

String sourceSet = configuration.substring(0, configuration.length() - 14);
return customJvmTestSuites.contains(sourceSet);
}
})
);
}


}
Loading
Loading