1package com.xtremelabs.robolectric.bytecode; 2 3import com.xtremelabs.robolectric.Robolectric; 4import com.xtremelabs.robolectric.internal.Implementation; 5import com.xtremelabs.robolectric.internal.Implements; 6import com.xtremelabs.robolectric.util.Join; 7import org.junit.Assert; 8import org.junit.Before; 9import org.junit.Test; 10 11import java.lang.reflect.Member; 12import java.lang.reflect.Method; 13import java.lang.reflect.Modifier; 14import java.util.ArrayList; 15import java.util.List; 16 17public class RobolectricWiringTest { 18 private List<String> mismatches; 19 20 @Before public void setUp() throws Exception { 21 mismatches = new ArrayList<String>(); 22 } 23 24 @Test 25 public void testAllImplementationMethodsHaveCorrectSignature() throws Exception { 26 for (Class<?> shadowClass : Robolectric.getDefaultShadowClasses()) { 27 verifyClass(shadowClass); 28 } 29 30 Assert.assertEquals("@Implementation method mismatch: " + Join.join("\n", mismatches), 0, mismatches.size()); 31 } 32 33 private void verifyClass(final Class<?> shadowClass) { 34 Implements annotation = shadowClass.getAnnotation(Implements.class); 35 Class implementedClass = annotation.value(); 36 37 try { 38 shadowClass.getConstructor(implementedClass); 39 } catch (NoSuchMethodException e) { 40 try { 41 shadowClass.getConstructor(); 42 } catch (NoSuchMethodException e1) { 43 mismatches.add("Missing constructor for " + shadowClass.getSimpleName()); 44 } 45 } 46 47 for (Method shadowMethod : shadowClass.getDeclaredMethods()) { 48 verifyMethod(implementedClass, shadowMethod); 49 } 50 } 51 52 private void verifyMethod(Class implementedClass, Method shadowMethod) { 53 Member implementedMember; 54 55 boolean isConstructor = shadowMethod.getName().equals("__constructor__"); 56 if (isAnnotatedImplementation(shadowMethod) || isConstructor) { 57 if (isConstructor) { 58 implementedMember = findConstructor(implementedClass, shadowMethod); 59 } else { 60 implementedMember = findMethod(implementedClass, shadowMethod); 61 } 62 if (implementedMember == null) { 63 mismatches.add(shadowMethod.toGenericString() + " doesn't match a real method"); 64 } else if (staticMismatch(shadowMethod, implementedMember)) { 65 mismatches.add(shadowMethod.toGenericString() + " doesn't match the staticness of the real method"); 66 } 67 if (!Modifier.isPublic(shadowMethod.getModifiers())) { 68 mismatches.add(shadowMethod.toGenericString() + " should be public"); 69 } 70 } else { 71 implementedMember = findMethod(implementedClass, shadowMethod); 72 if (implementedMember != null) { 73 mismatches.add(shadowMethod.toGenericString() + " should be annotated @Implementation"); 74 } 75 } 76 } 77 78 private boolean isAnnotatedImplementation(Method shadowMethod) { 79 // works around a weird bug causing overridden methods to show no annotations 80 try { 81 return shadowMethod.getDeclaringClass().getDeclaredMethod(shadowMethod.getName(), shadowMethod.getParameterTypes()).isAnnotationPresent(Implementation.class); 82 } catch (NoSuchMethodException e) { 83 throw new RuntimeException(e); 84 } 85 } 86 87 private Member findConstructor(Class implementedClass, Method shadowMethod) { 88 Class<?>[] parameterTypes = shadowMethod.getParameterTypes(); 89 try { 90 return implementedClass.getConstructor(parameterTypes); 91 } catch (NoSuchMethodException e1) { 92 try { 93 return implementedClass.getDeclaredConstructor(parameterTypes); 94 } catch (NoSuchMethodException e2) { 95 return null; 96 } 97 } 98 } 99 100 private Member findMethod(Class implementedClass, Method shadowMethod) { 101 Class<?>[] parameterTypes = shadowMethod.getParameterTypes(); 102 String methodName = shadowMethod.getName(); 103 try { 104 return implementedClass.getMethod(methodName, parameterTypes); 105 } catch (NoSuchMethodException e1) { 106 try { 107 return implementedClass.getDeclaredMethod(methodName, parameterTypes); 108 } catch (NoSuchMethodException e2) { 109 return null; 110 } 111 } 112 } 113 114 private boolean staticMismatch(Member shadowMethod, Member implementedMethod) { 115 return Modifier.isStatic(implementedMethod.getModifiers()) != Modifier.isStatic(shadowMethod.getModifiers()); 116 } 117} 118