| 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 "base/threading/thread_restrictions.h" | 20 #include "components/sync/test/fake_server/bookmark_entity.h" |
| 21 #include "components/sync/engine_impl/net/server_connection_manager.h" | 21 #include "components/sync/test/fake_server/permanent_entity.h" |
| 22 #include "components/sync/test/fake_server/tombstone_entity.h" |
| 23 #include "components/sync/test/fake_server/unique_client_entity.h" |
| 22 #include "net/base/net_errors.h" | 24 #include "net/base/net_errors.h" |
| 23 #include "net/http/http_status_code.h" | 25 #include "net/http/http_status_code.h" |
| 24 | 26 |
| 25 using std::string; | 27 using std::string; |
| 26 using std::vector; | 28 using std::vector; |
| 27 using syncer::GetModelType; | 29 using syncer::GetModelType; |
| 28 using syncer::GetModelTypeFromSpecifics; | 30 using syncer::GetModelTypeFromSpecifics; |
| 29 using syncer::LoopbackServerEntity; | |
| 30 using syncer::ModelType; | 31 using syncer::ModelType; |
| 31 using syncer::ModelTypeSet; | 32 using syncer::ModelTypeSet; |
| 32 | 33 |
| 33 namespace fake_server { | 34 namespace fake_server { |
| 34 | 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 |
| 35 FakeServer::FakeServer() | 141 FakeServer::FakeServer() |
| 36 : authenticated_(true), | 142 : version_(0), |
| 143 store_birthday_(0), |
| 144 authenticated_(true), |
| 37 error_type_(sync_pb::SyncEnums::SUCCESS), | 145 error_type_(sync_pb::SyncEnums::SUCCESS), |
| 38 alternate_triggered_errors_(false), | 146 alternate_triggered_errors_(false), |
| 39 request_counter_(0), | 147 request_counter_(0), |
| 40 network_enabled_(true), | 148 network_enabled_(true), |
| 41 weak_ptr_factory_(this) { | 149 weak_ptr_factory_(this) { |
| 42 loopback_server_storage_ = base::MakeUnique<base::ScopedTempDir>(); | 150 Init(); |
| 43 base::ThreadRestrictions::SetIOAllowed(true); | |
| 44 DCHECK(loopback_server_storage_->CreateUniqueTempDir()); | |
| 45 loopback_server_ = base::MakeUnique<syncer::LoopbackServer>( | |
| 46 loopback_server_storage_->GetPath().AppendASCII("profile.pb")); | |
| 47 loopback_server_->set_observer_for_tests(this); | |
| 48 } | 151 } |
| 49 | 152 |
| 50 FakeServer::~FakeServer() {} | 153 FakeServer::~FakeServer() {} |
| 51 | 154 |
| 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 |
| 52 void FakeServer::HandleCommand(const string& request, | 214 void FakeServer::HandleCommand(const string& request, |
| 53 const base::Closure& completion_closure, | 215 const base::Closure& completion_closure, |
| 54 int* error_code, | 216 int* error_code, |
| 55 int* response_code, | 217 int* response_code, |
| 56 std::string* response) { | 218 std::string* response) { |
| 57 DCHECK(thread_checker_.CalledOnValidThread()); | 219 DCHECK(thread_checker_.CalledOnValidThread()); |
| 58 | |
| 59 if (!network_enabled_) { | 220 if (!network_enabled_) { |
| 60 *error_code = net::ERR_FAILED; | 221 *error_code = net::ERR_FAILED; |
| 61 *response_code = net::ERR_FAILED; | 222 *response_code = net::ERR_FAILED; |
| 62 *response = string(); | 223 *response = string(); |
| 63 completion_closure.Run(); | 224 completion_closure.Run(); |
| 64 return; | 225 return; |
| 65 } | 226 } |
| 66 request_counter_++; | 227 request_counter_++; |
| 67 | 228 |
| 68 if (!authenticated_) { | 229 if (!authenticated_) { |
| 69 *error_code = 0; | 230 *error_code = 0; |
| 70 *response_code = net::HTTP_UNAUTHORIZED; | 231 *response_code = net::HTTP_UNAUTHORIZED; |
| 71 *response = string(); | 232 *response = string(); |
| 72 completion_closure.Run(); | 233 completion_closure.Run(); |
| 73 return; | 234 return; |
| 74 } | 235 } |
| 75 | 236 |
| 237 sync_pb::ClientToServerMessage message; |
| 238 bool parsed = message.ParseFromString(request); |
| 239 CHECK(parsed) << "Unable to parse the ClientToServerMessage."; |
| 240 |
| 76 sync_pb::ClientToServerResponse response_proto; | 241 sync_pb::ClientToServerResponse response_proto; |
| 77 *response_code = 200; | 242 |
| 78 *error_code = 0; | 243 if (message.has_store_birthday() && |
| 79 if (error_type_ != sync_pb::SyncEnums::SUCCESS && | 244 message.store_birthday() != GetStoreBirthday()) { |
| 80 ShouldSendTriggeredError()) { | 245 response_proto.set_error_code(sync_pb::SyncEnums::NOT_MY_BIRTHDAY); |
| 246 } else if (error_type_ != sync_pb::SyncEnums::SUCCESS && |
| 247 ShouldSendTriggeredError()) { |
| 81 response_proto.set_error_code(error_type_); | 248 response_proto.set_error_code(error_type_); |
| 82 } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) { | 249 } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) { |
| 83 sync_pb::ClientToServerResponse_Error* error = | 250 sync_pb::ClientToServerResponse_Error* error = |
| 84 response_proto.mutable_error(); | 251 response_proto.mutable_error(); |
| 85 error->CopyFrom(*(triggered_actionable_error_.get())); | 252 error->CopyFrom(*(triggered_actionable_error_.get())); |
| 86 } else { | 253 } else { |
| 87 sync_pb::ClientToServerMessage message; | 254 bool success = false; |
| 88 bool parsed = message.ParseFromString(request); | |
| 89 CHECK(parsed) << "Unable to parse the ClientToServerMessage."; | |
| 90 switch (message.message_contents()) { | 255 switch (message.message_contents()) { |
| 91 case sync_pb::ClientToServerMessage::GET_UPDATES: | 256 case sync_pb::ClientToServerMessage::GET_UPDATES: |
| 92 last_getupdates_message_ = message; | 257 last_getupdates_message_ = message; |
| 258 success = HandleGetUpdatesRequest(message.get_updates(), |
| 259 response_proto.mutable_get_updates()); |
| 93 break; | 260 break; |
| 94 case sync_pb::ClientToServerMessage::COMMIT: | 261 case sync_pb::ClientToServerMessage::COMMIT: |
| 95 last_commit_message_ = message; | 262 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; |
| 96 break; | 271 break; |
| 97 default: | 272 default: |
| 98 break; | 273 *error_code = net::ERR_NOT_IMPLEMENTED; |
| 99 // Don't care. | 274 *response_code = 0; |
| 275 *response = string(); |
| 276 completion_closure.Run(); |
| 277 return; |
| 100 } | 278 } |
| 101 | 279 |
| 102 int64_t response_code_large; | 280 if (!success) { |
| 103 syncer::HttpResponse::ServerConnectionCode server_status; | 281 // TODO(pvalenzuela): Add logging here so that tests have more info about |
| 104 base::ThreadRestrictions::SetIOAllowed(true); | 282 // the failure. |
| 105 loopback_server_->HandleCommand(request, &server_status, | 283 *error_code = net::ERR_FAILED; |
| 106 &response_code_large, response); | 284 *response_code = 0; |
| 107 *response_code = static_cast<int>(response_code_large); | 285 *response = string(); |
| 108 completion_closure.Run(); | 286 completion_closure.Run(); |
| 109 return; | 287 return; |
| 288 } |
| 289 |
| 290 response_proto.set_error_code(sync_pb::SyncEnums::SUCCESS); |
| 110 } | 291 } |
| 111 | 292 |
| 112 response_proto.set_store_birthday(loopback_server_->GetStoreBirthday()); | 293 response_proto.set_store_birthday(GetStoreBirthday()); |
| 294 |
| 295 *error_code = 0; |
| 296 *response_code = net::HTTP_OK; |
| 113 *response = response_proto.SerializeAsString(); | 297 *response = response_proto.SerializeAsString(); |
| 114 completion_closure.Run(); | 298 completion_closure.Run(); |
| 115 } | 299 } |
| 116 | 300 |
| 117 bool FakeServer::GetLastCommitMessage(sync_pb::ClientToServerMessage* message) { | 301 bool FakeServer::GetLastCommitMessage(sync_pb::ClientToServerMessage* message) { |
| 118 if (!last_commit_message_.has_commit()) | 302 if (!last_commit_message_.has_commit()) |
| 119 return false; | 303 return false; |
| 120 | 304 |
| 121 message->CopyFrom(last_commit_message_); | 305 message->CopyFrom(last_commit_message_); |
| 122 return true; | 306 return true; |
| 123 } | 307 } |
| 124 | 308 |
| 125 bool FakeServer::GetLastGetUpdatesMessage( | 309 bool FakeServer::GetLastGetUpdatesMessage( |
| 126 sync_pb::ClientToServerMessage* message) { | 310 sync_pb::ClientToServerMessage* message) { |
| 127 if (!last_getupdates_message_.has_get_updates()) | 311 if (!last_getupdates_message_.has_get_updates()) |
| 128 return false; | 312 return false; |
| 129 | 313 |
| 130 message->CopyFrom(last_getupdates_message_); | 314 message->CopyFrom(last_getupdates_message_); |
| 131 return true; | 315 return true; |
| 132 } | 316 } |
| 133 | 317 |
| 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 |
| 134 std::unique_ptr<base::DictionaryValue> | 498 std::unique_ptr<base::DictionaryValue> |
| 135 FakeServer::GetEntitiesAsDictionaryValue() { | 499 FakeServer::GetEntitiesAsDictionaryValue() { |
| 136 DCHECK(thread_checker_.CalledOnValidThread()); | 500 DCHECK(thread_checker_.CalledOnValidThread()); |
| 137 return loopback_server_->GetEntitiesAsDictionaryValue(); | 501 std::unique_ptr<base::DictionaryValue> dictionary( |
| 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()), |
| 508 base::MakeUnique<base::ListValue>()); |
| 509 } |
| 510 |
| 511 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); |
| 512 ++it) { |
| 513 const FakeServerEntity& entity = *it->second; |
| 514 if (IsDeletedOrPermanent(entity)) { |
| 515 // Tombstones are ignored as they don't represent current data. Folders |
| 516 // are also ignored as current verification infrastructure does not |
| 517 // consider them. |
| 518 continue; |
| 519 } |
| 520 base::ListValue* list_value; |
| 521 if (!dictionary->GetList(ModelTypeToString(entity.model_type()), |
| 522 &list_value)) { |
| 523 return std::unique_ptr<base::DictionaryValue>(); |
| 524 } |
| 525 // TODO(pvalenzuela): Store more data for each entity so additional |
| 526 // verification can be performed. One example of additional verification |
| 527 // is checking the correctness of the bookmark hierarchy. |
| 528 list_value->AppendString(entity.GetName()); |
| 529 } |
| 530 |
| 531 return dictionary; |
| 138 } | 532 } |
| 139 | 533 |
| 140 std::vector<sync_pb::SyncEntity> FakeServer::GetSyncEntitiesByModelType( | 534 std::vector<sync_pb::SyncEntity> FakeServer::GetSyncEntitiesByModelType( |
| 141 ModelType model_type) { | 535 ModelType model_type) { |
| 142 DCHECK(thread_checker_.CalledOnValidThread()); | 536 std::vector<sync_pb::SyncEntity> sync_entities; |
| 143 return loopback_server_->GetSyncEntitiesByModelType(model_type); | 537 DCHECK(thread_checker_.CalledOnValidThread()); |
| 144 } | 538 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); |
| 145 | 539 ++it) { |
| 146 void FakeServer::InjectEntity(std::unique_ptr<LoopbackServerEntity> entity) { | 540 const FakeServerEntity& entity = *it->second; |
| 147 DCHECK(thread_checker_.CalledOnValidThread()); | 541 if (!IsDeletedOrPermanent(entity) && entity.model_type() == model_type) { |
| 148 loopback_server_->SaveEntity(std::move(entity)); | 542 sync_pb::SyncEntity sync_entity; |
| 543 entity.SerializeAsProto(&sync_entity); |
| 544 sync_entities.push_back(sync_entity); |
| 545 } |
| 546 } |
| 547 return sync_entities; |
| 548 } |
| 549 |
| 550 void FakeServer::InjectEntity(std::unique_ptr<FakeServerEntity> entity) { |
| 551 DCHECK(thread_checker_.CalledOnValidThread()); |
| 552 SaveEntity(std::move(entity)); |
| 149 } | 553 } |
| 150 | 554 |
| 151 bool FakeServer::ModifyEntitySpecifics( | 555 bool FakeServer::ModifyEntitySpecifics( |
| 152 const std::string& id, | 556 const std::string& id, |
| 153 const sync_pb::EntitySpecifics& updated_specifics) { | 557 const sync_pb::EntitySpecifics& updated_specifics) { |
| 154 return loopback_server_->ModifyEntitySpecifics(id, updated_specifics); | 558 EntityMap::const_iterator iter = entities_.find(id); |
| 559 if (iter == entities_.end() || |
| 560 iter->second->model_type() != |
| 561 GetModelTypeFromSpecifics(updated_specifics)) { |
| 562 return false; |
| 563 } |
| 564 |
| 565 FakeServerEntity* entity = iter->second.get(); |
| 566 entity->SetSpecifics(updated_specifics); |
| 567 UpdateEntityVersion(entity); |
| 568 return true; |
| 155 } | 569 } |
| 156 | 570 |
| 157 bool FakeServer::ModifyBookmarkEntity( | 571 bool FakeServer::ModifyBookmarkEntity( |
| 158 const std::string& id, | 572 const std::string& id, |
| 159 const std::string& parent_id, | 573 const std::string& parent_id, |
| 160 const sync_pb::EntitySpecifics& updated_specifics) { | 574 const sync_pb::EntitySpecifics& updated_specifics) { |
| 161 return loopback_server_->ModifyBookmarkEntity(id, parent_id, | 575 EntityMap::const_iterator iter = entities_.find(id); |
| 162 updated_specifics); | 576 if (iter == entities_.end() || |
| 577 iter->second->model_type() != syncer::BOOKMARKS || |
| 578 GetModelTypeFromSpecifics(updated_specifics) != syncer::BOOKMARKS) { |
| 579 return false; |
| 580 } |
| 581 |
| 582 BookmarkEntity* entity = static_cast<BookmarkEntity*>(iter->second.get()); |
| 583 |
| 584 entity->SetParentId(parent_id); |
| 585 entity->SetSpecifics(updated_specifics); |
| 586 if (updated_specifics.has_bookmark()) { |
| 587 entity->SetName(updated_specifics.bookmark().title()); |
| 588 } |
| 589 UpdateEntityVersion(entity); |
| 590 return true; |
| 163 } | 591 } |
| 164 | 592 |
| 165 void FakeServer::ClearServerData() { | 593 void FakeServer::ClearServerData() { |
| 166 DCHECK(thread_checker_.CalledOnValidThread()); | 594 DCHECK(thread_checker_.CalledOnValidThread()); |
| 167 loopback_server_->ClearServerData(); | 595 entities_.clear(); |
| 596 keystore_keys_.clear(); |
| 597 ++store_birthday_; |
| 598 Init(); |
| 168 } | 599 } |
| 169 | 600 |
| 170 void FakeServer::SetAuthenticated() { | 601 void FakeServer::SetAuthenticated() { |
| 171 DCHECK(thread_checker_.CalledOnValidThread()); | 602 DCHECK(thread_checker_.CalledOnValidThread()); |
| 172 authenticated_ = true; | 603 authenticated_ = true; |
| 173 } | 604 } |
| 174 | 605 |
| 175 void FakeServer::SetUnauthenticated() { | 606 void FakeServer::SetUnauthenticated() { |
| 176 DCHECK(thread_checker_.CalledOnValidThread()); | 607 DCHECK(thread_checker_.CalledOnValidThread()); |
| 177 authenticated_ = false; | 608 authenticated_ = false; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 void FakeServer::AddObserver(Observer* observer) { | 666 void FakeServer::AddObserver(Observer* observer) { |
| 236 DCHECK(thread_checker_.CalledOnValidThread()); | 667 DCHECK(thread_checker_.CalledOnValidThread()); |
| 237 observers_.AddObserver(observer); | 668 observers_.AddObserver(observer); |
| 238 } | 669 } |
| 239 | 670 |
| 240 void FakeServer::RemoveObserver(Observer* observer) { | 671 void FakeServer::RemoveObserver(Observer* observer) { |
| 241 DCHECK(thread_checker_.CalledOnValidThread()); | 672 DCHECK(thread_checker_.CalledOnValidThread()); |
| 242 observers_.RemoveObserver(observer); | 673 observers_.RemoveObserver(observer); |
| 243 } | 674 } |
| 244 | 675 |
| 245 void FakeServer::OnCommit(const std::string& committer_id, | |
| 246 syncer::ModelTypeSet committed_model_types) { | |
| 247 for (auto& observer : observers_) | |
| 248 observer.OnCommit(committer_id, committed_model_types); | |
| 249 } | |
| 250 | |
| 251 void FakeServer::EnableNetwork() { | 676 void FakeServer::EnableNetwork() { |
| 252 DCHECK(thread_checker_.CalledOnValidThread()); | 677 DCHECK(thread_checker_.CalledOnValidThread()); |
| 253 network_enabled_ = true; | 678 network_enabled_ = true; |
| 254 } | 679 } |
| 255 | 680 |
| 256 void FakeServer::DisableNetwork() { | 681 void FakeServer::DisableNetwork() { |
| 257 DCHECK(thread_checker_.CalledOnValidThread()); | 682 DCHECK(thread_checker_.CalledOnValidThread()); |
| 258 network_enabled_ = false; | 683 network_enabled_ = false; |
| 259 } | 684 } |
| 260 | 685 |
| 686 std::string FakeServer::GetBookmarkBarFolderId() const { |
| 687 DCHECK(thread_checker_.CalledOnValidThread()); |
| 688 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); |
| 689 ++it) { |
| 690 FakeServerEntity* entity = it->second.get(); |
| 691 if (entity->GetName() == kBookmarkBarFolderName && entity->IsFolder() && |
| 692 entity->model_type() == syncer::BOOKMARKS) { |
| 693 return entity->id(); |
| 694 } |
| 695 } |
| 696 NOTREACHED() << "Bookmark Bar entity not found."; |
| 697 return ""; |
| 698 } |
| 699 |
| 261 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() { | 700 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() { |
| 262 DCHECK(thread_checker_.CalledOnValidThread()); | 701 DCHECK(thread_checker_.CalledOnValidThread()); |
| 263 return weak_ptr_factory_.GetWeakPtr(); | 702 return weak_ptr_factory_.GetWeakPtr(); |
| 264 } | 703 } |
| 265 | 704 |
| 705 std::string FakeServer::GetStoreBirthday() const { |
| 706 DCHECK(thread_checker_.CalledOnValidThread()); |
| 707 return base::Int64ToString(store_birthday_); |
| 708 } |
| 709 |
| 266 } // namespace fake_server | 710 } // namespace fake_server |
| OLD | NEW |