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