chromium/chrome/android/javatests/src/org/chromium/chrome/browser/browsing_data/BrowsingDataTest.java

// Copyright 2018 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.browsing_data;

import static org.chromium.base.test.util.Matchers.is;

import androidx.test.filters.SmallTest;

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.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.RequiresRestart;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.password_manager.PasswordManagerTestHelper;
import org.chromium.chrome.browser.password_manager.PasswordStoreBridge;
import org.chromium.chrome.browser.password_manager.PasswordStoreCredential;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.chrome.test.util.browser.signin.SigninTestRule;
import org.chromium.content_public.browser.test.util.JavaScriptUtils;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.ui.test.util.GmsCoreVersionRestriction;
import org.chromium.url.GURL;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeoutException;

/** Integration tests for browsing data deletion. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({
    ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
})
@Batch(Batch.PER_CLASS)
public class BrowsingDataTest {
    private static final String TEST_FILE = "/content/test/data/browsing_data/site_data.html";

    private EmbeddedTestServer mTestServer;
    private String mUrl;

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

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

    @Rule public SigninTestRule mSigninTestRule = new SigninTestRule();

    @Before
    public void setUp() throws Exception {
        mTestServer = sActivityTestRule.getTestServer();
        mUrl = mTestServer.getURL(TEST_FILE);
    }

    private void clearBrowsingData(int dataType, int timePeriod) throws TimeoutException {
        CallbackHelper helper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    BrowsingDataBridge.getForProfile(ProfileManager.getLastUsedRegularProfile())
                            .clearBrowsingData(
                                    helper::notifyCalled, new int[] {dataType}, timePeriod);
                });
        helper.waitForCallback(0);
    }

    private int getCookieCount() throws Exception {
        String[] out = {""};
        BrowsingDataCounterBridge[] counter = {null};
        CallbackHelper helper = new CallbackHelper();
        BrowsingDataCounterBridge.BrowsingDataCounterCallback callback =
                (result) -> {
                    if (result.equals("Calculating…")) return;
                    out[0] = result;
                    helper.notifyCalled();
                };
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    counter[0] =
                            new BrowsingDataCounterBridge(
                                    ProfileManager.getLastUsedRegularProfile(),
                                    callback,
                                    BrowsingDataType.SITE_DATA,
                                    ClearBrowsingDataTab.ADVANCED);
                });
        helper.waitForCallback(0);
        // The counter returns a result like "3 sites" or "None".
        if (out[0].equals("None")) return 0;
        String cookieCount = out[0].replaceAll("[^0-9]", "");
        Assert.assertFalse("Result should contain a number: " + out[0], cookieCount.isEmpty());
        return Integer.parseInt(cookieCount);
    }

    private String runJavascriptAsync(String type) throws Exception {
        return JavaScriptUtils.runJavascriptWithAsyncResult(
                sActivityTestRule.getWebContents(), type);
    }

    private String runJavascriptSync(String type) throws Exception {
        return JavaScriptUtils.executeJavaScriptAndWaitForResult(
                sActivityTestRule.getWebContents(), type);
    }

    /** Test cookies deletion. */
    @Test
    @SmallTest
    public void testCookiesDeleted() throws Exception {
        Assert.assertEquals(0, getCookieCount());
        sActivityTestRule.loadUrl(mUrl);
        Assert.assertEquals("false", runJavascriptSync("hasCookie()"));

        runJavascriptSync("setCookie()");
        Assert.assertEquals("true", runJavascriptSync("hasCookie()"));
        Assert.assertEquals(1, getCookieCount());

        clearBrowsingData(BrowsingDataType.SITE_DATA, TimePeriod.LAST_HOUR);
        Assert.assertEquals("false", runJavascriptSync("hasCookie()"));
        Assert.assertEquals(0, getCookieCount());
    }

    /** Test site data deletion. */
    @Test
    @SmallTest
    public void testSiteDataDeleted() throws Exception {
        // TODO(dullweber): Investigate, why WebSql fails this test.
        List<String> siteData =
                Arrays.asList(
                        "LocalStorage",
                        "ServiceWorker",
                        "CacheStorage",
                        "FileSystem",
                        "IndexedDb" /*, "WebSql"*/);
        sActivityTestRule.loadUrl(mUrl);

        for (String type : siteData) {
            Assert.assertEquals(type, 0, getCookieCount());
            Assert.assertEquals(type, "false", runJavascriptAsync("has" + type + "Async()"));

            runJavascriptAsync("set" + type + "Async()");
            Assert.assertEquals(type, 1, getCookieCount());
            Assert.assertEquals(type, "true", runJavascriptAsync("has" + type + "Async()"));

            clearBrowsingData(BrowsingDataType.SITE_DATA, TimePeriod.LAST_HOUR);
            Assert.assertEquals(type, 0, getCookieCount());
            Assert.assertEquals(type, "false", runJavascriptAsync("has" + type + "Async()"));

            // Some types create data by checking for them, so we need to do a cleanup at the end.
            clearBrowsingData(BrowsingDataType.SITE_DATA, TimePeriod.LAST_HOUR);
        }
    }

    /**
     * Test all data deletion for incognito profile. This only checks to see if an android specific
     * code crashes or not. For details see, crbug.com/990624.
     */
    @Test
    @SmallTest
    public void testAllDataDeletedForIncognito() throws Exception {
        // TODO(roagarwal) : Crashes on BrowsingDataType.SITE_SETTINGS, BrowsingDataType.BOOKMARKS
        // data types.
        CallbackHelper helper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    BrowsingDataBridge.getForProfile(ProfileManager.getLastUsedRegularProfile())
                            .clearBrowsingDataIncognitoForTesting(
                                    helper::notifyCalled,
                                    new int[] {
                                        BrowsingDataType.HISTORY,
                                        BrowsingDataType.CACHE,
                                        BrowsingDataType.SITE_DATA,
                                        BrowsingDataType.PASSWORDS,
                                        BrowsingDataType.FORM_DATA
                                    },
                                    TimePeriod.LAST_HOUR);
                });
        helper.waitForCallback(0);
    }

    /** Test that both local and account passwords are deleted. */
    @Test
    @SmallTest
    @Restriction({GmsCoreVersionRestriction.RESTRICTION_TYPE_VERSION_GE_24W15})
    @RequiresRestart("crbug.com/358427311")
    public void testLocalAndAccountPasswordsDeleted() throws Exception {
        // Set up a syncing user with one password in each store.
        mSigninTestRule.addTestAccountThenSigninAndEnableSync();
        PasswordManagerTestHelper.setAccountForPasswordStore(SigninTestRule.TEST_ACCOUNT_EMAIL);
        PasswordStoreBridge bridge =
                ThreadUtils.runOnUiThreadBlocking(
                        () -> new PasswordStoreBridge(sActivityTestRule.getProfile(false)));
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    bridge.insertPasswordCredentialInProfileStore(
                            new PasswordStoreCredential(
                                    new GURL("https://site1.com"), "user1", "pwd1"));
                    bridge.insertPasswordCredentialInAccountStore(
                            new PasswordStoreCredential(
                                    new GURL("https://site2.com"), "user2", "pwd2"));
                });
        CriteriaHelper.pollUiThread(
                () -> {
                    Criteria.checkThat(
                            "The profile store should've had one password",
                            bridge.getPasswordStoreCredentialsCountForProfileStore(),
                            is(1));
                    Criteria.checkThat(
                            "The account store should've had one password",
                            bridge.getPasswordStoreCredentialsCountForAccountStore(),
                            is(1));
                });

        clearBrowsingData(BrowsingDataType.PASSWORDS, TimePeriod.ALL_TIME);

        CriteriaHelper.pollUiThread(
                () -> {
                    Criteria.checkThat(
                            "The profile store should be empty",
                            bridge.getPasswordStoreCredentialsCountForProfileStore(),
                            is(0));
                    Criteria.checkThat(
                            "The account store should be empty",
                            bridge.getPasswordStoreCredentialsCountForAccountStore(),
                            is(0));
                });
    }

    /** Test history deletion. */
    @Test
    @SmallTest
    public void testHistoryDeleted() throws Exception {
        Assert.assertEquals(0, getCookieCount());
        sActivityTestRule.loadUrlInNewTab(mUrl);
        Assert.assertEquals("false", runJavascriptSync("hasHistory()"));

        runJavascriptSync("setHistory()");
        Assert.assertEquals("true", runJavascriptSync("hasHistory()"));

        clearBrowsingData(BrowsingDataType.HISTORY, TimePeriod.LAST_HOUR);
        Assert.assertEquals("false", runJavascriptSync("hasHistory()"));
    }
}