1 // Copyright 2013 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.
5 package org.chromium.chrome.browser.invalidation;
7 import android.accounts.Account;
8 import android.app.Activity;
9 import android.content.ComponentName;
10 import android.content.Context;
11 import android.content.Intent;
12 import android.content.pm.PackageManager;
13 import android.test.InstrumentationTestCase;
14 import android.test.suitebuilder.annotation.SmallTest;
16 import com.google.ipc.invalidation.external.client.types.ObjectId;
18 import org.chromium.base.ActivityState;
19 import org.chromium.base.ApplicationState;
20 import org.chromium.base.ApplicationStatus;
21 import org.chromium.base.CollectionUtil;
22 import org.chromium.base.test.util.AdvancedMockContext;
23 import org.chromium.base.test.util.Feature;
24 import org.chromium.sync.internal_api.pub.base.ModelType;
25 import org.chromium.sync.notifier.InvalidationIntentProtocol;
26 import org.chromium.sync.notifier.InvalidationPreferences;
27 import org.chromium.sync.notifier.InvalidationService;
28 import org.chromium.sync.notifier.SyncStatusHelper;
29 import org.chromium.sync.signin.AccountManagerHelper;
30 import org.chromium.sync.signin.ChromeSigninController;
31 import org.chromium.sync.test.util.MockSyncContentResolverDelegate;
33 import java.util.ArrayList;
34 import java.util.HashSet;
35 import java.util.List;
37 import java.util.concurrent.atomic.AtomicBoolean;
38 import java.util.concurrent.atomic.AtomicReference;
41 * Tests for the {@link InvalidationController}.
43 public class InvalidationControllerTest extends InstrumentationTestCase {
44 private IntentSavingContext mContext;
45 private InvalidationController mController;
48 protected void setUp() throws Exception {
49 mContext = new IntentSavingContext(getInstrumentation().getTargetContext());
50 mController = InvalidationController.get(mContext);
51 // We don't want to use the system content resolver, so we override it.
52 MockSyncContentResolverDelegate delegate = new MockSyncContentResolverDelegate();
53 // Android master sync can safely always be on.
54 delegate.setMasterSyncAutomatically(true);
55 SyncStatusHelper.overrideSyncStatusHelperForTests(mContext, delegate);
60 public void testStart() throws Exception {
62 assertEquals(1, mContext.getNumStartedIntents());
63 Intent intent = mContext.getStartedIntent(0);
64 validateIntentComponent(intent);
69 public void testStop() throws Exception {
71 assertEquals(1, mContext.getNumStartedIntents());
72 Intent intent = mContext.getStartedIntent(0);
73 validateIntentComponent(intent);
74 assertEquals(1, intent.getExtras().size());
75 assertTrue(intent.hasExtra(InvalidationIntentProtocol.EXTRA_STOP));
76 assertTrue(intent.getBooleanExtra(InvalidationIntentProtocol.EXTRA_STOP, false));
81 public void testResumingMainActivity() throws Exception {
82 // Resuming main activity should trigger a start if sync is enabled.
84 mController.onApplicationStateChange(ApplicationState.HAS_RUNNING_ACTIVITIES);
85 assertEquals(1, mContext.getNumStartedIntents());
86 Intent intent = mContext.getStartedIntent(0);
87 validateIntentComponent(intent);
92 public void testResumingMainActivityWithSyncDisabled() throws Exception {
93 // Resuming main activity should NOT trigger a start if sync is disabled.
95 mController.onApplicationStateChange(ApplicationState.HAS_RUNNING_ACTIVITIES);
96 assertEquals(0, mContext.getNumStartedIntents());
101 public void testPausingMainActivity() throws Exception {
102 // Resuming main activity should trigger a stop if sync is enabled.
104 mController.onApplicationStateChange(ApplicationState.HAS_PAUSED_ACTIVITIES);
105 assertEquals(1, mContext.getNumStartedIntents());
106 Intent intent = mContext.getStartedIntent(0);
107 validateIntentComponent(intent);
108 assertEquals(1, intent.getExtras().size());
109 assertTrue(intent.hasExtra(InvalidationIntentProtocol.EXTRA_STOP));
110 assertTrue(intent.getBooleanExtra(InvalidationIntentProtocol.EXTRA_STOP, false));
115 public void testPausingMainActivityWithSyncDisabled() throws Exception {
116 // Resuming main activity should NOT trigger a stop if sync is disabled.
118 mController.onApplicationStateChange(ApplicationState.HAS_PAUSED_ACTIVITIES);
119 assertEquals(0, mContext.getNumStartedIntents());
122 private void setupSync(boolean syncEnabled) {
123 Account account = AccountManagerHelper.createAccountFromName("test@gmail.com");
124 ChromeSigninController chromeSigninController = ChromeSigninController.get(mContext);
125 chromeSigninController.setSignedInAccountName(account.name);
126 SyncStatusHelper syncStatusHelper = SyncStatusHelper.get(mContext);
128 syncStatusHelper.enableAndroidSync(account);
130 syncStatusHelper.disableAndroidSync(account);
136 public void testEnsureConstructorRegistersListener() throws Exception {
137 final AtomicBoolean listenerCallbackCalled = new AtomicBoolean();
140 new InvalidationController(mContext) {
142 public void onApplicationStateChange(int newState) {
143 listenerCallbackCalled.set(true);
147 // Ensure initial state is correct.
148 assertFalse(listenerCallbackCalled.get());
150 // Ensure we get a callback, which means we have registered for them.
151 ApplicationStatus.onStateChangeForTesting(new Activity(), ActivityState.CREATED);
152 assertTrue(listenerCallbackCalled.get());
157 public void testRegisterForSpecificTypes() {
158 InvalidationController controller = new InvalidationController(mContext);
159 Account account = new Account("test@example.com", "bogus");
160 controller.setRegisteredTypes(account, false,
161 CollectionUtil.newHashSet(ModelType.BOOKMARK, ModelType.SESSION));
162 assertEquals(1, mContext.getNumStartedIntents());
164 // Validate destination.
165 Intent intent = mContext.getStartedIntent(0);
166 validateIntentComponent(intent);
167 assertEquals(InvalidationIntentProtocol.ACTION_REGISTER, intent.getAction());
170 Account intentAccount =
171 intent.getParcelableExtra(InvalidationIntentProtocol.EXTRA_ACCOUNT);
172 assertEquals(account, intentAccount);
174 // Validate registered types.
175 Set<String> expectedTypes = CollectionUtil.newHashSet(ModelType.BOOKMARK.name(),
176 ModelType.SESSION.name());
177 Set<String> actualTypes = new HashSet<String>();
178 actualTypes.addAll(intent.getStringArrayListExtra(
179 InvalidationIntentProtocol.EXTRA_REGISTERED_TYPES));
180 assertEquals(expectedTypes, actualTypes);
181 assertNull(InvalidationIntentProtocol.getRegisteredObjectIds(intent));
186 public void testRegisterForAllTypes() {
187 Account account = new Account("test@example.com", "bogus");
188 mController.setRegisteredTypes(account, true,
189 CollectionUtil.newHashSet(ModelType.BOOKMARK, ModelType.SESSION));
190 assertEquals(1, mContext.getNumStartedIntents());
192 // Validate destination.
193 Intent intent = mContext.getStartedIntent(0);
194 validateIntentComponent(intent);
195 assertEquals(InvalidationIntentProtocol.ACTION_REGISTER, intent.getAction());
198 Account intentAccount =
199 intent.getParcelableExtra(InvalidationIntentProtocol.EXTRA_ACCOUNT);
200 assertEquals(account, intentAccount);
202 // Validate registered types.
203 Set<String> expectedTypes = CollectionUtil.newHashSet(ModelType.ALL_TYPES_TYPE);
204 Set<String> actualTypes = new HashSet<String>();
205 actualTypes.addAll(intent.getStringArrayListExtra(
206 InvalidationIntentProtocol.EXTRA_REGISTERED_TYPES));
207 assertEquals(expectedTypes, actualTypes);
208 assertNull(InvalidationIntentProtocol.getRegisteredObjectIds(intent));
213 public void testRefreshShouldReadValuesFromDiskWithSpecificTypes() {
214 // Store some preferences for ModelTypes and account. We are using the helper class
215 // for this, so we don't have to deal with low-level details such as preference keys.
216 InvalidationPreferences invalidationPreferences = new InvalidationPreferences(mContext);
217 InvalidationPreferences.EditContext edit = invalidationPreferences.edit();
218 Set<String> storedModelTypes = new HashSet<String>();
219 storedModelTypes.add(ModelType.BOOKMARK.name());
220 storedModelTypes.add(ModelType.TYPED_URL.name());
221 Set<ModelType> refreshedTypes = new HashSet<ModelType>();
222 refreshedTypes.add(ModelType.BOOKMARK);
223 refreshedTypes.add(ModelType.TYPED_URL);
224 invalidationPreferences.setSyncTypes(edit, storedModelTypes);
225 Account storedAccount = AccountManagerHelper.createAccountFromName("test@gmail.com");
226 invalidationPreferences.setAccount(edit, storedAccount);
227 invalidationPreferences.commit(edit);
229 // Ensure all calls to {@link InvalidationController#setRegisteredTypes} store values
230 // we can inspect in the test.
231 final AtomicReference<Account> resultAccount = new AtomicReference<Account>();
232 final AtomicBoolean resultAllTypes = new AtomicBoolean();
233 final AtomicReference<Set<ModelType>> resultTypes = new AtomicReference<Set<ModelType>>();
234 InvalidationController controller = new InvalidationController(mContext) {
236 public void setRegisteredTypes(
237 Account account, boolean allTypes, Set<ModelType> types) {
238 resultAccount.set(account);
239 resultAllTypes.set(allTypes);
240 resultTypes.set(types);
245 controller.refreshRegisteredTypes(refreshedTypes);
247 // Validate the values.
248 assertEquals(storedAccount, resultAccount.get());
249 assertEquals(false, resultAllTypes.get());
250 assertEquals(ModelType.syncTypesToModelTypes(storedModelTypes), resultTypes.get());
255 public void testRefreshShouldReadValuesFromDiskWithAllTypes() {
256 // Store preferences for the ModelType.ALL_TYPES_TYPE and account. We
257 // are using the helper class for this, so we don't have to deal with
258 // low-level details such as preference keys.
259 InvalidationPreferences invalidationPreferences = new InvalidationPreferences(mContext);
260 InvalidationPreferences.EditContext edit = invalidationPreferences.edit();
261 List<String> storedModelTypes = new ArrayList<String>();
262 storedModelTypes.add(ModelType.ALL_TYPES_TYPE);
263 invalidationPreferences.setSyncTypes(edit, storedModelTypes);
264 Account storedAccount = AccountManagerHelper.createAccountFromName("test@gmail.com");
265 invalidationPreferences.setAccount(edit, storedAccount);
266 invalidationPreferences.commit(edit);
268 // Ensure all calls to {@link InvalidationController#setRegisteredTypes} store values
269 // we can inspect in the test.
270 final AtomicReference<Account> resultAccount = new AtomicReference<Account>();
271 final AtomicBoolean resultAllTypes = new AtomicBoolean();
272 final AtomicReference<Set<ModelType>> resultTypes = new AtomicReference<Set<ModelType>>();
273 InvalidationController controller = new InvalidationController(mContext) {
275 public void setRegisteredTypes(
276 Account account, boolean allTypes, Set<ModelType> types) {
277 resultAccount.set(account);
278 resultAllTypes.set(allTypes);
279 resultTypes.set(types);
284 controller.refreshRegisteredTypes(new HashSet<ModelType>());
286 // Validate the values.
287 assertEquals(storedAccount, resultAccount.get());
288 assertEquals(true, resultAllTypes.get());
293 public void testSetRegisteredObjectIds() {
294 InvalidationController controller = new InvalidationController(mContext);
295 ObjectId bookmark = ModelType.BOOKMARK.toObjectId();
296 controller.setRegisteredObjectIds(new int[] {1, 2, bookmark.getSource()},
297 new String[] {"a", "b", new String(bookmark.getName())});
298 assertEquals(1, mContext.getNumStartedIntents());
300 // Validate destination.
301 Intent intent = mContext.getStartedIntent(0);
302 validateIntentComponent(intent);
303 assertEquals(InvalidationIntentProtocol.ACTION_REGISTER, intent.getAction());
305 // Validate registered object ids. The bookmark object should not be registered since it is
307 assertNull(intent.getStringArrayListExtra(
308 InvalidationIntentProtocol.EXTRA_REGISTERED_TYPES));
309 Set<ObjectId> objectIds = InvalidationIntentProtocol.getRegisteredObjectIds(intent);
310 assertEquals(2, objectIds.size());
311 assertTrue(objectIds.contains(ObjectId.newInstance(1, "a".getBytes())));
312 assertTrue(objectIds.contains(ObjectId.newInstance(2, "b".getBytes())));
316 * Asserts that {@code intent} is destined for the correct component.
318 private static void validateIntentComponent(Intent intent) {
319 assertNotNull(intent.getComponent());
320 assertEquals(InvalidationService.class.getName(),
321 intent.getComponent().getClassName());
325 * Mock context that saves all intents given to {@code startService}.
327 private static class IntentSavingContext extends AdvancedMockContext {
328 private final List<Intent> startedIntents = new ArrayList<Intent>();
330 IntentSavingContext(Context targetContext) {
331 super(targetContext);
335 public ComponentName startService(Intent intent) {
336 startedIntents.add(intent);
337 return new ComponentName(this, getClass());
340 int getNumStartedIntents() {
341 return startedIntents.size();
344 Intent getStartedIntent(int idx) {
345 return startedIntents.get(idx);
349 public PackageManager getPackageManager() {
350 return getBaseContext().getPackageManager();