1// Copyright 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.sync.notifier.signin; 6 7import android.accounts.Account; 8import android.content.Context; 9import android.test.InstrumentationTestCase; 10import android.test.suitebuilder.annotation.SmallTest; 11 12import org.chromium.base.ThreadUtils; 13import org.chromium.base.test.util.Feature; 14import org.chromium.sync.notifier.SyncStatusHelper; 15import org.chromium.sync.notifier.SyncStatusHelper.CachedAccountSyncSettings; 16import org.chromium.sync.notifier.SyncStatusHelper.SyncSettingsChangedObserver; 17import org.chromium.sync.signin.ChromeSigninController; 18import org.chromium.sync.test.util.MockSyncContentResolverDelegate; 19 20public class SyncStatusHelperTest extends InstrumentationTestCase { 21 22 private static class CountingMockSyncContentResolverDelegate 23 extends MockSyncContentResolverDelegate { 24 private int mGetMasterSyncAutomaticallyCalls; 25 private int mGetSyncAutomaticallyCalls; 26 private int mGetIsSyncableCalls; 27 private int mSetIsSyncableCalls; 28 private int mSetSyncAutomaticallyCalls; 29 30 @Override 31 public boolean getMasterSyncAutomatically() { 32 mGetMasterSyncAutomaticallyCalls++; 33 return super.getMasterSyncAutomatically(); 34 } 35 36 @Override 37 public boolean getSyncAutomatically(Account account, String authority) { 38 mGetSyncAutomaticallyCalls++; 39 return super.getSyncAutomatically(account, authority); 40 } 41 42 @Override 43 public int getIsSyncable(Account account, String authority) { 44 mGetIsSyncableCalls++; 45 return super.getIsSyncable(account, authority); 46 } 47 48 @Override 49 public void setIsSyncable(Account account, String authority, int syncable) { 50 mSetIsSyncableCalls++; 51 super.setIsSyncable(account, authority, syncable); 52 } 53 54 @Override 55 public void setSyncAutomatically(Account account, String authority, boolean sync) { 56 mSetSyncAutomaticallyCalls++; 57 super.setSyncAutomatically(account, authority, sync); 58 } 59 } 60 61 private static class CountingCachedAccountSyncSettings extends CachedAccountSyncSettings { 62 private int mUpdateSyncSettingsForAccountInternalCalls; 63 private int mSetIsSyncableInternalCalls; 64 private int mSetSyncAutomaticallyInternalCalls; 65 66 public CountingCachedAccountSyncSettings(String contractAuthority, 67 MockSyncContentResolverDelegate contentResolverWrapper) { 68 super(contractAuthority, contentResolverWrapper); 69 } 70 71 @Override 72 protected void updateSyncSettingsForAccountInternal(Account account) { 73 mUpdateSyncSettingsForAccountInternalCalls++; 74 super.updateSyncSettingsForAccountInternal(account); 75 } 76 77 @Override 78 protected void setIsSyncableInternal(Account account) { 79 mSetIsSyncableInternalCalls++; 80 super.setIsSyncableInternal(account); 81 } 82 83 @Override 84 protected void setSyncAutomaticallyInternal(Account account, boolean value) { 85 mSetSyncAutomaticallyInternalCalls++; 86 super.setSyncAutomaticallyInternal(account, value); 87 } 88 89 public void resetCount() { 90 mUpdateSyncSettingsForAccountInternalCalls = 0; 91 } 92 } 93 94 private static class MockSyncSettingsObserver implements SyncSettingsChangedObserver { 95 private boolean mReceivedNotification; 96 97 public void clearNotification() { 98 mReceivedNotification = false; 99 } 100 101 public boolean didReceiveNotification() { 102 return mReceivedNotification; 103 } 104 105 @Override 106 public void syncSettingsChanged() { 107 mReceivedNotification = true; 108 } 109 } 110 111 private SyncStatusHelper mHelper; 112 private CountingMockSyncContentResolverDelegate mSyncContentResolverDelegate; 113 private String mAuthority; 114 private Account mTestAccount; 115 private Account mAlternateTestAccount; 116 private CountingCachedAccountSyncSettings mCachedAccountSyncSettings; 117 private MockSyncSettingsObserver mSyncSettingsObserver; 118 119 @Override 120 protected void setUp() throws Exception { 121 mSyncContentResolverDelegate = new CountingMockSyncContentResolverDelegate(); 122 Context context = getInstrumentation().getTargetContext(); 123 mCachedAccountSyncSettings = new CountingCachedAccountSyncSettings( 124 context.getPackageName(), mSyncContentResolverDelegate); 125 SyncStatusHelper.overrideSyncStatusHelperForTests( 126 context, mSyncContentResolverDelegate, mCachedAccountSyncSettings); 127 mHelper = SyncStatusHelper.get(getInstrumentation().getTargetContext()); 128 // Need to set the signed in account name to ensure that sync settings notifications 129 // update the right account. 130 ChromeSigninController.get( 131 getInstrumentation().getTargetContext()).setSignedInAccountName( 132 "account@example.com"); 133 mAuthority = SyncStatusHelper.get(getInstrumentation().getTargetContext()) 134 .getContractAuthority(); 135 mTestAccount = new Account("account@example.com", "com.google"); 136 mAlternateTestAccount = new Account("alternateAccount@example.com", "com.google"); 137 138 mSyncSettingsObserver = new MockSyncSettingsObserver(); 139 mHelper.registerSyncSettingsChangedObserver(mSyncSettingsObserver); 140 141 super.setUp(); 142 } 143 144 @SmallTest 145 @Feature({"Sync"}) 146 public void testToggleMasterSyncAutomaticallyFromSettings() throws InterruptedException { 147 mSyncContentResolverDelegate.setMasterSyncAutomatically(true); 148 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 149 assertTrue("master sync should be set", mHelper.isMasterSyncAutomaticallyEnabled()); 150 151 mSyncContentResolverDelegate.setMasterSyncAutomatically(false); 152 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 153 assertFalse("master sync should be unset", mHelper.isMasterSyncAutomaticallyEnabled()); 154 } 155 156 @SmallTest 157 @Feature({"Sync"}) 158 public void testToggleAccountSyncFromSettings() throws InterruptedException { 159 // Turn on syncability. 160 mSyncContentResolverDelegate.setMasterSyncAutomatically(true); 161 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 162 163 // First sync 164 mSyncContentResolverDelegate.setIsSyncable(mTestAccount, mAuthority, 1); 165 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 166 mSyncContentResolverDelegate.setSyncAutomatically(mTestAccount, mAuthority, true); 167 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 168 assertTrue("sync should be set", mHelper.isSyncEnabled(mTestAccount)); 169 assertTrue("sync should be set for chrome app", 170 mHelper.isSyncEnabledForChrome(mTestAccount)); 171 172 // Disable sync automatically for the app 173 mSyncContentResolverDelegate.setSyncAutomatically(mTestAccount, mAuthority, false); 174 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 175 assertFalse("sync should be unset", mHelper.isSyncEnabled(mTestAccount)); 176 assertFalse("sync should be unset for chrome app", 177 mHelper.isSyncEnabledForChrome(mTestAccount)); 178 179 // Re-enable sync 180 mSyncContentResolverDelegate.setSyncAutomatically(mTestAccount, mAuthority, true); 181 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 182 assertTrue("sync should be re-enabled", mHelper.isSyncEnabled(mTestAccount)); 183 assertTrue("sync should be unset for chrome app", 184 mHelper.isSyncEnabledForChrome(mTestAccount)); 185 186 // Disabled from master sync 187 mSyncContentResolverDelegate.setMasterSyncAutomatically(false); 188 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 189 assertFalse("sync should be disabled due to master sync", 190 mHelper.isSyncEnabled(mTestAccount)); 191 assertTrue("sync should be set for chrome app", 192 mHelper.isSyncEnabledForChrome(mTestAccount)); 193 } 194 195 @SmallTest 196 @Feature({"Sync"}) 197 public void testToggleAccountSyncFromApplication() throws InterruptedException { 198 // Turn on syncability. 199 mSyncContentResolverDelegate.setMasterSyncAutomatically(true); 200 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 201 202 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 203 @Override 204 public void run() { 205 mHelper.enableAndroidSync(mTestAccount); 206 } 207 }); 208 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 209 assertTrue("account should be synced", mHelper.isSyncEnabled(mTestAccount)); 210 211 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 212 @Override 213 public void run() { 214 mHelper.disableAndroidSync(mTestAccount); 215 } 216 }); 217 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 218 assertFalse("account should not be synced", mHelper.isSyncEnabled(mTestAccount)); 219 } 220 221 @SmallTest 222 @Feature({"Sync"}) 223 public void testToggleSyncabilityForMultipleAccounts() throws InterruptedException { 224 // Turn on syncability. 225 mSyncContentResolverDelegate.setMasterSyncAutomatically(true); 226 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 227 228 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 229 @Override 230 public void run() { 231 mHelper.enableAndroidSync(mTestAccount); 232 } 233 }); 234 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 235 assertTrue("account should be synced", mHelper.isSyncEnabled(mTestAccount)); 236 237 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 238 @Override 239 public void run() { 240 mHelper.enableAndroidSync(mAlternateTestAccount); 241 } 242 }); 243 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 244 assertTrue("alternate account should be synced", 245 mHelper.isSyncEnabled(mAlternateTestAccount)); 246 247 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 248 @Override 249 public void run() { 250 mHelper.disableAndroidSync(mAlternateTestAccount); 251 } 252 }); 253 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 254 assertFalse("alternate account should not be synced", 255 mHelper.isSyncEnabled(mAlternateTestAccount)); 256 assertTrue("account should still be synced", mHelper.isSyncEnabled(mTestAccount)); 257 258 // Ensure we don't erroneously re-use cached data. 259 assertFalse("null account should not be synced", mHelper.isSyncEnabled(null)); 260 } 261 262 @SmallTest 263 @Feature({"Sync"}) 264 public void testSyncSettingsCaching() throws InterruptedException { 265 // Turn on syncability. 266 mSyncContentResolverDelegate.setMasterSyncAutomatically(true); 267 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 268 269 ThreadUtils.runOnUiThreadBlocking(new Runnable() { 270 @Override 271 public void run() { 272 mHelper.enableAndroidSync(mTestAccount); 273 } 274 }); 275 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 276 assertTrue("account should be synced", mHelper.isSyncEnabled(mTestAccount)); 277 278 int masterSyncAutomaticallyCalls = 279 mSyncContentResolverDelegate.mGetMasterSyncAutomaticallyCalls; 280 int isSyncableCalls = mSyncContentResolverDelegate.mGetIsSyncableCalls; 281 int getSyncAutomaticallyAcalls = mSyncContentResolverDelegate.mGetSyncAutomaticallyCalls; 282 283 // Do a bunch of reads. 284 mHelper.isMasterSyncAutomaticallyEnabled(); 285 mHelper.isSyncEnabled(); 286 mHelper.isSyncEnabled(mTestAccount); 287 mHelper.isSyncEnabledForChrome(mTestAccount); 288 289 // Ensure values were read from cache. 290 assertEquals(masterSyncAutomaticallyCalls, 291 mSyncContentResolverDelegate.mGetMasterSyncAutomaticallyCalls); 292 assertEquals(isSyncableCalls, mSyncContentResolverDelegate.mGetIsSyncableCalls); 293 assertEquals(getSyncAutomaticallyAcalls, 294 mSyncContentResolverDelegate.mGetSyncAutomaticallyCalls); 295 296 // Do a bunch of reads for alternate account. 297 mHelper.isMasterSyncAutomaticallyEnabled(); 298 mHelper.isSyncEnabled(mAlternateTestAccount); 299 mHelper.isSyncEnabledForChrome(mAlternateTestAccount); 300 301 // Ensure master sync was cached but others are fetched once. 302 assertEquals(masterSyncAutomaticallyCalls, 303 mSyncContentResolverDelegate.mGetMasterSyncAutomaticallyCalls); 304 assertEquals(isSyncableCalls + 1, mSyncContentResolverDelegate.mGetIsSyncableCalls); 305 assertEquals(getSyncAutomaticallyAcalls + 1, 306 mSyncContentResolverDelegate.mGetSyncAutomaticallyCalls); 307 } 308 309 @SmallTest 310 @Feature({"Sync"}) 311 public void testGetContractAuthority() throws Exception { 312 assertEquals("The contract authority should be the package name.", 313 getInstrumentation().getTargetContext().getPackageName(), 314 mHelper.getContractAuthority()); 315 } 316 317 @SmallTest 318 @Feature({"Sync"}) 319 public void testCachedAccountSyncSettingsExitEarly() throws InterruptedException { 320 mSyncContentResolverDelegate.disableObserverNotifications(); 321 322 mCachedAccountSyncSettings.updateSyncSettingsForAccount(null); 323 assertTrue("Update sync settings failed to exit early", mCachedAccountSyncSettings. 324 mUpdateSyncSettingsForAccountInternalCalls == 0); 325 326 mCachedAccountSyncSettings.updateSyncSettingsForAccount(mTestAccount); 327 assertTrue("Update sync settings should not have exited early", mCachedAccountSyncSettings. 328 mUpdateSyncSettingsForAccountInternalCalls == 1); 329 330 mCachedAccountSyncSettings.setIsSyncable(mTestAccount); 331 assertTrue("setIsSyncable should not have exited early", 332 mCachedAccountSyncSettings.mSetIsSyncableInternalCalls == 1); 333 334 mCachedAccountSyncSettings.setIsSyncable(mTestAccount); 335 assertTrue("setIsSyncable failed to exit early", mCachedAccountSyncSettings. 336 mSetIsSyncableInternalCalls == 1); 337 338 mCachedAccountSyncSettings.setSyncAutomatically(mTestAccount, true); 339 assertTrue("setSyncAutomatically should not have to exited early", 340 mCachedAccountSyncSettings.mSetSyncAutomaticallyInternalCalls == 1); 341 342 mCachedAccountSyncSettings.setSyncAutomatically(mTestAccount, true); 343 assertTrue("setSyncAutomatically failed to exit early", 344 mCachedAccountSyncSettings.mSetSyncAutomaticallyInternalCalls == 1); 345 346 mCachedAccountSyncSettings.setSyncAutomatically(mTestAccount, false); 347 assertTrue("setSyncAutomatically should not have to exited early", 348 mCachedAccountSyncSettings.mSetSyncAutomaticallyInternalCalls == 2); 349 350 mCachedAccountSyncSettings.setSyncAutomatically(mTestAccount, false); 351 assertTrue("setSyncAutomatically failed to exit early", 352 mCachedAccountSyncSettings.mSetSyncAutomaticallyInternalCalls == 2); 353 } 354 355 @SmallTest 356 @Feature({"Sync"}) 357 public void testCachedAccountSyncSettingsDidUpdate() throws InterruptedException { 358 // Since we're just testing the cache we disable observer notifications to prevent 359 // notifications to SyncStatusHelper from mutating it. 360 mSyncContentResolverDelegate.disableObserverNotifications(); 361 362 mCachedAccountSyncSettings.clearUpdateStatus(); 363 mCachedAccountSyncSettings.getSyncAutomatically(mTestAccount); 364 assertTrue("getSyncAutomatically on un-populated cache failed to update DidUpdate flag", 365 mCachedAccountSyncSettings.getDidUpdateStatus()); 366 367 mCachedAccountSyncSettings.clearUpdateStatus(); 368 mCachedAccountSyncSettings.getSyncAutomatically(mTestAccount); 369 assertFalse("getSyncAutomatically on populated cache updated DidUpdate flag", 370 mCachedAccountSyncSettings.getDidUpdateStatus()); 371 372 mCachedAccountSyncSettings.updateSyncSettingsForAccount(mAlternateTestAccount); 373 assertTrue("updateSyncSettingsForAccount failed to update DidUpdate flag", 374 mCachedAccountSyncSettings.getDidUpdateStatus()); 375 376 mCachedAccountSyncSettings.clearUpdateStatus(); 377 378 mCachedAccountSyncSettings.updateSyncSettingsForAccount(mTestAccount); 379 assertTrue("updateSyncSettingsForAccount failed to update DidUpdate flag", 380 mCachedAccountSyncSettings.getDidUpdateStatus()); 381 382 mCachedAccountSyncSettings.clearUpdateStatus(); 383 384 mCachedAccountSyncSettings.updateSyncSettingsForAccount(mTestAccount); 385 assertFalse("updateSyncSettingsForAccount updated DidUpdate flag", 386 mCachedAccountSyncSettings.getDidUpdateStatus()); 387 388 mCachedAccountSyncSettings.clearUpdateStatus(); 389 mCachedAccountSyncSettings.setIsSyncable(mTestAccount); 390 assertTrue("setIsSyncable failed to update DidUpdate flag", 391 mCachedAccountSyncSettings.getDidUpdateStatus()); 392 393 mCachedAccountSyncSettings.clearUpdateStatus(); 394 mCachedAccountSyncSettings.setIsSyncable(mTestAccount); 395 assertFalse("setIsSyncable updated DidUpdate flag", 396 mCachedAccountSyncSettings.getDidUpdateStatus()); 397 398 mCachedAccountSyncSettings.clearUpdateStatus(); 399 mCachedAccountSyncSettings.setSyncAutomatically(mTestAccount, true); 400 assertTrue("setSyncAutomatically failed to update DidUpdate flag", 401 mCachedAccountSyncSettings.getDidUpdateStatus()); 402 403 mCachedAccountSyncSettings.clearUpdateStatus(); 404 mCachedAccountSyncSettings.setSyncAutomatically(mTestAccount, true); 405 assertFalse("setSyncAutomatically updated DidUpdate flag", 406 mCachedAccountSyncSettings.getDidUpdateStatus()); 407 408 mCachedAccountSyncSettings.clearUpdateStatus(); 409 mCachedAccountSyncSettings.setSyncAutomatically(mTestAccount, false); 410 assertTrue("setSyncAutomatically failed to update DidUpdate flag", 411 mCachedAccountSyncSettings.getDidUpdateStatus()); 412 413 mCachedAccountSyncSettings.clearUpdateStatus(); 414 mCachedAccountSyncSettings.setSyncAutomatically(mTestAccount, false); 415 assertFalse("setSyncAutomatically updated DidUpdate flag", 416 mCachedAccountSyncSettings.getDidUpdateStatus()); 417 } 418 419 @SmallTest 420 @Feature({"Sync"}) 421 public void testSyncStatusHelperPostsNotifications() throws InterruptedException { 422 // Turn on syncability. 423 mSyncContentResolverDelegate.setMasterSyncAutomatically(true); 424 mSyncContentResolverDelegate.waitForLastNotificationCompleted(); 425 426 mSyncSettingsObserver.clearNotification(); 427 mHelper.isSyncEnabled(mAlternateTestAccount); 428 assertTrue("isSyncEnabled on wrongly populated cache did not trigger observers", 429 mSyncSettingsObserver.didReceiveNotification()); 430 431 mSyncSettingsObserver.clearNotification(); 432 mHelper.isSyncEnabled(mTestAccount); 433 assertTrue("isSyncEnabled on wrongly populated cache did not trigger observers", 434 mSyncSettingsObserver.didReceiveNotification()); 435 436 mSyncSettingsObserver.clearNotification(); 437 mHelper.enableAndroidSync(mTestAccount); 438 assertTrue("enableAndroidSync did not trigger observers", 439 mSyncSettingsObserver.didReceiveNotification()); 440 441 mSyncSettingsObserver.clearNotification(); 442 mHelper.enableAndroidSync(mTestAccount); 443 assertFalse("enableAndroidSync triggered observers", 444 mSyncSettingsObserver.didReceiveNotification()); 445 446 mSyncSettingsObserver.clearNotification(); 447 mHelper.disableAndroidSync(mTestAccount); 448 assertTrue("disableAndroidSync did not trigger observers", 449 mSyncSettingsObserver.didReceiveNotification()); 450 451 mSyncSettingsObserver.clearNotification(); 452 mHelper.disableAndroidSync(mTestAccount); 453 assertFalse("disableAndroidSync triggered observers", 454 mSyncSettingsObserver.didReceiveNotification()); 455 } 456} 457