kiosk: Fix keyboard flow on network config UI.
[chromium-blink-merge.git] / sync / engine / directory_update_handler_unittest.cc
blobec8cc35f390e2bd6c74abbd8c0f9108f7ed35c61
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/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 static const int64 kDefaultVersion = 1000;
34 // A test harness for tests that focus on processing updates.
36 // Update processing is what occurs when we first download updates. It converts
37 // the received protobuf message into information in the syncable::Directory.
38 // Any invalid or redundant updates will be dropped at this point.
39 class DirectoryUpdateHandlerProcessUpdateTest : public ::testing::Test {
40 public:
41 DirectoryUpdateHandlerProcessUpdateTest()
42 : ui_worker_(new FakeModelWorker(GROUP_UI)) {
45 virtual ~DirectoryUpdateHandlerProcessUpdateTest() {}
47 virtual void SetUp() OVERRIDE {
48 dir_maker_.SetUp();
51 virtual void TearDown() OVERRIDE {
52 dir_maker_.TearDown();
55 syncable::Directory* dir() {
56 return dir_maker_.directory();
58 protected:
59 scoped_ptr<sync_pb::SyncEntity> CreateUpdate(
60 const std::string& id,
61 const std::string& parent,
62 const ModelType& type);
64 // This exists mostly to give tests access to the protected member function.
65 // Warning: This takes the syncable directory lock.
66 void UpdateSyncEntities(
67 DirectoryUpdateHandler* handler,
68 const SyncEntityList& applicable_updates,
69 sessions::StatusController* status);
71 // Another function to access private member functions.
72 void UpdateProgressMarkers(
73 DirectoryUpdateHandler* handler,
74 const sync_pb::DataTypeProgressMarker& progress);
76 scoped_refptr<FakeModelWorker> ui_worker() {
77 return ui_worker_;
80 bool EntryExists(const std::string& id) {
81 syncable::ReadTransaction trans(FROM_HERE, dir());
82 syncable::Entry e(&trans, syncable::GET_BY_ID,
83 syncable::Id::CreateFromServerId(id));
84 return e.good() && !e.GetIsDel();
87 private:
88 base::MessageLoop loop_; // Needed to initialize the directory.
89 TestDirectorySetterUpper dir_maker_;
90 scoped_refptr<FakeModelWorker> ui_worker_;
93 scoped_ptr<sync_pb::SyncEntity>
94 DirectoryUpdateHandlerProcessUpdateTest::CreateUpdate(
95 const std::string& id,
96 const std::string& parent,
97 const ModelType& type) {
98 scoped_ptr<sync_pb::SyncEntity> e(new sync_pb::SyncEntity());
99 e->set_id_string(id);
100 e->set_parent_id_string(parent);
101 e->set_non_unique_name(id);
102 e->set_name(id);
103 e->set_version(kDefaultVersion);
104 AddDefaultFieldValue(type, e->mutable_specifics());
105 return e.Pass();
108 void DirectoryUpdateHandlerProcessUpdateTest::UpdateSyncEntities(
109 DirectoryUpdateHandler* handler,
110 const SyncEntityList& applicable_updates,
111 sessions::StatusController* status) {
112 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, UNITTEST, dir());
113 handler->UpdateSyncEntities(&trans, applicable_updates, status);
116 void DirectoryUpdateHandlerProcessUpdateTest::UpdateProgressMarkers(
117 DirectoryUpdateHandler* handler,
118 const sync_pb::DataTypeProgressMarker& progress) {
119 handler->UpdateProgressMarker(progress);
122 static const char kCacheGuid[] = "IrcjZ2jyzHDV9Io4+zKcXQ==";
124 // Test that the bookmark tag is set on newly downloaded items.
125 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, NewBookmarkTag) {
126 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker());
127 sync_pb::GetUpdatesResponse gu_response;
128 sessions::StatusController status;
130 // Add a bookmark item to the update message.
131 std::string root = syncable::GetNullId().GetServerId();
132 syncable::Id server_id = syncable::Id::CreateFromServerId("b1");
133 scoped_ptr<sync_pb::SyncEntity> e =
134 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
135 e->set_originator_cache_guid(
136 std::string(kCacheGuid, arraysize(kCacheGuid)-1));
137 syncable::Id client_id = syncable::Id::CreateFromClientString("-2");
138 e->set_originator_client_item_id(client_id.GetServerId());
139 e->set_position_in_parent(0);
141 // Add it to the applicable updates list.
142 SyncEntityList bookmark_updates;
143 bookmark_updates.push_back(e.get());
145 // Process the update.
146 UpdateSyncEntities(&handler, bookmark_updates, &status);
148 syncable::ReadTransaction trans(FROM_HERE, dir());
149 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
150 ASSERT_TRUE(entry.good());
151 EXPECT_TRUE(UniquePosition::IsValidSuffix(entry.GetUniqueBookmarkTag()));
152 EXPECT_TRUE(entry.GetServerUniquePosition().IsValid());
154 // If this assertion fails, that might indicate that the algorithm used to
155 // generate bookmark tags has been modified. This could have implications for
156 // bookmark ordering. Please make sure you know what you're doing if you
157 // intend to make such a change.
158 EXPECT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", entry.GetUniqueBookmarkTag());
161 // Test the receipt of a type root node.
162 TEST_F(DirectoryUpdateHandlerProcessUpdateTest,
163 ReceiveServerCreatedBookmarkFolders) {
164 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker());
165 sync_pb::GetUpdatesResponse gu_response;
166 sessions::StatusController status;
168 // Create an update that mimics the bookmark root.
169 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
170 std::string root = syncable::GetNullId().GetServerId();
171 scoped_ptr<sync_pb::SyncEntity> e =
172 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
173 e->set_server_defined_unique_tag("google_chrome_bookmarks");
174 e->set_folder(true);
176 // Add it to the applicable updates list.
177 SyncEntityList bookmark_updates;
178 bookmark_updates.push_back(e.get());
180 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
182 // Process it.
183 UpdateSyncEntities(&handler, bookmark_updates, &status);
185 // Verify the results.
186 syncable::ReadTransaction trans(FROM_HERE, dir());
187 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
188 ASSERT_TRUE(entry.good());
190 EXPECT_FALSE(entry.ShouldMaintainPosition());
191 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
192 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
193 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
196 // Test the receipt of a non-bookmark item.
197 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ReceiveNonBookmarkItem) {
198 DirectoryUpdateHandler handler(dir(), AUTOFILL, ui_worker());
199 sync_pb::GetUpdatesResponse gu_response;
200 sessions::StatusController status;
202 std::string root = syncable::GetNullId().GetServerId();
203 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
204 scoped_ptr<sync_pb::SyncEntity> e =
205 CreateUpdate(SyncableIdToProto(server_id), root, AUTOFILL);
206 e->set_server_defined_unique_tag("9PGRuKdX5sHyGMB17CvYTXuC43I=");
208 // Add it to the applicable updates list.
209 SyncEntityList autofill_updates;
210 autofill_updates.push_back(e.get());
212 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
214 // Process it.
215 UpdateSyncEntities(&handler, autofill_updates, &status);
217 syncable::ReadTransaction trans(FROM_HERE, dir());
218 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
219 ASSERT_TRUE(entry.good());
221 EXPECT_FALSE(entry.ShouldMaintainPosition());
222 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
223 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
224 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
227 // Tests the setting of progress markers.
228 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ProcessNewProgressMarkers) {
229 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker());
231 sync_pb::DataTypeProgressMarker progress;
232 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS));
233 progress.set_token("token");
235 UpdateProgressMarkers(&handler, progress);
237 sync_pb::DataTypeProgressMarker saved;
238 dir()->GetDownloadProgress(BOOKMARKS, &saved);
240 EXPECT_EQ(progress.token(), saved.token());
241 EXPECT_EQ(progress.data_type_id(), saved.data_type_id());
244 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, GarbageCollectionByVersion) {
245 DirectoryUpdateHandler handler(dir(), SYNCED_NOTIFICATIONS, ui_worker());
246 sessions::StatusController status;
248 sync_pb::DataTypeProgressMarker progress;
249 progress.set_data_type_id(
250 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
251 progress.set_token("token");
252 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 10);
254 sync_pb::DataTypeContext context;
255 context.set_data_type_id(
256 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
257 context.set_context("context");
258 context.set_version(1);
260 scoped_ptr<sync_pb::SyncEntity> type_root =
261 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("root")),
262 syncable::GetNullId().GetServerId(),
263 SYNCED_NOTIFICATIONS);
264 type_root->set_server_defined_unique_tag(
265 ModelTypeToRootTag(SYNCED_NOTIFICATIONS));
266 type_root->set_folder(true);
268 scoped_ptr<sync_pb::SyncEntity> e1 =
269 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e1")),
270 type_root->id_string(),
271 SYNCED_NOTIFICATIONS);
273 scoped_ptr<sync_pb::SyncEntity> e2 =
274 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e2")),
275 type_root->id_string(),
276 SYNCED_NOTIFICATIONS);
277 e2->set_version(kDefaultVersion + 100);
279 // Add to the applicable updates list.
280 SyncEntityList updates;
281 updates.push_back(type_root.get());
282 updates.push_back(e1.get());
283 updates.push_back(e2.get());
285 // Process and apply updates.
286 EXPECT_EQ(
287 SYNCER_OK,
288 handler.ProcessGetUpdatesResponse(progress, context, updates, &status));
289 handler.ApplyUpdates(&status);
291 // Verify none is deleted because they are unapplied during GC.
292 EXPECT_TRUE(EntryExists(type_root->id_string()));
293 EXPECT_TRUE(EntryExists(e1->id_string()));
294 EXPECT_TRUE(EntryExists(e2->id_string()));
296 // Process and apply again. Old entry is deleted but not root.
297 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 20);
298 EXPECT_EQ(SYNCER_OK,
299 handler.ProcessGetUpdatesResponse(
300 progress, context, SyncEntityList(), &status));
301 handler.ApplyUpdates(&status);
302 EXPECT_TRUE(EntryExists(type_root->id_string()));
303 EXPECT_FALSE(EntryExists(e1->id_string()));
304 EXPECT_TRUE(EntryExists(e2->id_string()));
307 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ContextVersion) {
308 DirectoryUpdateHandler handler(dir(), SYNCED_NOTIFICATIONS, ui_worker());
309 sessions::StatusController status;
310 int field_number = GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS);
312 sync_pb::DataTypeProgressMarker progress;
313 progress.set_data_type_id(
314 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
315 progress.set_token("token");
317 sync_pb::DataTypeContext old_context;
318 old_context.set_version(1);
319 old_context.set_context("data");
320 old_context.set_data_type_id(field_number);
322 scoped_ptr<sync_pb::SyncEntity> type_root =
323 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("root")),
324 syncable::GetNullId().GetServerId(),
325 SYNCED_NOTIFICATIONS);
326 type_root->set_server_defined_unique_tag(
327 ModelTypeToRootTag(SYNCED_NOTIFICATIONS));
328 type_root->set_folder(true);
329 scoped_ptr<sync_pb::SyncEntity> e1 =
330 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e1")),
331 type_root->id_string(),
332 SYNCED_NOTIFICATIONS);
334 SyncEntityList updates;
335 updates.push_back(type_root.get());
336 updates.push_back(e1.get());
338 // The first response should be processed fine.
339 EXPECT_EQ(SYNCER_OK,
340 handler.ProcessGetUpdatesResponse(
341 progress, old_context, updates, &status));
342 handler.ApplyUpdates(&status);
344 EXPECT_TRUE(EntryExists(type_root->id_string()));
345 EXPECT_TRUE(EntryExists(e1->id_string()));
348 sync_pb::DataTypeContext dir_context;
349 syncable::ReadTransaction trans(FROM_HERE, dir());
350 trans.directory()->GetDataTypeContext(
351 &trans, SYNCED_NOTIFICATIONS, &dir_context);
352 EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
355 sync_pb::DataTypeContext new_context;
356 new_context.set_version(0);
357 new_context.set_context("old");
358 new_context.set_data_type_id(field_number);
360 scoped_ptr<sync_pb::SyncEntity> e2 =
361 CreateUpdate(SyncableIdToProto(syncable::Id::CreateFromServerId("e2")),
362 type_root->id_string(),
363 SYNCED_NOTIFICATIONS);
364 updates.clear();
365 updates.push_back(e2.get());
367 // The second response, with an old context version, should result in an
368 // error and the updates should be dropped.
369 EXPECT_EQ(DATATYPE_TRIGGERED_RETRY,
370 handler.ProcessGetUpdatesResponse(
371 progress, new_context, updates, &status));
372 handler.ApplyUpdates(&status);
374 EXPECT_FALSE(EntryExists(e2->id_string()));
377 sync_pb::DataTypeContext dir_context;
378 syncable::ReadTransaction trans(FROM_HERE, dir());
379 trans.directory()->GetDataTypeContext(
380 &trans, SYNCED_NOTIFICATIONS, &dir_context);
381 EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
385 // A test harness for tests that focus on applying updates.
387 // Update application is performed when we want to take updates that were
388 // previously downloaded, processed, and stored in our syncable::Directory
389 // and use them to update our local state (both the Directory's local state
390 // and the model's local state, though these tests focus only on the Directory's
391 // local state).
393 // This is kept separate from the update processing test in part for historical
394 // reasons, and in part because these tests may require a bit more infrastrcture
395 // in the future. Update application should happen on a different thread a lot
396 // of the time so these tests may end up requiring more infrastructure than the
397 // update processing tests. Currently, we're bypassing most of those issues by
398 // using FakeModelWorkers, so there's not much difference between the two test
399 // harnesses.
400 class DirectoryUpdateHandlerApplyUpdateTest : public ::testing::Test {
401 public:
402 DirectoryUpdateHandlerApplyUpdateTest()
403 : ui_worker_(new FakeModelWorker(GROUP_UI)),
404 password_worker_(new FakeModelWorker(GROUP_PASSWORD)),
405 passive_worker_(new FakeModelWorker(GROUP_PASSIVE)),
406 update_handler_map_deleter_(&update_handler_map_) {}
408 virtual void SetUp() OVERRIDE {
409 dir_maker_.SetUp();
410 entry_factory_.reset(new TestEntryFactory(directory()));
412 update_handler_map_.insert(std::make_pair(
413 BOOKMARKS,
414 new DirectoryUpdateHandler(directory(), BOOKMARKS, ui_worker_)));
415 update_handler_map_.insert(std::make_pair(
416 PASSWORDS,
417 new DirectoryUpdateHandler(directory(),
418 PASSWORDS,
419 password_worker_)));
422 virtual void TearDown() OVERRIDE {
423 dir_maker_.TearDown();
426 protected:
427 void ApplyBookmarkUpdates(sessions::StatusController* status) {
428 update_handler_map_[BOOKMARKS]->ApplyUpdates(status);
431 void ApplyPasswordUpdates(sessions::StatusController* status) {
432 update_handler_map_[PASSWORDS]->ApplyUpdates(status);
435 TestEntryFactory* entry_factory() {
436 return entry_factory_.get();
439 syncable::Directory* directory() {
440 return dir_maker_.directory();
443 private:
444 typedef std::map<ModelType, UpdateHandler*> UpdateHandlerMap;
446 base::MessageLoop loop_; // Needed to initialize the directory.
447 TestDirectorySetterUpper dir_maker_;
448 scoped_ptr<TestEntryFactory> entry_factory_;
450 scoped_refptr<FakeModelWorker> ui_worker_;
451 scoped_refptr<FakeModelWorker> password_worker_;
452 scoped_refptr<FakeModelWorker> passive_worker_;
454 UpdateHandlerMap update_handler_map_;
455 STLValueDeleter<UpdateHandlerMap> update_handler_map_deleter_;
458 namespace {
459 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
460 sync_pb::EntitySpecifics result;
461 AddDefaultFieldValue(BOOKMARKS, &result);
462 return result;
464 } // namespace
466 // Test update application for a few bookmark items.
467 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SimpleBookmark) {
468 sessions::StatusController status;
470 std::string root_server_id = syncable::GetNullId().GetServerId();
471 int64 parent_handle =
472 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
473 "parent", DefaultBookmarkSpecifics(), root_server_id);
474 int64 child_handle =
475 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
476 "child", DefaultBookmarkSpecifics(), "parent");
478 ApplyBookmarkUpdates(&status);
480 EXPECT_EQ(0, status.num_encryption_conflicts())
481 << "Simple update shouldn't result in conflicts";
482 EXPECT_EQ(0, status.num_hierarchy_conflicts())
483 << "Simple update shouldn't result in conflicts";
484 EXPECT_EQ(2, status.num_updates_applied())
485 << "All items should have been successfully applied";
488 syncable::ReadTransaction trans(FROM_HERE, directory());
490 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
491 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
493 ASSERT_TRUE(parent.good());
494 ASSERT_TRUE(child.good());
496 EXPECT_FALSE(parent.GetIsUnsynced());
497 EXPECT_FALSE(parent.GetIsUnappliedUpdate());
498 EXPECT_FALSE(child.GetIsUnsynced());
499 EXPECT_FALSE(child.GetIsUnappliedUpdate());
503 // Test that the applicator can handle updates delivered out of order.
504 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
505 BookmarkChildrenBeforeParent) {
506 // Start with some bookmarks whose parents are unknown.
507 std::string root_server_id = syncable::GetNullId().GetServerId();
508 int64 a_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
509 "a_child_created_first", DefaultBookmarkSpecifics(), "parent");
510 int64 x_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
511 "x_child_created_first", DefaultBookmarkSpecifics(), "parent");
513 // Update application will fail.
514 sessions::StatusController status1;
515 ApplyBookmarkUpdates(&status1);
516 EXPECT_EQ(0, status1.num_updates_applied());
517 EXPECT_EQ(2, status1.num_hierarchy_conflicts());
520 syncable::ReadTransaction trans(FROM_HERE, directory());
522 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
523 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
525 ASSERT_TRUE(a.good());
526 ASSERT_TRUE(x.good());
528 EXPECT_TRUE(a.GetIsUnappliedUpdate());
529 EXPECT_TRUE(x.GetIsUnappliedUpdate());
532 // Now add their parent and a few siblings.
533 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
534 "parent", DefaultBookmarkSpecifics(), root_server_id);
535 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
536 "a_child_created_second", DefaultBookmarkSpecifics(), "parent");
537 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
538 "x_child_created_second", DefaultBookmarkSpecifics(), "parent");
540 // Update application will succeed.
541 sessions::StatusController status2;
542 ApplyBookmarkUpdates(&status2);
543 EXPECT_EQ(5, status2.num_updates_applied())
544 << "All updates should have been successfully applied";
547 syncable::ReadTransaction trans(FROM_HERE, directory());
549 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
550 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
552 ASSERT_TRUE(a.good());
553 ASSERT_TRUE(x.good());
555 EXPECT_FALSE(a.GetIsUnappliedUpdate());
556 EXPECT_FALSE(x.GetIsUnappliedUpdate());
560 // Try to apply changes on an item that is both IS_UNSYNCED and
561 // IS_UNAPPLIED_UPDATE. Conflict resolution should be performed.
562 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SimpleBookmarkConflict) {
563 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem("x");
565 int original_server_version = -10;
567 syncable::ReadTransaction trans(FROM_HERE, directory());
568 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
569 original_server_version = e.GetServerVersion();
570 ASSERT_NE(original_server_version, e.GetBaseVersion());
571 EXPECT_TRUE(e.GetIsUnsynced());
574 sessions::StatusController status;
575 ApplyBookmarkUpdates(&status);
576 EXPECT_EQ(1, status.num_server_overwrites())
577 << "Unsynced and unapplied item conflict should be resolved";
578 EXPECT_EQ(0, status.num_updates_applied())
579 << "Update should not be applied; we should override the server.";
582 syncable::ReadTransaction trans(FROM_HERE, directory());
583 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
584 ASSERT_TRUE(e.good());
585 EXPECT_EQ(original_server_version, e.GetServerVersion());
586 EXPECT_EQ(original_server_version, e.GetBaseVersion());
587 EXPECT_FALSE(e.GetIsUnappliedUpdate());
589 // The unsynced flag will remain set until we successfully commit the item.
590 EXPECT_TRUE(e.GetIsUnsynced());
594 // Create a simple conflict that is also a hierarchy conflict. If we were to
595 // follow the normal "server wins" logic, we'd end up violating hierarchy
596 // constraints. The hierarchy conflict must take precedence. We can not allow
597 // the update to be applied. The item must remain in the conflict state.
598 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, HierarchyAndSimpleConflict) {
599 // Create a simply-conflicting item. It will start with valid parent ids.
600 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem(
601 "orphaned_by_server");
603 // Manually set the SERVER_PARENT_ID to bad value.
604 // A bad parent indicates a hierarchy conflict.
605 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
606 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
607 ASSERT_TRUE(entry.good());
609 entry.PutServerParentId(TestIdFactory::MakeServer("bogus_parent"));
612 sessions::StatusController status;
613 ApplyBookmarkUpdates(&status);
614 EXPECT_EQ(0, status.num_updates_applied());
615 EXPECT_EQ(0, status.num_server_overwrites());
616 EXPECT_EQ(1, status.num_hierarchy_conflicts());
619 syncable::ReadTransaction trans(FROM_HERE, directory());
620 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
621 ASSERT_TRUE(e.good());
622 EXPECT_TRUE(e.GetIsUnappliedUpdate());
623 EXPECT_TRUE(e.GetIsUnsynced());
627 // Attempt to apply an udpate that would create a bookmark folder loop. This
628 // application should fail.
629 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, BookmarkFolderLoop) {
630 // Item 'X' locally has parent of 'root'. Server is updating it to have
631 // parent of 'Y'.
633 // Create it as a child of root node.
634 int64 handle = entry_factory()->CreateSyncedItem("X", BOOKMARKS, true);
637 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
638 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
639 ASSERT_TRUE(entry.good());
641 // Re-parent from root to "Y"
642 entry.PutServerVersion(entry_factory()->GetNextRevision());
643 entry.PutIsUnappliedUpdate(true);
644 entry.PutServerParentId(TestIdFactory::MakeServer("Y"));
647 // Item 'Y' is child of 'X'.
648 entry_factory()->CreateUnsyncedItem(
649 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
650 BOOKMARKS, NULL);
652 // If the server's update were applied, we would have X be a child of Y, and Y
653 // as a child of X. That's a directory loop. The UpdateApplicator should
654 // prevent the update from being applied and note that this is a hierarchy
655 // conflict.
657 sessions::StatusController status;
658 ApplyBookmarkUpdates(&status);
660 // This should count as a hierarchy conflict.
661 EXPECT_EQ(1, status.num_hierarchy_conflicts());
664 syncable::ReadTransaction trans(FROM_HERE, directory());
665 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
666 ASSERT_TRUE(e.good());
667 EXPECT_TRUE(e.GetIsUnappliedUpdate());
668 EXPECT_FALSE(e.GetIsUnsynced());
672 // Test update application where the update has been orphaned by a local folder
673 // deletion. The update application attempt should fail.
674 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
675 HierarchyConflictDeletedParent) {
676 // Create a locally deleted parent item.
677 int64 parent_handle;
678 entry_factory()->CreateUnsyncedItem(
679 syncable::Id::CreateFromServerId("parent"), TestIdFactory::root(),
680 "parent", true, BOOKMARKS, &parent_handle);
682 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
683 syncable::MutableEntry entry(&trans,
684 syncable::GET_BY_HANDLE,
685 parent_handle);
686 entry.PutIsDel(true);
689 // Create an incoming child from the server.
690 int64 child_handle = entry_factory()->CreateUnappliedNewItemWithParent(
691 "child", DefaultBookmarkSpecifics(), "parent");
693 // The server's update may seem valid to some other client, but on this client
694 // that new item's parent no longer exists. The update should not be applied
695 // and the update applicator should indicate this is a hierarchy conflict.
697 sessions::StatusController status;
698 ApplyBookmarkUpdates(&status);
699 EXPECT_EQ(1, status.num_hierarchy_conflicts());
702 syncable::ReadTransaction trans(FROM_HERE, directory());
703 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
704 ASSERT_TRUE(child.good());
705 EXPECT_TRUE(child.GetIsUnappliedUpdate());
706 EXPECT_FALSE(child.GetIsUnsynced());
710 // Attempt to apply an update that deletes a folder where the folder has
711 // locally-created children. The update application should fail.
712 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
713 HierarchyConflictDeleteNonEmptyDirectory) {
714 // Create a server-deleted folder as a child of root node.
715 int64 parent_handle =
716 entry_factory()->CreateSyncedItem("parent", BOOKMARKS, true);
718 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
719 syncable::MutableEntry entry(&trans,
720 syncable::GET_BY_HANDLE,
721 parent_handle);
722 ASSERT_TRUE(entry.good());
724 // Delete it on the server.
725 entry.PutServerVersion(entry_factory()->GetNextRevision());
726 entry.PutIsUnappliedUpdate(true);
727 entry.PutServerParentId(TestIdFactory::root());
728 entry.PutServerIsDel(true);
731 // Create a local child of the server-deleted directory.
732 entry_factory()->CreateUnsyncedItem(
733 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
734 "child", false, BOOKMARKS, NULL);
736 // The server's request to delete the directory must be ignored, otherwise our
737 // unsynced new child would be orphaned. This is a hierarchy conflict.
739 sessions::StatusController status;
740 ApplyBookmarkUpdates(&status);
742 // This should count as a hierarchy conflict.
743 EXPECT_EQ(1, status.num_hierarchy_conflicts());
746 syncable::ReadTransaction trans(FROM_HERE, directory());
747 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
748 ASSERT_TRUE(parent.good());
749 EXPECT_TRUE(parent.GetIsUnappliedUpdate());
750 EXPECT_FALSE(parent.GetIsUnsynced());
754 // Attempt to apply updates where the updated item's parent is not known to this
755 // client. The update application attempt should fail.
756 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
757 HierarchyConflictUnknownParent) {
758 // We shouldn't be able to do anything with either of these items.
759 int64 x_handle = entry_factory()->CreateUnappliedNewItemWithParent(
760 "some_item", DefaultBookmarkSpecifics(), "unknown_parent");
761 int64 y_handle = entry_factory()->CreateUnappliedNewItemWithParent(
762 "some_other_item", DefaultBookmarkSpecifics(), "some_item");
764 sessions::StatusController status;
765 ApplyBookmarkUpdates(&status);
767 EXPECT_EQ(2, status.num_hierarchy_conflicts())
768 << "All updates with an unknown ancestors should be in conflict";
769 EXPECT_EQ(0, status.num_updates_applied())
770 << "No item with an unknown ancestor should be applied";
773 syncable::ReadTransaction trans(FROM_HERE, directory());
774 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
775 syncable::Entry y(&trans, syncable::GET_BY_HANDLE, y_handle);
776 ASSERT_TRUE(x.good());
777 ASSERT_TRUE(y.good());
778 EXPECT_TRUE(x.GetIsUnappliedUpdate());
779 EXPECT_TRUE(y.GetIsUnappliedUpdate());
780 EXPECT_FALSE(x.GetIsUnsynced());
781 EXPECT_FALSE(y.GetIsUnsynced());
785 // Attempt application of a mix of items. Some update application attempts will
786 // fail due to hierarchy conflicts. Others should succeed.
787 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, ItemsBothKnownAndUnknown) {
788 // See what happens when there's a mixture of good and bad updates.
789 std::string root_server_id = syncable::GetNullId().GetServerId();
790 int64 u1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
791 "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
792 int64 k1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
793 "first_known_item", DefaultBookmarkSpecifics(), root_server_id);
794 int64 u2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
795 "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
796 int64 k2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
797 "second_known_item", DefaultBookmarkSpecifics(), "first_known_item");
798 int64 k3_handle = entry_factory()->CreateUnappliedNewItemWithParent(
799 "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item");
800 int64 k4_handle = entry_factory()->CreateUnappliedNewItemWithParent(
801 "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id);
803 sessions::StatusController status;
804 ApplyBookmarkUpdates(&status);
806 EXPECT_EQ(2, status.num_hierarchy_conflicts())
807 << "The updates with unknown ancestors should be in conflict";
808 EXPECT_EQ(4, status.num_updates_applied())
809 << "The updates with known ancestors should be successfully applied";
812 syncable::ReadTransaction trans(FROM_HERE, directory());
813 syncable::Entry u1(&trans, syncable::GET_BY_HANDLE, u1_handle);
814 syncable::Entry u2(&trans, syncable::GET_BY_HANDLE, u2_handle);
815 syncable::Entry k1(&trans, syncable::GET_BY_HANDLE, k1_handle);
816 syncable::Entry k2(&trans, syncable::GET_BY_HANDLE, k2_handle);
817 syncable::Entry k3(&trans, syncable::GET_BY_HANDLE, k3_handle);
818 syncable::Entry k4(&trans, syncable::GET_BY_HANDLE, k4_handle);
819 ASSERT_TRUE(u1.good());
820 ASSERT_TRUE(u2.good());
821 ASSERT_TRUE(k1.good());
822 ASSERT_TRUE(k2.good());
823 ASSERT_TRUE(k3.good());
824 ASSERT_TRUE(k4.good());
825 EXPECT_TRUE(u1.GetIsUnappliedUpdate());
826 EXPECT_TRUE(u2.GetIsUnappliedUpdate());
827 EXPECT_FALSE(k1.GetIsUnappliedUpdate());
828 EXPECT_FALSE(k2.GetIsUnappliedUpdate());
829 EXPECT_FALSE(k3.GetIsUnappliedUpdate());
830 EXPECT_FALSE(k4.GetIsUnappliedUpdate());
834 // Attempt application of password upates where the passphrase is known.
835 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, DecryptablePassword) {
836 // Decryptable password updates should be applied.
837 Cryptographer* cryptographer;
839 // Storing the cryptographer separately is bad, but for this test we
840 // know it's safe.
841 syncable::ReadTransaction trans(FROM_HERE, directory());
842 cryptographer = directory()->GetCryptographer(&trans);
845 KeyParams params = {"localhost", "dummy", "foobar"};
846 cryptographer->AddKey(params);
848 sync_pb::EntitySpecifics specifics;
849 sync_pb::PasswordSpecificsData data;
850 data.set_origin("http://example.com");
852 cryptographer->Encrypt(data,
853 specifics.mutable_password()->mutable_encrypted());
854 int64 handle =
855 entry_factory()->CreateUnappliedNewItem("item", specifics, false);
857 sessions::StatusController status;
858 ApplyPasswordUpdates(&status);
860 EXPECT_EQ(1, status.num_updates_applied())
861 << "The updates that can be decrypted should be applied";
864 syncable::ReadTransaction trans(FROM_HERE, directory());
865 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
866 ASSERT_TRUE(e.good());
867 EXPECT_FALSE(e.GetIsUnappliedUpdate());
868 EXPECT_FALSE(e.GetIsUnsynced());
872 // Attempt application of encrypted items when the passphrase is not known.
873 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, UndecryptableData) {
874 // Undecryptable updates should not be applied.
875 sync_pb::EntitySpecifics encrypted_bookmark;
876 encrypted_bookmark.mutable_encrypted();
877 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
878 std::string root_server_id = syncable::GetNullId().GetServerId();
879 int64 folder_handle = entry_factory()->CreateUnappliedNewItemWithParent(
880 "folder",
881 encrypted_bookmark,
882 root_server_id);
883 int64 bookmark_handle = entry_factory()->CreateUnappliedNewItem(
884 "item2",
885 encrypted_bookmark,
886 false);
887 sync_pb::EntitySpecifics encrypted_password;
888 encrypted_password.mutable_password();
889 int64 password_handle = entry_factory()->CreateUnappliedNewItem(
890 "item3",
891 encrypted_password,
892 false);
894 sessions::StatusController status;
895 ApplyBookmarkUpdates(&status);
896 ApplyPasswordUpdates(&status);
898 EXPECT_EQ(3, status.num_encryption_conflicts())
899 << "Updates that can't be decrypted should be in encryption conflict";
900 EXPECT_EQ(0, status.num_updates_applied())
901 << "No update that can't be decrypted should be applied";
904 syncable::ReadTransaction trans(FROM_HERE, directory());
905 syncable::Entry folder(&trans, syncable::GET_BY_HANDLE, folder_handle);
906 syncable::Entry bm(&trans, syncable::GET_BY_HANDLE, bookmark_handle);
907 syncable::Entry pw(&trans, syncable::GET_BY_HANDLE, password_handle);
908 ASSERT_TRUE(folder.good());
909 ASSERT_TRUE(bm.good());
910 ASSERT_TRUE(pw.good());
911 EXPECT_TRUE(folder.GetIsUnappliedUpdate());
912 EXPECT_TRUE(bm.GetIsUnappliedUpdate());
913 EXPECT_TRUE(pw.GetIsUnappliedUpdate());
917 // Test a mix of decryptable and undecryptable updates.
918 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SomeUndecryptablePassword) {
919 Cryptographer* cryptographer;
921 int64 decryptable_handle = -1;
922 int64 undecryptable_handle = -1;
924 // Only decryptable password updates should be applied.
926 sync_pb::EntitySpecifics specifics;
927 sync_pb::PasswordSpecificsData data;
928 data.set_origin("http://example.com/1");
930 syncable::ReadTransaction trans(FROM_HERE, directory());
931 cryptographer = directory()->GetCryptographer(&trans);
933 KeyParams params = {"localhost", "dummy", "foobar"};
934 cryptographer->AddKey(params);
936 cryptographer->Encrypt(data,
937 specifics.mutable_password()->mutable_encrypted());
939 decryptable_handle =
940 entry_factory()->CreateUnappliedNewItem("item1", specifics, false);
943 // Create a new cryptographer, independent of the one in the session.
944 Cryptographer other_cryptographer(cryptographer->encryptor());
945 KeyParams params = {"localhost", "dummy", "bazqux"};
946 other_cryptographer.AddKey(params);
948 sync_pb::EntitySpecifics specifics;
949 sync_pb::PasswordSpecificsData data;
950 data.set_origin("http://example.com/2");
952 other_cryptographer.Encrypt(data,
953 specifics.mutable_password()->mutable_encrypted());
954 undecryptable_handle =
955 entry_factory()->CreateUnappliedNewItem("item2", specifics, false);
958 sessions::StatusController status;
959 ApplyPasswordUpdates(&status);
961 EXPECT_EQ(1, status.num_encryption_conflicts())
962 << "The updates that can't be decrypted should be in encryption "
963 << "conflict";
964 EXPECT_EQ(1, status.num_updates_applied())
965 << "The undecryptable password update shouldn't be applied";
968 syncable::ReadTransaction trans(FROM_HERE, directory());
969 syncable::Entry e1(&trans, syncable::GET_BY_HANDLE, decryptable_handle);
970 syncable::Entry e2(&trans, syncable::GET_BY_HANDLE, undecryptable_handle);
971 ASSERT_TRUE(e1.good());
972 ASSERT_TRUE(e2.good());
973 EXPECT_FALSE(e1.GetIsUnappliedUpdate());
974 EXPECT_TRUE(e2.GetIsUnappliedUpdate());
978 } // namespace syncer