Windows: Ambient Light API
[chromium-blink-merge.git] / sync / engine / directory_update_handler_unittest.cc
blob46e286015f5bd1af7b8f244b71d9c701a6ace4f5
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 bool TypeRootExists(ModelType model_type) {
88 syncable::ReadTransaction trans(FROM_HERE, dir());
89 syncable::Entry e(&trans, syncable::GET_TYPE_ROOT, model_type);
90 return e.good() && !e.GetIsDel();
93 protected:
94 // Used in the construction of DirectoryTypeDebugInfoEmitters.
95 ObserverList<TypeDebugInfoObserver> type_observers_;
97 private:
98 base::MessageLoop loop_; // Needed to initialize the directory.
99 TestDirectorySetterUpper dir_maker_;
100 scoped_refptr<FakeModelWorker> ui_worker_;
103 scoped_ptr<sync_pb::SyncEntity>
104 DirectoryUpdateHandlerProcessUpdateTest::CreateUpdate(
105 const std::string& id,
106 const std::string& parent,
107 const ModelType& type) {
108 scoped_ptr<sync_pb::SyncEntity> e(new sync_pb::SyncEntity());
109 e->set_id_string(id);
110 e->set_parent_id_string(parent);
111 e->set_non_unique_name(id);
112 e->set_name(id);
113 e->set_version(kDefaultVersion);
114 AddDefaultFieldValue(type, e->mutable_specifics());
115 return e.Pass();
118 void DirectoryUpdateHandlerProcessUpdateTest::UpdateSyncEntities(
119 DirectoryUpdateHandler* handler,
120 const SyncEntityList& applicable_updates,
121 sessions::StatusController* status) {
122 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, UNITTEST, dir());
123 handler->UpdateSyncEntities(&trans, applicable_updates, status);
126 void DirectoryUpdateHandlerProcessUpdateTest::UpdateProgressMarkers(
127 DirectoryUpdateHandler* handler,
128 const sync_pb::DataTypeProgressMarker& progress) {
129 handler->UpdateProgressMarker(progress);
132 static const char kCacheGuid[] = "IrcjZ2jyzHDV9Io4+zKcXQ==";
134 // Test that the bookmark tag is set on newly downloaded items.
135 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, NewBookmarkTag) {
136 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
137 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
138 sync_pb::GetUpdatesResponse gu_response;
139 sessions::StatusController status;
141 // Add a bookmark item to the update message.
142 std::string root = Id::GetRoot().GetServerId();
143 Id server_id = Id::CreateFromServerId("b1");
144 scoped_ptr<sync_pb::SyncEntity> e =
145 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
146 e->set_originator_cache_guid(
147 std::string(kCacheGuid, arraysize(kCacheGuid)-1));
148 Id client_id = Id::CreateFromClientString("-2");
149 e->set_originator_client_item_id(client_id.GetServerId());
150 e->set_position_in_parent(0);
152 // Add it to the applicable updates list.
153 SyncEntityList bookmark_updates;
154 bookmark_updates.push_back(e.get());
156 // Process the update.
157 UpdateSyncEntities(&handler, bookmark_updates, &status);
159 syncable::ReadTransaction trans(FROM_HERE, dir());
160 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
161 ASSERT_TRUE(entry.good());
162 EXPECT_TRUE(UniquePosition::IsValidSuffix(entry.GetUniqueBookmarkTag()));
163 EXPECT_TRUE(entry.GetServerUniquePosition().IsValid());
165 // If this assertion fails, that might indicate that the algorithm used to
166 // generate bookmark tags has been modified. This could have implications for
167 // bookmark ordering. Please make sure you know what you're doing if you
168 // intend to make such a change.
169 EXPECT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", entry.GetUniqueBookmarkTag());
172 // Test the receipt of a type root node.
173 TEST_F(DirectoryUpdateHandlerProcessUpdateTest,
174 ReceiveServerCreatedBookmarkFolders) {
175 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
176 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
177 sync_pb::GetUpdatesResponse gu_response;
178 sessions::StatusController status;
180 // Create an update that mimics the bookmark root.
181 Id server_id = Id::CreateFromServerId("xyz");
182 std::string root = Id::GetRoot().GetServerId();
183 scoped_ptr<sync_pb::SyncEntity> e =
184 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
185 e->set_server_defined_unique_tag("google_chrome_bookmarks");
186 e->set_folder(true);
188 // Add it to the applicable updates list.
189 SyncEntityList bookmark_updates;
190 bookmark_updates.push_back(e.get());
192 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
194 // Process it.
195 UpdateSyncEntities(&handler, bookmark_updates, &status);
197 // Verify the results.
198 syncable::ReadTransaction trans(FROM_HERE, dir());
199 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
200 ASSERT_TRUE(entry.good());
202 EXPECT_FALSE(entry.ShouldMaintainPosition());
203 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
204 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
205 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
208 // Test the receipt of a non-bookmark item.
209 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ReceiveNonBookmarkItem) {
210 DirectoryTypeDebugInfoEmitter emitter(AUTOFILL, &type_observers_);
211 DirectoryUpdateHandler handler(dir(), AUTOFILL, ui_worker(), &emitter);
212 sync_pb::GetUpdatesResponse gu_response;
213 sessions::StatusController status;
215 std::string root = Id::GetRoot().GetServerId();
216 Id server_id = Id::CreateFromServerId("xyz");
217 scoped_ptr<sync_pb::SyncEntity> e =
218 CreateUpdate(SyncableIdToProto(server_id), root, AUTOFILL);
219 e->set_server_defined_unique_tag("9PGRuKdX5sHyGMB17CvYTXuC43I=");
221 // Add it to the applicable updates list.
222 SyncEntityList autofill_updates;
223 autofill_updates.push_back(e.get());
225 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
227 // Process it.
228 UpdateSyncEntities(&handler, autofill_updates, &status);
230 syncable::ReadTransaction trans(FROM_HERE, dir());
231 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
232 ASSERT_TRUE(entry.good());
234 EXPECT_FALSE(entry.ShouldMaintainPosition());
235 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
236 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
237 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
240 // Tests the setting of progress markers.
241 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ProcessNewProgressMarkers) {
242 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
243 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
245 sync_pb::DataTypeProgressMarker progress;
246 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS));
247 progress.set_token("token");
249 UpdateProgressMarkers(&handler, progress);
251 sync_pb::DataTypeProgressMarker saved;
252 dir()->GetDownloadProgress(BOOKMARKS, &saved);
254 EXPECT_EQ(progress.token(), saved.token());
255 EXPECT_EQ(progress.data_type_id(), saved.data_type_id());
258 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, GarbageCollectionByVersion) {
259 DirectoryTypeDebugInfoEmitter emitter(SYNCED_NOTIFICATIONS, &type_observers_);
260 DirectoryUpdateHandler handler(dir(), SYNCED_NOTIFICATIONS,
261 ui_worker(), &emitter);
262 sessions::StatusController status;
264 sync_pb::DataTypeProgressMarker progress;
265 progress.set_data_type_id(
266 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
267 progress.set_token("token");
268 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 10);
270 sync_pb::DataTypeContext context;
271 context.set_data_type_id(
272 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
273 context.set_context("context");
274 context.set_version(1);
276 scoped_ptr<sync_pb::SyncEntity> e1 =
277 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e1")), "",
278 SYNCED_NOTIFICATIONS);
280 scoped_ptr<sync_pb::SyncEntity> e2 =
281 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e2")), "",
282 SYNCED_NOTIFICATIONS);
283 e2->set_version(kDefaultVersion + 100);
285 // Add to the applicable updates list.
286 SyncEntityList updates;
287 updates.push_back(e1.get());
288 updates.push_back(e2.get());
290 // Process and apply updates.
291 EXPECT_EQ(
292 SYNCER_OK,
293 handler.ProcessGetUpdatesResponse(progress, context, updates, &status));
294 handler.ApplyUpdates(&status);
296 // Verify none is deleted because they are unapplied during GC.
297 EXPECT_TRUE(TypeRootExists(SYNCED_NOTIFICATIONS));
298 EXPECT_TRUE(EntryExists(e1->id_string()));
299 EXPECT_TRUE(EntryExists(e2->id_string()));
301 // Process and apply again. Old entry is deleted but not root.
302 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 20);
303 EXPECT_EQ(SYNCER_OK,
304 handler.ProcessGetUpdatesResponse(
305 progress, context, SyncEntityList(), &status));
306 handler.ApplyUpdates(&status);
307 EXPECT_FALSE(EntryExists(e1->id_string()));
308 EXPECT_TRUE(EntryExists(e2->id_string()));
311 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ContextVersion) {
312 DirectoryTypeDebugInfoEmitter emitter(SYNCED_NOTIFICATIONS, &type_observers_);
313 DirectoryUpdateHandler handler(dir(), SYNCED_NOTIFICATIONS,
314 ui_worker(), &emitter);
315 sessions::StatusController status;
316 int field_number = GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS);
318 sync_pb::DataTypeProgressMarker progress;
319 progress.set_data_type_id(
320 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
321 progress.set_token("token");
323 sync_pb::DataTypeContext old_context;
324 old_context.set_version(1);
325 old_context.set_context("data");
326 old_context.set_data_type_id(field_number);
328 scoped_ptr<sync_pb::SyncEntity> e1 =
329 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e1")), "",
330 SYNCED_NOTIFICATIONS);
332 SyncEntityList updates;
333 updates.push_back(e1.get());
335 // The first response should be processed fine.
336 EXPECT_EQ(SYNCER_OK,
337 handler.ProcessGetUpdatesResponse(
338 progress, old_context, updates, &status));
339 handler.ApplyUpdates(&status);
341 // The PREFERENCES root should be auto-created.
342 EXPECT_TRUE(TypeRootExists(SYNCED_NOTIFICATIONS));
344 EXPECT_TRUE(EntryExists(e1->id_string()));
347 sync_pb::DataTypeContext dir_context;
348 syncable::ReadTransaction trans(FROM_HERE, dir());
349 trans.directory()->GetDataTypeContext(
350 &trans, SYNCED_NOTIFICATIONS, &dir_context);
351 EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
354 sync_pb::DataTypeContext new_context;
355 new_context.set_version(0);
356 new_context.set_context("old");
357 new_context.set_data_type_id(field_number);
359 scoped_ptr<sync_pb::SyncEntity> e2 =
360 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e2")), "",
361 SYNCED_NOTIFICATIONS);
362 updates.clear();
363 updates.push_back(e2.get());
365 // The second response, with an old context version, should result in an
366 // error and the updates should be dropped.
367 EXPECT_EQ(DATATYPE_TRIGGERED_RETRY,
368 handler.ProcessGetUpdatesResponse(
369 progress, new_context, updates, &status));
370 handler.ApplyUpdates(&status);
372 EXPECT_FALSE(EntryExists(e2->id_string()));
375 sync_pb::DataTypeContext dir_context;
376 syncable::ReadTransaction trans(FROM_HERE, dir());
377 trans.directory()->GetDataTypeContext(
378 &trans, SYNCED_NOTIFICATIONS, &dir_context);
379 EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
383 // See that updates containing attachment metadata are applied
384 // (i.e. server_attachment_metadata is copied to attachment_metadata).
385 TEST_F(DirectoryUpdateHandlerProcessUpdateTest,
386 ProcessAndApplyUpdatesWithAttachments) {
387 DirectoryTypeDebugInfoEmitter emitter(ARTICLES, &type_observers_);
388 DirectoryUpdateHandler handler(dir(), ARTICLES, ui_worker(), &emitter);
389 sessions::StatusController status;
391 sync_pb::DataTypeProgressMarker progress;
392 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(ARTICLES));
393 progress.set_token("token");
394 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 10);
396 sync_pb::DataTypeContext context;
397 context.set_data_type_id(GetSpecificsFieldNumberFromModelType(ARTICLES));
398 context.set_context("context");
399 context.set_version(1);
401 scoped_ptr<sync_pb::SyncEntity> e1 = CreateUpdate(
402 SyncableIdToProto(Id::CreateFromServerId("e1")), "", ARTICLES);
403 sync_pb::AttachmentIdProto* attachment_id = e1->add_attachment_id();
404 *attachment_id = CreateAttachmentIdProto();
406 SyncEntityList updates;
407 updates.push_back(e1.get());
409 // Process and apply updates.
410 EXPECT_EQ(
411 SYNCER_OK,
412 handler.ProcessGetUpdatesResponse(progress, context, updates, &status));
413 handler.ApplyUpdates(&status);
415 ASSERT_TRUE(TypeRootExists(ARTICLES));
416 ASSERT_TRUE(EntryExists(e1->id_string()));
418 syncable::ReadTransaction trans(FROM_HERE, dir());
419 syncable::Entry e(&trans,
420 syncable::GET_BY_ID,
421 Id::CreateFromServerId(e1->id_string()));
423 // See that the attachment_metadata is correct.
424 sync_pb::AttachmentMetadata attachment_metadata = e.GetAttachmentMetadata();
425 ASSERT_EQ(1, attachment_metadata.record_size());
426 ASSERT_EQ(attachment_id->SerializeAsString(),
427 attachment_metadata.record(0).id().SerializeAsString());
428 ASSERT_TRUE(attachment_metadata.record(0).is_on_server());
430 // See that attachment_metadata and server_attachment_metadata are equal.
431 ASSERT_EQ(attachment_metadata.SerializeAsString(),
432 e.GetServerAttachmentMetadata().SerializeAsString());
436 // A test harness for tests that focus on applying updates.
438 // Update application is performed when we want to take updates that were
439 // previously downloaded, processed, and stored in our syncable::Directory
440 // and use them to update our local state (both the Directory's local state
441 // and the model's local state, though these tests focus only on the Directory's
442 // local state).
444 // This is kept separate from the update processing test in part for historical
445 // reasons, and in part because these tests may require a bit more infrastrcture
446 // in the future. Update application should happen on a different thread a lot
447 // of the time so these tests may end up requiring more infrastructure than the
448 // update processing tests. Currently, we're bypassing most of those issues by
449 // using FakeModelWorkers, so there's not much difference between the two test
450 // harnesses.
451 class DirectoryUpdateHandlerApplyUpdateTest : public ::testing::Test {
452 public:
453 DirectoryUpdateHandlerApplyUpdateTest()
454 : ui_worker_(new FakeModelWorker(GROUP_UI)),
455 password_worker_(new FakeModelWorker(GROUP_PASSWORD)),
456 passive_worker_(new FakeModelWorker(GROUP_PASSIVE)),
457 bookmarks_emitter_(BOOKMARKS, &type_observers_),
458 passwords_emitter_(PASSWORDS, &type_observers_),
459 articles_emitter_(ARTICLES, &type_observers_),
460 update_handler_map_deleter_(&update_handler_map_) {}
462 void SetUp() override {
463 dir_maker_.SetUp();
464 entry_factory_.reset(new TestEntryFactory(directory()));
466 update_handler_map_.insert(std::make_pair(
467 BOOKMARKS,
468 new DirectoryUpdateHandler(directory(), BOOKMARKS,
469 ui_worker_, &bookmarks_emitter_)));
470 update_handler_map_.insert(std::make_pair(
471 PASSWORDS,
472 new DirectoryUpdateHandler(directory(),
473 PASSWORDS,
474 password_worker_,
475 &passwords_emitter_)));
476 update_handler_map_.insert(std::make_pair(
477 ARTICLES,
478 new DirectoryUpdateHandler(
479 directory(), ARTICLES, ui_worker_, &articles_emitter_)));
482 void TearDown() override { dir_maker_.TearDown(); }
484 const UpdateCounters& GetBookmarksUpdateCounters() {
485 return bookmarks_emitter_.GetUpdateCounters();
488 const UpdateCounters& GetPasswordsUpdateCounters() {
489 return passwords_emitter_.GetUpdateCounters();
492 const UpdateCounters& GetArticlesUpdateCounters() {
493 return articles_emitter_.GetUpdateCounters();
496 protected:
497 void ApplyBookmarkUpdates(sessions::StatusController* status) {
498 update_handler_map_[BOOKMARKS]->ApplyUpdates(status);
501 void ApplyPasswordUpdates(sessions::StatusController* status) {
502 update_handler_map_[PASSWORDS]->ApplyUpdates(status);
505 void ApplyArticlesUpdates(sessions::StatusController* status) {
506 update_handler_map_[ARTICLES]->ApplyUpdates(status);
509 TestEntryFactory* entry_factory() {
510 return entry_factory_.get();
513 syncable::Directory* directory() {
514 return dir_maker_.directory();
517 private:
518 typedef std::map<ModelType, UpdateHandler*> UpdateHandlerMap;
520 base::MessageLoop loop_; // Needed to initialize the directory.
521 TestDirectorySetterUpper dir_maker_;
522 scoped_ptr<TestEntryFactory> entry_factory_;
524 scoped_refptr<FakeModelWorker> ui_worker_;
525 scoped_refptr<FakeModelWorker> password_worker_;
526 scoped_refptr<FakeModelWorker> passive_worker_;
528 ObserverList<TypeDebugInfoObserver> type_observers_;
529 DirectoryTypeDebugInfoEmitter bookmarks_emitter_;
530 DirectoryTypeDebugInfoEmitter passwords_emitter_;
531 DirectoryTypeDebugInfoEmitter articles_emitter_;
533 UpdateHandlerMap update_handler_map_;
534 STLValueDeleter<UpdateHandlerMap> update_handler_map_deleter_;
537 namespace {
538 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
539 sync_pb::EntitySpecifics result;
540 AddDefaultFieldValue(BOOKMARKS, &result);
541 return result;
543 } // namespace
545 // Test update application for a few bookmark items.
546 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SimpleBookmark) {
547 sessions::StatusController status;
549 std::string root_server_id = Id::GetRoot().GetServerId();
550 int64 parent_handle =
551 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
552 "parent", DefaultBookmarkSpecifics(), root_server_id);
553 int64 child_handle =
554 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
555 "child", DefaultBookmarkSpecifics(), "parent");
557 ApplyBookmarkUpdates(&status);
559 const UpdateCounters& counter = GetBookmarksUpdateCounters();
560 EXPECT_EQ(0, counter.num_encryption_conflict_application_failures)
561 << "Simple update shouldn't result in conflicts";
562 EXPECT_EQ(0, counter.num_hierarchy_conflict_application_failures)
563 << "Simple update shouldn't result in conflicts";
564 EXPECT_EQ(2, counter.num_updates_applied)
565 << "All items should have been successfully applied";
568 syncable::ReadTransaction trans(FROM_HERE, directory());
570 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
571 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
573 ASSERT_TRUE(parent.good());
574 ASSERT_TRUE(child.good());
576 EXPECT_FALSE(parent.GetIsUnsynced());
577 EXPECT_FALSE(parent.GetIsUnappliedUpdate());
578 EXPECT_FALSE(child.GetIsUnsynced());
579 EXPECT_FALSE(child.GetIsUnappliedUpdate());
583 // Test that the applicator can handle updates delivered out of order.
584 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
585 BookmarkChildrenBeforeParent) {
586 // Start with some bookmarks whose parents are unknown.
587 std::string root_server_id = Id::GetRoot().GetServerId();
588 int64 a_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
589 "a_child_created_first", DefaultBookmarkSpecifics(), "parent");
590 int64 x_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
591 "x_child_created_first", DefaultBookmarkSpecifics(), "parent");
593 // Update application will fail.
594 sessions::StatusController status1;
595 ApplyBookmarkUpdates(&status1);
596 EXPECT_EQ(0, status1.num_updates_applied());
597 EXPECT_EQ(2, status1.num_hierarchy_conflicts());
600 syncable::ReadTransaction trans(FROM_HERE, directory());
602 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
603 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
605 ASSERT_TRUE(a.good());
606 ASSERT_TRUE(x.good());
608 EXPECT_TRUE(a.GetIsUnappliedUpdate());
609 EXPECT_TRUE(x.GetIsUnappliedUpdate());
612 // Now add their parent and a few siblings.
613 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
614 "parent", DefaultBookmarkSpecifics(), root_server_id);
615 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
616 "a_child_created_second", DefaultBookmarkSpecifics(), "parent");
617 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
618 "x_child_created_second", DefaultBookmarkSpecifics(), "parent");
620 // Update application will succeed.
621 sessions::StatusController status2;
622 ApplyBookmarkUpdates(&status2);
623 EXPECT_EQ(5, status2.num_updates_applied())
624 << "All updates should have been successfully applied";
627 syncable::ReadTransaction trans(FROM_HERE, directory());
629 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
630 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
632 ASSERT_TRUE(a.good());
633 ASSERT_TRUE(x.good());
635 EXPECT_FALSE(a.GetIsUnappliedUpdate());
636 EXPECT_FALSE(x.GetIsUnappliedUpdate());
640 // Try to apply changes on an item that is both IS_UNSYNCED and
641 // IS_UNAPPLIED_UPDATE. Conflict resolution should be performed.
642 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SimpleBookmarkConflict) {
643 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem("x");
645 int original_server_version = -10;
647 syncable::ReadTransaction trans(FROM_HERE, directory());
648 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
649 original_server_version = e.GetServerVersion();
650 ASSERT_NE(original_server_version, e.GetBaseVersion());
651 EXPECT_TRUE(e.GetIsUnsynced());
654 sessions::StatusController status;
655 ApplyBookmarkUpdates(&status);
657 const UpdateCounters& counters = GetBookmarksUpdateCounters();
658 EXPECT_EQ(1, counters.num_server_overwrites)
659 << "Unsynced and unapplied item conflict should be resolved";
660 EXPECT_EQ(0, counters.num_updates_applied)
661 << "Update should not be applied; we should override the server.";
664 syncable::ReadTransaction trans(FROM_HERE, directory());
665 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
666 ASSERT_TRUE(e.good());
667 EXPECT_EQ(original_server_version, e.GetServerVersion());
668 EXPECT_EQ(original_server_version, e.GetBaseVersion());
669 EXPECT_FALSE(e.GetIsUnappliedUpdate());
671 // The unsynced flag will remain set until we successfully commit the item.
672 EXPECT_TRUE(e.GetIsUnsynced());
676 // Create a simple conflict that is also a hierarchy conflict. If we were to
677 // follow the normal "server wins" logic, we'd end up violating hierarchy
678 // constraints. The hierarchy conflict must take precedence. We can not allow
679 // the update to be applied. The item must remain in the conflict state.
680 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, HierarchyAndSimpleConflict) {
681 // Create a simply-conflicting item. It will start with valid parent ids.
682 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem(
683 "orphaned_by_server");
685 // Manually set the SERVER_PARENT_ID to bad value.
686 // A bad parent indicates a hierarchy conflict.
687 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
688 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
689 ASSERT_TRUE(entry.good());
691 entry.PutServerParentId(TestIdFactory::MakeServer("bogus_parent"));
694 sessions::StatusController status;
695 ApplyBookmarkUpdates(&status);
697 const UpdateCounters& counters = GetBookmarksUpdateCounters();
698 EXPECT_EQ(0, counters.num_updates_applied);
699 EXPECT_EQ(0, counters.num_server_overwrites);
700 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
703 syncable::ReadTransaction trans(FROM_HERE, directory());
704 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
705 ASSERT_TRUE(e.good());
706 EXPECT_TRUE(e.GetIsUnappliedUpdate());
707 EXPECT_TRUE(e.GetIsUnsynced());
711 // Attempt to apply an udpate that would create a bookmark folder loop. This
712 // application should fail.
713 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, BookmarkFolderLoop) {
714 // Item 'X' locally has parent of 'root'. Server is updating it to have
715 // parent of 'Y'.
717 // Create it as a child of root node.
718 int64 handle = entry_factory()->CreateSyncedItem("X", BOOKMARKS, true);
721 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
722 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
723 ASSERT_TRUE(entry.good());
725 // Re-parent from root to "Y"
726 entry.PutServerVersion(entry_factory()->GetNextRevision());
727 entry.PutIsUnappliedUpdate(true);
728 entry.PutServerParentId(TestIdFactory::MakeServer("Y"));
731 // Item 'Y' is child of 'X'.
732 entry_factory()->CreateUnsyncedItem(
733 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
734 BOOKMARKS, NULL);
736 // If the server's update were applied, we would have X be a child of Y, and Y
737 // as a child of X. That's a directory loop. The UpdateApplicator should
738 // prevent the update from being applied and note that this is a hierarchy
739 // conflict.
741 sessions::StatusController status;
742 ApplyBookmarkUpdates(&status);
744 // This should count as a hierarchy conflict.
745 const UpdateCounters& counters = GetBookmarksUpdateCounters();
746 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
749 syncable::ReadTransaction trans(FROM_HERE, directory());
750 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
751 ASSERT_TRUE(e.good());
752 EXPECT_TRUE(e.GetIsUnappliedUpdate());
753 EXPECT_FALSE(e.GetIsUnsynced());
757 // Test update application where the update has been orphaned by a local folder
758 // deletion. The update application attempt should fail.
759 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
760 HierarchyConflictDeletedParent) {
761 // Create a locally deleted parent item.
762 int64 parent_handle;
763 entry_factory()->CreateUnsyncedItem(
764 Id::CreateFromServerId("parent"), TestIdFactory::root(),
765 "parent", true, BOOKMARKS, &parent_handle);
767 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
768 syncable::MutableEntry entry(&trans,
769 syncable::GET_BY_HANDLE,
770 parent_handle);
771 entry.PutIsDel(true);
774 // Create an incoming child from the server.
775 int64 child_handle = entry_factory()->CreateUnappliedNewItemWithParent(
776 "child", DefaultBookmarkSpecifics(), "parent");
778 // The server's update may seem valid to some other client, but on this client
779 // that new item's parent no longer exists. The update should not be applied
780 // and the update applicator should indicate this is a hierarchy conflict.
782 sessions::StatusController status;
783 ApplyBookmarkUpdates(&status);
784 const UpdateCounters& counters = GetBookmarksUpdateCounters();
785 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
788 syncable::ReadTransaction trans(FROM_HERE, directory());
789 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
790 ASSERT_TRUE(child.good());
791 EXPECT_TRUE(child.GetIsUnappliedUpdate());
792 EXPECT_FALSE(child.GetIsUnsynced());
796 // Attempt to apply an update that deletes a folder where the folder has
797 // locally-created children. The update application should fail.
798 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
799 HierarchyConflictDeleteNonEmptyDirectory) {
800 // Create a server-deleted folder as a child of root node.
801 int64 parent_handle =
802 entry_factory()->CreateSyncedItem("parent", BOOKMARKS, true);
804 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
805 syncable::MutableEntry entry(&trans,
806 syncable::GET_BY_HANDLE,
807 parent_handle);
808 ASSERT_TRUE(entry.good());
810 // Delete it on the server.
811 entry.PutServerVersion(entry_factory()->GetNextRevision());
812 entry.PutIsUnappliedUpdate(true);
813 entry.PutServerParentId(TestIdFactory::root());
814 entry.PutServerIsDel(true);
817 // Create a local child of the server-deleted directory.
818 entry_factory()->CreateUnsyncedItem(
819 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
820 "child", false, BOOKMARKS, NULL);
822 // The server's request to delete the directory must be ignored, otherwise our
823 // unsynced new child would be orphaned. This is a hierarchy conflict.
825 sessions::StatusController status;
826 ApplyBookmarkUpdates(&status);
828 // This should count as a hierarchy conflict.
829 const UpdateCounters& counters = GetBookmarksUpdateCounters();
830 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
833 syncable::ReadTransaction trans(FROM_HERE, directory());
834 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
835 ASSERT_TRUE(parent.good());
836 EXPECT_TRUE(parent.GetIsUnappliedUpdate());
837 EXPECT_FALSE(parent.GetIsUnsynced());
841 // Attempt to apply updates where the updated item's parent is not known to this
842 // client. The update application attempt should fail.
843 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
844 HierarchyConflictUnknownParent) {
845 // We shouldn't be able to do anything with either of these items.
846 int64 x_handle = entry_factory()->CreateUnappliedNewItemWithParent(
847 "some_item", DefaultBookmarkSpecifics(), "unknown_parent");
848 int64 y_handle = entry_factory()->CreateUnappliedNewItemWithParent(
849 "some_other_item", DefaultBookmarkSpecifics(), "some_item");
851 sessions::StatusController status;
852 ApplyBookmarkUpdates(&status);
854 const UpdateCounters& counters = GetBookmarksUpdateCounters();
855 EXPECT_EQ(2, counters.num_hierarchy_conflict_application_failures)
856 << "All updates with an unknown ancestors should be in conflict";
857 EXPECT_EQ(0, counters.num_updates_applied)
858 << "No item with an unknown ancestor should be applied";
861 syncable::ReadTransaction trans(FROM_HERE, directory());
862 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
863 syncable::Entry y(&trans, syncable::GET_BY_HANDLE, y_handle);
864 ASSERT_TRUE(x.good());
865 ASSERT_TRUE(y.good());
866 EXPECT_TRUE(x.GetIsUnappliedUpdate());
867 EXPECT_TRUE(y.GetIsUnappliedUpdate());
868 EXPECT_FALSE(x.GetIsUnsynced());
869 EXPECT_FALSE(y.GetIsUnsynced());
873 // Attempt application of a mix of items. Some update application attempts will
874 // fail due to hierarchy conflicts. Others should succeed.
875 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, ItemsBothKnownAndUnknown) {
876 // See what happens when there's a mixture of good and bad updates.
877 std::string root_server_id = Id::GetRoot().GetServerId();
878 int64 u1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
879 "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
880 int64 k1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
881 "first_known_item", DefaultBookmarkSpecifics(), root_server_id);
882 int64 u2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
883 "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
884 int64 k2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
885 "second_known_item", DefaultBookmarkSpecifics(), "first_known_item");
886 int64 k3_handle = entry_factory()->CreateUnappliedNewItemWithParent(
887 "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item");
888 int64 k4_handle = entry_factory()->CreateUnappliedNewItemWithParent(
889 "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id);
891 sessions::StatusController status;
892 ApplyBookmarkUpdates(&status);
894 const UpdateCounters& counters = GetBookmarksUpdateCounters();
895 EXPECT_EQ(2, counters.num_hierarchy_conflict_application_failures)
896 << "The updates with unknown ancestors should be in conflict";
897 EXPECT_EQ(4, counters.num_updates_applied)
898 << "The updates with known ancestors should be successfully applied";
901 syncable::ReadTransaction trans(FROM_HERE, directory());
902 syncable::Entry u1(&trans, syncable::GET_BY_HANDLE, u1_handle);
903 syncable::Entry u2(&trans, syncable::GET_BY_HANDLE, u2_handle);
904 syncable::Entry k1(&trans, syncable::GET_BY_HANDLE, k1_handle);
905 syncable::Entry k2(&trans, syncable::GET_BY_HANDLE, k2_handle);
906 syncable::Entry k3(&trans, syncable::GET_BY_HANDLE, k3_handle);
907 syncable::Entry k4(&trans, syncable::GET_BY_HANDLE, k4_handle);
908 ASSERT_TRUE(u1.good());
909 ASSERT_TRUE(u2.good());
910 ASSERT_TRUE(k1.good());
911 ASSERT_TRUE(k2.good());
912 ASSERT_TRUE(k3.good());
913 ASSERT_TRUE(k4.good());
914 EXPECT_TRUE(u1.GetIsUnappliedUpdate());
915 EXPECT_TRUE(u2.GetIsUnappliedUpdate());
916 EXPECT_FALSE(k1.GetIsUnappliedUpdate());
917 EXPECT_FALSE(k2.GetIsUnappliedUpdate());
918 EXPECT_FALSE(k3.GetIsUnappliedUpdate());
919 EXPECT_FALSE(k4.GetIsUnappliedUpdate());
923 // Attempt application of password upates where the passphrase is known.
924 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, DecryptablePassword) {
925 // Decryptable password updates should be applied.
926 Cryptographer* cryptographer;
928 // Storing the cryptographer separately is bad, but for this test we
929 // know it's safe.
930 syncable::ReadTransaction trans(FROM_HERE, directory());
931 cryptographer = directory()->GetCryptographer(&trans);
934 KeyParams params = {"localhost", "dummy", "foobar"};
935 cryptographer->AddKey(params);
937 sync_pb::EntitySpecifics specifics;
938 sync_pb::PasswordSpecificsData data;
939 data.set_origin("http://example.com");
941 cryptographer->Encrypt(data,
942 specifics.mutable_password()->mutable_encrypted());
943 int64 handle =
944 entry_factory()->CreateUnappliedNewItem("item", specifics, false);
946 sessions::StatusController status;
947 ApplyPasswordUpdates(&status);
949 const UpdateCounters& counters = GetPasswordsUpdateCounters();
950 EXPECT_EQ(1, counters.num_updates_applied)
951 << "The updates that can be decrypted should be applied";
954 syncable::ReadTransaction trans(FROM_HERE, directory());
955 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
956 ASSERT_TRUE(e.good());
957 EXPECT_FALSE(e.GetIsUnappliedUpdate());
958 EXPECT_FALSE(e.GetIsUnsynced());
962 // Attempt application of encrypted items when the passphrase is not known.
963 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, UndecryptableData) {
964 // Undecryptable updates should not be applied.
965 sync_pb::EntitySpecifics encrypted_bookmark;
966 encrypted_bookmark.mutable_encrypted();
967 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
968 std::string root_server_id = Id::GetRoot().GetServerId();
969 int64 folder_handle = entry_factory()->CreateUnappliedNewItemWithParent(
970 "folder",
971 encrypted_bookmark,
972 root_server_id);
973 int64 bookmark_handle = entry_factory()->CreateUnappliedNewItem(
974 "item2",
975 encrypted_bookmark,
976 false);
977 sync_pb::EntitySpecifics encrypted_password;
978 encrypted_password.mutable_password();
979 int64 password_handle = entry_factory()->CreateUnappliedNewItem(
980 "item3",
981 encrypted_password,
982 false);
984 sessions::StatusController status;
985 ApplyBookmarkUpdates(&status);
986 ApplyPasswordUpdates(&status);
988 const UpdateCounters& bm_counters = GetBookmarksUpdateCounters();
989 EXPECT_EQ(2, bm_counters.num_encryption_conflict_application_failures)
990 << "Updates that can't be decrypted should be in encryption conflict";
991 EXPECT_EQ(0, bm_counters.num_updates_applied)
992 << "No update that can't be decrypted should be applied";
994 const UpdateCounters& pw_counters = GetPasswordsUpdateCounters();
995 EXPECT_EQ(1, pw_counters.num_encryption_conflict_application_failures)
996 << "Updates that can't be decrypted should be in encryption conflict";
997 EXPECT_EQ(0, pw_counters.num_updates_applied)
998 << "No update that can't be decrypted should be applied";
1001 syncable::ReadTransaction trans(FROM_HERE, directory());
1002 syncable::Entry folder(&trans, syncable::GET_BY_HANDLE, folder_handle);
1003 syncable::Entry bm(&trans, syncable::GET_BY_HANDLE, bookmark_handle);
1004 syncable::Entry pw(&trans, syncable::GET_BY_HANDLE, password_handle);
1005 ASSERT_TRUE(folder.good());
1006 ASSERT_TRUE(bm.good());
1007 ASSERT_TRUE(pw.good());
1008 EXPECT_TRUE(folder.GetIsUnappliedUpdate());
1009 EXPECT_TRUE(bm.GetIsUnappliedUpdate());
1010 EXPECT_TRUE(pw.GetIsUnappliedUpdate());
1014 // Test a mix of decryptable and undecryptable updates.
1015 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SomeUndecryptablePassword) {
1016 Cryptographer* cryptographer;
1018 int64 decryptable_handle = -1;
1019 int64 undecryptable_handle = -1;
1021 // Only decryptable password updates should be applied.
1023 sync_pb::EntitySpecifics specifics;
1024 sync_pb::PasswordSpecificsData data;
1025 data.set_origin("http://example.com/1");
1027 syncable::ReadTransaction trans(FROM_HERE, directory());
1028 cryptographer = directory()->GetCryptographer(&trans);
1030 KeyParams params = {"localhost", "dummy", "foobar"};
1031 cryptographer->AddKey(params);
1033 cryptographer->Encrypt(data,
1034 specifics.mutable_password()->mutable_encrypted());
1036 decryptable_handle =
1037 entry_factory()->CreateUnappliedNewItem("item1", specifics, false);
1040 // Create a new cryptographer, independent of the one in the session.
1041 Cryptographer other_cryptographer(cryptographer->encryptor());
1042 KeyParams params = {"localhost", "dummy", "bazqux"};
1043 other_cryptographer.AddKey(params);
1045 sync_pb::EntitySpecifics specifics;
1046 sync_pb::PasswordSpecificsData data;
1047 data.set_origin("http://example.com/2");
1049 other_cryptographer.Encrypt(data,
1050 specifics.mutable_password()->mutable_encrypted());
1051 undecryptable_handle =
1052 entry_factory()->CreateUnappliedNewItem("item2", specifics, false);
1055 sessions::StatusController status;
1056 ApplyPasswordUpdates(&status);
1058 const UpdateCounters& counters = GetPasswordsUpdateCounters();
1059 EXPECT_EQ(1, counters.num_encryption_conflict_application_failures)
1060 << "The updates that can't be decrypted should be in encryption "
1061 << "conflict";
1062 EXPECT_EQ(1, counters.num_updates_applied)
1063 << "The undecryptable password update shouldn't be applied";
1066 syncable::ReadTransaction trans(FROM_HERE, directory());
1067 syncable::Entry e1(&trans, syncable::GET_BY_HANDLE, decryptable_handle);
1068 syncable::Entry e2(&trans, syncable::GET_BY_HANDLE, undecryptable_handle);
1069 ASSERT_TRUE(e1.good());
1070 ASSERT_TRUE(e2.good());
1071 EXPECT_FALSE(e1.GetIsUnappliedUpdate());
1072 EXPECT_TRUE(e2.GetIsUnappliedUpdate());
1076 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
1077 SimpleConflictDifferentAttachmentMetadata) {
1078 const bool is_folder = false;
1079 sync_pb::EntitySpecifics specifics;
1080 *specifics.mutable_article() = sync_pb::ArticleSpecifics();
1081 int64 handle = entry_factory()->CreateSyncedItem("art1", ARTICLES, is_folder);
1083 sync_pb::AttachmentIdProto local_attachment_id = CreateAttachmentIdProto();
1084 sync_pb::AttachmentIdProto server_attachment_id = CreateAttachmentIdProto();
1086 // Add an attachment to the local attachment metadata.
1087 sync_pb::AttachmentMetadata local_metadata;
1088 sync_pb::AttachmentMetadataRecord* local_record = local_metadata.add_record();
1089 *local_record->mutable_id() = local_attachment_id;
1090 local_record->set_is_on_server(true);
1091 entry_factory()->SetLocalAttachmentMetadataForItem(handle, local_metadata);
1093 // Add a different attachment to the server attachment metadata.
1094 sync_pb::AttachmentMetadata server_metadata;
1095 sync_pb::AttachmentMetadataRecord* server_record =
1096 server_metadata.add_record();
1097 *server_record->mutable_id() = server_attachment_id;
1098 server_record->set_is_on_server(true);
1099 entry_factory()->SetServerAttachmentMetadataForItem(handle, server_metadata);
1101 // At this point we have a simple conflict. The server says art1 should have
1102 // server_attachment_id, but the local sync engine says it should have
1103 // local_attachment_id.
1105 sessions::StatusController status;
1106 ApplyArticlesUpdates(&status);
1108 // See that the server won.
1109 const UpdateCounters& counters = GetArticlesUpdateCounters();
1110 EXPECT_EQ(1, counters.num_updates_applied);
1111 EXPECT_EQ(1, counters.num_local_overwrites);
1112 EXPECT_EQ(0, counters.num_server_overwrites);
1113 local_metadata = entry_factory()->GetLocalAttachmentMetadataForItem(handle);
1114 EXPECT_EQ(server_metadata.SerializeAsString(),
1115 local_metadata.SerializeAsString());
1118 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
1119 SimpleConflictSameAttachmentMetadataDifferentOrder) {
1120 const bool is_folder = false;
1121 sync_pb::EntitySpecifics specifics;
1122 *specifics.mutable_article() = sync_pb::ArticleSpecifics();
1123 int64 handle = entry_factory()->CreateSyncedItem("art1", ARTICLES, is_folder);
1125 sync_pb::AttachmentIdProto id1 = CreateAttachmentIdProto();
1126 sync_pb::AttachmentIdProto id2 = CreateAttachmentIdProto();
1128 // Add id1, then id2 to the local attachment metadata.
1129 sync_pb::AttachmentMetadata local_metadata;
1130 sync_pb::AttachmentMetadataRecord* record = local_metadata.add_record();
1131 *record->mutable_id() = id1;
1132 record->set_is_on_server(true);
1133 record = local_metadata.add_record();
1134 *record->mutable_id() = id2;
1135 record->set_is_on_server(true);
1136 entry_factory()->SetLocalAttachmentMetadataForItem(handle, local_metadata);
1138 // Add id1 and id2 to the server attachment metadata, but in reverse order.
1139 sync_pb::AttachmentMetadata server_metadata;
1140 record = server_metadata.add_record();
1141 *record->mutable_id() = id2;
1142 record->set_is_on_server(true);
1143 record = local_metadata.add_record();
1144 *record->mutable_id() = id1;
1145 record->set_is_on_server(true);
1146 entry_factory()->SetServerAttachmentMetadataForItem(handle, server_metadata);
1148 // At this point we have a (false) conflict.
1150 sessions::StatusController status;
1151 ApplyArticlesUpdates(&status);
1153 // See that the server won.
1154 const UpdateCounters& counters = GetArticlesUpdateCounters();
1155 EXPECT_EQ(1, counters.num_updates_applied);
1156 EXPECT_EQ(1, counters.num_local_overwrites);
1157 EXPECT_EQ(0, counters.num_server_overwrites);
1158 local_metadata = entry_factory()->GetLocalAttachmentMetadataForItem(handle);
1159 EXPECT_EQ(server_metadata.SerializeAsString(),
1160 local_metadata.SerializeAsString());
1163 } // namespace syncer