Fix FakeClock remove messages behaviour.

We currently only remove messages that have already been sent
to the actual Handler, not the pending ones that are only kept
in the FakeClock. Fix this by also removing matching messages
from the FakeClock list.

PiperOrigin-RevId: 353877049
This commit is contained in:
tonihei 2021-01-26 16:58:55 +00:00 committed by Ian Baker
parent 89ea38d155
commit a318e56d15
2 changed files with 105 additions and 2 deletions

View File

@ -108,6 +108,27 @@ public class FakeClock implements Clock {
maybeTriggerMessages();
}
private synchronized void removePendingHandlerMessages(ClockHandler handler, int what) {
for (int i = handlerMessages.size() - 1; i >= 0; i--) {
HandlerMessage message = handlerMessages.get(i);
if (message.handler.equals(handler) && message.what == what) {
handlerMessages.remove(i);
}
}
handler.handler.removeMessages(what);
}
private synchronized void removePendingHandlerMessages(
ClockHandler handler, @Nullable Object token) {
for (int i = handlerMessages.size() - 1; i >= 0; i--) {
HandlerMessage message = handlerMessages.get(i);
if (message.handler.equals(handler) && (token == null || message.obj == token)) {
handlerMessages.remove(i);
}
}
handler.handler.removeCallbacksAndMessages(token);
}
private synchronized boolean hasPendingMessage(ClockHandler handler, int what) {
for (int i = 0; i < handlerMessages.size(); i++) {
HandlerMessage message = handlerMessages.get(i);
@ -261,12 +282,12 @@ public class FakeClock implements Clock {
@Override
public void removeMessages(int what) {
handler.removeMessages(what);
removePendingHandlerMessages(/* handler= */ this, what);
}
@Override
public void removeCallbacksAndMessages(@Nullable Object token) {
handler.removeCallbacksAndMessages(token);
removePendingHandlerMessages(/* handler= */ this, token);
}
@Override

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.testutil;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.Shadows.shadowOf;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
@ -186,6 +187,87 @@ public final class FakeClockTest {
assertTestRunnableStates(new boolean[] {true, true, true, true, true}, testRunnables);
}
@Test
public void createHandler_removeMessages_removesMessages() {
HandlerThread handlerThread = new HandlerThread("FakeClockTest");
handlerThread.start();
FakeClock fakeClock = new FakeClock(/* initialTimeMs= */ 0);
TestCallback callback = new TestCallback();
HandlerWrapper handler = fakeClock.createHandler(handlerThread.getLooper(), callback);
TestCallback otherCallback = new TestCallback();
HandlerWrapper otherHandler = fakeClock.createHandler(handlerThread.getLooper(), otherCallback);
// Block any further execution on the HandlerThread until we had a chance to remove messages.
ConditionVariable startCondition = new ConditionVariable();
handler.post(startCondition::block);
TestRunnable testRunnable1 = new TestRunnable();
TestRunnable testRunnable2 = new TestRunnable();
Object messageToken = new Object();
handler.obtainMessage(/* what= */ 1, /* obj= */ messageToken).sendToTarget();
handler.sendEmptyMessageDelayed(/* what= */ 2, /* delayMs= */ 50);
handler.post(testRunnable1);
handler.postDelayed(testRunnable2, /* delayMs= */ 25);
handler.sendEmptyMessage(/* what= */ 3);
otherHandler.sendEmptyMessage(/* what= */ 2);
handler.removeMessages(/* what= */ 2);
handler.removeCallbacksAndMessages(messageToken);
startCondition.open();
fakeClock.advanceTime(50);
shadowOf(handlerThread.getLooper()).idle();
assertThat(callback.messages)
.containsExactly(
new MessageData(/* what= */ 3, /* arg1= */ 0, /* arg2= */ 0, /* obj=*/ null));
assertThat(testRunnable1.hasRun).isTrue();
assertThat(testRunnable2.hasRun).isTrue();
// Assert that message with same "what" on other handler wasn't removed.
assertThat(otherCallback.messages)
.containsExactly(
new MessageData(/* what= */ 2, /* arg1= */ 0, /* arg2= */ 0, /* obj=*/ null));
}
@Test
public void createHandler_removeAllMessages_removesAllMessages() {
HandlerThread handlerThread = new HandlerThread("FakeClockTest");
handlerThread.start();
FakeClock fakeClock = new FakeClock(/* initialTimeMs= */ 0);
TestCallback callback = new TestCallback();
HandlerWrapper handler = fakeClock.createHandler(handlerThread.getLooper(), callback);
TestCallback otherCallback = new TestCallback();
HandlerWrapper otherHandler = fakeClock.createHandler(handlerThread.getLooper(), otherCallback);
// Block any further execution on the HandlerThread until we had a chance to remove messages.
ConditionVariable startCondition = new ConditionVariable();
handler.post(startCondition::block);
TestRunnable testRunnable1 = new TestRunnable();
TestRunnable testRunnable2 = new TestRunnable();
Object messageToken = new Object();
handler.obtainMessage(/* what= */ 1, /* obj= */ messageToken).sendToTarget();
handler.sendEmptyMessageDelayed(/* what= */ 2, /* delayMs= */ 50);
handler.post(testRunnable1);
handler.postDelayed(testRunnable2, /* delayMs= */ 25);
handler.sendEmptyMessage(/* what= */ 3);
otherHandler.sendEmptyMessage(/* what= */ 1);
handler.removeCallbacksAndMessages(/* token= */ null);
startCondition.open();
fakeClock.advanceTime(50);
shadowOf(handlerThread.getLooper()).idle();
assertThat(callback.messages).isEmpty();
assertThat(testRunnable1.hasRun).isFalse();
assertThat(testRunnable2.hasRun).isFalse();
// Assert that message on other handler wasn't removed.
assertThat(otherCallback.messages)
.containsExactly(
new MessageData(/* what= */ 1, /* arg1= */ 0, /* arg2= */ 0, /* obj=*/ null));
}
private static void assertTestRunnableStates(boolean[] states, TestRunnable[] testRunnables) {
for (int i = 0; i < testRunnables.length; i++) {
assertThat(testRunnables[i].hasRun).isEqualTo(states[i]);