package net.bytebuddy.description.annotation;
import net.bytebuddy.test.utility.MockitoRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.mockito.Mock;
import java.lang.annotation.Annotation;
import java.lang.annotation.AnnotationTypeMismatchException;
import java.lang.annotation.IncompleteAnnotationException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collections;
import static org.hamcrest.CoreMatchers.any;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.*;
public class AnnotationDescriptionAnnotationInvocationHandlerTest {
private static final String FOO = "foo", BAR = "bar";
@Rule
public TestRule mockitoRule = new MockitoRule(this);
@Mock
private AnnotationValue<?, ?> annotationValue, otherAnnotationValue, freeAnnotationValue;
@Mock
private AnnotationValue.Loaded<?> loadedAnnotationValue, otherLoadedAnnotationValue;
@Before
@SuppressWarnings("unchecked")
public void setUp() throws Exception {
when(annotationValue.load(getClass().getClassLoader()))
.thenReturn((AnnotationValue.Loaded) loadedAnnotationValue);
when(otherAnnotationValue.load(getClass().getClassLoader()))
.thenReturn((AnnotationValue.Loaded) otherLoadedAnnotationValue);
}
@Test(expected = ClassNotFoundException.class)
public void testClassNotFoundExceptionIsTransparent() throws Throwable {
when(freeAnnotationValue.load(getClass().getClassLoader())).thenThrow(new ClassNotFoundException());
Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, freeAnnotationValue)))
.invoke(new Object(), Foo.class.getDeclaredMethod("foo"), new Object[0]);
}
@Test(expected = AnnotationTypeMismatchException.class)
@SuppressWarnings("unchecked")
public void testAnnotationTypeMismatchException() throws Throwable {
when(loadedAnnotationValue.resolve()).thenReturn(new Object());
Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Foo.class.getDeclaredMethod("foo"), new Object[0]);
}
@Test(expected = IncompleteAnnotationException.class)
@SuppressWarnings("unchecked")
public void testIncompleteAnnotationException() throws Throwable {
when(freeAnnotationValue.load(getClass().getClassLoader())).thenReturn((AnnotationValue.Loaded)
new AnnotationDescription.AnnotationInvocationHandler.MissingValue(Foo.class, "foo"));
Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, freeAnnotationValue)))
.invoke(new Object(), Foo.class.getDeclaredMethod("foo"), new Object[0]);
}
@Test(expected = EnumConstantNotPresentException.class)
@SuppressWarnings("unchecked")
public void testEnumConstantNotPresentException() throws Throwable {
when(freeAnnotationValue.load(getClass().getClassLoader())).thenReturn((AnnotationValue.Loaded)
new AnnotationValue.ForEnumerationDescription.UnknownRuntimeEnumeration(Bar.class, FOO));
Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, freeAnnotationValue)))
.invoke(new Object(), Foo.class.getDeclaredMethod("foo"), new Object[0]);
}
@Test(expected = IncompatibleClassChangeError.class)
@SuppressWarnings("unchecked")
public void testEnumTypeIncompatible() throws Throwable {
when(freeAnnotationValue.load(getClass().getClassLoader())).thenReturn((AnnotationValue.Loaded)
new AnnotationValue.ForEnumerationDescription.IncompatibleRuntimeType(Foo.class));
Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, freeAnnotationValue)))
.invoke(new Object(), Foo.class.getDeclaredMethod("foo"), new Object[0]);
}
@Test(expected = IncompatibleClassChangeError.class)
@SuppressWarnings("unchecked")
public void testAnnotationTypeIncompatible() throws Throwable {
when(freeAnnotationValue.load(getClass().getClassLoader())).thenReturn((AnnotationValue.Loaded)
new AnnotationValue.ForEnumerationDescription.IncompatibleRuntimeType(Foo.class));
Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, freeAnnotationValue)))
.invoke(new Object(), Foo.class.getDeclaredMethod("foo"), new Object[0]);
}
@Test(expected = RuntimeException.class)
public void testOtherExceptionIsTransparent() throws Throwable {
when(freeAnnotationValue.load(getClass().getClassLoader())).thenThrow(new RuntimeException());
Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, freeAnnotationValue)))
.invoke(new Object(), Foo.class.getDeclaredMethod("foo"), new Object[0]);
}
@Test
public void testEqualsToDirectIsTrue() throws Throwable {
when(loadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.RESOLVED);
when(loadedAnnotationValue.represents(FOO)).thenReturn(true);
assertThat(Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Object.class.getDeclaredMethod("equals", Object.class), new Object[]{new ExplicitFoo(FOO)}),
is((Object) true));
}
@Test
public void testEqualsToUnresolvedIsFalse() throws Throwable {
when(loadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.UNRESOLVED);
assertThat(Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Object.class.getDeclaredMethod("equals", Object.class), new Object[]{new ExplicitFoo(FOO)}),
is((Object) false));
verify(loadedAnnotationValue, never()).resolve();
}
@Test
public void testEqualsToUndefinedIsFalse() throws Throwable {
when(loadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.UNDEFINED);
assertThat(Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Object.class.getDeclaredMethod("equals", Object.class), new Object[]{new ExplicitFoo(FOO)}),
is((Object) false));
verify(loadedAnnotationValue, never()).resolve();
}
@Test
public void testEqualsToIndirectIsTrue() throws Throwable {
when(loadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.RESOLVED);
when(loadedAnnotationValue.represents(FOO)).thenReturn(true);
assertThat(Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Object.class.getDeclaredMethod("equals", Object.class),
new Object[]{Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[]{Foo.class}, new ExplicitFoo(FOO))}),
is((Object) true));
verify(loadedAnnotationValue).represents(FOO);
}
@Test
public void testEqualsToOtherHandlerIsTrue() throws Throwable {
when(loadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.RESOLVED);
when(loadedAnnotationValue.resolve()).thenReturn(FOO);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)));
assertThat(Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Object.class.getDeclaredMethod("equals", Object.class),
new Object[]{Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[]{Foo.class}, invocationHandler)}),
is((Object) true));
verify(loadedAnnotationValue, never()).resolve();
}
@Test
public void testEqualsToDirectIsFalse() throws Throwable {
when(loadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.RESOLVED);
when(loadedAnnotationValue.represents(BAR)).thenReturn(false);
assertThat(Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Object.class.getDeclaredMethod("equals", Object.class), new Object[]{new ExplicitFoo(BAR)}),
is((Object) false));
verify(loadedAnnotationValue).represents(BAR);
}
@Test
public void testEqualsToIndirectIsFalse() throws Throwable {
when(loadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.RESOLVED);
when(loadedAnnotationValue.represents(BAR)).thenReturn(false);
assertThat(Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Object.class.getDeclaredMethod("equals", Object.class),
new Object[]{Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[]{Foo.class}, new ExplicitFoo(BAR))}),
is((Object) false));
verify(loadedAnnotationValue).represents(BAR);
}
@Test
public void testEqualsToOtherHandlerIsFalse() throws Throwable {
when(loadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.RESOLVED);
when(loadedAnnotationValue.resolve()).thenReturn(FOO);
when(otherLoadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.RESOLVED);
when(otherLoadedAnnotationValue.resolve()).thenReturn(BAR);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, otherAnnotationValue)));
assertThat(Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Object.class.getDeclaredMethod("equals", Object.class),
new Object[]{Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[]{Foo.class}, invocationHandler)}),
is((Object) false));
verify(loadedAnnotationValue, never()).resolve();
verify(otherLoadedAnnotationValue, never()).resolve();
}
@Test
public void testEqualsToObjectIsFalse() throws Throwable {
when(loadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.RESOLVED);
when(loadedAnnotationValue.resolve()).thenReturn(FOO);
assertThat(Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Object.class.getDeclaredMethod("equals", Object.class), new Object[]{new Other()}),
is((Object) false));
verify(loadedAnnotationValue, never()).resolve();
}
@Test
public void testEqualsToInvocationExceptionIsFalse() throws Throwable {
when(loadedAnnotationValue.getState()).thenReturn(AnnotationValue.Loaded.State.RESOLVED);
when(loadedAnnotationValue.resolve()).thenReturn(FOO);
assertThat(Proxy.getInvocationHandler(AnnotationDescription.AnnotationInvocationHandler.of(getClass().getClassLoader(),
Foo.class,
Collections.<String, AnnotationValue<?, ?>>singletonMap(FOO, annotationValue)))
.invoke(new Object(), Object.class.getDeclaredMethod("equals", Object.class), new Object[]{new FooWithException()}),
is((Object) false));
verify(loadedAnnotationValue, never()).represents(any(String.class));
}
public enum Bar {
VALUE
}
public @interface Foo {
String foo();
}
public @interface DefaultFoo {
String foo() default FOO;
}
private static class FooWithException implements Foo {
@Override
public String foo() {
throw new RuntimeException();
}
@Override
public Class<? extends Annotation> annotationType() {
return Foo.class;
}
}
private static class ExplicitFoo implements Foo, InvocationHandler {
private final String value;
private ExplicitFoo(String value) {
this.value = value;
}
@Override
public String foo() {
return value;
}
@Override
public Class<? extends Annotation> annotationType() {
return Foo.class;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(this, args);
}
}
private static class Other implements Annotation {
@Override
public Class<? extends Annotation> annotationType() {
return Annotation.class;
}
}
}