chromium/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubLayoutAnimationRunnerImplUnitTest.java

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.hub;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import static org.chromium.ui.test.util.MockitoHelper.doRunnable;

import android.animation.AnimatorSet;
import android.animation.ValueAnimator;

import androidx.test.filters.SmallTest;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.shadows.ShadowLooper;

import org.chromium.base.supplier.SyncOneshotSupplierImpl;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.hub.HubLayoutAnimationRunner.AnimationState;

/** Unit tests for {@link HubLayoutAnimationRunnerImpl}. */
@RunWith(BaseRobolectricTestRunner.class)
public class HubLayoutAnimationRunnerImplUnitTest {
    private static final long TIMEOUT_MS = 3000L;

    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();

    @Mock private HubLayoutAnimatorProvider mAnimatorProvider;
    @Mock private HubLayoutAnimationListener mAnimatorListener;
    @Mock private HubLayoutAnimationListener mListener;

    private final SyncOneshotSupplierImpl<HubLayoutAnimator> mAnimatorSupplier =
            new SyncOneshotSupplierImpl<>();
    private HubLayoutAnimationRunner mRunner;
    private HubLayoutAnimator mAnimator;

    @Before
    public void setUp() {
        when(mAnimatorProvider.getPlannedAnimationType())
                .thenReturn(HubLayoutAnimationType.TRANSLATE_UP);
        when(mAnimatorProvider.getAnimatorSupplier()).thenReturn(mAnimatorSupplier);

        mRunner = HubLayoutAnimationRunnerFactory.createHubLayoutAnimationRunner(mAnimatorProvider);

        assertEquals(AnimationState.INITIALIZING, mRunner.getAnimationState());
        assertEquals(HubLayoutAnimationType.TRANSLATE_UP, mRunner.getAnimationType());

        ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 1.0f);
        animator.setDuration(1000L);
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.play(animator);

