Commit 3a4b4ccb authored by Mikel Astiz's avatar Mikel Astiz Committed by Commit Bot

[search engines] Add sync integration test for deletion of default

This patch adds a sync integration test that exercises a problematic
scenario: the deletion of a search engine via sync while the engine is
the default one locally.

This is possible because the default search engine is sync-ed via
preferences, that is, syncer::PREFERENCES, whereas the actual list of
search engines is sync-ed via another sync datatype, namely
syncer::SEARCH_ENGINES.

Ordering across different sync datatypes is not guaranteed and
therefore, if a user changes the default search engine and deletes the
old one, it is possible that a remote sync-ing device observes this
changes in the opposite order.

There is logic in TemplateURLService to special-case this scenario to
append underscores, and this patch simply documents this behavior using
a two-client sync integration test. Because reproducing the precise
problematic ordering is not feasible, the test leverages a
newly-introduced functionality in FakeServer that allows throttling
sync datatypes (resembling a very slow sync for preferences).

Change-Id: Ia0929717d0ea99a38969298b94f68a1c1ab44364
Bug: 1022775
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2471398
Commit-Queue: Mikel Astiz <mastiz@chromium.org>
Reviewed-by: default avatarMaksim Moskvitin <mmoskvitin@google.com>
Cr-Commit-Position: refs/heads/master@{#820238}
parent 9d53eecb
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include "chrome/browser/sync/test/integration/sync_datatype_helper.h" #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
#include "chrome/browser/sync/test/integration/sync_test.h" #include "chrome/browser/sync/test/integration/sync_test.h"
#include "components/search_engines/template_url.h" #include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_service.h"
using sync_datatype_helper::test; using sync_datatype_helper::test;
...@@ -54,7 +53,7 @@ bool TURLsMatch(const TemplateURL& turl1, const TemplateURL& turl2) { ...@@ -54,7 +53,7 @@ bool TURLsMatch(const TemplateURL& turl1, const TemplateURL& turl2) {
return result; return result;
} }
bool ServicesMatch(int profile_a, int profile_b) { bool ServicesMatch(int profile_a, int profile_b, std::ostream* os) {
TemplateURLService* service_a = TemplateURLService* service_a =
search_engines_helper::GetServiceForBrowserContext(profile_a); search_engines_helper::GetServiceForBrowserContext(profile_a);
TemplateURLService* service_b = TemplateURLService* service_b =
...@@ -66,14 +65,14 @@ bool ServicesMatch(int profile_a, int profile_b) { ...@@ -66,14 +65,14 @@ bool ServicesMatch(int profile_a, int profile_b) {
GUIDToTURLMap b_turls = CreateGUIDToTURLMap(service_b); GUIDToTURLMap b_turls = CreateGUIDToTURLMap(service_b);
if (a_turls.size() != b_turls.size()) { if (a_turls.size() != b_turls.size()) {
DVLOG(1) << "Service a and b do not match in size: " << a_turls.size() *os << "Service a and b do not match in size: " << a_turls.size() << " vs "
<< " vs " << b_turls.size() << " respectively."; << b_turls.size() << " respectively.";
return false; return false;
} }
for (auto it = a_turls.begin(); it != a_turls.end(); ++it) { for (auto it = a_turls.begin(); it != a_turls.end(); ++it) {
if (b_turls.find(it->first) == b_turls.end()) { if (b_turls.find(it->first) == b_turls.end()) {
DVLOG(1) << "TURL GUID from a not found in b's TURLs: " << it->first; *os << "TURL GUID from a not found in b's TURLs: " << it->first;
return false; return false;
} }
if (!TURLsMatch(*b_turls[it->first], *it->second)) if (!TURLsMatch(*b_turls[it->first], *it->second))
...@@ -83,12 +82,11 @@ bool ServicesMatch(int profile_a, int profile_b) { ...@@ -83,12 +82,11 @@ bool ServicesMatch(int profile_a, int profile_b) {
const TemplateURL* default_a = service_a->GetDefaultSearchProvider(); const TemplateURL* default_a = service_a->GetDefaultSearchProvider();
const TemplateURL* default_b = service_b->GetDefaultSearchProvider(); const TemplateURL* default_b = service_b->GetDefaultSearchProvider();
if (!TURLsMatch(*default_a, *default_b)) { if (!TURLsMatch(*default_a, *default_b)) {
DVLOG(1) << "Default search providers do not match: A's default: " *os << "Default search providers do not match: A's default: "
<< default_a->keyword() << default_a->keyword() << " B's default: " << default_b->keyword();
<< " B's default: " << default_b->keyword();
return false; return false;
} else { } else {
DVLOG(1) << "A had default with URL: " << default_a->url() *os << "A had default with URL: " << default_a->url()
<< " and keyword: " << default_a->keyword(); << " and keyword: " << default_a->keyword();
} }
...@@ -145,15 +143,19 @@ bool ServiceMatchesVerifier(int profile_index) { ...@@ -145,15 +143,19 @@ bool ServiceMatchesVerifier(int profile_index) {
} }
bool AllServicesMatch() { bool AllServicesMatch() {
return AllServicesMatch(&VLOG_STREAM(1));
}
bool AllServicesMatch(std::ostream* os) {
// Use 0 as the baseline. // Use 0 as the baseline.
if (test()->UseVerifier() && !ServiceMatchesVerifier(0)) { if (test()->UseVerifier() && !ServiceMatchesVerifier(0)) {
DVLOG(1) << "TemplateURLService 0 does not match verifier."; *os << "TemplateURLService 0 does not match verifier.";
return false; return false;
} }
for (int it = 1; it < test()->num_clients(); ++it) { for (int it = 1; it < test()->num_clients(); ++it) {
if (!ServicesMatch(0, it)) { if (!ServicesMatch(0, it, os)) {
DVLOG(1) << "TemplateURLService " << it << " does not match with " *os << "TemplateURLService " << it << " does not match with "
<< "service 0."; << "service 0.";
return false; return false;
} }
...@@ -252,15 +254,58 @@ void ChangeDefaultSearchProvider(int profile_index, int seed) { ...@@ -252,15 +254,58 @@ void ChangeDefaultSearchProvider(int profile_index, int seed) {
} }
bool HasSearchEngine(int profile_index, int seed) { bool HasSearchEngine(int profile_index, int seed) {
return HasSearchEngineWithKeyword(profile_index, CreateKeyword(seed));
}
bool HasSearchEngineWithKeyword(int profile_index,
const base::string16& keyword) {
TemplateURLService* service = GetServiceForBrowserContext(profile_index); TemplateURLService* service = GetServiceForBrowserContext(profile_index);
base::string16 keyword(CreateKeyword(seed));
TemplateURL* turl = service->GetTemplateURLForKeyword(keyword); TemplateURL* turl = service->GetTemplateURLForKeyword(keyword);
return turl != nullptr; return turl != nullptr;
} }
} // namespace search_engines_helper base::string16 GetDefaultSearchEngineKeyword(int profile_index) {
TemplateURLService* service = GetServiceForBrowserContext(profile_index);
return service->GetDefaultSearchProvider()->keyword();
}
SearchEnginesMatchChecker::SearchEnginesMatchChecker() {
if (test()->UseVerifier()) {
observer_.Add(GetVerifierService());
}
for (int i = 0; i < test()->num_clients(); ++i) {
observer_.Add(GetServiceForBrowserContext(i));
}
}
SearchEnginesMatchChecker::SearchEnginesMatchChecker() SearchEnginesMatchChecker::~SearchEnginesMatchChecker() = default;
: AwaitMatchStatusChangeChecker(
base::Bind(search_engines_helper::AllServicesMatch), bool SearchEnginesMatchChecker::IsExitConditionSatisfied(std::ostream* os) {
"All search engines match") {} return AllServicesMatch(os);
}
void SearchEnginesMatchChecker::OnTemplateURLServiceChanged() {
CheckExitCondition();
}
HasSearchEngineChecker::HasSearchEngineChecker(int profile_index, int seed)
: HasSearchEngineChecker(profile_index, CreateKeyword(seed)) {}
HasSearchEngineChecker::HasSearchEngineChecker(int profile_index,
const base::string16& keyword)
: service_(GetServiceForBrowserContext(profile_index)), keyword_(keyword) {
observer_.Add(service_);
}
HasSearchEngineChecker::~HasSearchEngineChecker() = default;
bool HasSearchEngineChecker::IsExitConditionSatisfied(std::ostream* os) {
return service_->GetTemplateURLForKeyword(keyword_) != nullptr;
}
void HasSearchEngineChecker::OnTemplateURLServiceChanged() {
CheckExitCondition();
}
} // namespace search_engines_helper
...@@ -9,8 +9,11 @@ ...@@ -9,8 +9,11 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "base/scoped_observer.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "chrome/browser/sync/test/integration/await_match_status_change_checker.h" #include "chrome/browser/sync/test/integration/status_change_checker.h"
#include "components/search_engines/template_url_service.h"
#include "components/search_engines/template_url_service_observer.h"
class Profile; class Profile;
class TemplateURL; class TemplateURL;
...@@ -32,6 +35,7 @@ bool ServiceMatchesVerifier(int profile_index); ...@@ -32,6 +35,7 @@ bool ServiceMatchesVerifier(int profile_index);
// Returns true iff all TemplateURLServices match with the verifier. // Returns true iff all TemplateURLServices match with the verifier.
bool AllServicesMatch(); bool AllServicesMatch();
bool AllServicesMatch(std::ostream* os);
// Create a TemplateURL with some test values based on |seed|. // Create a TemplateURL with some test values based on |seed|.
std::unique_ptr<TemplateURL> CreateTestTemplateURL( std::unique_ptr<TemplateURL> CreateTestTemplateURL(
...@@ -72,12 +76,54 @@ void ChangeDefaultSearchProvider(int profile_index, int seed); ...@@ -72,12 +76,54 @@ void ChangeDefaultSearchProvider(int profile_index, int seed);
// the search engine generated with |seed|. // the search engine generated with |seed|.
bool HasSearchEngine(int profile_index, int seed); bool HasSearchEngine(int profile_index, int seed);
} // namespace search_engines_helper // Returns true if the profile at |profile_index| has a search engine matching
// |keyword|.
bool HasSearchEngineWithKeyword(int profile_index,
const base::string16& keyword);
// Returns the keyword for the default search engine at |profile_index|.
base::string16 GetDefaultSearchEngineKeyword(int profile_index);
// Checker that blocks until all services have the same search engine data. // Checker that blocks until all services have the same search engine data.
class SearchEnginesMatchChecker : public AwaitMatchStatusChangeChecker { class SearchEnginesMatchChecker : public StatusChangeChecker,
public TemplateURLServiceObserver {
public: public:
SearchEnginesMatchChecker(); SearchEnginesMatchChecker();
~SearchEnginesMatchChecker() override;
// StatusChangeChecker overrides.
bool IsExitConditionSatisfied(std::ostream* os) override;
// TemplateURLServiceObserver overrides.
void OnTemplateURLServiceChanged() override;
private:
ScopedObserver<TemplateURLService, TemplateURLServiceObserver> observer_{
this};
};
// Checker that blocks until |profile_index| has a search engine matching the
// search engine generated with |seed| or |keyword|.
class HasSearchEngineChecker : public StatusChangeChecker,
public TemplateURLServiceObserver {
public:
HasSearchEngineChecker(int profile_index, int seed);
HasSearchEngineChecker(int profile_index, const base::string16& keyword);
~HasSearchEngineChecker() override;
// StatusChangeChecker overrides.
bool IsExitConditionSatisfied(std::ostream* os) override;
// TemplateURLServiceObserver overrides.
void OnTemplateURLServiceChanged() override;
private:
TemplateURLService* const service_;
const base::string16 keyword_;
ScopedObserver<TemplateURLService, TemplateURLServiceObserver> observer_{
this};
}; };
} // namespace search_engines_helper
#endif // CHROME_BROWSER_SYNC_TEST_INTEGRATION_SEARCH_ENGINES_HELPER_H_ #endif // CHROME_BROWSER_SYNC_TEST_INTEGRATION_SEARCH_ENGINES_HELPER_H_
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
using base::ASCIIToUTF16; using base::ASCIIToUTF16;
using search_engines_helper::SearchEnginesMatchChecker;
class TwoClientSearchEnginesSyncTest : public SyncTest { class TwoClientSearchEnginesSyncTest : public SyncTest {
public: public:
...@@ -305,3 +306,71 @@ IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest, ...@@ -305,3 +306,71 @@ IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest,
search_engines_helper::DeleteSearchEngineBySeed(0, 0); search_engines_helper::DeleteSearchEngineBySeed(0, 0);
ASSERT_TRUE(SearchEnginesMatchChecker().Wait()); ASSERT_TRUE(SearchEnginesMatchChecker().Wait());
} }
// Same as above that forces the deletion to propagate faster than the
// preference, which is complex to deal with for the receiving client (deletion
// of the default search engine), and currently leads to search engines with
// underscores being created. This is achieved in the test by throttling
// PREFERENCES in FakeServer, which prevents the sync-ing of the default search
// engine change.
IN_PROC_BROWSER_TEST_F(TwoClientSearchEnginesSyncTest,
DeleteSyncedDefaultWithoutPrefSync) {
ASSERT_TRUE(SetupClients());
search_engines_helper::AddSearchEngine(/*profile_index=*/0, /*seed=*/0);
search_engines_helper::AddSearchEngine(/*profile_index=*/0, /*seed=*/1);
search_engines_helper::AddSearchEngine(/*profile_index=*/1, /*seed=*/0);
search_engines_helper::AddSearchEngine(/*profile_index=*/1, /*seed=*/1);
search_engines_helper::ChangeDefaultSearchProvider(/*profile_index=*/0,
/*seed=*/0);
ASSERT_TRUE(SetupSync());
ASSERT_TRUE(SearchEnginesMatchChecker().Wait());
// Throttle PREFERENCES to block any commits to them, which in this case
// prevents the default search engine selection from sync-ing.
GetFakeServer()->SetThrottledTypes({syncer::PREFERENCES});
// Rule out search engines with underscores existing at this point.
// Note that seed==0 corresponds to keyword "test0".
ASSERT_TRUE(search_engines_helper::HasSearchEngineWithKeyword(
/*profile_index=*/0, ASCIIToUTF16("test0")));
ASSERT_FALSE(search_engines_helper::HasSearchEngineWithKeyword(
/*profile_index=*/0, ASCIIToUTF16("test0_")));
ASSERT_TRUE(search_engines_helper::HasSearchEngineWithKeyword(
/*profile_index=*/1, ASCIIToUTF16("test0")));
ASSERT_FALSE(search_engines_helper::HasSearchEngineWithKeyword(
/*profile_index=*/1, ASCIIToUTF16("test0_")));
// Change the default on the first client (profile index 0) and delete the old
// default.
search_engines_helper::ChangeDefaultSearchProvider(/*profile_index=*/0,
/*seed=*/1);
search_engines_helper::DeleteSearchEngineBySeed(/*profile_index=*/0,
/*seed=*/0);
// The test needs to wait until the second client (profile index 1) receives
// the deletion. In order to do so, use the first client (profile index 0) to
// create a third search engine (seed 2) and wait until it gets sync-ed to the
// second client (profile index 1).
search_engines_helper::AddSearchEngine(/*profile_index=*/0, /*seed=*/2);
ASSERT_TRUE(search_engines_helper::HasSearchEngineChecker(/*profile_index=*/1,
/*seed=*/2)
.Wait());
// In the receiving end (profile index 1), the deletion cannot be honored as
// is, so instead the keyword is changed for the default search engine by
// appending an underscore.
EXPECT_TRUE(search_engines_helper::HasSearchEngineWithKeyword(
/*profile_index=*/1, ASCIIToUTF16("test0_")));
EXPECT_FALSE(search_engines_helper::HasSearchEngineWithKeyword(
/*profile_index=*/1, ASCIIToUTF16("test0")));
EXPECT_EQ(
search_engines_helper::GetDefaultSearchEngineKeyword(/*profile_index=*/1),
ASCIIToUTF16("test0_"));
// The search engine with an underscore should sync back to profile index 0.
EXPECT_TRUE(search_engines_helper::HasSearchEngineChecker(
/*profile_index=*/0, ASCIIToUTF16("test0_"))
.Wait());
}
...@@ -326,6 +326,7 @@ net::HttpStatusCode LoopbackServer::HandleCommand( ...@@ -326,6 +326,7 @@ net::HttpStatusCode LoopbackServer::HandleCommand(
} else { } else {
bool success = false; bool success = false;
std::vector<ModelType> datatypes_to_migrate; std::vector<ModelType> datatypes_to_migrate;
ModelTypeSet throttled_datatypes_in_request;
switch (message.message_contents()) { switch (message.message_contents()) {
case sync_pb::ClientToServerMessage::GET_UPDATES: case sync_pb::ClientToServerMessage::GET_UPDATES:
success = HandleGetUpdatesRequest( success = HandleGetUpdatesRequest(
...@@ -334,9 +335,9 @@ net::HttpStatusCode LoopbackServer::HandleCommand( ...@@ -334,9 +335,9 @@ net::HttpStatusCode LoopbackServer::HandleCommand(
&datatypes_to_migrate); &datatypes_to_migrate);
break; break;
case sync_pb::ClientToServerMessage::COMMIT: case sync_pb::ClientToServerMessage::COMMIT:
success = HandleCommitRequest(message.commit(), success = HandleCommitRequest(
message.invalidator_client_id(), message.commit(), message.invalidator_client_id(),
response->mutable_commit()); response->mutable_commit(), &throttled_datatypes_in_request);
break; break;
case sync_pb::ClientToServerMessage::CLEAR_SERVER_DATA: case sync_pb::ClientToServerMessage::CLEAR_SERVER_DATA:
ClearServerData(); ClearServerData();
...@@ -350,19 +351,29 @@ net::HttpStatusCode LoopbackServer::HandleCommand( ...@@ -350,19 +351,29 @@ net::HttpStatusCode LoopbackServer::HandleCommand(
if (success) { if (success) {
response->set_error_code(sync_pb::SyncEnums::SUCCESS); response->set_error_code(sync_pb::SyncEnums::SUCCESS);
} else if (datatypes_to_migrate.empty()) { } else if (!datatypes_to_migrate.empty()) {
UMA_HISTOGRAM_ENUMERATION(
"Sync.Local.RequestTypeOnError", message.message_contents(),
sync_pb::ClientToServerMessage_Contents_Contents_MAX);
return net::HTTP_INTERNAL_SERVER_ERROR;
} else {
DLOG(WARNING) << "Migration required for " << datatypes_to_migrate.size() DLOG(WARNING) << "Migration required for " << datatypes_to_migrate.size()
<< " datatypes"; << " datatypes";
response->set_error_code(sync_pb::SyncEnums::MIGRATION_DONE);
for (ModelType type : datatypes_to_migrate) { for (ModelType type : datatypes_to_migrate) {
response->add_migrated_data_type_id( response->add_migrated_data_type_id(
GetSpecificsFieldNumberFromModelType(type)); GetSpecificsFieldNumberFromModelType(type));
} }
response->set_error_code(sync_pb::SyncEnums::MIGRATION_DONE); } else if (!throttled_datatypes_in_request.Empty()) {
DLOG(WARNING) << "Throttled datatypes: "
<< ModelTypeSetToString(throttled_datatypes_in_request);
response->set_error_code(sync_pb::SyncEnums::PARTIAL_FAILURE);
response->mutable_error()->set_error_type(
sync_pb::SyncEnums::PARTIAL_FAILURE);
for (ModelType type : throttled_datatypes_in_request) {
response->mutable_error()->add_error_data_type_ids(
syncer::GetSpecificsFieldNumberFromModelType(type));
}
} else {
UMA_HISTOGRAM_ENUMERATION(
"Sync.Local.RequestTypeOnError", message.message_contents(),
sync_pb::ClientToServerMessage_Contents_Contents_MAX);
return net::HTTP_INTERNAL_SERVER_ERROR;
} }
} }
...@@ -610,7 +621,8 @@ void LoopbackServer::DeleteChildren(const string& parent_id) { ...@@ -610,7 +621,8 @@ void LoopbackServer::DeleteChildren(const string& parent_id) {
bool LoopbackServer::HandleCommitRequest( bool LoopbackServer::HandleCommitRequest(
const sync_pb::CommitMessage& commit, const sync_pb::CommitMessage& commit,
const std::string& invalidator_client_id, const std::string& invalidator_client_id,
sync_pb::CommitResponse* response) { sync_pb::CommitResponse* response,
ModelTypeSet* throttled_datatypes_in_request) {
std::map<string, string> client_to_server_ids; std::map<string, string> client_to_server_ids;
string guid = commit.cache_guid(); string guid = commit.cache_guid();
ModelTypeSet committed_model_types; ModelTypeSet committed_model_types;
...@@ -630,6 +642,13 @@ bool LoopbackServer::HandleCommitRequest( ...@@ -630,6 +642,13 @@ bool LoopbackServer::HandleCommitRequest(
parent_id = client_to_server_ids[parent_id]; parent_id = client_to_server_ids[parent_id];
} }
const ModelType entity_model_type = GetModelType(client_entity);
if (throttled_types_.Has(entity_model_type)) {
entry_response->set_response_type(sync_pb::CommitResponse::OVER_QUOTA);
throttled_datatypes_in_request->Put(entity_model_type);
continue;
}
const string entity_id = const string entity_id =
CommitEntity(client_entity, entry_response, guid, parent_id); CommitEntity(client_entity, entry_response, guid, parent_id);
if (entity_id.empty()) { if (entity_id.empty()) {
...@@ -663,7 +682,7 @@ bool LoopbackServer::HandleCommitRequest( ...@@ -663,7 +682,7 @@ bool LoopbackServer::HandleCommitRequest(
if (observer_for_tests_) if (observer_for_tests_)
observer_for_tests_->OnCommit(invalidator_client_id, committed_model_types); observer_for_tests_->OnCommit(invalidator_client_id, committed_model_types);
return true; return throttled_datatypes_in_request->Empty();
} }
void LoopbackServer::ClearServerData() { void LoopbackServer::ClearServerData() {
......
...@@ -81,6 +81,10 @@ class LoopbackServer : public base::ImportantFileWriter::DataSerializer { ...@@ -81,6 +81,10 @@ class LoopbackServer : public base::ImportantFileWriter::DataSerializer {
void AddNewKeystoreKeyForTesting(); void AddNewKeystoreKeyForTesting();
void SetThrottledTypesForTesting(ModelTypeSet model_types) {
throttled_types_ = model_types;
}
private: private:
// Allow the FakeServer decorator to inspect the internals of this class. // Allow the FakeServer decorator to inspect the internals of this class.
friend class fake_server::FakeServer; friend class fake_server::FakeServer;
...@@ -108,7 +112,8 @@ class LoopbackServer : public base::ImportantFileWriter::DataSerializer { ...@@ -108,7 +112,8 @@ class LoopbackServer : public base::ImportantFileWriter::DataSerializer {
// Processes a Commit call. // Processes a Commit call.
bool HandleCommitRequest(const sync_pb::CommitMessage& message, bool HandleCommitRequest(const sync_pb::CommitMessage& message,
const std::string& invalidator_client_id, const std::string& invalidator_client_id,
sync_pb::CommitResponse* response); sync_pb::CommitResponse* response,
ModelTypeSet* throttled_datatypes_in_request);
void ClearServerData(); void ClearServerData();
...@@ -229,6 +234,8 @@ class LoopbackServer : public base::ImportantFileWriter::DataSerializer { ...@@ -229,6 +234,8 @@ class LoopbackServer : public base::ImportantFileWriter::DataSerializer {
int64_t store_birthday_; int64_t store_birthday_;
ModelTypeSet throttled_types_;
base::Optional<sync_pb::ChipBag> bag_of_chips_; base::Optional<sync_pb::ChipBag> bag_of_chips_;
std::map<ModelType, int> migration_versions_; std::map<ModelType, int> migration_versions_;
......
...@@ -577,6 +577,10 @@ void FakeServer::DisallowSendingEncryptionKeys() { ...@@ -577,6 +577,10 @@ void FakeServer::DisallowSendingEncryptionKeys() {
disallow_sending_encryption_keys_ = true; disallow_sending_encryption_keys_ = true;
} }
void FakeServer::SetThrottledTypes(syncer::ModelTypeSet types) {
loopback_server_->SetThrottledTypesForTesting(types);
}
bool FakeServer::ShouldSendTriggeredError() const { bool FakeServer::ShouldSendTriggeredError() const {
if (!alternate_triggered_errors_) if (!alternate_triggered_errors_)
return true; return true;
......
...@@ -194,6 +194,9 @@ class FakeServer : public syncer::LoopbackServer::ObserverForTests { ...@@ -194,6 +194,9 @@ class FakeServer : public syncer::LoopbackServer::ObserverForTests {
// encryption_keys. // encryption_keys.
void DisallowSendingEncryptionKeys(); void DisallowSendingEncryptionKeys();
// Mimics throttling of datatypes.
void SetThrottledTypes(syncer::ModelTypeSet types);
// Adds |observer| to FakeServer's observer list. This should be called // Adds |observer| to FakeServer's observer list. This should be called
// before the Profile associated with |observer| is connected to the server. // before the Profile associated with |observer| is connected to the server.
void AddObserver(Observer* observer); void AddObserver(Observer* observer);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment