blob: d604b79f585b615ccb85bce3606ad104c078dd1e [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
#include <cstddef>
#include <sstream>
#include <utility>
#include "base/check_deref.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "build/build_config.h"
#include "chrome/browser/enterprise/util/managed_browser_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/sync/test/integration/invalidations/invalidations_status_checker.h"
#include "chrome/browser/sync/test/integration/quiesce_status_change_checker.h"
#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
#include "chrome/browser/sync/test/integration/sync_signin_delegate.h"
#include "chrome/common/channel_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/sync/engine/net/url_translator.h"
#include "components/sync/engine/sync_string_conversions.h"
#include "components/sync/engine/traffic_logger.h"
#include "components/sync/protocol/sync.pb.h"
#include "components/sync/service/glue/sync_transport_data_prefs.h"
#include "components/sync/service/local_data_description.h"
#include "components/sync/service/sync_internals_util.h"
#include "content/public/browser/browser_context.h"
#include "content/public/test/simple_url_loader_test_helper.h"
#include "google_apis/google_api_keys.h"
#include "net/base/net_errors.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "third_party/zlib/google/compression_utils.h"
namespace {
using syncer::SyncCycleSnapshot;
using syncer::SyncServiceImpl;
constexpr char kSyncUrlClearServerDataKey[] = "sync-url-clear-server-data";
bool HasAuthError(SyncServiceImpl* service) {
return service->GetAuthError().state() ==
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
service->GetAuthError().state() ==
GoogleServiceAuthError::SERVICE_ERROR ||
service->GetAuthError().state() ==
GoogleServiceAuthError::REQUEST_CANCELED;
}
class EngineInitializeChecker : public SingleClientStatusChangeChecker {
public:
explicit EngineInitializeChecker(SyncServiceImpl* service)
: SingleClientStatusChangeChecker(service) {}
bool IsExitConditionSatisfied(std::ostream* os) override {
*os << "Waiting for sync engine initialization to complete; actual "
"transport state: "
<< syncer::sync_ui_util::TransportStateStringToDebugString(
service()->GetTransportState())
<< ", disable reasons: "
<< syncer::sync_ui_util::GetDisableReasonsDebugString(
service()->GetDisableReasons());
if (service()->IsEngineInitialized()) {
return true;
}
// Engine initialization is blocked by an auth error.
if (HasAuthError(service())) {
LOG(WARNING) << "Sync engine initialization blocked by auth error";
return true;
}
// Engine initialization is blocked by a failure to fetch Oauth2 tokens.
if (service()->IsRetryingAccessTokenFetchForTest()) {
LOG(WARNING) << "Sync engine initialization blocked by failure to fetch "
"access tokens";
return true;
}
// Still waiting on engine initialization.
return false;
}
};
class SyncSetupChecker : public SingleClientStatusChangeChecker {
public:
enum class State { kTransportActive, kFeatureActive };
SyncSetupChecker(SyncServiceImpl* service, State wait_for_state)
: SingleClientStatusChangeChecker(service),
wait_for_state_(wait_for_state) {}
bool IsExitConditionSatisfied(std::ostream* os) override {
*os << "Waiting for sync setup to complete";
syncer::SyncService::TransportState transport_state =
service()->GetTransportState();
if (transport_state == syncer::SyncService::TransportState::ACTIVE &&
(wait_for_state_ != State::kFeatureActive ||
service()->IsSyncFeatureActive())) {
return true;
}
// Sync is blocked by an auth error.
if (HasAuthError(service())) {
return true;
}
// Still waiting on sync setup.
return false;
}
private:
const State wait_for_state_;
};
class SyncTransportStateChecker : public SingleClientStatusChangeChecker {
public:
SyncTransportStateChecker(SyncServiceImpl* service,
syncer::SyncService::TransportState state)
: SingleClientStatusChangeChecker(service), state_(state) {}
bool IsExitConditionSatisfied(std::ostream* os) override {
*os << "Waiting for sync transport state to change";
return service()->GetTransportState() == state_;
}
private:
const syncer::SyncService::TransportState state_;
};
// Same as reset on chrome.google.com/data.
// This function will wait until the reset is done. If error occurs,
// it will log error messages.
bool ResetAccount(network::SharedURLLoaderFactory* url_loader_factory,
const std::string& access_token,
const GURL& url,
const std::string& username,
const std::string& birthday) {
// Generate https POST payload.
sync_pb::ClientToServerMessage message;
message.set_share(username);
message.set_message_contents(
sync_pb::ClientToServerMessage::CLEAR_SERVER_DATA);
message.set_store_birthday(birthday);
message.set_api_key(google_apis::GetAPIKey());
syncer::LogClientToServerMessage(message);
std::string payload;
message.SerializeToString(&payload);
std::string request_to_send;
compression::GzipCompress(payload, &request_to_send);
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = url;
resource_request->method = "POST";
resource_request->headers.SetHeader("Authorization",
"Bearer " + access_token);
resource_request->headers.SetHeader("Content-Encoding", "gzip");
resource_request->headers.SetHeader("Accept-Language", "en-US,en");
resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
std::unique_ptr<network::SimpleURLLoader> simple_loader =
network::SimpleURLLoader::Create(std::move(resource_request),
TRAFFIC_ANNOTATION_FOR_TESTS);
simple_loader->AttachStringForUpload(request_to_send,
"application/octet-stream");
simple_loader->SetTimeoutDuration(base::Seconds(10));
content::SimpleURLLoaderTestHelper url_loader_helper;
simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory, url_loader_helper.GetCallbackDeprecated());
url_loader_helper.WaitForCallback();
if (simple_loader->NetError() != 0) {
LOG(ERROR) << "Reset account failed with error "
<< net::ErrorToString(simple_loader->NetError())
<< ". The account will remain dirty and may cause test fail.";
return false;
}
return true;
}
std::unique_ptr<SyncSigninDelegate> CreateSyncSigninDelegateForType(
SyncServiceImplHarness::SigninType signin_type,
Profile* profile) {
CHECK(profile);
switch (signin_type) {
case SyncServiceImplHarness::SigninType::UI_SIGNIN:
return CreateSyncSigninDelegateWithLiveSignin(profile);
case SyncServiceImplHarness::SigninType::FAKE_SIGNIN:
return CreateSyncSigninDelegateWithFakeSignin(profile);
}
NOTREACHED();
}
} // namespace
// static
std::unique_ptr<SyncServiceImplHarness> SyncServiceImplHarness::Create(
Profile* profile,
SigninType signin_type) {
CHECK(profile);
return base::WrapUnique(new SyncServiceImplHarness(
profile, CreateSyncSigninDelegateForType(signin_type, profile)));
}
SyncServiceImplHarness::SyncServiceImplHarness(
Profile* profile,
std::unique_ptr<SyncSigninDelegate> signin_delegate)
: profile_(CHECK_DEREF(profile).GetWeakPtr()),
service_(SyncServiceFactory::GetAsSyncServiceImplForProfileForTesting(
profile)),
profile_debug_name_(profile->GetDebugName()),
signin_delegate_(std::move(signin_delegate)) {
CHECK(profile_);
CHECK(service_);
CHECK(signin_delegate_);
}
SyncServiceImplHarness::~SyncServiceImplHarness() = default;
signin::GaiaIdHash SyncServiceImplHarness::GetGaiaIdHashForPrimaryAccount()
const {
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_.get());
return signin::GaiaIdHash::FromGaiaId(
identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
.gaia);
}
GaiaId SyncServiceImplHarness::GetGaiaIdForAccount(
SyncTestAccount account) const {
return signin_delegate_->GetGaiaIdForAccount(account);
}
std::string SyncServiceImplHarness::GetEmailForAccount(
SyncTestAccount account) const {
return signin_delegate_->GetEmailForAccount(account);
}
bool SyncServiceImplHarness::SignInPrimaryAccount(SyncTestAccount account) {
if (!signin_delegate_->SignIn(account, signin::ConsentLevel::kSignin)) {
return false;
}
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_.get());
CHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin));
CHECK(identity_manager->HasPrimaryAccountWithRefreshToken(
signin::ConsentLevel::kSignin));
CHECK(!service()->GetAccountInfo().IsEmpty());
return true;
}
bool SyncServiceImplHarness::ResetSyncForPrimaryAccount() {
syncer::SyncTransportDataPrefs transport_data_prefs(
profile_->GetPrefs(), GetGaiaIdHashForPrimaryAccount());
// Generate the https url.
// CLEAR_SERVER_DATA isn't enabled on the prod Sync server,
// so --sync-url-clear-server-data can be used to specify an
// alternative endpoint.
// Note: Any OTA(Owned Test Account) tries to clear data need to be
// whitelisted.
auto* cmd_line = base::CommandLine::ForCurrentProcess();
DCHECK(cmd_line->HasSwitch(kSyncUrlClearServerDataKey))
<< "Missing switch " << kSyncUrlClearServerDataKey;
GURL base_url(cmd_line->GetSwitchValueASCII(kSyncUrlClearServerDataKey) +
"/command/?");
GURL url = syncer::AppendSyncQueryString(base_url,
transport_data_prefs.GetCacheGuid());
// Call sync server to clear sync data.
const std::string access_token = service()->GetAccessTokenForTest();
if (access_token.empty()) {
LOG(ERROR) << "Access token is not available.";
return false;
}
return ResetAccount(profile_->GetURLLoaderFactory().get(), access_token, url,
service()->GetAccountInfo().email,
transport_data_prefs.GetBirthday());
}
#if !BUILDFLAG(IS_CHROMEOS)
void SyncServiceImplHarness::SignOutPrimaryAccount() {
signin_delegate_->SignOut();
}
#endif // !BUILDFLAG(IS_CHROMEOS)
#if !BUILDFLAG(IS_ANDROID)
void SyncServiceImplHarness::EnterSyncPausedStateForPrimaryAccount() {
DCHECK(service_->IsSyncFeatureActive());
signin::SetInvalidRefreshTokenForPrimaryAccount(
IdentityManagerFactory::GetForProfile(profile_.get()));
}
bool SyncServiceImplHarness::ExitSyncPausedStateForPrimaryAccount() {
signin::SetRefreshTokenForPrimaryAccount(
IdentityManagerFactory::GetForProfile(profile_.get()));
// The engine was off in the sync-paused state, so wait for it to start.
return AwaitSyncSetupCompletion();
}
bool SyncServiceImplHarness::EnterSignInPendingStateForPrimaryAccount() {
CHECK_EQ(service_->GetTransportState(),
syncer::SyncServiceImpl::TransportState::ACTIVE);
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_.get());
CHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin));
signin::SetInvalidRefreshTokenForPrimaryAccount(identity_manager);
return AwaitSyncTransportPaused();
}
bool SyncServiceImplHarness::ExitSignInPendingStateForPrimaryAccount() {
CHECK_EQ(service_->GetTransportState(),
syncer::SyncService::TransportState::PAUSED);
signin::SetRefreshTokenForPrimaryAccount(
IdentityManagerFactory::GetForProfile(profile_.get()));
return AwaitSyncTransportActive();
}
#endif // !BUILDFLAG(IS_ANDROID)
bool SyncServiceImplHarness::SetupSync(SyncTestAccount account) {
bool result =
SetupSyncNoWaitForCompletion(account) && AwaitSyncSetupCompletion();
if (!result) {
LOG(ERROR) << profile_debug_name_ << ": SetupSync failed. Syncer status:\n"
<< GetServiceStatus();
} else {
DVLOG(1) << profile_debug_name_ << ": SetupSync successful.";
}
return result;
}
bool SyncServiceImplHarness::SetupSyncWithCustomSettings(
SetUserSettingsCallback user_settings_callback,
SyncTestAccount account) {
bool result = SetupSyncWithCustomSettingsNoWaitForCompletion(
std::move(user_settings_callback), account) &&
AwaitSyncSetupCompletion();
if (!result) {
LOG(ERROR) << profile_debug_name_ << ": SetupSync failed. Syncer status:\n"
<< GetServiceStatus();
} else {
DVLOG(1) << profile_debug_name_ << ": SetupSync successful.";
}
return result;
}
bool SyncServiceImplHarness::SetupSyncNoWaitForCompletion(
SyncTestAccount account) {
// By default, mimic the user confirming the default settings.
return SetupSyncWithCustomSettingsNoWaitForCompletion(
base::BindLambdaForTesting([](syncer::SyncUserSettings* user_settings) {
#if !BUILDFLAG(IS_CHROMEOS)
user_settings->SetInitialSyncFeatureSetupComplete(
syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
#endif // !BUILDFLAG(IS_CHROMEOS)
}),
account);
}
bool SyncServiceImplHarness::SetupSyncWithCustomSettingsNoWaitForCompletion(
SetUserSettingsCallback user_settings_callback,
SyncTestAccount account) {
if (service() == nullptr) {
LOG(ERROR) << "SetupSync(): service() is null.";
return false;
}
// Tell the sync service that setup is in progress so we don't start syncing
// until we've finished configuration.
sync_blocker_ = service()->GetSetupInProgressHandle();
if (!signin_delegate_->SignIn(account, signin::ConsentLevel::kSync)) {
return false;
}
if (account == SyncTestAccount::kEnterpriseAccount1 ||
account == SyncTestAccount::kGoogleDotComAccount1) {
enterprise_util::SetUserAcceptedAccountManagement(profile_.get(), true);
}
signin::IdentityManager* identity_manager =
IdentityManagerFactory::GetForProfile(profile_.get());
// Note that `ConsentLevel::kSync` is not actually guaranteed at this stage.
// Namely, live tests require closing the sync confirmation dialog before
// `ConsentLevel::kSync` is granted. This is achieved later below with
// `ConfirmSyncUI()`.
CHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin));
if (!AwaitEngineInitialization()) {
return false;
}
// Now give the caller a chance to configure settings (in particular, the
// selected data types) before actually starting to sync. This callback
// usually (but not necessarily) invokes SetInitialSyncFeatureSetupComplete().
std::move(user_settings_callback).Run(service()->GetUserSettings());
// Notify SyncServiceImpl that we are done with configuration.
sync_blocker_.reset();
if (!signin_delegate_->ConfirmSync()) {
return false;
}
CHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync));
CHECK(identity_manager->HasPrimaryAccountWithRefreshToken(
signin::ConsentLevel::kSync));
CHECK(!service()->GetAccountInfo().IsEmpty());
return true;
}
void SyncServiceImplHarness::FinishSyncSetup() {
#if !BUILDFLAG(IS_CHROMEOS)
service()->GetUserSettings()->SetInitialSyncFeatureSetupComplete(
syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
#endif // !BUILDFLAG(IS_CHROMEOS)
sync_blocker_.reset();
}
bool SyncServiceImplHarness::AwaitMutualSyncCycleCompletion(
SyncServiceImplHarness* partner) {
std::vector<SyncServiceImplHarness*> harnesses;
harnesses.push_back(this);
harnesses.push_back(partner);
return AwaitQuiescence(harnesses);
}
// static
bool SyncServiceImplHarness::AwaitQuiescence(
const std::vector<SyncServiceImplHarness*>& clients) {
if (clients.empty()) {
return true;
}
std::vector<raw_ptr<SyncServiceImpl, VectorExperimental>> services;
for (SyncServiceImplHarness* harness : clients) {
services.push_back(harness->service());
}
return QuiesceStatusChangeChecker(services).Wait();
}
bool SyncServiceImplHarness::AwaitEngineInitialization() {
if (!EngineInitializeChecker(service()).Wait()) {
LOG(ERROR) << "EngineInitializeChecker timed out.";
return false;
}
if (HasAuthError(service())) {
LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
return false;
}
if (service()->IsRetryingAccessTokenFetchForTest()) {
LOG(ERROR) << "Failed to fetch access token. Sync cannot proceed.";
return false;
}
if (!service()->IsEngineInitialized()) {
LOG(ERROR) << "Service engine not initialized.";
return false;
}
return true;
}
bool SyncServiceImplHarness::AwaitSyncSetupCompletion() {
CHECK(service()->GetUserSettings()->IsInitialSyncFeatureSetupComplete())
<< "Waiting for setup completion can only succeed after the first setup "
<< "got marked complete. Did you call SetupSync on this client?";
if (!SyncSetupChecker(service(), SyncSetupChecker::State::kFeatureActive)
.Wait()) {
LOG(ERROR) << "SyncSetupChecker timed out.";
return false;
}
// Signal an error if the initial sync wasn't successful.
if (HasAuthError(service())) {
LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
return false;
}
return true;
}
bool SyncServiceImplHarness::AwaitSyncTransportActive() {
if (!SyncSetupChecker(service(), SyncSetupChecker::State::kTransportActive)
.Wait()) {
LOG(ERROR) << "SyncSetupChecker timed out.";
return false;
}
// Signal an error if the initial sync wasn't successful.
if (HasAuthError(service())) {
LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
return false;
}
return true;
}
bool SyncServiceImplHarness::AwaitSyncTransportPaused() {
if (!SyncTransportStateChecker(service(),
syncer::SyncService::TransportState::PAUSED)
.Wait()) {
LOG(ERROR) << "SyncTransportStateChecker timed out.";
return false;
}
return true;
}
bool SyncServiceImplHarness::AwaitInvalidationsStatus(bool expected_status) {
return InvalidationsStatusChecker(service(), expected_status).Wait();
}
bool SyncServiceImplHarness::EnableSyncForType(
syncer::UserSelectableType type) {
DVLOG(1) << GetClientInfoString(
"EnableSyncForType(" +
std::string(syncer::GetUserSelectableTypeName(type)) + ")");
if (!IsSyncEnabledByUser()) {
bool result = SetupSyncWithCustomSettings(base::BindLambdaForTesting(
[type](syncer::SyncUserSettings* user_settings) {
user_settings->SetSelectedTypes(false, {type});
#if !BUILDFLAG(IS_CHROMEOS)
user_settings->SetInitialSyncFeatureSetupComplete(
syncer::SyncFirstSetupCompleteSource::ADVANCED_FLOW_CONFIRM);
#endif // !BUILDFLAG(IS_CHROMEOS)
}));
// If SetupSync() succeeded, then Sync must now be enabled.
DCHECK(!result || IsSyncEnabledByUser());
return result;
}
if (service() == nullptr) {
LOG(ERROR) << "EnableSyncForType(): service() is null.";
return false;
}
syncer::UserSelectableTypeSet selected_types =
service()->GetUserSettings()->GetSelectedTypes();
if (selected_types.Has(type)) {
DVLOG(1) << "EnableSyncForType(): Sync already enabled for type "
<< syncer::GetUserSelectableTypeName(type) << " on "
<< profile_debug_name_ << ".";
return true;
}
selected_types.Put(type);
service()->GetUserSettings()->SetSelectedTypes(false, selected_types);
if (AwaitSyncSetupCompletion()) {
DVLOG(1) << "EnableSyncForType(): Enabled sync for type "
<< syncer::GetUserSelectableTypeName(type) << " on "
<< profile_debug_name_ << ".";
return true;
}
DVLOG(0) << GetClientInfoString("EnableSyncForType failed");
return false;
}
bool SyncServiceImplHarness::DisableSyncForType(
syncer::UserSelectableType type) {
DVLOG(1) << GetClientInfoString(
"DisableSyncForType(" +
std::string(syncer::GetUserSelectableTypeName(type)) + ")");
if (service() == nullptr) {
LOG(ERROR) << "DisableSyncForType(): service() is null.";
return false;
}
syncer::UserSelectableTypeSet selected_types =
service()->GetUserSettings()->GetSelectedTypes();
if (!selected_types.Has(type)) {
DVLOG(1) << "DisableSyncForType(): Sync already disabled for type "
<< syncer::GetUserSelectableTypeName(type) << " on "
<< profile_debug_name_ << ".";
return true;
}
selected_types.Remove(type);
service()->GetUserSettings()->SetSelectedTypes(false, selected_types);
if (AwaitSyncSetupCompletion()) {
DVLOG(1) << "DisableSyncForType(): Disabled sync for type "
<< syncer::GetUserSelectableTypeName(type) << " on "
<< profile_debug_name_ << ".";
return true;
}
DVLOG(0) << GetClientInfoString("DisableSyncForDatatype failed");
return false;
}
bool SyncServiceImplHarness::EnableSyncForRegisteredDatatypes() {
DVLOG(1) << GetClientInfoString("EnableSyncForRegisteredDatatypes");
if (!IsSyncEnabledByUser()) {
bool result = SetupSync();
// If SetupSync() succeeded, then Sync must now be enabled.
DCHECK(!result || IsSyncEnabledByUser());
return result;
}
if (service() == nullptr) {
LOG(ERROR) << "EnableSyncForRegisteredDatatypes(): service() is null.";
return false;
}
service()->GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/true,
service()->GetUserSettings()->GetRegisteredSelectableTypes());
if (AwaitSyncSetupCompletion()) {
DVLOG(1)
<< "EnableSyncForRegisteredDatatypes(): Enabled sync for all datatypes "
<< "on " << profile_debug_name_ << ".";
return true;
}
DVLOG(0) << GetClientInfoString("EnableSyncForRegisteredDatatypes failed");
return false;
}
bool SyncServiceImplHarness::DisableSyncForAllDatatypes() {
DVLOG(1) << GetClientInfoString("DisableSyncForAllDatatypes");
if (service() == nullptr) {
LOG(ERROR) << "DisableSyncForAllDatatypes(): service() is null.";
return false;
}
service()->GetUserSettings()->SetSelectedTypes(
/*sync_everything=*/false, syncer::UserSelectableTypeSet());
DVLOG(1) << "DisableSyncForAllDatatypes(): Disabled sync for all "
<< "datatypes on " << profile_debug_name_;
return true;
}
SyncCycleSnapshot SyncServiceImplHarness::GetLastCycleSnapshot() const {
DCHECK(service() != nullptr) << "Sync service has not yet been set up.";
if (service()->IsSyncFeatureActive()) {
return service()->GetLastCycleSnapshotForDebugging();
}
return SyncCycleSnapshot();
}
absl::flat_hash_map<syncer::DataType, size_t>
SyncServiceImplHarness::GetTypesWithUnsyncedDataAndWait(
syncer::DataTypeSet requested_types) const {
base::test::TestFuture<absl::flat_hash_map<syncer::DataType, size_t>> future;
service()->GetTypesWithUnsyncedData(requested_types, future.GetCallback());
return future.Get();
}
syncer::LocalDataDescription
SyncServiceImplHarness::GetLocalDataDescriptionAndWait(
syncer::DataType data_type) {
base::test::TestFuture<
std::map<syncer::DataType, syncer::LocalDataDescription>>
descriptions;
service()->GetLocalDataDescriptions({data_type}, descriptions.GetCallback());
if (descriptions.Get().size() != 1u) {
ADD_FAILURE()
<< "The expected size of local data description map is 1. Found "
<< descriptions.Get().size() << '.';
return syncer::LocalDataDescription();
}
if (descriptions.Get().begin()->first != data_type) {
ADD_FAILURE()
<< DataTypeToDebugString(data_type)
<< " is the only expected key in the local data description map. Found "
<< DataTypeToDebugString(descriptions.Get().begin()->first) << '.';
return syncer::LocalDataDescription();
}
return descriptions.Get().begin()->second;
}
std::string SyncServiceImplHarness::GetServiceStatus() {
// This method is only used in test code for debugging purposes, so it's fine
// to include sensitive data in ConstructAboutInformation().
base::Value::Dict value = syncer::sync_ui_util::ConstructAboutInformation(
syncer::sync_ui_util::IncludeSensitiveData(true), service(),
chrome::GetChannelName(chrome::WithExtendedStable(true)));
std::string service_status;
base::JSONWriter::WriteWithOptions(
value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &service_status);
return service_status;
}
// TODO(sync): Clean up this method in a separate CL. Remove all snapshot fields
// and log shorter, more meaningful messages.
std::string SyncServiceImplHarness::GetClientInfoString(
const std::string& message) const {
std::stringstream os;
os << profile_debug_name_ << ": " << message << ": ";
if (service()) {
const SyncCycleSnapshot& snap = GetLastCycleSnapshot();
syncer::SyncStatus status;
service()->QueryDetailedSyncStatusForDebugging(&status);
// Capture select info from the sync session snapshot and syncer status.
os << ", has_unsynced_items: " << snap.has_remaining_local_changes()
<< ", did_commit: "
<< (snap.model_neutral_state().num_successful_commits == 0 &&
snap.model_neutral_state().commit_result.type() ==
syncer::SyncerError::Type::kSuccess)
<< ", server conflicts: " << snap.num_server_conflicts()
<< ", num_updates_downloaded : "
<< snap.model_neutral_state().num_updates_downloaded_total
<< ", passphrase_required: "
<< service()->GetUserSettings()->IsPassphraseRequired()
<< ", notifications_enabled: " << status.notifications_enabled
<< ", service_is_active: " << service()->IsSyncFeatureActive();
} else {
os << "Sync service not available";
}
return os.str();
}
bool SyncServiceImplHarness::IsSyncEnabledByUser() const {
return service()->GetUserSettings()->IsInitialSyncFeatureSetupComplete();
}