        mAnimator =
                new HubLayoutAnimator(
                        HubLayoutAnimationType.TRANSLATE_UP, animatorSet, mAnimatorListener);
    }

    @Test
    @SmallTest
    public void testRunWithImmediateAnimator_NoListeners() {
        HubLayoutAnimator animator =
                new HubLayoutAnimator(
                        HubLayoutAnimationType.FADE_IN, mAnimator.getAnimatorSet(), null);

        mAnimatorSupplier.set(animator);
        mRunner.runWithWaitForAnimatorTimeout(TIMEOUT_MS);

        assertEquals(AnimationState.WAITING_FOR_ANIMATOR, mRunner.getAnimationState());
        assertEquals(HubLayoutAnimationType.TRANSLATE_UP, mRunner.getAnimationType());

        ShadowLooper.runMainLooperOneTask();

        verify(mAnimatorListener, never()).beforeStart();
        verify(mListener, never()).beforeStart();

        assertEquals(AnimationState.STARTED, mRunner.getAnimationState());
        assertEquals(HubLayoutAnimationType.FADE_IN, mRunner.getAnimationType());
        verify(mAnimatorListener, never()).onStart();
        verify(mListener, never()).onStart();

        mAnimator.getAnimatorSet().end();

        assertEquals(AnimationState.FINISHED, mRunner.getAnimationState());
        assertEquals(HubLayoutAnimationType.FADE_IN, mRunner.getAnimationType());
        verify(mAnimatorListener, never()).onEnd(eq(false));
        verify(mListener, never()).onEnd(eq(false));
        verify(mAnimatorListener, never()).afterEnd();
        verify(mListener, never()).afterEnd();
    }

    @Test
    @SmallTest
    public void testRunWithAsyncAnimator_AnimatorListenerOnly() {
        mRunner.runWithWaitForAnimatorTimeout(TIMEOUT_MS);
        assertEquals(AnimationState.WAITING_FOR_ANIMATOR, mRunner.getAnimationState());

        mAnimatorSupplier.set(mAnimator);

        verify(mAnimatorListener).beforeStart();
        verify(mListener, never()).beforeStart();

        assertEquals(AnimationState.STARTED, mRunner.getAnimationState());

        verify(mAnimatorListener).onStart();
        verify(mListener, never()).onStart();

        mAnimator.getAnimatorSet().end();

        assertEquals(AnimationState.FINISHED, mRunner.getAnimationState());
        verify(mAnimatorListener).onEnd(eq(false));
        verify(mListener, never()).onEnd(eq(false));
        verify(mAnimatorListener).afterEnd();
        verify(mListener, never()).afterEnd();
    }

    @Test
    @SmallTest
    public void testRunOnTimeout_TwoListeners() {
        doRunnable(() -> mAnimatorSupplier.set(mAnimator))
                .when(mAnimatorProvider)
                .supplyAnimatorNow();

        mRunner.addListener(mListener);
        mRunner.runWithWaitForAnimatorTimeout(TIMEOUT_MS);
        assertEquals(AnimationState.WAITING_FOR_ANIMATOR, mRunner.getAnimationState());

        // Simulate a timeout.
        ShadowLooper.runMainLooperOneTask();

        verify(mAnimatorProvider).supplyAnimatorNow();

        verify(mAnimatorListener).beforeStart();
        verify(mListener).beforeStart();

        assertEquals(AnimationState.STARTED, mRunner.getAnimationState());

        verify(mAnimatorListener).onStart();
        verify(mListener).onStart();

        mAnimator.getAnimatorSet().end();

        assertEquals(AnimationState.FINISHED, mRunner.getAnimationState());
        verify(mAnimatorListener).onEnd(eq(false));
        verify(mListener).onEnd(eq(false));
        verify(mAnimatorListener).afterEnd();
        verify(mListener).afterEnd();
    }

    @Test
    @SmallTest
    public void testForceAnimationToFinish() {
        mRunner.addListener(mListener);

        mRunner.runWithWaitForAnimatorTimeout(TIMEOUT_MS);
        assertEquals(AnimationState.WAITING_FOR_ANIMATOR, mRunner.getAnimationState());

        mAnimatorSupplier.set(mAnimator);

        verify(mAnimatorListener).beforeStart();
        verify(mListener).beforeStart();

        assertEquals(AnimationState.STARTED, mRunner.getAnimationState());

        verify(mAnimatorListener).onStart();
        verify(mListener).onStart();

        mRunner.forceAnimationToFinish();

        assertEquals(AnimationState.FINISHED, mRunner.getAnimationState());
        verify(mAnimatorListener).onEnd(eq(true));
        verify(mListener).onEnd(eq(true));
        verify(mAnimatorListener).afterEnd();
        verify(mListener).afterEnd();
    }

    @Test
    @SmallTest
    public void testForceAnimationToFinishWithPostedOnAnimationReadyNoWaitForTask() {
        mRunner.addListener(mListener);

        mAnimatorSupplier.set(mAnimator);
        mRunner.runWithWaitForAnimatorTimeout(TIMEOUT_MS);
        assertEquals(AnimationState.WAITING_FOR_ANIMATOR, mRunner.getAnimationState());

        mRunner.forceAnimationToFinish();

        verify(mAnimatorListener).beforeStart();
        verify(mListener).beforeStart();

        verify(mAnimatorListener).onStart();
        verify(mListener).onStart();

        assertEquals(AnimationState.FINISHED, mRunner.getAnimationState());
        verify(mAnimatorListener).onEnd(eq(true));
        verify(mListener).onEnd(eq(true));
        verify(mAnimatorListener).afterEnd();
        verify(mListener).afterEnd();
    }

    @Test
    @SmallTest
    public void testForceAnimationToFinishWithNoAnimationSupplied() {
        doRunnable(() -> mAnimatorSupplier.set(mAnimator))
                .when(mAnimatorProvider)
                .supplyAnimatorNow();

        mRunner.addListener(mListener);
        mRunner.runWithWaitForAnimatorTimeout(TIMEOUT_MS);
        assertEquals(AnimationState.WAITING_FOR_ANIMATOR, mRunner.getAnimationState());

        mRunner.forceAnimationToFinish();

        verify(mAnimatorProvider).supplyAnimatorNow();

        assertEquals(AnimationState.FINISHED, mRunner.getAnimationState());

        verify(mAnimatorListener).beforeStart();
        verify(mListener).beforeStart();

        verify(mAnimatorListener).onStart();
        verify(mListener).onStart();

        verify(mAnimatorListener).onEnd(eq(true));
        verify(mListener).onEnd(eq(true));
        verify(mAnimatorListener).afterEnd();
        verify(mListener).afterEnd();
    }

    @Test
    @SmallTest
    public void testForceAnimationToFinishWithoutRunWithTimeout() {
        mRunner.addListener(mListener);

        assertEquals(AnimationState.INITIALIZING, mRunner.getAnimationState());

        mAnimatorSupplier.set(mAnimator);

        assertEquals(AnimationState.INITIALIZING, mRunner.getAnimationState());

        mRunner.forceAnimationToFinish();

        verify(mAnimatorListener).beforeStart();
        verify(mListener).beforeStart();

        assertEquals(AnimationState.FINISHED, mRunner.getAnimationState());

        verify(mAnimatorListener).onStart();
        verify(mListener).onStart();

        assertEquals(AnimationState.FINISHED, mRunner.getAnimationState());
        verify(mAnimatorListener).onEnd(eq(true));
        verify(mListener).onEnd(eq(true));
        verify(mAnimatorListener).afterEnd();
        verify(mListener).afterEnd();
    }
}