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