Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / sync / engine / directory_update_handler_unittest.cc
blob6052da19735233a995b73544fb68c717c7e97f4b
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::UNITTEST;
34 static const int64 kDefaultVersion = 1000;
36 // A test harness for tests that focus on processing updates.
38 // Update processing is what occurs when we first download updates. It converts
39 // the received protobuf message into information in the syncable::Directory.
40 // Any invalid or redundant updates will be dropped at this point.
41 class DirectoryUpdateHandlerProcessUpdateTest : public ::testing::Test {
42 public:
43 DirectoryUpdateHandlerProcessUpdateTest()
44 : ui_worker_(new FakeModelWorker(GROUP_UI)) {
47 virtual ~DirectoryUpdateHandlerProcessUpdateTest() {}
49 virtual void SetUp() OVERRIDE {
50 dir_maker_.SetUp();
53 virtual void TearDown() OVERRIDE {
54 dir_maker_.TearDown();
57 syncable::Directory* dir() {
58 return dir_maker_.directory();
61 protected:
62 scoped_ptr<sync_pb::SyncEntity> CreateUpdate(
63 const std::string& id,
64 const std::string& parent,
65 const ModelType& type);
67 // This exists mostly to give tests access to the protected member function.
68 // Warning: This takes the syncable directory lock.
69 void UpdateSyncEntities(
70 DirectoryUpdateHandler* handler,
71 const SyncEntityList& applicable_updates,
72 sessions::StatusController* status);
74 // Another function to access private member functions.
75 void UpdateProgressMarkers(
76 DirectoryUpdateHandler* handler,
77 const sync_pb::DataTypeProgressMarker& progress);
79 scoped_refptr<FakeModelWorker> ui_worker() {
80 return ui_worker_;
83 bool EntryExists(const std::string& id) {
84 syncable::ReadTransaction trans(FROM_HERE, dir());
85 syncable::Entry e(&trans, syncable::GET_BY_ID,
86 syncable::Id::CreateFromServerId(id));
87 return e.good() && !e.GetIsDel();
90 protected:
91 // Used in the construction of DirectoryTypeDebugInfoEmitters.
92 ObserverList<TypeDebugInfoObserver> type_observers_;
94 private:
95 base::MessageLoop loop_; // Needed to initialize the directory.
96 TestDirectorySetterUpper dir_maker_;
97 scoped_refptr<FakeModelWorker> ui_worker_;
100 scoped_ptr<sync_pb::SyncEntity>
101 DirectoryUpdateHandlerProcessUpdateTest::CreateUpdate(
102 const std::string& id,
103 const std::string& parent,
104 const ModelType& type) {
105 scoped_ptr<sync_pb::SyncEntity> e(new sync_pb::SyncEntity());
106 e->set_id_string(id);
107 e->set_parent_id_string(parent);
108 e->set_non_unique_name(id);
109 e->set_name(id);
110 e->set_version(kDefaultVersion);
111 AddDefaultFieldValue(type, e->mutable_specifics());
112 return e.Pass();
115 void DirectoryUpdateHandlerProcessUpdateTest::UpdateSyncEntities(
116 DirectoryUpdateHandler* handler,
117 const SyncEntityList& applicable_updates,
118 sessions::StatusController* status) {
119 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, UNITTEST, dir());
120 handler->UpdateSyncEntities(&trans, applicable_updates, status);
123 void DirectoryUpdateHandlerProcessUpdateTest::UpdateProgressMarkers(
124 DirectoryUpdateHandler* handler,
125 const sync_pb::DataTypeProgressMarker& progress) {
126 handler->UpdateProgressMarker(progress);
129 static const char kCacheGuid[] = "IrcjZ2jyzHDV9Io4+zKcXQ==";
131 // Test that the bookmark tag is set on newly downloaded items.
132 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, NewBookmarkTag) {
133 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
134 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
135 sync_pb::GetUpdatesResponse gu_response;
136 sessions::StatusController status;
138 // Add a bookmark item to the update message.
139 std::string root = syncable::GetNullId().GetServerId();
140 syncable::Id server_id = syncable::Id::CreateFromServerId("b1");
141 scoped_ptr<sync_pb::SyncEntity> e =
142 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
143 e->set_originator_cache_guid(
144 std::string(kCacheGuid, arraysize(kCacheGuid)-1));
145 syncable::Id client_id = syncable::Id::CreateFromClientString("-2");
146 e->set_originator_client_item_id(client_id.GetServerId());
147 e->set_position_in_parent(0);
149 // Add it to the applicable updates list.
150 SyncEntityList bookmark_updates;
151 bookmark_updates.push_back(e.get());
153 // Process the update.
154 UpdateSyncEntities(&handler, bookmark_updates, &status);
156 syncable::ReadTransaction trans(FROM_HERE, dir());
157 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
158 ASSERT_TRUE(entry.good());
159 EXPECT_TRUE(UniquePosition::IsValidSuffix(entry.GetUniqueBookmarkTag()));
160 EXPECT_TRUE(entry.GetServerUniquePosition().IsValid());
162 // If this assertion fails, that might indicate that the algorithm used to
163 // generate bookmark tags has been modified. This could have implications for
164 // bookmark ordering. Please make sure you know what you're doing if you
165 // intend to make such a change.
166 EXPECT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", entry.GetUniqueBookmarkTag());
169 // Test the receipt of a type root node.
170 TEST_F(DirectoryUpdateHandlerProcessUpdateTest,
171 ReceiveServerCreatedBookmarkFolders) {
172 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
173 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
174 sync_pb::GetUpdatesResponse gu_response;
175 sessions::StatusController status;
177 // Create an update that mimics the bookmark root.
178 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
179 std::string root = syncable::GetNullId().GetServerId();
180 scoped_ptr<sync_pb::SyncEntity> e =
181 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
182 e->set_server_defined_unique_tag("google_chrome_bookmarks");
183 e->set_folder(true);
185 // Add it to the applicable updates list.
186 SyncEntityList bookmark_updates;
187 bookmark_updates.push_back(e.get());
189 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
191 // Process it.
192 UpdateSyncEntities(&handler, bookmark_updates, &status);
194 // Verify the results.
195 syncable::ReadTransaction trans(FROM_HERE, dir());
196 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
197 ASSERT_TRUE(entry.good());
199 EXPECT_FALSE(entry.ShouldMaintainPosition());
200 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
201 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
202 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
205 // Test the receipt of a non-bookmark item.
206 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ReceiveNonBookmarkItem) {
207 DirectoryTypeDebugInfoEmitter emitter(AUTOFILL, &type_observers_);
208 DirectoryUpdateHandler handler(dir(), AUTOFILL, ui_worker(), &emitter);
209 sync_pb::GetUpdatesResponse gu_response;
210 sessions::StatusController status;
212 std::string root = syncable::GetNullId().GetServerId();
213 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
214 scoped_ptr<sync_pb::SyncEntity> e =
215 CreateUpdate(SyncableIdToProto(server_id), root, AUTOFILL);
216 e->set_server_defined_unique_tag("9PGRuKdX5sHyGMB17CvYTXuC43I=");
218 // Add it to the applicable updates list.
219 SyncEntityList autofill_updates;
220 autofill_updates.push_back(e.get());
222 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
224 // Process it.
225 UpdateSyncEntities(&handler, autofill_updates, &status);
227 syncable::ReadTransaction trans(FROM_HERE, dir());
228 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
229 ASSERT_TRUE(entry.good());
231 EXPECT_FALSE(entry.ShouldMaintainPosition());
232 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
233 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
234 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
237 // Tests the setting of progress markers.
238 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ProcessNewProgressMarkers) {
239 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
240 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
242 sync_pb::DataTypeProgressMarker progress;
243 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS));
244 progress.set_token("token");
246 UpdateProgressMarkers(&handler, progress);
248 sync_pb::DataTypeProgressMarker saved;
249 dir()->GetDownloadProgress(BOOKMARKS, &saved);
251 EXPECT_EQ(progress.token(), saved.token());
252 EXPECT_EQ(progress.data_type_id(), saved.data_type_id());
255 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, GarbageCollectionByVersion) {
256 DirectoryTypeDebugInfoEmitter emitter(SYNCED_NOTIFICATIONS, &type_observers_);
257 DirectoryUpdateHandler handler(dir(), SYNCED_NOTIFICATIONS,
258 ui_worker(), &emitter);
259 sessions::StatusController status;
261 sync_pb::DataTypeProgressMarker progress;
262 progress.set_data_type_id(
263 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
264 progress.set_token("token");
265 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 10);
267 sync_pb::DataTypeContext context;
268 context.set_data_type_id(
269 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
270 context.set_context("context");
271 context.set_version(1);
273 scoped_ptr<sync_pb::SyncEntity> type_root =
274 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("root")),
275 syncable::GetNullId().GetServerId(),
276 SYNCED_NOTIFICATIONS);
277 type_root->set_server_defined_unique_tag(
278 ModelTypeToRootTag(SYNCED_NOTIFICATIONS));
279 type_root->set_folder(true);
281 scoped_ptr<sync_pb::SyncEntity> e1 =
282 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e1")),
283 type_root->id_string(),
284 SYNCED_NOTIFICATIONS);
286 scoped_ptr<sync_pb::SyncEntity> e2 =
287 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e2")),
288 type_root->id_string(),
289 SYNCED_NOTIFICATIONS);
290 e2->set_version(kDefaultVersion + 100);
292 // Add to the applicable updates list.
293 SyncEntityList updates;
294 updates.push_back(type_root.get());
295 updates.push_back(e1.get());
296 updates.push_back(e2.get());
298 // Process and apply updates.
299 EXPECT_EQ(
300 SYNCER_OK,
301 handler.ProcessGetUpdatesResponse(progress, context, updates, &status));
302 handler.ApplyUpdates(&status);
304 // Verify none is deleted because they are unapplied during GC.
305 EXPECT_TRUE(EntryExists(type_root->id_string()));
306 EXPECT_TRUE(EntryExists(e1->id_string()));
307 EXPECT_TRUE(EntryExists(e2->id_string()));
309 // Process and apply again. Old entry is deleted but not root.
310 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 20);
311 EXPECT_EQ(SYNCER_OK,
312 handler.ProcessGetUpdatesResponse(
313 progress, context, SyncEntityList(), &status));
314 handler.ApplyUpdates(&status);
315 EXPECT_TRUE(EntryExists(type_root->id_string()));
316 EXPECT_FALSE(EntryExists(e1->id_string()));
317 EXPECT_TRUE(EntryExists(e2->id_string()));
320 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ContextVersion) {
321 DirectoryTypeDebugInfoEmitter emitter(SYNCED_NOTIFICATIONS, &type_observers_);
322 DirectoryUpdateHandler handler(dir(), SYNCED_NOTIFICATIONS,
323 ui_worker(), &emitter);
324 sessions::StatusController status;
325 int field_number = GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS);
327 sync_pb::DataTypeProgressMarker progress;
328 progress.set_data_type_id(
329 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
330 progress.set_token("token");
332 sync_pb::DataTypeContext old_context;
333 old_context.set_version(1);
334 old_context.set_context("data");
335 old_context.set_data_type_id(field_number);
337 scoped_ptr<sync_pb::SyncEntity> type_root =
338 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("root")),
339 syncable::GetNullId().GetServerId(),
340 SYNCED_NOTIFICATIONS);
341 type_root->set_server_defined_unique_tag(
342 ModelTypeToRootTag(SYNCED_NOTIFICATIONS));
343 type_root->set_folder(true);
344 scoped_ptr<sync_pb::SyncEntity> e1 =
345 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e1")),
346 type_root->id_string(),
347 SYNCED_NOTIFICATIONS);
349 SyncEntityList updates;
350 updates.push_back(type_root.get());
351 updates.push_back(e1.get());
353 // The first response should be processed fine.
354 EXPECT_EQ(SYNCER_OK,
355 handler.ProcessGetUpdatesResponse(
356 progress, old_context, updates, &status));
357 handler.ApplyUpdates(&status);
359 EXPECT_TRUE(EntryExists(type_root->id_string()));
360 EXPECT_TRUE(EntryExists(e1->id_string()));
363 sync_pb::DataTypeContext dir_context;
364 syncable::ReadTransaction trans(FROM_HERE, dir());
365 trans.directory()->GetDataTypeContext(
366 &trans, SYNCED_NOTIFICATIONS, &dir_context);
367 EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
370 sync_pb::DataTypeContext new_context;
371 new_context.set_version(0);
372 new_context.set_context("old");
373 new_context.set_data_type_id(field_number);
375 scoped_ptr<sync_pb::SyncEntity> e2 =
376 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e2")),
377 type_root->id_string(),
378 SYNCED_NOTIFICATIONS);
379 updates.clear();
380 updates.push_back(e2.get());
382 // The second response, with an old context version, should result in an
383 // error and the updates should be dropped.
384 EXPECT_EQ(DATATYPE_TRIGGERED_RETRY,
385 handler.ProcessGetUpdatesResponse(
386 progress, new_context, updates, &status));
387 handler.ApplyUpdates(&status);
389 EXPECT_FALSE(EntryExists(e2->id_string()));
392 sync_pb::DataTypeContext dir_context;
393 syncable::ReadTransaction trans(FROM_HERE, dir());
394 trans.directory()->GetDataTypeContext(
395 &trans, SYNCED_NOTIFICATIONS, &dir_context);
396 EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
400 // See that updates containing attachment metadata are applied
401 // (i.e. server_attachment_metadata is copied to attachment_metadata).
402 TEST_F(DirectoryUpdateHandlerProcessUpdateTest,
403 ProcessAndApplyUpdatesWithAttachments) {
404 DirectoryTypeDebugInfoEmitter emitter(ARTICLES, &type_observers_);
405 DirectoryUpdateHandler handler(dir(), ARTICLES, ui_worker(), &emitter);
406 sessions::StatusController status;
408 sync_pb::DataTypeProgressMarker progress;
409 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(ARTICLES));
410 progress.set_token("token");
411 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 10);
413 sync_pb::DataTypeContext context;
414 context.set_data_type_id(GetSpecificsFieldNumberFromModelType(ARTICLES));
415 context.set_context("context");
416 context.set_version(1);
418 scoped_ptr<sync_pb::SyncEntity> type_root =
419 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("root")),
420 syncable::GetNullId().GetServerId(),
421 ARTICLES);
422 type_root->set_server_defined_unique_tag(ModelTypeToRootTag(ARTICLES));
423 type_root->set_folder(true);
425 scoped_ptr<sync_pb::SyncEntity> e1 =
426 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e1")),
427 type_root->id_string(),
428 ARTICLES);
429 sync_pb::AttachmentIdProto* attachment_id = e1->add_attachment_id();
430 *attachment_id = CreateAttachmentIdProto();
432 SyncEntityList updates;
433 updates.push_back(type_root.get());
434 updates.push_back(e1.get());
436 // Process and apply updates.
437 EXPECT_EQ(
438 SYNCER_OK,
439 handler.ProcessGetUpdatesResponse(progress, context, updates, &status));
440 handler.ApplyUpdates(&status);
442 ASSERT_TRUE(EntryExists(type_root->id_string()));
443 ASSERT_TRUE(EntryExists(e1->id_string()));
445 syncable::ReadTransaction trans(FROM_HERE, dir());
446 syncable::Entry e(&trans,
447 syncable::GET_BY_ID,
448 syncable::Id::CreateFromServerId(e1->id_string()));
450 // See that the attachment_metadata is correct.
451 sync_pb::AttachmentMetadata attachment_metadata = e.GetAttachmentMetadata();
452 ASSERT_EQ(1, attachment_metadata.record_size());
453 ASSERT_EQ(attachment_id->SerializeAsString(),
454 attachment_metadata.record(0).id().SerializeAsString());
455 ASSERT_TRUE(attachment_metadata.record(0).is_on_server());
457 // See that attachment_metadata and server_attachment_metadata are equal.
458 ASSERT_EQ(attachment_metadata.SerializeAsString(),
459 e.GetServerAttachmentMetadata().SerializeAsString());
463 // A test harness for tests that focus on applying updates.
465 // Update application is performed when we want to take updates that were
466 // previously downloaded, processed, and stored in our syncable::Directory
467 // and use them to update our local state (both the Directory's local state
468 // and the model's local state, though these tests focus only on the Directory's
469 // local state).
471 // This is kept separate from the update processing test in part for historical
472 // reasons, and in part because these tests may require a bit more infrastrcture
473 // in the future. Update application should happen on a different thread a lot
474 // of the time so these tests may end up requiring more infrastructure than the
475 // update processing tests. Currently, we're bypassing most of those issues by
476 // using FakeModelWorkers, so there's not much difference between the two test
477 // harnesses.
478 class DirectoryUpdateHandlerApplyUpdateTest : public ::testing::Test {
479 public:
480 DirectoryUpdateHandlerApplyUpdateTest()
481 : ui_worker_(new FakeModelWorker(GROUP_UI)),
482 password_worker_(new FakeModelWorker(GROUP_PASSWORD)),
483 passive_worker_(new FakeModelWorker(GROUP_PASSIVE)),
484 bookmarks_emitter_(BOOKMARKS, &type_observers_),
485 passwords_emitter_(PASSWORDS, &type_observers_),
486 articles_emitter_(ARTICLES, &type_observers_),
487 update_handler_map_deleter_(&update_handler_map_) {}
489 virtual void SetUp() OVERRIDE {
490 dir_maker_.SetUp();
491 entry_factory_.reset(new TestEntryFactory(directory()));
493 update_handler_map_.insert(std::make_pair(
494 BOOKMARKS,
495 new DirectoryUpdateHandler(directory(), BOOKMARKS,
496 ui_worker_, &bookmarks_emitter_)));
497 update_handler_map_.insert(std::make_pair(
498 PASSWORDS,
499 new DirectoryUpdateHandler(directory(),
500 PASSWORDS,
501 password_worker_,
502 &passwords_emitter_)));
503 update_handler_map_.insert(std::make_pair(
504 ARTICLES,
505 new DirectoryUpdateHandler(
506 directory(), ARTICLES, ui_worker_, &articles_emitter_)));
509 virtual void TearDown() OVERRIDE {
510 dir_maker_.TearDown();
513 const UpdateCounters& GetBookmarksUpdateCounters() {
514 return bookmarks_emitter_.GetUpdateCounters();
517 const UpdateCounters& GetPasswordsUpdateCounters() {
518 return passwords_emitter_.GetUpdateCounters();
521 const UpdateCounters& GetArticlesUpdateCounters() {
522 return articles_emitter_.GetUpdateCounters();
525 protected:
526 void ApplyBookmarkUpdates(sessions::StatusController* status) {
527 update_handler_map_[BOOKMARKS]->ApplyUpdates(status);
530 void ApplyPasswordUpdates(sessions::StatusController* status) {
531 update_handler_map_[PASSWORDS]->ApplyUpdates(status);
534 void ApplyArticlesUpdates(sessions::StatusController* status) {
535 update_handler_map_[ARTICLES]->ApplyUpdates(status);
538 TestEntryFactory* entry_factory() {
539 return entry_factory_.get();
542 syncable::Directory* directory() {
543 return dir_maker_.directory();
546 private:
547 typedef std::map<ModelType, UpdateHandler*> UpdateHandlerMap;
549 base::MessageLoop loop_; // Needed to initialize the directory.
550 TestDirectorySetterUpper dir_maker_;
551 scoped_ptr<TestEntryFactory> entry_factory_;
553 scoped_refptr<FakeModelWorker> ui_worker_;
554 scoped_refptr<FakeModelWorker> password_worker_;
555 scoped_refptr<FakeModelWorker> passive_worker_;
557 ObserverList<TypeDebugInfoObserver> type_observers_;
558 DirectoryTypeDebugInfoEmitter bookmarks_emitter_;
559 DirectoryTypeDebugInfoEmitter passwords_emitter_;
560 DirectoryTypeDebugInfoEmitter articles_emitter_;
562 UpdateHandlerMap update_handler_map_;
563 STLValueDeleter<UpdateHandlerMap> update_handler_map_deleter_;
566 namespace {
567 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
568 sync_pb::EntitySpecifics result;
569 AddDefaultFieldValue(BOOKMARKS, &result);
570 return result;
572 } // namespace
574 // Test update application for a few bookmark items.
575 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SimpleBookmark) {
576 sessions::StatusController status;
578 std::string root_server_id = syncable::GetNullId().GetServerId();
579 int64 parent_handle =
580 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
581 "parent", DefaultBookmarkSpecifics(), root_server_id);
582 int64 child_handle =
583 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
584 "child", DefaultBookmarkSpecifics(), "parent");
586 ApplyBookmarkUpdates(&status);
588 const UpdateCounters& counter = GetBookmarksUpdateCounters();
589 EXPECT_EQ(0, counter.num_encryption_conflict_application_failures)
590 << "Simple update shouldn't result in conflicts";
591 EXPECT_EQ(0, counter.num_hierarchy_conflict_application_failures)
592 << "Simple update shouldn't result in conflicts";
593 EXPECT_EQ(2, counter.num_updates_applied)
594 << "All items should have been successfully applied";
597 syncable::ReadTransaction trans(FROM_HERE, directory());
599 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
600 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
602 ASSERT_TRUE(parent.good());
603 ASSERT_TRUE(child.good());
605 EXPECT_FALSE(parent.GetIsUnsynced());
606 EXPECT_FALSE(parent.GetIsUnappliedUpdate());
607 EXPECT_FALSE(child.GetIsUnsynced());
608 EXPECT_FALSE(child.GetIsUnappliedUpdate());
612 // Test that the applicator can handle updates delivered out of order.
613 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
614 BookmarkChildrenBeforeParent) {
615 // Start with some bookmarks whose parents are unknown.
616 std::string root_server_id = syncable::GetNullId().GetServerId();
617 int64 a_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
618 "a_child_created_first", DefaultBookmarkSpecifics(), "parent");
619 int64 x_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
620 "x_child_created_first", DefaultBookmarkSpecifics(), "parent");
622 // Update application will fail.
623 sessions::StatusController status1;
624 ApplyBookmarkUpdates(&status1);
625 EXPECT_EQ(0, status1.num_updates_applied());
626 EXPECT_EQ(2, status1.num_hierarchy_conflicts());
629 syncable::ReadTransaction trans(FROM_HERE, directory());
631 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
632 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
634 ASSERT_TRUE(a.good());
635 ASSERT_TRUE(x.good());
637 EXPECT_TRUE(a.GetIsUnappliedUpdate());
638 EXPECT_TRUE(x.GetIsUnappliedUpdate());
641 // Now add their parent and a few siblings.
642 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
643 "parent", DefaultBookmarkSpecifics(), root_server_id);
644 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
645 "a_child_created_second", DefaultBookmarkSpecifics(), "parent");
646 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
647 "x_child_created_second", DefaultBookmarkSpecifics(), "parent");
649 // Update application will succeed.
650 sessions::StatusController status2;
651 ApplyBookmarkUpdates(&status2);
652 EXPECT_EQ(5, status2.num_updates_applied())
653 << "All updates should have been successfully applied";
656 syncable::ReadTransaction trans(FROM_HERE, directory());
658 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
659 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
661 ASSERT_TRUE(a.good());
662 ASSERT_TRUE(x.good());
664 EXPECT_FALSE(a.GetIsUnappliedUpdate());
665 EXPECT_FALSE(x.GetIsUnappliedUpdate());
669 // Try to apply changes on an item that is both IS_UNSYNCED and
670 // IS_UNAPPLIED_UPDATE. Conflict resolution should be performed.
671 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SimpleBookmarkConflict) {
672 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem("x");
674 int original_server_version = -10;
676 syncable::ReadTransaction trans(FROM_HERE, directory());
677 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
678 original_server_version = e.GetServerVersion();
679 ASSERT_NE(original_server_version, e.GetBaseVersion());
680 EXPECT_TRUE(e.GetIsUnsynced());
683 sessions::StatusController status;
684 ApplyBookmarkUpdates(&status);
686 const UpdateCounters& counters = GetBookmarksUpdateCounters();
687 EXPECT_EQ(1, counters.num_server_overwrites)
688 << "Unsynced and unapplied item conflict should be resolved";
689 EXPECT_EQ(0, counters.num_updates_applied)
690 << "Update should not be applied; we should override the server.";
693 syncable::ReadTransaction trans(FROM_HERE, directory());
694 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
695 ASSERT_TRUE(e.good());
696 EXPECT_EQ(original_server_version, e.GetServerVersion());
697 EXPECT_EQ(original_server_version, e.GetBaseVersion());
698 EXPECT_FALSE(e.GetIsUnappliedUpdate());
700 // The unsynced flag will remain set until we successfully commit the item.
701 EXPECT_TRUE(e.GetIsUnsynced());
705 // Create a simple conflict that is also a hierarchy conflict. If we were to
706 // follow the normal "server wins" logic, we'd end up violating hierarchy
707 // constraints. The hierarchy conflict must take precedence. We can not allow
708 // the update to be applied. The item must remain in the conflict state.
709 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, HierarchyAndSimpleConflict) {
710 // Create a simply-conflicting item. It will start with valid parent ids.
711 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem(
712 "orphaned_by_server");
714 // Manually set the SERVER_PARENT_ID to bad value.
715 // A bad parent indicates a hierarchy conflict.
716 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
717 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
718 ASSERT_TRUE(entry.good());
720 entry.PutServerParentId(TestIdFactory::MakeServer("bogus_parent"));
723 sessions::StatusController status;
724 ApplyBookmarkUpdates(&status);
726 const UpdateCounters& counters = GetBookmarksUpdateCounters();
727 EXPECT_EQ(0, counters.num_updates_applied);
728 EXPECT_EQ(0, counters.num_server_overwrites);
729 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
732 syncable::ReadTransaction trans(FROM_HERE, directory());
733 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
734 ASSERT_TRUE(e.good());
735 EXPECT_TRUE(e.GetIsUnappliedUpdate());
736 EXPECT_TRUE(e.GetIsUnsynced());
740 // Attempt to apply an udpate that would create a bookmark folder loop. This
741 // application should fail.
742 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, BookmarkFolderLoop) {
743 // Item 'X' locally has parent of 'root'. Server is updating it to have
744 // parent of 'Y'.
746 // Create it as a child of root node.
747 int64 handle = entry_factory()->CreateSyncedItem("X", BOOKMARKS, true);
750 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
751 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
752 ASSERT_TRUE(entry.good());
754 // Re-parent from root to "Y"
755 entry.PutServerVersion(entry_factory()->GetNextRevision());
756 entry.PutIsUnappliedUpdate(true);
757 entry.PutServerParentId(TestIdFactory::MakeServer("Y"));
760 // Item 'Y' is child of 'X'.
761 entry_factory()->CreateUnsyncedItem(
762 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
763 BOOKMARKS, NULL);
765 // If the server's update were applied, we would have X be a child of Y, and Y
766 // as a child of X. That's a directory loop. The UpdateApplicator should
767 // prevent the update from being applied and note that this is a hierarchy
768 // conflict.
770 sessions::StatusController status;
771 ApplyBookmarkUpdates(&status);
773 // This should count as a hierarchy conflict.
774 const UpdateCounters& counters = GetBookmarksUpdateCounters();
775 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
778 syncable::ReadTransaction trans(FROM_HERE, directory());
779 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
780 ASSERT_TRUE(e.good());
781 EXPECT_TRUE(e.GetIsUnappliedUpdate());
782 EXPECT_FALSE(e.GetIsUnsynced());
786 // Test update application where the update has been orphaned by a local folder
787 // deletion. The update application attempt should fail.
788 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
789 HierarchyConflictDeletedParent) {
790 // Create a locally deleted parent item.
791 int64 parent_handle;
792 entry_factory()->CreateUnsyncedItem(
793 syncable::Id::CreateFromServerId("parent"), TestIdFactory::root(),
794 "parent", true, BOOKMARKS, &parent_handle);
796 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
797 syncable::MutableEntry entry(&trans,
798 syncable::GET_BY_HANDLE,
799 parent_handle);
800 entry.PutIsDel(true);
803 // Create an incoming child from the server.
804 int64 child_handle = entry_factory()->CreateUnappliedNewItemWithParent(
805 "child", DefaultBookmarkSpecifics(), "parent");
807 // The server's update may seem valid to some other client, but on this client
808 // that new item's parent no longer exists. The update should not be applied
809 // and the update applicator should indicate this is a hierarchy conflict.
811 sessions::StatusController status;
812 ApplyBookmarkUpdates(&status);
813 const UpdateCounters& counters = GetBookmarksUpdateCounters();
814 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
817 syncable::ReadTransaction trans(FROM_HERE, directory());
818 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
819 ASSERT_TRUE(child.good());
820 EXPECT_TRUE(child.GetIsUnappliedUpdate());
821 EXPECT_FALSE(child.GetIsUnsynced());
825 // Attempt to apply an update that deletes a folder where the folder has
826 // locally-created children. The update application should fail.
827 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
828 HierarchyConflictDeleteNonEmptyDirectory) {
829 // Create a server-deleted folder as a child of root node.
830 int64 parent_handle =
831 entry_factory()->CreateSyncedItem("parent", BOOKMARKS, true);
833 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
834 syncable::MutableEntry entry(&trans,
835 syncable::GET_BY_HANDLE,
836 parent_handle);
837 ASSERT_TRUE(entry.good());
839 // Delete it on the server.
840 entry.PutServerVersion(entry_factory()->GetNextRevision());
841 entry.PutIsUnappliedUpdate(true);
842 entry.PutServerParentId(TestIdFactory::root());
843 entry.PutServerIsDel(true);
846 // Create a local child of the server-deleted directory.
847 entry_factory()->CreateUnsyncedItem(
848 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
849 "child", false, BOOKMARKS, NULL);
851 // The server's request to delete the directory must be ignored, otherwise our
852 // unsynced new child would be orphaned. This is a hierarchy conflict.
854 sessions::StatusController status;
855 ApplyBookmarkUpdates(&status);
857 // This should count as a hierarchy conflict.
858 const UpdateCounters& counters = GetBookmarksUpdateCounters();
859 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
862 syncable::ReadTransaction trans(FROM_HERE, directory());
863 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
864 ASSERT_TRUE(parent.good());
865 EXPECT_TRUE(parent.GetIsUnappliedUpdate());
866 EXPECT_FALSE(parent.GetIsUnsynced());
870 // Attempt to apply updates where the updated item's parent is not known to this
871 // client. The update application attempt should fail.
872 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
873 HierarchyConflictUnknownParent) {
874 // We shouldn't be able to do anything with either of these items.
875 int64 x_handle = entry_factory()->CreateUnappliedNewItemWithParent(
876 "some_item", DefaultBookmarkSpecifics(), "unknown_parent");
877 int64 y_handle = entry_factory()->CreateUnappliedNewItemWithParent(
878 "some_other_item", DefaultBookmarkSpecifics(), "some_item");
880 sessions::StatusController status;
881 ApplyBookmarkUpdates(&status);
883 const UpdateCounters& counters = GetBookmarksUpdateCounters();
884 EXPECT_EQ(2, counters.num_hierarchy_conflict_application_failures)
885 << "All updates with an unknown ancestors should be in conflict";
886 EXPECT_EQ(0, counters.num_updates_applied)
887 << "No item with an unknown ancestor should be applied";
890 syncable::ReadTransaction trans(FROM_HERE, directory());
891 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
892 syncable::Entry y(&trans, syncable::GET_BY_HANDLE, y_handle);
893 ASSERT_TRUE(x.good());
894 ASSERT_TRUE(y.good());
895 EXPECT_TRUE(x.GetIsUnappliedUpdate());
896 EXPECT_TRUE(y.GetIsUnappliedUpdate());
897 EXPECT_FALSE(x.GetIsUnsynced());
898 EXPECT_FALSE(y.GetIsUnsynced());
902 // Attempt application of a mix of items. Some update application attempts will
903 // fail due to hierarchy conflicts. Others should succeed.
904 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, ItemsBothKnownAndUnknown) {
905 // See what happens when there's a mixture of good and bad updates.
906 std::string root_server_id = syncable::GetNullId().GetServerId();
907 int64 u1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
908 "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
909 int64 k1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
910 "first_known_item", DefaultBookmarkSpecifics(), root_server_id);
911 int64 u2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
912 "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
913 int64 k2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
914 "second_known_item", DefaultBookmarkSpecifics(), "first_known_item");
915 int64 k3_handle = entry_factory()->CreateUnappliedNewItemWithParent(
916 "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item");
917 int64 k4_handle = entry_factory()->CreateUnappliedNewItemWithParent(
918 "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id);
920 sessions::StatusController status;
921 ApplyBookmarkUpdates(&status);
923 const UpdateCounters& counters = GetBookmarksUpdateCounters();
924 EXPECT_EQ(2, counters.num_hierarchy_conflict_application_failures)
925 << "The updates with unknown ancestors should be in conflict";
926 EXPECT_EQ(4, counters.num_updates_applied)
927 << "The updates with known ancestors should be successfully applied";
930 syncable::ReadTransaction trans(FROM_HERE, directory());
931 syncable::Entry u1(&trans, syncable::GET_BY_HANDLE, u1_handle);
932 syncable::Entry u2(&trans, syncable::GET_BY_HANDLE, u2_handle);
933 syncable::Entry k1(&trans, syncable::GET_BY_HANDLE, k1_handle);
934 syncable::Entry k2(&trans, syncable::GET_BY_HANDLE, k2_handle);
935 syncable::Entry k3(&trans, syncable::GET_BY_HANDLE, k3_handle);
936 syncable::Entry k4(&trans, syncable::GET_BY_HANDLE, k4_handle);
937 ASSERT_TRUE(u1.good());
938 ASSERT_TRUE(u2.good());
939 ASSERT_TRUE(k1.good());
940 ASSERT_TRUE(k2.good());
941 ASSERT_TRUE(k3.good());
942 ASSERT_TRUE(k4.good());
943 EXPECT_TRUE(u1.GetIsUnappliedUpdate());
944 EXPECT_TRUE(u2.GetIsUnappliedUpdate());
945 EXPECT_FALSE(k1.GetIsUnappliedUpdate());
946 EXPECT_FALSE(k2.GetIsUnappliedUpdate());
947 EXPECT_FALSE(k3.GetIsUnappliedUpdate());
948 EXPECT_FALSE(k4.GetIsUnappliedUpdate());
952 // Attempt application of password upates where the passphrase is known.
953 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, DecryptablePassword) {
954 // Decryptable password updates should be applied.
955 Cryptographer* cryptographer;
957 // Storing the cryptographer separately is bad, but for this test we
958 // know it's safe.
959 syncable::ReadTransaction trans(FROM_HERE, directory());
960 cryptographer = directory()->GetCryptographer(&trans);
963 KeyParams params = {"localhost", "dummy", "foobar"};
964 cryptographer->AddKey(params);
966 sync_pb::EntitySpecifics specifics;
967 sync_pb::PasswordSpecificsData data;
968 data.set_origin("http://example.com");
970 cryptographer->Encrypt(data,
971 specifics.mutable_password()->mutable_encrypted());
972 int64 handle =
973 entry_factory()->CreateUnappliedNewItem("item", specifics, false);
975 sessions::StatusController status;
976 ApplyPasswordUpdates(&status);
978 const UpdateCounters& counters = GetPasswordsUpdateCounters();
979 EXPECT_EQ(1, counters.num_updates_applied)
980 << "The updates that can be decrypted should be applied";
983 syncable::ReadTransaction trans(FROM_HERE, directory());
984 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
985 ASSERT_TRUE(e.good());
986 EXPECT_FALSE(e.GetIsUnappliedUpdate());
987 EXPECT_FALSE(e.GetIsUnsynced());
991 // Attempt application of encrypted items when the passphrase is not known.
992 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, UndecryptableData) {
993 // Undecryptable updates should not be applied.
994 sync_pb::EntitySpecifics encrypted_bookmark;
995 encrypted_bookmark.mutable_encrypted();
996 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
997 std::string root_server_id = syncable::GetNullId().GetServerId();
998 int64 folder_handle = entry_factory()->CreateUnappliedNewItemWithParent(
999 "folder",
1000 encrypted_bookmark,
1001 root_server_id);
1002 int64 bookmark_handle = entry_factory()->CreateUnappliedNewItem(
1003 "item2",
1004 encrypted_bookmark,
1005 false);
1006 sync_pb::EntitySpecifics encrypted_password;
1007 encrypted_password.mutable_password();
1008 int64 password_handle = entry_factory()->CreateUnappliedNewItem(
1009 "item3",
1010 encrypted_password,
1011 false);
1013 sessions::StatusController status;
1014 ApplyBookmarkUpdates(&status);
1015 ApplyPasswordUpdates(&status);
1017 const UpdateCounters& bm_counters = GetBookmarksUpdateCounters();
1018 EXPECT_EQ(2, bm_counters.num_encryption_conflict_application_failures)
1019 << "Updates that can't be decrypted should be in encryption conflict";
1020 EXPECT_EQ(0, bm_counters.num_updates_applied)
1021 << "No update that can't be decrypted should be applied";
1023 const UpdateCounters& pw_counters = GetPasswordsUpdateCounters();
1024 EXPECT_EQ(1, pw_counters.num_encryption_conflict_application_failures)
1025 << "Updates that can't be decrypted should be in encryption conflict";
1026 EXPECT_EQ(0, pw_counters.num_updates_applied)
1027 << "No update that can't be decrypted should be applied";
1030 syncable::ReadTransaction trans(FROM_HERE, directory());
1031 syncable::Entry folder(&trans, syncable::GET_BY_HANDLE, folder_handle);
1032 syncable::Entry bm(&trans, syncable::GET_BY_HANDLE, bookmark_handle);
1033 syncable::Entry pw(&trans, syncable::GET_BY_HANDLE, password_handle);
1034 ASSERT_TRUE(folder.good());
1035 ASSERT_TRUE(bm.good());
1036 ASSERT_TRUE(pw.good());
1037 EXPECT_TRUE(folder.GetIsUnappliedUpdate());
1038 EXPECT_TRUE(bm.GetIsUnappliedUpdate());
1039 EXPECT_TRUE(pw.GetIsUnappliedUpdate());
1043 // Test a mix of decryptable and undecryptable updates.
1044 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SomeUndecryptablePassword) {
1045 Cryptographer* cryptographer;
1047 int64 decryptable_handle = -1;
1048 int64 undecryptable_handle = -1;
1050 // Only decryptable password updates should be applied.
1052 sync_pb::EntitySpecifics specifics;
1053 sync_pb::PasswordSpecificsData data;
1054 data.set_origin("http://example.com/1");
1056 syncable::ReadTransaction trans(FROM_HERE, directory());
1057 cryptographer = directory()->GetCryptographer(&trans);
1059 KeyParams params = {"localhost", "dummy", "foobar"};
1060 cryptographer->AddKey(params);
1062 cryptographer->Encrypt(data,
1063 specifics.mutable_password()->mutable_encrypted());
1065 decryptable_handle =
1066 entry_factory()->CreateUnappliedNewItem("item1", specifics, false);
1069 // Create a new cryptographer, independent of the one in the session.
1070 Cryptographer other_cryptographer(cryptographer->encryptor());
1071 KeyParams params = {"localhost", "dummy", "bazqux"};
1072 other_cryptographer.AddKey(params);
1074 sync_pb::EntitySpecifics specifics;
1075 sync_pb::PasswordSpecificsData data;
1076 data.set_origin("http://example.com/2");
1078 other_cryptographer.Encrypt(data,
1079 specifics.mutable_password()->mutable_encrypted());
1080 undecryptable_handle =
1081 entry_factory()->CreateUnappliedNewItem("item2", specifics, false);
1084 sessions::StatusController status;
1085 ApplyPasswordUpdates(&status);
1087 const UpdateCounters& counters = GetPasswordsUpdateCounters();
1088 EXPECT_EQ(1, counters.num_encryption_conflict_application_failures)
1089 << "The updates that can't be decrypted should be in encryption "
1090 << "conflict";
1091 EXPECT_EQ(1, counters.num_updates_applied)
1092 << "The undecryptable password update shouldn't be applied";
1095 syncable::ReadTransaction trans(FROM_HERE, directory());
1096 syncable::Entry e1(&trans, syncable::GET_BY_HANDLE, decryptable_handle);
1097 syncable::Entry e2(&trans, syncable::GET_BY_HANDLE, undecryptable_handle);
1098 ASSERT_TRUE(e1.good());
1099 ASSERT_TRUE(e2.good());
1100 EXPECT_FALSE(e1.GetIsUnappliedUpdate());
1101 EXPECT_TRUE(e2.GetIsUnappliedUpdate());
1105 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
1106 SimpleConflictDifferentAttachmentMetadata) {
1107 const bool is_folder = false;
1108 sync_pb::EntitySpecifics specifics;
1109 *specifics.mutable_article() = sync_pb::ArticleSpecifics();
1110 int64 handle = entry_factory()->CreateSyncedItem("art1", ARTICLES, is_folder);
1112 sync_pb::AttachmentIdProto local_attachment_id = CreateAttachmentIdProto();
1113 sync_pb::AttachmentIdProto server_attachment_id = CreateAttachmentIdProto();
1115 // Add an attachment to the local attachment metadata.
1116 sync_pb::AttachmentMetadata local_metadata;
1117 sync_pb::AttachmentMetadataRecord* local_record = local_metadata.add_record();
1118 *local_record->mutable_id() = local_attachment_id;
1119 local_record->set_is_on_server(true);
1120 entry_factory()->SetLocalAttachmentMetadataForItem(handle, local_metadata);
1122 // Add a different attachment to the server attachment metadata.
1123 sync_pb::AttachmentMetadata server_metadata;
1124 sync_pb::AttachmentMetadataRecord* server_record =
1125 server_metadata.add_record();
1126 *server_record->mutable_id() = server_attachment_id;
1127 server_record->set_is_on_server(true);
1128 entry_factory()->SetServerAttachmentMetadataForItem(handle, server_metadata);
1130 // At this point we have a simple conflict. The server says art1 should have
1131 // server_attachment_id, but the local sync engine says it should have
1132 // local_attachment_id.
1134 sessions::StatusController status;
1135 ApplyArticlesUpdates(&status);
1137 // See that the server won.
1138 const UpdateCounters& counters = GetArticlesUpdateCounters();
1139 EXPECT_EQ(1, counters.num_updates_applied);
1140 EXPECT_EQ(1, counters.num_local_overwrites);
1141 EXPECT_EQ(0, counters.num_server_overwrites);
1142 local_metadata = entry_factory()->GetLocalAttachmentMetadataForItem(handle);
1143 EXPECT_EQ(server_metadata.SerializeAsString(),
1144 local_metadata.SerializeAsString());
1147 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
1148 SimpleConflictSameAttachmentMetadataDifferentOrder) {
1149 const bool is_folder = false;
1150 sync_pb::EntitySpecifics specifics;
1151 *specifics.mutable_article() = sync_pb::ArticleSpecifics();
1152 int64 handle = entry_factory()->CreateSyncedItem("art1", ARTICLES, is_folder);
1154 sync_pb::AttachmentIdProto id1 = CreateAttachmentIdProto();
1155 sync_pb::AttachmentIdProto id2 = CreateAttachmentIdProto();
1157 // Add id1, then id2 to the local attachment metadata.
1158 sync_pb::AttachmentMetadata local_metadata;
1159 sync_pb::AttachmentMetadataRecord* record = local_metadata.add_record();
1160 *record->mutable_id() = id1;
1161 record->set_is_on_server(true);
1162 record = local_metadata.add_record();
1163 *record->mutable_id() = id2;
1164 record->set_is_on_server(true);
1165 entry_factory()->SetLocalAttachmentMetadataForItem(handle, local_metadata);
1167 // Add id1 and id2 to the server attachment metadata, but in reverse order.
1168 sync_pb::AttachmentMetadata server_metadata;
1169 record = server_metadata.add_record();
1170 *record->mutable_id() = id2;
1171 record->set_is_on_server(true);
1172 record = local_metadata.add_record();
1173 *record->mutable_id() = id1;
1174 record->set_is_on_server(true);
1175 entry_factory()->SetServerAttachmentMetadataForItem(handle, server_metadata);
1177 // At this point we have a (false) conflict.
1179 sessions::StatusController status;
1180 ApplyArticlesUpdates(&status);
1182 // See that the server won.
1183 const UpdateCounters& counters = GetArticlesUpdateCounters();
1184 EXPECT_EQ(1, counters.num_updates_applied);
1185 EXPECT_EQ(1, counters.num_local_overwrites);
1186 EXPECT_EQ(0, counters.num_server_overwrites);
1187 local_metadata = entry_factory()->GetLocalAttachmentMetadataForItem(handle);
1188 EXPECT_EQ(server_metadata.SerializeAsString(),
1189 local_metadata.SerializeAsString());
1192 } // namespace syncer