• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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