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

Commit 28a69b96 authored by Liran Binyamin's avatar Liran Binyamin Committed by Android (Google) Code Review
Browse files

Merge "Fix flaky tests in ExpandedAnimationControllerTest" into main

parents 9f09a316 e56b64dd
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -563,7 +563,7 @@ public class ExpandedAnimationController
                        ? p.x - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
                        : p.x + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
                animationForChild(child)
                        .translationX(fromX, p.y)
                        .translationX(fromX, p.x)
                        .start();
            } else {
                float fromY = p.y - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
@@ -634,4 +634,9 @@ public class ExpandedAnimationController
                    .start();
        }
    }

    /** Returns true if we're in the middle of a collapse or expand animation. */
    boolean isAnimating() {
        return mAnimatingCollapse || mAnimatingExpand;
    }
}
+69 −42
Original line number Diff line number Diff line
@@ -16,52 +16,51 @@

package com.android.wm.shell.bubbles.animation;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.PointF;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;

import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleStackView;

import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWith(AndroidJUnit4.class)
public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestCase {

    private int mDisplayWidth = 500;
    private int mDisplayHeight = 1000;

    private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class);
    private final Semaphore mBubbleRemovedSemaphore = new Semaphore(0);
    private final Runnable mOnBubbleAnimatedOutAction = mBubbleRemovedSemaphore::release;
    ExpandedAnimationController mExpandedController;

    private int mStackOffset;
    private PointF mExpansionPoint;
    private BubblePositioner mPositioner;
    private BubbleStackView.StackViewState mStackViewState = new BubbleStackView.StackViewState();
    private final BubbleStackView.StackViewState mStackViewState =
            new BubbleStackView.StackViewState();

    @SuppressLint("VisibleForTests")
    @Before
    public void setUp() throws Exception {
        super.setUp();
@@ -70,15 +69,13 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
                getContext().getSystemService(WindowManager.class));
        mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
                Insets.of(0, 0, 0, 0),
                new Rect(0, 0, mDisplayWidth, mDisplayHeight));
                new Rect(0, 0, 500, 1000));

        BubbleStackView stackView = mock(BubbleStackView.class);
        when(stackView.getState()).thenReturn(getStackViewState());

        mExpandedController = new ExpandedAnimationController(mPositioner,
                mOnBubbleAnimatedOutAction,
                stackView);
        spyOn(mExpandedController);

        addOneMoreThanBubbleLimitBubbles();
        mLayout.setActiveController(mExpandedController);
