blob: 60ea55160f894e3e0d66ee91dbd94a237d1507dd [file] [log] [blame]
package com.xtremelabs.robolectric.bytecode;
import com.xtremelabs.robolectric.Robolectric;
import com.xtremelabs.robolectric.internal.Implementation;
import com.xtremelabs.robolectric.internal.Implements;
import com.xtremelabs.robolectric.util.Join;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
public class RobolectricWiringTest {
private List<String> mismatches;
@Before public void setUp() throws Exception {
mismatches = new ArrayList<String>();
}
@Test
public void testAllImplementationMethodsHaveCorrectSignature() throws Exception {
for (Class<?> shadowClass : Robolectric.getDefaultShadowClasses()) {
verifyClass(shadowClass);
}
Assert.assertEquals("@Implementation method mismatch: " + Join.join("\n", mismatches), 0, mismatches.size());
}
private void verifyClass(final Class<?> shadowClass) {
Implements annotation = shadowClass.getAnnotation(Implements.class);
Class implementedClass = annotation.value();
try {
shadowClass.getConstructor(implementedClass);
} catch (NoSuchMethodException e) {
try {
shadowClass.getConstructor();
} catch (NoSuchMethodException e1) {
mismatches.add("Missing constructor for " + shadowClass.getSimpleName());
}
}
for (Method shadowMethod : shadowClass.getDeclaredMethods()) {
verifyMethod(implementedClass, shadowMethod);
}
}
private void verifyMethod(Class implementedClass, Method shadowMethod) {
Member implementedMember;
boolean isConstructor = shadowMethod.getName().equals("__constructor__");
if (isAnnotatedImplementation(shadowMethod) || isConstructor) {
if (isConstructor) {
implementedMember = findConstructor(implementedClass, shadowMethod);
} else {
implementedMember = findMethod(implementedClass, shadowMethod);
}
if (implementedMember == null) {
mismatches.add(shadowMethod.toGenericString() + " doesn't match a real method");
} else if (staticMismatch(shadowMethod, implementedMember)) {
mismatches.add(shadowMethod.toGenericString() + " doesn't match the staticness of the real method");
}
if (!Modifier.isPublic(shadowMethod.getModifiers())) {
mismatches.add(shadowMethod.toGenericString() + " should be public");
}
} else {
implementedMember = findMethod(implementedClass, shadowMethod);
if (implementedMember != null) {
mismatches.add(shadowMethod.toGenericString() + " should be annotated @Implementation");
}
}
}
private boolean isAnnotatedImplementation(Method shadowMethod) {
// works around a weird bug causing overridden methods to show no annotations
try {
return shadowMethod.getDeclaringClass().getDeclaredMethod(shadowMethod.getName(), shadowMethod.getParameterTypes()).isAnnotationPresent(Implementation.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
private Member findConstructor(Class implementedClass, Method shadowMethod) {
Class<?>[] parameterTypes = shadowMethod.getParameterTypes();
try {
return implementedClass.getConstructor(parameterTypes);
} catch (NoSuchMethodException e1) {
try {
return implementedClass.getDeclaredConstructor(parameterTypes);
} catch (NoSuchMethodException e2) {
return null;
}
}
}
private Member findMethod(Class implementedClass, Method shadowMethod) {
Class<?>[] parameterTypes = shadowMethod.getParameterTypes();
String methodName = shadowMethod.getName();
try {
return implementedClass.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e1) {
try {
return implementedClass.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e2) {
return null;
}
}
}
private boolean staticMismatch(Member shadowMethod, Member implementedMethod) {
return Modifier.isStatic(implementedMethod.getModifiers()) != Modifier.isStatic(shadowMethod.getModifiers());
}
}