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 readFieldValue(Class clazz, String fieldName, T instance) { + Preconditions.notNull(clazz, "Class must not be null"); + Preconditions.notBlank(fieldName, "Field name must not be null or blank"); + + try { + Field field = makeAccessible(clazz.getDeclaredField(fieldName)); + return Optional.ofNullable(field.get(instance)); + } + catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + return Optional.empty(); + } + } + /** * @see org.junit.platform.commons.support.ReflectionSupport#invokeMethod(Method, Object, Object...) */ public static Object invokeMethod(Method method, Object target, Object... args) { - Preconditions.notNull(method, "method must not be null"); + Preconditions.notNull(method, "Method must not be null"); Preconditions.condition((target != null || isStatic(method)), () -> String.format("Cannot invoke non-static method [%s] on a null target.", method.toGenericString())); @@ -373,11 +400,8 @@ public static Optional> loadClass(String name) { * Load a class by its primitive name or fully qualified name, * using the supplied {@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.). + *

See {@link org.junit.platform.commons.support.ReflectionSupport#loadClass(String)} + * for details on support for class names for arrays. * * @param name the name of the class to load; never {@code null} or blank * @param classLoader the {@code ClassLoader} to use; never {@code null} @@ -621,7 +645,7 @@ public static List> findNestedClasses(Class clazz, Predicate> candidates = new LinkedHashSet<>(); findNestedClasses(clazz, candidates); - return candidates.stream().filter(predicate).collect(toList()); + return candidates.stream().filter(predicate).collect(toUnmodifiableList()); } private static void findNestedClasses(Class clazz, Set> candidates) { @@ -629,13 +653,15 @@ private static void findNestedClasses(Class clazz, Set> candidates) return; } - // Search class hierarchy + // Candidates in current class candidates.addAll(Arrays.asList(clazz.getDeclaredClasses())); + + // Search class hierarchy findNestedClasses(clazz.getSuperclass(), candidates); // Search interface hierarchy - for (Class interfaceType : clazz.getInterfaces()) { - findNestedClasses(interfaceType, candidates); + for (Class ifc : clazz.getInterfaces()) { + findNestedClasses(ifc, candidates); } } @@ -664,7 +690,7 @@ public static Constructor getDeclaredConstructor(Class clazz) { } } - public static Optional getMethod(Class clazz, String methodName, Class... parameterTypes) { + static Optional getMethod(Class clazz, String methodName, Class... parameterTypes) { Preconditions.notNull(clazz, "Class must not be null"); Preconditions.notBlank(methodName, "Method name must not be null or blank"); @@ -676,28 +702,29 @@ public static Optional getMethod(Class clazz, String methodName, Clas } } - private static Class[] resolveParameterTypes(String parameterTypeNames) { + /** + * @see org.junit.platform.commons.support.ReflectionSupport#findMethod(Class, String, String) + */ + public static Optional findMethod(Class clazz, String methodName, String parameterTypeNames) { + return findMethod(clazz, methodName, resolveParameterTypes(clazz, methodName, parameterTypeNames)); + } + + private static Class[] resolveParameterTypes(Class clazz, String methodName, String parameterTypeNames) { if (StringUtils.isBlank(parameterTypeNames)) { return EMPTY_CLASS_ARRAY; } // @formatter:off return Arrays.stream(parameterTypeNames.split(",")) - .map(ReflectionUtils::loadRequiredParameterType) + .map(typeName -> loadRequiredParameterType(clazz, methodName, typeName)) .toArray(Class[]::new); // @formatter:on } - private static Class loadRequiredParameterType(String typeName) { + private static Class loadRequiredParameterType(Class clazz, String methodName, String typeName) { return loadClass(typeName).orElseThrow( - () -> new JUnitException(String.format("Failed to load parameter type [%s]", typeName))); - } - - /** - * @see org.junit.platform.commons.support.ReflectionSupport#findMethod(Class, String, String) - */ - public static Optional findMethod(Class clazz, String methodName, String parameterTypeNames) { - return findMethod(clazz, methodName, resolveParameterTypes(parameterTypeNames)); + () -> new JUnitException(String.format("Failed to load parameter type [%s] for method [%s] in class [%s].", + typeName, methodName, clazz.getName()))); } /** @@ -707,12 +734,9 @@ public static Optional findMethod(Class clazz, String methodName, Cla Preconditions.notNull(clazz, "Class must not be null"); Preconditions.notBlank(methodName, "Method name must not be null or blank"); - Class currentClass = clazz; - while (currentClass != null && currentClass != Object.class) { - + for (Class current = clazz; current != null && current != Object.class; current = current.getSuperclass()) { // Search for match in current type - Method[] methods = currentClass.isInterface() ? currentClass.getMethods() - : currentClass.getDeclaredMethods(); + List methods = current.isInterface() ? getMethods(current) : getDeclaredMethods(current, BOTTOM_UP); for (Method method : methods) { if (hasCompatibleSignature(method, methodName, parameterTypes)) { return Optional.of(method); @@ -720,15 +744,12 @@ public static Optional findMethod(Class clazz, String methodName, Cla } // Search for match in interfaces implemented by current type - for (Class ifc : currentClass.getInterfaces()) { + for (Class ifc : current.getInterfaces()) { Optional optional = findMethod(ifc, methodName, parameterTypes); if (optional.isPresent()) { return optional; } } - - // Search in class hierarchy - currentClass = currentClass.getSuperclass(); } return Optional.empty(); @@ -750,13 +771,6 @@ public static List findMethods(Class clazz, Predicate predica } /** - * Find all {@linkplain Method methods} of the supplied class or interface - * that match the specified {@code predicate}. - * - * @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} * @see org.junit.platform.commons.support.ReflectionSupport#findMethods(Class, Predicate, org.junit.platform.commons.support.HierarchyTraversalMode) */ public static List findMethods(Class clazz, Predicate predicate, @@ -775,14 +789,15 @@ public static List findMethods(Class clazz, Predicate predica } /** - * Return all methods in superclass hierarchy except from Object. + * Find all non-synthetic methods in the superclass and interface hierarchy, + * excluding Object. */ private static List findAllMethodsInHierarchy(Class clazz, HierarchyTraversalMode traversalMode) { Preconditions.notNull(clazz, "Class must not be null"); Preconditions.notNull(traversalMode, "HierarchyTraversalMode must not be null"); // @formatter:off - List localMethods = Arrays.stream(clazz.getDeclaredMethods()) + List localMethods = getDeclaredMethods(clazz, traversalMode).stream() .filter(method -> !method.isSynthetic()) .collect(toList()); List superclassMethods = getSuperclassMethods(clazz, traversalMode).stream() @@ -793,8 +808,6 @@ private static List findAllMethodsInHierarchy(Class clazz, HierarchyT .collect(toList()); // @formatter:on - localMethods.sort(ReflectionUtils::defaultMethodSorter); - List methods = new ArrayList<>(); if (traversalMode == TOP_DOWN) { methods.addAll(superclassMethods); @@ -809,7 +822,66 @@ private static List findAllMethodsInHierarchy(Class clazz, HierarchyT } /** - * Method comparator based upon JUnit4 org.junit.internal.MethodSorter implementation. + * Custom alternative to {@link Class#getMethods()} that sorts the methods + * and converts them to a mutable list. + */ + private static List getMethods(Class clazz) { + return toSortedMutableList(clazz.getMethods()); + } + + /** + * Custom alternative to {@link Class#getDeclaredMethods()} that sorts the + * methods and converts them to a mutable list. + * + *

In addition, the list returned by this method includes interface + * default methods which are either prepended or appended to the list of + * declared methods depending on the supplied traversal mode. + */ + private static List getDeclaredMethods(Class clazz, HierarchyTraversalMode traversalMode) { + // Note: getDefaultMethods() already sorts the methods, + List defaultMethods = getDefaultMethods(clazz); + List declaredMethods = toSortedMutableList(clazz.getDeclaredMethods()); + + // Take the traversal mode into account in order to retain the inherited + // nature of interface default methods. + if (traversalMode == BOTTOM_UP) { + declaredMethods.addAll(defaultMethods); + return declaredMethods; + } + else { + defaultMethods.addAll(declaredMethods); + return defaultMethods; + } + } + + /** + * Get a sorted, mutable list of all default methods present in interfaces + * implemented by the supplied class. + */ + private static List getDefaultMethods(Class clazz) { + List defaultMethods = new ArrayList<>(); + for (Class ifc : clazz.getInterfaces()) { + for (Method method : getMethods(ifc)) { + if (method.isDefault()) { + defaultMethods.add(method); + } + } + } + return defaultMethods; + } + + private static List toSortedMutableList(Method[] methods) { + // @formatter:off + return Arrays.stream(methods) + .sorted(ReflectionUtils::defaultMethodSorter) + // Use toCollection() instead of toList() to ensure list is mutable. + .collect(toCollection(ArrayList::new)); + // @formatter:on + } + + /** + * Method comparator based upon JUnit 4's {@code org.junit.internal.MethodSorter} + * implementation. */ private static int defaultMethodSorter(Method method1, Method method2) { String name1 = method1.getName(); @@ -824,54 +896,26 @@ private static int defaultMethodSorter(Method method1, Method method2) { return comparison; } - /** - * 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 readFieldValue(Class clazz, String fieldName, T instance) { - Preconditions.notNull(clazz, "Class must not be null"); - Preconditions.notBlank(fieldName, "Field name must not be null or blank"); - - try { - Field field = makeAccessible(clazz.getDeclaredField(fieldName)); - return Optional.ofNullable(field.get(instance)); - } - catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - return Optional.empty(); - } - } - private static List getInterfaceMethods(Class clazz, HierarchyTraversalMode traversalMode) { - Preconditions.notNull(clazz, "Class must not be null"); - Preconditions.notNull(traversalMode, "HierarchyTraversalMode must not be null"); - List allInterfaceMethods = new ArrayList<>(); for (Class ifc : clazz.getInterfaces()) { // @formatter:off - List localMethods = Arrays.stream(ifc.getDeclaredMethods()) + List localInterfaceMethods = getMethods(ifc).stream() .filter(m -> !isAbstract(m)) .collect(toList()); - List subInterfaceMethods = getInterfaceMethods(ifc, traversalMode).stream() - .filter(method -> !isMethodShadowedByLocalMethods(method, localMethods)) + List extendedInterfaceMethods = getInterfaceMethods(ifc, traversalMode).stream() + .filter(method -> !isMethodShadowedByLocalMethods(method, localInterfaceMethods)) .collect(toList()); // @formatter:on if (traversalMode == TOP_DOWN) { - allInterfaceMethods.addAll(subInterfaceMethods); + allInterfaceMethods.addAll(extendedInterfaceMethods); } - allInterfaceMethods.addAll(localMethods); + allInterfaceMethods.addAll(localInterfaceMethods); if (traversalMode == BOTTOM_UP) { - allInterfaceMethods.addAll(subInterfaceMethods); + allInterfaceMethods.addAll(extendedInterfaceMethods); } } return allInterfaceMethods; @@ -895,8 +939,9 @@ private static boolean isMethodShadowedBy(Method upper, Method lower) { /** * Determine if the supplied candidate method (typically a method higher in - * the type hierarchy) has a signature that is compatible with a method with - * the supplied values, taking method sub-signatures and generics into account. + * the type hierarchy) has a signature that is compatible with a method that + * has the supplied name and parameter types, taking method sub-signatures + * and generics into account. */ private static boolean hasCompatibleSignature(Method candidate, String methodName, Class[] parameterTypes) { if (!methodName.equals(candidate.getName())) { @@ -949,32 +994,15 @@ public static T makeAccessible(T object) { return object; } - /** - * Get the underlying cause of the supplied {@link Throwable}. - * - *

If the supplied {@code Throwable} is an instance of - * {@link InvocationTargetException}, this method will be invoked - * recursively with the underlying - * {@linkplain InvocationTargetException#getTargetException() target - * exception}; otherwise, this method simply returns the supplied - * {@code Throwable}. - */ - private static Throwable getUnderlyingCause(Throwable t) { - if (t instanceof InvocationTargetException) { - return getUnderlyingCause(((InvocationTargetException) t).getTargetException()); - } - return t; - } - /** * Return all classes and interfaces that can be used as assignment types * for instances of the specified {@link Class}, including itself. * - * @param clazz the {@code Class} to lookup + * @param clazz the {@code Class} to look up * @see Class#isAssignableFrom */ public static Set> getAllAssignmentCompatibleClasses(Class clazz) { - Preconditions.notNull(clazz, "class must not be null"); + Preconditions.notNull(clazz, "Class must not be null"); Set> result = new LinkedHashSet<>(); getAllAssignmentCompatibleClasses(clazz, result); @@ -992,10 +1020,21 @@ private static void getAllAssignmentCompatibleClasses(Class clazz, Set[] parameterTypes); + /** + * Get the underlying cause of the supplied {@link Throwable}. + * + *

If the supplied {@code Throwable} is an instance of + * {@link InvocationTargetException}, this method will be invoked + * recursively with the underlying + * {@linkplain InvocationTargetException#getTargetException() target + * exception}; otherwise, this method simply returns the supplied + * {@code Throwable}. + */ + private static Throwable getUnderlyingCause(Throwable t) { + if (t instanceof InvocationTargetException) { + return getUnderlyingCause(((InvocationTargetException) t).getTargetException()); + } + return t; } } diff --git a/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java b/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java index 50575afe1b7..99b7e1bd554 100644 --- a/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java +++ b/platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java @@ -38,7 +38,6 @@ import java.util.Optional; import java.util.Set; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.extensions.TempDirectory; @@ -890,8 +889,6 @@ void findMethodsWithShadowingUsingHierarchyDownMode() throws Exception { MethodShadowingChild.class.getMethod("method5", Long.class)); } - // TODO [#976] Enable failing @Disabled test. - @Disabled("Disabled until #976 is resolved") @Test void findMethodsReturnsAllOverloadedMethodsThatAreNotShadowed() throws Exception { Class clazz = InterfaceWithGenericDefaultMethodImpl.class; @@ -909,7 +906,7 @@ void findMethodsReturnsAllOverloadedMethodsThatAreNotShadowed() throws Exception // default method would be "foo(java.lang.Long)" when looked up via the // concrete parameterized class, but it apparently is only _visible_ as // "foo(java.lang.Number)" via reflection. - assertThat(signatures).containsExactlyInAnyOrder("foo(java.lang.Number)", "foo(java.lang.Double)"); + assertThat(signatures).containsExactly("foo(java.lang.Number)", "foo(java.lang.Double)"); } @Test