1 // Copyright (c) 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.InvalidationIntentProtocol;
25 import org.chromium.sync.notifier.InvalidationPreferences.EditContext;
26 import org.chromium.sync.signin.AccountManagerHelper;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.EnumSet;
31 import java.util.HashSet;
32 import java.util.List;
36 * Tests for the {@link InvalidationService}.
38 * @author dsmyers@google.com (Daniel Myers)
40 public class InvalidationServiceTest extends ServiceTestCase<TestableInvalidationService> {
41 /** Id used when creating clients. */
42 private static final byte[] CLIENT_ID = new byte[]{0, 4, 7};
44 /** Intents provided to {@link #startService}. */
45 private List<Intent> mStartServiceIntents;
47 public InvalidationServiceTest() {
48 super(TestableInvalidationService.class);
52 public void setUp() throws Exception {
54 mStartServiceIntents = new ArrayList<Intent>();
55 setContext(new AdvancedMockContext(getContext()) {
57 public ComponentName startService(Intent intent) {
58 mStartServiceIntents.add(intent);
59 return new ComponentName(this, InvalidationServiceTest.class);
66 public void tearDown() throws Exception {
67 if (InvalidationService.getIsClientStartedForTest()) {
68 Intent stopIntent = new Intent().putExtra(InvalidationIntentProtocol.EXTRA_STOP, true);
69 getService().onHandleIntent(stopIntent);
71 assertFalse(InvalidationService.getIsClientStartedForTest());
77 public void testComputeRegistrationOps() {
79 * Test plan: compute the set of registration operations resulting from various combinations
80 * of existing and desired registrations. Verifying that they are correct.
82 Set<ObjectId> regAccumulator = new HashSet<ObjectId>();
83 Set<ObjectId> unregAccumulator = new HashSet<ObjectId>();
85 // Empty existing and desired registrations should yield empty operation sets.
86 InvalidationService.computeRegistrationOps(
87 ModelType.modelTypesToObjectIds(
88 CollectionUtil.newHashSet(ModelType.BOOKMARK, ModelType.SESSION)),
89 ModelType.modelTypesToObjectIds(
90 CollectionUtil.newHashSet(ModelType.BOOKMARK, ModelType.SESSION)),
91 regAccumulator, unregAccumulator);
92 assertEquals(0, regAccumulator.size());
93 assertEquals(0, unregAccumulator.size());
95 // Equal existing and desired registrations should yield empty operation sets.
96 InvalidationService.computeRegistrationOps(new HashSet<ObjectId>(),
97 new HashSet<ObjectId>(), regAccumulator, unregAccumulator);
98 assertEquals(0, regAccumulator.size());
99 assertEquals(0, unregAccumulator.size());
101 // Empty existing and non-empty desired registrations should yield desired registrations
102 // as the registration operations to do and no unregistrations.
103 Set<ObjectId> desiredTypes =
104 CollectionUtil.newHashSet(
105 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId());
106 InvalidationService.computeRegistrationOps(
107 new HashSet<ObjectId>(),
109 regAccumulator, unregAccumulator);
111 CollectionUtil.newHashSet(
112 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId()),
113 new HashSet<ObjectId>(regAccumulator));
114 assertEquals(0, unregAccumulator.size());
115 regAccumulator.clear();
117 // Unequal existing and desired registrations should yield both registrations and
118 // unregistrations. We should unregister TYPED_URL and register BOOKMARK, keeping SESSION.
119 InvalidationService.computeRegistrationOps(
120 CollectionUtil.newHashSet(
121 ModelType.SESSION.toObjectId(), ModelType.TYPED_URL.toObjectId()),
122 CollectionUtil.newHashSet(
123 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId()),
124 regAccumulator, unregAccumulator);
125 assertEquals(CollectionUtil.newHashSet(ModelType.BOOKMARK.toObjectId()), regAccumulator);
126 assertEquals(CollectionUtil.newHashSet(ModelType.TYPED_URL.toObjectId()),
128 regAccumulator.clear();
129 unregAccumulator.clear();
134 public void testReady() {
136 * Test plan: call ready. Verify that the service sets the client id correctly and reissues
137 * pending registrations.
140 // Persist some registrations.
141 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
142 EditContext editContext = invPrefs.edit();
143 invPrefs.setSyncTypes(editContext, CollectionUtil.newArrayList("BOOKMARK", "SESSION"));
144 ObjectId objectId = ObjectId.newInstance(1, "obj".getBytes());
145 invPrefs.setObjectIds(editContext, CollectionUtil.newArrayList(objectId));
146 assertTrue(invPrefs.commit(editContext));
149 getService().ready(CLIENT_ID);
150 assertTrue(Arrays.equals(CLIENT_ID, InvalidationService.getClientIdForTest()));
151 byte[] otherCid = "otherCid".getBytes();
152 getService().ready(otherCid);
153 assertTrue(Arrays.equals(otherCid, InvalidationService.getClientIdForTest()));
155 // Verify registrations issued.
156 assertEquals(CollectionUtil.newHashSet(
157 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId(), objectId),
158 new HashSet<ObjectId>(getService().mRegistrations.get(0)));
163 public void testReissueRegistrations() {
165 * Test plan: call the reissueRegistrations method of the listener with both empty and
166 * non-empty sets of desired registrations stored in preferences. Verify that no register
167 * intent is set in the first case and that the appropriate register intent is sent in
171 // No persisted registrations.
172 getService().reissueRegistrations(CLIENT_ID);
173 assertTrue(getService().mRegistrations.isEmpty());
175 // Persist some registrations.
176 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
177 EditContext editContext = invPrefs.edit();
178 invPrefs.setSyncTypes(editContext, CollectionUtil.newArrayList("BOOKMARK", "SESSION"));
179 ObjectId objectId = ObjectId.newInstance(1, "obj".getBytes());
180 invPrefs.setObjectIds(editContext, CollectionUtil.newArrayList(objectId));
181 assertTrue(invPrefs.commit(editContext));
183 // Reissue registrations and verify that the appropriate registrations are issued.
184 getService().reissueRegistrations(CLIENT_ID);
185 assertEquals(1, getService().mRegistrations.size());
186 assertEquals(CollectionUtil.newHashSet(
187 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId(), objectId),
188 new HashSet<ObjectId>(getService().mRegistrations.get(0)));
193 public void testInformRegistrationStatus() {
195 * Test plan: call inform registration status under a variety of circumstances and verify
196 * that the appropriate (un)register calls are issued.
198 * 1. Registration of desired object. No calls issued.
199 * 2. Unregistration of undesired object. No calls issued.
200 * 3. Registration of undesired object. Unregistration issued.
201 * 4. Unregistration of desired object. Registration issued.
203 // Initial test setup: persist a single registration into preferences.
204 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
205 EditContext editContext = invPrefs.edit();
206 invPrefs.setSyncTypes(editContext, CollectionUtil.newArrayList("SESSION"));
207 ObjectId desiredObjectId = ObjectId.newInstance(1, "obj1".getBytes());
208 ObjectId undesiredObjectId = ObjectId.newInstance(1, "obj2".getBytes());
209 invPrefs.setObjectIds(editContext, CollectionUtil.newArrayList(desiredObjectId));
210 assertTrue(invPrefs.commit(editContext));
212 // Cases 1 and 2: calls matching desired state cause no actions.
213 getService().informRegistrationStatus(CLIENT_ID, ModelType.SESSION.toObjectId(),
214 RegistrationState.REGISTERED);
215 getService().informRegistrationStatus(CLIENT_ID, desiredObjectId,
216 RegistrationState.REGISTERED);
217 getService().informRegistrationStatus(CLIENT_ID, ModelType.BOOKMARK.toObjectId(),
218 RegistrationState.UNREGISTERED);
219 getService().informRegistrationStatus(CLIENT_ID, undesiredObjectId,
220 RegistrationState.UNREGISTERED);
221 assertTrue(getService().mRegistrations.isEmpty());
222 assertTrue(getService().mUnregistrations.isEmpty());
224 // Case 3: registration of undesired object triggers an unregistration.
225 getService().informRegistrationStatus(CLIENT_ID, ModelType.BOOKMARK.toObjectId(),
226 RegistrationState.REGISTERED);
227 getService().informRegistrationStatus(CLIENT_ID, undesiredObjectId,
228 RegistrationState.REGISTERED);
229 assertEquals(2, getService().mUnregistrations.size());
230 assertEquals(0, getService().mRegistrations.size());
231 assertEquals(CollectionUtil.newArrayList(ModelType.BOOKMARK.toObjectId()),
232 getService().mUnregistrations.get(0));
233 assertEquals(CollectionUtil.newArrayList(undesiredObjectId),
234 getService().mUnregistrations.get(1));
236 // Case 4: unregistration of a desired object triggers a registration.
237 getService().informRegistrationStatus(CLIENT_ID, ModelType.SESSION.toObjectId(),
238 RegistrationState.UNREGISTERED);
239 getService().informRegistrationStatus(CLIENT_ID, desiredObjectId,
240 RegistrationState.UNREGISTERED);
241 assertEquals(2, getService().mUnregistrations.size());
242 assertEquals(2, getService().mRegistrations.size());
243 assertEquals(CollectionUtil.newArrayList(ModelType.SESSION.toObjectId()),
244 getService().mRegistrations.get(0));
245 assertEquals(CollectionUtil.newArrayList(desiredObjectId),
246 getService().mRegistrations.get(1));
251 public void testInformRegistrationFailure() {
253 * Test plan: call inform registration failure under a variety of circumstances and verify
254 * that the appropriate (un)register calls are issued.
256 * 1. Transient registration failure for an object that should be registered. Register
258 * 2. Permanent registration failure for an object that should be registered. No calls.
259 * 3. Transient registration failure for an object that should not be registered. Unregister
261 * 4. Permanent registration failure for an object should not be registered. No calls.
264 // Initial test setup: persist a single registration into preferences.
265 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
266 EditContext editContext = invPrefs.edit();
267 invPrefs.setSyncTypes(editContext, CollectionUtil.newArrayList("SESSION"));
268 ObjectId desiredObjectId = ObjectId.newInstance(1, "obj1".getBytes());
269 ObjectId undesiredObjectId = ObjectId.newInstance(1, "obj2".getBytes());
270 invPrefs.setObjectIds(editContext, CollectionUtil.newArrayList(desiredObjectId));
271 assertTrue(invPrefs.commit(editContext));
273 // Cases 2 and 4: permanent registration failures never cause calls to be made.
274 getService().informRegistrationFailure(CLIENT_ID, ModelType.SESSION.toObjectId(), false,
276 getService().informRegistrationFailure(CLIENT_ID, ModelType.BOOKMARK.toObjectId(), false,
278 getService().informRegistrationFailure(CLIENT_ID, desiredObjectId, false, "");
279 getService().informRegistrationFailure(CLIENT_ID, undesiredObjectId, false, "");
280 assertTrue(getService().mRegistrations.isEmpty());
281 assertTrue(getService().mUnregistrations.isEmpty());
283 // Case 1: transient failure of a desired registration results in re-registration.
284 getService().informRegistrationFailure(CLIENT_ID, ModelType.SESSION.toObjectId(), true, "");
285 getService().informRegistrationFailure(CLIENT_ID, desiredObjectId, true, "");
286 assertEquals(2, getService().mRegistrations.size());
287 assertTrue(getService().mUnregistrations.isEmpty());
288 assertEquals(CollectionUtil.newArrayList(ModelType.SESSION.toObjectId()),
289 getService().mRegistrations.get(0));
290 assertEquals(CollectionUtil.newArrayList(desiredObjectId),
291 getService().mRegistrations.get(1));
293 // Case 3: transient failure of an undesired registration results in unregistration.
294 getService().informRegistrationFailure(CLIENT_ID, ModelType.BOOKMARK.toObjectId(), true,
296 getService().informRegistrationFailure(CLIENT_ID, undesiredObjectId, true, "");
297 assertEquals(2, getService().mRegistrations.size());
298 assertEquals(2, getService().mUnregistrations.size());
299 assertEquals(CollectionUtil.newArrayList(ModelType.BOOKMARK.toObjectId()),
300 getService().mUnregistrations.get(0));
301 assertEquals(CollectionUtil.newArrayList(undesiredObjectId),
302 getService().mUnregistrations.get(1));
307 public void testInformError() {
309 * Test plan: call informError with both permanent and transient errors. Verify that
310 * the transient error causes no action to be taken and that the permanent error causes
311 * the client to be stopped.
314 // Client needs to be started for the permament error to trigger and stop.
315 getService().setShouldRunStates(true, true);
316 getService().onCreate();
317 getService().onHandleIntent(new Intent());
318 getService().mStartedServices.clear(); // Discard start intent.
321 getService().informError(ErrorInfo.newInstance(0, true, "transient", null));
322 assertTrue(getService().mStartedServices.isEmpty());
325 getService().informError(ErrorInfo.newInstance(0, false, "permanent", null));
326 assertEquals(1, getService().mStartedServices.size());
327 Intent sentIntent = getService().mStartedServices.get(0);
328 Intent stopIntent = AndroidListener.createStopIntent(getContext());
329 assertTrue(stopIntent.filterEquals(sentIntent));
330 assertEquals(stopIntent.getExtras().keySet(), sentIntent.getExtras().keySet());
335 public void testReadWriteState() {
337 * Test plan: read, write, and read the internal notification client persistent state.
338 * Verify appropriate return values.
340 assertNull(getService().readState());
341 byte[] writtenState = new byte[]{7,4,0};
342 getService().writeState(writtenState);
343 assertTrue(Arrays.equals(writtenState, getService().readState()));
348 public void testInvalidateWithPayload() {
349 doTestInvalidate(true);
354 public void testInvalidateWithoutPayload() {
355 doTestInvalidate(false);
358 private void doTestInvalidate(boolean hasPayload) {
360 * Test plan: call invalidate() with an invalidation that may or may not have a payload.
361 * Verify the produced bundle has the correct fields.
365 ObjectId objectId = ObjectId.newInstance(55, "BOOKMARK".getBytes());
366 final String payload = "testInvalidate-" + hasPayload;
367 Invalidation invalidation = hasPayload ?
368 Invalidation.newInstance(objectId, version, payload.getBytes()) :
369 Invalidation.newInstance(objectId, version);
370 byte[] ackHandle = ("testInvalidate-" + hasPayload).getBytes();
371 getService().invalidate(invalidation, ackHandle);
374 assertEquals(1, getService().mRequestedSyncs.size());
375 Bundle syncBundle = getService().mRequestedSyncs.get(0);
376 assertEquals(55, syncBundle.getInt("objectSource"));
377 assertEquals("BOOKMARK", syncBundle.getString("objectId"));
378 assertEquals(version, syncBundle.getLong("version"));
379 assertEquals(hasPayload ? payload : "", syncBundle.getString("payload"));
381 // Ensure acknowledged.
382 assertSingleAcknowledgement(ackHandle);
387 public void testInvalidateUnknownVersion() {
389 * Test plan: call invalidateUnknownVersion(). Verify the produced bundle has the correct
392 ObjectId objectId = ObjectId.newInstance(55, "BOOKMARK".getBytes());
393 byte[] ackHandle = "testInvalidateUV".getBytes();
394 getService().invalidateUnknownVersion(objectId, ackHandle);
397 assertEquals(1, getService().mRequestedSyncs.size());
398 Bundle syncBundle = getService().mRequestedSyncs.get(0);
399 assertEquals(55, syncBundle.getInt("objectSource"));
400 assertEquals("BOOKMARK", syncBundle.getString("objectId"));
401 assertEquals(0, syncBundle.getLong("version"));
402 assertEquals("", syncBundle.getString("payload"));
404 // Ensure acknowledged.
405 assertSingleAcknowledgement(ackHandle);
410 public void testInvalidateAll() {
412 * Test plan: call invalidateAll(). Verify the produced bundle has the correct fields.
414 byte[] ackHandle = "testInvalidateAll".getBytes();
415 getService().invalidateAll(ackHandle);
418 assertEquals(1, getService().mRequestedSyncs.size());
419 Bundle syncBundle = getService().mRequestedSyncs.get(0);
420 assertEquals(0, syncBundle.keySet().size());
422 // Ensure acknowledged.
423 assertSingleAcknowledgement(ackHandle);
426 /** Asserts that the service received a single acknowledgement with handle {@code ackHandle}. */
427 private void assertSingleAcknowledgement(byte[] ackHandle) {
428 assertEquals(1, getService().mAcknowledgements.size());
429 assertTrue(Arrays.equals(ackHandle, getService().mAcknowledgements.get(0)));
434 public void testShouldClientBeRunning() {
436 * Test plan: call shouldClientBeRunning with various combinations of
437 * in-foreground/sync-enabled. Verify appropriate return values.
439 getService().setShouldRunStates(false, false);
440 assertFalse(getService().shouldClientBeRunning());
442 getService().setShouldRunStates(false, true);
443 assertFalse(getService().shouldClientBeRunning());
445 getService().setShouldRunStates(true, false);
446 assertFalse(getService().shouldClientBeRunning());
448 // Should only be running if both in the foreground and sync is enabled.
449 getService().setShouldRunStates(true, true);
450 assertTrue(getService().shouldClientBeRunning());
455 public void testStartAndStopClient() {
457 * Test plan: with Chrome configured so that the client should run, send it an empty
458 * intent. Even though no owning account is known, the client should still start. Send
459 * it a stop intent and verify that it stops.
462 // Note: we are manipulating the service object directly, rather than through startService,
463 // because otherwise we would need to handle the asynchronous execution model of the
464 // underlying IntentService.
465 getService().setShouldRunStates(true, true);
466 getService().onCreate();
468 Intent startIntent = new Intent();
469 getService().onHandleIntent(startIntent);
470 assertTrue(InvalidationService.getIsClientStartedForTest());
472 Intent stopIntent = new Intent().putExtra(InvalidationIntentProtocol.EXTRA_STOP, true);
473 getService().onHandleIntent(stopIntent);
474 assertFalse(InvalidationService.getIsClientStartedForTest());
476 // The issued intents should have been an AndroidListener start intent followed by an
477 // AndroidListener stop intent.
478 assertEquals(2, mStartServiceIntents.size());
479 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
480 assertTrue(isAndroidListenerStopIntent(mStartServiceIntents.get(1)));
485 public void testClientStopsWhenShouldNotBeRunning() {
487 * Test plan: start the client. Then, change the configuration so that Chrome should not
488 * be running. Send an intent to the service and verify that it stops.
490 getService().setShouldRunStates(true, true);
491 getService().onCreate();
493 // Start the service.
494 Intent startIntent = new Intent();
495 getService().onHandleIntent(startIntent);
496 assertTrue(InvalidationService.getIsClientStartedForTest());
498 // Change configuration.
499 getService().setShouldRunStates(false, false);
501 // Send an Intent and verify that the service stops.
502 getService().onHandleIntent(startIntent);
503 assertFalse(InvalidationService.getIsClientStartedForTest());
505 // The issued intents should have been an AndroidListener start intent followed by an
506 // AndroidListener stop intent.
507 assertEquals(2, mStartServiceIntents.size());
508 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
509 assertTrue(isAndroidListenerStopIntent(mStartServiceIntents.get(1)));
514 public void testRegistrationIntent() {
516 * Test plan: send a registration-change intent. Verify that it starts the client and
517 * sets both the account and registrations in shared preferences.
519 getService().setShouldRunStates(true, true);
520 getService().onCreate();
522 // Send register Intent.
523 Set<ModelType> desiredRegistrations = CollectionUtil.newHashSet(
524 ModelType.BOOKMARK, ModelType.SESSION);
525 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
526 Intent registrationIntent = InvalidationIntentProtocol.createRegisterIntent(account, false,
527 desiredRegistrations);
528 getService().onHandleIntent(registrationIntent);
530 // Verify client started and state written.
531 assertTrue(InvalidationService.getIsClientStartedForTest());
532 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
533 assertEquals(account, invPrefs.getSavedSyncedAccount());
534 assertEquals(ModelType.modelTypesToSyncTypes(desiredRegistrations),
535 invPrefs.getSavedSyncedTypes());
536 assertNull(invPrefs.getSavedObjectIds());
537 assertEquals(1, mStartServiceIntents.size());
538 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
540 // Send another registration-change intent, this type with all-types set to true, and
541 // verify that the on-disk state is updated and that no addition Intents are issued.
542 getService().onHandleIntent(
543 InvalidationIntentProtocol.createRegisterIntent(account, true, null));
544 assertEquals(account, invPrefs.getSavedSyncedAccount());
545 assertEquals(CollectionUtil.newHashSet(ModelType.ALL_TYPES_TYPE),
546 invPrefs.getSavedSyncedTypes());
547 assertEquals(1, mStartServiceIntents.size());
549 // Finally, send one more registration-change intent, this time with a different account,
550 // and verify that it both updates the account, stops thye existing client, and
551 // starts a new client.
552 Account account2 = AccountManagerHelper.createAccountFromName("test2@example.com");
553 getService().onHandleIntent(
554 InvalidationIntentProtocol.createRegisterIntent(account2, true, null));
555 assertEquals(account2, invPrefs.getSavedSyncedAccount());
556 assertEquals(3, mStartServiceIntents.size());
557 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
558 assertTrue(isAndroidListenerStopIntent(mStartServiceIntents.get(1)));
559 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(2)));
563 * Determines if the correct object ids have been written to preferences and registered with the
564 * invalidation client.
566 * @param expectedTypes The Sync types expected to be registered.
567 * @param expectedObjectIds The additional object ids expected to be registered.
568 * @param isReady Whether the client is ready to register/unregister.
570 private boolean expectedObjectIdsRegistered(Set<ModelType> expectedTypes,
571 Set<ObjectId> expectedObjectIds, boolean isReady) {
572 // Get synced types saved to preferences.
573 Set<String> expectedSyncTypes = ModelType.modelTypesToSyncTypes(expectedTypes);
574 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
575 Set<String> actualSyncTypes = invPrefs.getSavedSyncedTypes();
576 if (actualSyncTypes == null) {
577 actualSyncTypes = new HashSet<String>();
580 // Get object ids saved to preferences.
581 Set<ObjectId> actualObjectIds = invPrefs.getSavedObjectIds();
582 if (actualObjectIds == null) {
583 actualObjectIds = new HashSet<ObjectId>();
586 // Get expected registered object ids.
587 Set<ObjectId> expectedRegisteredIds = new HashSet<ObjectId>();
589 expectedRegisteredIds.addAll(ModelType.modelTypesToObjectIds(expectedTypes));
590 expectedRegisteredIds.addAll(expectedObjectIds);
593 return actualSyncTypes.equals(expectedSyncTypes) &&
594 actualObjectIds.equals(expectedObjectIds) &&
595 getService().mCurrentRegistrations.equals(expectedRegisteredIds);
600 public void testRegistrationIntentWithTypesAndObjectIds() {
602 * Test plan: send a mix of registration-change intents: some for Sync types and some for
603 * object ids. Verify that registering for Sync types does not interfere with object id
604 * registration and vice-versa.
606 getService().setShouldRunStates(true, true);
607 getService().onCreate();
609 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
610 Set<ObjectId> objectIds = new HashSet<ObjectId>();
611 Set<ModelType> types = new HashSet<ModelType>();
613 // Register for some object ids.
614 objectIds.add(ObjectId.newInstance(1, "obj1".getBytes()));
615 objectIds.add(ObjectId.newInstance(2, "obj2".getBytes()));
616 Intent registrationIntent =
617 InvalidationIntentProtocol.createRegisterIntent(account, new int[] {1, 2},
618 new String[] {"obj1", "obj2"});
619 getService().onHandleIntent(registrationIntent);
620 assertTrue(expectedObjectIdsRegistered(types, objectIds, false /* isReady */));
622 // Register for some types.
623 types.add(ModelType.BOOKMARK);
624 types.add(ModelType.SESSION);
625 registrationIntent = InvalidationIntentProtocol.createRegisterIntent(account, false, types);
626 getService().onHandleIntent(registrationIntent);
627 assertTrue(expectedObjectIdsRegistered(types, objectIds, false /* isReady */));
629 // Set client to be ready and verify registrations.
630 getService().ready(CLIENT_ID);
631 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
633 // Change object id registration with types registered.
634 objectIds.add(ObjectId.newInstance(3, "obj3".getBytes()));
636 InvalidationIntentProtocol.createRegisterIntent(account, new int[] {1, 2, 3},
637 new String[] {"obj1", "obj2", "obj3"});
638 getService().onHandleIntent(registrationIntent);
639 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
641 // Change type registration with object ids registered.
642 types.remove(ModelType.BOOKMARK);
643 registrationIntent = InvalidationIntentProtocol.createRegisterIntent(account, false, types);
644 getService().onHandleIntent(registrationIntent);
645 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
647 // Unregister all types.
649 registrationIntent = InvalidationIntentProtocol.createRegisterIntent(account, false, types);
650 getService().onHandleIntent(registrationIntent);
651 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
653 // Change object id registration with no types registered.
654 objectIds.remove(ObjectId.newInstance(2, "obj2".getBytes()));
656 InvalidationIntentProtocol.createRegisterIntent(account, new int[] {1, 3},
657 new String[] {"obj1", "obj3"});
658 getService().onHandleIntent(registrationIntent);
659 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
661 // Unregister all object ids.
663 registrationIntent = InvalidationIntentProtocol.createRegisterIntent(account, new int[0],
665 getService().onHandleIntent(registrationIntent);
666 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
668 // Change type registration with no object ids registered.
669 types.add(ModelType.BOOKMARK);
670 types.add(ModelType.PASSWORD);
671 registrationIntent = InvalidationIntentProtocol.createRegisterIntent(account, false, types);
672 getService().onHandleIntent(registrationIntent);
673 assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
678 public void testRegistrationIntentNoProxyTabsUsingReady() {
679 getService().setShouldRunStates(true, true);
680 getService().onCreate();
682 // Send register Intent.
683 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
684 Intent registrationIntent =
685 InvalidationIntentProtocol.createRegisterIntent(account, true, null);
686 getService().onHandleIntent(registrationIntent);
688 // Verify client started and state written.
689 assertTrue(InvalidationService.getIsClientStartedForTest());
690 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
691 assertEquals(account, invPrefs.getSavedSyncedAccount());
692 assertEquals(CollectionUtil.newHashSet(ModelType.ALL_TYPES_TYPE),
693 invPrefs.getSavedSyncedTypes());
694 assertEquals(1, mStartServiceIntents.size());
695 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
697 // Set client to be ready. This triggers registrations.
698 getService().ready(CLIENT_ID);
699 assertTrue(Arrays.equals(CLIENT_ID, InvalidationService.getClientIdForTest()));
701 // Ensure registrations are correct.
702 Set<ObjectId> expectedTypes =
703 ModelType.modelTypesToObjectIds(EnumSet.allOf(ModelType.class));
704 assertEquals(expectedTypes, new HashSet<ObjectId>(getService().mRegistrations.get(0)));
709 public void testRegistrationIntentNoProxyTabsAlreadyWithClientId() {
710 getService().setShouldRunStates(true, true);
711 getService().onCreate();
713 // Send register Intent with no desired types.
714 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
715 Intent registrationIntent = InvalidationIntentProtocol.createRegisterIntent(
716 account, false, new HashSet<ModelType>());
717 getService().onHandleIntent(registrationIntent);
719 // Verify client started and state written.
720 assertTrue(InvalidationService.getIsClientStartedForTest());
721 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
722 assertEquals(account, invPrefs.getSavedSyncedAccount());
723 assertEquals(new HashSet<String>(), invPrefs.getSavedSyncedTypes());
724 assertEquals(1, mStartServiceIntents.size());
725 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
727 // Make sure client is ready.
728 getService().ready(CLIENT_ID);
729 assertTrue(Arrays.equals(CLIENT_ID, InvalidationService.getClientIdForTest()));
731 // Choose to register for all types in an already ready client.
732 registrationIntent = InvalidationIntentProtocol.createRegisterIntent(account, true, null);
733 getService().onHandleIntent(registrationIntent);
735 // Ensure registrations are correct.
736 assertEquals(1, getService().mRegistrations.size());
737 Set<ObjectId> expectedTypes =
738 ModelType.modelTypesToObjectIds(EnumSet.allOf(ModelType.class));
739 assertEquals(expectedTypes, new HashSet<ObjectId>(getService().mRegistrations.get(0)));
744 public void testRegistrationIntentWhenClientShouldNotBeRunning() {
746 * Test plan: send a registration change event when the client should not be running.
747 * Verify that the service updates the on-disk state but does not start the client.
749 getService().onCreate();
751 // Send register Intent.
752 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
753 Set<ModelType> desiredRegistrations = CollectionUtil.newHashSet(
754 ModelType.BOOKMARK, ModelType.SESSION);
755 Intent registrationIntent = InvalidationIntentProtocol.createRegisterIntent(account, false,
756 desiredRegistrations);
757 getService().onHandleIntent(registrationIntent);
759 // Verify state written but client not started.
760 assertFalse(InvalidationService.getIsClientStartedForTest());
761 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
762 assertEquals(account, invPrefs.getSavedSyncedAccount());
763 assertEquals(ModelType.modelTypesToSyncTypes(desiredRegistrations),
764 invPrefs.getSavedSyncedTypes());
765 assertEquals(0, mStartServiceIntents.size());
770 public void testDeferredRegistrationsIssued() {
772 * Test plan: send a registration-change intent. Verify that the client issues a start
773 * intent but makes no registration calls. Issue a reissueRegistrations call and verify
774 * that the client does issue the appropriate registrations.
776 getService().setShouldRunStates(true, true);
777 getService().onCreate();
779 // Send register Intent. Verify client started but no registrations issued.
780 Account account = AccountManagerHelper.createAccountFromName("test@example.com");
781 Set<ModelType> desiredRegistrations = CollectionUtil.newHashSet(
782 ModelType.BOOKMARK, ModelType.SESSION);
783 Set<ObjectId> desiredObjectIds = ModelType.modelTypesToObjectIds(desiredRegistrations);
785 Intent registrationIntent = InvalidationIntentProtocol.createRegisterIntent(account, false,
786 desiredRegistrations);
787 getService().onHandleIntent(registrationIntent);
788 assertTrue(InvalidationService.getIsClientStartedForTest());
789 assertEquals(1, mStartServiceIntents.size());
790 assertTrue(isAndroidListenerStartIntent(mStartServiceIntents.get(0)));
791 InvalidationPreferences invPrefs = new InvalidationPreferences(getContext());
792 assertEquals(ModelType.modelTypesToSyncTypes(desiredRegistrations),
793 invPrefs.getSavedSyncedTypes());
794 assertEquals(desiredObjectIds, getService().readRegistrationsFromPrefs());
796 // Issue reissueRegistrations; verify registration intent issues.
797 getService().reissueRegistrations(CLIENT_ID);
798 assertEquals(2, mStartServiceIntents.size());
799 Intent expectedRegisterIntent = AndroidListener.createRegisterIntent(
803 Intent actualRegisterIntent = mStartServiceIntents.get(1);
804 assertTrue(expectedRegisterIntent.filterEquals(actualRegisterIntent));
805 assertEquals(expectedRegisterIntent.getExtras().keySet(),
806 actualRegisterIntent.getExtras().keySet());
809 new HashSet<ObjectId>(getService().mRegistrations.get(0)));
814 public void testRegistrationRetries() {
816 * Test plan: validate that the alarm receiver used by the AndroidListener underlying
817 * InvalidationService is correctly configured in the manifest and retries registrations
818 * with exponential backoff. May need to be implemented as a downstream Chrome for Android
821 // TODO(dsmyers): implement.
822 // Bug: https://code.google.com/p/chromium/issues/detail?id=172398
825 /** Returns whether {@code intent} is an {@link AndroidListener} start intent. */
826 private boolean isAndroidListenerStartIntent(Intent intent) {
827 Intent startIntent = AndroidListener.createStartIntent(getContext(),
828 InvalidationService.CLIENT_TYPE, "unused".getBytes());
829 return intent.getExtras().keySet().equals(startIntent.getExtras().keySet());
832 /** Returns whether {@code intent} is an {@link AndroidListener} stop intent. */
833 private boolean isAndroidListenerStopIntent(Intent intent) {
834 Intent stopIntent = AndroidListener.createStopIntent(getContext());
835 return intent.getExtras().keySet().equals(stopIntent.getExtras().keySet());