org.robolectric.annotation.LooperMode Maven / Gradle / Ivy
Show all versions of annotations Show documentation
package org.robolectric.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* A {@link org.robolectric.pluginapi.config.Configurer} annotation for controlling Robolectric's
* {@link android.os.Looper} behavior.
*
* Currently Robolectric will default to {@link LooperMode.Mode#PAUSED} behavior, but this can be
* overridden by applying a @LooperMode(NewMode) annotation to a test package, test class, or test
* method, or via the 'robolectric.looperMode' system property.
*
* @see org.robolectric.plugins.LooperModeConfigurer
* @see org.robolectric.util.Scheduler
* @see org.robolectric.shadows.ShadowLooper
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD})
public @interface LooperMode {
/** Specifies the different supported Looper modes. */
enum Mode {
/**
* Robolectric's default threading model prior to 4.4.
*
*
Tasks posted to Loopers are managed via a {@link org.robolectric.util.Scheduler}. {@link
* org.robolectric.util.Scheduler} behavior can be controlled via {@link
* org.robolectric.util.Scheduler#setIdleState(org.robolectric.util.Scheduler.IdleState)
* setIdleState(IdleState)}, with a default of {@link
* org.robolectric.util.Scheduler.IdleState#UNPAUSED UNPAUSED}.
*
*
There is only a single Looper thread - with tests and all posted Looper tasks executing on
* that thread.
*
*
{@link org.robolectric.shadows.ShadowLooper} APIs can also be used to control posted
* tasks, but most of those APIs just serve as a facade to {@link
* org.robolectric.util.Scheduler} APIs.
*
*
There are multiple problems with this mode. Some of the major ones are:
*
*
* - The default {@link org.robolectric.util.Scheduler.IdleState#UNPAUSED UNPAUSED} state
* will execute tasks posted to a {@link android.os.Looper} inline synchronously. This
* differs from real Android behaviour, and can cause issues with code that
* expects/enforces that posted tasks execute in the correct order, such as RecyclerViews.
*
- The {@link org.robolectric.util.Scheduler} list of Runnables can get out of sync with
* the Looper's {@link android.os.MessageQueue}, causing deadlocks or other race
* conditions.
*
- Each {@link org.robolectric.util.Scheduler} keeps its own time value, which can get out
* of sync.
*
- Background {@link android.os.Looper} tasks execute in the main thread, causing errors
* for code that enforces that it runs on a non-main {@link android.os.Looper} thread.
*
*
* @deprecated use LooperMode.PAUSED
*/
@Deprecated
LEGACY,
/**
* A mode that more accurately models real Android's {@link android.os.Looper} behavior.
*
* Conceptually LooperMode.PAUSED is similar to the LEGACY {@link
* org.robolectric.util.Scheduler.IdleState#PAUSED} in the following ways:
*
*
* - Tests run on the main looper thread
*
- Tasks posted to the main {@link android.os.Looper} are not executed automatically, and
* must be explicitly executed via {@link org.robolectric.shadows.ShadowLooper} APIs like
* {@link org.robolectric.shadows.ShadowLooper#idle()}. This guarantees execution order
* correctness
*
- {@link android.os.SystemClock} time is frozen, and can be manually advanced via
* Robolectric APIs.
*
*
* However, it has the following improvements:
*
*
* - Robolectric will warn users if a test fails with unexecuted tasks in the main Looper
* queue
*
- Robolectric test APIs, like {@link
* org.robolectric.android.controller.ActivityController#setup()}, will automatically idle
* the main {@link android.os.Looper}
*
- Each {@link android.os.Looper} has its own thread. Tasks posted to background loopers
* are executed asynchronously in separate threads.
*
- {@link android.os.Looper} use the real {@link android.os.MessageQueue} to store their
* queue of pending tasks
*
- There is only a single clock value, managed via {@link
* org.robolectric.shadows.ShadowSystemClock}. This can be explictly incremented via
* {@link android.os.SystemClock#setCurrentTimeMillis(long)}, or {@link
* org.robolectric.shadows.ShadowLooper#idleFor(Duration)}.
*
*
* A subset of the {@link org.robolectric.util.Scheduler} APIs for the 'foreground' scheduler
* are currently supported in this mode as well, although it is recommended to switch to use
* ShadowLooper APIs directly.
*
* To use:
*
*
* - Apply the LooperMode(PAUSED) annotation to your test package/class/method (or remove a
* LooperMode(LEGACY) annotation)
*
- Convert any background {@link org.robolectric.util.Scheduler} for controlling {@link
* android.os.Looper}s to shadowOf(looper)
*
- Convert any {@link org.robolectric.android.util.concurrent.RoboExecutorService} usages
* to {@link org.robolectric.android.util.concurrent.PausedExecutorService} or {@link
* org.robolectric.android.util.concurrent.InlineExecutorService}
*
- Run your tests. If you see an test failures like 'Main looper has queued unexecuted
* runnables.', you may need to insert shadowOf(getMainLooper()).idle() calls to your test
* to drain the main Looper.
*
*/
PAUSED,
/**
* A mode that simulates an android instrumentation test threading model, which has a separate
* test thread distinct from the main looper thread.
*
* Otherwise it is quite similar to PAUSED mode. The clock time is still fixed, and you can
* use shadowLooper methods to pause, unpause, and wait for any looper to be idle.
*
*
It is recommended to use this mode in tests that mostly use androidx.test APIs, which will
* support being called directly on the main thread or on the test thread. Most org.robolectric
* APIs that interact with the android UI (e.g. ActivityController) will raise an exception if
* called off the main thread.
*/
INSTRUMENTATION_TEST,
/**
* Currently not supported.
*
*
In future, will have free running threads with an automatically increasing clock.
*/
// RUNNING
}
/** Set the Looper mode. */
Mode value();
}