#include "base/run_loop.h"
#include "components/sync/driver/sync_service_impl.h"
#include "base/functional/bind.h"
#include "base/test/task_environment.h"
#include "build/chromeos_buildflags.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/sync/base/pref_names.h"
#include "components/sync/driver/data_type_manager_impl.h"
#include "components/sync/test/fake_data_type_controller.h"
#include "components/sync/test/fake_sync_api_component_factory.h"
#include "components/sync/test/fake_sync_engine.h"
#include "components/sync/test/sync_client_mock.h"
#include "components/sync/test/sync_service_impl_bundle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::ByMove;
using testing::Return;
namespace syncer {
namespace {
const char kEmail[] = "test_user@gmail.com";
class MockSyncServiceObserver : public SyncServiceObserver {
public:
MockSyncServiceObserver() = default;
MOCK_METHOD(void, OnStateChanged, (SyncService*), (override));
};
}
class SyncServiceImplStartupTest : public testing::Test {
public:
SyncServiceImplStartupTest()
: task_environment_(
base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME),
sync_prefs_(sync_service_impl_bundle_.pref_service()) {
sync_service_impl_bundle_.identity_test_env()
->SetAutomaticIssueOfAccessTokens(true);
}
~SyncServiceImplStartupTest() override { sync_service_->Shutdown(); }
void CreateSyncService(
SyncServiceImpl::StartBehavior start_behavior,
ModelTypeSet registered_types = ModelTypeSet(BOOKMARKS)) {
DataTypeController::TypeVector controllers;
for (ModelType type : registered_types) {
auto controller = std::make_unique<FakeDataTypeController>(type);
controller_map_[type] = controller.get();
controllers.push_back(std::move(controller));
}
std::unique_ptr<SyncClientMock> sync_client =
sync_service_impl_bundle_.CreateSyncClientMock();
ON_CALL(*sync_client, CreateDataTypeControllers)
.WillByDefault(Return(ByMove(std::move(controllers))));
sync_service_ = std::make_unique<SyncServiceImpl>(
sync_service_impl_bundle_.CreateBasicInitParams(
start_behavior, std::move(sync_client)));
}
void SimulateTestUserSignin() {
sync_service_impl_bundle_.identity_test_env()->MakePrimaryAccountAvailable(
kEmail, signin::ConsentLevel::kSync);
}
void SimulateRefreshTokensNotLoadedYet() {
sync_service_impl_bundle_.identity_test_env()->WaitForRefreshTokensLoaded();
sync_service_impl_bundle_.identity_test_env()
->ResetToAccountsNotYetLoadedFromDiskState();
}
void SimulateRefreshTokensLoad() {
sync_service_impl_bundle_.identity_test_env()->ReloadAccountsFromDisk();
sync_service_impl_bundle_.identity_test_env()->WaitForRefreshTokensLoaded();
}
void SimulateTestUserSigninWithoutRefreshToken() {
sync_service_impl_bundle_.identity_test_env()->SetPrimaryAccount(
kEmail, signin::ConsentLevel::kSync);
}
void UpdateCredentials() {
sync_service_impl_bundle_.identity_test_env()
->SetRefreshTokenForPrimaryAccount();
}
void SimulateWebSignout() {
sync_service_impl_bundle_.identity_test_env()
->SetInvalidRefreshTokenForPrimaryAccount();
}
void DisableAutomaticIssueOfAccessTokens() {
sync_service_impl_bundle_.identity_test_env()
->SetAutomaticIssueOfAccessTokens(false);
}
void RespondToTokenRequest() {
sync_service_impl_bundle_.identity_test_env()
->WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
"access_token", base::Time::Max());
}
SyncPrefs* sync_prefs() { return &sync_prefs_; }
SyncServiceImpl* sync_service() { return sync_service_.get(); }
PrefService* pref_service() {
return sync_service_impl_bundle_.pref_service();
}
FakeSyncApiComponentFactory* component_factory() {
return sync_service_impl_bundle_.component_factory();
}
DataTypeManagerImpl* data_type_manager() {
return component_factory()->last_created_data_type_manager();
}
FakeSyncEngine* engine() {
return component_factory()->last_created_engine();
}
FakeDataTypeController* get_controller(ModelType type) {
return controller_map_[type];
}
void FastForwardUntilNoTasksRemain() {
task_environment_.FastForwardUntilNoTasksRemain();
}
private:
base::test::SingleThreadTaskEnvironment task_environment_;
SyncServiceImplBundle sync_service_impl_bundle_;
SyncPrefs sync_prefs_;
std::unique_ptr<SyncServiceImpl> sync_service_;
std::map<ModelType, FakeDataTypeController*> controller_map_;
};
#if !BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(SyncServiceImplStartupTest, StartFirstTime) {
ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
EXPECT_EQ(
SyncService::DisableReasonSet(SyncService::DISABLE_REASON_NOT_SIGNED_IN,
SyncService::DISABLE_REASON_USER_CHOICE),
sync_service()->GetDisableReasons());
EXPECT_EQ(SyncService::TransportState::DISABLED,
sync_service()->GetTransportState());
EXPECT_EQ(nullptr, data_type_manager());
EXPECT_FALSE(engine());
EXPECT_EQ(base::Time(), sync_service()->GetLastSyncedTimeForDebugging());
EXPECT_FALSE(sync_prefs()->IsFirstSetupComplete());
sync_service()->SetSyncFeatureRequested();
std::unique_ptr<SyncSetupInProgressHandle> sync_blocker =
sync_service()->GetSetupInProgressHandle();
EXPECT_FALSE(sync_service()->IsEngineInitialized());
EXPECT_EQ(
SyncService::DisableReasonSet(SyncService::DISABLE_REASON_NOT_SIGNED_IN),
sync_service()->GetDisableReasons());
EXPECT_EQ(SyncService::TransportState::DISABLED,
sync_service()->GetTransportState());
SimulateTestUserSignin();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(sync_service()->IsEngineInitialized());
EXPECT_EQ(SyncService::DisableReasonSet(),
sync_service()->GetDisableReasons());
EXPECT_EQ(SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
sync_service()->GetTransportState());
sync_blocker.reset();
ASSERT_FALSE(sync_service()->IsSetupInProgress());
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
sync_service()->GetUserSettings()->SetFirstSetupComplete(
syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled());
EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
}
#endif
TEST_F(SyncServiceImplStartupTest, StartNoCredentials) {
SimulateRefreshTokensNotLoadedYet();
SimulateTestUserSigninWithoutRefreshToken();
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
EXPECT_TRUE(sync_service()->GetAccessTokenForTest().empty());
}
TEST_F(SyncServiceImplStartupTest, WebSignoutBeforeInitialization) {
SimulateTestUserSignin();
SimulateWebSignout();
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
EXPECT_EQ(SyncService::TransportState::PAUSED,
sync_service()->GetTransportState());
}
TEST_F(SyncServiceImplStartupTest, WebSignoutDuringDeferredStartup) {
SimulateTestUserSignin();
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START, {TYPED_URLS, SESSIONS});
sync_service()->Initialize();
ASSERT_EQ(SyncService::TransportState::START_DEFERRED,
sync_service()->GetTransportState());
MockSyncServiceObserver observer;
sync_service()->AddObserver(&observer);
EXPECT_CALL(observer, OnStateChanged(sync_service()))
.Times(testing::AtLeast(1))
.WillRepeatedly([&]() {
EXPECT_EQ(SyncService::TransportState::PAUSED,
sync_service()->GetTransportState());
});
SimulateWebSignout();
EXPECT_EQ(SyncService::TransportState::PAUSED,
sync_service()->GetTransportState());
sync_service()->RemoveObserver(&observer);
}
TEST_F(SyncServiceImplStartupTest, WebSignoutAfterInitialization) {
DisableAutomaticIssueOfAccessTokens();
SimulateTestUserSignin();
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
RespondToTokenRequest();
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
MockSyncServiceObserver observer;
sync_service()->AddObserver(&observer);
EXPECT_CALL(observer, OnStateChanged(sync_service()))
.Times(testing::AtLeast(1))
.WillRepeatedly([&]() {
EXPECT_EQ(SyncService::TransportState::PAUSED,
sync_service()->GetTransportState());
});
SimulateWebSignout();
EXPECT_EQ(SyncService::TransportState::PAUSED,
sync_service()->GetTransportState());
sync_service()->RemoveObserver(&observer);
}
TEST_F(SyncServiceImplStartupTest, StartInvalidCredentials) {
SimulateTestUserSignin();
sync_prefs()->SetSyncRequested(true);
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
component_factory()->AllowFakeEngineInitCompletion(false);
sync_service()->Initialize();
base::RunLoop().RunUntilIdle();
engine()->TriggerInitializationCompletion(false);
EXPECT_TRUE(sync_service()->HasUnrecoverableError());
EXPECT_EQ(SyncService::DisableReasonSet(
SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR),
sync_service()->GetDisableReasons());
EXPECT_EQ(SyncService::TransportState::DISABLED,
sync_service()->GetTransportState());
}
TEST_F(SyncServiceImplStartupTest, StartCrosNoCredentials) {
ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
SimulateRefreshTokensNotLoadedYet();
SimulateTestUserSigninWithoutRefreshToken();
CreateSyncService(SyncServiceImpl::AUTO_START);
sync_service()->Initialize();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
EXPECT_TRUE(sync_service()->GetUserSettings()->IsFirstSetupComplete());
}
TEST_F(SyncServiceImplStartupTest, StartCrosFirstTime) {
ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
SimulateTestUserSignin();
CreateSyncService(SyncServiceImpl::AUTO_START);
sync_service()->Initialize();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
}
TEST_F(SyncServiceImplStartupTest, StartNormal) {
sync_prefs()->SetFirstSetupComplete();
SimulateTestUserSignin();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
base::RunLoop().RunUntilIdle();
EXPECT_NE(nullptr, data_type_manager());
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
}
TEST_F(SyncServiceImplStartupTest, DisableSync) {
sync_prefs()->SetSyncRequested(true);
sync_prefs()->SetFirstSetupComplete();
SimulateTestUserSignin();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(sync_service()->IsSyncFeatureActive());
ASSERT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
ASSERT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
sync_service()->StopAndClear();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
sync_service()->StopAndClear();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
}
TEST_F(SyncServiceImplStartupTest, StartRecoverDatatypePrefs) {
pref_service()->ClearPref(prefs::kSyncKeepEverythingSynced);
for (UserSelectableType type : UserSelectableTypeSet::All()) {
pref_service()->ClearPref(SyncPrefs::GetPrefNameForType(type));
}
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
SimulateTestUserSignin();
sync_service()->Initialize();
EXPECT_TRUE(sync_prefs()->HasKeepEverythingSynced());
}
TEST_F(SyncServiceImplStartupTest, StartDontRecoverDatatypePrefs) {
sync_prefs()->SetSelectedTypes(
false,
UserSelectableTypeSet::All(),
{UserSelectableType::kBookmarks});
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
SimulateTestUserSignin();
sync_service()->Initialize();
EXPECT_FALSE(sync_prefs()->HasKeepEverythingSynced());
}
TEST_F(SyncServiceImplStartupTest, ManagedStartup) {
pref_service()->SetBoolean(prefs::kSyncManaged, true);
sync_prefs()->SetSyncRequested(true);
sync_prefs()->SetFirstSetupComplete();
SimulateTestUserSignin();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
EXPECT_EQ(SyncService::DisableReasonSet(
SyncService::DISABLE_REASON_ENTERPRISE_POLICY,
SyncService::DISABLE_REASON_USER_CHOICE),
sync_service()->GetDisableReasons());
EXPECT_EQ(nullptr, data_type_manager());
EXPECT_FALSE(engine());
}
TEST_F(SyncServiceImplStartupTest, SwitchManaged) {
sync_prefs()->SetSyncRequested(true);
sync_prefs()->SetFirstSetupComplete();
SimulateTestUserSignin();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(sync_service()->IsEngineInitialized());
EXPECT_EQ(SyncService::DisableReasonSet(),
sync_service()->GetDisableReasons());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled());
EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
ASSERT_EQ(0, get_controller(BOOKMARKS)->model()->clear_metadata_call_count());
pref_service()->SetBoolean(prefs::kSyncManaged, true);
base::RunLoop().RunUntilIdle();
ASSERT_EQ(SyncService::DisableReasonSet(
SyncService::DISABLE_REASON_ENTERPRISE_POLICY,
SyncService::DISABLE_REASON_USER_CHOICE),
sync_service()->GetDisableReasons());
EXPECT_FALSE(sync_service()->IsEngineInitialized());
EXPECT_EQ(SyncService::TransportState::DISABLED,
sync_service()->GetTransportState());
EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
EXPECT_EQ(1, get_controller(BOOKMARKS)->model()->clear_metadata_call_count());
pref_service()->SetBoolean(prefs::kSyncManaged, false);
base::RunLoop().RunUntilIdle();
ASSERT_EQ(
SyncService::DisableReasonSet(SyncService::DISABLE_REASON_USER_CHOICE),
sync_service()->GetDisableReasons());
EXPECT_TRUE(sync_service()->IsEngineInitialized());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
EXPECT_FALSE(sync_service()->GetUserSettings()->IsFirstSetupComplete());
EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
}
TEST_F(SyncServiceImplStartupTest, StartDownloadFailed) {
sync_prefs()->SetSyncRequested(true);
CreateSyncService(SyncServiceImpl::MANUAL_START);
SimulateTestUserSignin();
ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
component_factory()->AllowFakeEngineInitCompletion(false);
sync_service()->Initialize();
base::RunLoop().RunUntilIdle();
engine()->TriggerInitializationCompletion(false);
std::unique_ptr<SyncSetupInProgressHandle> sync_blocker =
sync_service()->GetSetupInProgressHandle();
sync_blocker.reset();
EXPECT_EQ(SyncService::DisableReasonSet(
SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR),
sync_service()->GetDisableReasons());
EXPECT_EQ(SyncService::TransportState::DISABLED,
sync_service()->GetTransportState());
}
#if !BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(SyncServiceImplStartupTest, FullStartupSequenceFirstTime) {
ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
CreateSyncService(SyncServiceImpl::MANUAL_START,
ModelTypeSet(SESSIONS, TYPED_URLS));
sync_service()->Initialize();
ASSERT_FALSE(sync_service()->CanSyncFeatureStart());
EXPECT_EQ(
SyncService::DisableReasonSet(SyncService::DISABLE_REASON_NOT_SIGNED_IN,
SyncService::DISABLE_REASON_USER_CHOICE),
sync_service()->GetDisableReasons());
EXPECT_EQ(SyncService::TransportState::DISABLED,
sync_service()->GetTransportState());
component_factory()->AllowFakeEngineInitCompletion(false);
SimulateTestUserSignin();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(
SyncService::DisableReasonSet(SyncService::DISABLE_REASON_USER_CHOICE),
sync_service()->GetDisableReasons());
EXPECT_EQ(SyncService::TransportState::INITIALIZING,
sync_service()->GetTransportState());
EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
ASSERT_TRUE(engine());
sync_service()->SetSyncFeatureRequested();
std::unique_ptr<SyncSetupInProgressHandle> setup_in_progress_handle =
sync_service()->GetSetupInProgressHandle();
engine()->TriggerInitializationCompletion(true);
ASSERT_TRUE(sync_service()->IsEngineInitialized());
EXPECT_EQ(SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
sync_service()->GetTransportState());
EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
sync_service()->GetUserSettings()->SetFirstSetupComplete(
syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
EXPECT_EQ(SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
sync_service()->GetTransportState());
EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled());
ASSERT_EQ(DataTypeController::NOT_RUNNING, get_controller(SESSIONS)->state());
get_controller(SESSIONS)->model()->EnableManualModelStart();
setup_in_progress_handle.reset();
EXPECT_EQ(SyncService::TransportState::CONFIGURING,
sync_service()->GetTransportState());
EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
EXPECT_NE(nullptr, data_type_manager());
EXPECT_TRUE(engine());
get_controller(SESSIONS)->model()->SimulateModelStartFinished();
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
}
#endif
TEST_F(SyncServiceImplStartupTest, FullStartupSequenceNthTime) {
SimulateTestUserSignin();
sync_prefs()->SetFirstSetupComplete();
sync_prefs()->SetSyncRequested(true);
CreateSyncService(SyncServiceImpl::MANUAL_START,
ModelTypeSet(SESSIONS, TYPED_URLS));
sync_service()->Initialize();
ASSERT_TRUE(sync_service()->CanSyncFeatureStart());
EXPECT_EQ(SyncService::TransportState::START_DEFERRED,
sync_service()->GetTransportState());
EXPECT_EQ(nullptr, data_type_manager());
EXPECT_FALSE(engine());
component_factory()->AllowFakeEngineInitCompletion(false);
FastForwardUntilNoTasksRemain();
EXPECT_EQ(SyncService::TransportState::INITIALIZING,
sync_service()->GetTransportState());
EXPECT_EQ(nullptr, data_type_manager());
EXPECT_TRUE(engine());
ASSERT_EQ(DataTypeController::NOT_RUNNING, get_controller(SESSIONS)->state());
get_controller(SESSIONS)->model()->EnableManualModelStart();
engine()->TriggerInitializationCompletion(true);
ASSERT_EQ(DataTypeController::MODEL_STARTING,
get_controller(SESSIONS)->state());
EXPECT_NE(nullptr, data_type_manager());
EXPECT_TRUE(engine());
get_controller(SESSIONS)->model()->SimulateModelStartFinished();
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
}
}