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"
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
{
39 SyncDirectoryUpdateHandlerProcessUpdateTest()
40 : ui_worker_(new FakeModelWorker(GROUP_UI
)) {
43 virtual ~SyncDirectoryUpdateHandlerProcessUpdateTest() {}
45 virtual void SetUp() OVERRIDE
{
49 virtual void TearDown() OVERRIDE
{
50 dir_maker_
.TearDown();
53 syncable::Directory
* dir() {
54 return dir_maker_
.directory();
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() {
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());
91 e
->set_parent_id_string(parent
);
92 e
->set_non_unique_name(id
);
95 AddDefaultFieldValue(type
, e
->mutable_specifics());
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");
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
));
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
));
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
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
250 class SyncDirectoryUpdateHandlerApplyUpdateTest
: public ::testing::Test
{
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
{
260 entry_factory_
.reset(new TestEntryFactory(directory()));
262 update_handler_map_
.insert(std::make_pair(
264 new SyncDirectoryUpdateHandler(directory(), BOOKMARKS
, ui_worker_
)));
265 update_handler_map_
.insert(std::make_pair(
267 new SyncDirectoryUpdateHandler(directory(),
272 virtual void TearDown() OVERRIDE
{
273 dir_maker_
.TearDown();
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();
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_
;
309 sync_pb::EntitySpecifics
DefaultBookmarkSpecifics() {
310 sync_pb::EntitySpecifics result
;
311 AddDefaultFieldValue(BOOKMARKS
, &result
);
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
);
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
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,
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
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.
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
,
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
,
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
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());
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(
733 int64 bookmark_handle
= entry_factory()->CreateUnappliedNewItem(
737 sync_pb::EntitySpecifics encrypted_password
;
738 encrypted_password
.mutable_password();
739 int64 password_handle
= entry_factory()->CreateUnappliedNewItem(
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());
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 "
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