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], µs_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