chromium/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/CookieControlsServiceBridgeTest.java

// Copyright 2020 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.site_settings;

import androidx.test.filters.SmallTest;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.components.content_settings.CookieControlsEnforcement;
import org.chromium.components.content_settings.CookieControlsMode;
import org.chromium.components.content_settings.PrefNames;
import org.chromium.components.prefs.PrefService;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.net.test.EmbeddedTestServer;

/** Integration tests for CookieControlsServiceBridge. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@Batch(CookieControlsBridgeTest.COOKIE_CONTROLS_BATCH_NAME)
public class CookieControlsServiceBridgeTest {
    private class TestCallbackHandler
            implements CookieControlsServiceBridge.CookieControlsServiceObserver {
        private CallbackHelper mHelper;

        public TestCallbackHandler(CallbackHelper helper) {
            mHelper = helper;
        }

        @Override
        public void sendCookieControlsUIChanges(
                boolean checked, @CookieControlsEnforcement int enforcement) {
            mChecked = checked;
            mEnforcement = enforcement;
            mHelper.notifyCalled();
        }
    }

    @ClassRule
    public static ChromeTabbedActivityTestRule sActivityTestRule =
            new ChromeTabbedActivityTestRule();

    @Rule
    public BlankCTATabInitialStateRule mBlankCTATabInitialStateRule =
            new BlankCTATabInitialStateRule(sActivityTestRule, false);

    private EmbeddedTestServer mTestServer;
    private CallbackHelper mCallbackHelper;
    private TestCallbackHandler mCallbackHandler;
    private CookieControlsServiceBridge mCookieControlsServiceBridge;
    private boolean mChecked;
    private @CookieControlsEnforcement int mEnforcement;

    @Before
    public void setUp() throws Exception {
        mCallbackHelper = new CallbackHelper();
        mCallbackHandler = new TestCallbackHandler(mCallbackHelper);
        mTestServer = sActivityTestRule.getTestServer();
    }

    @After
    public void tearDown() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    PrefService prefService =
                            UserPrefs.get(ProfileManager.getLastUsedRegularProfile());
                    prefService.clearPref(PrefNames.COOKIE_CONTROLS_MODE);
                });
    }

    private void setCookieControlsMode(@CookieControlsMode int mode) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    PrefService prefService =
                            UserPrefs.get(ProfileManager.getLastUsedRegularProfile());
                    prefService.setInteger(PrefNames.COOKIE_CONTROLS_MODE, mode);
                });
    }

    /** Test changing the bridge triggers callback for correct toggle state. */
    @Test
    @SmallTest
    @DisableFeatures(ChromeFeatureList.TRACKING_PROTECTION_3PCD)
    public void testCookieSettingsCheckedChanges() throws Exception {
        setCookieControlsMode(CookieControlsMode.OFF);
        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
        Tab tab = sActivityTestRule.loadUrlInNewTab(url, true); // incognito tab

        int currentCallCount = mCallbackHelper.getCallCount();
        // Create cookie settings bridge and wait for desired callbacks.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mCookieControlsServiceBridge =
                            new CookieControlsServiceBridge(tab.getProfile(), mCallbackHandler);
                    mCookieControlsServiceBridge.updateServiceIfNecessary();
                });
        // Initial callback after the bridge is created.
        mCallbackHelper.waitForCallback(currentCallCount, 1);

        // Test that the toggle switches on.
        boolean expectedChecked = true;
        mChecked = false;
        currentCallCount = mCallbackHelper.getCallCount();
        setCookieControlsMode(CookieControlsMode.INCOGNITO_ONLY);
        mCallbackHelper.waitForCallback(currentCallCount, 1);
        Assert.assertEquals(expectedChecked, mChecked);

        // Test that the toggle switches off.
        expectedChecked = false;
        mChecked = true;
        currentCallCount = mCallbackHelper.getCallCount();
        setCookieControlsMode(CookieControlsMode.OFF);
        mCallbackHelper.waitForCallback(currentCallCount, 1);
        Assert.assertEquals(expectedChecked, mChecked);

        // Test that the toggle switches back on and enforced (by settings)
        expectedChecked = true;
        mChecked = false;
        int expectedEnforcement = CookieControlsEnforcement.ENFORCED_BY_COOKIE_SETTING;
        mEnforcement = CookieControlsEnforcement.NO_ENFORCEMENT;
        currentCallCount = mCallbackHelper.getCallCount();
        setCookieControlsMode(CookieControlsMode.BLOCK_THIRD_PARTY);
        mCallbackHelper.waitForCallback(currentCallCount, 1);
        Assert.assertEquals(expectedChecked, mChecked);
        Assert.assertEquals(expectedEnforcement, mEnforcement);
    }

    /** Test the ability to set the cookie controls mode pref through the bridge. */
    @Test
    @SmallTest
    @DisableFeatures(ChromeFeatureList.TRACKING_PROTECTION_3PCD)
    public void testCookieBridgeWithTPCookiesDisabled() throws Exception {
        setCookieControlsMode(CookieControlsMode.OFF);
        final String url = mTestServer.getURL("/chrome/test/data/android/cookie.html");
        Tab tab = sActivityTestRule.loadUrlInNewTab(url, true); // incognito tab.

        boolean expectedChecked = true;
        mChecked = false;
        int currentCallCount = mCallbackHelper.getCallCount();
        // Create cookie controls service bridge and wait for desired callbacks.
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mCookieControlsServiceBridge =
                            new CookieControlsServiceBridge(tab.getProfile(), mCallbackHandler);
                    mCookieControlsServiceBridge.updateServiceIfNecessary();

                    mCookieControlsServiceBridge.handleCookieControlsToggleChanged(true);

                    Assert.assertEquals(
                            "CookieControlsMode should be incognito_only",
                            UserPrefs.get(ProfileManager.getLastUsedRegularProfile())
                                    .getInteger(PrefNames.COOKIE_CONTROLS_MODE),
                            CookieControlsMode.INCOGNITO_ONLY);
                });
        // One initial callback after creation, then another after the toggle change.
        mCallbackHelper.waitForCallback(currentCallCount, 2);
        Assert.assertEquals(expectedChecked, mChecked);
    }
}