// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "testing/gtest/include/gtest/gtest.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/prefs/pref_service.h" #include "chrome/browser/signin/fake_signin_manager.h" #include "chrome/browser/signin/signin_manager.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "chrome/browser/signin/token_service.h" #include "chrome/browser/signin/token_service_factory.h" #include "chrome/browser/sync/glue/data_type_manager.h" #include "chrome/browser/sync/glue/data_type_manager_mock.h" #include "chrome/browser/sync/profile_sync_components_factory_mock.h" #include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/browser/sync/profile_sync_test_util.h" #include "chrome/browser/sync/sync_prefs.h" #include "chrome/browser/sync/test_profile_sync_service.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "content/public/test/test_browser_thread.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/gaia_constants.h" #include "testing/gmock/include/gmock/gmock.h" using browser_sync::DataTypeManager; using browser_sync::DataTypeManagerMock; using content::BrowserThread; using testing::_; using testing::AnyNumber; using testing::DoAll; using testing::InvokeArgument; using testing::Mock; using testing::Return; ACTION_P(InvokeOnConfigureStart, pss) { TestProfileSyncService* service = static_cast(pss); service->OnConfigureStart(); } ACTION_P2(InvokeOnConfigureDone, pss, result) { TestProfileSyncService* service = static_cast(pss); DataTypeManager::ConfigureResult configure_result = static_cast(result); service->OnConfigureDone(configure_result); } class FakeTokenService : public TokenService { public: FakeTokenService() {} virtual ~FakeTokenService() {} virtual void LoadTokensFromDB() OVERRIDE { set_tokens_loaded(true); content::NotificationService::current()->Notify( chrome::NOTIFICATION_TOKEN_LOADING_FINISHED, content::Source(this), content::NotificationService::NoDetails()); } }; class ProfileSyncServiceStartupTest : public testing::Test { public: ProfileSyncServiceStartupTest() : ui_thread_(BrowserThread::UI, &ui_loop_), db_thread_(BrowserThread::DB), file_thread_(BrowserThread::FILE), io_thread_(BrowserThread::IO), profile_(new TestingProfile), sync_(NULL) {} virtual ~ProfileSyncServiceStartupTest() { } virtual void SetUp() { file_thread_.Start(); io_thread_.StartIOThread(); profile_->CreateRequestContext(); #if defined(OS_CHROMEOS) SigninManagerFactory::GetInstance()->SetTestingFactory( profile_.get(), FakeSigninManagerBase::Build); #else SigninManagerFactory::GetInstance()->SetTestingFactory( profile_.get(), FakeSigninManager::Build); #endif } virtual void TearDown() { sync_->RemoveObserver(&observer_); ProfileSyncServiceFactory::GetInstance()->SetTestingFactory( profile_.get(), NULL); profile_.reset(); // Pump messages posted by the sync core thread (which may end up // posting on the IO thread). ui_loop_.RunUntilIdle(); io_thread_.Stop(); file_thread_.Stop(); ui_loop_.RunUntilIdle(); } static BrowserContextKeyedService* BuildService( content::BrowserContext* profile) { return new TestProfileSyncService( new ProfileSyncComponentsFactoryMock(), static_cast(profile), SigninManagerFactory::GetForProfile(static_cast(profile)), ProfileSyncService::MANUAL_START, true); } void CreateSyncService() { sync_ = static_cast( ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse( profile_.get(), BuildService)); sync_->AddObserver(&observer_); sync_->set_synchronous_sync_configuration(); } protected: DataTypeManagerMock* SetUpDataTypeManager() { DataTypeManagerMock* data_type_manager = new DataTypeManagerMock(); EXPECT_CALL(*sync_->components_factory_mock(), CreateDataTypeManager(_, _, _, _, _)). WillOnce(Return(data_type_manager)); return data_type_manager; } base::MessageLoop ui_loop_; content::TestBrowserThread ui_thread_; content::TestBrowserThread db_thread_; content::TestBrowserThread file_thread_; content::TestBrowserThread io_thread_; scoped_ptr profile_; TestProfileSyncService* sync_; ProfileSyncServiceObserverMock observer_; }; class ProfileSyncServiceStartupCrosTest : public ProfileSyncServiceStartupTest { public: virtual void SetUp() { ProfileSyncServiceStartupTest::SetUp(); sync_ = static_cast( ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse( profile_.get(), BuildCrosService)); sync_->AddObserver(&observer_); sync_->set_synchronous_sync_configuration(); } static BrowserContextKeyedService* BuildCrosService( content::BrowserContext* context) { Profile* profile = static_cast(context); SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile); profile->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); signin->Initialize(profile, NULL); EXPECT_FALSE(signin->GetAuthenticatedUsername().empty()); return new TestProfileSyncService( new ProfileSyncComponentsFactoryMock(), profile, signin, ProfileSyncService::AUTO_START, true); } }; BrowserContextKeyedService* BuildFakeTokenService( content::BrowserContext* profile) { return new FakeTokenService(); } TEST_F(ProfileSyncServiceStartupTest, StartFirstTime) { // We've never completed startup. profile_->GetPrefs()->ClearPref(prefs::kSyncHasSetupCompleted); SigninManagerFactory::GetForProfile( profile_.get())->Initialize(profile_.get(), NULL); CreateSyncService(); DataTypeManagerMock* data_type_manager = SetUpDataTypeManager(); EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(0); // Should not actually start, rather just clean things up and wait // to be enabled. EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); sync_->Initialize(); // Preferences should be back to defaults. EXPECT_EQ(0, profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime)); EXPECT_FALSE(profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)); Mock::VerifyAndClearExpectations(data_type_manager); // Then start things up. EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(1); EXPECT_CALL(*data_type_manager, state()). WillOnce(Return(DataTypeManager::CONFIGURED)). WillOnce(Return(DataTypeManager::CONFIGURED)); EXPECT_CALL(*data_type_manager, Stop()).Times(1); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); sync_->SetSetupInProgress(true); // Simulate successful signin as test_user. profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); sync_->signin()->SetAuthenticatedUsername("test_user"); GoogleServiceSigninSuccessDetails details("test_user", ""); content::NotificationService::current()->Notify( chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, content::Source(profile_.get()), content::Details(&details)); // Create some tokens in the token service. TokenServiceFactory::GetForProfile(profile_.get())->IssueAuthTokenForTest( GaiaConstants::kSyncService, "sync_token"); TokenServiceFactory::GetForProfile(profile_.get())->IssueAuthTokenForTest( GaiaConstants::kGaiaOAuth2LoginRefreshToken, "oauth2_login_token"); // Simulate the UI telling sync it has finished setting up. sync_->SetSetupInProgress(false); EXPECT_TRUE(sync_->ShouldPushChanges()); } TEST_F(ProfileSyncServiceStartupTest, StartNoCredentials) { // We've never completed startup. profile_->GetPrefs()->ClearPref(prefs::kSyncHasSetupCompleted); SigninManagerFactory::GetForProfile( profile_.get())->Initialize(profile_.get(), NULL); TokenService* token_service = static_cast( TokenServiceFactory::GetInstance()->SetTestingFactoryAndUse( profile_.get(), BuildFakeTokenService)); CreateSyncService(); DataTypeManagerMock* data_type_manager = SetUpDataTypeManager(); EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(0); // Should not actually start, rather just clean things up and wait // to be enabled. EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); sync_->Initialize(); EXPECT_FALSE(sync_->GetBackendForTest()); // Preferences should be back to defaults. EXPECT_EQ(0, profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime)); EXPECT_FALSE(profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)); Mock::VerifyAndClearExpectations(data_type_manager); // Then start things up. EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(1); EXPECT_CALL(*data_type_manager, state()). WillOnce(Return(DataTypeManager::CONFIGURED)). WillOnce(Return(DataTypeManager::CONFIGURED)); EXPECT_CALL(*data_type_manager, Stop()).Times(1); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); sync_->SetSetupInProgress(true); // Simulate successful signin as test_user. profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); sync_->signin()->SetAuthenticatedUsername("test_user"); GoogleServiceSigninSuccessDetails details("test_user", ""); content::NotificationService::current()->Notify( chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, content::Source(profile_.get()), content::Details(&details)); // NOTE: Unlike StartFirstTime, this test does not issue any auth tokens. token_service->LoadTokensFromDB(); sync_->SetSetupInProgress(false); // Backend should initialize using a bogus GAIA token for credentials. EXPECT_TRUE(sync_->ShouldPushChanges()); } TEST_F(ProfileSyncServiceStartupTest, StartInvalidCredentials) { profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); SigninManagerFactory::GetForProfile( profile_.get())->Initialize(profile_.get(), NULL); CreateSyncService(); DataTypeManagerMock* data_type_manager = SetUpDataTypeManager(); EXPECT_CALL(*data_type_manager, Configure(_, _)).Times(0); TokenService* token_service = static_cast( TokenServiceFactory::GetInstance()->SetTestingFactoryAndUse( profile_.get(), BuildFakeTokenService)); token_service->LoadTokensFromDB(); // Tell the backend to stall while downloading control types (simulating an // auth error). sync_->fail_initial_download(); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); sync_->Initialize(); EXPECT_TRUE(sync_->GetBackendForTest()); EXPECT_FALSE(sync_->sync_initialized()); EXPECT_FALSE(sync_->ShouldPushChanges()); Mock::VerifyAndClearExpectations(data_type_manager); // Update the credentials, unstalling the backend. EXPECT_CALL(*data_type_manager, Configure(_, _)); EXPECT_CALL(*data_type_manager, state()). WillRepeatedly(Return(DataTypeManager::CONFIGURED)); EXPECT_CALL(*data_type_manager, Stop()).Times(1); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); sync_->SetSetupInProgress(true); // Simulate successful signin. GoogleServiceSigninSuccessDetails details("test_user", std::string()); content::NotificationService::current()->Notify( chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, content::Source(profile_.get()), content::Details(&details)); token_service->IssueAuthTokenForTest( GaiaConstants::kSyncService, "sync_token"); sync_->SetSetupInProgress(false); base::MessageLoop::current()->Run(); // Verify we successfully finish startup and configuration. EXPECT_TRUE(sync_->ShouldPushChanges()); } TEST_F(ProfileSyncServiceStartupCrosTest, StartCrosNoCredentials) { EXPECT_CALL(*sync_->components_factory_mock(), CreateDataTypeManager(_, _, _, _, _)).Times(0); profile_->GetPrefs()->ClearPref(prefs::kSyncHasSetupCompleted); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); TokenService* token_service = static_cast( TokenServiceFactory::GetInstance()->SetTestingFactoryAndUse( profile_.get(), BuildFakeTokenService)); sync_->Initialize(); // Sync should not start because there are no tokens yet. EXPECT_FALSE(sync_->ShouldPushChanges()); EXPECT_FALSE(sync_->GetBackendForTest()); token_service->LoadTokensFromDB(); sync_->SetSetupInProgress(false); // Sync should not start because there are still no tokens. EXPECT_FALSE(sync_->ShouldPushChanges()); EXPECT_FALSE(sync_->GetBackendForTest()); } TEST_F(ProfileSyncServiceStartupCrosTest, StartFirstTime) { DataTypeManagerMock* data_type_manager = SetUpDataTypeManager(); profile_->GetPrefs()->ClearPref(prefs::kSyncHasSetupCompleted); EXPECT_CALL(*data_type_manager, Configure(_, _)); EXPECT_CALL(*data_type_manager, state()). WillRepeatedly(Return(DataTypeManager::CONFIGURED)); EXPECT_CALL(*data_type_manager, Stop()).Times(1); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); TokenServiceFactory::GetForProfile(profile_.get())->IssueAuthTokenForTest( GaiaConstants::kSyncService, "sync_token"); sync_->Initialize(); EXPECT_TRUE(sync_->ShouldPushChanges()); } TEST_F(ProfileSyncServiceStartupTest, StartNormal) { // Pre load the tokens profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); SigninManagerFactory::GetForProfile(profile_.get())->Initialize( profile_.get(), NULL); CreateSyncService(); DataTypeManagerMock* data_type_manager = SetUpDataTypeManager(); EXPECT_CALL(*data_type_manager, Configure(_, _)); EXPECT_CALL(*data_type_manager, state()). WillRepeatedly(Return(DataTypeManager::CONFIGURED)); EXPECT_CALL(*data_type_manager, Stop()).Times(1); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); TokenServiceFactory::GetForProfile(profile_.get())->IssueAuthTokenForTest( GaiaConstants::kSyncService, "sync_token"); sync_->Initialize(); } // 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(ProfileSyncServiceStartupTest, StartRecoverDatatypePrefs) { // Clear the datatype preference fields (simulating bug 154940). profile_->GetPrefs()->ClearPref(prefs::kSyncKeepEverythingSynced); syncer::ModelTypeSet user_types = syncer::UserTypes(); for (syncer::ModelTypeSet::Iterator iter = user_types.First(); iter.Good(); iter.Inc()) { profile_->GetPrefs()->ClearPref( browser_sync::SyncPrefs::GetPrefNameForDataType(iter.Get())); } // Pre load the tokens profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); SigninManagerFactory::GetForProfile(profile_.get())->Initialize( profile_.get(), NULL); CreateSyncService(); DataTypeManagerMock* data_type_manager = SetUpDataTypeManager(); EXPECT_CALL(*data_type_manager, Configure(_, _)); EXPECT_CALL(*data_type_manager, state()). WillRepeatedly(Return(DataTypeManager::CONFIGURED)); EXPECT_CALL(*data_type_manager, Stop()).Times(1); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); TokenServiceFactory::GetForProfile(profile_.get())->IssueAuthTokenForTest( GaiaConstants::kSyncService, "sync_token"); sync_->Initialize(); EXPECT_TRUE(profile_->GetPrefs()->GetBoolean( prefs::kSyncKeepEverythingSynced)); } // Verify that the recovery of datatype preferences doesn't overwrite a valid // case where only bookmarks are enabled. TEST_F(ProfileSyncServiceStartupTest, StartDontRecoverDatatypePrefs) { // Explicitly set Keep Everything Synced to false and have only bookmarks // enabled. profile_->GetPrefs()->SetBoolean(prefs::kSyncKeepEverythingSynced, false); // Pre load the tokens profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); SigninManagerFactory::GetForProfile(profile_.get())->Initialize( profile_.get(), NULL); CreateSyncService(); DataTypeManagerMock* data_type_manager = SetUpDataTypeManager(); EXPECT_CALL(*data_type_manager, Configure(_, _)); EXPECT_CALL(*data_type_manager, state()). WillRepeatedly(Return(DataTypeManager::CONFIGURED)); EXPECT_CALL(*data_type_manager, Stop()).Times(1); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); TokenServiceFactory::GetForProfile(profile_.get())->IssueAuthTokenForTest( GaiaConstants::kSyncService, "sync_token"); sync_->Initialize(); EXPECT_FALSE(profile_->GetPrefs()->GetBoolean( prefs::kSyncKeepEverythingSynced)); } TEST_F(ProfileSyncServiceStartupTest, ManagedStartup) { // Service should not be started by Initialize() since it's managed. profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); SigninManagerFactory::GetForProfile(profile_.get())->Initialize( profile_.get(), NULL); CreateSyncService(); // Disable sync through policy. profile_->GetPrefs()->SetBoolean(prefs::kSyncManaged, true); EXPECT_CALL(*sync_->components_factory_mock(), CreateDataTypeManager(_, _, _, _, _)).Times(0); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); TokenServiceFactory::GetForProfile(profile_.get())->IssueAuthTokenForTest( GaiaConstants::kSyncService, "sync_token"); sync_->Initialize(); } TEST_F(ProfileSyncServiceStartupTest, SwitchManaged) { profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); SigninManagerFactory::GetForProfile(profile_.get())->Initialize( profile_.get(), NULL); CreateSyncService(); DataTypeManagerMock* data_type_manager = SetUpDataTypeManager(); EXPECT_CALL(*data_type_manager, Configure(_, _)); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); TokenServiceFactory::GetForProfile(profile_.get())->IssueAuthTokenForTest( GaiaConstants::kSyncService, "sync_token"); sync_->Initialize(); // The service should stop when switching to managed mode. Mock::VerifyAndClearExpectations(data_type_manager); EXPECT_CALL(*data_type_manager, state()). WillOnce(Return(DataTypeManager::CONFIGURED)); EXPECT_CALL(*data_type_manager, Stop()).Times(1); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); profile_->GetPrefs()->SetBoolean(prefs::kSyncManaged, true); // When switching back to unmanaged, the state should change, but the service // should not start up automatically (kSyncSetupCompleted will be false). Mock::VerifyAndClearExpectations(data_type_manager); EXPECT_CALL(*sync_->components_factory_mock(), CreateDataTypeManager(_, _, _, _, _)).Times(0); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); profile_->GetPrefs()->ClearPref(prefs::kSyncManaged); } TEST_F(ProfileSyncServiceStartupTest, StartFailure) { profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); SigninManagerFactory::GetForProfile(profile_.get())->Initialize( profile_.get(), NULL); CreateSyncService(); DataTypeManagerMock* data_type_manager = SetUpDataTypeManager(); DataTypeManager::ConfigureStatus status = DataTypeManager::ABORTED; syncer::SyncError error( FROM_HERE, "Association failed.", syncer::BOOKMARKS); std::list errors; errors.push_back(error); DataTypeManager::ConfigureResult result( status, syncer::ModelTypeSet(), errors, syncer::ModelTypeSet()); EXPECT_CALL(*data_type_manager, Configure(_, _)). WillRepeatedly( DoAll(InvokeOnConfigureStart(sync_), InvokeOnConfigureDone(sync_, result))); EXPECT_CALL(*data_type_manager, state()). WillOnce(Return(DataTypeManager::STOPPED)); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); TokenServiceFactory::GetForProfile(profile_.get())->IssueAuthTokenForTest( GaiaConstants::kSyncService, "sync_token"); sync_->Initialize(); EXPECT_TRUE(sync_->HasUnrecoverableError()); } TEST_F(ProfileSyncServiceStartupTest, StartDownloadFailed) { // Pre load the tokens profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername, "test_user"); SigninManagerFactory::GetForProfile(profile_.get())->Initialize( profile_.get(), NULL); CreateSyncService(); profile_->GetPrefs()->ClearPref(prefs::kSyncHasSetupCompleted); EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber()); TokenServiceFactory::GetForProfile(profile_.get())->IssueAuthTokenForTest( GaiaConstants::kSyncService, "sync_token"); sync_->fail_initial_download(); sync_->Initialize(); EXPECT_FALSE(sync_->sync_initialized()); EXPECT_FALSE(sync_->GetBackendForTest()); }