chromium/components/browser_ui/site_settings/android/junit/src/org/chromium/components/browser_ui/site_settings/SiteDataCleanerUnitTest.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.site_settings;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.robolectric.annotation.Config;

import org.chromium.base.Callback;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.JniMocker;
import org.chromium.components.browsing_data.content.BrowsingDataInfo;
import org.chromium.components.browsing_data.content.BrowsingDataModel;
import org.chromium.content_public.browser.BrowserContextHandle;
import org.chromium.url.GURL;
import org.chromium.url.Origin;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;

/** Unit tests for {@link SiteDataCleaner}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class SiteDataCleanerUnitTest {

    public static final Website ORIGIN_1 =
            new Website(WebsiteAddress.create("https://google.com"), null);
    public static final Website ORIGIN_2 =
            new Website(WebsiteAddress.create("mail.google.com"), null);
    public static final Website ORIGIN_3 =
            new Website(WebsiteAddress.create("https://docs.google.com"), null);
    public static final String GOOGLE_COM = "google.com";
    public static final WebsiteGroup GROUP =
            new WebsiteGroup(
                    GOOGLE_COM, new ArrayList<>(Arrays.asList(ORIGIN_1, ORIGIN_2, ORIGIN_3)));
    @Rule public JniMocker mJniMocker = new JniMocker();

    @Mock private WebsitePreferenceBridge.Natives mBridgeMock;

    @Mock private BrowserContextHandle mContextHandle;

    @Mock private SiteSettingsDelegate mSiteSettingsDelegate;

    @Mock private BrowsingDataModel mBrowsingDataModel;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mJniMocker.mock(WebsitePreferenceBridgeJni.TEST_HOOKS, mBridgeMock);
        doReturn(mContextHandle).when(mSiteSettingsDelegate).getBrowserContextHandle();
    }

    @Test
    public void testClearData() {
        final AtomicInteger callbacksReceived = new AtomicInteger(0);
        final Runnable callback = callbacksReceived::incrementAndGet;
        SiteDataCleaner.clearData(mSiteSettingsDelegate, GROUP, callback);
        // Check that the callback was invoked only once.
        Assert.assertEquals(1, callbacksReceived.get());
        // Verify that the bridge is called for each of the websites.
        for (Website website : GROUP.getWebsites()) {
            verify(mBridgeMock).clearCookieData(mContextHandle, website.getAddress().getOrigin());
            verify(mBridgeMock).clearBannerData(mContextHandle, website.getAddress().getOrigin());
            verify(mBridgeMock)
                    .clearMediaLicenses(mContextHandle, website.getAddress().getOrigin());
        }
    }

    @Test
    public void testClearDataWithBDM() {
        HashMap<Origin, BrowsingDataInfo> map = buildBrowsingDataModelInfo();

        doReturn(true).when(mSiteSettingsDelegate).isBrowsingDataModelFeatureEnabled();
        doReturn(map).when(mBrowsingDataModel).getBrowsingDataInfo(any(), anyBoolean());

        doAnswer(this::mockBDMCallback)
                .when(mSiteSettingsDelegate)
                .getBrowsingDataModel(any(Callback.class));

        doAnswer(this::mockBDMRemoveCallback)
                .when(mBrowsingDataModel)
                .removeBrowsingData(anyString(), any(Runnable.class));

        final AtomicInteger callbacksReceived = new AtomicInteger(0);
        final Runnable callback = callbacksReceived::incrementAndGet;
        SiteDataCleaner.clearData(mSiteSettingsDelegate, ORIGIN_1, callback);

        // Check that the callback was invoked only once.
        Assert.assertEquals(1, callbacksReceived.get());

        verify(mBridgeMock, times(1))
                .clearBannerData(mContextHandle, ORIGIN_1.getAddress().getOrigin());
        verify(mBridgeMock, times(1))
                .clearCookieData(mContextHandle, ORIGIN_1.getAddress().getOrigin());

        // Media licenses are cleared in the BDM.
        verify(mBridgeMock, times(0))
                .clearMediaLicenses(mContextHandle, ORIGIN_1.getAddress().getOrigin());
    }

    private static HashMap<Origin, BrowsingDataInfo> buildBrowsingDataModelInfo() {
        var map = new HashMap<Origin, BrowsingDataInfo>();
        var origin = Origin.create(new GURL(ORIGIN_1.getAddress().getOrigin()));
        map.put(origin, new BrowsingDataInfo(origin, 0, 100, false));
        return map;
    }

    private Object mockBDMCallback(InvocationOnMock invocation) {
        var callback = (Callback<BrowsingDataModel>) invocation.getArguments()[0];
        callback.onResult(mBrowsingDataModel);
        return null;
    }

    private Object mockBDMRemoveCallback(InvocationOnMock invocation) {
        var callback = (Runnable) invocation.getArguments()[1];
        callback.run();
        return null;
    }
}