Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db1a8279 authored by Shai Barack's avatar Shai Barack Committed by Mark Fasheh
Browse files

Add peekWhen() and pop() methods to TestLooperManager.

See: go/testable-messagequeue-bluedoc
Also promoted TestLooperManagerTest to CTS, and deleted
the unit test that's now redundant. See: ag/30617344

Bug: 379472827
Flag: android.os.message_queue_testability
Change-Id: I2d2b057dcc77fc75b27d937bbbf5de0702e2e300
parent fdd36b53
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -34721,6 +34721,8 @@ package android.os {
    method public boolean hasMessages(android.os.Handler, Object, int);
    method public boolean hasMessages(android.os.Handler, Object, Runnable);
    method public android.os.Message next();
    method @FlaggedApi("android.os.message_queue_testability") @Nullable public Long peekWhen();
    method @FlaggedApi("android.os.message_queue_testability") @Nullable public android.os.Message pop();
    method public void recycle(android.os.Message);
    method public void release();
  }
+32 −3
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@

package android.os;

import android.annotation.FlaggedApi;
import android.annotation.Nullable;
import android.util.ArraySet;

import java.util.concurrent.LinkedBlockingQueue;
@@ -93,9 +95,36 @@ public class TestLooperManager {
    }

    /**
     * Releases the looper to continue standard looping and processing of messages,
     * no further interactions with TestLooperManager will be allowed after
     * release() has been called.
     * Returns the next message that should be executed by this queue, and removes it from the
     * queue. If the queue is empty or no messages are deliverable, returns null.
     * This method never blocks.
     *
     * <p>Callers should always call {@link #recycle(Message)} on the message when all interactions
     * with it have completed.
     */
    @FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
    @Nullable
    public Message pop() {
        checkReleased();
        return mQueue.popForTest();
    }

    /**
     * Returns the values of {@link Message#when} of the next message that should be executed by
     * this queue. If the queue is empty or no messages are deliverable, returns null.
     * This method never blocks.
     */
    @FlaggedApi(Flags.FLAG_MESSAGE_QUEUE_TESTABILITY)
    @SuppressWarnings("AutoBoxing")  // box the primitive long, or return null to indicate no value
    @Nullable
    public Long peekWhen() {
        checkReleased();
        return mQueue.peekWhenForTest();
    }

    /**
     * Releases the looper to continue standard looping and processing of messages, no further
     * interactions with TestLooperManager will be allowed after release() has been called.
     */
    public void release() {
        synchronized (sHeldLoopers) {
+0 −91
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import android.platform.test.ravenwood.RavenwoodRule;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@RunWith(AndroidJUnit4.class)
public class TestLooperManagerTest {
    private static final String TAG = "TestLooperManagerTest";

    @Rule
    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
            .setProvideMainThread(true)
            .build();

    @Test
    public void testMainThread() throws Exception {
        doTest(Looper.getMainLooper());
    }

    @Test
    public void testCustomThread() throws Exception {
        final HandlerThread thread = new HandlerThread(TAG);
        thread.start();
        doTest(thread.getLooper());
    }

    private void doTest(Looper looper) throws Exception {
        final TestLooperManager tlm =
                InstrumentationRegistry.getInstrumentation().acquireLooperManager(looper);

        final Handler handler = new Handler(looper);
        final CountDownLatch latch = new CountDownLatch(1);

        assertFalse(tlm.hasMessages(handler, null, 42));

        handler.sendEmptyMessage(42);
        handler.post(() -> {
            latch.countDown();
        });
        assertTrue(tlm.hasMessages(handler, null, 42));
        assertFalse(latch.await(100, TimeUnit.MILLISECONDS));

        final Message first = tlm.next();
        assertEquals(42, first.what);
        assertNull(first.callback);
        tlm.execute(first);
        assertFalse(tlm.hasMessages(handler, null, 42));
        assertFalse(latch.await(100, TimeUnit.MILLISECONDS));
        tlm.recycle(first);

        final Message second = tlm.next();
        assertNotNull(second.callback);
        tlm.execute(second);
        assertFalse(tlm.hasMessages(handler, null, 42));
        assertTrue(latch.await(100, TimeUnit.MILLISECONDS));
        tlm.recycle(second);

        tlm.release();
    }
}