mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8347826: Introspector shows wrong method list after 8071693
Reviewed-by: azvegint, serb, aivanov
This commit is contained in:
parent
832c5b06e8
commit
c5f235c000
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,10 +31,11 @@ import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@ -105,13 +106,16 @@ final class MethodInfo {
|
||||
}
|
||||
}
|
||||
|
||||
// Add default methods inherited from interfaces
|
||||
for (Class<?> iface : type.getInterfaces()) {
|
||||
// Add methods inherited from interfaces
|
||||
Deque<Class<?>> ifaceDeque = new ArrayDeque<>(List.of(type.getInterfaces()));
|
||||
while (!ifaceDeque.isEmpty()) {
|
||||
Class<?> iface = ifaceDeque.removeLast();
|
||||
if (IGNORABLE_INTERFACES.contains(iface)) {
|
||||
continue;
|
||||
}
|
||||
ifaceDeque.addAll(List.of(iface.getInterfaces()));
|
||||
for (Method method : iface.getMethods()) {
|
||||
if (!Modifier.isAbstract(method.getModifiers())) {
|
||||
if (!Modifier.isAbstract(method.getModifiers()) && !method.isBridge()) {
|
||||
(list = createIfNeeded(list)).add(method);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -78,7 +78,8 @@ public final class PropertyInfo {
|
||||
}
|
||||
if (!isInitedToIsGetter && this.readList != null) {
|
||||
for (MethodInfo info : this.readList) {
|
||||
if ((this.read == null) || this.read.type.isAssignableFrom(info.type)) {
|
||||
if ((this.read == null) || (!info.method.isDefault()
|
||||
&& this.read.type.isAssignableFrom(info.type))) {
|
||||
this.read = info;
|
||||
this.type = info.type;
|
||||
}
|
||||
@ -91,6 +92,9 @@ public final class PropertyInfo {
|
||||
if (writeType == null) {
|
||||
this.write = info;
|
||||
writeType = info.type;
|
||||
} else if (isParentOfIncoming(this.write, info)) {
|
||||
this.write = info;
|
||||
writeType = info.type;
|
||||
} else if (writeType.isAssignableFrom(info.type)) {
|
||||
if ((this.write == null) || this.write.type.isAssignableFrom(info.type)) {
|
||||
this.write = info;
|
||||
@ -307,4 +311,16 @@ public final class PropertyInfo {
|
||||
? Collections.unmodifiableMap(map)
|
||||
: Collections.emptyMap();
|
||||
}
|
||||
|
||||
private static boolean isParentOfIncoming(MethodInfo current, MethodInfo incoming) {
|
||||
if (null == current) {
|
||||
return false;
|
||||
}
|
||||
Class<?> currentClass = current.method.getDeclaringClass();
|
||||
Class<?> incomingClass = incoming.method.getDeclaringClass();
|
||||
if (currentClass == incomingClass) {
|
||||
return false;
|
||||
}
|
||||
return currentClass.isAssignableFrom(incomingClass);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -1052,8 +1052,12 @@ public class Introspector {
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
MethodDescriptor composite = new MethodDescriptor(old, md);
|
||||
methods.put(name, composite);
|
||||
Class<?> oldClass = old.getMethod().getDeclaringClass();
|
||||
Class<?> mdClass = md.getMethod().getDeclaringClass();
|
||||
if (oldClass == mdClass || oldClass.isAssignableFrom(mdClass) || !mdClass.isAssignableFrom(oldClass)) {
|
||||
MethodDescriptor composite = new MethodDescriptor(old, md);
|
||||
methods.put(name, composite);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,20 +23,26 @@
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8071693
|
||||
* @bug 8071693 8347826
|
||||
* @summary Verify that the Introspector finds default methods inherited
|
||||
* from interfaces
|
||||
*/
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.MethodDescriptor;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.beans.SimpleBeanInfo;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class DefaultMethodBeanPropertyTest {
|
||||
|
||||
@ -78,11 +84,17 @@ public class DefaultMethodBeanPropertyTest {
|
||||
}
|
||||
|
||||
public static void testScenario1() {
|
||||
verifyMethods(D1.class,
|
||||
"public static int DefaultMethodBeanPropertyTest$A1.getStaticValue()",
|
||||
"public default int DefaultMethodBeanPropertyTest$A1.getValue()",
|
||||
"public java.lang.Integer DefaultMethodBeanPropertyTest$D1.getFoo()",
|
||||
"public java.lang.Float DefaultMethodBeanPropertyTest$D1.getObj()"
|
||||
);
|
||||
verifyProperties(D1.class,
|
||||
"getClass", // inherited method
|
||||
"getValue", // inherited default method
|
||||
"getFoo", // overridden interface method
|
||||
"getObj" // overridden default method
|
||||
"public final native java.lang.Class java.lang.Object.getClass()",
|
||||
"public default int DefaultMethodBeanPropertyTest$A1.getValue()",
|
||||
"public java.lang.Integer DefaultMethodBeanPropertyTest$D1.getFoo()",
|
||||
"public java.lang.Float DefaultMethodBeanPropertyTest$D1.getObj()"
|
||||
);
|
||||
}
|
||||
|
||||
@ -108,9 +120,12 @@ public class DefaultMethodBeanPropertyTest {
|
||||
}
|
||||
|
||||
public static void testScenario2() {
|
||||
verifyMethods(D2.class,
|
||||
"public default java.lang.Object DefaultMethodBeanPropertyTest$A2.getFoo()"
|
||||
);
|
||||
verifyProperties(D2.class,
|
||||
"getClass",
|
||||
"getFoo"
|
||||
"public final native java.lang.Class java.lang.Object.getClass()",
|
||||
"public default java.lang.Object DefaultMethodBeanPropertyTest$A2.getFoo()"
|
||||
);
|
||||
}
|
||||
|
||||
@ -144,60 +159,404 @@ public class DefaultMethodBeanPropertyTest {
|
||||
}
|
||||
|
||||
public static void testScenario3() {
|
||||
verifyProperties(D3.class,
|
||||
"getClass",
|
||||
"getFoo"
|
||||
verifyMethods(D3.class,
|
||||
"public java.util.NavigableSet DefaultMethodBeanPropertyTest$D3.getFoo()"
|
||||
);
|
||||
verifyProperties(D3.class,
|
||||
"public final native java.lang.Class java.lang.Object.getClass()",
|
||||
"public java.util.NavigableSet DefaultMethodBeanPropertyTest$D3.getFoo()"
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// //
|
||||
// SCENARIO 4 //
|
||||
// //
|
||||
//////////////////////////////////////
|
||||
|
||||
public interface A4 {
|
||||
default Object getDefault0() {
|
||||
return null;
|
||||
}
|
||||
default Object getDefault1() {
|
||||
return null;
|
||||
}
|
||||
default Object getDefault2() {
|
||||
return null;
|
||||
}
|
||||
default Object getDefault3() {
|
||||
return null;
|
||||
}
|
||||
Object getNonDefault();
|
||||
}
|
||||
|
||||
public class B4 implements A4 {
|
||||
@Override
|
||||
public Object getDefault1() {
|
||||
return new B4();
|
||||
}
|
||||
@Override
|
||||
public String getDefault2() {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Float getDefault3() {
|
||||
return null;
|
||||
}
|
||||
public Long getNonDefault() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void testScenario4() {
|
||||
verifyMethods(B4.class,
|
||||
"public default java.lang.Object DefaultMethodBeanPropertyTest$A4.getDefault0()",
|
||||
"public java.lang.Object DefaultMethodBeanPropertyTest$B4.getDefault1()",
|
||||
"public java.lang.String DefaultMethodBeanPropertyTest$B4.getDefault2()",
|
||||
"public java.lang.Float DefaultMethodBeanPropertyTest$B4.getDefault3()",
|
||||
"public java.lang.Long DefaultMethodBeanPropertyTest$B4.getNonDefault()"
|
||||
);
|
||||
verifyProperties(B4.class,
|
||||
"public final native java.lang.Class java.lang.Object.getClass()",
|
||||
"public default java.lang.Object DefaultMethodBeanPropertyTest$A4.getDefault0()",
|
||||
"public java.lang.Object DefaultMethodBeanPropertyTest$B4.getDefault1()",
|
||||
"public java.lang.String DefaultMethodBeanPropertyTest$B4.getDefault2()",
|
||||
"public java.lang.Float DefaultMethodBeanPropertyTest$B4.getDefault3()",
|
||||
"public java.lang.Long DefaultMethodBeanPropertyTest$B4.getNonDefault()"
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// //
|
||||
// SCENARIO 5 //
|
||||
// //
|
||||
//////////////////////////////////////
|
||||
|
||||
public interface A5 {
|
||||
public default void setParentFoo(Integer num) {
|
||||
}
|
||||
public default void setFoo(String num) {
|
||||
}
|
||||
public static int getStaticValue() {
|
||||
return 0;
|
||||
}
|
||||
private int getPrivateValue() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class B5 implements A5 {
|
||||
public void setFoo(Number num) {
|
||||
}
|
||||
public void setLocalFoo(Long num) {
|
||||
}
|
||||
public static int getStaticValue() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void testScenario5() {
|
||||
verifyMethods(B5.class,
|
||||
"public static int DefaultMethodBeanPropertyTest$B5.getStaticValue()",
|
||||
"public default void DefaultMethodBeanPropertyTest$A5.setFoo(java.lang.String)",
|
||||
"public default void DefaultMethodBeanPropertyTest$A5.setParentFoo(java.lang.Integer)",
|
||||
"public void DefaultMethodBeanPropertyTest$B5.setFoo(java.lang.Number)",
|
||||
"public void DefaultMethodBeanPropertyTest$B5.setLocalFoo(java.lang.Long)"
|
||||
);
|
||||
verifyProperties(B5.class,
|
||||
"public final native java.lang.Class java.lang.Object.getClass()",
|
||||
"public default void DefaultMethodBeanPropertyTest$A5.setParentFoo(java.lang.Integer)",
|
||||
"public void DefaultMethodBeanPropertyTest$B5.setFoo(java.lang.Number)",
|
||||
"public void DefaultMethodBeanPropertyTest$B5.setLocalFoo(java.lang.Long)"
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// //
|
||||
// SCENARIO 6 //
|
||||
// //
|
||||
//////////////////////////////////////
|
||||
|
||||
public class A6 {
|
||||
public void setParentFoo(Integer num) {
|
||||
}
|
||||
public void setFoo(Integer num) {
|
||||
}
|
||||
public static int getStaticValue() {
|
||||
return 0;
|
||||
}
|
||||
private int getPrivateValue() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class B6 extends A6 {
|
||||
public void setFoo(String num) {
|
||||
}
|
||||
public void setLocalFoo(Long num) {
|
||||
}
|
||||
public static int getStaticValue() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void testScenario6() {
|
||||
verifyMethods(B6.class,
|
||||
"public static int DefaultMethodBeanPropertyTest$B6.getStaticValue()",
|
||||
"public void DefaultMethodBeanPropertyTest$A6.setFoo(java.lang.Integer)",
|
||||
"public void DefaultMethodBeanPropertyTest$A6.setParentFoo(java.lang.Integer)",
|
||||
"public void DefaultMethodBeanPropertyTest$B6.setFoo(java.lang.String)",
|
||||
"public void DefaultMethodBeanPropertyTest$B6.setLocalFoo(java.lang.Long)"
|
||||
);
|
||||
verifyProperties(B6.class,
|
||||
"public final native java.lang.Class java.lang.Object.getClass()",
|
||||
"public void DefaultMethodBeanPropertyTest$A6.setParentFoo(java.lang.Integer)",
|
||||
"public void DefaultMethodBeanPropertyTest$B6.setFoo(java.lang.String)",
|
||||
"public void DefaultMethodBeanPropertyTest$B6.setLocalFoo(java.lang.Long)"
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// //
|
||||
// SCENARIO 7 //
|
||||
// //
|
||||
//////////////////////////////////////
|
||||
|
||||
interface A7<T> {
|
||||
T getValue();
|
||||
}
|
||||
|
||||
interface B7 {
|
||||
Runnable getValue();
|
||||
}
|
||||
|
||||
interface AB7 extends B7, A7<Object> {
|
||||
Runnable getValue();
|
||||
}
|
||||
|
||||
abstract class D7 implements AB7 {
|
||||
public void setValue(Runnable value) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void testScenario7() {
|
||||
verifyMethods(D7.class,
|
||||
"public void DefaultMethodBeanPropertyTest$D7.setValue(java.lang.Runnable)"
|
||||
);
|
||||
verifyProperties(D7.class,
|
||||
"public final native java.lang.Class java.lang.Object.getClass()",
|
||||
"public void DefaultMethodBeanPropertyTest$D7.setValue(java.lang.Runnable)"
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// //
|
||||
// SCENARIO 8 //
|
||||
// //
|
||||
//////////////////////////////////////
|
||||
|
||||
public interface A8 {
|
||||
public default void setFoo(Float num) {
|
||||
}
|
||||
public default void setFoo2(Integer num) {
|
||||
}
|
||||
}
|
||||
public interface B8 extends A8 {
|
||||
public default void setFoo(Integer num) {
|
||||
}
|
||||
public default void setFoo2(Float num) {
|
||||
}
|
||||
}
|
||||
|
||||
public class C8 implements B8 {
|
||||
}
|
||||
|
||||
public static void testScenario8() {
|
||||
verifyMethods(C8.class,
|
||||
"public default void DefaultMethodBeanPropertyTest$A8.setFoo(java.lang.Float)",
|
||||
"public default void DefaultMethodBeanPropertyTest$A8.setFoo2(java.lang.Integer)",
|
||||
"public default void DefaultMethodBeanPropertyTest$B8.setFoo(java.lang.Integer)",
|
||||
"public default void DefaultMethodBeanPropertyTest$B8.setFoo2(java.lang.Float)"
|
||||
);
|
||||
verifyProperties(C8.class,
|
||||
"public final native java.lang.Class java.lang.Object.getClass()",
|
||||
"public default void DefaultMethodBeanPropertyTest$B8.setFoo(java.lang.Integer)",
|
||||
"public default void DefaultMethodBeanPropertyTest$B8.setFoo2(java.lang.Float)"
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// //
|
||||
// SCENARIO 9 //
|
||||
// //
|
||||
//////////////////////////////////////
|
||||
|
||||
public class A9 {
|
||||
public void setFoo(Object value) {
|
||||
}
|
||||
public void setFoo(String value) {
|
||||
}
|
||||
public void setFoo2(Object value) {
|
||||
}
|
||||
public void setFoo2(Integer value) {
|
||||
}
|
||||
// For the same setters with inconvertible arg types PropertyInfo behavior is undefined.
|
||||
// public void setLocalFoo3(Long num) { }
|
||||
// public void setLocalFoo3(Float num) { }
|
||||
}
|
||||
|
||||
public static void testScenario9() {
|
||||
verifyMethods(A9.class,
|
||||
"public void DefaultMethodBeanPropertyTest$A9.setFoo(java.lang.String)",
|
||||
"public void DefaultMethodBeanPropertyTest$A9.setFoo(java.lang.Object)",
|
||||
"public void DefaultMethodBeanPropertyTest$A9.setFoo2(java.lang.Integer)",
|
||||
"public void DefaultMethodBeanPropertyTest$A9.setFoo2(java.lang.Object)"
|
||||
);
|
||||
verifyProperties(A9.class,
|
||||
"public final native java.lang.Class java.lang.Object.getClass()",
|
||||
"public void DefaultMethodBeanPropertyTest$A9.setFoo(java.lang.String)",
|
||||
"public void DefaultMethodBeanPropertyTest$A9.setFoo2(java.lang.Integer)"
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
// //
|
||||
// SCENARIO 10 //
|
||||
// //
|
||||
//////////////////////////////////////
|
||||
|
||||
public static class A10 {
|
||||
public Object getProp() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface B10 {
|
||||
Object getProp();
|
||||
}
|
||||
|
||||
public static class C10_1 extends A10 implements B10 {
|
||||
}
|
||||
|
||||
public static class C10_2 extends A10 implements B10 {
|
||||
}
|
||||
|
||||
public static class A10BeanInfo extends SimpleBeanInfo {
|
||||
public MethodDescriptor[] getMethodDescriptors() {
|
||||
try {
|
||||
Class params[] = {};
|
||||
MethodDescriptor md = new MethodDescriptor(A10.class.getDeclaredMethod("getProp", params));
|
||||
md.setDisplayName("display name");
|
||||
MethodDescriptor res[] = { md };
|
||||
return res;
|
||||
} catch (Exception exception) {
|
||||
throw new Error("unexpected exception", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class C10_1BeanInfo extends SimpleBeanInfo {
|
||||
public BeanInfo[] getAdditionalBeanInfo() {
|
||||
try {
|
||||
BeanInfo res[] = {
|
||||
Introspector.getBeanInfo(A10.class),
|
||||
Introspector.getBeanInfo(B10.class)
|
||||
};
|
||||
return res;
|
||||
} catch (IntrospectionException exception) {
|
||||
throw new Error("unexpected exception", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class C10_2BeanInfo extends SimpleBeanInfo {
|
||||
public BeanInfo[] getAdditionalBeanInfo() {
|
||||
try {
|
||||
BeanInfo res[] = {
|
||||
Introspector.getBeanInfo(B10.class),
|
||||
Introspector.getBeanInfo(A10.class)
|
||||
};
|
||||
return res;
|
||||
} catch (IntrospectionException exception) {
|
||||
throw new Error("unexpected exception", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void testScenario10() {
|
||||
{
|
||||
var md = getMethodDescriptor(C10_1.class, A10.class, "getProp");
|
||||
assertEquals("display name", md.getDisplayName(), "getDisplayName()");
|
||||
}
|
||||
{
|
||||
var md = getMethodDescriptor(C10_2.class, A10.class, "getProp");
|
||||
assertEquals("display name", md.getDisplayName(), "getDisplayName()");
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
public static void verifyProperties(Class<?> type, String... getterNames) {
|
||||
|
||||
// Gather expected properties
|
||||
final HashSet<PropertyDescriptor> expected = new HashSet<>();
|
||||
for (String methodName : getterNames) {
|
||||
final String suffix = methodName.substring(3);
|
||||
final String propName = Introspector.decapitalize(suffix);
|
||||
final Method getter;
|
||||
try {
|
||||
getter = type.getMethod(methodName);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new Error("unexpected error", e);
|
||||
}
|
||||
final PropertyDescriptor propDesc;
|
||||
try {
|
||||
propDesc = new PropertyDescriptor(propName, getter, null);
|
||||
} catch (IntrospectionException e) {
|
||||
throw new Error("unexpected error", e);
|
||||
}
|
||||
expected.add(propDesc);
|
||||
}
|
||||
|
||||
// Verify properties can be found directly
|
||||
expected.stream()
|
||||
.map(PropertyDescriptor::getName)
|
||||
.filter(name -> BeanUtils.getPropertyDescriptor(type, name) == null)
|
||||
.findFirst()
|
||||
.ifPresent(name -> {
|
||||
throw new Error("property \"" + name + "\" not found in " + type);
|
||||
});
|
||||
|
||||
// Gather actual properties
|
||||
final Set<PropertyDescriptor> actual =
|
||||
Set.of(BeanUtils.getPropertyDescriptors(type));
|
||||
|
||||
// Verify the two sets are the same
|
||||
private static void verifyEquality(String title, Set<String> expected, Set<String> actual) {
|
||||
if (!actual.equals(expected)) {
|
||||
throw new Error("mismatch: " + type
|
||||
+ "\nACTUAL:\n "
|
||||
+ actual.stream()
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.joining("\n "))
|
||||
+ "\nEXPECTED:\n "
|
||||
+ expected.stream()
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.joining("\n ")));
|
||||
throw new Error(title + " mismatch: "
|
||||
+ "\nACTUAL:\n "
|
||||
+ actual.stream()
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.joining("\n "))
|
||||
+ "\nEXPECTED:\n "
|
||||
+ expected.stream()
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.joining("\n ")));
|
||||
}
|
||||
}
|
||||
|
||||
public static void verifyProperties(Class<?> type, String... methodNames) {
|
||||
try {
|
||||
final Set<String> expected = new HashSet<>(Arrays.asList(methodNames));
|
||||
final Set<String> actual = Arrays
|
||||
.stream(Introspector.getBeanInfo(type)
|
||||
.getPropertyDescriptors())
|
||||
.flatMap(pd -> Stream.of(pd.getReadMethod(), pd.getWriteMethod()))
|
||||
.filter(Objects::nonNull)
|
||||
.map((Method m) -> m.toString())
|
||||
.collect(Collectors.toSet());
|
||||
verifyEquality("properties", expected, actual);
|
||||
} catch (IntrospectionException exception) {
|
||||
throw new Error("unexpected exception", exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static void verifyMethods(Class<?> type, String... methodNames) {
|
||||
try {
|
||||
final Set<String> expected = new HashSet<>(Arrays.asList(methodNames));
|
||||
final Set<String> actual = Arrays
|
||||
.stream(Introspector.getBeanInfo(type, Object.class)
|
||||
.getMethodDescriptors())
|
||||
.map(MethodDescriptor::getMethod)
|
||||
.map(Method::toString)
|
||||
.collect(Collectors.toSet());
|
||||
verifyEquality("methods", expected, actual);
|
||||
} catch (IntrospectionException exception) {
|
||||
throw new Error("unexpected exception", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodDescriptor getMethodDescriptor(Class cls, Class stop, String name) {
|
||||
try {
|
||||
for (var md : Introspector.getBeanInfo(cls, stop).getMethodDescriptors()) {
|
||||
if (md.getName().equals(name)) {
|
||||
return md;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (IntrospectionException exception) {
|
||||
throw new Error("unexpected exception", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertEquals(Object expected, Object actual, String msg) {
|
||||
if (!expected.equals(actual)) {
|
||||
throw new Error(msg + ":\nACTUAL: " + actual + "\nEXPECTED: " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,5 +566,12 @@ public class DefaultMethodBeanPropertyTest {
|
||||
testScenario1();
|
||||
testScenario2();
|
||||
testScenario3();
|
||||
testScenario4();
|
||||
testScenario5();
|
||||
testScenario6();
|
||||
testScenario7();
|
||||
testScenario8();
|
||||
testScenario9();
|
||||
testScenario10();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user