| Index: components/password_manager/core/browser/affiliation_backend_unittest.cc
|
| diff --git a/components/password_manager/core/browser/affiliation_backend_unittest.cc b/components/password_manager/core/browser/affiliation_backend_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..009cd9a3f1599b083b57a402e022d7669bbc2285
|
| --- /dev/null
|
| +++ b/components/password_manager/core/browser/affiliation_backend_unittest.cc
|
| @@ -0,0 +1,318 @@
|
| +// 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/affiliation_backend.h"
|
| +
|
| +#include "base/files/file_path.h"
|
| +#include "base/files/file_util.h"
|
| +#include "base/macros.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/test/simple_test_clock.h"
|
| +#include "base/test/test_simple_task_runner.h"
|
| +#include "components/password_manager/core/browser/fake_affiliation_api.h"
|
| +#include "components/password_manager/core/browser/mock_affiliation_consumer.h"
|
| +#include "net/url_request/url_request_context_getter.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace password_manager {
|
| +
|
| +namespace {
|
| +
|
| +const char kTestFacetURIAlpha1[] = "https://one.alpha.example.com";
|
| +const char kTestFacetURIAlpha2[] = "https://two.alpha.example.com";
|
| +const char kTestFacetURIAlpha3[] = "https://three.alpha.example.com";
|
| +const char kTestFacetURIBeta1[] = "https://one.beta.example.com";
|
| +const char kTestFacetURIBeta2[] = "https://two.beta.example.com";
|
| +const char kTestFacetURIGamma1[] = "https://gamma.example.com";
|
| +
|
| +AffiliatedFacets GetTestEquivalenceClassAlpha() {
|
| + AffiliatedFacets affiliated_facets;
|
| + affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
|
| + affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
|
| + affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha3));
|
| + return affiliated_facets;
|
| +}
|
| +
|
| +AffiliatedFacets GetTestEquivalenceClassBeta() {
|
| + AffiliatedFacets affiliated_facets;
|
| + affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
|
| + affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIBeta2));
|
| + return affiliated_facets;
|
| +}
|
| +
|
| +AffiliatedFacets GetTestEquivalenceClassGamma() {
|
| + AffiliatedFacets affiliated_facets;
|
| + affiliated_facets.push_back(FacetURI::FromCanonicalSpec(kTestFacetURIGamma1));
|
| + return affiliated_facets;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +class AffiliationBackendTest : public testing::Test {
|
| + public:
|
| + AffiliationBackendTest()
|
| + : consumer_task_runner_(new base::TestSimpleTaskRunner),
|
| + clock_(new base::SimpleTestClock) {}
|
| + ~AffiliationBackendTest() override {}
|
| +
|
| + protected:
|
| + void GetAffiliations(MockAffiliationConsumer* consumer,
|
| + const FacetURI& facet_uri,
|
| + bool cached_only) {
|
| + backend_->GetAffiliations(facet_uri, cached_only,
|
| + consumer->GetResultCallback(),
|
| + consumer_task_runner());
|
| + }
|
| +
|
| + void ExpectAndCompleteFetch(const FacetURI& expected_requested_facet_uri) {
|
| + ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
|
| + EXPECT_THAT(fake_affiliation_api()->GetNextRequestedFacets(),
|
| + testing::UnorderedElementsAre(expected_requested_facet_uri));
|
| + fake_affiliation_api()->ServeNextRequest();
|
| + }
|
| +
|
| + void ExpectFailureWithoutFetch(MockAffiliationConsumer* consumer) {
|
| + ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
|
| + consumer->ExpectFailure();
|
| + consumer_task_runner_->RunUntilIdle();
|
| + testing::Mock::VerifyAndClearExpectations(consumer);
|
| + }
|
| +
|
| + void GetAffiliationsAndExpectFetchAndThenResult(
|
| + const FacetURI& facet_uri,
|
| + const AffiliatedFacets& expected_result) {
|
| + GetAffiliations(mock_consumer(), facet_uri, false);
|
| + ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri));
|
| + mock_consumer()->ExpectSuccessWithResult(expected_result);
|
| + consumer_task_runner_->RunUntilIdle();
|
| + testing::Mock::VerifyAndClearExpectations(mock_consumer());
|
| + }
|
| +
|
| + void GetAffiliationsAndExpectResultWithoutFetch(
|
| + const FacetURI& facet_uri,
|
| + bool cached_only,
|
| + const AffiliatedFacets& expected_result) {
|
| + GetAffiliations(mock_consumer(), facet_uri, cached_only);
|
| + ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
|
| + mock_consumer()->ExpectSuccessWithResult(expected_result);
|
| + consumer_task_runner_->RunUntilIdle();
|
| + testing::Mock::VerifyAndClearExpectations(mock_consumer());
|
| + }
|
| +
|
| + void DestroyBackend() {
|
| + clock_ = nullptr;
|
| + backend_.reset();
|
| + }
|
| +
|
| + void AdvanceTime(base::TimeDelta delta) { clock_->Advance(delta); }
|
| +
|
| + AffiliationBackend* backend() { return backend_.get(); }
|
| + MockAffiliationConsumer* mock_consumer() { return &mock_consumer_; }
|
| +
|
| + base::TestSimpleTaskRunner* consumer_task_runner() {
|
| + return consumer_task_runner_.get();
|
| + }
|
| +
|
| + ScopedFakeAffiliationAPI* fake_affiliation_api() {
|
| + return &fake_affiliation_api_;
|
| + }
|
| +
|
| + private:
|
| + // testing::Test:
|
| + void SetUp() override {
|
| + clock_->Advance(base::TimeDelta::FromMicroseconds(1));
|
| + backend_.reset(new AffiliationBackend(NULL, make_scoped_ptr(clock_)));
|
| +
|
| + base::FilePath database_path;
|
| + ASSERT_TRUE(CreateTemporaryFile(&database_path));
|
| + backend_->Initialize(database_path);
|
| +
|
| + fake_affiliation_api_.AddTestEquivalenceClass(
|
| + GetTestEquivalenceClassAlpha());
|
| + fake_affiliation_api_.AddTestEquivalenceClass(
|
| + GetTestEquivalenceClassBeta());
|
| + fake_affiliation_api_.AddTestEquivalenceClass(
|
| + GetTestEquivalenceClassGamma());
|
| + }
|
| +
|
| + ScopedFakeAffiliationAPI fake_affiliation_api_;
|
| + MockAffiliationConsumer mock_consumer_;
|
| + scoped_refptr<base::TestSimpleTaskRunner> consumer_task_runner_;
|
| +
|
| + base::SimpleTestClock* clock_; // Owned by |backend_|.
|
| + scoped_ptr<AffiliationBackend> backend_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(AffiliationBackendTest);
|
| +};
|
| +
|
| +TEST_F(AffiliationBackendTest, OnDemandRequestSucceedsWithFetch) {
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
|
| + GetTestEquivalenceClassAlpha()));
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIBeta1),
|
| + GetTestEquivalenceClassBeta()));
|
| +}
|
| +
|
| +TEST_F(AffiliationBackendTest, CachedOnlyRequestFailsOnCacheMiss) {
|
| + GetAffiliations(mock_consumer(),
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
|
| + true /* cached_only */);
|
| + ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
|
| + mock_consumer()->ExpectFailure();
|
| + consumer_task_runner()->RunUntilIdle();
|
| + testing::Mock::VerifyAndClearExpectations(mock_consumer());
|
| +}
|
| +
|
| +// Two additional requests for unrelated facets come in while the network fetch
|
| +// triggered by the first request is in flight. There should be no simultaneous
|
| +// requests, and the additional facets should be queried together in a second
|
| +// fetch after the first fetch completes.
|
| +TEST_F(AffiliationBackendTest, ConcurrentUnrelatedRequests) {
|
| + FacetURI facet_alpha(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
|
| + FacetURI facet_beta(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
|
| + FacetURI facet_gamma(FacetURI::FromCanonicalSpec(kTestFacetURIGamma1));
|
| +
|
| + MockAffiliationConsumer second_consumer;
|
| + MockAffiliationConsumer third_consumer;
|
| + GetAffiliations(mock_consumer(), facet_alpha, false);
|
| + GetAffiliations(&second_consumer, facet_beta, false);
|
| + GetAffiliations(&third_consumer, facet_gamma, false);
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_alpha));
|
| + ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
|
| + EXPECT_THAT(fake_affiliation_api()->GetNextRequestedFacets(),
|
| + testing::UnorderedElementsAre(facet_beta, facet_gamma));
|
| + fake_affiliation_api()->ServeNextRequest();
|
| +
|
| + mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
|
| + second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassBeta());
|
| + third_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassGamma());
|
| + consumer_task_runner()->RunUntilIdle();
|
| + testing::Mock::VerifyAndClearExpectations(mock_consumer());
|
| +}
|
| +
|
| +TEST_F(AffiliationBackendTest, CacheServesSubsequentRequestForSameFacet) {
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
|
| + GetTestEquivalenceClassAlpha()));
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), false /* cached_only */,
|
| + GetTestEquivalenceClassAlpha()));
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), true /* cached_only */,
|
| + GetTestEquivalenceClassAlpha()));
|
| +}
|
| +
|
| +TEST_F(AffiliationBackendTest, CacheServesSubsequentRequestForAffiliatedFacet) {
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
|
| + GetTestEquivalenceClassAlpha()));
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2), false /* cached_only */,
|
| + GetTestEquivalenceClassAlpha()));
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2), true /* cached_only */,
|
| + GetTestEquivalenceClassAlpha()));
|
| +}
|
| +
|
| +TEST_F(AffiliationBackendTest, CacheExpiry) {
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
|
| + GetTestEquivalenceClassAlpha()));
|
| +
|
| + AdvanceTime(base::TimeDelta::FromHours(24) -
|
| + base::TimeDelta::FromMicroseconds(1));
|
| +
|
| + // Before the data becomes stale, both on-demand and cached-only requests are
|
| + // expected to be served from the cache.
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2), false /* cached_only */,
|
| + GetTestEquivalenceClassAlpha()));
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2), true /* cached_only */,
|
| + GetTestEquivalenceClassAlpha()));
|
| +
|
| + AdvanceTime(base::TimeDelta::FromMicroseconds(1));
|
| +
|
| + // After the data becomes stale, the cached-only request should fail, but the
|
| + // subsequent on-demand request should fetch the data again and succeed.
|
| + GetAffiliations(mock_consumer(),
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
|
| + true /* cached_only */);
|
| + ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
|
| +
|
| + mock_consumer()->ExpectFailure();
|
| + consumer_task_runner()->RunUntilIdle();
|
| + testing::Mock::VerifyAndClearExpectations(mock_consumer());
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
|
| + GetTestEquivalenceClassAlpha()));
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectResultWithoutFetch(
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2), true /* cached_only */,
|
| + GetTestEquivalenceClassAlpha()));
|
| +}
|
| +
|
| +// A second GetAffiliations() request for the same facet and a third request
|
| +// for an affiliated facet comes in while the network fetch triggered by the
|
| +// first request is in flight.
|
| +//
|
| +// There should be no simultaneous requests, so once the fetch completes, all
|
| +// three requests should be served without further fetches (they have the data).
|
| +TEST_F(AffiliationBackendTest,
|
| + CacheServesConcurrentRequestsForAffiliatedFacets) {
|
| + FacetURI facet_uri1(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
|
| + FacetURI facet_uri2(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
|
| +
|
| + MockAffiliationConsumer second_consumer;
|
| + MockAffiliationConsumer third_consumer;
|
| + GetAffiliations(mock_consumer(), facet_uri1, false);
|
| + GetAffiliations(&second_consumer, facet_uri1, false);
|
| + GetAffiliations(&third_consumer, facet_uri2, false);
|
| +
|
| + ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri1));
|
| + ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
|
| +
|
| + mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
|
| + second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
|
| + third_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
|
| + consumer_task_runner()->RunUntilIdle();
|
| + testing::Mock::VerifyAndClearExpectations(mock_consumer());
|
| +}
|
| +
|
| +TEST_F(AffiliationBackendTest, NothingExplodesWhenShutDownDuringFetch) {
|
| + GetAffiliations(mock_consumer(),
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
|
| + false /* cached_only */);
|
| + ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
|
| + fake_affiliation_api()->IgnoreNextRequest();
|
| + DestroyBackend();
|
| +}
|
| +
|
| +TEST_F(AffiliationBackendTest,
|
| + FailureCallbacksAreCalledIfBackendIsDestroyedWithPendingRequest) {
|
| + GetAffiliations(mock_consumer(),
|
| + FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
|
| + false /* cached_only */);
|
| + ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
|
| + // Currently, a GetAffiliations() request can only be blocked due to fetch in
|
| + // flight -- so emulate this condition when destroying the backend.
|
| + fake_affiliation_api()->IgnoreNextRequest();
|
| + DestroyBackend();
|
| + mock_consumer()->ExpectFailure();
|
| + consumer_task_runner()->RunUntilIdle();
|
| + testing::Mock::VerifyAndClearExpectations(mock_consumer());
|
| +}
|
| +
|
| +} // namespace password_manager
|
|
|