2020import org .openrewrite .internal .ListUtils ;
2121import org .openrewrite .java .*;
2222import org .openrewrite .java .search .UsesType ;
23+ import org .openrewrite .java .trait .Annotated ;
24+ import org .openrewrite .java .trait .Literal ;
25+ import org .openrewrite .java .trait .Traits ;
2326import org .openrewrite .java .tree .Expression ;
2427import org .openrewrite .java .tree .J ;
2528import org .openrewrite .java .tree .Space ;
@@ -47,8 +50,10 @@ public class JUnitParamsRunnerToParameterized extends Recipe {
4750 private static final AnnotationMatcher PARAMETERS_MATCHER = new AnnotationMatcher ("@junitparams.Parameters" );
4851 private static final AnnotationMatcher TEST_CASE_NAME_MATCHER = new AnnotationMatcher ("@junitparams.naming.TestCaseName" );
4952 private static final AnnotationMatcher NAMED_PARAMETERS_MATCHER = new AnnotationMatcher ("@junitparams.NamedParameters" );
53+ private static final AnnotationMatcher CONVERTER_MATCHER = new AnnotationMatcher ("@junitparams.converters.Param" );
5054
5155 private static final String INIT_METHOD_REFERENCES = "init-method-references" ;
56+ private static final String CSV_PARAMS = "csv-params" ;
5257 private static final String PARAMETERS_FOR_PREFIX = "parametersFor" ;
5358 private static final String PARAMETERIZED_TESTS = "parameterized-tests" ;
5459 private static final String INIT_METHODS_MAP = "named-parameters-map" ;
@@ -78,8 +83,9 @@ private static class ParameterizedTemplateVisitor extends JavaIsoVisitor<Executi
7883 @ Override
7984 public J .ClassDeclaration visitClassDeclaration (J .ClassDeclaration classDecl , ExecutionContext ctx ) {
8085 J .ClassDeclaration cd = super .visitClassDeclaration (classDecl , ctx );
81- Set <String > initMethods = getCursor ().getMessage (INIT_METHOD_REFERENCES );
82- if (initMethods != null && !initMethods .isEmpty ()) {
86+ Set <String > initMethods = getCursor ().computeMessageIfAbsent (INIT_METHOD_REFERENCES , v -> new HashSet <>());
87+ Boolean hasCsvParams = getCursor ().getMessage (CSV_PARAMS );
88+ if (!initMethods .isEmpty () || Boolean .TRUE .equals (hasCsvParams )) {
8389 doAfterVisit (new ParametersNoArgsImplicitMethodSource (initMethods ,
8490 getCursor ().computeMessageIfAbsent (INIT_METHODS_MAP , v -> new HashMap <>()),
8591 getCursor ().computeMessageIfAbsent (CONVERSION_NOT_SUPPORTED , v -> new HashSet <>()),
@@ -105,13 +111,17 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
105111 J .Annotation anno = super .visitAnnotation (annotation , ctx );
106112 Cursor classDeclCursor = getCursor ().dropParentUntil (J .ClassDeclaration .class ::isInstance );
107113 if (PARAMETERS_MATCHER .matches (anno )) {
114+ Annotated annotated = Traits .annotated (PARAMETERS_MATCHER ).require (annotation , getCursor ().getParentOrThrow ());
115+ String annotationArgumentValue = getAnnotationArgumentForInitMethod (annotated , "method" , "named" );
108116 classDeclCursor .computeMessageIfAbsent (PARAMETERIZED_TESTS , v -> new HashSet <>())
109117 .add (getCursor ().firstEnclosing (J .MethodDeclaration .class ).getSimpleName ());
110- String annotationArgumentValue = getAnnotationArgumentForInitMethod (anno , "method" , "named" );
111118 if (annotationArgumentValue != null ) {
112119 for (String method : annotationArgumentValue .split ("," )) {
113120 classDeclCursor .computeMessageIfAbsent (INIT_METHOD_REFERENCES , v -> new HashSet <>()).add (method );
114121 }
122+ } else if (isSupportedCsvParam (annotated )) {
123+ anno = getCsVParamTemplate (ctx ).apply (updateCursor (anno ), anno .getCoordinates ().replace (), anno .getArguments ().get (0 ));
124+ classDeclCursor .putMessage (CSV_PARAMS , Boolean .TRUE );
115125 } else if (anno .getArguments () != null && !anno .getArguments ().isEmpty ()) {
116126 // This conversion is not supported add a comment to the annotation and the method name to the not supported list
117127 String comment = " JunitParamsRunnerToParameterized conversion not supported" ;
@@ -125,48 +135,62 @@ public J.Annotation visitAnnotation(J.Annotation annotation, ExecutionContext ct
125135 unsupportedMethods .add (junitParamsDefaultInitMethodName (m .getSimpleName ()));
126136 }
127137 } else if (NAMED_PARAMETERS_MATCHER .matches (annotation )) {
128- String namedInitMethod = getLiteralAnnotationArgumentValue (annotation );
129- if (namedInitMethod != null ) {
138+ Annotated annotated = Traits .annotated (NAMED_PARAMETERS_MATCHER ).require (annotation , getCursor ().getParentOrThrow ());
139+ Optional <Literal > value = annotated .getDefaultAttribute ("value" );
140+ if (value .isPresent ()) {
130141 J .MethodDeclaration m = getCursor ().dropParentUntil (J .MethodDeclaration .class ::isInstance ).getValue ();
131142 classDeclCursor .computeMessageIfAbsent (INIT_METHOD_REFERENCES , v -> new HashSet <>()).add (m .getSimpleName ());
132- classDeclCursor .computeMessageIfAbsent (INIT_METHODS_MAP , v -> new HashMap <>()).put (namedInitMethod , m .getSimpleName ());
143+ classDeclCursor .computeMessageIfAbsent (INIT_METHODS_MAP , v -> new HashMap <>()).put (value . get (). getString () , m .getSimpleName ());
133144 }
134145 } else if (TEST_CASE_NAME_MATCHER .matches (anno )) {
146+ Annotated annotated = Traits .annotated (TEST_CASE_NAME_MATCHER ).require (annotation , getCursor ().getParentOrThrow ());
135147 // test name for ParameterizedTest argument
136- Object testNameArg = getLiteralAnnotationArgumentValue (anno );
137- String testName = testNameArg != null ? testNameArg .toString () : "{method}({params}) [{index}]" ;
138- J .MethodDeclaration md = getCursor ().dropParentUntil (J .MethodDeclaration .class ::isInstance ).getValue ();
139- classDeclCursor .computeMessageIfAbsent (INIT_METHODS_MAP , v -> new HashMap <>()).put (md .getSimpleName (), testName );
148+ Optional <Literal > value = annotated .getDefaultAttribute ("value" );
149+ if (value .isPresent ()) {
150+ Object testNameArg = value .get ().getString ();
151+ String testName = testNameArg != null ? testNameArg .toString () : "{method}({params}) [{index}]" ;
152+ J .MethodDeclaration md = getCursor ().dropParentUntil (J .MethodDeclaration .class ::isInstance ).getValue ();
153+ classDeclCursor .computeMessageIfAbsent (INIT_METHODS_MAP , v -> new HashMap <>()).put (md .getSimpleName (), testName );
154+ }
140155 }
141156 return anno ;
142157 }
143158
144- private @ Nullable String getLiteralAnnotationArgumentValue (J .Annotation anno ) {
145- String annotationArgumentValue = null ;
146- if (anno .getArguments () != null && anno .getArguments ().size () == 1 && anno .getArguments ().get (0 ) instanceof J .Literal ) {
147- J .Literal literal = (J .Literal ) anno .getArguments ().get (0 );
148- annotationArgumentValue = literal .getValue () != null ? literal .getValue ().toString () : null ;
159+ private @ Nullable String getAnnotationArgumentForInitMethod (Annotated annotated , String ... variableNames ) {
160+ for (String variableName : variableNames ) {
161+ Optional <Literal > attribute = annotated .getAttribute (variableName );
162+ if (attribute .isPresent ()) {
163+ return attribute .get ().getString ();
164+ }
149165 }
150- return annotationArgumentValue ;
166+ return null ;
151167 }
152168
153- private @ Nullable String getAnnotationArgumentForInitMethod (J .Annotation anno , String ... variableNames ) {
154- String value = null ;
155- if (anno .getArguments () != null && anno .getArguments ().size () == 1 &&
156- anno .getArguments ().get (0 ) instanceof J .Assignment &&
157- ((J .Assignment ) anno .getArguments ().get (0 )).getVariable () instanceof J .Identifier &&
158- ((J .Assignment ) anno .getArguments ().get (0 )).getAssignment () instanceof J .Literal ) {
159- J .Assignment annoArg = (J .Assignment ) anno .getArguments ().get (0 );
160- J .Literal assignment = (J .Literal ) annoArg .getAssignment ();
161- String identifier = ((J .Identifier ) annoArg .getVariable ()).getSimpleName ();
162- for (String variableName : variableNames ) {
163- if (variableName .equals (identifier )) {
164- value = assignment .getValue () != null ? assignment .getValue ().toString () : null ;
165- break ;
166- }
167- }
169+ private boolean isSupportedCsvParam (Annotated annotated ) {
170+ if (annotated .getTree ().getArguments () == null || annotated .getTree ().getArguments ().size () != 1 ) {
171+ return false ;
168172 }
169- return value ;
173+ Optional <Literal > value = annotated .getDefaultAttribute ("value" );
174+ return value .isPresent () &&
175+ value .get ().isArray () &&
176+ !doTestParamsHaveCustomConverter (getCursor ().firstEnclosing (J .MethodDeclaration .class ));
177+ }
178+
179+ private boolean doTestParamsHaveCustomConverter (J .@ Nullable MethodDeclaration method ) {
180+ if (method == null ) {
181+ return false ;
182+ }
183+ return method .getParameters ().stream ()
184+ .filter (param -> param instanceof J .VariableDeclarations )
185+ .map (J .VariableDeclarations .class ::cast )
186+ .anyMatch (v -> v .getLeadingAnnotations ().stream ().anyMatch (CONVERTER_MATCHER ::matches ));
187+ }
188+
189+ private static JavaTemplate getCsVParamTemplate (ExecutionContext ctx ) {
190+ return JavaTemplate .builder ("@CsvSource(#{any(java.lang.String[])})" )
191+ .imports ("org.junit.jupiter.params.provider.CsvSource" )
192+ .javaParser (JavaParser .fromJavaVersion ().classpathFromResources (ctx , "junit-jupiter-params" ))
193+ .build ();
170194 }
171195 }
172196
@@ -228,6 +252,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex
228252 maybeRemoveImport ("junitparams.naming.TestCaseName" );
229253 maybeAddImport ("org.junit.jupiter.params.ParameterizedTest" );
230254 maybeAddImport ("org.junit.jupiter.params.provider.MethodSource" );
255+ maybeAddImport ("org.junit.jupiter.params.provider.CsvSource" );
231256 return cd ;
232257 }
233258
0 commit comments