blob: 4d58a9aa8b3af1c331ff3c167befb0282c650213 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/run_loop.h"
#include "components/sync/driver/sync_service_impl.h"
#include "base/functional/bind.h"
#include "base/test/scoped_feature_list.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/features.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));
};
} // namespace
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);
// Hold a raw pointer to directly interact with the controller.
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 SimulateTestUserSigninWithoutSyncFeature() {
sync_service_impl_bundle_.identity_test_env()->MakePrimaryAccountAvailable(
kEmail, signin::ConsentLevel::kSignin);
}
void SimulateTestUserSigninAndEnableSyncFeature() {
sync_service_impl_bundle_.identity_test_env()->MakePrimaryAccountAvailable(
kEmail, signin::ConsentLevel::kSync);
}
void SimulateRefreshTokensNotLoadedYet() {
// First, wait for the actual refresh token load to complete if necessary.
// Otherwise, if it was still ongoing, it might reset the state back to
// "everything loaded" once it completes.
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 SimulateTestUserSigninAndEnableSyncFeatureWithoutRefreshToken() {
// Set the primary account *without* providing an OAuth token.
sync_service_impl_bundle_.identity_test_env()->SetPrimaryAccount(
kEmail, signin::ConsentLevel::kSync);
}
void UpdateCredentials() {
sync_service_impl_bundle_.identity_test_env()
->SetRefreshTokenForPrimaryAccount();
}
// Sets a special invalid refresh token. This is what happens when the primary
// (and sync-consented) account signs out on the web.
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_;
// The controllers are owned by |sync_service_|.
std::map<ModelType, FakeDataTypeController*> controller_map_;
};
// ChromeOS does not support sign-in after startup
#if !BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(SyncServiceImplStartupTest, StartFirstTime) {
// We've never completed startup.
ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
CreateSyncService(SyncServiceImpl::MANUAL_START);
// Should not actually start, rather just clean things up and wait
// to be enabled.
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());
// Preferences should be back to defaults.
EXPECT_EQ(base::Time(), sync_service()->GetLastSyncedTimeForDebugging());
EXPECT_FALSE(sync_prefs()->IsFirstSetupComplete());
// Sign in and turn sync on, without marking the first setup as complete.
SimulateTestUserSigninAndEnableSyncFeature();
sync_service()->SetSyncFeatureRequested();
std::unique_ptr<SyncSetupInProgressHandle> sync_blocker =
sync_service()->GetSetupInProgressHandle();
base::RunLoop().RunUntilIdle();
// The engine can start, and engine initialization is immediate in this test,
// so we bypass the INITIALIZING state.
EXPECT_TRUE(sync_service()->IsEngineInitialized());
EXPECT_EQ(SyncService::DisableReasonSet(),
sync_service()->GetDisableReasons());
EXPECT_EQ(SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
sync_service()->GetTransportState());
// Simulate the UI telling sync it has finished setting up. Note that this is
// a two-step process: Releasing the SetupInProgressHandle, and marking first
// setup complete.
// Since standalone transport is enabled, completed first-time setup is not a
// requirement, so the service will start up as soon as the setup handle is
// released.
sync_blocker.reset();
ASSERT_FALSE(sync_service()->IsSetupInProgress());
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
// Sync-the-feature is still not active, but rather pending confirmation.
EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
// Marking first setup complete will let SyncServiceImpl reconfigure the
// DataTypeManager in full Sync-the-feature mode.
sync_service()->GetUserSettings()->SetFirstSetupComplete(
syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
// This should have fully enabled sync.
EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled());
EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(SyncServiceImplStartupTest, StartNoCredentials) {
// We're already signed in, but don't have a refresh token.
SimulateRefreshTokensNotLoadedYet();
SimulateTestUserSigninAndEnableSyncFeatureWithoutRefreshToken();
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
FastForwardUntilNoTasksRemain();
// SyncServiceImpl should now be active, but of course not have an access
// token.
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
EXPECT_TRUE(sync_service()->GetAccessTokenForTest().empty());
// Note that SyncServiceImpl is not in an auth error state - no auth was
// attempted, so no error.
}
TEST_F(SyncServiceImplStartupTest, WebSignoutBeforeInitialization) {
// There is a primary account, but it's in a "web signout" aka sync-paused
// state.
SimulateTestUserSigninAndEnableSyncFeature();
SimulateWebSignout();
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
// SyncServiceImpl should now be in the paused state.
EXPECT_EQ(SyncService::TransportState::PAUSED,
sync_service()->GetTransportState());
}
TEST_F(SyncServiceImplStartupTest, WebSignoutDuringDeferredStartup) {
// There is a primary account. It is theoretically in the "web signout" aka
// sync-paused error state, but the identity code hasn't detected that yet
// (because auth errors are not persisted).
SimulateTestUserSigninAndEnableSyncFeature();
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
ASSERT_EQ(SyncService::TransportState::START_DEFERRED,
sync_service()->GetTransportState());
MockSyncServiceObserver observer;
sync_service()->AddObserver(&observer);
// Entering the sync-paused state should trigger a notification.
// Note: Depending on the exact sequence of IdentityManager::Observer calls
// (refresh token changed and/or auth error changed), there might be multiple
// notifications.
EXPECT_CALL(observer, OnStateChanged(sync_service()))
.Times(testing::AtLeast(1))
.WillRepeatedly([&]() {
EXPECT_EQ(SyncService::TransportState::PAUSED,
sync_service()->GetTransportState());
});
// Now sign out on the web to enter the sync-paused state.
SimulateWebSignout();
// SyncServiceImpl should now be in the paused state.
EXPECT_EQ(SyncService::TransportState::PAUSED,
sync_service()->GetTransportState());
sync_service()->RemoveObserver(&observer);
}
TEST_F(SyncServiceImplStartupTest, WebSignoutAfterInitialization) {
// This test has to wait for the access token request to complete, so disable
// automatic issuing of tokens.
DisableAutomaticIssueOfAccessTokens();
SimulateTestUserSigninAndEnableSyncFeature();
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
// Respond to the token request to finish the initialization flow.
RespondToTokenRequest();
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
MockSyncServiceObserver observer;
sync_service()->AddObserver(&observer);
// Entering the sync-paused state should trigger a notification.
// Note: Depending on the exact sequence of IdentityManager::Observer calls
// (refresh token changed and/or auth error changed), there might be multiple
// notifications.
EXPECT_CALL(observer, OnStateChanged(sync_service()))
.Times(testing::AtLeast(1))
.WillRepeatedly([&]() {
EXPECT_EQ(SyncService::TransportState::PAUSED,
sync_service()->GetTransportState());
});
// Now sign out on the web to enter the sync-paused state.
SimulateWebSignout();
// SyncServiceImpl should now be in the paused state.
EXPECT_EQ(SyncService::TransportState::PAUSED,
sync_service()->GetTransportState());
sync_service()->RemoveObserver(&observer);
}
TEST_F(SyncServiceImplStartupTest, StartInvalidCredentials) {
SimulateTestUserSigninAndEnableSyncFeature();
sync_prefs()->SetSyncRequested(true);
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
// Prevent automatic (and successful) completion of engine initialization.
component_factory()->AllowFakeEngineInitCompletion(false);
sync_service()->Initialize();
FastForwardUntilNoTasksRemain();
// Simulate an auth error while downloading control types.
engine()->TriggerInitializationCompletion(/*success=*/false);
// Engine initialization failures puts the service into an unrecoverable error
// state. It'll take either a browser restart or a full sign-out+sign-in to
// get out of this.
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) {
// We've never completed startup.
ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
// On ChromeOS, the user is always immediately signed in, but a refresh token
// isn't necessarily available yet.
SimulateRefreshTokensNotLoadedYet();
SimulateTestUserSigninAndEnableSyncFeatureWithoutRefreshToken();
CreateSyncService(SyncServiceImpl::AUTO_START);
// Calling Initialize should cause the service to immediately create and
// initialize the engine, and configure the DataTypeManager.
sync_service()->Initialize();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
// Sync should be considered active, even though there is no refresh token.
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
// Since we're in AUTO_START mode, FirstSetupComplete gets set automatically.
EXPECT_TRUE(sync_service()->GetUserSettings()->IsFirstSetupComplete());
}
TEST_F(SyncServiceImplStartupTest, StartCrosFirstTime) {
// We've never completed Sync startup.
ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
// There is already a signed-in user.
SimulateTestUserSigninAndEnableSyncFeature();
// Sync should become active, even though IsFirstSetupComplete wasn't set yet,
// due to AUTO_START.
CreateSyncService(SyncServiceImpl::AUTO_START);
sync_service()->Initialize();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
}
TEST_F(SyncServiceImplStartupTest, StartNormal) {
// We have previously completed the initial Sync setup, and the user is
// already signed in.
sync_prefs()->SetFirstSetupComplete();
SimulateTestUserSigninAndEnableSyncFeature();
CreateSyncService(SyncServiceImpl::MANUAL_START);
// Since all conditions for starting Sync are already fulfilled, calling
// Initialize should immediately create and initialize the engine and
// configure the DataTypeManager. In this test, all of these operations are
// synchronous.
sync_service()->Initialize();
FastForwardUntilNoTasksRemain();
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();
SimulateTestUserSigninAndEnableSyncFeature();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
FastForwardUntilNoTasksRemain();
ASSERT_TRUE(sync_service()->IsSyncFeatureActive());
ASSERT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
ASSERT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
// On StopAndClear(), the sync service will immediately start up again in
// transport mode.
sync_service()->StopAndClear();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
// Sync-the-feature is still considered off.
EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
// Call StopAndClear() again while the sync service is already in transport
// mode. It should immediately start up again in transport mode.
sync_service()->StopAndClear();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(DataTypeManager::CONFIGURED, data_type_manager()->state());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
}
// Test that we can recover from a case where a bug in the code resulted in
// OnUserChoseDatatypes not being properly called and datatype preferences
// therefore being left unset.
TEST_F(SyncServiceImplStartupTest, StartRecoverDatatypePrefs) {
// Clear the datatype preference fields (simulating bug 154940).
pref_service()->ClearPref(prefs::kSyncKeepEverythingSynced);
for (UserSelectableType type : UserSelectableTypeSet::All()) {
pref_service()->ClearPref(SyncPrefs::GetPrefNameForTypeForTesting(type));
}
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
SimulateTestUserSigninAndEnableSyncFeature();
sync_service()->Initialize();
EXPECT_TRUE(sync_prefs()->HasKeepEverythingSynced());
}
// Verify that the recovery of datatype preferences doesn't overwrite a valid
// case where only bookmarks are enabled.
TEST_F(SyncServiceImplStartupTest, StartDontRecoverDatatypePrefs) {
// Explicitly set Keep Everything Synced to false and have only bookmarks
// enabled.
sync_prefs()->SetSelectedTypes(
/*keep_everything_synced=*/false,
/*registered_types=*/UserSelectableTypeSet::All(),
/*selected_types=*/{UserSelectableType::kBookmarks});
sync_prefs()->SetFirstSetupComplete();
CreateSyncService(SyncServiceImpl::MANUAL_START);
SimulateTestUserSigninAndEnableSyncFeature();
sync_service()->Initialize();
EXPECT_FALSE(sync_prefs()->HasKeepEverythingSynced());
}
TEST_F(SyncServiceImplStartupTest, ManagedStartup) {
// Sync was previously enabled, but a policy was set while Chrome wasn't
// running.
pref_service()->SetBoolean(prefs::kSyncManaged, true);
sync_prefs()->SetSyncRequested(true);
sync_prefs()->SetFirstSetupComplete();
SimulateTestUserSigninAndEnableSyncFeature();
CreateSyncService(SyncServiceImpl::MANUAL_START);
sync_service()->Initialize();
// Sync was disabled due to the policy, setting SyncRequested to false and
// causing DISABLE_REASON_USER_CHOICE.
EXPECT_EQ(SyncService::DisableReasonSet(
SyncService::DISABLE_REASON_ENTERPRISE_POLICY,
SyncService::DISABLE_REASON_USER_CHOICE),
sync_service()->GetDisableReasons());
// Service should not be started by Initialize() since it's managed.
EXPECT_EQ(nullptr, data_type_manager());
EXPECT_FALSE(engine());
}
class SyncServiceImplStartupTestWithIgnoreSyncRequestedFeature
: public SyncServiceImplStartupTest,
public ::testing::WithParamInterface<bool> {
public:
SyncServiceImplStartupTestWithIgnoreSyncRequestedFeature() {
#if !BUILDFLAG(IS_CHROMEOS_ASH)
scoped_feature_list_.InitWithFeatureState(
kSyncIgnoreSyncRequestedPreference, GetParam());
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
}
~SyncServiceImplStartupTestWithIgnoreSyncRequestedFeature() override =
default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_P(SyncServiceImplStartupTestWithIgnoreSyncRequestedFeature,
SwitchManaged) {
// Sync starts out fully set up and enabled.
sync_prefs()->SetSyncRequested(true);
sync_prefs()->SetFirstSetupComplete();
SimulateTestUserSigninAndEnableSyncFeature();
CreateSyncService(SyncServiceImpl::MANUAL_START);
// Initialize() and wait for deferred startup.
sync_service()->Initialize();
FastForwardUntilNoTasksRemain();
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());
// The service should stop when switching to managed mode.
pref_service()->SetBoolean(prefs::kSyncManaged, true);
// Give re-startup a chance to happen (it shouldn't!).
base::RunLoop().RunUntilIdle();
// Sync was disabled due to the policy, setting SyncRequested to false and
// causing DISABLE_REASON_USER_CHOICE.
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());
// When switching back to unmanaged, Sync-the-transport should start up
// automatically, which causes (re)creation of SyncEngine and
// DataTypeManager.
pref_service()->SetBoolean(prefs::kSyncManaged, false);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(sync_service()->IsEngineInitialized());
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
// On ChromeOS Ash, DISABLE_REASON_USER_CHOICE stays even after the policy is
// removed, for historic reasons. It is unclear if this behavior is optional,
// because it is indistinguishable from the sync-reset-via-dashboard case.
// It can be resolved by invoking SetSyncFeatureRequested().
#if BUILDFLAG(IS_CHROMEOS_ASH)
EXPECT_EQ(
SyncService::DisableReasonSet(SyncService::DISABLE_REASON_USER_CHOICE),
sync_service()->GetDisableReasons());
#else
if (GetParam()) {
EXPECT_EQ(SyncService::DisableReasonSet(),
sync_service()->GetDisableReasons());
} else {
EXPECT_EQ(
SyncService::DisableReasonSet(SyncService::DISABLE_REASON_USER_CHOICE),
sync_service()->GetDisableReasons());
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
EXPECT_FALSE(sync_service()->GetUserSettings()->IsFirstSetupComplete());
EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
EXPECT_FALSE(sync_service()->IsSyncFeatureActive());
}
INSTANTIATE_TEST_SUITE_P(
SyncIgnoreSyncRequestedPreference,
SyncServiceImplStartupTestWithIgnoreSyncRequestedFeature,
::testing::Values(false, true));
TEST_F(SyncServiceImplStartupTest, StartDownloadFailed) {
sync_prefs()->SetSyncRequested(true);
CreateSyncService(SyncServiceImpl::MANUAL_START);
SimulateTestUserSigninAndEnableSyncFeature();
ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
// Prevent automatic (and successful) completion of engine initialization.
component_factory()->AllowFakeEngineInitCompletion(false);
sync_service()->Initialize();
FastForwardUntilNoTasksRemain();
// Simulate a failure while downloading control types.
engine()->TriggerInitializationCompletion(/*success=*/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());
}
// ChromeOS does not support sign-in after startup
#if !BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(SyncServiceImplStartupTest, FullStartupSequenceFirstTime) {
// We've never completed startup.
ASSERT_FALSE(sync_prefs()->IsFirstSetupComplete());
CreateSyncService(SyncServiceImpl::MANUAL_START, ModelTypeSet(SESSIONS));
sync_service()->Initialize();
ASSERT_FALSE(sync_service()->CanSyncFeatureStart());
// There is no signed-in user, so also nobody has decided that Sync should be
// started.
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());
// Sign in. Now Sync-the-transport can start. Since this was triggered by an
// explicit user event, deferred startup is bypassed.
// Sync-the-feature still doesn't start until the user says they want it.
component_factory()->AllowFakeEngineInitCompletion(false);
SimulateTestUserSigninWithoutSyncFeature();
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());
// Initiate Sync (the feature) setup before the engine initializes itself in
// transport mode.
SimulateTestUserSigninAndEnableSyncFeature();
sync_service()->SetSyncFeatureRequested();
std::unique_ptr<SyncSetupInProgressHandle> setup_in_progress_handle =
sync_service()->GetSetupInProgressHandle();
// Once the engine calls back and says it's initialized, we're just waiting
// for the user to finish the initial configuration (choosing data types etc.)
// before actually syncing data.
engine()->TriggerInitializationCompletion(/*success=*/true);
ASSERT_TRUE(sync_service()->IsEngineInitialized());
EXPECT_EQ(SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
sync_service()->GetTransportState());
EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled());
// Once the user finishes the initial setup, the service can actually start
// configuring the data types. Just marking the initial setup as complete
// isn't enough though, because setup is still considered in progress (we
// haven't released the setup-in-progress handle).
sync_service()->GetUserSettings()->SetFirstSetupComplete(
syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
EXPECT_EQ(SyncService::TransportState::PENDING_DESIRED_CONFIGURATION,
sync_service()->GetTransportState());
EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled());
// Prevent immediate configuration of one datatype, to verify the state
// during CONFIGURING.
ASSERT_EQ(DataTypeController::NOT_RUNNING, get_controller(SESSIONS)->state());
get_controller(SESSIONS)->model()->EnableManualModelStart();
// Releasing the setup in progress handle lets the service actually configure
// the DataTypeManager.
setup_in_progress_handle.reset();
// While DataTypeManager configuration is ongoing, the overall state is still
// CONFIGURING.
EXPECT_EQ(SyncService::TransportState::CONFIGURING,
sync_service()->GetTransportState());
EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
EXPECT_NE(nullptr, data_type_manager());
EXPECT_TRUE(engine());
// Finally, once the DataTypeManager says it's done with configuration, Sync
// is actually fully up and running.
get_controller(SESSIONS)->model()->SimulateModelStartFinished();
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
EXPECT_TRUE(sync_service()->IsSyncFeatureActive());
}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(SyncServiceImplStartupTest, FullStartupSequenceNthTime) {
// The user is already signed in and has completed Sync setup before.
SimulateTestUserSigninAndEnableSyncFeature();
sync_prefs()->SetFirstSetupComplete();
sync_prefs()->SetSyncRequested(true);
CreateSyncService(SyncServiceImpl::MANUAL_START, ModelTypeSet(SESSIONS));
sync_service()->Initialize();
ASSERT_TRUE(sync_service()->CanSyncFeatureStart());
// Nothing is preventing Sync from starting, but it should be deferred so as
// to now slow down browser startup.
EXPECT_EQ(SyncService::TransportState::START_DEFERRED,
sync_service()->GetTransportState());
EXPECT_EQ(nullptr, data_type_manager());
EXPECT_FALSE(engine());
// Wait for the deferred startup timer to expire. The Sync service will start
// and initialize the engine.
component_factory()->AllowFakeEngineInitCompletion(false);
FastForwardUntilNoTasksRemain();
EXPECT_EQ(SyncService::TransportState::INITIALIZING,
sync_service()->GetTransportState());
EXPECT_EQ(nullptr, data_type_manager());
EXPECT_TRUE(engine());
// Prevent immediate configuration of one datatype, to verify the state
// during CONFIGURING.
ASSERT_EQ(DataTypeController::NOT_RUNNING, get_controller(SESSIONS)->state());
get_controller(SESSIONS)->model()->EnableManualModelStart();
// Once the engine calls back and says it's initialized, the DataTypeManager
// will start configuring, since initial setup is already done.
engine()->TriggerInitializationCompletion(/*success=*/true);
ASSERT_EQ(DataTypeController::MODEL_STARTING,
get_controller(SESSIONS)->state());
EXPECT_NE(nullptr, data_type_manager());
EXPECT_TRUE(engine());
// Finally, once the DataTypeManager says it's done with configuration, Sync
// is actually fully up and running.
get_controller(SESSIONS)->model()->SimulateModelStartFinished();
EXPECT_EQ(SyncService::TransportState::ACTIVE,
sync_service()->GetTransportState());
}
TEST_F(SyncServiceImplStartupTest,
ShouldClearMetadataForAlreadyDisabledTypesBeforeConfigurationDone) {
sync_prefs()->SetFirstSetupComplete();
// Simulate types disabled during previous run.
sync_prefs()->SetSelectedTypes(
/*keep_everything_synced=*/false,
/*registered_types=*/
{UserSelectableType::kBookmarks, UserSelectableType::kReadingList},
/*selected_types=*/{UserSelectableType::kBookmarks});
sync_prefs()->SetSyncRequested(true);
SimulateTestUserSigninAndEnableSyncFeature();
CreateSyncService(SyncServiceImpl::MANUAL_START,
/*registered_types=*/ModelTypeSet(BOOKMARKS, READING_LIST));
sync_service()->Initialize();
// Metadata was cleared for disabled types ...
EXPECT_EQ(1,
get_controller(READING_LIST)->model()->clear_metadata_call_count());
// ... but not for the ones not disabled.
EXPECT_EQ(0, get_controller(BOOKMARKS)->model()->clear_metadata_call_count());
}
} // namespace syncer