@@ -86,9 +83,18 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
        Resources res = mLayout.getResources();
        mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
        mExpansionPoint = new PointF(100, 100);

        getStackViewState();
        when(stackView.getState()).thenAnswer(i -> getStackViewState());
        waitForMainThread();
    }

    public BubbleStackView.StackViewState getStackViewState() {
    @After
    public void tearDown() {
        waitForMainThread();
    }

    private BubbleStackView.StackViewState getStackViewState() {
        mStackViewState.numberOfBubbles = mLayout.getChildCount();
        mStackViewState.selectedIndex = 0;
        mStackViewState.onLeft = mPositioner.isStackOnLeft(mExpansionPoint);
@@ -96,68 +102,71 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
    }

    @Test
    @Ignore
    public void testExpansionAndCollapse() throws InterruptedException {
        Runnable afterExpand = mock(Runnable.class);
        mExpandedController.expandFromStack(afterExpand);
        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);

    public void testExpansionAndCollapse() throws Exception {
        expand();
        testBubblesInCorrectExpandedPositions();
        verify(afterExpand).run();
        waitForMainThread();

        Runnable afterCollapse = mock(Runnable.class);
        final Semaphore semaphore = new Semaphore(0);
        Runnable afterCollapse = semaphore::release;
        mExpandedController.collapseBackToStack(mExpansionPoint, false, afterCollapse);
        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);

        testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1);
        verify(afterExpand).run();
        assertThat(semaphore.tryAcquire(1, 2, TimeUnit.SECONDS)).isTrue();
        waitForAnimation();
        testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y);
    }

    @Test
    @Ignore
    public void testOnChildAdded() throws InterruptedException {
    public void testOnChildAdded() throws Exception {
        expand();
        waitForMainThread();

        // Add another new view and wait for its animation.
        final View newView = new FrameLayout(getContext());
        mLayout.addView(newView, 0);
        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);

        waitForAnimation();
        testBubblesInCorrectExpandedPositions();
    }

    @Test
    @Ignore
    public void testOnChildRemoved() throws InterruptedException {
    public void testOnChildRemoved() throws Exception {
        expand();
        waitForMainThread();

        // Remove some views and see if the remaining child views still pass the expansion test.
        // Remove some views and verify the remaining child views still pass the expansion test.
        mLayout.removeView(mViews.get(0));
        mLayout.removeView(mViews.get(3));
        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);

        // Removing a view will invoke onBubbleAnimatedOutAction. Block until it gets called twice.
        assertThat(mBubbleRemovedSemaphore.tryAcquire(2, 2, TimeUnit.SECONDS)).isTrue();

        waitForAnimation();
        testBubblesInCorrectExpandedPositions();
    }

    @Test
    public void testDragBubbleOutDoesntNPE() throws InterruptedException {
    public void testDragBubbleOutDoesntNPE() {
        mExpandedController.onGestureFinished();
        mExpandedController.dragBubbleOut(mViews.get(0), 1, 1);
    }

    /** Expand the stack and wait for animations to finish. */
    private void expand() throws InterruptedException {
        mExpandedController.expandFromStack(mock(Runnable.class));
        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
        final Semaphore semaphore = new Semaphore(0);
        Runnable afterExpand = semaphore::release;

        mExpandedController.expandFromStack(afterExpand);
        assertThat(semaphore.tryAcquire(1, TimeUnit.SECONDS)).isTrue();
    }

    /** Check that children are in the correct positions for being stacked. */
    private void testStackedAtPosition(float x, float y, int offsetMultiplier) {
    private void testStackedAtPosition(float x, float y) {
        // Make sure the rest of the stack moved again, including the first bubble not moving, and
        // is stacked to the right now that we're on the right side of the screen.
        for (int i = 0; i < mLayout.getChildCount(); i++) {
            assertEquals(x + i * offsetMultiplier * mStackOffset,
                    mLayout.getChildAt(i).getTranslationX(), 2f);
            assertEquals(y, mLayout.getChildAt(i).getTranslationY(), 2f);
            assertEquals(x, mLayout.getChildAt(i).getTranslationX(), 2f);
            assertEquals(y + Math.min(i, 1) * mStackOffset, mLayout.getChildAt(i).getTranslationY(),
                    2f);
            assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f);
        }
    }
@@ -175,4 +184,22 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
                    mLayout.getChildAt(i).getTranslationY(), 2f);
        }
    }

    private void waitForAnimation() throws Exception {
        final Semaphore semaphore = new Semaphore(0);
        boolean[] animating = new boolean[]{ true };
        for (int i = 0; i < 4; i++) {
            if (animating[0]) {
                mMainThreadHandler.post(() -> {
                    if (!mExpandedController.isAnimating()) {
                        animating[0] = false;
                        semaphore.release();
                    }
                });
                Thread.sleep(500);
            }
        }
        assertThat(semaphore.tryAcquire(1, 2, TimeUnit.SECONDS)).isTrue();
        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
    }
}
+14 −0
Original line number Diff line number Diff line
@@ -164,11 +164,17 @@ public class PhysicsAnimationLayoutTestCase extends ShellTestCase {

        @Override
        public void cancelAllAnimations() {
            if (mLayout.getChildCount() == 0) {
                return;
            }
            mMainThreadHandler.post(super::cancelAllAnimations);
        }

        @Override
        public void cancelAnimationsOnView(View view) {
            if (mLayout.getChildCount() == 0) {
                return;
            }
            mMainThreadHandler.post(() -> super.cancelAnimationsOnView(view));
        }

@@ -221,6 +227,9 @@ public class PhysicsAnimationLayoutTestCase extends ShellTestCase {

            @Override
            protected void startPathAnimation() {
                if (mLayout.getChildCount() == 0) {
                    return;
                }
                mMainThreadHandler.post(super::startPathAnimation);
            }
        }
@@ -322,4 +331,9 @@ public class PhysicsAnimationLayoutTestCase extends ShellTestCase {
            e.printStackTrace();
        }
    }

    /** Waits for the main thread to finish processing all pending runnables. */
    public void waitForMainThread() {
        runOnMainThreadAndBlock(() -> {});
    }
}