| // 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 "components/password_manager/core/browser/android_affiliation/affiliated_match_helper.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/scoped_mock_time_message_loop_task_runner.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "components/password_manager/core/browser/android_affiliation/affiliation_service.h" |
| #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h" |
| #include "components/password_manager/core/browser/test_password_store.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace password_manager { |
| |
| namespace { |
| |
| using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss; |
| |
| class MockAffiliationService : public testing::StrictMock<AffiliationService> { |
| public: |
| MockAffiliationService() : testing::StrictMock<AffiliationService>(nullptr) { |
| testing::DefaultValue<AffiliatedFacets>::Set(AffiliatedFacets()); |
| } |
| |
| ~MockAffiliationService() override {} |
| |
| MOCK_METHOD2(OnGetAffiliationsAndBrandingCalled, |
| AffiliatedFacets(const FacetURI&, StrategyOnCacheMiss)); |
| MOCK_METHOD2(Prefetch, void(const FacetURI&, const base::Time&)); |
| MOCK_METHOD2(CancelPrefetch, void(const FacetURI&, const base::Time&)); |
| MOCK_METHOD1(TrimCacheForFacetURI, void(const FacetURI&)); |
| |
| void GetAffiliationsAndBranding( |
| const FacetURI& facet_uri, |
| StrategyOnCacheMiss cache_miss_strategy, |
| const ResultCallback& result_callback) override { |
| AffiliatedFacets affiliation = |
| OnGetAffiliationsAndBrandingCalled(facet_uri, cache_miss_strategy); |
| result_callback.Run(affiliation, !affiliation.empty()); |
| } |
| |
| void ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( |
| const FacetURI& expected_facet_uri, |
| StrategyOnCacheMiss expected_cache_miss_strategy, |
| const AffiliatedFacets& affiliations_to_return) { |
| EXPECT_CALL(*this, OnGetAffiliationsAndBrandingCalled( |
| expected_facet_uri, expected_cache_miss_strategy)) |
| .WillOnce(testing::Return(affiliations_to_return)); |
| } |
| |
| void ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure( |
| const FacetURI& expected_facet_uri, |
| StrategyOnCacheMiss expected_cache_miss_strategy) { |
| EXPECT_CALL(*this, OnGetAffiliationsAndBrandingCalled( |
| expected_facet_uri, expected_cache_miss_strategy)) |
| .WillOnce(testing::Return(AffiliatedFacets())); |
| } |
| |
| void ExpectCallToPrefetch(const char* expected_facet_uri_spec) { |
| EXPECT_CALL(*this, |
| Prefetch(FacetURI::FromCanonicalSpec(expected_facet_uri_spec), |
| base::Time::Max())) |
| .RetiresOnSaturation(); |
| } |
| |
| void ExpectCallToCancelPrefetch(const char* expected_facet_uri_spec) { |
| EXPECT_CALL(*this, CancelPrefetch( |
| FacetURI::FromCanonicalSpec(expected_facet_uri_spec), |
| base::Time::Max())) |
| .RetiresOnSaturation(); |
| } |
| |
| void ExpectCallToTrimCacheForFacetURI(const char* expected_facet_uri_spec) { |
| EXPECT_CALL(*this, TrimCacheForFacetURI(FacetURI::FromCanonicalSpec( |
| expected_facet_uri_spec))) |
| .RetiresOnSaturation(); |
| } |
| |
| private: |
| DISALLOW_ASSIGN(MockAffiliationService); |
| }; |
| |
| const char kTestWebFacetURIAlpha1[] = "https://one.alpha.example.com"; |
| const char kTestWebFacetURIAlpha2[] = "https://two.alpha.example.com"; |
| const char kTestAndroidFacetURIAlpha3[] = |
| "android://hash@com.example.alpha.android"; |
| const char kTestAndroidFacetNameAlpha3[] = "Facet Name Alpha 3"; |
| const char kTestAndroidFacetIconURLAlpha3[] = "https://example.com/alpha_3.png"; |
| const char kTestWebRealmAlpha1[] = "https://one.alpha.example.com/"; |
| const char kTestWebRealmAlpha2[] = "https://two.alpha.example.com/"; |
| const char kTestAndroidRealmAlpha3[] = |
| "android://hash@com.example.alpha.android/"; |
| |
| const char kTestWebFacetURIBeta1[] = "https://one.beta.example.com"; |
| const char kTestAndroidFacetURIBeta2[] = |
| "android://hash@com.example.beta.android"; |
| const char kTestAndroidFacetNameBeta2[] = "Facet Name Beta 2"; |
| const char kTestAndroidFacetIconURLBeta2[] = "https://example.com/beta_2.png"; |
| const char kTestAndroidFacetURIBeta3[] = |
| "android://hash@com.yetanother.beta.android"; |
| const char kTestAndroidFacetNameBeta3[] = "Facet Name Beta 3"; |
| const char kTestAndroidFacetIconURLBeta3[] = "https://example.com/beta_3.png"; |
| const char kTestWebRealmBeta1[] = "https://one.beta.example.com/"; |
| const char kTestAndroidRealmBeta2[] = |
| "android://hash@com.example.beta.android/"; |
| const char kTestAndroidRealmBeta3[] = |
| "android://hash@com.yetanother.beta.android/"; |
| |
| const char kTestAndroidFacetURIGamma[] = |
| "android://hash@com.example.gamma.android"; |
| const char kTestAndroidRealmGamma[] = |
| "android://hash@com.example.gamma.android"; |
| |
| const char kTestUsername[] = "JohnDoe"; |
| const char kTestPassword[] = "secret"; |
| |
| AffiliatedFacets GetTestEquivalenceClassAlpha() { |
| return { |
| {FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1)}, |
| {FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha2)}, |
| {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3), |
| FacetBrandingInfo{kTestAndroidFacetNameAlpha3, |
| GURL(kTestAndroidFacetIconURLAlpha3)}}, |
| }; |
| } |
| |
| AffiliatedFacets GetTestEquivalenceClassBeta() { |
| return { |
| {FacetURI::FromCanonicalSpec(kTestWebFacetURIBeta1)}, |
| {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2), |
| FacetBrandingInfo{kTestAndroidFacetNameBeta2, |
| GURL(kTestAndroidFacetIconURLBeta2)}}, |
| {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta3), |
| FacetBrandingInfo{kTestAndroidFacetNameBeta3, |
| GURL(kTestAndroidFacetIconURLBeta3)}}, |
| }; |
| } |
| |
| autofill::PasswordForm GetTestAndroidCredentials(const char* signon_realm) { |
| autofill::PasswordForm form; |
| form.scheme = autofill::PasswordForm::SCHEME_HTML; |
| form.signon_realm = signon_realm; |
| form.username_value = base::ASCIIToUTF16(kTestUsername); |
| form.password_value = base::ASCIIToUTF16(kTestPassword); |
| return form; |
| } |
| |
| autofill::PasswordForm GetTestBlacklistedAndroidCredentials( |
| const char* signon_realm) { |
| autofill::PasswordForm form = GetTestAndroidCredentials(signon_realm); |
| form.blacklisted_by_user = true; |
| return form; |
| } |
| |
| PasswordStore::FormDigest GetTestObservedWebForm(const char* signon_realm, |
| const char* origin) { |
| return {autofill::PasswordForm::SCHEME_HTML, signon_realm, |
| origin ? GURL(origin) : GURL()}; |
| } |
| |
| } // namespace |
| |
| class AffiliatedMatchHelperTest : public testing::Test { |
| public: |
| AffiliatedMatchHelperTest() |
| : expecting_result_callback_(false), mock_affiliation_service_(nullptr) {} |
| ~AffiliatedMatchHelperTest() override {} |
| |
| protected: |
| void RunDeferredInitialization() { |
| mock_time_task_runner_->RunUntilIdle(); |
| ASSERT_EQ(AffiliatedMatchHelper::kInitializationDelayOnStartup, |
| mock_time_task_runner_->NextPendingTaskDelay()); |
| mock_time_task_runner_->FastForwardBy( |
| AffiliatedMatchHelper::kInitializationDelayOnStartup); |
| } |
| |
| void ExpectNoDeferredTasks() { |
| mock_time_task_runner_->RunUntilIdle(); |
| ASSERT_FALSE(mock_time_task_runner_->HasPendingTask()); |
| } |
| |
| void RunUntilIdle() { |
| // TODO(gab): Add support for base::RunLoop().RunUntilIdle() in scope of |
| // ScopedMockTimeMessageLoopTaskRunner and use it instead of this helper |
| // method. |
| mock_time_task_runner_->RunUntilIdle(); |
| } |
| |
| void AddLogin(const autofill::PasswordForm& form) { |
| password_store_->AddLogin(form); |
| RunUntilIdle(); |
| } |
| |
| void UpdateLoginWithPrimaryKey( |
| const autofill::PasswordForm& new_form, |
| const autofill::PasswordForm& old_primary_key) { |
| password_store_->UpdateLoginWithPrimaryKey(new_form, old_primary_key); |
| RunUntilIdle(); |
| } |
| |
| void RemoveLogin(const autofill::PasswordForm& form) { |
| password_store_->RemoveLogin(form); |
| RunUntilIdle(); |
| } |
| |
| void AddAndroidAndNonAndroidTestLogins() { |
| AddLogin(GetTestAndroidCredentials(kTestAndroidRealmAlpha3)); |
| AddLogin(GetTestAndroidCredentials(kTestAndroidRealmBeta2)); |
| AddLogin(GetTestBlacklistedAndroidCredentials(kTestAndroidRealmBeta3)); |
| AddLogin(GetTestAndroidCredentials(kTestAndroidRealmGamma)); |
| |
| AddLogin(GetTestAndroidCredentials(kTestWebRealmAlpha1)); |
| AddLogin(GetTestAndroidCredentials(kTestWebRealmAlpha2)); |
| } |
| |
| void RemoveAndroidAndNonAndroidTestLogins() { |
| RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmAlpha3)); |
| RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmBeta2)); |
| RemoveLogin(GetTestBlacklistedAndroidCredentials(kTestAndroidRealmBeta3)); |
| RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmGamma)); |
| |
| RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha1)); |
| RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha2)); |
| } |
| |
| void ExpectPrefetchForAndroidTestLogins() { |
| mock_affiliation_service()->ExpectCallToPrefetch( |
| kTestAndroidFacetURIAlpha3); |
| mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta2); |
| mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta3); |
| mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIGamma); |
| } |
| |
| void ExpectCancelPrefetchForAndroidTestLogins() { |
| mock_affiliation_service()->ExpectCallToCancelPrefetch( |
| kTestAndroidFacetURIAlpha3); |
| mock_affiliation_service()->ExpectCallToCancelPrefetch( |
| kTestAndroidFacetURIBeta2); |
| mock_affiliation_service()->ExpectCallToCancelPrefetch( |
| kTestAndroidFacetURIBeta3); |
| mock_affiliation_service()->ExpectCallToCancelPrefetch( |
| kTestAndroidFacetURIGamma); |
| } |
| |
| void ExpectTrimCacheForAndroidTestLogins() { |
| mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI( |
| kTestAndroidFacetURIAlpha3); |
| mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI( |
| kTestAndroidFacetURIBeta2); |
| mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI( |
| kTestAndroidFacetURIBeta3); |
| mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI( |
| kTestAndroidFacetURIGamma); |
| } |
| |
| std::vector<std::string> GetAffiliatedAndroidRealms( |
| const PasswordStore::FormDigest& observed_form) { |
| expecting_result_callback_ = true; |
| match_helper()->GetAffiliatedAndroidRealms( |
| observed_form, |
| base::Bind(&AffiliatedMatchHelperTest::OnAffiliatedRealmsCallback, |
| base::Unretained(this))); |
| RunUntilIdle(); |
| EXPECT_FALSE(expecting_result_callback_); |
| return last_result_realms_; |
| } |
| |
| std::vector<std::string> GetAffiliatedWebRealms( |
| const PasswordStore::FormDigest& android_form) { |
| expecting_result_callback_ = true; |
| match_helper()->GetAffiliatedWebRealms( |
| android_form, |
| base::Bind(&AffiliatedMatchHelperTest::OnAffiliatedRealmsCallback, |
| base::Unretained(this))); |
| RunUntilIdle(); |
| EXPECT_FALSE(expecting_result_callback_); |
| return last_result_realms_; |
| } |
| |
| std::vector<std::unique_ptr<autofill::PasswordForm>> |
| InjectAffiliationAndBrandingInformation( |
| std::vector<std::unique_ptr<autofill::PasswordForm>> forms) { |
| expecting_result_callback_ = true; |
| match_helper()->InjectAffiliationAndBrandingInformation( |
| std::move(forms), |
| base::Bind(&AffiliatedMatchHelperTest::OnFormsCallback, |
| base::Unretained(this))); |
| RunUntilIdle(); |
| EXPECT_FALSE(expecting_result_callback_); |
| return std::move(last_result_forms_); |
| } |
| |
| void DestroyMatchHelper() { match_helper_.reset(); } |
| |
| TestPasswordStore* password_store() { return password_store_.get(); } |
| |
| MockAffiliationService* mock_affiliation_service() { |
| return mock_affiliation_service_; |
| } |
| |
| AffiliatedMatchHelper* match_helper() { return match_helper_.get(); } |
| |
| private: |
| void OnAffiliatedRealmsCallback( |
| const std::vector<std::string>& affiliated_realms) { |
| EXPECT_TRUE(expecting_result_callback_); |
| expecting_result_callback_ = false; |
| last_result_realms_ = affiliated_realms; |
| } |
| |
| void OnFormsCallback( |
| std::vector<std::unique_ptr<autofill::PasswordForm>> forms) { |
| EXPECT_TRUE(expecting_result_callback_); |
| expecting_result_callback_ = false; |
| last_result_forms_.swap(forms); |
| } |
| |
| // testing::Test: |
| void SetUp() override { |
| std::unique_ptr<MockAffiliationService> service( |
| new MockAffiliationService()); |
| mock_affiliation_service_ = service.get(); |
| |
| password_store_ = new TestPasswordStore; |
| password_store_->Init(syncer::SyncableService::StartSyncFlare(), nullptr); |
| |
| match_helper_.reset( |
| new AffiliatedMatchHelper(password_store_.get(), std::move(service))); |
| } |
| |
| void TearDown() override { |
| match_helper_.reset(); |
| password_store_->ShutdownOnUIThread(); |
| password_store_ = nullptr; |
| // Clean up on the background thread. |
| RunUntilIdle(); |
| } |
| |
| base::test::ScopedTaskEnvironment task_environment_; |
| base::ScopedMockTimeMessageLoopTaskRunner mock_time_task_runner_; |
| |
| std::vector<std::string> last_result_realms_; |
| std::vector<std::unique_ptr<autofill::PasswordForm>> last_result_forms_; |
| bool expecting_result_callback_; |
| |
| scoped_refptr<TestPasswordStore> password_store_; |
| std::unique_ptr<AffiliatedMatchHelper> match_helper_; |
| |
| // Owned by |match_helper_|. |
| MockAffiliationService* mock_affiliation_service_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AffiliatedMatchHelperTest); |
| }; |
| |
| // GetAffiliatedAndroidRealm* tests verify that GetAffiliatedAndroidRealms() |
| // returns the realms of affiliated Android applications, but only Android |
| // applications, and only if the observed form is a secure HTML login form. |
| |
| TEST_F(AffiliatedMatchHelperTest, GetAffiliatedAndroidRealmsYieldsResults) { |
| mock_affiliation_service() |
| ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( |
| FacetURI::FromCanonicalSpec(kTestWebFacetURIBeta1), |
| StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta()); |
| EXPECT_THAT(GetAffiliatedAndroidRealms( |
| GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)), |
| testing::UnorderedElementsAre(kTestAndroidRealmBeta2, |
| kTestAndroidRealmBeta3)); |
| } |
| |
| TEST_F(AffiliatedMatchHelperTest, |
| GetAffiliatedAndroidRealmsYieldsOnlyAndroidApps) { |
| mock_affiliation_service() |
| ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( |
| FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1), |
| StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha()); |
| // This verifies that |kTestWebRealmAlpha2| is not returned. |
| EXPECT_THAT(GetAffiliatedAndroidRealms( |
| GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr)), |
| testing::UnorderedElementsAre(kTestAndroidRealmAlpha3)); |
| } |
| |
| TEST_F(AffiliatedMatchHelperTest, |
| GetAffiliatedAndroidRealmsYieldsEmptyResultsForHTTPBasicAuthForms) { |
| PasswordStore::FormDigest http_auth_observed_form( |
| GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr)); |
| http_auth_observed_form.scheme = autofill::PasswordForm::SCHEME_BASIC; |
| EXPECT_THAT(GetAffiliatedAndroidRealms(http_auth_observed_form), |
| testing::IsEmpty()); |
| } |
| |
| TEST_F(AffiliatedMatchHelperTest, |
| GetAffiliatedAndroidRealmsYieldsEmptyResultsForHTTPDigestAuthForms) { |
| PasswordStore::FormDigest http_auth_observed_form( |
| GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr)); |
| http_auth_observed_form.scheme = autofill::PasswordForm::SCHEME_DIGEST; |
| EXPECT_THAT(GetAffiliatedAndroidRealms(http_auth_observed_form), |
| testing::IsEmpty()); |
| } |
| |
| TEST_F(AffiliatedMatchHelperTest, |
| GetAffiliatedAndroidRealmsYieldsEmptyResultsForAndroidKeyedForms) { |
| PasswordStore::FormDigest android_observed_form( |
| GetTestAndroidCredentials(kTestAndroidRealmBeta2)); |
| EXPECT_THAT(GetAffiliatedAndroidRealms(android_observed_form), |
| testing::IsEmpty()); |
| } |
| |
| TEST_F(AffiliatedMatchHelperTest, |
| GetAffiliatedAndroidRealmsYieldsEmptyResultsWhenNoPrefetch) { |
| mock_affiliation_service() |
| ->ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure( |
| FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1), |
| StrategyOnCacheMiss::FAIL); |
| EXPECT_THAT(GetAffiliatedAndroidRealms( |
| GetTestObservedWebForm(kTestWebRealmAlpha1, nullptr)), |
| testing::IsEmpty()); |
| } |
| |
| // GetAffiliatedWebRealms* tests verify that GetAffiliatedWebRealms() returns |
| // the realms of web sites affiliated with the given Android application, but |
| // only web sites, and only if an Android application is queried. |
| |
| TEST_F(AffiliatedMatchHelperTest, GetAffiliatedWebRealmsYieldsResults) { |
| mock_affiliation_service() |
| ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( |
| FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3), |
| StrategyOnCacheMiss::FETCH_OVER_NETWORK, |
| GetTestEquivalenceClassAlpha()); |
| PasswordStore::FormDigest android_form( |
| GetTestAndroidCredentials(kTestAndroidRealmAlpha3)); |
| EXPECT_THAT( |
| GetAffiliatedWebRealms(android_form), |
| testing::UnorderedElementsAre(kTestWebRealmAlpha1, kTestWebRealmAlpha2)); |
| } |
| |
| TEST_F(AffiliatedMatchHelperTest, GetAffiliatedWebRealmsYieldsOnlyWebsites) { |
| mock_affiliation_service() |
| ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( |
| FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2), |
| StrategyOnCacheMiss::FETCH_OVER_NETWORK, |
| GetTestEquivalenceClassBeta()); |
| PasswordStore::FormDigest android_form( |
| GetTestAndroidCredentials(kTestAndroidRealmBeta2)); |
| // This verifies that |kTestAndroidRealmBeta3| is not returned. |
| EXPECT_THAT(GetAffiliatedWebRealms(android_form), |
| testing::UnorderedElementsAre(kTestWebRealmBeta1)); |
| } |
| |
| TEST_F(AffiliatedMatchHelperTest, |
| GetAffiliatedWebRealmsYieldsEmptyResultsForWebKeyedForms) { |
| EXPECT_THAT(GetAffiliatedWebRealms( |
| GetTestObservedWebForm(kTestWebRealmBeta1, nullptr)), |
| testing::IsEmpty()); |
| } |
| |
| // Verifies that InjectAffiliationAndBrandingInformation() injects the realms of |
| // web sites affiliated with the given Android application into the password |
| // forms, as well as branding information corresponding to the application, if |
| // any. |
| TEST_F(AffiliatedMatchHelperTest, InjectAffiliationAndBrandingInformation) { |
| std::vector<std::unique_ptr<autofill::PasswordForm>> forms; |
| |
| forms.push_back(std::make_unique<autofill::PasswordForm>( |
| GetTestAndroidCredentials(kTestAndroidRealmAlpha3))); |
| mock_affiliation_service() |
| ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( |
| FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3), |
| StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha()); |
| |
| forms.push_back(std::make_unique<autofill::PasswordForm>( |
| GetTestAndroidCredentials(kTestAndroidRealmBeta2))); |
| mock_affiliation_service() |
| ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( |
| FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2), |
| StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta()); |
| |
| forms.push_back(std::make_unique<autofill::PasswordForm>( |
| GetTestAndroidCredentials(kTestAndroidRealmBeta3))); |
| mock_affiliation_service() |
| ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( |
| FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta3), |
| StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta()); |
| |
| forms.push_back(std::make_unique<autofill::PasswordForm>( |
| GetTestAndroidCredentials(kTestAndroidRealmGamma))); |
| mock_affiliation_service() |
| ->ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure( |
| FacetURI::FromCanonicalSpec(kTestAndroidFacetURIGamma), |
| StrategyOnCacheMiss::FAIL); |
| |
| PasswordStore::FormDigest digest = |
| GetTestObservedWebForm(kTestWebRealmBeta1, nullptr); |
| autofill::PasswordForm web_form; |
| web_form.scheme = digest.scheme; |
| web_form.signon_realm = digest.signon_realm; |
| web_form.origin = digest.origin; |
| forms.push_back(std::make_unique<autofill::PasswordForm>(web_form)); |
| |
| size_t expected_form_count = forms.size(); |
| std::vector<std::unique_ptr<autofill::PasswordForm>> results( |
| InjectAffiliationAndBrandingInformation(std::move(forms))); |
| ASSERT_EQ(expected_form_count, results.size()); |
| EXPECT_THAT(results[0]->affiliated_web_realm, |
| testing::AnyOf(kTestWebRealmAlpha1, kTestWebRealmAlpha2)); |
| EXPECT_EQ(kTestAndroidFacetNameAlpha3, results[0]->app_display_name); |
| EXPECT_EQ(kTestAndroidFacetIconURLAlpha3, |
| results[0]->app_icon_url.possibly_invalid_spec()); |
| EXPECT_THAT(results[1]->affiliated_web_realm, |
| testing::Eq(kTestWebRealmBeta1)); |
| EXPECT_EQ(kTestAndroidFacetNameBeta2, results[1]->app_display_name); |
| EXPECT_EQ(kTestAndroidFacetIconURLBeta2, |
| results[1]->app_icon_url.possibly_invalid_spec()); |
| EXPECT_THAT(results[2]->affiliated_web_realm, |
| testing::Eq(kTestWebRealmBeta1)); |
| EXPECT_EQ(kTestAndroidFacetNameBeta3, results[2]->app_display_name); |
| EXPECT_EQ(kTestAndroidFacetIconURLBeta3, |
| results[2]->app_icon_url.possibly_invalid_spec()); |
| EXPECT_THAT(results[3]->affiliated_web_realm, testing::IsEmpty()); |
| EXPECT_THAT(results[4]->affiliated_web_realm, testing::IsEmpty()); |
| } |
| |
| // Note: IsValidWebCredential() is tested as part of GetAffiliatedAndroidRealms |
| // tests above. |
| TEST_F(AffiliatedMatchHelperTest, IsValidAndroidCredential) { |
| EXPECT_FALSE(AffiliatedMatchHelper::IsValidAndroidCredential( |
| GetTestObservedWebForm(kTestWebRealmBeta1, nullptr))); |
| PasswordStore::FormDigest android_credential( |
| GetTestAndroidCredentials(kTestAndroidRealmBeta2)); |
| EXPECT_TRUE( |
| AffiliatedMatchHelper::IsValidAndroidCredential(android_credential)); |
| } |
| |
| // Verifies that affiliations for Android applications with pre-existing |
| // credentials on start-up are prefetched. |
| TEST_F( |
| AffiliatedMatchHelperTest, |
| PrefetchAffiliationsAndBrandingForPreexistingAndroidCredentialsOnStartup) { |
| AddAndroidAndNonAndroidTestLogins(); |
| |
| match_helper()->Initialize(); |
| RunUntilIdle(); |
| |
| ExpectPrefetchForAndroidTestLogins(); |
| ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization()); |
| } |
| |
| // Stores credentials for Android applications between Initialize() and |
| // DoDeferredInitialization(). Verifies that corresponding affiliation |
| // information gets prefetched. |
| TEST_F(AffiliatedMatchHelperTest, |
| PrefetchAffiliationsForAndroidCredentialsAddedInInitializationDelay) { |
| match_helper()->Initialize(); |
| RunUntilIdle(); |
| |
| AddAndroidAndNonAndroidTestLogins(); |
| |
| ExpectPrefetchForAndroidTestLogins(); |
| ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization()); |
| } |
| |
| // Stores credentials for Android applications after DoDeferredInitialization(). |
| // Verifies that corresponding affiliation information gets prefetched. |
| TEST_F(AffiliatedMatchHelperTest, |
| PrefetchAffiliationsForAndroidCredentialsAddedAfterInitialization) { |
| match_helper()->Initialize(); |
| ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization()); |
| |
| ExpectPrefetchForAndroidTestLogins(); |
| AddAndroidAndNonAndroidTestLogins(); |
| } |
| |
| TEST_F(AffiliatedMatchHelperTest, |
| CancelPrefetchingAffiliationsAndBrandingForRemovedAndroidCredentials) { |
| AddAndroidAndNonAndroidTestLogins(); |
| match_helper()->Initialize(); |
| ExpectPrefetchForAndroidTestLogins(); |
| ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization()); |
| |
| ExpectCancelPrefetchForAndroidTestLogins(); |
| ExpectTrimCacheForAndroidTestLogins(); |
| RemoveAndroidAndNonAndroidTestLogins(); |
| } |
| |
| // Verify that whenever the primary key is updated for a credential (in which |
| // case both REMOVE and ADD change notifications are sent out), then Prefetch() |
| // is called in response to the addition before the call to |
| // TrimCacheForFacetURI() in response to the removal, so that cached data is not |
| // deleted and then immediately re-fetched. |
| TEST_F(AffiliatedMatchHelperTest, PrefetchBeforeTrimForPrimaryKeyUpdates) { |
| AddAndroidAndNonAndroidTestLogins(); |
| match_helper()->Initialize(); |
| ExpectPrefetchForAndroidTestLogins(); |
| ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization()); |
| |
| mock_affiliation_service()->ExpectCallToCancelPrefetch( |
| kTestAndroidFacetURIAlpha3); |
| |
| { |
| testing::InSequence in_sequence; |
| mock_affiliation_service()->ExpectCallToPrefetch( |
| kTestAndroidFacetURIAlpha3); |
| mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI( |
| kTestAndroidFacetURIAlpha3); |
| } |
| |
| autofill::PasswordForm old_form( |
| GetTestAndroidCredentials(kTestAndroidRealmAlpha3)); |
| autofill::PasswordForm new_form(old_form); |
| new_form.username_value = base::ASCIIToUTF16("NewUserName"); |
| UpdateLoginWithPrimaryKey(new_form, old_form); |
| } |
| |
| // Stores and removes four credentials for the same an Android application, and |
| // expects that Prefetch() and CancelPrefetch() will each be called four times. |
| TEST_F(AffiliatedMatchHelperTest, |
| DuplicateCredentialsArePrefetchWithMultiplicity) { |
| EXPECT_CALL(*mock_affiliation_service(), |
| Prefetch(FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3), |
| base::Time::Max())) |
| .Times(4); |
| |
| autofill::PasswordForm android_form( |
| GetTestAndroidCredentials(kTestAndroidRealmAlpha3)); |
| AddLogin(android_form); |
| |
| // Store two credentials before initialization. |
| autofill::PasswordForm android_form2(android_form); |
| android_form2.username_value = base::ASCIIToUTF16("JohnDoe2"); |
| AddLogin(android_form2); |
| |
| match_helper()->Initialize(); |
| RunUntilIdle(); |
| |
| // Store one credential between initialization and deferred initialization. |
| autofill::PasswordForm android_form3(android_form); |
| android_form3.username_value = base::ASCIIToUTF16("JohnDoe3"); |
| AddLogin(android_form3); |
| |
| ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization()); |
| |
| // Store one credential after deferred initialization. |
| autofill::PasswordForm android_form4(android_form); |
| android_form4.username_value = base::ASCIIToUTF16("JohnDoe4"); |
| AddLogin(android_form4); |
| |
| for (size_t i = 0; i < 4; ++i) { |
| mock_affiliation_service()->ExpectCallToCancelPrefetch( |
| kTestAndroidFacetURIAlpha3); |
| mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI( |
| kTestAndroidFacetURIAlpha3); |
| } |
| |
| RemoveLogin(android_form); |
| RemoveLogin(android_form2); |
| RemoveLogin(android_form3); |
| RemoveLogin(android_form4); |
| } |
| |
| TEST_F(AffiliatedMatchHelperTest, DestroyBeforeDeferredInitialization) { |
| match_helper()->Initialize(); |
| RunUntilIdle(); |
| DestroyMatchHelper(); |
| ASSERT_NO_FATAL_FAILURE(ExpectNoDeferredTasks()); |
| } |
| |
| } // namespace password_manager |