diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c9b0cf2..d0f106a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -75,7 +75,7 @@ jobs:
--server-id-deploy repo.spring.io \
--repo-deploy-releases release \
--repo-deploy-snapshots snapshot \
- --exclude-patterns="*-example*.*,*-test*.*,*-docs-*.*"
+ --exclude-patterns="*-example*.*,*-test-*.*,*-docs-*.*"
echo JFROG_CLI_BUILD_NAME=spring-rewrite-commons-main >> $GITHUB_ENV
echo JFROG_CLI_BUILD_NUMBER=$GITHUB_RUN_NUMBER >> $GITHUB_ENV
diff --git a/spring-rewrite-commons-functional-tests/dependency-resolution-tests/pom.xml b/spring-rewrite-commons-functional-tests/dependency-resolution-tests/pom.xml
new file mode 100644
index 0000000..d5feeea
--- /dev/null
+++ b/spring-rewrite-commons-functional-tests/dependency-resolution-tests/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ org.springframework.rewrite
+ spring-rewrite-commons-functional-tests
+ 0.1.0-SNAPSHOT
+ ../pom.xml
+
+
+ dependency-resolution-tests
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ org.springframework.rewrite
+ spring-rewrite-commons-launcher
+ ${project.version}
+
+
+ org.openrewrite
+ rewrite-java
+ 8.12.0
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.rewrite
+ spring-rewrite-commons-launcher
+ test
+ test-jar
+ 0.1.0-SNAPSHOT
+ test
+
+
+
+
\ No newline at end of file
diff --git a/spring-rewrite-commons-functional-tests/dependency-resolution-tests/src/test/java/org/springframework/rewrite/MavenTypeResolutionTest.java b/spring-rewrite-commons-functional-tests/dependency-resolution-tests/src/test/java/org/springframework/rewrite/MavenTypeResolutionTest.java
new file mode 100644
index 0000000..06897b4
--- /dev/null
+++ b/spring-rewrite-commons-functional-tests/dependency-resolution-tests/src/test/java/org/springframework/rewrite/MavenTypeResolutionTest.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2021 - 2023 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.springframework.rewrite;
+
+import org.jetbrains.annotations.NotNull;
+import org.junit.jupiter.api.*;
+import org.junit.jupiter.api.io.TempDir;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.RecipeRun;
+import org.openrewrite.SourceFile;
+import org.openrewrite.internal.InMemoryLargeSourceSet;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.marker.JavaSourceSet;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.springframework.rewrite.parsers.RewriteExecutionContext;
+import org.springframework.rewrite.parsers.RewriteProjectParsingResult;
+import org.springframework.rewrite.parsers.SpringRewriteProperties;
+import org.springframework.rewrite.parsers.maven.ClasspathDependencies;
+import org.springframework.rewrite.support.openrewrite.GenericOpenRewriteRecipe;
+import org.springframework.rewrite.test.util.ParserExecutionHelper;
+import org.springframework.rewrite.test.util.TestProjectHelper;
+
+import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MavenTypeResolutionTest {
+
+ @Nested
+ @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+ class WithSingleModuleMavenProject {
+
+ private static RewriteProjectParsingResult parsingResult;
+
+ /**
+ * Given: A Project with pom file containing dependency to @SpringBootApplication
+ * and a simple Java class When: The project is parsed Then: The classpath should
+ * contain the simple class and the @SpringBootApplication annotation. The
+ * typesInUse is empty because the simple class does not import/use any types
+ */
+ @Test
+ @Order(1)
+ @DisplayName("parsed project should have correct classpath")
+ void parsedProjectShouldHaveCorrectClasspath(@TempDir Path baseDir) {
+ // given
+ pepareProject(baseDir);
+ // when
+ parsingResult = new ParserExecutionHelper().parseWithRewriteProjectParser(baseDir,
+ new SpringRewriteProperties());
+ // then
+ verifyTypesOnClasspathAndNoTypesInUse(parsingResult);
+ }
+
+ /**
+ * Given: The parsed project from (1) When: The annotation is added to the simple
+ * class Then: The classpath should contain the simple class and
+ * the @SpringBootApplication annotation. The typesInUse contains
+ * the @SpringBootApplication
+ */
+ @Test
+ @Order(2)
+ @DisplayName("annotating the class adds the annotation to typesInUse")
+ void annotatingTheClassAddsTheAnnotationToTypesInUse() {
+ // Given: parsed project
+ List sourceFiles = parsingResult.sourceFiles();
+ assertThat(sourceFiles).isNotEmpty();
+ // When: Add @SpringBootApplication annotation to class
+ RecipeRun recipeRun = annotateClass(sourceFiles);
+ // Then: annotation is in typesInUse
+ verifyAnnotationIsNowInUsedTypes(recipeRun);
+ }
+
+ private static void verifyAnnotationIsNowInUsedTypes(RecipeRun recipeRun) {
+ SourceFile after = recipeRun.getChangeset().getAllResults().get(0).getAfter();
+ assertThat(after).isInstanceOf(J.CompilationUnit.class);
+ J.CompilationUnit cu = (J.CompilationUnit) after;
+ List classpath = cu.getMarkers()
+ .findFirst(JavaSourceSet.class)
+ .get()
+ .getClasspath()
+ .stream()
+ .map(JavaType.FullyQualified::getFullyQualifiedName)
+ .toList();
+ assertThat(classpath).contains("org.springframework.boot.autoconfigure.SpringBootApplication", "SomeClass");
+
+ List typesInUse = cu.getTypesInUse()
+ .getTypesInUse()
+ .stream()
+ .map(JavaType.FullyQualified.class::cast)
+ .map(JavaType.FullyQualified::getFullyQualifiedName)
+ .toList();
+ assertThat(typesInUse)
+ .containsExactlyInAnyOrder("org.springframework.boot.autoconfigure.SpringBootApplication");
+ }
+
+ @NotNull
+ private static RecipeRun annotateClass(List sourceFiles) {
+ RecipeRun recipeRun = new GenericOpenRewriteRecipe<>(() -> new JavaIsoVisitor<>() {
+ @Override
+ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl,
+ ExecutionContext executionContext) {
+ J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, executionContext);
+ if (cd.getSimpleName().equals("SomeClass")) {
+ ClasspathDependencies classpathDependencies = ((J.CompilationUnit) getCursor()
+ .dropParentUntil(J.CompilationUnit.class::isInstance)
+ .getValue()).getMarkers().findFirst(ClasspathDependencies.class).get();
+ String annotationFqName = "org.springframework.boot.autoconfigure.SpringBootApplication";
+ cd = JavaTemplate.builder("@SpringBootApplication")
+ .imports(annotationFqName)
+ .javaParser(JavaParser.fromJavaVersion()
+ .classpath(classpathDependencies.getDependencies())
+ .logCompilationWarningsAndErrors(true))
+ .build()
+ .apply(getCursor(), cd.getCoordinates()
+ .addAnnotation(Comparator.comparing(J.Annotation::getSimpleName)));
+ maybeAddImport(annotationFqName);
+ }
+ return cd;
+ }
+ }).run(new InMemoryLargeSourceSet(sourceFiles), new RewriteExecutionContext());
+ return recipeRun;
+ }
+
+ private static void verifyTypesOnClasspathAndNoTypesInUse(RewriteProjectParsingResult parallelParsingResult) {
+ J.CompilationUnit cuBefore = parallelParsingResult.sourceFiles()
+ .stream()
+ .filter(J.CompilationUnit.class::isInstance)
+ .map(J.CompilationUnit.class::cast)
+ .findFirst()
+ .get();
+ List typesInUseBefore = cuBefore.getTypesInUse()
+ .getTypesInUse()
+ .stream()
+ .map(JavaType.FullyQualified.class::cast)
+ .map(JavaType.FullyQualified::getFullyQualifiedName)
+ .toList();
+ assertThat(typesInUseBefore).isEmpty();
+ List classpathBefore = cuBefore.getMarkers()
+ .findFirst(JavaSourceSet.class)
+ .get()
+ .getClasspath()
+ .stream()
+ .map(JavaType.FullyQualified::getFullyQualifiedName)
+ .toList();
+ assertThat(classpathBefore).contains("org.springframework.boot.autoconfigure.SpringBootApplication",
+ "SomeClass");
+ }
+
+ private static void pepareProject(Path baseDir) {
+ TestProjectHelper.createTestProject(baseDir)
+ .addResource("pom.xml",
+ """
+
+
+ 4.0.0
+ org.example
+ artifact
+ 0.1.0-SNAPSHOT
+
+ 17
+ 17
+
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ 3.1.3
+
+
+
+ """)
+
+ .addResource("src/main/java/SomeClass.java",
+ // @formatter:off
+ """
+ public class SomeClass {}
+ """
+ // @formatter:on
+ )
+ .writeToFilesystem();
+ }
+
+ }
+
+ @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+ class WithMultiModuleMavenProject {
+
+ }
+
+}
diff --git a/spring-rewrite-commons-functional-tests/pom.xml b/spring-rewrite-commons-functional-tests/pom.xml
index a84951c..e35baaf 100644
--- a/spring-rewrite-commons-functional-tests/pom.xml
+++ b/spring-rewrite-commons-functional-tests/pom.xml
@@ -23,6 +23,7 @@
private-artifact-repository-tests
+ dependency-resolution-tests
diff --git a/spring-rewrite-commons-launcher/pom.xml b/spring-rewrite-commons-launcher/pom.xml
index b671ba3..a695662 100644
--- a/spring-rewrite-commons-launcher/pom.xml
+++ b/spring-rewrite-commons-launcher/pom.xml
@@ -186,7 +186,7 @@
test
- **/parsers/maven/**
+ **/parsers/maven/**,**/test/util/**
diff --git a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/test/util/TestProjectHelper.java b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/test/util/TestProjectHelper.java
index 9d2d65b..befece9 100644
--- a/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/test/util/TestProjectHelper.java
+++ b/spring-rewrite-commons-launcher/src/test/java/org/springframework/rewrite/test/util/TestProjectHelper.java
@@ -120,4 +120,10 @@ public class TestProjectHelper {
ResourceUtil.write(targetDir, resources);
}
+ public TestProjectHelper addResource(String relativePath, String content) {
+ DummyResource dummyResource = new DummyResource(targetDir.resolve(relativePath), content);
+ this.resources.add(dummyResource);
+ return this;
+ }
+
}