package net.bytebuddy.implementation.bind.annotation; import net.bytebuddy.description.annotation.AnnotationList; import net.bytebuddy.description.method.ParameterDescription; import net.bytebuddy.description.method.ParameterList; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bind.ArgumentTypeResolver; import net.bytebuddy.implementation.bind.MethodDelegationBinder; import net.bytebuddy.implementation.bytecode.StackSize; 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.lang.annotation.Annotation; import java.util.ArrayList; import java.util.List; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class ArgumentBinderTest extends AbstractAnnotationBinderTest<Argument> { @Mock private TypeDescription sourceType, targetType; @Mock private TypeDescription.Generic genericSourceType, genericTargetType; public ArgumentBinderTest() { super(Argument.class); } @Override @Before @SuppressWarnings("unchecked") public void setUp() throws Exception { super.setUp(); when(genericSourceType.asErasure()).thenReturn(sourceType); when(sourceType.asGenericType()).thenReturn(genericSourceType); when(genericSourceType.accept(any(TypeDescription.Generic.Visitor.class))).thenReturn(genericSourceType); when(genericTargetType.asErasure()).thenReturn(targetType); when(targetType.asGenericType()).thenReturn(genericTargetType); when(genericTargetType.accept(any(TypeDescription.Generic.Visitor.class))).thenReturn(targetType); } @Override protected TargetMethodAnnotationDrivenBinder.ParameterBinder<Argument> getSimpleBinder() { return Argument.Binder.INSTANCE; } @Test public void testLegalBindingNoRuntimeTypeUnique() throws Exception { assertBinding(Assigner.Typing.STATIC, Argument.BindingMechanic.UNIQUE); } @Test public void testLegalBindingRuntimeTypeUnique() throws Exception { assertBinding(Assigner.Typing.DYNAMIC, Argument.BindingMechanic.UNIQUE); } @Test public void testLegalBindingNoRuntimeTypeAnonymous() throws Exception { assertBinding(Assigner.Typing.STATIC, Argument.BindingMechanic.ANONYMOUS); } @Test public void testLegalBindingRuntimeTypeAnonymous() throws Exception { assertBinding(Assigner.Typing.DYNAMIC, Argument.BindingMechanic.ANONYMOUS); } private void assertBinding(Assigner.Typing typing, Argument.BindingMechanic bindingMechanic) throws Exception { final int sourceIndex = 2; when(stackManipulation.isValid()).thenReturn(true); when(annotation.value()).thenReturn(sourceIndex); when(annotation.bindingMechanic()).thenReturn(bindingMechanic); List<TypeDescription> typeDescriptions = new ArrayList<TypeDescription>(sourceIndex + 1); for (int i = 0; i < sourceIndex; i++) { TypeDescription typeDescription = mock(TypeDescription.class); when(typeDescription.getStackSize()).thenReturn(StackSize.ZERO); typeDescriptions.add(i, typeDescription); } when(sourceType.getStackSize()).thenReturn(StackSize.ZERO); typeDescriptions.add(sourceIndex, sourceType); when(source.getParameters()).thenReturn(new ParameterList.Explicit.ForTypes(source, typeDescriptions)); when(source.isStatic()).thenReturn(false); when(target.getType()).thenReturn(genericTargetType); MethodDelegationBinder.ParameterBinding<?> parameterBinding = Argument.Binder.INSTANCE .bind(annotationDescription, source, target, implementationTarget, assigner, typing); assertThat(parameterBinding.isValid(), is(true)); Object expectedToken = new ArgumentTypeResolver.ParameterIndexToken(sourceIndex); if (bindingMechanic == Argument.BindingMechanic.UNIQUE) { assertThat(parameterBinding.getIdentificationToken(), is(expectedToken)); assertThat(parameterBinding.getIdentificationToken().hashCode(), is(expectedToken.hashCode())); } else { assertThat(parameterBinding.getIdentificationToken(), not(expectedToken)); assertThat(parameterBinding.getIdentificationToken().hashCode(), not(expectedToken.hashCode())); } verify(annotation, atLeast(1)).value(); verify(source, atLeast(1)).getParameters(); verify(target, atLeast(1)).getType(); verify(target, never()).getDeclaredAnnotations(); verify(assigner).assign(genericSourceType, genericTargetType, typing); verifyNoMoreInteractions(assigner); } @Test public void testIllegalBinding() throws Exception { final int sourceIndex = 0, targetIndex = 0; when(annotation.value()).thenReturn(sourceIndex); when(target.getIndex()).thenReturn(targetIndex); when(source.getParameters()).thenReturn(new ParameterList.Empty<ParameterDescription.InDefinedShape>()); MethodDelegationBinder.ParameterBinding<?> parameterBinding = Argument.Binder.INSTANCE .bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); assertThat(parameterBinding.isValid(), is(false)); verify(annotation, atLeast(1)).value(); verify(source, atLeast(1)).getParameters(); verifyZeroInteractions(assigner); } @Test(expected = IllegalArgumentException.class) public void testNegativeAnnotationValue() throws Exception { when(annotation.value()).thenReturn(-1); Argument.Binder.INSTANCE.bind(annotationDescription, source, target, implementationTarget, assigner, Assigner.Typing.STATIC); } @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(Argument.Binder.class).apply(); ObjectPropertyAssertion.of(Argument.BindingMechanic.class).apply(); } @SuppressWarnings("unused") private static class Carrier { private void method(@Argument(0) Void parameter) { /* do nothing */ } } }