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