• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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 // Mock ServerConnectionManager class for use in client regression tests.
6 
7 #include "sync/test/engine/mock_connection_manager.h"
8 
9 #include <map>
10 
11 #include "base/location.h"
12 #include "base/strings/stringprintf.h"
13 #include "sync/engine/syncer_proto_util.h"
14 #include "sync/protocol/bookmark_specifics.pb.h"
15 #include "sync/syncable/directory.h"
16 #include "sync/syncable/syncable_write_transaction.h"
17 #include "sync/test/engine/test_id_factory.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 using std::find;
21 using std::map;
22 using std::string;
23 using sync_pb::ClientToServerMessage;
24 using sync_pb::CommitMessage;
25 using sync_pb::CommitResponse;
26 using sync_pb::GetUpdatesMessage;
27 using sync_pb::SyncEnums;
28 
29 namespace syncer {
30 
31 using syncable::WriteTransaction;
32 
33 static char kValidAuthToken[] = "AuthToken";
34 static char kCacheGuid[] = "kqyg7097kro6GSUod+GSg==";
35 
MockConnectionManager(syncable::Directory * directory,CancelationSignal * signal)36 MockConnectionManager::MockConnectionManager(syncable::Directory* directory,
37                                              CancelationSignal* signal)
38     : ServerConnectionManager("unused", 0, false, signal),
39       server_reachable_(true),
40       conflict_all_commits_(false),
41       conflict_n_commits_(0),
42       next_new_id_(10000),
43       store_birthday_("Store BDay!"),
44       store_birthday_sent_(false),
45       client_stuck_(false),
46       countdown_to_postbuffer_fail_(0),
47       directory_(directory),
48       mid_commit_observer_(NULL),
49       throttling_(false),
50       fail_with_auth_invalid_(false),
51       fail_non_periodic_get_updates_(false),
52       next_position_in_parent_(2),
53       use_legacy_bookmarks_protocol_(false),
54       num_get_updates_requests_(0) {
55   SetNewTimestamp(0);
56   SetAuthToken(kValidAuthToken);
57 }
58 
~MockConnectionManager()59 MockConnectionManager::~MockConnectionManager() {
60   EXPECT_TRUE(update_queue_.empty()) << "Unfetched updates.";
61 }
62 
SetCommitTimeRename(string prepend)63 void MockConnectionManager::SetCommitTimeRename(string prepend) {
64   commit_time_rename_prepended_string_ = prepend;
65 }
66 
SetMidCommitCallback(const base::Closure & callback)67 void MockConnectionManager::SetMidCommitCallback(
68     const base::Closure& callback) {
69   mid_commit_callback_ = callback;
70 }
71 
SetMidCommitObserver(MockConnectionManager::MidCommitObserver * observer)72 void MockConnectionManager::SetMidCommitObserver(
73     MockConnectionManager::MidCommitObserver* observer) {
74     mid_commit_observer_ = observer;
75 }
76 
PostBufferToPath(PostBufferParams * params,const string & path,const string & auth_token,ScopedServerStatusWatcher * watcher)77 bool MockConnectionManager::PostBufferToPath(PostBufferParams* params,
78     const string& path,
79     const string& auth_token,
80     ScopedServerStatusWatcher* watcher) {
81   ClientToServerMessage post;
82   CHECK(post.ParseFromString(params->buffer_in));
83   CHECK(post.has_protocol_version());
84   CHECK(post.has_api_key());
85   CHECK(post.has_bag_of_chips());
86 
87   requests_.push_back(post);
88   client_stuck_ = post.sync_problem_detected();
89   sync_pb::ClientToServerResponse response;
90   response.Clear();
91 
92   if (directory_) {
93     // If the Directory's locked when we do this, it's a problem as in normal
94     // use this function could take a while to return because it accesses the
95     // network. As we can't test this we do the next best thing and hang here
96     // when there's an issue.
97     CHECK(directory_->good());
98     WriteTransaction wt(FROM_HERE, syncable::UNITTEST, directory_);
99   }
100 
101   if (auth_token.empty()) {
102     params->response.server_status = HttpResponse::SYNC_AUTH_ERROR;
103     return false;
104   }
105 
106   if (auth_token != kValidAuthToken) {
107     // Simulate server-side auth failure.
108     params->response.server_status = HttpResponse::SYNC_AUTH_ERROR;
109     InvalidateAndClearAuthToken();
110   }
111 
112   if (--countdown_to_postbuffer_fail_ == 0) {
113     // Fail as countdown hits zero.
114     params->response.server_status = HttpResponse::SYNC_SERVER_ERROR;
115     return false;
116   }
117 
118   if (!server_reachable_) {
119     params->response.server_status = HttpResponse::CONNECTION_UNAVAILABLE;
120     return false;
121   }
122 
123   // Default to an ok connection.
124   params->response.server_status = HttpResponse::SERVER_CONNECTION_OK;
125   response.set_error_code(SyncEnums::SUCCESS);
126   const string current_store_birthday = store_birthday();
127   response.set_store_birthday(current_store_birthday);
128   if (post.has_store_birthday() && post.store_birthday() !=
129       current_store_birthday) {
130     response.set_error_code(SyncEnums::NOT_MY_BIRTHDAY);
131     response.set_error_message("Merry Unbirthday!");
132     response.SerializeToString(&params->buffer_out);
133     store_birthday_sent_ = true;
134     return true;
135   }
136   bool result = true;
137   EXPECT_TRUE(!store_birthday_sent_ || post.has_store_birthday() ||
138               post.message_contents() == ClientToServerMessage::AUTHENTICATE);
139   store_birthday_sent_ = true;
140 
141   if (post.message_contents() == ClientToServerMessage::COMMIT) {
142     ProcessCommit(&post, &response);
143   } else if (post.message_contents() == ClientToServerMessage::GET_UPDATES) {
144     ProcessGetUpdates(&post, &response);
145   } else {
146     EXPECT_TRUE(false) << "Unknown/unsupported ClientToServerMessage";
147     return false;
148   }
149 
150   {
151     base::AutoLock lock(response_code_override_lock_);
152     if (throttling_) {
153       response.set_error_code(SyncEnums::THROTTLED);
154       throttling_ = false;
155     }
156 
157     if (fail_with_auth_invalid_)
158       response.set_error_code(SyncEnums::AUTH_INVALID);
159   }
160 
161   response.SerializeToString(&params->buffer_out);
162   if (post.message_contents() == ClientToServerMessage::COMMIT &&
163       !mid_commit_callback_.is_null()) {
164     mid_commit_callback_.Run();
165     mid_commit_callback_.Reset();
166   }
167   if (mid_commit_observer_) {
168     mid_commit_observer_->Observe();
169   }
170 
171   return result;
172 }
173 
GetUpdateResponse()174 sync_pb::GetUpdatesResponse* MockConnectionManager::GetUpdateResponse() {
175   if (update_queue_.empty()) {
176     NextUpdateBatch();
177   }
178   return &update_queue_.back();
179 }
180 
AddDefaultBookmarkData(sync_pb::SyncEntity * entity,bool is_folder)181 void MockConnectionManager::AddDefaultBookmarkData(sync_pb::SyncEntity* entity,
182                                                    bool is_folder) {
183   if (use_legacy_bookmarks_protocol_) {
184     sync_pb::SyncEntity_BookmarkData* data = entity->mutable_bookmarkdata();
185     data->set_bookmark_folder(is_folder);
186 
187     if (!is_folder) {
188       data->set_bookmark_url("http://google.com");
189     }
190   } else {
191     entity->set_folder(is_folder);
192     entity->mutable_specifics()->mutable_bookmark();
193     if (!is_folder) {
194       entity->mutable_specifics()->mutable_bookmark()->
195           set_url("http://google.com");
196     }
197   }
198 }
199 
AddUpdateDirectory(int id,int parent_id,string name,int64 version,int64 sync_ts,std::string originator_cache_guid,std::string originator_client_item_id)200 sync_pb::SyncEntity* MockConnectionManager::AddUpdateDirectory(
201     int id,
202     int parent_id,
203     string name,
204     int64 version,
205     int64 sync_ts,
206     std::string originator_cache_guid,
207     std::string originator_client_item_id) {
208   return AddUpdateDirectory(TestIdFactory::FromNumber(id),
209                             TestIdFactory::FromNumber(parent_id),
210                             name,
211                             version,
212                             sync_ts,
213                             originator_cache_guid,
214                             originator_client_item_id);
215 }
216 
SetGUClientCommand(sync_pb::ClientCommand * command)217 void MockConnectionManager::SetGUClientCommand(
218     sync_pb::ClientCommand* command) {
219   gu_client_command_.reset(command);
220 }
221 
SetCommitClientCommand(sync_pb::ClientCommand * command)222 void MockConnectionManager::SetCommitClientCommand(
223     sync_pb::ClientCommand* command) {
224   commit_client_command_.reset(command);
225 }
226 
SetTransientErrorId(syncable::Id id)227 void MockConnectionManager::SetTransientErrorId(syncable::Id id) {
228   transient_error_ids_.push_back(id);
229 }
230 
AddUpdateBookmark(int id,int parent_id,string name,int64 version,int64 sync_ts,string originator_client_item_id,string originator_cache_guid)231 sync_pb::SyncEntity* MockConnectionManager::AddUpdateBookmark(
232     int id, int parent_id,
233     string name, int64 version,
234     int64 sync_ts,
235     string originator_client_item_id,
236     string originator_cache_guid) {
237   return AddUpdateBookmark(TestIdFactory::FromNumber(id),
238                            TestIdFactory::FromNumber(parent_id),
239                            name,
240                            version,
241                            sync_ts,
242                            originator_client_item_id,
243                            originator_cache_guid);
244 }
245 
AddUpdateSpecifics(int id,int parent_id,string name,int64 version,int64 sync_ts,bool is_dir,int64 position,const sync_pb::EntitySpecifics & specifics)246 sync_pb::SyncEntity* MockConnectionManager::AddUpdateSpecifics(
247     int id,
248     int parent_id,
249     string name,
250     int64 version,
251     int64 sync_ts,
252     bool is_dir,
253     int64 position,
254     const sync_pb::EntitySpecifics& specifics) {
255   sync_pb::SyncEntity* ent = AddUpdateMeta(
256       TestIdFactory::FromNumber(id).GetServerId(),
257       TestIdFactory::FromNumber(parent_id).GetServerId(),
258       name, version, sync_ts);
259   ent->set_position_in_parent(position);
260   ent->mutable_specifics()->CopyFrom(specifics);
261   ent->set_folder(is_dir);
262   return ent;
263 }
264 
AddUpdateSpecifics(int id,int parent_id,string name,int64 version,int64 sync_ts,bool is_dir,int64 position,const sync_pb::EntitySpecifics & specifics,string originator_cache_guid,string originator_client_item_id)265 sync_pb::SyncEntity* MockConnectionManager::AddUpdateSpecifics(
266     int id,
267     int parent_id,
268     string name,
269     int64 version,
270     int64 sync_ts,
271     bool is_dir,
272     int64 position,
273     const sync_pb::EntitySpecifics& specifics,
274     string originator_cache_guid,
275     string originator_client_item_id) {
276   sync_pb::SyncEntity* ent = AddUpdateSpecifics(
277       id, parent_id, name, version, sync_ts, is_dir, position, specifics);
278   ent->set_originator_cache_guid(originator_cache_guid);
279   ent->set_originator_client_item_id(originator_client_item_id);
280   return ent;
281 }
282 
SetNigori(int id,int64 version,int64 sync_ts,const sync_pb::EntitySpecifics & specifics)283 sync_pb::SyncEntity* MockConnectionManager::SetNigori(
284     int id,
285     int64 version,
286     int64 sync_ts,
287     const sync_pb::EntitySpecifics& specifics) {
288   sync_pb::SyncEntity* ent = GetUpdateResponse()->add_entries();
289   ent->set_id_string(TestIdFactory::FromNumber(id).GetServerId());
290   ent->set_parent_id_string(TestIdFactory::FromNumber(0).GetServerId());
291   ent->set_server_defined_unique_tag(ModelTypeToRootTag(NIGORI));
292   ent->set_name("Nigori");
293   ent->set_non_unique_name("Nigori");
294   ent->set_version(version);
295   ent->set_sync_timestamp(sync_ts);
296   ent->set_mtime(sync_ts);
297   ent->set_ctime(1);
298   ent->set_position_in_parent(0);
299   ent->set_folder(false);
300   ent->mutable_specifics()->CopyFrom(specifics);
301   return ent;
302 }
303 
AddUpdatePref(string id,string parent_id,string client_tag,int64 version,int64 sync_ts)304 sync_pb::SyncEntity* MockConnectionManager::AddUpdatePref(string id,
305                                                           string parent_id,
306                                                           string client_tag,
307                                                           int64 version,
308                                                           int64 sync_ts) {
309   sync_pb::SyncEntity* ent =
310       AddUpdateMeta(id, parent_id, " ", version, sync_ts);
311 
312   ent->set_client_defined_unique_tag(client_tag);
313 
314   sync_pb::EntitySpecifics specifics;
315   AddDefaultFieldValue(PREFERENCES, &specifics);
316   ent->mutable_specifics()->CopyFrom(specifics);
317 
318   return ent;
319 }
320 
AddUpdateFull(string id,string parent_id,string name,int64 version,int64 sync_ts,bool is_dir)321 sync_pb::SyncEntity* MockConnectionManager::AddUpdateFull(
322     string id, string parent_id,
323     string name, int64 version,
324     int64 sync_ts, bool is_dir) {
325   sync_pb::SyncEntity* ent =
326       AddUpdateMeta(id, parent_id, name, version, sync_ts);
327   AddDefaultBookmarkData(ent, is_dir);
328   return ent;
329 }
330 
AddUpdateMeta(string id,string parent_id,string name,int64 version,int64 sync_ts)331 sync_pb::SyncEntity* MockConnectionManager::AddUpdateMeta(
332     string id, string parent_id,
333     string name, int64 version,
334     int64 sync_ts) {
335   sync_pb::SyncEntity* ent = GetUpdateResponse()->add_entries();
336   ent->set_id_string(id);
337   ent->set_parent_id_string(parent_id);
338   ent->set_non_unique_name(name);
339   ent->set_name(name);
340   ent->set_version(version);
341   ent->set_sync_timestamp(sync_ts);
342   ent->set_mtime(sync_ts);
343   ent->set_ctime(1);
344   ent->set_position_in_parent(GeneratePositionInParent());
345 
346   // This isn't perfect, but it works well enough.  This is an update, which
347   // means the ID is a server ID, which means it never changes.  By making
348   // kCacheGuid also never change, we guarantee that the same item always has
349   // the same originator_cache_guid and originator_client_item_id.
350   //
351   // Unfortunately, neither this class nor the tests that use it explicitly
352   // track sync entitites, so supporting proper cache guids and client item IDs
353   // would require major refactoring.  The ID used here ought to be the "c-"
354   // style ID that was sent up on the commit.
355   ent->set_originator_cache_guid(kCacheGuid);
356   ent->set_originator_client_item_id(id);
357 
358   return ent;
359 }
360 
AddUpdateDirectory(string id,string parent_id,string name,int64 version,int64 sync_ts,std::string originator_cache_guid,std::string originator_client_item_id)361 sync_pb::SyncEntity* MockConnectionManager::AddUpdateDirectory(
362     string id,
363     string parent_id,
364     string name,
365     int64 version,
366     int64 sync_ts,
367     std::string originator_cache_guid,
368     std::string originator_client_item_id) {
369   sync_pb::SyncEntity* ret =
370       AddUpdateFull(id, parent_id, name, version, sync_ts, true);
371   ret->set_originator_cache_guid(originator_cache_guid);
372   ret->set_originator_client_item_id(originator_client_item_id);
373   return ret;
374 }
375 
AddUpdateBookmark(string id,string parent_id,string name,int64 version,int64 sync_ts,string originator_cache_guid,string originator_client_item_id)376 sync_pb::SyncEntity* MockConnectionManager::AddUpdateBookmark(
377     string id,
378     string parent_id,
379     string name, int64 version,
380     int64 sync_ts,
381     string originator_cache_guid,
382     string originator_client_item_id) {
383   sync_pb::SyncEntity* ret =
384       AddUpdateFull(id, parent_id, name, version, sync_ts, false);
385   ret->set_originator_cache_guid(originator_cache_guid);
386   ret->set_originator_client_item_id(originator_client_item_id);
387   return ret;
388 }
389 
AddUpdateFromLastCommit()390 sync_pb::SyncEntity* MockConnectionManager::AddUpdateFromLastCommit() {
391   EXPECT_EQ(1, last_sent_commit().entries_size());
392   EXPECT_EQ(1, last_commit_response().entryresponse_size());
393   EXPECT_EQ(CommitResponse::SUCCESS,
394       last_commit_response().entryresponse(0).response_type());
395 
396   if (last_sent_commit().entries(0).deleted()) {
397     AddUpdateTombstone(syncable::Id::CreateFromServerId(
398         last_sent_commit().entries(0).id_string()));
399   } else {
400     sync_pb::SyncEntity* ent = GetUpdateResponse()->add_entries();
401     ent->CopyFrom(last_sent_commit().entries(0));
402     ent->clear_insert_after_item_id();
403     ent->clear_old_parent_id();
404     ent->set_position_in_parent(
405         last_commit_response().entryresponse(0).position_in_parent());
406     ent->set_version(
407         last_commit_response().entryresponse(0).version());
408     ent->set_id_string(
409         last_commit_response().entryresponse(0).id_string());
410 
411     // This is the same hack as in AddUpdateMeta.  See the comment in that
412     // function for more information.
413     ent->set_originator_cache_guid(kCacheGuid);
414     ent->set_originator_client_item_id(
415         last_commit_response().entryresponse(0).id_string());
416 
417     if (last_sent_commit().entries(0).has_unique_position()) {
418       ent->mutable_unique_position()->CopyFrom(
419           last_sent_commit().entries(0).unique_position());
420     }
421 
422     // Tests don't currently care about the following:
423     // parent_id_string, name, non_unique_name.
424   }
425   return GetMutableLastUpdate();
426 }
427 
AddUpdateTombstone(const syncable::Id & id)428 void MockConnectionManager::AddUpdateTombstone(const syncable::Id& id) {
429   // Tombstones have only the ID set and dummy values for the required fields.
430   sync_pb::SyncEntity* ent = GetUpdateResponse()->add_entries();
431   ent->set_id_string(id.GetServerId());
432   ent->set_version(0);
433   ent->set_name("");
434   ent->set_deleted(true);
435 
436   // Make sure we can still extract the ModelType from this tombstone.
437   ent->mutable_specifics()->mutable_bookmark();
438 }
439 
SetLastUpdateDeleted()440 void MockConnectionManager::SetLastUpdateDeleted() {
441   // Tombstones have only the ID set.  Wipe anything else.
442   string id_string = GetMutableLastUpdate()->id_string();
443   GetUpdateResponse()->mutable_entries()->RemoveLast();
444   AddUpdateTombstone(syncable::Id::CreateFromServerId(id_string));
445 }
446 
SetLastUpdateOriginatorFields(const string & client_id,const string & entry_id)447 void MockConnectionManager::SetLastUpdateOriginatorFields(
448     const string& client_id,
449     const string& entry_id) {
450   GetMutableLastUpdate()->set_originator_cache_guid(client_id);
451   GetMutableLastUpdate()->set_originator_client_item_id(entry_id);
452 }
453 
SetLastUpdateServerTag(const string & tag)454 void MockConnectionManager::SetLastUpdateServerTag(const string& tag) {
455   GetMutableLastUpdate()->set_server_defined_unique_tag(tag);
456 }
457 
SetLastUpdateClientTag(const string & tag)458 void MockConnectionManager::SetLastUpdateClientTag(const string& tag) {
459   GetMutableLastUpdate()->set_client_defined_unique_tag(tag);
460 }
461 
SetLastUpdatePosition(int64 server_position)462 void MockConnectionManager::SetLastUpdatePosition(int64 server_position) {
463   GetMutableLastUpdate()->set_position_in_parent(server_position);
464 }
465 
SetNewTimestamp(int ts)466 void MockConnectionManager::SetNewTimestamp(int ts) {
467   next_token_ = base::StringPrintf("mock connection ts = %d", ts);
468   ApplyToken();
469 }
470 
ApplyToken()471 void MockConnectionManager::ApplyToken() {
472   if (!update_queue_.empty()) {
473     GetUpdateResponse()->clear_new_progress_marker();
474     sync_pb::DataTypeProgressMarker* new_marker =
475         GetUpdateResponse()->add_new_progress_marker();
476     new_marker->set_data_type_id(-1);  // Invalid -- clients shouldn't see.
477     new_marker->set_token(next_token_);
478   }
479 }
480 
SetChangesRemaining(int64 timestamp)481 void MockConnectionManager::SetChangesRemaining(int64 timestamp) {
482   GetUpdateResponse()->set_changes_remaining(timestamp);
483 }
484 
ProcessGetUpdates(sync_pb::ClientToServerMessage * csm,sync_pb::ClientToServerResponse * response)485 void MockConnectionManager::ProcessGetUpdates(
486     sync_pb::ClientToServerMessage* csm,
487     sync_pb::ClientToServerResponse* response) {
488   CHECK(csm->has_get_updates());
489   ASSERT_EQ(csm->message_contents(), ClientToServerMessage::GET_UPDATES);
490   const GetUpdatesMessage& gu = csm->get_updates();
491   num_get_updates_requests_++;
492   EXPECT_FALSE(gu.has_from_timestamp());
493   EXPECT_FALSE(gu.has_requested_types());
494 
495   if (fail_non_periodic_get_updates_) {
496     EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::PERIODIC,
497               gu.caller_info().source());
498   }
499 
500   // Verify that the items we're about to send back to the client are of
501   // the types requested by the client.  If this fails, it probably indicates
502   // a test bug.
503   EXPECT_TRUE(gu.fetch_folders());
504   EXPECT_FALSE(gu.has_requested_types());
505   if (update_queue_.empty()) {
506     GetUpdateResponse();
507   }
508   sync_pb::GetUpdatesResponse* updates = &update_queue_.front();
509   for (int i = 0; i < updates->entries_size(); ++i) {
510     if (!updates->entries(i).deleted()) {
511       ModelType entry_type = GetModelType(updates->entries(i));
512       EXPECT_TRUE(
513           IsModelTypePresentInSpecifics(gu.from_progress_marker(), entry_type))
514           << "Syncer did not request updates being provided by the test.";
515     }
516   }
517 
518   response->mutable_get_updates()->CopyFrom(*updates);
519 
520   // Set appropriate progress markers, overriding the value squirreled
521   // away by ApplyToken().
522   std::string token = response->get_updates().new_progress_marker(0).token();
523   response->mutable_get_updates()->clear_new_progress_marker();
524   for (int i = 0; i < gu.from_progress_marker_size(); ++i) {
525     sync_pb::DataTypeProgressMarker* new_marker =
526         response->mutable_get_updates()->add_new_progress_marker();
527     new_marker->set_data_type_id(gu.from_progress_marker(i).data_type_id());
528     new_marker->set_token(token);
529   }
530 
531   // Fill the keystore key if requested.
532   if (gu.need_encryption_key())
533     response->mutable_get_updates()->add_encryption_keys(keystore_key_);
534 
535   update_queue_.pop_front();
536 
537   if (gu_client_command_) {
538     response->mutable_client_command()->CopyFrom(*gu_client_command_.get());
539   }
540 }
541 
SetKeystoreKey(const std::string & key)542 void MockConnectionManager::SetKeystoreKey(const std::string& key) {
543   // Note: this is not a thread-safe set, ok for now.  NOT ok if tests
544   // run the syncer on the background thread while this method is called.
545   keystore_key_ = key;
546 }
547 
ShouldConflictThisCommit()548 bool MockConnectionManager::ShouldConflictThisCommit() {
549   bool conflict = false;
550   if (conflict_all_commits_) {
551     conflict = true;
552   } else if (conflict_n_commits_ > 0) {
553     conflict = true;
554     --conflict_n_commits_;
555   }
556   return conflict;
557 }
558 
ShouldTransientErrorThisId(syncable::Id id)559 bool MockConnectionManager::ShouldTransientErrorThisId(syncable::Id id) {
560   return find(transient_error_ids_.begin(), transient_error_ids_.end(), id)
561       != transient_error_ids_.end();
562 }
563 
ProcessCommit(sync_pb::ClientToServerMessage * csm,sync_pb::ClientToServerResponse * response_buffer)564 void MockConnectionManager::ProcessCommit(
565     sync_pb::ClientToServerMessage* csm,
566     sync_pb::ClientToServerResponse* response_buffer) {
567   CHECK(csm->has_commit());
568   ASSERT_EQ(csm->message_contents(), ClientToServerMessage::COMMIT);
569   map <string, string> changed_ids;
570   const CommitMessage& commit_message = csm->commit();
571   CommitResponse* commit_response = response_buffer->mutable_commit();
572   commit_messages_.push_back(new CommitMessage);
573   commit_messages_.back()->CopyFrom(commit_message);
574   map<string, sync_pb::CommitResponse_EntryResponse*> response_map;
575   for (int i = 0; i < commit_message.entries_size() ; i++) {
576     const sync_pb::SyncEntity& entry = commit_message.entries(i);
577     CHECK(entry.has_id_string());
578     string id_string = entry.id_string();
579     ASSERT_LT(entry.name().length(), 256ul) << " name probably too long. True "
580         "server name checking not implemented";
581     syncable::Id id;
582     if (entry.version() == 0) {
583       // Relies on our new item string id format. (string representation of a
584       // negative number).
585       id = syncable::Id::CreateFromClientString(id_string);
586     } else {
587       id = syncable::Id::CreateFromServerId(id_string);
588     }
589     committed_ids_.push_back(id);
590 
591     if (response_map.end() == response_map.find(id_string))
592       response_map[id_string] = commit_response->add_entryresponse();
593     sync_pb::CommitResponse_EntryResponse* er = response_map[id_string];
594     if (ShouldConflictThisCommit()) {
595       er->set_response_type(CommitResponse::CONFLICT);
596       continue;
597     }
598     if (ShouldTransientErrorThisId(id)) {
599       er->set_response_type(CommitResponse::TRANSIENT_ERROR);
600       continue;
601     }
602     er->set_response_type(CommitResponse::SUCCESS);
603     er->set_version(entry.version() + 1);
604     if (!commit_time_rename_prepended_string_.empty()) {
605       // Commit time rename sent down from the server.
606       er->set_name(commit_time_rename_prepended_string_ + entry.name());
607     }
608     string parent_id_string = entry.parent_id_string();
609     // Remap id's we've already assigned.
610     if (changed_ids.end() != changed_ids.find(parent_id_string)) {
611       parent_id_string = changed_ids[parent_id_string];
612       er->set_parent_id_string(parent_id_string);
613     }
614     if (entry.has_version() && 0 != entry.version()) {
615       er->set_id_string(id_string);  // Allows verification.
616     } else {
617       string new_id = base::StringPrintf("mock_server:%d", next_new_id_++);
618       changed_ids[id_string] = new_id;
619       er->set_id_string(new_id);
620     }
621   }
622   commit_responses_.push_back(new CommitResponse(*commit_response));
623 
624   if (commit_client_command_) {
625     response_buffer->mutable_client_command()->CopyFrom(
626         *commit_client_command_.get());
627   }
628 }
629 
AddUpdateDirectory(syncable::Id id,syncable::Id parent_id,string name,int64 version,int64 sync_ts,string originator_cache_guid,string originator_client_item_id)630 sync_pb::SyncEntity* MockConnectionManager::AddUpdateDirectory(
631     syncable::Id id,
632     syncable::Id parent_id,
633     string name,
634     int64 version,
635     int64 sync_ts,
636     string originator_cache_guid,
637     string originator_client_item_id) {
638   return AddUpdateDirectory(id.GetServerId(), parent_id.GetServerId(),
639                             name, version, sync_ts, originator_cache_guid,
640                             originator_client_item_id);
641 }
642 
AddUpdateBookmark(syncable::Id id,syncable::Id parent_id,string name,int64 version,int64 sync_ts,string originator_cache_guid,string originator_client_item_id)643 sync_pb::SyncEntity* MockConnectionManager::AddUpdateBookmark(
644     syncable::Id id,
645     syncable::Id parent_id,
646     string name,
647     int64 version,
648     int64 sync_ts,
649     string originator_cache_guid,
650     string originator_client_item_id) {
651   return AddUpdateBookmark(id.GetServerId(), parent_id.GetServerId(),
652                            name, version, sync_ts, originator_cache_guid,
653                            originator_client_item_id);
654 }
655 
GetMutableLastUpdate()656 sync_pb::SyncEntity* MockConnectionManager::GetMutableLastUpdate() {
657   sync_pb::GetUpdatesResponse* updates = GetUpdateResponse();
658   EXPECT_GT(updates->entries_size(), 0);
659   return updates->mutable_entries()->Mutable(updates->entries_size() - 1);
660 }
661 
NextUpdateBatch()662 void MockConnectionManager::NextUpdateBatch() {
663   update_queue_.push_back(sync_pb::GetUpdatesResponse::default_instance());
664   SetChangesRemaining(0);
665   ApplyToken();
666 }
667 
last_sent_commit() const668 const CommitMessage& MockConnectionManager::last_sent_commit() const {
669   EXPECT_TRUE(!commit_messages_.empty());
670   return *commit_messages_.back();
671 }
672 
last_commit_response() const673 const CommitResponse& MockConnectionManager::last_commit_response() const {
674   EXPECT_TRUE(!commit_responses_.empty());
675   return *commit_responses_.back();
676 }
677 
678 const sync_pb::ClientToServerMessage&
last_request() const679     MockConnectionManager::last_request() const {
680   EXPECT_TRUE(!requests_.empty());
681   return requests_.back();
682 }
683 
684 const std::vector<sync_pb::ClientToServerMessage>&
requests() const685     MockConnectionManager::requests() const {
686   return requests_;
687 }
688 
IsModelTypePresentInSpecifics(const google::protobuf::RepeatedPtrField<sync_pb::DataTypeProgressMarker> & filter,ModelType value)689 bool MockConnectionManager::IsModelTypePresentInSpecifics(
690     const google::protobuf::RepeatedPtrField<
691         sync_pb::DataTypeProgressMarker>& filter,
692     ModelType value) {
693   int data_type_id = GetSpecificsFieldNumberFromModelType(value);
694   for (int i = 0; i < filter.size(); ++i) {
695     if (filter.Get(i).data_type_id() == data_type_id) {
696       return true;
697     }
698   }
699   return false;
700 }
701 
702 sync_pb::DataTypeProgressMarker const*
GetProgressMarkerForType(const google::protobuf::RepeatedPtrField<sync_pb::DataTypeProgressMarker> & filter,ModelType value)703     MockConnectionManager::GetProgressMarkerForType(
704         const google::protobuf::RepeatedPtrField<
705             sync_pb::DataTypeProgressMarker>& filter,
706         ModelType value) {
707   int data_type_id = GetSpecificsFieldNumberFromModelType(value);
708   for (int i = 0; i < filter.size(); ++i) {
709     if (filter.Get(i).data_type_id() == data_type_id) {
710       return &(filter.Get(i));
711     }
712   }
713   return NULL;
714 }
715 
SetServerReachable()716 void MockConnectionManager::SetServerReachable() {
717   server_reachable_ = true;
718 }
719 
SetServerNotReachable()720 void MockConnectionManager::SetServerNotReachable() {
721   server_reachable_ = false;
722 }
723 
UpdateConnectionStatus()724 void MockConnectionManager::UpdateConnectionStatus() {
725   if (!server_reachable_) {
726     server_status_ = HttpResponse::CONNECTION_UNAVAILABLE;
727   } else {
728     server_status_ = HttpResponse::SERVER_CONNECTION_OK;
729   }
730 }
731 
SetServerStatus(HttpResponse::ServerConnectionCode server_status)732 void MockConnectionManager::SetServerStatus(
733     HttpResponse::ServerConnectionCode server_status) {
734   server_status_ = server_status;
735 }
736 
737 }  // namespace syncer
738