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