1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sync/engine/directory_update_handler.h"
7 #include "base/compiler_specific.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/stl_util.h"
10 #include "sync/engine/syncer_proto_util.h"
11 #include "sync/internal_api/public/base/attachment_id_proto.h"
12 #include "sync/internal_api/public/base/model_type.h"
13 #include "sync/internal_api/public/test/test_entry_factory.h"
14 #include "sync/protocol/sync.pb.h"
15 #include "sync/sessions/directory_type_debug_info_emitter.h"
16 #include "sync/sessions/status_controller.h"
17 #include "sync/syncable/directory.h"
18 #include "sync/syncable/entry.h"
19 #include "sync/syncable/mutable_entry.h"
20 #include "sync/syncable/syncable_model_neutral_write_transaction.h"
21 #include "sync/syncable/syncable_proto_util.h"
22 #include "sync/syncable/syncable_read_transaction.h"
23 #include "sync/syncable/syncable_write_transaction.h"
24 #include "sync/test/engine/fake_model_worker.h"
25 #include "sync/test/engine/test_directory_setter_upper.h"
26 #include "sync/test/engine/test_id_factory.h"
27 #include "sync/test/engine/test_syncable_utils.h"
28 #include "testing/gtest/include/gtest/gtest.h"
33 using syncable::UNITTEST
;
35 static const int64 kDefaultVersion
= 1000;
37 // A test harness for tests that focus on processing updates.
39 // Update processing is what occurs when we first download updates. It converts
40 // the received protobuf message into information in the syncable::Directory.
41 // Any invalid or redundant updates will be dropped at this point.
42 class DirectoryUpdateHandlerProcessUpdateTest
: public ::testing::Test
{
44 DirectoryUpdateHandlerProcessUpdateTest()
45 : ui_worker_(new FakeModelWorker(GROUP_UI
)) {
48 ~DirectoryUpdateHandlerProcessUpdateTest() override
{}
50 void SetUp() override
{ dir_maker_
.SetUp(); }
52 void TearDown() override
{ dir_maker_
.TearDown(); }
54 syncable::Directory
* dir() {
55 return dir_maker_
.directory();
59 scoped_ptr
<sync_pb::SyncEntity
> CreateUpdate(
60 const std::string
& id
,
61 const std::string
& parent
,
62 const ModelType
& type
);
64 // This exists mostly to give tests access to the protected member function.
65 // Warning: This takes the syncable directory lock.
66 void UpdateSyncEntities(
67 DirectoryUpdateHandler
* handler
,
68 const SyncEntityList
& applicable_updates
,
69 sessions::StatusController
* status
);
71 // Another function to access private member functions.
72 void UpdateProgressMarkers(
73 DirectoryUpdateHandler
* handler
,
74 const sync_pb::DataTypeProgressMarker
& progress
);
76 scoped_refptr
<FakeModelWorker
> ui_worker() {
80 bool EntryExists(const std::string
& id
) {
81 syncable::ReadTransaction
trans(FROM_HERE
, dir());
82 syncable::Entry
e(&trans
, syncable::GET_BY_ID
,
83 Id::CreateFromServerId(id
));
84 return e
.good() && !e
.GetIsDel();
88 // Used in the construction of DirectoryTypeDebugInfoEmitters.
89 ObserverList
<TypeDebugInfoObserver
> type_observers_
;
92 base::MessageLoop loop_
; // Needed to initialize the directory.
93 TestDirectorySetterUpper dir_maker_
;
94 scoped_refptr
<FakeModelWorker
> ui_worker_
;
97 scoped_ptr
<sync_pb::SyncEntity
>
98 DirectoryUpdateHandlerProcessUpdateTest::CreateUpdate(
99 const std::string
& id
,
100 const std::string
& parent
,
101 const ModelType
& type
) {
102 scoped_ptr
<sync_pb::SyncEntity
> e(new sync_pb::SyncEntity());
103 e
->set_id_string(id
);
104 e
->set_parent_id_string(parent
);
105 e
->set_non_unique_name(id
);
107 e
->set_version(kDefaultVersion
);
108 AddDefaultFieldValue(type
, e
->mutable_specifics());
112 void DirectoryUpdateHandlerProcessUpdateTest::UpdateSyncEntities(
113 DirectoryUpdateHandler
* handler
,
114 const SyncEntityList
& applicable_updates
,
115 sessions::StatusController
* status
) {
116 syncable::ModelNeutralWriteTransaction
trans(FROM_HERE
, UNITTEST
, dir());
117 handler
->UpdateSyncEntities(&trans
, applicable_updates
, status
);
120 void DirectoryUpdateHandlerProcessUpdateTest::UpdateProgressMarkers(
121 DirectoryUpdateHandler
* handler
,
122 const sync_pb::DataTypeProgressMarker
& progress
) {
123 handler
->UpdateProgressMarker(progress
);
126 static const char kCacheGuid
[] = "IrcjZ2jyzHDV9Io4+zKcXQ==";
128 // Test that the bookmark tag is set on newly downloaded items.
129 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
, NewBookmarkTag
) {
130 DirectoryTypeDebugInfoEmitter
emitter(BOOKMARKS
, &type_observers_
);
131 DirectoryUpdateHandler
handler(dir(), BOOKMARKS
, ui_worker(), &emitter
);
132 sync_pb::GetUpdatesResponse gu_response
;
133 sessions::StatusController status
;
135 // Add a bookmark item to the update message.
136 std::string root
= Id::GetRoot().GetServerId();
137 Id server_id
= Id::CreateFromServerId("b1");
138 scoped_ptr
<sync_pb::SyncEntity
> e
=
139 CreateUpdate(SyncableIdToProto(server_id
), root
, BOOKMARKS
);
140 e
->set_originator_cache_guid(
141 std::string(kCacheGuid
, arraysize(kCacheGuid
)-1));
142 Id client_id
= Id::CreateFromClientString("-2");
143 e
->set_originator_client_item_id(client_id
.GetServerId());
144 e
->set_position_in_parent(0);
146 // Add it to the applicable updates list.
147 SyncEntityList bookmark_updates
;
148 bookmark_updates
.push_back(e
.get());
150 // Process the update.
151 UpdateSyncEntities(&handler
, bookmark_updates
, &status
);
153 syncable::ReadTransaction
trans(FROM_HERE
, dir());
154 syncable::Entry
entry(&trans
, syncable::GET_BY_ID
, server_id
);
155 ASSERT_TRUE(entry
.good());
156 EXPECT_TRUE(UniquePosition::IsValidSuffix(entry
.GetUniqueBookmarkTag()));
157 EXPECT_TRUE(entry
.GetServerUniquePosition().IsValid());
159 // If this assertion fails, that might indicate that the algorithm used to
160 // generate bookmark tags has been modified. This could have implications for
161 // bookmark ordering. Please make sure you know what you're doing if you
162 // intend to make such a change.
163 EXPECT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", entry
.GetUniqueBookmarkTag());
166 // Test the receipt of a type root node.
167 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
,
168 ReceiveServerCreatedBookmarkFolders
) {
169 DirectoryTypeDebugInfoEmitter
emitter(BOOKMARKS
, &type_observers_
);
170 DirectoryUpdateHandler
handler(dir(), BOOKMARKS
, ui_worker(), &emitter
);
171 sync_pb::GetUpdatesResponse gu_response
;
172 sessions::StatusController status
;
174 // Create an update that mimics the bookmark root.
175 Id server_id
= Id::CreateFromServerId("xyz");
176 std::string root
= Id::GetRoot().GetServerId();
177 scoped_ptr
<sync_pb::SyncEntity
> e
=
178 CreateUpdate(SyncableIdToProto(server_id
), root
, BOOKMARKS
);
179 e
->set_server_defined_unique_tag("google_chrome_bookmarks");
182 // Add it to the applicable updates list.
183 SyncEntityList bookmark_updates
;
184 bookmark_updates
.push_back(e
.get());
186 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e
));
189 UpdateSyncEntities(&handler
, bookmark_updates
, &status
);
191 // Verify the results.
192 syncable::ReadTransaction
trans(FROM_HERE
, dir());
193 syncable::Entry
entry(&trans
, syncable::GET_BY_ID
, server_id
);
194 ASSERT_TRUE(entry
.good());
196 EXPECT_FALSE(entry
.ShouldMaintainPosition());
197 EXPECT_FALSE(entry
.GetUniquePosition().IsValid());
198 EXPECT_FALSE(entry
.GetServerUniquePosition().IsValid());
199 EXPECT_TRUE(entry
.GetUniqueBookmarkTag().empty());
202 // Test the receipt of a non-bookmark item.
203 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
, ReceiveNonBookmarkItem
) {
204 DirectoryTypeDebugInfoEmitter
emitter(AUTOFILL
, &type_observers_
);
205 DirectoryUpdateHandler
handler(dir(), AUTOFILL
, ui_worker(), &emitter
);
206 sync_pb::GetUpdatesResponse gu_response
;
207 sessions::StatusController status
;
209 std::string root
= Id::GetRoot().GetServerId();
210 Id server_id
= Id::CreateFromServerId("xyz");
211 scoped_ptr
<sync_pb::SyncEntity
> e
=
212 CreateUpdate(SyncableIdToProto(server_id
), root
, AUTOFILL
);
213 e
->set_server_defined_unique_tag("9PGRuKdX5sHyGMB17CvYTXuC43I=");
215 // Add it to the applicable updates list.
216 SyncEntityList autofill_updates
;
217 autofill_updates
.push_back(e
.get());
219 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e
));
222 UpdateSyncEntities(&handler
, autofill_updates
, &status
);
224 syncable::ReadTransaction
trans(FROM_HERE
, dir());
225 syncable::Entry
entry(&trans
, syncable::GET_BY_ID
, server_id
);
226 ASSERT_TRUE(entry
.good());
228 EXPECT_FALSE(entry
.ShouldMaintainPosition());
229 EXPECT_FALSE(entry
.GetUniquePosition().IsValid());
230 EXPECT_FALSE(entry
.GetServerUniquePosition().IsValid());
231 EXPECT_TRUE(entry
.GetUniqueBookmarkTag().empty());
234 // Tests the setting of progress markers.
235 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
, ProcessNewProgressMarkers
) {
236 DirectoryTypeDebugInfoEmitter
emitter(BOOKMARKS
, &type_observers_
);
237 DirectoryUpdateHandler
handler(dir(), BOOKMARKS
, ui_worker(), &emitter
);
239 sync_pb::DataTypeProgressMarker progress
;
240 progress
.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS
));
241 progress
.set_token("token");
243 UpdateProgressMarkers(&handler
, progress
);
245 sync_pb::DataTypeProgressMarker saved
;
246 dir()->GetDownloadProgress(BOOKMARKS
, &saved
);
248 EXPECT_EQ(progress
.token(), saved
.token());
249 EXPECT_EQ(progress
.data_type_id(), saved
.data_type_id());
252 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
, GarbageCollectionByVersion
) {
253 DirectoryTypeDebugInfoEmitter
emitter(SYNCED_NOTIFICATIONS
, &type_observers_
);
254 DirectoryUpdateHandler
handler(dir(), SYNCED_NOTIFICATIONS
,
255 ui_worker(), &emitter
);
256 sessions::StatusController status
;
258 sync_pb::DataTypeProgressMarker progress
;
259 progress
.set_data_type_id(
260 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS
));
261 progress
.set_token("token");
262 progress
.mutable_gc_directive()->set_version_watermark(kDefaultVersion
+ 10);
264 sync_pb::DataTypeContext context
;
265 context
.set_data_type_id(
266 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS
));
267 context
.set_context("context");
268 context
.set_version(1);
270 scoped_ptr
<sync_pb::SyncEntity
> type_root
=
271 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("root")),
272 Id::GetRoot().GetServerId(), SYNCED_NOTIFICATIONS
);
273 type_root
->set_server_defined_unique_tag(
274 ModelTypeToRootTag(SYNCED_NOTIFICATIONS
));
275 type_root
->set_folder(true);
277 scoped_ptr
<sync_pb::SyncEntity
> e1
=
278 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e1")),
279 type_root
->id_string(),
280 SYNCED_NOTIFICATIONS
);
282 scoped_ptr
<sync_pb::SyncEntity
> e2
=
283 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e2")),
284 type_root
->id_string(),
285 SYNCED_NOTIFICATIONS
);
286 e2
->set_version(kDefaultVersion
+ 100);
288 // Add to the applicable updates list.
289 SyncEntityList updates
;
290 updates
.push_back(type_root
.get());
291 updates
.push_back(e1
.get());
292 updates
.push_back(e2
.get());
294 // Process and apply updates.
297 handler
.ProcessGetUpdatesResponse(progress
, context
, updates
, &status
));
298 handler
.ApplyUpdates(&status
);
300 // Verify none is deleted because they are unapplied during GC.
301 EXPECT_TRUE(EntryExists(type_root
->id_string()));
302 EXPECT_TRUE(EntryExists(e1
->id_string()));
303 EXPECT_TRUE(EntryExists(e2
->id_string()));
305 // Process and apply again. Old entry is deleted but not root.
306 progress
.mutable_gc_directive()->set_version_watermark(kDefaultVersion
+ 20);
308 handler
.ProcessGetUpdatesResponse(
309 progress
, context
, SyncEntityList(), &status
));
310 handler
.ApplyUpdates(&status
);
311 EXPECT_TRUE(EntryExists(type_root
->id_string()));
312 EXPECT_FALSE(EntryExists(e1
->id_string()));
313 EXPECT_TRUE(EntryExists(e2
->id_string()));
316 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
, ContextVersion
) {
317 DirectoryTypeDebugInfoEmitter
emitter(SYNCED_NOTIFICATIONS
, &type_observers_
);
318 DirectoryUpdateHandler
handler(dir(), SYNCED_NOTIFICATIONS
,
319 ui_worker(), &emitter
);
320 sessions::StatusController status
;
321 int field_number
= GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS
);
323 sync_pb::DataTypeProgressMarker progress
;
324 progress
.set_data_type_id(
325 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS
));
326 progress
.set_token("token");
328 sync_pb::DataTypeContext old_context
;
329 old_context
.set_version(1);
330 old_context
.set_context("data");
331 old_context
.set_data_type_id(field_number
);
333 scoped_ptr
<sync_pb::SyncEntity
> type_root
=
334 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("root")),
335 Id::GetRoot().GetServerId(), SYNCED_NOTIFICATIONS
);
336 type_root
->set_server_defined_unique_tag(
337 ModelTypeToRootTag(SYNCED_NOTIFICATIONS
));
338 type_root
->set_folder(true);
339 scoped_ptr
<sync_pb::SyncEntity
> e1
=
340 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e1")),
341 type_root
->id_string(),
342 SYNCED_NOTIFICATIONS
);
344 SyncEntityList updates
;
345 updates
.push_back(type_root
.get());
346 updates
.push_back(e1
.get());
348 // The first response should be processed fine.
350 handler
.ProcessGetUpdatesResponse(
351 progress
, old_context
, updates
, &status
));
352 handler
.ApplyUpdates(&status
);
354 EXPECT_TRUE(EntryExists(type_root
->id_string()));
355 EXPECT_TRUE(EntryExists(e1
->id_string()));
358 sync_pb::DataTypeContext dir_context
;
359 syncable::ReadTransaction
trans(FROM_HERE
, dir());
360 trans
.directory()->GetDataTypeContext(
361 &trans
, SYNCED_NOTIFICATIONS
, &dir_context
);
362 EXPECT_EQ(old_context
.SerializeAsString(), dir_context
.SerializeAsString());
365 sync_pb::DataTypeContext new_context
;
366 new_context
.set_version(0);
367 new_context
.set_context("old");
368 new_context
.set_data_type_id(field_number
);
370 scoped_ptr
<sync_pb::SyncEntity
> e2
=
371 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e2")),
372 type_root
->id_string(),
373 SYNCED_NOTIFICATIONS
);
375 updates
.push_back(e2
.get());
377 // The second response, with an old context version, should result in an
378 // error and the updates should be dropped.
379 EXPECT_EQ(DATATYPE_TRIGGERED_RETRY
,
380 handler
.ProcessGetUpdatesResponse(
381 progress
, new_context
, updates
, &status
));
382 handler
.ApplyUpdates(&status
);
384 EXPECT_FALSE(EntryExists(e2
->id_string()));
387 sync_pb::DataTypeContext dir_context
;
388 syncable::ReadTransaction
trans(FROM_HERE
, dir());
389 trans
.directory()->GetDataTypeContext(
390 &trans
, SYNCED_NOTIFICATIONS
, &dir_context
);
391 EXPECT_EQ(old_context
.SerializeAsString(), dir_context
.SerializeAsString());
395 // See that updates containing attachment metadata are applied
396 // (i.e. server_attachment_metadata is copied to attachment_metadata).
397 TEST_F(DirectoryUpdateHandlerProcessUpdateTest
,
398 ProcessAndApplyUpdatesWithAttachments
) {
399 DirectoryTypeDebugInfoEmitter
emitter(ARTICLES
, &type_observers_
);
400 DirectoryUpdateHandler
handler(dir(), ARTICLES
, ui_worker(), &emitter
);
401 sessions::StatusController status
;
403 sync_pb::DataTypeProgressMarker progress
;
404 progress
.set_data_type_id(GetSpecificsFieldNumberFromModelType(ARTICLES
));
405 progress
.set_token("token");
406 progress
.mutable_gc_directive()->set_version_watermark(kDefaultVersion
+ 10);
408 sync_pb::DataTypeContext context
;
409 context
.set_data_type_id(GetSpecificsFieldNumberFromModelType(ARTICLES
));
410 context
.set_context("context");
411 context
.set_version(1);
413 scoped_ptr
<sync_pb::SyncEntity
> type_root
=
414 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("root")),
415 Id::GetRoot().GetServerId(), ARTICLES
);
416 type_root
->set_server_defined_unique_tag(ModelTypeToRootTag(ARTICLES
));
417 type_root
->set_folder(true);
419 scoped_ptr
<sync_pb::SyncEntity
> e1
=
420 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e1")),
421 type_root
->id_string(),
423 sync_pb::AttachmentIdProto
* attachment_id
= e1
->add_attachment_id();
424 *attachment_id
= CreateAttachmentIdProto();
426 SyncEntityList updates
;
427 updates
.push_back(type_root
.get());
428 updates
.push_back(e1
.get());
430 // Process and apply updates.
433 handler
.ProcessGetUpdatesResponse(progress
, context
, updates
, &status
));
434 handler
.ApplyUpdates(&status
);
436 ASSERT_TRUE(EntryExists(type_root
->id_string()));
437 ASSERT_TRUE(EntryExists(e1
->id_string()));
439 syncable::ReadTransaction
trans(FROM_HERE
, dir());
440 syncable::Entry
e(&trans
,
442 Id::CreateFromServerId(e1
->id_string()));
444 // See that the attachment_metadata is correct.
445 sync_pb::AttachmentMetadata attachment_metadata
= e
.GetAttachmentMetadata();
446 ASSERT_EQ(1, attachment_metadata
.record_size());
447 ASSERT_EQ(attachment_id
->SerializeAsString(),
448 attachment_metadata
.record(0).id().SerializeAsString());
449 ASSERT_TRUE(attachment_metadata
.record(0).is_on_server());
451 // See that attachment_metadata and server_attachment_metadata are equal.
452 ASSERT_EQ(attachment_metadata
.SerializeAsString(),
453 e
.GetServerAttachmentMetadata().SerializeAsString());
457 // A test harness for tests that focus on applying updates.
459 // Update application is performed when we want to take updates that were
460 // previously downloaded, processed, and stored in our syncable::Directory
461 // and use them to update our local state (both the Directory's local state
462 // and the model's local state, though these tests focus only on the Directory's
465 // This is kept separate from the update processing test in part for historical
466 // reasons, and in part because these tests may require a bit more infrastrcture
467 // in the future. Update application should happen on a different thread a lot
468 // of the time so these tests may end up requiring more infrastructure than the
469 // update processing tests. Currently, we're bypassing most of those issues by
470 // using FakeModelWorkers, so there's not much difference between the two test
472 class DirectoryUpdateHandlerApplyUpdateTest
: public ::testing::Test
{
474 DirectoryUpdateHandlerApplyUpdateTest()
475 : ui_worker_(new FakeModelWorker(GROUP_UI
)),
476 password_worker_(new FakeModelWorker(GROUP_PASSWORD
)),
477 passive_worker_(new FakeModelWorker(GROUP_PASSIVE
)),
478 bookmarks_emitter_(BOOKMARKS
, &type_observers_
),
479 passwords_emitter_(PASSWORDS
, &type_observers_
),
480 articles_emitter_(ARTICLES
, &type_observers_
),
481 update_handler_map_deleter_(&update_handler_map_
) {}
483 void SetUp() override
{
485 entry_factory_
.reset(new TestEntryFactory(directory()));
487 update_handler_map_
.insert(std::make_pair(
489 new DirectoryUpdateHandler(directory(), BOOKMARKS
,
490 ui_worker_
, &bookmarks_emitter_
)));
491 update_handler_map_
.insert(std::make_pair(
493 new DirectoryUpdateHandler(directory(),
496 &passwords_emitter_
)));
497 update_handler_map_
.insert(std::make_pair(
499 new DirectoryUpdateHandler(
500 directory(), ARTICLES
, ui_worker_
, &articles_emitter_
)));
503 void TearDown() override
{ dir_maker_
.TearDown(); }
505 const UpdateCounters
& GetBookmarksUpdateCounters() {
506 return bookmarks_emitter_
.GetUpdateCounters();
509 const UpdateCounters
& GetPasswordsUpdateCounters() {
510 return passwords_emitter_
.GetUpdateCounters();
513 const UpdateCounters
& GetArticlesUpdateCounters() {
514 return articles_emitter_
.GetUpdateCounters();
518 void ApplyBookmarkUpdates(sessions::StatusController
* status
) {
519 update_handler_map_
[BOOKMARKS
]->ApplyUpdates(status
);
522 void ApplyPasswordUpdates(sessions::StatusController
* status
) {
523 update_handler_map_
[PASSWORDS
]->ApplyUpdates(status
);
526 void ApplyArticlesUpdates(sessions::StatusController
* status
) {
527 update_handler_map_
[ARTICLES
]->ApplyUpdates(status
);
530 TestEntryFactory
* entry_factory() {
531 return entry_factory_
.get();
534 syncable::Directory
* directory() {
535 return dir_maker_
.directory();
539 typedef std::map
<ModelType
, UpdateHandler
*> UpdateHandlerMap
;
541 base::MessageLoop loop_
; // Needed to initialize the directory.
542 TestDirectorySetterUpper dir_maker_
;
543 scoped_ptr
<TestEntryFactory
> entry_factory_
;
545 scoped_refptr
<FakeModelWorker
> ui_worker_
;
546 scoped_refptr
<FakeModelWorker
> password_worker_
;
547 scoped_refptr
<FakeModelWorker
> passive_worker_
;
549 ObserverList
<TypeDebugInfoObserver
> type_observers_
;
550 DirectoryTypeDebugInfoEmitter bookmarks_emitter_
;
551 DirectoryTypeDebugInfoEmitter passwords_emitter_
;
552 DirectoryTypeDebugInfoEmitter articles_emitter_
;
554 UpdateHandlerMap update_handler_map_
;
555 STLValueDeleter
<UpdateHandlerMap
> update_handler_map_deleter_
;
559 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
560 sync_pb::EntitySpecifics result
;
561 AddDefaultFieldValue(BOOKMARKS
, &result
);
566 // Test update application for a few bookmark items.
567 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, SimpleBookmark
) {
568 sessions::StatusController status
;
570 std::string root_server_id
= Id::GetRoot().GetServerId();
571 int64 parent_handle
=
572 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
573 "parent", DefaultBookmarkSpecifics(), root_server_id
);
575 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
576 "child", DefaultBookmarkSpecifics(), "parent");
578 ApplyBookmarkUpdates(&status
);
580 const UpdateCounters
& counter
= GetBookmarksUpdateCounters();
581 EXPECT_EQ(0, counter
.num_encryption_conflict_application_failures
)
582 << "Simple update shouldn't result in conflicts";
583 EXPECT_EQ(0, counter
.num_hierarchy_conflict_application_failures
)
584 << "Simple update shouldn't result in conflicts";
585 EXPECT_EQ(2, counter
.num_updates_applied
)
586 << "All items should have been successfully applied";
589 syncable::ReadTransaction
trans(FROM_HERE
, directory());
591 syncable::Entry
parent(&trans
, syncable::GET_BY_HANDLE
, parent_handle
);
592 syncable::Entry
child(&trans
, syncable::GET_BY_HANDLE
, child_handle
);
594 ASSERT_TRUE(parent
.good());
595 ASSERT_TRUE(child
.good());
597 EXPECT_FALSE(parent
.GetIsUnsynced());
598 EXPECT_FALSE(parent
.GetIsUnappliedUpdate());
599 EXPECT_FALSE(child
.GetIsUnsynced());
600 EXPECT_FALSE(child
.GetIsUnappliedUpdate());
604 // Test that the applicator can handle updates delivered out of order.
605 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
606 BookmarkChildrenBeforeParent
) {
607 // Start with some bookmarks whose parents are unknown.
608 std::string root_server_id
= Id::GetRoot().GetServerId();
609 int64 a_handle
= entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
610 "a_child_created_first", DefaultBookmarkSpecifics(), "parent");
611 int64 x_handle
= entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
612 "x_child_created_first", DefaultBookmarkSpecifics(), "parent");
614 // Update application will fail.
615 sessions::StatusController status1
;
616 ApplyBookmarkUpdates(&status1
);
617 EXPECT_EQ(0, status1
.num_updates_applied());
618 EXPECT_EQ(2, status1
.num_hierarchy_conflicts());
621 syncable::ReadTransaction
trans(FROM_HERE
, directory());
623 syncable::Entry
a(&trans
, syncable::GET_BY_HANDLE
, a_handle
);
624 syncable::Entry
x(&trans
, syncable::GET_BY_HANDLE
, x_handle
);
626 ASSERT_TRUE(a
.good());
627 ASSERT_TRUE(x
.good());
629 EXPECT_TRUE(a
.GetIsUnappliedUpdate());
630 EXPECT_TRUE(x
.GetIsUnappliedUpdate());
633 // Now add their parent and a few siblings.
634 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
635 "parent", DefaultBookmarkSpecifics(), root_server_id
);
636 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
637 "a_child_created_second", DefaultBookmarkSpecifics(), "parent");
638 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
639 "x_child_created_second", DefaultBookmarkSpecifics(), "parent");
641 // Update application will succeed.
642 sessions::StatusController status2
;
643 ApplyBookmarkUpdates(&status2
);
644 EXPECT_EQ(5, status2
.num_updates_applied())
645 << "All updates should have been successfully applied";
648 syncable::ReadTransaction
trans(FROM_HERE
, directory());
650 syncable::Entry
a(&trans
, syncable::GET_BY_HANDLE
, a_handle
);
651 syncable::Entry
x(&trans
, syncable::GET_BY_HANDLE
, x_handle
);
653 ASSERT_TRUE(a
.good());
654 ASSERT_TRUE(x
.good());
656 EXPECT_FALSE(a
.GetIsUnappliedUpdate());
657 EXPECT_FALSE(x
.GetIsUnappliedUpdate());
661 // Try to apply changes on an item that is both IS_UNSYNCED and
662 // IS_UNAPPLIED_UPDATE. Conflict resolution should be performed.
663 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, SimpleBookmarkConflict
) {
664 int64 handle
= entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem("x");
666 int original_server_version
= -10;
668 syncable::ReadTransaction
trans(FROM_HERE
, directory());
669 syncable::Entry
e(&trans
, syncable::GET_BY_HANDLE
, handle
);
670 original_server_version
= e
.GetServerVersion();
671 ASSERT_NE(original_server_version
, e
.GetBaseVersion());
672 EXPECT_TRUE(e
.GetIsUnsynced());
675 sessions::StatusController status
;
676 ApplyBookmarkUpdates(&status
);
678 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
679 EXPECT_EQ(1, counters
.num_server_overwrites
)
680 << "Unsynced and unapplied item conflict should be resolved";
681 EXPECT_EQ(0, counters
.num_updates_applied
)
682 << "Update should not be applied; we should override the server.";
685 syncable::ReadTransaction
trans(FROM_HERE
, directory());
686 syncable::Entry
e(&trans
, syncable::GET_BY_HANDLE
, handle
);
687 ASSERT_TRUE(e
.good());
688 EXPECT_EQ(original_server_version
, e
.GetServerVersion());
689 EXPECT_EQ(original_server_version
, e
.GetBaseVersion());
690 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
692 // The unsynced flag will remain set until we successfully commit the item.
693 EXPECT_TRUE(e
.GetIsUnsynced());
697 // Create a simple conflict that is also a hierarchy conflict. If we were to
698 // follow the normal "server wins" logic, we'd end up violating hierarchy
699 // constraints. The hierarchy conflict must take precedence. We can not allow
700 // the update to be applied. The item must remain in the conflict state.
701 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, HierarchyAndSimpleConflict
) {
702 // Create a simply-conflicting item. It will start with valid parent ids.
703 int64 handle
= entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem(
704 "orphaned_by_server");
706 // Manually set the SERVER_PARENT_ID to bad value.
707 // A bad parent indicates a hierarchy conflict.
708 syncable::WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
709 syncable::MutableEntry
entry(&trans
, syncable::GET_BY_HANDLE
, handle
);
710 ASSERT_TRUE(entry
.good());
712 entry
.PutServerParentId(TestIdFactory::MakeServer("bogus_parent"));
715 sessions::StatusController status
;
716 ApplyBookmarkUpdates(&status
);
718 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
719 EXPECT_EQ(0, counters
.num_updates_applied
);
720 EXPECT_EQ(0, counters
.num_server_overwrites
);
721 EXPECT_EQ(1, counters
.num_hierarchy_conflict_application_failures
);
724 syncable::ReadTransaction
trans(FROM_HERE
, directory());
725 syncable::Entry
e(&trans
, syncable::GET_BY_HANDLE
, handle
);
726 ASSERT_TRUE(e
.good());
727 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
728 EXPECT_TRUE(e
.GetIsUnsynced());
732 // Attempt to apply an udpate that would create a bookmark folder loop. This
733 // application should fail.
734 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, BookmarkFolderLoop
) {
735 // Item 'X' locally has parent of 'root'. Server is updating it to have
738 // Create it as a child of root node.
739 int64 handle
= entry_factory()->CreateSyncedItem("X", BOOKMARKS
, true);
742 syncable::WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
743 syncable::MutableEntry
entry(&trans
, syncable::GET_BY_HANDLE
, handle
);
744 ASSERT_TRUE(entry
.good());
746 // Re-parent from root to "Y"
747 entry
.PutServerVersion(entry_factory()->GetNextRevision());
748 entry
.PutIsUnappliedUpdate(true);
749 entry
.PutServerParentId(TestIdFactory::MakeServer("Y"));
752 // Item 'Y' is child of 'X'.
753 entry_factory()->CreateUnsyncedItem(
754 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
757 // If the server's update were applied, we would have X be a child of Y, and Y
758 // as a child of X. That's a directory loop. The UpdateApplicator should
759 // prevent the update from being applied and note that this is a hierarchy
762 sessions::StatusController status
;
763 ApplyBookmarkUpdates(&status
);
765 // This should count as a hierarchy conflict.
766 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
767 EXPECT_EQ(1, counters
.num_hierarchy_conflict_application_failures
);
770 syncable::ReadTransaction
trans(FROM_HERE
, directory());
771 syncable::Entry
e(&trans
, syncable::GET_BY_HANDLE
, handle
);
772 ASSERT_TRUE(e
.good());
773 EXPECT_TRUE(e
.GetIsUnappliedUpdate());
774 EXPECT_FALSE(e
.GetIsUnsynced());
778 // Test update application where the update has been orphaned by a local folder
779 // deletion. The update application attempt should fail.
780 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
781 HierarchyConflictDeletedParent
) {
782 // Create a locally deleted parent item.
784 entry_factory()->CreateUnsyncedItem(
785 Id::CreateFromServerId("parent"), TestIdFactory::root(),
786 "parent", true, BOOKMARKS
, &parent_handle
);
788 syncable::WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
789 syncable::MutableEntry
entry(&trans
,
790 syncable::GET_BY_HANDLE
,
792 entry
.PutIsDel(true);
795 // Create an incoming child from the server.
796 int64 child_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
797 "child", DefaultBookmarkSpecifics(), "parent");
799 // The server's update may seem valid to some other client, but on this client
800 // that new item's parent no longer exists. The update should not be applied
801 // and the update applicator should indicate this is a hierarchy conflict.
803 sessions::StatusController status
;
804 ApplyBookmarkUpdates(&status
);
805 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
806 EXPECT_EQ(1, counters
.num_hierarchy_conflict_application_failures
);
809 syncable::ReadTransaction
trans(FROM_HERE
, directory());
810 syncable::Entry
child(&trans
, syncable::GET_BY_HANDLE
, child_handle
);
811 ASSERT_TRUE(child
.good());
812 EXPECT_TRUE(child
.GetIsUnappliedUpdate());
813 EXPECT_FALSE(child
.GetIsUnsynced());
817 // Attempt to apply an update that deletes a folder where the folder has
818 // locally-created children. The update application should fail.
819 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
820 HierarchyConflictDeleteNonEmptyDirectory
) {
821 // Create a server-deleted folder as a child of root node.
822 int64 parent_handle
=
823 entry_factory()->CreateSyncedItem("parent", BOOKMARKS
, true);
825 syncable::WriteTransaction
trans(FROM_HERE
, UNITTEST
, directory());
826 syncable::MutableEntry
entry(&trans
,
827 syncable::GET_BY_HANDLE
,
829 ASSERT_TRUE(entry
.good());
831 // Delete it on the server.
832 entry
.PutServerVersion(entry_factory()->GetNextRevision());
833 entry
.PutIsUnappliedUpdate(true);
834 entry
.PutServerParentId(TestIdFactory::root());
835 entry
.PutServerIsDel(true);
838 // Create a local child of the server-deleted directory.
839 entry_factory()->CreateUnsyncedItem(
840 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
841 "child", false, BOOKMARKS
, NULL
);
843 // The server's request to delete the directory must be ignored, otherwise our
844 // unsynced new child would be orphaned. This is a hierarchy conflict.
846 sessions::StatusController status
;
847 ApplyBookmarkUpdates(&status
);
849 // This should count as a hierarchy conflict.
850 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
851 EXPECT_EQ(1, counters
.num_hierarchy_conflict_application_failures
);
854 syncable::ReadTransaction
trans(FROM_HERE
, directory());
855 syncable::Entry
parent(&trans
, syncable::GET_BY_HANDLE
, parent_handle
);
856 ASSERT_TRUE(parent
.good());
857 EXPECT_TRUE(parent
.GetIsUnappliedUpdate());
858 EXPECT_FALSE(parent
.GetIsUnsynced());
862 // Attempt to apply updates where the updated item's parent is not known to this
863 // client. The update application attempt should fail.
864 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
865 HierarchyConflictUnknownParent
) {
866 // We shouldn't be able to do anything with either of these items.
867 int64 x_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
868 "some_item", DefaultBookmarkSpecifics(), "unknown_parent");
869 int64 y_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
870 "some_other_item", DefaultBookmarkSpecifics(), "some_item");
872 sessions::StatusController status
;
873 ApplyBookmarkUpdates(&status
);
875 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
876 EXPECT_EQ(2, counters
.num_hierarchy_conflict_application_failures
)
877 << "All updates with an unknown ancestors should be in conflict";
878 EXPECT_EQ(0, counters
.num_updates_applied
)
879 << "No item with an unknown ancestor should be applied";
882 syncable::ReadTransaction
trans(FROM_HERE
, directory());
883 syncable::Entry
x(&trans
, syncable::GET_BY_HANDLE
, x_handle
);
884 syncable::Entry
y(&trans
, syncable::GET_BY_HANDLE
, y_handle
);
885 ASSERT_TRUE(x
.good());
886 ASSERT_TRUE(y
.good());
887 EXPECT_TRUE(x
.GetIsUnappliedUpdate());
888 EXPECT_TRUE(y
.GetIsUnappliedUpdate());
889 EXPECT_FALSE(x
.GetIsUnsynced());
890 EXPECT_FALSE(y
.GetIsUnsynced());
894 // Attempt application of a mix of items. Some update application attempts will
895 // fail due to hierarchy conflicts. Others should succeed.
896 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, ItemsBothKnownAndUnknown
) {
897 // See what happens when there's a mixture of good and bad updates.
898 std::string root_server_id
= Id::GetRoot().GetServerId();
899 int64 u1_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
900 "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
901 int64 k1_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
902 "first_known_item", DefaultBookmarkSpecifics(), root_server_id
);
903 int64 u2_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
904 "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
905 int64 k2_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
906 "second_known_item", DefaultBookmarkSpecifics(), "first_known_item");
907 int64 k3_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
908 "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item");
909 int64 k4_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
910 "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id
);
912 sessions::StatusController status
;
913 ApplyBookmarkUpdates(&status
);
915 const UpdateCounters
& counters
= GetBookmarksUpdateCounters();
916 EXPECT_EQ(2, counters
.num_hierarchy_conflict_application_failures
)
917 << "The updates with unknown ancestors should be in conflict";
918 EXPECT_EQ(4, counters
.num_updates_applied
)
919 << "The updates with known ancestors should be successfully applied";
922 syncable::ReadTransaction
trans(FROM_HERE
, directory());
923 syncable::Entry
u1(&trans
, syncable::GET_BY_HANDLE
, u1_handle
);
924 syncable::Entry
u2(&trans
, syncable::GET_BY_HANDLE
, u2_handle
);
925 syncable::Entry
k1(&trans
, syncable::GET_BY_HANDLE
, k1_handle
);
926 syncable::Entry
k2(&trans
, syncable::GET_BY_HANDLE
, k2_handle
);
927 syncable::Entry
k3(&trans
, syncable::GET_BY_HANDLE
, k3_handle
);
928 syncable::Entry
k4(&trans
, syncable::GET_BY_HANDLE
, k4_handle
);
929 ASSERT_TRUE(u1
.good());
930 ASSERT_TRUE(u2
.good());
931 ASSERT_TRUE(k1
.good());
932 ASSERT_TRUE(k2
.good());
933 ASSERT_TRUE(k3
.good());
934 ASSERT_TRUE(k4
.good());
935 EXPECT_TRUE(u1
.GetIsUnappliedUpdate());
936 EXPECT_TRUE(u2
.GetIsUnappliedUpdate());
937 EXPECT_FALSE(k1
.GetIsUnappliedUpdate());
938 EXPECT_FALSE(k2
.GetIsUnappliedUpdate());
939 EXPECT_FALSE(k3
.GetIsUnappliedUpdate());
940 EXPECT_FALSE(k4
.GetIsUnappliedUpdate());
944 // Attempt application of password upates where the passphrase is known.
945 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, DecryptablePassword
) {
946 // Decryptable password updates should be applied.
947 Cryptographer
* cryptographer
;
949 // Storing the cryptographer separately is bad, but for this test we
951 syncable::ReadTransaction
trans(FROM_HERE
, directory());
952 cryptographer
= directory()->GetCryptographer(&trans
);
955 KeyParams params
= {"localhost", "dummy", "foobar"};
956 cryptographer
->AddKey(params
);
958 sync_pb::EntitySpecifics specifics
;
959 sync_pb::PasswordSpecificsData data
;
960 data
.set_origin("http://example.com");
962 cryptographer
->Encrypt(data
,
963 specifics
.mutable_password()->mutable_encrypted());
965 entry_factory()->CreateUnappliedNewItem("item", specifics
, false);
967 sessions::StatusController status
;
968 ApplyPasswordUpdates(&status
);
970 const UpdateCounters
& counters
= GetPasswordsUpdateCounters();
971 EXPECT_EQ(1, counters
.num_updates_applied
)
972 << "The updates that can be decrypted should be applied";
975 syncable::ReadTransaction
trans(FROM_HERE
, directory());
976 syncable::Entry
e(&trans
, syncable::GET_BY_HANDLE
, handle
);
977 ASSERT_TRUE(e
.good());
978 EXPECT_FALSE(e
.GetIsUnappliedUpdate());
979 EXPECT_FALSE(e
.GetIsUnsynced());
983 // Attempt application of encrypted items when the passphrase is not known.
984 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, UndecryptableData
) {
985 // Undecryptable updates should not be applied.
986 sync_pb::EntitySpecifics encrypted_bookmark
;
987 encrypted_bookmark
.mutable_encrypted();
988 AddDefaultFieldValue(BOOKMARKS
, &encrypted_bookmark
);
989 std::string root_server_id
= Id::GetRoot().GetServerId();
990 int64 folder_handle
= entry_factory()->CreateUnappliedNewItemWithParent(
994 int64 bookmark_handle
= entry_factory()->CreateUnappliedNewItem(
998 sync_pb::EntitySpecifics encrypted_password
;
999 encrypted_password
.mutable_password();
1000 int64 password_handle
= entry_factory()->CreateUnappliedNewItem(
1005 sessions::StatusController status
;
1006 ApplyBookmarkUpdates(&status
);
1007 ApplyPasswordUpdates(&status
);
1009 const UpdateCounters
& bm_counters
= GetBookmarksUpdateCounters();
1010 EXPECT_EQ(2, bm_counters
.num_encryption_conflict_application_failures
)
1011 << "Updates that can't be decrypted should be in encryption conflict";
1012 EXPECT_EQ(0, bm_counters
.num_updates_applied
)
1013 << "No update that can't be decrypted should be applied";
1015 const UpdateCounters
& pw_counters
= GetPasswordsUpdateCounters();
1016 EXPECT_EQ(1, pw_counters
.num_encryption_conflict_application_failures
)
1017 << "Updates that can't be decrypted should be in encryption conflict";
1018 EXPECT_EQ(0, pw_counters
.num_updates_applied
)
1019 << "No update that can't be decrypted should be applied";
1022 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1023 syncable::Entry
folder(&trans
, syncable::GET_BY_HANDLE
, folder_handle
);
1024 syncable::Entry
bm(&trans
, syncable::GET_BY_HANDLE
, bookmark_handle
);
1025 syncable::Entry
pw(&trans
, syncable::GET_BY_HANDLE
, password_handle
);
1026 ASSERT_TRUE(folder
.good());
1027 ASSERT_TRUE(bm
.good());
1028 ASSERT_TRUE(pw
.good());
1029 EXPECT_TRUE(folder
.GetIsUnappliedUpdate());
1030 EXPECT_TRUE(bm
.GetIsUnappliedUpdate());
1031 EXPECT_TRUE(pw
.GetIsUnappliedUpdate());
1035 // Test a mix of decryptable and undecryptable updates.
1036 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
, SomeUndecryptablePassword
) {
1037 Cryptographer
* cryptographer
;
1039 int64 decryptable_handle
= -1;
1040 int64 undecryptable_handle
= -1;
1042 // Only decryptable password updates should be applied.
1044 sync_pb::EntitySpecifics specifics
;
1045 sync_pb::PasswordSpecificsData data
;
1046 data
.set_origin("http://example.com/1");
1048 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1049 cryptographer
= directory()->GetCryptographer(&trans
);
1051 KeyParams params
= {"localhost", "dummy", "foobar"};
1052 cryptographer
->AddKey(params
);
1054 cryptographer
->Encrypt(data
,
1055 specifics
.mutable_password()->mutable_encrypted());
1057 decryptable_handle
=
1058 entry_factory()->CreateUnappliedNewItem("item1", specifics
, false);
1061 // Create a new cryptographer, independent of the one in the session.
1062 Cryptographer
other_cryptographer(cryptographer
->encryptor());
1063 KeyParams params
= {"localhost", "dummy", "bazqux"};
1064 other_cryptographer
.AddKey(params
);
1066 sync_pb::EntitySpecifics specifics
;
1067 sync_pb::PasswordSpecificsData data
;
1068 data
.set_origin("http://example.com/2");
1070 other_cryptographer
.Encrypt(data
,
1071 specifics
.mutable_password()->mutable_encrypted());
1072 undecryptable_handle
=
1073 entry_factory()->CreateUnappliedNewItem("item2", specifics
, false);
1076 sessions::StatusController status
;
1077 ApplyPasswordUpdates(&status
);
1079 const UpdateCounters
& counters
= GetPasswordsUpdateCounters();
1080 EXPECT_EQ(1, counters
.num_encryption_conflict_application_failures
)
1081 << "The updates that can't be decrypted should be in encryption "
1083 EXPECT_EQ(1, counters
.num_updates_applied
)
1084 << "The undecryptable password update shouldn't be applied";
1087 syncable::ReadTransaction
trans(FROM_HERE
, directory());
1088 syncable::Entry
e1(&trans
, syncable::GET_BY_HANDLE
, decryptable_handle
);
1089 syncable::Entry
e2(&trans
, syncable::GET_BY_HANDLE
, undecryptable_handle
);
1090 ASSERT_TRUE(e1
.good());
1091 ASSERT_TRUE(e2
.good());
1092 EXPECT_FALSE(e1
.GetIsUnappliedUpdate());
1093 EXPECT_TRUE(e2
.GetIsUnappliedUpdate());
1097 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
1098 SimpleConflictDifferentAttachmentMetadata
) {
1099 const bool is_folder
= false;
1100 sync_pb::EntitySpecifics specifics
;
1101 *specifics
.mutable_article() = sync_pb::ArticleSpecifics();
1102 int64 handle
= entry_factory()->CreateSyncedItem("art1", ARTICLES
, is_folder
);
1104 sync_pb::AttachmentIdProto local_attachment_id
= CreateAttachmentIdProto();
1105 sync_pb::AttachmentIdProto server_attachment_id
= CreateAttachmentIdProto();
1107 // Add an attachment to the local attachment metadata.
1108 sync_pb::AttachmentMetadata local_metadata
;
1109 sync_pb::AttachmentMetadataRecord
* local_record
= local_metadata
.add_record();
1110 *local_record
->mutable_id() = local_attachment_id
;
1111 local_record
->set_is_on_server(true);
1112 entry_factory()->SetLocalAttachmentMetadataForItem(handle
, local_metadata
);
1114 // Add a different attachment to the server attachment metadata.
1115 sync_pb::AttachmentMetadata server_metadata
;
1116 sync_pb::AttachmentMetadataRecord
* server_record
=
1117 server_metadata
.add_record();
1118 *server_record
->mutable_id() = server_attachment_id
;
1119 server_record
->set_is_on_server(true);
1120 entry_factory()->SetServerAttachmentMetadataForItem(handle
, server_metadata
);
1122 // At this point we have a simple conflict. The server says art1 should have
1123 // server_attachment_id, but the local sync engine says it should have
1124 // local_attachment_id.
1126 sessions::StatusController status
;
1127 ApplyArticlesUpdates(&status
);
1129 // See that the server won.
1130 const UpdateCounters
& counters
= GetArticlesUpdateCounters();
1131 EXPECT_EQ(1, counters
.num_updates_applied
);
1132 EXPECT_EQ(1, counters
.num_local_overwrites
);
1133 EXPECT_EQ(0, counters
.num_server_overwrites
);
1134 local_metadata
= entry_factory()->GetLocalAttachmentMetadataForItem(handle
);
1135 EXPECT_EQ(server_metadata
.SerializeAsString(),
1136 local_metadata
.SerializeAsString());
1139 TEST_F(DirectoryUpdateHandlerApplyUpdateTest
,
1140 SimpleConflictSameAttachmentMetadataDifferentOrder
) {
1141 const bool is_folder
= false;
1142 sync_pb::EntitySpecifics specifics
;
1143 *specifics
.mutable_article() = sync_pb::ArticleSpecifics();
1144 int64 handle
= entry_factory()->CreateSyncedItem("art1", ARTICLES
, is_folder
);
1146 sync_pb::AttachmentIdProto id1
= CreateAttachmentIdProto();
1147 sync_pb::AttachmentIdProto id2
= CreateAttachmentIdProto();
1149 // Add id1, then id2 to the local attachment metadata.
1150 sync_pb::AttachmentMetadata local_metadata
;
1151 sync_pb::AttachmentMetadataRecord
* record
= local_metadata
.add_record();
1152 *record
->mutable_id() = id1
;
1153 record
->set_is_on_server(true);
1154 record
= local_metadata
.add_record();
1155 *record
->mutable_id() = id2
;
1156 record
->set_is_on_server(true);
1157 entry_factory()->SetLocalAttachmentMetadataForItem(handle
, local_metadata
);
1159 // Add id1 and id2 to the server attachment metadata, but in reverse order.
1160 sync_pb::AttachmentMetadata server_metadata
;
1161 record
= server_metadata
.add_record();
1162 *record
->mutable_id() = id2
;
1163 record
->set_is_on_server(true);
1164 record
= local_metadata
.add_record();
1165 *record
->mutable_id() = id1
;
1166 record
->set_is_on_server(true);
1167 entry_factory()->SetServerAttachmentMetadataForItem(handle
, server_metadata
);
1169 // At this point we have a (false) conflict.
1171 sessions::StatusController status
;
1172 ApplyArticlesUpdates(&status
);
1174 // See that the server won.
1175 const UpdateCounters
& counters
= GetArticlesUpdateCounters();
1176 EXPECT_EQ(1, counters
.num_updates_applied
);
1177 EXPECT_EQ(1, counters
.num_local_overwrites
);
1178 EXPECT_EQ(0, counters
.num_server_overwrites
);
1179 local_metadata
= entry_factory()->GetLocalAttachmentMetadataForItem(handle
);
1180 EXPECT_EQ(server_metadata
.SerializeAsString(),
1181 local_metadata
.SerializeAsString());
1184 } // namespace syncer