258c7b65c32bc9873a6e1e2cccb06d8aff5f55c5
[platform/framework/web/crosswalk.git] / src / sync / android / javatests / src / org / chromium / sync / notifier / InvalidationServiceTest.java
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.
4
5 package org.chromium.sync.notifier;
6
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;
13
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;
19
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;
26
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;
32 import java.util.Set;
33
34 /**
35  * Tests for the {@link InvalidationService}.
36  *
37  * @author dsmyers@google.com (Daniel Myers)
38  */
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};
42
43     /** Intents provided to {@link #startService}. */
44     private List<Intent> mStartServiceIntents;
45
46     public InvalidationServiceTest() {
47         super(TestableInvalidationService.class);
48     }
49
50     @Override
51     public void setUp() throws Exception {
52         super.setUp();
53         mStartServiceIntents = new ArrayList<Intent>();
54         setContext(new AdvancedMockContext(getContext()) {
55             @Override
56             public ComponentName startService(Intent intent) {
57                 mStartServiceIntents.add(intent);
58                 return new ComponentName(this, InvalidationServiceTest.class);
59             }
60         });
61         setupService();
62     }
63
64     @Override
65     public void tearDown() throws Exception {
66         if (InvalidationService.getIsClientStartedForTest()) {
67             Intent stopIntent = createStopIntent();
68             getService().onHandleIntent(stopIntent);
69         }
70         assertFalse(InvalidationService.getIsClientStartedForTest());
71         super.tearDown();
72     }
73
74     @SmallTest
75     @Feature({"Sync"})
76     public void testComputeRegistrationOps() {
77         /*
78          * Test plan: compute the set of registration operations resulting from various combinations
79          * of existing and desired registrations. Verifying that they are correct.
80          */
81         Set<ObjectId> regAccumulator = new HashSet<ObjectId>();
82         Set<ObjectId> unregAccumulator = new HashSet<ObjectId>();
83
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());
93
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());
99
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>(),
107                 desiredTypes,
108                 regAccumulator, unregAccumulator);
109         assertEquals(
110                 CollectionUtil.newHashSet(
111                         ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId()),
112                 new HashSet<ObjectId>(regAccumulator));
113         assertEquals(0, unregAccumulator.size());
114         regAccumulator.clear();
115
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()),
126                 unregAccumulator);
127         regAccumulator.clear();
128         unregAccumulator.clear();
129     }
130
131     @SmallTest
132     @Feature({"Sync"})
133     public void testReady() {
134        /**
135         * Test plan: call ready. Verify that the service sets the client id correctly and reissues
136         * pending registrations.
137         */
138
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));
146
147         // Issue ready.
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()));
153
154         // Verify registrations issued.
155         assertEquals(CollectionUtil.newHashSet(
156                 ModelType.BOOKMARK.toObjectId(), ModelType.SESSION.toObjectId(), objectId),
157                 new HashSet<ObjectId>(getService().mRegistrations.get(0)));
158     }
159
160     @SmallTest
161     @Feature({"Sync"})
162     public void testReissueRegistrations() {
163         /*
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
167          * the second.
168          */
169
170         // No persisted registrations.
171         getService().reissueRegistrations(CLIENT_ID);
172         assertTrue(getService().mRegistrations.isEmpty());
173
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));
181
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)));
188     }
189
190     @SmallTest
191     @Feature({"Sync"})
192     public void testInformRegistrationStatus() {
193         /*
194          * Test plan: call inform registration status under a variety of circumstances and verify
195          * that the appropriate (un)register calls are issued.
196          *
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.
201          */
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));
210
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());
222
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));
234
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));
246     }
247
248     @SmallTest
249     @Feature({"Sync"})
250     public void testInformRegistrationFailure() {
251         /*
252          * Test plan: call inform registration failure under a variety of circumstances and verify
253          * that the appropriate (un)register calls are issued.
254          *
255          * 1. Transient registration failure for an object that should be registered. Register
256          *    should be called.
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
259          *    should be called.
260          * 4. Permanent registration failure for an object should not be registered. No calls.
261          */
262
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));
271
272         // Cases 2 and 4: permanent registration failures never cause calls to be made.
273         getService().informRegistrationFailure(CLIENT_ID, ModelType.SESSION.toObjectId(), false,
274                 "");
275         getService().informRegistrationFailure(CLIENT_ID, ModelType.BOOKMARK.toObjectId(), false,
276                 "");
277         getService().informRegistrationFailure(CLIENT_ID, desiredObjectId, false, "");
278         getService().informRegistrationFailure(CLIENT_ID, undesiredObjectId, false, "");
279         assertTrue(getService().mRegistrations.isEmpty());
280         assertTrue(getService().mUnregistrations.isEmpty());
281
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));
291
292         // Case 3: transient failure of an undesired registration results in unregistration.
293         getService().informRegistrationFailure(CLIENT_ID, ModelType.BOOKMARK.toObjectId(), true,
294                 "");
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));
302     }
303
304     @SmallTest
305     @Feature({"Sync"})
306     public void testInformError() {
307         /*
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.
311          */
312
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.
318
319         // Transient error.
320         getService().informError(ErrorInfo.newInstance(0, true, "transient", null));
321         assertTrue(getService().mStartedServices.isEmpty());
322
323         // Permanent error.
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());
330     }
331
332     @SmallTest
333     @Feature({"Sync"})
334     public void testReadWriteState() {
335         /*
336          * Test plan: read, write, and read the internal notification client persistent state.
337          * Verify appropriate return values.
338          */
339         assertNull(getService().readState());
340         byte[] writtenState = new byte[]{7, 4, 0};
341         getService().writeState(writtenState);
342         assertTrue(Arrays.equals(writtenState, getService().readState()));
343     }
344
345     @SmallTest
346     @Feature({"Sync"})
347     public void testInvalidateWithPayload() {
348         doTestInvalidate(true);
349     }
350
351     @SmallTest
352     @Feature({"Sync"})
353     public void testInvalidateWithoutPayload() {
354         doTestInvalidate(false);
355     }
356
357     private void doTestInvalidate(boolean hasPayload) {
358         /*
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.
361          */
362         // Call invalidate.
363         int version = 4747;
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);
371
372         // Validate bundle.
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"));
379
380         // Ensure acknowledged.
381         assertSingleAcknowledgement(ackHandle);
382     }
383
384     @SmallTest
385     @Feature({"Sync"})
386     public void testInvalidateUnknownVersion() {
387         /*
388          * Test plan: call invalidateUnknownVersion(). Verify the produced bundle has the correct
389          * fields.
390          */
391         ObjectId objectId = ObjectId.newInstance(55, "BOOKMARK".getBytes());
392         byte[] ackHandle = "testInvalidateUV".getBytes();
393         getService().invalidateUnknownVersion(objectId, ackHandle);
394
395         // Validate bundle.
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"));
402
403         // Ensure acknowledged.
404         assertSingleAcknowledgement(ackHandle);
405     }
406
407     @SmallTest
408     @Feature({"Sync"})
409     public void testInvalidateAll() {
410         /*
411          * Test plan: call invalidateAll(). Verify the produced bundle has the correct fields.
412          */
413         byte[] ackHandle = "testInvalidateAll".getBytes();
414         getService().invalidateAll(ackHandle);
415
416         // Validate bundle.
417         assertEquals(1, getService().mRequestedSyncs.size());
418         Bundle syncBundle = getService().mRequestedSyncs.get(0);
419         assertEquals(0, syncBundle.keySet().size());
420
421         // Ensure acknowledged.
422         assertSingleAcknowledgement(ackHandle);
423     }
424
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)));
429     }
430
431     @SmallTest
432     @Feature({"Sync"})
433     public void testShouldClientBeRunning() {
434         /*
435          * Test plan: call shouldClientBeRunning with various combinations of
436          * in-foreground/sync-enabled. Verify appropriate return values.
437          */
438         getService().setShouldRunStates(false, false);
439         assertFalse(getService().shouldClientBeRunning());
440
441         getService().setShouldRunStates(false, true);
442         assertFalse(getService().shouldClientBeRunning());
443
444         getService().setShouldRunStates(true, false);
445         assertFalse(getService().shouldClientBeRunning());
446
447         // Should only be running if both in the foreground and sync is enabled.
448         getService().setShouldRunStates(true, true);
449         assertTrue(getService().shouldClientBeRunning());
450     }
451
452     @SmallTest
453     @Feature({"Sync"})
454     public void testStartAndStopClient() {
455         /*
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.
459          */
460
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();
466
467         Intent startIntent = createStartIntent();
468         getService().onHandleIntent(startIntent);
469         assertTrue(InvalidationService.getIsClientStartedForTest());
470
471         Intent stopIntent = createStopIntent();
472         getService().onHandleIntent(stopIntent);
473         assertFalse(InvalidationService.getIsClientStartedForTest());
474
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)));
480     }
481
482     @SmallTest
483     @Feature({"Sync"})
484     public void testClientStopsWhenShouldNotBeRunning() {
485         /*
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.
488          */
489         getService().setShouldRunStates(true, true);
490         getService().onCreate();
491
492         // Start the service.
493         Intent startIntent = createStartIntent();
494         getService().onHandleIntent(startIntent);
495         assertTrue(InvalidationService.getIsClientStartedForTest());
496
497         // Change configuration.
498         getService().setShouldRunStates(false, false);
499
500         // Send an Intent and verify that the service stops.
501         getService().onHandleIntent(startIntent);
502         assertFalse(InvalidationService.getIsClientStartedForTest());
503
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)));
509     }
510
511     @SmallTest
512     @Feature({"Sync"})
513     public void testRegistrationIntent() {
514         /*
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.
517          */
518         getService().setShouldRunStates(true, true);
519         getService().onCreate();
520
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);
527
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)));
537
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());
545
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)));
556     }
557
558     /**
559      * Determines if the correct object ids have been written to preferences and registered with the
560      * invalidation client.
561      *
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.
565      */
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>();
574         }
575
576         // Get object ids saved to preferences.
577         Set<ObjectId> actualObjectIds = invPrefs.getSavedObjectIds();
578         if (actualObjectIds == null) {
579             actualObjectIds = new HashSet<ObjectId>();
580         }
581
582         // Get expected registered object ids.
583         Set<ObjectId> expectedRegisteredIds = new HashSet<ObjectId>();
584         if (isReady) {
585             expectedRegisteredIds.addAll(ModelType.modelTypesToObjectIds(expectedTypes));
586             expectedRegisteredIds.addAll(expectedObjectIds);
587         }
588
589         return actualSyncTypes.equals(expectedSyncTypes) &&
590                 actualObjectIds.equals(expectedObjectIds) &&
591                 getService().mCurrentRegistrations.equals(expectedRegisteredIds);
592     }
593
594     @SmallTest
595     @Feature({"Sync"})
596     public void testRegistrationIntentWithTypesAndObjectIds() {
597         /*
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.
601          */
602         getService().setShouldRunStates(true, true);
603         getService().onCreate();
604
605         Account account = AccountManagerHelper.createAccountFromName("test@example.com");
606         Set<ObjectId> objectIds = new HashSet<ObjectId>();
607         Set<ModelType> types = new HashSet<ModelType>();
608
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 */));
616
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 */));
623
624         // Set client to be ready and verify registrations.
625         getService().ready(CLIENT_ID);
626         assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
627
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 */));
634
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 */));
640
641         // Unregister all types.
642         types.clear();
643         registrationIntent = createRegisterIntent(account, false, types);
644         getService().onHandleIntent(registrationIntent);
645         assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
646
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 */));
653
654         // Unregister all object ids.
655         objectIds.clear();
656         registrationIntent = createRegisterIntent(account, new int[0], new String[0]);
657         getService().onHandleIntent(registrationIntent);
658         assertTrue(expectedObjectIdsRegistered(types, objectIds, true /* isReady */));
659
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 */));
666     }
667
668     @SmallTest
669     @Feature({"Sync"})
670     public void testRegistrationIntentNoProxyTabsUsingReady() {
671         getService().setShouldRunStates(true, true);
672         getService().onCreate();
673
674         // Send register Intent.
675         Account account = AccountManagerHelper.createAccountFromName("test@example.com");
676         Intent registrationIntent = createRegisterIntent(account, true, null);
677         getService().onHandleIntent(registrationIntent);
678
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)));
687
688         // Set client to be ready. This triggers registrations.
689         getService().ready(CLIENT_ID);
690         assertTrue(Arrays.equals(CLIENT_ID, InvalidationService.getClientIdForTest()));
691
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)));
696     }
697
698     @SmallTest
699     @Feature({"Sync"})
700     public void testRegistrationIntentNoProxyTabsAlreadyWithClientId() {
701         getService().setShouldRunStates(true, true);
702         getService().onCreate();
703
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);
708
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)));
716
717         // Make sure client is ready.
718         getService().ready(CLIENT_ID);
719         assertTrue(Arrays.equals(CLIENT_ID, InvalidationService.getClientIdForTest()));
720
721         // Choose to register for all types in an already ready client.
722         registrationIntent = createRegisterIntent(account, true, null);
723         getService().onHandleIntent(registrationIntent);
724
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)));
730     }
731
732     @SmallTest
733     @Feature({"Sync"})
734     public void testRegistrationIntentWhenClientShouldNotBeRunning() {
735         /*
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.
738          */
739         getService().onCreate();
740
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);
747
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());
755     }
756
757     @SmallTest
758     @Feature({"Sync"})
759     public void testDeferredRegistrationsIssued() {
760         /*
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.
764          */
765         getService().setShouldRunStates(true, true);
766         getService().onCreate();
767
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);
773
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());
783
784         // Issue reissueRegistrations; verify registration intent issues.
785         getService().reissueRegistrations(CLIENT_ID);
786         assertEquals(2, mStartServiceIntents.size());
787         Intent expectedRegisterIntent = AndroidListener.createRegisterIntent(
788                 getContext(),
789                 CLIENT_ID,
790                 desiredObjectIds);
791         Intent actualRegisterIntent = mStartServiceIntents.get(1);
792         assertTrue(expectedRegisterIntent.filterEquals(actualRegisterIntent));
793         assertEquals(expectedRegisterIntent.getExtras().keySet(),
794                 actualRegisterIntent.getExtras().keySet());
795         assertEquals(
796                 desiredObjectIds,
797                 new HashSet<ObjectId>(getService().mRegistrations.get(0)));
798     }
799
800     @SmallTest
801     @Feature({"Sync"})
802     public void testRegistrationRetries() {
803         /*
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
807          * test.
808          */
809         // TODO(dsmyers): implement.
810         // Bug: https://code.google.com/p/chromium/issues/detail?id=172398
811     }
812
813     /** Creates an intent to start the InvalidationService. */
814     private Intent createStartIntent() {
815         Intent intent = new Intent();
816         return intent;
817     }
818
819     /** Creates an intent to stop the InvalidationService. */
820     private Intent createStopIntent() {
821         Intent intent = new Intent();
822         intent.putExtra(InvalidationIntentProtocol.EXTRA_STOP, true);
823         return intent;
824     }
825
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);
829         return intent;
830     }
831
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);
837         return intent;
838     }
839
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());
845     }
846
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());
851     }
852 }