- add sources.
[platform/framework/web/crosswalk.git] / src / sync / tools / testserver / chromiumsync_test.py
1 #!/usr/bin/env python
2 # Copyright 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Tests exercising chromiumsync and SyncDataModel."""
7
8 import pickle
9 import unittest
10
11 import autofill_specifics_pb2
12 import bookmark_specifics_pb2
13 import chromiumsync
14 import managed_user_specifics_pb2
15 import sync_pb2
16 import theme_specifics_pb2
17
18 class SyncDataModelTest(unittest.TestCase):
19   def setUp(self):
20     self.model = chromiumsync.SyncDataModel()
21     # The Synced Bookmarks folder is not created by default
22     self._expect_synced_bookmarks_folder = False
23
24   def AddToModel(self, proto):
25     self.model._entries[proto.id_string] = proto
26
27   def GetChangesFromTimestamp(self, requested_types, timestamp):
28     message = sync_pb2.GetUpdatesMessage()
29     message.from_timestamp = timestamp
30     for data_type in requested_types:
31       getattr(message.requested_types,
32               chromiumsync.SYNC_TYPE_TO_DESCRIPTOR[
33                   data_type].name).SetInParent()
34     return self.model.GetChanges(
35         chromiumsync.UpdateSieve(message, self.model.migration_history))
36
37   def FindMarkerByNumber(self, markers, datatype):
38     """Search a list of progress markers and find the one for a datatype."""
39     for marker in markers:
40       if marker.data_type_id == datatype.number:
41         return marker
42     self.fail('Required marker not found: %s' % datatype.name)
43
44   def testPermanentItemSpecs(self):
45     specs = chromiumsync.SyncDataModel._PERMANENT_ITEM_SPECS
46
47     declared_specs = set(['0'])
48     for spec in specs:
49       self.assertTrue(spec.parent_tag in declared_specs, 'parent tags must '
50                       'be declared before use')
51       declared_specs.add(spec.tag)
52
53     unique_datatypes = set([x.sync_type for x in specs])
54     self.assertEqual(unique_datatypes,
55                      set(chromiumsync.ALL_TYPES[1:]),
56                      'Every sync datatype should have a permanent folder '
57                      'associated with it')
58
59   def testSaveEntry(self):
60     proto = sync_pb2.SyncEntity()
61     proto.id_string = 'abcd'
62     proto.version = 0
63     self.assertFalse(self.model._ItemExists(proto.id_string))
64     self.model._SaveEntry(proto)
65     self.assertEqual(1, proto.version)
66     self.assertTrue(self.model._ItemExists(proto.id_string))
67     self.model._SaveEntry(proto)
68     self.assertEqual(2, proto.version)
69     proto.version = 0
70     self.assertTrue(self.model._ItemExists(proto.id_string))
71     self.assertEqual(2, self.model._entries[proto.id_string].version)
72
73   def testCreatePermanentItems(self):
74     self.model._CreateDefaultPermanentItems(chromiumsync.ALL_TYPES)
75     self.assertEqual(len(chromiumsync.ALL_TYPES) + 1,
76                      len(self.model._entries))
77
78   def ExpectedPermanentItemCount(self, sync_type):
79     if sync_type == chromiumsync.BOOKMARK:
80       if self._expect_synced_bookmarks_folder:
81         return 4
82       else:
83         return 3
84     else:
85       return 1
86
87   def testGetChangesFromTimestampZeroForEachType(self):
88     all_types = chromiumsync.ALL_TYPES[1:]
89     for sync_type in all_types:
90       self.model = chromiumsync.SyncDataModel()
91       request_types = [sync_type]
92
93       version, changes, remaining = (
94           self.GetChangesFromTimestamp(request_types, 0))
95
96       expected_count = self.ExpectedPermanentItemCount(sync_type)
97       self.assertEqual(expected_count, version)
98       self.assertEqual(expected_count, len(changes))
99       for change in changes:
100         self.assertTrue(change.HasField('server_defined_unique_tag'))
101         self.assertEqual(change.version, change.sync_timestamp)
102         self.assertTrue(change.version <= version)
103
104       # Test idempotence: another GetUpdates from ts=0 shouldn't recreate.
105       version, changes, remaining = (
106           self.GetChangesFromTimestamp(request_types, 0))
107       self.assertEqual(expected_count, version)
108       self.assertEqual(expected_count, len(changes))
109       self.assertEqual(0, remaining)
110
111       # Doing a wider GetUpdates from timestamp zero shouldn't recreate either.
112       new_version, changes, remaining = (
113           self.GetChangesFromTimestamp(all_types, 0))
114       if self._expect_synced_bookmarks_folder:
115         self.assertEqual(len(chromiumsync.SyncDataModel._PERMANENT_ITEM_SPECS),
116                          new_version)
117       else:
118         self.assertEqual(
119             len(chromiumsync.SyncDataModel._PERMANENT_ITEM_SPECS) -1,
120             new_version)
121       self.assertEqual(new_version, len(changes))
122       self.assertEqual(0, remaining)
123       version, changes, remaining = (
124           self.GetChangesFromTimestamp(request_types, 0))
125       self.assertEqual(new_version, version)
126       self.assertEqual(expected_count, len(changes))
127       self.assertEqual(0, remaining)
128
129   def testBatchSize(self):
130     for sync_type in chromiumsync.ALL_TYPES[1:]:
131       specifics = chromiumsync.GetDefaultEntitySpecifics(sync_type)
132       self.model = chromiumsync.SyncDataModel()
133       request_types = [sync_type]
134
135       for i in range(self.model._BATCH_SIZE*3):
136         entry = sync_pb2.SyncEntity()
137         entry.id_string = 'batch test %d' % i
138         entry.specifics.CopyFrom(specifics)
139         self.model._SaveEntry(entry)
140       last_bit = self.ExpectedPermanentItemCount(sync_type)
141       version, changes, changes_remaining = (
142           self.GetChangesFromTimestamp(request_types, 0))
143       self.assertEqual(self.model._BATCH_SIZE, version)
144       self.assertEqual(self.model._BATCH_SIZE*2 + last_bit, changes_remaining)
145       version, changes, changes_remaining = (
146           self.GetChangesFromTimestamp(request_types, version))
147       self.assertEqual(self.model._BATCH_SIZE*2, version)
148       self.assertEqual(self.model._BATCH_SIZE + last_bit, changes_remaining)
149       version, changes, changes_remaining = (
150           self.GetChangesFromTimestamp(request_types, version))
151       self.assertEqual(self.model._BATCH_SIZE*3, version)
152       self.assertEqual(last_bit, changes_remaining)
153       version, changes, changes_remaining = (
154           self.GetChangesFromTimestamp(request_types, version))
155       self.assertEqual(self.model._BATCH_SIZE*3 + last_bit, version)
156       self.assertEqual(0, changes_remaining)
157
158       # Now delete a third of the items.
159       for i in xrange(self.model._BATCH_SIZE*3 - 1, 0, -3):
160         entry = sync_pb2.SyncEntity()
161         entry.id_string = 'batch test %d' % i
162         entry.deleted = True
163         self.model._SaveEntry(entry)
164
165       # The batch counts shouldn't change.
166       version, changes, changes_remaining = (
167           self.GetChangesFromTimestamp(request_types, 0))
168       self.assertEqual(self.model._BATCH_SIZE, len(changes))
169       self.assertEqual(self.model._BATCH_SIZE*2 + last_bit, changes_remaining)
170       version, changes, changes_remaining = (
171           self.GetChangesFromTimestamp(request_types, version))
172       self.assertEqual(self.model._BATCH_SIZE, len(changes))
173       self.assertEqual(self.model._BATCH_SIZE + last_bit, changes_remaining)
174       version, changes, changes_remaining = (
175           self.GetChangesFromTimestamp(request_types, version))
176       self.assertEqual(self.model._BATCH_SIZE, len(changes))
177       self.assertEqual(last_bit, changes_remaining)
178       version, changes, changes_remaining = (
179           self.GetChangesFromTimestamp(request_types, version))
180       self.assertEqual(last_bit, len(changes))
181       self.assertEqual(self.model._BATCH_SIZE*4 + last_bit, version)
182       self.assertEqual(0, changes_remaining)
183
184   def testCommitEachDataType(self):
185     for sync_type in chromiumsync.ALL_TYPES[1:]:
186       specifics = chromiumsync.GetDefaultEntitySpecifics(sync_type)
187       self.model = chromiumsync.SyncDataModel()
188       my_cache_guid = '112358132134'
189       parent = 'foobar'
190       commit_session = {}
191
192       # Start with a GetUpdates from timestamp 0, to populate permanent items.
193       original_version, original_changes, changes_remaining = (
194           self.GetChangesFromTimestamp([sync_type], 0))
195
196       def DoCommit(original=None, id_string='', name=None, parent=None,
197                    position=0):
198         proto = sync_pb2.SyncEntity()
199         if original is not None:
200           proto.version = original.version
201           proto.id_string = original.id_string
202           proto.parent_id_string = original.parent_id_string
203           proto.name = original.name
204         else:
205           proto.id_string = id_string
206           proto.version = 0
207         proto.specifics.CopyFrom(specifics)
208         if name is not None:
209           proto.name = name
210         if parent:
211           proto.parent_id_string = parent.id_string
212         proto.insert_after_item_id = 'please discard'
213         proto.position_in_parent = position
214         proto.folder = True
215         proto.deleted = False
216         result = self.model.CommitEntry(proto, my_cache_guid, commit_session)
217         self.assertTrue(result)
218         return (proto, result)
219
220       # Commit a new item.
221       proto1, result1 = DoCommit(name='namae', id_string='Foo',
222                                  parent=original_changes[-1], position=100)
223       # Commit an item whose parent is another item (referenced via the
224       # pre-commit ID).
225       proto2, result2 = DoCommit(name='Secondo', id_string='Bar',
226                                  parent=proto1, position=-100)
227         # Commit a sibling of the second item.
228       proto3, result3 = DoCommit(name='Third!', id_string='Baz',
229                                  parent=proto1, position=-50)
230
231       self.assertEqual(3, len(commit_session))
232       for p, r in [(proto1, result1), (proto2, result2), (proto3, result3)]:
233         self.assertNotEqual(r.id_string, p.id_string)
234         self.assertEqual(r.originator_client_item_id, p.id_string)
235         self.assertEqual(r.originator_cache_guid, my_cache_guid)
236         self.assertTrue(r is not self.model._entries[r.id_string],
237                         "Commit result didn't make a defensive copy.")
238         self.assertTrue(p is not self.model._entries[r.id_string],
239                         "Commit result didn't make a defensive copy.")
240         self.assertEqual(commit_session.get(p.id_string), r.id_string)
241         self.assertTrue(r.version > original_version)
242       self.assertEqual(result1.parent_id_string, proto1.parent_id_string)
243       self.assertEqual(result2.parent_id_string, result1.id_string)
244       version, changes, remaining = (
245           self.GetChangesFromTimestamp([sync_type], original_version))
246       self.assertEqual(3, len(changes))
247       self.assertEqual(0, remaining)
248       self.assertEqual(original_version + 3, version)
249       self.assertEqual([result1, result2, result3], changes)
250       for c in changes:
251         self.assertTrue(c is not self.model._entries[c.id_string],
252                         "GetChanges didn't make a defensive copy.")
253       self.assertTrue(result2.position_in_parent < result3.position_in_parent)
254       self.assertEqual(-100, result2.position_in_parent)
255
256       # Now update the items so that the second item is the parent of the
257       # first; with the first sandwiched between two new items (4 and 5).
258       # Do this in a new commit session, meaning we'll reference items from
259       # the first batch by their post-commit, server IDs.
260       commit_session = {}
261       old_cache_guid = my_cache_guid
262       my_cache_guid = 'A different GUID'
263       proto2b, result2b = DoCommit(original=result2,
264                                    parent=original_changes[-1])
265       proto4, result4 = DoCommit(id_string='ID4', name='Four',
266                                  parent=result2, position=-200)
267       proto1b, result1b = DoCommit(original=result1,
268                                    parent=result2, position=-150)
269       proto5, result5 = DoCommit(id_string='ID5', name='Five', parent=result2,
270                                  position=150)
271
272       self.assertEqual(2, len(commit_session), 'Only new items in second '
273                        'batch should be in the session')
274       for p, r, original in [(proto2b, result2b, proto2),
275                              (proto4, result4, proto4),
276                              (proto1b, result1b, proto1),
277                              (proto5, result5, proto5)]:
278         self.assertEqual(r.originator_client_item_id, original.id_string)
279         if original is not p:
280           self.assertEqual(r.id_string, p.id_string,
281                            'Ids should be stable after first commit')
282           self.assertEqual(r.originator_cache_guid, old_cache_guid)
283         else:
284           self.assertNotEqual(r.id_string, p.id_string)
285           self.assertEqual(r.originator_cache_guid, my_cache_guid)
286           self.assertEqual(commit_session.get(p.id_string), r.id_string)
287         self.assertTrue(r is not self.model._entries[r.id_string],
288                         "Commit result didn't make a defensive copy.")
289         self.assertTrue(p is not self.model._entries[r.id_string],
290                         "Commit didn't make a defensive copy.")
291         self.assertTrue(r.version > p.version)
292       version, changes, remaining = (
293           self.GetChangesFromTimestamp([sync_type], original_version))
294       self.assertEqual(5, len(changes))
295       self.assertEqual(0, remaining)
296       self.assertEqual(original_version + 7, version)
297       self.assertEqual([result3, result2b, result4, result1b, result5], changes)
298       for c in changes:
299         self.assertTrue(c is not self.model._entries[c.id_string],
300                         "GetChanges didn't make a defensive copy.")
301       self.assertTrue(result4.parent_id_string ==
302                       result1b.parent_id_string ==
303                       result5.parent_id_string ==
304                       result2b.id_string)
305       self.assertTrue(result4.position_in_parent <
306                       result1b.position_in_parent <
307                       result5.position_in_parent)
308
309   def testUpdateSieve(self):
310     # from_timestamp, legacy mode
311     autofill = chromiumsync.SYNC_TYPE_FIELDS['autofill']
312     theme = chromiumsync.SYNC_TYPE_FIELDS['theme']
313     msg = sync_pb2.GetUpdatesMessage()
314     msg.from_timestamp = 15412
315     msg.requested_types.autofill.SetInParent()
316     msg.requested_types.theme.SetInParent()
317
318     sieve = chromiumsync.UpdateSieve(msg)
319     self.assertEqual(sieve._state,
320         {chromiumsync.TOP_LEVEL: 15412,
321          chromiumsync.AUTOFILL: 15412,
322          chromiumsync.THEME: 15412})
323
324     response = sync_pb2.GetUpdatesResponse()
325     sieve.SaveProgress(15412, response)
326     self.assertEqual(0, len(response.new_progress_marker))
327     self.assertFalse(response.HasField('new_timestamp'))
328
329     response = sync_pb2.GetUpdatesResponse()
330     sieve.SaveProgress(15413, response)
331     self.assertEqual(0, len(response.new_progress_marker))
332     self.assertTrue(response.HasField('new_timestamp'))
333     self.assertEqual(15413, response.new_timestamp)
334
335     # Existing tokens
336     msg = sync_pb2.GetUpdatesMessage()
337     marker = msg.from_progress_marker.add()
338     marker.data_type_id = autofill.number
339     marker.token = pickle.dumps((15412, 1))
340     marker = msg.from_progress_marker.add()
341     marker.data_type_id = theme.number
342     marker.token = pickle.dumps((15413, 1))
343     sieve = chromiumsync.UpdateSieve(msg)
344     self.assertEqual(sieve._state,
345         {chromiumsync.TOP_LEVEL: 15412,
346          chromiumsync.AUTOFILL: 15412,
347          chromiumsync.THEME: 15413})
348
349     response = sync_pb2.GetUpdatesResponse()
350     sieve.SaveProgress(15413, response)
351     self.assertEqual(1, len(response.new_progress_marker))
352     self.assertFalse(response.HasField('new_timestamp'))
353     marker = response.new_progress_marker[0]
354     self.assertEqual(marker.data_type_id, autofill.number)
355     self.assertEqual(pickle.loads(marker.token), (15413, 1))
356     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
357
358     # Empty tokens indicating from timestamp = 0
359     msg = sync_pb2.GetUpdatesMessage()
360     marker = msg.from_progress_marker.add()
361     marker.data_type_id = autofill.number
362     marker.token = pickle.dumps((412, 1))
363     marker = msg.from_progress_marker.add()
364     marker.data_type_id = theme.number
365     marker.token = ''
366     sieve = chromiumsync.UpdateSieve(msg)
367     self.assertEqual(sieve._state,
368         {chromiumsync.TOP_LEVEL: 0,
369          chromiumsync.AUTOFILL: 412,
370          chromiumsync.THEME: 0})
371     response = sync_pb2.GetUpdatesResponse()
372     sieve.SaveProgress(1, response)
373     self.assertEqual(1, len(response.new_progress_marker))
374     self.assertFalse(response.HasField('new_timestamp'))
375     marker = response.new_progress_marker[0]
376     self.assertEqual(marker.data_type_id, theme.number)
377     self.assertEqual(pickle.loads(marker.token), (1, 1))
378     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
379
380     response = sync_pb2.GetUpdatesResponse()
381     sieve.SaveProgress(412, response)
382     self.assertEqual(1, len(response.new_progress_marker))
383     self.assertFalse(response.HasField('new_timestamp'))
384     marker = response.new_progress_marker[0]
385     self.assertEqual(marker.data_type_id, theme.number)
386     self.assertEqual(pickle.loads(marker.token), (412, 1))
387     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
388
389     response = sync_pb2.GetUpdatesResponse()
390     sieve.SaveProgress(413, response)
391     self.assertEqual(2, len(response.new_progress_marker))
392     self.assertFalse(response.HasField('new_timestamp'))
393     marker = self.FindMarkerByNumber(response.new_progress_marker, theme)
394     self.assertEqual(pickle.loads(marker.token), (413, 1))
395     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
396     marker = self.FindMarkerByNumber(response.new_progress_marker, autofill)
397     self.assertEqual(pickle.loads(marker.token), (413, 1))
398     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
399
400     # Migration token timestamps (client gives timestamp, server returns token)
401     # These are for migrating from the old 'timestamp' protocol to the
402     # progressmarker protocol, and have nothing to do with the MIGRATION_DONE
403     # error code.
404     msg = sync_pb2.GetUpdatesMessage()
405     marker = msg.from_progress_marker.add()
406     marker.data_type_id = autofill.number
407     marker.timestamp_token_for_migration = 15213
408     marker = msg.from_progress_marker.add()
409     marker.data_type_id = theme.number
410     marker.timestamp_token_for_migration = 15211
411     sieve = chromiumsync.UpdateSieve(msg)
412     self.assertEqual(sieve._state,
413         {chromiumsync.TOP_LEVEL: 15211,
414          chromiumsync.AUTOFILL: 15213,
415          chromiumsync.THEME: 15211})
416     response = sync_pb2.GetUpdatesResponse()
417     sieve.SaveProgress(16000, response)  # There were updates
418     self.assertEqual(2, len(response.new_progress_marker))
419     self.assertFalse(response.HasField('new_timestamp'))
420     marker = self.FindMarkerByNumber(response.new_progress_marker, theme)
421     self.assertEqual(pickle.loads(marker.token), (16000, 1))
422     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
423     marker = self.FindMarkerByNumber(response.new_progress_marker, autofill)
424     self.assertEqual(pickle.loads(marker.token), (16000, 1))
425     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
426
427     msg = sync_pb2.GetUpdatesMessage()
428     marker = msg.from_progress_marker.add()
429     marker.data_type_id = autofill.number
430     marker.timestamp_token_for_migration = 3000
431     marker = msg.from_progress_marker.add()
432     marker.data_type_id = theme.number
433     marker.timestamp_token_for_migration = 3000
434     sieve = chromiumsync.UpdateSieve(msg)
435     self.assertEqual(sieve._state,
436         {chromiumsync.TOP_LEVEL: 3000,
437          chromiumsync.AUTOFILL: 3000,
438          chromiumsync.THEME: 3000})
439     response = sync_pb2.GetUpdatesResponse()
440     sieve.SaveProgress(3000, response)  # Already up to date
441     self.assertEqual(2, len(response.new_progress_marker))
442     self.assertFalse(response.HasField('new_timestamp'))
443     marker = self.FindMarkerByNumber(response.new_progress_marker, theme)
444     self.assertEqual(pickle.loads(marker.token), (3000, 1))
445     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
446     marker = self.FindMarkerByNumber(response.new_progress_marker, autofill)
447     self.assertEqual(pickle.loads(marker.token), (3000, 1))
448     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
449
450   def testCheckRaiseTransientError(self):
451     testserver = chromiumsync.TestServer()
452     http_code, raw_respon = testserver.HandleSetTransientError()
453     self.assertEqual(http_code, 200)
454     try:
455       testserver.CheckTransientError()
456       self.fail('Should have raised transient error exception')
457     except chromiumsync.TransientError:
458       self.assertTrue(testserver.transient_error)
459
460   def testUpdateSieveStoreMigration(self):
461     autofill = chromiumsync.SYNC_TYPE_FIELDS['autofill']
462     theme = chromiumsync.SYNC_TYPE_FIELDS['theme']
463     migrator = chromiumsync.MigrationHistory()
464     msg = sync_pb2.GetUpdatesMessage()
465     marker = msg.from_progress_marker.add()
466     marker.data_type_id = autofill.number
467     marker.token = pickle.dumps((15412, 1))
468     marker = msg.from_progress_marker.add()
469     marker.data_type_id = theme.number
470     marker.token = pickle.dumps((15413, 1))
471     sieve = chromiumsync.UpdateSieve(msg, migrator)
472     sieve.CheckMigrationState()
473
474     migrator.Bump([chromiumsync.BOOKMARK, chromiumsync.PASSWORD])  # v=2
475     sieve = chromiumsync.UpdateSieve(msg, migrator)
476     sieve.CheckMigrationState()
477     self.assertEqual(sieve._state,
478         {chromiumsync.TOP_LEVEL: 15412,
479          chromiumsync.AUTOFILL: 15412,
480          chromiumsync.THEME: 15413})
481
482     migrator.Bump([chromiumsync.AUTOFILL, chromiumsync.PASSWORD])  # v=3
483     sieve = chromiumsync.UpdateSieve(msg, migrator)
484     try:
485       sieve.CheckMigrationState()
486       self.fail('Should have raised.')
487     except chromiumsync.MigrationDoneError, error:
488       # We want this to happen.
489       self.assertEqual([chromiumsync.AUTOFILL], error.datatypes)
490
491     msg = sync_pb2.GetUpdatesMessage()
492     marker = msg.from_progress_marker.add()
493     marker.data_type_id = autofill.number
494     marker.token = ''
495     marker = msg.from_progress_marker.add()
496     marker.data_type_id = theme.number
497     marker.token = pickle.dumps((15413, 1))
498     sieve = chromiumsync.UpdateSieve(msg, migrator)
499     sieve.CheckMigrationState()
500     response = sync_pb2.GetUpdatesResponse()
501     sieve.SaveProgress(15412, response)  # There were updates
502     self.assertEqual(1, len(response.new_progress_marker))
503     self.assertFalse(response.HasField('new_timestamp'))
504     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
505     marker = self.FindMarkerByNumber(response.new_progress_marker, autofill)
506     self.assertEqual(pickle.loads(marker.token), (15412, 3))
507     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
508     msg = sync_pb2.GetUpdatesMessage()
509     marker = msg.from_progress_marker.add()
510     marker.data_type_id = autofill.number
511     marker.token = pickle.dumps((15412, 3))
512     marker = msg.from_progress_marker.add()
513     marker.data_type_id = theme.number
514     marker.token = pickle.dumps((15413, 1))
515     sieve = chromiumsync.UpdateSieve(msg, migrator)
516     sieve.CheckMigrationState()
517
518     migrator.Bump([chromiumsync.THEME, chromiumsync.AUTOFILL])  # v=4
519     migrator.Bump([chromiumsync.AUTOFILL])                      # v=5
520     sieve = chromiumsync.UpdateSieve(msg, migrator)
521     try:
522       sieve.CheckMigrationState()
523       self.fail("Should have raised.")
524     except chromiumsync.MigrationDoneError, error:
525       # We want this to happen.
526       self.assertEqual(set([chromiumsync.THEME, chromiumsync.AUTOFILL]),
527                        set(error.datatypes))
528     msg = sync_pb2.GetUpdatesMessage()
529     marker = msg.from_progress_marker.add()
530     marker.data_type_id = autofill.number
531     marker.token = ''
532     marker = msg.from_progress_marker.add()
533     marker.data_type_id = theme.number
534     marker.token = pickle.dumps((15413, 1))
535     sieve = chromiumsync.UpdateSieve(msg, migrator)
536     try:
537       sieve.CheckMigrationState()
538       self.fail("Should have raised.")
539     except chromiumsync.MigrationDoneError, error:
540       # We want this to happen.
541       self.assertEqual([chromiumsync.THEME], error.datatypes)
542
543     msg = sync_pb2.GetUpdatesMessage()
544     marker = msg.from_progress_marker.add()
545     marker.data_type_id = autofill.number
546     marker.token = ''
547     marker = msg.from_progress_marker.add()
548     marker.data_type_id = theme.number
549     marker.token = ''
550     sieve = chromiumsync.UpdateSieve(msg, migrator)
551     sieve.CheckMigrationState()
552     response = sync_pb2.GetUpdatesResponse()
553     sieve.SaveProgress(15412, response)  # There were updates
554     self.assertEqual(2, len(response.new_progress_marker))
555     self.assertFalse(response.HasField('new_timestamp'))
556     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
557     marker = self.FindMarkerByNumber(response.new_progress_marker, autofill)
558     self.assertEqual(pickle.loads(marker.token), (15412, 5))
559     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
560     marker = self.FindMarkerByNumber(response.new_progress_marker, theme)
561     self.assertEqual(pickle.loads(marker.token), (15412, 4))
562     self.assertFalse(marker.HasField('timestamp_token_for_migration'))
563     msg = sync_pb2.GetUpdatesMessage()
564     marker = msg.from_progress_marker.add()
565     marker.data_type_id = autofill.number
566     marker.token = pickle.dumps((15412, 5))
567     marker = msg.from_progress_marker.add()
568     marker.data_type_id = theme.number
569     marker.token = pickle.dumps((15413, 4))
570     sieve = chromiumsync.UpdateSieve(msg, migrator)
571     sieve.CheckMigrationState()
572
573   def testCreateSyncedBookmarks(self):
574     version1, changes, remaining = (
575         self.GetChangesFromTimestamp([chromiumsync.BOOKMARK], 0))
576     id_string = self.model._MakeCurrentId(chromiumsync.BOOKMARK,
577                                           '<server tag>synced_bookmarks')
578     self.assertFalse(self.model._ItemExists(id_string))
579     self._expect_synced_bookmarks_folder = True
580     self.model.TriggerCreateSyncedBookmarks()
581     self.assertTrue(self.model._ItemExists(id_string))
582
583     # Check that the version changed when the folder was created and the only
584     # change was the folder creation.
585     version2, changes, remaining = (
586         self.GetChangesFromTimestamp([chromiumsync.BOOKMARK], version1))
587     self.assertEqual(len(changes), 1)
588     self.assertEqual(changes[0].id_string, id_string)
589     self.assertNotEqual(version1, version2)
590     self.assertEqual(
591         self.ExpectedPermanentItemCount(chromiumsync.BOOKMARK),
592         version2)
593
594     # Ensure getting from timestamp 0 includes the folder.
595     version, changes, remaining = (
596         self.GetChangesFromTimestamp([chromiumsync.BOOKMARK], 0))
597     self.assertEqual(
598         self.ExpectedPermanentItemCount(chromiumsync.BOOKMARK),
599         len(changes))
600     self.assertEqual(version2, version)
601
602   def testAcknowledgeManagedUser(self):
603     # Create permanent items.
604     self.GetChangesFromTimestamp([chromiumsync.MANAGED_USER], 0)
605     proto = sync_pb2.SyncEntity()
606     proto.id_string = 'abcd'
607     proto.version = 0
608
609     # Make sure the managed_user field exists.
610     proto.specifics.managed_user.acknowledged = False
611     self.assertTrue(proto.specifics.HasField('managed_user'))
612     self.AddToModel(proto)
613     version1, changes1, remaining1 = (
614         self.GetChangesFromTimestamp([chromiumsync.MANAGED_USER], 0))
615     for change in changes1:
616       self.assertTrue(not change.specifics.managed_user.acknowledged)
617
618     # Turn on managed user acknowledgement
619     self.model.acknowledge_managed_users = True
620
621     version2, changes2, remaining2 = (
622         self.GetChangesFromTimestamp([chromiumsync.MANAGED_USER], 0))
623     for change in changes2:
624       self.assertTrue(change.specifics.managed_user.acknowledged)
625
626   def testGetKey(self):
627     [key1] = self.model.GetKeystoreKeys()
628     [key2] = self.model.GetKeystoreKeys()
629     self.assertTrue(len(key1))
630     self.assertEqual(key1, key2)
631
632     # Trigger the rotation. A subsequent GetUpdates should return the nigori
633     # node (whose timestamp was bumped by the rotation).
634     version1, changes, remaining = (
635         self.GetChangesFromTimestamp([chromiumsync.NIGORI], 0))
636     self.model.TriggerRotateKeystoreKeys()
637     version2, changes, remaining = (
638         self.GetChangesFromTimestamp([chromiumsync.NIGORI], version1))
639     self.assertNotEqual(version1, version2)
640     self.assertEquals(len(changes), 1)
641     self.assertEquals(changes[0].name, "Nigori")
642
643     # The current keys should contain the old keys, with the new key appended.
644     [key1, key3] = self.model.GetKeystoreKeys()
645     self.assertEquals(key1, key2)
646     self.assertNotEqual(key1, key3)
647     self.assertTrue(len(key3) > 0)
648
649   def testTriggerEnableKeystoreEncryption(self):
650     version1, changes, remaining = (
651         self.GetChangesFromTimestamp([chromiumsync.EXPERIMENTS], 0))
652     keystore_encryption_id_string = (
653         self.model._ClientTagToId(
654             chromiumsync.EXPERIMENTS,
655             chromiumsync.KEYSTORE_ENCRYPTION_EXPERIMENT_TAG))
656
657     self.assertFalse(self.model._ItemExists(keystore_encryption_id_string))
658     self.model.TriggerEnableKeystoreEncryption()
659     self.assertTrue(self.model._ItemExists(keystore_encryption_id_string))
660
661     # The creation of the experiment should be downloaded on the next
662     # GetUpdates.
663     version2, changes, remaining = (
664         self.GetChangesFromTimestamp([chromiumsync.EXPERIMENTS], version1))
665     self.assertEqual(len(changes), 1)
666     self.assertEqual(changes[0].id_string, keystore_encryption_id_string)
667     self.assertNotEqual(version1, version2)
668
669     # Verify the experiment was created properly and is enabled.
670     self.assertEqual(chromiumsync.KEYSTORE_ENCRYPTION_EXPERIMENT_TAG,
671                      changes[0].client_defined_unique_tag)
672     self.assertTrue(changes[0].HasField("specifics"))
673     self.assertTrue(changes[0].specifics.HasField("experiments"))
674     self.assertTrue(
675         changes[0].specifics.experiments.HasField("keystore_encryption"))
676     self.assertTrue(
677         changes[0].specifics.experiments.keystore_encryption.enabled)
678
679 if __name__ == '__main__':
680   unittest.main()