chromium/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/PhoneCaptureStateTokenTest.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.chrome.browser.toolbar.top;

import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;

import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.toolbar.ButtonData;
import org.chromium.chrome.browser.toolbar.ButtonDataImpl;
import org.chromium.chrome.browser.toolbar.top.ToolbarPhone.VisualState;

/** Unit tests for {@link PhoneCaptureStateToken}. */
@RunWith(BaseRobolectricTestRunner.class)
public class PhoneCaptureStateTokenTest {
    private static final @ColorInt int DEFAULT_TINT = Color.TRANSPARENT;
    private static final int DEFAULT_TAB_COUNT = 1;
    private static final ButtonData DEFAULT_BUTTON_DATA = makeButtonDate();
    private static final @VisualState int DEFAULT_VISUAL_STATE = VisualState.NORMAL;
    private static final String DEFAULT_URL_TEXT = "https://www.example.com/";
    private static final CharSequence DEFAULT_URL_HINT_TEXT = null;
    private static final @DrawableRes int DEFAULT_SECURITY_ICON = 0;
    private static final boolean DEFAULT_HOME_BUTTON_IS_VISIBLE = true;
    private static final boolean DEFAULT_IS_SHOWING_UPDATE_BADGE_DURING_LAST_CAPTURE = false;
    private static final boolean DEFAULT_IS_PAINT_PREVIEW = false;
    private static final float DEFAULT_PROGRESS = 0.1f;
    private static final int DEFAULT_UNFOCUSED_LOCATION_BAR_LAYOUT_WIDTH = 2;

    // Not static/final because they're initialized in #before(). Apparently ColorStateList.valueOf
    // calls into Android native code, and cannot be done too early.
    private ColorStateList mDefaultHomeButtonColorStateList;
    private PhoneCaptureStateToken mDefaultPhoneCaptureStateToken;

    private static ButtonData makeButtonDate() {
        // Uses default equals impl, reference quality, to compare. Values do not matter.
        return new ButtonDataImpl(false, null, null, "", false, null, false, 0, 0, false);
    }

    @Before
    public void before() {
        mDefaultHomeButtonColorStateList = ColorStateList.valueOf(DEFAULT_TINT);
        mDefaultPhoneCaptureStateToken = new PhoneCustomTabCaptureStateTokenBuilder().build();
    }

