OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 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/internal_api/public/test/fake_server.h" | |
6 | |
7 #include <limits> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/basictypes.h" | |
12 #include "base/logging.h" | |
13 #include "base/strings/string_number_conversions.h" | |
14 #include "net/base/net_errors.h" | |
15 #include "sync/internal_api/public/base/model_type.h" | |
16 #include "sync/protocol/sync.pb.h" | |
17 | |
18 using std::string; | |
19 | |
20 // The parent tag for childen of the root node. | |
21 static const std::string kRootParentTag = "0"; | |
22 | |
23 namespace syncer { | |
24 namespace { | |
25 | |
26 // A filter used during GetUpdates calls to determine what information to | |
rlarocque
2014/01/04 00:06:24
You don't need to indent for the namespace.
pval...(no longer on Chromium)
2014/01/06 19:24:32
oops, fixed
| |
27 // send back to the client. There is a 1:1 correspondence between any given | |
28 // GetUpdates call and an UpdateSieve instance. | |
29 class UpdateSieve { | |
30 public: | |
31 // Populates |state_| based on |get_updates_message|. | |
32 UpdateSieve(const sync_pb::GetUpdatesMessage& get_updates_message) | |
33 : get_updates_message_(get_updates_message) { | |
34 DCHECK_GT(get_updates_message.from_progress_marker_size(), 0); | |
35 | |
36 int64 min_version = std::numeric_limits<int64>::max(); | |
37 for (int i = 0; i < get_updates_message_.from_progress_marker_size(); | |
38 i++) { | |
39 sync_pb::DataTypeProgressMarker marker = | |
40 get_updates_message_.from_progress_marker(i); | |
41 | |
42 int64 version; | |
43 if (!base::StringToInt64(marker.token(), &version)) { | |
44 version = 0; | |
rlarocque
2014/01/04 00:06:24
This is a good example of why doing work in a cons
pval...(no longer on Chromium)
2014/01/06 19:24:32
I like this idea. I'll make the change, but how ca
rlarocque
2014/01/10 20:09:41
The first request will have an empty progress toke
pval...(no longer on Chromium)
2014/01/14 01:48:09
I've added the factory method and considered the f
| |
45 } | |
46 | |
47 ModelType model_type = GetModelTypeFromSpecificsFieldNumber( | |
48 marker.data_type_id()); | |
49 state_[model_type] = version; | |
50 | |
51 if (version < min_version) | |
52 min_version = version; | |
53 } | |
54 | |
55 min_version_ = min_version; | |
56 } | |
57 | |
58 ~UpdateSieve() { } | |
59 | |
60 // Sets the progress markers in |get_updates_response| given the progress | |
61 // markers from |get_updates_message_| 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) { | |
66 for (ModelTypeToVersionMap::iterator it = state_.begin(); | |
67 it != state_.end(); ++it) { | |
68 sync_pb::DataTypeProgressMarker* new_marker = | |
69 get_updates_response->add_new_progress_marker(); | |
70 new_marker->set_data_type_id( | |
71 GetSpecificsFieldNumberFromModelType(it->first)); | |
72 | |
73 ModelType model_type = GetModelTypeFromSpecificsFieldNumber( | |
74 new_marker->data_type_id()); | |
75 int64 version = new_version > state_[model_type] ? | |
rlarocque
2014/01/04 00:06:24
nit: use std::max instead?
Also, what does this d
pval...(no longer on Chromium)
2014/01/06 19:24:32
Changed to use std::max.
As for the purpose, I be
| |
76 new_version : state_[model_type]; | |
77 new_marker->set_token(base::Int64ToString(version)); | |
78 } | |
79 } | |
80 | |
81 // Determines whether the server should send |entity| to the client based | |
82 // on its type and version. | |
83 bool ClientWantsItem(const sync_pb::SyncEntity& entity) { | |
84 ModelType model_type = GetModelType(entity); | |
85 return state_[model_type] < entity.version(); | |
86 } | |
87 | |
88 // Returns the mininum version seen across all types. | |
89 int64 GetMinVersion() { | |
90 return min_version_; | |
91 } | |
92 | |
93 // Returns the data type IDs of types being synced for the first time. | |
94 std::vector<ModelType> GetFirstTimeTypes() { | |
95 std::vector<ModelType> types; | |
96 | |
97 for (ModelTypeToVersionMap::iterator it = state_.begin(); | |
98 it != state_.end(); ++it) { | |
99 if (state_[it->first] == 0) | |
100 types.push_back(it->first); | |
101 } | |
102 | |
103 return types; | |
104 } | |
105 | |
106 private: | |
107 typedef std::map<ModelType, int64> ModelTypeToVersionMap; | |
108 | |
109 // The GetUpdatesMessage associated with this instance. | |
110 const sync_pb::GetUpdatesMessage& get_updates_message_; | |
111 | |
112 // Maps data type IDs to the latest version seen for that type. | |
113 ModelTypeToVersionMap state_; | |
rlarocque
2014/01/04 00:06:24
I think state_ is not a very good name. How about
pval...(no longer on Chromium)
2014/01/06 19:24:32
Done.
| |
114 | |
115 // The minimum version seen among all data types. | |
116 int min_version_; | |
117 }; | |
118 | |
119 } // namespace | |
120 | |
121 FakeServer::FakeServer() : version_(0), birthday_("1234567890") { | |
122 keystore_keys_.push_back("1111111111111111"); | |
123 } | |
124 | |
125 FakeServer::~FakeServer() { } | |
126 | |
127 void FakeServer::CreateDefaultPermanentItems( | |
128 const std::vector<ModelType>& first_time_types) { | |
129 for (std::vector<ModelType>::const_iterator it = first_time_types.begin(); | |
130 it != first_time_types.end(); ++it) { | |
131 if (!ModelTypeSet::All().Has(*it)) { | |
132 NOTREACHED() << "An unexpected ModelType was encountered."; | |
133 } | |
134 | |
135 ModelType model_type = *it; | |
136 CreateSyncEntity(model_type, | |
137 ModelTypeToRootTag(model_type), | |
138 ModelTypeToString(model_type), | |
139 kRootParentTag); | |
140 | |
141 if (model_type == BOOKMARKS) { | |
142 CreateSyncEntity(BOOKMARKS, | |
143 "bookmark_bar", | |
144 "Bookmark Bar", | |
145 ModelTypeToRootTag(BOOKMARKS)); | |
146 CreateSyncEntity(BOOKMARKS, | |
147 "other_bookmarks", | |
148 "Other Bookmarks", | |
149 ModelTypeToRootTag(BOOKMARKS)); | |
150 } | |
151 } | |
152 | |
153 | |
154 // TODO(pvalenzuela): Create the mobile bookmarks folder when the fake server | |
155 // is used by mobile tests. | |
156 } | |
157 | |
158 void FakeServer::CreateSyncEntity(ModelType model_type, | |
159 const std::string& id, | |
160 const std::string& name, | |
161 const std::string& parent_tag) { | |
162 DCHECK(!id.empty()); | |
163 DCHECK(!name.empty()); | |
164 DCHECK(!parent_tag.empty()); | |
165 | |
166 sync_pb::SyncEntity entity; | |
167 entity.set_id_string(id); | |
168 entity.set_non_unique_name(name); | |
169 entity.set_name(name); | |
170 entity.set_server_defined_unique_tag(id); | |
171 entity.set_folder(true); | |
172 entity.set_deleted(false); | |
173 | |
174 entity.set_parent_id_string(parent_tag); | |
175 | |
176 if (parent_tag != kRootParentTag && model_type == BOOKMARKS) { | |
177 // Use a dummy value here. | |
178 entity.set_position_in_parent(1337); | |
179 } | |
180 | |
181 sync_pb::EntitySpecifics* specifics = entity.mutable_specifics(); | |
182 AddDefaultFieldValue(model_type, specifics); | |
183 | |
184 SaveEntity(entity); | |
185 } | |
186 | |
187 void FakeServer::SaveEntity(sync_pb::SyncEntity entity) { | |
188 version_++; | |
189 entity.set_version(version_); | |
190 entity.set_sync_timestamp(version_); | |
191 | |
192 sync_pb::SyncEntity original_entity = entities_[entity.id_string()]; | |
193 entity.set_originator_cache_guid(original_entity.originator_cache_guid()); | |
194 entity.set_originator_client_item_id( | |
195 original_entity.originator_client_item_id()); | |
196 | |
197 entities_[entity.id_string()] = entity; | |
198 } | |
199 | |
200 int FakeServer::HandleCommand(string request, | |
201 int* response_code, | |
202 string* response) { | |
203 sync_pb::ClientToServerMessage message; | |
204 DCHECK(message.ParseFromString(request)); | |
205 | |
206 sync_pb::ClientToServerResponse response_proto; | |
207 switch (message.message_contents()) { | |
208 case sync_pb::ClientToServerMessage::GET_UPDATES: | |
209 response_proto = HandleGetUpdatesRequest(message); | |
210 break; | |
211 case sync_pb::ClientToServerMessage::COMMIT: | |
212 response_proto = HandleCommitRequest(message); | |
213 break; | |
214 default: | |
215 return net::ERR_NOT_IMPLEMENTED; | |
216 } | |
217 | |
218 *response_code = 200; | |
219 *response = response_proto.SerializeAsString(); | |
220 return 0; | |
221 } | |
222 | |
223 bool SyncEntityVersionComparator(const sync_pb::SyncEntity& first, | |
224 const sync_pb::SyncEntity& second) { | |
225 return first.version() < second.version(); | |
226 } | |
227 | |
228 sync_pb::ClientToServerResponse FakeServer::HandleGetUpdatesRequest( | |
229 const sync_pb::ClientToServerMessage& message) { | |
230 sync_pb::ClientToServerResponse response; | |
231 response.set_error_code(sync_pb::SyncEnums::SUCCESS); | |
232 response.set_store_birthday(birthday_); | |
233 | |
234 sync_pb::GetUpdatesResponse* get_updates_response = | |
235 response.mutable_get_updates(); | |
236 // TODO(pvalenzuela): Implement batching instead of sending all information | |
237 // at once. | |
238 get_updates_response->set_changes_remaining(0); | |
239 | |
240 UpdateSieve sieve(message.get_updates()); | |
241 CreateDefaultPermanentItems(sieve.GetFirstTimeTypes()); | |
242 | |
243 int64 min_version = sieve.GetMinVersion(); | |
244 | |
245 std::vector<sync_pb::SyncEntity> filtered_entities; | |
246 for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); | |
247 ++it) { | |
248 sync_pb::SyncEntity entity = it->second; | |
249 if (entity.version() > min_version) { | |
250 filtered_entities.push_back(entity); | |
251 } | |
252 } | |
253 | |
254 std::sort(filtered_entities.begin(), filtered_entities.end(), | |
255 SyncEntityVersionComparator); | |
256 | |
257 bool send_encryption_keys_based_on_nigori = false; | |
258 for (std::vector<sync_pb::SyncEntity>::iterator it = | |
rlarocque
2014/01/04 00:06:24
I don't think you gain much by iterating twice. C
pval...(no longer on Chromium)
2014/01/06 19:24:32
Ah, much nicer. Thanks.
| |
259 filtered_entities.begin(); it != filtered_entities.end(); ++it) { | |
260 if (sieve.ClientWantsItem(*it)) { | |
261 sync_pb::SyncEntity* entity = get_updates_response->add_entries(); | |
262 entity->CopyFrom(*it); | |
263 | |
264 if (entity->name() == ModelTypeToString(NIGORI)) { | |
265 send_encryption_keys_based_on_nigori = | |
266 entity->specifics().nigori().passphrase_type() == | |
267 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE; | |
268 } | |
269 } | |
270 } | |
271 | |
272 if (send_encryption_keys_based_on_nigori || | |
273 message.get_updates().need_encryption_key()) { | |
274 for (std::vector<std::string>::iterator it = keystore_keys_.begin(); | |
275 it != keystore_keys_.end(); ++it) { | |
276 get_updates_response->add_encryption_keys(*it); | |
277 } | |
278 } | |
279 | |
280 int new_version = filtered_entities.empty() ? | |
281 0 : filtered_entities.back().version(); | |
282 | |
283 sieve.UpdateProgressMarkers(new_version, get_updates_response); | |
284 | |
285 return response; | |
286 } | |
287 | |
288 sync_pb::SyncEntity FakeServer::CommitEntity(sync_pb::SyncEntity entity, | |
289 string guid) { | |
290 // TODO(pvalenzuela): Implement this. Right now this method cheats and | |
291 // doesn't actually commit. | |
292 return entity; | |
293 } | |
294 | |
295 sync_pb::ClientToServerResponse FakeServer::HandleCommitRequest( | |
296 const sync_pb::ClientToServerMessage& message) { | |
297 sync_pb::ClientToServerResponse response; | |
298 response.set_error_code(sync_pb::SyncEnums::SUCCESS); | |
299 response.set_store_birthday(birthday_); | |
300 | |
301 sync_pb::CommitMessage commit = message.commit(); | |
302 string guid = commit.cache_guid(); | |
303 | |
304 sync_pb::CommitResponse* commit_response = response.mutable_commit(); | |
305 | |
306 ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it; | |
307 for (it = commit.entries().begin(); it != commit.entries().end(); ++it) { | |
308 sync_pb::CommitResponse_EntryResponse* entry_response = | |
309 commit_response->add_entryresponse(); | |
310 | |
311 sync_pb::SyncEntity server_entity = CommitEntity(*it, guid); | |
312 | |
313 entry_response->set_id_string(server_entity.id_string()); | |
314 entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS); | |
315 entry_response->set_version(it->version() + 1); | |
316 } | |
317 | |
318 return response; | |
319 } | |
320 | |
321 } // namespace syncer | |
OLD | NEW |