1 package org.robolectric; 2 3 import static android.os.Build.VERSION_CODES.P; 4 import static com.google.common.base.Preconditions.checkState; 5 6 import android.annotation.IdRes; 7 import android.annotation.RequiresApi; 8 import android.app.Activity; 9 import android.app.ActivityThread; 10 import android.app.AppComponentFactory; 11 import android.app.Fragment; 12 import android.app.IntentService; 13 import android.app.LoadedApk; 14 import android.app.Service; 15 import android.app.backup.BackupAgent; 16 import android.content.ContentProvider; 17 import android.content.Context; 18 import android.content.Intent; 19 import android.os.Bundle; 20 import android.os.Looper; 21 import android.util.AttributeSet; 22 import android.view.View; 23 import javax.annotation.Nullable; 24 import org.robolectric.android.AttributeSetBuilderImpl; 25 import org.robolectric.android.AttributeSetBuilderImpl.ArscResourceResolver; 26 import org.robolectric.android.controller.ActivityController; 27 import org.robolectric.android.controller.BackupAgentController; 28 import org.robolectric.android.controller.ContentProviderController; 29 import org.robolectric.android.controller.FragmentController; 30 import org.robolectric.android.controller.IntentServiceController; 31 import org.robolectric.android.controller.ServiceController; 32 import org.robolectric.shadows.ShadowApplication; 33 import org.robolectric.util.Logger; 34 import org.robolectric.util.ReflectionHelpers; 35 import org.robolectric.util.Scheduler; 36 37 public class Robolectric { 38 buildService(Class<T> serviceClass)39 public static <T extends Service> ServiceController<T> buildService(Class<T> serviceClass) { 40 return buildService(serviceClass, null); 41 } 42 buildService(Class<T> serviceClass, Intent intent)43 public static <T extends Service> ServiceController<T> buildService(Class<T> serviceClass, Intent intent) { 44 return ServiceController.of(instantiateService(serviceClass, intent), intent); 45 } 46 setupService(Class<T> serviceClass)47 public static <T extends Service> T setupService(Class<T> serviceClass) { 48 return buildService(serviceClass).create().get(); 49 } 50 buildIntentService(Class<T> serviceClass)51 public static <T extends IntentService> IntentServiceController<T> buildIntentService(Class<T> serviceClass) { 52 return buildIntentService(serviceClass, null); 53 } 54 buildIntentService(Class<T> serviceClass, Intent intent)55 public static <T extends IntentService> IntentServiceController<T> buildIntentService(Class<T> serviceClass, Intent intent) { 56 return IntentServiceController.of(instantiateService(serviceClass, intent), intent); 57 } 58 setupIntentService(Class<T> serviceClass)59 public static <T extends IntentService> T setupIntentService(Class<T> serviceClass) { 60 return buildIntentService(serviceClass).create().get(); 61 } 62 buildContentProvider(Class<T> contentProviderClass)63 public static <T extends ContentProvider> ContentProviderController<T> buildContentProvider(Class<T> contentProviderClass) { 64 return ContentProviderController.of(instantiateContentProvider(contentProviderClass)); 65 } 66 setupContentProvider(Class<T> contentProviderClass)67 public static <T extends ContentProvider> T setupContentProvider(Class<T> contentProviderClass) { 68 return buildContentProvider(contentProviderClass).create().get(); 69 } 70 setupContentProvider(Class<T> contentProviderClass, String authority)71 public static <T extends ContentProvider> T setupContentProvider(Class<T> contentProviderClass, String authority) { 72 return buildContentProvider(contentProviderClass).create(authority).get(); 73 } 74 75 /** 76 * Creates a ActivityController for the given activity class. 77 * 78 * <p>Consider using {@link androidx.test.core.app.ActivityScenario} instead, which provides 79 * higher-level, streamlined APIs to control the lifecycle and it works with instrumentation tests 80 * too. 81 */ buildActivity(Class<T> activityClass)82 public static <T extends Activity> ActivityController<T> buildActivity(Class<T> activityClass) { 83 return buildActivity(activityClass, /* intent= */ null, /* activityOptions= */ null); 84 } 85 86 /** 87 * Creates a ActivityController for the given activity class with the intent. 88 * 89 * <p>Note: the activity class is not determined by the intent. 90 * 91 * <p>Consider using {@link androidx.test.core.app.ActivityScenario} instead, which provides 92 * higher-level, streamlined APIs to control the lifecycle and it works with instrumentation tests 93 * too. 94 */ buildActivity( Class<T> activityClass, Intent intent)95 public static <T extends Activity> ActivityController<T> buildActivity( 96 Class<T> activityClass, Intent intent) { 97 return buildActivity(activityClass, intent, /* activityOptions= */ null); 98 } 99 100 /** 101 * Creates a ActivityController for the given activity class with the intent and activity options. 102 * 103 * <p>Note: the activity class is not determined by the intent. 104 * 105 * <p>Note: Display ID is the only option currently supported in the options bundle. Other options 106 * are ignored. 107 * 108 * <p>Consider using {@link androidx.test.core.app.ActivityScenario} instead, which provides 109 * higher-level, streamlined APIs to control the lifecycle and it works with instrumentation tests 110 * too. 111 */ buildActivity( Class<T> activityClass, Intent intent, @Nullable Bundle activityOptions)112 public static <T extends Activity> ActivityController<T> buildActivity( 113 Class<T> activityClass, Intent intent, @Nullable Bundle activityOptions) { 114 checkState( 115 Thread.currentThread() == Looper.getMainLooper().getThread(), 116 "buildActivity must be called on main Looper thread"); 117 return ActivityController.of( 118 instantiateActivity(activityClass, intent), intent, activityOptions); 119 } 120 121 /** 122 * Simulates starting activity with the given class type and returns its reference. 123 * 124 * <p>Use {@link androidx.test.core.app.ActivityScenario} instead, which works with 125 * instrumentation tests too. 126 * 127 * @deprecated use {@link androidx.test.core.app.ActivityScenario} 128 */ 129 @Deprecated 130 @SuppressWarnings("InlineMeSuggester") setupActivity(Class<T> activityClass)131 public static final <T extends Activity> T setupActivity(Class<T> activityClass) { 132 return buildActivity(activityClass).setup().get(); 133 } 134 135 /** 136 * Creates a FragmentController for the given fragment class. 137 * 138 * <p>FragmentController provides low-level APIs to control its lifecycle. Please consider using 139 * {@link androidx.fragment.app.testing.FragmentScenario} instead, which provides higher level 140 * APIs and works with instrumentation tests too. 141 * 142 * @deprecated Native Fragments have been deprecated in Android P. Android encourages developers 143 * to use androidx fragments, to test these use FragmentScenario. 144 */ 145 @Deprecated buildFragment(Class<T> fragmentClass)146 public static <T extends Fragment> FragmentController<T> buildFragment(Class<T> fragmentClass) { 147 return FragmentController.of(ReflectionHelpers.callConstructor(fragmentClass)); 148 } 149 150 /** 151 * Creates a FragmentController for the given fragment class with the arguments. 152 * 153 * <p>FragmentController provides low-level APIs to control its lifecycle. Please consider using 154 * {@link androidx.fragment.app.testing.FragmentScenario} instead, which provides higher level 155 * APIs and works with instrumentation tests too. 156 * 157 * @deprecated Native Fragments have been deprecated in Android P. Android encourages developers 158 * to use androidx fragments, to test these use FragmentScenario. 159 */ 160 @Deprecated buildFragment( Class<T> fragmentClass, Bundle arguments)161 public static <T extends Fragment> FragmentController<T> buildFragment( 162 Class<T> fragmentClass, Bundle arguments) { 163 return FragmentController.of(ReflectionHelpers.callConstructor(fragmentClass), arguments); 164 } 165 166 /** 167 * Creates a FragmentController for the given fragment class in the specified host activity. 168 * 169 * <p>In general, it's a bad practice to design a fragment having dependency to a specific 170 * activity. Consider removing the dependency and use other {@link #buildFragment} method or 171 * {@link androidx.fragment.app.testing.FragmentScenario}. 172 * 173 * <p>FragmentController provides low-level APIs to control its lifecycle. Please consider using 174 * {@link androidx.fragment.app.testing.FragmentScenario} instead, which provides higher level 175 * APIs and works with instrumentation tests too. 176 * 177 * @deprecated Native Fragments have been deprecated in Android P. Android encourages developers 178 * to use androidx fragments, to test these use FragmentScenario. 179 */ 180 @Deprecated buildFragment( Class<T> fragmentClass, Class<? extends Activity> activityClass)181 public static <T extends Fragment> FragmentController<T> buildFragment( 182 Class<T> fragmentClass, Class<? extends Activity> activityClass) { 183 return FragmentController.of(ReflectionHelpers.callConstructor(fragmentClass), activityClass); 184 } 185 186 /** 187 * Creates a FragmentController for the given fragment class. The given intent is set to the host 188 * activity. 189 * 190 * <p>Note: the host activity class is not determined by the intent. 191 * 192 * <p>FragmentController provides low-level APIs to control its lifecycle. Please consider using 193 * {@link androidx.fragment.app.testing.FragmentScenario} instead, which provides higher level 194 * APIs and works with instrumentation tests too. 195 * 196 * @deprecated Native Fragments have been deprecated in Android P. Android encourages developers 197 * to use androidx fragments, to test these use FragmentScenario. 198 */ 199 @Deprecated buildFragment( Class<T> fragmentClass, Intent intent)200 public static <T extends Fragment> FragmentController<T> buildFragment( 201 Class<T> fragmentClass, Intent intent) { 202 return FragmentController.of(ReflectionHelpers.callConstructor(fragmentClass), intent); 203 } 204 205 /** 206 * Creates a FragmentController for the given fragment class with the arguments. The given intent 207 * is set to the host activity. 208 * 209 * <p>Note: the host activity class is not determined by the intent. 210 * 211 * <p>FragmentController provides low-level APIs to control its lifecycle. Please consider using 212 * {@link androidx.fragment.app.testing.FragmentScenario} instead, which provides higher level 213 * APIs and works with instrumentation tests too. 214 * 215 * @deprecated Native Fragments have been deprecated in Android P. Android encourages developers 216 * to use androidx fragments, to test these use FragmentScenario. 217 */ 218 @Deprecated buildFragment( Class<T> fragmentClass, Intent intent, Bundle arguments)219 public static <T extends Fragment> FragmentController<T> buildFragment( 220 Class<T> fragmentClass, Intent intent, Bundle arguments) { 221 return FragmentController.of(ReflectionHelpers.callConstructor(fragmentClass), intent, arguments); 222 } 223 224 /** 225 * Creates a FragmentController for the given fragment class in the specified host activity. The 226 * given intent is set to the host activity. 227 * 228 * <p>Note: the host activity class is not determined by the intent. 229 * 230 * <p>In general, it's a bad practice to design a fragment having dependency to a specific 231 * activity. Consider removing the dependency and use other {@link #buildFragment} method or 232 * {@link androidx.fragment.app.testing.FragmentScenario}. 233 * 234 * <p>FragmentController provides low-level APIs to control its lifecycle. Please consider using 235 * {@link androidx.fragment.app.testing.FragmentScenario} instead, which provides higher level 236 * APIs and works with instrumentation tests too. 237 * 238 * @deprecated Native Fragments have been deprecated in Android P. Android encourages developers 239 * to use androidx fragments, to test these use FragmentScenario. 240 */ 241 @Deprecated buildFragment( Class<T> fragmentClass, Class<? extends Activity> activityClass, Intent intent)242 public static <T extends Fragment> FragmentController<T> buildFragment( 243 Class<T> fragmentClass, Class<? extends Activity> activityClass, Intent intent) { 244 return FragmentController.of(ReflectionHelpers.callConstructor(fragmentClass), activityClass, intent); 245 } 246 247 /** 248 * Creates a FragmentController for the given fragment class in the specified host activity with 249 * the arguments. 250 * 251 * <p>In general, it's a bad practice to design a fragment having dependency to a specific 252 * activity. Consider removing the dependency and use other {@link #buildFragment} method or 253 * {@link androidx.fragment.app.testing.FragmentScenario}. 254 * 255 * <p>FragmentController provides low-level APIs to control its lifecycle. Please consider using 256 * {@link androidx.fragment.app.testing.FragmentScenario} instead, which provides higher level 257 * APIs and works with instrumentation tests too. 258 * 259 * @deprecated Native Fragments have been deprecated in Android P. Android encourages developers 260 * to use androidx fragments, to test these use FragmentScenario. 261 */ 262 @Deprecated buildFragment( Class<T> fragmentClass, Class<? extends Activity> activityClass, Bundle arguments)263 public static <T extends Fragment> FragmentController<T> buildFragment( 264 Class<T> fragmentClass, Class<? extends Activity> activityClass, Bundle arguments) { 265 return FragmentController.of(ReflectionHelpers.callConstructor(fragmentClass), activityClass, arguments); 266 } 267 268 /** 269 * Creates a FragmentController for the given fragment class in the specified host activity with 270 * the arguments. The given intent is set to the host activity. 271 * 272 * <p>Note: the host activity class is not determined by the intent. 273 * 274 * <p>In general, it's a bad practice to design a fragment having dependency to a specific 275 * activity. Consider removing the dependency and use other {@link #buildFragment} method or 276 * {@link androidx.fragment.app.testing.FragmentScenario}. 277 * 278 * <p>FragmentController provides low-level APIs to control its lifecycle. Please consider using 279 * {@link androidx.fragment.app.testing.FragmentScenario} instead, which provides higher level 280 * APIs and works with instrumentation tests too. 281 * 282 * @deprecated Native Fragments have been deprecated in Android P. Android encourages developers 283 * to use androidx fragments, to test these use FragmentScenario. 284 */ 285 @Deprecated buildFragment( Class<T> fragmentClass, Class<? extends Activity> activityClass, Intent intent, Bundle arguments)286 public static <T extends Fragment> FragmentController<T> buildFragment( 287 Class<T> fragmentClass, 288 Class<? extends Activity> activityClass, 289 Intent intent, 290 Bundle arguments) { 291 return FragmentController.of(ReflectionHelpers.callConstructor(fragmentClass), activityClass, intent, arguments); 292 } 293 buildBackupAgent(Class<T> backupAgentClass)294 public static <T extends BackupAgent> BackupAgentController<T> buildBackupAgent(Class<T> backupAgentClass) { 295 return BackupAgentController.of(ReflectionHelpers.callConstructor(backupAgentClass)); 296 } 297 setupBackupAgent(Class<T> backupAgentClass)298 public static <T extends BackupAgent> T setupBackupAgent(Class<T> backupAgentClass) { 299 return buildBackupAgent(backupAgentClass).create().get(); 300 } 301 302 /** 303 * Allows for the programmatic creation of an {@link AttributeSet}. 304 * 305 * Useful for testing {@link View} classes without the need for creating XML snippets. 306 */ buildAttributeSet()307 public static org.robolectric.android.AttributeSetBuilder buildAttributeSet() { 308 309 return new AttributeSetBuilderImpl( 310 new ArscResourceResolver(RuntimeEnvironment.getApplication())) {}; 311 312 } 313 314 /** 315 * Builder of {@link AttributeSet}s. 316 * 317 * @deprecated Use {@link org.robolectric.android.AttributeSetBuilder} instead. 318 */ 319 @Deprecated 320 public interface AttributeSetBuilder { 321 /** 322 * Set an attribute to the given value. 323 * 324 * The value will be interpreted according to the attribute's format. 325 * 326 * @param resId The attribute resource id to set. 327 * @param value The value to set. 328 * @return This {@link org.robolectric.android.AttributeSetBuilder}. 329 */ addAttribute(@dRes int resId, String value)330 AttributeSetBuilder addAttribute(@IdRes int resId, String value); 331 332 /** 333 * Set the style attribute to the given value. 334 * 335 * The value will be interpreted as a resource reference. 336 * 337 * @param value The value for the specified attribute in this {@link AttributeSet}. 338 * @return This {@link org.robolectric.android.AttributeSetBuilder}. 339 */ setStyleAttribute(String value)340 AttributeSetBuilder setStyleAttribute(String value); 341 342 /** 343 * Build an {@link AttributeSet} with the antecedent attributes. 344 * 345 * @return A new {@link AttributeSet}. 346 */ build()347 AttributeSet build(); 348 } 349 350 /** 351 * Return the foreground scheduler (e.g. the UI thread scheduler). 352 * 353 * @return Foreground scheduler. 354 */ getForegroundThreadScheduler()355 public static Scheduler getForegroundThreadScheduler() { 356 return RuntimeEnvironment.getMasterScheduler(); 357 } 358 359 /** 360 * Execute all runnables that have been enqueued on the foreground scheduler. 361 */ flushForegroundThreadScheduler()362 public static void flushForegroundThreadScheduler() { 363 getForegroundThreadScheduler().advanceToLastPostedRunnable(); 364 } 365 366 /** 367 * Return the background scheduler. 368 * 369 * @return Background scheduler. 370 */ getBackgroundThreadScheduler()371 public static Scheduler getBackgroundThreadScheduler() { 372 return ShadowApplication.getInstance().getBackgroundThreadScheduler(); 373 } 374 375 /** 376 * Execute all runnables that have been enqueued on the background scheduler. 377 */ flushBackgroundThreadScheduler()378 public static void flushBackgroundThreadScheduler() { 379 getBackgroundThreadScheduler().advanceToLastPostedRunnable(); 380 } 381 382 @SuppressWarnings({"NewApi", "unchecked"}) instantiateService(Class<T> serviceClass, Intent intent)383 private static <T extends Service> T instantiateService(Class<T> serviceClass, Intent intent) { 384 if (RuntimeEnvironment.getApiLevel() >= P) { 385 final LoadedApk loadedApk = getLoadedApk(); 386 AppComponentFactory factory = getAppComponentFactory(loadedApk); 387 if (factory != null) { 388 try { 389 Service instance = 390 factory.instantiateService( 391 loadedApk.getClassLoader(), serviceClass.getName(), intent); 392 if (instance != null && serviceClass.isAssignableFrom(instance.getClass())) { 393 return (T) instance; 394 } 395 } catch (ReflectiveOperationException e) { 396 Logger.debug("Failed to instantiate Service using AppComponentFactory", e); 397 } 398 } 399 } 400 return ReflectionHelpers.callConstructor(serviceClass); 401 } 402 403 @SuppressWarnings({"NewApi", "unchecked"}) instantiateContentProvider(Class<T> providerClass)404 private static <T extends ContentProvider> T instantiateContentProvider(Class<T> providerClass) { 405 if (RuntimeEnvironment.getApiLevel() >= P) { 406 final LoadedApk loadedApk = getLoadedApk(); 407 AppComponentFactory factory = getAppComponentFactory(loadedApk); 408 if (factory != null) { 409 try { 410 ContentProvider instance = 411 factory.instantiateProvider(loadedApk.getClassLoader(), providerClass.getName()); 412 if (instance != null && providerClass.isAssignableFrom(instance.getClass())) { 413 return (T) instance; 414 } 415 } catch (ReflectiveOperationException e) { 416 Logger.debug("Failed to instantiate ContentProvider using AppComponentFactory", e); 417 } 418 } 419 } 420 return ReflectionHelpers.callConstructor(providerClass); 421 } 422 423 @SuppressWarnings({"NewApi", "unchecked"}) instantiateActivity(Class<T> activityClass, Intent intent)424 private static <T extends Activity> T instantiateActivity(Class<T> activityClass, Intent intent) { 425 if (RuntimeEnvironment.getApiLevel() >= P) { 426 final LoadedApk loadedApk = getLoadedApk(); 427 AppComponentFactory factory = getAppComponentFactory(loadedApk); 428 if (factory != null) { 429 try { 430 Activity instance = 431 factory.instantiateActivity( 432 loadedApk.getClassLoader(), activityClass.getName(), intent); 433 if (instance != null && activityClass.isAssignableFrom(instance.getClass())) { 434 return (T) instance; 435 } 436 } catch (ReflectiveOperationException e) { 437 Logger.debug("Failed to instantiate Activity using AppComponentFactory", e); 438 } 439 } 440 } 441 return ReflectionHelpers.callConstructor(activityClass); 442 } 443 444 @Nullable 445 @RequiresApi(api = P) getAppComponentFactory(final LoadedApk loadedApk)446 private static AppComponentFactory getAppComponentFactory(final LoadedApk loadedApk) { 447 if (RuntimeEnvironment.getApiLevel() < P) { 448 return null; 449 } 450 if (loadedApk == null || loadedApk.getApplicationInfo().appComponentFactory == null) { 451 return null; 452 } 453 return loadedApk.getAppFactory(); 454 } 455 getLoadedApk()456 private static LoadedApk getLoadedApk() { 457 final ActivityThread activityThread = (ActivityThread) RuntimeEnvironment.getActivityThread(); 458 return activityThread.getPackageInfo( 459 activityThread.getApplication().getApplicationInfo(), null, Context.CONTEXT_INCLUDE_CODE); 460 } 461 } 462