    @Test
    public void testSameSnapshots() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.NONE,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentTint() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().setTint(Color.RED).build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.TINT,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentTabCount() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().setTabCount(2).build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.TAB_COUNT,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentOptionalButtonData() {
        ButtonDataImpl otherButtonData = (ButtonDataImpl) makeButtonDate();
        otherButtonData.setCanShow(!otherButtonData.canShow());
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setOptionalButtonData(otherButtonData)
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.OPTIONAL_BUTTON,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testNullOptionalButtonData() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().setOptionalButtonData(null).build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.OPTIONAL_BUTTON,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testSameOptionalButtonData_DifferentDrawable() {
        ButtonDataImpl buttonData = (ButtonDataImpl) makeButtonDate();
        // Create a capture state with the original ButtonData.
        PhoneCaptureStateToken initialPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setOptionalButtonData(buttonData)
                        .build();
        buttonData.updateDrawable(new ColorDrawable());
        // Then create another capture when the button's drawable gets updated.
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setOptionalButtonData(buttonData)
                        .build();

        Assert.assertEquals(
                ToolbarSnapshotDifference.OPTIONAL_BUTTON,
                PhoneCaptureStateToken.getAnyDifference(
                        initialPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentVisualState() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setVisualState(VisualState.INCOGNITO)
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.VISUAL_STATE,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentUrlText() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setUrlText("https://www.other.com/")
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.URL_TEXT,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentUrlText_SameHintText() {
        PhoneCaptureStateToken initialPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setVisibleTextPrefixHint(DEFAULT_URL_TEXT)
                        .build();
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setUrlText(DEFAULT_URL_TEXT + "additional/paths/")
                        .setVisibleTextPrefixHint(DEFAULT_URL_TEXT)
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.NONE,
                PhoneCaptureStateToken.getAnyDifference(
                        initialPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testSameUrlText_DifferentHintText() {
        PhoneCaptureStateToken initialPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setVisibleTextPrefixHint(DEFAULT_URL_TEXT.substring(0, 2))
                        .build();
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setVisibleTextPrefixHint(DEFAULT_URL_TEXT.substring(0, 3))
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.NONE,
                PhoneCaptureStateToken.getAnyDifference(
                        initialPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testSameUrlText_BothNullHintText() {
        PhoneCaptureStateToken initialPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().setVisibleTextPrefixHint(null).build();
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().setVisibleTextPrefixHint(null).build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.NONE,
                PhoneCaptureStateToken.getAnyDifference(
                        initialPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testSameUrlText_NullHintText() {
        PhoneCaptureStateToken initialPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().setVisibleTextPrefixHint(null).build();
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setVisibleTextPrefixHint(DEFAULT_URL_TEXT)
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.NONE,
                PhoneCaptureStateToken.getAnyDifference(
                        initialPhoneCaptureStateToken, otherPhoneCaptureStateToken));
        Assert.assertEquals(
                ToolbarSnapshotDifference.NONE,
                PhoneCaptureStateToken.getAnyDifference(
                        otherPhoneCaptureStateToken, initialPhoneCaptureStateToken));
    }

    @Test
    @EnableFeatures(ChromeFeatureList.ANDROID_NO_VISIBLE_HINT_FOR_DIFFERENT_TLD)
    public void testCompareHintToPreviousUrl() {
        String latestVisibleHint = DEFAULT_URL_TEXT + "aaaaa";
        PhoneCaptureStateToken initialPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setUrlText(latestVisibleHint + "foo")
                        .setVisibleTextPrefixHint(null)
                        .build();
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setUrlText(latestVisibleHint + "bar")
                        .setVisibleTextPrefixHint(latestVisibleHint)
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.NONE,
                PhoneCaptureStateToken.getAnyDifference(
                        initialPhoneCaptureStateToken, otherPhoneCaptureStateToken));
        Assert.assertEquals(
                ToolbarSnapshotDifference.URL_TEXT,
                PhoneCaptureStateToken.getAnyDifference(
                        otherPhoneCaptureStateToken, initialPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentSecurityIcon() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().setSecurityIcon(-1).build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.SECURITY_ICON,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentHomeButtonColorStateList() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setHomeButtonColorStateList(ColorStateList.valueOf(Color.RED))
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.HOME_BUTTON,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentHomeButtonIsVisible() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().setHomeButtonIsVisible(false).build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.HOME_BUTTON,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testSameColorStateList() {
        // Create the ColorStateList by hand. ColorStateList.valueOf will inconsistently reuse
        // objects, but this ColorStateList should never have reference equality with the default.
        ColorStateList colorStateList =
                new ColorStateList(new int[][] {new int[] {}}, new int[] {DEFAULT_TINT});
        Assert.assertNotEquals(mDefaultHomeButtonColorStateList, colorStateList);

        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setHomeButtonColorStateList(colorStateList)
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.NONE,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentIsShowingUpdateBadgeDuringLastCapture() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setIsShowingUpdateBadgeDuringLastCapture(true)
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.SHOWING_UPDATE_BADGE,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentIsPaintPreview() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().setIsPaintPreview(true).build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.PAINT_PREVIEW,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentProgress() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder().setProgress(0.2f).build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.NONE,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    @Test
    public void testDifferentUnfocusedLocationBarLayoutWidth() {
        PhoneCaptureStateToken otherPhoneCaptureStateToken =
                new PhoneCustomTabCaptureStateTokenBuilder()
                        .setUnfocusedLocationBarLayoutWidth(100)
                        .build();
        Assert.assertEquals(
                ToolbarSnapshotDifference.LOCATION_BAR_WIDTH,
                PhoneCaptureStateToken.getAnyDifference(
                        mDefaultPhoneCaptureStateToken, otherPhoneCaptureStateToken));
    }

    private class PhoneCustomTabCaptureStateTokenBuilder {
        private @ColorInt int mTint = DEFAULT_TINT;
        private int mTabCount = DEFAULT_TAB_COUNT;
        private ButtonData mOptionalButtonData = DEFAULT_BUTTON_DATA;
        private @VisualState int mVisualState = DEFAULT_VISUAL_STATE;
        private String mUrlText = DEFAULT_URL_TEXT;
        private @Nullable CharSequence mVisibleTextPrefixHint = DEFAULT_URL_HINT_TEXT;
        private @DrawableRes int mSecurityIcon = DEFAULT_SECURITY_ICON;
        private ColorStateList mHomeButtonColorStateList = mDefaultHomeButtonColorStateList;
        private boolean mHomeButtonIsVisible = DEFAULT_HOME_BUTTON_IS_VISIBLE;
        private boolean mIsShowingUpdateBadgeDuringLastCapture =
                DEFAULT_IS_SHOWING_UPDATE_BADGE_DURING_LAST_CAPTURE;
        private boolean mIsPaintPreview = DEFAULT_IS_PAINT_PREVIEW;
        private float mProgress = DEFAULT_PROGRESS;
        private int mUnfocusedLocationBarLayoutWidth = DEFAULT_UNFOCUSED_LOCATION_BAR_LAYOUT_WIDTH;

        public PhoneCustomTabCaptureStateTokenBuilder setTint(@ColorInt int tint) {
            mTint = tint;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setTabCount(int tabCount) {
            mTabCount = tabCount;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setOptionalButtonData(
                ButtonData optionalButtonData) {
            mOptionalButtonData = optionalButtonData;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setVisualState(@VisualState int visualState) {
            mVisualState = visualState;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setUrlText(String urlText) {
            mUrlText = urlText;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setVisibleTextPrefixHint(
                CharSequence visibleTextPrefixHint) {
            mVisibleTextPrefixHint = visibleTextPrefixHint;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setSecurityIcon(
                @DrawableRes int securityIcon) {
            mSecurityIcon = securityIcon;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setHomeButtonColorStateList(
                ColorStateList homeButtonColorStateList) {
            mHomeButtonColorStateList = homeButtonColorStateList;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setHomeButtonIsVisible(
                boolean homeButtonIsVisible) {
            mHomeButtonIsVisible = homeButtonIsVisible;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setIsShowingUpdateBadgeDuringLastCapture(
                boolean isShowingUpdateBadgeDuringLastCapture) {
            mIsShowingUpdateBadgeDuringLastCapture = isShowingUpdateBadgeDuringLastCapture;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setIsPaintPreview(boolean isPaintPreview) {
            mIsPaintPreview = isPaintPreview;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setProgress(float progress) {
            mProgress = progress;
            return this;
        }

        public PhoneCustomTabCaptureStateTokenBuilder setUnfocusedLocationBarLayoutWidth(
                int unfocusedLocationBarLayoutWidth) {
            mUnfocusedLocationBarLayoutWidth = unfocusedLocationBarLayoutWidth;
            return this;
        }

        public PhoneCaptureStateToken build() {
            VisibleUrlText visibleUrlText = new VisibleUrlText(mUrlText, mVisibleTextPrefixHint);
            return new PhoneCaptureStateToken(
                    mTint,
                    mTabCount,
                    mOptionalButtonData,
                    mVisualState,
                    visibleUrlText,
                    mSecurityIcon,
                    mHomeButtonColorStateList,
                    mHomeButtonIsVisible,
                    mIsShowingUpdateBadgeDuringLastCapture,
                    mIsPaintPreview,
                    mProgress,
                    mUnfocusedLocationBarLayoutWidth);
        }
    }
}