chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/ChromeTransitionDrawableTest.java

// Copyright 2022 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.components.browser_ui.widget;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;

import android.graphics.drawable.Drawable;

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.annotation.Config;
import org.robolectric.shadows.ShadowLooper;

import org.chromium.base.test.BaseRobolectricTestRunner;

import java.util.concurrent.atomic.AtomicBoolean;

/** Tests for {@link ChromeTransitionDrawable} class. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class ChromeTransitionDrawableTest {
    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock private Drawable mInitialDrawable;
    @Mock private Drawable mFinalDrawable;

    private ChromeTransitionDrawable mTransitionDrawable;
    private int mInitialDrawableAlpha;
    private int mFinalDrawableAlpha;

    @Before
    public void setUp() {
        doReturn(mInitialDrawable).when(mInitialDrawable).mutate();
        doReturn(mFinalDrawable).when(mFinalDrawable).mutate();
        doAnswer(
                        invocation -> {
                            mInitialDrawableAlpha = invocation.getArgument(0);
                            return null;
                        })
                .when(mInitialDrawable)
                .setAlpha(anyInt());
        doAnswer(
                        invocation -> {
                            mFinalDrawableAlpha = invocation.getArgument(0);
                            return null;
                        })
                .when(mFinalDrawable)
                .setAlpha(anyInt());
        mInitialDrawableAlpha = 255;
        mFinalDrawableAlpha = 255;
        mTransitionDrawable = new ChromeTransitionDrawable(mInitialDrawable, mFinalDrawable);
        mTransitionDrawable.setCrossFadeEnabled(true);
    }

    @Test
    public void testTransition() {
        AtomicBoolean endActionRan = new AtomicBoolean(false);
        mTransitionDrawable
                .startTransition()
                .setDuration(100)
                .withEndAction(() -> endActionRan.set(true));
        assertEquals(255, mInitialDrawableAlpha);
        assertEquals(0, mFinalDrawableAlpha);

        while (mFinalDrawableAlpha < 120) {
            ShadowLooper.runMainLooperOneTask();
        }

        assertEquals(255 - mFinalDrawableAlpha, mInitialDrawableAlpha);

        while (mFinalDrawableAlpha < 255) {
            ShadowLooper.runMainLooperOneTask();
        }

        assertEquals(0, mInitialDrawableAlpha);
        assertTrue(endActionRan.get());
    }

    @Test
    public void testTransitionInterrupted() {
        mTransitionDrawable.startTransition();
        assertEquals(255, mInitialDrawableAlpha);
        assertEquals(0, mFinalDrawableAlpha);

        while (mFinalDrawableAlpha < 120) {
            ShadowLooper.runMainLooperOneTask();
        }

        assertEquals(255 - mFinalDrawableAlpha, mInitialDrawableAlpha);

        mTransitionDrawable.startTransition();

        assertEquals(255, mInitialDrawableAlpha);
        assertEquals(0, mFinalDrawableAlpha);

        ShadowLooper.idleMainLooper();
        assertEquals(255, mFinalDrawableAlpha);
        assertEquals(0, mInitialDrawableAlpha);
    }

    @Test
    public void testTransitionNoCrossfade() {
        mTransitionDrawable.setCrossFadeEnabled(false);
        AtomicBoolean endActionRan = new AtomicBoolean(false);
        mTransitionDrawable
                .startTransition()
                .setDuration(100)
                .withEndAction(() -> endActionRan.set(true));
        assertEquals(255, mInitialDrawableAlpha);
        assertEquals(0, mFinalDrawableAlpha);

        while (mFinalDrawableAlpha < 120) {
            ShadowLooper.runMainLooperOneTask();
        }

        assertEquals(255, mInitialDrawableAlpha);

        while (mFinalDrawableAlpha < 255) {
            ShadowLooper.runMainLooperOneTask();
        }

        assertEquals(255, mInitialDrawableAlpha);
        assertTrue(endActionRan.get());
    }

    @Test
    public void testFinishTransition_toInitial() {
        mTransitionDrawable.startTransition();
        assertEquals(255, mInitialDrawableAlpha);
        assertEquals(0, mFinalDrawableAlpha);

        while (mFinalDrawableAlpha < 120) {
            ShadowLooper.runMainLooperOneTask();
        }

        assertEquals(255 - mFinalDrawableAlpha, mInitialDrawableAlpha);
        mTransitionDrawable.finishTransition(false);

        assertEquals(255, mInitialDrawableAlpha);
        assertEquals(0, mFinalDrawableAlpha);
    }

    @Test
    public void testFinishTransition_toFinal() {
        mTransitionDrawable.startTransition();
        assertEquals(255, mInitialDrawableAlpha);
        assertEquals(0, mFinalDrawableAlpha);

        while (mFinalDrawableAlpha < 120) {
            ShadowLooper.runMainLooperOneTask();
        }

        assertEquals(255 - mFinalDrawableAlpha, mInitialDrawableAlpha);
        mTransitionDrawable.finishTransition(true);

        assertEquals(255, mFinalDrawableAlpha);
        assertEquals(0, mInitialDrawableAlpha);
    }

    @Test
    public void testReverseTransition() {
        AtomicBoolean endActionRan = new AtomicBoolean(false);
        mTransitionDrawable
                .startTransition()
                .setDuration(100)
                .withEndAction(() -> endActionRan.set(true));
        assertEquals(255, mInitialDrawableAlpha);
        assertEquals(0, mFinalDrawableAlpha);

        while (mFinalDrawableAlpha < 120) {
            ShadowLooper.runMainLooperOneTask();
        }

        assertEquals(255 - mFinalDrawableAlpha, mInitialDrawableAlpha);

        mTransitionDrawable.reverseTransition();
        assertEquals(255 - mFinalDrawableAlpha, mInitialDrawableAlpha);
        assertFalse(endActionRan.get());

        ShadowLooper.idleMainLooper();
        assertEquals(255, mInitialDrawableAlpha);
        assertEquals(0, mFinalDrawableAlpha);
    }

    @Test
    public void testEndAction() {
        AtomicBoolean endActionRan = new AtomicBoolean(false);
        AtomicBoolean endActionRan2 = new AtomicBoolean(false);

        mTransitionDrawable
                .startTransition()
                .setDuration(100)
                .withEndAction(() -> endActionRan.set(true));
        ShadowLooper.runMainLooperOneTask();

        mTransitionDrawable.reverseTransition().withEndAction(() -> endActionRan2.set(true));
        ShadowLooper.idleMainLooper();
        assertFalse(endActionRan.get());
        assertTrue(endActionRan2.get());
    }

    @Test
    public void testSetCrossfadeEnabled() {
        mTransitionDrawable.setCrossFadeEnabled(true);
        mTransitionDrawable.startTransition().setDuration(100);
        assertEquals(255, mInitialDrawableAlpha);
        assertEquals(0, mFinalDrawableAlpha);

        while (mFinalDrawableAlpha < 120) {
            ShadowLooper.runMainLooperOneTask();
        }

        int initialDrawableAlphaBeforeSet = mInitialDrawableAlpha;
        assertEquals(255 - mFinalDrawableAlpha, initialDrawableAlphaBeforeSet);

        mTransitionDrawable.setCrossFadeEnabled(false);
        assertEquals(255, mInitialDrawableAlpha);

        mTransitionDrawable.setCrossFadeEnabled(true);
        assertEquals(initialDrawableAlphaBeforeSet, mInitialDrawableAlpha);
    }
}