OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "sync/test/fake_server/fake_server.h" |
| 6 |
| 7 #include <limits> |
| 8 #include <string> |
| 9 #include <vector> |
| 10 |
| 11 #include "base/basictypes.h" |
| 12 #include "base/logging.h" |
| 13 #include "base/memory/scoped_ptr.h" |
| 14 #include "base/strings/string_number_conversions.h" |
| 15 #include "sync/internal_api/public/base/model_type.h" |
| 16 #include "sync/protocol/sync.pb.h" |
| 17 |
| 18 using std::string; |
| 19 |
| 20 // The parent tag for childen of the root node. |
| 21 static const char kRootParentTag[] = "0"; |
| 22 |
| 23 namespace syncer { |
| 24 namespace { |
| 25 |
| 26 // A filter used during GetUpdates calls to determine what information to |
| 27 // send back to the client. There is a 1:1 correspondence between any given |
| 28 // GetUpdates call and an UpdateSieve instance. |
| 29 class UpdateSieve { |
| 30 public: |
| 31 ~UpdateSieve() { } |
| 32 |
| 33 // Factory method for creating an UpdateSieve. |
| 34 static scoped_ptr<UpdateSieve> Create( |
| 35 const sync_pb::GetUpdatesMessage& get_updates_message); |
| 36 |
| 37 // Sets the progress markers in |get_updates_response| given the progress |
| 38 // markers from the original GetUpdatesMessage and |new_version| (the latest |
| 39 // version in the entries sent back). |
| 40 void UpdateProgressMarkers( |
| 41 int64 new_version, |
| 42 sync_pb::GetUpdatesResponse* get_updates_response) const { |
| 43 ModelTypeToVersionMap::const_iterator it; |
| 44 for (it = request_from_version_.begin(); it != request_from_version_.end(); |
| 45 ++it) { |
| 46 sync_pb::DataTypeProgressMarker* new_marker = |
| 47 get_updates_response->add_new_progress_marker(); |
| 48 new_marker->set_data_type_id( |
| 49 GetSpecificsFieldNumberFromModelType(it->first)); |
| 50 |
| 51 int64 version = std::max(new_version, it->second); |
| 52 new_marker->set_token(base::Int64ToString(version)); |
| 53 } |
| 54 } |
| 55 |
| 56 // Determines whether the server should send |entity| to the client based |
| 57 // on its type and version. |
| 58 bool ClientWantsItem(const sync_pb::SyncEntity& entity) const { |
| 59 ModelTypeToVersionMap::const_iterator it = |
| 60 request_from_version_.find(GetModelType(entity)); |
| 61 |
| 62 return it == request_from_version_.end() ? |
| 63 false : it->second < entity.version(); |
| 64 } |
| 65 |
| 66 // Returns the mininum version seen across all types. |
| 67 int64 GetMinVersion() const { |
| 68 return min_version_; |
| 69 } |
| 70 |
| 71 // Returns the data type IDs of types being synced for the first time. |
| 72 std::vector<ModelType> GetFirstTimeTypes() const { |
| 73 std::vector<ModelType> types; |
| 74 |
| 75 ModelTypeToVersionMap::const_iterator it; |
| 76 for (it = request_from_version_.begin(); it != request_from_version_.end(); |
| 77 ++it) { |
| 78 if (it->second == 0) |
| 79 types.push_back(it->first); |
| 80 } |
| 81 |
| 82 return types; |
| 83 } |
| 84 |
| 85 private: |
| 86 typedef std::map<ModelType, int64> ModelTypeToVersionMap; |
| 87 |
| 88 // Creates an UpdateSieve. |
| 89 UpdateSieve(const ModelTypeToVersionMap request_from_version, |
| 90 const int64 min_version) |
| 91 : request_from_version_(request_from_version), |
| 92 min_version_(min_version) { } |
| 93 |
| 94 // Maps data type IDs to the latest version seen for that type. |
| 95 const ModelTypeToVersionMap request_from_version_; |
| 96 |
| 97 // The minimum version seen among all data types. |
| 98 const int min_version_; |
| 99 }; |
| 100 |
| 101 scoped_ptr<UpdateSieve> UpdateSieve::Create( |
| 102 const sync_pb::GetUpdatesMessage& get_updates_message) { |
| 103 DCHECK_GT(get_updates_message.from_progress_marker_size(), 0); |
| 104 |
| 105 UpdateSieve::ModelTypeToVersionMap request_from_version; |
| 106 int64 min_version = std::numeric_limits<int64>::max(); |
| 107 for (int i = 0; i < get_updates_message.from_progress_marker_size(); i++) { |
| 108 sync_pb::DataTypeProgressMarker marker = |
| 109 get_updates_message.from_progress_marker(i); |
| 110 |
| 111 int64 version; |
| 112 if (!marker.has_token() || marker.token().empty()) { |
| 113 // Initialize the version on the first request for this type. |
| 114 version = 0; |
| 115 } else { |
| 116 DCHECK(base::StringToInt64(marker.token(), &version)); |
| 117 } |
| 118 |
| 119 ModelType model_type = GetModelTypeFromSpecificsFieldNumber( |
| 120 marker.data_type_id()); |
| 121 request_from_version[model_type] = version; |
| 122 |
| 123 if (version < min_version) |
| 124 min_version = version; |
| 125 } |
| 126 |
| 127 return scoped_ptr<UpdateSieve>( |
| 128 new UpdateSieve(request_from_version, min_version)); |
| 129 } |
| 130 |
| 131 } // namespace |
| 132 |
| 133 FakeServer::FakeServer() : version_(0), birthday_("1234567890") { |
| 134 keystore_keys_.push_back("1111111111111111"); |
| 135 } |
| 136 |
| 137 FakeServer::~FakeServer() { } |
| 138 |
| 139 void FakeServer::CreateDefaultPermanentItems( |
| 140 const std::vector<ModelType>& first_time_types) { |
| 141 for (std::vector<ModelType>::const_iterator it = first_time_types.begin(); |
| 142 it != first_time_types.end(); ++it) { |
| 143 if (!ModelTypeSet::All().Has(*it)) { |
| 144 NOTREACHED() << "An unexpected ModelType was encountered."; |
| 145 } |
| 146 |
| 147 ModelType model_type = *it; |
| 148 CreateSyncEntity(model_type, |
| 149 ModelTypeToRootTag(model_type), |
| 150 ModelTypeToString(model_type), |
| 151 kRootParentTag); |
| 152 |
| 153 if (model_type == BOOKMARKS) { |
| 154 CreateSyncEntity(BOOKMARKS, |
| 155 "bookmark_bar", |
| 156 "Bookmark Bar", |
| 157 ModelTypeToRootTag(BOOKMARKS)); |
| 158 CreateSyncEntity(BOOKMARKS, |
| 159 "other_bookmarks", |
| 160 "Other Bookmarks", |
| 161 ModelTypeToRootTag(BOOKMARKS)); |
| 162 } |
| 163 } |
| 164 |
| 165 |
| 166 // TODO(pvalenzuela): Create the mobile bookmarks folder when the fake server |
| 167 // is used by mobile tests. |
| 168 } |
| 169 |
| 170 void FakeServer::CreateSyncEntity(ModelType model_type, |
| 171 const std::string& id, |
| 172 const std::string& name, |
| 173 const std::string& parent_tag) { |
| 174 DCHECK(!id.empty()); |
| 175 DCHECK(!name.empty()); |
| 176 DCHECK(!parent_tag.empty()); |
| 177 |
| 178 sync_pb::SyncEntity entity; |
| 179 entity.set_id_string(id); |
| 180 entity.set_non_unique_name(name); |
| 181 entity.set_name(name); |
| 182 entity.set_server_defined_unique_tag(id); |
| 183 entity.set_folder(true); |
| 184 entity.set_deleted(false); |
| 185 |
| 186 entity.set_parent_id_string(parent_tag); |
| 187 |
| 188 if (parent_tag != kRootParentTag && model_type == BOOKMARKS) { |
| 189 // Use a dummy value here. |
| 190 entity.set_position_in_parent(1337); |
| 191 } |
| 192 |
| 193 sync_pb::EntitySpecifics* specifics = entity.mutable_specifics(); |
| 194 AddDefaultFieldValue(model_type, specifics); |
| 195 |
| 196 SaveEntity(entity); |
| 197 } |
| 198 |
| 199 void FakeServer::SaveEntity(sync_pb::SyncEntity entity) { |
| 200 version_++; |
| 201 entity.set_version(version_); |
| 202 entity.set_sync_timestamp(version_); |
| 203 |
| 204 sync_pb::SyncEntity original_entity = entities_[entity.id_string()]; |
| 205 entity.set_originator_cache_guid(original_entity.originator_cache_guid()); |
| 206 entity.set_originator_client_item_id( |
| 207 original_entity.originator_client_item_id()); |
| 208 |
| 209 entities_[entity.id_string()] = entity; |
| 210 } |
| 211 |
| 212 int FakeServer::HandleCommand(string request, |
| 213 int* response_code, |
| 214 string* response) { |
| 215 sync_pb::ClientToServerMessage message; |
| 216 DCHECK(message.ParseFromString(request)); |
| 217 |
| 218 sync_pb::ClientToServerResponse response_proto; |
| 219 switch (message.message_contents()) { |
| 220 case sync_pb::ClientToServerMessage::GET_UPDATES: |
| 221 response_proto = HandleGetUpdatesRequest(message); |
| 222 break; |
| 223 case sync_pb::ClientToServerMessage::COMMIT: |
| 224 response_proto = HandleCommitRequest(message); |
| 225 break; |
| 226 default: |
| 227 // Any nonzero int will indicate a failure here. This one happens to |
| 228 // be net::ERR_NOT_IMPLEMENTED. |
| 229 return -11; |
| 230 } |
| 231 |
| 232 *response_code = 200; |
| 233 *response = response_proto.SerializeAsString(); |
| 234 return 0; |
| 235 } |
| 236 |
| 237 bool SyncEntityVersionComparator(const sync_pb::SyncEntity& first, |
| 238 const sync_pb::SyncEntity& second) { |
| 239 return first.version() < second.version(); |
| 240 } |
| 241 |
| 242 sync_pb::ClientToServerResponse FakeServer::HandleGetUpdatesRequest( |
| 243 const sync_pb::ClientToServerMessage& message) { |
| 244 sync_pb::ClientToServerResponse response; |
| 245 response.set_error_code(sync_pb::SyncEnums::SUCCESS); |
| 246 response.set_store_birthday(birthday_); |
| 247 |
| 248 sync_pb::GetUpdatesResponse* get_updates_response = |
| 249 response.mutable_get_updates(); |
| 250 // TODO(pvalenzuela): Implement batching instead of sending all information |
| 251 // at once. |
| 252 get_updates_response->set_changes_remaining(0); |
| 253 |
| 254 scoped_ptr<UpdateSieve> sieve = UpdateSieve::Create(message.get_updates()); |
| 255 CreateDefaultPermanentItems(sieve->GetFirstTimeTypes()); |
| 256 |
| 257 int64 min_version = sieve->GetMinVersion(); |
| 258 |
| 259 bool send_encryption_keys_based_on_nigori = false; |
| 260 int64 max_response_version = 0; |
| 261 for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); |
| 262 ++it) { |
| 263 sync_pb::SyncEntity entity = it->second; |
| 264 if (entity.version() > min_version && sieve->ClientWantsItem(entity)) { |
| 265 sync_pb::SyncEntity* response_entity = |
| 266 get_updates_response->add_entries(); |
| 267 response_entity->CopyFrom(entity); |
| 268 max_response_version = std::max(max_response_version, |
| 269 response_entity->version()); |
| 270 |
| 271 if (response_entity->name() == ModelTypeToString(NIGORI)) { |
| 272 send_encryption_keys_based_on_nigori = |
| 273 response_entity->specifics().nigori().passphrase_type() == |
| 274 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE; |
| 275 } |
| 276 } |
| 277 } |
| 278 |
| 279 if (send_encryption_keys_based_on_nigori || |
| 280 message.get_updates().need_encryption_key()) { |
| 281 for (std::vector<std::string>::iterator it = keystore_keys_.begin(); |
| 282 it != keystore_keys_.end(); ++it) { |
| 283 get_updates_response->add_encryption_keys(*it); |
| 284 } |
| 285 } |
| 286 |
| 287 sieve->UpdateProgressMarkers(max_response_version, get_updates_response); |
| 288 |
| 289 return response; |
| 290 } |
| 291 |
| 292 sync_pb::SyncEntity FakeServer::CommitEntity(sync_pb::SyncEntity entity, |
| 293 string guid) { |
| 294 // TODO(pvalenzuela): Implement this. Right now this method cheats and |
| 295 // doesn't actually commit. |
| 296 return entity; |
| 297 } |
| 298 |
| 299 sync_pb::ClientToServerResponse FakeServer::HandleCommitRequest( |
| 300 const sync_pb::ClientToServerMessage& message) { |
| 301 sync_pb::ClientToServerResponse response; |
| 302 response.set_error_code(sync_pb::SyncEnums::SUCCESS); |
| 303 response.set_store_birthday(birthday_); |
| 304 |
| 305 sync_pb::CommitMessage commit = message.commit(); |
| 306 string guid = commit.cache_guid(); |
| 307 |
| 308 sync_pb::CommitResponse* commit_response = response.mutable_commit(); |
| 309 |
| 310 ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it; |
| 311 for (it = commit.entries().begin(); it != commit.entries().end(); ++it) { |
| 312 sync_pb::CommitResponse_EntryResponse* entry_response = |
| 313 commit_response->add_entryresponse(); |
| 314 |
| 315 sync_pb::SyncEntity server_entity = CommitEntity(*it, guid); |
| 316 |
| 317 entry_response->set_id_string(server_entity.id_string()); |
| 318 entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS); |
| 319 entry_response->set_version(it->version() + 1); |
| 320 } |
| 321 |
| 322 return response; |
| 323 } |
| 324 |
| 325 } // namespace syncer |
OLD | NEW |