Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: sync/internal_api/test/fake_server.cc

Issue 115243007: Basic implementation of the Sync C++ fake server (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698