package net.bytebuddy.implementation.bind.annotation; import net.bytebuddy.description.field.FieldDescription; import net.bytebuddy.description.field.FieldList; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.ParameterDescription; import net.bytebuddy.description.method.ParameterList; import net.bytebuddy.description.type.TypeDefinition; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.MethodAccessorFactory; import net.bytebuddy.implementation.bind.MethodDelegationBinder; import net.bytebuddy.implementation.bytecode.StackSize; import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.test.utility.ObjectPropertyAssertion; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class FieldProxyBinderTest extends AbstractAnnotationBinderTest<FieldProxy> { private static final String FOO = "foo"; @Mock private MethodDescription.InDefinedShape getterMethod, setterMethod; @Mock private TypeDescription setterType, getterType, fieldType; @Mock private TypeDescription.Generic genericSetterType, genericGetterType, genericFieldType; @Mock private FieldDescription.InDefinedShape fieldDescription; public FieldProxyBinderTest() { super(FieldProxy.class); } @Override @Before public void setUp() throws Exception { super.setUp(); when(getterMethod.getDeclaringType()).thenReturn(getterType); when(setterMethod.getDeclaringType()).thenReturn(setterType); when(instrumentedType.getDeclaredFields()).thenReturn(new FieldList.Explicit<FieldDescription.InDefinedShape>(fieldDescription)); when(fieldDescription.getType()).thenReturn(genericFieldType); when(genericFieldType.getSort()).thenReturn(TypeDefinition.Sort.NON_GENERIC); when(genericFieldType.getStackSize()).thenReturn(StackSize.ZERO); when(genericFieldType.asErasure()).thenReturn(fieldType); when(fieldType.getSort()).thenReturn(TypeDefinition.Sort.NON_GENERIC); when(fieldType.asErasure()).thenReturn(fieldType); when(fieldType.getInternalName()).thenReturn(FOO); when(genericSetterType.asErasure()).thenReturn(setterType); when(genericGetterType.asErasure()).thenReturn(getterType); } @Override protected TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldProxy> getSimpleBinder() { return new FieldProxy.Binder(getterMethod, setterMethod); } @Test(expected = IllegalStateException.class) public void testFieldOfArrayThrowsException() throws Exception { doReturn(Object[].class).when(annotation).declaringType(); new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); } @Test(expected = IllegalStateException.class) public void testFieldOfPrimitiveThrowsException() throws Exception { doReturn(int.class).when(annotation).declaringType(); new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); } @Test(expected = IllegalStateException.class) public void testIllegalType() throws Exception { doReturn(Foo.class).when(annotation).declaringType(); when(annotation.value()).thenReturn(FOO); TypeDescription targetType = mock(TypeDescription.class); TypeDescription.Generic genericTargetType = mock(TypeDescription.Generic.class); when(genericTargetType.asErasure()).thenReturn(targetType); when(target.getType()).thenReturn(genericTargetType); when(instrumentedType.isAssignableTo(new TypeDescription.ForLoadedType(Foo.class))).thenReturn(true); new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); } @Test public void testGetterForImplicitNamedFieldInHierarchy() throws Exception { when(target.getType()).thenReturn(genericGetterType); doReturn(void.class).when(annotation).declaringType(); when(annotation.value()).thenReturn(FieldProxy.Binder.BEAN_PROPERTY); when(fieldDescription.getActualName()).thenReturn(FOO); when(source.getReturnType()).thenReturn(genericFieldType); when(source.getParameters()).thenReturn(new ParameterList.Empty<ParameterDescription.InDefinedShape>()); when(source.getName()).thenReturn("getFoo"); when(source.getActualName()).thenReturn("getFoo"); when(source.getInternalName()).thenReturn("getFoo"); when(fieldDescription.isVisibleTo(instrumentedType)).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> binding = new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(binding.isValid(), is(true)); } @Test public void testGetterForExplicitNamedFieldInHierarchy() throws Exception { when(target.getType()).thenReturn(genericGetterType); doReturn(void.class).when(annotation).declaringType(); when(annotation.value()).thenReturn(FOO); when(fieldDescription.getActualName()).thenReturn(FOO); when(source.getReturnType()).thenReturn(genericFieldType); when(source.getParameters()).thenReturn(new ParameterList.Empty<ParameterDescription.InDefinedShape>()); when(source.getName()).thenReturn("getFoo"); when(source.getInternalName()).thenReturn("getFoo"); when(fieldDescription.isVisibleTo(instrumentedType)).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> binding = new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(binding.isValid(), is(true)); } @Test public void testGetterForImplicitNamedFieldInNamedType() throws Exception { when(target.getType()).thenReturn(genericGetterType); doReturn(Foo.class).when(annotation).declaringType(); when(instrumentedType.isAssignableTo(new TypeDescription.ForLoadedType(Foo.class))).thenReturn(true); when(annotation.value()).thenReturn(FieldProxy.Binder.BEAN_PROPERTY); when(fieldDescription.getInternalName()).thenReturn(FOO); when(source.getReturnType()).thenReturn(genericFieldType); when(source.getParameters()).thenReturn(new ParameterList.Empty<ParameterDescription.InDefinedShape>()); when(source.getName()).thenReturn("getFoo"); when(source.getActualName()).thenReturn("getFoo"); when(source.getInternalName()).thenReturn("getFoo"); when(fieldDescription.isVisibleTo(instrumentedType)).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> binding = new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(binding.isValid(), is(true)); } @Test public void testGetterForExplicitNamedFieldInNamedType() throws Exception { when(target.getType()).thenReturn(genericGetterType); doReturn(Foo.class).when(annotation).declaringType(); when(instrumentedType.isAssignableTo(new TypeDescription.ForLoadedType(Foo.class))).thenReturn(true); when(annotation.value()).thenReturn(FOO); when(fieldDescription.getInternalName()).thenReturn(FOO); when(source.getReturnType()).thenReturn(genericFieldType); when(source.getParameters()).thenReturn(new ParameterList.Empty<ParameterDescription.InDefinedShape>()); when(source.getName()).thenReturn("getFoo"); when(source.getInternalName()).thenReturn("getFoo"); when(fieldDescription.isVisibleTo(instrumentedType)).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> binding = new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(binding.isValid(), is(true)); } @Test public void testSetterForImplicitNamedFieldInHierarchy() throws Exception { when(target.getType()).thenReturn(genericSetterType); doReturn(void.class).when(annotation).declaringType(); when(annotation.value()).thenReturn(FieldProxy.Binder.BEAN_PROPERTY); when(fieldDescription.getActualName()).thenReturn(FOO); when(source.getReturnType()).thenReturn(TypeDescription.Generic.VOID); when(source.getParameters()).thenReturn(new ParameterList.Explicit.ForTypes(source, fieldType)); when(source.getActualName()).thenReturn("setFoo"); when(source.getInternalName()).thenReturn("setFoo"); when(fieldDescription.isVisibleTo(instrumentedType)).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> binding = new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(binding.isValid(), is(true)); } @Test public void testSetterForExplicitNamedFieldInHierarchy() throws Exception { when(target.getType()).thenReturn(genericSetterType); doReturn(void.class).when(annotation).declaringType(); when(annotation.value()).thenReturn(FOO); when(fieldDescription.getActualName()).thenReturn(FOO); when(source.getReturnType()).thenReturn(TypeDescription.Generic.VOID); when(source.getParameters()).thenReturn(new ParameterList.Explicit.ForTypes(source, fieldType)); when(source.getName()).thenReturn("setFoo"); when(source.getInternalName()).thenReturn("setFoo"); when(fieldDescription.isVisibleTo(instrumentedType)).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> binding = new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(binding.isValid(), is(true)); } @Test public void testSetterForImplicitNamedFieldInNamedType() throws Exception { when(target.getType()).thenReturn(genericSetterType); doReturn(Foo.class).when(annotation).declaringType(); when(instrumentedType.isAssignableTo(new TypeDescription.ForLoadedType(Foo.class))).thenReturn(true); when(annotation.value()).thenReturn(FieldProxy.Binder.BEAN_PROPERTY); when(fieldDescription.getActualName()).thenReturn(FOO); when(source.getReturnType()).thenReturn(TypeDescription.Generic.VOID); when(source.getParameters()).thenReturn(new ParameterList.Explicit.ForTypes(source, fieldType)); when(source.getName()).thenReturn("setFoo"); when(source.getActualName()).thenReturn("setFoo"); when(source.getInternalName()).thenReturn("setFoo"); when(fieldDescription.isVisibleTo(instrumentedType)).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> binding = new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(binding.isValid(), is(true)); } @Test public void testSetterForExplicitNamedFieldInNamedType() throws Exception { when(target.getType()).thenReturn(genericSetterType); doReturn(Foo.class).when(annotation).declaringType(); when(instrumentedType.isAssignableTo(new TypeDescription.ForLoadedType(Foo.class))).thenReturn(true); when(annotation.value()).thenReturn(FOO); when(fieldDescription.getActualName()).thenReturn(FOO); when(source.getReturnType()).thenReturn(TypeDescription.Generic.VOID); when(source.getParameters()).thenReturn(new ParameterList.Explicit.ForTypes(source, fieldType)); when(source.getName()).thenReturn("setFoo"); when(source.getInternalName()).thenReturn("setFoo"); when(fieldDescription.isVisibleTo(instrumentedType)).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> binding = new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(binding.isValid(), is(true)); } @Test public void testDefiningTypeNotAssignable() throws Exception { when(target.getType()).thenReturn(genericSetterType); doReturn(Foo.class).when(annotation).declaringType(); when(instrumentedType.isAssignableTo(new TypeDescription.ForLoadedType(Foo.class))).thenReturn(false); MethodDelegationBinder.ParameterBinding<?> binding = new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(binding.isValid(), is(false)); } @Test(expected = IllegalStateException.class) public void testDefiningTypePrimitive() throws Exception { when(target.getType()).thenReturn(genericSetterType); doReturn(int.class).when(annotation).declaringType(); new FieldProxy.Binder(getterMethod, setterMethod).bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); } @Test(expected = IllegalStateException.class) public void testUnresolvedResolverNoProxyType() throws Exception { FieldProxy.Binder.FieldResolver.Unresolved.INSTANCE.getProxyType(); } @Test(expected = IllegalStateException.class) public void testUnresolvedResolverNoApplication() throws Exception { FieldProxy.Binder.FieldResolver.Unresolved.INSTANCE.apply(mock(DynamicType.Builder.class), mock(FieldDescription.class), mock(Assigner.class), mock(MethodAccessorFactory.class)); } @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(FieldProxy.Binder.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.StaticFieldConstructor.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.InstanceFieldConstructor.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.InstanceFieldConstructor.Appender.class).refine(new ObjectPropertyAssertion.Refinement<Implementation.Target>() { @Override @SuppressWarnings("unchecked") public void apply(Implementation.Target mock) { TypeDescription typeDescription = mock(TypeDescription.class); when(mock.getInstrumentedType()).thenReturn(typeDescription); FieldList fieldList = mock(FieldList.class); FieldList filteredFieldList = mock(FieldList.class); when(typeDescription.getDeclaredFields()).thenReturn(fieldList); when(fieldList.filter(any(ElementMatcher.class))).thenReturn(filteredFieldList); when(filteredFieldList.getOnly()).thenReturn(mock(FieldDescription.class)); } }).skipSynthetic().apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.FieldResolver.ForGetter.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.FieldResolver.ForSetter.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.FieldResolver.ForGetterSetterPair.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.FieldResolver.Unresolved.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.FieldResolver.Factory.Simplex.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.FieldResolver.Factory.Duplex.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.AccessorProxy.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.FieldGetter.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.FieldGetter.Appender.class).refine(new ObjectPropertyAssertion.Refinement<Implementation.Target>() { @Override public void apply(Implementation.Target mock) { when(mock.getInstrumentedType()).thenReturn(mock(TypeDescription.class)); } }).skipSynthetic().apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.FieldSetter.class).apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.FieldSetter.Appender.class).refine(new ObjectPropertyAssertion.Refinement<Implementation.Target>() { @Override public void apply(Implementation.Target mock) { when(mock.getInstrumentedType()).thenReturn(mock(TypeDescription.class)); } }).skipSynthetic().apply(); ObjectPropertyAssertion.of(FieldProxy.Binder.AccessorProxy.class).apply(); } public static class Foo { public Foo foo; } }