package org.robolectric.shadows; import java.util.ArrayList; import java.util.List; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.TestRunners; import org.robolectric.util.Scheduler; import static org.assertj.core.api.Assertions.assertThat; import static org.robolectric.Shadows.shadowOf; import static org.robolectric.util.ReflectionHelpers.*; import static org.robolectric.util.ReflectionHelpers.ClassParameter.from; @RunWith(TestRunners.MultiApiSelfTest.class) public class ShadowMessageQueueTest { private Looper looper; private MessageQueue queue; private ShadowMessageQueue shadowQueue; private Message testMessage; private TestHandler handler; private Scheduler scheduler; private String quitField; private static class TestHandler extends Handler { public List<Message> handled = new ArrayList<>(); public TestHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { handled.add(msg); } } private static Looper newLooper() { return newLooper(true); } private static Looper newLooper(boolean canQuit) { return callConstructor(Looper.class, from(boolean.class, canQuit)); } @Before public void setUp() throws Exception { // Queues and loopers are closely linked; can't easily test one without the other. looper = newLooper(); handler = new TestHandler(looper); queue = looper.getQueue(); shadowQueue = shadowOf(queue); scheduler = shadowQueue.getScheduler(); scheduler.pause(); testMessage = handler.obtainMessage(); quitField = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT ? "mQuitting" : "mQuiting"; } @Test public void test_setGetHead() { shadowQueue.setHead(testMessage); assertThat(shadowQueue.getHead()).as("getHead()").isSameAs(testMessage); } private boolean enqueueMessage(Message msg, long when) { return callInstanceMethod(queue, "enqueueMessage", from(Message.class, msg), from(long.class, when) ); } private void removeMessages(Handler handler, int what, Object token) { callInstanceMethod(queue, "removeMessages", from(Handler.class, handler), from(int.class, what), from(Object.class, token) ); } @Test public void enqueueMessage_setsHead() { enqueueMessage(testMessage, 100); assertThat(shadowQueue.getHead()).as("head").isSameAs(testMessage); } @Test public void enqueueMessage_returnsTrue() { assertThat(enqueueMessage(testMessage, 100)).as("retval").isTrue(); } @Test public void enqueueMessage_setsWhen() { enqueueMessage(testMessage, 123); assertThat(testMessage.getWhen()).as("when").isEqualTo(123); } @Test public void enqueueMessage_returnsFalse_whenQuitting() { setField(queue, quitField, true); assertThat(enqueueMessage(testMessage, 1)).as("enqueueMessage()").isFalse(); } @Test public void enqueueMessage_doesntSchedule_whenQuitting() { setField(queue, quitField, true); enqueueMessage(testMessage, 1); assertThat(scheduler.size()).as("scheduler_size").isEqualTo(0); } @Test public void enqueuedMessage_isSentToHandler() { enqueueMessage(testMessage, 200); scheduler.advanceTo(199); assertThat(handler.handled).as("handled:before").isEmpty(); scheduler.advanceTo(200); assertThat(handler.handled).as("handled:after").containsExactly(testMessage); } @Test public void removedMessage_isNotSentToHandler() { enqueueMessage(testMessage, 200); assertThat(scheduler.size()).as("scheduler size:before").isEqualTo(1); removeMessages(handler, testMessage.what, null); scheduler.advanceToLastPostedRunnable(); assertThat(scheduler.size()).as("scheduler size:after").isEqualTo(0); assertThat(handler.handled).as("handled").isEmpty(); } @Test public void enqueueMessage_withZeroWhen_postsAtFront() { enqueueMessage(testMessage, 0); Message m2 = handler.obtainMessage(2); enqueueMessage(m2, 0); scheduler.advanceToLastPostedRunnable(); assertThat(handler.handled).as("handled").containsExactly(m2, testMessage); } @Test public void dispatchedMessage_isMarkedInUse_andRecycled() { Handler handler = new Handler(looper) { @Override public void handleMessage(Message msg) { boolean inUse = callInstanceMethod(msg, "isInUse"); assertThat(inUse).as(msg.what + ":inUse").isTrue(); Message next = getField(msg, "next"); assertThat(next).as(msg.what + ":next").isNull(); } }; Message msg = handler.obtainMessage(1); enqueueMessage(msg, 200); Message msg2 = handler.obtainMessage(2); enqueueMessage(msg2, 205); scheduler.advanceToNextPostedRunnable(); // Check that it's been properly recycled. assertThat(msg.what).as("msg.what").isZero(); scheduler.advanceToNextPostedRunnable(); assertThat(msg2.what).as("msg2.what").isZero(); } @Test public void reset_shouldClearMessageQueue() { Message msg = handler.obtainMessage(1234); Message msg2 = handler.obtainMessage(5678); handler.sendMessage(msg); handler.sendMessage(msg2); assertThat(handler.hasMessages(1234)).as("before-1234").isTrue(); assertThat(handler.hasMessages(5678)).as("before-5678").isTrue(); shadowQueue.reset(); assertThat(handler.hasMessages(1234)).as("after-1234").isFalse(); assertThat(handler.hasMessages(5678)).as("after-5678").isFalse(); } }