Skip to content

Commit 7ca15d9

Browse files
committed
Ensure ReflectionUtils.findFields() returns distinct fields
Closes #3646
1 parent 6aaa3f2 commit 7ca15d9

File tree

5 files changed

+51
-12
lines changed

5 files changed

+51
-12
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.11.0-M1.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ repository on GitHub.
1616
[[release-notes-5.11.0-M1-junit-platform-bug-fixes]]
1717
==== Bug Fixes
1818

19-
*
19+
* `ReflectionSupport.findFields(...)` now returns a distinct set of fields.
2020

2121
[[release-notes-5.11.0-M1-junit-platform-deprecations-and-breaking-changes]]
2222
==== Deprecations and Breaking Changes

junit-platform-commons/src/main/java/org/junit/platform/commons/support/AnnotationSupport.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ public static List<Field> findPublicAnnotatedFields(Class<?> clazz, Class<?> fie
262262
}
263263

264264
/**
265-
* Find all {@linkplain Field fields} of the supplied class or interface
266-
* that are annotated or <em>meta-annotated</em> with the specified
265+
* Find all distinct {@linkplain Field fields} of the supplied class or
266+
* interface that are annotated or <em>meta-annotated</em> with the specified
267267
* {@code annotationType}, using top-down search semantics within the type
268268
* hierarchy.
269269
*
@@ -289,8 +289,8 @@ public static List<Field> findAnnotatedFields(Class<?> clazz, Class<? extends An
289289
}
290290

291291
/**
292-
* Find all {@linkplain Field fields} of the supplied class or interface
293-
* that are annotated or <em>meta-annotated</em> with the specified
292+
* Find all distinct {@linkplain Field fields} of the supplied class or
293+
* interface that are annotated or <em>meta-annotated</em> with the specified
294294
* {@code annotationType} and match the specified {@code predicate}, using
295295
* top-down search semantics within the type hierarchy.
296296
*
@@ -318,8 +318,8 @@ public static List<Field> findAnnotatedFields(Class<?> clazz, Class<? extends An
318318
}
319319

320320
/**
321-
* Find all {@linkplain Field fields} of the supplied class or interface
322-
* that are annotated or <em>meta-annotated</em> with the specified
321+
* Find all distinct {@linkplain Field fields} of the supplied class or
322+
* interface that are annotated or <em>meta-annotated</em> with the specified
323323
* {@code annotationType} and match the specified {@code predicate}, using
324324
* the supplied hierarchy traversal mode.
325325
*

junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,8 @@ public static Object invokeMethod(Method method, Object target, Object... args)
298298
}
299299

300300
/**
301-
* Find all {@linkplain Field fields} of the supplied class or interface
302-
* that match the specified {@code predicate}.
301+
* Find all distinct {@linkplain Field fields} of the supplied class or
302+
* interface that match the specified {@code predicate}.
303303
*
304304
* <p>Fields declared in the same class or interface will be ordered using
305305
* an algorithm that is deterministic but intentionally nonobvious.
@@ -325,8 +325,8 @@ public static List<Field> findFields(Class<?> clazz, Predicate<Field> predicate,
325325
}
326326

327327
/**
328-
* Find all {@linkplain Field fields} of the supplied class or interface
329-
* that match the specified {@code predicate}.
328+
* Find all distinct {@linkplain Field fields} of the supplied class or
329+
* interface that match the specified {@code predicate}.
330330
*
331331
* <p>Fields declared in the same class or interface will be ordered using
332332
* an algorithm that is deterministic but intentionally nonobvious.

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,11 @@ public static Stream<Field> streamFields(Class<?> clazz, Predicate<Field> predic
12021202
Preconditions.notNull(predicate, "Predicate must not be null");
12031203
Preconditions.notNull(traversalMode, "HierarchyTraversalMode must not be null");
12041204

1205-
return findAllFieldsInHierarchy(clazz, traversalMode).stream().filter(predicate);
1205+
// @formatter:off
1206+
return findAllFieldsInHierarchy(clazz, traversalMode).stream()
1207+
.filter(predicate)
1208+
.distinct();
1209+
// @formatter:on
12061210
}
12071211

12081212
private static List<Field> findAllFieldsInHierarchy(Class<?> clazz, HierarchyTraversalMode traversalMode) {

platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,25 @@ void readFieldValuesFromClass() {
14061406
assertThat(values).containsExactly(2.5, "constant", 99);
14071407
}
14081408

1409+
/**
1410+
* @see https://github.com/junit-team/junit5/issues/3646
1411+
* @since 1.11
1412+
*/
1413+
@Test
1414+
void readFieldValuesFromInteracesAndClassesInTypeHierarchy() {
1415+
var fields = findFields(InterfaceWithField.class, ReflectionUtils::isStatic, TOP_DOWN);
1416+
var values = ReflectionUtils.readFieldValues(fields, null);
1417+
assertThat(values).containsOnly("ifc");
1418+
1419+
fields = findFields(SuperclassWithFieldAndFieldFromInterface.class, ReflectionUtils::isStatic, TOP_DOWN);
1420+
values = ReflectionUtils.readFieldValues(fields, null);
1421+
assertThat(values).containsExactly("ifc", "super");
1422+
1423+
fields = findFields(SubclassWithFieldAndFieldFromInterface.class, ReflectionUtils::isStatic, TOP_DOWN);
1424+
values = ReflectionUtils.readFieldValues(fields, null);
1425+
assertThat(values).containsExactly("ifc", "super", "sub");
1426+
}
1427+
14091428
@Test
14101429
void readFieldValuesFromInstanceWithTypeFilterForString() {
14111430
var fields = findFields(ClassWithFields.class, isA(String.class), TOP_DOWN);
@@ -1942,6 +1961,22 @@ public static class ClassWithFields {
19421961

19431962
}
19441963

1964+
interface InterfaceWithField {
1965+
1966+
String interfacePath = "ifc";
1967+
}
1968+
1969+
static class SuperclassWithFieldAndFieldFromInterface implements InterfaceWithField {
1970+
1971+
static final String superPath = "super";
1972+
}
1973+
1974+
static class SubclassWithFieldAndFieldFromInterface extends SuperclassWithFieldAndFieldFromInterface
1975+
implements InterfaceWithField {
1976+
1977+
static final String subPath = "sub";
1978+
}
1979+
19451980
@SuppressWarnings("unused")
19461981
private static class ClassWithOneCustomConstructor {
19471982

0 commit comments

Comments
 (0)