chromium/chrome/browser/page_image_service/android/junit/src/org/chromium/chrome/browser/page_image_service/ImageServiceBridgeUnitTest.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.page_image_service;

import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

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.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.annotation.Config;

import org.chromium.base.Callback;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.HistogramWatcher;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.browser.page_image_service.ImageServiceMetrics.SalientImageUrlFetchResult;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.components.image_fetcher.ImageFetcher;
import org.chromium.page_image_service.mojom.ClientId;
import org.chromium.url.GURL;
import org.chromium.url.JUnitTestGURLs;

/** Unit tests for {@link ImageServiceBridge}. */
@Batch(Batch.UNIT_TESTS)
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public final class ImageServiceBridgeUnitTest {
    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
    @Rule public JniMocker mJniMocker = new JniMocker();

    private static final GURL PAGE_URL = JUnitTestGURLs.URL_1;
    private static final GURL SALIENT_IMAGE_URL = JUnitTestGURLs.URL_2;
    private static final int CLIENT_ID = ClientId.BOOKMARKS;
    private static final String STRING_CLIENT_ID = "Test";
    private static final String TEST_HISTOGRAM =
            "PageImageService.Android.SalientImageUrlFetchResult." + STRING_CLIENT_ID;

    @Mock private ImageServiceBridge.Natives mImageServiceBridgeJni;
    @Mock private Profile mProfile;
    @Mock private ImageFetcher mImageFetcher;
    @Mock private Callback<GURL> mUrlCallback;
    @Captor private ArgumentCaptor<Callback<GURL>> mUrlCallbackCaptor;

    private ImageServiceBridge mImageServiceBridge;

    @Before
    public void setUp() {
        mJniMocker.mock(ImageServiceBridgeJni.TEST_HOOKS, mImageServiceBridgeJni);

        mImageServiceBridge =
                new ImageServiceBridge(
                        CLIENT_ID,
                        ImageFetcher.POWER_BOOKMARKS_CLIENT_NAME,
                        mProfile,
                        mImageFetcher);
        verify(mImageServiceBridgeJni).init(eq(mProfile));
        doReturn(STRING_CLIENT_ID).when(mImageServiceBridgeJni).clientIdToString(anyInt());
    }

    @Test
    @SmallTest
    public void testFetchImageUrlFor() {
        HistogramWatcher histogramWatcher =
                HistogramWatcher.newBuilder()
                        .expectIntRecord(
                                TEST_HISTOGRAM, SalientImageUrlFetchResult.FAILED_FROM_NETWORK)
                        .build();

        mImageServiceBridge.fetchImageUrlFor(/* isAccountData= */ true, PAGE_URL, mUrlCallback);

        verify(mImageServiceBridgeJni)
                .fetchImageUrlFor(
                        anyLong(),
                        eq(true),
                        eq(CLIENT_ID),
                        eq(PAGE_URL),
                        mUrlCallbackCaptor.capture());

        // Verifies the case that no salient image URL is found.
        mUrlCallbackCaptor.getValue().onResult(null);
        verify(mUrlCallback).onResult(isNull());
        histogramWatcher.assertExpected();

        histogramWatcher =
                HistogramWatcher.newBuilder()
                        .expectIntRecord(
                                TEST_HISTOGRAM, SalientImageUrlFetchResult.FAILED_FROM_CACHE)
                        .build();

        mImageServiceBridge.fetchImageUrlFor(/* isAccountData= */ true, PAGE_URL, mUrlCallback);
        histogramWatcher.assertExpected();

        histogramWatcher =
                HistogramWatcher.newBuilder()
                        .expectIntRecord(
                                TEST_HISTOGRAM, SalientImageUrlFetchResult.SUCCEED_FROM_NETWORK)
                        .build();

        // Verifies the case that a salient image URL if found.
        mUrlCallbackCaptor.getValue().onResult(SALIENT_IMAGE_URL);
        verify(mUrlCallback).onResult(eq(SALIENT_IMAGE_URL));
        assertTrue(mImageServiceBridge.isUrlCachedForTesting(PAGE_URL, SALIENT_IMAGE_URL));
        histogramWatcher.assertExpected();

        histogramWatcher =
                HistogramWatcher.newBuilder()
                        .expectIntRecord(
                                TEST_HISTOGRAM, SalientImageUrlFetchResult.SUCCEED_FROM_CACHE)
                        .build();

        // Verifies that the cached salient image URL will be used immediately if exists.
        mImageServiceBridge.fetchImageUrlFor(/* isAccountData= */ true, PAGE_URL, mUrlCallback);
        verify(mUrlCallback, times(2)).onResult(eq(SALIENT_IMAGE_URL));
    }

    @Test
    @SmallTest
    public void testDestroy() {
        mImageServiceBridge.destroy();
        verify(mImageServiceBridgeJni).destroy(anyLong());
        verify(mImageFetcher).destroy();
    }

    @Test
    @SmallTest
    public void testClear() {
        mImageServiceBridge.fetchImageUrlFor(/* isAccountData= */ true, PAGE_URL, mUrlCallback);

        verify(mImageServiceBridgeJni)
                .fetchImageUrlFor(
                        anyLong(),
                        eq(true),
                        eq(CLIENT_ID),
                        eq(PAGE_URL),
                        mUrlCallbackCaptor.capture());
        mUrlCallbackCaptor.getValue().onResult(SALIENT_IMAGE_URL);
        assertFalse(mImageServiceBridge.isUrlCacheEmptyForTesting());

        // Verifies that cleaning up the bridge deletes the cache of the image fetcher, but doesn't
        // delete the cache of salient image URLs.
        mImageServiceBridge.clear();
        verify(mImageFetcher).clear();
        assertFalse(mImageServiceBridge.isUrlCacheEmptyForTesting());
    }
}