|
29 | 29 | import org.openrewrite.tree.ParseError; |
30 | 30 | import org.openrewrite.tree.ParsingExecutionContextView; |
31 | 31 |
|
| 32 | +import java.lang.annotation.Annotation; |
| 33 | +import java.lang.reflect.Field; |
32 | 34 | import java.nio.charset.StandardCharsets; |
33 | 35 | import java.nio.file.Path; |
34 | 36 | import java.util.*; |
@@ -174,19 +176,25 @@ default void rewriteRun(Consumer<RecipeSpec> spec, SourceSpec<?>... sourceSpecs) |
174 | 176 | assertThat(recipeSerializer.read(recipeSerializer.write(recipe))) |
175 | 177 | .as("Recipe must be serializable/deserializable") |
176 | 178 | .isEqualTo(recipe); |
177 | | - assertThatCode(() -> { |
178 | | - Recipe r = new RecipeLoader(recipe.getClass().getClassLoader()) |
179 | | - .load(recipe.getClass(), null); |
180 | | - // getRecipeList should not fail with default parameters from RecipeLoader. |
181 | | - r.getRecipeList(); |
182 | | - // We add recipes to HashSet in some places, we need to validate that hashCode and equals does not fail. |
183 | | - //noinspection ResultOfMethodCallIgnored |
184 | | - r.hashCode(); |
185 | | - //noinspection EqualsWithItself,ResultOfMethodCallIgnored |
186 | | - r.equals(r); |
187 | | - }) |
188 | | - .as("Recipe must be able to instantiate via RecipeLoader") |
189 | | - .doesNotThrowAnyException(); |
| 179 | + // Skip RecipeLoader null-instantiation test for Kotlin recipes with required options, |
| 180 | + // as Jackson's Kotlin module enforces non-nullability and will fail when trying to |
| 181 | + // instantiate with null arguments. The serialization round-trip test above still |
| 182 | + // validates that actual recipe instances work correctly. |
| 183 | + if (!RewriteTestUtils.isKotlinRecipeWithRequiredOptions(recipe.getClass())) { |
| 184 | + assertThatCode(() -> { |
| 185 | + Recipe r = new RecipeLoader(recipe.getClass().getClassLoader()) |
| 186 | + .load(recipe.getClass(), null); |
| 187 | + // getRecipeList should not fail with default parameters from RecipeLoader. |
| 188 | + r.getRecipeList(); |
| 189 | + // We add recipes to HashSet in some places, we need to validate that hashCode and equals does not fail. |
| 190 | + //noinspection ResultOfMethodCallIgnored |
| 191 | + r.hashCode(); |
| 192 | + //noinspection EqualsWithItself,ResultOfMethodCallIgnored |
| 193 | + r.equals(r); |
| 194 | + }) |
| 195 | + .as("Recipe must be able to instantiate via RecipeLoader") |
| 196 | + .doesNotThrowAnyException(); |
| 197 | + } |
190 | 198 | validateRecipeNameAndDescription(recipe); |
191 | 199 | validateRecipeOptions(recipe); |
192 | 200 | } |
@@ -709,6 +717,30 @@ default void validateRecipeOptions(Recipe recipe) { |
709 | 717 | } |
710 | 718 |
|
711 | 719 | class RewriteTestUtils { |
| 720 | + |
| 721 | + /** |
| 722 | + * Checks if a Kotlin recipe class has required options that will fail when |
| 723 | + * instantiated with null arguments via Jackson. This is because Jackson's |
| 724 | + * Kotlin module enforces non-nullability for Kotlin classes. |
| 725 | + * <p> |
| 726 | + * This check only applies to Kotlin classes since Java classes can have |
| 727 | + * null values for any parameter type. |
| 728 | + */ |
| 729 | + static boolean isKotlinRecipeWithRequiredOptions(Class<?> recipeClass) { |
| 730 | + for (Annotation a : recipeClass.getDeclaredAnnotations()) { |
| 731 | + if ("kotlin.Metadata".equals(a.annotationType().getName())) { |
| 732 | + // Check for @Option fields with required=true (which is the default) |
| 733 | + for (Field field : recipeClass.getDeclaredFields()) { |
| 734 | + Option option = field.getAnnotation(Option.class); |
| 735 | + if (option != null && option.required()) { |
| 736 | + return true; |
| 737 | + } |
| 738 | + } |
| 739 | + } |
| 740 | + } |
| 741 | + return false; |
| 742 | + } |
| 743 | + |
712 | 744 | static boolean groupSourceSpecsByParser(List<Parser.Builder> parserBuilders, Map<Parser.Builder, List<SourceSpec<?>>> sourceSpecsByParser, SourceSpec<?> sourceSpec) { |
713 | 745 | for (Map.Entry<Parser.Builder, List<SourceSpec<?>>> entry : sourceSpecsByParser.entrySet()) { |
714 | 746 | if (entry.getKey().getSourceFileType().equals(sourceSpec.sourceFileType) && sourceSpec.getParser().getClass().isAssignableFrom(entry.getKey().getClass())) { |
|
0 commit comments