| // 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 <memory> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.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/profile_metrics/state.h" |
| #include "components/signin/public/base/signin_pref_names.h" |
| #include "components/signin/public/identity_manager/account_info.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; |
| |
| namespace { |
| |
| #if !BUILDFLAG(IS_CHROMEOS_ASH) |
| AccountInfo GetValidAccountInfo(std::string email, |
| CoreAccountId account_id, |
| std::string given_name, |
| std::string full_name, |
| std::string hosted_domain) { |
| AccountInfo account_info; |
| account_info.email = email; |
| account_info.gaia = account_id.ToString(); |
| account_info.account_id = account_id; |
| account_info.given_name = given_name; |
| account_info.full_name = full_name; |
| account_info.hosted_domain = hosted_domain; |
| account_info.locale = email; |
| account_info.picture_url = "example.com"; |
| return account_info; |
| } |
| |
| #if BUILDFLAG(ENABLE_DICE_SUPPORT) |
| const char kChromiumOrgDomain[] = "chromium.org"; |
| #endif // BUILDFLAG(ENABLE_DICE_SUPPORT) |
| |
| #endif // !BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| class GAIAInfoUpdateServiceTestBase : public testing::Test { |
| protected: |
| explicit GAIAInfoUpdateServiceTestBase( |
| signin::AccountConsistencyMethod account_consistency) |
| : testing_profile_manager_(TestingBrowserProcess::GetGlobal()), |
| identity_test_env_(/*test_url_loader_factory=*/nullptr, |
| /*pref_service=*/nullptr, |
| account_consistency, |
| /*test_signin_client=*/nullptr) {} |
| ~GAIAInfoUpdateServiceTestBase() override = default; |
| |
| void SetUp() override { |
| testing::Test::SetUp(); |
| ASSERT_TRUE(testing_profile_manager_.SetUp()); |
| RecreateGAIAInfoUpdateService(); |
| } |
| |
| void RecreateGAIAInfoUpdateService() { |
| if (service_) |
| service_->Shutdown(); |
| |
| service_ = std::make_unique<GAIAInfoUpdateService>( |
| identity_test_env_.identity_manager(), |
| testing_profile_manager_.profile_attributes_storage(), |
| profile()->GetPath()); |
| } |
| |
| void TearDown() override { |
| if (service_) { |
| service_->Shutdown(); |
| service_.reset(); |
| } |
| } |
| |
| TestingProfile* profile() { |
| if (!profile_) |
| CreateProfile("Person 1"); |
| return profile_; |
| } |
| |
| signin::IdentityTestEnvironment* identity_test_env() { |
| return &identity_test_env_; |
| } |
| |
| ProfileAttributesStorage* storage() { |
| return testing_profile_manager_.profile_attributes_storage(); |
| } |
| |
| GAIAInfoUpdateService* service() { return service_.get(); } |
| |
| void CreateProfile(const std::string& name) { |
| profile_ = testing_profile_manager_.CreateTestingProfile( |
| name, std::unique_ptr<sync_preferences::PrefServiceSyncable>(), |
| base::UTF8ToUTF16(name), 0, std::string(), |
| TestingProfile::TestingFactories()); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_; |
| TestingProfileManager testing_profile_manager_; |
| TestingProfile* profile_ = nullptr; |
| signin::IdentityTestEnvironment identity_test_env_; |
| std::unique_ptr<GAIAInfoUpdateService> service_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(GAIAInfoUpdateServiceTestBase); |
| }; |
| |
| class GAIAInfoUpdateServiceTest : public GAIAInfoUpdateServiceTestBase { |
| protected: |
| GAIAInfoUpdateServiceTest() |
| : GAIAInfoUpdateServiceTestBase( |
| signin::AccountConsistencyMethod::kDisabled) {} |
| ~GAIAInfoUpdateServiceTest() override = default; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(GAIAInfoUpdateServiceTest); |
| }; |
| |
| } // namespace |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // This feature should never be enabled on ChromeOS. |
| TEST_F(GAIAInfoUpdateServiceTest, ShouldUseGAIAProfileInfo) { |
| EXPECT_FALSE(GAIAInfoUpdateService::ShouldUseGAIAProfileInfo(profile())); |
| } |
| #else // BUILDFLAG(IS_CHROMEOS_ASH) |
| TEST_F(GAIAInfoUpdateServiceTest, SyncOnSyncOff) { |
| AccountInfo info = |
| identity_test_env()->MakeAccountAvailable("pat@example.com"); |
| base::RunLoop().RunUntilIdle(); |
| identity_test_env()->SetPrimaryAccount(info.email); |
| info = GetValidAccountInfo(info.email, info.account_id, "Pat", "Pat Foo", |
| kNoHostedDomainFound); |
| signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(), |
| info); |
| base::RunLoop().RunUntilIdle(); |
| |
| ASSERT_EQ(1u, storage()->GetNumberOfProfiles()); |
| ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front(); |
| EXPECT_EQ(entry->GetGAIAGivenName(), u"Pat"); |
| EXPECT_EQ(entry->GetGAIAName(), u"Pat Foo"); |
| EXPECT_EQ(entry->GetHostedDomain(), kNoHostedDomainFound); |
| |
| gfx::Image gaia_picture = gfx::test::CreateImage(256, 256); |
| signin::SimulateAccountImageFetch(identity_test_env()->identity_manager(), |
| info.account_id, "GAIA_IMAGE_URL_WITH_SIZE", |
| gaia_picture); |
| // Set a fake picture URL. |
| EXPECT_TRUE(gfx::test::AreImagesEqual(gaia_picture, entry->GetAvatarIcon())); |
| // Log out. |
| identity_test_env()->ClearPrimaryAccount(); |
| // Verify that the GAIA name and picture, and picture URL are unset. |
| EXPECT_TRUE(entry->GetGAIAGivenName().empty()); |
| EXPECT_TRUE(entry->GetGAIAName().empty()); |
| EXPECT_EQ(nullptr, entry->GetGAIAPicture()); |
| EXPECT_TRUE(entry->GetHostedDomain().empty()); |
| } |
| |
| #if BUILDFLAG(ENABLE_DICE_SUPPORT) |
| namespace { |
| class GAIAInfoUpdateServiceDiceTest : public GAIAInfoUpdateServiceTestBase { |
| protected: |
| GAIAInfoUpdateServiceDiceTest() |
| : GAIAInfoUpdateServiceTestBase(signin::AccountConsistencyMethod::kDice) { |
| } |
| ~GAIAInfoUpdateServiceDiceTest() override = default; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(GAIAInfoUpdateServiceDiceTest); |
| }; |
| } // namespace |
| |
| TEST_F(GAIAInfoUpdateServiceDiceTest, RevokeSyncConsent) { |
| AccountInfo info = |
| identity_test_env()->MakeAccountAvailable("pat@example.com"); |
| base::RunLoop().RunUntilIdle(); |
| identity_test_env()->SetPrimaryAccount(info.email); |
| info = GetValidAccountInfo(info.email, info.account_id, "Pat", "Pat Foo", |
| kNoHostedDomainFound); |
| signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(), |
| info); |
| base::RunLoop().RunUntilIdle(); |
| |
| ASSERT_EQ(1u, storage()->GetNumberOfProfiles()); |
| ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front(); |
| gfx::Image gaia_picture = gfx::test::CreateImage(256, 256); |
| signin::SimulateAccountImageFetch(identity_test_env()->identity_manager(), |
| info.account_id, "GAIA_IMAGE_URL_WITH_SIZE", |
| gaia_picture); |
| // Revoke sync consent (stay signed in with the primary account). |
| identity_test_env()->RevokeSyncConsent(); |
| ASSERT_TRUE(identity_test_env()->identity_manager()->HasPrimaryAccount( |
| signin::ConsentLevel::kSignin)); |
| // Verify that the GAIA name and picture, and picture URL are not cleared |
| // as unconsented primary account still exists. |
| EXPECT_EQ(entry->GetGAIAGivenName(), u"Pat"); |
| EXPECT_EQ(entry->GetGAIAName(), u"Pat Foo"); |
| EXPECT_EQ(entry->GetHostedDomain(), kNoHostedDomainFound); |
| EXPECT_TRUE(gfx::test::AreImagesEqual(gaia_picture, entry->GetAvatarIcon())); |
| } |
| |
| TEST_F(GAIAInfoUpdateServiceTest, LogInLogOut) { |
| std::string email = "pat@example.com"; |
| AccountInfo info = |
| identity_test_env()->MakeUnconsentedPrimaryAccountAvailable(email); |
| EXPECT_TRUE(identity_test_env()->identity_manager()->HasPrimaryAccount( |
| signin::ConsentLevel::kSignin)); |
| EXPECT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount( |
| signin::ConsentLevel::kSync)); |
| info = GetValidAccountInfo(info.email, info.account_id, "Pat", "Pat Foo", |
| kNoHostedDomainFound); |
| signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(), |
| info); |
| base::RunLoop().RunUntilIdle(); |
| |
| ASSERT_EQ(1u, storage()->GetNumberOfProfiles()); |
| ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front(); |
| EXPECT_EQ(entry->GetGAIAGivenName(), u"Pat"); |
| EXPECT_EQ(entry->GetGAIAName(), u"Pat Foo"); |
| EXPECT_EQ(entry->GetHostedDomain(), kNoHostedDomainFound); |
| |
| gfx::Image gaia_picture = gfx::test::CreateImage(256, 256); |
| signin::SimulateAccountImageFetch(identity_test_env()->identity_manager(), |
| info.account_id, "GAIA_IMAGE_URL_WITH_SIZE", |
| gaia_picture); |
| // Set a fake picture URL. |
| EXPECT_TRUE(gfx::test::AreImagesEqual(gaia_picture, entry->GetAvatarIcon())); |
| // Log out. |
| identity_test_env()->ClearPrimaryAccount(); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Verify that the GAIA name and picture, and picture URL are unset. |
| EXPECT_TRUE(entry->GetGAIAGivenName().empty()); |
| EXPECT_TRUE(entry->GetGAIAName().empty()); |
| EXPECT_EQ(nullptr, entry->GetGAIAPicture()); |
| EXPECT_TRUE(entry->GetHostedDomain().empty()); |
| } |
| |
| TEST_F(GAIAInfoUpdateServiceTest, LogInLogOutLogIn) { |
| std::string email1 = "pat1@example.com"; |
| AccountInfo info1 = identity_test_env()->MakeAccountAvailableWithCookies( |
| email1, signin::GetTestGaiaIdForEmail(email1)); |
| base::RunLoop().RunUntilIdle(); |
| info1 = GetValidAccountInfo(info1.email, info1.account_id, "Pat 1", |
| "Pat Foo The First", kNoHostedDomainFound); |
| signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(), |
| info1); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_EQ(1u, storage()->GetNumberOfProfiles()); |
| ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front(); |
| |
| // Test correct histogram recording for all accounts info that has no getters. |
| base::HistogramTester tester; |
| entry->RecordAccountMetrics(); |
| tester.ExpectBucketCount( |
| "Profile.AllAccounts.Names", |
| /*sample=*/profile_metrics::AllAccountsNames::kLikelySingleName, |
| /*expected_count=*/1); |
| tester.ExpectBucketCount( |
| "Profile.AllAccounts.Categories", |
| /*sample=*/profile_metrics::AllAccountsCategories::kSingleCategory, |
| /*expected_count=*/1); |
| |
| // Log out and record the metric again, sign-out wipes previous info in the |
| // entry so again the default values get reported. |
| identity_test_env()->SetCookieAccounts({}); |
| entry->RecordAccountMetrics(); |
| tester.ExpectBucketCount( |
| "Profile.AllAccounts.Names", |
| /*sample=*/profile_metrics::AllAccountsNames::kLikelySingleName, |
| /*expected_count=*/2); |
| tester.ExpectBucketCount( |
| "Profile.AllAccounts.Categories", |
| /*sample=*/profile_metrics::AllAccountsCategories::kSingleCategory, |
| /*expected_count=*/2); |
| |
| std::string email2 = "pat2@example.com"; |
| AccountInfo info2 = identity_test_env()->MakeAccountAvailableWithCookies( |
| email2, signin::GetTestGaiaIdForEmail(email2)); |
| base::RunLoop().RunUntilIdle(); |
| info2 = GetValidAccountInfo(info2.email, info2.account_id, "Pat 2", |
| "Pat Foo The Second", kChromiumOrgDomain); |
| signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(), |
| info2); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_EQ(1u, storage()->GetNumberOfProfiles()); |
| |
| // Because due to the complete sign-out, the info about the previous account |
| // got wiped. Thus the same default metrics get recorded again, despite the |
| // second account has a different gaia name and a different account category |
| // than the first one. |
| entry->RecordAccountMetrics(); |
| tester.ExpectBucketCount( |
| "Profile.AllAccounts.Names", |
| /*sample=*/profile_metrics::AllAccountsNames::kLikelySingleName, |
| /*expected_count=*/3); |
| tester.ExpectBucketCount( |
| "Profile.AllAccounts.Categories", |
| /*sample=*/profile_metrics::AllAccountsCategories::kSingleCategory, |
| /*expected_count=*/3); |
| tester.ExpectTotalCount("Profile.AllAccounts.Names", /*expected_count=*/3); |
| tester.ExpectTotalCount("Profile.AllAccounts.Categories", |
| /*expected_count=*/3); |
| } |
| |
| TEST_F(GAIAInfoUpdateServiceTest, MultiLoginAndLogOut) { |
| // Make two accounts available with both refresh token and cookies. |
| AccountInfo info1 = |
| identity_test_env()->MakeAccountAvailable("pat@example.com"); |
| AccountInfo info2 = |
| identity_test_env()->MakeAccountAvailable("pat2@example.com"); |
| identity_test_env()->SetCookieAccounts( |
| {{info1.email, info1.gaia}, {info2.email, info2.gaia}}); |
| base::RunLoop().RunUntilIdle(); |
| info1 = GetValidAccountInfo(info1.email, info1.account_id, "Pat 1", |
| "Pat Foo The First", kNoHostedDomainFound); |
| // Make the second account an enterprise account by setting a hosted domain. |
| info2 = GetValidAccountInfo(info2.email, info2.account_id, "Pat 2", |
| "Pat Foo The Second", kChromiumOrgDomain); |
| signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(), |
| info1); |
| signin::UpdateAccountInfoForAccount(identity_test_env()->identity_manager(), |
| info2); |
| base::RunLoop().RunUntilIdle(); |
| ASSERT_EQ(1u, storage()->GetNumberOfProfiles()); |
| ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front(); |
| |
| // Test correct histogram recording for all accounts info that has no getters. |
| // The two accounts have both different gaia names and account categories. |
| base::HistogramTester tester; |
| entry->RecordAccountMetrics(); |
| tester.ExpectBucketCount( |
| "Profile.AllAccounts.Names", |
| /*sample=*/profile_metrics::AllAccountsNames::kMultipleNamesWithoutSync, |
| /*expected_count=*/1); |
| tester.ExpectBucketCount( |
| "Profile.AllAccounts.Categories", |
| /*sample=*/ |
| profile_metrics::AllAccountsCategories::kBothConsumerAndEnterpriseNoSync, |
| /*expected_count=*/1); |
| |
| // Log out and record the metric again, sign-out wipes previous info in the |
| // entry so the default values get reported. |
| identity_test_env()->SetCookieAccounts({}); |
| entry->RecordAccountMetrics(); |
| tester.ExpectBucketCount( |
| "Profile.AllAccounts.Names", |
| /*sample=*/profile_metrics::AllAccountsNames::kLikelySingleName, |
| /*expected_count=*/1); |
| tester.ExpectBucketCount( |
| "Profile.AllAccounts.Categories", |
| /*sample=*/profile_metrics::AllAccountsCategories::kSingleCategory, |
| /*expected_count=*/1); |
| tester.ExpectTotalCount("Profile.AllAccounts.Names", /*expected_count=*/2); |
| tester.ExpectTotalCount("Profile.AllAccounts.Categories", |
| /*expected_count=*/2); |
| } |
| #endif // !BUILDFLAG(ENABLE_DICE_SUPPORT) |
| |
| TEST_F(GAIAInfoUpdateServiceTest, 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( |
| signin::ConsentLevel::kSignin)); |
| ASSERT_EQ(1u, storage()->GetNumberOfProfiles()); |
| ProfileAttributesEntry* entry = storage()->GetAllProfilesAttributes().front(); |
| entry->SetGAIAName(u"foo"); |
| entry->SetGAIAGivenName(u"Pat Foo"); |
| gfx::Image gaia_picture = gfx::test::CreateImage(256, 256); |
| entry->SetGAIAPicture("GAIA_IMAGE_URL_WITH_SIZE", gaia_picture); |
| entry->SetHostedDomain(kNoHostedDomainFound); |
| |
| // 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. |
| RecreateGAIAInfoUpdateService(); |
| |
| EXPECT_TRUE(entry->GetGAIAName().empty()); |
| EXPECT_TRUE(entry->GetGAIAGivenName().empty()); |
| EXPECT_FALSE(entry->GetGAIAPicture()); |
| EXPECT_TRUE(entry->GetHostedDomain().empty()); |
| } |
| |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |