Skip to content

Commit a9a3cf5

Browse files
committed
Fix bug and polish contribution
This commit fixes a bug regarding whitespace in parameter lists for local factory methods specified via `@MethodSource`. Specifically, whitespace is now supported. For example, @MethodSource("data(int, java.lang.String)") is now supported (note the space after the comma). As a side note, whitespace in parameter lists was already supported for a fully qualified method name. See #3080, #3101
1 parent 825ea38 commit a9a3cf5

File tree

4 files changed

+57
-15
lines changed

4 files changed

+57
-15
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.9.2.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ JUnit repository on GitHub.
3838

3939
==== Bug Fixes
4040

41-
* Introduce new `@MethodSource` syntax to add the possibility to explicitly select
42-
an overloaded local factory method without specifying its fully qualified name
41+
* New `@MethodSource` syntax for explicitly selecting an overloaded local factory method
42+
without specifying its fully qualified name.
4343

4444
==== Deprecations and Breaking Changes
4545

junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@
1111
package org.junit.jupiter.params.provider;
1212

1313
import static java.lang.String.format;
14+
import static java.util.Arrays.stream;
15+
import static java.util.stream.Collectors.joining;
1416
import static java.util.stream.Collectors.toList;
1517
import static org.junit.jupiter.params.provider.Arguments.arguments;
1618
import static org.junit.platform.commons.util.AnnotationUtils.isAnnotated;
1719
import static org.junit.platform.commons.util.CollectionUtils.isConvertibleToStream;
1820

