| 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::LoopbackServer; |
| 30 using syncer::LoopbackServerEntity; |
| 31 using syncer::ModelType; | 31 using syncer::ModelType; |
| 32 using syncer::ModelTypeSet; | 32 using syncer::ModelTypeSet; |
| 33 | 33 |
| 34 namespace fake_server { | 34 namespace fake_server { |
| 35 | 35 |
| 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() | 36 FakeServer::FakeServer() |
| 142 : version_(0), | 37 : authenticated_(true), |
| 143 store_birthday_(0), | |
| 144 authenticated_(true), | |
| 145 error_type_(sync_pb::SyncEnums::SUCCESS), | 38 error_type_(sync_pb::SyncEnums::SUCCESS), |
| 146 alternate_triggered_errors_(false), | 39 alternate_triggered_errors_(false), |
| 147 request_counter_(0), | 40 request_counter_(0), |
| 148 network_enabled_(true), | 41 network_enabled_(true), |
| 149 weak_ptr_factory_(this) { | 42 weak_ptr_factory_(this) { |
| 150 Init(); | 43 base::ThreadRestrictions::SetIOAllowed(true); |
| 44 loopback_server_storage_ = base::MakeUnique<base::ScopedTempDir>(); |
| 45 if (!loopback_server_storage_->CreateUniqueTempDir()) { |
| 46 NOTREACHED() << "Creating temp dir failed."; |
| 47 } |
| 48 loopback_server_ = base::MakeUnique<syncer::LoopbackServer>( |
| 49 loopback_server_storage_->GetPath().AppendASCII("profile.pb")); |
| 50 loopback_server_->set_observer_for_tests(this); |
| 151 } | 51 } |
| 152 | 52 |
| 153 FakeServer::~FakeServer() {} | 53 FakeServer::~FakeServer() {} |
| 154 | 54 |
| 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, | 55 void FakeServer::HandleCommand(const string& request, |
| 215 const base::Closure& completion_closure, | 56 const base::Closure& completion_closure, |
| 216 int* error_code, | 57 int* error_code, |
| 217 int* response_code, | 58 int* response_code, |
| 218 std::string* response) { | 59 std::string* response) { |
| 219 DCHECK(thread_checker_.CalledOnValidThread()); | 60 DCHECK(thread_checker_.CalledOnValidThread()); |
| 61 |
| 220 if (!network_enabled_) { | 62 if (!network_enabled_) { |
| 221 *error_code = net::ERR_FAILED; | 63 *error_code = net::ERR_FAILED; |
| 222 *response_code = net::ERR_FAILED; | 64 *response_code = net::ERR_FAILED; |
| 223 *response = string(); | 65 *response = string(); |
| 224 completion_closure.Run(); | 66 completion_closure.Run(); |
| 225 return; | 67 return; |
| 226 } | 68 } |
| 227 request_counter_++; | 69 request_counter_++; |
| 228 | 70 |
| 229 if (!authenticated_) { | 71 if (!authenticated_) { |
| 230 *error_code = 0; | 72 *error_code = 0; |
| 231 *response_code = net::HTTP_UNAUTHORIZED; | 73 *response_code = net::HTTP_UNAUTHORIZED; |
| 232 *response = string(); | 74 *response = string(); |
| 233 completion_closure.Run(); | 75 completion_closure.Run(); |
| 234 return; | 76 return; |
| 235 } | 77 } |
| 236 | 78 |
| 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; | 79 sync_pb::ClientToServerResponse response_proto; |
| 242 | 80 *response_code = 200; |
| 243 if (message.has_store_birthday() && | 81 *error_code = 0; |
| 244 message.store_birthday() != GetStoreBirthday()) { | 82 if (error_type_ != sync_pb::SyncEnums::SUCCESS && |
| 245 response_proto.set_error_code(sync_pb::SyncEnums::NOT_MY_BIRTHDAY); | 83 ShouldSendTriggeredError()) { |
| 246 } else if (error_type_ != sync_pb::SyncEnums::SUCCESS && | |
| 247 ShouldSendTriggeredError()) { | |
| 248 response_proto.set_error_code(error_type_); | 84 response_proto.set_error_code(error_type_); |
| 249 } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) { | 85 } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) { |
| 250 sync_pb::ClientToServerResponse_Error* error = | 86 sync_pb::ClientToServerResponse_Error* error = |
| 251 response_proto.mutable_error(); | 87 response_proto.mutable_error(); |
| 252 error->CopyFrom(*(triggered_actionable_error_.get())); | 88 error->CopyFrom(*(triggered_actionable_error_.get())); |
| 253 } else { | 89 } else { |
| 254 bool success = false; | 90 sync_pb::ClientToServerMessage message; |
| 91 bool parsed = message.ParseFromString(request); |
| 92 CHECK(parsed) << "Unable to parse the ClientToServerMessage."; |
| 255 switch (message.message_contents()) { | 93 switch (message.message_contents()) { |
| 256 case sync_pb::ClientToServerMessage::GET_UPDATES: | 94 case sync_pb::ClientToServerMessage::GET_UPDATES: |
| 257 last_getupdates_message_ = message; | 95 last_getupdates_message_ = message; |
| 258 success = HandleGetUpdatesRequest(message.get_updates(), | |
| 259 response_proto.mutable_get_updates()); | |
| 260 break; | 96 break; |
| 261 case sync_pb::ClientToServerMessage::COMMIT: | 97 case sync_pb::ClientToServerMessage::COMMIT: |
| 262 last_commit_message_ = message; | 98 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; | 99 break; |
| 272 default: | 100 default: |
| 273 *error_code = net::ERR_NOT_IMPLEMENTED; | 101 break; |
| 274 *response_code = 0; | 102 // Don't care. |
| 275 *response = string(); | |
| 276 completion_closure.Run(); | |
| 277 return; | |
| 278 } | 103 } |
| 279 | 104 |
| 280 if (!success) { | 105 int64_t response_code_large; |
| 281 // TODO(pvalenzuela): Add logging here so that tests have more info about | 106 syncer::HttpResponse::ServerConnectionCode server_status; |
| 282 // the failure. | 107 base::ThreadRestrictions::SetIOAllowed(true); |
| 283 *error_code = net::ERR_FAILED; | 108 loopback_server_->HandleCommand(request, &server_status, |
| 284 *response_code = 0; | 109 &response_code_large, response); |
| 285 *response = string(); | 110 *response_code = static_cast<int>(response_code_large); |
| 286 completion_closure.Run(); | 111 completion_closure.Run(); |
| 287 return; | 112 return; |
| 288 } | |
| 289 | |
| 290 response_proto.set_error_code(sync_pb::SyncEnums::SUCCESS); | |
| 291 } | 113 } |
| 292 | 114 |
| 293 response_proto.set_store_birthday(GetStoreBirthday()); | 115 response_proto.set_store_birthday(loopback_server_->GetStoreBirthday()); |
| 294 | |
| 295 *error_code = 0; | |
| 296 *response_code = net::HTTP_OK; | |
| 297 *response = response_proto.SerializeAsString(); | 116 *response = response_proto.SerializeAsString(); |
| 298 completion_closure.Run(); | 117 completion_closure.Run(); |
| 299 } | 118 } |
| 300 | 119 |
| 301 bool FakeServer::GetLastCommitMessage(sync_pb::ClientToServerMessage* message) { | 120 bool FakeServer::GetLastCommitMessage(sync_pb::ClientToServerMessage* message) { |
| 302 if (!last_commit_message_.has_commit()) | 121 if (!last_commit_message_.has_commit()) |
| 303 return false; | 122 return false; |
| 304 | 123 |
| 305 message->CopyFrom(last_commit_message_); | 124 message->CopyFrom(last_commit_message_); |
| 306 return true; | 125 return true; |
| 307 } | 126 } |
| 308 | 127 |
| 309 bool FakeServer::GetLastGetUpdatesMessage( | 128 bool FakeServer::GetLastGetUpdatesMessage( |
| 310 sync_pb::ClientToServerMessage* message) { | 129 sync_pb::ClientToServerMessage* message) { |
| 311 if (!last_getupdates_message_.has_get_updates()) | 130 if (!last_getupdates_message_.has_get_updates()) |
| 312 return false; | 131 return false; |
| 313 | 132 |
| 314 message->CopyFrom(last_getupdates_message_); | 133 message->CopyFrom(last_getupdates_message_); |
| 315 return true; | 134 return true; |
| 316 } | 135 } |
| 317 | 136 |
| 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 syncer::ModelType type = GetModelType(client_entity); | |
| 374 if (client_entity.deleted()) { | |
| 375 entity = TombstoneEntity::Create(client_entity.id_string(), | |
| 376 client_entity.client_defined_unique_tag()); | |
| 377 DeleteChildren(client_entity.id_string()); | |
| 378 } else if (type == syncer::NIGORI) { | |
| 379 // NIGORI is the only permanent item type that should be updated by the | |
| 380 // client. | |
| 381 EntityMap::const_iterator iter = entities_.find(client_entity.id_string()); | |
| 382 CHECK(iter != entities_.end()); | |
| 383 entity = PermanentEntity::CreateUpdatedNigoriEntity(client_entity, | |
| 384 *iter->second); | |
| 385 } else if (type == syncer::BOOKMARKS) { | |
| 386 // TODO(pvalenzuela): Validate entity's parent ID. | |
| 387 EntityMap::const_iterator iter = entities_.find(client_entity.id_string()); | |
| 388 if (iter != entities_.end()) { | |
| 389 entity = BookmarkEntity::CreateUpdatedVersion(client_entity, | |
| 390 *iter->second, parent_id); | |
| 391 } else { | |
| 392 entity = BookmarkEntity::CreateNew(client_entity, parent_id, client_guid); | |
| 393 } | |
| 394 } else { | |
| 395 entity = UniqueClientEntity::Create(client_entity); | |
| 396 } | |
| 397 | |
| 398 const std::string id = entity->id(); | |
| 399 SaveEntity(std::move(entity)); | |
| 400 BuildEntryResponseForSuccessfulCommit(id, entry_response); | |
| 401 return id; | |
| 402 } | |
| 403 | |
| 404 void FakeServer::OverrideResponseType( | 137 void FakeServer::OverrideResponseType( |
| 405 ResponseTypeProvider response_type_override) { | 138 LoopbackServer::ResponseTypeProvider response_type_override) { |
| 406 response_type_override_ = std::move(response_type_override); | 139 loopback_server_->OverrideResponseType(std::move(response_type_override)); |
| 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(response_type_override_ | |
| 416 ? response_type_override_.Run(entity) | |
| 417 : sync_pb::CommitResponse::SUCCESS); | |
| 418 entry_response->set_id_string(entity.id()); | |
| 419 | |
| 420 if (entity.IsDeleted()) { | |
| 421 entry_response->set_version(entity.GetVersion() + 1); | |
| 422 } else { | |
| 423 entry_response->set_version(entity.GetVersion()); | |
| 424 entry_response->set_name(entity.GetName()); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 bool FakeServer::IsChild(const string& id, const string& potential_parent_id) { | |
| 429 EntityMap::const_iterator iter = entities_.find(id); | |
| 430 if (iter == entities_.end()) { | |
| 431 // We've hit an ID (probably the imaginary root entity) that isn't stored | |
| 432 // by the server, so it can't be a child. | |
| 433 return false; | |
| 434 } | |
| 435 | |
| 436 const FakeServerEntity& entity = *iter->second; | |
| 437 if (entity.GetParentId() == potential_parent_id) | |
| 438 return true; | |
| 439 | |
| 440 // Recursively look up the tree. | |
| 441 return IsChild(entity.GetParentId(), potential_parent_id); | |
| 442 } | |
| 443 | |
| 444 void FakeServer::DeleteChildren(const string& id) { | |
| 445 std::vector<std::unique_ptr<FakeServerEntity>> tombstones; | |
| 446 // Find all the children of id. | |
| 447 for (const auto& entity : entities_) { | |
| 448 if (IsChild(entity.first, id)) { | |
| 449 tombstones.push_back(TombstoneEntity::Create( | |
| 450 entity.first, entity.second->client_defined_unique_tag())); | |
| 451 } | |
| 452 } | |
| 453 | |
| 454 for (auto& tombstone : tombstones) { | |
| 455 SaveEntity(std::move(tombstone)); | |
| 456 } | |
| 457 } | |
| 458 | |
| 459 bool FakeServer::HandleCommitRequest(const sync_pb::CommitMessage& commit, | |
| 460 const std::string& invalidator_client_id, | |
| 461 sync_pb::CommitResponse* response) { | |
| 462 std::map<string, string> client_to_server_ids; | |
| 463 string guid = commit.cache_guid(); | |
| 464 ModelTypeSet committed_model_types; | |
| 465 | |
| 466 // TODO(pvalenzuela): Add validation of CommitMessage.entries. | |
| 467 ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it; | |
| 468 for (it = commit.entries().begin(); it != commit.entries().end(); ++it) { | |
| 469 sync_pb::CommitResponse_EntryResponse* entry_response = | |
| 470 response->add_entryresponse(); | |
| 471 | |
| 472 sync_pb::SyncEntity client_entity = *it; | |
| 473 string parent_id = client_entity.parent_id_string(); | |
| 474 if (client_to_server_ids.find(parent_id) != client_to_server_ids.end()) { | |
| 475 parent_id = client_to_server_ids[parent_id]; | |
| 476 } | |
| 477 | |
| 478 const string entity_id = | |
| 479 CommitEntity(client_entity, entry_response, guid, parent_id); | |
| 480 if (entity_id.empty()) { | |
| 481 return false; | |
| 482 } | |
| 483 | |
| 484 // Record the ID if it was renamed. | |
| 485 if (entity_id != client_entity.id_string()) { | |
| 486 client_to_server_ids[client_entity.id_string()] = entity_id; | |
| 487 } | |
| 488 | |
| 489 EntityMap::const_iterator iter = entities_.find(entity_id); | |
| 490 CHECK(iter != entities_.end()); | |
| 491 committed_model_types.Put(iter->second->model_type()); | |
| 492 } | |
| 493 | |
| 494 for (auto& observer : observers_) | |
| 495 observer.OnCommit(invalidator_client_id, committed_model_types); | |
| 496 | |
| 497 return true; | |
| 498 } | 140 } |
| 499 | 141 |
| 500 std::unique_ptr<base::DictionaryValue> | 142 std::unique_ptr<base::DictionaryValue> |
| 501 FakeServer::GetEntitiesAsDictionaryValue() { | 143 FakeServer::GetEntitiesAsDictionaryValue() { |
| 502 DCHECK(thread_checker_.CalledOnValidThread()); | 144 DCHECK(thread_checker_.CalledOnValidThread()); |
| 503 std::unique_ptr<base::DictionaryValue> dictionary( | 145 return loopback_server_->GetEntitiesAsDictionaryValue(); |
| 504 new base::DictionaryValue()); | |
| 505 | |
| 506 // Initialize an empty ListValue for all ModelTypes. | |
| 507 ModelTypeSet all_types = ModelTypeSet::All(); | |
| 508 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { | |
| 509 dictionary->Set(ModelTypeToString(it.Get()), | |
| 510 base::MakeUnique<base::ListValue>()); | |
| 511 } | |
| 512 | |
| 513 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | |
| 514 ++it) { | |
| 515 const FakeServerEntity& entity = *it->second; | |
| 516 if (IsDeletedOrPermanent(entity)) { | |
| 517 // Tombstones are ignored as they don't represent current data. Folders | |
| 518 // are also ignored as current verification infrastructure does not | |
| 519 // consider them. | |
| 520 continue; | |
| 521 } | |
| 522 base::ListValue* list_value; | |
| 523 if (!dictionary->GetList(ModelTypeToString(entity.model_type()), | |
| 524 &list_value)) { | |
| 525 return std::unique_ptr<base::DictionaryValue>(); | |
| 526 } | |
| 527 // TODO(pvalenzuela): Store more data for each entity so additional | |
| 528 // verification can be performed. One example of additional verification | |
| 529 // is checking the correctness of the bookmark hierarchy. | |
| 530 list_value->AppendString(entity.GetName()); | |
| 531 } | |
| 532 | |
| 533 return dictionary; | |
| 534 } | 146 } |
| 535 | 147 |
| 536 std::vector<sync_pb::SyncEntity> FakeServer::GetSyncEntitiesByModelType( | 148 std::vector<sync_pb::SyncEntity> FakeServer::GetSyncEntitiesByModelType( |
| 537 ModelType model_type) { | 149 ModelType model_type) { |
| 538 std::vector<sync_pb::SyncEntity> sync_entities; | |
| 539 DCHECK(thread_checker_.CalledOnValidThread()); | 150 DCHECK(thread_checker_.CalledOnValidThread()); |
| 540 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | 151 return loopback_server_->GetSyncEntitiesByModelType(model_type); |
| 541 ++it) { | |
| 542 const FakeServerEntity& entity = *it->second; | |
| 543 if (!IsDeletedOrPermanent(entity) && entity.model_type() == model_type) { | |
| 544 sync_pb::SyncEntity sync_entity; | |
| 545 entity.SerializeAsProto(&sync_entity); | |
| 546 sync_entities.push_back(sync_entity); | |
| 547 } | |
| 548 } | |
| 549 return sync_entities; | |
| 550 } | 152 } |
| 551 | 153 |
| 552 void FakeServer::InjectEntity(std::unique_ptr<FakeServerEntity> entity) { | 154 void FakeServer::InjectEntity(std::unique_ptr<LoopbackServerEntity> entity) { |
| 553 DCHECK(thread_checker_.CalledOnValidThread()); | 155 DCHECK(thread_checker_.CalledOnValidThread()); |
| 554 SaveEntity(std::move(entity)); | 156 loopback_server_->SaveEntity(std::move(entity)); |
| 555 } | 157 } |
| 556 | 158 |
| 557 bool FakeServer::ModifyEntitySpecifics( | 159 bool FakeServer::ModifyEntitySpecifics( |
| 558 const std::string& id, | 160 const std::string& id, |
| 559 const sync_pb::EntitySpecifics& updated_specifics) { | 161 const sync_pb::EntitySpecifics& updated_specifics) { |
| 560 EntityMap::const_iterator iter = entities_.find(id); | 162 return loopback_server_->ModifyEntitySpecifics(id, updated_specifics); |
| 561 if (iter == entities_.end() || | |
| 562 iter->second->model_type() != | |
| 563 GetModelTypeFromSpecifics(updated_specifics)) { | |
| 564 return false; | |
| 565 } | |
| 566 | |
| 567 FakeServerEntity* entity = iter->second.get(); | |
| 568 entity->SetSpecifics(updated_specifics); | |
| 569 UpdateEntityVersion(entity); | |
| 570 return true; | |
| 571 } | 163 } |
| 572 | 164 |
| 573 bool FakeServer::ModifyBookmarkEntity( | 165 bool FakeServer::ModifyBookmarkEntity( |
| 574 const std::string& id, | 166 const std::string& id, |
| 575 const std::string& parent_id, | 167 const std::string& parent_id, |
| 576 const sync_pb::EntitySpecifics& updated_specifics) { | 168 const sync_pb::EntitySpecifics& updated_specifics) { |
| 577 EntityMap::const_iterator iter = entities_.find(id); | 169 return loopback_server_->ModifyBookmarkEntity(id, parent_id, |
| 578 if (iter == entities_.end() || | 170 updated_specifics); |
| 579 iter->second->model_type() != syncer::BOOKMARKS || | |
| 580 GetModelTypeFromSpecifics(updated_specifics) != syncer::BOOKMARKS) { | |
| 581 return false; | |
| 582 } | |
| 583 | |
| 584 BookmarkEntity* entity = static_cast<BookmarkEntity*>(iter->second.get()); | |
| 585 | |
| 586 entity->SetParentId(parent_id); | |
| 587 entity->SetSpecifics(updated_specifics); | |
| 588 if (updated_specifics.has_bookmark()) { | |
| 589 entity->SetName(updated_specifics.bookmark().title()); | |
| 590 } | |
| 591 UpdateEntityVersion(entity); | |
| 592 return true; | |
| 593 } | 171 } |
| 594 | 172 |
| 595 void FakeServer::ClearServerData() { | 173 void FakeServer::ClearServerData() { |
| 596 DCHECK(thread_checker_.CalledOnValidThread()); | 174 DCHECK(thread_checker_.CalledOnValidThread()); |
| 597 entities_.clear(); | 175 loopback_server_->ClearServerData(); |
| 598 keystore_keys_.clear(); | |
| 599 ++store_birthday_; | |
| 600 Init(); | |
| 601 } | 176 } |
| 602 | 177 |
| 603 void FakeServer::SetAuthenticated() { | 178 void FakeServer::SetAuthenticated() { |
| 604 DCHECK(thread_checker_.CalledOnValidThread()); | 179 DCHECK(thread_checker_.CalledOnValidThread()); |
| 605 authenticated_ = true; | 180 authenticated_ = true; |
| 606 } | 181 } |
| 607 | 182 |
| 608 void FakeServer::SetUnauthenticated() { | 183 void FakeServer::SetUnauthenticated() { |
| 609 DCHECK(thread_checker_.CalledOnValidThread()); | 184 DCHECK(thread_checker_.CalledOnValidThread()); |
| 610 authenticated_ = false; | 185 authenticated_ = false; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 668 void FakeServer::AddObserver(Observer* observer) { | 243 void FakeServer::AddObserver(Observer* observer) { |
| 669 DCHECK(thread_checker_.CalledOnValidThread()); | 244 DCHECK(thread_checker_.CalledOnValidThread()); |
| 670 observers_.AddObserver(observer); | 245 observers_.AddObserver(observer); |
| 671 } | 246 } |
| 672 | 247 |
| 673 void FakeServer::RemoveObserver(Observer* observer) { | 248 void FakeServer::RemoveObserver(Observer* observer) { |
| 674 DCHECK(thread_checker_.CalledOnValidThread()); | 249 DCHECK(thread_checker_.CalledOnValidThread()); |
| 675 observers_.RemoveObserver(observer); | 250 observers_.RemoveObserver(observer); |
| 676 } | 251 } |
| 677 | 252 |
| 253 void FakeServer::OnCommit(const std::string& committer_id, |
| 254 syncer::ModelTypeSet committed_model_types) { |
| 255 for (auto& observer : observers_) |
| 256 observer.OnCommit(committer_id, committed_model_types); |
| 257 } |
| 258 |
| 678 void FakeServer::EnableNetwork() { | 259 void FakeServer::EnableNetwork() { |
| 679 DCHECK(thread_checker_.CalledOnValidThread()); | 260 DCHECK(thread_checker_.CalledOnValidThread()); |
| 680 network_enabled_ = true; | 261 network_enabled_ = true; |
| 681 } | 262 } |
| 682 | 263 |
| 683 void FakeServer::DisableNetwork() { | 264 void FakeServer::DisableNetwork() { |
| 684 DCHECK(thread_checker_.CalledOnValidThread()); | 265 DCHECK(thread_checker_.CalledOnValidThread()); |
| 685 network_enabled_ = false; | 266 network_enabled_ = false; |
| 686 } | 267 } |
| 687 | 268 |
| 688 std::string FakeServer::GetBookmarkBarFolderId() const { | |
| 689 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 690 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | |
| 691 ++it) { | |
| 692 FakeServerEntity* entity = it->second.get(); | |
| 693 if (entity->GetName() == kBookmarkBarFolderName && entity->IsFolder() && | |
| 694 entity->model_type() == syncer::BOOKMARKS) { | |
| 695 return entity->id(); | |
| 696 } | |
| 697 } | |
| 698 NOTREACHED() << "Bookmark Bar entity not found."; | |
| 699 return ""; | |
| 700 } | |
| 701 | |
| 702 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() { | 269 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() { |
| 703 DCHECK(thread_checker_.CalledOnValidThread()); | 270 DCHECK(thread_checker_.CalledOnValidThread()); |
| 704 return weak_ptr_factory_.GetWeakPtr(); | 271 return weak_ptr_factory_.GetWeakPtr(); |
| 705 } | 272 } |
| 706 | 273 |
| 707 std::string FakeServer::GetStoreBirthday() const { | |
| 708 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 709 return base::Int64ToString(store_birthday_); | |
| 710 } | |
| 711 | |
| 712 } // namespace fake_server | 274 } // namespace fake_server |
| OLD | NEW |