diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeClock.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeClock.java index 0c21c1cfae..4dea57f087 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeClock.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeClock.java @@ -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 diff --git a/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java index 18a0f04116..c6b39d7f27 100644 --- a/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java +++ b/testutils/src/test/java/com/google/android/exoplayer2/testutil/FakeClockTest.java @@ -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]);