Commit 45920ca9 authored by engedy's avatar engedy Committed by Commit bot

AffiliationBackend: Implement the better part of on-demand fetching.

This CL implements AffiliationBackend::GetAffiliation(), including support for caching, but without support for some error scenarios.

The missing parts, viz. Prefetch() functionality and error handling will be added later to reduce CL size. Given that the code is not yet exercised outside of tests, this should not create any problems.

BUG=437865
TBR=gcasto@chromium.org

Review URL: https://codereview.chromium.org/868253005

Cr-Commit-Position: refs/heads/master@{#314320}
parent 2b3079c9
...@@ -191,9 +191,11 @@ ...@@ -191,9 +191,11 @@
'ownership/owner_key_util_impl_unittest.cc', 'ownership/owner_key_util_impl_unittest.cc',
'packed_ct_ev_whitelist/bit_stream_reader_unittest.cc', 'packed_ct_ev_whitelist/bit_stream_reader_unittest.cc',
'packed_ct_ev_whitelist/packed_ct_ev_whitelist_unittest.cc', 'packed_ct_ev_whitelist/packed_ct_ev_whitelist_unittest.cc',
'password_manager/core/browser/affiliation_backend_unittest.cc',
'password_manager/core/browser/affiliation_database_unittest.cc', 'password_manager/core/browser/affiliation_database_unittest.cc',
'password_manager/core/browser/affiliation_fetch_throttler_unittest.cc', 'password_manager/core/browser/affiliation_fetch_throttler_unittest.cc',
'password_manager/core/browser/affiliation_fetcher_unittest.cc', 'password_manager/core/browser/affiliation_fetcher_unittest.cc',
'password_manager/core/browser/affiliation_service_unittest.cc',
'password_manager/core/browser/affiliation_utils_unittest.cc', 'password_manager/core/browser/affiliation_utils_unittest.cc',
'password_manager/core/browser/browser_save_password_progress_logger_unittest.cc', 'password_manager/core/browser/browser_save_password_progress_logger_unittest.cc',
'password_manager/core/browser/export/csv_writer_unittest.cc', 'password_manager/core/browser/export/csv_writer_unittest.cc',
......
...@@ -159,8 +159,12 @@ ...@@ -159,8 +159,12 @@
], ],
'sources': [ 'sources': [
# Note: sources list duplicated in GN build. # Note: sources list duplicated in GN build.
'password_manager/core/browser/fake_affiliation_api.cc',
'password_manager/core/browser/fake_affiliation_api.h',
'password_manager/core/browser/fake_affiliation_fetcher.cc', 'password_manager/core/browser/fake_affiliation_fetcher.cc',
'password_manager/core/browser/fake_affiliation_fetcher.h', 'password_manager/core/browser/fake_affiliation_fetcher.h',
'password_manager/core/browser/mock_affiliation_consumer.cc',
'password_manager/core/browser/mock_affiliation_consumer.h',
'password_manager/core/browser/mock_password_store.cc', 'password_manager/core/browser/mock_password_store.cc',
'password_manager/core/browser/mock_password_store.h', 'password_manager/core/browser/mock_password_store.h',
'password_manager/core/browser/password_form_data.cc', 'password_manager/core/browser/password_form_data.cc',
......
...@@ -122,8 +122,12 @@ proto_library("proto") { ...@@ -122,8 +122,12 @@ proto_library("proto") {
source_set("test_support") { source_set("test_support") {
testonly = true testonly = true
sources = [ sources = [
"fake_affiliation_api.cc",
"fake_affiliation_api.h",
"fake_affiliation_fetcher.cc", "fake_affiliation_fetcher.cc",
"fake_affiliation_fetcher.h", "fake_affiliation_fetcher.h",
"mock_affiliation_consumer.cc",
"mock_affiliation_consumer.h",
"mock_password_store.cc", "mock_password_store.cc",
"mock_password_store.h", "mock_password_store.h",
"password_form_data.cc", "password_form_data.cc",
......
...@@ -5,41 +5,119 @@ ...@@ -5,41 +5,119 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_BACKEND_H_ #ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_BACKEND_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_BACKEND_H_ #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_BACKEND_H_
#include <string> #include <map>
#include <vector>
#include "base/containers/scoped_ptr_hash_map.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "components/password_manager/core/browser/affiliation_fetcher_delegate.h"
#include "components/password_manager/core/browser/affiliation_service.h" #include "components/password_manager/core/browser/affiliation_service.h"
#include "components/password_manager/core/browser/affiliation_utils.h"
namespace base { namespace base {
class Time; class Clock;
class FilePath; class FilePath;
class TaskRunner; class ThreadChecker;
} class Time;
} // namespace base
namespace net {
class URLRequestContextGetter;
} // namespace net
namespace password_manager { namespace password_manager {
class FacetURI; class AffiliationDatabase;
class AffiliationFetcher;
// Implements the bulk of AffiliationService; runs on a background thread. // The AffiliationBackend is the part of the AffiliationService that lives on a
class AffiliationBackend { // background thread suitable for performing blocking I/O. As most tasks require
// I/O, the backend ends up doing most of the work for the AffiliationService;
// the latter being just a thin layer that delegates most tasks to the backend.
//
// This class is not thread-safe, but it is fine to construct it on one thread
// and then transfer it to the background thread for the rest of its life.
// Initialize() must be called already on the background thread.
class AffiliationBackend : public AffiliationFetcherDelegate {
public: public:
AffiliationBackend(); // Constructs an instance that will use |request_context_getter| for all
~AffiliationBackend(); // network requests, and will rely on |time_source| to tell the current time,
// which is expected to always be strictly greater than the NULL time.
// Construction is very cheap, expensive steps are deferred to Initialize().
AffiliationBackend(
const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
scoped_ptr<base::Clock> time_source);
~AffiliationBackend() override;
void Initialize(); // Performs the I/O-heavy part of initialization. The database to cache
// affiliation information locally will be opened/created at |db_path|.
void Initialize(const base::FilePath& db_path);
// Implementations for functions of the same name in AffiliationService. // Implementations for methods of the same name in AffiliationService. They
void GetAffiliations(const FacetURI& facet_uri, // are not documented here again. See affiliation_service.h for details:
bool cached_only, void GetAffiliations(
const AffiliationService::ResultCallback& callback, const FacetURI& facet_uri,
scoped_refptr<base::TaskRunner> callback_task_runner); bool cached_only,
const AffiliationService::ResultCallback& callback,
const scoped_refptr<base::TaskRunner>& callback_task_runner);
void Prefetch(const FacetURI& facet_uri, const base::Time& keep_fresh_until); void Prefetch(const FacetURI& facet_uri, const base::Time& keep_fresh_until);
void CancelPrefetch(const FacetURI& facet_uri, void CancelPrefetch(const FacetURI& facet_uri,
const base::Time& keep_fresh_until); const base::Time& keep_fresh_until);
void TrimCache(); void TrimCache();
private: private:
class FacetManager;
friend class FacetManager;
// Collects facet URIs that require fetching and issues a network request
// against the Affiliation API to fetch corresponding affiliation information.
void SendNetworkRequest();
// Gets the current time as per |clock_|. The returned time will always be
// strictly greater than the NULL time. Used by FacetManager.
base::Time GetCurrentTime();
// Reads and returns the last update time of the equivalence class containing
// |facet_uri| from the database, or, if no such equivalence class is stored,
// returns the NULL time. Used by FacetManager.
base::Time ReadLastUpdateTimeFromDatabase(const FacetURI& facet_uri);
// Reads the equivalence class containing |facet_uri| from the database and
// returns true if found; returns false otherwise. Used by FacetManager.
bool ReadAffiliationsFromDatabase(
const FacetURI& facet_uri,
AffiliatedFacetsWithUpdateTime* affiliations);
// Signals the fetching logic that there is at least one facet that needs to
// be fetched immediately. Called by FacetManager.
void SignalNeedNetworkRequest();
// AffiliationFetcherDelegate:
void OnFetchSucceeded(
scoped_ptr<AffiliationFetcherDelegate::Result> result) override;
void OnFetchFailed() override;
void OnMalformedResponse() override;
// Created in Initialize(), and ensures that all subsequent methods are called
// on the same thread.
scoped_ptr<base::ThreadChecker> thread_checker_;
scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
// Will always return a Now() that is strictly greater than the NULL time.
scoped_ptr<base::Clock> clock_;
scoped_ptr<AffiliationDatabase> cache_;
scoped_ptr<AffiliationFetcher> fetcher_;
// Contains a FacetManager for each facet URI that need ongoing attention. To
// save memory, managers are discarded as soon as they become redundant.
base::ScopedPtrHashMap<FacetURI, FacetManager> facet_managers_;
base::WeakPtrFactory<AffiliationBackend> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(AffiliationBackend); DISALLOW_COPY_AND_ASSIGN(AffiliationBackend);
}; };
......
...@@ -6,10 +6,13 @@ ...@@ -6,10 +6,13 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/location.h" #include "base/location.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h" #include "base/thread_task_runner_handle.h"
#include "base/time/default_clock.h"
#include "components/password_manager/core/browser/affiliation_backend.h" #include "components/password_manager/core/browser/affiliation_backend.h"
#include "net/url_request/url_request_context_getter.h"
namespace password_manager { namespace password_manager {
...@@ -28,13 +31,16 @@ AffiliationService::~AffiliationService() { ...@@ -28,13 +31,16 @@ AffiliationService::~AffiliationService() {
} }
} }
void AffiliationService::Initialize() { void AffiliationService::Initialize(
net::URLRequestContextGetter* request_context_getter,
const base::FilePath& db_path) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!backend_); DCHECK(!backend_);
backend_ = new AffiliationBackend(); backend_ = new AffiliationBackend(request_context_getter,
make_scoped_ptr(new base::DefaultClock));
backend_task_runner_->PostTask( backend_task_runner_->PostTask(
FROM_HERE, FROM_HERE, base::Bind(&AffiliationBackend::Initialize,
base::Bind(&AffiliationBackend::Initialize, base::Unretained(backend_))); base::Unretained(backend_), db_path));
} }
void AffiliationService::GetAffiliations( void AffiliationService::GetAffiliations(
......
...@@ -16,8 +16,13 @@ ...@@ -16,8 +16,13 @@
#include "components/password_manager/core/browser/affiliation_utils.h" #include "components/password_manager/core/browser/affiliation_utils.h"
namespace base { namespace base {
class FilePath;
class SingleThreadTaskRunner; class SingleThreadTaskRunner;
} } // namespace base
namespace net {
class URLRequestContextGetter;
} // namespace net
namespace password_manager { namespace password_manager {
...@@ -97,7 +102,8 @@ class AffiliationService : public KeyedService { ...@@ -97,7 +102,8 @@ class AffiliationService : public KeyedService {
// Initializes the service by creating its backend and transferring it to the // Initializes the service by creating its backend and transferring it to the
// thread corresponding to |backend_task_runner_|. // thread corresponding to |backend_task_runner_|.
void Initialize(); void Initialize(net::URLRequestContextGetter* request_context_getter,
const base::FilePath& db_path);
// Looks up facets affiliated with the facet identified by |facet_uri|. If // Looks up facets affiliated with the facet identified by |facet_uri|. If
// |cached_only| is true, the results will be based solely on prefetched // |cached_only| is true, the results will be based solely on prefetched
......
// Copyright 2014 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.
// Note: This test focuses on functionality implemented in AffiliationService
// itself. More thorough The AffiliationBackend is tested in-depth separarately.
#include "components/password_manager/core/browser/affiliation_service.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/test_simple_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "components/password_manager/core/browser/fake_affiliation_api.h"
#include "components/password_manager/core/browser/mock_affiliation_consumer.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";
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;
}
} // namespace
class AffiliationServiceTest : public testing::Test {
public:
AffiliationServiceTest()
: main_task_runner_(new base::TestSimpleTaskRunner),
background_task_runner_(new base::TestSimpleTaskRunner),
main_task_runner_handle_(main_task_runner_) {}
~AffiliationServiceTest() override {}
protected:
void DestroyService() { service_.reset(); }
AffiliationService* service() { return service_.get(); }
MockAffiliationConsumer* mock_consumer() { return &mock_consumer_; }
base::TestSimpleTaskRunner* main_task_runner() {
return main_task_runner_.get();
}
base::TestSimpleTaskRunner* background_task_runner() {
return background_task_runner_.get();
}
ScopedFakeAffiliationAPI* fake_affiliation_api() {
return &fake_affiliation_api_;
}
private:
// testing::Test:
void SetUp() override {
base::FilePath database_path;
ASSERT_TRUE(CreateTemporaryFile(&database_path));
service_.reset(new AffiliationService(background_task_runner()));
service_->Initialize(NULL, database_path);
// Note: the background task runner is purposely not pumped here, so that
// the tests also verify that the service can be used synchronously right
// away after having been constructed.
fake_affiliation_api_.AddTestEquivalenceClass(
GetTestEquivalenceClassAlpha());
}
void TearDown() override {
// The service uses DeleteSoon to asynchronously destroy its backend. Pump
// the background thread to make sure destruction actually takes place.
DestroyService();
background_task_runner_->RunUntilIdle();
}
ScopedFakeAffiliationAPI fake_affiliation_api_;
MockAffiliationConsumer mock_consumer_;
scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_;
scoped_refptr<base::TestSimpleTaskRunner> background_task_runner_;
base::ThreadTaskRunnerHandle main_task_runner_handle_;
scoped_ptr<AffiliationService> service_;
DISALLOW_COPY_AND_ASSIGN(AffiliationServiceTest);
};
TEST_F(AffiliationServiceTest, GetAffiliations) {
// The first request allows on-demand fetching, and should trigger a fetch.
// Then, it should succeed after the fetch is complete.
service()->GetAffiliations(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
false /* cached_only */,
mock_consumer()->GetResultCallback());
background_task_runner()->RunUntilIdle();
ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
fake_affiliation_api()->ServeNextRequest();
mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
main_task_runner()->RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(mock_consumer());
// The second request should be (and can be) served from cache.
service()->GetAffiliations(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
true /* cached_only */,
mock_consumer()->GetResultCallback());
background_task_runner()->RunUntilIdle();
ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
main_task_runner()->RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(mock_consumer());
// The third request is also restricted to the cache, but cannot be served
// from cache, thus it should fail.
service()->GetAffiliations(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1),
true /* cached_only */,
mock_consumer()->GetResultCallback());
background_task_runner()->RunUntilIdle();
ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
mock_consumer()->ExpectFailure();
main_task_runner()->RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(mock_consumer());
}
TEST_F(AffiliationServiceTest, ShutdownWhileTasksArePosted) {
service()->GetAffiliations(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
false, mock_consumer()->GetResultCallback());
DestroyService();
background_task_runner()->RunUntilIdle();
ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
fake_affiliation_api()->IgnoreNextRequest();
mock_consumer()->ExpectFailure();
main_task_runner()->RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(mock_consumer());
}
} // namespace password_manager
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "components/password_manager/core/browser/affiliation_utils.h" #include "components/password_manager/core/browser/affiliation_utils.h"
#include <algorithm> #include <algorithm>
#include <ostream>
#include "base/base64.h" #include "base/base64.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
...@@ -268,6 +269,10 @@ AffiliatedFacetsWithUpdateTime::~AffiliatedFacetsWithUpdateTime() { ...@@ -268,6 +269,10 @@ AffiliatedFacetsWithUpdateTime::~AffiliatedFacetsWithUpdateTime() {
// Helpers -------------------------------------------------------------------- // Helpers --------------------------------------------------------------------
std::ostream& operator<<(std::ostream& os, const FacetURI& facet_uri) {
return os << facet_uri.potentially_invalid_spec();
}
bool AreEquivalenceClassesEqual(const AffiliatedFacets& a, bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
const AffiliatedFacets& b) { const AffiliatedFacets& b) {
if (a.size() != b.size()) if (a.size() != b.size())
......
...@@ -44,9 +44,11 @@ ...@@ -44,9 +44,11 @@
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_UTILS_H_ #ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_UTILS_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_UTILS_H_ #define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_UTILS_H_
#include <iosfwd>
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/containers/hash_tables.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "url/url_parse.h" #include "url/url_parse.h"
...@@ -118,6 +120,11 @@ class FacetURI { ...@@ -118,6 +120,11 @@ class FacetURI {
return canonical_spec_; return canonical_spec_;
} }
// Returns the text of the encapsulated canonical URI, even if it is invalid.
const std::string& potentially_invalid_spec() const {
return canonical_spec_;
}
private: private:
// Internal constructor to be used by the static factory methods. // Internal constructor to be used by the static factory methods.
FacetURI(const std::string& canonical_spec, bool is_valid); FacetURI(const std::string& canonical_spec, bool is_valid);
...@@ -154,6 +161,21 @@ struct AffiliatedFacetsWithUpdateTime { ...@@ -154,6 +161,21 @@ struct AffiliatedFacetsWithUpdateTime {
bool AreEquivalenceClassesEqual(const AffiliatedFacets& a, bool AreEquivalenceClassesEqual(const AffiliatedFacets& a,
const AffiliatedFacets& b); const AffiliatedFacets& b);
// For logging use only.
std::ostream& operator<<(std::ostream& os, const FacetURI& facet_uri);
} // namespace password_manager } // namespace password_manager
// Provide a hash function so that hash_sets and maps can contain FacetURIs.
namespace BASE_HASH_NAMESPACE {
template <>
struct hash<password_manager::FacetURI> {
size_t operator()(const password_manager::FacetURI& facet_uri) const {
return hash<std::string>()(facet_uri.potentially_invalid_spec());
}
};
} // namespace BASE_HASH_NAMESPACE
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_UTILS_H_ #endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_UTILS_H_
// 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/fake_affiliation_api.h"
#include <algorithm>
#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace password_manager {
ScopedFakeAffiliationAPI::ScopedFakeAffiliationAPI() {
}
ScopedFakeAffiliationAPI::~ScopedFakeAffiliationAPI() {
// Note that trying to provide details of dangling fetchers would be unwise,
// as it is quite possible that they have been destroyed already.
EXPECT_FALSE(HasPendingRequest())
<< "Pending AffilitionFetcher on shutdown.\n"
<< "Call IgnoreNextRequest() if this is intended.";
}
void ScopedFakeAffiliationAPI::AddTestEquivalenceClass(
const AffiliatedFacets& affiliated_facets) {
preset_equivalence_relation_.push_back(affiliated_facets);
}
bool ScopedFakeAffiliationAPI::HasPendingRequest() {
return fake_fetcher_factory_.has_pending_fetchers();
}
std::vector<FacetURI> ScopedFakeAffiliationAPI::GetNextRequestedFacets() {
if (fake_fetcher_factory_.has_pending_fetchers())
return fake_fetcher_factory_.PeekNextFetcher()->requested_facet_uris();
return std::vector<FacetURI>();
}
void ScopedFakeAffiliationAPI::ServeNextRequest() {
if (!fake_fetcher_factory_.has_pending_fetchers())
return;
FakeAffiliationFetcher* fetcher = fake_fetcher_factory_.PopNextFetcher();
scoped_ptr<AffiliationFetcherDelegate::Result> fake_response(
new AffiliationFetcherDelegate::Result);
for (const auto& preset_equivalence_class : preset_equivalence_relation_) {
bool had_intersection_with_request = false;
for (const auto& requested_facet_uri : fetcher->requested_facet_uris()) {
if (std::find(preset_equivalence_class.begin(),
preset_equivalence_class.end(),
requested_facet_uri) != preset_equivalence_class.end()) {
had_intersection_with_request = true;
break;
}
}
if (had_intersection_with_request)
fake_response->push_back(preset_equivalence_class);
}
fetcher->SimulateSuccess(fake_response.Pass());
}
void ScopedFakeAffiliationAPI::IgnoreNextRequest() {
if (!fake_fetcher_factory_.has_pending_fetchers())
return;
ignore_result(fake_fetcher_factory_.PopNextFetcher());
}
} // namespace password_manager
// 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.
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FAKE_AFFILIATION_API_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FAKE_AFFILIATION_API_H_
#include <vector>
#include "base/macros.h"
#include "components/password_manager/core/browser/affiliation_utils.h"
#include "components/password_manager/core/browser/fake_affiliation_fetcher.h"
namespace password_manager {
// Intercepts all AffiliationFetcher requests while in scope, and manufactures
// API responses based on a set of equivalence classes predefined by the tests.
class ScopedFakeAffiliationAPI {
public:
ScopedFakeAffiliationAPI();
~ScopedFakeAffiliationAPI();
// Adds |affiliated_facets| to the set of equivalence classes that will form
// the basis for calculating the fake API responses.
void AddTestEquivalenceClass(const AffiliatedFacets& affiliated_facets);
// Returns whether or not there is at least one pending fetch.
bool HasPendingRequest();
// Returns the list of facet URIs being looked up by the next pending fetch;
// or an empty list if there are no pending fetches.
std::vector<FacetURI> GetNextRequestedFacets();
// Calculates the response to, and completes the next pending fetch, if any,
// with success.
void ServeNextRequest();
// Ignores the next pending request, if any, without completing it.
void IgnoreNextRequest();
private:
ScopedFakeAffiliationFetcherFactory fake_fetcher_factory_;
std::vector<AffiliatedFacets> preset_equivalence_relation_;
DISALLOW_COPY_AND_ASSIGN(ScopedFakeAffiliationAPI);
};
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FAKE_AFFILIATION_API_H_
...@@ -39,13 +39,18 @@ password_manager::ScopedFakeAffiliationFetcherFactory:: ...@@ -39,13 +39,18 @@ password_manager::ScopedFakeAffiliationFetcherFactory::
AffiliationFetcher::SetFactoryForTesting(nullptr); AffiliationFetcher::SetFactoryForTesting(nullptr);
} }
FakeAffiliationFetcher* ScopedFakeAffiliationFetcherFactory::GetNextFetcher() { FakeAffiliationFetcher* ScopedFakeAffiliationFetcherFactory::PopNextFetcher() {
DCHECK(!pending_fetchers_.empty()); DCHECK(!pending_fetchers_.empty());
FakeAffiliationFetcher* first = pending_fetchers_.front(); FakeAffiliationFetcher* first = pending_fetchers_.front();
pending_fetchers_.pop(); pending_fetchers_.pop();
return first; return first;
} }
FakeAffiliationFetcher* ScopedFakeAffiliationFetcherFactory::PeekNextFetcher() {
DCHECK(!pending_fetchers_.empty());
return pending_fetchers_.front();
}
AffiliationFetcher* ScopedFakeAffiliationFetcherFactory::CreateInstance( AffiliationFetcher* ScopedFakeAffiliationFetcherFactory::CreateInstance(
net::URLRequestContextGetter* request_context_getter, net::URLRequestContextGetter* request_context_getter,
const std::vector<FacetURI>& facet_ids, const std::vector<FacetURI>& facet_ids,
......
...@@ -50,12 +50,17 @@ class ScopedFakeAffiliationFetcherFactory ...@@ -50,12 +50,17 @@ class ScopedFakeAffiliationFetcherFactory
// Returns the next FakeAffiliationFetcher instance previously produced, so // Returns the next FakeAffiliationFetcher instance previously produced, so
// that that the testing code can inject a response and simulate completion // that that the testing code can inject a response and simulate completion
// or failure of the request. // or failure of the request. The fetcher is removed from the queue of pending
// fetchers.
// //
// Note that the factory does not retain ownership of the produced fetchers, // Note that the factory does not retain ownership of the produced fetchers,
// so that the tests should ensure that the corresponding production code will // so that the tests should ensure that the corresponding production code will
// not destroy them before they are accessed here. // not destroy them before they are accessed here.
FakeAffiliationFetcher* GetNextFetcher(); FakeAffiliationFetcher* PopNextFetcher();
// Same as above, but the fetcher is not removed from the queue of pending
// fetchers.
FakeAffiliationFetcher* PeekNextFetcher();
bool has_pending_fetchers() const { return !pending_fetchers_.empty(); } bool has_pending_fetchers() const { return !pending_fetchers_.empty(); }
......
// 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/mock_affiliation_consumer.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
namespace password_manager {
MockAffiliationConsumer::MockAffiliationConsumer() {
EXPECT_CALL(*this, OnResultCallback(testing::_, testing::_)).Times(0);
}
MockAffiliationConsumer::~MockAffiliationConsumer() {
}
void MockAffiliationConsumer::ExpectSuccessWithResult(
const AffiliatedFacets& expected_result) {
EXPECT_CALL(*this, OnResultCallback(
testing::UnorderedElementsAreArray(expected_result),
true)).Times(1);
}
void MockAffiliationConsumer::ExpectFailure() {
EXPECT_CALL(*this, OnResultCallback(testing::UnorderedElementsAre(), false))
.Times(1);
}
AffiliationService::ResultCallback
MockAffiliationConsumer::GetResultCallback() {
return base::Bind(&MockAffiliationConsumer::OnResultCallback,
base::Unretained(this));
}
} // namespace password_manager
// 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.
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_AFFILIATION_CONSUMER_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_AFFILIATION_CONSUMER_H_
#include "base/macros.h"
#include "components/password_manager/core/browser/affiliation_service.h"
#include "components/password_manager/core/browser/affiliation_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace password_manager {
// A mock consumer of AffiliationService::GetAffiliations().
class MockAffiliationConsumer {
public:
MockAffiliationConsumer();
~MockAffiliationConsumer();
// Expects that the result callback will be called exactly once and that it
// will indicate success and return |expected_result|.
void ExpectSuccessWithResult(const AffiliatedFacets& expected_result);
// Expects that the result callback will be called exactly once and that it
// will indicate a failed lookup.
void ExpectFailure();
AffiliationService::ResultCallback GetResultCallback();
private:
MOCK_METHOD2(OnResultCallback, void(const AffiliatedFacets&, bool));
DISALLOW_COPY_AND_ASSIGN(MockAffiliationConsumer);
};
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_MOCK_AFFILIATION_CONSUMER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment