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