package net.bytebuddy.implementation.bind.annotation; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeList; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.bind.MethodDelegationBinder; import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.test.utility.ObjectPropertyAssertion; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import java.io.Serializable; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class DefaultCallBinderTest extends AbstractAnnotationBinderTest<DefaultCall> { private static final Class<?> NON_INTERFACE_TYPE = Object.class, INTERFACE_TYPE = Serializable.class, VOID_TYPE = void.class; @Mock private TypeDescription targetParameterType, firstInterface, secondInterface; @Mock private TypeDescription.Generic genericTargetParameterType, firstGenericInterface, secondGenericInterface; @Mock private MethodDescription.SignatureToken token; @Mock private Implementation.SpecialMethodInvocation specialMethodInvocation; public DefaultCallBinderTest() { super(DefaultCall.class); } @Override @Before public void setUp() throws Exception { super.setUp(); when(target.getType()).thenReturn(genericTargetParameterType); when(genericTargetParameterType.asErasure()).thenReturn(targetParameterType); when(implementationTarget.invokeDefault(token)).thenReturn(specialMethodInvocation); when(implementationTarget.invokeDefault(eq(token), any(TypeDescription.class))).thenReturn(specialMethodInvocation); when(firstGenericInterface.asErasure()).thenReturn(firstInterface); when(secondGenericInterface.asErasure()).thenReturn(secondInterface); when(firstInterface.asGenericType()).thenReturn(firstGenericInterface); when(secondInterface.asGenericType()).thenReturn(secondGenericInterface); } @Override protected TargetMethodAnnotationDrivenBinder.ParameterBinder<DefaultCall> getSimpleBinder() { return DefaultCall.Binder.INSTANCE; } @Test public void testImplicitLookupIsUnique() throws Exception { when(targetParameterType.represents(any(Class.class))).thenReturn(true); when(specialMethodInvocation.isValid()).thenReturn(true, false); doReturn(VOID_TYPE).when(annotation).targetType(); when(source.asSignatureToken()).thenReturn(token); MethodDelegationBinder.ParameterBinding<?> parameterBinding = DefaultCall.Binder.INSTANCE .bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(parameterBinding.isValid(), is(true)); verify(implementationTarget).invokeDefault(token); verifyNoMoreInteractions(implementationTarget); } @Test public void testImplicitLookupIsAmbiguousNullFallback() throws Exception { when(targetParameterType.represents(any(Class.class))).thenReturn(true); when(specialMethodInvocation.isValid()).thenReturn(true, false); doReturn(VOID_TYPE).when(annotation).targetType(); when(source.asSignatureToken()).thenReturn(token); when(source.isSpecializableFor(firstInterface)).thenReturn(true); when(source.isSpecializableFor(secondInterface)).thenReturn(true); when(instrumentedType.getInterfaces()).thenReturn(new TypeList.Generic.Explicit(firstInterface, secondInterface)); when(annotation.nullIfImpossible()).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> parameterBinding = DefaultCall.Binder.INSTANCE .bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(parameterBinding.isValid(), is(true)); verify(implementationTarget).invokeDefault(token); verifyNoMoreInteractions(implementationTarget); } @Test public void testExplicitLookup() throws Exception { when(targetParameterType.represents(any(Class.class))).thenReturn(true); when(specialMethodInvocation.isValid()).thenReturn(true); doReturn(INTERFACE_TYPE).when(annotation).targetType(); when(source.asSignatureToken()).thenReturn(token); MethodDelegationBinder.ParameterBinding<?> parameterBinding = DefaultCall.Binder.INSTANCE .bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(parameterBinding.isValid(), is(true)); verify(implementationTarget).invokeDefault(token, new TypeDescription.ForLoadedType(INTERFACE_TYPE)); verifyNoMoreInteractions(implementationTarget); } @Test(expected = IllegalStateException.class) public void testNonInterfaceTarget() throws Exception { when(targetParameterType.represents(any(Class.class))).thenReturn(true); doReturn(NON_INTERFACE_TYPE).when(annotation).targetType(); DefaultCall.Binder.INSTANCE.bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); } @Test(expected = IllegalStateException.class) public void testIllegalAnnotatedValue() throws Exception { DefaultCall.Binder.INSTANCE.bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); } @Test public void testConstructorIsNotInvokeable() throws Exception { when(targetParameterType.represents(any(Class.class))).thenReturn(true); when(source.isConstructor()).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> parameterBinding = DefaultCall.Binder.INSTANCE .bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); verifyZeroInteractions(implementationTarget); assertThat(parameterBinding.isValid(), is(false)); } @Test public void testConstructorNullFallback() throws Exception { when(targetParameterType.represents(any(Class.class))).thenReturn(true); when(source.isConstructor()).thenReturn(true); when(annotation.nullIfImpossible()).thenReturn(true); MethodDelegationBinder.ParameterBinding<?> parameterBinding = DefaultCall.Binder.INSTANCE .bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); verifyZeroInteractions(implementationTarget); assertThat(parameterBinding.isValid(), is(true)); } @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(DefaultCall.Binder.class).apply(); ObjectPropertyAssertion.of(DefaultCall.Binder.DefaultMethodLocator.Implicit.class).apply(); ObjectPropertyAssertion.of(DefaultCall.Binder.DefaultMethodLocator.Explicit.class).apply(); } }