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

Commit 1a9ecd5c authored by Chris Li's avatar Chris Li
Browse files

Deflake ActivityWindowInfo tests

Because relayout can trigger the callback with the actual window size,
make sure to test on the main thread.

Bug: 330669647
Test: atest FrameworksCoreTests:ActivityThreadTest
Change-Id: I9eb42c608c8dc89a391cb0a0ccefa2d70ca1a778
parent 7417d528
Loading
Loading
Loading
Loading
+106 −70
Original line number Diff line number Diff line
@@ -34,9 +34,11 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.annotation.NonNull;
@@ -67,7 +69,6 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.DisplayMetrics;
@@ -90,7 +91,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
@@ -123,9 +123,7 @@ public class ActivityThreadTest {
    @Rule(order = 1)
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);

    @Mock
    private BiConsumer<IBinder, ActivityWindowInfo> mActivityWindowInfoListener;

    private ActivityWindowInfoListener mActivityWindowInfoListener;
    private WindowTokenClientController mOriginalWindowTokenClientController;
    private Configuration mOriginalAppConfig;

@@ -140,6 +138,7 @@ public class ActivityThreadTest {
        mOriginalWindowTokenClientController = WindowTokenClientController.getInstance();
        mOriginalAppConfig = new Configuration(ActivityThread.currentActivityThread()
                .getConfiguration());
        mActivityWindowInfoListener = spy(new ActivityWindowInfoListener());
    }

    @After
