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