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
3 // found in the LICENSE file.
4 
5 #include "components/sync/test/fake_server/fake_server.h"
6 
7 #include <algorithm>
8 #include <limits>
9 #include <set>
10 #include <utility>
11 
12 #include "base/command_line.h"
13 #include "base/guid.h"
14 #include "base/hash/hash.h"
15 #include "base/json/json_writer.h"
16 #include "base/logging.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/synchronization/lock.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "base/time/time.h"
25 #include "base/values.h"
26 #include "components/sync/engine_impl/net/server_connection_manager.h"
27 #include "components/sync/protocol/proto_value_conversions.h"
28 #include "net/base/net_errors.h"
29 #include "net/http/http_status_code.h"
30 
31 using syncer::GetModelType;
32 using syncer::GetModelTypeFromSpecifics;
33 using syncer::LoopbackServer;
34 using syncer::LoopbackServerEntity;
35 using syncer::ModelType;
36 using syncer::ModelTypeSet;
37 
38 namespace fake_server {
39 
FakeServer()40 FakeServer::FakeServer()
41     : commit_error_type_(sync_pb::SyncEnums::SUCCESS),
42       error_type_(sync_pb::SyncEnums::SUCCESS),
43       alternate_triggered_errors_(false),
44       request_counter_(0),
45       disallow_sending_encryption_keys_(false) {
46   base::ScopedAllowBlockingForTesting allow_blocking;
47   loopback_server_storage_ = std::make_unique<base::ScopedTempDir>();
48   if (!loopback_server_storage_->CreateUniqueTempDir()) {
49     NOTREACHED() << "Creating temp dir failed.";
50   }
51   loopback_server_ = std::make_unique<syncer::LoopbackServer>(
52       loopback_server_storage_->GetPath().AppendASCII("profile.pb"));
53   loopback_server_->set_observer_for_tests(this);
54 }
55 
FakeServer(const base::FilePath & user_data_dir)56 FakeServer::FakeServer(const base::FilePath& user_data_dir)
57     : commit_error_type_(sync_pb::SyncEnums::SUCCESS),
58       error_type_(sync_pb::SyncEnums::SUCCESS),
59       alternate_triggered_errors_(false),
60       request_counter_(0),
61       disallow_sending_encryption_keys_(false) {
62   base::ScopedAllowBlockingForTesting allow_blocking;
63   base::FilePath loopback_server_path =
64       user_data_dir.AppendASCII("FakeSyncServer");
65   loopback_server_ = std::make_unique<syncer::LoopbackServer>(
66       loopback_server_path.AppendASCII("profile.pb"));
67   loopback_server_->set_observer_for_tests(this);
68 }
69 
~FakeServer()70 FakeServer::~FakeServer() {
71   base::ScopedAllowBlockingForTesting allow_blocking;
72   loopback_server_storage_.reset();
73 }
74 
75 namespace {
76 
77 struct HashAndTime {
78   uint64_t hash;
79   base::Time time;
80 };
81 
82 std::unique_ptr<sync_pb::DataTypeProgressMarker>
RemoveFullUpdateTypeProgressMarkerIfExists(ModelType model_type,sync_pb::ClientToServerMessage * message)83 RemoveFullUpdateTypeProgressMarkerIfExists(
84     ModelType model_type,
85     sync_pb::ClientToServerMessage* message) {
86   DCHECK(model_type == syncer::AUTOFILL_WALLET_DATA ||
87          model_type == syncer::AUTOFILL_WALLET_OFFER);
88   google::protobuf::RepeatedPtrField<sync_pb::DataTypeProgressMarker>*
89       progress_markers =
90           message->mutable_get_updates()->mutable_from_progress_marker();
91   for (int index = 0; index < progress_markers->size(); ++index) {
92     if (syncer::GetModelTypeFromSpecificsFieldNumber(
93             progress_markers->Get(index).data_type_id()) == model_type) {
94       auto result = std::make_unique<sync_pb::DataTypeProgressMarker>(
95           progress_markers->Get(index));
96       progress_markers->erase(progress_markers->begin() + index);
97       return result;
98     }
99   }
100   return nullptr;
101 }
102 
VerifyNoProgressMarkerExistsInResponseForFullUpdateType(sync_pb::GetUpdatesResponse * gu_response)103 void VerifyNoProgressMarkerExistsInResponseForFullUpdateType(
104     sync_pb::GetUpdatesResponse* gu_response) {
105   for (const sync_pb::DataTypeProgressMarker& marker :
106        gu_response->new_progress_marker()) {
107     ModelType type =
108         syncer::GetModelTypeFromSpecificsFieldNumber(marker.data_type_id());
109     // Verified there is no progress marker for the full sync type we cared
110     // about.
111     DCHECK(type != syncer::AUTOFILL_WALLET_DATA &&
112            type != syncer::AUTOFILL_WALLET_OFFER);
113   }
114 }
115 
116 // Returns a hash representing |entities| including each entity's ID and
117 // version, in a way that the order of the entities is irrelevant.
ComputeEntitiesHash(const std::vector<sync_pb::SyncEntity> & entities)118 uint64_t ComputeEntitiesHash(const std::vector<sync_pb::SyncEntity>& entities) {
119   // Make sure to pick a token that will be consistent across clients when
120   // receiving the same data. We sum up the hashes which has the nice side
121   // effect of being independent of the order.
122   uint64_t hash = 0;
123   for (const auto& entity : entities) {
124     hash += base::PersistentHash(entity.id_string());
125     hash += entity.version();
126   }
127   return hash;
128 }
129 
130 // Encodes a hash and timestamp in a string that is meant to be used as progress
131 // marker token.
PackProgressMarkerToken(const HashAndTime & hash_and_time)132 std::string PackProgressMarkerToken(const HashAndTime& hash_and_time) {
133   return base::NumberToString(hash_and_time.hash) + " " +
134          base::NumberToString(
135              hash_and_time.time.ToDeltaSinceWindowsEpoch().InMicroseconds());
136 }
137 
138 // Reverse for PackProgressMarkerToken.
UnpackProgressMarkerToken(const std::string & token)139 HashAndTime UnpackProgressMarkerToken(const std::string& token) {
140   // The hash is stored as a first piece of the string (space delimited), the
141   // second piece is the timestamp.
142   HashAndTime hash_and_time;
143   std::vector<base::StringPiece> pieces =
144       base::SplitStringPiece(token, base::kWhitespaceASCII,
145                              base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
146   uint64_t micros_since_windows_epoch = 0;
147   if (pieces.size() != 2 ||
148       !base::StringToUint64(pieces[0], &hash_and_time.hash) ||
149       !base::StringToUint64(pieces[1], &micros_since_windows_epoch)) {
150     // The hash defaults to an arbitrary hash which should in practice never
151     // match actual hashes (zero is avoided because it's actually a sum).
152     return {std::numeric_limits<uint64_t>::max(), base::Time()};
153   }
154 
155   hash_and_time.time = base::Time::FromDeltaSinceWindowsEpoch(
156       base::TimeDelta::FromMicroseconds(micros_since_windows_epoch));
157   return hash_and_time;
158 }
159 
PopulateFullUpdateTypeResults(const std::vector<sync_pb::SyncEntity> & entities,const sync_pb::DataTypeProgressMarker & old_marker,sync_pb::GetUpdatesResponse * gu_response)160 void PopulateFullUpdateTypeResults(
161     const std::vector<sync_pb::SyncEntity>& entities,
162     const sync_pb::DataTypeProgressMarker& old_marker,
163     sync_pb::GetUpdatesResponse* gu_response) {
164   sync_pb::DataTypeProgressMarker* new_marker =
165       gu_response->add_new_progress_marker();
166   new_marker->set_data_type_id(old_marker.data_type_id());
167 
168   uint64_t hash = ComputeEntitiesHash(entities);
169 
170   // We also include information about the fetch time in the token. This is
171   // in-line with the server behavior and -- as it keeps changing -- allows
172   // integration tests to wait for a GetUpdates call to finish, even if they
173   // don't contain data updates.
174   new_marker->set_token(PackProgressMarkerToken({hash, base::Time::Now()}));
175 
176   if (!old_marker.has_token() ||
177       !AreFullUpdateTypeDataProgressMarkersEquivalent(old_marker,
178                                                       *new_marker)) {
179     // New data available; include new elements and tell the client to drop all
180     // previous data.
181     int64_t version =
182         (base::Time::Now() - base::Time::UnixEpoch()).InMilliseconds();
183     for (const auto& entity : entities) {
184       sync_pb::SyncEntity* response_entity = gu_response->add_entries();
185       *response_entity = entity;
186       response_entity->set_version(version);
187     }
188 
189     // Set the GC directive to implement non-incremental reads.
190     new_marker->mutable_gc_directive()->set_type(
191         sync_pb::GarbageCollectionDirective::VERSION_WATERMARK);
192     new_marker->mutable_gc_directive()->set_version_watermark(version - 1);
193   }
194 }
195 
PrettyPrintValue(std::unique_ptr<base::DictionaryValue> value)196 std::string PrettyPrintValue(std::unique_ptr<base::DictionaryValue> value) {
197   std::string message;
198   base::JSONWriter::WriteWithOptions(
199       *value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &message);
200   return message;
201 }
202 
203 }  // namespace
204 
AreFullUpdateTypeDataProgressMarkersEquivalent(const sync_pb::DataTypeProgressMarker & marker1,const sync_pb::DataTypeProgressMarker & marker2)205 bool AreFullUpdateTypeDataProgressMarkersEquivalent(
206     const sync_pb::DataTypeProgressMarker& marker1,
207     const sync_pb::DataTypeProgressMarker& marker2) {
208   return UnpackProgressMarkerToken(marker1.token()).hash ==
209          UnpackProgressMarkerToken(marker2.token()).hash;
210 }
211 
HandleCommand(const std::string & request,std::string * response)212 net::HttpStatusCode FakeServer::HandleCommand(const std::string& request,
213                                               std::string* response) {
214   DCHECK(thread_checker_.CalledOnValidThread());
215   response->clear();
216 
217   request_counter_++;
218 
219   sync_pb::ClientToServerMessage message;
220   bool parsed = message.ParseFromString(request);
221   DCHECK(parsed) << "Unable to parse the ClientToServerMessage.";
222 
223   LogForTestFailure(FROM_HERE, "REQUEST",
224                     PrettyPrintValue(syncer::ClientToServerMessageToValue(
225                         message, /*include_specifics=*/true)));
226 
227   sync_pb::ClientToServerResponse response_proto;
228   net::HttpStatusCode http_status_code =
229       HandleParsedCommand(message, &response_proto);
230 
231   LogForTestFailure(FROM_HERE, "RESPONSE",
232                     PrettyPrintValue(syncer::ClientToServerResponseToValue(
233                         response_proto,
234                         /*include_specifics=*/true)));
235 
236   *response = response_proto.SerializeAsString();
237   return http_status_code;
238 }
239 
HandleParsedCommand(const sync_pb::ClientToServerMessage & message,sync_pb::ClientToServerResponse * response)240 net::HttpStatusCode FakeServer::HandleParsedCommand(
241     const sync_pb::ClientToServerMessage& message,
242     sync_pb::ClientToServerResponse* response) {
243   DCHECK(response);
244   response->Clear();
245 
246   // Store last message from the client in any case.
247   switch (message.message_contents()) {
248     case sync_pb::ClientToServerMessage::GET_UPDATES:
249       last_getupdates_message_ = message;
250       break;
251     case sync_pb::ClientToServerMessage::COMMIT:
252       last_commit_message_ = message;
253       break;
254     default:
255       // Don't care.
256       break;
257   }
258 
259   if (http_error_status_code_) {
260     return *http_error_status_code_;
261   }
262 
263   if (message.message_contents() == sync_pb::ClientToServerMessage::COMMIT &&
264       commit_error_type_ != sync_pb::SyncEnums::SUCCESS &&
265       ShouldSendTriggeredError()) {
266     response->set_error_code(commit_error_type_);
267     response->set_store_birthday(loopback_server_->GetStoreBirthday());
268     return net::HTTP_OK;
269   }
270 
271   if (error_type_ != sync_pb::SyncEnums::SUCCESS &&
272       ShouldSendTriggeredError()) {
273     response->set_error_code(error_type_);
274     response->set_store_birthday(loopback_server_->GetStoreBirthday());
275     return net::HTTP_OK;
276   }
277 
278   if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) {
279     *response->mutable_error() = *triggered_actionable_error_;
280     response->set_store_birthday(loopback_server_->GetStoreBirthday());
281     return net::HTTP_OK;
282   }
283 
284   // The loopback server does not know how to handle Wallet or Offer requests
285   // -- and should not. The FakeServer is handling those instead. The
286   // loopback server has a strong expectations about how progress tokens are
287   // structured. To not interfere with this, we remove progress markers for
288   // full-update types before passing the request to the loopback server.
289   sync_pb::ClientToServerMessage message_without_full_update_type = message;
290   std::unique_ptr<sync_pb::DataTypeProgressMarker> wallet_marker =
291       RemoveFullUpdateTypeProgressMarkerIfExists(
292           syncer::AUTOFILL_WALLET_DATA, &message_without_full_update_type);
293   std::unique_ptr<sync_pb::DataTypeProgressMarker> offer_marker =
294       RemoveFullUpdateTypeProgressMarkerIfExists(
295           syncer::AUTOFILL_WALLET_OFFER, &message_without_full_update_type);
296   net::HttpStatusCode http_status_code =
297       SendToLoopbackServer(message_without_full_update_type, response);
298 
299   if (response->has_get_updates() && disallow_sending_encryption_keys_) {
300     response->mutable_get_updates()->clear_encryption_keys();
301   }
302 
303   if (http_status_code == net::HTTP_OK &&
304       message.message_contents() ==
305           sync_pb::ClientToServerMessage::GET_UPDATES) {
306     // The response from the loopback server should never have an existing
307     // progress marker for full-update types (because FakeServer removes it from
308     // the request).
309     VerifyNoProgressMarkerExistsInResponseForFullUpdateType(
310         response->mutable_get_updates());
311 
312     if (wallet_marker != nullptr) {
313       PopulateFullUpdateTypeResults(wallet_entities_, *wallet_marker,
314                                     response->mutable_get_updates());
315     }
316 
317     if (offer_marker != nullptr) {
318       PopulateFullUpdateTypeResults(offer_entities_, *offer_marker,
319                                     response->mutable_get_updates());
320     }
321   }
322 
323   if (http_status_code == net::HTTP_OK &&
324       response->error_code() == sync_pb::SyncEnums::SUCCESS) {
325     *response->mutable_client_command() = client_command_;
326   }
327 
328   return http_status_code;
329 }
330 
SendToLoopbackServer(const sync_pb::ClientToServerMessage & message,sync_pb::ClientToServerResponse * response)331 net::HttpStatusCode FakeServer::SendToLoopbackServer(
332     const sync_pb::ClientToServerMessage& message,
333     sync_pb::ClientToServerResponse* response) {
334   base::ScopedAllowBlockingForTesting allow_blocking;
335   return loopback_server_->HandleCommand(message, response);
336 }
337 
GetLastCommitMessage(sync_pb::ClientToServerMessage * message)338 bool FakeServer::GetLastCommitMessage(sync_pb::ClientToServerMessage* message) {
339   if (!last_commit_message_.has_commit())
340     return false;
341 
342   message->CopyFrom(last_commit_message_);
343   return true;
344 }
345 
GetLastGetUpdatesMessage(sync_pb::ClientToServerMessage * message)346 bool FakeServer::GetLastGetUpdatesMessage(
347     sync_pb::ClientToServerMessage* message) {
348   if (!last_getupdates_message_.has_get_updates())
349     return false;
350 
351   message->CopyFrom(last_getupdates_message_);
352   return true;
353 }
354 
OverrideResponseType(LoopbackServer::ResponseTypeProvider response_type_override)355 void FakeServer::OverrideResponseType(
356     LoopbackServer::ResponseTypeProvider response_type_override) {
357   loopback_server_->OverrideResponseType(std::move(response_type_override));
358 }
359 
360 std::unique_ptr<base::DictionaryValue>
GetEntitiesAsDictionaryValue()361 FakeServer::GetEntitiesAsDictionaryValue() {
362   DCHECK(thread_checker_.CalledOnValidThread());
363   return loopback_server_->GetEntitiesAsDictionaryValue();
364 }
365 
GetSyncEntitiesByModelType(ModelType model_type)366 std::vector<sync_pb::SyncEntity> FakeServer::GetSyncEntitiesByModelType(
367     ModelType model_type) {
368   DCHECK(thread_checker_.CalledOnValidThread());
369   return loopback_server_->GetSyncEntitiesByModelType(model_type);
370 }
371 
372 std::vector<sync_pb::SyncEntity>
GetPermanentSyncEntitiesByModelType(ModelType model_type)373 FakeServer::GetPermanentSyncEntitiesByModelType(ModelType model_type) {
374   DCHECK(thread_checker_.CalledOnValidThread());
375   return loopback_server_->GetPermanentSyncEntitiesByModelType(model_type);
376 }
377 
GetKeystoreKeys() const378 const std::vector<std::vector<uint8_t>>& FakeServer::GetKeystoreKeys() const {
379   DCHECK(thread_checker_.CalledOnValidThread());
380   return loopback_server_->GetKeystoreKeysForTesting();
381 }
382 
TriggerKeystoreKeyRotation()383 void FakeServer::TriggerKeystoreKeyRotation() {
384   DCHECK(thread_checker_.CalledOnValidThread());
385   loopback_server_->AddNewKeystoreKeyForTesting();
386 
387   std::vector<sync_pb::SyncEntity> nigori_entities =
388       loopback_server_->GetPermanentSyncEntitiesByModelType(syncer::NIGORI);
389 
390   DCHECK_EQ(nigori_entities.size(), 1U);
391   bool success =
392       ModifyEntitySpecifics(LoopbackServerEntity::GetTopLevelId(syncer::NIGORI),
393                             nigori_entities[0].specifics());
394   DCHECK(success);
395 }
396 
InjectEntity(std::unique_ptr<LoopbackServerEntity> entity)397 void FakeServer::InjectEntity(std::unique_ptr<LoopbackServerEntity> entity) {
398   DCHECK(thread_checker_.CalledOnValidThread());
399   DCHECK(entity->GetModelType() != syncer::AUTOFILL_WALLET_DATA &&
400          entity->GetModelType() != syncer::AUTOFILL_WALLET_OFFER)
401       << "Wallet/Offer data must be injected via "
402          "SetWalletData()/SetOfferData().";
403 
404   const ModelType model_type = entity->GetModelType();
405 
406   loopback_server_->SaveEntity(std::move(entity));
407 
408   // Notify observers so invalidations are mimic-ed.
409   OnCommit(/*committer_invalidator_client_id=*/std::string(),
410            /*committed_model_types=*/{model_type});
411 }
412 
SetWalletData(const std::vector<sync_pb::SyncEntity> & wallet_entities)413 base::Time FakeServer::SetWalletData(
414     const std::vector<sync_pb::SyncEntity>& wallet_entities) {
415   DCHECK(!wallet_entities.empty());
416   ModelType model_type =
417       GetModelTypeFromSpecifics(wallet_entities[0].specifics());
418   DCHECK(model_type == syncer::AUTOFILL_WALLET_DATA);
419   wallet_entities_ = wallet_entities;
420 
421   const base::Time now = base::Time::Now();
422   const int64_t version = (now - base::Time::UnixEpoch()).InMilliseconds();
423 
424   for (sync_pb::SyncEntity& entity : wallet_entities_) {
425     DCHECK(!entity.has_client_defined_unique_tag())
426         << "The sync server doesn not provide a client tag for wallet entries.";
427     DCHECK(!entity.id_string().empty()) << "server id required!";
428 
429     // The version is overridden during serving of the entities, but is useful
430     // here to influence the entities' hash.
431     entity.set_version(version);
432   }
433 
434   OnCommit(/*committer_invalidator_client_id=*/std::string(),
435            /*committed_model_types=*/{syncer::AUTOFILL_WALLET_DATA});
436 
437   return now;
438 }
439 
SetOfferData(const std::vector<sync_pb::SyncEntity> & offer_entities)440 base::Time FakeServer::SetOfferData(
441     const std::vector<sync_pb::SyncEntity>& offer_entities) {
442   DCHECK(!offer_entities.empty());
443   ModelType model_type =
444       GetModelTypeFromSpecifics(offer_entities[0].specifics());
445   DCHECK(model_type == syncer::AUTOFILL_WALLET_OFFER);
446   offer_entities_ = offer_entities;
447 
448   const base::Time now = base::Time::Now();
449   const int64_t version = (now - base::Time::UnixEpoch()).InMilliseconds();
450 
451   for (sync_pb::SyncEntity& entity : offer_entities_) {
452     DCHECK(!entity.has_client_defined_unique_tag())
453         << "The sync server doesn not provide a client tag for offer entries.";
454     DCHECK(!entity.id_string().empty()) << "server id required!";
455 
456     // The version is overridden during serving of the entities, but is useful
457     // here to influence the entities' hash.
458     entity.set_version(version);
459   }
460 
461   OnCommit(/*committer_id=*/std::string(),
462            /*committed_model_types=*/{syncer::AUTOFILL_WALLET_OFFER});
463 
464   return now;
465 }
466 
467 // static
GetProgressMarkerTimestamp(const sync_pb::DataTypeProgressMarker & progress_marker)468 base::Time FakeServer::GetProgressMarkerTimestamp(
469     const sync_pb::DataTypeProgressMarker& progress_marker) {
470   return UnpackProgressMarkerToken(progress_marker.token()).time;
471 }
472 
ModifyEntitySpecifics(const std::string & id,const sync_pb::EntitySpecifics & updated_specifics)473 bool FakeServer::ModifyEntitySpecifics(
474     const std::string& id,
475     const sync_pb::EntitySpecifics& updated_specifics) {
476   if (!loopback_server_->ModifyEntitySpecifics(id, updated_specifics)) {
477     return false;
478   }
479 
480   // Notify observers so invalidations are mimic-ed.
481   OnCommit(
482       /*committer_invalidator_client_id=*/std::string(),
483       /*committed_model_types=*/{GetModelTypeFromSpecifics(updated_specifics)});
484 
485   return true;
486 }
487 
ModifyBookmarkEntity(const std::string & id,const std::string & parent_id,const sync_pb::EntitySpecifics & updated_specifics)488 bool FakeServer::ModifyBookmarkEntity(
489     const std::string& id,
490     const std::string& parent_id,
491     const sync_pb::EntitySpecifics& updated_specifics) {
492   if (!loopback_server_->ModifyBookmarkEntity(id, parent_id,
493                                               updated_specifics)) {
494     return false;
495   }
496 
497   // Notify observers so invalidations are mimic-ed.
498   OnCommit(/*committer_invalidator_client_id=*/std::string(),
499            /*committed_model_types=*/{syncer::BOOKMARKS});
500 
501   return true;
502 }
503 
ClearServerData()504 void FakeServer::ClearServerData() {
505   DCHECK(thread_checker_.CalledOnValidThread());
506   base::ScopedAllowBlockingForTesting allow_blocking;
507   loopback_server_->ClearServerData();
508 }
509 
SetHttpError(net::HttpStatusCode http_status_code)510 void FakeServer::SetHttpError(net::HttpStatusCode http_status_code) {
511   DCHECK(thread_checker_.CalledOnValidThread());
512   DCHECK_GT(http_status_code, 0);
513   http_error_status_code_ = http_status_code;
514 }
515 
ClearHttpError()516 void FakeServer::ClearHttpError() {
517   DCHECK(thread_checker_.CalledOnValidThread());
518   http_error_status_code_ = base::nullopt;
519 }
520 
SetClientCommand(const sync_pb::ClientCommand & client_command)521 void FakeServer::SetClientCommand(
522     const sync_pb::ClientCommand& client_command) {
523   DCHECK(thread_checker_.CalledOnValidThread());
524   client_command_ = client_command;
525 }
526 
TriggerCommitError(const sync_pb::SyncEnums::ErrorType & error_type)527 void FakeServer::TriggerCommitError(
528     const sync_pb::SyncEnums::ErrorType& error_type) {
529   DCHECK(thread_checker_.CalledOnValidThread());
530   DCHECK(error_type == sync_pb::SyncEnums::SUCCESS || !HasTriggeredError());
531 
532   commit_error_type_ = error_type;
533 }
534 
TriggerError(const sync_pb::SyncEnums::ErrorType & error_type)535 void FakeServer::TriggerError(const sync_pb::SyncEnums::ErrorType& error_type) {
536   DCHECK(thread_checker_.CalledOnValidThread());
537   DCHECK(error_type == sync_pb::SyncEnums::SUCCESS || !HasTriggeredError());
538 
539   error_type_ = error_type;
540 }
541 
TriggerActionableError(const sync_pb::SyncEnums::ErrorType & error_type,const std::string & description,const std::string & url,const sync_pb::SyncEnums::Action & action)542 void FakeServer::TriggerActionableError(
543     const sync_pb::SyncEnums::ErrorType& error_type,
544     const std::string& description,
545     const std::string& url,
546     const sync_pb::SyncEnums::Action& action) {
547   DCHECK(thread_checker_.CalledOnValidThread());
548   DCHECK(!HasTriggeredError());
549 
550   sync_pb::ClientToServerResponse_Error* error =
551       new sync_pb::ClientToServerResponse_Error();
552   error->set_error_type(error_type);
553   error->set_error_description(description);
554   error->set_action(action);
555   triggered_actionable_error_.reset(error);
556 }
557 
ClearActionableError()558 void FakeServer::ClearActionableError() {
559   triggered_actionable_error_.reset();
560 }
561 
EnableAlternatingTriggeredErrors()562 bool FakeServer::EnableAlternatingTriggeredErrors() {
563   DCHECK(thread_checker_.CalledOnValidThread());
564   if (error_type_ == sync_pb::SyncEnums::SUCCESS &&
565       !triggered_actionable_error_) {
566     DVLOG(1) << "No triggered error set. Alternating can't be enabled.";
567     return false;
568   }
569 
570   alternate_triggered_errors_ = true;
571   // Reset the counter so that the the first request yields a triggered error.
572   request_counter_ = 0;
573   return true;
574 }
575 
DisallowSendingEncryptionKeys()576 void FakeServer::DisallowSendingEncryptionKeys() {
577   disallow_sending_encryption_keys_ = true;
578 }
579 
SetThrottledTypes(syncer::ModelTypeSet types)580 void FakeServer::SetThrottledTypes(syncer::ModelTypeSet types) {
581   loopback_server_->SetThrottledTypesForTesting(types);
582 }
583 
ShouldSendTriggeredError() const584 bool FakeServer::ShouldSendTriggeredError() const {
585   if (!alternate_triggered_errors_)
586     return true;
587 
588   // Check that the counter is odd so that we trigger an error on the first
589   // request after alternating is enabled.
590   return request_counter_ % 2 != 0;
591 }
592 
HasTriggeredError() const593 bool FakeServer::HasTriggeredError() const {
594   return commit_error_type_ != sync_pb::SyncEnums::SUCCESS ||
595          error_type_ != sync_pb::SyncEnums::SUCCESS ||
596          triggered_actionable_error_;
597 }
598 
AddObserver(Observer * observer)599 void FakeServer::AddObserver(Observer* observer) {
600   DCHECK(thread_checker_.CalledOnValidThread());
601   observers_.AddObserver(observer);
602 }
603 
RemoveObserver(Observer * observer)604 void FakeServer::RemoveObserver(Observer* observer) {
605   DCHECK(thread_checker_.CalledOnValidThread());
606   observers_.RemoveObserver(observer);
607 }
608 
OnCommit(const std::string & committer_invalidator_client_id,syncer::ModelTypeSet committed_model_types)609 void FakeServer::OnCommit(const std::string& committer_invalidator_client_id,
610                           syncer::ModelTypeSet committed_model_types) {
611   for (auto& observer : observers_)
612     observer.OnCommit(committer_invalidator_client_id, committed_model_types);
613 }
614 
OnHistoryCommit(const std::string & url)615 void FakeServer::OnHistoryCommit(const std::string& url) {
616   committed_history_urls_.insert(url);
617 }
618 
EnableStrongConsistencyWithConflictDetectionModel()619 void FakeServer::EnableStrongConsistencyWithConflictDetectionModel() {
620   DCHECK(thread_checker_.CalledOnValidThread());
621   loopback_server_->EnableStrongConsistencyWithConflictDetectionModel();
622 }
623 
SetMaxGetUpdatesBatchSize(int batch_size)624 void FakeServer::SetMaxGetUpdatesBatchSize(int batch_size) {
625   DCHECK(thread_checker_.CalledOnValidThread());
626   loopback_server_->SetMaxGetUpdatesBatchSize(batch_size);
627 }
628 
SetBagOfChips(const sync_pb::ChipBag & bag_of_chips)629 void FakeServer::SetBagOfChips(const sync_pb::ChipBag& bag_of_chips) {
630   DCHECK(thread_checker_.CalledOnValidThread());
631   loopback_server_->SetBagOfChipsForTesting(bag_of_chips);
632 }
633 
TriggerMigrationDoneError(syncer::ModelTypeSet types)634 void FakeServer::TriggerMigrationDoneError(syncer::ModelTypeSet types) {
635   DCHECK(thread_checker_.CalledOnValidThread());
636   loopback_server_->TriggerMigrationForTesting(types);
637 }
638 
GetCommittedHistoryURLs() const639 const std::set<std::string>& FakeServer::GetCommittedHistoryURLs() const {
640   return committed_history_urls_;
641 }
642 
GetStoreBirthday() const643 std::string FakeServer::GetStoreBirthday() const {
644   return loopback_server_->GetStoreBirthday();
645 }
646 
AsWeakPtr()647 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() {
648   DCHECK(thread_checker_.CalledOnValidThread());
649   return weak_ptr_factory_.GetWeakPtr();
650 }
651 
LogForTestFailure(const base::Location & location,const std::string & title,const std::string & body)652 void FakeServer::LogForTestFailure(const base::Location& location,
653                                    const std::string& title,
654                                    const std::string& body) {
655   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
656           "disable-fake-server-failure-output")) {
657     return;
658   }
659   gtest_scoped_traces_.push_back(std::make_unique<testing::ScopedTrace>(
660       location.file_name(), location.line_number(),
661       base::StringPrintf("--- %s %d (reverse chronological order) ---\n%s",
662                          title.c_str(), request_counter_, body.c_str())));
663 }
664 
665 }  // namespace fake_server
666