Update .DEPS.git
[chromium-blink-merge.git] / sync / engine / sync_directory_update_handler_unittest.cc
blob3cc8e7d11c009253b87f4e4cb515f60235b9bd68
1 // Copyright 2013 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/sync_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/status_controller.h"
15 #include "sync/syncable/directory.h"
16 #include "sync/syncable/entry.h"
17 #include "sync/syncable/mutable_entry.h"
18 #include "sync/syncable/syncable_model_neutral_write_transaction.h"
19 #include "sync/syncable/syncable_proto_util.h"
20 #include "sync/syncable/syncable_read_transaction.h"
21 #include "sync/syncable/syncable_write_transaction.h"
22 #include "sync/test/engine/fake_model_worker.h"
23 #include "sync/test/engine/test_directory_setter_upper.h"
24 #include "sync/test/engine/test_id_factory.h"
25 #include "sync/test/engine/test_syncable_utils.h"
26 #include "testing/gtest/include/gtest/gtest.h"
28 namespace syncer {
30 using syncable::UNITTEST;
32 // A test harness for tests that focus on processing updates.
34 // Update processing is what occurs when we first download updates. It converts
35 // the received protobuf message into information in the syncable::Directory.
36 // Any invalid or redundant updates will be dropped at this point.
37 class SyncDirectoryUpdateHandlerProcessUpdateTest : public ::testing::Test {
38 public:
39 SyncDirectoryUpdateHandlerProcessUpdateTest()
40 : ui_worker_(new FakeModelWorker(GROUP_UI)) {
43 virtual ~SyncDirectoryUpdateHandlerProcessUpdateTest() {}
45 virtual void SetUp() OVERRIDE {
46 dir_maker_.SetUp();
49 virtual void TearDown() OVERRIDE {
50 dir_maker_.TearDown();
53 syncable::Directory* dir() {
54 return dir_maker_.directory();
56 protected:
57 scoped_ptr<sync_pb::SyncEntity> CreateUpdate(
58 const std::string& id,
59 const std::string& parent,
60 const ModelType& type);
62 // This exists mostly to give tests access to the protected member function.
63 // Warning: This takes the syncable directory lock.
64 void UpdateSyncEntities(
65 SyncDirectoryUpdateHandler* handler,
66 const SyncEntityList& applicable_updates,
67 sessions::StatusController* status);
69 // Another function to access private member functions.
70 void UpdateProgressMarkers(
71 SyncDirectoryUpdateHandler* handler,
72 const sync_pb::DataTypeProgressMarker& progress);
74 scoped_refptr<FakeModelWorker> ui_worker() {
75 return ui_worker_;
78 private:
79 base::MessageLoop loop_; // Needed to initialize the directory.
80 TestDirectorySetterUpper dir_maker_;
81 scoped_refptr<FakeModelWorker> ui_worker_;
84 scoped_ptr<sync_pb::SyncEntity>
85 SyncDirectoryUpdateHandlerProcessUpdateTest::CreateUpdate(
86 const std::string& id,
87 const std::string& parent,
88 const ModelType& type) {
89 scoped_ptr<sync_pb::SyncEntity> e(new sync_pb::SyncEntity());
90 e->set_id_string(id);
91 e->set_parent_id_string(parent);
92 e->set_non_unique_name(id);
93 e->set_name(id);
94 e->set_version(1000);
95 AddDefaultFieldValue(type, e->mutable_specifics());
96 return e.Pass();
99 void SyncDirectoryUpdateHandlerProcessUpdateTest::UpdateSyncEntities(
100 SyncDirectoryUpdateHandler* handler,
101 const SyncEntityList& applicable_updates,
102 sessions::StatusController* status) {
103 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, UNITTEST, dir());
104 handler->UpdateSyncEntities(&trans, applicable_updates, status);
107 void SyncDirectoryUpdateHandlerProcessUpdateTest::UpdateProgressMarkers(
108 SyncDirectoryUpdateHandler* handler,
109 const sync_pb::DataTypeProgressMarker& progress) {
110 handler->UpdateProgressMarker(progress);
113 static const char kCacheGuid[] = "IrcjZ2jyzHDV9Io4+zKcXQ==";
115 // Test that the bookmark tag is set on newly downloaded items.
116 TEST_F(SyncDirectoryUpdateHandlerProcessUpdateTest, NewBookmarkTag) {
117 SyncDirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker());
118 sync_pb::GetUpdatesResponse gu_response;
119 sessions::StatusController status;
121 // Add a bookmark item to the update message.
122 std::string root = syncable::GetNullId().GetServerId();
123 syncable::Id server_id = syncable::Id::CreateFromServerId("b1");
124 scoped_ptr<sync_pb::SyncEntity> e =
125 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
126 e->set_originator_cache_guid(
127 std::string(kCacheGuid, arraysize(kCacheGuid)-1));
128 syncable::Id client_id = syncable::Id::CreateFromClientString("-2");
129 e->set_originator_client_item_id(client_id.GetServerId());
130 e->set_position_in_parent(0);
132 // Add it to the applicable updates list.
133 SyncEntityList bookmark_updates;
134 bookmark_updates.push_back(e.get());
136 // Process the update.
137 UpdateSyncEntities(&handler, bookmark_updates, &status);
139 syncable::ReadTransaction trans(FROM_HERE, dir());
140 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
141 ASSERT_TRUE(entry.good());
142 EXPECT_TRUE(UniquePosition::IsValidSuffix(entry.GetUniqueBookmarkTag()));
143 EXPECT_TRUE(entry.GetServerUniquePosition().IsValid());
145 // If this assertion fails, that might indicate that the algorithm used to
146 // generate bookmark tags has been modified. This could have implications for
147 // bookmark ordering. Please make sure you know what you're doing if you
148 // intend to make such a change.
149 EXPECT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", entry.GetUniqueBookmarkTag());
152 // Test the receipt of a type root node.
153 TEST_F(SyncDirectoryUpdateHandlerProcessUpdateTest,
154 ReceiveServerCreatedBookmarkFolders) {
155 SyncDirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker());
156 sync_pb::GetUpdatesResponse gu_response;
157 sessions::StatusController status;
159 // Create an update that mimics the bookmark root.
160 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
161 std::string root = syncable::GetNullId().GetServerId();
162 scoped_ptr<sync_pb::SyncEntity> e =
163 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
164 e->set_server_defined_unique_tag("google_chrome_bookmarks");
165 e->set_folder(true);
167 // Add it to the applicable updates list.
168 SyncEntityList bookmark_updates;
169 bookmark_updates.push_back(e.get());
171 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
173 // Process it.
174 UpdateSyncEntities(&handler, bookmark_updates, &status);
176 // Verify the results.
177 syncable::ReadTransaction trans(FROM_HERE, dir());
178 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
179 ASSERT_TRUE(entry.good());
181 EXPECT_FALSE(entry.ShouldMaintainPosition());
182 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
183 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
184 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
187 // Test the receipt of a non-bookmark item.
188 TEST_F(SyncDirectoryUpdateHandlerProcessUpdateTest, ReceiveNonBookmarkItem) {
189 SyncDirectoryUpdateHandler handler(dir(), PREFERENCES, ui_worker());
190 sync_pb::GetUpdatesResponse gu_response;
191 sessions::StatusController status;
193 std::string root = syncable::GetNullId().GetServerId();
194 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
195 scoped_ptr<sync_pb::SyncEntity> e =
196 CreateUpdate(SyncableIdToProto(server_id), root, PREFERENCES);
197 e->set_server_defined_unique_tag("9PGRuKdX5sHyGMB17CvYTXuC43I=");
199 // Add it to the applicable updates list.
200 SyncEntityList autofill_updates;
201 autofill_updates.push_back(e.get());
203 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
205 // Process it.
206 UpdateSyncEntities(&handler, autofill_updates, &status);
208 syncable::ReadTransaction trans(FROM_HERE, dir());
209 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
210 ASSERT_TRUE(entry.good());
212 EXPECT_FALSE(entry.ShouldMaintainPosition());
213 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
214 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
215 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
218 // Tests the setting of progress markers.
219 TEST_F(SyncDirectoryUpdateHandlerProcessUpdateTest, ProcessNewProgressMarkers) {
220 SyncDirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker());
222 sync_pb::DataTypeProgressMarker progress;
223 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS));
224 progress.set_token("token");
226 UpdateProgressMarkers(&handler, progress);
228 sync_pb::DataTypeProgressMarker saved;
229 dir()->GetDownloadProgress(BOOKMARKS, &saved);
231 EXPECT_EQ(progress.token(), saved.token());
232 EXPECT_EQ(progress.data_type_id(), saved.data_type_id());
235 // A test harness for tests that focus on applying updates.
237 // Update application is performed when we want to take updates that were
238 // previously downloaded, processed, and stored in our syncable::Directory
239 // and use them to update our local state (both the Directory's local state
240 // and the model's local state, though these tests focus only on the Directory's
241 // local state).
243 // This is kept separate from the update processing test in part for historical
244 // reasons, and in part because these tests may require a bit more infrastrcture
245 // in the future. Update application should happen on a different thread a lot
246 // of the time so these tests may end up requiring more infrastructure than the
247 // update processing tests. Currently, we're bypassing most of those issues by
248 // using FakeModelWorkers, so there's not much difference between the two test
249 // harnesses.
250 class SyncDirectoryUpdateHandlerApplyUpdateTest : public ::testing::Test {
251 public:
252 SyncDirectoryUpdateHandlerApplyUpdateTest()
253 : ui_worker_(new FakeModelWorker(GROUP_UI)),
254 password_worker_(new FakeModelWorker(GROUP_PASSWORD)),
255 passive_worker_(new FakeModelWorker(GROUP_PASSIVE)),
256 update_handler_map_deleter_(&update_handler_map_) {}
258 virtual void SetUp() OVERRIDE {
259 dir_maker_.SetUp();
260 entry_factory_.reset(new TestEntryFactory(directory()));
262 update_handler_map_.insert(std::make_pair(
263 BOOKMARKS,
264 new SyncDirectoryUpdateHandler(directory(), BOOKMARKS, ui_worker_)));
265 update_handler_map_.insert(std::make_pair(
266 PASSWORDS,
267 new SyncDirectoryUpdateHandler(directory(),
268 PASSWORDS,
269 password_worker_)));
272 virtual void TearDown() OVERRIDE {
273 dir_maker_.TearDown();
276 protected:
277 void ApplyBookmarkUpdates(sessions::StatusController* status) {
278 update_handler_map_[BOOKMARKS]->ApplyUpdates(status);
281 void ApplyPasswordUpdates(sessions::StatusController* status) {
282 update_handler_map_[PASSWORDS]->ApplyUpdates(status);
285 TestEntryFactory* entry_factory() {
286 return entry_factory_.get();
289 syncable::Directory* directory() {
290 return dir_maker_.directory();
293 private:
294 typedef std::map<ModelType, SyncDirectoryUpdateHandler*> UpdateHandlerMap;
296 base::MessageLoop loop_; // Needed to initialize the directory.
297 TestDirectorySetterUpper dir_maker_;
298 scoped_ptr<TestEntryFactory> entry_factory_;
300 scoped_refptr<FakeModelWorker> ui_worker_;
301 scoped_refptr<FakeModelWorker> password_worker_;
302 scoped_refptr<FakeModelWorker> passive_worker_;
304 UpdateHandlerMap update_handler_map_;
305 STLValueDeleter<UpdateHandlerMap> update_handler_map_deleter_;
308 namespace {
309 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
310 sync_pb::EntitySpecifics result;
311 AddDefaultFieldValue(BOOKMARKS, &result);
312 return result;
314 } // namespace
316 // Test update application for a few bookmark items.
317 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, SimpleBookmark) {
318 sessions::StatusController status;
320 std::string root_server_id = syncable::GetNullId().GetServerId();
321 int64 parent_handle =
322 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
323 "parent", DefaultBookmarkSpecifics(), root_server_id);
324 int64 child_handle =
325 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
326 "child", DefaultBookmarkSpecifics(), "parent");
328 ApplyBookmarkUpdates(&status);
330 EXPECT_EQ(0, status.num_encryption_conflicts())
331 << "Simple update shouldn't result in conflicts";
332 EXPECT_EQ(0, status.num_hierarchy_conflicts())
333 << "Simple update shouldn't result in conflicts";
334 EXPECT_EQ(2, status.num_updates_applied())
335 << "All items should have been successfully applied";
338 syncable::ReadTransaction trans(FROM_HERE, directory());
340 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
341 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
343 ASSERT_TRUE(parent.good());
344 ASSERT_TRUE(child.good());
346 EXPECT_FALSE(parent.GetIsUnsynced());
347 EXPECT_FALSE(parent.GetIsUnappliedUpdate());
348 EXPECT_FALSE(child.GetIsUnsynced());
349 EXPECT_FALSE(child.GetIsUnappliedUpdate());
353 // Test that the applicator can handle updates delivered out of order.
354 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
355 BookmarkChildrenBeforeParent) {
356 // Start with some bookmarks whose parents are unknown.
357 std::string root_server_id = syncable::GetNullId().GetServerId();
358 int64 a_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
359 "a_child_created_first", DefaultBookmarkSpecifics(), "parent");
360 int64 x_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
361 "x_child_created_first", DefaultBookmarkSpecifics(), "parent");
363 // Update application will fail.
364 sessions::StatusController status1;
365 ApplyBookmarkUpdates(&status1);
366 EXPECT_EQ(0, status1.num_updates_applied());
367 EXPECT_EQ(2, status1.num_hierarchy_conflicts());
370 syncable::ReadTransaction trans(FROM_HERE, directory());
372 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
373 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
375 ASSERT_TRUE(a.good());
376 ASSERT_TRUE(x.good());
378 EXPECT_TRUE(a.GetIsUnappliedUpdate());
379 EXPECT_TRUE(x.GetIsUnappliedUpdate());
382 // Now add their parent and a few siblings.
383 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
384 "parent", DefaultBookmarkSpecifics(), root_server_id);
385 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
386 "a_child_created_second", DefaultBookmarkSpecifics(), "parent");
387 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
388 "x_child_created_second", DefaultBookmarkSpecifics(), "parent");
390 // Update application will succeed.
391 sessions::StatusController status2;
392 ApplyBookmarkUpdates(&status2);
393 EXPECT_EQ(5, status2.num_updates_applied())
394 << "All updates should have been successfully applied";
397 syncable::ReadTransaction trans(FROM_HERE, directory());
399 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
400 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
402 ASSERT_TRUE(a.good());
403 ASSERT_TRUE(x.good());
405 EXPECT_FALSE(a.GetIsUnappliedUpdate());
406 EXPECT_FALSE(x.GetIsUnappliedUpdate());
410 // Try to apply changes on an item that is both IS_UNSYNCED and
411 // IS_UNAPPLIED_UPDATE. Conflict resolution should be performed.
412 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, SimpleBookmarkConflict) {
413 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem("x");
415 int original_server_version = -10;
417 syncable::ReadTransaction trans(FROM_HERE, directory());
418 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
419 original_server_version = e.GetServerVersion();
420 ASSERT_NE(original_server_version, e.GetBaseVersion());
421 EXPECT_TRUE(e.GetIsUnsynced());
424 sessions::StatusController status;
425 ApplyBookmarkUpdates(&status);
426 EXPECT_EQ(1, status.num_server_overwrites())
427 << "Unsynced and unapplied item conflict should be resolved";
428 EXPECT_EQ(0, status.num_updates_applied())
429 << "Update should not be applied; we should override the server.";
432 syncable::ReadTransaction trans(FROM_HERE, directory());
433 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
434 ASSERT_TRUE(e.good());
435 EXPECT_EQ(original_server_version, e.GetServerVersion());
436 EXPECT_EQ(original_server_version, e.GetBaseVersion());
437 EXPECT_FALSE(e.GetIsUnappliedUpdate());
439 // The unsynced flag will remain set until we successfully commit the item.
440 EXPECT_TRUE(e.GetIsUnsynced());
444 // Create a simple conflict that is also a hierarchy conflict. If we were to
445 // follow the normal "server wins" logic, we'd end up violating hierarchy
446 // constraints. The hierarchy conflict must take precedence. We can not allow
447 // the update to be applied. The item must remain in the conflict state.
448 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, HierarchyAndSimpleConflict) {
449 // Create a simply-conflicting item. It will start with valid parent ids.
450 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem(
451 "orphaned_by_server");
453 // Manually set the SERVER_PARENT_ID to bad value.
454 // A bad parent indicates a hierarchy conflict.
455 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
456 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
457 ASSERT_TRUE(entry.good());
459 entry.PutServerParentId(TestIdFactory::MakeServer("bogus_parent"));
462 sessions::StatusController status;
463 ApplyBookmarkUpdates(&status);
464 EXPECT_EQ(0, status.num_updates_applied());
465 EXPECT_EQ(0, status.num_server_overwrites());
466 EXPECT_EQ(1, status.num_hierarchy_conflicts());
469 syncable::ReadTransaction trans(FROM_HERE, directory());
470 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
471 ASSERT_TRUE(e.good());
472 EXPECT_TRUE(e.GetIsUnappliedUpdate());
473 EXPECT_TRUE(e.GetIsUnsynced());
477 // Attempt to apply an udpate that would create a bookmark folder loop. This
478 // application should fail.
479 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, BookmarkFolderLoop) {
480 // Item 'X' locally has parent of 'root'. Server is updating it to have
481 // parent of 'Y'.
483 // Create it as a child of root node.
484 int64 handle = entry_factory()->CreateSyncedItem("X", BOOKMARKS, true);
487 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
488 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
489 ASSERT_TRUE(entry.good());
491 // Re-parent from root to "Y"
492 entry.PutServerVersion(entry_factory()->GetNextRevision());
493 entry.PutIsUnappliedUpdate(true);
494 entry.PutServerParentId(TestIdFactory::MakeServer("Y"));
497 // Item 'Y' is child of 'X'.
498 entry_factory()->CreateUnsyncedItem(
499 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
500 BOOKMARKS, NULL);
502 // If the server's update were applied, we would have X be a child of Y, and Y
503 // as a child of X. That's a directory loop. The UpdateApplicator should
504 // prevent the update from being applied and note that this is a hierarchy
505 // conflict.
507 sessions::StatusController status;
508 ApplyBookmarkUpdates(&status);
510 // This should count as a hierarchy conflict.
511 EXPECT_EQ(1, status.num_hierarchy_conflicts());
514 syncable::ReadTransaction trans(FROM_HERE, directory());
515 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
516 ASSERT_TRUE(e.good());
517 EXPECT_TRUE(e.GetIsUnappliedUpdate());
518 EXPECT_FALSE(e.GetIsUnsynced());
522 // Test update application where the update has been orphaned by a local folder
523 // deletion. The update application attempt should fail.
524 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
525 HierarchyConflictDeletedParent) {
526 // Create a locally deleted parent item.
527 int64 parent_handle;
528 entry_factory()->CreateUnsyncedItem(
529 syncable::Id::CreateFromServerId("parent"), TestIdFactory::root(),
530 "parent", true, BOOKMARKS, &parent_handle);
532 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
533 syncable::MutableEntry entry(&trans,
534 syncable::GET_BY_HANDLE,
535 parent_handle);
536 entry.PutIsDel(true);
539 // Create an incoming child from the server.
540 int64 child_handle = entry_factory()->CreateUnappliedNewItemWithParent(
541 "child", DefaultBookmarkSpecifics(), "parent");
543 // The server's update may seem valid to some other client, but on this client
544 // that new item's parent no longer exists. The update should not be applied
545 // and the update applicator should indicate this is a hierarchy conflict.
547 sessions::StatusController status;
548 ApplyBookmarkUpdates(&status);
549 EXPECT_EQ(1, status.num_hierarchy_conflicts());
552 syncable::ReadTransaction trans(FROM_HERE, directory());
553 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
554 ASSERT_TRUE(child.good());
555 EXPECT_TRUE(child.GetIsUnappliedUpdate());
556 EXPECT_FALSE(child.GetIsUnsynced());
560 // Attempt to apply an update that deletes a folder where the folder has
561 // locally-created children. The update application should fail.
562 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
563 HierarchyConflictDeleteNonEmptyDirectory) {
564 // Create a server-deleted folder as a child of root node.
565 int64 parent_handle =
566 entry_factory()->CreateSyncedItem("parent", BOOKMARKS, true);
568 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
569 syncable::MutableEntry entry(&trans,
570 syncable::GET_BY_HANDLE,
571 parent_handle);
572 ASSERT_TRUE(entry.good());
574 // Delete it on the server.
575 entry.PutServerVersion(entry_factory()->GetNextRevision());
576 entry.PutIsUnappliedUpdate(true);
577 entry.PutServerParentId(TestIdFactory::root());
578 entry.PutServerIsDel(true);
581 // Create a local child of the server-deleted directory.
582 entry_factory()->CreateUnsyncedItem(
583 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
584 "child", false, BOOKMARKS, NULL);
586 // The server's request to delete the directory must be ignored, otherwise our
587 // unsynced new child would be orphaned. This is a hierarchy conflict.
589 sessions::StatusController status;
590 ApplyBookmarkUpdates(&status);
592 // This should count as a hierarchy conflict.
593 EXPECT_EQ(1, status.num_hierarchy_conflicts());
596 syncable::ReadTransaction trans(FROM_HERE, directory());
597 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
598 ASSERT_TRUE(parent.good());
599 EXPECT_TRUE(parent.GetIsUnappliedUpdate());
600 EXPECT_FALSE(parent.GetIsUnsynced());
604 // Attempt to apply updates where the updated item's parent is not known to this
605 // client. The update application attempt should fail.
606 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
607 HierarchyConflictUnknownParent) {
608 // We shouldn't be able to do anything with either of these items.
609 int64 x_handle = entry_factory()->CreateUnappliedNewItemWithParent(
610 "some_item", DefaultBookmarkSpecifics(), "unknown_parent");
611 int64 y_handle = entry_factory()->CreateUnappliedNewItemWithParent(
612 "some_other_item", DefaultBookmarkSpecifics(), "some_item");
614 sessions::StatusController status;
615 ApplyBookmarkUpdates(&status);
617 EXPECT_EQ(2, status.num_hierarchy_conflicts())
618 << "All updates with an unknown ancestors should be in conflict";
619 EXPECT_EQ(0, status.num_updates_applied())
620 << "No item with an unknown ancestor should be applied";
623 syncable::ReadTransaction trans(FROM_HERE, directory());
624 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
625 syncable::Entry y(&trans, syncable::GET_BY_HANDLE, y_handle);
626 ASSERT_TRUE(x.good());
627 ASSERT_TRUE(y.good());
628 EXPECT_TRUE(x.GetIsUnappliedUpdate());
629 EXPECT_TRUE(y.GetIsUnappliedUpdate());
630 EXPECT_FALSE(x.GetIsUnsynced());
631 EXPECT_FALSE(y.GetIsUnsynced());
635 // Attempt application of a mix of items. Some update application attempts will
636 // fail due to hierarchy conflicts. Others should succeed.
637 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, ItemsBothKnownAndUnknown) {
638 // See what happens when there's a mixture of good and bad updates.
639 std::string root_server_id = syncable::GetNullId().GetServerId();
640 int64 u1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
641 "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
642 int64 k1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
643 "first_known_item", DefaultBookmarkSpecifics(), root_server_id);
644 int64 u2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
645 "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
646 int64 k2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
647 "second_known_item", DefaultBookmarkSpecifics(), "first_known_item");
648 int64 k3_handle = entry_factory()->CreateUnappliedNewItemWithParent(
649 "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item");
650 int64 k4_handle = entry_factory()->CreateUnappliedNewItemWithParent(
651 "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id);
653 sessions::StatusController status;
654 ApplyBookmarkUpdates(&status);
656 EXPECT_EQ(2, status.num_hierarchy_conflicts())
657 << "The updates with unknown ancestors should be in conflict";
658 EXPECT_EQ(4, status.num_updates_applied())
659 << "The updates with known ancestors should be successfully applied";
662 syncable::ReadTransaction trans(FROM_HERE, directory());
663 syncable::Entry u1(&trans, syncable::GET_BY_HANDLE, u1_handle);
664 syncable::Entry u2(&trans, syncable::GET_BY_HANDLE, u2_handle);
665 syncable::Entry k1(&trans, syncable::GET_BY_HANDLE, k1_handle);
666 syncable::Entry k2(&trans, syncable::GET_BY_HANDLE, k2_handle);
667 syncable::Entry k3(&trans, syncable::GET_BY_HANDLE, k3_handle);
668 syncable::Entry k4(&trans, syncable::GET_BY_HANDLE, k4_handle);
669 ASSERT_TRUE(u1.good());
670 ASSERT_TRUE(u2.good());
671 ASSERT_TRUE(k1.good());
672 ASSERT_TRUE(k2.good());
673 ASSERT_TRUE(k3.good());
674 ASSERT_TRUE(k4.good());
675 EXPECT_TRUE(u1.GetIsUnappliedUpdate());
676 EXPECT_TRUE(u2.GetIsUnappliedUpdate());
677 EXPECT_FALSE(k1.GetIsUnappliedUpdate());
678 EXPECT_FALSE(k2.GetIsUnappliedUpdate());
679 EXPECT_FALSE(k3.GetIsUnappliedUpdate());
680 EXPECT_FALSE(k4.GetIsUnappliedUpdate());
684 // Attempt application of password upates where the passphrase is known.
685 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, DecryptablePassword) {
686 // Decryptable password updates should be applied.
687 Cryptographer* cryptographer;
689 // Storing the cryptographer separately is bad, but for this test we
690 // know it's safe.
691 syncable::ReadTransaction trans(FROM_HERE, directory());
692 cryptographer = directory()->GetCryptographer(&trans);
695 KeyParams params = {"localhost", "dummy", "foobar"};
696 cryptographer->AddKey(params);
698 sync_pb::EntitySpecifics specifics;
699 sync_pb::PasswordSpecificsData data;
700 data.set_origin("http://example.com");
702 cryptographer->Encrypt(data,
703 specifics.mutable_password()->mutable_encrypted());
704 int64 handle =
705 entry_factory()->CreateUnappliedNewItem("item", specifics, false);
707 sessions::StatusController status;
708 ApplyPasswordUpdates(&status);
710 EXPECT_EQ(1, status.num_updates_applied())
711 << "The updates that can be decrypted should be applied";
714 syncable::ReadTransaction trans(FROM_HERE, directory());
715 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
716 ASSERT_TRUE(e.good());
717 EXPECT_FALSE(e.GetIsUnappliedUpdate());
718 EXPECT_FALSE(e.GetIsUnsynced());
722 // Attempt application of encrypted items when the passphrase is not known.
723 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, UndecryptableData) {
724 // Undecryptable updates should not be applied.
725 sync_pb::EntitySpecifics encrypted_bookmark;
726 encrypted_bookmark.mutable_encrypted();
727 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
728 std::string root_server_id = syncable::GetNullId().GetServerId();
729 int64 folder_handle = entry_factory()->CreateUnappliedNewItemWithParent(
730 "folder",
731 encrypted_bookmark,
732 root_server_id);
733 int64 bookmark_handle = entry_factory()->CreateUnappliedNewItem(
734 "item2",
735 encrypted_bookmark,
736 false);
737 sync_pb::EntitySpecifics encrypted_password;
738 encrypted_password.mutable_password();
739 int64 password_handle = entry_factory()->CreateUnappliedNewItem(
740 "item3",
741 encrypted_password,
742 false);
744 sessions::StatusController status;
745 ApplyBookmarkUpdates(&status);
746 ApplyPasswordUpdates(&status);
748 EXPECT_EQ(3, status.num_encryption_conflicts())
749 << "Updates that can't be decrypted should be in encryption conflict";
750 EXPECT_EQ(0, status.num_updates_applied())
751 << "No update that can't be decrypted should be applied";
754 syncable::ReadTransaction trans(FROM_HERE, directory());
755 syncable::Entry folder(&trans, syncable::GET_BY_HANDLE, folder_handle);
756 syncable::Entry bm(&trans, syncable::GET_BY_HANDLE, bookmark_handle);
757 syncable::Entry pw(&trans, syncable::GET_BY_HANDLE, password_handle);
758 ASSERT_TRUE(folder.good());
759 ASSERT_TRUE(bm.good());
760 ASSERT_TRUE(pw.good());
761 EXPECT_TRUE(folder.GetIsUnappliedUpdate());
762 EXPECT_TRUE(bm.GetIsUnappliedUpdate());
763 EXPECT_TRUE(pw.GetIsUnappliedUpdate());
767 // Test a mix of decryptable and undecryptable updates.
768 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, SomeUndecryptablePassword) {
769 Cryptographer* cryptographer;
771 int64 decryptable_handle = -1;
772 int64 undecryptable_handle = -1;
774 // Only decryptable password updates should be applied.
776 sync_pb::EntitySpecifics specifics;
777 sync_pb::PasswordSpecificsData data;
778 data.set_origin("http://example.com/1");
780 syncable::ReadTransaction trans(FROM_HERE, directory());
781 cryptographer = directory()->GetCryptographer(&trans);
783 KeyParams params = {"localhost", "dummy", "foobar"};
784 cryptographer->AddKey(params);
786 cryptographer->Encrypt(data,
787 specifics.mutable_password()->mutable_encrypted());
789 decryptable_handle =
790 entry_factory()->CreateUnappliedNewItem("item1", specifics, false);
793 // Create a new cryptographer, independent of the one in the session.
794 Cryptographer other_cryptographer(cryptographer->encryptor());
795 KeyParams params = {"localhost", "dummy", "bazqux"};
796 other_cryptographer.AddKey(params);
798 sync_pb::EntitySpecifics specifics;
799 sync_pb::PasswordSpecificsData data;
800 data.set_origin("http://example.com/2");
802 other_cryptographer.Encrypt(data,
803 specifics.mutable_password()->mutable_encrypted());
804 undecryptable_handle =
805 entry_factory()->CreateUnappliedNewItem("item2", specifics, false);
808 sessions::StatusController status;
809 ApplyPasswordUpdates(&status);
811 EXPECT_EQ(1, status.num_encryption_conflicts())
812 << "The updates that can't be decrypted should be in encryption "
813 << "conflict";
814 EXPECT_EQ(1, status.num_updates_applied())
815 << "The undecryptable password update shouldn't be applied";
818 syncable::ReadTransaction trans(FROM_HERE, directory());
819 syncable::Entry e1(&trans, syncable::GET_BY_HANDLE, decryptable_handle);
820 syncable::Entry e2(&trans, syncable::GET_BY_HANDLE, undecryptable_handle);
821 ASSERT_TRUE(e1.good());
822 ASSERT_TRUE(e2.good());
823 EXPECT_FALSE(e1.GetIsUnappliedUpdate());
824 EXPECT_TRUE(e2.GetIsUnappliedUpdate());
828 } // namespace syncer