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