ServiceWorker: Consolidate version manipulation functions in SWProviderContext
[chromium-blink-merge.git] / sync / test / fake_server / fake_server.cc
blob45d03b4f3c7d163539e33595c00e806c230d18d0
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
3 // found in the LICENSE file.
5 #include "sync/test/fake_server/fake_server.h"
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10 #include <vector>
12 #include "base/basictypes.h"
13 #include "base/guid.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/synchronization/lock.h"
21 #include "net/base/net_errors.h"
22 #include "net/http/http_status_code.h"
23 #include "sync/internal_api/public/base/model_type.h"
24 #include "sync/protocol/sync.pb.h"
25 #include "sync/test/fake_server/bookmark_entity.h"
26 #include "sync/test/fake_server/permanent_entity.h"
27 #include "sync/test/fake_server/tombstone_entity.h"
28 #include "sync/test/fake_server/unique_client_entity.h"
30 using std::string;
31 using std::vector;
33 using syncer::GetModelType;
34 using syncer::ModelType;
35 using syncer::ModelTypeSet;
37 // The default store birthday value.
38 static const char kDefaultStoreBirthday[] = "1234567890";
40 // The default keystore key.
41 static const char kDefaultKeystoreKey[] = "1111111111111111";
43 namespace fake_server {
45 class FakeServerEntity;
47 namespace {
49 // A filter used during GetUpdates calls to determine what information to
50 // send back to the client. There is a 1:1 correspondence between any given
51 // GetUpdates call and an UpdateSieve instance.
52 class UpdateSieve {
53 public:
54 ~UpdateSieve() { }
56 // Factory method for creating an UpdateSieve.
57 static scoped_ptr<UpdateSieve> Create(
58 const sync_pb::GetUpdatesMessage& get_updates_message);
60 // Sets the progress markers in |get_updates_response| given the progress
61 // markers from the original GetUpdatesMessage and |new_version| (the latest
62 // version in the entries sent back).
63 void UpdateProgressMarkers(
64 int64 new_version,
65 sync_pb::GetUpdatesResponse* get_updates_response) const {
66 ModelTypeToVersionMap::const_iterator it;
67 for (it = request_from_version_.begin(); it != request_from_version_.end();
68 ++it) {
69 sync_pb::DataTypeProgressMarker* new_marker =
70 get_updates_response->add_new_progress_marker();
71 new_marker->set_data_type_id(
72 GetSpecificsFieldNumberFromModelType(it->first));
74 int64 version = std::max(new_version, it->second);
75 new_marker->set_token(base::Int64ToString(version));
79 // Determines whether the server should send an |entity| to the client as
80 // part of a GetUpdatesResponse.
81 bool ClientWantsItem(FakeServerEntity* entity) const {
82 int64 version = entity->GetVersion();
83 if (version <= min_version_) {
84 return false;
85 } else if (entity->IsDeleted()) {
86 return true;
89 ModelTypeToVersionMap::const_iterator it =
90 request_from_version_.find(entity->GetModelType());
92 return it == request_from_version_.end() ? false : it->second < version;
95 // Returns the minimum version seen across all types.
96 int64 GetMinVersion() const {
97 return min_version_;
100 private:
101 typedef std::map<ModelType, int64> ModelTypeToVersionMap;
103 // Creates an UpdateSieve.
104 UpdateSieve(const ModelTypeToVersionMap request_from_version,
105 const int64 min_version)
106 : request_from_version_(request_from_version),
107 min_version_(min_version) { }
109 // Maps data type IDs to the latest version seen for that type.
110 const ModelTypeToVersionMap request_from_version_;
112 // The minimum version seen among all data types.
113 const int min_version_;
116 scoped_ptr<UpdateSieve> UpdateSieve::Create(
117 const sync_pb::GetUpdatesMessage& get_updates_message) {
118 CHECK_GT(get_updates_message.from_progress_marker_size(), 0)
119 << "A GetUpdates request must have at least one progress marker.";
121 UpdateSieve::ModelTypeToVersionMap request_from_version;
122 int64 min_version = std::numeric_limits<int64>::max();
123 for (int i = 0; i < get_updates_message.from_progress_marker_size(); i++) {
124 sync_pb::DataTypeProgressMarker marker =
125 get_updates_message.from_progress_marker(i);
127 int64 version = 0;
128 // Let the version remain zero if there is no token or an empty token (the
129 // first request for this type).
130 if (marker.has_token() && !marker.token().empty()) {
131 bool parsed = base::StringToInt64(marker.token(), &version);
132 CHECK(parsed) << "Unable to parse progress marker token.";
135 ModelType model_type = syncer::GetModelTypeFromSpecificsFieldNumber(
136 marker.data_type_id());
137 request_from_version[model_type] = version;
139 if (version < min_version)
140 min_version = version;
143 return scoped_ptr<UpdateSieve>(
144 new UpdateSieve(request_from_version, min_version));
147 } // namespace
149 FakeServer::FakeServer() : version_(0),
150 store_birthday_(kDefaultStoreBirthday),
151 authenticated_(true),
152 error_type_(sync_pb::SyncEnums::SUCCESS),
153 alternate_triggered_errors_(false),
154 request_counter_(0),
155 network_enabled_(true) {
156 keystore_keys_.push_back(kDefaultKeystoreKey);
157 CHECK(CreateDefaultPermanentItems());
160 FakeServer::~FakeServer() {
161 STLDeleteContainerPairSecondPointers(entities_.begin(), entities_.end());
164 bool FakeServer::CreatePermanentBookmarkFolder(const char* server_tag,
165 const char* name) {
166 FakeServerEntity* entity =
167 PermanentEntity::Create(syncer::BOOKMARKS, server_tag, name,
168 ModelTypeToRootTag(syncer::BOOKMARKS));
169 if (entity == NULL)
170 return false;
172 SaveEntity(entity);
173 return true;
176 bool FakeServer::CreateDefaultPermanentItems() {
177 ModelTypeSet all_types = syncer::ProtocolTypes();
178 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
179 ModelType model_type = it.Get();
180 FakeServerEntity* top_level_entity =
181 PermanentEntity::CreateTopLevel(model_type);
182 if (top_level_entity == NULL) {
183 return false;
185 SaveEntity(top_level_entity);
187 if (model_type == syncer::BOOKMARKS) {
188 if (!CreatePermanentBookmarkFolder("bookmark_bar", "Bookmark Bar"))
189 return false;
190 if (!CreatePermanentBookmarkFolder("other_bookmarks", "Other Bookmarks"))
191 return false;
195 return true;
198 bool FakeServer::CreateMobileBookmarksPermanentItem() {
199 // This folder is called "Synced Bookmarks" by sync and is renamed
200 // "Mobile Bookmarks" by the mobile client UIs.
201 FakeServerEntity* mobile_bookmarks_entity =
202 PermanentEntity::Create(syncer::BOOKMARKS,
203 "synced_bookmarks",
204 "Synced Bookmarks",
205 ModelTypeToRootTag(syncer::BOOKMARKS));
206 if (mobile_bookmarks_entity == NULL) {
207 return false;
209 SaveEntity(mobile_bookmarks_entity);
210 return true;
213 void FakeServer::SaveEntity(FakeServerEntity* entity) {
214 delete entities_[entity->GetId()];
215 entity->SetVersion(++version_);
216 entities_[entity->GetId()] = entity;
219 void FakeServer::HandleCommand(const string& request,
220 const HandleCommandCallback& callback) {
221 if (!network_enabled_) {
222 callback.Run(net::ERR_FAILED, net::ERR_FAILED, string());
223 return;
225 request_counter_++;
227 if (!authenticated_) {
228 callback.Run(0, net::HTTP_UNAUTHORIZED, string());
229 return;
232 sync_pb::ClientToServerMessage message;
233 bool parsed = message.ParseFromString(request);
234 CHECK(parsed) << "Unable to parse the ClientToServerMessage.";
236 sync_pb::ClientToServerResponse response_proto;
238 if (message.has_store_birthday() &&
239 message.store_birthday() != store_birthday_) {
240 response_proto.set_error_code(sync_pb::SyncEnums::NOT_MY_BIRTHDAY);
241 } else if (error_type_ != sync_pb::SyncEnums::SUCCESS &&
242 ShouldSendTriggeredError()) {
243 response_proto.set_error_code(error_type_);
244 } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) {
245 sync_pb::ClientToServerResponse_Error* error =
246 response_proto.mutable_error();
247 error->CopyFrom(*(triggered_actionable_error_.get()));
248 } else {
249 bool success = false;
250 switch (message.message_contents()) {
251 case sync_pb::ClientToServerMessage::GET_UPDATES:
252 success = HandleGetUpdatesRequest(message.get_updates(),
253 response_proto.mutable_get_updates());
254 break;
255 case sync_pb::ClientToServerMessage::COMMIT:
256 success = HandleCommitRequest(message.commit(),
257 message.invalidator_client_id(),
258 response_proto.mutable_commit());
259 break;
260 default:
261 callback.Run(net::ERR_NOT_IMPLEMENTED, 0, string());;
262 return;
265 if (!success) {
266 // TODO(pvalenzuela): Add logging here so that tests have more info about
267 // the failure.
268 callback.Run(net::ERR_FAILED, 0, string());
269 return;
272 response_proto.set_error_code(sync_pb::SyncEnums::SUCCESS);
275 response_proto.set_store_birthday(store_birthday_);
276 callback.Run(0, net::HTTP_OK, response_proto.SerializeAsString());
279 bool FakeServer::HandleGetUpdatesRequest(
280 const sync_pb::GetUpdatesMessage& get_updates,
281 sync_pb::GetUpdatesResponse* response) {
282 // TODO(pvalenzuela): Implement batching instead of sending all information
283 // at once.
284 response->set_changes_remaining(0);
286 scoped_ptr<UpdateSieve> sieve = UpdateSieve::Create(get_updates);
288 if (get_updates.create_mobile_bookmarks_folder() &&
289 !CreateMobileBookmarksPermanentItem()) {
290 return false;
293 bool send_encryption_keys_based_on_nigori = false;
294 int64 max_response_version = 0;
295 for (EntityMap::iterator it = entities_.begin(); it != entities_.end();
296 ++it) {
297 FakeServerEntity* entity = it->second;
298 if (sieve->ClientWantsItem(entity)) {
299 sync_pb::SyncEntity* response_entity = response->add_entries();
300 entity->SerializeAsProto(response_entity);
301 max_response_version = std::max(max_response_version,
302 response_entity->version());
304 if (entity->GetModelType() == syncer::NIGORI) {
305 send_encryption_keys_based_on_nigori =
306 response_entity->specifics().nigori().passphrase_type() ==
307 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE;
312 if (send_encryption_keys_based_on_nigori ||
313 get_updates.need_encryption_key()) {
314 for (vector<string>::iterator it = keystore_keys_.begin();
315 it != keystore_keys_.end(); ++it) {
316 response->add_encryption_keys(*it);
320 sieve->UpdateProgressMarkers(max_response_version, response);
321 return true;
324 string FakeServer::CommitEntity(
325 const sync_pb::SyncEntity& client_entity,
326 sync_pb::CommitResponse_EntryResponse* entry_response,
327 string client_guid,
328 string parent_id) {
329 if (client_entity.version() == 0 && client_entity.deleted()) {
330 return string();
333 FakeServerEntity* entity;
334 if (client_entity.deleted()) {
335 entity = TombstoneEntity::Create(client_entity.id_string());
336 // TODO(pvalenzuela): Change the behavior of DeleteChilden so that it does
337 // not modify server data if it fails.
338 if (!DeleteChildren(client_entity.id_string())) {
339 return string();
341 } else if (GetModelType(client_entity) == syncer::NIGORI) {
342 // NIGORI is the only permanent item type that should be updated by the
343 // client.
344 entity = PermanentEntity::CreateUpdatedNigoriEntity(
345 client_entity,
346 entities_[client_entity.id_string()]);
347 } else if (client_entity.has_client_defined_unique_tag()) {
348 entity = UniqueClientEntity::Create(client_entity);
349 } else {
350 // TODO(pvalenzuela): Validate entity's parent ID.
351 if (entities_.find(client_entity.id_string()) != entities_.end()) {
352 entity = BookmarkEntity::CreateUpdatedVersion(
353 client_entity,
354 entities_[client_entity.id_string()],
355 parent_id);
356 } else {
357 entity = BookmarkEntity::CreateNew(client_entity, parent_id, client_guid);
361 if (entity == NULL) {
362 // TODO(pvalenzuela): Add logging so that it is easier to determine why
363 // creation failed.
364 return string();
367 SaveEntity(entity);
368 BuildEntryResponseForSuccessfulCommit(entry_response, entity);
369 return entity->GetId();
372 void FakeServer::BuildEntryResponseForSuccessfulCommit(
373 sync_pb::CommitResponse_EntryResponse* entry_response,
374 FakeServerEntity* entity) {
375 entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS);
376 entry_response->set_id_string(entity->GetId());
378 if (entity->IsDeleted()) {
379 entry_response->set_version(entity->GetVersion() + 1);
380 } else {
381 entry_response->set_version(entity->GetVersion());
382 entry_response->set_name(entity->GetName());
386 bool FakeServer::IsChild(const string& id, const string& potential_parent_id) {
387 if (entities_.find(id) == entities_.end()) {
388 // We've hit an ID (probably the imaginary root entity) that isn't stored
389 // by the server, so it can't be a child.
390 return false;
391 } else if (entities_[id]->GetParentId() == potential_parent_id) {
392 return true;
393 } else {
394 // Recursively look up the tree.
395 return IsChild(entities_[id]->GetParentId(), potential_parent_id);
399 bool FakeServer::DeleteChildren(const string& id) {
400 vector<string> child_ids;
401 for (EntityMap::iterator it = entities_.begin(); it != entities_.end();
402 ++it) {
403 if (IsChild(it->first, id)) {
404 child_ids.push_back(it->first);
408 for (vector<string>::iterator it = child_ids.begin(); it != child_ids.end();
409 ++it) {
410 FakeServerEntity* tombstone = TombstoneEntity::Create(*it);
411 if (tombstone == NULL) {
412 LOG(WARNING) << "Tombstone creation failed for entity with ID " << *it;
413 return false;
415 SaveEntity(tombstone);
418 return true;
421 bool FakeServer::HandleCommitRequest(
422 const sync_pb::CommitMessage& commit,
423 const std::string& invalidator_client_id,
424 sync_pb::CommitResponse* response) {
425 std::map<string, string> client_to_server_ids;
426 string guid = commit.cache_guid();
427 ModelTypeSet committed_model_types;
429 // TODO(pvalenzuela): Add validation of CommitMessage.entries.
430 ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it;
431 for (it = commit.entries().begin(); it != commit.entries().end(); ++it) {
432 sync_pb::CommitResponse_EntryResponse* entry_response =
433 response->add_entryresponse();
435 sync_pb::SyncEntity client_entity = *it;
436 string parent_id = client_entity.parent_id_string();
437 if (client_to_server_ids.find(parent_id) !=
438 client_to_server_ids.end()) {
439 parent_id = client_to_server_ids[parent_id];
442 string entity_id = CommitEntity(client_entity,
443 entry_response,
444 guid,
445 parent_id);
446 if (entity_id.empty()) {
447 return false;
450 // Record the ID if it was renamed.
451 if (entity_id != client_entity.id_string()) {
452 client_to_server_ids[client_entity.id_string()] = entity_id;
454 FakeServerEntity* entity = entities_[entity_id];
455 committed_model_types.Put(entity->GetModelType());
458 FOR_EACH_OBSERVER(Observer, observers_,
459 OnCommit(invalidator_client_id, committed_model_types));
460 return true;
463 scoped_ptr<base::DictionaryValue> FakeServer::GetEntitiesAsDictionaryValue() {
464 scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue());
466 // Initialize an empty ListValue for all ModelTypes.
467 ModelTypeSet all_types = ModelTypeSet::All();
468 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
469 dictionary->Set(ModelTypeToString(it.Get()), new base::ListValue());
472 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
473 ++it) {
474 FakeServerEntity* entity = it->second;
475 if (entity->IsDeleted() || entity->IsFolder()) {
476 // Tombstones are ignored as they don't represent current data. Folders
477 // are also ignored as current verification infrastructure does not
478 // consider them.
479 continue;
481 base::ListValue* list_value;
482 if (!dictionary->GetList(ModelTypeToString(entity->GetModelType()),
483 &list_value)) {
484 return scoped_ptr<base::DictionaryValue>();
486 // TODO(pvalenzuela): Store more data for each entity so additional
487 // verification can be performed. One example of additional verification
488 // is checking the correctness of the bookmark hierarchy.
489 list_value->Append(new base::StringValue(entity->GetName()));
492 return dictionary.Pass();
495 void FakeServer::InjectEntity(scoped_ptr<FakeServerEntity> entity) {
496 SaveEntity(entity.release());
499 bool FakeServer::SetNewStoreBirthday(const string& store_birthday) {
500 if (store_birthday_ == store_birthday)
501 return false;
503 store_birthday_ = store_birthday;
504 return true;
507 void FakeServer::SetAuthenticated() {
508 authenticated_ = true;
511 void FakeServer::SetUnauthenticated() {
512 authenticated_ = false;
515 bool FakeServer::TriggerError(const sync_pb::SyncEnums::ErrorType& error_type) {
516 if (triggered_actionable_error_.get()) {
517 DVLOG(1) << "Only one type of error can be triggered at any given time.";
518 return false;
521 error_type_ = error_type;
522 return true;
525 bool FakeServer::TriggerActionableError(
526 const sync_pb::SyncEnums::ErrorType& error_type,
527 const string& description,
528 const string& url,
529 const sync_pb::SyncEnums::Action& action) {
530 if (error_type_ != sync_pb::SyncEnums::SUCCESS) {
531 DVLOG(1) << "Only one type of error can be triggered at any given time.";
532 return false;
535 sync_pb::ClientToServerResponse_Error* error =
536 new sync_pb::ClientToServerResponse_Error();
537 error->set_error_type(error_type);
538 error->set_error_description(description);
539 error->set_url(url);
540 error->set_action(action);
541 triggered_actionable_error_.reset(error);
542 return true;
545 bool FakeServer::EnableAlternatingTriggeredErrors() {
546 if (error_type_ == sync_pb::SyncEnums::SUCCESS &&
547 !triggered_actionable_error_.get()) {
548 DVLOG(1) << "No triggered error set. Alternating can't be enabled.";
549 return false;
552 alternate_triggered_errors_ = true;
553 // Reset the counter so that the the first request yields a triggered error.
554 request_counter_ = 0;
555 return true;
558 bool FakeServer::ShouldSendTriggeredError() const {
559 if (!alternate_triggered_errors_)
560 return true;
562 // Check that the counter is odd so that we trigger an error on the first
563 // request after alternating is enabled.
564 return request_counter_ % 2 != 0;
567 void FakeServer::AddObserver(Observer* observer) {
568 observers_.AddObserver(observer);
571 void FakeServer::RemoveObserver(Observer* observer) {
572 observers_.RemoveObserver(observer);
575 void FakeServer::EnableNetwork() {
576 network_enabled_ = true;
579 void FakeServer::DisableNetwork() {
580 network_enabled_ = false;
583 } // namespace fake_server