1921
import java.lang.reflect.Method;
20-
import java.util.Arrays;
2122
import java.util.List;
2223
import java.util.function.Predicate;
2324
import java.util.stream.Stream;
@@ -28,7 +29,6 @@
2829
import org.junit.jupiter.api.extension.ExtensionContext;
2930
import org.junit.jupiter.params.support.AnnotationConsumer;
3031
import org.junit.platform.commons.JUnitException;
31-
import org.junit.platform.commons.util.ClassUtils;
3232
import org.junit.platform.commons.util.CollectionUtils;
3333
import org.junit.platform.commons.util.Preconditions;
3434
import org.junit.platform.commons.util.ReflectionUtils;
@@ -50,7 +50,7 @@ public void accept(MethodSource annotation) {
5050
public Stream<Arguments> provideArguments(ExtensionContext context) {
5151
Object testInstance = context.getTestInstance().orElse(null);
5252
// @formatter:off
53-
return Arrays.stream(this.methodNames)
53+
return stream(this.methodNames)
5454
.map(factoryMethodName -> getFactoryMethod(context, factoryMethodName))
5555
.map(factoryMethod -> context.getExecutableInvoker().invoke(factoryMethod, testInstance))
5656
.flatMap(CollectionUtils::toStream)
@@ -73,11 +73,20 @@ private static boolean looksLikeAFullyQualifiedMethodName(String factoryMethodNa
7373
if (factoryMethodName.contains("#")) {
7474
return true;
7575
}
76-
if (factoryMethodName.contains(".") && factoryMethodName.contains("(")) {
77-
// Excluding cases of simple method names with parameters
78-
return factoryMethodName.indexOf(".") < factoryMethodName.indexOf("(");
76+
int indexOfDot = factoryMethodName.indexOf(".");
77+
if (indexOfDot == -1) {
78+
return false;
7979
}
80-
return factoryMethodName.contains(".");
80+
int indexOfOpeningParenthesis = factoryMethodName.indexOf("(");
81+
if (indexOfOpeningParenthesis > 0) {
82+
// Exclude simple method names with parameters
83+
return indexOfDot < indexOfOpeningParenthesis;
84+
}
85+
// If we get this far, we conclude the supplied factory method name "looks"
86+
// like it was intended to be a fully qualified method name, even if the
87+
// syntax is invalid. We do this in order to provide better diagnostics for
88+
// the user when a fully qualified method name is in fact invalid.
89+
return true;
8190
}
8291

8392
private Method getFactoryMethodByFullyQualifiedName(String fullyQualifiedMethodName) {
@@ -143,12 +152,21 @@ private List<Method> findFactoryMethodsBySimpleName(Class<?> testClass, Method t
143152

144153
private static List<Method> filterFactoryMethodsWithMatchingParameters(List<Method> factoryMethods,
145154
String factoryMethodName, String factoryMethodParameters) {
155+
146156
if (!factoryMethodName.endsWith(")")) {
147-
// If parameters are not specified, no choice is made
157+
// If parameters are not specified, nothing is filtered.
148158
return factoryMethods;
149159
}
150-
Predicate<Method> hasRequiredParameters = method -> factoryMethodParameters.equals(
151-
ClassUtils.nullSafeToString(method.getParameterTypes()));
160+
161+
// Compare against canonical parameter list, ignoring whitespace.
162+
String parameterList = factoryMethodParameters.replaceAll("\\s+", "");
163+
Predicate<Method> hasRequiredParameters = method -> {
164+
if (parameterList.isEmpty()) {
165+
return method.getParameterCount() == 0;
166+
}
167+
return parameterList.equals(stream(method.getParameterTypes()).map(Class::getName).collect(joining(",")));
168+
};
169+
152170
return factoryMethods.stream().filter(hasRequiredParameters).collect(toList());
153171
}
154172

junit-jupiter-params/src/test/java/org/junit/jupiter/params/provider/MethodArgumentsProviderTests.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.junit.jupiter.engine.config.JupiterConfiguration;
3838
import org.junit.jupiter.engine.execution.DefaultExecutableInvoker;
3939
import org.junit.jupiter.engine.extension.MutableExtensionRegistry;
40+
import org.junit.jupiter.params.ParameterizedTest;
4041
import org.junit.platform.commons.JUnitException;
4142
import org.junit.platform.commons.PreconditionViolationException;
4243
import org.junit.platform.commons.util.ReflectionUtils;
@@ -408,16 +409,25 @@ void providesArgumentsUsingSimpleNameWithParameter() {
408409
assertThat(arguments).containsExactly(array("foo!"), array("bar!"));
409410
}
410411

412+
@ParameterizedTest
413+
@ValueSource(strings = { "java.lang.String,java.lang.String", "java.lang.String, java.lang.String",
414+
"java.lang.String, java.lang.String" })
415+
void providesArgumentsUsingSimpleNameWithMultipleParameters(String params) {
416+
var arguments = provideArguments("stringStreamProviderWithOrWithoutParameter(" + params + ")");
417+
assertThat(arguments).containsExactly(array("foo!!"), array("bar!!"));
418+
}
419+
411420
@Test
412421
void throwsExceptionWhenSeveralFactoryMethodsWithSameNameAreAvailable() {
413422
var exception = assertThrows(PreconditionViolationException.class,
414423
() -> provideArguments("stringStreamProviderWithOrWithoutParameter").toArray());
415424

416425
assertThat(exception.getMessage())//
417-
.startsWith("2 factory methods named [stringStreamProviderWithOrWithoutParameter] were found in "
426+
.startsWith("3 factory methods named [stringStreamProviderWithOrWithoutParameter] were found in "
418427
+ "class [org.junit.jupiter.params.provider.MethodArgumentsProviderTests$TestCase]: ")//
419428
.contains("stringStreamProviderWithOrWithoutParameter()",
420-
"stringStreamProviderWithOrWithoutParameter(java.lang.String)");
429+
"stringStreamProviderWithOrWithoutParameter(java.lang.String)",
430+
"stringStreamProviderWithOrWithoutParameter(java.lang.String,java.lang.String)");
421431
}
422432

423433
@Test
@@ -428,6 +438,16 @@ void providesArgumentsUsingFactoryMethodSelectedViaFullyQualifiedNameWithParamet
428438
assertThat(arguments).containsExactly(array("foo!"), array("bar!"));
429439
}
430440

441+
@ParameterizedTest
442+
@ValueSource(strings = { "java.lang.String,java.lang.String", "java.lang.String, java.lang.String",
443+
"java.lang.String, java.lang.String" })
444+
void providesArgumentsUsingFactoryMethodSelectedViaFullyQualifiedNameWithMultipleParameters(String params) {
445+
var arguments = provideArguments(
446+
TestCase.class.getName() + "#stringStreamProviderWithOrWithoutParameter(" + params + ")");
447+
448+
assertThat(arguments).containsExactly(array("foo!!"), array("bar!!"));
449+
}
450+
431451
@Test
432452
void providesArgumentsUsingFactoryMethodSelectedViaFullyQualifiedNameWithoutParameter() {
433453
var arguments = provideArguments(
@@ -522,6 +542,10 @@ static Stream<String> stringStreamProviderWithOrWithoutParameter(String paramete
522542
return stringStreamProviderWithParameter(parameter);
523543
}
524544

545+
static Stream<String> stringStreamProviderWithOrWithoutParameter(String parameter1, String parameter2) {
546+
return stringStreamProviderWithParameter(parameter1 + parameter2);
547+
}
548+
525549
// @ParameterizedTest
526550
// @MethodSource // use default, inferred factory method
527551
void overloadedStringStreamProvider(Object parameter) {

junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,7 @@ public static String[] parseFullyQualifiedMethodName(String fullyQualifiedMethod
928928
* @param qualifiedMethodName a qualified method name, never {@code null} or blank
929929
* @return a 2-element array of strings containing the parsed values
930930
*/
931-
@API(status = INTERNAL, since = "1.9")
931+
@API(status = INTERNAL, since = "1.9.2")
932932
public static String[] parseQualifiedMethodName(String qualifiedMethodName) {
933933
String methodName = qualifiedMethodName;
934934
String methodParameters = "";

0 commit comments

Comments
 (0)