• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.annotation;
2 
3 import java.lang.annotation.Documented;
4 import java.lang.annotation.ElementType;
5 import java.lang.annotation.Retention;
6 import java.lang.annotation.RetentionPolicy;
7 import java.lang.annotation.Target;
8 import java.time.Duration;
9 
10 /**
11  * A {@link org.robolectric.pluginapi.config.Configurer} annotation for controlling Robolectric's
12  * {@link android.os.Looper} behavior.
13  *
14  * <p>Currently Robolectric will default to {@link LooperMode.Mode#PAUSED} behavior, but this can be
15  * overridden by applying a @LooperMode(NewMode) annotation to a test package, test class, or test
16  * method, or via the 'robolectric.looperMode' system property.
17  *
18  * @see org.robolectric.plugins.LooperModeConfigurer
19  * @see org.robolectric.util.Scheduler
20  * @see org.robolectric.shadows.ShadowLooper
21  */
22 @Documented
23 @Retention(RetentionPolicy.RUNTIME)
24 @Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD})
25 public @interface LooperMode {
26 
27   /** Specifies the different supported Looper modes. */
28   enum Mode {
29     /**
30      * Robolectric's default threading model prior to 4.4.
31      *
32      * <p>Tasks posted to Loopers are managed via a {@link org.robolectric.util.Scheduler}. {@link
33      * org.robolectric.util.Scheduler} behavior can be controlled via {@link
34      * org.robolectric.util.Scheduler#setIdleState(org.robolectric.util.Scheduler.IdleState)
35      * setIdleState(IdleState)}, with a default of {@link
36      * org.robolectric.util.Scheduler.IdleState#UNPAUSED UNPAUSED}.
37      *
38      * <p>There is only a single Looper thread - with tests and all posted Looper tasks executing on
39      * that thread.
40      *
41      * <p>{@link org.robolectric.shadows.ShadowLooper} APIs can also be used to control posted
42      * tasks, but most of those APIs just serve as a facade to {@link
43      * org.robolectric.util.Scheduler} APIs.
44      *
45      * <p>There are multiple problems with this mode. Some of the major ones are:
46      *
47      * <ol>
48      *   <li>The default {@link org.robolectric.util.Scheduler.IdleState#UNPAUSED UNPAUSED} state
49      *       will execute tasks posted to a {@link android.os.Looper} inline synchronously. This
50      *       differs from real Android behaviour, and can cause issues with code that
51      *       expects/enforces that posted tasks execute in the correct order, such as RecyclerViews.
52      *   <li>The {@link org.robolectric.util.Scheduler} list of Runnables can get out of sync with
53      *       the Looper's {@link android.os.MessageQueue}, causing deadlocks or other race
54      *       conditions.
55      *   <li>Each {@link org.robolectric.util.Scheduler} keeps its own time value, which can get out
56      *       of sync.
57      *   <li>Background {@link android.os.Looper} tasks execute in the main thread, causing errors
58      *       for code that enforces that it runs on a non-main {@link android.os.Looper} thread.
59      * </ol>
60      *
61      * @deprecated use LooperMode.PAUSED
62      */
63     @Deprecated
64     LEGACY,
65 
66     /**
67      * A mode that more accurately models real Android's {@link android.os.Looper} behavior.
68      *
69      * <p>Conceptually LooperMode.PAUSED is similar to the LEGACY {@link
70      * org.robolectric.util.Scheduler.IdleState#PAUSED} in the following ways:
71      *
72      * <ul>
73      *   <li>Tests run on the main looper thread
74      *   <li>Tasks posted to the main {@link android.os.Looper} are not executed automatically, and
75      *       must be explicitly executed via {@link org.robolectric.shadows.ShadowLooper} APIs like
76      *       {@link org.robolectric.shadows.ShadowLooper#idle()}. This guarantees execution order
77      *       correctness
78      *   <li>{@link android.os.SystemClock} time is frozen, and can be manually advanced via
79      *       Robolectric APIs.
80      * </ul>
81      *
82      * However, it has the following improvements:
83      *
84      * <ul>
85      *   <li>Robolectric will warn users if a test fails with unexecuted tasks in the main Looper
86      *       queue
87      *   <li>Robolectric test APIs, like {@link
88      *       org.robolectric.android.controller.ActivityController#setup()}, will automatically idle
89      *       the main {@link android.os.Looper}
90      *   <li>Each {@link android.os.Looper} has its own thread. Tasks posted to background loopers
91      *       are executed asynchronously in separate threads.
92      *   <li>{@link android.os.Looper} use the real {@link android.os.MessageQueue} to store their
93      *       queue of pending tasks
94      *   <li>There is only a single clock value, managed via {@link
95      *       org.robolectric.shadows.ShadowSystemClock}. This can be explictly incremented via
96      *       {@link org.robolectric.shadows.ShadowSystemClock#advanceBy(Duration)}, or {@link
97      *       org.robolectric.shadows.ShadowLooper#idleFor(Duration)}.
98      * </ul>
99      *
100      * A subset of the {@link org.robolectric.util.Scheduler} APIs for the 'foreground' scheduler
101      * are currently supported in this mode as well, although it is recommended to switch to use
102      * ShadowLooper APIs directly.
103      *
104      * <p>To use:
105      *
106      * <ul>
107      *   <li>Apply the LooperMode(PAUSED) annotation to your test package/class/method (or remove a
108      *       LooperMode(LEGACY) annotation)
109      *   <li>Convert any background {@link org.robolectric.util.Scheduler} for controlling {@link
110      *       android.os.Looper}s to shadowOf(looper)
111      *   <li>Convert any {@link org.robolectric.android.util.concurrent.RoboExecutorService} usages
112      *       to {@link org.robolectric.android.util.concurrent.PausedExecutorService} or {@link
113      *       org.robolectric.android.util.concurrent.InlineExecutorService}
114      *   <li>Run your tests. If you see an test failures like 'Main looper has queued unexecuted
115      *       runnables.', you may need to insert shadowOf(getMainLooper()).idle() calls to your test
116      *       to drain the main Looper.
117      * </ul>
118      */
119     PAUSED,
120 
121     /**
122      * A mode that simulates an android instrumentation test threading model, which has a separate
123      * test thread distinct from the main looper thread.
124      *
125      * <p>Otherwise it is quite similar to PAUSED mode. The clock time is still fixed, and you can
126      * use shadowLooper methods to pause, unpause, and wait for any looper to be idle.
127      *
128      * <p>It is recommended to use this mode in tests that mostly use androidx.test APIs, which will
129      * support being called directly on the main thread or on the test thread. Most org.robolectric
130      * APIs that interact with the android UI (e.g. ActivityController) will raise an exception if
131      * called off the main thread.
132      */
133     INSTRUMENTATION_TEST,
134 
135     /**
136      * Currently not supported.
137      *
138      * <p>In future, will have free running threads with an automatically increasing clock.
139      */
140     // RUNNING
141   }
142 
143   /** Set the Looper mode. */
value()144   Mode value();
145 }
146