diff --git a/documentation/src/docs/asciidoc/release-notes-5.0.0-RC1.adoc b/documentation/src/docs/asciidoc/release-notes-5.0.0-RC1.adoc
index 81af09b9490..59c6b0c509e 100644
--- a/documentation/src/docs/asciidoc/release-notes-5.0.0-RC1.adoc
+++ b/documentation/src/docs/asciidoc/release-notes-5.0.0-RC1.adoc
@@ -22,7 +22,10 @@ on GitHub.
* Generic, interface `default` methods that are not overridden by an implementing class
can now be _selected_ by class, method name, and parameter types or by _fully qualified
method name_. This applies to method selectors in `DiscoverySelectors` as well as to
- both `findMethod(*)` variants in `ReflectionSupport`.
+ both `findMethod()` variants in `ReflectionSupport`.
+* A non-overridden interface default method whose method signature is overloaded by a
+ locally declared method is now properly discovered when searching for methods within a
+ class hierarchy using `findMethods()` in `ReflectionSupport`.
===== Deprecations and Breaking Changes
@@ -40,6 +43,9 @@ on GitHub.
`@AfterAll`, `BeforeEach`, or `@AfterEach` no longer halt the execution of the entire
test plan during the discovery phase. Rather, such errors are now reported during the
execution of the affected test class.
+* A non-overridden interface default method whose method signature is overloaded by a
+ locally declared method is now properly included in the test plan. This applies to
+ default methods annotated with Jupiter annotations such as `@Test, `@BeforeEach`, etc.
===== Deprecations and Breaking Changes
diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java
index d1bc6d5d171..a91334305b1 100644
--- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java
+++ b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java
@@ -22,7 +22,6 @@
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
@@ -111,8 +110,6 @@ void executeTestCaseWithOverloadedMethodNextToGenericDefaultMethodSelectedByFull
// @formatter:on
}
- // TODO [#976] Enable failing @Disabled test.
- @Disabled("Disabled until #976 is resolved")
@Test
void executeTestCaseWithOverloadedMethodNextToGenericDefaultMethodSelectedByClass() throws Exception {
Class> clazz = GenericTestCaseWithDefaultMethod.class;
diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java
index be59675b748..5a8b497c2e3 100644
--- a/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java
+++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java
@@ -36,6 +36,24 @@ private ReflectionSupport() {
}
///CLOVER:ON
+ /**
+ * Load a class by its primitive name or fully qualified name,
+ * using the default {@link ClassLoader}.
+ *
+ *
Class names for arrays may be specified using either the JVM's internal
+ * String representation (e.g., {@code [[I} for {@code int[][]},
+ * {@code [Lava.lang.String;} for {@code java.lang.String[]}, etc.) or
+ * source code syntax (e.g., {@code int[][]}, {@code java.lang.String[]},
+ * etc.).
+ *
+ * @param name the name of the class to load; never {@code null} or blank
+ * @return an {@code Optional} containing the loaded class; never {@code null}
+ * but potentially empty if no such class could be loaded
+ */
+ public static Optional> loadClass(String name) {
+ return ReflectionUtils.loadClass(name);
+ }
+
/**
* Find all {@linkplain Class classes} of the supplied {@code root}
* {@linkplain URI} that match the specified {@code classTester} and
@@ -44,7 +62,8 @@ private ReflectionSupport() {
* @param root the root URI to start scanning
* @param classTester the class type filter; never {@code null}
* @param classNameFilter the class name filter; never {@code null}
- * @return the list of all such classes found; neither {@code null} nor mutable
+ * @return an immutable list of all such classes found; never {@code null}
+ * but potentially empty
*/
public static List> findAllClassesInClasspathRoot(URI root, Predicate> classTester,
Predicate classNameFilter) {
@@ -59,7 +78,8 @@ public static List> findAllClassesInClasspathRoot(URI root, Predicate> findAllClassesInPackage(String basePackageName, Predicate> classTester,
Predicate classNameFilter) {
@@ -67,36 +87,38 @@ public static List> findAllClassesInPackage(String basePackageName, Pre
}
/**
- * Find all {@linkplain Method methods} of the supplied class or interface
- * that match the specified {@code predicate}.
+ * Create a new instance of the specified {@link Class} by invoking
+ * the constructor whose argument list matches the types of the supplied
+ * arguments.
*
- *
If you're are looking for methods annotated with a certain annotation
- * type, consider using {@linkplain AnnotationSupport#findAnnotatedMethods(Class, Class, HierarchyTraversalMode)}.
+ *
The constructor will be made accessible if necessary, and any checked
+ * exception will be {@linkplain ExceptionUtils#throwAsUncheckedException masked}
+ * as an unchecked exception.
*
- * @param clazz the class or interface in which to find the methods; never {@code null}
- * @param predicate the method filter; never {@code null}
- * @param traversalMode the hierarchy traversal mode; never {@code null}
- * @return the list of all such methods found; neither {@code null} nor mutable
+ * @param clazz the class to instantiate; never {@code null}
+ * @param args the arguments to pass to the constructor none of which may be {@code null}
+ * @return the new instance
+ * @see ExceptionUtils#throwAsUncheckedException(Throwable)
*/
- public static List findMethods(Class> clazz, Predicate predicate,
- HierarchyTraversalMode traversalMode) {
-
- return ReflectionUtils.findMethods(clazz, predicate,
- ReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));
+ public static T newInstance(Class clazz, Object... args) {
+ return ReflectionUtils.newInstance(clazz, args);
}
/**
- * Load a class by its primitive name or fully qualified name,
- * using the default {@link ClassLoader}.
- *
- *
See {@link ReflectionUtils#loadClass(String, ClassLoader)} for details on
- * support for class names for arrays.
+ * Invoke the supplied method, making it accessible if necessary and
+ * {@linkplain ExceptionUtils#throwAsUncheckedException masking} any
+ * checked exception as an unchecked exception.
*
- * @param name the name of the class to load; never {@code null} or blank
- * @see ReflectionUtils#loadClass(String, ClassLoader)
+ * @param method the method to invoke; never {@code null}
+ * @param target the object on which to invoke the method; may be
+ * {@code null} if the method is {@code static}
+ * @param args the arguments to pass to the method
+ * @return the value returned by the method invocation or {@code null}
+ * if the return type is {@code void}
+ * @see ExceptionUtils#throwAsUncheckedException(Throwable)
*/
- public static Optional> loadClass(String name) {
- return ReflectionUtils.loadClass(name);
+ public static Object invokeMethod(Method method, Object target, Object... args) {
+ return ReflectionUtils.invokeMethod(method, target, args);
}
/**
@@ -136,39 +158,23 @@ public static Optional findMethod(Class> clazz, String methodName, Cla
}
/**
- * Create a new instance of the specified {@link Class} by invoking
- * the constructor whose argument list matches the types of the supplied
- * arguments.
+ * Find all {@linkplain Method methods} of the supplied class or interface
+ * that match the specified {@code predicate}.
*
- *
The constructor will be made accessible if necessary, and any checked
- * exception will be {@linkplain ExceptionUtils#throwAsUncheckedException masked}
- * as an unchecked exception.
+ *
If you're are looking for methods annotated with a certain annotation
+ * type, consider using {@linkplain AnnotationSupport#findAnnotatedMethods(Class, Class, HierarchyTraversalMode)}.
*
- * @param clazz the class to instantiate; never {@code null}
- * @param args the arguments to pass to the constructor none of which may be {@code null}
- * @return the new instance
- * @see ReflectionUtils#newInstance(java.lang.reflect.Constructor, Object...)
- * @see ExceptionUtils#throwAsUncheckedException(Throwable)
+ * @param clazz the class or interface in which to find the methods; never {@code null}
+ * @param predicate the method filter; never {@code null}
+ * @param traversalMode the hierarchy traversal mode; never {@code null}
+ * @return an immutable list of all such methods found; never {@code null}
+ * but potentially empty
*/
- public static T newInstance(Class clazz, Object... args) {
- return ReflectionUtils.newInstance(clazz, args);
- }
+ public static List findMethods(Class> clazz, Predicate predicate,
+ HierarchyTraversalMode traversalMode) {
- /**
- * Invoke the supplied method, making it accessible if necessary and
- * {@linkplain ExceptionUtils#throwAsUncheckedException masking} any
- * checked exception as an unchecked exception.
- *
- * @param method the method to invoke; never {@code null}
- * @param target the object on which to invoke the method; may be
- * {@code null} if the method is {@code static}
- * @param args the arguments to pass to the method
- * @return the value returned by the method invocation or {@code null}
- * if the return type is {@code void}
- * @see ExceptionUtils#throwAsUncheckedException(Throwable)
- */
- public static Object invokeMethod(Method method, Object target, Object... args) {
- return ReflectionUtils.invokeMethod(method, target, args);
+ return ReflectionUtils.findMethods(clazz, predicate,
+ ReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));
}
/**
@@ -177,7 +183,8 @@ public static Object invokeMethod(Method method, Object target, Object... args)
* @param clazz the class to be searched; never {@code null}
* @param predicate the predicate against which the list of nested classes is
* checked; never {@code null}
- * @return the list of all such classes found; never {@code null}
+ * @return an immutable list of all such classes found; never {@code null}
+ * but potentially empty
*/
public static List> findNestedClasses(Class> clazz, Predicate> predicate) {
return ReflectionUtils.findNestedClasses(clazz, predicate);
diff --git a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java
index 8fd440d77d5..65f711bcec2 100644
--- a/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java
+++ b/junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java
@@ -10,6 +10,7 @@
package org.junit.platform.commons.util;
+import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.junit.platform.commons.meta.API.Usage.Internal;
@@ -306,11 +307,12 @@ public static Class> getWrapperType(Class> type) {
/**
* @see org.junit.platform.commons.support.ReflectionSupport#newInstance(Class, Object...)
+ * @see #newInstance(Constructor, Object...)
*/
public static T newInstance(Class clazz, Object... args) {
- Preconditions.notNull(clazz, "class must not be null");
- Preconditions.notNull(args, "argument array must not be null");
- Preconditions.containsNoNullElements(args, "individual arguments must not be null");
+ Preconditions.notNull(clazz, "Class must not be null");
+ Preconditions.notNull(args, "Argument array must not be null");
+ Preconditions.containsNoNullElements(args, "Individual arguments must not be null");
try {
Class>[] parameterTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
@@ -336,7 +338,7 @@ public static T newInstance(Class clazz, Object... args) {
* @see ExceptionUtils#throwAsUncheckedException(Throwable)
*/
public static T newInstance(Constructor constructor, Object... args) {
- Preconditions.notNull(constructor, "constructor must not be null");
+ Preconditions.notNull(constructor, "Constructor must not be null");
try {
return makeAccessible(constructor).newInstance(args);
@@ -346,11 +348,36 @@ public static T newInstance(Constructor constructor, Object... args) {
}
}
+ /**
+ * Read the value of a potentially inaccessible field.
+ *
+ *
If the field does not exist, an exception occurs while reading it, or
+ * the value of the field is {@code null}, an empty {@link Optional} is
+ * returned.
+ *
+ * @param clazz the class where the field is declared; never {@code null}
+ * @param fieldName the name of the field; never {@code null} or empty
+ * @param instance the instance from where the value is to be read; may
+ * be {@code null} for a static field
+ */
+ public static Optional