| // Copyright 2015 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 <stddef.h> |
| |
| #include <unordered_set> |
| |
| #include "base/files/file_util.h" |
| #include "base/format_macros.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/profiles/profile_avatar_downloader.h" |
| #include "chrome/browser/profiles/profile_avatar_icon_util.h" |
| #include "chrome/browser/profiles/profile_info_cache.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/signin/signin_util.h" |
| #include "chrome/browser/supervised_user/supervised_user_constants.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile_manager.h" |
| #include "components/account_id/account_id.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "content/public/test/test_utils.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| |
| using ::testing::Mock; |
| using ::testing::_; |
| |
| namespace { |
| // The ProfileMetadataEntry accessors aren't just plain old accessors to local |
| // members so they'll be tested. The following helpers will make the testing |
| // code less verbose. |
| #define TEST_ACCESSORS(entry_type, entry, member, first_value, second_value) \ |
| TestAccessors(&entry, \ |
| &entry_type::Get ## member, \ |
| &entry_type::Set ## member, \ |
| first_value, \ |
| second_value); |
| |
| #define TEST_STRING16_ACCESSORS(entry_type, entry, member) \ |
| TEST_ACCESSORS(entry_type, entry, member, \ |
| base::ASCIIToUTF16("first_" #member "_value"), \ |
| base::ASCIIToUTF16("second_" #member "_value")); |
| |
| #define TEST_STRING_ACCESSORS(entry_type, entry, member) \ |
| TEST_ACCESSORS(entry_type, entry, member, \ |
| std::string("first_" #member "_value"), \ |
| std::string("second_" #member "_value")); |
| |
| #define TEST_BOOL_ACCESSORS(entry_type, entry, member) \ |
| TestAccessors(&entry, &entry_type::member, &entry_type::Set##member, true, \ |
| false); |
| |
| template<typename TValue, typename TGetter, typename TSetter> |
| void TestAccessors(ProfileAttributesEntry** entry, |
| TGetter getter_func, |
| TSetter setter_func, |
| TValue first_value, |
| TValue second_value) { |
| (*entry->*setter_func)(first_value); |
| EXPECT_EQ(first_value, (*entry->*getter_func)()); |
| (*entry->*setter_func)(second_value); |
| EXPECT_EQ(second_value, (*entry->*getter_func)()); |
| } |
| |
| class ProfileAttributesTestObserver |
| : public ProfileAttributesStorage::Observer { |
| public: |
| MOCK_METHOD1(OnProfileAdded, void(const base::FilePath& profile_path)); |
| MOCK_METHOD1(OnProfileWillBeRemoved, |
| void(const base::FilePath& profile_path)); |
| MOCK_METHOD2(OnProfileWasRemoved, |
| void(const base::FilePath& profile_path, |
| const base::string16& profile_name)); |
| MOCK_METHOD2(OnProfileNameChanged, |
| void(const base::FilePath& profile_path, |
| const base::string16& old_profile_name)); |
| MOCK_METHOD1(OnProfileAuthInfoChanged, |
| void(const base::FilePath& profile_path)); |
| MOCK_METHOD1(OnProfileAvatarChanged, |
| void(const base::FilePath& profile_path)); |
| MOCK_METHOD1(OnProfileHighResAvatarLoaded, |
| void(const base::FilePath& profile_path)); |
| MOCK_METHOD1(OnProfileSigninRequiredChanged, |
| void(const base::FilePath& profile_path)); |
| MOCK_METHOD1(OnProfileSupervisedUserIdChanged, |
| void(const base::FilePath& profile_path)); |
| MOCK_METHOD1(OnProfileIsOmittedChanged, |
| void(const base::FilePath& profile_path)); |
| }; |
| } // namespace |
| |
| class ProfileAttributesStorageTest : public testing::Test { |
| public: |
| ProfileAttributesStorageTest() |
| : testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {} |
| ~ProfileAttributesStorageTest() override {} |
| |
| protected: |
| void SetUp() override { |
| ASSERT_TRUE(testing_profile_manager_.SetUp()); |
| VerifyAndResetCallExpectations(); |
| EnableObserver(); |
| } |
| |
| void TearDown() override { |
| } |
| |
| base::FilePath GetProfilePath(const std::string& base_name) { |
| return testing_profile_manager_.profile_manager()->user_data_dir(). |
| AppendASCII(base_name); |
| } |
| |
| void VerifyAndResetCallExpectations() { |
| Mock::VerifyAndClear(&observer_); |
| EXPECT_CALL(observer_, OnProfileAdded(_)).Times(0); |
| EXPECT_CALL(observer_, OnProfileWillBeRemoved(_)).Times(0); |
| EXPECT_CALL(observer_, OnProfileWasRemoved(_, _)).Times(0); |
| EXPECT_CALL(observer_, OnProfileNameChanged(_, _)).Times(0); |
| EXPECT_CALL(observer_, OnProfileAuthInfoChanged(_)).Times(0); |
| EXPECT_CALL(observer_, OnProfileAvatarChanged(_)).Times(0); |
| EXPECT_CALL(observer_, OnProfileHighResAvatarLoaded(_)).Times(0); |
| EXPECT_CALL(observer_, OnProfileSigninRequiredChanged(_)).Times(0); |
| EXPECT_CALL(observer_, OnProfileSupervisedUserIdChanged(_)).Times(0); |
| EXPECT_CALL(observer_, OnProfileIsOmittedChanged(_)).Times(0); |
| } |
| |
| void EnableObserver() { storage()->AddObserver(&observer_); } |
| void DisableObserver() { storage()->RemoveObserver(&observer_); } |
| |
| void AddCallExpectationsForRemoveProfile(size_t profile_number) { |
| base::FilePath profile_path = GetProfilePath( |
| base::StringPrintf("testing_profile_path%" PRIuS, profile_number)); |
| base::string16 profile_name = base::ASCIIToUTF16( |
| base::StringPrintf("testing_profile_name%" PRIuS, profile_number)); |
| |
| ::testing::InSequence dummy; |
| EXPECT_CALL(observer(), OnProfileWillBeRemoved(profile_path)).Times(1); |
| EXPECT_CALL(observer(), OnProfileWasRemoved(profile_path, profile_name)) |
| .Times(1); |
| } |
| |
| ProfileAttributesStorage* storage() { |
| return profile_info_cache(); |
| } |
| |
| ProfileInfoCache* profile_info_cache() { |
| return testing_profile_manager_.profile_info_cache(); |
| } |
| |
| ProfileAttributesTestObserver& observer() { return observer_; } |
| |
| void AddTestingProfile() { |
| size_t number_of_profiles = storage()->GetNumberOfProfiles(); |
| |
| base::FilePath profile_path = GetProfilePath( |
| base::StringPrintf("testing_profile_path%" PRIuS, number_of_profiles)); |
| |
| EXPECT_CALL(observer(), OnProfileAdded(profile_path)) |
| .Times(1) |
| .RetiresOnSaturation(); |
| |
| storage()->AddProfile( |
| profile_path, |
| base::ASCIIToUTF16(base::StringPrintf("testing_profile_name%" PRIuS, |
| number_of_profiles)), |
| base::StringPrintf("testing_profile_gaia%" PRIuS, number_of_profiles), |
| base::ASCIIToUTF16(base::StringPrintf("testing_profile_user%" PRIuS, |
| number_of_profiles)), |
| number_of_profiles, std::string(""), EmptyAccountId()); |
| |
| EXPECT_EQ(number_of_profiles + 1, storage()->GetNumberOfProfiles()); |
| } |
| |
| TestingProfileManager testing_profile_manager_; |
| |
| private: |
| content::TestBrowserThreadBundle thread_bundle_; |
| ProfileAttributesTestObserver observer_; |
| }; |
| |
| TEST_F(ProfileAttributesStorageTest, ProfileNotFound) { |
| EXPECT_EQ(0U, storage()->GetNumberOfProfiles()); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_FALSE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| |
| AddTestingProfile(); |
| EXPECT_EQ(1U, storage()->GetNumberOfProfiles()); |
| |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| ASSERT_FALSE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path1"), &entry)); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, AddProfile) { |
| EXPECT_EQ(0U, storage()->GetNumberOfProfiles()); |
| |
| EXPECT_CALL(observer(), OnProfileAdded(GetProfilePath("new_profile_path_1"))) |
| .Times(1); |
| |
| storage()->AddProfile(GetProfilePath("new_profile_path_1"), |
| base::ASCIIToUTF16("new_profile_name_1"), |
| std::string("new_profile_gaia_1"), |
| base::ASCIIToUTF16("new_profile_username_1"), 1, |
| std::string(""), EmptyAccountId()); |
| |
| VerifyAndResetCallExpectations(); |
| EXPECT_EQ(1U, storage()->GetNumberOfProfiles()); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("new_profile_path_1"), &entry)); |
| EXPECT_EQ(base::ASCIIToUTF16("new_profile_name_1"), entry->GetName()); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, RemoveProfile) { |
| EXPECT_EQ(0U, storage()->GetNumberOfProfiles()); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_FALSE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| |
| AddTestingProfile(); |
| EXPECT_EQ(1U, storage()->GetNumberOfProfiles()); |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| EXPECT_EQ(base::ASCIIToUTF16("testing_profile_name0"), entry->GetName()); |
| |
| // Deleting an existing profile. This should call observers and make the entry |
| // un-retrievable. |
| AddCallExpectationsForRemoveProfile(0); |
| storage()->RemoveProfile(GetProfilePath("testing_profile_path0")); |
| VerifyAndResetCallExpectations(); |
| EXPECT_EQ(0U, storage()->GetNumberOfProfiles()); |
| EXPECT_FALSE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, MultipleProfiles) { |
| EXPECT_EQ(0U, storage()->GetNumberOfProfiles()); |
| |
| for (size_t i = 0; i < 5; ++i) { |
| AddTestingProfile(); |
| EXPECT_EQ(i + 1, storage()->GetNumberOfProfiles()); |
| EXPECT_EQ(i + 1, storage()->GetAllProfilesAttributes().size()); |
| EXPECT_EQ(i + 1, storage()->GetAllProfilesAttributesSortedByName().size()); |
| } |
| |
| EXPECT_EQ(5U, storage()->GetNumberOfProfiles()); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| EXPECT_EQ(base::ASCIIToUTF16("testing_profile_name0"), entry->GetName()); |
| |
| AddCallExpectationsForRemoveProfile(0); |
| storage()->RemoveProfile(GetProfilePath("testing_profile_path0")); |
| VerifyAndResetCallExpectations(); |
| ASSERT_FALSE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| EXPECT_EQ(4U, storage()->GetNumberOfProfiles()); |
| |
| std::vector<ProfileAttributesEntry*> entries = |
| storage()->GetAllProfilesAttributes(); |
| for (auto* entry : entries) { |
| EXPECT_NE(GetProfilePath("testing_profile_path0"), entry->GetPath()); |
| } |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, InitialValues) { |
| AddTestingProfile(); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| EXPECT_EQ(GetProfilePath("testing_profile_path0"), entry->GetPath()); |
| EXPECT_EQ(base::ASCIIToUTF16("testing_profile_name0"), entry->GetName()); |
| EXPECT_EQ(std::string("testing_profile_gaia0"), entry->GetGAIAId()); |
| EXPECT_EQ(base::ASCIIToUTF16("testing_profile_user0"), entry->GetUserName()); |
| EXPECT_EQ(0U, entry->GetAvatarIconIndex()); |
| EXPECT_EQ(std::string(""), entry->GetSupervisedUserId()); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, EntryAccessors) { |
| AddTestingProfile(); |
| |
| base::FilePath path = GetProfilePath("testing_profile_path0"); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath(path, &entry)); |
| EXPECT_EQ(path, entry->GetPath()); |
| |
| EXPECT_CALL(observer(), OnProfileNameChanged(path, _)).Times(2); |
| TEST_STRING16_ACCESSORS(ProfileAttributesEntry, entry, Name); |
| VerifyAndResetCallExpectations(); |
| |
| TEST_STRING16_ACCESSORS(ProfileAttributesEntry, entry, ShortcutName); |
| TEST_STRING_ACCESSORS(ProfileAttributesEntry, entry, LocalAuthCredentials); |
| TEST_STRING_ACCESSORS( |
| ProfileAttributesEntry, entry, PasswordChangeDetectionToken); |
| TEST_ACCESSORS(ProfileAttributesEntry, entry, BackgroundStatus, true, false); |
| |
| TEST_STRING16_ACCESSORS(ProfileAttributesEntry, entry, GAIAName); |
| TEST_STRING16_ACCESSORS(ProfileAttributesEntry, entry, GAIAGivenName); |
| |
| EXPECT_CALL(observer(), OnProfileAvatarChanged(path)).Times(2); |
| TEST_BOOL_ACCESSORS(ProfileAttributesEntry, entry, IsUsingGAIAPicture); |
| VerifyAndResetCallExpectations(); |
| |
| EXPECT_CALL(observer(), OnProfileIsOmittedChanged(path)).Times(2); |
| TEST_BOOL_ACCESSORS(ProfileAttributesEntry, entry, IsOmitted); |
| VerifyAndResetCallExpectations(); |
| |
| TEST_BOOL_ACCESSORS(ProfileAttributesEntry, entry, IsEphemeral); |
| |
| EXPECT_CALL(observer(), OnProfileNameChanged(path, _)).Times(2); |
| TEST_BOOL_ACCESSORS(ProfileAttributesEntry, entry, IsUsingDefaultName); |
| VerifyAndResetCallExpectations(); |
| |
| TEST_BOOL_ACCESSORS(ProfileAttributesEntry, entry, IsUsingDefaultAvatar); |
| TEST_BOOL_ACCESSORS(ProfileAttributesEntry, entry, IsAuthError); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, EntryInternalAccessors) { |
| AddTestingProfile(); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| |
| EXPECT_EQ(GetProfilePath("testing_profile_path0"), entry->GetPath()); |
| |
| const char key[] = "test"; |
| |
| // Tests whether the accessors store and retrieve values correctly. |
| EXPECT_TRUE(entry->SetString(key, std::string("abcd"))); |
| ASSERT_TRUE(entry->GetValue(key)); |
| EXPECT_EQ(base::Value::Type::STRING, entry->GetValue(key)->type()); |
| EXPECT_EQ(std::string("abcd"), entry->GetString(key)); |
| EXPECT_EQ(base::UTF8ToUTF16("abcd"), entry->GetString16(key)); |
| EXPECT_EQ(0.0, entry->GetDouble(key)); |
| EXPECT_FALSE(entry->GetBool(key)); |
| EXPECT_FALSE(entry->IsDouble(key)); |
| |
| EXPECT_TRUE(entry->SetString16(key, base::UTF8ToUTF16("efgh"))); |
| ASSERT_TRUE(entry->GetValue(key)); |
| EXPECT_EQ(base::Value::Type::STRING, entry->GetValue(key)->type()); |
| EXPECT_EQ(std::string("efgh"), entry->GetString(key)); |
| EXPECT_EQ(base::UTF8ToUTF16("efgh"), entry->GetString16(key)); |
| EXPECT_EQ(0.0, entry->GetDouble(key)); |
| EXPECT_FALSE(entry->GetBool(key)); |
| EXPECT_FALSE(entry->IsDouble(key)); |
| |
| EXPECT_TRUE(entry->SetDouble(key, 12.5)); |
| ASSERT_TRUE(entry->GetValue(key)); |
| EXPECT_EQ(base::Value::Type::DOUBLE, entry->GetValue(key)->type()); |
| EXPECT_EQ(std::string(), entry->GetString(key)); |
| EXPECT_EQ(base::UTF8ToUTF16(""), entry->GetString16(key)); |
| EXPECT_EQ(12.5, entry->GetDouble(key)); |
| EXPECT_FALSE(entry->GetBool(key)); |
| EXPECT_TRUE(entry->IsDouble(key)); |
| |
| EXPECT_TRUE(entry->SetBool(key, true)); |
| ASSERT_TRUE(entry->GetValue(key)); |
| EXPECT_EQ(base::Value::Type::BOOLEAN, entry->GetValue(key)->type()); |
| EXPECT_EQ(std::string(), entry->GetString(key)); |
| EXPECT_EQ(base::UTF8ToUTF16(""), entry->GetString16(key)); |
| EXPECT_EQ(0.0, entry->GetDouble(key)); |
| EXPECT_TRUE(entry->GetBool(key)); |
| EXPECT_FALSE(entry->IsDouble(key)); |
| |
| // Test whether the setters returns correctly. Setters should return true if |
| // the previously stored value is different from the new value. |
| entry->SetBool(key, true); |
| EXPECT_TRUE(entry->SetString(key, std::string("abcd"))); |
| EXPECT_FALSE(entry->SetString(key, std::string("abcd"))); |
| EXPECT_FALSE(entry->SetString16(key, base::UTF8ToUTF16("abcd"))); |
| |
| EXPECT_TRUE(entry->SetString16(key, base::UTF8ToUTF16("efgh"))); |
| EXPECT_FALSE(entry->SetString16(key, base::UTF8ToUTF16("efgh"))); |
| EXPECT_FALSE(entry->SetString(key, std::string("efgh"))); |
| |
| EXPECT_TRUE(entry->SetDouble(key, 12.5)); |
| EXPECT_FALSE(entry->SetDouble(key, 12.5)); |
| EXPECT_TRUE(entry->SetDouble(key, 15.0)); |
| |
| EXPECT_TRUE(entry->SetString(key, std::string("abcd"))); |
| |
| EXPECT_TRUE(entry->SetBool(key, true)); |
| EXPECT_FALSE(entry->SetBool(key, true)); |
| EXPECT_TRUE(entry->SetBool(key, false)); |
| |
| EXPECT_TRUE(entry->SetString16(key, base::UTF8ToUTF16("efgh"))); |
| |
| // If previous data is not there, setters should returns true even if the |
| // defaults (empty string, 0.0, or false) are written. |
| EXPECT_TRUE(entry->SetString("test1", std::string())); |
| EXPECT_TRUE(entry->SetString16("test2", base::string16())); |
| EXPECT_TRUE(entry->SetDouble("test3", 0.0)); |
| EXPECT_TRUE(entry->SetBool("test4", false)); |
| |
| // If previous data is in a wrong type, setters should returns true even if |
| // the defaults (empty string, 0.0, or false) are written. |
| EXPECT_TRUE(entry->SetString("test3", std::string())); |
| EXPECT_TRUE(entry->SetString16("test4", base::string16())); |
| EXPECT_TRUE(entry->SetDouble("test1", 0.0)); |
| EXPECT_TRUE(entry->SetBool("test2", false)); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, ProfileActiveTime) { |
| AddTestingProfile(); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| |
| // Check the state before active time is stored. |
| const char kActiveTimeKey[] = "active_time"; |
| EXPECT_FALSE(entry->IsDouble(kActiveTimeKey)); |
| EXPECT_EQ(base::Time(), entry->GetActiveTime()); |
| |
| // Store the time and check for the result. Allow for a difference one second |
| // because the 64-bit integral representation in base::Time is rounded off to |
| // a double, which is what base::Value stores. http://crbug.com/346827 |
| base::Time lower_bound = base::Time::Now() - base::TimeDelta::FromSeconds(1); |
| entry->SetActiveTimeToNow(); |
| base::Time upper_bound = base::Time::Now() + base::TimeDelta::FromSeconds(1); |
| EXPECT_TRUE(entry->IsDouble(kActiveTimeKey)); |
| EXPECT_LE(lower_bound, entry->GetActiveTime()); |
| EXPECT_GE(upper_bound, entry->GetActiveTime()); |
| |
| // If the active time was less than one hour ago, SetActiveTimeToNow should do |
| // nothing. |
| base::Time past = base::Time::Now() - base::TimeDelta::FromMinutes(10); |
| lower_bound = past - base::TimeDelta::FromSeconds(1); |
| upper_bound = past + base::TimeDelta::FromSeconds(1); |
| ASSERT_TRUE(entry->SetDouble(kActiveTimeKey, past.ToDoubleT())); |
| base::Time stored_time = entry->GetActiveTime(); |
| ASSERT_LE(lower_bound, stored_time); |
| ASSERT_GE(upper_bound, stored_time); |
| entry->SetActiveTimeToNow(); |
| EXPECT_EQ(stored_time, entry->GetActiveTime()); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, AuthInfo) { |
| AddTestingProfile(); |
| |
| base::FilePath path = GetProfilePath("testing_profile_path0"); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath(path, &entry)); |
| |
| EXPECT_CALL(observer(), OnProfileAuthInfoChanged(path)).Times(1); |
| entry->SetAuthInfo("", base::string16()); |
| VerifyAndResetCallExpectations(); |
| ASSERT_FALSE(entry->IsAuthenticated()); |
| EXPECT_EQ(base::string16(), entry->GetUserName()); |
| EXPECT_EQ("", entry->GetGAIAId()); |
| |
| EXPECT_CALL(observer(), OnProfileAuthInfoChanged(path)).Times(1); |
| entry->SetAuthInfo("foo", base::ASCIIToUTF16("bar")); |
| VerifyAndResetCallExpectations(); |
| ASSERT_TRUE(entry->IsAuthenticated()); |
| EXPECT_EQ(base::ASCIIToUTF16("bar"), entry->GetUserName()); |
| EXPECT_EQ("foo", entry->GetGAIAId()); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, SupervisedUsersAccessors) { |
| AddTestingProfile(); |
| |
| base::FilePath path = GetProfilePath("testing_profile_path0"); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &entry)); |
| |
| entry->SetSupervisedUserId(""); |
| ASSERT_FALSE(entry->IsSupervised()); |
| ASSERT_FALSE(entry->IsChild()); |
| ASSERT_FALSE(entry->IsLegacySupervised()); |
| |
| EXPECT_CALL(observer(), OnProfileSupervisedUserIdChanged(path)).Times(1); |
| entry->SetSupervisedUserId("some_supervised_user_id"); |
| VerifyAndResetCallExpectations(); |
| ASSERT_TRUE(entry->IsSupervised()); |
| ASSERT_FALSE(entry->IsChild()); |
| ASSERT_TRUE(entry->IsLegacySupervised()); |
| |
| EXPECT_CALL(observer(), OnProfileSupervisedUserIdChanged(path)).Times(1); |
| entry->SetSupervisedUserId(supervised_users::kChildAccountSUID); |
| VerifyAndResetCallExpectations(); |
| ASSERT_TRUE(entry->IsSupervised()); |
| ASSERT_TRUE(entry->IsChild()); |
| ASSERT_FALSE(entry->IsLegacySupervised()); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, ReSortTriggered) { |
| DisableObserver(); // No need to test observers in this test. |
| |
| storage()->AddProfile(GetProfilePath("alpha_path"), |
| base::ASCIIToUTF16("alpha"), std::string("alpha_gaia"), |
| base::ASCIIToUTF16("alpha_username"), 1, |
| std::string(""), EmptyAccountId()); |
| |
| storage()->AddProfile(GetProfilePath("lima_path"), base::ASCIIToUTF16("lima"), |
| std::string("lima_gaia"), |
| base::ASCIIToUTF16("lima_username"), 1, std::string(""), |
| EmptyAccountId()); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("alpha_path"), &entry)); |
| |
| // Trigger a ProfileInfoCache re-sort. |
| entry->SetName(base::ASCIIToUTF16("zulu_name")); |
| EXPECT_EQ(GetProfilePath("alpha_path"), entry->GetPath()); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, RemoveOtherProfile) { |
| AddTestingProfile(); |
| AddTestingProfile(); |
| AddTestingProfile(); |
| |
| EXPECT_EQ(3U, storage()->GetNumberOfProfiles()); |
| |
| ProfileAttributesEntry* first_entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &first_entry)); |
| |
| ProfileAttributesEntry* second_entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path1"), &second_entry)); |
| |
| EXPECT_EQ( |
| base::ASCIIToUTF16("testing_profile_name0"), first_entry->GetName()); |
| |
| AddCallExpectationsForRemoveProfile(1); |
| storage()->RemoveProfile(GetProfilePath("testing_profile_path1")); |
| VerifyAndResetCallExpectations(); |
| ASSERT_FALSE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path1"), &second_entry)); |
| |
| EXPECT_EQ(GetProfilePath("testing_profile_path0"), first_entry->GetPath()); |
| EXPECT_EQ( |
| base::ASCIIToUTF16("testing_profile_name0"), first_entry->GetName()); |
| |
| // Deleting through the ProfileInfoCache should be reflected in the |
| // ProfileAttributesStorage as well. |
| AddCallExpectationsForRemoveProfile(2); |
| profile_info_cache()->RemoveProfile( |
| GetProfilePath("testing_profile_path2")); |
| VerifyAndResetCallExpectations(); |
| ASSERT_FALSE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path2"), &second_entry)); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, AccessFromElsewhere) { |
| AddTestingProfile(); |
| |
| DisableObserver(); // No need to test observers in this test. |
| |
| ProfileAttributesEntry* first_entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &first_entry)); |
| |
| ProfileAttributesEntry* second_entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath( |
| GetProfilePath("testing_profile_path0"), &second_entry)); |
| |
| first_entry->SetName(base::ASCIIToUTF16("NewName")); |
| EXPECT_EQ(base::ASCIIToUTF16("NewName"), second_entry->GetName()); |
| EXPECT_EQ(first_entry, second_entry); |
| |
| // The ProfileInfoCache should also reflect the changes and its changes |
| // should be reflected by the ProfileAttributesStorage. |
| size_t index = profile_info_cache()->GetIndexOfProfileWithPath( |
| GetProfilePath("testing_profile_path0")); |
| EXPECT_EQ(base::ASCIIToUTF16("NewName"), |
| profile_info_cache()->GetNameOfProfileAtIndex(index)); |
| |
| profile_info_cache()->SetNameOfProfileAtIndex( |
| index, base::ASCIIToUTF16("OtherNewName")); |
| EXPECT_EQ(base::ASCIIToUTF16("OtherNewName"), first_entry->GetName()); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, ChooseAvatarIconIndexForNewProfile) { |
| size_t total_icon_count = profiles::GetDefaultAvatarIconCount(); |
| #if defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| size_t generic_icon_count = profiles::GetGenericAvatarIconCount(); |
| ASSERT_LE(generic_icon_count, total_icon_count); |
| #endif |
| |
| // Run ChooseAvatarIconIndexForNewProfile |num_iterations| times before using |
| // the final |icon_index| to add a profile. Multiple checks are needed because |
| // ChooseAvatarIconIndexForNewProfile is non-deterministic. |
| const int num_iterations = 10; |
| std::unordered_set<int> used_icon_indices; |
| |
| for (size_t i = 0; i < total_icon_count; ++i) { |
| EXPECT_EQ(i, storage()->GetNumberOfProfiles()); |
| |
| size_t icon_index = 0; |
| for (int iter = 0; iter < num_iterations; ++iter) { |
| icon_index = storage()->ChooseAvatarIconIndexForNewProfile(); |
| // Icon must not be used. |
| ASSERT_EQ(0u, used_icon_indices.count(icon_index)); |
| ASSERT_GT(total_icon_count, icon_index); |
| |
| #if defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| if (i < total_icon_count - generic_icon_count) |
| ASSERT_LE(generic_icon_count, icon_index); |
| else |
| ASSERT_GT(generic_icon_count, icon_index); |
| #endif |
| } |
| |
| used_icon_indices.insert(icon_index); |
| |
| base::FilePath profile_path = |
| GetProfilePath(base::StringPrintf("testing_profile_path%" PRIuS, i)); |
| EXPECT_CALL(observer(), OnProfileAdded(profile_path)).Times(1); |
| storage()->AddProfile(profile_path, base::string16(), std::string(), |
| base::string16(), icon_index, std::string(), |
| EmptyAccountId()); |
| VerifyAndResetCallExpectations(); |
| } |
| |
| for (int iter = 0; iter < num_iterations; ++iter) { |
| // All icons are used up, expect any valid icon. |
| ASSERT_GT(total_icon_count, |
| storage()->ChooseAvatarIconIndexForNewProfile()); |
| } |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, ProfileForceSigninLock) { |
| signin_util::SetForceSigninForTesting(true); |
| |
| AddTestingProfile(); |
| |
| base::FilePath path = GetProfilePath("testing_profile_path0"); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath(path, &entry)); |
| ASSERT_FALSE(entry->IsSigninRequired()); |
| |
| entry->LockForceSigninProfile(false); |
| ASSERT_FALSE(entry->IsSigninRequired()); |
| |
| EXPECT_CALL(observer(), OnProfileSigninRequiredChanged(path)).Times(1); |
| entry->LockForceSigninProfile(true); |
| VerifyAndResetCallExpectations(); |
| ASSERT_TRUE(entry->IsSigninRequired()); |
| |
| EXPECT_CALL(observer(), OnProfileSigninRequiredChanged(path)).Times(1); |
| entry->SetIsSigninRequired(false); |
| VerifyAndResetCallExpectations(); |
| ASSERT_FALSE(entry->IsSigninRequired()); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, AvatarIconIndex) { |
| AddTestingProfile(); |
| |
| base::FilePath profile_path = GetProfilePath("testing_profile_path0"); |
| |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath(profile_path, &entry)); |
| ASSERT_EQ(0U, entry->GetAvatarIconIndex()); |
| |
| EXPECT_CALL(observer(), OnProfileAvatarChanged(profile_path)).Times(1); |
| entry->SetAvatarIconIndex(2U); |
| VerifyAndResetCallExpectations(); |
| ASSERT_EQ(2U, entry->GetAvatarIconIndex()); |
| |
| EXPECT_CALL(observer(), OnProfileAvatarChanged(profile_path)).Times(1); |
| entry->SetAvatarIconIndex(3U); |
| VerifyAndResetCallExpectations(); |
| ASSERT_EQ(3U, entry->GetAvatarIconIndex()); |
| } |
| |
| // High res avatar downloading is only supported on desktop. |
| #if !defined(OS_ANDROID) && !defined(OS_CHROMEOS) |
| TEST_F(ProfileAttributesStorageTest, DownloadHighResAvatarTest) { |
| storage()->set_disable_avatar_download_for_testing(false); |
| |
| const size_t kIconIndex = 0; |
| base::FilePath icon_path = |
| profiles::GetPathOfHighResAvatarAtIndex(kIconIndex); |
| |
| ASSERT_EQ(0U, storage()->GetNumberOfProfiles()); |
| base::FilePath profile_path = GetProfilePath("path_1"); |
| EXPECT_CALL(observer(), OnProfileAdded(profile_path)).Times(1); |
| storage()->AddProfile(profile_path, base::ASCIIToUTF16("name_1"), |
| std::string(), base::string16(), kIconIndex, |
| std::string(), EmptyAccountId()); |
| ASSERT_EQ(1U, storage()->GetNumberOfProfiles()); |
| VerifyAndResetCallExpectations(); |
| |
| // Make sure there are no avatars already on disk. |
| content::RunAllTasksUntilIdle(); |
| ASSERT_FALSE(base::PathExists(icon_path)); |
| |
| // We haven't downloaded any high-res avatars yet. |
| EXPECT_EQ(0U, storage()->cached_avatar_images_.size()); |
| |
| // After adding a new profile, the download of high-res avatar will be |
| // triggered. But the downloader won't ever call OnFetchComplete in the test. |
| EXPECT_EQ(1U, storage()->avatar_images_downloads_in_progress_.size()); |
| |
| // |GetHighResAvater| does not contain a cached avatar, so it should return |
| // null. |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath(profile_path, &entry)); |
| EXPECT_FALSE(entry->GetHighResAvatar()); |
| |
| // The previous |GetHighResAvater| starts |LoadAvatarPictureFromPath| async. |
| // The async code will end up at |OnAvatarPictureLoaded| storing an empty |
| // image in the cache. |
| EXPECT_CALL(observer(), OnProfileHighResAvatarLoaded(profile_path)).Times(1); |
| content::RunAllTasksUntilIdle(); |
| VerifyAndResetCallExpectations(); |
| std::string icon_filename = |
| profiles::GetDefaultAvatarIconFileNameAtIndex(kIconIndex); |
| EXPECT_EQ(1U, storage()->cached_avatar_images_.size()); |
| EXPECT_TRUE(storage()->cached_avatar_images_[icon_filename]->IsEmpty()); |
| |
| // Simulate downloading a high-res avatar. |
| ProfileAvatarDownloader avatar_downloader( |
| kIconIndex, base::Bind(&ProfileAttributesStorage::SaveAvatarImageAtPath, |
| base::Unretained(storage()), entry->GetPath())); |
| |
| // Put a real bitmap into "bitmap": a 2x2 bitmap of green 32 bit pixels. |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(2, 2); |
| bitmap.eraseColor(SK_ColorGREEN); |
| |
| avatar_downloader.OnFetchComplete(GURL("http://www.google.com/avatar.png"), |
| &bitmap); |
| |
| // Now the download should not be in progress anymore. |
| EXPECT_EQ(0U, storage()->avatar_images_downloads_in_progress_.size()); |
| |
| // The image should have been cached. |
| EXPECT_EQ(1U, storage()->cached_avatar_images_.size()); |
| EXPECT_FALSE(storage()->cached_avatar_images_[icon_filename]->IsEmpty()); |
| EXPECT_EQ(storage()->cached_avatar_images_[icon_filename].get(), |
| entry->GetHighResAvatar()); |
| |
| // Since we are not using GAIA image, |GetAvatarIcon| should return the same |
| // image as |GetHighResAvatar| in desktop. |
| EXPECT_EQ(storage()->cached_avatar_images_[icon_filename].get(), |
| &entry->GetAvatarIcon()); |
| |
| // Finish the async calls that save the image to the disk. |
| EXPECT_CALL(observer(), OnProfileHighResAvatarLoaded(profile_path)).Times(1); |
| content::RunAllTasksUntilIdle(); |
| VerifyAndResetCallExpectations(); |
| |
| // Clean up. |
| EXPECT_NE(std::string::npos, icon_path.MaybeAsASCII().find(icon_filename)); |
| ASSERT_TRUE(base::PathExists(icon_path)); |
| EXPECT_TRUE(base::DeleteFile(icon_path, false)); |
| EXPECT_FALSE(base::PathExists(icon_path)); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, NothingToDownloadHighResAvatarTest) { |
| storage()->set_disable_avatar_download_for_testing(false); |
| |
| const size_t kIconIndex = profiles::GetPlaceholderAvatarIndex(); |
| |
| EXPECT_EQ(0U, storage()->GetNumberOfProfiles()); |
| base::FilePath profile_path = GetProfilePath("path_1"); |
| EXPECT_CALL(observer(), OnProfileAdded(profile_path)).Times(1); |
| storage()->AddProfile(profile_path, base::ASCIIToUTF16("name_1"), |
| std::string(), base::string16(), kIconIndex, |
| std::string(), EmptyAccountId()); |
| EXPECT_EQ(1U, storage()->GetNumberOfProfiles()); |
| content::RunAllTasksUntilIdle(); |
| |
| // We haven't tried to download any high-res avatars as the specified icon is |
| // just a placeholder. |
| EXPECT_EQ(0U, storage()->cached_avatar_images_.size()); |
| EXPECT_EQ(0U, storage()->avatar_images_downloads_in_progress_.size()); |
| } |
| |
| TEST_F(ProfileAttributesStorageTest, LoadAvatarFromDiskTest) { |
| const size_t kIconIndex = 0; |
| base::FilePath icon_path = |
| profiles::GetPathOfHighResAvatarAtIndex(kIconIndex); |
| |
| // Create the avatar on the disk, which is a valid 1x1 transparent png. |
| base::FilePath dir = icon_path.DirName(); |
| ASSERT_FALSE(base::DirectoryExists(dir)); |
| ASSERT_TRUE(base::CreateDirectory(dir)); |
| ASSERT_FALSE(base::PathExists(icon_path)); |
| const char bitmap[] = |
| "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52" |
| "\x00\x00\x00\x01\x00\x00\x00\x01\x01\x00\x00\x00\x00\x37\x6E\xF9" |
| "\x24\x00\x00\x00\x0A\x49\x44\x41\x54\x08\x1D\x63\x60\x00\x00\x00" |
| "\x02\x00\x01\xCF\xC8\x35\xE5\x00\x00\x00\x00\x49\x45\x4E\x44\xAE" |
| "\x42\x60\x82"; |
| base::WriteFile(icon_path, bitmap, sizeof(bitmap)); |
| ASSERT_TRUE(base::PathExists(icon_path)); |
| |
| // Add a new profile. |
| ASSERT_EQ(0U, storage()->GetNumberOfProfiles()); |
| base::FilePath profile_path = GetProfilePath("path_1"); |
| EXPECT_CALL(observer(), OnProfileAdded(profile_path)).Times(1); |
| storage()->AddProfile(profile_path, base::ASCIIToUTF16("name_1"), |
| std::string(), base::string16(), kIconIndex, |
| std::string(), EmptyAccountId()); |
| EXPECT_EQ(1U, storage()->GetNumberOfProfiles()); |
| VerifyAndResetCallExpectations(); |
| |
| // Load the avatar image. |
| storage()->set_disable_avatar_download_for_testing(false); |
| ProfileAttributesEntry* entry; |
| ASSERT_TRUE(storage()->GetProfileAttributesWithPath(profile_path, &entry)); |
| ASSERT_FALSE(entry->IsUsingGAIAPicture()); |
| EXPECT_CALL(observer(), OnProfileHighResAvatarLoaded(profile_path)).Times(1); |
| entry->GetAvatarIcon(); |
| |
| // Wait until the avatar image finish loading. |
| content::RunAllTasksUntilIdle(); |
| VerifyAndResetCallExpectations(); |
| |
| // Clean up. |
| EXPECT_TRUE(base::DeleteFile(icon_path, false)); |
| EXPECT_FALSE(base::PathExists(icon_path)); |
| } |
| #endif |