@@ -808,66 +807,73 @@ public class ActivityThreadTest {
    @Test
    public void testActivityWindowInfoChanged_activityLaunch() {
        mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG);

        ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener(
                mActivityWindowInfoListener);

        final Activity activity = mActivityTestRule.launchActivity(new Intent());
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
        mActivityWindowInfoListener.await();
        final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity);

        verify(mActivityWindowInfoListener).accept(activityClientRecord.token,
        // In case the system change the window after launch, there can be more than one callback.
        verify(mActivityWindowInfoListener, atLeastOnce()).accept(activityClientRecord.token,
                activityClientRecord.getActivityWindowInfo());
    }

    @Test
    public void testActivityWindowInfoChanged_activityRelaunch() throws RemoteException {
    public void testActivityWindowInfoChanged_activityRelaunch() {
        mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG);

        ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener(
                mActivityWindowInfoListener);

        final Activity activity = mActivityTestRule.launchActivity(new Intent());
        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
        appThread.scheduleTransaction(newRelaunchResumeTransaction(activity));
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
        mActivityWindowInfoListener.await();
        final ActivityClientRecord activityClientRecord = getActivityClientRecord(activity);

        // Run on main thread to avoid racing from updating from window relayout.
        final ActivityThread activityThread = activity.getActivityThread();
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
            // Try relaunch with the same ActivityWindowInfo
            clearInvocations(mActivityWindowInfoListener);
            activityThread.executeTransaction(newRelaunchResumeTransaction(activity));

            // The same ActivityWindowInfo won't trigger duplicated callback.
        verify(mActivityWindowInfoListener).accept(activityClientRecord.token,
            verify(mActivityWindowInfoListener, never()).accept(activityClientRecord.token,
                    activityClientRecord.getActivityWindowInfo());

            // Try relaunch with different ActivityWindowInfo
            final Configuration currentConfig = activity.getResources().getConfiguration();
        final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
        activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000),
            final ActivityWindowInfo newInfo = new ActivityWindowInfo();
            newInfo.set(true /* isEmbedded */, new Rect(0, 0, 1000, 2000),
                    new Rect(0, 0, 1000, 1000));
            final ActivityRelaunchItem relaunchItem = ActivityRelaunchItem.obtain(
                    activity.getActivityToken(), null, null, 0,
                    new MergedConfiguration(currentConfig, currentConfig),
                false /* preserveWindow */, activityWindowInfo);
                    false /* preserveWindow */, newInfo);
            final ClientTransaction transaction = newTransaction(activity);
            transaction.addTransactionItem(relaunchItem);
        appThread.scheduleTransaction(transaction);
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();

        verify(mActivityWindowInfoListener).accept(activityClientRecord.token,
                activityWindowInfo);
            clearInvocations(mActivityWindowInfoListener);
            activityThread.executeTransaction(transaction);

            // Trigger callback with a different ActivityWindowInfo
            verify(mActivityWindowInfoListener).accept(activityClientRecord.token, newInfo);
        });
    }

    @Test
    public void testActivityWindowInfoChanged_activityConfigurationChanged()
            throws RemoteException {
    public void testActivityWindowInfoChanged_activityConfigurationChanged() {
        mSetFlagsRule.enableFlags(FLAG_ACTIVITY_WINDOW_INFO_FLAG);

        ClientTransactionListenerController.getInstance().registerActivityWindowInfoChangedListener(
                mActivityWindowInfoListener);

        final Activity activity = mActivityTestRule.launchActivity(new Intent());
        final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
        mActivityWindowInfoListener.await();

        clearInvocations(mActivityWindowInfoListener);
        final Configuration config = new Configuration(activity.getResources().getConfiguration());
        final ActivityThread activityThread = activity.getActivityThread();
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
            // Trigger callback with different ActivityWindowInfo
            final Configuration config = new Configuration(activity.getResources()
                    .getConfiguration());
            config.seq++;
            final Rect taskBounds = new Rect(0, 0, 1000, 2000);
            final Rect taskFragmentBounds = new Rect(0, 0, 1000, 1000);
@@ -878,26 +884,30 @@ public class ActivityThreadTest {
                            activity.getActivityToken(), config, activityWindowInfo);
            final ClientTransaction transaction = newTransaction(activity);
            transaction.addTransactionItem(activityConfigurationChangeItem);
        appThread.scheduleTransaction(transaction);
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();

            clearInvocations(mActivityWindowInfoListener);
            activityThread.executeTransaction(transaction);

            // Trigger callback with a different ActivityWindowInfo
            verify(mActivityWindowInfoListener).accept(activity.getActivityToken(),
                    activityWindowInfo);

        clearInvocations(mActivityWindowInfoListener);
        final ActivityWindowInfo activityWindowInfo2 = new ActivityWindowInfo();
        activityWindowInfo2.set(true /* isEmbedded */, taskBounds, taskFragmentBounds);
            // Try callback with the same ActivityWindowInfo
            final ActivityWindowInfo activityWindowInfo2 =
                    new ActivityWindowInfo(activityWindowInfo);
            config.seq++;
            final ActivityConfigurationChangeItem activityConfigurationChangeItem2 =
                    ActivityConfigurationChangeItem.obtain(
                            activity.getActivityToken(), config, activityWindowInfo2);
            final ClientTransaction transaction2 = newTransaction(activity);
            transaction2.addTransactionItem(activityConfigurationChangeItem2);
        appThread.scheduleTransaction(transaction);
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();

            clearInvocations(mActivityWindowInfoListener);
            activityThread.executeTransaction(transaction);

            // The same ActivityWindowInfo won't trigger duplicated callback.
            verify(mActivityWindowInfoListener, never()).accept(any(), any());
        });
    }

    /**
@@ -958,10 +968,12 @@ public class ActivityThreadTest {
    @NonNull
    private static ClientTransaction newRelaunchResumeTransaction(@NonNull Activity activity) {
        final Configuration currentConfig = activity.getResources().getConfiguration();
        final ActivityWindowInfo activityWindowInfo = getActivityClientRecord(activity)
                .getActivityWindowInfo();
        final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(
                activity.getActivityToken(), null, null, 0,
                new MergedConfiguration(currentConfig, currentConfig),
                false /* preserveWindow */, new ActivityWindowInfo());
                false /* preserveWindow */, activityWindowInfo);
        final ResumeActivityItem resumeStateRequest =
                ResumeActivityItem.obtain(activity.getActivityToken(), true /* isForward */,
                        false /* shouldSendCompatFakeFocus*/);
@@ -1127,4 +1139,28 @@ public class ActivityThreadTest {
            return mPipEnterSkipped;
        }
    }

    public static class ActivityWindowInfoListener implements
            BiConsumer<IBinder, ActivityWindowInfo> {

        CountDownLatch mCallbackLatch = new CountDownLatch(1);

        @Override
        public void accept(@NonNull IBinder activityToken,
                @NonNull ActivityWindowInfo activityWindowInfo) {
            mCallbackLatch.countDown();
        }

        /**
         * When the test is expecting to receive a callback, waits until the callback is triggered.
         */
        void await() {
            InstrumentationRegistry.getInstrumentation().waitForIdleSync();
            try {
                mCallbackLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}