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(¶ms->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(¶ms->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