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