Delete old ScreenOrientationDelegte
[chromium-blink-merge.git] / sync / engine / directory_update_handler_unittest.cc
blob10eead9aff5abb92e911e4252799eb73f6ff7720
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"
30 namespace syncer {
32 using syncable::Id;
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 {
43 public:
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();
58 protected:
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() {
77 return 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();
87 protected:
88 // Used in the construction of DirectoryTypeDebugInfoEmitters.
89 ObserverList<TypeDebugInfoObserver> type_observers_;
91 private:
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);
106 e->set_name(id);
107 e->set_version(kDefaultVersion);
108 AddDefaultFieldValue(type, e->mutable_specifics());
109 return e.Pass();
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");
180 e->set_folder(true);
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));
188 // Process it.
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));
221 // Process it.
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.
295 EXPECT_EQ(
296 SYNCER_OK,
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);
307 EXPECT_EQ(SYNCER_OK,
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.
349 EXPECT_EQ(SYNCER_OK,
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);
374 updates.clear();
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(),
422 ARTICLES);
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.
431 EXPECT_EQ(
432 SYNCER_OK,
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,
441 syncable::GET_BY_ID,
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
463 // local state).
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
471 // harnesses.
472 class DirectoryUpdateHandlerApplyUpdateTest : public ::testing::Test {
473 public:
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 {
484 dir_maker_.SetUp();
485 entry_factory_.reset(new TestEntryFactory(directory()));
487 update_handler_map_.insert(std::make_pair(
488 BOOKMARKS,
489 new DirectoryUpdateHandler(directory(), BOOKMARKS,
490 ui_worker_, &bookmarks_emitter_)));
491 update_handler_map_.insert(std::make_pair(
492 PASSWORDS,
493 new DirectoryUpdateHandler(directory(),
494 PASSWORDS,
495 password_worker_,
496 &passwords_emitter_)));
497 update_handler_map_.insert(std::make_pair(
498 ARTICLES,
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();
517 protected:
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();
538 private:
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_;
558 namespace {
559 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
560 sync_pb::EntitySpecifics result;
561 AddDefaultFieldValue(BOOKMARKS, &result);
562 return result;
564 } // namespace
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);
574 int64 child_handle =
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
736 // parent of 'Y'.
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,
755 BOOKMARKS, NULL);
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
760 // conflict.
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.
783 int64 parent_handle;
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,
791 parent_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,
828 parent_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
950 // know it's safe.
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());
964 int64 handle =
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(
991 "folder",
992 encrypted_bookmark,
993 root_server_id);
994 int64 bookmark_handle = entry_factory()->CreateUnappliedNewItem(
995 "item2",
996 encrypted_bookmark,
997 false);
998 sync_pb::EntitySpecifics encrypted_password;
999 encrypted_password.mutable_password();
1000 int64 password_handle = entry_factory()->CreateUnappliedNewItem(
1001 "item3",
1002 encrypted_password,
1003 false);
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 "
1082 << "conflict";
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