OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python2.4 |
| 2 # Copyright (c) 2010 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 unittest |
| 9 |
| 10 from google.protobuf import text_format |
| 11 |
| 12 import chromiumsync |
| 13 import sync_pb2 |
| 14 |
| 15 class SyncDataModelTest(unittest.TestCase): |
| 16 def setUp(self): |
| 17 self.model = chromiumsync.SyncDataModel() |
| 18 |
| 19 def AddToModel(self, proto): |
| 20 self.model._entries[proto.id_string] = proto |
| 21 |
| 22 def testPermanentItemSpecs(self): |
| 23 SPECS = chromiumsync.SyncDataModel._PERMANENT_ITEM_SPECS |
| 24 # parent_tags must be declared before use. |
| 25 declared_specs = set(['0']) |
| 26 for spec in SPECS: |
| 27 self.assertTrue(spec.parent_tag in declared_specs) |
| 28 declared_specs.add(spec.tag) |
| 29 # Every sync datatype should have a permanent folder associated with it. |
| 30 unique_datatypes = set([x.sync_type for x in SPECS]) |
| 31 self.assertEqual(unique_datatypes, |
| 32 set(chromiumsync.ALL_TYPES)) |
| 33 |
| 34 def testSaveEntry(self): |
| 35 proto = sync_pb2.SyncEntity() |
| 36 proto.id_string = 'abcd'; |
| 37 proto.version = 0; |
| 38 self.assertFalse(self.model._ItemExists(proto.id_string)) |
| 39 self.model._SaveEntry(proto) |
| 40 self.assertEqual(1, proto.version) |
| 41 self.assertTrue(self.model._ItemExists(proto.id_string)) |
| 42 self.model._SaveEntry(proto) |
| 43 self.assertEqual(2, proto.version) |
| 44 proto.version = 0 |
| 45 self.assertTrue(self.model._ItemExists(proto.id_string)) |
| 46 self.assertEqual(2, self.model._entries[proto.id_string].version) |
| 47 |
| 48 def testWritePosition(self): |
| 49 def MakeProto(id_string, parent, position): |
| 50 proto = sync_pb2.SyncEntity() |
| 51 proto.id_string = id_string |
| 52 proto.position_in_parent = position |
| 53 proto.parent_id_string = parent |
| 54 self.AddToModel(proto) |
| 55 |
| 56 MakeProto('a', 'X', 1000) |
| 57 MakeProto('b', 'X', 1800) |
| 58 MakeProto('c', 'X', 2600) |
| 59 MakeProto('a1', 'Z', 1007) |
| 60 MakeProto('a2', 'Z', 1807) |
| 61 MakeProto('a3', 'Z', 2607) |
| 62 MakeProto('s', 'Y', 10000) |
| 63 |
| 64 def AssertPositionResult(my_id, parent_id, prev_id, expected_position): |
| 65 entry = sync_pb2.SyncEntity() |
| 66 entry.id_string = my_id |
| 67 self.model._WritePosition(entry, parent_id, prev_id) |
| 68 self.assertEqual(expected_position, entry.position_in_parent) |
| 69 self.assertEqual(parent_id, entry.parent_id_string) |
| 70 self.assertFalse(entry.HasField('insert_after_item_id')) |
| 71 |
| 72 AssertPositionResult('new', 'new_parent', '', 0) |
| 73 AssertPositionResult('new', 'Y', '', 10000 - (2 ** 20)) |
| 74 AssertPositionResult('new', 'Y', 's', 10000 + (2 ** 20)) |
| 75 AssertPositionResult('s', 'Y', '', 10000) |
| 76 AssertPositionResult('s', 'Y', 's', 10000) |
| 77 AssertPositionResult('a1', 'Z', '', 1007) |
| 78 |
| 79 AssertPositionResult('new', 'X', '', 1000 - (2 ** 20)) |
| 80 AssertPositionResult('new', 'X', 'a', 1100) |
| 81 AssertPositionResult('new', 'X', 'b', 1900) |
| 82 AssertPositionResult('new', 'X', 'c', 2600 + (2 ** 20)) |
| 83 |
| 84 AssertPositionResult('a1', 'X', '', 1000 - (2 ** 20)) |
| 85 AssertPositionResult('a1', 'X', 'a', 1100) |
| 86 AssertPositionResult('a1', 'X', 'b', 1900) |
| 87 AssertPositionResult('a1', 'X', 'c', 2600 + (2 ** 20)) |
| 88 |
| 89 AssertPositionResult('a', 'X', '', 1000) |
| 90 AssertPositionResult('a', 'X', 'b', 1900) |
| 91 AssertPositionResult('a', 'X', 'c', 2600 + (2 ** 20)) |
| 92 |
| 93 AssertPositionResult('b', 'X', '', 1000 - (2 ** 20)) |
| 94 AssertPositionResult('b', 'X', 'a', 1800) |
| 95 AssertPositionResult('b', 'X', 'c', 2600 + (2 ** 20)) |
| 96 |
| 97 AssertPositionResult('c', 'X', '', 1000 - (2 ** 20)) |
| 98 AssertPositionResult('c', 'X', 'a', 1100) |
| 99 AssertPositionResult('c', 'X', 'b', 2600) |
| 100 |
| 101 def testCreatePermanentItems(self): |
| 102 self.model._CreatePermanentItems(chromiumsync.ALL_TYPES) |
| 103 self.assertEqual(len(chromiumsync.ALL_TYPES) + 2, |
| 104 len(self.model._entries)) |
| 105 |
| 106 def ExpectedPermanentItemCount(self, sync_type): |
| 107 if sync_type == chromiumsync.BOOKMARK: |
| 108 return 4 |
| 109 elif sync_type == chromiumsync.TOP_LEVEL: |
| 110 return 1 |
| 111 else: |
| 112 return 2 |
| 113 |
| 114 def testGetChangesFromTimestampZeroForEachType(self): |
| 115 for sync_type in chromiumsync.ALL_TYPES: |
| 116 self.model = chromiumsync.SyncDataModel() |
| 117 request_types = [sync_type, chromiumsync.TOP_LEVEL] |
| 118 |
| 119 version, changes = self.model.GetChangesFromTimestamp(request_types, 0) |
| 120 |
| 121 expected_count = self.ExpectedPermanentItemCount(sync_type) |
| 122 self.assertEqual(expected_count, version) |
| 123 self.assertEqual(expected_count, len(changes)) |
| 124 self.assertEqual('google_chrome', changes[0].server_defined_unique_tag) |
| 125 for change in changes: |
| 126 self.assertTrue(change.HasField('server_defined_unique_tag')) |
| 127 self.assertEqual(change.version, change.sync_timestamp) |
| 128 self.assertTrue(change.version <= version) |
| 129 |
| 130 # Test idempotence: another GetUpdates from ts=0 shouldn't recreate. |
| 131 version, changes = self.model.GetChangesFromTimestamp(request_types, 0) |
| 132 self.assertEqual(expected_count, version) |
| 133 self.assertEqual(expected_count, len(changes)) |
| 134 |
| 135 # Doing a wider GetUpdates from timestamp zero shouldn't recreate either. |
| 136 new_version, changes = self.model.GetChangesFromTimestamp( |
| 137 chromiumsync.ALL_TYPES, 0) |
| 138 self.assertEqual(len(chromiumsync.SyncDataModel._PERMANENT_ITEM_SPECS), |
| 139 new_version) |
| 140 self.assertEqual(new_version, len(changes)) |
| 141 version, changes = self.model.GetChangesFromTimestamp(request_types, 0) |
| 142 self.assertEqual(new_version, version) |
| 143 self.assertEqual(expected_count, len(changes)) |
| 144 |
| 145 def testBatchSize(self): |
| 146 for sync_type in chromiumsync.ALL_TYPES[1:]: |
| 147 specifics = chromiumsync.GetDefaultEntitySpecifics(sync_type) |
| 148 self.model = chromiumsync.SyncDataModel() |
| 149 request_types = [sync_type, chromiumsync.TOP_LEVEL] |
| 150 |
| 151 for i in range(self.model._BATCH_SIZE*3): |
| 152 entry = sync_pb2.SyncEntity() |
| 153 entry.id_string = 'batch test %d' % i |
| 154 entry.specifics.CopyFrom(specifics) |
| 155 self.model._SaveEntry(entry) |
| 156 version, changes = self.model.GetChangesFromTimestamp(request_types, 0) |
| 157 self.assertEqual(self.model._BATCH_SIZE, version) |
| 158 version, changes = self.model.GetChangesFromTimestamp(request_types, |
| 159 version) |
| 160 self.assertEqual(self.model._BATCH_SIZE*2, version) |
| 161 version, changes = self.model.GetChangesFromTimestamp(request_types, |
| 162 version) |
| 163 self.assertEqual(self.model._BATCH_SIZE*3, version) |
| 164 expected_dingleberry = self.ExpectedPermanentItemCount(sync_type) |
| 165 version, changes = self.model.GetChangesFromTimestamp(request_types, |
| 166 version) |
| 167 self.assertEqual(self.model._BATCH_SIZE*3 + expected_dingleberry, |
| 168 version) |
| 169 |
| 170 # Now delete a third of the items. |
| 171 for i in xrange(self.model._BATCH_SIZE*3 - 1, 0, -3): |
| 172 entry = sync_pb2.SyncEntity() |
| 173 entry.id_string = 'batch test %d' % i |
| 174 entry.deleted = True |
| 175 self.model._SaveEntry(entry) |
| 176 |
| 177 # The batch counts shouldn't change. |
| 178 version, changes = self.model.GetChangesFromTimestamp(request_types, 0) |
| 179 self.assertEqual(self.model._BATCH_SIZE, len(changes)) |
| 180 version, changes = self.model.GetChangesFromTimestamp(request_types, |
| 181 version) |
| 182 self.assertEqual(self.model._BATCH_SIZE, len(changes)) |
| 183 version, changes = self.model.GetChangesFromTimestamp(request_types, |
| 184 version) |
| 185 self.assertEqual(self.model._BATCH_SIZE, len(changes)) |
| 186 expected_dingleberry = self.ExpectedPermanentItemCount(sync_type) |
| 187 version, changes = self.model.GetChangesFromTimestamp(request_types, |
| 188 version) |
| 189 self.assertEqual(expected_dingleberry, len(changes)) |
| 190 self.assertEqual(self.model._BATCH_SIZE*4 + expected_dingleberry, version) |
| 191 |
| 192 def testCommitEachDataType(self): |
| 193 for sync_type in chromiumsync.ALL_TYPES[1:]: |
| 194 specifics = chromiumsync.GetDefaultEntitySpecifics(sync_type) |
| 195 self.model = chromiumsync.SyncDataModel() |
| 196 my_cache_guid = '112358132134' |
| 197 parent = 'foobar' |
| 198 commit_session = {} |
| 199 |
| 200 # Start with a GetUpdates from timestamp 0, to populate permanent items. |
| 201 original_version, original_changes = ( |
| 202 self.model.GetChangesFromTimestamp([sync_type], 0)) |
| 203 |
| 204 def DoCommit(original=None, id='', name=None, parent=None, prev=None): |
| 205 proto = sync_pb2.SyncEntity() |
| 206 if original is not None: |
| 207 proto.version = original.version |
| 208 proto.id_string = original.id_string |
| 209 proto.parent_id_string = original.parent_id_string |
| 210 proto.name = original.name |
| 211 else: |
| 212 proto.id_string = id |
| 213 proto.version = 0 |
| 214 proto.specifics.CopyFrom(specifics) |
| 215 if name is not None: |
| 216 proto.name = name |
| 217 if parent: |
| 218 proto.parent_id_string = parent.id_string |
| 219 if prev: |
| 220 proto.insert_after_item_id = prev.id_string |
| 221 else: |
| 222 proto.insert_after_item_id = '' |
| 223 proto.folder = True |
| 224 proto.deleted = False |
| 225 result = self.model.CommitEntry(proto, my_cache_guid, commit_session) |
| 226 self.assertTrue(result) |
| 227 return (proto, result) |
| 228 |
| 229 # Commit a new item. |
| 230 proto1, result1 = DoCommit(name='namae', id='Foo', |
| 231 parent=original_changes[-1]) |
| 232 # Commit an item whose parent is another item (referenced via the |
| 233 # pre-commit ID). |
| 234 proto2, result2 = DoCommit(name='Secondo', id='Bar', |
| 235 parent=proto1) |
| 236 # Commit a sibling of the second item. |
| 237 proto3, result3 = DoCommit(name='Third!', id='Baz', |
| 238 parent=proto1, prev=proto2) |
| 239 |
| 240 self.assertEqual(3, len(commit_session)) |
| 241 for p, r in [(proto1, result1), (proto2, result2), (proto3, result3)]: |
| 242 self.assertNotEqual(r.id_string, p.id_string) |
| 243 self.assertEqual(r.originator_client_item_id, p.id_string) |
| 244 self.assertEqual(r.originator_cache_guid, my_cache_guid) |
| 245 self.assertTrue(r is not self.model._entries[r.id_string], |
| 246 "Commit result didn't make a defensive copy.") |
| 247 self.assertTrue(p is not self.model._entries[r.id_string], |
| 248 "Commit result didn't make a defensive copy.") |
| 249 self.assertEqual(commit_session.get(p.id_string), r.id_string) |
| 250 self.assertTrue(r.version > original_version) |
| 251 self.assertEqual(result1.parent_id_string, proto1.parent_id_string) |
| 252 self.assertEqual(result2.parent_id_string, result1.id_string) |
| 253 version, changes = self.model.GetChangesFromTimestamp([sync_type], |
| 254 original_version) |
| 255 self.assertEqual(3, len(changes)) |
| 256 self.assertEqual(original_version + 3, version) |
| 257 self.assertEqual([result1, result2, result3], changes) |
| 258 for c in changes: |
| 259 self.assertTrue(c is not self.model._entries[c.id_string], |
| 260 "GetChanges didn't make a defensive copy.") |
| 261 self.assertTrue(result2.position_in_parent < result3.position_in_parent) |
| 262 self.assertEqual(0, result2.position_in_parent) |
| 263 |
| 264 # Now update the items so that the second item is the parent of the |
| 265 # first; with the first sandwiched between two new items (4 and 5). |
| 266 # Do this in a new commit session, meaning we'll reference items from |
| 267 # the first batch by their post-commit, server IDs. |
| 268 commit_session = {} |
| 269 old_cache_guid = my_cache_guid |
| 270 my_cache_guid = 'A different GUID' |
| 271 proto2b, result2b = DoCommit(original=result2, |
| 272 parent=original_changes[-1]) |
| 273 proto4, result4 = DoCommit(id='ID4', name='Four', |
| 274 parent=result2, prev=None) |
| 275 proto1b, result1b = DoCommit(original=result1, |
| 276 parent=result2, prev=proto4) |
| 277 proto5, result5 = DoCommit(id='ID5', name='Five', parent=result2, |
| 278 prev=result1) |
| 279 |
| 280 self.assertEqual(2, len(commit_session), |
| 281 'Only new items in second batch should be in the session') |
| 282 for p, r, original in [(proto2b, result2b, proto2), |
| 283 (proto4, result4, proto4), |
| 284 (proto1b, result1b, proto1), |
| 285 (proto5, result5, proto5)]: |
| 286 self.assertEqual(r.originator_client_item_id, original.id_string) |
| 287 if original is not p: |
| 288 self.assertEqual(r.id_string, p.id_string, |
| 289 'Ids should be stable after first commit') |
| 290 self.assertEqual(r.originator_cache_guid, old_cache_guid) |
| 291 else: |
| 292 self.assertNotEqual(r.id_string, p.id_string) |
| 293 self.assertEqual(r.originator_cache_guid, my_cache_guid) |
| 294 self.assertEqual(commit_session.get(p.id_string), r.id_string) |
| 295 self.assertTrue(r is not self.model._entries[r.id_string], |
| 296 "Commit result didn't make a defensive copy.") |
| 297 self.assertTrue(p is not self.model._entries[r.id_string], |
| 298 "Commit didn't make a defensive copy.") |
| 299 self.assertTrue(r.version > p.version) |
| 300 version, changes = self.model.GetChangesFromTimestamp([sync_type], |
| 301 original_version) |
| 302 self.assertEqual(5, len(changes)) |
| 303 self.assertEqual(original_version + 7, version) |
| 304 self.assertEqual([result3, result2b, result4, result1b, result5], changes) |
| 305 for c in changes: |
| 306 self.assertTrue(c is not self.model._entries[c.id_string], |
| 307 "GetChanges didn't make a defensive copy.") |
| 308 self.assertTrue(result4.parent_id_string == |
| 309 result1b.parent_id_string == |
| 310 result5.parent_id_string == |
| 311 result2b.id_string) |
| 312 self.assertTrue(result4.position_in_parent < |
| 313 result1b.position_in_parent < |
| 314 result5.position_in_parent) |
| 315 |
| 316 if __name__ == '__main__': |
| 317 unittest.main() |
OLD | NEW |