Skip to content

Commit 13a3aa7

Browse files
toniheicopybara-github
authored andcommitted
Add TestPlayerRunHelper run(player).untilBackgroundThreadCondition(..)
This method is useful for cases where the target condition can become true outside of a message on the main thread. To ensure we don't execute the rest of the test method in parallel with other code, we have to introduce artifical messages on the main thread that check the target condition. PiperOrigin-RevId: 628072444
1 parent 00ce572 commit 13a3aa7

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed

libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/RobolectricUtil.java

+24
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ public long elapsedRealtime() {
6363
*
6464
* <p>Must be called on the main test thread.
6565
*
66+
* <p>Note for {@link androidx.media3.test.utils.FakeClock} users: If the condition changes
67+
* outside of a main {@link Looper} message, for example because it's checking a volatile variable
68+
* or shared synchronized state that is updated on a background thread, or because checking the
69+
* condition itself may cause it to become true, then the remainder of the test method may be
70+
* executed in parallel with other background thread messages.
71+
*
6672
* @param condition The condition.
6773
* @throws TimeoutException If the {@link #DEFAULT_TIMEOUT_MS} is exceeded.
6874
*/
@@ -76,6 +82,12 @@ public static void runMainLooperUntil(Supplier<Boolean> condition) throws Timeou
7682
*
7783
* <p>Must be called on the main test thread.
7884
*
85+
* <p>Note for {@link androidx.media3.test.utils.FakeClock} users: If the condition changes
86+
* outside of a main {@link Looper} message, for example because it's checking a volatile variable
87+
* or shared synchronized state that is updated on a background thread, or because checking the
88+
* condition itself may cause it to become true, then the remainder of the test method may be
89+
* executed in parallel with other background thread messages.
90+
*
7991
* @param condition The condition.
8092
* @param timeoutMs The timeout in milliseconds.
8193
* @param clock The {@link Clock} to measure the timeout.
@@ -91,6 +103,12 @@ public static void runMainLooperUntil(Supplier<Boolean> condition, long timeoutM
91103
*
92104
* <p>Must be called on the thread corresponding to the {@code looper}.
93105
*
106+
* <p>Note for {@link androidx.media3.test.utils.FakeClock} users: If the condition changes
107+
* outside of a message on this {@code Looper}, for example because it's checking a volatile
108+
* variable or shared synchronized state that is updated on a background thread, or because
109+
* checking the condition itself may cause it to become true, then the remainder of the test
110+
* method may be executed in parallel with other background thread messages.
111+
*
94112
* @param looper The {@link Looper}.
95113
* @param condition The condition.
96114
* @throws TimeoutException If the {@link #DEFAULT_TIMEOUT_MS} is exceeded.
@@ -105,6 +123,12 @@ public static void runLooperUntil(Looper looper, Supplier<Boolean> condition)
105123
*
106124
* <p>Must be called on the thread corresponding to the {@code looper}.
107125
*
126+
* <p>Note for {@link androidx.media3.test.utils.FakeClock} users: If the condition changes
127+
* outside of a message on this {@code Looper}, for example because it's checking a volatile
128+
* variable or shared synchronized state that is updated on a background thread, or because
129+
* checking the condition itself may cause it to become true, then the remainder of the test
130+
* method may be executed in parallel with other background thread messages.
131+
*
108132
* @param looper The {@link Looper}.
109133
* @param condition The condition.
110134
* @param timeoutMs The timeout in milliseconds.

libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/TestPlayerRunHelper.java

+42
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import androidx.media3.common.Player;
2727
import androidx.media3.common.Timeline;
2828
import androidx.media3.common.util.ConditionVariable;
29+
import androidx.media3.common.util.HandlerWrapper;
2930
import androidx.media3.common.util.NullableType;
3031
import androidx.media3.common.util.UnstableApi;
3132
import androidx.media3.common.util.Util;
@@ -411,6 +412,47 @@ public void untilPendingCommandsAreFullyHandled() throws Exception {
411412
runMainLooperUntil(receivedMessageCallback::get);
412413
}
413414

415+
/**
416+
* Runs tasks of the main {@link Looper} until the specified condition becomes true independent
417+
* of a message on the main {@link Looper}.
418+
*
419+
* <p>This method is useful for cases where the condition may change outside of a main {@link
420+
* Looper} message, for example because it's checking a volatile variable or shared synchronized
421+
* state that is updated on a background thread, or because checking the condition itself may
422+
* cause it to become true.
423+
*
424+
* <p>This method ensures the condition is checked within artificially created main {@link
425+
* Looper} messages. When using a {@link androidx.media3.test.utils.FakeClock}, this guarantees
426+
* the remainder of the test method is not executed in parallel with other background thread
427+
* messages.
428+
*
429+
* @param backgroundThreadCondition The condition to wait for.
430+
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
431+
* exceeded.
432+
*/
433+
public void untilBackgroundThreadCondition(Supplier<Boolean> backgroundThreadCondition)
434+
throws Exception {
435+
if (backgroundThreadCondition.get()) {
436+
return;
437+
}
438+
AtomicBoolean conditionTrue = new AtomicBoolean();
439+
HandlerWrapper handler =
440+
player.getClock().createHandler(Util.getCurrentOrMainLooper(), /* callback= */ null);
441+
Runnable checkCondition =
442+
new Runnable() {
443+
@Override
444+
public void run() {
445+
if (backgroundThreadCondition.get()) {
446+
conditionTrue.set(true);
447+
} else {
448+
handler.postDelayed(this, /* delayMs= */ 1);
449+
}
450+
}
451+
};
452+
handler.post(checkCondition);
453+
runUntil(conditionTrue::get);
454+
}
455+
414456
@Override
415457
public ExoPlayerRunResult ignoringNonFatalErrors() {
416458
checkState(!hasBeenUsed);

0 commit comments

Comments
 (0)