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