diff options
Diffstat (limited to 'chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc')
-rw-r--r-- | chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc b/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc new file mode 100644 index 00000000000..db62fe2ed5e --- /dev/null +++ b/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc @@ -0,0 +1,670 @@ +// 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/ptr_util.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_mock_time_message_loop_task_runner.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 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; + + match_helper_.reset( + new AffiliatedMatchHelper(password_store_.get(), std::move(service))); + } + + void TearDown() override { + match_helper_.reset(); + password_store_->ShutdownOnUIThread(); + password_store_ = nullptr; + } + + base::MessageLoop message_loop_; + 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(base::MakeUnique<autofill::PasswordForm>( + GetTestAndroidCredentials(kTestAndroidRealmAlpha3))); + mock_affiliation_service() + ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( + FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3), + StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassAlpha()); + + forms.push_back(base::MakeUnique<autofill::PasswordForm>( + GetTestAndroidCredentials(kTestAndroidRealmBeta2))); + mock_affiliation_service() + ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( + FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2), + StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta()); + + forms.push_back(base::MakeUnique<autofill::PasswordForm>( + GetTestAndroidCredentials(kTestAndroidRealmBeta3))); + mock_affiliation_service() + ->ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult( + FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta3), + StrategyOnCacheMiss::FAIL, GetTestEquivalenceClassBeta()); + + forms.push_back(base::MakeUnique<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(base::MakeUnique<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(RunDeferredInitialization()); +} + +} // namespace password_manager |