Home | History | Annotate | Download | only in engine
      1 // Copyright 2012 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 // Syncer unit tests. Unfortunately a lot of these tests
      6 // are outdated and need to be reworked and updated.
      7 
      8 #include <algorithm>
      9 #include <limits>
     10 #include <list>
     11 #include <map>
     12 #include <set>
     13 #include <string>
     14 
     15 #include "base/bind.h"
     16 #include "base/bind_helpers.h"
     17 #include "base/callback.h"
     18 #include "base/compiler_specific.h"
     19 #include "base/location.h"
     20 #include "base/memory/scoped_ptr.h"
     21 #include "base/message_loop/message_loop.h"
     22 #include "base/strings/string_number_conversions.h"
     23 #include "base/strings/stringprintf.h"
     24 #include "base/time/time.h"
     25 #include "build/build_config.h"
     26 #include "sync/engine/get_commit_ids.h"
     27 #include "sync/engine/net/server_connection_manager.h"
     28 #include "sync/engine/sync_scheduler_impl.h"
     29 #include "sync/engine/syncer.h"
     30 #include "sync/engine/syncer_proto_util.h"
     31 #include "sync/internal_api/public/base/cancelation_signal.h"
     32 #include "sync/internal_api/public/base/model_type.h"
     33 #include "sync/internal_api/public/engine/model_safe_worker.h"
     34 #include "sync/internal_api/public/sessions/commit_counters.h"
     35 #include "sync/internal_api/public/sessions/status_counters.h"
     36 #include "sync/internal_api/public/sessions/update_counters.h"
     37 #include "sync/protocol/bookmark_specifics.pb.h"
     38 #include "sync/protocol/nigori_specifics.pb.h"
     39 #include "sync/protocol/preference_specifics.pb.h"
     40 #include "sync/protocol/sync.pb.h"
     41 #include "sync/sessions/sync_session_context.h"
     42 #include "sync/syncable/mutable_entry.h"
     43 #include "sync/syncable/nigori_util.h"
     44 #include "sync/syncable/syncable_delete_journal.h"
     45 #include "sync/syncable/syncable_read_transaction.h"
     46 #include "sync/syncable/syncable_util.h"
     47 #include "sync/syncable/syncable_write_transaction.h"
     48 #include "sync/test/engine/fake_model_worker.h"
     49 #include "sync/test/engine/mock_connection_manager.h"
     50 #include "sync/test/engine/mock_nudge_handler.h"
     51 #include "sync/test/engine/test_directory_setter_upper.h"
     52 #include "sync/test/engine/test_id_factory.h"
     53 #include "sync/test/engine/test_syncable_utils.h"
     54 #include "sync/test/fake_encryptor.h"
     55 #include "sync/test/fake_sync_encryption_handler.h"
     56 #include "sync/test/sessions/mock_debug_info_getter.h"
     57 #include "sync/util/cryptographer.h"
     58 #include "sync/util/extensions_activity.h"
     59 #include "sync/util/time.h"
     60 #include "testing/gtest/include/gtest/gtest.h"
     61 
     62 using base::TimeDelta;
     63 
     64 using std::count;
     65 using std::map;
     66 using std::multimap;
     67 using std::set;
     68 using std::string;
     69 using std::vector;
     70 
     71 namespace syncer {
     72 
     73 using syncable::BaseTransaction;
     74 using syncable::Blob;
     75 using syncable::CountEntriesWithName;
     76 using syncable::Directory;
     77 using syncable::Entry;
     78 using syncable::GetFirstEntryWithName;
     79 using syncable::GetOnlyEntryWithName;
     80 using syncable::Id;
     81 using syncable::kEncryptedString;
     82 using syncable::MutableEntry;
     83 using syncable::WriteTransaction;
     84 
     85 using syncable::CREATE;
     86 using syncable::GET_BY_HANDLE;
     87 using syncable::GET_BY_ID;
     88 using syncable::GET_BY_CLIENT_TAG;
     89 using syncable::GET_BY_SERVER_TAG;
     90 using syncable::GET_TYPE_ROOT;
     91 using syncable::UNITTEST;
     92 
     93 using sessions::MockDebugInfoGetter;
     94 using sessions::StatusController;
     95 using sessions::SyncSessionContext;
     96 using sessions::SyncSession;
     97 
     98 namespace {
     99 
    100 // A helper to hold on to the counters emitted by the sync engine.
    101 class TypeDebugInfoCache : public TypeDebugInfoObserver {
    102  public:
    103   TypeDebugInfoCache();
    104   virtual ~TypeDebugInfoCache();
    105 
    106   CommitCounters GetLatestCommitCounters(ModelType type) const;
    107   UpdateCounters GetLatestUpdateCounters(ModelType type) const;
    108   StatusCounters GetLatestStatusCounters(ModelType type) const;
    109 
    110   // TypeDebugInfoObserver implementation.
    111   virtual void OnCommitCountersUpdated(
    112       syncer::ModelType type,
    113       const CommitCounters& counters) OVERRIDE;
    114   virtual void OnUpdateCountersUpdated(
    115       syncer::ModelType type,
    116       const UpdateCounters& counters) OVERRIDE;
    117   virtual void OnStatusCountersUpdated(
    118       syncer::ModelType type,
    119       const StatusCounters& counters) OVERRIDE;
    120 
    121  private:
    122   std::map<ModelType, CommitCounters> commit_counters_map_;
    123   std::map<ModelType, UpdateCounters> update_counters_map_;
    124   std::map<ModelType, StatusCounters> status_counters_map_;
    125 };
    126 
    127 TypeDebugInfoCache::TypeDebugInfoCache() {}
    128 
    129 TypeDebugInfoCache::~TypeDebugInfoCache() {}
    130 
    131 CommitCounters TypeDebugInfoCache::GetLatestCommitCounters(
    132     ModelType type) const {
    133   std::map<ModelType, CommitCounters>::const_iterator it =
    134       commit_counters_map_.find(type);
    135   if (it == commit_counters_map_.end()) {
    136     return CommitCounters();
    137   } else {
    138     return it->second;
    139   }
    140 }
    141 
    142 UpdateCounters TypeDebugInfoCache::GetLatestUpdateCounters(
    143     ModelType type) const {
    144   std::map<ModelType, UpdateCounters>::const_iterator it =
    145       update_counters_map_.find(type);
    146   if (it == update_counters_map_.end()) {
    147     return UpdateCounters();
    148   } else {
    149     return it->second;
    150   }
    151 }
    152 
    153 StatusCounters TypeDebugInfoCache::GetLatestStatusCounters(
    154     ModelType type) const {
    155   std::map<ModelType, StatusCounters>::const_iterator it =
    156       status_counters_map_.find(type);
    157   if (it == status_counters_map_.end()) {
    158     return StatusCounters();
    159   } else {
    160     return it->second;
    161   }
    162 }
    163 
    164 void TypeDebugInfoCache::OnCommitCountersUpdated(
    165     syncer::ModelType type,
    166     const CommitCounters& counters) {
    167   commit_counters_map_[type] = counters;
    168 }
    169 
    170 void TypeDebugInfoCache::OnUpdateCountersUpdated(
    171     syncer::ModelType type,
    172     const UpdateCounters& counters) {
    173   update_counters_map_[type] = counters;
    174 }
    175 
    176 void TypeDebugInfoCache::OnStatusCountersUpdated(
    177     syncer::ModelType type,
    178     const StatusCounters& counters) {
    179   status_counters_map_[type] = counters;
    180 }
    181 
    182 } // namespace
    183 
    184 class SyncerTest : public testing::Test,
    185                    public SyncSession::Delegate,
    186                    public SyncEngineEventListener {
    187  protected:
    188   SyncerTest()
    189       : extensions_activity_(new ExtensionsActivity),
    190         syncer_(NULL),
    191         saw_syncer_event_(false),
    192         last_client_invalidation_hint_buffer_size_(10) {
    193 }
    194 
    195   // SyncSession::Delegate implementation.
    196   virtual void OnThrottled(const base::TimeDelta& throttle_duration) OVERRIDE {
    197     FAIL() << "Should not get silenced.";
    198   }
    199   virtual void OnTypesThrottled(
    200       ModelTypeSet types,
    201       const base::TimeDelta& throttle_duration) OVERRIDE {
    202     FAIL() << "Should not get silenced.";
    203   }
    204   virtual bool IsCurrentlyThrottled() OVERRIDE {
    205     return false;
    206   }
    207   virtual void OnReceivedLongPollIntervalUpdate(
    208       const base::TimeDelta& new_interval) OVERRIDE {
    209     last_long_poll_interval_received_ = new_interval;
    210   }
    211   virtual void OnReceivedShortPollIntervalUpdate(
    212       const base::TimeDelta& new_interval) OVERRIDE {
    213     last_short_poll_interval_received_ = new_interval;
    214   }
    215   virtual void OnReceivedCustomNudgeDelays(
    216       const std::map<ModelType, base::TimeDelta>& delay_map) OVERRIDE {
    217     std::map<ModelType, base::TimeDelta>::const_iterator iter =
    218         delay_map.find(SESSIONS);
    219     if (iter != delay_map.end() && iter->second > base::TimeDelta())
    220       last_sessions_commit_delay_ = iter->second;
    221     iter = delay_map.find(BOOKMARKS);
    222     if (iter != delay_map.end() && iter->second > base::TimeDelta())
    223       last_bookmarks_commit_delay_ = iter->second;
    224   }
    225   virtual void OnReceivedClientInvalidationHintBufferSize(
    226       int size) OVERRIDE {
    227     last_client_invalidation_hint_buffer_size_ = size;
    228   }
    229   virtual void OnReceivedGuRetryDelay(const base::TimeDelta& delay) OVERRIDE {}
    230   virtual void OnReceivedMigrationRequest(ModelTypeSet types) OVERRIDE {}
    231   virtual void OnProtocolEvent(const ProtocolEvent& event) OVERRIDE {}
    232   virtual void OnSyncProtocolError(const SyncProtocolError& error) OVERRIDE {}
    233 
    234   void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
    235     // We're just testing the sync engine here, so we shunt everything to
    236     // the SyncerThread.  Datatypes which aren't enabled aren't in the map.
    237     for (ModelTypeSet::Iterator it = enabled_datatypes_.First();
    238          it.Good(); it.Inc()) {
    239       (*out)[it.Get()] = GROUP_PASSIVE;
    240     }
    241   }
    242 
    243   virtual void OnSyncCycleEvent(const SyncCycleEvent& event) OVERRIDE {
    244     DVLOG(1) << "HandleSyncEngineEvent in unittest " << event.what_happened;
    245     // we only test for entry-specific events, not status changed ones.
    246     switch (event.what_happened) {
    247       case SyncCycleEvent::SYNC_CYCLE_BEGIN: // Fall through.
    248       case SyncCycleEvent::STATUS_CHANGED:
    249       case SyncCycleEvent::SYNC_CYCLE_ENDED:
    250         return;
    251       default:
    252         CHECK(false) << "Handling unknown error type in unit tests!!";
    253     }
    254     saw_syncer_event_ = true;
    255   }
    256 
    257   virtual void OnActionableError(const SyncProtocolError& error) OVERRIDE {}
    258   virtual void OnRetryTimeChanged(base::Time retry_time) OVERRIDE {}
    259   virtual void OnThrottledTypesChanged(ModelTypeSet throttled_types) OVERRIDE {}
    260   virtual void OnMigrationRequested(ModelTypeSet types) OVERRIDE {}
    261 
    262   void ResetSession() {
    263     session_.reset(SyncSession::Build(context_.get(), this));
    264   }
    265 
    266   void SyncShareNudge() {
    267     ResetSession();
    268 
    269     // Pretend we've seen a local change, to make the nudge_tracker look normal.
    270     nudge_tracker_.RecordLocalChange(ModelTypeSet(BOOKMARKS));
    271 
    272     EXPECT_TRUE(
    273         syncer_->NormalSyncShare(
    274             context_->GetEnabledTypes(),
    275             nudge_tracker_,
    276             session_.get()));
    277   }
    278 
    279   void SyncShareConfigure() {
    280     ResetSession();
    281     EXPECT_TRUE(syncer_->ConfigureSyncShare(
    282             context_->GetEnabledTypes(),
    283             sync_pb::GetUpdatesCallerInfo::RECONFIGURATION,
    284             session_.get()));
    285   }
    286 
    287   virtual void SetUp() {
    288     dir_maker_.SetUp();
    289     mock_server_.reset(new MockConnectionManager(directory(),
    290                                                  &cancelation_signal_));
    291     debug_info_getter_.reset(new MockDebugInfoGetter);
    292     EnableDatatype(BOOKMARKS);
    293     EnableDatatype(NIGORI);
    294     EnableDatatype(PREFERENCES);
    295     EnableDatatype(NIGORI);
    296     workers_.push_back(scoped_refptr<ModelSafeWorker>(
    297         new FakeModelWorker(GROUP_PASSIVE)));
    298     std::vector<SyncEngineEventListener*> listeners;
    299     listeners.push_back(this);
    300 
    301     ModelSafeRoutingInfo routing_info;
    302     GetModelSafeRoutingInfo(&routing_info);
    303 
    304     model_type_registry_.reset(
    305         new ModelTypeRegistry(workers_, directory(), &mock_nudge_handler_));
    306     model_type_registry_->RegisterDirectoryTypeDebugInfoObserver(
    307         &debug_info_cache_);
    308 
    309     context_.reset(new SyncSessionContext(
    310         mock_server_.get(),
    311         directory(),
    312         extensions_activity_.get(),
    313         listeners,
    314         debug_info_getter_.get(),
    315         model_type_registry_.get(),
    316         true,   // enable keystore encryption
    317         false,  // force enable pre-commit GU avoidance experiment
    318         "fake_invalidator_client_id"));
    319     context_->SetRoutingInfo(routing_info);
    320     syncer_ = new Syncer(&cancelation_signal_);
    321 
    322     syncable::ReadTransaction trans(FROM_HERE, directory());
    323     syncable::Directory::Metahandles children;
    324     directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
    325     ASSERT_EQ(0u, children.size());
    326     saw_syncer_event_ = false;
    327     root_id_ = TestIdFactory::root();
    328     parent_id_ = ids_.MakeServer("parent id");
    329     child_id_ = ids_.MakeServer("child id");
    330     directory()->set_store_birthday(mock_server_->store_birthday());
    331     mock_server_->SetKeystoreKey("encryption_key");
    332   }
    333 
    334   virtual void TearDown() {
    335     model_type_registry_->UnregisterDirectoryTypeDebugInfoObserver(
    336         &debug_info_cache_);
    337     mock_server_.reset();
    338     delete syncer_;
    339     syncer_ = NULL;
    340     dir_maker_.TearDown();
    341   }
    342 
    343   void WriteTestDataToEntry(WriteTransaction* trans, MutableEntry* entry) {
    344     EXPECT_FALSE(entry->GetIsDir());
    345     EXPECT_FALSE(entry->GetIsDel());
    346     sync_pb::EntitySpecifics specifics;
    347     specifics.mutable_bookmark()->set_url("http://demo/");
    348     specifics.mutable_bookmark()->set_favicon("PNG");
    349     entry->PutSpecifics(specifics);
    350     entry->PutIsUnsynced(true);
    351   }
    352   void VerifyTestDataInEntry(BaseTransaction* trans, Entry* entry) {
    353     EXPECT_FALSE(entry->GetIsDir());
    354     EXPECT_FALSE(entry->GetIsDel());
    355     VerifyTestBookmarkDataInEntry(entry);
    356   }
    357   void VerifyTestBookmarkDataInEntry(Entry* entry) {
    358     const sync_pb::EntitySpecifics& specifics = entry->GetSpecifics();
    359     EXPECT_TRUE(specifics.has_bookmark());
    360     EXPECT_EQ("PNG", specifics.bookmark().favicon());
    361     EXPECT_EQ("http://demo/", specifics.bookmark().url());
    362   }
    363 
    364   void VerifyHierarchyConflictsReported(
    365       const sync_pb::ClientToServerMessage& message) {
    366     // Our request should have included a warning about hierarchy conflicts.
    367     const sync_pb::ClientStatus& client_status = message.client_status();
    368     EXPECT_TRUE(client_status.has_hierarchy_conflict_detected());
    369     EXPECT_TRUE(client_status.hierarchy_conflict_detected());
    370   }
    371 
    372   void VerifyNoHierarchyConflictsReported(
    373       const sync_pb::ClientToServerMessage& message) {
    374     // Our request should have reported no hierarchy conflicts detected.
    375     const sync_pb::ClientStatus& client_status = message.client_status();
    376     EXPECT_TRUE(client_status.has_hierarchy_conflict_detected());
    377     EXPECT_FALSE(client_status.hierarchy_conflict_detected());
    378   }
    379 
    380   void VerifyHierarchyConflictsUnspecified(
    381       const sync_pb::ClientToServerMessage& message) {
    382     // Our request should have neither confirmed nor denied hierarchy conflicts.
    383     const sync_pb::ClientStatus& client_status = message.client_status();
    384     EXPECT_FALSE(client_status.has_hierarchy_conflict_detected());
    385   }
    386 
    387   sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
    388     sync_pb::EntitySpecifics result;
    389     AddDefaultFieldValue(BOOKMARKS, &result);
    390     return result;
    391   }
    392 
    393   sync_pb::EntitySpecifics DefaultPreferencesSpecifics() {
    394     sync_pb::EntitySpecifics result;
    395     AddDefaultFieldValue(PREFERENCES, &result);
    396     return result;
    397   }
    398   // Enumeration of alterations to entries for commit ordering tests.
    399   enum EntryFeature {
    400     LIST_END = 0,  // Denotes the end of the list of features from below.
    401     SYNCED,  // Items are unsynced by default
    402     DELETED,
    403     OLD_MTIME,
    404     MOVED_FROM_ROOT,
    405   };
    406 
    407   struct CommitOrderingTest {
    408     // expected commit index.
    409     int commit_index;
    410     // Details about the item
    411     syncable::Id id;
    412     syncable::Id parent_id;
    413     EntryFeature features[10];
    414 
    415     static CommitOrderingTest MakeLastCommitItem() {
    416       CommitOrderingTest last_commit_item;
    417       last_commit_item.commit_index = -1;
    418       last_commit_item.id = TestIdFactory::root();
    419       return last_commit_item;
    420     }
    421   };
    422 
    423   void RunCommitOrderingTest(CommitOrderingTest* test) {
    424     map<int, syncable::Id> expected_positions;
    425     {  // Transaction scope.
    426       WriteTransaction trans(FROM_HERE, UNITTEST, directory());
    427       while (!test->id.IsRoot()) {
    428         if (test->commit_index >= 0) {
    429           map<int, syncable::Id>::value_type entry(test->commit_index,
    430                                                    test->id);
    431           bool double_position = !expected_positions.insert(entry).second;
    432           ASSERT_FALSE(double_position) << "Two id's expected at one position";
    433         }
    434         string utf8_name = test->id.GetServerId();
    435         string name(utf8_name.begin(), utf8_name.end());
    436         MutableEntry entry(&trans, CREATE, BOOKMARKS, test->parent_id, name);
    437 
    438         entry.PutId(test->id);
    439         if (test->id.ServerKnows()) {
    440           entry.PutBaseVersion(5);
    441           entry.PutServerVersion(5);
    442           entry.PutServerParentId(test->parent_id);
    443         }
    444         entry.PutIsDir(true);
    445         entry.PutIsUnsynced(true);
    446         entry.PutSpecifics(DefaultBookmarkSpecifics());
    447         // Set the time to 30 seconds in the future to reduce the chance of
    448         // flaky tests.
    449         const base::Time& now_plus_30s =
    450             base::Time::Now() + base::TimeDelta::FromSeconds(30);
    451         const base::Time& now_minus_2h =
    452             base::Time::Now() - base::TimeDelta::FromHours(2);
    453         entry.PutMtime(now_plus_30s);
    454         for (size_t i = 0 ; i < arraysize(test->features) ; ++i) {
    455           switch (test->features[i]) {
    456             case LIST_END:
    457               break;
    458             case SYNCED:
    459               entry.PutIsUnsynced(false);
    460               break;
    461             case DELETED:
    462               entry.PutIsDel(true);
    463               break;
    464             case OLD_MTIME:
    465               entry.PutMtime(now_minus_2h);
    466               break;
    467             case MOVED_FROM_ROOT:
    468               entry.PutServerParentId(trans.root_id());
    469               break;
    470             default:
    471               FAIL() << "Bad value in CommitOrderingTest list";
    472           }
    473         }
    474         test++;
    475       }
    476     }
    477     SyncShareNudge();
    478     ASSERT_TRUE(expected_positions.size() ==
    479                 mock_server_->committed_ids().size());
    480     // If this test starts failing, be aware other sort orders could be valid.
    481     for (size_t i = 0; i < expected_positions.size(); ++i) {
    482       SCOPED_TRACE(i);
    483       EXPECT_EQ(1u, expected_positions.count(i));
    484       EXPECT_EQ(expected_positions[i], mock_server_->committed_ids()[i]);
    485     }
    486   }
    487 
    488   CommitCounters GetCommitCounters(ModelType type) {
    489     return debug_info_cache_.GetLatestCommitCounters(type);
    490   }
    491 
    492   UpdateCounters GetUpdateCounters(ModelType type) {
    493     return debug_info_cache_.GetLatestUpdateCounters(type);
    494   }
    495 
    496   StatusCounters GetStatusCounters(ModelType type) {
    497     return debug_info_cache_.GetLatestStatusCounters(type);
    498   }
    499 
    500   Directory* directory() {
    501     return dir_maker_.directory();
    502   }
    503 
    504   const std::string local_cache_guid() {
    505     return directory()->cache_guid();
    506   }
    507 
    508   const std::string foreign_cache_guid() {
    509     return "kqyg7097kro6GSUod+GSg==";
    510   }
    511 
    512   int64 CreateUnsyncedDirectory(const string& entry_name,
    513       const string& idstring) {
    514     return CreateUnsyncedDirectory(entry_name,
    515         syncable::Id::CreateFromServerId(idstring));
    516   }
    517 
    518   int64 CreateUnsyncedDirectory(const string& entry_name,
    519       const syncable::Id& id) {
    520     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
    521     MutableEntry entry(
    522         &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), entry_name);
    523     EXPECT_TRUE(entry.good());
    524     entry.PutIsUnsynced(true);
    525     entry.PutIsDir(true);
    526     entry.PutSpecifics(DefaultBookmarkSpecifics());
    527     entry.PutBaseVersion(id.ServerKnows() ? 1 : 0);
    528     entry.PutId(id);
    529     return entry.GetMetahandle();
    530   }
    531 
    532   void EnableDatatype(ModelType model_type) {
    533     enabled_datatypes_.Put(model_type);
    534 
    535     ModelSafeRoutingInfo routing_info;
    536     GetModelSafeRoutingInfo(&routing_info);
    537 
    538     if (context_) {
    539       context_->SetRoutingInfo(routing_info);
    540     }
    541 
    542     mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
    543   }
    544 
    545   void DisableDatatype(ModelType model_type) {
    546     enabled_datatypes_.Remove(model_type);
    547 
    548     ModelSafeRoutingInfo routing_info;
    549     GetModelSafeRoutingInfo(&routing_info);
    550 
    551     if (context_) {
    552       context_->SetRoutingInfo(routing_info);
    553     }
    554 
    555     mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
    556   }
    557 
    558   Cryptographer* GetCryptographer(syncable::BaseTransaction* trans) {
    559     return directory()->GetCryptographer(trans);
    560   }
    561 
    562   // Configures SyncSessionContext and NudgeTracker so Syncer won't call
    563   // GetUpdates prior to Commit. This method can be used to ensure a Commit is
    564   // not preceeded by GetUpdates.
    565   void ConfigureNoGetUpdatesRequired() {
    566     context_->set_server_enabled_pre_commit_update_avoidance(true);
    567     nudge_tracker_.OnInvalidationsEnabled();
    568     nudge_tracker_.RecordSuccessfulSyncCycle();
    569 
    570     ASSERT_FALSE(context_->ShouldFetchUpdatesBeforeCommit());
    571     ASSERT_FALSE(nudge_tracker_.IsGetUpdatesRequired());
    572   }
    573 
    574   base::MessageLoop message_loop_;
    575 
    576   // Some ids to aid tests. Only the root one's value is specific. The rest
    577   // are named for test clarity.
    578   // TODO(chron): Get rid of these inbuilt IDs. They only make it
    579   // more confusing.
    580   syncable::Id root_id_;
    581   syncable::Id parent_id_;
    582   syncable::Id child_id_;
    583 
    584   TestIdFactory ids_;
    585 
    586   TestDirectorySetterUpper dir_maker_;
    587   FakeEncryptor encryptor_;
    588   scoped_refptr<ExtensionsActivity> extensions_activity_;
    589   scoped_ptr<MockConnectionManager> mock_server_;
    590   CancelationSignal cancelation_signal_;
    591 
    592   Syncer* syncer_;
    593 
    594   scoped_ptr<SyncSession> session_;
    595   TypeDebugInfoCache debug_info_cache_;
    596   MockNudgeHandler mock_nudge_handler_;
    597   scoped_ptr<ModelTypeRegistry> model_type_registry_;
    598   scoped_ptr<SyncSessionContext> context_;
    599   bool saw_syncer_event_;
    600   base::TimeDelta last_short_poll_interval_received_;
    601   base::TimeDelta last_long_poll_interval_received_;
    602   base::TimeDelta last_sessions_commit_delay_;
    603   base::TimeDelta last_bookmarks_commit_delay_;
    604   int last_client_invalidation_hint_buffer_size_;
    605   std::vector<scoped_refptr<ModelSafeWorker> > workers_;
    606 
    607   ModelTypeSet enabled_datatypes_;
    608   sessions::NudgeTracker nudge_tracker_;
    609   scoped_ptr<MockDebugInfoGetter> debug_info_getter_;
    610 
    611   DISALLOW_COPY_AND_ASSIGN(SyncerTest);
    612 };
    613 
    614 TEST_F(SyncerTest, TestCallGatherUnsyncedEntries) {
    615   {
    616     Syncer::UnsyncedMetaHandles handles;
    617     {
    618       syncable::ReadTransaction trans(FROM_HERE, directory());
    619       GetUnsyncedEntries(&trans, &handles);
    620     }
    621     ASSERT_EQ(0u, handles.size());
    622   }
    623   // TODO(sync): When we can dynamically connect and disconnect the mock
    624   // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
    625   // regression for a very old bug.
    626 }
    627 
    628 TEST_F(SyncerTest, GetCommitIdsFiltersThrottledEntries) {
    629   const ModelTypeSet throttled_types(BOOKMARKS);
    630   sync_pb::EntitySpecifics bookmark_data;
    631   AddDefaultFieldValue(BOOKMARKS, &bookmark_data);
    632 
    633   mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
    634                                    foreign_cache_guid(), "-1");
    635   SyncShareNudge();
    636 
    637   {
    638     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
    639     MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
    640     ASSERT_TRUE(A.good());
    641     A.PutIsUnsynced(true);
    642     A.PutSpecifics(bookmark_data);
    643     A.PutNonUniqueName("bookmark");
    644   }
    645 
    646   // Now sync without enabling bookmarks.
    647   mock_server_->ExpectGetUpdatesRequestTypes(
    648       Difference(context_->GetEnabledTypes(), ModelTypeSet(BOOKMARKS)));
    649   ResetSession();
    650   syncer_->NormalSyncShare(
    651       Difference(context_->GetEnabledTypes(), ModelTypeSet(BOOKMARKS)),
    652       nudge_tracker_,
    653       session_.get());
    654 
    655   {
    656     // Nothing should have been committed as bookmarks is throttled.
    657     syncable::ReadTransaction rtrans(FROM_HERE, directory());
    658     Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
    659     ASSERT_TRUE(entryA.good());
    660     EXPECT_TRUE(entryA.GetIsUnsynced());
    661   }
    662 
    663   // Sync again with bookmarks enabled.
    664   mock_server_->ExpectGetUpdatesRequestTypes(context_->GetEnabledTypes());
    665   SyncShareNudge();
    666   {
    667     // It should have been committed.
    668     syncable::ReadTransaction rtrans(FROM_HERE, directory());
    669     Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
    670     ASSERT_TRUE(entryA.good());
    671     EXPECT_FALSE(entryA.GetIsUnsynced());
    672   }
    673 }
    674 
    675 // We use a macro so we can preserve the error location.
    676 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
    677                      parent_id, version, server_version, id_fac, rtrans) \
    678   do { \
    679     Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
    680     ASSERT_TRUE(entryA.good()); \
    681     /* We don't use EXPECT_EQ here because when the left side param is false,
    682     gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
    683     EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \
    684     EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \
    685     EXPECT_TRUE(prev_initialized == \
    686               IsRealDataType(GetModelTypeFromSpecifics( \
    687                   entryA.GetBaseServerSpecifics()))); \
    688     EXPECT_TRUE(parent_id == -1 || \
    689                 entryA.GetParentId()== id_fac.FromNumber(parent_id)); \
    690     EXPECT_EQ(version, entryA.GetBaseVersion()); \
    691     EXPECT_EQ(server_version, entryA.GetServerVersion()); \
    692   } while (0)
    693 
    694 TEST_F(SyncerTest, GetCommitIdsFiltersUnreadyEntries) {
    695   KeyParams key_params = {"localhost", "dummy", "foobar"};
    696   KeyParams other_params = {"localhost", "dummy", "foobar2"};
    697   sync_pb::EntitySpecifics bookmark, encrypted_bookmark;
    698   bookmark.mutable_bookmark()->set_url("url");
    699   bookmark.mutable_bookmark()->set_title("title");
    700   AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
    701   mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
    702                                    foreign_cache_guid(), "-1");
    703   mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
    704                                    foreign_cache_guid(), "-2");
    705   mock_server_->AddUpdateDirectory(3, 0, "C", 10, 10,
    706                                    foreign_cache_guid(), "-3");
    707   mock_server_->AddUpdateDirectory(4, 0, "D", 10, 10,
    708                                    foreign_cache_guid(), "-4");
    709   SyncShareNudge();
    710   // Server side change will put A in conflict.
    711   mock_server_->AddUpdateDirectory(1, 0, "A", 20, 20,
    712                                    foreign_cache_guid(), "-1");
    713   {
    714     // Mark bookmarks as encrypted and set the cryptographer to have pending
    715     // keys.
    716     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
    717     Cryptographer other_cryptographer(&encryptor_);
    718     other_cryptographer.AddKey(other_params);
    719     sync_pb::EntitySpecifics specifics;
    720     sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
    721     other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
    722     dir_maker_.encryption_handler()->EnableEncryptEverything();
    723     // Set up with an old passphrase, but have pending keys
    724     GetCryptographer(&wtrans)->AddKey(key_params);
    725     GetCryptographer(&wtrans)->Encrypt(bookmark,
    726                                     encrypted_bookmark.mutable_encrypted());
    727     GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag());
    728 
    729     // In conflict but properly encrypted.
    730     MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
    731     ASSERT_TRUE(A.good());
    732     A.PutIsUnsynced(true);
    733     A.PutSpecifics(encrypted_bookmark);
    734     A.PutNonUniqueName(kEncryptedString);
    735     // Not in conflict and properly encrypted.
    736     MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
    737     ASSERT_TRUE(B.good());
    738     B.PutIsUnsynced(true);
    739     B.PutSpecifics(encrypted_bookmark);
    740     B.PutNonUniqueName(kEncryptedString);
    741     // Unencrypted specifics.
    742     MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
    743     ASSERT_TRUE(C.good());
    744     C.PutIsUnsynced(true);
    745     C.PutNonUniqueName(kEncryptedString);
    746     // Unencrypted non_unique_name.
    747     MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
    748     ASSERT_TRUE(D.good());
    749     D.PutIsUnsynced(true);
    750     D.PutSpecifics(encrypted_bookmark);
    751     D.PutNonUniqueName("not encrypted");
    752   }
    753   SyncShareNudge();
    754   {
    755     // Nothing should have commited due to bookmarks being encrypted and
    756     // the cryptographer having pending keys. A would have been resolved
    757     // as a simple conflict, but still be unsynced until the next sync cycle.
    758     syncable::ReadTransaction rtrans(FROM_HERE, directory());
    759     VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_, &rtrans);
    760     VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_, &rtrans);
    761     VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
    762     VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
    763 
    764     // Resolve the pending keys.
    765     GetCryptographer(&rtrans)->DecryptPendingKeys(other_params);
    766   }
    767   SyncShareNudge();
    768   {
    769     // All properly encrypted and non-conflicting items should commit. "A" was
    770     // conflicting, but last sync cycle resolved it as simple conflict, so on
    771     // this sync cycle it committed succesfullly.
    772     syncable::ReadTransaction rtrans(FROM_HERE, directory());
    773     // Committed successfully.
    774     VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
    775     // Committed successfully.
    776     VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
    777     // Was not properly encrypted.
    778     VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
    779     // Was not properly encrypted.
    780     VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
    781   }
    782   {
    783     // Fix the remaining items.
    784     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
    785     MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
    786     ASSERT_TRUE(C.good());
    787     C.PutSpecifics(encrypted_bookmark);
    788     C.PutNonUniqueName(kEncryptedString);
    789     MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
    790     ASSERT_TRUE(D.good());
    791     D.PutSpecifics(encrypted_bookmark);
    792     D.PutNonUniqueName(kEncryptedString);
    793   }
    794   SyncShareNudge();
    795   {
    796     const StatusController& status_controller = session_->status_controller();
    797     // Expect success.
    798     EXPECT_EQ(status_controller.model_neutral_state().commit_result, SYNCER_OK);
    799     // None should be unsynced anymore.
    800     syncable::ReadTransaction rtrans(FROM_HERE, directory());
    801     VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
    802     VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
    803     VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_, &rtrans);
    804     VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_, &rtrans);
    805   }
    806 }
    807 
    808 // This test uses internal knowledge of the directory to test correctness of
    809 // GetCommitIds.  In almost every other test, the hierarchy is created from
    810 // parent to child order, and so parents always have metahandles that are
    811 // smaller than those of their children.  This makes it very difficult to test
    812 // some GetCommitIds edge cases, since it uses metahandle ordering as
    813 // a starting point.
    814 TEST_F(SyncerTest, GetCommitIds_VerifyDeletionCommitOrder) {
    815   {
    816     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
    817 
    818     // Create four bookmarks folders at the root node.
    819     for (int i = 1; i < 5; ++i) {
    820       MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "");
    821       entry.PutId(ids_.FromNumber(i));
    822       entry.PutIsDir(true);
    823       entry.PutBaseVersion(5);
    824       entry.PutServerVersion(5);
    825       entry.PutServerParentId(trans.root_id());
    826       entry.PutServerIsDir(true);
    827       entry.PutIsUnsynced(true);
    828       entry.PutSpecifics(DefaultBookmarkSpecifics());
    829     }
    830 
    831     // Now iterate in reverse order make a hierarchy of them.
    832     // While we're at it, also mark them as deleted.
    833     syncable::Id parent_id = trans.root_id();
    834     for (int i = 4; i > 0; --i) {
    835       MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(i));
    836       entry.PutParentId(parent_id);
    837       entry.PutServerParentId(parent_id);
    838       entry.PutIsDel(true);
    839       parent_id = ids_.FromNumber(i);
    840     }
    841   }
    842 
    843   {
    844     // Run GetCommitIds, the function being tested.
    845     syncable::Directory::Metahandles result_handles;
    846     syncable::ReadTransaction trans(FROM_HERE, directory());
    847     GetCommitIdsForType(&trans, BOOKMARKS, 100, &result_handles);
    848 
    849     // Now verify the output.  We expect four results in child to parent order.
    850     ASSERT_EQ(4U, result_handles.size());
    851 
    852     Entry entry0(&trans, GET_BY_HANDLE, result_handles[0]);
    853     EXPECT_EQ(ids_.FromNumber(1), entry0.GetId());
    854 
    855     Entry entry1(&trans, GET_BY_HANDLE, result_handles[1]);
    856     EXPECT_EQ(ids_.FromNumber(2), entry1.GetId());
    857 
    858     Entry entry2(&trans, GET_BY_HANDLE, result_handles[2]);
    859     EXPECT_EQ(ids_.FromNumber(3), entry2.GetId());
    860 
    861     Entry entry3(&trans, GET_BY_HANDLE, result_handles[3]);
    862     EXPECT_EQ(ids_.FromNumber(4), entry3.GetId());
    863   }
    864 }
    865 
    866 TEST_F(SyncerTest, EncryptionAwareConflicts) {
    867   KeyParams key_params = {"localhost", "dummy", "foobar"};
    868   Cryptographer other_cryptographer(&encryptor_);
    869   other_cryptographer.AddKey(key_params);
    870   sync_pb::EntitySpecifics bookmark, encrypted_bookmark, modified_bookmark;
    871   bookmark.mutable_bookmark()->set_title("title");
    872   other_cryptographer.Encrypt(bookmark,
    873                               encrypted_bookmark.mutable_encrypted());
    874   AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
    875   modified_bookmark.mutable_bookmark()->set_title("title2");
    876   other_cryptographer.Encrypt(modified_bookmark,
    877                               modified_bookmark.mutable_encrypted());
    878   sync_pb::EntitySpecifics pref, encrypted_pref, modified_pref;
    879   pref.mutable_preference()->set_name("name");
    880   AddDefaultFieldValue(PREFERENCES, &encrypted_pref);
    881   other_cryptographer.Encrypt(pref,
    882                               encrypted_pref.mutable_encrypted());
    883   modified_pref.mutable_preference()->set_name("name2");
    884   other_cryptographer.Encrypt(modified_pref,
    885                               modified_pref.mutable_encrypted());
    886   {
    887     // Mark bookmarks and preferences as encrypted and set the cryptographer to
    888     // have pending keys.
    889     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
    890     sync_pb::EntitySpecifics specifics;
    891     sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
    892     other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
    893     dir_maker_.encryption_handler()->EnableEncryptEverything();
    894     GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag());
    895     EXPECT_TRUE(GetCryptographer(&wtrans)->has_pending_keys());
    896   }
    897 
    898   // We need to remember the exact position of our local items, so we can
    899   // make updates that do not modify those positions.
    900   UniquePosition pos1;
    901   UniquePosition pos2;
    902   UniquePosition pos3;
    903 
    904   mock_server_->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark,
    905                                    foreign_cache_guid(), "-1");
    906   mock_server_->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark,
    907                                    foreign_cache_guid(), "-2");
    908   mock_server_->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark,
    909                                    foreign_cache_guid(), "-3");
    910   mock_server_->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref);
    911   SyncShareNudge();
    912   {
    913     // Initial state. Everything is normal.
    914     syncable::ReadTransaction rtrans(FROM_HERE, directory());
    915     VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_, &rtrans);
    916     VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_, &rtrans);
    917     VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_, &rtrans);
    918     VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_, &rtrans);
    919 
    920     Entry entry1(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
    921     ASSERT_TRUE(entry1.GetUniquePosition().Equals(
    922         entry1.GetServerUniquePosition()));
    923     pos1 = entry1.GetUniquePosition();
    924     Entry entry2(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
    925     pos2 = entry2.GetUniquePosition();
    926     Entry entry3(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(3));
    927     pos3 = entry3.GetUniquePosition();
    928   }
    929 
    930   // Server side encryption will not be applied due to undecryptable data.
    931   // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
    932   mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 20, 20, true, 0,
    933                                    encrypted_bookmark,
    934                                    foreign_cache_guid(), "-1");
    935   mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 20, 20, false, 2,
    936                                    encrypted_bookmark,
    937                                    foreign_cache_guid(), "-2");
    938   mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 20, 20, false, 1,
    939                                    encrypted_bookmark,
    940                                    foreign_cache_guid(), "-3");
    941   mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 20, 20, false, 0,
    942                                    encrypted_pref,
    943                                    foreign_cache_guid(), "-4");
    944   SyncShareNudge();
    945   {
    946     // All should be unapplied due to being undecryptable and have a valid
    947     // BASE_SERVER_SPECIFICS.
    948     syncable::ReadTransaction rtrans(FROM_HERE, directory());
    949     VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_, &rtrans);
    950     VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_, &rtrans);
    951     VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
    952     VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_, &rtrans);
    953   }
    954 
    955   // Server side change that don't modify anything should not affect
    956   // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
    957   mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 30, 30, true, 0,
    958                                    encrypted_bookmark,
    959                                    foreign_cache_guid(), "-1");
    960   mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 30, 30, false, 2,
    961                                    encrypted_bookmark,
    962                                    foreign_cache_guid(), "-2");
    963   // Item 3 doesn't change.
    964   mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 30, 30, false, 0,
    965                                    encrypted_pref,
    966                                    foreign_cache_guid(), "-4");
    967   SyncShareNudge();
    968   {
    969     // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
    970     // All should remain unapplied due to be undecryptable.
    971     syncable::ReadTransaction rtrans(FROM_HERE, directory());
    972     VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_, &rtrans);
    973     VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
    974     VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
    975     VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
    976   }
    977 
    978   // Positional changes, parent changes, and specifics changes should reset
    979   // BASE_SERVER_SPECIFICS.
    980   // Became unencrypted.
    981   mock_server_->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark,
    982                                    foreign_cache_guid(), "-1");
    983   // Reordered to after item 2.
    984   mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 30, 30, false, 3,
    985                                    encrypted_bookmark,
    986                                    foreign_cache_guid(), "-3");
    987   SyncShareNudge();
    988   {
    989     // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
    990     // Items 1 is now unencrypted, so should have applied normally.
    991     syncable::ReadTransaction rtrans(FROM_HERE, directory());
    992     VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_, &rtrans);
    993     VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
    994     VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_, &rtrans);
    995     VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
    996   }
    997 
    998   // Make local changes, which should remain unsynced for items 2, 3, 4.
    999   {
   1000     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1001     MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   1002     ASSERT_TRUE(A.good());
   1003     A.PutSpecifics(modified_bookmark);
   1004     A.PutNonUniqueName(kEncryptedString);
   1005     A.PutIsUnsynced(true);
   1006     MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
   1007     ASSERT_TRUE(B.good());
   1008     B.PutSpecifics(modified_bookmark);
   1009     B.PutNonUniqueName(kEncryptedString);
   1010     B.PutIsUnsynced(true);
   1011     MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
   1012     ASSERT_TRUE(C.good());
   1013     C.PutSpecifics(modified_bookmark);
   1014     C.PutNonUniqueName(kEncryptedString);
   1015     C.PutIsUnsynced(true);
   1016     MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
   1017     ASSERT_TRUE(D.good());
   1018     D.PutSpecifics(modified_pref);
   1019     D.PutNonUniqueName(kEncryptedString);
   1020     D.PutIsUnsynced(true);
   1021   }
   1022   SyncShareNudge();
   1023   {
   1024     // Item 1 remains unsynced due to there being pending keys.
   1025     // Items 2, 3, 4 should remain unsynced since they were not up to date.
   1026     syncable::ReadTransaction rtrans(FROM_HERE, directory());
   1027     VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_, &rtrans);
   1028     VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_, &rtrans);
   1029     VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_, &rtrans);
   1030     VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_, &rtrans);
   1031   }
   1032 
   1033   {
   1034     syncable::ReadTransaction rtrans(FROM_HERE, directory());
   1035     // Resolve the pending keys.
   1036     GetCryptographer(&rtrans)->DecryptPendingKeys(key_params);
   1037   }
   1038   // First cycle resolves conflicts, second cycle commits changes.
   1039   SyncShareNudge();
   1040   EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_server_overwrites);
   1041   EXPECT_EQ(1, GetUpdateCounters(PREFERENCES).num_server_overwrites);
   1042   EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_local_overwrites);
   1043 
   1044   // We successfully commited item(s).
   1045   EXPECT_EQ(2, GetCommitCounters(BOOKMARKS).num_commits_attempted);
   1046   EXPECT_EQ(2, GetCommitCounters(BOOKMARKS).num_commits_success);
   1047   EXPECT_EQ(1, GetCommitCounters(PREFERENCES).num_commits_attempted);
   1048   EXPECT_EQ(1, GetCommitCounters(PREFERENCES).num_commits_success);
   1049 
   1050   SyncShareNudge();
   1051 
   1052   // Everything should be resolved now. The local changes should have
   1053   // overwritten the server changes for 2 and 4, while the server changes
   1054   // overwrote the local for entry 3.
   1055   //
   1056   // Expect there will be no new overwrites.
   1057   EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_server_overwrites);
   1058   EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_local_overwrites);
   1059 
   1060   EXPECT_EQ(2, GetCommitCounters(BOOKMARKS).num_commits_success);
   1061   EXPECT_EQ(1, GetCommitCounters(PREFERENCES).num_commits_success);
   1062 
   1063   syncable::ReadTransaction rtrans(FROM_HERE, directory());
   1064   VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_, &rtrans);
   1065   VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_, &rtrans);
   1066   VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_, &rtrans);
   1067   VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_, &rtrans);
   1068 }
   1069 
   1070 #undef VERIFY_ENTRY
   1071 
   1072 TEST_F(SyncerTest, TestGetUnsyncedAndSimpleCommit) {
   1073   {
   1074     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1075     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
   1076     ASSERT_TRUE(parent.good());
   1077     parent.PutIsUnsynced(true);
   1078     parent.PutIsDir(true);
   1079     parent.PutSpecifics(DefaultBookmarkSpecifics());
   1080     parent.PutBaseVersion(1);
   1081     parent.PutId(parent_id_);
   1082     MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete");
   1083     ASSERT_TRUE(child.good());
   1084     child.PutId(child_id_);
   1085     child.PutBaseVersion(1);
   1086     WriteTestDataToEntry(&wtrans, &child);
   1087   }
   1088 
   1089   SyncShareNudge();
   1090   ASSERT_EQ(2u, mock_server_->committed_ids().size());
   1091   // If this test starts failing, be aware other sort orders could be valid.
   1092   EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
   1093   EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
   1094   {
   1095     syncable::ReadTransaction rt(FROM_HERE, directory());
   1096     Entry entry(&rt, syncable::GET_BY_ID, child_id_);
   1097     ASSERT_TRUE(entry.good());
   1098     VerifyTestDataInEntry(&rt, &entry);
   1099   }
   1100 }
   1101 
   1102 TEST_F(SyncerTest, TestPurgeWhileUnsynced) {
   1103   // Similar to above, but throw a purge operation into the mix. Bug 49278.
   1104   syncable::Id pref_node_id = TestIdFactory::MakeServer("Tim");
   1105   {
   1106     directory()->SetDownloadProgress(BOOKMARKS,
   1107                                      syncable::BuildProgress(BOOKMARKS));
   1108     directory()->SetDownloadProgress(PREFERENCES,
   1109                                      syncable::BuildProgress(PREFERENCES));
   1110     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1111     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
   1112     ASSERT_TRUE(parent.good());
   1113     parent.PutIsUnsynced(true);
   1114     parent.PutIsDir(true);
   1115     parent.PutSpecifics(DefaultBookmarkSpecifics());
   1116     parent.PutBaseVersion(1);
   1117     parent.PutId(parent_id_);
   1118     MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete");
   1119     ASSERT_TRUE(child.good());
   1120     child.PutId(child_id_);
   1121     child.PutBaseVersion(1);
   1122     WriteTestDataToEntry(&wtrans, &child);
   1123 
   1124     MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Tim");
   1125     ASSERT_TRUE(parent2.good());
   1126     parent2.PutIsUnsynced(true);
   1127     parent2.PutIsDir(true);
   1128     parent2.PutSpecifics(DefaultPreferencesSpecifics());
   1129     parent2.PutBaseVersion(1);
   1130     parent2.PutId(pref_node_id);
   1131   }
   1132 
   1133   directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES),
   1134                                       ModelTypeSet(),
   1135                                       ModelTypeSet());
   1136 
   1137   SyncShareNudge();
   1138   ASSERT_EQ(2U, mock_server_->committed_ids().size());
   1139   // If this test starts failing, be aware other sort orders could be valid.
   1140   EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
   1141   EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
   1142   {
   1143     syncable::ReadTransaction rt(FROM_HERE, directory());
   1144     Entry entry(&rt, syncable::GET_BY_ID, child_id_);
   1145     ASSERT_TRUE(entry.good());
   1146     VerifyTestDataInEntry(&rt, &entry);
   1147   }
   1148   directory()->SaveChanges();
   1149   {
   1150     syncable::ReadTransaction rt(FROM_HERE, directory());
   1151     Entry entry(&rt, syncable::GET_BY_ID, pref_node_id);
   1152     ASSERT_FALSE(entry.good());
   1153   }
   1154 }
   1155 
   1156 TEST_F(SyncerTest, TestPurgeWhileUnapplied) {
   1157   // Similar to above, but for unapplied items. Bug 49278.
   1158   {
   1159     directory()->SetDownloadProgress(BOOKMARKS,
   1160                                      syncable::BuildProgress(BOOKMARKS));
   1161     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1162     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
   1163     ASSERT_TRUE(parent.good());
   1164     parent.PutIsUnappliedUpdate(true);
   1165     parent.PutIsDir(true);
   1166     parent.PutSpecifics(DefaultBookmarkSpecifics());
   1167     parent.PutBaseVersion(1);
   1168     parent.PutId(parent_id_);
   1169   }
   1170 
   1171   directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS),
   1172                                       ModelTypeSet(),
   1173                                       ModelTypeSet());
   1174 
   1175   SyncShareNudge();
   1176   directory()->SaveChanges();
   1177   {
   1178     syncable::ReadTransaction rt(FROM_HERE, directory());
   1179     Entry entry(&rt, syncable::GET_BY_ID, parent_id_);
   1180     ASSERT_FALSE(entry.good());
   1181   }
   1182 }
   1183 
   1184 TEST_F(SyncerTest, TestPurgeWithJournal) {
   1185   {
   1186     directory()->SetDownloadProgress(BOOKMARKS,
   1187                                      syncable::BuildProgress(BOOKMARKS));
   1188     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1189     MutableEntry parent(&wtrans, syncable::CREATE, BOOKMARKS, wtrans.root_id(),
   1190                         "Pete");
   1191     ASSERT_TRUE(parent.good());
   1192     parent.PutIsDir(true);
   1193     parent.PutSpecifics(DefaultBookmarkSpecifics());
   1194     parent.PutBaseVersion(1);
   1195     parent.PutId(parent_id_);
   1196     MutableEntry child(&wtrans, syncable::CREATE, BOOKMARKS, parent_id_,
   1197                        "Pete");
   1198     ASSERT_TRUE(child.good());
   1199     child.PutId(child_id_);
   1200     child.PutBaseVersion(1);
   1201     WriteTestDataToEntry(&wtrans, &child);
   1202 
   1203     MutableEntry parent2(&wtrans, syncable::CREATE, PREFERENCES,
   1204                          wtrans.root_id(), "Tim");
   1205     ASSERT_TRUE(parent2.good());
   1206     parent2.PutIsDir(true);
   1207     parent2.PutSpecifics(DefaultPreferencesSpecifics());
   1208     parent2.PutBaseVersion(1);
   1209     parent2.PutId(TestIdFactory::MakeServer("Tim"));
   1210   }
   1211 
   1212   directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES, BOOKMARKS),
   1213                                       ModelTypeSet(BOOKMARKS),
   1214                                       ModelTypeSet());
   1215   {
   1216     // Verify bookmark nodes are saved in delete journal but not preference
   1217     // node.
   1218     syncable::ReadTransaction rt(FROM_HERE, directory());
   1219     syncable::DeleteJournal* delete_journal = directory()->delete_journal();
   1220     EXPECT_EQ(2u, delete_journal->GetDeleteJournalSize(&rt));
   1221     syncable::EntryKernelSet journal_entries;
   1222     directory()->delete_journal()->GetDeleteJournals(&rt, BOOKMARKS,
   1223                                                      &journal_entries);
   1224     EXPECT_EQ(parent_id_, (*journal_entries.begin())->ref(syncable::ID));
   1225     EXPECT_EQ(child_id_, (*journal_entries.rbegin())->ref(syncable::ID));
   1226   }
   1227 }
   1228 
   1229 TEST_F(SyncerTest, ResetVersions) {
   1230   // Download the top level pref node and some pref items.
   1231   mock_server_->AddUpdateDirectory(
   1232       parent_id_, root_id_, "prefs", 1, 10, std::string(), std::string());
   1233   mock_server_->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES));
   1234   mock_server_->AddUpdatePref("id1", parent_id_.GetServerId(), "tag1", 20, 20);
   1235   mock_server_->AddUpdatePref("id2", parent_id_.GetServerId(), "tag2", 30, 30);
   1236   mock_server_->AddUpdatePref("id3", parent_id_.GetServerId(), "tag3", 40, 40);
   1237   SyncShareNudge();
   1238 
   1239   {
   1240     // Modify one of the preferences locally, mark another one as unapplied,
   1241     // and create another unsynced preference.
   1242     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1243     MutableEntry entry(&wtrans, GET_BY_CLIENT_TAG, "tag1");
   1244     entry.PutIsUnsynced(true);
   1245 
   1246     MutableEntry entry2(&wtrans, GET_BY_CLIENT_TAG, "tag2");
   1247     entry2.PutIsUnappliedUpdate(true);
   1248 
   1249     MutableEntry entry4(&wtrans, CREATE, PREFERENCES, parent_id_, "name");
   1250     entry4.PutUniqueClientTag("tag4");
   1251     entry4.PutIsUnsynced(true);
   1252   }
   1253 
   1254   {
   1255     // Reset the versions.
   1256     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1257     ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans, PREFERENCES));
   1258   }
   1259 
   1260   {
   1261     // Verify the synced items are all with version 1 now, with
   1262     // unsynced/unapplied state preserved.
   1263     syncable::ReadTransaction trans(FROM_HERE, directory());
   1264     Entry entry(&trans, GET_BY_CLIENT_TAG, "tag1");
   1265     EXPECT_EQ(1, entry.GetBaseVersion());
   1266     EXPECT_EQ(1, entry.GetServerVersion());
   1267     EXPECT_TRUE(entry.GetIsUnsynced());
   1268     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   1269     Entry entry2(&trans, GET_BY_CLIENT_TAG, "tag2");
   1270     EXPECT_EQ(1, entry2.GetBaseVersion());
   1271     EXPECT_EQ(1, entry2.GetServerVersion());
   1272     EXPECT_FALSE(entry2.GetIsUnsynced());
   1273     EXPECT_TRUE(entry2.GetIsUnappliedUpdate());
   1274     Entry entry3(&trans, GET_BY_CLIENT_TAG, "tag3");
   1275     EXPECT_EQ(1, entry3.GetBaseVersion());
   1276     EXPECT_EQ(1, entry3.GetServerVersion());
   1277     EXPECT_FALSE(entry3.GetIsUnsynced());
   1278     EXPECT_FALSE(entry3.GetIsUnappliedUpdate());
   1279 
   1280     // Entry 4 (the locally created one) should remain the same.
   1281     Entry entry4(&trans, GET_BY_CLIENT_TAG, "tag4");
   1282     EXPECT_EQ(-1, entry4.GetBaseVersion());
   1283     EXPECT_EQ(0, entry4.GetServerVersion());
   1284     EXPECT_TRUE(entry4.GetIsUnsynced());
   1285     EXPECT_FALSE(entry4.GetIsUnappliedUpdate());
   1286   }
   1287 }
   1288 
   1289 TEST_F(SyncerTest, TestCommitListOrderingTwoItemsTall) {
   1290   CommitOrderingTest items[] = {
   1291     {1, ids_.FromNumber(-1001), ids_.FromNumber(-1000)},
   1292     {0, ids_.FromNumber(-1000), ids_.FromNumber(0)},
   1293     CommitOrderingTest::MakeLastCommitItem(),
   1294   };
   1295   RunCommitOrderingTest(items);
   1296 }
   1297 
   1298 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTall) {
   1299   CommitOrderingTest items[] = {
   1300     {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
   1301     {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
   1302     {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
   1303     CommitOrderingTest::MakeLastCommitItem(),
   1304   };
   1305   RunCommitOrderingTest(items);
   1306 }
   1307 
   1308 TEST_F(SyncerTest, TestCommitListOrderingFourItemsTall) {
   1309   CommitOrderingTest items[] = {
   1310     {3, ids_.FromNumber(-2003), ids_.FromNumber(-2002)},
   1311     {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
   1312     {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
   1313     {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
   1314     CommitOrderingTest::MakeLastCommitItem(),
   1315   };
   1316   RunCommitOrderingTest(items);
   1317 }
   1318 
   1319 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTallLimitedSize) {
   1320   context_->set_max_commit_batch_size(2);
   1321   CommitOrderingTest items[] = {
   1322     {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
   1323     {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
   1324     {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
   1325     CommitOrderingTest::MakeLastCommitItem(),
   1326   };
   1327   RunCommitOrderingTest(items);
   1328 }
   1329 
   1330 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItem) {
   1331   CommitOrderingTest items[] = {
   1332     {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
   1333     CommitOrderingTest::MakeLastCommitItem(),
   1334   };
   1335   RunCommitOrderingTest(items);
   1336 }
   1337 
   1338 TEST_F(SyncerTest, TestCommitListOrderingSingleUncommittedDeletedItem) {
   1339   CommitOrderingTest items[] = {
   1340     {-1, ids_.FromNumber(-1000), ids_.FromNumber(0), {DELETED}},
   1341     CommitOrderingTest::MakeLastCommitItem(),
   1342   };
   1343   RunCommitOrderingTest(items);
   1344 }
   1345 
   1346 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItemWithUnroll) {
   1347   CommitOrderingTest items[] = {
   1348     {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
   1349     CommitOrderingTest::MakeLastCommitItem(),
   1350   };
   1351   RunCommitOrderingTest(items);
   1352 }
   1353 
   1354 TEST_F(SyncerTest,
   1355        TestCommitListOrderingSingleLongDeletedItemWithUnroll) {
   1356   CommitOrderingTest items[] = {
   1357     {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
   1358     CommitOrderingTest::MakeLastCommitItem(),
   1359   };
   1360   RunCommitOrderingTest(items);
   1361 }
   1362 
   1363 TEST_F(SyncerTest, TestCommitListOrderingTwoLongDeletedItemWithUnroll) {
   1364   CommitOrderingTest items[] = {
   1365     {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
   1366     {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME}},
   1367     CommitOrderingTest::MakeLastCommitItem(),
   1368   };
   1369   RunCommitOrderingTest(items);
   1370 }
   1371 
   1372 TEST_F(SyncerTest, TestCommitListOrdering3LongDeletedItemsWithSizeLimit) {
   1373   context_->set_max_commit_batch_size(2);
   1374   CommitOrderingTest items[] = {
   1375     {2, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
   1376     {1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME}},
   1377     {0, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
   1378     CommitOrderingTest::MakeLastCommitItem(),
   1379   };
   1380   RunCommitOrderingTest(items);
   1381 }
   1382 
   1383 TEST_F(SyncerTest, TestCommitListOrderingTwoDeletedItemsWithUnroll) {
   1384   CommitOrderingTest items[] = {
   1385     {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
   1386     {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED}},
   1387     CommitOrderingTest::MakeLastCommitItem(),
   1388   };
   1389   RunCommitOrderingTest(items);
   1390 }
   1391 
   1392 TEST_F(SyncerTest, TestCommitListOrderingComplexDeletionScenario) {
   1393   CommitOrderingTest items[] = {
   1394     {2, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
   1395     {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
   1396     {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
   1397     {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
   1398     {0, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
   1399     CommitOrderingTest::MakeLastCommitItem(),
   1400   };
   1401   RunCommitOrderingTest(items);
   1402 }
   1403 
   1404 TEST_F(SyncerTest,
   1405        TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes) {
   1406   CommitOrderingTest items[] = {
   1407     {3, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
   1408     {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
   1409     {2, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
   1410     {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
   1411     {1, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
   1412     {0, ids_.FromNumber(1005), ids_.FromNumber(1003), {DELETED}},
   1413     CommitOrderingTest::MakeLastCommitItem(),
   1414   };
   1415   RunCommitOrderingTest(items);
   1416 }
   1417 
   1418 TEST_F(SyncerTest, TestCommitListOrderingDeleteMovedItems) {
   1419   CommitOrderingTest items[] = {
   1420     {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
   1421     {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME,
   1422                                               MOVED_FROM_ROOT}},
   1423     CommitOrderingTest::MakeLastCommitItem(),
   1424   };
   1425   RunCommitOrderingTest(items);
   1426 }
   1427 
   1428 TEST_F(SyncerTest, TestCommitListOrderingWithNesting) {
   1429   const base::Time& now_minus_2h =
   1430       base::Time::Now() - base::TimeDelta::FromHours(2);
   1431   {
   1432     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1433     {
   1434       MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bob");
   1435       ASSERT_TRUE(parent.good());
   1436       parent.PutIsUnsynced(true);
   1437       parent.PutIsDir(true);
   1438       parent.PutSpecifics(DefaultBookmarkSpecifics());
   1439       parent.PutId(ids_.FromNumber(100));
   1440       parent.PutBaseVersion(1);
   1441       MutableEntry child(
   1442           &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(100), "Bob");
   1443       ASSERT_TRUE(child.good());
   1444       child.PutIsUnsynced(true);
   1445       child.PutIsDir(true);
   1446       child.PutSpecifics(DefaultBookmarkSpecifics());
   1447       child.PutId(ids_.FromNumber(101));
   1448       child.PutBaseVersion(1);
   1449       MutableEntry grandchild(
   1450           &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(101), "Bob");
   1451       ASSERT_TRUE(grandchild.good());
   1452       grandchild.PutId(ids_.FromNumber(102));
   1453       grandchild.PutIsUnsynced(true);
   1454       grandchild.PutSpecifics(DefaultBookmarkSpecifics());
   1455       grandchild.PutBaseVersion(1);
   1456     }
   1457     {
   1458       // Create three deleted items which deletions we expect to be sent to the
   1459       // server.
   1460       MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete");
   1461       ASSERT_TRUE(parent.good());
   1462       parent.PutId(ids_.FromNumber(103));
   1463       parent.PutIsUnsynced(true);
   1464       parent.PutIsDir(true);
   1465       parent.PutSpecifics(DefaultBookmarkSpecifics());
   1466       parent.PutIsDel(true);
   1467       parent.PutBaseVersion(1);
   1468       parent.PutMtime(now_minus_2h);
   1469       MutableEntry child(
   1470           &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(103), "Pete");
   1471       ASSERT_TRUE(child.good());
   1472       child.PutId(ids_.FromNumber(104));
   1473       child.PutIsUnsynced(true);
   1474       child.PutIsDir(true);
   1475       child.PutSpecifics(DefaultBookmarkSpecifics());
   1476       child.PutIsDel(true);
   1477       child.PutBaseVersion(1);
   1478       child.PutMtime(now_minus_2h);
   1479       MutableEntry grandchild(
   1480           &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(104), "Pete");
   1481       ASSERT_TRUE(grandchild.good());
   1482       grandchild.PutId(ids_.FromNumber(105));
   1483       grandchild.PutIsUnsynced(true);
   1484       grandchild.PutIsDel(true);
   1485       grandchild.PutIsDir(false);
   1486       grandchild.PutSpecifics(DefaultBookmarkSpecifics());
   1487       grandchild.PutBaseVersion(1);
   1488       grandchild.PutMtime(now_minus_2h);
   1489     }
   1490   }
   1491 
   1492   SyncShareNudge();
   1493   ASSERT_EQ(6u, mock_server_->committed_ids().size());
   1494   // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
   1495   // It will treat these like moves.
   1496   vector<syncable::Id> commit_ids(mock_server_->committed_ids());
   1497   EXPECT_TRUE(ids_.FromNumber(100) == commit_ids[0]);
   1498   EXPECT_TRUE(ids_.FromNumber(101) == commit_ids[1]);
   1499   EXPECT_TRUE(ids_.FromNumber(102) == commit_ids[2]);
   1500   // We don't guarantee the delete orders in this test, only that they occur
   1501   // at the end.
   1502   std::sort(commit_ids.begin() + 3, commit_ids.end());
   1503   EXPECT_TRUE(ids_.FromNumber(103) == commit_ids[3]);
   1504   EXPECT_TRUE(ids_.FromNumber(104) == commit_ids[4]);
   1505   EXPECT_TRUE(ids_.FromNumber(105) == commit_ids[5]);
   1506 }
   1507 
   1508 TEST_F(SyncerTest, TestCommitListOrderingWithNewItems) {
   1509   syncable::Id parent1_id = ids_.MakeServer("p1");
   1510   syncable::Id parent2_id = ids_.MakeServer("p2");
   1511 
   1512   {
   1513     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1514     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "1");
   1515     ASSERT_TRUE(parent.good());
   1516     parent.PutIsUnsynced(true);
   1517     parent.PutIsDir(true);
   1518     parent.PutSpecifics(DefaultBookmarkSpecifics());
   1519     parent.PutId(parent1_id);
   1520     MutableEntry child(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "2");
   1521     ASSERT_TRUE(child.good());
   1522     child.PutIsUnsynced(true);
   1523     child.PutIsDir(true);
   1524     child.PutSpecifics(DefaultBookmarkSpecifics());
   1525     child.PutId(parent2_id);
   1526     parent.PutBaseVersion(1);
   1527     child.PutBaseVersion(1);
   1528   }
   1529   {
   1530     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1531     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent1_id, "A");
   1532     ASSERT_TRUE(parent.good());
   1533     parent.PutIsUnsynced(true);
   1534     parent.PutIsDir(true);
   1535     parent.PutSpecifics(DefaultBookmarkSpecifics());
   1536     parent.PutId(ids_.FromNumber(102));
   1537     MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent1_id, "B");
   1538     ASSERT_TRUE(child.good());
   1539     child.PutIsUnsynced(true);
   1540     child.PutIsDir(true);
   1541     child.PutSpecifics(DefaultBookmarkSpecifics());
   1542     child.PutId(ids_.FromNumber(-103));
   1543     parent.PutBaseVersion(1);
   1544   }
   1545   {
   1546     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1547     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent2_id, "A");
   1548     ASSERT_TRUE(parent.good());
   1549     parent.PutIsUnsynced(true);
   1550     parent.PutIsDir(true);
   1551     parent.PutSpecifics(DefaultBookmarkSpecifics());
   1552     parent.PutId(ids_.FromNumber(-104));
   1553     MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent2_id, "B");
   1554     ASSERT_TRUE(child.good());
   1555     child.PutIsUnsynced(true);
   1556     child.PutIsDir(true);
   1557     child.PutSpecifics(DefaultBookmarkSpecifics());
   1558     child.PutId(ids_.FromNumber(105));
   1559     child.PutBaseVersion(1);
   1560   }
   1561 
   1562   SyncShareNudge();
   1563   ASSERT_EQ(6u, mock_server_->committed_ids().size());
   1564 
   1565   // This strange iteration and std::count() usage is to allow the order to
   1566   // vary.  All we really care about is that parent1_id and parent2_id are the
   1567   // first two IDs, and that the children make up the next four.  Other than
   1568   // that, ordering doesn't matter.
   1569 
   1570   vector<syncable::Id>::const_iterator i =
   1571       mock_server_->committed_ids().begin();
   1572   vector<syncable::Id>::const_iterator parents_begin = i;
   1573   i++;
   1574   i++;
   1575   vector<syncable::Id>::const_iterator parents_end = i;
   1576   vector<syncable::Id>::const_iterator children_begin = i;
   1577   vector<syncable::Id>::const_iterator children_end =
   1578       mock_server_->committed_ids().end();
   1579 
   1580   EXPECT_EQ(1, count(parents_begin, parents_end, parent1_id));
   1581   EXPECT_EQ(1, count(parents_begin, parents_end, parent2_id));
   1582 
   1583   EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-103)));
   1584   EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(102)));
   1585   EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(105)));
   1586   EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-104)));
   1587 }
   1588 
   1589 TEST_F(SyncerTest, TestCommitListOrderingCounterexample) {
   1590   syncable::Id child2_id = ids_.NewServerId();
   1591 
   1592   {
   1593     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1594     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "P");
   1595     ASSERT_TRUE(parent.good());
   1596     parent.PutIsUnsynced(true);
   1597     parent.PutIsDir(true);
   1598     parent.PutSpecifics(DefaultBookmarkSpecifics());
   1599     parent.PutId(parent_id_);
   1600     MutableEntry child1(&wtrans, CREATE, BOOKMARKS, parent_id_, "1");
   1601     ASSERT_TRUE(child1.good());
   1602     child1.PutIsUnsynced(true);
   1603     child1.PutId(child_id_);
   1604     child1.PutSpecifics(DefaultBookmarkSpecifics());
   1605     MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent_id_, "2");
   1606     ASSERT_TRUE(child2.good());
   1607     child2.PutIsUnsynced(true);
   1608     child2.PutSpecifics(DefaultBookmarkSpecifics());
   1609     child2.PutId(child2_id);
   1610 
   1611     parent.PutBaseVersion(1);
   1612     child1.PutBaseVersion(1);
   1613     child2.PutBaseVersion(1);
   1614   }
   1615 
   1616   SyncShareNudge();
   1617   ASSERT_EQ(3u, mock_server_->committed_ids().size());
   1618   EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
   1619   // There are two possible valid orderings.
   1620   if (child2_id == mock_server_->committed_ids()[1]) {
   1621     EXPECT_TRUE(child2_id == mock_server_->committed_ids()[1]);
   1622     EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[2]);
   1623   } else {
   1624     EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
   1625     EXPECT_TRUE(child2_id == mock_server_->committed_ids()[2]);
   1626   }
   1627 }
   1628 
   1629 TEST_F(SyncerTest, TestCommitListOrderingAndNewParent) {
   1630   string parent1_name = "1";
   1631   string parent2_name = "A";
   1632   string child_name = "B";
   1633 
   1634   {
   1635     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1636     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(),
   1637                         parent1_name);
   1638     ASSERT_TRUE(parent.good());
   1639     parent.PutIsUnsynced(true);
   1640     parent.PutIsDir(true);
   1641     parent.PutSpecifics(DefaultBookmarkSpecifics());
   1642     parent.PutId(parent_id_);
   1643     parent.PutBaseVersion(1);
   1644   }
   1645 
   1646   syncable::Id parent2_id = ids_.NewLocalId();
   1647   syncable::Id child_id = ids_.NewServerId();
   1648   {
   1649     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1650     MutableEntry parent2(
   1651         &wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name);
   1652     ASSERT_TRUE(parent2.good());
   1653     parent2.PutIsUnsynced(true);
   1654     parent2.PutIsDir(true);
   1655     parent2.PutSpecifics(DefaultBookmarkSpecifics());
   1656     parent2.PutId(parent2_id);
   1657 
   1658     MutableEntry child(
   1659         &wtrans, CREATE, BOOKMARKS, parent2_id, child_name);
   1660     ASSERT_TRUE(child.good());
   1661     child.PutIsUnsynced(true);
   1662     child.PutIsDir(true);
   1663     child.PutSpecifics(DefaultBookmarkSpecifics());
   1664     child.PutId(child_id);
   1665     child.PutBaseVersion(1);
   1666   }
   1667 
   1668   SyncShareNudge();
   1669   ASSERT_EQ(3u, mock_server_->committed_ids().size());
   1670   // If this test starts failing, be aware other sort orders could be valid.
   1671   EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
   1672   EXPECT_TRUE(parent2_id == mock_server_->committed_ids()[1]);
   1673   EXPECT_TRUE(child_id == mock_server_->committed_ids()[2]);
   1674   {
   1675     syncable::ReadTransaction rtrans(FROM_HERE, directory());
   1676     // Check that things committed correctly.
   1677     Entry entry_1(&rtrans, syncable::GET_BY_ID, parent_id_);
   1678     EXPECT_EQ(entry_1.GetNonUniqueName(), parent1_name);
   1679     // Check that parent2 is a subfolder of parent1.
   1680     EXPECT_EQ(1, CountEntriesWithName(&rtrans,
   1681                                       parent_id_,
   1682                                       parent2_name));
   1683 
   1684     // Parent2 was a local ID and thus should have changed on commit!
   1685     Entry pre_commit_entry_parent2(&rtrans, syncable::GET_BY_ID, parent2_id);
   1686     ASSERT_FALSE(pre_commit_entry_parent2.good());
   1687 
   1688     // Look up the new ID.
   1689     Id parent2_committed_id =
   1690         GetOnlyEntryWithName(&rtrans, parent_id_, parent2_name);
   1691     EXPECT_TRUE(parent2_committed_id.ServerKnows());
   1692 
   1693     Entry child(&rtrans, syncable::GET_BY_ID, child_id);
   1694     EXPECT_EQ(parent2_committed_id, child.GetParentId());
   1695   }
   1696 }
   1697 
   1698 TEST_F(SyncerTest, TestCommitListOrderingAndNewParentAndChild) {
   1699   string parent_name = "1";
   1700   string parent2_name = "A";
   1701   string child_name = "B";
   1702 
   1703   {
   1704     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1705     MutableEntry parent(&wtrans,
   1706                         CREATE, BOOKMARKS,
   1707                         wtrans.root_id(),
   1708                         parent_name);
   1709     ASSERT_TRUE(parent.good());
   1710     parent.PutIsUnsynced(true);
   1711     parent.PutIsDir(true);
   1712     parent.PutSpecifics(DefaultBookmarkSpecifics());
   1713     parent.PutId(parent_id_);
   1714     parent.PutBaseVersion(1);
   1715   }
   1716 
   1717   int64 meta_handle_b;
   1718   const Id parent2_local_id = ids_.NewLocalId();
   1719   const Id child_local_id = ids_.NewLocalId();
   1720   {
   1721     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   1722     MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name);
   1723     ASSERT_TRUE(parent2.good());
   1724     parent2.PutIsUnsynced(true);
   1725     parent2.PutIsDir(true);
   1726     parent2.PutSpecifics(DefaultBookmarkSpecifics());
   1727 
   1728     parent2.PutId(parent2_local_id);
   1729     MutableEntry child(
   1730         &wtrans, CREATE, BOOKMARKS, parent2_local_id, child_name);
   1731     ASSERT_TRUE(child.good());
   1732     child.PutIsUnsynced(true);
   1733     child.PutIsDir(true);
   1734     child.PutSpecifics(DefaultBookmarkSpecifics());
   1735     child.PutId(child_local_id);
   1736     meta_handle_b = child.GetMetahandle();
   1737   }
   1738 
   1739   SyncShareNudge();
   1740   ASSERT_EQ(3u, mock_server_->committed_ids().size());
   1741   // If this test starts failing, be aware other sort orders could be valid.
   1742   EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
   1743   EXPECT_TRUE(parent2_local_id == mock_server_->committed_ids()[1]);
   1744   EXPECT_TRUE(child_local_id == mock_server_->committed_ids()[2]);
   1745   {
   1746     syncable::ReadTransaction rtrans(FROM_HERE, directory());
   1747 
   1748     Entry parent(&rtrans, syncable::GET_BY_ID,
   1749                  GetOnlyEntryWithName(&rtrans, rtrans.root_id(), parent_name));
   1750     ASSERT_TRUE(parent.good());
   1751     EXPECT_TRUE(parent.GetId().ServerKnows());
   1752 
   1753     Entry parent2(&rtrans, syncable::GET_BY_ID,
   1754                   GetOnlyEntryWithName(&rtrans, parent.GetId(), parent2_name));
   1755     ASSERT_TRUE(parent2.good());
   1756     EXPECT_TRUE(parent2.GetId().ServerKnows());
   1757 
   1758     // Id changed on commit, so this should fail.
   1759     Entry local_parent2_id_entry(&rtrans,
   1760                                  syncable::GET_BY_ID,
   1761                                  parent2_local_id);
   1762     ASSERT_FALSE(local_parent2_id_entry.good());
   1763 
   1764     Entry entry_b(&rtrans, syncable::GET_BY_HANDLE, meta_handle_b);
   1765     EXPECT_TRUE(entry_b.GetId().ServerKnows());
   1766     EXPECT_TRUE(parent2.GetId()== entry_b.GetParentId());
   1767   }
   1768 }
   1769 
   1770 TEST_F(SyncerTest, UpdateWithZeroLengthName) {
   1771   // One illegal update
   1772   mock_server_->AddUpdateDirectory(
   1773       1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1");
   1774   // And one legal one that we're going to delete.
   1775   mock_server_->AddUpdateDirectory(2, 0, "FOO", 1, 10,
   1776                                    foreign_cache_guid(), "-2");
   1777   SyncShareNudge();
   1778   // Delete the legal one. The new update has a null name.
   1779   mock_server_->AddUpdateDirectory(
   1780       2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2");
   1781   mock_server_->SetLastUpdateDeleted();
   1782   SyncShareNudge();
   1783 }
   1784 
   1785 TEST_F(SyncerTest, TestBasicUpdate) {
   1786   string id = "some_id";
   1787   string parent_id = "0";
   1788   string name = "in_root";
   1789   int64 version = 10;
   1790   int64 timestamp = 10;
   1791   mock_server_->AddUpdateDirectory(id, parent_id, name, version, timestamp,
   1792                                    foreign_cache_guid(), "-1");
   1793 
   1794   SyncShareNudge();
   1795   {
   1796     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   1797     Entry entry(&trans, GET_BY_ID,
   1798                syncable::Id::CreateFromServerId("some_id"));
   1799     ASSERT_TRUE(entry.good());
   1800     EXPECT_TRUE(entry.GetIsDir());
   1801     EXPECT_TRUE(entry.GetServerVersion()== version);
   1802     EXPECT_TRUE(entry.GetBaseVersion()== version);
   1803     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   1804     EXPECT_FALSE(entry.GetIsUnsynced());
   1805     EXPECT_FALSE(entry.GetServerIsDel());
   1806     EXPECT_FALSE(entry.GetIsDel());
   1807   }
   1808 }
   1809 
   1810 TEST_F(SyncerTest, IllegalAndLegalUpdates) {
   1811   Id root = TestIdFactory::root();
   1812   // Should apply just fine.
   1813   mock_server_->AddUpdateDirectory(1, 0, "in_root", 10, 10,
   1814                                    foreign_cache_guid(), "-1");
   1815 
   1816   // Same name. But this SHOULD work.
   1817   mock_server_->AddUpdateDirectory(2, 0, "in_root", 10, 10,
   1818                                    foreign_cache_guid(), "-2");
   1819 
   1820   // Unknown parent: should never be applied. "-80" is a legal server ID,
   1821   // because any string sent by the server is a legal server ID in the sync
   1822   // protocol, but it's not the ID of any item known to the client.  This
   1823   // update should succeed validation, but be stuck in the unapplied state
   1824   // until an item with the server ID "-80" arrives.
   1825   mock_server_->AddUpdateDirectory(3, -80, "bad_parent", 10, 10,
   1826                                    foreign_cache_guid(), "-3");
   1827 
   1828   SyncShareNudge();
   1829 
   1830   // Id 3 should be in conflict now.
   1831   EXPECT_EQ(
   1832       1,
   1833       GetUpdateCounters(BOOKMARKS).num_hierarchy_conflict_application_failures);
   1834 
   1835   // The only request in that loop should have been a GetUpdate.
   1836   // At that point, we didn't know whether or not we had conflicts.
   1837   ASSERT_TRUE(mock_server_->last_request().has_get_updates());
   1838   VerifyHierarchyConflictsUnspecified(mock_server_->last_request());
   1839 
   1840   // These entries will be used in the second set of updates.
   1841   mock_server_->AddUpdateDirectory(4, 0, "newer_version", 20, 10,
   1842                                    foreign_cache_guid(), "-4");
   1843   mock_server_->AddUpdateDirectory(5, 0, "circular1", 10, 10,
   1844                                    foreign_cache_guid(), "-5");
   1845   mock_server_->AddUpdateDirectory(6, 5, "circular2", 10, 10,
   1846                                    foreign_cache_guid(), "-6");
   1847   mock_server_->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10,
   1848                                    foreign_cache_guid(), "-9");
   1849   mock_server_->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10,
   1850                                    foreign_cache_guid(), "-100");
   1851   mock_server_->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10,
   1852                                    foreign_cache_guid(), "-10");
   1853 
   1854   SyncShareNudge();
   1855   // The three items with an unresolved parent should be unapplied (3, 9, 100).
   1856   // The name clash should also still be in conflict.
   1857   EXPECT_EQ(
   1858       3,
   1859       GetUpdateCounters(BOOKMARKS).num_hierarchy_conflict_application_failures);
   1860 
   1861   // This time around, we knew that there were conflicts.
   1862   ASSERT_TRUE(mock_server_->last_request().has_get_updates());
   1863   VerifyHierarchyConflictsReported(mock_server_->last_request());
   1864 
   1865   {
   1866     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   1867     // Even though it has the same name, it should work.
   1868     Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
   1869     ASSERT_TRUE(name_clash.good());
   1870     EXPECT_FALSE(name_clash.GetIsUnappliedUpdate())
   1871         << "Duplicate name SHOULD be OK.";
   1872 
   1873     Entry bad_parent(&trans, GET_BY_ID, ids_.FromNumber(3));
   1874     ASSERT_TRUE(bad_parent.good());
   1875     EXPECT_TRUE(bad_parent.GetIsUnappliedUpdate())
   1876         << "child of unknown parent should be in conflict";
   1877 
   1878     Entry bad_parent_child(&trans, GET_BY_ID, ids_.FromNumber(9));
   1879     ASSERT_TRUE(bad_parent_child.good());
   1880     EXPECT_TRUE(bad_parent_child.GetIsUnappliedUpdate())
   1881         << "grandchild of unknown parent should be in conflict";
   1882 
   1883     Entry bad_parent_child2(&trans, GET_BY_ID, ids_.FromNumber(100));
   1884     ASSERT_TRUE(bad_parent_child2.good());
   1885     EXPECT_TRUE(bad_parent_child2.GetIsUnappliedUpdate())
   1886         << "great-grandchild of unknown parent should be in conflict";
   1887   }
   1888 
   1889   // Updating 1 should not affect item 2 of the same name.
   1890   mock_server_->AddUpdateDirectory(1, 0, "new_name", 20, 20,
   1891                                    foreign_cache_guid(), "-1");
   1892 
   1893   // Moving 5 under 6 will create a cycle: a conflict.
   1894   mock_server_->AddUpdateDirectory(5, 6, "circular3", 20, 20,
   1895                                    foreign_cache_guid(), "-5");
   1896 
   1897   // Flip the is_dir bit: should fail verify & be dropped.
   1898   mock_server_->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20,
   1899                                   foreign_cache_guid(), "-10");
   1900   SyncShareNudge();
   1901 
   1902   // Version number older than last known: should fail verify & be dropped.
   1903   mock_server_->AddUpdateDirectory(4, 0, "old_version", 10, 10,
   1904                                    foreign_cache_guid(), "-4");
   1905   SyncShareNudge();
   1906   {
   1907     syncable::ReadTransaction trans(FROM_HERE, directory());
   1908 
   1909     Entry still_a_dir(&trans, GET_BY_ID, ids_.FromNumber(10));
   1910     ASSERT_TRUE(still_a_dir.good());
   1911     EXPECT_FALSE(still_a_dir.GetIsUnappliedUpdate());
   1912     EXPECT_EQ(10u, still_a_dir.GetBaseVersion());
   1913     EXPECT_EQ(10u, still_a_dir.GetServerVersion());
   1914     EXPECT_TRUE(still_a_dir.GetIsDir());
   1915 
   1916     Entry rename(&trans, GET_BY_ID, ids_.FromNumber(1));
   1917     ASSERT_TRUE(rename.good());
   1918     EXPECT_EQ(root, rename.GetParentId());
   1919     EXPECT_EQ("new_name", rename.GetNonUniqueName());
   1920     EXPECT_FALSE(rename.GetIsUnappliedUpdate());
   1921     EXPECT_TRUE(ids_.FromNumber(1) == rename.GetId());
   1922     EXPECT_EQ(20u, rename.GetBaseVersion());
   1923 
   1924     Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
   1925     ASSERT_TRUE(name_clash.good());
   1926     EXPECT_EQ(root, name_clash.GetParentId());
   1927     EXPECT_TRUE(ids_.FromNumber(2) == name_clash.GetId());
   1928     EXPECT_EQ(10u, name_clash.GetBaseVersion());
   1929     EXPECT_EQ("in_root", name_clash.GetNonUniqueName());
   1930 
   1931     Entry ignored_old_version(&trans, GET_BY_ID, ids_.FromNumber(4));
   1932     ASSERT_TRUE(ignored_old_version.good());
   1933     EXPECT_TRUE(
   1934         ignored_old_version.GetNonUniqueName()== "newer_version");
   1935     EXPECT_FALSE(ignored_old_version.GetIsUnappliedUpdate());
   1936     EXPECT_EQ(20u, ignored_old_version.GetBaseVersion());
   1937 
   1938     Entry circular_parent_issue(&trans, GET_BY_ID, ids_.FromNumber(5));
   1939     ASSERT_TRUE(circular_parent_issue.good());
   1940     EXPECT_TRUE(circular_parent_issue.GetIsUnappliedUpdate())
   1941         << "circular move should be in conflict";
   1942     EXPECT_TRUE(circular_parent_issue.GetParentId()== root_id_);
   1943     EXPECT_TRUE(circular_parent_issue.GetServerParentId()==
   1944                 ids_.FromNumber(6));
   1945     EXPECT_EQ(10u, circular_parent_issue.GetBaseVersion());
   1946 
   1947     Entry circular_parent_target(&trans, GET_BY_ID, ids_.FromNumber(6));
   1948     ASSERT_TRUE(circular_parent_target.good());
   1949     EXPECT_FALSE(circular_parent_target.GetIsUnappliedUpdate());
   1950     EXPECT_TRUE(circular_parent_issue.GetId()==
   1951         circular_parent_target.GetParentId());
   1952     EXPECT_EQ(10u, circular_parent_target.GetBaseVersion());
   1953   }
   1954 
   1955   EXPECT_FALSE(saw_syncer_event_);
   1956   EXPECT_EQ(
   1957       4,
   1958       GetUpdateCounters(BOOKMARKS).num_hierarchy_conflict_application_failures);
   1959 }
   1960 
   1961 // A commit with a lost response produces an update that has to be reunited with
   1962 // its parent.
   1963 TEST_F(SyncerTest, CommitReuniteUpdateAdjustsChildren) {
   1964   // Create a folder in the root.
   1965   int64 metahandle_folder;
   1966   {
   1967     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   1968     MutableEntry entry(
   1969         &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
   1970     ASSERT_TRUE(entry.good());
   1971     entry.PutIsDir(true);
   1972     entry.PutSpecifics(DefaultBookmarkSpecifics());
   1973     entry.PutIsUnsynced(true);
   1974     metahandle_folder = entry.GetMetahandle();
   1975   }
   1976 
   1977   // Verify it and pull the ID out of the folder.
   1978   syncable::Id folder_id;
   1979   int64 metahandle_entry;
   1980   {
   1981     syncable::ReadTransaction trans(FROM_HERE, directory());
   1982     Entry entry(&trans, GET_BY_HANDLE, metahandle_folder);
   1983     ASSERT_TRUE(entry.good());
   1984     folder_id = entry.GetId();
   1985     ASSERT_TRUE(!folder_id.ServerKnows());
   1986   }
   1987 
   1988   // Create an entry in the newly created folder.
   1989   {
   1990     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   1991     MutableEntry entry(&trans, CREATE, BOOKMARKS, folder_id, "new_entry");
   1992     ASSERT_TRUE(entry.good());
   1993     metahandle_entry = entry.GetMetahandle();
   1994     WriteTestDataToEntry(&trans, &entry);
   1995   }
   1996 
   1997   // Verify it and pull the ID out of the entry.
   1998   syncable::Id entry_id;
   1999   {
   2000     syncable::ReadTransaction trans(FROM_HERE, directory());
   2001     Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
   2002     ASSERT_TRUE(entry.good());
   2003     EXPECT_EQ(folder_id, entry.GetParentId());
   2004     EXPECT_EQ("new_entry", entry.GetNonUniqueName());
   2005     entry_id = entry.GetId();
   2006     EXPECT_TRUE(!entry_id.ServerKnows());
   2007     VerifyTestDataInEntry(&trans, &entry);
   2008   }
   2009 
   2010   // Now, to emulate a commit response failure, we just don't commit it.
   2011   int64 new_version = 150;  // any larger value.
   2012   int64 timestamp = 20;  // arbitrary value.
   2013   syncable::Id new_folder_id =
   2014       syncable::Id::CreateFromServerId("folder_server_id");
   2015 
   2016   // The following update should cause the folder to both apply the update, as
   2017   // well as reassociate the id.
   2018   mock_server_->AddUpdateDirectory(new_folder_id, root_id_,
   2019       "new_folder", new_version, timestamp,
   2020       local_cache_guid(), folder_id.GetServerId());
   2021 
   2022   // We don't want it accidentally committed, just the update applied.
   2023   mock_server_->set_conflict_all_commits(true);
   2024 
   2025   // Alright! Apply that update!
   2026   SyncShareNudge();
   2027   {
   2028     // The folder's ID should have been updated.
   2029     syncable::ReadTransaction trans(FROM_HERE, directory());
   2030     Entry folder(&trans, GET_BY_HANDLE, metahandle_folder);
   2031     ASSERT_TRUE(folder.good());
   2032     EXPECT_EQ("new_folder", folder.GetNonUniqueName());
   2033     EXPECT_TRUE(new_version == folder.GetBaseVersion());
   2034     EXPECT_TRUE(new_folder_id == folder.GetId());
   2035     EXPECT_TRUE(folder.GetId().ServerKnows());
   2036     EXPECT_EQ(trans.root_id(), folder.GetParentId());
   2037 
   2038     // Since it was updated, the old folder should not exist.
   2039     Entry old_dead_folder(&trans, GET_BY_ID, folder_id);
   2040     EXPECT_FALSE(old_dead_folder.good());
   2041 
   2042     // The child's parent should have changed.
   2043     Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
   2044     ASSERT_TRUE(entry.good());
   2045     EXPECT_EQ("new_entry", entry.GetNonUniqueName());
   2046     EXPECT_EQ(new_folder_id, entry.GetParentId());
   2047     EXPECT_TRUE(!entry.GetId().ServerKnows());
   2048     VerifyTestDataInEntry(&trans, &entry);
   2049   }
   2050 }
   2051 
   2052 // A commit with a lost response produces an update that has to be reunited with
   2053 // its parent.
   2054 TEST_F(SyncerTest, CommitReuniteUpdate) {
   2055   // Create an entry in the root.
   2056   int64 entry_metahandle;
   2057   {
   2058     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2059     MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry");
   2060     ASSERT_TRUE(entry.good());
   2061     entry_metahandle = entry.GetMetahandle();
   2062     WriteTestDataToEntry(&trans, &entry);
   2063   }
   2064 
   2065   // Verify it and pull the ID out.
   2066   syncable::Id entry_id;
   2067   {
   2068     syncable::ReadTransaction trans(FROM_HERE, directory());
   2069 
   2070     Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
   2071     ASSERT_TRUE(entry.good());
   2072     entry_id = entry.GetId();
   2073     EXPECT_TRUE(!entry_id.ServerKnows());
   2074     VerifyTestDataInEntry(&trans, &entry);
   2075   }
   2076 
   2077   // Now, to emulate a commit response failure, we just don't commit it.
   2078   int64 new_version = 150;  // any larger value.
   2079   int64 timestamp = 20;  // arbitrary value.
   2080   syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
   2081 
   2082   // Generate an update from the server with a relevant ID reassignment.
   2083   mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
   2084       "new_entry", new_version, timestamp,
   2085       local_cache_guid(), entry_id.GetServerId());
   2086 
   2087   // We don't want it accidentally committed, just the update applied.
   2088   mock_server_->set_conflict_all_commits(true);
   2089 
   2090   // Alright! Apply that update!
   2091   SyncShareNudge();
   2092   {
   2093     syncable::ReadTransaction trans(FROM_HERE, directory());
   2094     Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
   2095     ASSERT_TRUE(entry.good());
   2096     EXPECT_TRUE(new_version == entry.GetBaseVersion());
   2097     EXPECT_TRUE(new_entry_id == entry.GetId());
   2098     EXPECT_EQ("new_entry", entry.GetNonUniqueName());
   2099   }
   2100 }
   2101 
   2102 // A commit with a lost response must work even if the local entry was deleted
   2103 // before the update is applied. We should not duplicate the local entry in
   2104 // this case, but just create another one alongside. We may wish to examine
   2105 // this behavior in the future as it can create hanging uploads that never
   2106 // finish, that must be cleaned up on the server side after some time.
   2107 TEST_F(SyncerTest, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry) {
   2108   // Create a entry in the root.
   2109   int64 entry_metahandle;
   2110   {
   2111     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2112     MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry");
   2113     ASSERT_TRUE(entry.good());
   2114     entry_metahandle = entry.GetMetahandle();
   2115     WriteTestDataToEntry(&trans, &entry);
   2116   }
   2117   // Verify it and pull the ID out.
   2118   syncable::Id entry_id;
   2119   {
   2120     syncable::ReadTransaction trans(FROM_HERE, directory());
   2121     Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
   2122     ASSERT_TRUE(entry.good());
   2123     entry_id = entry.GetId();
   2124     EXPECT_TRUE(!entry_id.ServerKnows());
   2125     VerifyTestDataInEntry(&trans, &entry);
   2126   }
   2127 
   2128   // Now, to emulate a commit response failure, we just don't commit it.
   2129   int64 new_version = 150;  // any larger value.
   2130   int64 timestamp = 20;  // arbitrary value.
   2131   syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
   2132 
   2133   // Generate an update from the server with a relevant ID reassignment.
   2134   mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
   2135       "new_entry", new_version, timestamp,
   2136       local_cache_guid(), entry_id.GetServerId());
   2137 
   2138   // We don't want it accidentally committed, just the update applied.
   2139   mock_server_->set_conflict_all_commits(true);
   2140 
   2141   // Purposefully delete the entry now before the update application finishes.
   2142   {
   2143     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2144     Id new_entry_id = GetOnlyEntryWithName(
   2145         &trans, trans.root_id(), "new_entry");
   2146     MutableEntry entry(&trans, GET_BY_ID, new_entry_id);
   2147     ASSERT_TRUE(entry.good());
   2148     entry.PutIsDel(true);
   2149   }
   2150 
   2151   // Just don't CHECK fail in sync, have the update split.
   2152   SyncShareNudge();
   2153   {
   2154     syncable::ReadTransaction trans(FROM_HERE, directory());
   2155     Id new_entry_id = GetOnlyEntryWithName(
   2156         &trans, trans.root_id(), "new_entry");
   2157     Entry entry(&trans, GET_BY_ID, new_entry_id);
   2158     ASSERT_TRUE(entry.good());
   2159     EXPECT_FALSE(entry.GetIsDel());
   2160 
   2161     Entry old_entry(&trans, GET_BY_ID, entry_id);
   2162     ASSERT_TRUE(old_entry.good());
   2163     EXPECT_TRUE(old_entry.GetIsDel());
   2164   }
   2165 }
   2166 
   2167 // TODO(chron): Add more unsanitized name tests.
   2168 TEST_F(SyncerTest, ConflictMatchingEntryHandlesUnsanitizedNames) {
   2169   mock_server_->AddUpdateDirectory(1, 0, "A/A", 10, 10,
   2170                                    foreign_cache_guid(), "-1");
   2171   mock_server_->AddUpdateDirectory(2, 0, "B/B", 10, 10,
   2172                                    foreign_cache_guid(), "-2");
   2173   mock_server_->set_conflict_all_commits(true);
   2174   SyncShareNudge();
   2175   {
   2176     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   2177 
   2178     MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   2179     ASSERT_TRUE(A.good());
   2180     A.PutIsUnsynced(true);
   2181     A.PutIsUnappliedUpdate(true);
   2182     A.PutServerVersion(20);
   2183 
   2184     MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
   2185     ASSERT_TRUE(B.good());
   2186     B.PutIsUnappliedUpdate(true);
   2187     B.PutServerVersion(20);
   2188   }
   2189   SyncShareNudge();
   2190   saw_syncer_event_ = false;
   2191   mock_server_->set_conflict_all_commits(false);
   2192 
   2193   {
   2194     syncable::ReadTransaction trans(FROM_HERE, directory());
   2195 
   2196     Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
   2197     ASSERT_TRUE(A.good());
   2198     EXPECT_TRUE(A.GetIsUnsynced()== false);
   2199     EXPECT_TRUE(A.GetIsUnappliedUpdate()== false);
   2200     EXPECT_TRUE(A.GetServerVersion()== 20);
   2201 
   2202     Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
   2203     ASSERT_TRUE(B.good());
   2204     EXPECT_TRUE(B.GetIsUnsynced()== false);
   2205     EXPECT_TRUE(B.GetIsUnappliedUpdate()== false);
   2206     EXPECT_TRUE(B.GetServerVersion()== 20);
   2207   }
   2208 }
   2209 
   2210 TEST_F(SyncerTest, ConflictMatchingEntryHandlesNormalNames) {
   2211   mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
   2212                                    foreign_cache_guid(), "-1");
   2213   mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
   2214                                    foreign_cache_guid(), "-2");
   2215   mock_server_->set_conflict_all_commits(true);
   2216   SyncShareNudge();
   2217   {
   2218     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   2219 
   2220     MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   2221     ASSERT_TRUE(A.good());
   2222     A.PutIsUnsynced(true);
   2223     A.PutIsUnappliedUpdate(true);
   2224     A.PutServerVersion(20);
   2225 
   2226     MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
   2227     ASSERT_TRUE(B.good());
   2228     B.PutIsUnappliedUpdate(true);
   2229     B.PutServerVersion(20);
   2230   }
   2231   SyncShareNudge();
   2232   saw_syncer_event_ = false;
   2233   mock_server_->set_conflict_all_commits(false);
   2234 
   2235   {
   2236     syncable::ReadTransaction trans(FROM_HERE, directory());
   2237 
   2238     Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
   2239     ASSERT_TRUE(A.good());
   2240     EXPECT_TRUE(A.GetIsUnsynced()== false);
   2241     EXPECT_TRUE(A.GetIsUnappliedUpdate()== false);
   2242     EXPECT_TRUE(A.GetServerVersion()== 20);
   2243 
   2244     Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
   2245     ASSERT_TRUE(B.good());
   2246     EXPECT_TRUE(B.GetIsUnsynced()== false);
   2247     EXPECT_TRUE(B.GetIsUnappliedUpdate()== false);
   2248     EXPECT_TRUE(B.GetServerVersion()== 20);
   2249   }
   2250 }
   2251 
   2252 TEST_F(SyncerTest, ReverseFolderOrderingTest) {
   2253   mock_server_->AddUpdateDirectory(4, 3, "ggchild", 10, 10,
   2254                                    foreign_cache_guid(), "-4");
   2255   mock_server_->AddUpdateDirectory(3, 2, "gchild", 10, 10,
   2256                                    foreign_cache_guid(), "-3");
   2257   mock_server_->AddUpdateDirectory(5, 4, "gggchild", 10, 10,
   2258                                    foreign_cache_guid(), "-5");
   2259   mock_server_->AddUpdateDirectory(2, 1, "child", 10, 10,
   2260                                    foreign_cache_guid(), "-2");
   2261   mock_server_->AddUpdateDirectory(1, 0, "parent", 10, 10,
   2262                                    foreign_cache_guid(), "-1");
   2263   SyncShareNudge();
   2264   syncable::ReadTransaction trans(FROM_HERE, directory());
   2265 
   2266   Id child_id = GetOnlyEntryWithName(
   2267         &trans, ids_.FromNumber(4), "gggchild");
   2268   Entry child(&trans, GET_BY_ID, child_id);
   2269   ASSERT_TRUE(child.good());
   2270 }
   2271 
   2272 class EntryCreatedInNewFolderTest : public SyncerTest {
   2273  public:
   2274   void CreateFolderInBob() {
   2275     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2276     MutableEntry bob(&trans,
   2277                      syncable::GET_BY_ID,
   2278                      GetOnlyEntryWithName(&trans,
   2279                                           TestIdFactory::root(),
   2280                                           "bob"));
   2281     CHECK(bob.good());
   2282 
   2283     MutableEntry entry2(
   2284         &trans, CREATE, BOOKMARKS, bob.GetId(), "bob");
   2285     CHECK(entry2.good());
   2286     entry2.PutIsDir(true);
   2287     entry2.PutIsUnsynced(true);
   2288     entry2.PutSpecifics(DefaultBookmarkSpecifics());
   2289   }
   2290 };
   2291 
   2292 TEST_F(EntryCreatedInNewFolderTest, EntryCreatedInNewFolderMidSync) {
   2293   {
   2294     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2295     MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
   2296     ASSERT_TRUE(entry.good());
   2297     entry.PutIsDir(true);
   2298     entry.PutIsUnsynced(true);
   2299     entry.PutSpecifics(DefaultBookmarkSpecifics());
   2300   }
   2301 
   2302   mock_server_->SetMidCommitCallback(
   2303       base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob,
   2304                  base::Unretained(this)));
   2305   SyncShareNudge();
   2306   // We loop until no unsynced handles remain, so we will commit both ids.
   2307   EXPECT_EQ(2u, mock_server_->committed_ids().size());
   2308   {
   2309     syncable::ReadTransaction trans(FROM_HERE, directory());
   2310     Entry parent_entry(&trans, syncable::GET_BY_ID,
   2311         GetOnlyEntryWithName(&trans, TestIdFactory::root(), "bob"));
   2312     ASSERT_TRUE(parent_entry.good());
   2313 
   2314     Id child_id =
   2315         GetOnlyEntryWithName(&trans, parent_entry.GetId(), "bob");
   2316     Entry child(&trans, syncable::GET_BY_ID, child_id);
   2317     ASSERT_TRUE(child.good());
   2318     EXPECT_EQ(parent_entry.GetId(), child.GetParentId());
   2319   }
   2320 }
   2321 
   2322 TEST_F(SyncerTest, NegativeIDInUpdate) {
   2323   mock_server_->AddUpdateBookmark(-10, 0, "bad", 40, 40,
   2324                                   foreign_cache_guid(), "-100");
   2325   SyncShareNudge();
   2326   // The negative id would make us CHECK!
   2327 }
   2328 
   2329 TEST_F(SyncerTest, UnappliedUpdateOnCreatedItemItemDoesNotCrash) {
   2330   int64 metahandle_fred;
   2331   syncable::Id orig_id;
   2332   {
   2333     // Create an item.
   2334     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2335     MutableEntry fred_match(&trans, CREATE, BOOKMARKS, trans.root_id(),
   2336                             "fred_match");
   2337     ASSERT_TRUE(fred_match.good());
   2338     metahandle_fred = fred_match.GetMetahandle();
   2339     orig_id = fred_match.GetId();
   2340     WriteTestDataToEntry(&trans, &fred_match);
   2341   }
   2342   // Commit it.
   2343   SyncShareNudge();
   2344   EXPECT_EQ(1u, mock_server_->committed_ids().size());
   2345   mock_server_->set_conflict_all_commits(true);
   2346   syncable::Id fred_match_id;
   2347   {
   2348     // Now receive a change from outside.
   2349     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2350     MutableEntry fred_match(&trans, GET_BY_HANDLE, metahandle_fred);
   2351     ASSERT_TRUE(fred_match.good());
   2352     EXPECT_TRUE(fred_match.GetId().ServerKnows());
   2353     fred_match_id = fred_match.GetId();
   2354     mock_server_->AddUpdateBookmark(fred_match_id, trans.root_id(),
   2355         "fred_match", 40, 40, local_cache_guid(), orig_id.GetServerId());
   2356   }
   2357   // Run the syncer.
   2358   for (int i = 0 ; i < 30 ; ++i) {
   2359     SyncShareNudge();
   2360   }
   2361 }
   2362 
   2363 /**
   2364  * In the event that we have a double changed entry, that is changed on both
   2365  * the client and the server, the conflict resolver should just drop one of
   2366  * them and accept the other.
   2367  */
   2368 
   2369 TEST_F(SyncerTest, DoublyChangedWithResolver) {
   2370   syncable::Id local_id;
   2371   {
   2372     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   2373     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder");
   2374     ASSERT_TRUE(parent.good());
   2375     parent.PutIsDir(true);
   2376     parent.PutId(parent_id_);
   2377     parent.PutBaseVersion(5);
   2378     parent.PutSpecifics(DefaultBookmarkSpecifics());
   2379     MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete.htm");
   2380     ASSERT_TRUE(child.good());
   2381     local_id = child.GetId();
   2382     child.PutId(child_id_);
   2383     child.PutBaseVersion(10);
   2384     WriteTestDataToEntry(&wtrans, &child);
   2385   }
   2386   mock_server_->AddUpdateBookmark(child_id_, parent_id_, "Pete2.htm", 11, 10,
   2387                                   local_cache_guid(), local_id.GetServerId());
   2388   mock_server_->set_conflict_all_commits(true);
   2389   SyncShareNudge();
   2390   syncable::Directory::Metahandles children;
   2391   {
   2392     syncable::ReadTransaction trans(FROM_HERE, directory());
   2393     directory()->GetChildHandlesById(&trans, parent_id_, &children);
   2394     // We expect the conflict resolver to preserve the local entry.
   2395     Entry child(&trans, syncable::GET_BY_ID, child_id_);
   2396     ASSERT_TRUE(child.good());
   2397     EXPECT_TRUE(child.GetIsUnsynced());
   2398     EXPECT_FALSE(child.GetIsUnappliedUpdate());
   2399     EXPECT_TRUE(child.GetSpecifics().has_bookmark());
   2400     EXPECT_EQ("Pete.htm", child.GetNonUniqueName());
   2401     VerifyTestBookmarkDataInEntry(&child);
   2402   }
   2403 
   2404   // Only one entry, since we just overwrite one.
   2405   EXPECT_EQ(1u, children.size());
   2406   saw_syncer_event_ = false;
   2407 }
   2408 
   2409 // We got this repro case when someone was editing bookmarks while sync was
   2410 // occuring. The entry had changed out underneath the user.
   2411 TEST_F(SyncerTest, CommitsUpdateDoesntAlterEntry) {
   2412   const base::Time& test_time = ProtoTimeToTime(123456);
   2413   syncable::Id local_id;
   2414   int64 entry_metahandle;
   2415   {
   2416     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   2417     MutableEntry entry(&wtrans, CREATE, BOOKMARKS, root_id_, "Pete");
   2418     ASSERT_TRUE(entry.good());
   2419     EXPECT_FALSE(entry.GetId().ServerKnows());
   2420     local_id = entry.GetId();
   2421     entry.PutIsDir(true);
   2422     entry.PutSpecifics(DefaultBookmarkSpecifics());
   2423     entry.PutIsUnsynced(true);
   2424     entry.PutMtime(test_time);
   2425     entry_metahandle = entry.GetMetahandle();
   2426   }
   2427   SyncShareNudge();
   2428   syncable::Id id;
   2429   int64 version;
   2430   {
   2431     syncable::ReadTransaction trans(FROM_HERE, directory());
   2432     Entry entry(&trans, syncable::GET_BY_HANDLE, entry_metahandle);
   2433     ASSERT_TRUE(entry.good());
   2434     id = entry.GetId();
   2435     EXPECT_TRUE(id.ServerKnows());
   2436     version = entry.GetBaseVersion();
   2437   }
   2438   sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
   2439   update->set_originator_cache_guid(local_cache_guid());
   2440   update->set_originator_client_item_id(local_id.GetServerId());
   2441   EXPECT_EQ("Pete", update->name());
   2442   EXPECT_EQ(id.GetServerId(), update->id_string());
   2443   EXPECT_EQ(root_id_.GetServerId(), update->parent_id_string());
   2444   EXPECT_EQ(version, update->version());
   2445   SyncShareNudge();
   2446   {
   2447     syncable::ReadTransaction trans(FROM_HERE, directory());
   2448     Entry entry(&trans, syncable::GET_BY_ID, id);
   2449     ASSERT_TRUE(entry.good());
   2450     EXPECT_TRUE(entry.GetMtime()== test_time);
   2451   }
   2452 }
   2453 
   2454 TEST_F(SyncerTest, ParentAndChildBothMatch) {
   2455   const FullModelTypeSet all_types = FullModelTypeSet::All();
   2456   syncable::Id parent_id = ids_.NewServerId();
   2457   syncable::Id child_id = ids_.NewServerId();
   2458   syncable::Id parent_local_id;
   2459   syncable::Id child_local_id;
   2460 
   2461 
   2462   {
   2463     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   2464     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder");
   2465     ASSERT_TRUE(parent.good());
   2466     parent_local_id = parent.GetId();
   2467     parent.PutIsDir(true);
   2468     parent.PutIsUnsynced(true);
   2469     parent.PutId(parent_id);
   2470     parent.PutBaseVersion(1);
   2471     parent.PutSpecifics(DefaultBookmarkSpecifics());
   2472 
   2473     MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "test.htm");
   2474     ASSERT_TRUE(child.good());
   2475     child_local_id = child.GetId();
   2476     child.PutId(child_id);
   2477     child.PutBaseVersion(1);
   2478     child.PutSpecifics(DefaultBookmarkSpecifics());
   2479     WriteTestDataToEntry(&wtrans, &child);
   2480   }
   2481   mock_server_->AddUpdateDirectory(parent_id, root_id_, "Folder", 10, 10,
   2482                                    local_cache_guid(),
   2483                                    parent_local_id.GetServerId());
   2484   mock_server_->AddUpdateBookmark(child_id, parent_id, "test.htm", 10, 10,
   2485                                   local_cache_guid(),
   2486                                   child_local_id.GetServerId());
   2487   mock_server_->set_conflict_all_commits(true);
   2488   SyncShareNudge();
   2489   SyncShareNudge();
   2490   SyncShareNudge();
   2491   {
   2492     syncable::ReadTransaction trans(FROM_HERE, directory());
   2493     Directory::Metahandles children;
   2494     directory()->GetChildHandlesById(&trans, root_id_, &children);
   2495     EXPECT_EQ(1u, children.size());
   2496     directory()->GetChildHandlesById(&trans, parent_id, &children);
   2497     EXPECT_EQ(1u, children.size());
   2498     std::vector<int64> unapplied;
   2499     directory()->GetUnappliedUpdateMetaHandles(&trans, all_types, &unapplied);
   2500     EXPECT_EQ(0u, unapplied.size());
   2501     syncable::Directory::Metahandles unsynced;
   2502     directory()->GetUnsyncedMetaHandles(&trans, &unsynced);
   2503     EXPECT_EQ(0u, unsynced.size());
   2504     saw_syncer_event_ = false;
   2505   }
   2506 }
   2507 
   2508 TEST_F(SyncerTest, CommittingNewDeleted) {
   2509   {
   2510     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2511     MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
   2512     entry.PutIsUnsynced(true);
   2513     entry.PutIsDel(true);
   2514   }
   2515   SyncShareNudge();
   2516   EXPECT_EQ(0u, mock_server_->committed_ids().size());
   2517 }
   2518 
   2519 // Original problem synopsis:
   2520 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion()
   2521 // Client creates entry, client finishes committing entry. Between
   2522 // commit and getting update back, we delete the entry.
   2523 // We get the update for the entry, but the local one was modified
   2524 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
   2525 // We commit deletion and get a new version number.
   2526 // We apply unapplied updates again before we get the update about the deletion.
   2527 // This means we have an unapplied update where server_version < base_version.
   2528 TEST_F(SyncerTest, UnappliedUpdateDuringCommit) {
   2529   // This test is a little fake.
   2530   {
   2531     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2532     MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
   2533     entry.PutId(ids_.FromNumber(20));
   2534     entry.PutBaseVersion(1);
   2535     entry.PutServerVersion(1);
   2536     entry.PutServerParentId(ids_.FromNumber(9999));  // Bad parent.
   2537     entry.PutIsUnsynced(true);
   2538     entry.PutIsUnappliedUpdate(true);
   2539     entry.PutSpecifics(DefaultBookmarkSpecifics());
   2540     entry.PutServerSpecifics(DefaultBookmarkSpecifics());
   2541     entry.PutIsDel(false);
   2542   }
   2543   SyncShareNudge();
   2544   EXPECT_EQ(1, session_->status_controller().TotalNumConflictingItems());
   2545   saw_syncer_event_ = false;
   2546 }
   2547 
   2548 // Original problem synopsis:
   2549 //   Illegal parent
   2550 // Unexpected error during sync if we:
   2551 //   make a new folder bob
   2552 //   wait for sync
   2553 //   make a new folder fred
   2554 //   move bob into fred
   2555 //   remove bob
   2556 //   remove fred
   2557 // if no syncing occured midway, bob will have an illegal parent
   2558 TEST_F(SyncerTest, DeletingEntryInFolder) {
   2559   // This test is a little fake.
   2560   int64 existing_metahandle;
   2561   {
   2562     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2563     MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "existing");
   2564     ASSERT_TRUE(entry.good());
   2565     entry.PutIsDir(true);
   2566     entry.PutSpecifics(DefaultBookmarkSpecifics());
   2567     entry.PutIsUnsynced(true);
   2568     existing_metahandle = entry.GetMetahandle();
   2569   }
   2570   SyncShareNudge();
   2571   {
   2572     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2573     MutableEntry newfolder(&trans, CREATE, BOOKMARKS, trans.root_id(), "new");
   2574     ASSERT_TRUE(newfolder.good());
   2575     newfolder.PutIsDir(true);
   2576     newfolder.PutSpecifics(DefaultBookmarkSpecifics());
   2577     newfolder.PutIsUnsynced(true);
   2578 
   2579     MutableEntry existing(&trans, GET_BY_HANDLE, existing_metahandle);
   2580     ASSERT_TRUE(existing.good());
   2581     existing.PutParentId(newfolder.GetId());
   2582     existing.PutIsUnsynced(true);
   2583     EXPECT_TRUE(existing.GetId().ServerKnows());
   2584 
   2585     newfolder.PutIsDel(true);
   2586     existing.PutIsDel(true);
   2587   }
   2588   SyncShareNudge();
   2589   EXPECT_EQ(0, GetCommitCounters(BOOKMARKS).num_commits_conflict);
   2590 }
   2591 
   2592 TEST_F(SyncerTest, DeletingEntryWithLocalEdits) {
   2593   int64 newfolder_metahandle;
   2594 
   2595   mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
   2596                                    foreign_cache_guid(), "-1");
   2597   SyncShareNudge();
   2598   {
   2599     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2600     MutableEntry newfolder(
   2601         &trans, CREATE, BOOKMARKS, ids_.FromNumber(1), "local");
   2602     ASSERT_TRUE(newfolder.good());
   2603     newfolder.PutIsUnsynced(true);
   2604     newfolder.PutIsDir(true);
   2605     newfolder.PutSpecifics(DefaultBookmarkSpecifics());
   2606     newfolder_metahandle = newfolder.GetMetahandle();
   2607   }
   2608   mock_server_->AddUpdateDirectory(1, 0, "bob", 2, 20,
   2609                                    foreign_cache_guid(), "-1");
   2610   mock_server_->SetLastUpdateDeleted();
   2611   SyncShareConfigure();
   2612   {
   2613     syncable::ReadTransaction trans(FROM_HERE, directory());
   2614     Entry entry(&trans, syncable::GET_BY_HANDLE, newfolder_metahandle);
   2615     ASSERT_TRUE(entry.good());
   2616   }
   2617 }
   2618 
   2619 TEST_F(SyncerTest, FolderSwapUpdate) {
   2620   mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10,
   2621                                    foreign_cache_guid(), "-7801");
   2622   mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10,
   2623                                    foreign_cache_guid(), "-1024");
   2624   SyncShareNudge();
   2625   mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20,
   2626                                    foreign_cache_guid(), "-1024");
   2627   mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20,
   2628                                    foreign_cache_guid(), "-7801");
   2629   SyncShareNudge();
   2630   {
   2631     syncable::ReadTransaction trans(FROM_HERE, directory());
   2632     Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
   2633     ASSERT_TRUE(id1.good());
   2634     EXPECT_TRUE("fred" == id1.GetNonUniqueName());
   2635     EXPECT_TRUE(root_id_ == id1.GetParentId());
   2636     Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
   2637     ASSERT_TRUE(id2.good());
   2638     EXPECT_TRUE("bob" == id2.GetNonUniqueName());
   2639     EXPECT_TRUE(root_id_ == id2.GetParentId());
   2640   }
   2641   saw_syncer_event_ = false;
   2642 }
   2643 
   2644 TEST_F(SyncerTest, NameCollidingFolderSwapWorksFine) {
   2645   mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10,
   2646                                    foreign_cache_guid(), "-7801");
   2647   mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10,
   2648                                    foreign_cache_guid(), "-1024");
   2649   mock_server_->AddUpdateDirectory(4096, 0, "alice", 1, 10,
   2650                                    foreign_cache_guid(), "-4096");
   2651   SyncShareNudge();
   2652   {
   2653     syncable::ReadTransaction trans(FROM_HERE, directory());
   2654     Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
   2655     ASSERT_TRUE(id1.good());
   2656     EXPECT_TRUE("bob" == id1.GetNonUniqueName());
   2657     EXPECT_TRUE(root_id_ == id1.GetParentId());
   2658     Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
   2659     ASSERT_TRUE(id2.good());
   2660     EXPECT_TRUE("fred" == id2.GetNonUniqueName());
   2661     EXPECT_TRUE(root_id_ == id2.GetParentId());
   2662     Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
   2663     ASSERT_TRUE(id3.good());
   2664     EXPECT_TRUE("alice" == id3.GetNonUniqueName());
   2665     EXPECT_TRUE(root_id_ == id3.GetParentId());
   2666   }
   2667   mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20,
   2668                                    foreign_cache_guid(), "-1024");
   2669   mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20,
   2670                                    foreign_cache_guid(), "-7801");
   2671   mock_server_->AddUpdateDirectory(4096, 0, "bob", 2, 20,
   2672                                    foreign_cache_guid(), "-4096");
   2673   SyncShareNudge();
   2674   {
   2675     syncable::ReadTransaction trans(FROM_HERE, directory());
   2676     Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
   2677     ASSERT_TRUE(id1.good());
   2678     EXPECT_TRUE("fred" == id1.GetNonUniqueName());
   2679     EXPECT_TRUE(root_id_ == id1.GetParentId());
   2680     Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
   2681     ASSERT_TRUE(id2.good());
   2682     EXPECT_TRUE("bob" == id2.GetNonUniqueName());
   2683     EXPECT_TRUE(root_id_ == id2.GetParentId());
   2684     Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
   2685     ASSERT_TRUE(id3.good());
   2686     EXPECT_TRUE("bob" == id3.GetNonUniqueName());
   2687     EXPECT_TRUE(root_id_ == id3.GetParentId());
   2688   }
   2689   saw_syncer_event_ = false;
   2690 }
   2691 
   2692 // Committing more than kDefaultMaxCommitBatchSize items requires that
   2693 // we post more than one commit command to the server.  This test makes
   2694 // sure that scenario works as expected.
   2695 TEST_F(SyncerTest, CommitManyItemsInOneGo_Success) {
   2696   uint32 num_batches = 3;
   2697   uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
   2698   {
   2699     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2700     for (uint32 i = 0; i < items_to_commit; i++) {
   2701       string nameutf8 = base::StringPrintf("%d", i);
   2702       string name(nameutf8.begin(), nameutf8.end());
   2703       MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
   2704       e.PutIsUnsynced(true);
   2705       e.PutIsDir(true);
   2706       e.PutSpecifics(DefaultBookmarkSpecifics());
   2707     }
   2708   }
   2709   ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
   2710 
   2711   SyncShareNudge();
   2712   EXPECT_EQ(num_batches, mock_server_->commit_messages().size());
   2713   EXPECT_EQ(0, directory()->unsynced_entity_count());
   2714 }
   2715 
   2716 // Test that a single failure to contact the server will cause us to exit the
   2717 // commit loop immediately.
   2718 TEST_F(SyncerTest, CommitManyItemsInOneGo_PostBufferFail) {
   2719   uint32 num_batches = 3;
   2720   uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
   2721   {
   2722     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2723     for (uint32 i = 0; i < items_to_commit; i++) {
   2724       string nameutf8 = base::StringPrintf("%d", i);
   2725       string name(nameutf8.begin(), nameutf8.end());
   2726       MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
   2727       e.PutIsUnsynced(true);
   2728       e.PutIsDir(true);
   2729       e.PutSpecifics(DefaultBookmarkSpecifics());
   2730     }
   2731   }
   2732   ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
   2733 
   2734   // The second commit should fail.  It will be preceded by one successful
   2735   // GetUpdate and one succesful commit.
   2736   mock_server_->FailNthPostBufferToPathCall(3);
   2737   SyncShareNudge();
   2738 
   2739   EXPECT_EQ(1U, mock_server_->commit_messages().size());
   2740   EXPECT_EQ(SYNC_SERVER_ERROR,
   2741             session_->status_controller().model_neutral_state().commit_result);
   2742   EXPECT_EQ(items_to_commit - kDefaultMaxCommitBatchSize,
   2743             directory()->unsynced_entity_count());
   2744 }
   2745 
   2746 // Test that a single conflict response from the server will cause us to exit
   2747 // the commit loop immediately.
   2748 TEST_F(SyncerTest, CommitManyItemsInOneGo_CommitConflict) {
   2749   uint32 num_batches = 2;
   2750   uint32 items_to_commit = kDefaultMaxCommitBatchSize * num_batches;
   2751   {
   2752     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2753     for (uint32 i = 0; i < items_to_commit; i++) {
   2754       string nameutf8 = base::StringPrintf("%d", i);
   2755       string name(nameutf8.begin(), nameutf8.end());
   2756       MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
   2757       e.PutIsUnsynced(true);
   2758       e.PutIsDir(true);
   2759       e.PutSpecifics(DefaultBookmarkSpecifics());
   2760     }
   2761   }
   2762   ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count());
   2763 
   2764   // Return a CONFLICT response for the first item.
   2765   mock_server_->set_conflict_n_commits(1);
   2766   SyncShareNudge();
   2767 
   2768   // We should stop looping at the first sign of trouble.
   2769   EXPECT_EQ(1U, mock_server_->commit_messages().size());
   2770   EXPECT_EQ(items_to_commit - (kDefaultMaxCommitBatchSize - 1),
   2771             directory()->unsynced_entity_count());
   2772 }
   2773 
   2774 // Tests that sending debug info events works.
   2775 TEST_F(SyncerTest, SendDebugInfoEventsOnGetUpdates_HappyCase) {
   2776   debug_info_getter_->AddDebugEvent();
   2777   debug_info_getter_->AddDebugEvent();
   2778 
   2779   SyncShareNudge();
   2780 
   2781   // Verify we received one GetUpdates request with two debug info events.
   2782   EXPECT_EQ(1U, mock_server_->requests().size());
   2783   ASSERT_TRUE(mock_server_->last_request().has_get_updates());
   2784   EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size());
   2785 
   2786   SyncShareNudge();
   2787 
   2788   // See that we received another GetUpdates request, but that it contains no
   2789   // debug info events.
   2790   EXPECT_EQ(2U, mock_server_->requests().size());
   2791   ASSERT_TRUE(mock_server_->last_request().has_get_updates());
   2792   EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
   2793 
   2794   debug_info_getter_->AddDebugEvent();
   2795 
   2796   SyncShareNudge();
   2797 
   2798   // See that we received another GetUpdates request and it contains one debug
   2799   // info event.
   2800   EXPECT_EQ(3U, mock_server_->requests().size());
   2801   ASSERT_TRUE(mock_server_->last_request().has_get_updates());
   2802   EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
   2803 }
   2804 
   2805 // Tests that debug info events are dropped on server error.
   2806 TEST_F(SyncerTest, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop) {
   2807   debug_info_getter_->AddDebugEvent();
   2808   debug_info_getter_->AddDebugEvent();
   2809 
   2810   mock_server_->FailNextPostBufferToPathCall();
   2811   SyncShareNudge();
   2812 
   2813   // Verify we attempted to send one GetUpdates request with two debug info
   2814   // events.
   2815   EXPECT_EQ(1U, mock_server_->requests().size());
   2816   ASSERT_TRUE(mock_server_->last_request().has_get_updates());
   2817   EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size());
   2818 
   2819   SyncShareNudge();
   2820 
   2821   // See that the client resent the two debug info events.
   2822   EXPECT_EQ(2U, mock_server_->requests().size());
   2823   ASSERT_TRUE(mock_server_->last_request().has_get_updates());
   2824   EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size());
   2825 
   2826   // The previous send was successful so this next one shouldn't generate any
   2827   // debug info events.
   2828   SyncShareNudge();
   2829   EXPECT_EQ(3U, mock_server_->requests().size());
   2830   ASSERT_TRUE(mock_server_->last_request().has_get_updates());
   2831   EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
   2832 }
   2833 
   2834 // Tests that sending debug info events on Commit works.
   2835 TEST_F(SyncerTest, SendDebugInfoEventsOnCommit_HappyCase) {
   2836   // Make sure GetUpdate isn't call as it would "steal" debug info events before
   2837   // Commit has a chance to send them.
   2838   ConfigureNoGetUpdatesRequired();
   2839 
   2840   // Generate a debug info event and trigger a commit.
   2841   debug_info_getter_->AddDebugEvent();
   2842   CreateUnsyncedDirectory("X", "id_X");
   2843   SyncShareNudge();
   2844 
   2845   // Verify that the last request received is a Commit and that it contains a
   2846   // debug info event.
   2847   EXPECT_EQ(1U, mock_server_->requests().size());
   2848   ASSERT_TRUE(mock_server_->last_request().has_commit());
   2849   EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
   2850 
   2851   // Generate another commit, but no debug info event.
   2852   CreateUnsyncedDirectory("Y", "id_Y");
   2853   SyncShareNudge();
   2854 
   2855   // See that it was received and contains no debug info events.
   2856   EXPECT_EQ(2U, mock_server_->requests().size());
   2857   ASSERT_TRUE(mock_server_->last_request().has_commit());
   2858   EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
   2859 }
   2860 
   2861 // Tests that debug info events are not dropped on server error.
   2862 TEST_F(SyncerTest, SendDebugInfoEventsOnCommit_PostFailsDontDrop) {
   2863   // Make sure GetUpdate isn't call as it would "steal" debug info events before
   2864   // Commit has a chance to send them.
   2865   ConfigureNoGetUpdatesRequired();
   2866 
   2867   mock_server_->FailNextPostBufferToPathCall();
   2868 
   2869   // Generate a debug info event and trigger a commit.
   2870   debug_info_getter_->AddDebugEvent();
   2871   CreateUnsyncedDirectory("X", "id_X");
   2872   SyncShareNudge();
   2873 
   2874   // Verify that the last request sent is a Commit and that it contains a debug
   2875   // info event.
   2876   EXPECT_EQ(1U, mock_server_->requests().size());
   2877   ASSERT_TRUE(mock_server_->last_request().has_commit());
   2878   EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
   2879 
   2880   // Try again.
   2881   SyncShareNudge();
   2882 
   2883   // Verify that we've received another Commit and that it contains a debug info
   2884   // event (just like the previous one).
   2885   EXPECT_EQ(2U, mock_server_->requests().size());
   2886   ASSERT_TRUE(mock_server_->last_request().has_commit());
   2887   EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size());
   2888 
   2889   // Generate another commit and try again.
   2890   CreateUnsyncedDirectory("Y", "id_Y");
   2891   SyncShareNudge();
   2892 
   2893   // See that it was received and contains no debug info events.
   2894   EXPECT_EQ(3U, mock_server_->requests().size());
   2895   ASSERT_TRUE(mock_server_->last_request().has_commit());
   2896   EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size());
   2897 }
   2898 
   2899 TEST_F(SyncerTest, HugeConflict) {
   2900   int item_count = 300;  // We should be able to do 300 or 3000 w/o issue.
   2901 
   2902   syncable::Id parent_id = ids_.NewServerId();
   2903   syncable::Id last_id = parent_id;
   2904   vector<syncable::Id> tree_ids;
   2905 
   2906   // Create a lot of updates for which the parent does not exist yet.
   2907   // Generate a huge deep tree which should all fail to apply at first.
   2908   {
   2909     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2910     for (int i = 0; i < item_count ; i++) {
   2911       syncable::Id next_id = ids_.NewServerId();
   2912       syncable::Id local_id = ids_.NewLocalId();
   2913       tree_ids.push_back(next_id);
   2914       mock_server_->AddUpdateDirectory(next_id, last_id, "BOB", 2, 20,
   2915                                        foreign_cache_guid(),
   2916                                        local_id.GetServerId());
   2917       last_id = next_id;
   2918     }
   2919   }
   2920   SyncShareNudge();
   2921 
   2922   // Check they're in the expected conflict state.
   2923   {
   2924     syncable::ReadTransaction trans(FROM_HERE, directory());
   2925     for (int i = 0; i < item_count; i++) {
   2926       Entry e(&trans, GET_BY_ID, tree_ids[i]);
   2927       // They should all exist but none should be applied.
   2928       ASSERT_TRUE(e.good());
   2929       EXPECT_TRUE(e.GetIsDel());
   2930       EXPECT_TRUE(e.GetIsUnappliedUpdate());
   2931     }
   2932   }
   2933 
   2934   // Add the missing parent directory.
   2935   mock_server_->AddUpdateDirectory(parent_id, TestIdFactory::root(),
   2936       "BOB", 2, 20, foreign_cache_guid(), "-3500");
   2937   SyncShareNudge();
   2938 
   2939   // Now they should all be OK.
   2940   {
   2941     syncable::ReadTransaction trans(FROM_HERE, directory());
   2942     for (int i = 0; i < item_count; i++) {
   2943       Entry e(&trans, GET_BY_ID, tree_ids[i]);
   2944       ASSERT_TRUE(e.good());
   2945       EXPECT_FALSE(e.GetIsDel());
   2946       EXPECT_FALSE(e.GetIsUnappliedUpdate());
   2947     }
   2948   }
   2949 }
   2950 
   2951 TEST_F(SyncerTest, DontCrashOnCaseChange) {
   2952   mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
   2953                                    foreign_cache_guid(), "-1");
   2954   SyncShareNudge();
   2955   {
   2956     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   2957     MutableEntry e(&trans, GET_BY_ID, ids_.FromNumber(1));
   2958     ASSERT_TRUE(e.good());
   2959     e.PutIsUnsynced(true);
   2960   }
   2961   mock_server_->set_conflict_all_commits(true);
   2962   mock_server_->AddUpdateDirectory(1, 0, "BOB", 2, 20,
   2963                                    foreign_cache_guid(), "-1");
   2964   SyncShareNudge();  // USED TO CAUSE AN ASSERT
   2965   saw_syncer_event_ = false;
   2966 }
   2967 
   2968 TEST_F(SyncerTest, UnsyncedItemAndUpdate) {
   2969   mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
   2970                                    foreign_cache_guid(), "-1");
   2971   SyncShareNudge();
   2972   mock_server_->set_conflict_all_commits(true);
   2973   mock_server_->AddUpdateDirectory(2, 0, "bob", 2, 20,
   2974                                    foreign_cache_guid(), "-2");
   2975   SyncShareNudge();  // USED TO CAUSE AN ASSERT
   2976   saw_syncer_event_ = false;
   2977 }
   2978 
   2979 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath) {
   2980   mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
   2981                                   foreign_cache_guid(), "-1");
   2982   SyncShareNudge();
   2983   int64 local_folder_handle;
   2984   syncable::Id local_folder_id;
   2985   {
   2986     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   2987     MutableEntry new_entry(
   2988         &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm");
   2989     ASSERT_TRUE(new_entry.good());
   2990     local_folder_id = new_entry.GetId();
   2991     local_folder_handle = new_entry.GetMetahandle();
   2992     new_entry.PutIsUnsynced(true);
   2993     new_entry.PutSpecifics(DefaultBookmarkSpecifics());
   2994     MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   2995     ASSERT_TRUE(old.good());
   2996     WriteTestDataToEntry(&wtrans, &old);
   2997   }
   2998   mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
   2999                                   foreign_cache_guid(), "-1");
   3000   mock_server_->set_conflict_all_commits(true);
   3001   SyncShareNudge();
   3002   saw_syncer_event_ = false;
   3003   {
   3004     // Update #20 should have been dropped in favor of the local version.
   3005     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3006     MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   3007     MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
   3008     ASSERT_TRUE(server.good());
   3009     ASSERT_TRUE(local.good());
   3010     EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
   3011     EXPECT_FALSE(server.GetIsUnappliedUpdate());
   3012     EXPECT_FALSE(local.GetIsUnappliedUpdate());
   3013     EXPECT_TRUE(server.GetIsUnsynced());
   3014     EXPECT_TRUE(local.GetIsUnsynced());
   3015     EXPECT_EQ("Foo.htm", server.GetNonUniqueName());
   3016     EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
   3017   }
   3018   // Allow local changes to commit.
   3019   mock_server_->set_conflict_all_commits(false);
   3020   SyncShareNudge();
   3021   saw_syncer_event_ = false;
   3022 
   3023   // Now add a server change to make the two names equal.  There should
   3024   // be no conflict with that, since names are not unique.
   3025   mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
   3026                                   foreign_cache_guid(), "-1");
   3027   SyncShareNudge();
   3028   saw_syncer_event_ = false;
   3029   {
   3030     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3031     MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   3032     MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
   3033     ASSERT_TRUE(server.good());
   3034     ASSERT_TRUE(local.good());
   3035     EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
   3036     EXPECT_FALSE(server.GetIsUnappliedUpdate());
   3037     EXPECT_FALSE(local.GetIsUnappliedUpdate());
   3038     EXPECT_FALSE(server.GetIsUnsynced());
   3039     EXPECT_FALSE(local.GetIsUnsynced());
   3040     EXPECT_EQ("Bar.htm", server.GetNonUniqueName());
   3041     EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
   3042     EXPECT_EQ("http://google.com",  // Default from AddUpdateBookmark.
   3043         server.GetSpecifics().bookmark().url());
   3044   }
   3045 }
   3046 
   3047 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
   3048 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto) {
   3049   mock_server_->set_use_legacy_bookmarks_protocol(true);
   3050   mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10,
   3051                                   foreign_cache_guid(), "-1");
   3052   SyncShareNudge();
   3053   int64 local_folder_handle;
   3054   syncable::Id local_folder_id;
   3055   {
   3056     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3057     MutableEntry new_entry(
   3058         &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm");
   3059     ASSERT_TRUE(new_entry.good());
   3060     local_folder_id = new_entry.GetId();
   3061     local_folder_handle = new_entry.GetMetahandle();
   3062     new_entry.PutIsUnsynced(true);
   3063     new_entry.PutSpecifics(DefaultBookmarkSpecifics());
   3064     MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   3065     ASSERT_TRUE(old.good());
   3066     WriteTestDataToEntry(&wtrans, &old);
   3067   }
   3068   mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20,
   3069                                   foreign_cache_guid(), "-1");
   3070   mock_server_->set_conflict_all_commits(true);
   3071   SyncShareNudge();
   3072   saw_syncer_event_ = false;
   3073   {
   3074     // Update #20 should have been dropped in favor of the local version.
   3075     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3076     MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   3077     MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
   3078     ASSERT_TRUE(server.good());
   3079     ASSERT_TRUE(local.good());
   3080     EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
   3081     EXPECT_FALSE(server.GetIsUnappliedUpdate());
   3082     EXPECT_FALSE(local.GetIsUnappliedUpdate());
   3083     EXPECT_TRUE(server.GetIsUnsynced());
   3084     EXPECT_TRUE(local.GetIsUnsynced());
   3085     EXPECT_EQ("Foo.htm", server.GetNonUniqueName());
   3086     EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
   3087   }
   3088   // Allow local changes to commit.
   3089   mock_server_->set_conflict_all_commits(false);
   3090   SyncShareNudge();
   3091   saw_syncer_event_ = false;
   3092 
   3093   // Now add a server change to make the two names equal.  There should
   3094   // be no conflict with that, since names are not unique.
   3095   mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30,
   3096                                   foreign_cache_guid(), "-1");
   3097   SyncShareNudge();
   3098   saw_syncer_event_ = false;
   3099   {
   3100     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3101     MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   3102     MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
   3103     ASSERT_TRUE(server.good());
   3104     ASSERT_TRUE(local.good());
   3105     EXPECT_TRUE(local.GetMetahandle()!= server.GetMetahandle());
   3106     EXPECT_FALSE(server.GetIsUnappliedUpdate());
   3107     EXPECT_FALSE(local.GetIsUnappliedUpdate());
   3108     EXPECT_FALSE(server.GetIsUnsynced());
   3109     EXPECT_FALSE(local.GetIsUnsynced());
   3110     EXPECT_EQ("Bar.htm", server.GetNonUniqueName());
   3111     EXPECT_EQ("Bar.htm", local.GetNonUniqueName());
   3112     EXPECT_EQ("http://google.com",  // Default from AddUpdateBookmark.
   3113         server.GetSpecifics().bookmark().url());
   3114   }
   3115 }
   3116 
   3117 // Circular links should be resolved by the server.
   3118 TEST_F(SyncerTest, SiblingDirectoriesBecomeCircular) {
   3119   // we don't currently resolve this. This test ensures we don't.
   3120   mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
   3121                                    foreign_cache_guid(), "-1");
   3122   mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
   3123                                    foreign_cache_guid(), "-2");
   3124   SyncShareNudge();
   3125   {
   3126     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3127     MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   3128     ASSERT_TRUE(A.good());
   3129     A.PutIsUnsynced(true);
   3130     A.PutParentId(ids_.FromNumber(2));
   3131     A.PutNonUniqueName("B");
   3132   }
   3133   mock_server_->AddUpdateDirectory(2, 1, "A", 20, 20,
   3134                                    foreign_cache_guid(), "-2");
   3135   mock_server_->set_conflict_all_commits(true);
   3136   SyncShareNudge();
   3137   saw_syncer_event_ = false;
   3138   {
   3139     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3140     MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   3141     ASSERT_TRUE(A.good());
   3142     MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
   3143     ASSERT_TRUE(B.good());
   3144     EXPECT_TRUE(A.GetNonUniqueName()== "B");
   3145     EXPECT_TRUE(B.GetNonUniqueName()== "B");
   3146   }
   3147 }
   3148 
   3149 TEST_F(SyncerTest, SwapEntryNames) {
   3150   // Simple transaction test.
   3151   mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
   3152                                    foreign_cache_guid(), "-1");
   3153   mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10,
   3154                                    foreign_cache_guid(), "-2");
   3155   mock_server_->set_conflict_all_commits(true);
   3156   SyncShareNudge();
   3157   {
   3158     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3159     MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
   3160     ASSERT_TRUE(A.good());
   3161     A.PutIsUnsynced(true);
   3162     MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
   3163     ASSERT_TRUE(B.good());
   3164     B.PutIsUnsynced(true);
   3165     A.PutNonUniqueName("C");
   3166     B.PutNonUniqueName("A");
   3167     A.PutNonUniqueName("B");
   3168   }
   3169   SyncShareNudge();
   3170   saw_syncer_event_ = false;
   3171 }
   3172 
   3173 TEST_F(SyncerTest, DualDeletionWithNewItemNameClash) {
   3174   mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10,
   3175                                    foreign_cache_guid(), "-1");
   3176   mock_server_->AddUpdateBookmark(2, 0, "B", 10, 10,
   3177                                   foreign_cache_guid(), "-2");
   3178   mock_server_->set_conflict_all_commits(true);
   3179   SyncShareNudge();
   3180   {
   3181     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3182     MutableEntry B(&trans, GET_BY_ID, ids_.FromNumber(2));
   3183     ASSERT_TRUE(B.good());
   3184     WriteTestDataToEntry(&trans, &B);
   3185     B.PutIsDel(true);
   3186   }
   3187   mock_server_->AddUpdateBookmark(2, 0, "A", 11, 11,
   3188                                   foreign_cache_guid(), "-2");
   3189   mock_server_->SetLastUpdateDeleted();
   3190   SyncShareNudge();
   3191   {
   3192     syncable::ReadTransaction trans(FROM_HERE, directory());
   3193     Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
   3194     ASSERT_TRUE(B.good());
   3195     EXPECT_FALSE(B.GetIsUnsynced());
   3196     EXPECT_FALSE(B.GetIsUnappliedUpdate());
   3197   }
   3198   saw_syncer_event_ = false;
   3199 }
   3200 
   3201 // When we undelete an entity as a result of conflict resolution, we reuse the
   3202 // existing server id and preserve the old version, simply updating the server
   3203 // version with the new non-deleted entity.
   3204 TEST_F(SyncerTest, ResolveWeWroteTheyDeleted) {
   3205   int64 bob_metahandle;
   3206 
   3207   mock_server_->AddUpdateBookmark(1, 0, "bob", 1, 10,
   3208                                   foreign_cache_guid(), "-1");
   3209   SyncShareNudge();
   3210   {
   3211     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3212     MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
   3213     ASSERT_TRUE(bob.good());
   3214     bob_metahandle = bob.GetMetahandle();
   3215     WriteTestDataToEntry(&trans, &bob);
   3216   }
   3217   mock_server_->AddUpdateBookmark(1, 0, "bob", 2, 10,
   3218                                   foreign_cache_guid(), "-1");
   3219   mock_server_->SetLastUpdateDeleted();
   3220   mock_server_->set_conflict_all_commits(true);
   3221   SyncShareNudge();
   3222   SyncShareNudge();
   3223   {
   3224     syncable::ReadTransaction trans(FROM_HERE, directory());
   3225     Entry bob(&trans, GET_BY_HANDLE, bob_metahandle);
   3226     ASSERT_TRUE(bob.good());
   3227     EXPECT_TRUE(bob.GetIsUnsynced());
   3228     EXPECT_TRUE(bob.GetId().ServerKnows());
   3229     EXPECT_FALSE(bob.GetIsUnappliedUpdate());
   3230     EXPECT_FALSE(bob.GetIsDel());
   3231     EXPECT_EQ(2, bob.GetServerVersion());
   3232     EXPECT_EQ(2, bob.GetBaseVersion());
   3233   }
   3234   saw_syncer_event_ = false;
   3235 }
   3236 
   3237 // This test is to reproduce a check failure. Sometimes we would get a bad ID
   3238 // back when creating an entry.
   3239 TEST_F(SyncerTest, DuplicateIDReturn) {
   3240   {
   3241     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3242     MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob");
   3243     ASSERT_TRUE(folder.good());
   3244     folder.PutIsUnsynced(true);
   3245     folder.PutIsDir(true);
   3246     folder.PutSpecifics(DefaultBookmarkSpecifics());
   3247     MutableEntry folder2(&trans, CREATE, BOOKMARKS, trans.root_id(), "fred");
   3248     ASSERT_TRUE(folder2.good());
   3249     folder2.PutIsUnsynced(false);
   3250     folder2.PutIsDir(true);
   3251     folder2.PutSpecifics(DefaultBookmarkSpecifics());
   3252     folder2.PutBaseVersion(3);
   3253     folder2.PutId(syncable::Id::CreateFromServerId("mock_server:10000"));
   3254   }
   3255   mock_server_->set_next_new_id(10000);
   3256   EXPECT_EQ(1u, directory()->unsynced_entity_count());
   3257   // we get back a bad id in here (should never happen).
   3258   SyncShareNudge();
   3259   EXPECT_EQ(1u, directory()->unsynced_entity_count());
   3260   SyncShareNudge();  // another bad id in here.
   3261   EXPECT_EQ(0u, directory()->unsynced_entity_count());
   3262   saw_syncer_event_ = false;
   3263 }
   3264 
   3265 TEST_F(SyncerTest, DeletedEntryWithBadParentInLoopCalculation) {
   3266   mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10,
   3267                                    foreign_cache_guid(), "-1");
   3268   SyncShareNudge();
   3269   {
   3270     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3271     MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
   3272     ASSERT_TRUE(bob.good());
   3273     // This is valid, because the parent could have gone away a long time ago.
   3274     bob.PutParentId(ids_.FromNumber(54));
   3275     bob.PutIsDel(true);
   3276     bob.PutIsUnsynced(true);
   3277   }
   3278   mock_server_->AddUpdateDirectory(2, 1, "fred", 1, 10,
   3279                                    foreign_cache_guid(), "-2");
   3280   SyncShareNudge();
   3281   SyncShareNudge();
   3282 }
   3283 
   3284 TEST_F(SyncerTest, ConflictResolverMergesLocalDeleteAndServerUpdate) {
   3285   syncable::Id local_id;
   3286   {
   3287     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3288 
   3289     MutableEntry local_deleted(
   3290         &trans, CREATE, BOOKMARKS, trans.root_id(), "name");
   3291     local_id = local_deleted.GetId();
   3292     local_deleted.PutId(ids_.FromNumber(1));
   3293     local_deleted.PutBaseVersion(1);
   3294     local_deleted.PutIsDel(true);
   3295     local_deleted.PutIsDir(false);
   3296     local_deleted.PutIsUnsynced(true);
   3297     local_deleted.PutSpecifics(DefaultBookmarkSpecifics());
   3298   }
   3299 
   3300   mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10,
   3301                                   local_cache_guid(),
   3302                                   local_id.GetServerId());
   3303 
   3304   // We don't care about actually committing, just the resolution.
   3305   mock_server_->set_conflict_all_commits(true);
   3306   SyncShareNudge();
   3307 
   3308   {
   3309     syncable::ReadTransaction trans(FROM_HERE, directory());
   3310     Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
   3311     EXPECT_TRUE(local_deleted.GetBaseVersion()== 10);
   3312     EXPECT_TRUE(local_deleted.GetIsUnappliedUpdate()== false);
   3313     EXPECT_TRUE(local_deleted.GetIsUnsynced()== true);
   3314     EXPECT_TRUE(local_deleted.GetIsDel()== true);
   3315     EXPECT_TRUE(local_deleted.GetIsDir()== false);
   3316   }
   3317 }
   3318 
   3319 // See what happens if the IS_DIR bit gets flipped.  This can cause us
   3320 // all kinds of disasters.
   3321 TEST_F(SyncerTest, UpdateFlipsTheFolderBit) {
   3322   // Local object: a deleted directory (container), revision 1, unsynced.
   3323   {
   3324     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3325 
   3326     MutableEntry local_deleted(
   3327         &trans, CREATE, BOOKMARKS, trans.root_id(), "name");
   3328     local_deleted.PutId(ids_.FromNumber(1));
   3329     local_deleted.PutBaseVersion(1);
   3330     local_deleted.PutIsDel(true);
   3331     local_deleted.PutIsDir(true);
   3332     local_deleted.PutIsUnsynced(true);
   3333     local_deleted.PutSpecifics(DefaultBookmarkSpecifics());
   3334   }
   3335 
   3336   // Server update: entry-type object (not a container), revision 10.
   3337   mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10,
   3338                                   local_cache_guid(),
   3339                                   ids_.FromNumber(1).GetServerId());
   3340 
   3341   // Don't attempt to commit.
   3342   mock_server_->set_conflict_all_commits(true);
   3343 
   3344   // The syncer should not attempt to apply the invalid update.
   3345   SyncShareNudge();
   3346 
   3347   {
   3348     syncable::ReadTransaction trans(FROM_HERE, directory());
   3349     Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
   3350     EXPECT_TRUE(local_deleted.GetBaseVersion()== 1);
   3351     EXPECT_TRUE(local_deleted.GetIsUnappliedUpdate()== false);
   3352     EXPECT_TRUE(local_deleted.GetIsUnsynced()== true);
   3353     EXPECT_TRUE(local_deleted.GetIsDel()== true);
   3354     EXPECT_TRUE(local_deleted.GetIsDir()== true);
   3355   }
   3356 }
   3357 
   3358 // Bug Synopsis:
   3359 // Merge conflict resolution will merge a new local entry with another entry
   3360 // that needs updates, resulting in CHECK.
   3361 TEST_F(SyncerTest, MergingExistingItems) {
   3362   mock_server_->set_conflict_all_commits(true);
   3363   mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10,
   3364                                   local_cache_guid(), "-1");
   3365   SyncShareNudge();
   3366   {
   3367     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3368     MutableEntry entry(
   3369         &trans, CREATE, BOOKMARKS, trans.root_id(), "Copy of base");
   3370     WriteTestDataToEntry(&trans, &entry);
   3371   }
   3372   mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
   3373                                   local_cache_guid(), "-1");
   3374   SyncShareNudge();
   3375 }
   3376 
   3377 // In this test a long changelog contains a child at the start of the changelog
   3378 // and a parent at the end. While these updates are in progress the client would
   3379 // appear stuck.
   3380 TEST_F(SyncerTest, LongChangelistWithApplicationConflict) {
   3381   const int depth = 400;
   3382   syncable::Id folder_id = ids_.FromNumber(1);
   3383 
   3384   // First we an item in a folder in the root. However the folder won't come
   3385   // till much later.
   3386   syncable::Id stuck_entry_id = TestIdFactory::FromNumber(99999);
   3387   mock_server_->AddUpdateDirectory(stuck_entry_id,
   3388       folder_id, "stuck", 1, 1,
   3389       foreign_cache_guid(), "-99999");
   3390   mock_server_->SetChangesRemaining(depth - 1);
   3391   SyncShareNudge();
   3392 
   3393   // Buffer up a very long series of downloads.
   3394   // We should never be stuck (conflict resolution shouldn't
   3395   // kick in so long as we're making forward progress).
   3396   for (int i = 0; i < depth; i++) {
   3397     mock_server_->NextUpdateBatch();
   3398     mock_server_->SetNewTimestamp(i + 1);
   3399     mock_server_->SetChangesRemaining(depth - i);
   3400   }
   3401 
   3402   SyncShareNudge();
   3403 
   3404   // Ensure our folder hasn't somehow applied.
   3405   {
   3406     syncable::ReadTransaction trans(FROM_HERE, directory());
   3407     Entry child(&trans, GET_BY_ID, stuck_entry_id);
   3408     EXPECT_TRUE(child.good());
   3409     EXPECT_TRUE(child.GetIsUnappliedUpdate());
   3410     EXPECT_TRUE(child.GetIsDel());
   3411     EXPECT_FALSE(child.GetIsUnsynced());
   3412   }
   3413 
   3414   // And finally the folder.
   3415   mock_server_->AddUpdateDirectory(folder_id,
   3416       TestIdFactory::root(), "folder", 1, 1,
   3417       foreign_cache_guid(), "-1");
   3418   mock_server_->SetChangesRemaining(0);
   3419   SyncShareNudge();
   3420   SyncShareNudge();
   3421   // Check that everything is as expected after the commit.
   3422   {
   3423     syncable::ReadTransaction trans(FROM_HERE, directory());
   3424     Entry entry(&trans, GET_BY_ID, folder_id);
   3425     ASSERT_TRUE(entry.good());
   3426     Entry child(&trans, GET_BY_ID, stuck_entry_id);
   3427     EXPECT_EQ(entry.GetId(), child.GetParentId());
   3428     EXPECT_EQ("stuck", child.GetNonUniqueName());
   3429     EXPECT_TRUE(child.good());
   3430   }
   3431 }
   3432 
   3433 TEST_F(SyncerTest, DontMergeTwoExistingItems) {
   3434   mock_server_->set_conflict_all_commits(true);
   3435   mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10,
   3436                                   foreign_cache_guid(), "-1");
   3437   mock_server_->AddUpdateBookmark(2, 0, "base2", 10, 10,
   3438                                   foreign_cache_guid(), "-2");
   3439   SyncShareNudge();
   3440   {
   3441     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3442     MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
   3443     ASSERT_TRUE(entry.good());
   3444     entry.PutNonUniqueName("Copy of base");
   3445     entry.PutIsUnsynced(true);
   3446   }
   3447   mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50,
   3448                                   foreign_cache_guid(), "-1");
   3449   SyncShareNudge();
   3450   {
   3451     syncable::ReadTransaction trans(FROM_HERE, directory());
   3452     Entry entry1(&trans, GET_BY_ID, ids_.FromNumber(1));
   3453     EXPECT_FALSE(entry1.GetIsUnappliedUpdate());
   3454     EXPECT_FALSE(entry1.GetIsUnsynced());
   3455     EXPECT_FALSE(entry1.GetIsDel());
   3456     Entry entry2(&trans, GET_BY_ID, ids_.FromNumber(2));
   3457     EXPECT_FALSE(entry2.GetIsUnappliedUpdate());
   3458     EXPECT_TRUE(entry2.GetIsUnsynced());
   3459     EXPECT_FALSE(entry2.GetIsDel());
   3460     EXPECT_EQ(entry1.GetNonUniqueName(), entry2.GetNonUniqueName());
   3461   }
   3462 }
   3463 
   3464 TEST_F(SyncerTest, TestUndeleteUpdate) {
   3465   mock_server_->set_conflict_all_commits(true);
   3466   mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1,
   3467                                    foreign_cache_guid(), "-1");
   3468   mock_server_->AddUpdateDirectory(2, 1, "bar", 1, 2,
   3469                                    foreign_cache_guid(), "-2");
   3470   SyncShareNudge();
   3471   mock_server_->AddUpdateDirectory(2, 1, "bar", 2, 3,
   3472                                    foreign_cache_guid(), "-2");
   3473   mock_server_->SetLastUpdateDeleted();
   3474   SyncShareNudge();
   3475 
   3476   int64 metahandle;
   3477   {
   3478     syncable::ReadTransaction trans(FROM_HERE, directory());
   3479     Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
   3480     ASSERT_TRUE(entry.good());
   3481     EXPECT_TRUE(entry.GetIsDel());
   3482     metahandle = entry.GetMetahandle();
   3483   }
   3484   mock_server_->AddUpdateDirectory(1, 0, "foo", 2, 4,
   3485                                    foreign_cache_guid(), "-1");
   3486   mock_server_->SetLastUpdateDeleted();
   3487   SyncShareNudge();
   3488   // This used to be rejected as it's an undeletion. Now, it results in moving
   3489   // the delete path aside.
   3490   mock_server_->AddUpdateDirectory(2, 1, "bar", 3, 5,
   3491                                    foreign_cache_guid(), "-2");
   3492   SyncShareNudge();
   3493   {
   3494     syncable::ReadTransaction trans(FROM_HERE, directory());
   3495     Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
   3496     ASSERT_TRUE(entry.good());
   3497     EXPECT_TRUE(entry.GetIsDel());
   3498     EXPECT_FALSE(entry.GetServerIsDel());
   3499     EXPECT_TRUE(entry.GetIsUnappliedUpdate());
   3500     EXPECT_NE(entry.GetMetahandle(), metahandle);
   3501   }
   3502 }
   3503 
   3504 TEST_F(SyncerTest, TestMoveSanitizedNamedFolder) {
   3505   mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1,
   3506                                    foreign_cache_guid(), "-1");
   3507   mock_server_->AddUpdateDirectory(2, 0, ":::", 1, 2,
   3508                                    foreign_cache_guid(), "-2");
   3509   SyncShareNudge();
   3510   {
   3511     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3512     MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
   3513     ASSERT_TRUE(entry.good());
   3514     entry.PutParentId(ids_.FromNumber(1));
   3515     EXPECT_TRUE(entry.PutIsUnsynced(true));
   3516   }
   3517   SyncShareNudge();
   3518   // We use the same sync ts as before so our times match up.
   3519   mock_server_->AddUpdateDirectory(2, 1, ":::", 2, 2,
   3520                                    foreign_cache_guid(), "-2");
   3521   SyncShareNudge();
   3522 }
   3523 
   3524 // Don't crash when this occurs.
   3525 TEST_F(SyncerTest, UpdateWhereParentIsNotAFolder) {
   3526   mock_server_->AddUpdateBookmark(1, 0, "B", 10, 10,
   3527                                   foreign_cache_guid(), "-1");
   3528   mock_server_->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10,
   3529                                    foreign_cache_guid(), "-2");
   3530   // Used to cause a CHECK
   3531   SyncShareNudge();
   3532   {
   3533     syncable::ReadTransaction rtrans(FROM_HERE, directory());
   3534     Entry good_entry(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
   3535     ASSERT_TRUE(good_entry.good());
   3536     EXPECT_FALSE(good_entry.GetIsUnappliedUpdate());
   3537     Entry bad_parent(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
   3538     ASSERT_TRUE(bad_parent.good());
   3539     EXPECT_TRUE(bad_parent.GetIsUnappliedUpdate());
   3540   }
   3541 }
   3542 
   3543 TEST_F(SyncerTest, DirectoryUpdateTest) {
   3544   Id in_root_id = ids_.NewServerId();
   3545   Id in_in_root_id = ids_.NewServerId();
   3546 
   3547   mock_server_->AddUpdateDirectory(in_root_id, TestIdFactory::root(),
   3548                                    "in_root_name", 2, 2,
   3549                                    foreign_cache_guid(), "-1");
   3550   mock_server_->AddUpdateDirectory(in_in_root_id, in_root_id,
   3551                                    "in_in_root_name", 3, 3,
   3552                                    foreign_cache_guid(), "-2");
   3553   SyncShareNudge();
   3554   {
   3555     syncable::ReadTransaction trans(FROM_HERE, directory());
   3556     Entry in_root(&trans, GET_BY_ID, in_root_id);
   3557     ASSERT_TRUE(in_root.good());
   3558     EXPECT_EQ("in_root_name", in_root.GetNonUniqueName());
   3559     EXPECT_EQ(TestIdFactory::root(), in_root.GetParentId());
   3560 
   3561     Entry in_in_root(&trans, GET_BY_ID, in_in_root_id);
   3562     ASSERT_TRUE(in_in_root.good());
   3563     EXPECT_EQ("in_in_root_name", in_in_root.GetNonUniqueName());
   3564     EXPECT_EQ(in_root_id, in_in_root.GetParentId());
   3565   }
   3566 }
   3567 
   3568 TEST_F(SyncerTest, DirectoryCommitTest) {
   3569   syncable::Id in_root_id, in_dir_id;
   3570   int64 foo_metahandle;
   3571   int64 bar_metahandle;
   3572 
   3573   {
   3574     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3575     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "foo");
   3576     ASSERT_TRUE(parent.good());
   3577     parent.PutIsUnsynced(true);
   3578     parent.PutIsDir(true);
   3579     parent.PutSpecifics(DefaultBookmarkSpecifics());
   3580     in_root_id = parent.GetId();
   3581     foo_metahandle = parent.GetMetahandle();
   3582 
   3583     MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "bar");
   3584     ASSERT_TRUE(child.good());
   3585     child.PutIsUnsynced(true);
   3586     child.PutIsDir(true);
   3587     child.PutSpecifics(DefaultBookmarkSpecifics());
   3588     bar_metahandle = child.GetMetahandle();
   3589     in_dir_id = parent.GetId();
   3590   }
   3591   SyncShareNudge();
   3592   {
   3593     syncable::ReadTransaction trans(FROM_HERE, directory());
   3594     Entry fail_by_old_id_entry(&trans, GET_BY_ID, in_root_id);
   3595     ASSERT_FALSE(fail_by_old_id_entry.good());
   3596 
   3597     Entry foo_entry(&trans, GET_BY_HANDLE, foo_metahandle);
   3598     ASSERT_TRUE(foo_entry.good());
   3599     EXPECT_EQ("foo", foo_entry.GetNonUniqueName());
   3600     EXPECT_NE(foo_entry.GetId(), in_root_id);
   3601 
   3602     Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle);
   3603     ASSERT_TRUE(bar_entry.good());
   3604     EXPECT_EQ("bar", bar_entry.GetNonUniqueName());
   3605     EXPECT_NE(bar_entry.GetId(), in_dir_id);
   3606     EXPECT_EQ(foo_entry.GetId(), bar_entry.GetParentId());
   3607   }
   3608 }
   3609 
   3610 TEST_F(SyncerTest, TestClientCommandDuringUpdate) {
   3611   using sync_pb::ClientCommand;
   3612 
   3613   ClientCommand* command = new ClientCommand();
   3614   command->set_set_sync_poll_interval(8);
   3615   command->set_set_sync_long_poll_interval(800);
   3616   command->set_sessions_commit_delay_seconds(3141);
   3617   sync_pb::CustomNudgeDelay* bookmark_delay =
   3618       command->add_custom_nudge_delays();
   3619   bookmark_delay->set_datatype_id(
   3620       GetSpecificsFieldNumberFromModelType(BOOKMARKS));
   3621   bookmark_delay->set_delay_ms(950);
   3622   command->set_client_invalidation_hint_buffer_size(11);
   3623   mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1,
   3624                                    foreign_cache_guid(), "-1");
   3625   mock_server_->SetGUClientCommand(command);
   3626   SyncShareNudge();
   3627 
   3628   EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_);
   3629   EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_);
   3630   EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_);
   3631   EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_);
   3632   EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_);
   3633 
   3634   command = new ClientCommand();
   3635   command->set_set_sync_poll_interval(180);
   3636   command->set_set_sync_long_poll_interval(190);
   3637   command->set_sessions_commit_delay_seconds(2718);
   3638   bookmark_delay = command->add_custom_nudge_delays();
   3639   bookmark_delay->set_datatype_id(
   3640       GetSpecificsFieldNumberFromModelType(BOOKMARKS));
   3641   bookmark_delay->set_delay_ms(1050);
   3642   command->set_client_invalidation_hint_buffer_size(9);
   3643   mock_server_->AddUpdateDirectory(
   3644       1, 0, "in_root", 1, 1, foreign_cache_guid(), "-1");
   3645   mock_server_->SetGUClientCommand(command);
   3646   SyncShareNudge();
   3647 
   3648   EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_);
   3649   EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_);
   3650   EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_);
   3651   EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_);
   3652   EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_);
   3653 }
   3654 
   3655 TEST_F(SyncerTest, TestClientCommandDuringCommit) {
   3656   using sync_pb::ClientCommand;
   3657 
   3658   ClientCommand* command = new ClientCommand();
   3659   command->set_set_sync_poll_interval(8);
   3660   command->set_set_sync_long_poll_interval(800);
   3661   command->set_sessions_commit_delay_seconds(3141);
   3662   sync_pb::CustomNudgeDelay* bookmark_delay =
   3663       command->add_custom_nudge_delays();
   3664   bookmark_delay->set_datatype_id(
   3665       GetSpecificsFieldNumberFromModelType(BOOKMARKS));
   3666   bookmark_delay->set_delay_ms(950);
   3667   command->set_client_invalidation_hint_buffer_size(11);
   3668   CreateUnsyncedDirectory("X", "id_X");
   3669   mock_server_->SetCommitClientCommand(command);
   3670   SyncShareNudge();
   3671 
   3672   EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_);
   3673   EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_);
   3674   EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_);
   3675   EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_);
   3676   EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_);
   3677 
   3678   command = new ClientCommand();
   3679   command->set_set_sync_poll_interval(180);
   3680   command->set_set_sync_long_poll_interval(190);
   3681   command->set_sessions_commit_delay_seconds(2718);
   3682   bookmark_delay = command->add_custom_nudge_delays();
   3683   bookmark_delay->set_datatype_id(
   3684       GetSpecificsFieldNumberFromModelType(BOOKMARKS));
   3685   bookmark_delay->set_delay_ms(1050);
   3686   command->set_client_invalidation_hint_buffer_size(9);
   3687   CreateUnsyncedDirectory("Y", "id_Y");
   3688   mock_server_->SetCommitClientCommand(command);
   3689   SyncShareNudge();
   3690 
   3691   EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_);
   3692   EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_);
   3693   EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_);
   3694   EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_);
   3695   EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_);
   3696 }
   3697 
   3698 TEST_F(SyncerTest, EnsureWeSendUpOldParent) {
   3699   syncable::Id folder_one_id = ids_.FromNumber(1);
   3700   syncable::Id folder_two_id = ids_.FromNumber(2);
   3701 
   3702   mock_server_->AddUpdateDirectory(folder_one_id, TestIdFactory::root(),
   3703       "folder_one", 1, 1, foreign_cache_guid(), "-1");
   3704   mock_server_->AddUpdateDirectory(folder_two_id, TestIdFactory::root(),
   3705       "folder_two", 1, 1, foreign_cache_guid(), "-2");
   3706   SyncShareNudge();
   3707   {
   3708     // A moved entry should send an "old parent."
   3709     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3710     MutableEntry entry(&trans, GET_BY_ID, folder_one_id);
   3711     ASSERT_TRUE(entry.good());
   3712     entry.PutParentId(folder_two_id);
   3713     entry.PutIsUnsynced(true);
   3714     // A new entry should send no "old parent."
   3715     MutableEntry create(
   3716         &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder");
   3717     create.PutIsUnsynced(true);
   3718     create.PutSpecifics(DefaultBookmarkSpecifics());
   3719   }
   3720   SyncShareNudge();
   3721   const sync_pb::CommitMessage& commit = mock_server_->last_sent_commit();
   3722   ASSERT_EQ(2, commit.entries_size());
   3723   EXPECT_TRUE(commit.entries(0).parent_id_string() == "2");
   3724   EXPECT_TRUE(commit.entries(0).old_parent_id() == "0");
   3725   EXPECT_FALSE(commit.entries(1).has_old_parent_id());
   3726 }
   3727 
   3728 TEST_F(SyncerTest, Test64BitVersionSupport) {
   3729   int64 really_big_int = std::numeric_limits<int64>::max() - 12;
   3730   const string name("ringo's dang orang ran rings around my o-ring");
   3731   int64 item_metahandle;
   3732 
   3733   // Try writing max int64 to the version fields of a meta entry.
   3734   {
   3735     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3736     MutableEntry entry(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
   3737     ASSERT_TRUE(entry.good());
   3738     entry.PutBaseVersion(really_big_int);
   3739     entry.PutServerVersion(really_big_int);
   3740     entry.PutId(ids_.NewServerId());
   3741     item_metahandle = entry.GetMetahandle();
   3742   }
   3743   // Now read it back out and make sure the value is max int64.
   3744   syncable::ReadTransaction rtrans(FROM_HERE, directory());
   3745   Entry entry(&rtrans, syncable::GET_BY_HANDLE, item_metahandle);
   3746   ASSERT_TRUE(entry.good());
   3747   EXPECT_TRUE(really_big_int == entry.GetBaseVersion());
   3748 }
   3749 
   3750 TEST_F(SyncerTest, TestSimpleUndelete) {
   3751   Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
   3752   mock_server_->set_conflict_all_commits(true);
   3753   // Let there be an entry from the server.
   3754   mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
   3755                                   foreign_cache_guid(), "-1");
   3756   SyncShareNudge();
   3757   // Check it out and delete it.
   3758   {
   3759     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3760     MutableEntry entry(&wtrans, GET_BY_ID, id);
   3761     ASSERT_TRUE(entry.good());
   3762     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   3763     EXPECT_FALSE(entry.GetIsUnsynced());
   3764     EXPECT_FALSE(entry.GetIsDel());
   3765     // Delete it locally.
   3766     entry.PutIsDel(true);
   3767   }
   3768   SyncShareNudge();
   3769   // Confirm we see IS_DEL and not SERVER_IS_DEL.
   3770   {
   3771     syncable::ReadTransaction trans(FROM_HERE, directory());
   3772     Entry entry(&trans, GET_BY_ID, id);
   3773     ASSERT_TRUE(entry.good());
   3774     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   3775     EXPECT_FALSE(entry.GetIsUnsynced());
   3776     EXPECT_TRUE(entry.GetIsDel());
   3777     EXPECT_FALSE(entry.GetServerIsDel());
   3778   }
   3779   SyncShareNudge();
   3780   // Update from server confirming deletion.
   3781   mock_server_->AddUpdateBookmark(id, root, "foo", 2, 11,
   3782                                   foreign_cache_guid(), "-1");
   3783   mock_server_->SetLastUpdateDeleted();
   3784   SyncShareNudge();
   3785   // IS_DEL AND SERVER_IS_DEL now both true.
   3786   {
   3787     syncable::ReadTransaction trans(FROM_HERE, directory());
   3788     Entry entry(&trans, GET_BY_ID, id);
   3789     ASSERT_TRUE(entry.good());
   3790     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   3791     EXPECT_FALSE(entry.GetIsUnsynced());
   3792     EXPECT_TRUE(entry.GetIsDel());
   3793     EXPECT_TRUE(entry.GetServerIsDel());
   3794   }
   3795   // Undelete from server.
   3796   mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
   3797                                   foreign_cache_guid(), "-1");
   3798   SyncShareNudge();
   3799   // IS_DEL and SERVER_IS_DEL now both false.
   3800   {
   3801     syncable::ReadTransaction trans(FROM_HERE, directory());
   3802     Entry entry(&trans, GET_BY_ID, id);
   3803     ASSERT_TRUE(entry.good());
   3804     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   3805     EXPECT_FALSE(entry.GetIsUnsynced());
   3806     EXPECT_FALSE(entry.GetIsDel());
   3807     EXPECT_FALSE(entry.GetServerIsDel());
   3808   }
   3809 }
   3810 
   3811 TEST_F(SyncerTest, TestUndeleteWithMissingDeleteUpdate) {
   3812   Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
   3813   // Let there be a entry, from the server.
   3814   mock_server_->set_conflict_all_commits(true);
   3815   mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10,
   3816                                   foreign_cache_guid(), "-1");
   3817   SyncShareNudge();
   3818   // Check it out and delete it.
   3819   {
   3820     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   3821     MutableEntry entry(&wtrans, GET_BY_ID, id);
   3822     ASSERT_TRUE(entry.good());
   3823     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   3824     EXPECT_FALSE(entry.GetIsUnsynced());
   3825     EXPECT_FALSE(entry.GetIsDel());
   3826     // Delete it locally.
   3827     entry.PutIsDel(true);
   3828   }
   3829   SyncShareNudge();
   3830   // Confirm we see IS_DEL and not SERVER_IS_DEL.
   3831   {
   3832     syncable::ReadTransaction trans(FROM_HERE, directory());
   3833     Entry entry(&trans, GET_BY_ID, id);
   3834     ASSERT_TRUE(entry.good());
   3835     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   3836     EXPECT_FALSE(entry.GetIsUnsynced());
   3837     EXPECT_TRUE(entry.GetIsDel());
   3838     EXPECT_FALSE(entry.GetServerIsDel());
   3839   }
   3840   SyncShareNudge();
   3841   // Say we do not get an update from server confirming deletion. Undelete
   3842   // from server
   3843   mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12,
   3844                                   foreign_cache_guid(), "-1");
   3845   SyncShareNudge();
   3846   // IS_DEL and SERVER_IS_DEL now both false.
   3847   {
   3848     syncable::ReadTransaction trans(FROM_HERE, directory());
   3849     Entry entry(&trans, GET_BY_ID, id);
   3850     ASSERT_TRUE(entry.good());
   3851     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   3852     EXPECT_FALSE(entry.GetIsUnsynced());
   3853     EXPECT_FALSE(entry.GetIsDel());
   3854     EXPECT_FALSE(entry.GetServerIsDel());
   3855   }
   3856 }
   3857 
   3858 TEST_F(SyncerTest, TestUndeleteIgnoreCorrectlyUnappliedUpdate) {
   3859   Id id1 = ids_.MakeServer("first"), id2 = ids_.MakeServer("second");
   3860   Id root = TestIdFactory::root();
   3861   // Duplicate! expect path clashing!
   3862   mock_server_->set_conflict_all_commits(true);
   3863   mock_server_->AddUpdateBookmark(id1, root, "foo", 1, 10,
   3864                                   foreign_cache_guid(), "-1");
   3865   mock_server_->AddUpdateBookmark(id2, root, "foo", 1, 10,
   3866                                   foreign_cache_guid(), "-2");
   3867   SyncShareNudge();
   3868   mock_server_->AddUpdateBookmark(id2, root, "foo2", 2, 20,
   3869                                   foreign_cache_guid(), "-2");
   3870   SyncShareNudge();  // Now just don't explode.
   3871 }
   3872 
   3873 TEST_F(SyncerTest, ClientTagServerCreatedUpdatesWork) {
   3874   mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
   3875                                    foreign_cache_guid(), "-1");
   3876   mock_server_->SetLastUpdateClientTag("permfolder");
   3877 
   3878   SyncShareNudge();
   3879 
   3880   {
   3881     syncable::ReadTransaction trans(FROM_HERE, directory());
   3882     Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
   3883     ASSERT_TRUE(perm_folder.good());
   3884     EXPECT_FALSE(perm_folder.GetIsDel());
   3885     EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
   3886     EXPECT_FALSE(perm_folder.GetIsUnsynced());
   3887     EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
   3888     EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1");
   3889   }
   3890 
   3891   mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
   3892                                    foreign_cache_guid(), "-1");
   3893   mock_server_->SetLastUpdateClientTag("permfolder");
   3894   SyncShareNudge();
   3895 
   3896   {
   3897     syncable::ReadTransaction trans(FROM_HERE, directory());
   3898 
   3899     Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
   3900     ASSERT_TRUE(perm_folder.good());
   3901     EXPECT_FALSE(perm_folder.GetIsDel());
   3902     EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
   3903     EXPECT_FALSE(perm_folder.GetIsUnsynced());
   3904     EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
   3905     EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem_renamed");
   3906   }
   3907 }
   3908 
   3909 TEST_F(SyncerTest, ClientTagIllegalUpdateIgnored) {
   3910   mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10,
   3911                                    foreign_cache_guid(), "-1");
   3912   mock_server_->SetLastUpdateClientTag("permfolder");
   3913 
   3914   SyncShareNudge();
   3915 
   3916   {
   3917     syncable::ReadTransaction trans(FROM_HERE, directory());
   3918     Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
   3919     ASSERT_TRUE(perm_folder.good());
   3920     EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
   3921     EXPECT_FALSE(perm_folder.GetIsUnsynced());
   3922     EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder");
   3923     EXPECT_TRUE(perm_folder.GetNonUniqueName()== "permitem1");
   3924     EXPECT_TRUE(perm_folder.GetId().ServerKnows());
   3925   }
   3926 
   3927   mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100,
   3928                                    foreign_cache_guid(), "-1");
   3929   mock_server_->SetLastUpdateClientTag("wrongtag");
   3930   SyncShareNudge();
   3931 
   3932   {
   3933     syncable::ReadTransaction trans(FROM_HERE, directory());
   3934 
   3935     // This update is rejected because it has the same ID, but a
   3936     // different tag than one that is already on the client.
   3937     // The client has a ServerKnows ID, which cannot be overwritten.
   3938     Entry rejected_update(&trans, GET_BY_CLIENT_TAG, "wrongtag");
   3939     EXPECT_FALSE(rejected_update.good());
   3940 
   3941     Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
   3942     ASSERT_TRUE(perm_folder.good());
   3943     EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
   3944     EXPECT_FALSE(perm_folder.GetIsUnsynced());
   3945     EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1");
   3946   }
   3947 }
   3948 
   3949 TEST_F(SyncerTest, ClientTagUncommittedTagMatchesUpdate) {
   3950   int64 original_metahandle = 0;
   3951 
   3952   {
   3953     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   3954     MutableEntry pref(
   3955         &trans, CREATE, PREFERENCES, ids_.root(), "name");
   3956     ASSERT_TRUE(pref.good());
   3957     pref.PutUniqueClientTag("tag");
   3958     pref.PutIsUnsynced(true);
   3959     EXPECT_FALSE(pref.GetIsUnappliedUpdate());
   3960     EXPECT_FALSE(pref.GetId().ServerKnows());
   3961     original_metahandle = pref.GetMetahandle();
   3962   }
   3963 
   3964   syncable::Id server_id = TestIdFactory::MakeServer("id");
   3965   mock_server_->AddUpdatePref(server_id.GetServerId(),
   3966                               ids_.root().GetServerId(),
   3967                               "tag", 10, 100);
   3968   mock_server_->set_conflict_all_commits(true);
   3969 
   3970   SyncShareNudge();
   3971   // This should cause client tag reunion, preserving the metahandle.
   3972   {
   3973     syncable::ReadTransaction trans(FROM_HERE, directory());
   3974 
   3975     Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
   3976     ASSERT_TRUE(pref.good());
   3977     EXPECT_FALSE(pref.GetIsDel());
   3978     EXPECT_FALSE(pref.GetIsUnappliedUpdate());
   3979     EXPECT_TRUE(pref.GetIsUnsynced());
   3980     EXPECT_EQ(10, pref.GetBaseVersion());
   3981     // Entry should have been given the new ID while preserving the
   3982     // metahandle; client should have won the conflict resolution.
   3983     EXPECT_EQ(original_metahandle, pref.GetMetahandle());
   3984     EXPECT_EQ("tag", pref.GetUniqueClientTag());
   3985     EXPECT_TRUE(pref.GetId().ServerKnows());
   3986   }
   3987 
   3988   mock_server_->set_conflict_all_commits(false);
   3989   SyncShareNudge();
   3990 
   3991   // The resolved entry ought to commit cleanly.
   3992   {
   3993     syncable::ReadTransaction trans(FROM_HERE, directory());
   3994 
   3995     Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
   3996     ASSERT_TRUE(pref.good());
   3997     EXPECT_FALSE(pref.GetIsDel());
   3998     EXPECT_FALSE(pref.GetIsUnappliedUpdate());
   3999     EXPECT_FALSE(pref.GetIsUnsynced());
   4000     EXPECT_TRUE(10 < pref.GetBaseVersion());
   4001     // Entry should have been given the new ID while preserving the
   4002     // metahandle; client should have won the conflict resolution.
   4003     EXPECT_EQ(original_metahandle, pref.GetMetahandle());
   4004     EXPECT_EQ("tag", pref.GetUniqueClientTag());
   4005     EXPECT_TRUE(pref.GetId().ServerKnows());
   4006   }
   4007 }
   4008 
   4009 TEST_F(SyncerTest, ClientTagConflictWithDeletedLocalEntry) {
   4010   {
   4011     // Create a deleted local entry with a unique client tag.
   4012     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   4013     MutableEntry pref(
   4014         &trans, CREATE, PREFERENCES, ids_.root(), "name");
   4015     ASSERT_TRUE(pref.good());
   4016     ASSERT_FALSE(pref.GetId().ServerKnows());
   4017     pref.PutUniqueClientTag("tag");
   4018     pref.PutIsUnsynced(true);
   4019 
   4020     // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit.
   4021     // (We never attempt to commit server-unknown deleted items, so this
   4022     // helps us clean up those entries).
   4023     pref.PutIsDel(true);
   4024   }
   4025 
   4026   // Prepare an update with the same unique client tag.
   4027   syncable::Id server_id = TestIdFactory::MakeServer("id");
   4028   mock_server_->AddUpdatePref(server_id.GetServerId(),
   4029                               ids_.root().GetServerId(),
   4030                               "tag", 10, 100);
   4031 
   4032   SyncShareNudge();
   4033   // The local entry will be overwritten.
   4034   {
   4035     syncable::ReadTransaction trans(FROM_HERE, directory());
   4036 
   4037     Entry pref(&trans, GET_BY_CLIENT_TAG, "tag");
   4038     ASSERT_TRUE(pref.good());
   4039     ASSERT_TRUE(pref.GetId().ServerKnows());
   4040     EXPECT_FALSE(pref.GetIsDel());
   4041     EXPECT_FALSE(pref.GetIsUnappliedUpdate());
   4042     EXPECT_FALSE(pref.GetIsUnsynced());
   4043     EXPECT_EQ(pref.GetBaseVersion(), 10);
   4044     EXPECT_EQ(pref.GetUniqueClientTag(), "tag");
   4045   }
   4046 }
   4047 
   4048 TEST_F(SyncerTest, ClientTagUpdateClashesWithLocalEntry) {
   4049   // This test is written assuming that ID comparison
   4050   // will work out in a particular way.
   4051   EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(2));
   4052   EXPECT_TRUE(ids_.FromNumber(3) < ids_.FromNumber(4));
   4053 
   4054   syncable::Id id1 = TestIdFactory::MakeServer("1");
   4055   mock_server_->AddUpdatePref(id1.GetServerId(), ids_.root().GetServerId(),
   4056                               "tag1", 10, 100);
   4057 
   4058   syncable::Id id4 = TestIdFactory::MakeServer("4");
   4059   mock_server_->AddUpdatePref(id4.GetServerId(), ids_.root().GetServerId(),
   4060                               "tag2", 11, 110);
   4061 
   4062   mock_server_->set_conflict_all_commits(true);
   4063 
   4064   SyncShareNudge();
   4065   int64 tag1_metahandle = syncable::kInvalidMetaHandle;
   4066   int64 tag2_metahandle = syncable::kInvalidMetaHandle;
   4067   // This should cause client tag overwrite.
   4068   {
   4069     syncable::ReadTransaction trans(FROM_HERE, directory());
   4070 
   4071     Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
   4072     ASSERT_TRUE(tag1.good());
   4073     ASSERT_TRUE(tag1.GetId().ServerKnows());
   4074     ASSERT_TRUE(id1 == tag1.GetId());
   4075     EXPECT_FALSE(tag1.GetIsDel());
   4076     EXPECT_FALSE(tag1.GetIsUnappliedUpdate());
   4077     EXPECT_FALSE(tag1.GetIsUnsynced());
   4078     EXPECT_EQ(10, tag1.GetBaseVersion());
   4079     EXPECT_EQ("tag1", tag1.GetUniqueClientTag());
   4080     tag1_metahandle = tag1.GetMetahandle();
   4081 
   4082     Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
   4083     ASSERT_TRUE(tag2.good());
   4084     ASSERT_TRUE(tag2.GetId().ServerKnows());
   4085     ASSERT_TRUE(id4 == tag2.GetId());
   4086     EXPECT_FALSE(tag2.GetIsDel());
   4087     EXPECT_FALSE(tag2.GetIsUnappliedUpdate());
   4088     EXPECT_FALSE(tag2.GetIsUnsynced());
   4089     EXPECT_EQ(11, tag2.GetBaseVersion());
   4090     EXPECT_EQ("tag2", tag2.GetUniqueClientTag());
   4091     tag2_metahandle = tag2.GetMetahandle();
   4092 
   4093     syncable::Directory::Metahandles children;
   4094     directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
   4095     ASSERT_EQ(2U, children.size());
   4096   }
   4097 
   4098   syncable::Id id2 = TestIdFactory::MakeServer("2");
   4099   mock_server_->AddUpdatePref(id2.GetServerId(), ids_.root().GetServerId(),
   4100                               "tag1", 12, 120);
   4101   syncable::Id id3 = TestIdFactory::MakeServer("3");
   4102   mock_server_->AddUpdatePref(id3.GetServerId(), ids_.root().GetServerId(),
   4103                               "tag2", 13, 130);
   4104   SyncShareNudge();
   4105 
   4106   {
   4107     syncable::ReadTransaction trans(FROM_HERE, directory());
   4108 
   4109     Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
   4110     ASSERT_TRUE(tag1.good());
   4111     ASSERT_TRUE(tag1.GetId().ServerKnows());
   4112     ASSERT_EQ(id1, tag1.GetId())
   4113         << "ID 1 should be kept, since it was less than ID 2.";
   4114     EXPECT_FALSE(tag1.GetIsDel());
   4115     EXPECT_FALSE(tag1.GetIsUnappliedUpdate());
   4116     EXPECT_FALSE(tag1.GetIsUnsynced());
   4117     EXPECT_EQ(10, tag1.GetBaseVersion());
   4118     EXPECT_EQ("tag1", tag1.GetUniqueClientTag());
   4119     EXPECT_EQ(tag1_metahandle, tag1.GetMetahandle());
   4120 
   4121     Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
   4122     ASSERT_TRUE(tag2.good());
   4123     ASSERT_TRUE(tag2.GetId().ServerKnows());
   4124     ASSERT_EQ(id3, tag2.GetId())
   4125         << "ID 3 should be kept, since it was less than ID 4.";
   4126     EXPECT_FALSE(tag2.GetIsDel());
   4127     EXPECT_FALSE(tag2.GetIsUnappliedUpdate());
   4128     EXPECT_FALSE(tag2.GetIsUnsynced());
   4129     EXPECT_EQ(13, tag2.GetBaseVersion());
   4130     EXPECT_EQ("tag2", tag2.GetUniqueClientTag());
   4131     EXPECT_EQ(tag2_metahandle, tag2.GetMetahandle());
   4132 
   4133     syncable::Directory::Metahandles children;
   4134     directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
   4135     ASSERT_EQ(2U, children.size());
   4136   }
   4137 }
   4138 
   4139 TEST_F(SyncerTest, ClientTagClashWithinBatchOfUpdates) {
   4140   // This test is written assuming that ID comparison
   4141   // will work out in a particular way.
   4142   EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(4));
   4143   EXPECT_TRUE(ids_.FromNumber(201) < ids_.FromNumber(205));
   4144 
   4145   // Least ID: winner.
   4146   mock_server_->AddUpdatePref(ids_.FromNumber(1).GetServerId(),
   4147                               ids_.root().GetServerId(), "tag a", 1, 10);
   4148   mock_server_->AddUpdatePref(ids_.FromNumber(2).GetServerId(),
   4149                               ids_.root().GetServerId(), "tag a", 11, 110);
   4150   mock_server_->AddUpdatePref(ids_.FromNumber(3).GetServerId(),
   4151                               ids_.root().GetServerId(), "tag a", 12, 120);
   4152   mock_server_->AddUpdatePref(ids_.FromNumber(4).GetServerId(),
   4153                               ids_.root().GetServerId(), "tag a", 13, 130);
   4154 
   4155   mock_server_->AddUpdatePref(ids_.FromNumber(105).GetServerId(),
   4156                               ids_.root().GetServerId(), "tag b", 14, 140);
   4157   mock_server_->AddUpdatePref(ids_.FromNumber(102).GetServerId(),
   4158                               ids_.root().GetServerId(), "tag b", 15, 150);
   4159   // Least ID: winner.
   4160   mock_server_->AddUpdatePref(ids_.FromNumber(101).GetServerId(),
   4161                               ids_.root().GetServerId(), "tag b", 16, 160);
   4162   mock_server_->AddUpdatePref(ids_.FromNumber(104).GetServerId(),
   4163                               ids_.root().GetServerId(), "tag b", 17, 170);
   4164 
   4165   mock_server_->AddUpdatePref(ids_.FromNumber(205).GetServerId(),
   4166                               ids_.root().GetServerId(), "tag c", 18, 180);
   4167   mock_server_->AddUpdatePref(ids_.FromNumber(202).GetServerId(),
   4168                               ids_.root().GetServerId(), "tag c", 19, 190);
   4169   mock_server_->AddUpdatePref(ids_.FromNumber(204).GetServerId(),
   4170                               ids_.root().GetServerId(), "tag c", 20, 200);
   4171   // Least ID: winner.
   4172   mock_server_->AddUpdatePref(ids_.FromNumber(201).GetServerId(),
   4173                               ids_.root().GetServerId(), "tag c", 21, 210);
   4174 
   4175   mock_server_->set_conflict_all_commits(true);
   4176 
   4177   SyncShareNudge();
   4178   // This should cause client tag overwrite.
   4179   {
   4180     syncable::ReadTransaction trans(FROM_HERE, directory());
   4181 
   4182     Entry tag_a(&trans, GET_BY_CLIENT_TAG, "tag a");
   4183     ASSERT_TRUE(tag_a.good());
   4184     EXPECT_TRUE(tag_a.GetId().ServerKnows());
   4185     EXPECT_EQ(ids_.FromNumber(1), tag_a.GetId());
   4186     EXPECT_FALSE(tag_a.GetIsDel());
   4187     EXPECT_FALSE(tag_a.GetIsUnappliedUpdate());
   4188     EXPECT_FALSE(tag_a.GetIsUnsynced());
   4189     EXPECT_EQ(1, tag_a.GetBaseVersion());
   4190     EXPECT_EQ("tag a", tag_a.GetUniqueClientTag());
   4191 
   4192     Entry tag_b(&trans, GET_BY_CLIENT_TAG, "tag b");
   4193     ASSERT_TRUE(tag_b.good());
   4194     EXPECT_TRUE(tag_b.GetId().ServerKnows());
   4195     EXPECT_EQ(ids_.FromNumber(101), tag_b.GetId());
   4196     EXPECT_FALSE(tag_b.GetIsDel());
   4197     EXPECT_FALSE(tag_b.GetIsUnappliedUpdate());
   4198     EXPECT_FALSE(tag_b.GetIsUnsynced());
   4199     EXPECT_EQ(16, tag_b.GetBaseVersion());
   4200     EXPECT_EQ("tag b", tag_b.GetUniqueClientTag());
   4201 
   4202     Entry tag_c(&trans, GET_BY_CLIENT_TAG, "tag c");
   4203     ASSERT_TRUE(tag_c.good());
   4204     EXPECT_TRUE(tag_c.GetId().ServerKnows());
   4205     EXPECT_EQ(ids_.FromNumber(201), tag_c.GetId());
   4206     EXPECT_FALSE(tag_c.GetIsDel());
   4207     EXPECT_FALSE(tag_c.GetIsUnappliedUpdate());
   4208     EXPECT_FALSE(tag_c.GetIsUnsynced());
   4209     EXPECT_EQ(21, tag_c.GetBaseVersion());
   4210     EXPECT_EQ("tag c", tag_c.GetUniqueClientTag());
   4211 
   4212     syncable::Directory::Metahandles children;
   4213     directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
   4214     ASSERT_EQ(3U, children.size());
   4215   }
   4216 }
   4217 
   4218 TEST_F(SyncerTest, UniqueServerTagUpdates) {
   4219   // As a hurdle, introduce an item whose name is the same as the tag value
   4220   // we'll use later.
   4221   int64 hurdle_handle = CreateUnsyncedDirectory("bob", "id_bob");
   4222   {
   4223     syncable::ReadTransaction trans(FROM_HERE, directory());
   4224     Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
   4225     ASSERT_TRUE(hurdle.good());
   4226     ASSERT_TRUE(!hurdle.GetIsDel());
   4227     ASSERT_TRUE(hurdle.GetUniqueServerTag().empty());
   4228     ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob");
   4229 
   4230     // Try to lookup by the tagname.  These should fail.
   4231     Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
   4232     EXPECT_FALSE(tag_alpha.good());
   4233     Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
   4234     EXPECT_FALSE(tag_bob.good());
   4235   }
   4236 
   4237   // Now download some tagged items as updates.
   4238   mock_server_->AddUpdateDirectory(
   4239       1, 0, "update1", 1, 10, std::string(), std::string());
   4240   mock_server_->SetLastUpdateServerTag("alpha");
   4241   mock_server_->AddUpdateDirectory(
   4242       2, 0, "update2", 2, 20, std::string(), std::string());
   4243   mock_server_->SetLastUpdateServerTag("bob");
   4244   SyncShareNudge();
   4245 
   4246   {
   4247     syncable::ReadTransaction trans(FROM_HERE, directory());
   4248 
   4249     // The new items should be applied as new entries, and we should be able
   4250     // to look them up by their tag values.
   4251     Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
   4252     ASSERT_TRUE(tag_alpha.good());
   4253     ASSERT_TRUE(!tag_alpha.GetIsDel());
   4254     ASSERT_TRUE(tag_alpha.GetUniqueServerTag()== "alpha");
   4255     ASSERT_TRUE(tag_alpha.GetNonUniqueName()== "update1");
   4256     Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
   4257     ASSERT_TRUE(tag_bob.good());
   4258     ASSERT_TRUE(!tag_bob.GetIsDel());
   4259     ASSERT_TRUE(tag_bob.GetUniqueServerTag()== "bob");
   4260     ASSERT_TRUE(tag_bob.GetNonUniqueName()== "update2");
   4261     // The old item should be unchanged.
   4262     Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
   4263     ASSERT_TRUE(hurdle.good());
   4264     ASSERT_TRUE(!hurdle.GetIsDel());
   4265     ASSERT_TRUE(hurdle.GetUniqueServerTag().empty());
   4266     ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob");
   4267   }
   4268 }
   4269 
   4270 TEST_F(SyncerTest, GetUpdatesSetsRequestedTypes) {
   4271   // The expectations of this test happen in the MockConnectionManager's
   4272   // GetUpdates handler.  EnableDatatype sets the expectation value from our
   4273   // set of enabled/disabled datatypes.
   4274   EnableDatatype(BOOKMARKS);
   4275   SyncShareNudge();
   4276   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4277 
   4278   EnableDatatype(AUTOFILL);
   4279   SyncShareNudge();
   4280   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4281 
   4282   EnableDatatype(PREFERENCES);
   4283   SyncShareNudge();
   4284   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4285 
   4286   DisableDatatype(BOOKMARKS);
   4287   SyncShareNudge();
   4288   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4289 
   4290   DisableDatatype(AUTOFILL);
   4291   SyncShareNudge();
   4292   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4293 
   4294   DisableDatatype(PREFERENCES);
   4295   EnableDatatype(AUTOFILL);
   4296   SyncShareNudge();
   4297   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4298 }
   4299 
   4300 // A typical scenario: server and client each have one update for the other.
   4301 // This is the "happy path" alternative to UpdateFailsThenDontCommit.
   4302 TEST_F(SyncerTest, UpdateThenCommit) {
   4303   syncable::Id to_receive = ids_.NewServerId();
   4304   syncable::Id to_commit = ids_.NewLocalId();
   4305 
   4306   mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
   4307                                    foreign_cache_guid(), "-1");
   4308   int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
   4309   SyncShareNudge();
   4310 
   4311   // The sync cycle should have included a GetUpdate, then a commit.  By the
   4312   // time the commit happened, we should have known for sure that there were no
   4313   // hierarchy conflicts, and reported this fact to the server.
   4314   ASSERT_TRUE(mock_server_->last_request().has_commit());
   4315   VerifyNoHierarchyConflictsReported(mock_server_->last_request());
   4316 
   4317   syncable::ReadTransaction trans(FROM_HERE, directory());
   4318 
   4319   Entry received(&trans, GET_BY_ID, to_receive);
   4320   ASSERT_TRUE(received.good());
   4321   EXPECT_FALSE(received.GetIsUnsynced());
   4322   EXPECT_FALSE(received.GetIsUnappliedUpdate());
   4323 
   4324   Entry committed(&trans, GET_BY_HANDLE, commit_handle);
   4325   ASSERT_TRUE(committed.good());
   4326   EXPECT_FALSE(committed.GetIsUnsynced());
   4327   EXPECT_FALSE(committed.GetIsUnappliedUpdate());
   4328 }
   4329 
   4330 // Same as above, but this time we fail to download updates.
   4331 // We should not attempt to commit anything unless we successfully downloaded
   4332 // updates, otherwise we risk causing a server-side conflict.
   4333 TEST_F(SyncerTest, UpdateFailsThenDontCommit) {
   4334   syncable::Id to_receive = ids_.NewServerId();
   4335   syncable::Id to_commit = ids_.NewLocalId();
   4336 
   4337   mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10,
   4338                                    foreign_cache_guid(), "-1");
   4339   int64 commit_handle = CreateUnsyncedDirectory("y", to_commit);
   4340   mock_server_->FailNextPostBufferToPathCall();
   4341   SyncShareNudge();
   4342 
   4343   syncable::ReadTransaction trans(FROM_HERE, directory());
   4344 
   4345   // We did not receive this update.
   4346   Entry received(&trans, GET_BY_ID, to_receive);
   4347   ASSERT_FALSE(received.good());
   4348 
   4349   // And our local update remains unapplied.
   4350   Entry committed(&trans, GET_BY_HANDLE, commit_handle);
   4351   ASSERT_TRUE(committed.good());
   4352   EXPECT_TRUE(committed.GetIsUnsynced());
   4353   EXPECT_FALSE(committed.GetIsUnappliedUpdate());
   4354 
   4355   // Inform the Mock we won't be fetching all updates.
   4356   mock_server_->ClearUpdatesQueue();
   4357 }
   4358 
   4359 // Downloads two updates and applies them successfully.
   4360 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates.
   4361 TEST_F(SyncerTest, ConfigureDownloadsTwoBatchesSuccess) {
   4362   syncable::Id node1 = ids_.NewServerId();
   4363   syncable::Id node2 = ids_.NewServerId();
   4364 
   4365   // Construct the first GetUpdates response.
   4366   mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
   4367                                    foreign_cache_guid(), "-2");
   4368   mock_server_->SetChangesRemaining(1);
   4369   mock_server_->NextUpdateBatch();
   4370 
   4371   // Construct the second GetUpdates response.
   4372   mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
   4373                                    foreign_cache_guid(), "-2");
   4374 
   4375   SyncShareConfigure();
   4376 
   4377   syncable::ReadTransaction trans(FROM_HERE, directory());
   4378   // Both nodes should be downloaded and applied.
   4379 
   4380   Entry n1(&trans, GET_BY_ID, node1);
   4381   ASSERT_TRUE(n1.good());
   4382   EXPECT_FALSE(n1.GetIsUnappliedUpdate());
   4383 
   4384   Entry n2(&trans, GET_BY_ID, node2);
   4385   ASSERT_TRUE(n2.good());
   4386   EXPECT_FALSE(n2.GetIsUnappliedUpdate());
   4387 }
   4388 
   4389 // Same as the above case, but this time the second batch fails to download.
   4390 TEST_F(SyncerTest, ConfigureFailsDontApplyUpdates) {
   4391   syncable::Id node1 = ids_.NewServerId();
   4392   syncable::Id node2 = ids_.NewServerId();
   4393 
   4394   // The scenario: we have two batches of updates with one update each.  A
   4395   // normal confgure step would download all the updates one batch at a time and
   4396   // apply them.  This configure will succeed in downloading the first batch
   4397   // then fail when downloading the second.
   4398   mock_server_->FailNthPostBufferToPathCall(2);
   4399 
   4400   // Construct the first GetUpdates response.
   4401   mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10,
   4402                                    foreign_cache_guid(), "-1");
   4403   mock_server_->SetChangesRemaining(1);
   4404   mock_server_->NextUpdateBatch();
   4405 
   4406   // Consutrct the second GetUpdates response.
   4407   mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20,
   4408                                    foreign_cache_guid(), "-2");
   4409 
   4410   SyncShareConfigure();
   4411 
   4412   syncable::ReadTransaction trans(FROM_HERE, directory());
   4413 
   4414   // The first node was downloaded, but not applied.
   4415   Entry n1(&trans, GET_BY_ID, node1);
   4416   ASSERT_TRUE(n1.good());
   4417   EXPECT_TRUE(n1.GetIsUnappliedUpdate());
   4418 
   4419   // The second node was not downloaded.
   4420   Entry n2(&trans, GET_BY_ID, node2);
   4421   EXPECT_FALSE(n2.good());
   4422 
   4423   // One update remains undownloaded.
   4424   mock_server_->ClearUpdatesQueue();
   4425 }
   4426 
   4427 TEST_F(SyncerTest, GetKeySuccess) {
   4428   {
   4429     syncable::ReadTransaction rtrans(FROM_HERE, directory());
   4430     EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
   4431   }
   4432 
   4433   SyncShareConfigure();
   4434 
   4435   EXPECT_EQ(session_->status_controller().last_get_key_result(), SYNCER_OK);
   4436   {
   4437     syncable::ReadTransaction rtrans(FROM_HERE, directory());
   4438     EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
   4439   }
   4440 }
   4441 
   4442 TEST_F(SyncerTest, GetKeyEmpty) {
   4443   {
   4444     syncable::ReadTransaction rtrans(FROM_HERE, directory());
   4445     EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
   4446   }
   4447 
   4448   mock_server_->SetKeystoreKey(std::string());
   4449   SyncShareConfigure();
   4450 
   4451   EXPECT_NE(session_->status_controller().last_get_key_result(), SYNCER_OK);
   4452   {
   4453     syncable::ReadTransaction rtrans(FROM_HERE, directory());
   4454     EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans));
   4455   }
   4456 }
   4457 
   4458 // Test what happens if a client deletes, then recreates, an object very
   4459 // quickly.  It is possible that the deletion gets sent as a commit, and
   4460 // the undelete happens during the commit request.  The principle here
   4461 // is that with a single committing client, conflicts should never
   4462 // be encountered, and a client encountering its past actions during
   4463 // getupdates should never feed back to override later actions.
   4464 //
   4465 // In cases of ordering A-F below, the outcome should be the same.
   4466 //   Exercised by UndeleteDuringCommit:
   4467 //     A. Delete - commit - undelete - commitresponse.
   4468 //     B. Delete - commit - undelete - commitresponse - getupdates.
   4469 //   Exercised by UndeleteBeforeCommit:
   4470 //     C. Delete - undelete - commit - commitresponse.
   4471 //     D. Delete - undelete - commit - commitresponse - getupdates.
   4472 //   Exercised by UndeleteAfterCommit:
   4473 //     E. Delete - commit - commitresponse - undelete - commit
   4474 //        - commitresponse.
   4475 //     F. Delete - commit - commitresponse - undelete - commit -
   4476 //        - commitresponse - getupdates.
   4477 class SyncerUndeletionTest : public SyncerTest {
   4478  public:
   4479   SyncerUndeletionTest()
   4480       : client_tag_("foobar"),
   4481         metahandle_(syncable::kInvalidMetaHandle) {
   4482   }
   4483 
   4484   void Create() {
   4485     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   4486     MutableEntry perm_folder(
   4487         &trans, CREATE, BOOKMARKS, ids_.root(), "clientname");
   4488     ASSERT_TRUE(perm_folder.good());
   4489     perm_folder.PutUniqueClientTag(client_tag_);
   4490     perm_folder.PutIsUnsynced(true);
   4491     perm_folder.PutSyncing(false);
   4492     perm_folder.PutSpecifics(DefaultBookmarkSpecifics());
   4493     EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate());
   4494     EXPECT_FALSE(perm_folder.GetId().ServerKnows());
   4495     metahandle_ = perm_folder.GetMetahandle();
   4496     local_id_ = perm_folder.GetId();
   4497   }
   4498 
   4499   void Delete() {
   4500     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   4501     MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
   4502     ASSERT_TRUE(entry.good());
   4503     EXPECT_EQ(metahandle_, entry.GetMetahandle());
   4504     entry.PutIsDel(true);
   4505     entry.PutIsUnsynced(true);
   4506     entry.PutSyncing(false);
   4507   }
   4508 
   4509   void Undelete() {
   4510     WriteTransaction trans(FROM_HERE, UNITTEST, directory());
   4511     MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
   4512     ASSERT_TRUE(entry.good());
   4513     EXPECT_EQ(metahandle_, entry.GetMetahandle());
   4514     EXPECT_TRUE(entry.GetIsDel());
   4515     entry.PutIsDel(false);
   4516     entry.PutIsUnsynced(true);
   4517     entry.PutSyncing(false);
   4518   }
   4519 
   4520   int64 GetMetahandleOfTag() {
   4521     syncable::ReadTransaction trans(FROM_HERE, directory());
   4522     Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
   4523     EXPECT_TRUE(entry.good());
   4524     if (!entry.good()) {
   4525       return syncable::kInvalidMetaHandle;
   4526     }
   4527     return entry.GetMetahandle();
   4528   }
   4529 
   4530   void ExpectUnsyncedCreation() {
   4531     syncable::ReadTransaction trans(FROM_HERE, directory());
   4532     Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
   4533 
   4534     EXPECT_EQ(metahandle_, entry.GetMetahandle());
   4535     EXPECT_FALSE(entry.GetIsDel());
   4536     EXPECT_FALSE(entry.GetServerIsDel());  // Never been committed.
   4537     EXPECT_GE(0, entry.GetBaseVersion());
   4538     EXPECT_TRUE(entry.GetIsUnsynced());
   4539     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   4540   }
   4541 
   4542   void ExpectUnsyncedUndeletion() {
   4543     syncable::ReadTransaction trans(FROM_HERE, directory());
   4544     Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
   4545 
   4546     EXPECT_EQ(metahandle_, entry.GetMetahandle());
   4547     EXPECT_FALSE(entry.GetIsDel());
   4548     EXPECT_TRUE(entry.GetServerIsDel());
   4549     EXPECT_EQ(0, entry.GetBaseVersion());
   4550     EXPECT_TRUE(entry.GetIsUnsynced());
   4551     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   4552     EXPECT_TRUE(entry.GetId().ServerKnows());
   4553   }
   4554 
   4555   void ExpectUnsyncedEdit() {
   4556     syncable::ReadTransaction trans(FROM_HERE, directory());
   4557     Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
   4558 
   4559     EXPECT_EQ(metahandle_, entry.GetMetahandle());
   4560     EXPECT_FALSE(entry.GetIsDel());
   4561     EXPECT_FALSE(entry.GetServerIsDel());
   4562     EXPECT_LT(0, entry.GetBaseVersion());
   4563     EXPECT_TRUE(entry.GetIsUnsynced());
   4564     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   4565     EXPECT_TRUE(entry.GetId().ServerKnows());
   4566   }
   4567 
   4568   void ExpectUnsyncedDeletion() {
   4569     syncable::ReadTransaction trans(FROM_HERE, directory());
   4570     Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
   4571 
   4572     EXPECT_EQ(metahandle_, entry.GetMetahandle());
   4573     EXPECT_TRUE(entry.GetIsDel());
   4574     EXPECT_FALSE(entry.GetServerIsDel());
   4575     EXPECT_TRUE(entry.GetIsUnsynced());
   4576     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   4577     EXPECT_LT(0, entry.GetBaseVersion());
   4578     EXPECT_LT(0, entry.GetServerVersion());
   4579   }
   4580 
   4581   void ExpectSyncedAndCreated() {
   4582     syncable::ReadTransaction trans(FROM_HERE, directory());
   4583     Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
   4584 
   4585     EXPECT_EQ(metahandle_, entry.GetMetahandle());
   4586     EXPECT_FALSE(entry.GetIsDel());
   4587     EXPECT_FALSE(entry.GetServerIsDel());
   4588     EXPECT_LT(0, entry.GetBaseVersion());
   4589     EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion());
   4590     EXPECT_FALSE(entry.GetIsUnsynced());
   4591     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   4592   }
   4593 
   4594   void ExpectSyncedAndDeleted() {
   4595     syncable::ReadTransaction trans(FROM_HERE, directory());
   4596     Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
   4597 
   4598     EXPECT_EQ(metahandle_, entry.GetMetahandle());
   4599     EXPECT_TRUE(entry.GetIsDel());
   4600     EXPECT_TRUE(entry.GetServerIsDel());
   4601     EXPECT_FALSE(entry.GetIsUnsynced());
   4602     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   4603     EXPECT_GE(0, entry.GetBaseVersion());
   4604     EXPECT_GE(0, entry.GetServerVersion());
   4605   }
   4606 
   4607  protected:
   4608   const std::string client_tag_;
   4609   syncable::Id local_id_;
   4610   int64 metahandle_;
   4611 };
   4612 
   4613 TEST_F(SyncerUndeletionTest, UndeleteDuringCommit) {
   4614   Create();
   4615   ExpectUnsyncedCreation();
   4616   SyncShareNudge();
   4617 
   4618   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4619   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4620   ExpectSyncedAndCreated();
   4621 
   4622   // Delete, begin committing the delete, then undelete while committing.
   4623   Delete();
   4624   ExpectUnsyncedDeletion();
   4625   mock_server_->SetMidCommitCallback(
   4626       base::Bind(&SyncerUndeletionTest::Undelete, base::Unretained(this)));
   4627   SyncShareNudge();
   4628 
   4629   // We will continue to commit until all nodes are synced, so we expect
   4630   // that both the delete and following undelete were committed.  We haven't
   4631   // downloaded any updates, though, so the SERVER fields will be the same
   4632   // as they were at the start of the cycle.
   4633   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4634   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4635 
   4636   {
   4637     syncable::ReadTransaction trans(FROM_HERE, directory());
   4638     Entry entry(&trans, GET_BY_HANDLE, metahandle_);
   4639 
   4640     // Server fields lag behind.
   4641     EXPECT_FALSE(entry.GetServerIsDel());
   4642 
   4643     // We have committed the second (undelete) update.
   4644     EXPECT_FALSE(entry.GetIsDel());
   4645     EXPECT_FALSE(entry.GetIsUnsynced());
   4646     EXPECT_FALSE(entry.GetIsUnappliedUpdate());
   4647   }
   4648 
   4649   // Now, encounter a GetUpdates corresponding to the deletion from
   4650   // the server.  The undeletion should prevail again and be committed.
   4651   // None of this should trigger any conflict detection -- it is perfectly
   4652   // normal to recieve updates from our own commits.
   4653   mock_server_->SetMidCommitCallback(base::Closure());
   4654   sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
   4655   update->set_originator_cache_guid(local_cache_guid());
   4656   update->set_originator_client_item_id(local_id_.GetServerId());
   4657 
   4658   SyncShareNudge();
   4659   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4660   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4661   ExpectSyncedAndCreated();
   4662 }
   4663 
   4664 TEST_F(SyncerUndeletionTest, UndeleteBeforeCommit) {
   4665   Create();
   4666   ExpectUnsyncedCreation();
   4667   SyncShareNudge();
   4668 
   4669   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4670   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4671   ExpectSyncedAndCreated();
   4672 
   4673   // Delete and undelete, then sync to pick up the result.
   4674   Delete();
   4675   ExpectUnsyncedDeletion();
   4676   Undelete();
   4677   ExpectUnsyncedEdit();  // Edit, not undelete: server thinks it exists.
   4678   SyncShareNudge();
   4679 
   4680   // The item ought to have committed successfully.
   4681   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4682   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4683   ExpectSyncedAndCreated();
   4684   {
   4685     syncable::ReadTransaction trans(FROM_HERE, directory());
   4686     Entry entry(&trans, GET_BY_HANDLE, metahandle_);
   4687     EXPECT_EQ(2, entry.GetBaseVersion());
   4688   }
   4689 
   4690   // Now, encounter a GetUpdates corresponding to the just-committed
   4691   // update.
   4692   sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
   4693   update->set_originator_cache_guid(local_cache_guid());
   4694   update->set_originator_client_item_id(local_id_.GetServerId());
   4695   SyncShareNudge();
   4696   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4697   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4698   ExpectSyncedAndCreated();
   4699 }
   4700 
   4701 TEST_F(SyncerUndeletionTest, UndeleteAfterCommitButBeforeGetUpdates) {
   4702   Create();
   4703   ExpectUnsyncedCreation();
   4704   SyncShareNudge();
   4705 
   4706   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4707   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4708   ExpectSyncedAndCreated();
   4709 
   4710   // Delete and commit.
   4711   Delete();
   4712   ExpectUnsyncedDeletion();
   4713   SyncShareNudge();
   4714 
   4715   // The item ought to have committed successfully.
   4716   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4717   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4718   ExpectSyncedAndDeleted();
   4719 
   4720   // Before the GetUpdates, the item is locally undeleted.
   4721   Undelete();
   4722   ExpectUnsyncedUndeletion();
   4723 
   4724   // Now, encounter a GetUpdates corresponding to the just-committed
   4725   // deletion update.  The undeletion should prevail.
   4726   mock_server_->AddUpdateFromLastCommit();
   4727   SyncShareNudge();
   4728   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4729   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4730   ExpectSyncedAndCreated();
   4731 }
   4732 
   4733 TEST_F(SyncerUndeletionTest, UndeleteAfterDeleteAndGetUpdates) {
   4734   Create();
   4735   ExpectUnsyncedCreation();
   4736   SyncShareNudge();
   4737 
   4738   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4739   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4740   ExpectSyncedAndCreated();
   4741 
   4742   sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
   4743   update->set_originator_cache_guid(local_cache_guid());
   4744   update->set_originator_client_item_id(local_id_.GetServerId());
   4745   SyncShareNudge();
   4746   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4747   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4748   ExpectSyncedAndCreated();
   4749 
   4750   // Delete and commit.
   4751   Delete();
   4752   ExpectUnsyncedDeletion();
   4753   SyncShareNudge();
   4754 
   4755   // The item ought to have committed successfully.
   4756   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4757   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4758   ExpectSyncedAndDeleted();
   4759 
   4760   // Now, encounter a GetUpdates corresponding to the just-committed
   4761   // deletion update.  Should be consistent.
   4762   mock_server_->AddUpdateFromLastCommit();
   4763   SyncShareNudge();
   4764   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4765   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4766   ExpectSyncedAndDeleted();
   4767 
   4768   // After the GetUpdates, the item is locally undeleted.
   4769   Undelete();
   4770   ExpectUnsyncedUndeletion();
   4771 
   4772   // Now, encounter a GetUpdates corresponding to the just-committed
   4773   // deletion update.  The undeletion should prevail.
   4774   SyncShareNudge();
   4775   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4776   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4777   ExpectSyncedAndCreated();
   4778 }
   4779 
   4780 // Test processing of undeletion GetUpdateses.
   4781 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletes) {
   4782   Create();
   4783   ExpectUnsyncedCreation();
   4784   SyncShareNudge();
   4785 
   4786   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4787   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4788   ExpectSyncedAndCreated();
   4789 
   4790   // Add a delete from the server.
   4791   sync_pb::SyncEntity* update1 = mock_server_->AddUpdateFromLastCommit();
   4792   update1->set_originator_cache_guid(local_cache_guid());
   4793   update1->set_originator_client_item_id(local_id_.GetServerId());
   4794   SyncShareNudge();
   4795   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4796   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4797   ExpectSyncedAndCreated();
   4798 
   4799   // Some other client deletes the item.
   4800   {
   4801     syncable::ReadTransaction trans(FROM_HERE, directory());
   4802     Entry entry(&trans, GET_BY_HANDLE, metahandle_);
   4803     mock_server_->AddUpdateTombstone(entry.GetId());
   4804   }
   4805   SyncShareNudge();
   4806 
   4807   // The update ought to have applied successfully.
   4808   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4809   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4810   ExpectSyncedAndDeleted();
   4811 
   4812   // Undelete it locally.
   4813   Undelete();
   4814   ExpectUnsyncedUndeletion();
   4815   SyncShareNudge();
   4816   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4817   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4818   ExpectSyncedAndCreated();
   4819 
   4820   // Now, encounter a GetUpdates corresponding to the just-committed
   4821   // deletion update.  The undeletion should prevail.
   4822   sync_pb::SyncEntity* update2 = mock_server_->AddUpdateFromLastCommit();
   4823   update2->set_originator_cache_guid(local_cache_guid());
   4824   update2->set_originator_client_item_id(local_id_.GetServerId());
   4825   SyncShareNudge();
   4826   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4827   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4828   ExpectSyncedAndCreated();
   4829 }
   4830 
   4831 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletesImmediately) {
   4832   Create();
   4833   ExpectUnsyncedCreation();
   4834   SyncShareNudge();
   4835 
   4836   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4837   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4838   ExpectSyncedAndCreated();
   4839 
   4840   // Some other client deletes the item before we get a chance
   4841   // to GetUpdates our original request.
   4842   {
   4843     syncable::ReadTransaction trans(FROM_HERE, directory());
   4844     Entry entry(&trans, GET_BY_HANDLE, metahandle_);
   4845     mock_server_->AddUpdateTombstone(entry.GetId());
   4846   }
   4847   SyncShareNudge();
   4848 
   4849   // The update ought to have applied successfully.
   4850   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4851   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4852   ExpectSyncedAndDeleted();
   4853 
   4854   // Undelete it locally.
   4855   Undelete();
   4856   ExpectUnsyncedUndeletion();
   4857   SyncShareNudge();
   4858   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4859   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4860   ExpectSyncedAndCreated();
   4861 
   4862   // Now, encounter a GetUpdates corresponding to the just-committed
   4863   // deletion update.  The undeletion should prevail.
   4864   sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
   4865   update->set_originator_cache_guid(local_cache_guid());
   4866   update->set_originator_client_item_id(local_id_.GetServerId());
   4867   SyncShareNudge();
   4868   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4869   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4870   ExpectSyncedAndCreated();
   4871 }
   4872 
   4873 TEST_F(SyncerUndeletionTest, OtherClientUndeletes) {
   4874   Create();
   4875   ExpectUnsyncedCreation();
   4876   SyncShareNudge();
   4877 
   4878   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4879   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4880   ExpectSyncedAndCreated();
   4881 
   4882   // Get the updates of our just-committed entry.
   4883   sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
   4884   update->set_originator_cache_guid(local_cache_guid());
   4885   update->set_originator_client_item_id(local_id_.GetServerId());
   4886   SyncShareNudge();
   4887   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4888   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4889   ExpectSyncedAndCreated();
   4890 
   4891   // We delete the item.
   4892   Delete();
   4893   ExpectUnsyncedDeletion();
   4894   SyncShareNudge();
   4895 
   4896   // The update ought to have applied successfully.
   4897   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4898   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4899   ExpectSyncedAndDeleted();
   4900 
   4901   // Now, encounter a GetUpdates corresponding to the just-committed
   4902   // deletion update.
   4903   mock_server_->AddUpdateFromLastCommit();
   4904   SyncShareNudge();
   4905   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4906   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4907   ExpectSyncedAndDeleted();
   4908 
   4909   // Some other client undeletes the item.
   4910   {
   4911     syncable::ReadTransaction trans(FROM_HERE, directory());
   4912     Entry entry(&trans, GET_BY_HANDLE, metahandle_);
   4913     mock_server_->AddUpdateBookmark(
   4914         entry.GetId(),
   4915         entry.GetParentId(),
   4916         "Thadeusz", 100, 1000,
   4917         local_cache_guid(), local_id_.GetServerId());
   4918   }
   4919   mock_server_->SetLastUpdateClientTag(client_tag_);
   4920   SyncShareNudge();
   4921   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4922   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4923   ExpectSyncedAndCreated();
   4924   {
   4925     syncable::ReadTransaction trans(FROM_HERE, directory());
   4926     Entry entry(&trans, GET_BY_HANDLE, metahandle_);
   4927     EXPECT_EQ("Thadeusz", entry.GetNonUniqueName());
   4928   }
   4929 }
   4930 
   4931 TEST_F(SyncerUndeletionTest, OtherClientUndeletesImmediately) {
   4932   Create();
   4933   ExpectUnsyncedCreation();
   4934   SyncShareNudge();
   4935 
   4936   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4937   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4938   ExpectSyncedAndCreated();
   4939 
   4940   // Get the updates of our just-committed entry.
   4941   sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
   4942   update->set_originator_cache_guid(local_cache_guid());
   4943   {
   4944     syncable::ReadTransaction trans(FROM_HERE, directory());
   4945     Entry entry(&trans, GET_BY_HANDLE, metahandle_);
   4946     update->set_originator_client_item_id(local_id_.GetServerId());
   4947   }
   4948   SyncShareNudge();
   4949   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4950   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4951   ExpectSyncedAndCreated();
   4952 
   4953   // We delete the item.
   4954   Delete();
   4955   ExpectUnsyncedDeletion();
   4956   SyncShareNudge();
   4957 
   4958   // The update ought to have applied successfully.
   4959   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4960   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4961   ExpectSyncedAndDeleted();
   4962 
   4963   // Some other client undeletes before we see the update from our
   4964   // commit.
   4965   {
   4966     syncable::ReadTransaction trans(FROM_HERE, directory());
   4967     Entry entry(&trans, GET_BY_HANDLE, metahandle_);
   4968     mock_server_->AddUpdateBookmark(
   4969         entry.GetId(),
   4970         entry.GetParentId(),
   4971         "Thadeusz", 100, 1000,
   4972         local_cache_guid(), local_id_.GetServerId());
   4973   }
   4974   mock_server_->SetLastUpdateClientTag(client_tag_);
   4975   SyncShareNudge();
   4976   EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
   4977   EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
   4978   ExpectSyncedAndCreated();
   4979   {
   4980     syncable::ReadTransaction trans(FROM_HERE, directory());
   4981     Entry entry(&trans, GET_BY_HANDLE, metahandle_);
   4982     EXPECT_EQ("Thadeusz", entry.GetNonUniqueName());
   4983   }
   4984 }
   4985 
   4986 enum {
   4987   TEST_PARAM_BOOKMARK_ENABLE_BIT,
   4988   TEST_PARAM_AUTOFILL_ENABLE_BIT,
   4989   TEST_PARAM_BIT_COUNT
   4990 };
   4991 
   4992 class MixedResult :
   4993     public SyncerTest,
   4994     public ::testing::WithParamInterface<int> {
   4995  protected:
   4996   bool ShouldFailBookmarkCommit() {
   4997     return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT)) == 0;
   4998   }
   4999   bool ShouldFailAutofillCommit() {
   5000     return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT)) == 0;
   5001   }
   5002 };
   5003 
   5004 INSTANTIATE_TEST_CASE_P(ExtensionsActivity,
   5005                         MixedResult,
   5006                         testing::Range(0, 1 << TEST_PARAM_BIT_COUNT));
   5007 
   5008 TEST_P(MixedResult, ExtensionsActivity) {
   5009   {
   5010     WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
   5011 
   5012     MutableEntry pref(&wtrans, CREATE, PREFERENCES, wtrans.root_id(), "pref");
   5013     ASSERT_TRUE(pref.good());
   5014     pref.PutIsUnsynced(true);
   5015 
   5016     MutableEntry bookmark(
   5017         &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bookmark");
   5018     ASSERT_TRUE(bookmark.good());
   5019     bookmark.PutIsUnsynced(true);
   5020 
   5021     if (ShouldFailBookmarkCommit()) {
   5022       mock_server_->SetTransientErrorId(bookmark.GetId());
   5023     }
   5024 
   5025     if (ShouldFailAutofillCommit()) {
   5026       mock_server_->SetTransientErrorId(pref.GetId());
   5027     }
   5028   }
   5029 
   5030 
   5031   // Put some extenions activity records into the monitor.
   5032   {
   5033     ExtensionsActivity::Records records;
   5034     records["ABC"].extension_id = "ABC";
   5035     records["ABC"].bookmark_write_count = 2049U;
   5036     records["xyz"].extension_id = "xyz";
   5037     records["xyz"].bookmark_write_count = 4U;
   5038     context_->extensions_activity()->PutRecords(records);
   5039   }
   5040 
   5041   SyncShareNudge();
   5042 
   5043   ExtensionsActivity::Records final_monitor_records;
   5044   context_->extensions_activity()->GetAndClearRecords(&final_monitor_records);
   5045   if (ShouldFailBookmarkCommit()) {
   5046     ASSERT_EQ(2U, final_monitor_records.size())
   5047         << "Should restore records after unsuccessful bookmark commit.";
   5048     EXPECT_EQ("ABC", final_monitor_records["ABC"].extension_id);
   5049     EXPECT_EQ("xyz", final_monitor_records["xyz"].extension_id);
   5050     EXPECT_EQ(2049U, final_monitor_records["ABC"].bookmark_write_count);
   5051     EXPECT_EQ(4U,    final_monitor_records["xyz"].bookmark_write_count);
   5052   } else {
   5053     EXPECT_TRUE(final_monitor_records.empty())
   5054         << "Should not restore records after successful bookmark commit.";
   5055   }
   5056 }
   5057 
   5058 }  // namespace syncer
   5059