Home | History | Annotate | Download | only in bytecode
      1 package com.xtremelabs.robolectric.bytecode;
      2 
      3 import com.xtremelabs.robolectric.Robolectric;
      4 import com.xtremelabs.robolectric.internal.Implementation;
      5 import com.xtremelabs.robolectric.internal.Implements;
      6 import com.xtremelabs.robolectric.util.Join;
      7 import org.junit.Assert;
      8 import org.junit.Before;
      9 import org.junit.Test;
     10 
     11 import java.lang.reflect.Member;
     12 import java.lang.reflect.Method;
     13 import java.lang.reflect.Modifier;
     14 import java.util.ArrayList;
     15 import java.util.List;
     16 
     17 public 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