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