Home | History | Annotate | Download | only in engine
      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.
      4 
      5 #include "sync/engine/sync_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/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"
     27 
     28 namespace syncer {
     29 
     30 using syncable::UNITTEST;
     31 
     32 // A test harness for tests that focus on processing updates.
     33 //
     34 // Update processing is what occurs when we first download updates.  It converts
     35 // the received protobuf message into information in the syncable::Directory.
     36 // Any invalid or redundant updates will be dropped at this point.
     37 class SyncDirectoryUpdateHandlerProcessUpdateTest : public ::testing::Test {
     38  public:
     39   SyncDirectoryUpdateHandlerProcessUpdateTest()
     40       : ui_worker_(new FakeModelWorker(GROUP_UI)) {
     41   }
     42 
     43   virtual ~SyncDirectoryUpdateHandlerProcessUpdateTest() {}
     44 
     45   virtual void SetUp() OVERRIDE {
     46     dir_maker_.SetUp();
     47   }
     48 
     49   virtual void TearDown() OVERRIDE {
     50     dir_maker_.TearDown();
     51   }
     52 
     53   syncable::Directory* dir() {
     54     return dir_maker_.directory();
     55   }
     56  protected:
     57   scoped_ptr<sync_pb::SyncEntity> CreateUpdate(
     58       const std::string& id,
     59       const std::string& parent,
     60       const ModelType& type);
     61 
     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);
     68 
     69   // Another function to access private member functions.
     70   void UpdateProgressMarkers(
     71       SyncDirectoryUpdateHandler* handler,
     72       const sync_pb::DataTypeProgressMarker& progress);
     73 
     74   scoped_refptr<FakeModelWorker> ui_worker() {
     75     return ui_worker_;
     76   }
     77 
     78  private:
     79   base::MessageLoop loop_;  // Needed to initialize the directory.
     80   TestDirectorySetterUpper dir_maker_;
     81   scoped_refptr<FakeModelWorker> ui_worker_;
     82 };
     83 
     84 scoped_ptr<sync_pb::SyncEntity>
     85 SyncDirectoryUpdateHandlerProcessUpdateTest::CreateUpdate(
     86     const std::string& id,
     87     const std::string& parent,
     88     const ModelType& type) {
     89   scoped_ptr<sync_pb::SyncEntity> e(new sync_pb::SyncEntity());
     90   e->set_id_string(id);
     91   e->set_parent_id_string(parent);
     92   e->set_non_unique_name(id);
     93   e->set_name(id);
     94   e->set_version(1000);
     95   AddDefaultFieldValue(type, e->mutable_specifics());
     96   return e.Pass();
     97 }
     98 
     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);
    105 }
    106 
    107 void SyncDirectoryUpdateHandlerProcessUpdateTest::UpdateProgressMarkers(
    108     SyncDirectoryUpdateHandler* handler,
    109     const sync_pb::DataTypeProgressMarker& progress) {
    110   handler->UpdateProgressMarker(progress);
    111 }
    112 
    113 static const char kCacheGuid[] = "IrcjZ2jyzHDV9Io4+zKcXQ==";
    114 
    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;
    120 
    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);
    131 
    132   // Add it to the applicable updates list.
    133   SyncEntityList bookmark_updates;
    134   bookmark_updates.push_back(e.get());
    135 
    136   // Process the update.
    137   UpdateSyncEntities(&handler, bookmark_updates, &status);
    138 
    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());
    144 
    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());
    150 }
    151 
    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;
    158 
    159   // Create an update that mimics the bookmark root.
    160   syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
    161   std::string root = syncable::GetNullId().GetServerId();
    162   scoped_ptr<sync_pb::SyncEntity> e =
    163       CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
    164   e->set_server_defined_unique_tag("google_chrome_bookmarks");
    165   e->set_folder(true);
    166 
    167   // Add it to the applicable updates list.
    168   SyncEntityList bookmark_updates;
    169   bookmark_updates.push_back(e.get());
    170 
    171   EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
    172 
    173   // Process it.
    174   UpdateSyncEntities(&handler, bookmark_updates, &status);
    175 
    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());
    180 
    181   EXPECT_FALSE(entry.ShouldMaintainPosition());
    182   EXPECT_FALSE(entry.GetUniquePosition().IsValid());
    183   EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
    184   EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
    185 }
    186 
    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;
    192 
    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=");
    198 
    199   // Add it to the applicable updates list.
    200   SyncEntityList autofill_updates;
    201   autofill_updates.push_back(e.get());
    202 
    203   EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
    204 
    205   // Process it.
    206   UpdateSyncEntities(&handler, autofill_updates, &status);
    207 
    208   syncable::ReadTransaction trans(FROM_HERE, dir());
    209   syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
    210   ASSERT_TRUE(entry.good());
    211 
    212   EXPECT_FALSE(entry.ShouldMaintainPosition());
    213   EXPECT_FALSE(entry.GetUniquePosition().IsValid());
    214   EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
    215   EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
    216 }
    217 
    218 // Tests the setting of progress markers.
    219 TEST_F(SyncDirectoryUpdateHandlerProcessUpdateTest, ProcessNewProgressMarkers) {
    220   SyncDirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker());
    221 
    222   sync_pb::DataTypeProgressMarker progress;
    223   progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS));
    224   progress.set_token("token");
    225 
    226   UpdateProgressMarkers(&handler, progress);
    227 
    228   sync_pb::DataTypeProgressMarker saved;
    229   dir()->GetDownloadProgress(BOOKMARKS, &saved);
    230 
    231   EXPECT_EQ(progress.token(), saved.token());
    232   EXPECT_EQ(progress.data_type_id(), saved.data_type_id());
    233 }
    234 
    235 // A test harness for tests that focus on applying updates.
    236 //
    237 // Update application is performed when we want to take updates that were
    238 // previously downloaded, processed, and stored in our syncable::Directory
    239 // and use them to update our local state (both the Directory's local state
    240 // and the model's local state, though these tests focus only on the Directory's
    241 // local state).
    242 //
    243 // This is kept separate from the update processing test in part for historical
    244 // reasons, and in part because these tests may require a bit more infrastrcture
    245 // in the future.  Update application should happen on a different thread a lot
    246 // of the time so these tests may end up requiring more infrastructure than the
    247 // update processing tests.  Currently, we're bypassing most of those issues by
    248 // using FakeModelWorkers, so there's not much difference between the two test
    249 // harnesses.
    250 class SyncDirectoryUpdateHandlerApplyUpdateTest : public ::testing::Test {
    251  public:
    252   SyncDirectoryUpdateHandlerApplyUpdateTest()
    253       : ui_worker_(new FakeModelWorker(GROUP_UI)),
    254         password_worker_(new FakeModelWorker(GROUP_PASSWORD)),
    255         passive_worker_(new FakeModelWorker(GROUP_PASSIVE)),
    256         update_handler_map_deleter_(&update_handler_map_) {}
    257 
    258   virtual void SetUp() OVERRIDE {
    259     dir_maker_.SetUp();
    260     entry_factory_.reset(new TestEntryFactory(directory()));
    261 
    262     update_handler_map_.insert(std::make_pair(
    263         BOOKMARKS,
    264         new SyncDirectoryUpdateHandler(directory(), BOOKMARKS, ui_worker_)));
    265     update_handler_map_.insert(std::make_pair(
    266         PASSWORDS,
    267         new SyncDirectoryUpdateHandler(directory(),
    268                                        PASSWORDS,
    269                                        password_worker_)));
    270   }
    271 
    272   virtual void TearDown() OVERRIDE {
    273     dir_maker_.TearDown();
    274   }
    275 
    276  protected:
    277   void ApplyBookmarkUpdates(sessions::StatusController* status) {
    278     update_handler_map_[BOOKMARKS]->ApplyUpdates(status);
    279   }
    280 
    281   void ApplyPasswordUpdates(sessions::StatusController* status) {
    282     update_handler_map_[PASSWORDS]->ApplyUpdates(status);
    283   }
    284 
    285   TestEntryFactory* entry_factory() {
    286     return entry_factory_.get();
    287   }
    288 
    289   syncable::Directory* directory() {
    290     return dir_maker_.directory();
    291   }
    292 
    293  private:
    294   base::MessageLoop loop_;  // Needed to initialize the directory.
    295   TestDirectorySetterUpper dir_maker_;
    296   scoped_ptr<TestEntryFactory> entry_factory_;
    297 
    298   scoped_refptr<FakeModelWorker> ui_worker_;
    299   scoped_refptr<FakeModelWorker> password_worker_;
    300   scoped_refptr<FakeModelWorker> passive_worker_;
    301 
    302   UpdateHandlerMap update_handler_map_;
    303   STLValueDeleter<UpdateHandlerMap> update_handler_map_deleter_;
    304 };
    305 
    306 namespace {
    307 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
    308   sync_pb::EntitySpecifics result;
    309   AddDefaultFieldValue(BOOKMARKS, &result);
    310   return result;
    311 }
    312 } // namespace
    313 
    314 // Test update application for a few bookmark items.
    315 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, SimpleBookmark) {
    316   sessions::StatusController status;
    317 
    318   std::string root_server_id = syncable::GetNullId().GetServerId();
    319   int64 parent_handle =
    320       entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
    321           "parent", DefaultBookmarkSpecifics(), root_server_id);
    322   int64 child_handle =
    323       entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
    324           "child", DefaultBookmarkSpecifics(), "parent");
    325 
    326   ApplyBookmarkUpdates(&status);
    327 
    328   EXPECT_EQ(0, status.num_encryption_conflicts())
    329       << "Simple update shouldn't result in conflicts";
    330   EXPECT_EQ(0, status.num_hierarchy_conflicts())
    331       << "Simple update shouldn't result in conflicts";
    332   EXPECT_EQ(2, status.num_updates_applied())
    333       << "All items should have been successfully applied";
    334 
    335   {
    336     syncable::ReadTransaction trans(FROM_HERE, directory());
    337 
    338     syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
    339     syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
    340 
    341     ASSERT_TRUE(parent.good());
    342     ASSERT_TRUE(child.good());
    343 
    344     EXPECT_FALSE(parent.GetIsUnsynced());
    345     EXPECT_FALSE(parent.GetIsUnappliedUpdate());
    346     EXPECT_FALSE(child.GetIsUnsynced());
    347     EXPECT_FALSE(child.GetIsUnappliedUpdate());
    348   }
    349 }
    350 
    351 // Test that the applicator can handle updates delivered out of order.
    352 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
    353        BookmarkChildrenBeforeParent) {
    354   // Start with some bookmarks whose parents are unknown.
    355   std::string root_server_id = syncable::GetNullId().GetServerId();
    356   int64 a_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
    357       "a_child_created_first", DefaultBookmarkSpecifics(), "parent");
    358   int64 x_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
    359       "x_child_created_first", DefaultBookmarkSpecifics(), "parent");
    360 
    361   // Update application will fail.
    362   sessions::StatusController status1;
    363   ApplyBookmarkUpdates(&status1);
    364   EXPECT_EQ(0, status1.num_updates_applied());
    365   EXPECT_EQ(2, status1.num_hierarchy_conflicts());
    366 
    367   {
    368     syncable::ReadTransaction trans(FROM_HERE, directory());
    369 
    370     syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
    371     syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
    372 
    373     ASSERT_TRUE(a.good());
    374     ASSERT_TRUE(x.good());
    375 
    376     EXPECT_TRUE(a.GetIsUnappliedUpdate());
    377     EXPECT_TRUE(x.GetIsUnappliedUpdate());
    378   }
    379 
    380   // Now add their parent and a few siblings.
    381   entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
    382       "parent", DefaultBookmarkSpecifics(), root_server_id);
    383   entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
    384       "a_child_created_second", DefaultBookmarkSpecifics(), "parent");
    385   entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
    386       "x_child_created_second", DefaultBookmarkSpecifics(), "parent");
    387 
    388   // Update application will succeed.
    389   sessions::StatusController status2;
    390   ApplyBookmarkUpdates(&status2);
    391   EXPECT_EQ(5, status2.num_updates_applied())
    392       << "All updates should have been successfully applied";
    393 
    394   {
    395     syncable::ReadTransaction trans(FROM_HERE, directory());
    396 
    397     syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
    398     syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
    399 
    400     ASSERT_TRUE(a.good());
    401     ASSERT_TRUE(x.good());
    402 
    403     EXPECT_FALSE(a.GetIsUnappliedUpdate());
    404     EXPECT_FALSE(x.GetIsUnappliedUpdate());
    405   }
    406 }
    407 
    408 // Try to apply changes on an item that is both IS_UNSYNCED and
    409 // IS_UNAPPLIED_UPDATE.  Conflict resolution should be performed.
    410 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, SimpleBookmarkConflict) {
    411   int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem("x");
    412 
    413   int original_server_version = -10;
    414   {
    415     syncable::ReadTransaction trans(FROM_HERE, directory());
    416     syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
    417     original_server_version = e.GetServerVersion();
    418     ASSERT_NE(original_server_version, e.GetBaseVersion());
    419     EXPECT_TRUE(e.GetIsUnsynced());
    420   }
    421 
    422   sessions::StatusController status;
    423   ApplyBookmarkUpdates(&status);
    424   EXPECT_EQ(1, status.num_server_overwrites())
    425       << "Unsynced and unapplied item conflict should be resolved";
    426   EXPECT_EQ(0, status.num_updates_applied())
    427       << "Update should not be applied; we should override the server.";
    428 
    429   {
    430     syncable::ReadTransaction trans(FROM_HERE, directory());
    431     syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
    432     ASSERT_TRUE(e.good());
    433     EXPECT_EQ(original_server_version, e.GetServerVersion());
    434     EXPECT_EQ(original_server_version, e.GetBaseVersion());
    435     EXPECT_FALSE(e.GetIsUnappliedUpdate());
    436 
    437     // The unsynced flag will remain set until we successfully commit the item.
    438     EXPECT_TRUE(e.GetIsUnsynced());
    439   }
    440 }
    441 
    442 // Create a simple conflict that is also a hierarchy conflict.  If we were to
    443 // follow the normal "server wins" logic, we'd end up violating hierarchy
    444 // constraints.  The hierarchy conflict must take precedence.  We can not allow
    445 // the update to be applied.  The item must remain in the conflict state.
    446 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, HierarchyAndSimpleConflict) {
    447   // Create a simply-conflicting item.  It will start with valid parent ids.
    448   int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem(
    449       "orphaned_by_server");
    450   {
    451     // Manually set the SERVER_PARENT_ID to bad value.
    452     // A bad parent indicates a hierarchy conflict.
    453     syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
    454     syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
    455     ASSERT_TRUE(entry.good());
    456 
    457     entry.PutServerParentId(TestIdFactory::MakeServer("bogus_parent"));
    458   }
    459 
    460   sessions::StatusController status;
    461   ApplyBookmarkUpdates(&status);
    462   EXPECT_EQ(0, status.num_updates_applied());
    463   EXPECT_EQ(0, status.num_server_overwrites());
    464   EXPECT_EQ(1, status.num_hierarchy_conflicts());
    465 
    466   {
    467     syncable::ReadTransaction trans(FROM_HERE, directory());
    468     syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
    469     ASSERT_TRUE(e.good());
    470     EXPECT_TRUE(e.GetIsUnappliedUpdate());
    471     EXPECT_TRUE(e.GetIsUnsynced());
    472   }
    473 }
    474 
    475 // Attempt to apply an udpate that would create a bookmark folder loop.  This
    476 // application should fail.
    477 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, BookmarkFolderLoop) {
    478   // Item 'X' locally has parent of 'root'.  Server is updating it to have
    479   // parent of 'Y'.
    480 
    481   // Create it as a child of root node.
    482   int64 handle = entry_factory()->CreateSyncedItem("X", BOOKMARKS, true);
    483 
    484   {
    485     syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
    486     syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
    487     ASSERT_TRUE(entry.good());
    488 
    489     // Re-parent from root to "Y"
    490     entry.PutServerVersion(entry_factory()->GetNextRevision());
    491     entry.PutIsUnappliedUpdate(true);
    492     entry.PutServerParentId(TestIdFactory::MakeServer("Y"));
    493   }
    494 
    495   // Item 'Y' is child of 'X'.
    496   entry_factory()->CreateUnsyncedItem(
    497       TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
    498       BOOKMARKS, NULL);
    499 
    500   // If the server's update were applied, we would have X be a child of Y, and Y
    501   // as a child of X.  That's a directory loop.  The UpdateApplicator should
    502   // prevent the update from being applied and note that this is a hierarchy
    503   // conflict.
    504 
    505   sessions::StatusController status;
    506   ApplyBookmarkUpdates(&status);
    507 
    508   // This should count as a hierarchy conflict.
    509   EXPECT_EQ(1, status.num_hierarchy_conflicts());
    510 
    511   {
    512     syncable::ReadTransaction trans(FROM_HERE, directory());
    513     syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
    514     ASSERT_TRUE(e.good());
    515     EXPECT_TRUE(e.GetIsUnappliedUpdate());
    516     EXPECT_FALSE(e.GetIsUnsynced());
    517   }
    518 }
    519 
    520 // Test update application where the update has been orphaned by a local folder
    521 // deletion.  The update application attempt should fail.
    522 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
    523        HierarchyConflictDeletedParent) {
    524   // Create a locally deleted parent item.
    525   int64 parent_handle;
    526   entry_factory()->CreateUnsyncedItem(
    527       syncable::Id::CreateFromServerId("parent"), TestIdFactory::root(),
    528       "parent", true, BOOKMARKS, &parent_handle);
    529   {
    530     syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
    531     syncable::MutableEntry entry(&trans,
    532                                  syncable::GET_BY_HANDLE,
    533                                  parent_handle);
    534     entry.PutIsDel(true);
    535   }
    536 
    537   // Create an incoming child from the server.
    538   int64 child_handle = entry_factory()->CreateUnappliedNewItemWithParent(
    539       "child", DefaultBookmarkSpecifics(), "parent");
    540 
    541   // The server's update may seem valid to some other client, but on this client
    542   // that new item's parent no longer exists.  The update should not be applied
    543   // and the update applicator should indicate this is a hierarchy conflict.
    544 
    545   sessions::StatusController status;
    546   ApplyBookmarkUpdates(&status);
    547   EXPECT_EQ(1, status.num_hierarchy_conflicts());
    548 
    549   {
    550     syncable::ReadTransaction trans(FROM_HERE, directory());
    551     syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
    552     ASSERT_TRUE(child.good());
    553     EXPECT_TRUE(child.GetIsUnappliedUpdate());
    554     EXPECT_FALSE(child.GetIsUnsynced());
    555   }
    556 }
    557 
    558 // Attempt to apply an update that deletes a folder where the folder has
    559 // locally-created children.  The update application should fail.
    560 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
    561        HierarchyConflictDeleteNonEmptyDirectory) {
    562   // Create a server-deleted folder as a child of root node.
    563   int64 parent_handle =
    564       entry_factory()->CreateSyncedItem("parent", BOOKMARKS, true);
    565   {
    566     syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
    567     syncable::MutableEntry entry(&trans,
    568                                  syncable::GET_BY_HANDLE,
    569                                  parent_handle);
    570     ASSERT_TRUE(entry.good());
    571 
    572     // Delete it on the server.
    573     entry.PutServerVersion(entry_factory()->GetNextRevision());
    574     entry.PutIsUnappliedUpdate(true);
    575     entry.PutServerParentId(TestIdFactory::root());
    576     entry.PutServerIsDel(true);
    577   }
    578 
    579   // Create a local child of the server-deleted directory.
    580   entry_factory()->CreateUnsyncedItem(
    581       TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
    582       "child", false, BOOKMARKS, NULL);
    583 
    584   // The server's request to delete the directory must be ignored, otherwise our
    585   // unsynced new child would be orphaned.  This is a hierarchy conflict.
    586 
    587   sessions::StatusController status;
    588   ApplyBookmarkUpdates(&status);
    589 
    590   // This should count as a hierarchy conflict.
    591   EXPECT_EQ(1, status.num_hierarchy_conflicts());
    592 
    593   {
    594     syncable::ReadTransaction trans(FROM_HERE, directory());
    595     syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
    596     ASSERT_TRUE(parent.good());
    597     EXPECT_TRUE(parent.GetIsUnappliedUpdate());
    598     EXPECT_FALSE(parent.GetIsUnsynced());
    599   }
    600 }
    601 
    602 // Attempt to apply updates where the updated item's parent is not known to this
    603 // client.  The update application attempt should fail.
    604 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
    605        HierarchyConflictUnknownParent) {
    606   // We shouldn't be able to do anything with either of these items.
    607   int64 x_handle = entry_factory()->CreateUnappliedNewItemWithParent(
    608       "some_item", DefaultBookmarkSpecifics(), "unknown_parent");
    609   int64 y_handle = entry_factory()->CreateUnappliedNewItemWithParent(
    610       "some_other_item", DefaultBookmarkSpecifics(), "some_item");
    611 
    612   sessions::StatusController status;
    613   ApplyBookmarkUpdates(&status);
    614 
    615   EXPECT_EQ(2, status.num_hierarchy_conflicts())
    616       << "All updates with an unknown ancestors should be in conflict";
    617   EXPECT_EQ(0, status.num_updates_applied())
    618       << "No item with an unknown ancestor should be applied";
    619 
    620   {
    621     syncable::ReadTransaction trans(FROM_HERE, directory());
    622     syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
    623     syncable::Entry y(&trans, syncable::GET_BY_HANDLE, y_handle);
    624     ASSERT_TRUE(x.good());
    625     ASSERT_TRUE(y.good());
    626     EXPECT_TRUE(x.GetIsUnappliedUpdate());
    627     EXPECT_TRUE(y.GetIsUnappliedUpdate());
    628     EXPECT_FALSE(x.GetIsUnsynced());
    629     EXPECT_FALSE(y.GetIsUnsynced());
    630   }
    631 }
    632 
    633 // Attempt application of a mix of items.  Some update application attempts will
    634 // fail due to hierarchy conflicts.  Others should succeed.
    635 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, ItemsBothKnownAndUnknown) {
    636   // See what happens when there's a mixture of good and bad updates.
    637   std::string root_server_id = syncable::GetNullId().GetServerId();
    638   int64 u1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
    639       "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
    640   int64 k1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
    641       "first_known_item", DefaultBookmarkSpecifics(), root_server_id);
    642   int64 u2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
    643       "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
    644   int64 k2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
    645       "second_known_item", DefaultBookmarkSpecifics(), "first_known_item");
    646   int64 k3_handle = entry_factory()->CreateUnappliedNewItemWithParent(
    647       "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item");
    648   int64 k4_handle = entry_factory()->CreateUnappliedNewItemWithParent(
    649       "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id);
    650 
    651   sessions::StatusController status;
    652   ApplyBookmarkUpdates(&status);
    653 
    654   EXPECT_EQ(2, status.num_hierarchy_conflicts())
    655       << "The updates with unknown ancestors should be in conflict";
    656   EXPECT_EQ(4, status.num_updates_applied())
    657       << "The updates with known ancestors should be successfully applied";
    658 
    659   {
    660     syncable::ReadTransaction trans(FROM_HERE, directory());
    661     syncable::Entry u1(&trans, syncable::GET_BY_HANDLE, u1_handle);
    662     syncable::Entry u2(&trans, syncable::GET_BY_HANDLE, u2_handle);
    663     syncable::Entry k1(&trans, syncable::GET_BY_HANDLE, k1_handle);
    664     syncable::Entry k2(&trans, syncable::GET_BY_HANDLE, k2_handle);
    665     syncable::Entry k3(&trans, syncable::GET_BY_HANDLE, k3_handle);
    666     syncable::Entry k4(&trans, syncable::GET_BY_HANDLE, k4_handle);
    667     ASSERT_TRUE(u1.good());
    668     ASSERT_TRUE(u2.good());
    669     ASSERT_TRUE(k1.good());
    670     ASSERT_TRUE(k2.good());
    671     ASSERT_TRUE(k3.good());
    672     ASSERT_TRUE(k4.good());
    673     EXPECT_TRUE(u1.GetIsUnappliedUpdate());
    674     EXPECT_TRUE(u2.GetIsUnappliedUpdate());
    675     EXPECT_FALSE(k1.GetIsUnappliedUpdate());
    676     EXPECT_FALSE(k2.GetIsUnappliedUpdate());
    677     EXPECT_FALSE(k3.GetIsUnappliedUpdate());
    678     EXPECT_FALSE(k4.GetIsUnappliedUpdate());
    679   }
    680 }
    681 
    682 // Attempt application of password upates where the passphrase is known.
    683 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, DecryptablePassword) {
    684   // Decryptable password updates should be applied.
    685   Cryptographer* cryptographer;
    686   {
    687     // Storing the cryptographer separately is bad, but for this test we
    688     // know it's safe.
    689     syncable::ReadTransaction trans(FROM_HERE, directory());
    690     cryptographer = directory()->GetCryptographer(&trans);
    691   }
    692 
    693   KeyParams params = {"localhost", "dummy", "foobar"};
    694   cryptographer->AddKey(params);
    695 
    696   sync_pb::EntitySpecifics specifics;
    697   sync_pb::PasswordSpecificsData data;
    698   data.set_origin("http://example.com");
    699 
    700   cryptographer->Encrypt(data,
    701                          specifics.mutable_password()->mutable_encrypted());
    702   int64 handle =
    703       entry_factory()->CreateUnappliedNewItem("item", specifics, false);
    704 
    705   sessions::StatusController status;
    706   ApplyPasswordUpdates(&status);
    707 
    708   EXPECT_EQ(1, status.num_updates_applied())
    709       << "The updates that can be decrypted should be applied";
    710 
    711   {
    712     syncable::ReadTransaction trans(FROM_HERE, directory());
    713     syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
    714     ASSERT_TRUE(e.good());
    715     EXPECT_FALSE(e.GetIsUnappliedUpdate());
    716     EXPECT_FALSE(e.GetIsUnsynced());
    717   }
    718 }
    719 
    720 // Attempt application of encrypted items when the passphrase is not known.
    721 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, UndecryptableData) {
    722   // Undecryptable updates should not be applied.
    723   sync_pb::EntitySpecifics encrypted_bookmark;
    724   encrypted_bookmark.mutable_encrypted();
    725   AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
    726   std::string root_server_id = syncable::GetNullId().GetServerId();
    727   int64 folder_handle = entry_factory()->CreateUnappliedNewItemWithParent(
    728       "folder",
    729       encrypted_bookmark,
    730       root_server_id);
    731   int64 bookmark_handle = entry_factory()->CreateUnappliedNewItem(
    732       "item2",
    733       encrypted_bookmark,
    734       false);
    735   sync_pb::EntitySpecifics encrypted_password;
    736   encrypted_password.mutable_password();
    737   int64 password_handle = entry_factory()->CreateUnappliedNewItem(
    738       "item3",
    739       encrypted_password,
    740       false);
    741 
    742   sessions::StatusController status;
    743   ApplyBookmarkUpdates(&status);
    744   ApplyPasswordUpdates(&status);
    745 
    746   EXPECT_EQ(3, status.num_encryption_conflicts())
    747       << "Updates that can't be decrypted should be in encryption conflict";
    748   EXPECT_EQ(0, status.num_updates_applied())
    749       << "No update that can't be decrypted should be applied";
    750 
    751   {
    752     syncable::ReadTransaction trans(FROM_HERE, directory());
    753     syncable::Entry folder(&trans, syncable::GET_BY_HANDLE, folder_handle);
    754     syncable::Entry bm(&trans, syncable::GET_BY_HANDLE, bookmark_handle);
    755     syncable::Entry pw(&trans, syncable::GET_BY_HANDLE, password_handle);
    756     ASSERT_TRUE(folder.good());
    757     ASSERT_TRUE(bm.good());
    758     ASSERT_TRUE(pw.good());
    759     EXPECT_TRUE(folder.GetIsUnappliedUpdate());
    760     EXPECT_TRUE(bm.GetIsUnappliedUpdate());
    761     EXPECT_TRUE(pw.GetIsUnappliedUpdate());
    762   }
    763 }
    764 
    765 // Test a mix of decryptable and undecryptable updates.
    766 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, SomeUndecryptablePassword) {
    767   Cryptographer* cryptographer;
    768 
    769   int64 decryptable_handle = -1;
    770   int64 undecryptable_handle = -1;
    771 
    772   // Only decryptable password updates should be applied.
    773   {
    774     sync_pb::EntitySpecifics specifics;
    775     sync_pb::PasswordSpecificsData data;
    776     data.set_origin("http://example.com/1");
    777     {
    778       syncable::ReadTransaction trans(FROM_HERE, directory());
    779       cryptographer = directory()->GetCryptographer(&trans);
    780 
    781       KeyParams params = {"localhost", "dummy", "foobar"};
    782       cryptographer->AddKey(params);
    783 
    784       cryptographer->Encrypt(data,
    785           specifics.mutable_password()->mutable_encrypted());
    786     }
    787     decryptable_handle =
    788         entry_factory()->CreateUnappliedNewItem("item1", specifics, false);
    789   }
    790   {
    791     // Create a new cryptographer, independent of the one in the session.
    792     Cryptographer other_cryptographer(cryptographer->encryptor());
    793     KeyParams params = {"localhost", "dummy", "bazqux"};
    794     other_cryptographer.AddKey(params);
    795 
    796     sync_pb::EntitySpecifics specifics;
    797     sync_pb::PasswordSpecificsData data;
    798     data.set_origin("http://example.com/2");
    799 
    800     other_cryptographer.Encrypt(data,
    801         specifics.mutable_password()->mutable_encrypted());
    802     undecryptable_handle =
    803         entry_factory()->CreateUnappliedNewItem("item2", specifics, false);
    804   }
    805 
    806   sessions::StatusController status;
    807   ApplyPasswordUpdates(&status);
    808 
    809   EXPECT_EQ(1, status.num_encryption_conflicts())
    810       << "The updates that can't be decrypted should be in encryption "
    811       << "conflict";
    812   EXPECT_EQ(1, status.num_updates_applied())
    813       << "The undecryptable password update shouldn't be applied";
    814 
    815   {
    816     syncable::ReadTransaction trans(FROM_HERE, directory());
    817     syncable::Entry e1(&trans, syncable::GET_BY_HANDLE, decryptable_handle);
    818     syncable::Entry e2(&trans, syncable::GET_BY_HANDLE, undecryptable_handle);
    819     ASSERT_TRUE(e1.good());
    820     ASSERT_TRUE(e2.good());
    821     EXPECT_FALSE(e1.GetIsUnappliedUpdate());
    822     EXPECT_TRUE(e2.GetIsUnappliedUpdate());
    823   }
    824 }
    825 
    826 }  // namespace syncer
    827