chromium/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncTest.java

// Copyright 2014 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.sync;

import androidx.test.filters.LargeTest;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
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.DisabledTest;
import org.chromium.base.test.util.DoNotBatch;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Matchers;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.components.signin.identitymanager.ConsentLevel;
import org.chromium.components.sync.DataType;
import org.chromium.components.sync.LocalDataDescription;
import org.chromium.components.sync.PassphraseType;
import org.chromium.components.sync.UserSelectableType;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/** Test suite for Sync. */
@RunWith(ChromeJUnit4ClassRunner.class)
@DoNotBatch(reason = "TODO(crbug.com/40743432): SyncTestRule doesn't support batching.")
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class SyncTest {
    @Rule public SyncTestRule mSyncTestRule = new SyncTestRule();

    /** Waits until {@link SyncService#isSyncingUnencryptedUrls} returns desired value. */
    private void waitForIsSyncingUnencryptedUrls(boolean desiredValue) {
        CriteriaHelper.pollUiThread(
                () -> {
                    Criteria.checkThat(
                            mSyncTestRule.getSyncService().isSyncingUnencryptedUrls(),
                            Matchers.is(desiredValue));
                },
                SyncTestUtil.TIMEOUT_MS,
                SyncTestUtil.INTERVAL_MS);
    }

    @Test
    @LargeTest
    @Feature({"Sync"})
    @DisabledTest(message = "https://crbug.com/1197554")
    public void testSignInAndOut() {
        CoreAccountInfo accountInfo = mSyncTestRule.setUpAccountAndEnableSyncForTesting();

        // Signing out should disable sync.
        mSyncTestRule.signOut();
        Assert.assertFalse(SyncTestUtil.isSyncFeatureEnabled());

        // Signing back in should re-enable sync.
        mSyncTestRule.signinAndEnableSync(accountInfo);
        Assert.assertTrue("Sync should be re-enabled.", SyncTestUtil.isSyncFeatureActive());
    }

    @Test
    @LargeTest
    @Feature({"Sync"})
    public void testStopAndClear() {
        mSyncTestRule.setUpAccountAndEnableSyncForTesting();
        CriteriaHelper.pollUiThread(
                () ->
                        IdentityServicesProvider.get()
                                .getIdentityManager(ProfileManager.getLastUsedRegularProfile())
                                .hasPrimaryAccount(ConsentLevel.SYNC),
                "Timed out checking that hasPrimaryAccount(ConsentLevel.SYNC) == true",
                SyncTestUtil.TIMEOUT_MS,
                SyncTestUtil.INTERVAL_MS);

        mSyncTestRule.clearServerData();

        // Clearing server data should turn off sync and sign out of chrome.
        Assert.assertNull(mSyncTestRule.getPrimaryAccount(ConsentLevel.SYNC));
        Assert.assertFalse(SyncTestUtil.isSyncFeatureEnabled());
        CriteriaHelper.pollUiThread(
                () ->
                        !IdentityServicesProvider.get()
                                .getIdentityManager(ProfileManager.getLastUsedRegularProfile())
                                .hasPrimaryAccount(ConsentLevel.SYNC),
                "Timed out checking that hasPrimaryAccount(ConsentLevel.SYNC) == false",
                SyncTestUtil.TIMEOUT_MS,
                SyncTestUtil.INTERVAL_MS);
    }

    @Test
    @LargeTest
    @Feature({"Sync"})
    public void testStopAndStartSync() {
        CoreAccountInfo accountInfo = mSyncTestRule.setUpAccountAndEnableSyncForTesting();
        Assert.assertEquals(accountInfo, mSyncTestRule.getPrimaryAccount(ConsentLevel.SYNC));

        // Signing out should disable sync.
        mSyncTestRule.signOut();
        Assert.assertFalse(SyncTestUtil.isSyncFeatureEnabled());
        Assert.assertNull(mSyncTestRule.getPrimaryAccount(ConsentLevel.SYNC));

        accountInfo = mSyncTestRule.setUpAccountAndEnableSyncForTesting();

        Assert.assertTrue(SyncTestUtil.isSyncFeatureEnabled());
        Assert.assertEquals(accountInfo, mSyncTestRule.getPrimaryAccount(ConsentLevel.SYNC));
    }

    @Test
    @LargeTest
    @Feature({"Sync"})
    public void testIsSyncingUnencryptedUrlsWhileUsingKeystorePassphrase() {
        mSyncTestRule.setUpAccountAndEnableSyncForTesting();
        // By default Sync is being setup with KEYSTORE_PASSPHRASE and all types enabled.
        CriteriaHelper.pollUiThread(
                () ->
                        mSyncTestRule.getSyncService().getPassphraseType()
                                == PassphraseType.KEYSTORE_PASSPHRASE,
                "Timed out checking getPassphraseType() == PassphraseType.KEYSTORE_PASSPHRASE",
                SyncTestUtil.TIMEOUT_MS,
                SyncTestUtil.INTERVAL_MS);
        waitForIsSyncingUnencryptedUrls(true);

        // isSyncingUnencryptedUrls() should return false when history is disabled.
        mSyncTestRule.disableDataType(UserSelectableType.HISTORY);
        waitForIsSyncingUnencryptedUrls(false);

        // Now enable only history datatypes and verify that isSyncingUnencryptedUrls() returns true
        // again.
        mSyncTestRule.setSelectedTypes(
                false, new HashSet<>(Arrays.asList(UserSelectableType.HISTORY)));
        waitForIsSyncingUnencryptedUrls(true);
    }

    @Test
    @LargeTest
    @Feature({"Sync"})
    public void testIsSyncingUnencryptedUrlsWhileUsingTrustedVaultPassprhase() {
        mSyncTestRule.getFakeServerHelper().setTrustedVaultNigori(new byte[] {1, 2, 3, 4});
        mSyncTestRule.setUpAccountAndEnableSyncForTesting();

        // isSyncingUnencryptedUrls() should treat TRUSTED_VAULT_PASSPHRASE in exactly the same way
        // as KEYSTORE_PASSPHRASE.
        CriteriaHelper.pollUiThread(
                () ->
                        mSyncTestRule.getSyncService().getPassphraseType()
                                == PassphraseType.TRUSTED_VAULT_PASSPHRASE,
                "Timed out checking getPassphraseType() == PassphraseType.TRUSTED_VAULT_PASSPHRASE",
                SyncTestUtil.TIMEOUT_MS,
                SyncTestUtil.INTERVAL_MS);
        waitForIsSyncingUnencryptedUrls(true);

        // isSyncingUnencryptedUrls() should return false when history is disabled.
        mSyncTestRule.disableDataType(UserSelectableType.HISTORY);
        waitForIsSyncingUnencryptedUrls(false);

        // Now enable only history datatypes and verify that isSyncingUnencryptedUrls() returns true
        // again.
        mSyncTestRule.setSelectedTypes(
                false, new HashSet<>(Arrays.asList(UserSelectableType.HISTORY)));
        waitForIsSyncingUnencryptedUrls(true);
    }

    @Test
    @LargeTest
    @Feature({"Sync"})
    public void testIsSyncingUnencryptedUrlsWhileUsingCustomPassphrase() {
        mSyncTestRule.setUpAccountAndEnableSyncForTesting();
        SyncTestUtil.encryptWithPassphrase("passphrase");
        CriteriaHelper.pollUiThread(
                () ->
                        mSyncTestRule.getSyncService().getPassphraseType()
                                == PassphraseType.CUSTOM_PASSPHRASE,
                "Timed out checking getPassphraseType() == PassphraseType.CUSTOM_PASSPHRASE",
                SyncTestUtil.TIMEOUT_MS,
                SyncTestUtil.INTERVAL_MS);

        // isSyncingUnencryptedUrls() should return false with CUSTOM_PASSPHRASE no matter which
        // datatypes are enabled.
        waitForIsSyncingUnencryptedUrls(false);
    }

    @Test
    @LargeTest
    @Feature({"Sync"})
    public void testGetLocalDataDescription() throws Exception {
        CoreAccountInfo accountInfo = mSyncTestRule.setUpAccountAndSignInForTesting();
        Assert.assertEquals(accountInfo, mSyncTestRule.getPrimaryAccount(ConsentLevel.SIGNIN));

        CallbackHelper callbackHelper = new CallbackHelper();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mSyncTestRule
                            .getSyncService()
                            .getLocalDataDescriptions(
                                    Set.of(
                                            DataType.BOOKMARKS,
                                            DataType.PASSWORDS,
                                            DataType.READING_LIST),
                                    localDataDescriptionsMap -> {
                                        int sum =
                                                localDataDescriptionsMap.values().stream()
                                                        .map(LocalDataDescription::itemCount)
                                                        .reduce(0, Integer::sum);
                                        Assert.assertEquals(0, sum);
                                        callbackHelper.notifyCalled();
                                        return;
                                    });
                });
        callbackHelper.waitForOnly();
    }
}