chromium/chrome/browser/tab_ui/android/junit/src/org/chromium/chrome/browser/tab_ui/TabListFaviconProviderTest.java

// Copyright 2024 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.tab_ui;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;

import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

import androidx.annotation.ColorInt;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;

import org.chromium.base.Callback;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.ComposedTabFavicon;
import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.ResourceTabFavicon;
import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.StaticTabFaviconType;
import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.TabFavicon;
import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.TabFaviconFetcher;
import org.chromium.chrome.browser.tab_ui.TabListFaviconProvider.UrlTabFavicon;
import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
import org.chromium.chrome.browser.ui.favicon.FaviconHelper.ComposedFaviconImageCallback;
import org.chromium.chrome.browser.ui.favicon.FaviconHelper.FaviconImageCallback;
import org.chromium.url.GURL;
import org.chromium.url.JUnitTestGURLs;

import java.util.Arrays;

/** Unit tests for {@link TabListFaviconProvider}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class TabListFaviconProviderTest {
    private GURL mUrl1;
    private GURL mUrl2;

    private Activity mActivity;
    @Mock private Profile mProfile;
    @Mock private FaviconHelper mMockFaviconHelper;
    @Captor private ArgumentCaptor<FaviconImageCallback> mFaviconImageCallbackCaptor;

    @Captor
    private ArgumentCaptor<ComposedFaviconImageCallback> mComposedFaviconImageCallbackCaptor;

    private TabListFaviconProvider mTabListFaviconProvider;

    private int mUniqueColorValue;

    private Bitmap newBitmap() {
        Bitmap image = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
        int colorValue = ++mUniqueColorValue;
        final @ColorInt int colorInt = Color.rgb(colorValue, colorValue, colorValue);
        image.eraseColor(colorInt);
        return image;
    }

    private Drawable newDrawable() {
        Bitmap image = newBitmap();
        Resources resources = ContextUtils.getApplicationContext().getResources();
        return new BitmapDrawable(resources, image);
    }

    @Before
    public void setUp() {
        mActivity = Robolectric.setupActivity(Activity.class);
        mActivity.setTheme(R.style.Theme_BrowserUI_DayNight);

        MockitoAnnotations.initMocks(this);
        mUrl1 = JUnitTestGURLs.URL_1;
        mUrl2 = JUnitTestGURLs.URL_2;

        mTabListFaviconProvider =
                new TabListFaviconProvider(
                        mActivity,
                        false,
                        org.chromium.components.browser_ui.styles.R.dimen
                                .default_favicon_corner_radius);
        mTabListFaviconProvider.initForTesting(mProfile, mMockFaviconHelper);
    }

    @Test
    public void testUrlTabFavicon() {
        TabFavicon urlTabFavicon = new UrlTabFavicon(newDrawable(), mUrl1);
        Assert.assertEquals(urlTabFavicon, new UrlTabFavicon(newDrawable(), mUrl1));
        Assert.assertNotEquals(urlTabFavicon, new UrlTabFavicon(newDrawable(), mUrl2));
        Assert.assertNotEquals(urlTabFavicon, null);
        Assert.assertNotEquals(
                urlTabFavicon,
                new ResourceTabFavicon(newDrawable(), StaticTabFaviconType.ROUNDED_GLOBE));
    }

    @Test
    public void testComposedTabFavicon() {
        TabFavicon composedTabFavicon = new ComposedTabFavicon(newDrawable(), new GURL[] {mUrl1});
        Assert.assertEquals(
                composedTabFavicon, new ComposedTabFavicon(newDrawable(), new GURL[] {mUrl1}));
        Assert.assertNotEquals(
                composedTabFavicon,
                new ComposedTabFavicon(newDrawable(), new GURL[] {mUrl1, mUrl2}));
        Assert.assertNotEquals(
                composedTabFavicon, new ComposedTabFavicon(newDrawable(), new GURL[] {mUrl2}));
        Assert.assertNotEquals(
                composedTabFavicon, new ComposedTabFavicon(newDrawable(), new GURL[] {}));
        Assert.assertNotEquals(
                composedTabFavicon,
                new ResourceTabFavicon(newDrawable(), StaticTabFaviconType.ROUNDED_GLOBE));

        TabFavicon composedTabFavicon2 =
                new ComposedTabFavicon(newDrawable(), new GURL[] {mUrl1, mUrl2});
        Assert.assertEquals(
                composedTabFavicon2,
                new ComposedTabFavicon(newDrawable(), new GURL[] {mUrl1, mUrl2}));
        Assert.assertNotEquals(
                composedTabFavicon2,
                new ComposedTabFavicon(newDrawable(), new GURL[] {mUrl2, mUrl1}));
        Assert.assertNotEquals(
                composedTabFavicon2, new ComposedTabFavicon(newDrawable(), new GURL[] {mUrl1}));
    }

    @Test
    public void testResourceTabFavicon() {
        TabFavicon resourceTabFavicon =
                new ResourceTabFavicon(newDrawable(), StaticTabFaviconType.ROUNDED_GLOBE);
        Assert.assertEquals(
                resourceTabFavicon,
                new ResourceTabFavicon(newDrawable(), StaticTabFaviconType.ROUNDED_GLOBE));
        Assert.assertNotEquals(
                resourceTabFavicon,
                new ResourceTabFavicon(newDrawable(), StaticTabFaviconType.ROUNDED_CHROME));
        Assert.assertNotEquals(resourceTabFavicon, new UrlTabFavicon(newDrawable(), mUrl1));
    }

    @Test
    public void testDefaultFaviconFetcher_NonIncognito() {
        TabFaviconFetcher fetcher = mTabListFaviconProvider.getDefaultFaviconFetcher(false);
        TabFavicon favicon = (ResourceTabFavicon) doFetchFavicon(fetcher);
        Assert.assertEquals(
                favicon, new ResourceTabFavicon(newDrawable(), StaticTabFaviconType.ROUNDED_GLOBE));
    }

    @Test
    public void testDefaultFaviconFetcher_Incognito() {
        TabFaviconFetcher fetcher = mTabListFaviconProvider.getDefaultFaviconFetcher(true);
        TabFavicon favicon = (ResourceTabFavicon) doFetchFavicon(fetcher);
        Assert.assertEquals(
                favicon,
                new ResourceTabFavicon(
                        newDrawable(), StaticTabFaviconType.ROUNDED_GLOBE_INCOGNITO));
    }

    @Test
    public void testFaviconFromBitmapFetcher() {
        TabFaviconFetcher fetcher =
                mTabListFaviconProvider.getFaviconFromBitmapFetcher(newBitmap(), mUrl2);
        TabFavicon favicon = (UrlTabFavicon) doFetchFavicon(fetcher);
        Assert.assertEquals(favicon, new UrlTabFavicon(newDrawable(), mUrl2));
    }

    @Test
    public void testFaviconForUrlFetcher() {
        TabFaviconFetcher fetcher = mTabListFaviconProvider.getFaviconForUrlFetcher(mUrl1, false);
        TabFavicon favicon =
                (UrlTabFavicon)
                        doFetchFavicon(
                                () -> {
                                    verify(mMockFaviconHelper)
                                            .getLocalFaviconImageForURL(
                                                    eq(mProfile),
                                                    eq(mUrl1),
                                                    anyInt(),
                                                    mFaviconImageCallbackCaptor.capture());
                                    mFaviconImageCallbackCaptor
                                            .getValue()
                                            .onFaviconAvailable(newBitmap(), mUrl1);
                                },
                                fetcher);
        Assert.assertEquals(favicon, new UrlTabFavicon(newDrawable(), mUrl1));
    }

    @Test
    public void testComposedFaviconImageFetcher() {
        GURL[] urls = new GURL[] {mUrl1, mUrl2};
        TabFaviconFetcher fetcher =
                mTabListFaviconProvider.getComposedFaviconImageFetcher(Arrays.asList(urls), false);
        TabFavicon favicon =
                (ComposedTabFavicon)
                        doFetchFavicon(
                                () -> {
                                    verify(mMockFaviconHelper)
                                            .getComposedFaviconImage(
                                                    eq(mProfile),
                                                    eq(Arrays.asList(urls)),
                                                    anyInt(),
                                                    mComposedFaviconImageCallbackCaptor.capture());
                                    mComposedFaviconImageCallbackCaptor
                                            .getValue()
                                            .onComposedFaviconAvailable(newBitmap(), urls);
                                },
                                fetcher);
        Assert.assertEquals(favicon, new ComposedTabFavicon(newDrawable(), urls));
    }

    private TabFavicon doFetchFavicon(Runnable after, TabFaviconFetcher fetcher) {
        TabFavicon[] faviconHolder = new TabFavicon[1];
        Callback<TabFavicon> callback =
                tabFavicon -> {
                    faviconHolder[0] = tabFavicon;
                };
        fetcher.fetch(callback);
        after.run();
        return faviconHolder[0];
    }

    private TabFavicon doFetchFavicon(TabFaviconFetcher fetcher) {
        return doFetchFavicon(() -> {}, fetcher);
    }
}