Add non-blocking message processing

TestableLooper.processMessages will block if the argument is less than
the number of available messages.

This adds a new method that will process the minimum of the argument or
available messages and return the number of messages processed.

Test: atest TestableLooperTest
Bug: 289042855
Change-Id: Ied9a3769a0ed2ed7723190853f9091721539df08
This commit is contained in:
Fabián Kozynski
2023-07-12 12:36:07 -04:00
parent 1ad38e26cc
commit c5f5ee57fb
2 changed files with 68 additions and 14 deletions

View File

@@ -32,6 +32,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* This is a wrapper around {@link TestLooperManager} to make it easier to manage
@@ -55,7 +56,6 @@ public class TestableLooper {
private MessageHandler mMessageHandler;
private Handler mHandler;
private Runnable mEmptyMessage;
private TestLooperManager mQueueWrapper;
static {
@@ -121,14 +121,39 @@ public class TestableLooper {
* @param num Number of messages to parse
*/
public int processMessages(int num) {
return processMessagesInternal(num, null);
}
private int processMessagesInternal(int num, Runnable barrierRunnable) {
for (int i = 0; i < num; i++) {
if (!parseMessageInt()) {
if (!processSingleMessage(barrierRunnable)) {
return i + 1;
}
}
return num;
}
/**
* Process up to a certain number of messages, not blocking if the queue has less messages than
* that
* @param num the maximum number of messages to process
* @return the number of messages processed. This will be at most {@code num}.
*/
public int processMessagesNonBlocking(int num) {
final AtomicBoolean reachedBarrier = new AtomicBoolean(false);
Runnable barrierRunnable = () -> {
reachedBarrier.set(true);
};
mHandler.post(barrierRunnable);
waitForMessage(mQueueWrapper, mHandler, barrierRunnable);
try {
return processMessagesInternal(num, barrierRunnable) + (reachedBarrier.get() ? -1 : 0);
} finally {
mHandler.removeCallbacks(barrierRunnable);
}
}
/**
* Process messages in the queue until no more are found.
*/
@@ -165,19 +190,20 @@ public class TestableLooper {
private int processQueuedMessages() {
int count = 0;
mEmptyMessage = () -> { };
mHandler.post(mEmptyMessage);
waitForMessage(mQueueWrapper, mHandler, mEmptyMessage);
while (parseMessageInt()) count++;
Runnable barrierRunnable = () -> { };
mHandler.post(barrierRunnable);
waitForMessage(mQueueWrapper, mHandler, barrierRunnable);
while (processSingleMessage(barrierRunnable)) count++;
return count;
}
private boolean parseMessageInt() {
private boolean processSingleMessage(Runnable barrierRunnable) {
try {
Message result = mQueueWrapper.next();
if (result != null) {
// This is a break message.
if (result.getCallback() == mEmptyMessage) {
if (result.getCallback() == barrierRunnable) {
mQueueWrapper.execute(result);
mQueueWrapper.recycle(result);
return false;
}

View File

@@ -27,12 +27,6 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -40,6 +34,11 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableLooper.MessageHandler;
import android.testing.TestableLooper.RunWithLooper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -240,4 +239,33 @@ public class TestableLooperTest {
inOrder.verify(handler).dispatchMessage(messageC);
}
@Test
public void testProcessMessagesNonBlocking_onlyArgNumber() {
Handler h = new Handler(mTestableLooper.getLooper());
Runnable r = mock(Runnable.class);
h.post(r);
h.post(r);
h.post(r);
int processed = mTestableLooper.processMessagesNonBlocking(2);
verify(r, times(2)).run();
assertEquals(2, processed);
}
@Test
public void testProcessMessagesNonBlocking_lessMessagesThanArg() {
Handler h = new Handler(mTestableLooper.getLooper());
Runnable r = mock(Runnable.class);
h.post(r);
h.post(r);
h.post(r);
int processed = mTestableLooper.processMessagesNonBlocking(5);
verify(r, times(3)).run();
assertEquals(3, processed);
}
}