Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / sync / test / fake_server / fake_server.cc
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.
4
5 #include "sync/test/fake_server/fake_server.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10 #include <vector>
11
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"
29
30 using std::string;
31 using std::vector;
32
33 using syncer::GetModelType;
34 using syncer::ModelType;
35 using syncer::ModelTypeSet;
36
37 // The default store birthday value.
38 static const char kDefaultStoreBirthday[] = "1234567890";
39
40 // The default keystore key.
41 static const char kDefaultKeystoreKey[] = "1111111111111111";
42
43 namespace fake_server {
44
45 class FakeServerEntity;
46
47 namespace {
48
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() { }
55
56   // Factory method for creating an UpdateSieve.
57   static scoped_ptr<UpdateSieve> Create(
58       const sync_pb::GetUpdatesMessage& get_updates_message);
59
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));
73
74       int64 version = std::max(new_version, it->second);
75       new_marker->set_token(base::Int64ToString(version));
76     }
77   }
78
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;
87     }
88
89     ModelTypeToVersionMap::const_iterator it =
90         request_from_version_.find(entity->GetModelType());
91
92     return it == request_from_version_.end() ? false : it->second < version;
93   }
94
95   // Returns the minimum version seen across all types.
96   int64 GetMinVersion() const {
97     return min_version_;
98   }
99
100  private:
101   typedef std::map<ModelType, int64> ModelTypeToVersionMap;
102
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) { }
108
109   // Maps data type IDs to the latest version seen for that type.
110   const ModelTypeToVersionMap request_from_version_;
111
112   // The minimum version seen among all data types.
113   const int min_version_;
114 };
115
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.";
120
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);
126
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.";
133     }
134
135     ModelType model_type = syncer::GetModelTypeFromSpecificsFieldNumber(
136         marker.data_type_id());
137     request_from_version[model_type] = version;
138
139     if (version < min_version)
140       min_version = version;
141   }
142
143   return scoped_ptr<UpdateSieve>(
144       new UpdateSieve(request_from_version, min_version));
145 }
146
147 }  // namespace
148
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());
158 }
159
160 FakeServer::~FakeServer() {
161   STLDeleteContainerPairSecondPointers(entities_.begin(), entities_.end());
162 }
163
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;
172     }
173     SaveEntity(top_level_entity);
174
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;
183       }
184       SaveEntity(bookmark_bar_entity);
185
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;
193       }
194       SaveEntity(other_bookmarks_entity);
195     }
196   }
197
198   return true;
199 }
200
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;
211   }
212   SaveEntity(mobile_bookmarks_entity);
213   return true;
214 }
215
216 void FakeServer::SaveEntity(FakeServerEntity* entity) {
217   delete entities_[entity->GetId()];
218   entity->SetVersion(++version_);
219   entities_[entity->GetId()] = entity;
220 }
221
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;
227   }
228   request_counter_++;
229
230   if (!authenticated_) {
231     callback.Run(0, net::HTTP_UNAUTHORIZED, string());
232     return;
233   }
234
235   sync_pb::ClientToServerMessage message;
236   bool parsed = message.ParseFromString(request);
237   CHECK(parsed) << "Unable to parse the ClientToServerMessage.";
238
239   sync_pb::ClientToServerResponse response_proto;
240
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;
266     }
267
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;
273     }
274
275     response_proto.set_error_code(sync_pb::SyncEnums::SUCCESS);
276   }
277
278   response_proto.set_store_birthday(store_birthday_);
279   callback.Run(0, net::HTTP_OK, response_proto.SerializeAsString());
280 }
281
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);
288
289   scoped_ptr<UpdateSieve> sieve = UpdateSieve::Create(get_updates);
290
291   if (get_updates.create_mobile_bookmarks_folder() &&
292       !CreateMobileBookmarksPermanentItem()) {
293     return false;
294   }
295
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());
306
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;
311       }
312     }
313   }
314
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);
320     }
321   }
322
323   sieve->UpdateProgressMarkers(max_response_version, response);
324   return true;
325 }
326
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();
334   }
335
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();
343     }
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);
361     }
362   }
363
364   if (entity == NULL) {
365     // TODO(pvalenzuela): Add logging so that it is easier to determine why
366     // creation failed.
367     return string();
368   }
369
370   SaveEntity(entity);
371   BuildEntryResponseForSuccessfulCommit(entry_response, entity);
372   return entity->GetId();
373 }
374
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());
380
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());
386     }
387 }
388
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);
399   }
400 }
401
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);
408     }
409   }
410
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;
417     }
418     SaveEntity(tombstone);
419   }
420
421   return true;
422 }
423
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;
431
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();
437
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];
443     }
444
445     string entity_id = CommitEntity(client_entity,
446                                     entry_response,
447                                     guid,
448                                     parent_id);
449     if (entity_id.empty()) {
450       return false;
451     }
452
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;
456     }
457     FakeServerEntity* entity = entities_[entity_id];
458     committed_model_types.Put(entity->GetModelType());
459   }
460
461   FOR_EACH_OBSERVER(Observer, observers_,
462                     OnCommit(invalidator_client_id, committed_model_types));
463   return true;
464 }
465
466 scoped_ptr<base::DictionaryValue> FakeServer::GetEntitiesAsDictionaryValue() {
467   scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue());
468
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());
473   }
474
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;
483     }
484     base::ListValue* list_value;
485     if (!dictionary->GetList(ModelTypeToString(entity->GetModelType()),
486                                                &list_value)) {
487       return scoped_ptr<base::DictionaryValue>();
488     }
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()));
493   }
494
495   return dictionary.Pass();
496 }
497
498 void FakeServer::InjectEntity(scoped_ptr<FakeServerEntity> entity) {
499   SaveEntity(entity.release());
500 }
501
502 bool FakeServer::SetNewStoreBirthday(const string& store_birthday) {
503   if (store_birthday_ == store_birthday)
504     return false;
505
506   store_birthday_ = store_birthday;
507   return true;
508 }
509
510 void FakeServer::SetAuthenticated() {
511   authenticated_ = true;
512 }
513
514 void FakeServer::SetUnauthenticated() {
515   authenticated_ = false;
516 }
517
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;
522   }
523
524   error_type_ = error_type;
525   return true;
526 }
527
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;
536   }
537
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;
546 }
547
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;
553   }
554
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;
559 }
560
561 bool FakeServer::ShouldSendTriggeredError() const {
562   if (!alternate_triggered_errors_)
563     return true;
564
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;
568 }
569
570 void FakeServer::AddObserver(Observer* observer) {
571   observers_.AddObserver(observer);
572 }
573
574 void FakeServer::RemoveObserver(Observer* observer) {
575   observers_.RemoveObserver(observer);
576 }
577
578 void FakeServer::EnableNetwork() {
579   network_enabled_ = true;
580 }
581
582 void FakeServer::DisableNetwork() {
583   network_enabled_ = false;
584 }
585
586 }  // namespace fake_server