blob: 1ca912e94858fffac3b1a7fe69938f9421de6817 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.sync;
import android.accounts.Account;
import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.Callback;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.Feature;
import org.chromium.components.signin.AccountManagerFacade;
import org.chromium.components.signin.ChromeSigninController;
import org.chromium.components.signin.test.util.AccountHolder;
import org.chromium.components.signin.test.util.FakeAccountManagerDelegate;
import org.chromium.components.sync.AndroidSyncSettings.AndroidSyncSettingsObserver;
import org.chromium.components.sync.test.util.MockSyncContentResolverDelegate;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Tests for AndroidSyncSettings.
*/
@RunWith(BaseJUnit4ClassRunner.class)
public class AndroidSyncSettingsTest {
private static class CountingMockSyncContentResolverDelegate
extends MockSyncContentResolverDelegate {
private final AtomicInteger mGetMasterSyncAutomaticallyCalls = new AtomicInteger();
private final AtomicInteger mGetSyncAutomaticallyCalls = new AtomicInteger();
private final AtomicInteger mGetIsSyncableCalls = new AtomicInteger();
private final AtomicInteger mSetIsSyncableCalls = new AtomicInteger();
private final AtomicInteger mSetSyncAutomaticallyCalls = new AtomicInteger();
private final AtomicInteger mRemovePeriodicSyncCalls = new AtomicInteger();
@Override
public boolean getMasterSyncAutomatically() {
mGetMasterSyncAutomaticallyCalls.getAndIncrement();
return super.getMasterSyncAutomatically();
}
@Override
public boolean getSyncAutomatically(Account account, String authority) {
mGetSyncAutomaticallyCalls.getAndIncrement();
return super.getSyncAutomatically(account, authority);
}
@Override
public int getIsSyncable(Account account, String authority) {
mGetIsSyncableCalls.getAndIncrement();
return super.getIsSyncable(account, authority);
}
@Override
public void setIsSyncable(Account account, String authority, int syncable) {
mSetIsSyncableCalls.getAndIncrement();
super.setIsSyncable(account, authority, syncable);
}
@Override
public void setSyncAutomatically(Account account, String authority, boolean sync) {
mSetSyncAutomaticallyCalls.getAndIncrement();
super.setSyncAutomatically(account, authority, sync);
}
@Override
public void removePeriodicSync(Account account, String authority, Bundle extras) {
mRemovePeriodicSyncCalls.getAndIncrement();
super.removePeriodicSync(account, authority, extras);
}
}
private static class MockSyncSettingsObserver implements AndroidSyncSettingsObserver {
private boolean mReceivedNotification;
public void clearNotification() {
mReceivedNotification = false;
}
public boolean receivedNotification() {
return mReceivedNotification;
}
@Override
public void androidSyncSettingsChanged() {
mReceivedNotification = true;
}
}
private CountingMockSyncContentResolverDelegate mSyncContentResolverDelegate;
private String mAuthority;
private Account mAccount;
private Account mAlternateAccount;
private MockSyncSettingsObserver mSyncSettingsObserver;
private FakeAccountManagerDelegate mAccountManager;
private CallbackHelper mCallbackHelper;
private int mNumberOfCallsToWait;
@Before
public void setUp() throws Exception {
mNumberOfCallsToWait = 0;
mCallbackHelper = new CallbackHelper();
setupTestAccounts();
// Set signed in account to mAccount before initializing AndroidSyncSettings to let
// AndroidSyncSettings establish correct assumptions.
ChromeSigninController.get().setSignedInAccountName(mAccount.name);
mSyncContentResolverDelegate = new CountingMockSyncContentResolverDelegate();
overrideAndroidSyncSettings();
mAuthority = AndroidSyncSettings.get().getContractAuthority();
Assert.assertEquals(1, mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority));
mSyncSettingsObserver = new MockSyncSettingsObserver();
AndroidSyncSettings.get().registerObserver(mSyncSettingsObserver);
}
/**
* Overrides AndroidSyncSettings passing mSyncContentResolverDelegate and waits for settings
* changes to propagate to ContentResolverDelegate.
*/
private void overrideAndroidSyncSettings() throws Exception {
AndroidSyncSettings.overrideForTests(
mSyncContentResolverDelegate, (Boolean result) -> mCallbackHelper.notifyCalled());
mNumberOfCallsToWait++;
mCallbackHelper.waitForCallback(0, mNumberOfCallsToWait);
}
private void setupTestAccounts() {
mAccountManager = new FakeAccountManagerDelegate(
FakeAccountManagerDelegate.DISABLE_PROFILE_DATA_SOURCE);
AccountManagerFacade.overrideAccountManagerFacadeForTests(mAccountManager);
mAccount = addTestAccount("account@example.com");
mAlternateAccount = addTestAccount("alternate@example.com");
}
private Account addTestAccount(String name) {
Account account = AccountManagerFacade.createAccountFromName(name);
AccountHolder holder = AccountHolder.builder(account).alwaysAccept(true).build();
mAccountManager.addAccountHolderBlocking(holder);
return account;
}
@After
public void tearDown() throws Exception {
if (mNumberOfCallsToWait > 0) mCallbackHelper.waitForCallback(0, mNumberOfCallsToWait);
}
private void enableChromeSyncOnUiThread() {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
AndroidSyncSettings.get().enableChromeSync();
}
});
}
private void disableChromeSyncOnUiThread() {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
AndroidSyncSettings.get().disableChromeSync();
}
});
}
private void updateAccountSync(Account account) throws InterruptedException, TimeoutException {
updateAccount(account);
mCallbackHelper.waitForCallback(0, mNumberOfCallsToWait);
}
private void updateAccount(Account account) {
updateAccountWithCallback(account, (Boolean result) -> {
mCallbackHelper.notifyCalled();
});
}
private void updateAccountWithCallback(Account account, Callback<Boolean> callback) {
AndroidSyncSettings.get().updateAccount(account, callback);
mNumberOfCallsToWait++;
}
@Test
@SmallTest
@Feature({"Sync"})
public void testAccountInitialization() throws InterruptedException, TimeoutException {
// mAccount was set to be syncable and not have periodic syncs.
Assert.assertEquals(1, mSyncContentResolverDelegate.mSetIsSyncableCalls.get());
Assert.assertEquals(1, mSyncContentResolverDelegate.mRemovePeriodicSyncCalls.get());
updateAccountSync(null);
// mAccount was set to be not syncable.
Assert.assertEquals(2, mSyncContentResolverDelegate.mSetIsSyncableCalls.get());
Assert.assertEquals(1, mSyncContentResolverDelegate.mRemovePeriodicSyncCalls.get());
updateAccount(mAlternateAccount);
// mAlternateAccount was set to be syncable and not have periodic syncs.
Assert.assertEquals(3, mSyncContentResolverDelegate.mSetIsSyncableCalls.get());
Assert.assertEquals(2, mSyncContentResolverDelegate.mRemovePeriodicSyncCalls.get());
}
@Test
@SmallTest
@Feature({"Sync"})
public void testToggleMasterSyncFromSettings() throws InterruptedException {
mSyncContentResolverDelegate.setMasterSyncAutomatically(true);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertTrue(
"master sync should be set", AndroidSyncSettings.get().isMasterSyncEnabled());
mSyncContentResolverDelegate.setMasterSyncAutomatically(false);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertFalse(
"master sync should be unset", AndroidSyncSettings.get().isMasterSyncEnabled());
}
@Test
@SmallTest
@Feature({"Sync"})
public void testToggleChromeSyncFromSettings() throws InterruptedException {
// Turn on syncability.
mSyncContentResolverDelegate.setMasterSyncAutomatically(true);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
// First sync
mSyncContentResolverDelegate.setIsSyncable(mAccount, mAuthority, 1);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
mSyncContentResolverDelegate.setSyncAutomatically(mAccount, mAuthority, true);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertTrue("sync should be set", AndroidSyncSettings.get().isSyncEnabled());
Assert.assertTrue("sync should be set for chrome app",
AndroidSyncSettings.get().isChromeSyncEnabled());
// Disable sync automatically for the app
mSyncContentResolverDelegate.setSyncAutomatically(mAccount, mAuthority, false);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertFalse("sync should be unset", AndroidSyncSettings.get().isSyncEnabled());
Assert.assertFalse("sync should be unset for chrome app",
AndroidSyncSettings.get().isChromeSyncEnabled());
// Re-enable sync
mSyncContentResolverDelegate.setSyncAutomatically(mAccount, mAuthority, true);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertTrue("sync should be re-enabled", AndroidSyncSettings.get().isSyncEnabled());
Assert.assertTrue("sync should be set for chrome app",
AndroidSyncSettings.get().isChromeSyncEnabled());
// Disabled from master sync
mSyncContentResolverDelegate.setMasterSyncAutomatically(false);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertFalse("sync should be disabled due to master sync",
AndroidSyncSettings.get().isSyncEnabled());
Assert.assertFalse(
"master sync should be disabled", AndroidSyncSettings.get().isMasterSyncEnabled());
Assert.assertTrue("sync should be set for chrome app",
AndroidSyncSettings.get().isChromeSyncEnabled());
}
@Test
@SmallTest
@Feature({"Sync"})
public void testToggleAccountSyncFromApplication() throws InterruptedException {
// Turn on syncability.
mSyncContentResolverDelegate.setMasterSyncAutomatically(true);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
enableChromeSyncOnUiThread();
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertTrue("account should be synced", AndroidSyncSettings.get().isSyncEnabled());
disableChromeSyncOnUiThread();
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertFalse(
"account should not be synced", AndroidSyncSettings.get().isSyncEnabled());
}
@Test
@SmallTest
@Feature({"Sync"})
public void testToggleSyncabilityForMultipleAccounts() throws InterruptedException {
// Turn on syncability.
mSyncContentResolverDelegate.setMasterSyncAutomatically(true);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
enableChromeSyncOnUiThread();
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertTrue("account should be synced", AndroidSyncSettings.get().isSyncEnabled());
updateAccount(mAlternateAccount);
enableChromeSyncOnUiThread();
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertTrue(
"alternate account should be synced", AndroidSyncSettings.get().isSyncEnabled());
disableChromeSyncOnUiThread();
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertFalse("alternate account should not be synced",
AndroidSyncSettings.get().isSyncEnabled());
updateAccount(mAccount);
Assert.assertTrue(
"account should still be synced", AndroidSyncSettings.get().isSyncEnabled());
// Ensure we don't erroneously re-use cached data.
updateAccount(null);
Assert.assertFalse(
"null account should not be synced", AndroidSyncSettings.get().isSyncEnabled());
}
@Test
@SmallTest
@Feature({"Sync"})
public void testSyncSettingsCaching() throws InterruptedException {
// Turn on syncability.
mSyncContentResolverDelegate.setMasterSyncAutomatically(true);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
enableChromeSyncOnUiThread();
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertTrue("account should be synced", AndroidSyncSettings.get().isSyncEnabled());
int masterSyncAutomaticallyCalls =
mSyncContentResolverDelegate.mGetMasterSyncAutomaticallyCalls.get();
int isSyncableCalls = mSyncContentResolverDelegate.mGetIsSyncableCalls.get();
int getSyncAutomaticallyAcalls =
mSyncContentResolverDelegate.mGetSyncAutomaticallyCalls.get();
// Do a bunch of reads.
AndroidSyncSettings.get().isMasterSyncEnabled();
AndroidSyncSettings.get().isSyncEnabled();
AndroidSyncSettings.get().isChromeSyncEnabled();
// Ensure values were read from cache.
Assert.assertEquals(masterSyncAutomaticallyCalls,
mSyncContentResolverDelegate.mGetMasterSyncAutomaticallyCalls.get());
Assert.assertEquals(
isSyncableCalls, mSyncContentResolverDelegate.mGetIsSyncableCalls.get());
Assert.assertEquals(getSyncAutomaticallyAcalls,
mSyncContentResolverDelegate.mGetSyncAutomaticallyCalls.get());
// Do a bunch of reads for alternate account.
updateAccount(mAlternateAccount);
AndroidSyncSettings.get().isMasterSyncEnabled();
AndroidSyncSettings.get().isSyncEnabled();
AndroidSyncSettings.get().isChromeSyncEnabled();
// Ensure settings were only fetched once.
Assert.assertEquals(masterSyncAutomaticallyCalls + 1,
mSyncContentResolverDelegate.mGetMasterSyncAutomaticallyCalls.get());
Assert.assertEquals(
isSyncableCalls + 1, mSyncContentResolverDelegate.mGetIsSyncableCalls.get());
Assert.assertEquals(getSyncAutomaticallyAcalls + 1,
mSyncContentResolverDelegate.mGetSyncAutomaticallyCalls.get());
}
@Test
@SmallTest
@Feature({"Sync"})
public void testGetContractAuthority() throws Exception {
Assert.assertEquals("The contract authority should be the package name.",
InstrumentationRegistry.getTargetContext().getPackageName(),
AndroidSyncSettings.get().getContractAuthority());
}
@Test
@SmallTest
@Feature({"Sync"})
public void testAndroidSyncSettingsPostsNotifications() throws InterruptedException {
// Turn on syncability.
mSyncContentResolverDelegate.setMasterSyncAutomatically(true);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
mSyncSettingsObserver.clearNotification();
AndroidSyncSettings.get().enableChromeSync();
Assert.assertTrue("enableChromeSync should trigger observers",
mSyncSettingsObserver.receivedNotification());
mSyncSettingsObserver.clearNotification();
updateAccount(mAlternateAccount);
Assert.assertTrue("switching to account with different settings should notify",
mSyncSettingsObserver.receivedNotification());
mSyncSettingsObserver.clearNotification();
updateAccount(mAccount);
Assert.assertTrue("switching to account with different settings should notify",
mSyncSettingsObserver.receivedNotification());
mSyncSettingsObserver.clearNotification();
AndroidSyncSettings.get().enableChromeSync();
Assert.assertFalse("enableChromeSync shouldn't trigger observers",
mSyncSettingsObserver.receivedNotification());
mSyncSettingsObserver.clearNotification();
AndroidSyncSettings.get().disableChromeSync();
Assert.assertTrue("disableChromeSync should trigger observers",
mSyncSettingsObserver.receivedNotification());
mSyncSettingsObserver.clearNotification();
AndroidSyncSettings.get().disableChromeSync();
Assert.assertFalse("disableChromeSync shouldn't observers",
mSyncSettingsObserver.receivedNotification());
}
@Test
@SmallTest
@Feature({"Sync"})
public void testIsSyncableOnSigninAndNotOnSignout()
throws InterruptedException, TimeoutException {
Assert.assertEquals(1, mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority));
updateAccountWithCallback(null, (Boolean result) -> {
Assert.assertTrue(result);
mCallbackHelper.notifyCalled();
});
mCallbackHelper.waitForCallback(0, mNumberOfCallsToWait);
Assert.assertEquals(0, mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority));
updateAccount(mAccount);
Assert.assertEquals(1, mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority));
}
/**
* Regression test for crbug.com/475299.
*/
@Test
@SmallTest
@Feature({"Sync"})
public void testSyncableIsAlwaysSetWhenEnablingSync() throws InterruptedException {
// Setup bad state.
mSyncContentResolverDelegate.setMasterSyncAutomatically(true);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
mSyncContentResolverDelegate.setIsSyncable(mAccount, mAuthority, 1);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
mSyncContentResolverDelegate.setSyncAutomatically(mAccount, mAuthority, true);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
mSyncContentResolverDelegate.setIsSyncable(mAccount, mAuthority, 0);
mSyncContentResolverDelegate.waitForLastNotificationCompleted();
Assert.assertTrue(mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority) == 0);
Assert.assertTrue(mSyncContentResolverDelegate.getSyncAutomatically(mAccount, mAuthority));
// Ensure bug is fixed.
enableChromeSyncOnUiThread();
Assert.assertEquals(1, mSyncContentResolverDelegate.getIsSyncable(mAccount, mAuthority));
// Should still be enabled.
Assert.assertTrue(mSyncContentResolverDelegate.getSyncAutomatically(mAccount, mAuthority));
}
}