[SyncFS] Build indexes from FileTracker entries on disk.
[chromium-blink-merge.git] / sync / engine / directory_update_handler_unittest.cc
blob837e9f418c4e912e43d62bca5f4384069b468f2a
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/model_type.h"
12 #include "sync/internal_api/public/test/test_entry_factory.h"
13 #include "sync/protocol/sync.pb.h"
14 #include "sync/sessions/directory_type_debug_info_emitter.h"
15 #include "sync/sessions/status_controller.h"
16 #include "sync/syncable/directory.h"
17 #include "sync/syncable/entry.h"
18 #include "sync/syncable/mutable_entry.h"
19 #include "sync/syncable/syncable_model_neutral_write_transaction.h"
20 #include "sync/syncable/syncable_proto_util.h"
21 #include "sync/syncable/syncable_read_transaction.h"
22 #include "sync/syncable/syncable_write_transaction.h"
23 #include "sync/test/engine/fake_model_worker.h"
24 #include "sync/test/engine/test_directory_setter_upper.h"
25 #include "sync/test/engine/test_id_factory.h"
26 #include "sync/test/engine/test_syncable_utils.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 namespace syncer {
31 using syncable::UNITTEST;
33 static const int64 kDefaultVersion = 1000;
35 // A test harness for tests that focus on processing updates.
37 // Update processing is what occurs when we first download updates. It converts
38 // the received protobuf message into information in the syncable::Directory.
39 // Any invalid or redundant updates will be dropped at this point.
40 class DirectoryUpdateHandlerProcessUpdateTest : public ::testing::Test {
41 public:
42 DirectoryUpdateHandlerProcessUpdateTest()
43 : ui_worker_(new FakeModelWorker(GROUP_UI)) {
46 virtual ~DirectoryUpdateHandlerProcessUpdateTest() {}
48 virtual void SetUp() OVERRIDE {
49 dir_maker_.SetUp();
52 virtual void TearDown() OVERRIDE {
53 dir_maker_.TearDown();
56 syncable::Directory* dir() {
57 return dir_maker_.directory();
60 protected:
61 scoped_ptr<sync_pb::SyncEntity> CreateUpdate(
62 const std::string& id,
63 const std::string& parent,
64 const ModelType& type);
66 // This exists mostly to give tests access to the protected member function.
67 // Warning: This takes the syncable directory lock.
68 void UpdateSyncEntities(
69 DirectoryUpdateHandler* handler,
70 const SyncEntityList& applicable_updates,
71 sessions::StatusController* status);
73 // Another function to access private member functions.
74 void UpdateProgressMarkers(
75 DirectoryUpdateHandler* handler,
76 const sync_pb::DataTypeProgressMarker& progress);
78 scoped_refptr<FakeModelWorker> ui_worker() {
79 return ui_worker_;
82 bool EntryExists(const std::string& id) {
83 syncable::ReadTransaction trans(FROM_HERE, dir());
84 syncable::Entry e(&trans, syncable::GET_BY_ID,
85 syncable::Id::CreateFromServerId(id));
86 return e.good() && !e.GetIsDel();
89 protected:
90 // Used in the construction of DirectoryTypeDebugInfoEmitters.
91 ObserverList<TypeDebugInfoObserver> type_observers_;
93 private:
94 base::MessageLoop loop_; // Needed to initialize the directory.
95 TestDirectorySetterUpper dir_maker_;
96 scoped_refptr<FakeModelWorker> ui_worker_;
99 scoped_ptr<sync_pb::SyncEntity>
100 DirectoryUpdateHandlerProcessUpdateTest::CreateUpdate(
101 const std::string& id,
102 const std::string& parent,
103 const ModelType& type) {
104 scoped_ptr<sync_pb::SyncEntity> e(new sync_pb::SyncEntity());
105 e->set_id_string(id);
106 e->set_parent_id_string(parent);
107 e->set_non_unique_name(id);
108 e->set_name(id);
109 e->set_version(kDefaultVersion);
110 AddDefaultFieldValue(type, e->mutable_specifics());
111 return e.Pass();
114 void DirectoryUpdateHandlerProcessUpdateTest::UpdateSyncEntities(
115 DirectoryUpdateHandler* handler,
116 const SyncEntityList& applicable_updates,
117 sessions::StatusController* status) {
118 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, UNITTEST, dir());
119 handler->UpdateSyncEntities(&trans, applicable_updates, status);
122 void DirectoryUpdateHandlerProcessUpdateTest::UpdateProgressMarkers(
123 DirectoryUpdateHandler* handler,
124 const sync_pb::DataTypeProgressMarker& progress) {
125 handler->UpdateProgressMarker(progress);
128 static const char kCacheGuid[] = "IrcjZ2jyzHDV9Io4+zKcXQ==";
130 // Test that the bookmark tag is set on newly downloaded items.
131 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, NewBookmarkTag) {
132 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
133 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
134 sync_pb::GetUpdatesResponse gu_response;
135 sessions::StatusController status;
137 // Add a bookmark item to the update message.
138 std::string root = syncable::GetNullId().GetServerId();
139 syncable::Id server_id = syncable::Id::CreateFromServerId("b1");
140 scoped_ptr<sync_pb::SyncEntity> e =
141 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
142 e->set_originator_cache_guid(
143 std::string(kCacheGuid, arraysize(kCacheGuid)-1));
144 syncable::Id client_id = syncable::Id::CreateFromClientString("-2");
145 e->set_originator_client_item_id(client_id.GetServerId());
146 e->set_position_in_parent(0);
148 // Add it to the applicable updates list.
149 SyncEntityList bookmark_updates;
150 bookmark_updates.push_back(e.get());
152 // Process the update.
153 UpdateSyncEntities(&handler, bookmark_updates, &status);
155 syncable::ReadTransaction trans(FROM_HERE, dir());
156 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
157 ASSERT_TRUE(entry.good());
158 EXPECT_TRUE(UniquePosition::IsValidSuffix(entry.GetUniqueBookmarkTag()));
159 EXPECT_TRUE(entry.GetServerUniquePosition().IsValid());
161 // If this assertion fails, that might indicate that the algorithm used to
162 // generate bookmark tags has been modified. This could have implications for
163 // bookmark ordering. Please make sure you know what you're doing if you
164 // intend to make such a change.
165 EXPECT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", entry.GetUniqueBookmarkTag());
168 // Test the receipt of a type root node.
169 TEST_F(DirectoryUpdateHandlerProcessUpdateTest,
170 ReceiveServerCreatedBookmarkFolders) {
171 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
172 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
173 sync_pb::GetUpdatesResponse gu_response;
174 sessions::StatusController status;
176 // Create an update that mimics the bookmark root.
177 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
178 std::string root = syncable::GetNullId().GetServerId();
179 scoped_ptr<sync_pb::SyncEntity> e =
180 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
181 e->set_server_defined_unique_tag("google_chrome_bookmarks");
182 e->set_folder(true);
184 // Add it to the applicable updates list.
185 SyncEntityList bookmark_updates;
186 bookmark_updates.push_back(e.get());
188 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
190 // Process it.
191 UpdateSyncEntities(&handler, bookmark_updates, &status);
193 // Verify the results.
194 syncable::ReadTransaction trans(FROM_HERE, dir());
195 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
196 ASSERT_TRUE(entry.good());
198 EXPECT_FALSE(entry.ShouldMaintainPosition());
199 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
200 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
201 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
204 // Test the receipt of a non-bookmark item.
205 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ReceiveNonBookmarkItem) {
206 DirectoryTypeDebugInfoEmitter emitter(AUTOFILL, &type_observers_);
207 DirectoryUpdateHandler handler(dir(), AUTOFILL, ui_worker(), &emitter);
208 sync_pb::GetUpdatesResponse gu_response;
209 sessions::StatusController status;
211 std::string root = syncable::GetNullId().GetServerId();
212 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
213 scoped_ptr<sync_pb::SyncEntity> e =
214 CreateUpdate(SyncableIdToProto(server_id), root, AUTOFILL);
215 e->set_server_defined_unique_tag("9PGRuKdX5sHyGMB17CvYTXuC43I=");
217 // Add it to the applicable updates list.
218 SyncEntityList autofill_updates;
219 autofill_updates.push_back(e.get());
221 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
223 // Process it.
224 UpdateSyncEntities(&handler, autofill_updates, &status);
226 syncable::ReadTransaction trans(FROM_HERE, dir());
227 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
228 ASSERT_TRUE(entry.good());
230 EXPECT_FALSE(entry.ShouldMaintainPosition());
231 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
232 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
233 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
236 // Tests the setting of progress markers.
237 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ProcessNewProgressMarkers) {
238 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
239 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
241 sync_pb::DataTypeProgressMarker progress;
242 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS));
243 progress.set_token("token");
245 UpdateProgressMarkers(&handler, progress);
247 sync_pb::DataTypeProgressMarker saved;
248 dir()->GetDownloadProgress(BOOKMARKS, &saved);
250 EXPECT_EQ(progress.token(), saved.token());
251 EXPECT_EQ(progress.data_type_id(), saved.data_type_id());
254 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, GarbageCollectionByVersion) {
255 DirectoryTypeDebugInfoEmitter emitter(SYNCED_NOTIFICATIONS, &type_observers_);
256 DirectoryUpdateHandler handler(dir(), SYNCED_NOTIFICATIONS,
257 ui_worker(), &emitter);
258 sessions::StatusController status;
260 sync_pb::DataTypeProgressMarker progress;
261 progress.set_data_type_id(
262 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
263 progress.set_token("token");
264 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 10);
266 sync_pb::DataTypeContext context;
267 context.set_data_type_id(
268 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
269 context.set_context("context");
270 context.set_version(1);
272 scoped_ptr<sync_pb::SyncEntity> type_root =
273 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("root")),
274 syncable::GetNullId().GetServerId(),
275 SYNCED_NOTIFICATIONS);
276 type_root->set_server_defined_unique_tag(
277 ModelTypeToRootTag(SYNCED_NOTIFICATIONS));
278 type_root->set_folder(true);
280 scoped_ptr<sync_pb::SyncEntity> e1 =
281 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e1")),
282 type_root->id_string(),
283 SYNCED_NOTIFICATIONS);
285 scoped_ptr<sync_pb::SyncEntity> e2 =
286 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e2")),
287 type_root->id_string(),
288 SYNCED_NOTIFICATIONS);
289 e2->set_version(kDefaultVersion + 100);
291 // Add to the applicable updates list.
292 SyncEntityList updates;
293 updates.push_back(type_root.get());
294 updates.push_back(e1.get());
295 updates.push_back(e2.get());
297 // Process and apply updates.
298 EXPECT_EQ(
299 SYNCER_OK,
300 handler.ProcessGetUpdatesResponse(progress, context, updates, &status));
301 handler.ApplyUpdates(&status);
303 // Verify none is deleted because they are unapplied during GC.
304 EXPECT_TRUE(EntryExists(type_root->id_string()));
305 EXPECT_TRUE(EntryExists(e1->id_string()));
306 EXPECT_TRUE(EntryExists(e2->id_string()));
308 // Process and apply again. Old entry is deleted but not root.
309 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 20);
310 EXPECT_EQ(SYNCER_OK,
311 handler.ProcessGetUpdatesResponse(
312 progress, context, SyncEntityList(), &status));
313 handler.ApplyUpdates(&status);
314 EXPECT_TRUE(EntryExists(type_root->id_string()));
315 EXPECT_FALSE(EntryExists(e1->id_string()));
316 EXPECT_TRUE(EntryExists(e2->id_string()));
319 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ContextVersion) {
320 DirectoryTypeDebugInfoEmitter emitter(SYNCED_NOTIFICATIONS, &type_observers_);
321 DirectoryUpdateHandler handler(dir(), SYNCED_NOTIFICATIONS,
322 ui_worker(), &emitter);
323 sessions::StatusController status;
324 int field_number = GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS);
326 sync_pb::DataTypeProgressMarker progress;
327 progress.set_data_type_id(
328 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
329 progress.set_token("token");
331 sync_pb::DataTypeContext old_context;
332 old_context.set_version(1);
333 old_context.set_context("data");
334 old_context.set_data_type_id(field_number);
336 scoped_ptr<sync_pb::SyncEntity> type_root =
337 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("root")),
338 syncable::GetNullId().GetServerId(),
339 SYNCED_NOTIFICATIONS);
340 type_root->set_server_defined_unique_tag(
341 ModelTypeToRootTag(SYNCED_NOTIFICATIONS));
342 type_root->set_folder(true);
343 scoped_ptr<sync_pb::SyncEntity> e1 =
344 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e1")),
345 type_root->id_string(),
346 SYNCED_NOTIFICATIONS);
348 SyncEntityList updates;
349 updates.push_back(type_root.get());
350 updates.push_back(e1.get());
352 // The first response should be processed fine.
353 EXPECT_EQ(SYNCER_OK,
354 handler.ProcessGetUpdatesResponse(
355 progress, old_context, updates, &status));
356 handler.ApplyUpdates(&status);
358 EXPECT_TRUE(EntryExists(type_root->id_string()));
359 EXPECT_TRUE(EntryExists(e1->id_string()));
362 sync_pb::DataTypeContext dir_context;
363 syncable::ReadTransaction trans(FROM_HERE, dir());
364 trans.directory()->GetDataTypeContext(
365 &trans, SYNCED_NOTIFICATIONS, &dir_context);
366 EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
369 sync_pb::DataTypeContext new_context;
370 new_context.set_version(0);
371 new_context.set_context("old");
372 new_context.set_data_type_id(field_number);
374 scoped_ptr<sync_pb::SyncEntity> e2 =
375 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e2")),
376 type_root->id_string(),
377 SYNCED_NOTIFICATIONS);
378 updates.clear();
379 updates.push_back(e2.get());
381 // The second response, with an old context version, should result in an
382 // error and the updates should be dropped.
383 EXPECT_EQ(DATATYPE_TRIGGERED_RETRY,
384 handler.ProcessGetUpdatesResponse(
385 progress, new_context, updates, &status));
386 handler.ApplyUpdates(&status);
388 EXPECT_FALSE(EntryExists(e2->id_string()));
391 sync_pb::DataTypeContext dir_context;
392 syncable::ReadTransaction trans(FROM_HERE, dir());
393 trans.directory()->GetDataTypeContext(
394 &trans, SYNCED_NOTIFICATIONS, &dir_context);
395 EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
399 // A test harness for tests that focus on applying updates.
401 // Update application is performed when we want to take updates that were
402 // previously downloaded, processed, and stored in our syncable::Directory
403 // and use them to update our local state (both the Directory's local state
404 // and the model's local state, though these tests focus only on the Directory's
405 // local state).
407 // This is kept separate from the update processing test in part for historical
408 // reasons, and in part because these tests may require a bit more infrastrcture
409 // in the future. Update application should happen on a different thread a lot
410 // of the time so these tests may end up requiring more infrastructure than the
411 // update processing tests. Currently, we're bypassing most of those issues by
412 // using FakeModelWorkers, so there's not much difference between the two test
413 // harnesses.
414 class DirectoryUpdateHandlerApplyUpdateTest : public ::testing::Test {
415 public:
416 DirectoryUpdateHandlerApplyUpdateTest()
417 : ui_worker_(new FakeModelWorker(GROUP_UI)),
418 password_worker_(new FakeModelWorker(GROUP_PASSWORD)),
419 passive_worker_(new FakeModelWorker(GROUP_PASSIVE)),
420 bookmarks_emitter_(BOOKMARKS, &type_observers_),
421 passwords_emitter_(PASSWORDS, &type_observers_),
422 update_handler_map_deleter_(&update_handler_map_) {}
424 virtual void SetUp() OVERRIDE {
425 dir_maker_.SetUp();
426 entry_factory_.reset(new TestEntryFactory(directory()));
428 update_handler_map_.insert(std::make_pair(
429 BOOKMARKS,
430 new DirectoryUpdateHandler(directory(), BOOKMARKS,
431 ui_worker_, &bookmarks_emitter_)));
432 update_handler_map_.insert(std::make_pair(
433 PASSWORDS,
434 new DirectoryUpdateHandler(directory(),
435 PASSWORDS,
436 password_worker_,
437 &passwords_emitter_)));
440 virtual void TearDown() OVERRIDE {
441 dir_maker_.TearDown();
444 const UpdateCounters& GetBookmarksUpdateCounters() {
445 return bookmarks_emitter_.GetUpdateCounters();
448 const UpdateCounters& GetPasswordsUpdateCounters() {
449 return passwords_emitter_.GetUpdateCounters();
452 protected:
453 void ApplyBookmarkUpdates(sessions::StatusController* status) {
454 update_handler_map_[BOOKMARKS]->ApplyUpdates(status);
457 void ApplyPasswordUpdates(sessions::StatusController* status) {
458 update_handler_map_[PASSWORDS]->ApplyUpdates(status);
461 TestEntryFactory* entry_factory() {
462 return entry_factory_.get();
465 syncable::Directory* directory() {
466 return dir_maker_.directory();
469 private:
470 typedef std::map<ModelType, UpdateHandler*> UpdateHandlerMap;
472 base::MessageLoop loop_; // Needed to initialize the directory.
473 TestDirectorySetterUpper dir_maker_;
474 scoped_ptr<TestEntryFactory> entry_factory_;
476 scoped_refptr<FakeModelWorker> ui_worker_;
477 scoped_refptr<FakeModelWorker> password_worker_;
478 scoped_refptr<FakeModelWorker> passive_worker_;
480 ObserverList<TypeDebugInfoObserver> type_observers_;
481 DirectoryTypeDebugInfoEmitter bookmarks_emitter_;
482 DirectoryTypeDebugInfoEmitter passwords_emitter_;
484 UpdateHandlerMap update_handler_map_;
485 STLValueDeleter<UpdateHandlerMap> update_handler_map_deleter_;
488 namespace {
489 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
490 sync_pb::EntitySpecifics result;
491 AddDefaultFieldValue(BOOKMARKS, &result);
492 return result;
494 } // namespace
496 // Test update application for a few bookmark items.
497 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SimpleBookmark) {
498 sessions::StatusController status;
500 std::string root_server_id = syncable::GetNullId().GetServerId();
501 int64 parent_handle =
502 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
503 "parent", DefaultBookmarkSpecifics(), root_server_id);
504 int64 child_handle =
505 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
506 "child", DefaultBookmarkSpecifics(), "parent");
508 ApplyBookmarkUpdates(&status);
510 const UpdateCounters& counter = GetBookmarksUpdateCounters();
511 EXPECT_EQ(0, counter.num_encryption_conflict_application_failures)
512 << "Simple update shouldn't result in conflicts";
513 EXPECT_EQ(0, counter.num_hierarchy_conflict_application_failures)
514 << "Simple update shouldn't result in conflicts";
515 EXPECT_EQ(2, counter.num_updates_applied)
516 << "All items should have been successfully applied";
519 syncable::ReadTransaction trans(FROM_HERE, directory());
521 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
522 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
524 ASSERT_TRUE(parent.good());
525 ASSERT_TRUE(child.good());
527 EXPECT_FALSE(parent.GetIsUnsynced());
528 EXPECT_FALSE(parent.GetIsUnappliedUpdate());
529 EXPECT_FALSE(child.GetIsUnsynced());
530 EXPECT_FALSE(child.GetIsUnappliedUpdate());
534 // Test that the applicator can handle updates delivered out of order.
535 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
536 BookmarkChildrenBeforeParent) {
537 // Start with some bookmarks whose parents are unknown.
538 std::string root_server_id = syncable::GetNullId().GetServerId();
539 int64 a_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
540 "a_child_created_first", DefaultBookmarkSpecifics(), "parent");
541 int64 x_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
542 "x_child_created_first", DefaultBookmarkSpecifics(), "parent");
544 // Update application will fail.
545 sessions::StatusController status1;
546 ApplyBookmarkUpdates(&status1);
547 EXPECT_EQ(0, status1.num_updates_applied());
548 EXPECT_EQ(2, status1.num_hierarchy_conflicts());
551 syncable::ReadTransaction trans(FROM_HERE, directory());
553 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
554 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
556 ASSERT_TRUE(a.good());
557 ASSERT_TRUE(x.good());
559 EXPECT_TRUE(a.GetIsUnappliedUpdate());
560 EXPECT_TRUE(x.GetIsUnappliedUpdate());
563 // Now add their parent and a few siblings.
564 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
565 "parent", DefaultBookmarkSpecifics(), root_server_id);
566 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
567 "a_child_created_second", DefaultBookmarkSpecifics(), "parent");
568 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
569 "x_child_created_second", DefaultBookmarkSpecifics(), "parent");
571 // Update application will succeed.
572 sessions::StatusController status2;
573 ApplyBookmarkUpdates(&status2);
574 EXPECT_EQ(5, status2.num_updates_applied())
575 << "All updates should have been successfully applied";
578 syncable::ReadTransaction trans(FROM_HERE, directory());
580 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
581 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
583 ASSERT_TRUE(a.good());
584 ASSERT_TRUE(x.good());
586 EXPECT_FALSE(a.GetIsUnappliedUpdate());
587 EXPECT_FALSE(x.GetIsUnappliedUpdate());
591 // Try to apply changes on an item that is both IS_UNSYNCED and
592 // IS_UNAPPLIED_UPDATE. Conflict resolution should be performed.
593 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SimpleBookmarkConflict) {
594 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem("x");
596 int original_server_version = -10;
598 syncable::ReadTransaction trans(FROM_HERE, directory());
599 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
600 original_server_version = e.GetServerVersion();
601 ASSERT_NE(original_server_version, e.GetBaseVersion());
602 EXPECT_TRUE(e.GetIsUnsynced());
605 sessions::StatusController status;
606 ApplyBookmarkUpdates(&status);
608 const UpdateCounters& counters = GetBookmarksUpdateCounters();
609 EXPECT_EQ(1, counters.num_server_overwrites)
610 << "Unsynced and unapplied item conflict should be resolved";
611 EXPECT_EQ(0, counters.num_updates_applied)
612 << "Update should not be applied; we should override the server.";
615 syncable::ReadTransaction trans(FROM_HERE, directory());
616 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
617 ASSERT_TRUE(e.good());
618 EXPECT_EQ(original_server_version, e.GetServerVersion());
619 EXPECT_EQ(original_server_version, e.GetBaseVersion());
620 EXPECT_FALSE(e.GetIsUnappliedUpdate());
622 // The unsynced flag will remain set until we successfully commit the item.
623 EXPECT_TRUE(e.GetIsUnsynced());
627 // Create a simple conflict that is also a hierarchy conflict. If we were to
628 // follow the normal "server wins" logic, we'd end up violating hierarchy
629 // constraints. The hierarchy conflict must take precedence. We can not allow
630 // the update to be applied. The item must remain in the conflict state.
631 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, HierarchyAndSimpleConflict) {
632 // Create a simply-conflicting item. It will start with valid parent ids.
633 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem(
634 "orphaned_by_server");
636 // Manually set the SERVER_PARENT_ID to bad value.
637 // A bad parent indicates a hierarchy conflict.
638 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
639 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
640 ASSERT_TRUE(entry.good());
642 entry.PutServerParentId(TestIdFactory::MakeServer("bogus_parent"));
645 sessions::StatusController status;
646 ApplyBookmarkUpdates(&status);
648 const UpdateCounters& counters = GetBookmarksUpdateCounters();
649 EXPECT_EQ(0, counters.num_updates_applied);
650 EXPECT_EQ(0, counters.num_server_overwrites);
651 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
654 syncable::ReadTransaction trans(FROM_HERE, directory());
655 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
656 ASSERT_TRUE(e.good());
657 EXPECT_TRUE(e.GetIsUnappliedUpdate());
658 EXPECT_TRUE(e.GetIsUnsynced());
662 // Attempt to apply an udpate that would create a bookmark folder loop. This
663 // application should fail.
664 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, BookmarkFolderLoop) {
665 // Item 'X' locally has parent of 'root'. Server is updating it to have
666 // parent of 'Y'.
668 // Create it as a child of root node.
669 int64 handle = entry_factory()->CreateSyncedItem("X", BOOKMARKS, true);
672 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
673 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
674 ASSERT_TRUE(entry.good());
676 // Re-parent from root to "Y"
677 entry.PutServerVersion(entry_factory()->GetNextRevision());
678 entry.PutIsUnappliedUpdate(true);
679 entry.PutServerParentId(TestIdFactory::MakeServer("Y"));
682 // Item 'Y' is child of 'X'.
683 entry_factory()->CreateUnsyncedItem(
684 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
685 BOOKMARKS, NULL);
687 // If the server's update were applied, we would have X be a child of Y, and Y
688 // as a child of X. That's a directory loop. The UpdateApplicator should
689 // prevent the update from being applied and note that this is a hierarchy
690 // conflict.
692 sessions::StatusController status;
693 ApplyBookmarkUpdates(&status);
695 // This should count as a hierarchy conflict.
696 const UpdateCounters& counters = GetBookmarksUpdateCounters();
697 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
700 syncable::ReadTransaction trans(FROM_HERE, directory());
701 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
702 ASSERT_TRUE(e.good());
703 EXPECT_TRUE(e.GetIsUnappliedUpdate());
704 EXPECT_FALSE(e.GetIsUnsynced());
708 // Test update application where the update has been orphaned by a local folder
709 // deletion. The update application attempt should fail.
710 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
711 HierarchyConflictDeletedParent) {
712 // Create a locally deleted parent item.
713 int64 parent_handle;
714 entry_factory()->CreateUnsyncedItem(
715 syncable::Id::CreateFromServerId("parent"), TestIdFactory::root(),
716 "parent", true, BOOKMARKS, &parent_handle);
718 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
719 syncable::MutableEntry entry(&trans,
720 syncable::GET_BY_HANDLE,
721 parent_handle);
722 entry.PutIsDel(true);
725 // Create an incoming child from the server.
726 int64 child_handle = entry_factory()->CreateUnappliedNewItemWithParent(
727 "child", DefaultBookmarkSpecifics(), "parent");
729 // The server's update may seem valid to some other client, but on this client
730 // that new item's parent no longer exists. The update should not be applied
731 // and the update applicator should indicate this is a hierarchy conflict.
733 sessions::StatusController status;
734 ApplyBookmarkUpdates(&status);
735 const UpdateCounters& counters = GetBookmarksUpdateCounters();
736 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
739 syncable::ReadTransaction trans(FROM_HERE, directory());
740 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
741 ASSERT_TRUE(child.good());
742 EXPECT_TRUE(child.GetIsUnappliedUpdate());
743 EXPECT_FALSE(child.GetIsUnsynced());
747 // Attempt to apply an update that deletes a folder where the folder has
748 // locally-created children. The update application should fail.
749 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
750 HierarchyConflictDeleteNonEmptyDirectory) {
751 // Create a server-deleted folder as a child of root node.
752 int64 parent_handle =
753 entry_factory()->CreateSyncedItem("parent", BOOKMARKS, true);
755 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
756 syncable::MutableEntry entry(&trans,
757 syncable::GET_BY_HANDLE,
758 parent_handle);
759 ASSERT_TRUE(entry.good());
761 // Delete it on the server.
762 entry.PutServerVersion(entry_factory()->GetNextRevision());
763 entry.PutIsUnappliedUpdate(true);
764 entry.PutServerParentId(TestIdFactory::root());
765 entry.PutServerIsDel(true);
768 // Create a local child of the server-deleted directory.
769 entry_factory()->CreateUnsyncedItem(
770 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
771 "child", false, BOOKMARKS, NULL);
773 // The server's request to delete the directory must be ignored, otherwise our
774 // unsynced new child would be orphaned. This is a hierarchy conflict.
776 sessions::StatusController status;
777 ApplyBookmarkUpdates(&status);
779 // This should count as a hierarchy conflict.
780 const UpdateCounters& counters = GetBookmarksUpdateCounters();
781 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
784 syncable::ReadTransaction trans(FROM_HERE, directory());
785 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
786 ASSERT_TRUE(parent.good());
787 EXPECT_TRUE(parent.GetIsUnappliedUpdate());
788 EXPECT_FALSE(parent.GetIsUnsynced());
792 // Attempt to apply updates where the updated item's parent is not known to this
793 // client. The update application attempt should fail.
794 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
795 HierarchyConflictUnknownParent) {
796 // We shouldn't be able to do anything with either of these items.
797 int64 x_handle = entry_factory()->CreateUnappliedNewItemWithParent(
798 "some_item", DefaultBookmarkSpecifics(), "unknown_parent");
799 int64 y_handle = entry_factory()->CreateUnappliedNewItemWithParent(
800 "some_other_item", DefaultBookmarkSpecifics(), "some_item");
802 sessions::StatusController status;
803 ApplyBookmarkUpdates(&status);
805 const UpdateCounters& counters = GetBookmarksUpdateCounters();
806 EXPECT_EQ(2, counters.num_hierarchy_conflict_application_failures)
807 << "All updates with an unknown ancestors should be in conflict";
808 EXPECT_EQ(0, counters.num_updates_applied)
809 << "No item with an unknown ancestor should be applied";
812 syncable::ReadTransaction trans(FROM_HERE, directory());
813 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
814 syncable::Entry y(&trans, syncable::GET_BY_HANDLE, y_handle);
815 ASSERT_TRUE(x.good());
816 ASSERT_TRUE(y.good());
817 EXPECT_TRUE(x.GetIsUnappliedUpdate());
818 EXPECT_TRUE(y.GetIsUnappliedUpdate());
819 EXPECT_FALSE(x.GetIsUnsynced());
820 EXPECT_FALSE(y.GetIsUnsynced());
824 // Attempt application of a mix of items. Some update application attempts will
825 // fail due to hierarchy conflicts. Others should succeed.
826 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, ItemsBothKnownAndUnknown) {
827 // See what happens when there's a mixture of good and bad updates.
828 std::string root_server_id = syncable::GetNullId().GetServerId();
829 int64 u1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
830 "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
831 int64 k1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
832 "first_known_item", DefaultBookmarkSpecifics(), root_server_id);
833 int64 u2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
834 "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
835 int64 k2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
836 "second_known_item", DefaultBookmarkSpecifics(), "first_known_item");
837 int64 k3_handle = entry_factory()->CreateUnappliedNewItemWithParent(
838 "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item");
839 int64 k4_handle = entry_factory()->CreateUnappliedNewItemWithParent(
840 "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id);
842 sessions::StatusController status;
843 ApplyBookmarkUpdates(&status);
845 const UpdateCounters& counters = GetBookmarksUpdateCounters();
846 EXPECT_EQ(2, counters.num_hierarchy_conflict_application_failures)
847 << "The updates with unknown ancestors should be in conflict";
848 EXPECT_EQ(4, counters.num_updates_applied)
849 << "The updates with known ancestors should be successfully applied";
852 syncable::ReadTransaction trans(FROM_HERE, directory());
853 syncable::Entry u1(&trans, syncable::GET_BY_HANDLE, u1_handle);
854 syncable::Entry u2(&trans, syncable::GET_BY_HANDLE, u2_handle);
855 syncable::Entry k1(&trans, syncable::GET_BY_HANDLE, k1_handle);
856 syncable::Entry k2(&trans, syncable::GET_BY_HANDLE, k2_handle);
857 syncable::Entry k3(&trans, syncable::GET_BY_HANDLE, k3_handle);
858 syncable::Entry k4(&trans, syncable::GET_BY_HANDLE, k4_handle);
859 ASSERT_TRUE(u1.good());
860 ASSERT_TRUE(u2.good());
861 ASSERT_TRUE(k1.good());
862 ASSERT_TRUE(k2.good());
863 ASSERT_TRUE(k3.good());
864 ASSERT_TRUE(k4.good());
865 EXPECT_TRUE(u1.GetIsUnappliedUpdate());
866 EXPECT_TRUE(u2.GetIsUnappliedUpdate());
867 EXPECT_FALSE(k1.GetIsUnappliedUpdate());
868 EXPECT_FALSE(k2.GetIsUnappliedUpdate());
869 EXPECT_FALSE(k3.GetIsUnappliedUpdate());
870 EXPECT_FALSE(k4.GetIsUnappliedUpdate());
874 // Attempt application of password upates where the passphrase is known.
875 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, DecryptablePassword) {
876 // Decryptable password updates should be applied.
877 Cryptographer* cryptographer;
879 // Storing the cryptographer separately is bad, but for this test we
880 // know it's safe.
881 syncable::ReadTransaction trans(FROM_HERE, directory());
882 cryptographer = directory()->GetCryptographer(&trans);
885 KeyParams params = {"localhost", "dummy", "foobar"};
886 cryptographer->AddKey(params);
888 sync_pb::EntitySpecifics specifics;
889 sync_pb::PasswordSpecificsData data;
890 data.set_origin("http://example.com");
892 cryptographer->Encrypt(data,
893 specifics.mutable_password()->mutable_encrypted());
894 int64 handle =
895 entry_factory()->CreateUnappliedNewItem("item", specifics, false);
897 sessions::StatusController status;
898 ApplyPasswordUpdates(&status);
900 const UpdateCounters& counters = GetPasswordsUpdateCounters();
901 EXPECT_EQ(1, counters.num_updates_applied)
902 << "The updates that can be decrypted should be applied";
905 syncable::ReadTransaction trans(FROM_HERE, directory());
906 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
907 ASSERT_TRUE(e.good());
908 EXPECT_FALSE(e.GetIsUnappliedUpdate());
909 EXPECT_FALSE(e.GetIsUnsynced());
913 // Attempt application of encrypted items when the passphrase is not known.
914 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, UndecryptableData) {
915 // Undecryptable updates should not be applied.
916 sync_pb::EntitySpecifics encrypted_bookmark;
917 encrypted_bookmark.mutable_encrypted();
918 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
919 std::string root_server_id = syncable::GetNullId().GetServerId();
920 int64 folder_handle = entry_factory()->CreateUnappliedNewItemWithParent(
921 "folder",
922 encrypted_bookmark,
923 root_server_id);
924 int64 bookmark_handle = entry_factory()->CreateUnappliedNewItem(
925 "item2",
926 encrypted_bookmark,
927 false);
928 sync_pb::EntitySpecifics encrypted_password;
929 encrypted_password.mutable_password();
930 int64 password_handle = entry_factory()->CreateUnappliedNewItem(
931 "item3",
932 encrypted_password,
933 false);
935 sessions::StatusController status;
936 ApplyBookmarkUpdates(&status);
937 ApplyPasswordUpdates(&status);
939 const UpdateCounters& bm_counters = GetBookmarksUpdateCounters();
940 EXPECT_EQ(2, bm_counters.num_encryption_conflict_application_failures)
941 << "Updates that can't be decrypted should be in encryption conflict";
942 EXPECT_EQ(0, bm_counters.num_updates_applied)
943 << "No update that can't be decrypted should be applied";
945 const UpdateCounters& pw_counters = GetPasswordsUpdateCounters();
946 EXPECT_EQ(1, pw_counters.num_encryption_conflict_application_failures)
947 << "Updates that can't be decrypted should be in encryption conflict";
948 EXPECT_EQ(0, pw_counters.num_updates_applied)
949 << "No update that can't be decrypted should be applied";
952 syncable::ReadTransaction trans(FROM_HERE, directory());
953 syncable::Entry folder(&trans, syncable::GET_BY_HANDLE, folder_handle);
954 syncable::Entry bm(&trans, syncable::GET_BY_HANDLE, bookmark_handle);
955 syncable::Entry pw(&trans, syncable::GET_BY_HANDLE, password_handle);
956 ASSERT_TRUE(folder.good());
957 ASSERT_TRUE(bm.good());
958 ASSERT_TRUE(pw.good());
959 EXPECT_TRUE(folder.GetIsUnappliedUpdate());
960 EXPECT_TRUE(bm.GetIsUnappliedUpdate());
961 EXPECT_TRUE(pw.GetIsUnappliedUpdate());
965 // Test a mix of decryptable and undecryptable updates.
966 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SomeUndecryptablePassword) {
967 Cryptographer* cryptographer;
969 int64 decryptable_handle = -1;
970 int64 undecryptable_handle = -1;
972 // Only decryptable password updates should be applied.
974 sync_pb::EntitySpecifics specifics;
975 sync_pb::PasswordSpecificsData data;
976 data.set_origin("http://example.com/1");
978 syncable::ReadTransaction trans(FROM_HERE, directory());
979 cryptographer = directory()->GetCryptographer(&trans);
981 KeyParams params = {"localhost", "dummy", "foobar"};
982 cryptographer->AddKey(params);
984 cryptographer->Encrypt(data,
985 specifics.mutable_password()->mutable_encrypted());
987 decryptable_handle =
988 entry_factory()->CreateUnappliedNewItem("item1", specifics, false);
991 // Create a new cryptographer, independent of the one in the session.
992 Cryptographer other_cryptographer(cryptographer->encryptor());
993 KeyParams params = {"localhost", "dummy", "bazqux"};
994 other_cryptographer.AddKey(params);
996 sync_pb::EntitySpecifics specifics;
997 sync_pb::PasswordSpecificsData data;
998 data.set_origin("http://example.com/2");
1000 other_cryptographer.Encrypt(data,
1001 specifics.mutable_password()->mutable_encrypted());
1002 undecryptable_handle =
1003 entry_factory()->CreateUnappliedNewItem("item2", specifics, false);
1006 sessions::StatusController status;
1007 ApplyPasswordUpdates(&status);
1009 const UpdateCounters& counters = GetPasswordsUpdateCounters();
1010 EXPECT_EQ(1, counters.num_encryption_conflict_application_failures)
1011 << "The updates that can't be decrypted should be in encryption "
1012 << "conflict";
1013 EXPECT_EQ(1, counters.num_updates_applied)
1014 << "The undecryptable password update shouldn't be applied";
1017 syncable::ReadTransaction trans(FROM_HERE, directory());
1018 syncable::Entry e1(&trans, syncable::GET_BY_HANDLE, decryptable_handle);
1019 syncable::Entry e2(&trans, syncable::GET_BY_HANDLE, undecryptable_handle);
1020 ASSERT_TRUE(e1.good());
1021 ASSERT_TRUE(e2.good());
1022 EXPECT_FALSE(e1.GetIsUnappliedUpdate());
1023 EXPECT_TRUE(e2.GetIsUnappliedUpdate());
1027 } // namespace syncer