| // 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 "chrome/browser/profiles/gaia_info_update_service.h" |
| |
| #include <stddef.h> |
| |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/profiles/profile_attributes_entry.h" |
| #include "chrome/browser/profiles/profile_attributes_storage.h" |
| #include "chrome/browser/profiles/profile_downloader.h" |
| #include "chrome/browser/profiles/profile_info_cache.h" |
| #include "chrome/browser/profiles/profile_info_cache_unittest.h" |
| #include "chrome/browser/profiles/profiles_state.h" |
| #include "chrome/browser/signin/chrome_signin_client_factory.h" |
| #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" |
| #include "chrome/browser/signin/test_signin_client_builder.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/base/testing_profile_manager.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/signin/core/browser/account_tracker_service.h" |
| #include "components/signin/core/browser/signin_pref_names.h" |
| #include "components/sync_preferences/pref_service_syncable.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "ui/gfx/image/image.h" |
| #include "ui/gfx/image/image_unittest_util.h" |
| |
| using ::testing::Return; |
| using ::testing::NiceMock; |
| |
| namespace { |
| |
| class ProfileDownloaderMock : public ProfileDownloader { |
| public: |
| explicit ProfileDownloaderMock(ProfileDownloaderDelegate* delegate) |
| : ProfileDownloader(delegate) { |
| } |
| |
| ~ProfileDownloaderMock() override {} |
| |
| MOCK_CONST_METHOD0(GetProfileFullName, base::string16()); |
| MOCK_CONST_METHOD0(GetProfileGivenName, base::string16()); |
| MOCK_CONST_METHOD0(GetProfilePicture, SkBitmap()); |
| MOCK_CONST_METHOD0(GetProfilePictureStatus, |
| ProfileDownloader::PictureStatus()); |
| MOCK_CONST_METHOD0(GetProfilePictureURL, std::string()); |
| MOCK_CONST_METHOD0(GetProfileHostedDomain, base::string16()); |
| }; |
| |
| class GAIAInfoUpdateServiceMock : public GAIAInfoUpdateService { |
| public: |
| explicit GAIAInfoUpdateServiceMock(Profile* profile) |
| : GAIAInfoUpdateService(profile) { |
| } |
| |
| ~GAIAInfoUpdateServiceMock() override {} |
| |
| MOCK_METHOD0(Update, void()); |
| }; |
| |
| // TODO(anthonyvd) : remove ProfileInfoCacheTest from the test fixture. |
| class GAIAInfoUpdateServiceTestBase : public ProfileInfoCacheTest { |
| protected: |
| explicit GAIAInfoUpdateServiceTestBase(bool create_gaia_info_service_on_setup) |
| : create_gaia_info_service_on_setup_(create_gaia_info_service_on_setup) {} |
| ~GAIAInfoUpdateServiceTestBase() override = default; |
| |
| void SetUp() override { |
| ProfileInfoCacheTest::SetUp(); |
| if (create_gaia_info_service_on_setup_) { |
| service_.reset(new NiceMock<GAIAInfoUpdateServiceMock>(profile())); |
| downloader_.reset(new NiceMock<ProfileDownloaderMock>(service())); |
| } |
| |
| identity_test_env_adaptor_ = |
| std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile()); |
| }; |
| |
| void TearDown() override { |
| if (downloader_) |
| downloader_.reset(); |
| if (service_) { |
| service_->Shutdown(); |
| service_.reset(); |
| } |
| ProfileInfoCacheTest::TearDown(); |
| } |
| |
| Profile* profile() { |
| if (!profile_) |
| profile_ = CreateProfile("Person 1"); |
| return profile_; |
| } |
| |
| identity::IdentityTestEnvironment* identity_test_env() { |
| return identity_test_env_adaptor_->identity_test_env(); |
| } |
| |
| ProfileAttributesStorage* storage() { |
| return testing_profile_manager_.profile_attributes_storage(); |
| } |
| |
| NiceMock<GAIAInfoUpdateServiceMock>* service() { return service_.get(); } |
| NiceMock<ProfileDownloaderMock>* downloader() { return downloader_.get(); } |
| |
| Profile* CreateProfile(const std::string& name) { |
| TestingProfile::TestingFactories testing_factories = |
| IdentityTestEnvironmentProfileAdaptor:: |
| GetIdentityTestEnvironmentFactories(); |
| testing_factories.emplace_back( |
| ChromeSigninClientFactory::GetInstance(), |
| base::BindRepeating(&signin::BuildTestSigninClient)); |
| Profile* profile = testing_profile_manager_.CreateTestingProfile( |
| name, std::unique_ptr<sync_preferences::PrefServiceSyncable>(), |
| base::UTF8ToUTF16(name), 0, std::string(), |
| std::move(testing_factories)); |
| // The testing manager sets the profile name manually, which counts as |
| // a user-customized profile name. Reset this to match the default name |
| // we are actually using. |
| ProfileAttributesEntry* entry = nullptr; |
| // TODO(anthonyvd) : refactor the function so the following assertion can be |
| // changed to ASSERT_TRUE. |
| EXPECT_TRUE(storage()->GetProfileAttributesWithPath(profile->GetPath(), |
| &entry)); |
| entry->SetIsUsingDefaultName(true); |
| return profile; |
| } |
| |
| static std::string GivenName(const std::string& id) { |
| return id + "first"; |
| } |
| static std::string FullName(const std::string& id) { |
| return GivenName(id) + " " + id + "last"; |
| } |
| static base::string16 GivenName16(const std::string& id) { |
| return base::UTF8ToUTF16(GivenName(id)); |
| } |
| static base::string16 FullName16(const std::string& id) { |
| return base::UTF8ToUTF16(FullName(id)); |
| } |
| |
| void ProfileDownloadSuccess( |
| const base::string16& full_name, |
| const base::string16& given_name, |
| const gfx::Image& image, |
| const std::string& url, |
| const base::string16& hosted_domain) { |
| EXPECT_CALL(*downloader(), GetProfileFullName()). |
| WillOnce(Return(full_name)); |
| EXPECT_CALL(*downloader(), GetProfileGivenName()). |
| WillOnce(Return(given_name)); |
| const SkBitmap* bmp = image.ToSkBitmap(); |
| EXPECT_CALL(*downloader(), GetProfilePicture()).WillOnce(Return(*bmp)); |
| EXPECT_CALL(*downloader(), GetProfilePictureStatus()). |
| WillOnce(Return(ProfileDownloader::PICTURE_SUCCESS)); |
| EXPECT_CALL(*downloader(), GetProfilePictureURL()).WillOnce(Return(url)); |
| EXPECT_CALL(*downloader(), GetProfileHostedDomain()). |
| WillOnce(Return(hosted_domain)); |
| |
| service()->OnProfileDownloadSuccess(downloader()); |
| } |
| |
| void RenameProfile(const base::string16& full_name, |
| const base::string16& given_name) { |
| gfx::Image image = gfx::test::CreateImage(256, 256); |
| std::string url("foo.com"); |
| ProfileDownloadSuccess(full_name, given_name, image, url, base::string16()); |
| |
| // Make sure the right profile was updated correctly. |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE( |
| storage()->GetProfileAttributesWithPath(profile()->GetPath(), &entry)); |
| EXPECT_EQ(full_name, entry->GetGAIAName()); |
| EXPECT_EQ(given_name, entry->GetGAIAGivenName()); |
| } |
| |
| const bool create_gaia_info_service_on_setup_; |
| Profile* profile_ = nullptr; |
| std::unique_ptr<IdentityTestEnvironmentProfileAdaptor> |
| identity_test_env_adaptor_; |
| std::unique_ptr<NiceMock<GAIAInfoUpdateServiceMock>> service_; |
| std::unique_ptr<NiceMock<ProfileDownloaderMock>> downloader_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(GAIAInfoUpdateServiceTestBase); |
| }; |
| |
| class GAIAInfoUpdateServiceTest : public GAIAInfoUpdateServiceTestBase { |
| public: |
| GAIAInfoUpdateServiceTest() |
| : GAIAInfoUpdateServiceTestBase( |
| /*create_gaia_info_service_on_setup_=*/true) {} |
| ~GAIAInfoUpdateServiceTest() override = default; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(GAIAInfoUpdateServiceTest); |
| }; |
| |
| class GAIAInfoUpdateServiceMiscTest : public GAIAInfoUpdateServiceTestBase, |
| public ProfileInfoCacheObserver { |
| public: |
| GAIAInfoUpdateServiceMiscTest() |
| : GAIAInfoUpdateServiceTestBase( |
| /*create_gaia_info_service_on_setup_=*/false) {} |
| ~GAIAInfoUpdateServiceMiscTest() override = default; |
| |
| void OnProfileNameChanged(const base::FilePath& profile_path, |
| const base::string16& old_profile_name) override { |
| profile_name_changed_count_++; |
| } |
| |
| void OnProfileAvatarChanged(const base::FilePath& profile_path) override { |
| profile_avatar_changed_count_++; |
| } |
| |
| protected: |
| unsigned int profile_name_changed_count_ = 0; |
| unsigned int profile_avatar_changed_count_ = 0; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(GAIAInfoUpdateServiceMiscTest); |
| }; |
| |
| } // namespace |
| |
| TEST_F(GAIAInfoUpdateServiceTest, DownloadSuccess) { |
| // No URL should be cached yet. |
| EXPECT_EQ(std::string(), service()->GetCachedPictureURL()); |
| EXPECT_EQ(std::string(), profile()->GetPrefs()-> |
| GetString(prefs::kGoogleServicesHostedDomain)); |
| |
| base::string16 name = base::ASCIIToUTF16("Pat Smith"); |
| base::string16 given_name = base::ASCIIToUTF16("Pat"); |
| gfx::Image image = gfx::test::CreateImage(256, 256); |
| std::string url("foo.com"); |
| base::string16 hosted_domain(base::ASCIIToUTF16("")); |
| ProfileDownloadSuccess(name, given_name, image, url, hosted_domain); |
| |
| // On success the GAIA info should be updated. |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath(profile()->GetPath(), |
| &entry)); |
| EXPECT_EQ(name, entry->GetGAIAName()); |
| EXPECT_EQ(given_name, entry->GetGAIAGivenName()); |
| EXPECT_TRUE(gfx::test::AreImagesEqual(image, *entry->GetGAIAPicture())); |
| EXPECT_EQ(url, service()->GetCachedPictureURL()); |
| EXPECT_EQ( |
| AccountTrackerService::kNoHostedDomainFound, |
| profile()->GetPrefs()->GetString(prefs::kGoogleServicesHostedDomain)); |
| } |
| |
| TEST_F(GAIAInfoUpdateServiceTest, DownloadFailure) { |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath(profile()->GetPath(), |
| &entry)); |
| base::string16 old_name = entry->GetName(); |
| gfx::Image old_image = entry->GetAvatarIcon(); |
| |
| EXPECT_EQ(std::string(), service()->GetCachedPictureURL()); |
| |
| service()->OnProfileDownloadFailure(downloader(), |
| ProfileDownloaderDelegate::SERVICE_ERROR); |
| |
| // On failure nothing should be updated. |
| EXPECT_EQ(old_name, entry->GetName()); |
| EXPECT_EQ(base::string16(), entry->GetGAIAName()); |
| EXPECT_EQ(base::string16(), entry->GetGAIAGivenName()); |
| EXPECT_TRUE(gfx::test::AreImagesEqual(old_image, entry->GetAvatarIcon())); |
| EXPECT_EQ(nullptr, entry->GetGAIAPicture()); |
| EXPECT_EQ(std::string(), service()->GetCachedPictureURL()); |
| EXPECT_EQ(std::string(), |
| profile()->GetPrefs()->GetString(prefs::kGoogleServicesHostedDomain)); |
| } |
| |
| TEST_F(GAIAInfoUpdateServiceTest, ProfileLockEnabledForWhitelist) { |
| // No URL should be cached yet. |
| EXPECT_EQ(std::string(), service()->GetCachedPictureURL()); |
| |
| base::string16 name = base::ASCIIToUTF16("Pat Smith"); |
| base::string16 given_name = base::ASCIIToUTF16("Pat"); |
| gfx::Image image = gfx::test::CreateImage(256, 256); |
| std::string url("foo.com"); |
| base::string16 hosted_domain(base::ASCIIToUTF16("google.com")); |
| ProfileDownloadSuccess(name, given_name, image, url, hosted_domain); |
| |
| EXPECT_EQ("google.com", profile()->GetPrefs()-> |
| GetString(prefs::kGoogleServicesHostedDomain)); |
| } |
| |
| // TODO(anthonyvd) : remove or update test once the refactoring of the internals |
| // of ProfileInfoCache is complete. |
| TEST_F(GAIAInfoUpdateServiceTest, HandlesProfileReordering) { |
| size_t index = GetCache()->GetIndexOfProfileWithPath(profile()->GetPath()); |
| GetCache()->SetNameOfProfileAtIndex(index, FullName16("B")); |
| GetCache()->SetProfileIsUsingDefaultNameAtIndex(index, true); |
| |
| CreateProfile(FullName("A")); |
| CreateProfile(FullName("C")); |
| CreateProfile(FullName("E")); |
| |
| size_t index_before = |
| GetCache()->GetIndexOfProfileWithPath(profile()->GetPath()); |
| |
| // Rename our profile. |
| RenameProfile(FullName16("D"), GivenName16("D")); |
| // Profiles should have been reordered in the cache. |
| EXPECT_NE(index_before, |
| GetCache()->GetIndexOfProfileWithPath(profile()->GetPath())); |
| // Rename the profile back to the original name, it should go back to its |
| // original position. |
| RenameProfile(FullName16("B"), GivenName16("B")); |
| EXPECT_EQ(index_before, |
| GetCache()->GetIndexOfProfileWithPath(profile()->GetPath())); |
| |
| // Rename only the given name of our profile. |
| RenameProfile(FullName16("B"), GivenName16("D")); |
| // Rename the profile back to the original name, it should go back to its |
| // original position. |
| RenameProfile(FullName16("B"), GivenName16("B")); |
| EXPECT_EQ(index_before, |
| GetCache()->GetIndexOfProfileWithPath(profile()->GetPath())); |
| |
| // Rename only the full name of our profile. |
| RenameProfile(FullName16("D"), GivenName16("B")); |
| // Rename the profile back to the original name, it should go back to its |
| // original position. |
| RenameProfile(FullName16("B"), GivenName16("B")); |
| EXPECT_EQ(index_before, |
| GetCache()->GetIndexOfProfileWithPath(profile()->GetPath())); |
| } |
| |
| TEST_F(GAIAInfoUpdateServiceTest, ShouldUseGAIAProfileInfo) { |
| #if defined(OS_CHROMEOS) |
| // This feature should never be enabled on ChromeOS. |
| EXPECT_FALSE(GAIAInfoUpdateService::ShouldUseGAIAProfileInfo(profile())); |
| #endif |
| } |
| |
| TEST_F(GAIAInfoUpdateServiceTest, ScheduleUpdate) { |
| EXPECT_TRUE(service()->timer_.IsRunning()); |
| service()->timer_.Stop(); |
| EXPECT_FALSE(service()->timer_.IsRunning()); |
| service()->ScheduleNextUpdate(); |
| EXPECT_TRUE(service()->timer_.IsRunning()); |
| } |
| |
| #if !defined(OS_CHROMEOS) |
| |
| TEST_F(GAIAInfoUpdateServiceTest, LogOut) { |
| identity_test_env()->SetPrimaryAccount("pat@example.com"); |
| base::string16 gaia_name = base::UTF8ToUTF16("Pat Foo"); |
| |
| ASSERT_EQ(1u, storage()->GetNumberOfProfiles()); |
| ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front(); |
| entry->SetGAIAName(gaia_name); |
| gfx::Image gaia_picture = gfx::test::CreateImage(256, 256); |
| entry->SetGAIAPicture(&gaia_picture); |
| |
| // Set a fake picture URL. |
| profile()->GetPrefs()->SetString(prefs::kProfileGAIAInfoPictureURL, |
| "example.com"); |
| |
| EXPECT_FALSE(service()->GetCachedPictureURL().empty()); |
| |
| // Log out. |
| identity_test_env()->ClearPrimaryAccount(); |
| // Verify that the GAIA name and picture, and picture URL are unset. |
| EXPECT_TRUE(entry->GetGAIAName().empty()); |
| EXPECT_EQ(nullptr, entry->GetGAIAPicture()); |
| EXPECT_TRUE(service()->GetCachedPictureURL().empty()); |
| } |
| |
| TEST_F(GAIAInfoUpdateServiceTest, LogIn) { |
| // Log in. |
| EXPECT_CALL(*service(), Update()); |
| identity_test_env()->SetPrimaryAccount("pat@example.com"); |
| } |
| |
| TEST_F(GAIAInfoUpdateServiceMiscTest, ClearGaiaInfoOnStartup) { |
| // Simulate a state where the profile entry has GAIA related information |
| // when there is not primary account set. |
| ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount()); |
| ASSERT_EQ(1u, storage()->GetNumberOfProfiles()); |
| ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front(); |
| entry->SetGAIAName(base::UTF8ToUTF16("foo")); |
| entry->SetGAIAGivenName(base::UTF8ToUTF16("Pat Foo")); |
| gfx::Image gaia_picture = gfx::test::CreateImage(256, 256); |
| entry->SetGAIAPicture(&gaia_picture); |
| |
| GetCache()->AddObserver(this); |
| |
| // Verify that creating the GAIAInfoUpdateService resets the GAIA related |
| // profile attributes if the profile no longer has a primary account and that |
| // the profile info cache observer wass notified about profile name and |
| // avatar changes. |
| service_.reset(new NiceMock<GAIAInfoUpdateServiceMock>(profile())); |
| EXPECT_TRUE(entry->GetGAIAName().empty()); |
| EXPECT_TRUE(entry->GetGAIAGivenName().empty()); |
| EXPECT_FALSE(entry->GetGAIAPicture()); |
| EXPECT_EQ(1U, profile_name_changed_count_); |
| EXPECT_EQ(1U, profile_avatar_changed_count_); |
| |
| GetCache()->RemoveObserver(this); |
| } |
| |
| // Regression test for http://crbug.com/900374 |
| TEST_F(GAIAInfoUpdateServiceMiscTest, |
| ClearGaiaInfoForSignedOutProfileDoesNotNotifyProfileObservers) { |
| // Simulate a state where the profile entry has no GAIA related information |
| // and when there is not primary account set. |
| ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount()); |
| ASSERT_EQ(1u, storage()->GetNumberOfProfiles()); |
| ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front(); |
| ASSERT_TRUE(entry->GetGAIAName().empty()); |
| ASSERT_TRUE(entry->GetGAIAGivenName().empty()); |
| ASSERT_FALSE(entry->GetGAIAPicture()); |
| |
| GetCache()->AddObserver(this); |
| |
| // Verify that the state for the profile entry did not change and that the |
| // profile info cache observer was not notified about any profile name |
| // and avatar changes. |
| service_.reset(new NiceMock<GAIAInfoUpdateServiceMock>(profile())); |
| EXPECT_TRUE(entry->GetGAIAName().empty()); |
| EXPECT_TRUE(entry->GetGAIAGivenName().empty()); |
| EXPECT_FALSE(entry->GetGAIAPicture()); |
| EXPECT_EQ(0U, profile_name_changed_count_); |
| EXPECT_EQ(0U, profile_avatar_changed_count_); |
| |
| GetCache()->RemoveObserver(this); |
| } |
| |
| #endif |