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