| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "sync/test/fake_server/fake_server.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 | |
| 9 #include <algorithm> | |
| 10 #include <limits> | |
| 11 #include <memory> | |
| 12 #include <set> | |
| 13 #include <string> | |
| 14 #include <utility> | |
| 15 #include <vector> | |
| 16 | |
| 17 #include "base/guid.h" | |
| 18 #include "base/logging.h" | |
| 19 #include "base/stl_util.h" | |
| 20 #include "base/strings/string_number_conversions.h" | |
| 21 #include "base/strings/string_util.h" | |
| 22 #include "base/strings/stringprintf.h" | |
| 23 #include "base/synchronization/lock.h" | |
| 24 #include "net/base/net_errors.h" | |
| 25 #include "net/http/http_status_code.h" | |
| 26 #include "sync/internal_api/public/base/model_type.h" | |
| 27 #include "sync/protocol/sync.pb.h" | |
| 28 #include "sync/test/fake_server/bookmark_entity.h" | |
| 29 #include "sync/test/fake_server/permanent_entity.h" | |
| 30 #include "sync/test/fake_server/tombstone_entity.h" | |
| 31 #include "sync/test/fake_server/unique_client_entity.h" | |
| 32 | |
| 33 using std::string; | |
| 34 using std::vector; | |
| 35 | |
| 36 using syncer::GetModelType; | |
| 37 using syncer::GetModelTypeFromSpecifics; | |
| 38 using syncer::ModelType; | |
| 39 using syncer::ModelTypeSet; | |
| 40 | |
| 41 namespace fake_server { | |
| 42 | |
| 43 class FakeServerEntity; | |
| 44 | |
| 45 namespace { | |
| 46 | |
| 47 // The default keystore key. | |
| 48 static const char kDefaultKeystoreKey[] = "1111111111111111"; | |
| 49 | |
| 50 // Properties of the bookmark bar permanent folder. | |
| 51 static const char kBookmarkBarFolderServerTag[] = "bookmark_bar"; | |
| 52 static const char kBookmarkBarFolderName[] = "Bookmark Bar"; | |
| 53 | |
| 54 // Properties of the other bookmarks permanent folder. | |
| 55 static const char kOtherBookmarksFolderServerTag[] = "other_bookmarks"; | |
| 56 static const char kOtherBookmarksFolderName[] = "Other Bookmarks"; | |
| 57 | |
| 58 // Properties of the synced bookmarks permanent folder. | |
| 59 static const char kSyncedBookmarksFolderServerTag[] = "synced_bookmarks"; | |
| 60 static const char kSyncedBookmarksFolderName[] = "Synced Bookmarks"; | |
| 61 | |
| 62 // A filter used during GetUpdates calls to determine what information to | |
| 63 // send back to the client. There is a 1:1 correspondence between any given | |
| 64 // GetUpdates call and an UpdateSieve instance. | |
| 65 class UpdateSieve { | |
| 66 public: | |
| 67 ~UpdateSieve() { } | |
| 68 | |
| 69 // Factory method for creating an UpdateSieve. | |
| 70 static std::unique_ptr<UpdateSieve> Create( | |
| 71 const sync_pb::GetUpdatesMessage& get_updates_message); | |
| 72 | |
| 73 // Sets the progress markers in |get_updates_response| given the progress | |
| 74 // markers from the original GetUpdatesMessage and |new_version| (the latest | |
| 75 // version in the entries sent back). | |
| 76 void UpdateProgressMarkers( | |
| 77 int64_t new_version, | |
| 78 sync_pb::GetUpdatesResponse* get_updates_response) const { | |
| 79 ModelTypeToVersionMap::const_iterator it; | |
| 80 for (it = request_from_version_.begin(); it != request_from_version_.end(); | |
| 81 ++it) { | |
| 82 sync_pb::DataTypeProgressMarker* new_marker = | |
| 83 get_updates_response->add_new_progress_marker(); | |
| 84 new_marker->set_data_type_id( | |
| 85 GetSpecificsFieldNumberFromModelType(it->first)); | |
| 86 | |
| 87 int64_t version = std::max(new_version, it->second); | |
| 88 new_marker->set_token(base::Int64ToString(version)); | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 // Determines whether the server should send an |entity| to the client as | |
| 93 // part of a GetUpdatesResponse. | |
| 94 bool ClientWantsItem(const FakeServerEntity& entity) const { | |
| 95 int64_t version = entity.GetVersion(); | |
| 96 if (version <= min_version_) { | |
| 97 return false; | |
| 98 } else if (entity.IsDeleted()) { | |
| 99 return true; | |
| 100 } | |
| 101 | |
| 102 ModelTypeToVersionMap::const_iterator it = | |
| 103 request_from_version_.find(entity.GetModelType()); | |
| 104 | |
| 105 return it == request_from_version_.end() ? false : it->second < version; | |
| 106 } | |
| 107 | |
| 108 // Returns the minimum version seen across all types. | |
| 109 int64_t GetMinVersion() const { return min_version_; } | |
| 110 | |
| 111 private: | |
| 112 typedef std::map<ModelType, int64_t> ModelTypeToVersionMap; | |
| 113 | |
| 114 // Creates an UpdateSieve. | |
| 115 UpdateSieve(const ModelTypeToVersionMap request_from_version, | |
| 116 const int64_t min_version) | |
| 117 : request_from_version_(request_from_version), | |
| 118 min_version_(min_version) {} | |
| 119 | |
| 120 // Maps data type IDs to the latest version seen for that type. | |
| 121 const ModelTypeToVersionMap request_from_version_; | |
| 122 | |
| 123 // The minimum version seen among all data types. | |
| 124 const int min_version_; | |
| 125 }; | |
| 126 | |
| 127 std::unique_ptr<UpdateSieve> UpdateSieve::Create( | |
| 128 const sync_pb::GetUpdatesMessage& get_updates_message) { | |
| 129 CHECK_GT(get_updates_message.from_progress_marker_size(), 0) | |
| 130 << "A GetUpdates request must have at least one progress marker."; | |
| 131 | |
| 132 UpdateSieve::ModelTypeToVersionMap request_from_version; | |
| 133 int64_t min_version = std::numeric_limits<int64_t>::max(); | |
| 134 for (int i = 0; i < get_updates_message.from_progress_marker_size(); i++) { | |
| 135 sync_pb::DataTypeProgressMarker marker = | |
| 136 get_updates_message.from_progress_marker(i); | |
| 137 | |
| 138 int64_t version = 0; | |
| 139 // Let the version remain zero if there is no token or an empty token (the | |
| 140 // first request for this type). | |
| 141 if (marker.has_token() && !marker.token().empty()) { | |
| 142 bool parsed = base::StringToInt64(marker.token(), &version); | |
| 143 CHECK(parsed) << "Unable to parse progress marker token."; | |
| 144 } | |
| 145 | |
| 146 ModelType model_type = syncer::GetModelTypeFromSpecificsFieldNumber( | |
| 147 marker.data_type_id()); | |
| 148 request_from_version[model_type] = version; | |
| 149 | |
| 150 if (version < min_version) | |
| 151 min_version = version; | |
| 152 } | |
| 153 | |
| 154 return std::unique_ptr<UpdateSieve>( | |
| 155 new UpdateSieve(request_from_version, min_version)); | |
| 156 } | |
| 157 | |
| 158 // Returns whether |entity| is deleted or permanent. | |
| 159 bool IsDeletedOrPermanent(const FakeServerEntity& entity) { | |
| 160 return entity.IsDeleted() || entity.IsPermanent(); | |
| 161 } | |
| 162 | |
| 163 } // namespace | |
| 164 | |
| 165 FakeServer::FakeServer() : version_(0), | |
| 166 store_birthday_(0), | |
| 167 authenticated_(true), | |
| 168 error_type_(sync_pb::SyncEnums::SUCCESS), | |
| 169 alternate_triggered_errors_(false), | |
| 170 request_counter_(0), | |
| 171 network_enabled_(true), | |
| 172 weak_ptr_factory_(this) { | |
| 173 Init(); | |
| 174 } | |
| 175 | |
| 176 FakeServer::~FakeServer() {} | |
| 177 | |
| 178 void FakeServer::Init() { | |
| 179 keystore_keys_.push_back(kDefaultKeystoreKey); | |
| 180 | |
| 181 const bool create_result = CreateDefaultPermanentItems(); | |
| 182 DCHECK(create_result) << "Permanent items were not created successfully."; | |
| 183 } | |
| 184 | |
| 185 bool FakeServer::CreatePermanentBookmarkFolder(const std::string& server_tag, | |
| 186 const std::string& name) { | |
| 187 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 188 std::unique_ptr<FakeServerEntity> entity = | |
| 189 PermanentEntity::Create(syncer::BOOKMARKS, server_tag, name, | |
| 190 ModelTypeToRootTag(syncer::BOOKMARKS)); | |
| 191 if (!entity) | |
| 192 return false; | |
| 193 | |
| 194 SaveEntity(std::move(entity)); | |
| 195 return true; | |
| 196 } | |
| 197 | |
| 198 bool FakeServer::CreateDefaultPermanentItems() { | |
| 199 // Permanent folders are always required for Bookmarks (hierarchical | |
| 200 // structure) and Nigori (data stored in permanent root folder). | |
| 201 ModelTypeSet permanent_folder_types = | |
| 202 ModelTypeSet(syncer::BOOKMARKS, syncer::NIGORI); | |
| 203 | |
| 204 for (ModelTypeSet::Iterator it = permanent_folder_types.First(); it.Good(); | |
| 205 it.Inc()) { | |
| 206 ModelType model_type = it.Get(); | |
| 207 | |
| 208 std::unique_ptr<FakeServerEntity> top_level_entity = | |
| 209 PermanentEntity::CreateTopLevel(model_type); | |
| 210 if (!top_level_entity) { | |
| 211 return false; | |
| 212 } | |
| 213 SaveEntity(std::move(top_level_entity)); | |
| 214 | |
| 215 if (model_type == syncer::BOOKMARKS) { | |
| 216 if (!CreatePermanentBookmarkFolder(kBookmarkBarFolderServerTag, | |
| 217 kBookmarkBarFolderName)) | |
| 218 return false; | |
| 219 if (!CreatePermanentBookmarkFolder(kOtherBookmarksFolderServerTag, | |
| 220 kOtherBookmarksFolderName)) | |
| 221 return false; | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 return true; | |
| 226 } | |
| 227 | |
| 228 void FakeServer::UpdateEntityVersion(FakeServerEntity* entity) { | |
| 229 entity->SetVersion(++version_); | |
| 230 } | |
| 231 | |
| 232 void FakeServer::SaveEntity(std::unique_ptr<FakeServerEntity> entity) { | |
| 233 UpdateEntityVersion(entity.get()); | |
| 234 entities_[entity->GetId()] = std::move(entity); | |
| 235 } | |
| 236 | |
| 237 void FakeServer::HandleCommand(const string& request, | |
| 238 const base::Closure& completion_closure, | |
| 239 int* error_code, | |
| 240 int* response_code, | |
| 241 std::string* response) { | |
| 242 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 243 if (!network_enabled_) { | |
| 244 *error_code = net::ERR_FAILED; | |
| 245 *response_code = net::ERR_FAILED; | |
| 246 *response = string(); | |
| 247 completion_closure.Run(); | |
| 248 return; | |
| 249 } | |
| 250 request_counter_++; | |
| 251 | |
| 252 if (!authenticated_) { | |
| 253 *error_code = 0; | |
| 254 *response_code = net::HTTP_UNAUTHORIZED; | |
| 255 *response = string(); | |
| 256 completion_closure.Run(); | |
| 257 return; | |
| 258 } | |
| 259 | |
| 260 sync_pb::ClientToServerMessage message; | |
| 261 bool parsed = message.ParseFromString(request); | |
| 262 CHECK(parsed) << "Unable to parse the ClientToServerMessage."; | |
| 263 | |
| 264 sync_pb::ClientToServerResponse response_proto; | |
| 265 | |
| 266 if (message.has_store_birthday() && | |
| 267 message.store_birthday() != GetStoreBirthday()) { | |
| 268 response_proto.set_error_code(sync_pb::SyncEnums::NOT_MY_BIRTHDAY); | |
| 269 } else if (error_type_ != sync_pb::SyncEnums::SUCCESS && | |
| 270 ShouldSendTriggeredError()) { | |
| 271 response_proto.set_error_code(error_type_); | |
| 272 } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) { | |
| 273 sync_pb::ClientToServerResponse_Error* error = | |
| 274 response_proto.mutable_error(); | |
| 275 error->CopyFrom(*(triggered_actionable_error_.get())); | |
| 276 } else { | |
| 277 bool success = false; | |
| 278 switch (message.message_contents()) { | |
| 279 case sync_pb::ClientToServerMessage::GET_UPDATES: | |
| 280 last_getupdates_message_ = message; | |
| 281 success = HandleGetUpdatesRequest(message.get_updates(), | |
| 282 response_proto.mutable_get_updates()); | |
| 283 break; | |
| 284 case sync_pb::ClientToServerMessage::COMMIT: | |
| 285 last_commit_message_ = message; | |
| 286 success = HandleCommitRequest(message.commit(), | |
| 287 message.invalidator_client_id(), | |
| 288 response_proto.mutable_commit()); | |
| 289 break; | |
| 290 case sync_pb::ClientToServerMessage::CLEAR_SERVER_DATA: | |
| 291 ClearServerData(); | |
| 292 response_proto.mutable_clear_server_data(); | |
| 293 success = true; | |
| 294 break; | |
| 295 default: | |
| 296 *error_code = net::ERR_NOT_IMPLEMENTED; | |
| 297 *response_code = 0; | |
| 298 *response = string(); | |
| 299 completion_closure.Run(); | |
| 300 return; | |
| 301 } | |
| 302 | |
| 303 if (!success) { | |
| 304 // TODO(pvalenzuela): Add logging here so that tests have more info about | |
| 305 // the failure. | |
| 306 *error_code = net::ERR_FAILED; | |
| 307 *response_code = 0; | |
| 308 *response = string(); | |
| 309 completion_closure.Run(); | |
| 310 return; | |
| 311 } | |
| 312 | |
| 313 response_proto.set_error_code(sync_pb::SyncEnums::SUCCESS); | |
| 314 } | |
| 315 | |
| 316 response_proto.set_store_birthday(GetStoreBirthday()); | |
| 317 | |
| 318 *error_code = 0; | |
| 319 *response_code = net::HTTP_OK; | |
| 320 *response = response_proto.SerializeAsString(); | |
| 321 completion_closure.Run(); | |
| 322 } | |
| 323 | |
| 324 bool FakeServer::GetLastCommitMessage( | |
| 325 sync_pb::ClientToServerMessage* message) { | |
| 326 if (!last_commit_message_.has_commit()) | |
| 327 return false; | |
| 328 | |
| 329 message->CopyFrom(last_commit_message_); | |
| 330 return true; | |
| 331 } | |
| 332 | |
| 333 bool FakeServer::GetLastGetUpdatesMessage( | |
| 334 sync_pb::ClientToServerMessage* message) { | |
| 335 if (!last_getupdates_message_.has_get_updates()) | |
| 336 return false; | |
| 337 | |
| 338 message->CopyFrom(last_getupdates_message_); | |
| 339 return true; | |
| 340 } | |
| 341 | |
| 342 bool FakeServer::HandleGetUpdatesRequest( | |
| 343 const sync_pb::GetUpdatesMessage& get_updates, | |
| 344 sync_pb::GetUpdatesResponse* response) { | |
| 345 // TODO(pvalenzuela): Implement batching instead of sending all information | |
| 346 // at once. | |
| 347 response->set_changes_remaining(0); | |
| 348 | |
| 349 std::unique_ptr<UpdateSieve> sieve = UpdateSieve::Create(get_updates); | |
| 350 | |
| 351 // This folder is called "Synced Bookmarks" by sync and is renamed | |
| 352 // "Mobile Bookmarks" by the mobile client UIs. | |
| 353 if (get_updates.create_mobile_bookmarks_folder() && | |
| 354 !CreatePermanentBookmarkFolder(kSyncedBookmarksFolderServerTag, | |
| 355 kSyncedBookmarksFolderName)) { | |
| 356 return false; | |
| 357 } | |
| 358 | |
| 359 bool send_encryption_keys_based_on_nigori = false; | |
| 360 int64_t max_response_version = 0; | |
| 361 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | |
| 362 ++it) { | |
| 363 const FakeServerEntity& entity = *it->second; | |
| 364 if (sieve->ClientWantsItem(entity)) { | |
| 365 sync_pb::SyncEntity* response_entity = response->add_entries(); | |
| 366 entity.SerializeAsProto(response_entity); | |
| 367 | |
| 368 max_response_version = std::max(max_response_version, | |
| 369 response_entity->version()); | |
| 370 | |
| 371 if (entity.GetModelType() == syncer::NIGORI) { | |
| 372 send_encryption_keys_based_on_nigori = | |
| 373 response_entity->specifics().nigori().passphrase_type() == | |
| 374 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE; | |
| 375 } | |
| 376 } | |
| 377 } | |
| 378 | |
| 379 if (send_encryption_keys_based_on_nigori || | |
| 380 get_updates.need_encryption_key()) { | |
| 381 for (vector<string>::iterator it = keystore_keys_.begin(); | |
| 382 it != keystore_keys_.end(); ++it) { | |
| 383 response->add_encryption_keys(*it); | |
| 384 } | |
| 385 } | |
| 386 | |
| 387 sieve->UpdateProgressMarkers(max_response_version, response); | |
| 388 return true; | |
| 389 } | |
| 390 | |
| 391 string FakeServer::CommitEntity( | |
| 392 const sync_pb::SyncEntity& client_entity, | |
| 393 sync_pb::CommitResponse_EntryResponse* entry_response, | |
| 394 const string& client_guid, | |
| 395 const string& parent_id) { | |
| 396 if (client_entity.version() == 0 && client_entity.deleted()) { | |
| 397 return string(); | |
| 398 } | |
| 399 | |
| 400 std::unique_ptr<FakeServerEntity> entity; | |
| 401 if (client_entity.deleted()) { | |
| 402 entity = TombstoneEntity::Create(client_entity.id_string()); | |
| 403 DeleteChildren(client_entity.id_string()); | |
| 404 } else if (GetModelType(client_entity) == syncer::NIGORI) { | |
| 405 // NIGORI is the only permanent item type that should be updated by the | |
| 406 // client. | |
| 407 EntityMap::const_iterator iter = entities_.find(client_entity.id_string()); | |
| 408 CHECK(iter != entities_.end()); | |
| 409 entity = PermanentEntity::CreateUpdatedNigoriEntity(client_entity, | |
| 410 *iter->second); | |
| 411 } else if (client_entity.has_client_defined_unique_tag()) { | |
| 412 entity = UniqueClientEntity::Create(client_entity); | |
| 413 } else { | |
| 414 // TODO(pvalenzuela): Validate entity's parent ID. | |
| 415 EntityMap::const_iterator iter = entities_.find(client_entity.id_string()); | |
| 416 if (iter != entities_.end()) { | |
| 417 entity = BookmarkEntity::CreateUpdatedVersion(client_entity, | |
| 418 *iter->second, parent_id); | |
| 419 } else { | |
| 420 entity = BookmarkEntity::CreateNew(client_entity, parent_id, client_guid); | |
| 421 } | |
| 422 } | |
| 423 | |
| 424 if (!entity) { | |
| 425 // TODO(pvalenzuela): Add logging so that it is easier to determine why | |
| 426 // creation failed. | |
| 427 return string(); | |
| 428 } | |
| 429 | |
| 430 const std::string id = entity->GetId(); | |
| 431 SaveEntity(std::move(entity)); | |
| 432 BuildEntryResponseForSuccessfulCommit(id, entry_response); | |
| 433 return id; | |
| 434 } | |
| 435 | |
| 436 void FakeServer::BuildEntryResponseForSuccessfulCommit( | |
| 437 const std::string& entity_id, | |
| 438 sync_pb::CommitResponse_EntryResponse* entry_response) { | |
| 439 EntityMap::const_iterator iter = entities_.find(entity_id); | |
| 440 CHECK(iter != entities_.end()); | |
| 441 const FakeServerEntity& entity = *iter->second; | |
| 442 entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS); | |
| 443 entry_response->set_id_string(entity.GetId()); | |
| 444 | |
| 445 if (entity.IsDeleted()) { | |
| 446 entry_response->set_version(entity.GetVersion() + 1); | |
| 447 } else { | |
| 448 entry_response->set_version(entity.GetVersion()); | |
| 449 entry_response->set_name(entity.GetName()); | |
| 450 } | |
| 451 } | |
| 452 | |
| 453 bool FakeServer::IsChild(const string& id, const string& potential_parent_id) { | |
| 454 EntityMap::const_iterator iter = entities_.find(id); | |
| 455 if (iter == entities_.end()) { | |
| 456 // We've hit an ID (probably the imaginary root entity) that isn't stored | |
| 457 // by the server, so it can't be a child. | |
| 458 return false; | |
| 459 } | |
| 460 | |
| 461 const FakeServerEntity& entity = *iter->second; | |
| 462 if (entity.GetParentId() == potential_parent_id) | |
| 463 return true; | |
| 464 | |
| 465 // Recursively look up the tree. | |
| 466 return IsChild(entity.GetParentId(), potential_parent_id); | |
| 467 } | |
| 468 | |
| 469 void FakeServer::DeleteChildren(const string& id) { | |
| 470 std::set<string> tombstones_ids; | |
| 471 // Find all the children of id. | |
| 472 for (auto& entity : entities_) { | |
| 473 if (IsChild(entity.first, id)) { | |
| 474 tombstones_ids.insert(entity.first); | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 for (auto& tombstone_id : tombstones_ids) { | |
| 479 SaveEntity(TombstoneEntity::Create(tombstone_id)); | |
| 480 } | |
| 481 } | |
| 482 | |
| 483 bool FakeServer::HandleCommitRequest(const sync_pb::CommitMessage& commit, | |
| 484 const std::string& invalidator_client_id, | |
| 485 sync_pb::CommitResponse* response) { | |
| 486 std::map<string, string> client_to_server_ids; | |
| 487 string guid = commit.cache_guid(); | |
| 488 ModelTypeSet committed_model_types; | |
| 489 | |
| 490 // TODO(pvalenzuela): Add validation of CommitMessage.entries. | |
| 491 ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it; | |
| 492 for (it = commit.entries().begin(); it != commit.entries().end(); ++it) { | |
| 493 sync_pb::CommitResponse_EntryResponse* entry_response = | |
| 494 response->add_entryresponse(); | |
| 495 | |
| 496 sync_pb::SyncEntity client_entity = *it; | |
| 497 string parent_id = client_entity.parent_id_string(); | |
| 498 if (client_to_server_ids.find(parent_id) != | |
| 499 client_to_server_ids.end()) { | |
| 500 parent_id = client_to_server_ids[parent_id]; | |
| 501 } | |
| 502 | |
| 503 const string entity_id = | |
| 504 CommitEntity(client_entity, entry_response, guid, parent_id); | |
| 505 if (entity_id.empty()) { | |
| 506 return false; | |
| 507 } | |
| 508 | |
| 509 // Record the ID if it was renamed. | |
| 510 if (entity_id != client_entity.id_string()) { | |
| 511 client_to_server_ids[client_entity.id_string()] = entity_id; | |
| 512 } | |
| 513 | |
| 514 EntityMap::const_iterator iter = entities_.find(entity_id); | |
| 515 CHECK(iter != entities_.end()); | |
| 516 committed_model_types.Put(iter->second->GetModelType()); | |
| 517 } | |
| 518 | |
| 519 FOR_EACH_OBSERVER(Observer, observers_, | |
| 520 OnCommit(invalidator_client_id, committed_model_types)); | |
| 521 return true; | |
| 522 } | |
| 523 | |
| 524 std::unique_ptr<base::DictionaryValue> | |
| 525 FakeServer::GetEntitiesAsDictionaryValue() { | |
| 526 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 527 std::unique_ptr<base::DictionaryValue> dictionary( | |
| 528 new base::DictionaryValue()); | |
| 529 | |
| 530 // Initialize an empty ListValue for all ModelTypes. | |
| 531 ModelTypeSet all_types = ModelTypeSet::All(); | |
| 532 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { | |
| 533 dictionary->Set(ModelTypeToString(it.Get()), new base::ListValue()); | |
| 534 } | |
| 535 | |
| 536 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | |
| 537 ++it) { | |
| 538 const FakeServerEntity& entity = *it->second; | |
| 539 if (IsDeletedOrPermanent(entity)) { | |
| 540 // Tombstones are ignored as they don't represent current data. Folders | |
| 541 // are also ignored as current verification infrastructure does not | |
| 542 // consider them. | |
| 543 continue; | |
| 544 } | |
| 545 base::ListValue* list_value; | |
| 546 if (!dictionary->GetList(ModelTypeToString(entity.GetModelType()), | |
| 547 &list_value)) { | |
| 548 return std::unique_ptr<base::DictionaryValue>(); | |
| 549 } | |
| 550 // TODO(pvalenzuela): Store more data for each entity so additional | |
| 551 // verification can be performed. One example of additional verification | |
| 552 // is checking the correctness of the bookmark hierarchy. | |
| 553 list_value->AppendString(entity.GetName()); | |
| 554 } | |
| 555 | |
| 556 return dictionary; | |
| 557 } | |
| 558 | |
| 559 std::vector<sync_pb::SyncEntity> FakeServer::GetSyncEntitiesByModelType( | |
| 560 ModelType model_type) { | |
| 561 std::vector<sync_pb::SyncEntity> sync_entities; | |
| 562 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 563 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | |
| 564 ++it) { | |
| 565 const FakeServerEntity& entity = *it->second; | |
| 566 if (!IsDeletedOrPermanent(entity) && entity.GetModelType() == model_type) { | |
| 567 sync_pb::SyncEntity sync_entity; | |
| 568 entity.SerializeAsProto(&sync_entity); | |
| 569 sync_entities.push_back(sync_entity); | |
| 570 } | |
| 571 } | |
| 572 return sync_entities; | |
| 573 } | |
| 574 | |
| 575 void FakeServer::InjectEntity(std::unique_ptr<FakeServerEntity> entity) { | |
| 576 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 577 SaveEntity(std::move(entity)); | |
| 578 } | |
| 579 | |
| 580 bool FakeServer::ModifyEntitySpecifics( | |
| 581 const std::string& id, | |
| 582 const sync_pb::EntitySpecifics& updated_specifics) { | |
| 583 EntityMap::const_iterator iter = entities_.find(id); | |
| 584 if (iter == entities_.end() || | |
| 585 iter->second->GetModelType() != | |
| 586 GetModelTypeFromSpecifics(updated_specifics)) { | |
| 587 return false; | |
| 588 } | |
| 589 | |
| 590 FakeServerEntity* entity = iter->second.get(); | |
| 591 entity->SetSpecifics(updated_specifics); | |
| 592 UpdateEntityVersion(entity); | |
| 593 return true; | |
| 594 } | |
| 595 | |
| 596 bool FakeServer::ModifyBookmarkEntity( | |
| 597 const std::string& id, | |
| 598 const std::string& parent_id, | |
| 599 const sync_pb::EntitySpecifics& updated_specifics) { | |
| 600 EntityMap::const_iterator iter = entities_.find(id); | |
| 601 if (iter == entities_.end() || | |
| 602 iter->second->GetModelType() != syncer::BOOKMARKS || | |
| 603 GetModelTypeFromSpecifics(updated_specifics) != syncer::BOOKMARKS) { | |
| 604 return false; | |
| 605 } | |
| 606 | |
| 607 BookmarkEntity* entity = static_cast<BookmarkEntity*>(iter->second.get()); | |
| 608 | |
| 609 entity->SetParentId(parent_id); | |
| 610 entity->SetSpecifics(updated_specifics); | |
| 611 if (updated_specifics.has_bookmark()) { | |
| 612 entity->SetName(updated_specifics.bookmark().title()); | |
| 613 } | |
| 614 UpdateEntityVersion(entity); | |
| 615 return true; | |
| 616 } | |
| 617 | |
| 618 void FakeServer::ClearServerData() { | |
| 619 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 620 entities_.clear(); | |
| 621 keystore_keys_.clear(); | |
| 622 ++store_birthday_; | |
| 623 Init(); | |
| 624 } | |
| 625 | |
| 626 void FakeServer::SetAuthenticated() { | |
| 627 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 628 authenticated_ = true; | |
| 629 } | |
| 630 | |
| 631 void FakeServer::SetUnauthenticated() { | |
| 632 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 633 authenticated_ = false; | |
| 634 } | |
| 635 | |
| 636 bool FakeServer::TriggerError(const sync_pb::SyncEnums::ErrorType& error_type) { | |
| 637 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 638 if (triggered_actionable_error_.get()) { | |
| 639 DVLOG(1) << "Only one type of error can be triggered at any given time."; | |
| 640 return false; | |
| 641 } | |
| 642 | |
| 643 error_type_ = error_type; | |
| 644 return true; | |
| 645 } | |
| 646 | |
| 647 bool FakeServer::TriggerActionableError( | |
| 648 const sync_pb::SyncEnums::ErrorType& error_type, | |
| 649 const string& description, | |
| 650 const string& url, | |
| 651 const sync_pb::SyncEnums::Action& action) { | |
| 652 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 653 if (error_type_ != sync_pb::SyncEnums::SUCCESS) { | |
| 654 DVLOG(1) << "Only one type of error can be triggered at any given time."; | |
| 655 return false; | |
| 656 } | |
| 657 | |
| 658 sync_pb::ClientToServerResponse_Error* error = | |
| 659 new sync_pb::ClientToServerResponse_Error(); | |
| 660 error->set_error_type(error_type); | |
| 661 error->set_error_description(description); | |
| 662 error->set_url(url); | |
| 663 error->set_action(action); | |
| 664 triggered_actionable_error_.reset(error); | |
| 665 return true; | |
| 666 } | |
| 667 | |
| 668 bool FakeServer::EnableAlternatingTriggeredErrors() { | |
| 669 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 670 if (error_type_ == sync_pb::SyncEnums::SUCCESS && | |
| 671 !triggered_actionable_error_.get()) { | |
| 672 DVLOG(1) << "No triggered error set. Alternating can't be enabled."; | |
| 673 return false; | |
| 674 } | |
| 675 | |
| 676 alternate_triggered_errors_ = true; | |
| 677 // Reset the counter so that the the first request yields a triggered error. | |
| 678 request_counter_ = 0; | |
| 679 return true; | |
| 680 } | |
| 681 | |
| 682 bool FakeServer::ShouldSendTriggeredError() const { | |
| 683 if (!alternate_triggered_errors_) | |
| 684 return true; | |
| 685 | |
| 686 // Check that the counter is odd so that we trigger an error on the first | |
| 687 // request after alternating is enabled. | |
| 688 return request_counter_ % 2 != 0; | |
| 689 } | |
| 690 | |
| 691 void FakeServer::AddObserver(Observer* observer) { | |
| 692 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 693 observers_.AddObserver(observer); | |
| 694 } | |
| 695 | |
| 696 void FakeServer::RemoveObserver(Observer* observer) { | |
| 697 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 698 observers_.RemoveObserver(observer); | |
| 699 } | |
| 700 | |
| 701 void FakeServer::EnableNetwork() { | |
| 702 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 703 network_enabled_ = true; | |
| 704 } | |
| 705 | |
| 706 void FakeServer::DisableNetwork() { | |
| 707 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 708 network_enabled_ = false; | |
| 709 } | |
| 710 | |
| 711 std::string FakeServer::GetBookmarkBarFolderId() const { | |
| 712 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 713 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | |
| 714 ++it) { | |
| 715 FakeServerEntity* entity = it->second.get(); | |
| 716 if (entity->GetName() == kBookmarkBarFolderName && | |
| 717 entity->IsFolder() && | |
| 718 entity->GetModelType() == syncer::BOOKMARKS) { | |
| 719 return entity->GetId(); | |
| 720 } | |
| 721 } | |
| 722 NOTREACHED() << "Bookmark Bar entity not found."; | |
| 723 return ""; | |
| 724 } | |
| 725 | |
| 726 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() { | |
| 727 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 728 return weak_ptr_factory_.GetWeakPtr(); | |
| 729 } | |
| 730 | |
| 731 std::string FakeServer::GetStoreBirthday() const { | |
| 732 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 733 return base::Int64ToString(store_birthday_); | |
| 734 } | |
| 735 | |
| 736 } // namespace fake_server | |
| OLD | NEW |