Home | History | Annotate | Download | only in testserver
      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()
    681