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 // Mock ServerConnectionManager class for use in client regression tests.
      6 
      7 #include "sync/test/engine/mock_connection_manager.h"
      8 
      9 #include <map>
     10 
     11 #include "base/location.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "sync/engine/syncer_proto_util.h"
     14 #include "sync/protocol/bookmark_specifics.pb.h"
     15 #include "sync/syncable/directory.h"
     16 #include "sync/syncable/syncable_write_transaction.h"
     17 #include "sync/test/engine/test_id_factory.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 
     20 using std::find;
     21 using std::map;
     22 using std::string;
     23 using sync_pb::ClientToServerMessage;
     24 using sync_pb::CommitMessage;
     25 using sync_pb::CommitResponse;
     26 using sync_pb::GetUpdatesMessage;
     27 using sync_pb::SyncEnums;
     28 
     29 namespace syncer {
     30 
     31 using syncable::WriteTransaction;
     32 
     33 static char kValidAuthToken[] = "AuthToken";
     34 static char kCacheGuid[] = "kqyg7097kro6GSUod+GSg==";
     35 
     36 MockConnectionManager::MockConnectionManager(syncable::Directory* directory)
     37     : ServerConnectionManager("unused", 0, false, false),
     38       server_reachable_(true),
     39       conflict_all_commits_(false),
     40       conflict_n_commits_(0),
     41       next_new_id_(10000),
     42       store_birthday_("Store BDay!"),
     43       store_birthday_sent_(false),
     44       client_stuck_(false),
     45       countdown_to_postbuffer_fail_(0),
     46       directory_(directory),
     47       mid_commit_observer_(NULL),
     48       throttling_(false),
     49       fail_with_auth_invalid_(false),
     50       fail_non_periodic_get_updates_(false),
     51       next_position_in_parent_(2),
     52       use_legacy_bookmarks_protocol_(false),
     53       num_get_updates_requests_(0) {
     54   SetNewTimestamp(0);
     55   SetAuthToken(kValidAuthToken);
     56 }
     57 
     58 MockConnectionManager::~MockConnectionManager() {
     59   EXPECT_TRUE(update_queue_.empty()) << "Unfetched updates.";
     60 }
     61 
     62 void MockConnectionManager::SetCommitTimeRename(string prepend) {
     63   commit_time_rename_prepended_string_ = prepend;
     64 }
     65 
     66 void MockConnectionManager::SetMidCommitCallback(
     67     const base::Closure& callback) {
     68   mid_commit_callback_ = callback;
     69 }
     70 
     71 void MockConnectionManager::SetMidCommitObserver(
     72     MockConnectionManager::MidCommitObserver* observer) {
     73     mid_commit_observer_ = observer;
     74 }
     75 
     76 bool MockConnectionManager::PostBufferToPath(PostBufferParams* params,
     77     const string& path,
     78     const string& auth_token,
     79     ScopedServerStatusWatcher* watcher) {
     80   ClientToServerMessage post;
     81   CHECK(post.ParseFromString(params->buffer_in));
     82   CHECK(post.has_protocol_version());
     83   CHECK(post.has_api_key());
     84   CHECK(post.has_bag_of_chips());
     85   last_request_.CopyFrom(post);
     86   client_stuck_ = post.sync_problem_detected();
     87   sync_pb::ClientToServerResponse response;
     88   response.Clear();
     89 
     90   if (directory_) {
     91     // If the Directory's locked when we do this, it's a problem as in normal
     92     // use this function could take a while to return because it accesses the
     93     // network. As we can't test this we do the next best thing and hang here
     94     // when there's an issue.
     95     CHECK(directory_->good());
     96     WriteTransaction wt(FROM_HERE, syncable::UNITTEST, directory_);
     97   }
     98 
     99   if (auth_token.empty()) {
    100     params->response.server_status = HttpResponse::SYNC_AUTH_ERROR;
    101     return false;
    102   }
    103 
    104   if (auth_token != kValidAuthToken) {
    105     // Simulate server-side auth failure.
    106     params->response.server_status = HttpResponse::SYNC_AUTH_ERROR;
    107     InvalidateAndClearAuthToken();
    108   }
    109 
    110   if (--countdown_to_postbuffer_fail_ == 0) {
    111     // Fail as countdown hits zero.
    112     params->response.server_status = HttpResponse::SYNC_SERVER_ERROR;
    113     return false;
    114   }
    115 
    116   if (!server_reachable_) {
    117     params->response.server_status = HttpResponse::CONNECTION_UNAVAILABLE;
    118     return false;
    119   }
    120 
    121   // Default to an ok connection.
    122   params->response.server_status = HttpResponse::SERVER_CONNECTION_OK;
    123   response.set_error_code(SyncEnums::SUCCESS);
    124   const string current_store_birthday = store_birthday();
    125   response.set_store_birthday(current_store_birthday);
    126   if (post.has_store_birthday() && post.store_birthday() !=
    127       current_store_birthday) {
    128     response.set_error_code(SyncEnums::NOT_MY_BIRTHDAY);
    129     response.set_error_message("Merry Unbirthday!");
    130     response.SerializeToString(&params->buffer_out);
    131     store_birthday_sent_ = true;
    132     return true;
    133   }
    134   bool result = true;
    135   EXPECT_TRUE(!store_birthday_sent_ || post.has_store_birthday() ||
    136               post.message_contents() == ClientToServerMessage::AUTHENTICATE);
    137   store_birthday_sent_ = true;
    138 
    139   if (post.message_contents() == ClientToServerMessage::COMMIT) {
    140     ProcessCommit(&post, &response);
    141   } else if (post.message_contents() == ClientToServerMessage::GET_UPDATES) {
    142     ProcessGetUpdates(&post, &response);
    143   } else {
    144     EXPECT_TRUE(false) << "Unknown/unsupported ClientToServerMessage";
    145     return false;
    146   }
    147 
    148   {
    149     base::AutoLock lock(response_code_override_lock_);
    150     if (throttling_) {
    151       response.set_error_code(SyncEnums::THROTTLED);
    152       throttling_ = false;
    153     }
    154 
    155     if (fail_with_auth_invalid_)
    156       response.set_error_code(SyncEnums::AUTH_INVALID);
    157   }
    158 
    159   response.SerializeToString(&params->buffer_out);
    160   if (post.message_contents() == ClientToServerMessage::COMMIT &&
    161       !mid_commit_callback_.is_null()) {
    162     mid_commit_callback_.Run();
    163     mid_commit_callback_.Reset();
    164   }
    165   if (mid_commit_observer_) {
    166     mid_commit_observer_->Observe();
    167   }
    168 
    169   return result;
    170 }
    171 
    172 sync_pb::GetUpdatesResponse* MockConnectionManager::GetUpdateResponse() {
    173   if (update_queue_.empty()) {
    174     NextUpdateBatch();
    175   }
    176   return &update_queue_.back();
    177 }
    178 
    179 void MockConnectionManager::AddDefaultBookmarkData(sync_pb::SyncEntity* entity,
    180                                                    bool is_folder) {
    181   if (use_legacy_bookmarks_protocol_) {
    182     sync_pb::SyncEntity_BookmarkData* data = entity->mutable_bookmarkdata();
    183     data->set_bookmark_folder(is_folder);
    184 
    185     if (!is_folder) {
    186       data->set_bookmark_url("http://google.com");
    187     }
    188   } else {
    189     entity->set_folder(is_folder);
    190     entity->mutable_specifics()->mutable_bookmark();
    191     if (!is_folder) {
    192       entity->mutable_specifics()->mutable_bookmark()->
    193           set_url("http://google.com");
    194     }
    195   }
    196 }
    197 
    198 sync_pb::SyncEntity* MockConnectionManager::AddUpdateDirectory(
    199     int id,
    200     int parent_id,
    201     string name,
    202     int64 version,
    203     int64 sync_ts,
    204     std::string originator_cache_guid,
    205     std::string originator_client_item_id) {
    206   return AddUpdateDirectory(TestIdFactory::FromNumber(id),
    207                             TestIdFactory::FromNumber(parent_id),
    208                             name,
    209                             version,
    210                             sync_ts,
    211                             originator_cache_guid,
    212                             originator_client_item_id);
    213 }
    214 
    215 void MockConnectionManager::SetGUClientCommand(
    216     sync_pb::ClientCommand* command) {
    217   gu_client_command_.reset(command);
    218 }
    219 
    220 void MockConnectionManager::SetCommitClientCommand(
    221     sync_pb::ClientCommand* command) {
    222   commit_client_command_.reset(command);
    223 }
    224 
    225 void MockConnectionManager::SetTransientErrorId(syncable::Id id) {
    226   transient_error_ids_.push_back(id);
    227 }
    228 
    229 sync_pb::SyncEntity* MockConnectionManager::AddUpdateBookmark(
    230     int id, int parent_id,
    231     string name, int64 version,
    232     int64 sync_ts,
    233     string originator_client_item_id,
    234     string originator_cache_guid) {
    235   return AddUpdateBookmark(TestIdFactory::FromNumber(id),
    236                            TestIdFactory::FromNumber(parent_id),
    237                            name,
    238                            version,
    239                            sync_ts,
    240                            originator_client_item_id,
    241                            originator_cache_guid);
    242 }
    243 
    244 sync_pb::SyncEntity* MockConnectionManager::AddUpdateSpecifics(
    245     int id,
    246     int parent_id,
    247     string name,
    248     int64 version,
    249     int64 sync_ts,
    250     bool is_dir,
    251     int64 position,
    252     const sync_pb::EntitySpecifics& specifics) {
    253   sync_pb::SyncEntity* ent = AddUpdateMeta(
    254       TestIdFactory::FromNumber(id).GetServerId(),
    255       TestIdFactory::FromNumber(parent_id).GetServerId(),
    256       name, version, sync_ts);
    257   ent->set_position_in_parent(position);
    258   ent->mutable_specifics()->CopyFrom(specifics);
    259   ent->set_folder(is_dir);
    260   return ent;
    261 }
    262 
    263 sync_pb::SyncEntity* MockConnectionManager::AddUpdateSpecifics(
    264     int id,
    265     int parent_id,
    266     string name,
    267     int64 version,
    268     int64 sync_ts,
    269     bool is_dir,
    270     int64 position,
    271     const sync_pb::EntitySpecifics& specifics,
    272     string originator_cache_guid,
    273     string originator_client_item_id) {
    274   sync_pb::SyncEntity* ent = AddUpdateSpecifics(
    275       id, parent_id, name, version, sync_ts, is_dir, position, specifics);
    276   ent->set_originator_cache_guid(originator_cache_guid);
    277   ent->set_originator_client_item_id(originator_client_item_id);
    278   return ent;
    279 }
    280 
    281 sync_pb::SyncEntity* MockConnectionManager::SetNigori(
    282     int id,
    283     int64 version,
    284     int64 sync_ts,
    285     const sync_pb::EntitySpecifics& specifics) {
    286   sync_pb::SyncEntity* ent = GetUpdateResponse()->add_entries();
    287   ent->set_id_string(TestIdFactory::FromNumber(id).GetServerId());
    288   ent->set_parent_id_string(TestIdFactory::FromNumber(0).GetServerId());
    289   ent->set_server_defined_unique_tag(ModelTypeToRootTag(NIGORI));
    290   ent->set_name("Nigori");
    291   ent->set_non_unique_name("Nigori");
    292   ent->set_version(version);
    293   ent->set_sync_timestamp(sync_ts);
    294   ent->set_mtime(sync_ts);
    295   ent->set_ctime(1);
    296   ent->set_position_in_parent(0);
    297   ent->set_folder(false);
    298   ent->mutable_specifics()->CopyFrom(specifics);
    299   return ent;
    300 }
    301 
    302 sync_pb::SyncEntity* MockConnectionManager::AddUpdatePref(string id,
    303                                                           string parent_id,
    304                                                           string client_tag,
    305                                                           int64 version,
    306                                                           int64 sync_ts) {
    307   sync_pb::SyncEntity* ent =
    308       AddUpdateMeta(id, parent_id, " ", version, sync_ts);
    309 
    310   ent->set_client_defined_unique_tag(client_tag);
    311 
    312   sync_pb::EntitySpecifics specifics;
    313   AddDefaultFieldValue(PREFERENCES, &specifics);
    314   ent->mutable_specifics()->CopyFrom(specifics);
    315 
    316   return ent;
    317 }
    318 
    319 sync_pb::SyncEntity* MockConnectionManager::AddUpdateFull(
    320     string id, string parent_id,
    321     string name, int64 version,
    322     int64 sync_ts, bool is_dir) {
    323   sync_pb::SyncEntity* ent =
    324       AddUpdateMeta(id, parent_id, name, version, sync_ts);
    325   AddDefaultBookmarkData(ent, is_dir);
    326   return ent;
    327 }
    328 
    329 sync_pb::SyncEntity* MockConnectionManager::AddUpdateMeta(
    330     string id, string parent_id,
    331     string name, int64 version,
    332     int64 sync_ts) {
    333   sync_pb::SyncEntity* ent = GetUpdateResponse()->add_entries();
    334   ent->set_id_string(id);
    335   ent->set_parent_id_string(parent_id);
    336   ent->set_non_unique_name(name);
    337   ent->set_name(name);
    338   ent->set_version(version);
    339   ent->set_sync_timestamp(sync_ts);
    340   ent->set_mtime(sync_ts);
    341   ent->set_ctime(1);
    342   ent->set_position_in_parent(GeneratePositionInParent());
    343 
    344   // This isn't perfect, but it works well enough.  This is an update, which
    345   // means the ID is a server ID, which means it never changes.  By making
    346   // kCacheGuid also never change, we guarantee that the same item always has
    347   // the same originator_cache_guid and originator_client_item_id.
    348   //
    349   // Unfortunately, neither this class nor the tests that use it explicitly
    350   // track sync entitites, so supporting proper cache guids and client item IDs
    351   // would require major refactoring.  The ID used here ought to be the "c-"
    352   // style ID that was sent up on the commit.
    353   ent->set_originator_cache_guid(kCacheGuid);
    354   ent->set_originator_client_item_id(id);
    355 
    356   return ent;
    357 }
    358 
    359 sync_pb::SyncEntity* MockConnectionManager::AddUpdateDirectory(
    360     string id,
    361     string parent_id,
    362     string name,
    363     int64 version,
    364     int64 sync_ts,
    365     std::string originator_cache_guid,
    366     std::string originator_client_item_id) {
    367   sync_pb::SyncEntity* ret =
    368       AddUpdateFull(id, parent_id, name, version, sync_ts, true);
    369   ret->set_originator_cache_guid(originator_cache_guid);
    370   ret->set_originator_client_item_id(originator_client_item_id);
    371   return ret;
    372 }
    373 
    374 sync_pb::SyncEntity* MockConnectionManager::AddUpdateBookmark(
    375     string id,
    376     string parent_id,
    377     string name, int64 version,
    378     int64 sync_ts,
    379     string originator_cache_guid,
    380     string originator_client_item_id) {
    381   sync_pb::SyncEntity* ret =
    382       AddUpdateFull(id, parent_id, name, version, sync_ts, false);
    383   ret->set_originator_cache_guid(originator_cache_guid);
    384   ret->set_originator_client_item_id(originator_client_item_id);
    385   return ret;
    386 }
    387 
    388 sync_pb::SyncEntity* MockConnectionManager::AddUpdateFromLastCommit() {
    389   EXPECT_EQ(1, last_sent_commit().entries_size());
    390   EXPECT_EQ(1, last_commit_response().entryresponse_size());
    391   EXPECT_EQ(CommitResponse::SUCCESS,
    392       last_commit_response().entryresponse(0).response_type());
    393 
    394   if (last_sent_commit().entries(0).deleted()) {
    395     AddUpdateTombstone(syncable::Id::CreateFromServerId(
    396         last_sent_commit().entries(0).id_string()));
    397   } else {
    398     sync_pb::SyncEntity* ent = GetUpdateResponse()->add_entries();
    399     ent->CopyFrom(last_sent_commit().entries(0));
    400     ent->clear_insert_after_item_id();
    401     ent->clear_old_parent_id();
    402     ent->set_position_in_parent(
    403         last_commit_response().entryresponse(0).position_in_parent());
    404     ent->set_version(
    405         last_commit_response().entryresponse(0).version());
    406     ent->set_id_string(
    407         last_commit_response().entryresponse(0).id_string());
    408 
    409     // This is the same hack as in AddUpdateMeta.  See the comment in that
    410     // function for more information.
    411     ent->set_originator_cache_guid(kCacheGuid);
    412     ent->set_originator_client_item_id(
    413         last_commit_response().entryresponse(0).id_string());
    414 
    415     if (last_sent_commit().entries(0).has_unique_position()) {
    416       ent->mutable_unique_position()->CopyFrom(
    417           last_sent_commit().entries(0).unique_position());
    418     }
    419 
    420     // Tests don't currently care about the following:
    421     // parent_id_string, name, non_unique_name.
    422   }
    423   return GetMutableLastUpdate();
    424 }
    425 
    426 void MockConnectionManager::AddUpdateTombstone(const syncable::Id& id) {
    427   // Tombstones have only the ID set and dummy values for the required fields.
    428   sync_pb::SyncEntity* ent = GetUpdateResponse()->add_entries();
    429   ent->set_id_string(id.GetServerId());
    430   ent->set_version(0);
    431   ent->set_name("");
    432   ent->set_deleted(true);
    433 }
    434 
    435 void MockConnectionManager::SetLastUpdateDeleted() {
    436   // Tombstones have only the ID set.  Wipe anything else.
    437   string id_string = GetMutableLastUpdate()->id_string();
    438   GetUpdateResponse()->mutable_entries()->RemoveLast();
    439   AddUpdateTombstone(syncable::Id::CreateFromServerId(id_string));
    440 }
    441 
    442 void MockConnectionManager::SetLastUpdateOriginatorFields(
    443     const string& client_id,
    444     const string& entry_id) {
    445   GetMutableLastUpdate()->set_originator_cache_guid(client_id);
    446   GetMutableLastUpdate()->set_originator_client_item_id(entry_id);
    447 }
    448 
    449 void MockConnectionManager::SetLastUpdateServerTag(const string& tag) {
    450   GetMutableLastUpdate()->set_server_defined_unique_tag(tag);
    451 }
    452 
    453 void MockConnectionManager::SetLastUpdateClientTag(const string& tag) {
    454   GetMutableLastUpdate()->set_client_defined_unique_tag(tag);
    455 }
    456 
    457 void MockConnectionManager::SetLastUpdatePosition(int64 server_position) {
    458   GetMutableLastUpdate()->set_position_in_parent(server_position);
    459 }
    460 
    461 void MockConnectionManager::SetNewTimestamp(int ts) {
    462   next_token_ = base::StringPrintf("mock connection ts = %d", ts);
    463   ApplyToken();
    464 }
    465 
    466 void MockConnectionManager::ApplyToken() {
    467   if (!update_queue_.empty()) {
    468     GetUpdateResponse()->clear_new_progress_marker();
    469     sync_pb::DataTypeProgressMarker* new_marker =
    470         GetUpdateResponse()->add_new_progress_marker();
    471     new_marker->set_data_type_id(-1);  // Invalid -- clients shouldn't see.
    472     new_marker->set_token(next_token_);
    473   }
    474 }
    475 
    476 void MockConnectionManager::SetChangesRemaining(int64 timestamp) {
    477   GetUpdateResponse()->set_changes_remaining(timestamp);
    478 }
    479 
    480 void MockConnectionManager::ProcessGetUpdates(
    481     sync_pb::ClientToServerMessage* csm,
    482     sync_pb::ClientToServerResponse* response) {
    483   CHECK(csm->has_get_updates());
    484   ASSERT_EQ(csm->message_contents(), ClientToServerMessage::GET_UPDATES);
    485   const GetUpdatesMessage& gu = csm->get_updates();
    486   num_get_updates_requests_++;
    487   EXPECT_FALSE(gu.has_from_timestamp());
    488   EXPECT_FALSE(gu.has_requested_types());
    489 
    490   if (fail_non_periodic_get_updates_) {
    491     EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::PERIODIC,
    492               gu.caller_info().source());
    493   }
    494 
    495   // Verify that the GetUpdates filter sent by the Syncer matches the test
    496   // expectation.
    497   ModelTypeSet protocol_types = ProtocolTypes();
    498   for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
    499        iter.Inc()) {
    500     ModelType model_type = iter.Get();
    501     sync_pb::DataTypeProgressMarker const* progress_marker =
    502         GetProgressMarkerForType(gu.from_progress_marker(), model_type);
    503     EXPECT_EQ(expected_filter_.Has(model_type), (progress_marker != NULL))
    504         << "Syncer requested_types differs from test expectation.";
    505     if (progress_marker) {
    506       EXPECT_EQ((expected_states_.count(model_type) > 0 ?
    507                  expected_states_[model_type].payload :
    508                  std::string()),
    509                 progress_marker->notification_hint());
    510     }
    511   }
    512 
    513   // Verify that the items we're about to send back to the client are of
    514   // the types requested by the client.  If this fails, it probably indicates
    515   // a test bug.
    516   EXPECT_TRUE(gu.fetch_folders());
    517   EXPECT_FALSE(gu.has_requested_types());
    518   if (update_queue_.empty()) {
    519     GetUpdateResponse();
    520   }
    521   sync_pb::GetUpdatesResponse* updates = &update_queue_.front();
    522   for (int i = 0; i < updates->entries_size(); ++i) {
    523     if (!updates->entries(i).deleted()) {
    524       ModelType entry_type = GetModelType(updates->entries(i));
    525       EXPECT_TRUE(
    526           IsModelTypePresentInSpecifics(gu.from_progress_marker(), entry_type))
    527           << "Syncer did not request updates being provided by the test.";
    528     }
    529   }
    530 
    531   response->mutable_get_updates()->CopyFrom(*updates);
    532 
    533   // Set appropriate progress markers, overriding the value squirreled
    534   // away by ApplyToken().
    535   std::string token = response->get_updates().new_progress_marker(0).token();
    536   response->mutable_get_updates()->clear_new_progress_marker();
    537   for (int i = 0; i < gu.from_progress_marker_size(); ++i) {
    538     if (gu.from_progress_marker(i).token() != token) {
    539       sync_pb::DataTypeProgressMarker* new_marker =
    540           response->mutable_get_updates()->add_new_progress_marker();
    541       new_marker->set_data_type_id(gu.from_progress_marker(i).data_type_id());
    542       new_marker->set_token(token);
    543     }
    544   }
    545 
    546   // Fill the keystore key if requested.
    547   if (gu.need_encryption_key())
    548     response->mutable_get_updates()->add_encryption_keys(keystore_key_);
    549 
    550   update_queue_.pop_front();
    551 
    552   if (gu_client_command_) {
    553     response->mutable_client_command()->CopyFrom(*gu_client_command_.get());
    554   }
    555 }
    556 
    557 void MockConnectionManager::SetKeystoreKey(const std::string& key) {
    558   // Note: this is not a thread-safe set, ok for now.  NOT ok if tests
    559   // run the syncer on the background thread while this method is called.
    560   keystore_key_ = key;
    561 }
    562 
    563 bool MockConnectionManager::ShouldConflictThisCommit() {
    564   bool conflict = false;
    565   if (conflict_all_commits_) {
    566     conflict = true;
    567   } else if (conflict_n_commits_ > 0) {
    568     conflict = true;
    569     --conflict_n_commits_;
    570   }
    571   return conflict;
    572 }
    573 
    574 bool MockConnectionManager::ShouldTransientErrorThisId(syncable::Id id) {
    575   return find(transient_error_ids_.begin(), transient_error_ids_.end(), id)
    576       != transient_error_ids_.end();
    577 }
    578 
    579 void MockConnectionManager::ProcessCommit(
    580     sync_pb::ClientToServerMessage* csm,
    581     sync_pb::ClientToServerResponse* response_buffer) {
    582   CHECK(csm->has_commit());
    583   ASSERT_EQ(csm->message_contents(), ClientToServerMessage::COMMIT);
    584   map <string, string> changed_ids;
    585   const CommitMessage& commit_message = csm->commit();
    586   CommitResponse* commit_response = response_buffer->mutable_commit();
    587   commit_messages_.push_back(new CommitMessage);
    588   commit_messages_.back()->CopyFrom(commit_message);
    589   map<string, sync_pb::CommitResponse_EntryResponse*> response_map;
    590   for (int i = 0; i < commit_message.entries_size() ; i++) {
    591     const sync_pb::SyncEntity& entry = commit_message.entries(i);
    592     CHECK(entry.has_id_string());
    593     string id_string = entry.id_string();
    594     ASSERT_LT(entry.name().length(), 256ul) << " name probably too long. True "
    595         "server name checking not implemented";
    596     syncable::Id id;
    597     if (entry.version() == 0) {
    598       // Relies on our new item string id format. (string representation of a
    599       // negative number).
    600       id = syncable::Id::CreateFromClientString(id_string);
    601     } else {
    602       id = syncable::Id::CreateFromServerId(id_string);
    603     }
    604     committed_ids_.push_back(id);
    605 
    606     if (response_map.end() == response_map.find(id_string))
    607       response_map[id_string] = commit_response->add_entryresponse();
    608     sync_pb::CommitResponse_EntryResponse* er = response_map[id_string];
    609     if (ShouldConflictThisCommit()) {
    610       er->set_response_type(CommitResponse::CONFLICT);
    611       continue;
    612     }
    613     if (ShouldTransientErrorThisId(id)) {
    614       er->set_response_type(CommitResponse::TRANSIENT_ERROR);
    615       continue;
    616     }
    617     er->set_response_type(CommitResponse::SUCCESS);
    618     er->set_version(entry.version() + 1);
    619     if (!commit_time_rename_prepended_string_.empty()) {
    620       // Commit time rename sent down from the server.
    621       er->set_name(commit_time_rename_prepended_string_ + entry.name());
    622     }
    623     string parent_id_string = entry.parent_id_string();
    624     // Remap id's we've already assigned.
    625     if (changed_ids.end() != changed_ids.find(parent_id_string)) {
    626       parent_id_string = changed_ids[parent_id_string];
    627       er->set_parent_id_string(parent_id_string);
    628     }
    629     if (entry.has_version() && 0 != entry.version()) {
    630       er->set_id_string(id_string);  // Allows verification.
    631     } else {
    632       string new_id = base::StringPrintf("mock_server:%d", next_new_id_++);
    633       changed_ids[id_string] = new_id;
    634       er->set_id_string(new_id);
    635     }
    636   }
    637   commit_responses_.push_back(new CommitResponse(*commit_response));
    638 
    639   if (commit_client_command_) {
    640     response_buffer->mutable_client_command()->CopyFrom(
    641         *commit_client_command_.get());
    642   }
    643 }
    644 
    645 sync_pb::SyncEntity* MockConnectionManager::AddUpdateDirectory(
    646     syncable::Id id,
    647     syncable::Id parent_id,
    648     string name,
    649     int64 version,
    650     int64 sync_ts,
    651     string originator_cache_guid,
    652     string originator_client_item_id) {
    653   return AddUpdateDirectory(id.GetServerId(), parent_id.GetServerId(),
    654                             name, version, sync_ts, originator_cache_guid,
    655                             originator_client_item_id);
    656 }
    657 
    658 sync_pb::SyncEntity* MockConnectionManager::AddUpdateBookmark(
    659     syncable::Id id,
    660     syncable::Id parent_id,
    661     string name,
    662     int64 version,
    663     int64 sync_ts,
    664     string originator_cache_guid,
    665     string originator_client_item_id) {
    666   return AddUpdateBookmark(id.GetServerId(), parent_id.GetServerId(),
    667                            name, version, sync_ts, originator_cache_guid,
    668                            originator_client_item_id);
    669 }
    670 
    671 sync_pb::SyncEntity* MockConnectionManager::GetMutableLastUpdate() {
    672   sync_pb::GetUpdatesResponse* updates = GetUpdateResponse();
    673   EXPECT_GT(updates->entries_size(), 0);
    674   return updates->mutable_entries()->Mutable(updates->entries_size() - 1);
    675 }
    676 
    677 void MockConnectionManager::NextUpdateBatch() {
    678   update_queue_.push_back(sync_pb::GetUpdatesResponse::default_instance());
    679   SetChangesRemaining(0);
    680   ApplyToken();
    681 }
    682 
    683 const CommitMessage& MockConnectionManager::last_sent_commit() const {
    684   EXPECT_TRUE(!commit_messages_.empty());
    685   return *commit_messages_.back();
    686 }
    687 
    688 const CommitResponse& MockConnectionManager::last_commit_response() const {
    689   EXPECT_TRUE(!commit_responses_.empty());
    690   return *commit_responses_.back();
    691 }
    692 
    693 bool MockConnectionManager::IsModelTypePresentInSpecifics(
    694     const google::protobuf::RepeatedPtrField<
    695         sync_pb::DataTypeProgressMarker>& filter,
    696     ModelType value) {
    697   int data_type_id = GetSpecificsFieldNumberFromModelType(value);
    698   for (int i = 0; i < filter.size(); ++i) {
    699     if (filter.Get(i).data_type_id() == data_type_id) {
    700       return true;
    701     }
    702   }
    703   return false;
    704 }
    705 
    706 sync_pb::DataTypeProgressMarker const*
    707     MockConnectionManager::GetProgressMarkerForType(
    708         const google::protobuf::RepeatedPtrField<
    709             sync_pb::DataTypeProgressMarker>& filter,
    710         ModelType value) {
    711   int data_type_id = GetSpecificsFieldNumberFromModelType(value);
    712   for (int i = 0; i < filter.size(); ++i) {
    713     if (filter.Get(i).data_type_id() == data_type_id) {
    714       return &(filter.Get(i));
    715     }
    716   }
    717   return NULL;
    718 }
    719 
    720 void MockConnectionManager::SetServerReachable() {
    721   server_reachable_ = true;
    722 }
    723 
    724 void MockConnectionManager::SetServerNotReachable() {
    725   server_reachable_ = false;
    726 }
    727 
    728 void MockConnectionManager::UpdateConnectionStatus() {
    729   if (!server_reachable_) {
    730     server_status_ = HttpResponse::CONNECTION_UNAVAILABLE;
    731   } else {
    732     server_status_ = HttpResponse::SERVER_CONNECTION_OK;
    733   }
    734 }
    735 
    736 void MockConnectionManager::SetServerStatus(
    737     HttpResponse::ServerConnectionCode server_status) {
    738   server_status_ = server_status;
    739 }
    740 
    741 }  // namespace syncer
    742