Home | History | Annotate | Download | only in robolectric
      1 package org.robolectric;
      2 
      3 import java.lang.annotation.ElementType;
      4 import java.lang.annotation.Retention;
      5 import java.lang.annotation.RetentionPolicy;
      6 import java.lang.annotation.Target;
      7 import java.lang.reflect.Field;
      8 import java.lang.reflect.Method;
      9 import javax.annotation.Nonnull;
     10 import org.junit.runners.model.FrameworkMethod;
     11 import org.junit.runners.model.InitializationError;
     12 import org.robolectric.annotation.Config;
     13 import org.robolectric.internal.ParallelUniverseInterface;
     14 import org.robolectric.internal.SdkConfig;
     15 import org.robolectric.internal.SdkEnvironment;
     16 import org.robolectric.internal.bytecode.InstrumentationConfiguration;
     17 import org.robolectric.internal.bytecode.InstrumentationConfiguration.Builder;
     18 import org.robolectric.manifest.AndroidManifest;
     19 
     20 /**
     21  * Test runner which prevents full initialization (bootstrap) of the Android process at test setup.
     22  */
     23 public class BootstrapDeferringRobolectricTestRunner extends RobolectricTestRunner {
     24 
     25   private static BootstrapWrapper bootstrapWrapper;
     26 
     27   public BootstrapDeferringRobolectricTestRunner(Class<?> testClass) throws InitializationError {
     28     super(testClass);
     29   }
     30 
     31   @Nonnull
     32   @Override
     33   protected Class<? extends TestLifecycle> getTestLifecycleClass() {
     34     return MyTestLifecycle.class;
     35   }
     36 
     37   @Override
     38   ParallelUniverseInterface getHooksInterface(SdkEnvironment sdkEnvironment) {
     39     bootstrapWrapper = new BootstrapWrapper(super.getHooksInterface(sdkEnvironment));
     40     return bootstrapWrapper;
     41   }
     42 
     43   @Nonnull
     44   @Override
     45   protected InstrumentationConfiguration createClassLoaderConfig(FrameworkMethod method) {
     46     return new Builder(super.createClassLoaderConfig(method))
     47         .doNotAcquireClass(BootstrapDeferringRobolectricTestRunner.class)
     48         .doNotAcquireClass(RoboInject.class)
     49         .doNotAcquireClass(MyTestLifecycle.class)
     50         .doNotAcquireClass(BootstrapWrapper.class)
     51         .build();
     52   }
     53 
     54   @Retention(RetentionPolicy.RUNTIME)
     55   @Target(ElementType.FIELD)
     56   public @interface RoboInject {
     57   }
     58 
     59   public static class MyTestLifecycle extends DefaultTestLifecycle {
     60     @Override
     61     public void prepareTest(Object test) {
     62       super.prepareTest(test);
     63       for (Field field : test.getClass().getDeclaredFields()) {
     64         if (field.getAnnotation(RoboInject.class) != null) {
     65           if (field.getType().isAssignableFrom(BootstrapWrapper.class)) {
     66             field.setAccessible(true);
     67             try {
     68               field.set(test, bootstrapWrapper);
     69             } catch (IllegalAccessException e) {
     70               throw new RuntimeException("can't set " + field, e);
     71             }
     72           }
     73         }
     74       }
     75     }
     76   }
     77 
     78   public static class BootstrapWrapper implements ParallelUniverseInterface {
     79     public ParallelUniverseInterface hooksInterface;
     80     public boolean legacyResources;
     81     public ApkLoader apkLoader;
     82     public Method method;
     83     public Config config;
     84     public AndroidManifest appManifest;
     85     public SdkEnvironment sdkEnvironment;
     86 
     87     public BootstrapWrapper(ParallelUniverseInterface hooksInterface) {
     88       this.hooksInterface = hooksInterface;
     89     }
     90 
     91     @Override
     92     public void setSdkConfig(SdkConfig sdkConfig) {
     93       hooksInterface.setSdkConfig(sdkConfig);
     94     }
     95 
     96     @Override
     97     public void setResourcesMode(boolean legacyResources) {
     98       hooksInterface.setResourcesMode(legacyResources);
     99       this.legacyResources = legacyResources;
    100     }
    101 
    102     @Override
    103     public void setUpApplicationState(ApkLoader apkLoader, Method method, Config config,
    104         AndroidManifest appManifest, SdkEnvironment sdkEnvironment) {
    105       this.apkLoader = apkLoader;
    106       this.method = method;
    107       this.config = config;
    108       this.appManifest = appManifest;
    109       this.sdkEnvironment = sdkEnvironment;
    110     }
    111 
    112     @Override
    113     public Thread getMainThread() {
    114       return hooksInterface.getMainThread();
    115     }
    116 
    117     @Override
    118     public void setMainThread(Thread newMainThread) {
    119       hooksInterface.setMainThread(newMainThread);
    120     }
    121 
    122     @Override
    123     public void tearDownApplication() {
    124       hooksInterface.tearDownApplication();
    125     }
    126 
    127     @Override
    128     public Object getCurrentApplication() {
    129       return hooksInterface.getCurrentApplication();
    130     }
    131 
    132     public void callSetUpApplicationState() {
    133       hooksInterface.setUpApplicationState(apkLoader, method, config, appManifest, sdkEnvironment);
    134     }
    135   }
    136 }
    137