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

Commit e56b64dd authored by Liran Binyamin's avatar Liran Binyamin
Browse files

Fix flaky tests in ExpandedAnimationControllerTest

50x runs: http://ab/I80000010225198369

Fixes: 234506180
Test: atest ExpandedAnimationControllerTest

Change-Id: I535dbf2569e810f1a4cc706159586b78bb9eaf22
parent bfe91020
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(() -> {});
    }
}