Mark two tests in ImeTest.java as flaky
[chromium-blink-merge.git] / sync / test / fake_server / fake_server.cc
blob74637035cdb45f20dd451bde950c055005e58982
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::CreateDefaultPermanentItems() {
165 ModelTypeSet all_types = syncer::ProtocolTypes();
166 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
167 ModelType model_type = it.Get();
168 FakeServerEntity* top_level_entity =
169 PermanentEntity::CreateTopLevel(model_type);
170 if (top_level_entity == NULL) {
171 return false;
173 SaveEntity(top_level_entity);
175 if (model_type == syncer::BOOKMARKS) {
176 FakeServerEntity* bookmark_bar_entity =
177 PermanentEntity::Create(syncer::BOOKMARKS,
178 "bookmark_bar",
179 "Bookmark Bar",
180 ModelTypeToRootTag(syncer::BOOKMARKS));
181 if (bookmark_bar_entity == NULL) {
182 return false;
184 SaveEntity(bookmark_bar_entity);
186 FakeServerEntity* other_bookmarks_entity =
187 PermanentEntity::Create(syncer::BOOKMARKS,
188 "other_bookmarks",
189 "Other Bookmarks",
190 ModelTypeToRootTag(syncer::BOOKMARKS));
191 if (other_bookmarks_entity == NULL) {
192 return false;
194 SaveEntity(other_bookmarks_entity);
198 return true;
201 bool FakeServer::CreateMobileBookmarksPermanentItem() {
202 // This folder is called "Synced Bookmarks" by sync and is renamed
203 // "Mobile Bookmarks" by the mobile client UIs.
204 FakeServerEntity* mobile_bookmarks_entity =
205 PermanentEntity::Create(syncer::BOOKMARKS,
206 "synced_bookmarks",
207 "Synced Bookmarks",
208 ModelTypeToRootTag(syncer::BOOKMARKS));
209 if (mobile_bookmarks_entity == NULL) {
210 return false;
212 SaveEntity(mobile_bookmarks_entity);
213 return true;
216 void FakeServer::SaveEntity(FakeServerEntity* entity) {
217 delete entities_[entity->GetId()];
218 entity->SetVersion(++version_);
219 entities_[entity->GetId()] = entity;
222 void FakeServer::HandleCommand(const string& request,
223 const HandleCommandCallback& callback) {
224 if (!network_enabled_) {
225 callback.Run(net::ERR_FAILED, net::ERR_FAILED, string());
226 return;
228 request_counter_++;
230 if (!authenticated_) {
231 callback.Run(0, net::HTTP_UNAUTHORIZED, string());
232 return;
235 sync_pb::ClientToServerMessage message;
236 bool parsed = message.ParseFromString(request);
237 CHECK(parsed) << "Unable to parse the ClientToServerMessage.";
239 sync_pb::ClientToServerResponse response_proto;
241 if (message.has_store_birthday() &&
242 message.store_birthday() != store_birthday_) {
243 response_proto.set_error_code(sync_pb::SyncEnums::NOT_MY_BIRTHDAY);
244 } else if (error_type_ != sync_pb::SyncEnums::SUCCESS &&
245 ShouldSendTriggeredError()) {
246 response_proto.set_error_code(error_type_);
247 } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) {
248 sync_pb::ClientToServerResponse_Error* error =
249 response_proto.mutable_error();
250 error->CopyFrom(*(triggered_actionable_error_.get()));
251 } else {
252 bool success = false;
253 switch (message.message_contents()) {
254 case sync_pb::ClientToServerMessage::GET_UPDATES:
255 success = HandleGetUpdatesRequest(message.get_updates(),
256 response_proto.mutable_get_updates());
257 break;
258 case sync_pb::ClientToServerMessage::COMMIT:
259 success = HandleCommitRequest(message.commit(),
260 message.invalidator_client_id(),
261 response_proto.mutable_commit());
262 break;
263 default:
264 callback.Run(net::ERR_NOT_IMPLEMENTED, 0, string());;
265 return;
268 if (!success) {
269 // TODO(pvalenzuela): Add logging here so that tests have more info about
270 // the failure.
271 callback.Run(net::ERR_FAILED, 0, string());
272 return;
275 response_proto.set_error_code(sync_pb::SyncEnums::SUCCESS);
278 response_proto.set_store_birthday(store_birthday_);
279 callback.Run(0, net::HTTP_OK, response_proto.SerializeAsString());
282 bool FakeServer::HandleGetUpdatesRequest(
283 const sync_pb::GetUpdatesMessage& get_updates,
284 sync_pb::GetUpdatesResponse* response) {
285 // TODO(pvalenzuela): Implement batching instead of sending all information
286 // at once.
287 response->set_changes_remaining(0);
289 scoped_ptr<UpdateSieve> sieve = UpdateSieve::Create(get_updates);
291 if (get_updates.create_mobile_bookmarks_folder() &&
292 !CreateMobileBookmarksPermanentItem()) {
293 return false;
296 bool send_encryption_keys_based_on_nigori = false;
297 int64 max_response_version = 0;
298 for (EntityMap::iterator it = entities_.begin(); it != entities_.end();
299 ++it) {
300 FakeServerEntity* entity = it->second;
301 if (sieve->ClientWantsItem(entity)) {
302 sync_pb::SyncEntity* response_entity = response->add_entries();
303 entity->SerializeAsProto(response_entity);
304 max_response_version = std::max(max_response_version,
305 response_entity->version());
307 if (entity->GetModelType() == syncer::NIGORI) {
308 send_encryption_keys_based_on_nigori =
309 response_entity->specifics().nigori().passphrase_type() ==
310 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE;
315 if (send_encryption_keys_based_on_nigori ||
316 get_updates.need_encryption_key()) {
317 for (vector<string>::iterator it = keystore_keys_.begin();
318 it != keystore_keys_.end(); ++it) {
319 response->add_encryption_keys(*it);
323 sieve->UpdateProgressMarkers(max_response_version, response);
324 return true;
327 string FakeServer::CommitEntity(
328 const sync_pb::SyncEntity& client_entity,
329 sync_pb::CommitResponse_EntryResponse* entry_response,
330 string client_guid,
331 string parent_id) {
332 if (client_entity.version() == 0 && client_entity.deleted()) {
333 return string();
336 FakeServerEntity* entity;
337 if (client_entity.deleted()) {
338 entity = TombstoneEntity::Create(client_entity.id_string());
339 // TODO(pvalenzuela): Change the behavior of DeleteChilden so that it does
340 // not modify server data if it fails.
341 if (!DeleteChildren(client_entity.id_string())) {
342 return string();
344 } else if (GetModelType(client_entity) == syncer::NIGORI) {
345 // NIGORI is the only permanent item type that should be updated by the
346 // client.
347 entity = PermanentEntity::CreateUpdatedNigoriEntity(
348 client_entity,
349 entities_[client_entity.id_string()]);
350 } else if (client_entity.has_client_defined_unique_tag()) {
351 entity = UniqueClientEntity::Create(client_entity);
352 } else {
353 // TODO(pvalenzuela): Validate entity's parent ID.
354 if (entities_.find(client_entity.id_string()) != entities_.end()) {
355 entity = BookmarkEntity::CreateUpdatedVersion(
356 client_entity,
357 entities_[client_entity.id_string()],
358 parent_id);
359 } else {
360 entity = BookmarkEntity::CreateNew(client_entity, parent_id, client_guid);
364 if (entity == NULL) {
365 // TODO(pvalenzuela): Add logging so that it is easier to determine why
366 // creation failed.
367 return string();
370 SaveEntity(entity);
371 BuildEntryResponseForSuccessfulCommit(entry_response, entity);
372 return entity->GetId();
375 void FakeServer::BuildEntryResponseForSuccessfulCommit(
376 sync_pb::CommitResponse_EntryResponse* entry_response,
377 FakeServerEntity* entity) {
378 entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS);
379 entry_response->set_id_string(entity->GetId());
381 if (entity->IsDeleted()) {
382 entry_response->set_version(entity->GetVersion() + 1);
383 } else {
384 entry_response->set_version(entity->GetVersion());
385 entry_response->set_name(entity->GetName());
389 bool FakeServer::IsChild(const string& id, const string& potential_parent_id) {
390 if (entities_.find(id) == entities_.end()) {
391 // We've hit an ID (probably the imaginary root entity) that isn't stored
392 // by the server, so it can't be a child.
393 return false;
394 } else if (entities_[id]->GetParentId() == potential_parent_id) {
395 return true;
396 } else {
397 // Recursively look up the tree.
398 return IsChild(entities_[id]->GetParentId(), potential_parent_id);
402 bool FakeServer::DeleteChildren(const string& id) {
403 vector<string> child_ids;
404 for (EntityMap::iterator it = entities_.begin(); it != entities_.end();
405 ++it) {
406 if (IsChild(it->first, id)) {
407 child_ids.push_back(it->first);
411 for (vector<string>::iterator it = child_ids.begin(); it != child_ids.end();
412 ++it) {
413 FakeServerEntity* tombstone = TombstoneEntity::Create(*it);
414 if (tombstone == NULL) {
415 LOG(WARNING) << "Tombstone creation failed for entity with ID " << *it;
416 return false;
418 SaveEntity(tombstone);
421 return true;
424 bool FakeServer::HandleCommitRequest(
425 const sync_pb::CommitMessage& commit,
426 const std::string& invalidator_client_id,
427 sync_pb::CommitResponse* response) {
428 std::map<string, string> client_to_server_ids;
429 string guid = commit.cache_guid();
430 ModelTypeSet committed_model_types;
432 // TODO(pvalenzuela): Add validation of CommitMessage.entries.
433 ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it;
434 for (it = commit.entries().begin(); it != commit.entries().end(); ++it) {
435 sync_pb::CommitResponse_EntryResponse* entry_response =
436 response->add_entryresponse();
438 sync_pb::SyncEntity client_entity = *it;
439 string parent_id = client_entity.parent_id_string();
440 if (client_to_server_ids.find(parent_id) !=
441 client_to_server_ids.end()) {
442 parent_id = client_to_server_ids[parent_id];
445 string entity_id = CommitEntity(client_entity,
446 entry_response,
447 guid,
448 parent_id);
449 if (entity_id.empty()) {
450 return false;
453 // Record the ID if it was renamed.
454 if (entity_id != client_entity.id_string()) {
455 client_to_server_ids[client_entity.id_string()] = entity_id;
457 FakeServerEntity* entity = entities_[entity_id];
458 committed_model_types.Put(entity->GetModelType());
461 FOR_EACH_OBSERVER(Observer, observers_,
462 OnCommit(invalidator_client_id, committed_model_types));
463 return true;
466 scoped_ptr<base::DictionaryValue> FakeServer::GetEntitiesAsDictionaryValue() {
467 scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue());
469 // Initialize an empty ListValue for all ModelTypes.
470 ModelTypeSet all_types = ModelTypeSet::All();
471 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
472 dictionary->Set(ModelTypeToString(it.Get()), new base::ListValue());
475 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
476 ++it) {
477 FakeServerEntity* entity = it->second;
478 if (entity->IsDeleted() || entity->IsFolder()) {
479 // Tombstones are ignored as they don't represent current data. Folders
480 // are also ignored as current verification infrastructure does not
481 // consider them.
482 continue;
484 base::ListValue* list_value;
485 if (!dictionary->GetList(ModelTypeToString(entity->GetModelType()),
486 &list_value)) {
487 return scoped_ptr<base::DictionaryValue>();
489 // TODO(pvalenzuela): Store more data for each entity so additional
490 // verification can be performed. One example of additional verification
491 // is checking the correctness of the bookmark hierarchy.
492 list_value->Append(new base::StringValue(entity->GetName()));
495 return dictionary.Pass();
498 void FakeServer::InjectEntity(scoped_ptr<FakeServerEntity> entity) {
499 SaveEntity(entity.release());
502 bool FakeServer::SetNewStoreBirthday(const string& store_birthday) {
503 if (store_birthday_ == store_birthday)
504 return false;
506 store_birthday_ = store_birthday;
507 return true;
510 void FakeServer::SetAuthenticated() {
511 authenticated_ = true;
514 void FakeServer::SetUnauthenticated() {
515 authenticated_ = false;
518 bool FakeServer::TriggerError(const sync_pb::SyncEnums::ErrorType& error_type) {
519 if (triggered_actionable_error_.get()) {
520 DVLOG(1) << "Only one type of error can be triggered at any given time.";
521 return false;
524 error_type_ = error_type;
525 return true;
528 bool FakeServer::TriggerActionableError(
529 const sync_pb::SyncEnums::ErrorType& error_type,
530 const string& description,
531 const string& url,
532 const sync_pb::SyncEnums::Action& action) {
533 if (error_type_ != sync_pb::SyncEnums::SUCCESS) {
534 DVLOG(1) << "Only one type of error can be triggered at any given time.";
535 return false;
538 sync_pb::ClientToServerResponse_Error* error =
539 new sync_pb::ClientToServerResponse_Error();
540 error->set_error_type(error_type);
541 error->set_error_description(description);
542 error->set_url(url);
543 error->set_action(action);
544 triggered_actionable_error_.reset(error);
545 return true;
548 bool FakeServer::EnableAlternatingTriggeredErrors() {
549 if (error_type_ == sync_pb::SyncEnums::SUCCESS &&
550 !triggered_actionable_error_.get()) {
551 DVLOG(1) << "No triggered error set. Alternating can't be enabled.";
552 return false;
555 alternate_triggered_errors_ = true;
556 // Reset the counter so that the the first request yields a triggered error.
557 request_counter_ = 0;
558 return true;
561 bool FakeServer::ShouldSendTriggeredError() const {
562 if (!alternate_triggered_errors_)
563 return true;
565 // Check that the counter is odd so that we trigger an error on the first
566 // request after alternating is enabled.
567 return request_counter_ % 2 != 0;
570 void FakeServer::AddObserver(Observer* observer) {
571 observers_.AddObserver(observer);
574 void FakeServer::RemoveObserver(Observer* observer) {
575 observers_.RemoveObserver(observer);
578 void FakeServer::EnableNetwork() {
579 network_enabled_ = true;
582 void FakeServer::DisableNetwork() {
583 network_enabled_ = false;
586 } // namespace fake_server