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