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.sync.notifier;
7 import android.accounts.Account;
8 import android.content.ComponentName;
9 import android.content.Intent;
10 import android.os.Bundle;
11 import android.test.ServiceTestCase;
12 import android.test.suitebuilder.annotation.SmallTest;
14 import com.google.ipc.invalidation.external.client.InvalidationListener.RegistrationState;
15 import com.google.ipc.invalidation.external.client.contrib.AndroidListener;
16 import com.google.ipc.invalidation.external.client.types.ErrorInfo;
17 import com.google.ipc.invalidation.external.client.types.Invalidation;
18 import com.google.ipc.invalidation.external.client.types.ObjectId;
20 import org.chromium.base.CollectionUtil;
21 import org.chromium.base.test.util.AdvancedMockContext;
22 import org.chromium.base.test.util.Feature;
23 import org.chromium.sync.internal_api.pub.base.ModelType;
24 import org.chromium.sync.notifier.InvalidationPreferences.EditContext;
25 import org.chromium.sync.signin.AccountManagerHelper;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.EnumSet;
30 import java.util.HashSet;
31 import java.util.List;
35 * Tests for the {@link InvalidationService}.
37 * @author dsmyers@google.com (Daniel Myers)
39 public class InvalidationServiceTest extends ServiceTestCase<TestableInvalidationService> {
40 /** Id used when creating clients. */
41 private static final byte[] CLIENT_ID = new byte[]{0, 4, 7};
43 /** Intents provided to {@link #startService}. */
44 private List<Intent> mStartServiceIntents;
46 public InvalidationServiceTest() {
47 super(TestableInvalidationService.class);
51 public void setUp() throws Exception {
53 mStartServiceIntents = new ArrayList<Intent>();
54 setContext(new AdvancedMockContext(getContext()) {
56 public ComponentName startService(Intent intent) {
57 mStartServiceIntents.add(intent);
58 return new ComponentName(this, InvalidationServiceTest.class);
65 public void tearDown() throws Exception {
66 if (InvalidationService.getIsClientStartedForTest()) {
67 Intent stopIntent = createStopIntent();
68 getService().onHandleIntent(stopIntent);
70 assertFalse(InvalidationService.getIsClientStartedForTest());
76 public void testComputeRegistrationOps() {
78 * Test plan: compute the set of registration operations resulting from various combinations
79 * of existing and desired registrations. Verifying that they are correct.
81 Set<ObjectId> regAccumulator = new HashSet<ObjectId>();
82 Set<ObjectId> unregAccumulator = new HashSet<ObjectId>();
84 // Empty existing and desired registrations should yield empty operation sets.
85 InvalidationService.computeRegistrationOps(
86 ModelType.modelTypesToObjectIds(
87 CollectionUtil.newHashSet(ModelType.BOOKMARK, ModelType.SESSION)),
88 ModelType.modelTypesToObjectIds(
89 CollectionUtil.newHashSet(ModelType.BOOKMARK, ModelType.SESSION)),
90 regAccumulator, unregAccumulator);
91 assertEquals(0, regAccumulator.size());
92 assertEquals(0, unregAccumulator.size());
94 // Equal existing and desired registrations should yield empty operation sets.
95 InvalidationService.computeRegistrationOps(new HashSet<ObjectId>(),
96 new HashSet<ObjectId>(), regAccumulator, unregAccumulator);
97 assertEquals(0, regAccumulator.size());
98 assertEquals(0, unregAccumulator.size());
100 // Empty existing and non-empty desired registrations should yield desired registrations
101 // as the registration operations to do and no unregistrations.
102 Set<ObjectId> desiredTypes =
103 CollectionUtil.newHashSet(
104 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId());
105 InvalidationService.computeRegistrationOps(
106 new HashSet<ObjectId>(),
108 regAccumulator, unregAccumulator);
110 CollectionUtil.newHashSet(
111 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId()),
112 new HashSet<ObjectId>(regAccumulator));
113 assertEquals(0, unregAccumulator.size());
114 regAccumulator.clear();
116 // Unequal existing and desired registrations should yield both registrations and
117 // unregistrations. We should unregister TYPED_URL and register BOOKMARK, keeping SESSION.
118 InvalidationService.computeRegistrationOps(
119 CollectionUtil.newHashSet(
120 ModelType.SESSION.toObjectId(), ModelType.TYPED_URL.toObjectId()),
121 CollectionUtil.newHashSet(
122 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId()),
123 regAccumulator, unregAccumulator);
124 assertEquals(CollectionUtil.newHashSet(ModelType.BOOKMARK.toObjectId()), regAccumulator);
125 assertEquals(CollectionUtil.newHashSet(ModelType.TYPED_URL.toObjectId()),
127 regAccumulator.clear();
128 unregAccumulator.clear();
133 public void testReady() {
135 * Test plan: call ready. Verify that the service sets the client id correctly and reissues
136 * pending registrations.
139 // Persist some registrations.
140 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
141 EditContext editContext = invPrefs.edit();
142 invPrefs.setSyncTypes(editContext, CollectionUtil.newArrayList("BOOKMARK", "SESSION"));
143 ObjectId objectId = ObjectId.newInstance(1, "obj".getBytes());
144 invPrefs.setObjectIds(editContext, CollectionUtil.newArrayList(objectId));
145 assertTrue(invPrefs.commit(editContext));
148 getService().ready(CLIENT_ID);
149 assertTrue(Arrays.equals(CLIENT_ID, InvalidationService.getClientIdForTest()));
150 byte[] otherCid = "otherCid".getBytes();
151 getService().ready(otherCid);
152 assertTrue(Arrays.equals(otherCid, InvalidationService.getClientIdForTest()));
154 // Verify registrations issued.
155 assertEquals(CollectionUtil.newHashSet(
156 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId(), objectId),
157 new HashSet<ObjectId>(getService().mRegistrations.get(0)));
162 public void testReissueRegistrations() {
164 * Test plan: call the reissueRegistrations method of the listener with both empty and
165 * non-empty sets of desired registrations stored in preferences. Verify that no register
166 * intent is set in the first case and that the appropriate register intent is sent in
170 // No persisted registrations.
171 getService().reissueRegistrations(CLIENT_ID);
172 assertTrue(getService().mRegistrations.isEmpty());
174 // Persist some registrations.
175 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
176 EditContext editContext = invPrefs.edit();
177 invPrefs.setSyncTypes(editContext, CollectionUtil.newArrayList("BOOKMARK", "SESSION"));
178 ObjectId objectId = ObjectId.newInstance(1, "obj".getBytes());
179 invPrefs.setObjectIds(editContext, CollectionUtil.newArrayList(objectId));
180 assertTrue(invPrefs.commit(editContext));
182 // Reissue registrations and verify that the appropriate registrations are issued.
183 getService().reissueRegistrations(CLIENT_ID);
184 assertEquals(1, getService().mRegistrations.size());
185 assertEquals(CollectionUtil.newHashSet(
186 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId(), objectId),
187 new HashSet<ObjectId>(getService().mRegistrations.get(0)));
192 public void testInformRegistrationStatus() {
194 * Test plan: call inform registration status under a variety of circumstances and verify
195 * that the appropriate (un)register calls are issued.
197 * 1. Registration of desired object. No calls issued.
198 * 2. Unregistration of undesired object. No calls issued.
199 * 3. Registration of undesired object. Unregistration issued.
200 * 4. Unregistration of desired object. Registration issued.
202 // Initial test setup: persist a single registration into preferences.
203 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
204 EditContext editContext = invPrefs.edit();
205 invPrefs.setSyncTypes(editContext, CollectionUtil.newArrayList("SESSION"));
206 ObjectId desiredObjectId = ObjectId.newInstance(1, "obj1".getBytes());
207 ObjectId undesiredObjectId = ObjectId.newInstance(1, "obj2".getBytes());
208 invPrefs.setObjectIds(editContext, CollectionUtil.newArrayList(desiredObjectId));
209 assertTrue(invPrefs.commit(editContext));
211 // Cases 1 and 2: calls matching desired state cause no actions.
212 getService().informRegistrationStatus(CLIENT_ID, ModelType.SESSION.toObjectId(),
213 RegistrationState.REGISTERED);
214 getService().informRegistrationStatus(CLIENT_ID, desiredObjectId,
215 RegistrationState.REGISTERED);
216 getService().informRegistrationStatus(CLIENT_ID, ModelType.BOOKMARK.toObjectId(),
217 RegistrationState.UNREGISTERED);
218 getService().informRegistrationStatus(CLIENT_ID, undesiredObjectId,
219 RegistrationState.UNREGISTERED);
220 assertTrue(getService().mRegistrations.isEmpty());
221 assertTrue(getService().mUnregistrations.isEmpty());
223 // Case 3: registration of undesired object triggers an unregistration.
224 getService().informRegistrationStatus(CLIENT_ID, ModelType.BOOKMARK.toObjectId(),
225 RegistrationState.REGISTERED);
226 getService().informRegistrationStatus(CLIENT_ID, undesiredObjectId,
227 RegistrationState.REGISTERED);
228 assertEquals(2, getService().mUnregistrations.size());
229 assertEquals(0, getService().mRegistrations.size());
230 assertEquals(CollectionUtil.newArrayList(ModelType.BOOKMARK.toObjectId()),
231 getService().mUnregistrations.get(0));
232 assertEquals(CollectionUtil.newArrayList(undesiredObjectId),
233 getService().mUnregistrations.get(1));
235 // Case 4: unregistration of a desired object triggers a registration.
236 getService().informRegistrationStatus(CLIENT_ID, ModelType.SESSION.toObjectId(),
237 RegistrationState.UNREGISTERED);
238 getService().informRegistrationStatus(CLIENT_ID, desiredObjectId,
239 RegistrationState.UNREGISTERED);
240 assertEquals(2, getService().mUnregistrations.size());
241 assertEquals(2, getService().mRegistrations.size());
242 assertEquals(CollectionUtil.newArrayList(ModelType.SESSION.toObjectId()),
243 getService().mRegistrations.get(0));
244 assertEquals(CollectionUtil.newArrayList(desiredObjectId),
245 getService().mRegistrations.get(1));
250 public void testInformRegistrationFailure() {
252 * Test plan: call inform registration failure under a variety of circumstances and verify
253 * that the appropriate (un)register calls are issued.
255 * 1. Transient registration failure for an object that should be registered. Register
257 * 2. Permanent registration failure for an object that should be registered. No calls.
258 * 3. Transient registration failure for an object that should not be registered. Unregister
260 * 4. Permanent registration failure for an object should not be registered. No calls.
263 // Initial test setup: persist a single registration into preferences.
264 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
265 EditContext editContext = invPrefs.edit();
266 invPrefs.setSyncTypes(editContext, CollectionUtil.newArrayList("SESSION"));
267 ObjectId desiredObjectId = ObjectId.newInstance(1, "obj1".getBytes());
268 ObjectId undesiredObjectId = ObjectId.newInstance(1, "obj2".getBytes());
269 invPrefs.setObjectIds(editContext, CollectionUtil.newArrayList(desiredObjectId));
270 assertTrue(invPrefs.commit(editContext));
272 // Cases 2 and 4: permanent registration failures never cause calls to be made.
273 getService().informRegistrationFailure(CLIENT_ID, ModelType.SESSION.toObjectId(), false,
275 getService().informRegistrationFailure(CLIENT_ID, ModelType.BOOKMARK.toObjectId(), false,
277 getService().informRegistrationFailure(CLIENT_ID, desiredObjectId, false, "");
278 getService().informRegistrationFailure(CLIENT_ID, undesiredObjectId, false, "");
279 assertTrue(getService().mRegistrations.isEmpty());
280 assertTrue(getService().mUnregistrations.isEmpty());
282 // Case 1: transient failure of a desired registration results in re-registration.
283 getService().informRegistrationFailure(CLIENT_ID, ModelType.SESSION.toObjectId(), true, "");
284 getService().informRegistrationFailure(CLIENT_ID, desiredObjectId, true, "");
285 assertEquals(2, getService().mRegistrations.size());
286 assertTrue(getService().mUnregistrations.isEmpty());
287 assertEquals(CollectionUtil.newArrayList(ModelType.SESSION.toObjectId()),
288 getService().mRegistrations.get(0));
289 assertEquals(CollectionUtil.newArrayList(desiredObjectId),
290 getService().mRegistrations.get(1));
292 // Case 3: transient failure of an undesired registration results in unregistration.
293 getService().informRegistrationFailure(CLIENT_ID, ModelType.BOOKMARK.toObjectId(), true,
295 getService().informRegistrationFailure(CLIENT_ID, undesiredObjectId, true, "");
296 assertEquals(2, getService().mRegistrations.size());
297 assertEquals(2, getService().mUnregistrations.size());
298 assertEquals(CollectionUtil.newArrayList(ModelType.BOOKMARK.toObjectId()),
299 getService().mUnregistrations.get(0));
300 assertEquals(CollectionUtil.newArrayList(undesiredObjectId),
301 getService().mUnregistrations.get(1));
306 public void testInformError() {
308 * Test plan: call informError with both permanent and transient errors. Verify that
309 * the transient error causes no action to be taken and that the permanent error causes
310 * the client to be stopped.
313 // Client needs to be started for the permament error to trigger and stop.
314 getService().setShouldRunStates(true, true);
315 getService().onCreate();
316 getService().onHandleIntent(createStartIntent());
317 getService().mStartedServices.clear(); // Discard start intent.
320 getService().informError(ErrorInfo.newInstance(0, true, "transient", null));
321 assertTrue(getService().mStartedServices.isEmpty());
324 getService().informError(ErrorInfo.newInstance(0, false, "permanent", null));
325 assertEquals(1, getService().mStartedServices.size());
326 Intent sentIntent = getService().mStartedServices.get(0);
327 Intent stopIntent = AndroidListener.createStopIntent(getContext());
328 assertTrue(stopIntent.filterEquals(sentIntent));
329 assertEquals(stopIntent.getExtras().keySet(), sentIntent.getExtras().keySet());
334 public void testReadWriteState() {
336 * Test plan: read, write, and read the internal notification client persistent state.
337 * Verify appropriate return values.
339 assertNull(getService().readState());
340 byte[] writtenState = new byte[]{7, 4, 0};
341 getService().writeState(writtenState);
342 assertTrue(Arrays.equals(writtenState, getService().readState()));
347 public void testInvalidateWithPayload() {
348 doTestInvalidate(true);
353 public void testInvalidateWithoutPayload() {
354 doTestInvalidate(false);
357 private void doTestInvalidate(boolean hasPayload) {
359 * Test plan: call invalidate() with an invalidation that may or may not have a payload.
360 * Verify the produced bundle has the correct fields.
364 ObjectId objectId = ObjectId.newInstance(55, "BOOKMARK".getBytes());
365 final String payload = "testInvalidate-" + hasPayload;
366 Invalidation invalidation = hasPayload ?
367 Invalidation.newInstance(objectId, version, payload.getBytes()) :
368 Invalidation.newInstance(objectId, version);
369 byte[] ackHandle = ("testInvalidate-" + hasPayload).getBytes();
370 getService().invalidate(invalidation, ackHandle);
373 assertEquals(1, getService().mRequestedSyncs.size());
374 Bundle syncBundle = getService().mRequestedSyncs.get(0);
375 assertEquals(55, syncBundle.getInt("objectSource"));
376 assertEquals("BOOKMARK", syncBundle.getString("objectId"));
377 assertEquals(version, syncBundle.getLong("version"));
378 assertEquals(hasPayload ? payload : "", syncBundle.getString("payload"));
380 // Ensure acknowledged.
381 assertSingleAcknowledgement(ackHandle);
386 public void testInvalidateUnknownVersion() {
388 * Test plan: call invalidateUnknownVersion(). Verify the produced bundle has the correct
391 ObjectId objectId = ObjectId.newInstance(55, "BOOKMARK".getBytes());
392 byte[] ackHandle = "testInvalidateUV".getBytes();
393 getService().invalidateUnknownVersion(objectId, ackHandle);
396 assertEquals(1, getService().mRequestedSyncs.size());
397 Bundle syncBundle = getService().mRequestedSyncs.get(0);
398 assertEquals(55, syncBundle.getInt("objectSource"));
399 assertEquals("BOOKMARK", syncBundle.getString("objectId"));
400 assertEquals(0, syncBundle.getLong("version"));
401 assertEquals("", syncBundle.getString("payload"));
403 // Ensure acknowledged.
404 assertSingleAcknowledgement(ackHandle);
409 public void testInvalidateAll() {
411 * Test plan: call invalidateAll(). Verify the produced bundle has the correct fields.
413 byte[] ackHandle = "testInvalidateAll".getBytes();
414 getService().invalidateAll(ackHandle);
417 assertEquals(1, getService().mRequestedSyncs.size());
418 Bundle syncBundle = getService().mRequestedSyncs.get(0);
419 assertEquals(0, syncBundle.keySet().size());
421 // Ensure acknowledged.
422 assertSingleAcknowledgement(ackHandle);
425 /** Asserts that the service received a single acknowledgement with handle {@code ackHandle}. */
426 private void assertSingleAcknowledgement(byte[] ackHandle) {
427 assertEquals(1, getService().mAcknowledgements.size());
428 assertTrue(Arrays.equals(ackHandle, getService().mAcknowledgements.get(0)));
433 public void testShouldClientBeRunning() {
435 * Test plan: call shouldClientBeRunning with various combinations of
436 * in-foreground/sync-enabled. Verify appropriate return values.
438 getService().setShouldRunStates(false, false);
439 assertFalse(getService().shouldClientBeRunning());
441 getService().setShouldRunStates(false, true);
442 assertFalse(getService().shouldClientBeRunning());
444 getService().setShouldRunStates(true, false);
445 assertFalse(getService().shouldClientBeRunning());
447 // Should only be running if both in the foreground and sync is enabled.
448 getService().setShouldRunStates(true, true);
449 assertTrue(getService().shouldClientBeRunning());
454 public void testStartAndStopClient() {
456 * Test plan: with Chrome configured so that the client should run, send it an empty
457 * intent. Even though no owning account is known, the client should still start. Send
458 * it a stop intent and verify that it stops.
461 // Note: we are manipulating the service object directly, rather than through startService,
462 // because otherwise we would need to handle the asynchronous execution model of the
463 // underlying IntentService.
464 getService().setShouldRunStates(true, true);
465 getService().onCreate();
467 Intent startIntent = createStartIntent();
468 getService().onHandleIntent(startIntent);
469 assertTrue(InvalidationService.getIsClientStartedForTest());
471 Intent stopIntent = createStopIntent();
472 getService().onHandleIntent(stopIntent);
473 assertFalse(InvalidationService.getIsClientStartedForTest());
475 // The issued intents should have been an AndroidListener start intent followed by an
476 // AndroidListener stop intent.
477 assertEquals(2, mStartServiceIntents.size());
478 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
479 assertTrue(isAndroidListenerStopIntent(mStartServiceIntents.get(1)));
484 public void testClientStopsWhenShouldNotBeRunning() {
486 * Test plan: start the client. Then, change the configuration so that Chrome should not
487 * be running. Send an intent to the service and verify that it stops.
489 getService().setShouldRunStates(true, true);
490 getService().onCreate();
492 // Start the service.
493 Intent startIntent = createStartIntent();
494 getService().onHandleIntent(startIntent);
495 assertTrue(InvalidationService.getIsClientStartedForTest());
497 // Change configuration.
498 getService().setShouldRunStates(false, false);
500 // Send an Intent and verify that the service stops.
501 getService().onHandleIntent(startIntent);
502 assertFalse(InvalidationService.getIsClientStartedForTest());
504 // The issued intents should have been an AndroidListener start intent followed by an
505 // AndroidListener stop intent.
506 assertEquals(2, mStartServiceIntents.size());
507 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
508 assertTrue(isAndroidListenerStopIntent(mStartServiceIntents.get(1)));
513 public void testRegistrationIntent() {
515 * Test plan: send a registration-change intent. Verify that it starts the client and
516 * sets both the account and registrations in shared preferences.
518 getService().setShouldRunStates(true, true);
519 getService().onCreate();
521 // Send register Intent.
522 Set<ModelType> desiredRegistrations = CollectionUtil.newHashSet(
523 ModelType.BOOKMARK, ModelType.SESSION);
524 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
525 Intent registrationIntent = createRegisterIntent(account, false, desiredRegistrations);
526 getService().onHandleIntent(registrationIntent);
528 // Verify client started and state written.
529 assertTrue(InvalidationService.getIsClientStartedForTest());
530 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
531 assertEquals(account, invPrefs.getSavedSyncedAccount());
532 assertEquals(ModelType.modelTypesToSyncTypesForTest(desiredRegistrations),
533 invPrefs.getSavedSyncedTypes());
534 assertNull(invPrefs.getSavedObjectIds());
535 assertEquals(1, mStartServiceIntents.size());
536 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
538 // Send another registration-change intent, this type with all-types set to true, and
539 // verify that the on-disk state is updated and that no addition Intents are issued.
540 getService().onHandleIntent(createRegisterIntent(account, true, null));
541 assertEquals(account, invPrefs.getSavedSyncedAccount());
542 assertEquals(CollectionUtil.newHashSet(ModelType.ALL_TYPES_TYPE),
543 invPrefs.getSavedSyncedTypes());
544 assertEquals(1, mStartServiceIntents.size());
546 // Finally, send one more registration-change intent, this time with a different account,
547 // and verify that it both updates the account, stops thye existing client, and
548 // starts a new client.
549 Account account2 = AccountManagerHelper.createAccountFromName("test2@example.com");
550 getService().onHandleIntent(createRegisterIntent(account2, true, null));
551 assertEquals(account2, invPrefs.getSavedSyncedAccount());
552 assertEquals(3, mStartServiceIntents.size());
553 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
554 assertTrue(isAndroidListenerStopIntent(mStartServiceIntents.get(1)));
555 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(2)));
559 * Determines if the correct object ids have been written to preferences and registered with the
560 * invalidation client.
562 * @param expectedTypes The Sync types expected to be registered.
563 * @param expectedObjectIds The additional object ids expected to be registered.
564 * @param isReady Whether the client is ready to register/unregister.
566 private boolean expectedObjectIdsRegistered(Set<ModelType> expectedTypes,
567 Set<ObjectId> expectedObjectIds, boolean isReady) {
568 // Get synced types saved to preferences.
569 Set<String> expectedSyncTypes = ModelType.modelTypesToSyncTypesForTest(expectedTypes);
570 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
571 Set<String> actualSyncTypes = invPrefs.getSavedSyncedTypes();
572 if (actualSyncTypes == null) {
573 actualSyncTypes = new HashSet<String>();
576 // Get object ids saved to preferences.
577 Set<ObjectId> actualObjectIds = invPrefs.getSavedObjectIds();
578 if (actualObjectIds == null) {
579 actualObjectIds = new HashSet<ObjectId>();
582 // Get expected registered object ids.
583 Set<ObjectId> expectedRegisteredIds = new HashSet<ObjectId>();
585 expectedRegisteredIds.addAll(ModelType.modelTypesToObjectIds(expectedTypes));
586 expectedRegisteredIds.addAll(expectedObjectIds);
589 return actualSyncTypes.equals(expectedSyncTypes) &&
590 actualObjectIds.equals(expectedObjectIds) &&
591 getService().mCurrentRegistrations.equals(expectedRegisteredIds);
596 public void testRegistrationIntentWithTypesAndObjectIds() {
598 * Test plan: send a mix of registration-change intents: some for Sync types and some for
599 * object ids. Verify that registering for Sync types does not interfere with object id
600 * registration and vice-versa.
602 getService().setShouldRunStates(true, true);
603 getService().onCreate();
605 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
606 Set<ObjectId> objectIds = new HashSet<ObjectId>();
607 Set<ModelType> types = new HashSet<ModelType>();
609 // Register for some object ids.
610 objectIds.add(ObjectId.newInstance(1, "obj1".getBytes()));
611 objectIds.add(ObjectId.newInstance(2, "obj2".getBytes()));
612 Intent registrationIntent =
613 createRegisterIntent(account, new int[] {1, 2}, new String[] {"obj1", "obj2"});
614 getService().onHandleIntent(registrationIntent);
615 assertTrue(expectedObjectIdsRegistered(types, objectIds, false /* isReady */));
617 // Register for some types.
618 types.add(ModelType.BOOKMARK);
619 types.add(ModelType.SESSION);
620 registrationIntent = createRegisterIntent(account, false, types);
621 getService().onHandleIntent(registrationIntent);
622 assertTrue(expectedObjectIdsRegistered(types, objectIds, false /* isReady */));
624 // Set client to be ready and verify registrations.
625 getService().ready(CLIENT_ID);
626 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
628 // Change object id registration with types registered.
629 objectIds.add(ObjectId.newInstance(3, "obj3".getBytes()));
630 registrationIntent = createRegisterIntent(
631 account, new int[] {1, 2, 3}, new String[] {"obj1", "obj2", "obj3"});
632 getService().onHandleIntent(registrationIntent);
633 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
635 // Change type registration with object ids registered.
636 types.remove(ModelType.BOOKMARK);
637 registrationIntent = createRegisterIntent(account, false, types);
638 getService().onHandleIntent(registrationIntent);
639 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
641 // Unregister all types.
643 registrationIntent = createRegisterIntent(account, false, types);
644 getService().onHandleIntent(registrationIntent);
645 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
647 // Change object id registration with no types registered.
648 objectIds.remove(ObjectId.newInstance(2, "obj2".getBytes()));
649 registrationIntent = createRegisterIntent(
650 account, new int[] {1, 3}, new String[] {"obj1", "obj3"});
651 getService().onHandleIntent(registrationIntent);
652 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
654 // Unregister all object ids.
656 registrationIntent = createRegisterIntent(account, new int[0], new String[0]);
657 getService().onHandleIntent(registrationIntent);
658 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
660 // Change type registration with no object ids registered.
661 types.add(ModelType.BOOKMARK);
662 types.add(ModelType.PASSWORD);
663 registrationIntent = createRegisterIntent(account, false, types);
664 getService().onHandleIntent(registrationIntent);
665 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
670 public void testRegistrationIntentNoProxyTabsUsingReady() {
671 getService().setShouldRunStates(true, true);
672 getService().onCreate();
674 // Send register Intent.
675 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
676 Intent registrationIntent = createRegisterIntent(account, true, null);
677 getService().onHandleIntent(registrationIntent);
679 // Verify client started and state written.
680 assertTrue(InvalidationService.getIsClientStartedForTest());
681 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
682 assertEquals(account, invPrefs.getSavedSyncedAccount());
683 assertEquals(CollectionUtil.newHashSet(ModelType.ALL_TYPES_TYPE),
684 invPrefs.getSavedSyncedTypes());
685 assertEquals(1, mStartServiceIntents.size());
686 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
688 // Set client to be ready. This triggers registrations.
689 getService().ready(CLIENT_ID);
690 assertTrue(Arrays.equals(CLIENT_ID, InvalidationService.getClientIdForTest()));
692 // Ensure registrations are correct.
693 Set<ObjectId> expectedTypes =
694 ModelType.modelTypesToObjectIds(EnumSet.allOf(ModelType.class));
695 assertEquals(expectedTypes, new HashSet<ObjectId>(getService().mRegistrations.get(0)));
700 public void testRegistrationIntentNoProxyTabsAlreadyWithClientId() {
701 getService().setShouldRunStates(true, true);
702 getService().onCreate();
704 // Send register Intent with no desired types.
705 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
706 Intent registrationIntent = createRegisterIntent(account, false, new HashSet<ModelType>());
707 getService().onHandleIntent(registrationIntent);
709 // Verify client started and state written.
710 assertTrue(InvalidationService.getIsClientStartedForTest());
711 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
712 assertEquals(account, invPrefs.getSavedSyncedAccount());
713 assertEquals(new HashSet<String>(), invPrefs.getSavedSyncedTypes());
714 assertEquals(1, mStartServiceIntents.size());
715 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
717 // Make sure client is ready.
718 getService().ready(CLIENT_ID);
719 assertTrue(Arrays.equals(CLIENT_ID, InvalidationService.getClientIdForTest()));
721 // Choose to register for all types in an already ready client.
722 registrationIntent = createRegisterIntent(account, true, null);
723 getService().onHandleIntent(registrationIntent);
725 // Ensure registrations are correct.
726 assertEquals(1, getService().mRegistrations.size());
727 Set<ObjectId> expectedTypes =
728 ModelType.modelTypesToObjectIds(EnumSet.allOf(ModelType.class));
729 assertEquals(expectedTypes, new HashSet<ObjectId>(getService().mRegistrations.get(0)));
734 public void testRegistrationIntentWhenClientShouldNotBeRunning() {
736 * Test plan: send a registration change event when the client should not be running.
737 * Verify that the service updates the on-disk state but does not start the client.
739 getService().onCreate();
741 // Send register Intent.
742 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
743 Set<ModelType> desiredRegistrations = CollectionUtil.newHashSet(
744 ModelType.BOOKMARK, ModelType.SESSION);
745 Intent registrationIntent = createRegisterIntent(account, false, desiredRegistrations);
746 getService().onHandleIntent(registrationIntent);
748 // Verify state written but client not started.
749 assertFalse(InvalidationService.getIsClientStartedForTest());
750 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
751 assertEquals(account, invPrefs.getSavedSyncedAccount());
752 assertEquals(ModelType.modelTypesToSyncTypesForTest(desiredRegistrations),
753 invPrefs.getSavedSyncedTypes());
754 assertEquals(0, mStartServiceIntents.size());
759 public void testDeferredRegistrationsIssued() {
761 * Test plan: send a registration-change intent. Verify that the client issues a start
762 * intent but makes no registration calls. Issue a reissueRegistrations call and verify
763 * that the client does issue the appropriate registrations.
765 getService().setShouldRunStates(true, true);
766 getService().onCreate();
768 // Send register Intent. Verify client started but no registrations issued.
769 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
770 Set<ModelType> desiredRegistrations = CollectionUtil.newHashSet(
771 ModelType.BOOKMARK, ModelType.SESSION);
772 Set<ObjectId> desiredObjectIds = ModelType.modelTypesToObjectIds(desiredRegistrations);
774 Intent registrationIntent = createRegisterIntent(account, false, desiredRegistrations);
775 getService().onHandleIntent(registrationIntent);
776 assertTrue(InvalidationService.getIsClientStartedForTest());
777 assertEquals(1, mStartServiceIntents.size());
778 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
779 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
780 assertEquals(ModelType.modelTypesToSyncTypesForTest(desiredRegistrations),
781 invPrefs.getSavedSyncedTypes());
782 assertEquals(desiredObjectIds, getService().readRegistrationsFromPrefs());
784 // Issue reissueRegistrations; verify registration intent issues.
785 getService().reissueRegistrations(CLIENT_ID);
786 assertEquals(2, mStartServiceIntents.size());
787 Intent expectedRegisterIntent = AndroidListener.createRegisterIntent(
791 Intent actualRegisterIntent = mStartServiceIntents.get(1);
792 assertTrue(expectedRegisterIntent.filterEquals(actualRegisterIntent));
793 assertEquals(expectedRegisterIntent.getExtras().keySet(),
794 actualRegisterIntent.getExtras().keySet());
797 new HashSet<ObjectId>(getService().mRegistrations.get(0)));
802 public void testRegistrationRetries() {
804 * Test plan: validate that the alarm receiver used by the AndroidListener underlying
805 * InvalidationService is correctly configured in the manifest and retries registrations
806 * with exponential backoff. May need to be implemented as a downstream Chrome for Android
809 // TODO(dsmyers): implement.
810 // Bug: https://code.google.com/p/chromium/issues/detail?id=172398
813 /** Creates an intent to start the InvalidationService. */
814 private Intent createStartIntent() {
815 Intent intent = new Intent();
819 /** Creates an intent to stop the InvalidationService. */
820 private Intent createStopIntent() {
821 Intent intent = new Intent();
822 intent.putExtra(InvalidationIntentProtocol.EXTRA_STOP, true);
826 /** Creates an intent to register some types with the InvalidationService. */
827 private Intent createRegisterIntent(Account account, boolean allTypes, Set<ModelType> types) {
828 Intent intent = InvalidationIntentProtocol.createRegisterIntent(account, allTypes, types);
832 /** Creates an intent to register some types with the InvalidationService. */
833 private Intent createRegisterIntent(
834 Account account, int[] objectSources, String[] objectNames) {
835 Intent intent = InvalidationIntentProtocol.createRegisterIntent(
836 account, objectSources, objectNames);
840 /** Returns whether {@code intent} is an {@link AndroidListener} start intent. */
841 private boolean isAndroidListenerStartIntent(Intent intent) {
842 Intent startIntent = AndroidListener.createStartIntent(getContext(),
843 InvalidationService.CLIENT_TYPE, "unused".getBytes());
844 return intent.getExtras().keySet().equals(startIntent.getExtras().keySet());
847 /** Returns whether {@code intent} is an {@link AndroidListener} stop intent. */
848 private boolean isAndroidListenerStopIntent(Intent intent) {
849 Intent stopIntent = AndroidListener.createStopIntent(getContext());
850 return intent.getExtras().keySet().equals(stopIntent.getExtras().keySet());