From bf1832da8f6aa187c0dee5b458cceabd1148eeaf Mon Sep 17 00:00:00 2001 From: Johannes Link Date: Sun, 14 Apr 2024 11:18:15 +0200 Subject: [PATCH] Fixed bug 562 --- TODO.md | 3 -- .../support/JqwikReflectionSupport.java | 8 +++-- .../support/JqwikReflectionSupportTests.java | 36 +++++++++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/TODO.md b/TODO.md index 6c2f6605b..66cef7cf2 100644 --- a/TODO.md +++ b/TODO.md @@ -1,8 +1,5 @@ # 1.8.5 - - Fix class inheritance bug: https://github.com/jqwik-team/jqwik/issues/562 - - # 1.8.x - Replace jsr305 with jspecify diff --git a/engine/src/main/java/net/jqwik/engine/support/JqwikReflectionSupport.java b/engine/src/main/java/net/jqwik/engine/support/JqwikReflectionSupport.java index cbc767f87..4b1ebd038 100644 --- a/engine/src/main/java/net/jqwik/engine/support/JqwikReflectionSupport.java +++ b/engine/src/main/java/net/jqwik/engine/support/JqwikReflectionSupport.java @@ -102,6 +102,10 @@ public static T newInstance(Constructor constructor, Object... args) { * Find all {@linkplain Method methods} as in ReflectionSupport.findMethods(..) but also use outer classes to look for * methods. * + *

+ * Duplicate methods (through) inheritance are de-duplicated. The first occurrence of a method is kept. + *

+ * * @param clazz The class in which you start the search * @param predicate The condition to check for all candidate methods * @param traversalMode Traverse hierarchy up or down. Determines the order in resulting list. @@ -113,11 +117,11 @@ public static List findMethodsPotentiallyOuter( HierarchyTraversalMode traversalMode ) { List> searchClasses = getDeclaringClasses(clazz, traversalMode); - List foundMethods = new ArrayList<>(); + Set foundMethods = new LinkedHashSet<>(); for (Class searchClass : searchClasses) { foundMethods.addAll(ReflectionSupport.findMethods(searchClass, predicate, traversalMode)); } - return foundMethods; + return new ArrayList<>(foundMethods); } /** diff --git a/engine/src/test/java/net/jqwik/engine/support/JqwikReflectionSupportTests.java b/engine/src/test/java/net/jqwik/engine/support/JqwikReflectionSupportTests.java index 0969314a6..3410ec219 100644 --- a/engine/src/test/java/net/jqwik/engine/support/JqwikReflectionSupportTests.java +++ b/engine/src/test/java/net/jqwik/engine/support/JqwikReflectionSupportTests.java @@ -6,6 +6,8 @@ import java.util.function.*; import java.util.stream.*; +import org.junit.platform.commons.support.*; + import net.jqwik.api.*; import net.jqwik.api.constraints.*; @@ -182,6 +184,30 @@ void getFunctionMethod() { assertThat(JqwikReflectionSupport.getFunctionMethod(DataOutput.class)).isNotPresent(); } + @Example + void findMethodsPotentiallyOuter() { + List methods = JqwikReflectionSupport.findMethodsPotentiallyOuter( + AbstractClass.ConcreteSubclass.class, + method -> method.getName().startsWith("method"), + HierarchyTraversalMode.BOTTOM_UP + ); + assertThat(methods).hasSize(2); + + List methodsInSub = JqwikReflectionSupport.findMethodsPotentiallyOuter( + AbstractClass.ConcreteSubclass.class, + method -> method.getName().startsWith("methodInConcreteSubclass"), + HierarchyTraversalMode.BOTTOM_UP + ); + assertThat(methodsInSub).hasSize(1); + + List methodsInBase = JqwikReflectionSupport.findMethodsPotentiallyOuter( + AbstractClass.ConcreteSubclass.class, + method -> method.getName().startsWith("methodInAbstractClass"), + HierarchyTraversalMode.BOTTOM_UP + ); + assertThat(methodsInBase).hasSize(1); + } + private static class Outer { Inner createInner() { @@ -225,3 +251,13 @@ private static class OuterWithConstructor { } } + +abstract class AbstractClass { + + void methodInAbstractClass() {} + + static class ConcreteSubclass extends AbstractClass { + + void methodInConcreteSubclass() {} + } +} \ No newline at end of file