summaryrefslogtreecommitdiff
path: root/chromium/components/password_manager/core/browser/android_affiliation/affiliated_match_helper_unittest.cc
diff options
context:
space:
mode:
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.cc670
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