Commit 4865f4b3 authored by engedy's avatar engedy Committed by Commit bot

Integrate throttling logic into AffiliationBackend.

This CL also changes ownership of the TickClock object used by the AffiliationFetchThrottler, so that it is now owned by the AffiliationBackend.

Furthermore, this CL makes the AffiliationBackend no longer rely on using ThreadTaskRunnerHandle to schedule asynchronous operations, but instead take a SingleThreadTaskRunner to use in its constructor. This allows delayed tasks (which are now present) to be posted to the correct task runner in tests too (namely, in AffiliationServiceTests).

BUG=437865

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

Cr-Commit-Position: refs/heads/master@{#320887}
parent 579a70c5
...@@ -9,12 +9,12 @@ ...@@ -9,12 +9,12 @@
#include "base/bind.h" #include "base/bind.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/task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
#include "base/time/clock.h" #include "base/time/clock.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "components/password_manager/core/browser/affiliation_database.h" #include "components/password_manager/core/browser/affiliation_database.h"
#include "components/password_manager/core/browser/affiliation_fetch_throttler.h"
#include "components/password_manager/core/browser/affiliation_fetcher.h" #include "components/password_manager/core/browser/affiliation_fetcher.h"
#include "components/password_manager/core/browser/facet_manager.h" #include "components/password_manager/core/browser/facet_manager.h"
#include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_context_getter.h"
...@@ -23,9 +23,13 @@ namespace password_manager { ...@@ -23,9 +23,13 @@ namespace password_manager {
AffiliationBackend::AffiliationBackend( AffiliationBackend::AffiliationBackend(
const scoped_refptr<net::URLRequestContextGetter>& request_context_getter, const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
scoped_ptr<base::Clock> time_source) const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
scoped_ptr<base::Clock> time_source,
scoped_ptr<base::TickClock> time_tick_source)
: request_context_getter_(request_context_getter), : request_context_getter_(request_context_getter),
task_runner_(task_runner),
clock_(time_source.Pass()), clock_(time_source.Pass()),
tick_clock_(time_tick_source.Pass()),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
DCHECK_LT(base::Time(), clock_->Now()); DCHECK_LT(base::Time(), clock_->Now());
} }
...@@ -35,6 +39,11 @@ AffiliationBackend::~AffiliationBackend() { ...@@ -35,6 +39,11 @@ AffiliationBackend::~AffiliationBackend() {
void AffiliationBackend::Initialize(const base::FilePath& db_path) { void AffiliationBackend::Initialize(const base::FilePath& db_path) {
thread_checker_.reset(new base::ThreadChecker); thread_checker_.reset(new base::ThreadChecker);
DCHECK(!throttler_);
throttler_.reset(
new AffiliationFetchThrottler(this, task_runner_, tick_clock_.get()));
cache_.reset(new AffiliationDatabase()); cache_.reset(new AffiliationDatabase());
if (!cache_->Init(db_path)) { if (!cache_->Init(db_path)) {
// TODO(engedy): Implement this. crbug.com/437865. // TODO(engedy): Implement this. crbug.com/437865.
...@@ -99,20 +108,6 @@ FacetManager* AffiliationBackend::GetOrCreateFacetManager( ...@@ -99,20 +108,6 @@ FacetManager* AffiliationBackend::GetOrCreateFacetManager(
return facet_managers_.get(facet_uri); return facet_managers_.get(facet_uri);
} }
void AffiliationBackend::SendNetworkRequest() {
DCHECK(!fetcher_);
std::vector<FacetURI> requested_facet_uris;
for (const auto& facet_manager_pair : facet_managers_) {
if (facet_manager_pair.second->DoesRequireFetch())
requested_facet_uris.push_back(facet_manager_pair.first);
}
DCHECK(!requested_facet_uris.empty());
fetcher_.reset(AffiliationFetcher::Create(request_context_getter_.get(),
requested_facet_uris, this));
fetcher_->StartRequest();
}
void AffiliationBackend::OnSendNotification(const FacetURI& facet_uri) { void AffiliationBackend::OnSendNotification(const FacetURI& facet_uri) {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
...@@ -132,17 +127,14 @@ bool AffiliationBackend::ReadAffiliationsFromDatabase( ...@@ -132,17 +127,14 @@ bool AffiliationBackend::ReadAffiliationsFromDatabase(
} }
void AffiliationBackend::SignalNeedNetworkRequest() { void AffiliationBackend::SignalNeedNetworkRequest() {
// TODO(engedy): Add more sophisticated throttling logic. crbug.com/437865. throttler_->SignalNetworkRequestNeeded();
if (fetcher_)
return;
SendNetworkRequest();
} }
void AffiliationBackend::RequestNotificationAtTime(const FacetURI& facet_uri, void AffiliationBackend::RequestNotificationAtTime(const FacetURI& facet_uri,
base::Time time) { base::Time time) {
// TODO(engedy): Avoid spamming the task runner; only ever schedule the first // TODO(engedy): Avoid spamming the task runner; only ever schedule the first
// callback. crbug.com/437865. // callback. crbug.com/437865.
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( task_runner_->PostDelayedTask(
FROM_HERE, base::Bind(&AffiliationBackend::OnSendNotification, FROM_HERE, base::Bind(&AffiliationBackend::OnSendNotification,
weak_ptr_factory_.GetWeakPtr(), facet_uri), weak_ptr_factory_.GetWeakPtr(), facet_uri),
time - clock_->Now()); time - clock_->Now());
...@@ -151,7 +143,9 @@ void AffiliationBackend::RequestNotificationAtTime(const FacetURI& facet_uri, ...@@ -151,7 +143,9 @@ void AffiliationBackend::RequestNotificationAtTime(const FacetURI& facet_uri,
void AffiliationBackend::OnFetchSucceeded( void AffiliationBackend::OnFetchSucceeded(
scoped_ptr<AffiliationFetcherDelegate::Result> result) { scoped_ptr<AffiliationFetcherDelegate::Result> result) {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
fetcher_.reset(); fetcher_.reset();
throttler_->InformOfNetworkRequestComplete(true);
for (const AffiliatedFacets& affiliated_facets : *result) { for (const AffiliatedFacets& affiliated_facets : *result) {
AffiliatedFacetsWithUpdateTime affiliation; AffiliatedFacetsWithUpdateTime affiliation;
...@@ -182,7 +176,7 @@ void AffiliationBackend::OnFetchSucceeded( ...@@ -182,7 +176,7 @@ void AffiliationBackend::OnFetchSucceeded(
// requests came in while the current fetch was in flight. // requests came in while the current fetch was in flight.
for (const auto& facet_manager_pair : facet_managers_) { for (const auto& facet_manager_pair : facet_managers_) {
if (facet_manager_pair.second->DoesRequireFetch()) { if (facet_manager_pair.second->DoesRequireFetch()) {
SendNetworkRequest(); throttler_->SignalNetworkRequestNeeded();
return; return;
} }
} }
...@@ -191,15 +185,46 @@ void AffiliationBackend::OnFetchSucceeded( ...@@ -191,15 +185,46 @@ void AffiliationBackend::OnFetchSucceeded(
void AffiliationBackend::OnFetchFailed() { void AffiliationBackend::OnFetchFailed() {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
// TODO(engedy): Implement this. crbug.com/437865. fetcher_.reset();
NOTIMPLEMENTED(); throttler_->InformOfNetworkRequestComplete(false);
// Trigger a retry if a fetch is still needed.
for (const auto& facet_manager_pair : facet_managers_) {
if (facet_manager_pair.second->DoesRequireFetch()) {
throttler_->SignalNetworkRequestNeeded();
return;
}
}
} }
void AffiliationBackend::OnMalformedResponse() { void AffiliationBackend::OnMalformedResponse() {
DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread());
// TODO(engedy): Implement this. crbug.com/437865. // TODO(engedy): Potentially handle this case differently. crbug.com/437865.
NOTIMPLEMENTED(); OnFetchFailed();
}
bool AffiliationBackend::OnCanSendNetworkRequest() {
DCHECK(!fetcher_);
std::vector<FacetURI> requested_facet_uris;
for (const auto& facet_manager_pair : facet_managers_) {
if (facet_manager_pair.second->DoesRequireFetch())
requested_facet_uris.push_back(facet_manager_pair.first);
}
// In case a request is no longer needed, return false to indicate this.
if (requested_facet_uris.empty())
return false;
fetcher_.reset(AffiliationFetcher::Create(request_context_getter_.get(),
requested_facet_uris, this));
fetcher_->StartRequest();
return true;
}
void AffiliationBackend::SetThrottlerForTesting(
scoped_ptr<AffiliationFetchThrottler> throttler) {
throttler_ = throttler.Pass();
} }
} // namespace password_manager } // namespace password_manager
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#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 "base/memory/weak_ptr.h"
#include "components/password_manager/core/browser/affiliation_fetch_throttler_delegate.h"
#include "components/password_manager/core/browser/affiliation_fetcher_delegate.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" #include "components/password_manager/core/browser/affiliation_utils.h"
...@@ -20,7 +21,9 @@ ...@@ -20,7 +21,9 @@
namespace base { namespace base {
class Clock; class Clock;
class FilePath; class FilePath;
class SingleThreadTaskRunner;
class ThreadChecker; class ThreadChecker;
class TickClock;
class Time; class Time;
} // namespace base } // namespace base
...@@ -32,6 +35,7 @@ namespace password_manager { ...@@ -32,6 +35,7 @@ namespace password_manager {
class AffiliationDatabase; class AffiliationDatabase;
class AffiliationFetcher; class AffiliationFetcher;
class AffiliationFetchThrottler;
class FacetManager; class FacetManager;
// The AffiliationBackend is the part of the AffiliationService that lives on a // The AffiliationBackend is the part of the AffiliationService that lives on a
...@@ -43,15 +47,18 @@ class FacetManager; ...@@ -43,15 +47,18 @@ class FacetManager;
// and then transfer it to the background thread for the rest of its life. // and then transfer it to the background thread for the rest of its life.
// Initialize() must be called already on the final (background) thread. // Initialize() must be called already on the final (background) thread.
class AffiliationBackend : public FacetManagerHost, class AffiliationBackend : public FacetManagerHost,
public AffiliationFetcherDelegate { public AffiliationFetcherDelegate,
public AffiliationFetchThrottlerDelegate {
public: public:
// Constructs an instance that will use |request_context_getter| for all // Constructs an instance that will use |request_context_getter| for all
// network requests, and will rely on |time_source| to tell the current time, // network requests, use |task_runner| for asynchronous tasks, and will rely
// which is expected to always be no less than the Unix epoch. // on |time_source| and |time_tick_source| to tell the current time/ticks.
// Construction is very cheap, expensive steps are deferred to Initialize(). // Construction is very cheap, expensive steps are deferred to Initialize().
AffiliationBackend( AffiliationBackend(
const scoped_refptr<net::URLRequestContextGetter>& request_context_getter, const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
scoped_ptr<base::Clock> time_source); const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
scoped_ptr<base::Clock> time_source,
scoped_ptr<base::TickClock> time_tick_source);
~AffiliationBackend() override; ~AffiliationBackend() override;
// Performs the I/O-heavy part of initialization. The database used to cache // Performs the I/O-heavy part of initialization. The database used to cache
...@@ -77,10 +84,6 @@ class AffiliationBackend : public FacetManagerHost, ...@@ -77,10 +84,6 @@ class AffiliationBackend : public FacetManagerHost,
// storing it into |facet_managers_| if it did not exist. // storing it into |facet_managers_| if it did not exist.
FacetManager* GetOrCreateFacetManager(const FacetURI& facet_uri); FacetManager* GetOrCreateFacetManager(const FacetURI& facet_uri);
// Collects facet URIs that require fetching and issues a network request
// against the Affiliation API to fetch corresponding affiliation information.
void SendNetworkRequest();
// Scheduled by RequestNotificationAtTime() to be called back at times when a // Scheduled by RequestNotificationAtTime() to be called back at times when a
// FacetManager needs to be notified. // FacetManager needs to be notified.
void OnSendNotification(const FacetURI& facet_uri); void OnSendNotification(const FacetURI& facet_uri);
...@@ -99,20 +102,28 @@ class AffiliationBackend : public FacetManagerHost, ...@@ -99,20 +102,28 @@ class AffiliationBackend : public FacetManagerHost,
void OnFetchFailed() override; void OnFetchFailed() override;
void OnMalformedResponse() override; void OnMalformedResponse() override;
// Used only for testing. // AffiliationFetchThrottlerDelegate:
bool OnCanSendNetworkRequest() override;
// Returns the number of in-memory FacetManagers. Used only for testing.
size_t facet_manager_count_for_testing() { return facet_managers_.size(); } size_t facet_manager_count_for_testing() { return facet_managers_.size(); }
// To be called after Initialize() to use |throttler| instead of the default
// one. Used only for testing.
void SetThrottlerForTesting(scoped_ptr<AffiliationFetchThrottler> throttler);
// Created in Initialize(), and ensures that all subsequent methods are called // Created in Initialize(), and ensures that all subsequent methods are called
// on the same thread. // on the same thread.
scoped_ptr<base::ThreadChecker> thread_checker_; scoped_ptr<base::ThreadChecker> thread_checker_;
scoped_refptr<net::URLRequestContextGetter> request_context_getter_; scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
// Will always return a Now() that is strictly greater than the NULL time.
scoped_ptr<base::Clock> clock_; scoped_ptr<base::Clock> clock_;
scoped_ptr<base::TickClock> tick_clock_;
scoped_ptr<AffiliationDatabase> cache_; scoped_ptr<AffiliationDatabase> cache_;
scoped_ptr<AffiliationFetcher> fetcher_; scoped_ptr<AffiliationFetcher> fetcher_;
scoped_ptr<AffiliationFetchThrottler> throttler_;
// Contains a FacetManager for each facet URI that need ongoing attention. To // Contains a FacetManager for each facet URI that need ongoing attention. To
// save memory, managers are discarded as soon as they become redundant. // save memory, managers are discarded as soon as they become redundant.
......
...@@ -11,8 +11,10 @@ ...@@ -11,8 +11,10 @@
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/test/test_mock_time_task_runner.h" #include "base/test/test_mock_time_task_runner.h"
#include "base/test/test_simple_task_runner.h" #include "base/test/test_simple_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "base/time/clock.h" #include "base/time/clock.h"
#include "base/time/tick_clock.h"
#include "components/password_manager/core/browser/affiliation_fetch_throttler.h"
#include "components/password_manager/core/browser/affiliation_fetch_throttler_delegate.h"
#include "components/password_manager/core/browser/facet_manager.h" #include "components/password_manager/core/browser/facet_manager.h"
#include "components/password_manager/core/browser/fake_affiliation_api.h" #include "components/password_manager/core/browser/fake_affiliation_api.h"
#include "components/password_manager/core/browser/mock_affiliation_consumer.h" #include "components/password_manager/core/browser/mock_affiliation_consumer.h"
...@@ -24,6 +26,70 @@ namespace password_manager { ...@@ -24,6 +26,70 @@ namespace password_manager {
namespace { namespace {
// Mock fetch throttler that has some extra logic to accurately portray the real
// AffiliationFetchThrottler in how it ignores SignalNetworkRequestNeeded()
// requests when a request is already known to be needed or one is already in
// flight, and in how it goes back to the idle state afterwards.
class MockAffiliationFetchThrottler : public AffiliationFetchThrottler {
public:
MockAffiliationFetchThrottler(AffiliationFetchThrottlerDelegate* delegate)
: AffiliationFetchThrottler(delegate, nullptr, nullptr),
signaled_network_request_needed_(false) {
EXPECT_CALL(*this, OnInformOfNetworkRequestComplete(testing::_)).Times(0);
}
~MockAffiliationFetchThrottler() {
EXPECT_FALSE(signaled_network_request_needed_);
}
// Expects that InformOfNetworkRequestComplete() will be called to indicate
// either success or failure, depending on |expected_success|.
void ExpectInformOfNetworkRequestComplete(bool expected_success) {
EXPECT_CALL(*this, OnInformOfNetworkRequestComplete(expected_success));
}
// Informs the |delegate_| that it can send the needed network request.
// Returns true if the |delegate_| reported that it actually ended up issuing
// a request.
bool LetNetworkRequestBeSent() {
EXPECT_TRUE(has_signaled_network_request_needed());
if (!delegate_->OnCanSendNetworkRequest()) {
reset_signaled_network_request_needed();
return false;
}
return true;
}
// Whether or not the throttler is 'signaled', meaning that the real throttler
// would eventually call OnCanSendNetworkRequest() on the |delegate_|.
bool has_signaled_network_request_needed() const {
return signaled_network_request_needed_;
}
// Forces the mock throttler back to 'non-signaled' state. Normally, this does
// not need to be manually called, as this is done by the mock automatically.
void reset_signaled_network_request_needed() {
signaled_network_request_needed_ = false;
}
private:
MOCK_METHOD1(OnInformOfNetworkRequestComplete, void(bool));
// AffiliationFetchThrottler:
void SignalNetworkRequestNeeded() override {
signaled_network_request_needed_ = true;
}
void InformOfNetworkRequestComplete(bool success) override {
OnInformOfNetworkRequestComplete(success);
reset_signaled_network_request_needed();
}
bool signaled_network_request_needed_;
DISALLOW_COPY_AND_ASSIGN(MockAffiliationFetchThrottler);
};
const char kTestFacetURIAlpha1[] = "https://one.alpha.example.com"; const char kTestFacetURIAlpha1[] = "https://one.alpha.example.com";
const char kTestFacetURIAlpha2[] = "https://two.alpha.example.com"; const char kTestFacetURIAlpha2[] = "https://two.alpha.example.com";
const char kTestFacetURIAlpha3[] = "https://three.alpha.example.com"; const char kTestFacetURIAlpha3[] = "https://three.alpha.example.com";
...@@ -60,6 +126,10 @@ base::TimeDelta GetCacheSoftExpiryPeriod() { ...@@ -60,6 +126,10 @@ base::TimeDelta GetCacheSoftExpiryPeriod() {
return base::TimeDelta::FromHours(FacetManager::kCacheSoftExpiryInHours); return base::TimeDelta::FromHours(FacetManager::kCacheSoftExpiryInHours);
} }
base::TimeDelta GetShortTestPeriod() {
return base::TimeDelta::FromHours(1);
}
// Returns a smallest time difference that this test cares about. // Returns a smallest time difference that this test cares about.
base::TimeDelta Epsilon() { base::TimeDelta Epsilon() {
return base::TimeDelta::FromMicroseconds(1); return base::TimeDelta::FromMicroseconds(1);
...@@ -70,12 +140,9 @@ base::TimeDelta Epsilon() { ...@@ -70,12 +140,9 @@ base::TimeDelta Epsilon() {
class AffiliationBackendTest : public testing::Test { class AffiliationBackendTest : public testing::Test {
public: public:
AffiliationBackendTest() AffiliationBackendTest()
: consumer_task_runner_(new base::TestSimpleTaskRunner), : backend_task_runner_(new base::TestMockTimeTaskRunner),
backend_task_runner_(new base::TestMockTimeTaskRunner), consumer_task_runner_(new base::TestSimpleTaskRunner),
backend_task_runner_handle_(backend_task_runner_), mock_fetch_throttler_(nullptr) {}
backend_(new AffiliationBackend(NULL,
backend_task_runner_->GetMockClock())) {
}
~AffiliationBackendTest() override {} ~AffiliationBackendTest() override {}
protected: protected:
...@@ -95,15 +162,45 @@ class AffiliationBackendTest : public testing::Test { ...@@ -95,15 +162,45 @@ class AffiliationBackendTest : public testing::Test {
backend_->CancelPrefetch(facet_uri, keep_fresh_until); backend_->CancelPrefetch(facet_uri, keep_fresh_until);
} }
void ExpectNeedForFetchAndLetItBeSent() {
ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
ASSERT_TRUE(mock_fetch_throttler()->LetNetworkRequestBeSent());
}
void ExpectAndCompleteFetch(
const std::vector<FacetURI>& expected_requested_facet_uris) {
ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
EXPECT_THAT(
fake_affiliation_api()->GetNextRequestedFacets(),
testing::UnorderedElementsAreArray(expected_requested_facet_uris));
mock_fetch_throttler()->ExpectInformOfNetworkRequestComplete(true);
fake_affiliation_api()->ServeNextRequest();
testing::Mock::VerifyAndClearExpectations(mock_fetch_throttler());
}
void ExpectAndCompleteFetch(const FacetURI& expected_requested_facet_uri) { void ExpectAndCompleteFetch(const FacetURI& expected_requested_facet_uri) {
std::vector<FacetURI> expected_facet_uris;
expected_facet_uris.push_back(expected_requested_facet_uri);
ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(expected_facet_uris));
}
void ExpectAndFailFetch(const FacetURI& expected_requested_facet_uri) {
ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest()); ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
EXPECT_THAT(fake_affiliation_api()->GetNextRequestedFacets(), EXPECT_THAT(fake_affiliation_api()->GetNextRequestedFacets(),
testing::UnorderedElementsAre(expected_requested_facet_uri)); testing::UnorderedElementsAre(expected_requested_facet_uri));
fake_affiliation_api()->ServeNextRequest(); mock_fetch_throttler()->ExpectInformOfNetworkRequestComplete(false);
fake_affiliation_api()->FailNextRequest();
testing::Mock::VerifyAndClearExpectations(mock_fetch_throttler());
} }
void ExpectFailureWithoutFetch(MockAffiliationConsumer* consumer) { void ExpectNoFetchNeeded() {
ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest()); ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
ASSERT_FALSE(mock_fetch_throttler()->has_signaled_network_request_needed());
}
void ExpectFailureWithoutFetch(MockAffiliationConsumer* consumer) {
ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
consumer->ExpectFailure(); consumer->ExpectFailure();
consumer_task_runner_->RunUntilIdle(); consumer_task_runner_->RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(consumer); testing::Mock::VerifyAndClearExpectations(consumer);
...@@ -113,6 +210,7 @@ class AffiliationBackendTest : public testing::Test { ...@@ -113,6 +210,7 @@ class AffiliationBackendTest : public testing::Test {
const FacetURI& facet_uri, const FacetURI& facet_uri,
const AffiliatedFacets& expected_result) { const AffiliatedFacets& expected_result) {
GetAffiliations(mock_consumer(), facet_uri, false); GetAffiliations(mock_consumer(), facet_uri, false);
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri)); ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri));
mock_consumer()->ExpectSuccessWithResult(expected_result); mock_consumer()->ExpectSuccessWithResult(expected_result);
consumer_task_runner_->RunUntilIdle(); consumer_task_runner_->RunUntilIdle();
...@@ -124,7 +222,7 @@ class AffiliationBackendTest : public testing::Test { ...@@ -124,7 +222,7 @@ class AffiliationBackendTest : public testing::Test {
bool cached_only, bool cached_only,
const AffiliatedFacets& expected_result) { const AffiliatedFacets& expected_result) {
GetAffiliations(mock_consumer(), facet_uri, cached_only); GetAffiliations(mock_consumer(), facet_uri, cached_only);
ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest()); ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
mock_consumer()->ExpectSuccessWithResult(expected_result); mock_consumer()->ExpectSuccessWithResult(expected_result);
consumer_task_runner_->RunUntilIdle(); consumer_task_runner_->RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(mock_consumer()); testing::Mock::VerifyAndClearExpectations(mock_consumer());
...@@ -138,6 +236,7 @@ class AffiliationBackendTest : public testing::Test { ...@@ -138,6 +236,7 @@ class AffiliationBackendTest : public testing::Test {
void PrefetchAndExpectFetch(const FacetURI& facet_uri, void PrefetchAndExpectFetch(const FacetURI& facet_uri,
base::Time keep_fresh_until) { base::Time keep_fresh_until) {
Prefetch(facet_uri, keep_fresh_until); Prefetch(facet_uri, keep_fresh_until);
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri)); ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri));
} }
...@@ -181,8 +280,6 @@ class AffiliationBackendTest : public testing::Test { ...@@ -181,8 +280,6 @@ class AffiliationBackendTest : public testing::Test {
return backend_task_runner_.get(); return backend_task_runner_.get();
} }
MockAffiliationConsumer* mock_consumer() { return &mock_consumer_; }
base::TestSimpleTaskRunner* consumer_task_runner() { base::TestSimpleTaskRunner* consumer_task_runner() {
return consumer_task_runner_.get(); return consumer_task_runner_.get();
} }
...@@ -191,12 +288,24 @@ class AffiliationBackendTest : public testing::Test { ...@@ -191,12 +288,24 @@ class AffiliationBackendTest : public testing::Test {
return &fake_affiliation_api_; return &fake_affiliation_api_;
} }
MockAffiliationConsumer* mock_consumer() { return &mock_consumer_; }
MockAffiliationFetchThrottler* mock_fetch_throttler() {
return mock_fetch_throttler_;
}
private: private:
// testing::Test: // testing::Test:
void SetUp() override { void SetUp() override {
base::FilePath database_path; base::FilePath database_path;
ASSERT_TRUE(CreateTemporaryFile(&database_path)); ASSERT_TRUE(CreateTemporaryFile(&database_path));
backend_.reset(new AffiliationBackend(
NULL, backend_task_runner_, backend_task_runner_->GetMockClock(),
backend_task_runner_->GetMockTickClock()));
backend_->Initialize(database_path); backend_->Initialize(database_path);
mock_fetch_throttler_ = new MockAffiliationFetchThrottler(backend_.get());
backend_->SetThrottlerForTesting(
make_scoped_ptr<AffiliationFetchThrottler>(mock_fetch_throttler_));
fake_affiliation_api_.AddTestEquivalenceClass( fake_affiliation_api_.AddTestEquivalenceClass(
GetTestEquivalenceClassAlpha()); GetTestEquivalenceClassAlpha());
...@@ -206,14 +315,13 @@ class AffiliationBackendTest : public testing::Test { ...@@ -206,14 +315,13 @@ class AffiliationBackendTest : public testing::Test {
GetTestEquivalenceClassGamma()); GetTestEquivalenceClassGamma());
} }
ScopedFakeAffiliationAPI fake_affiliation_api_;
MockAffiliationConsumer mock_consumer_;
scoped_refptr<base::TestSimpleTaskRunner> consumer_task_runner_;
scoped_refptr<base::TestMockTimeTaskRunner> backend_task_runner_; scoped_refptr<base::TestMockTimeTaskRunner> backend_task_runner_;
base::ThreadTaskRunnerHandle backend_task_runner_handle_; scoped_refptr<base::TestSimpleTaskRunner> consumer_task_runner_;
ScopedFakeAffiliationAPI fake_affiliation_api_;
MockAffiliationConsumer mock_consumer_;
scoped_ptr<AffiliationBackend> backend_; scoped_ptr<AffiliationBackend> backend_;
MockAffiliationFetchThrottler* mock_fetch_throttler_; // Owned by |backend_|.
DISALLOW_COPY_AND_ASSIGN(AffiliationBackendTest); DISALLOW_COPY_AND_ASSIGN(AffiliationBackendTest);
}; };
...@@ -248,30 +356,36 @@ TEST_F(AffiliationBackendTest, ExpiredPrefetchTriggersNoInitialFetch) { ...@@ -248,30 +356,36 @@ TEST_F(AffiliationBackendTest, ExpiredPrefetchTriggersNoInitialFetch) {
// already expired. // already expired.
Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
backend_task_runner()->Now()); backend_task_runner()->Now());
ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
EXPECT_EQ(0u, backend_facet_manager_count()); EXPECT_EQ(0u, backend_facet_manager_count());
EXPECT_FALSE(fake_affiliation_api()->HasPendingRequest());
EXPECT_FALSE(backend_task_runner()->HasPendingTask()); EXPECT_FALSE(backend_task_runner()->HasPendingTask());
} }
// One additional GetAffiliations() and one Prefetch() request for unrelated // One additional GetAffiliations() and one Prefetch() request come in, both for
// facets come in while the network fetch triggered by the first request is in // unrelated facets, shortly after an initial GetAffiliations() request.
// flight. There should be no simultaneous requests, and the additional facets //
// should be queried together in a second fetch after the first fetch completes. // Suppose that the network request triggered by the first GetAffiliations()
// request has already been initiated when the other requests arrive. As there
// should be no simultaneous requests, the additional facets should be queried
// together in a second fetch after the first fetch completes.
TEST_F(AffiliationBackendTest, ConcurrentUnrelatedRequests) { TEST_F(AffiliationBackendTest, ConcurrentUnrelatedRequests) {
FacetURI facet_alpha(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)); FacetURI facet_alpha(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
FacetURI facet_beta(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1)); FacetURI facet_beta(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
FacetURI facet_gamma(FacetURI::FromCanonicalSpec(kTestFacetURIGamma1)); FacetURI facet_gamma(FacetURI::FromCanonicalSpec(kTestFacetURIGamma1));
// Pretend the fetch is already away when the two other requests come in.
MockAffiliationConsumer second_consumer; MockAffiliationConsumer second_consumer;
GetAffiliations(mock_consumer(), facet_alpha, false); GetAffiliations(mock_consumer(), facet_alpha, false);
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
GetAffiliations(&second_consumer, facet_beta, false); GetAffiliations(&second_consumer, facet_beta, false);
Prefetch(facet_gamma, base::Time::Max()); Prefetch(facet_gamma, base::Time::Max());
std::vector<FacetURI> second_fetch_uris;
second_fetch_uris.push_back(facet_beta);
second_fetch_uris.push_back(facet_gamma);
ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_alpha)); ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_alpha));
ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest()); ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
EXPECT_THAT(fake_affiliation_api()->GetNextRequestedFacets(), ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(second_fetch_uris));
testing::UnorderedElementsAre(facet_beta, facet_gamma));
fake_affiliation_api()->ServeNextRequest();
mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha()); mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassBeta()); second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassBeta());
...@@ -284,6 +398,72 @@ TEST_F(AffiliationBackendTest, ConcurrentUnrelatedRequests) { ...@@ -284,6 +398,72 @@ TEST_F(AffiliationBackendTest, ConcurrentUnrelatedRequests) {
EXPECT_GE(1u, backend_facet_manager_count()); EXPECT_GE(1u, backend_facet_manager_count());
} }
// Now suppose that the first fetch is somewhat delayed (e.g., because network
// requests are throttled), so the other requests arrive before it is actually
// issued. In this case, all facet URIs should be queried together in one fetch.
TEST_F(AffiliationBackendTest, ConcurrentUnrelatedRequests2) {
FacetURI facet_alpha(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
FacetURI facet_beta(FacetURI::FromCanonicalSpec(kTestFacetURIBeta1));
FacetURI facet_gamma(FacetURI::FromCanonicalSpec(kTestFacetURIGamma1));
MockAffiliationConsumer second_consumer;
GetAffiliations(mock_consumer(), facet_alpha, false);
GetAffiliations(&second_consumer, facet_beta, false);
Prefetch(facet_gamma, base::Time::Max());
std::vector<FacetURI> fetched_uris;
fetched_uris.push_back(facet_alpha);
fetched_uris.push_back(facet_beta);
fetched_uris.push_back(facet_gamma);
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(fetched_uris));
ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassBeta());
consumer_task_runner()->RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(mock_consumer());
// Now that the two GetAffiliation() requests have been completed, the first
// two FacetManagers should be discarded. The third FacetManager corresponding
// to the prefetched facet should be kept.
EXPECT_GE(1u, backend_facet_manager_count());
}
TEST_F(AffiliationBackendTest, RetryIsMadeOnFailedFetch) {
FacetURI facet_uri(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1));
GetAffiliations(mock_consumer(), facet_uri, false /* cached_only */);
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
ASSERT_NO_FATAL_FAILURE(ExpectAndFailFetch(facet_uri));
EXPECT_EQ(1u, backend_facet_manager_count());
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri));
mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
consumer_task_runner()->RunUntilIdle();
testing::Mock::VerifyAndClearExpectations(mock_consumer());
EXPECT_EQ(0u, backend_facet_manager_count());
}
// The Prefetch() request expires before fetching corresponding affiliation
// information would be allowed. The fetch should be abandoned.
TEST_F(AffiliationBackendTest, FetchIsNoLongerNeededOnceAllowed) {
Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
backend_task_runner()->Now() + GetShortTestPeriod());
ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest());
AdvanceTime(GetShortTestPeriod() + Epsilon());
bool did_send_request = mock_fetch_throttler()->LetNetworkRequestBeSent();
EXPECT_FALSE(did_send_request);
ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
EXPECT_EQ(0u, backend_facet_manager_count());
}
TEST_F(AffiliationBackendTest, CacheServesSubsequentRequestForSameFacet) { TEST_F(AffiliationBackendTest, CacheServesSubsequentRequestForSameFacet) {
ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult( ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFetchAndThenResult(
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
...@@ -347,11 +527,12 @@ TEST_F(AffiliationBackendTest, ...@@ -347,11 +527,12 @@ TEST_F(AffiliationBackendTest,
MockAffiliationConsumer second_consumer; MockAffiliationConsumer second_consumer;
MockAffiliationConsumer third_consumer; MockAffiliationConsumer third_consumer;
GetAffiliations(mock_consumer(), facet_uri1, false); GetAffiliations(mock_consumer(), facet_uri1, false);
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
GetAffiliations(&second_consumer, facet_uri1, false); GetAffiliations(&second_consumer, facet_uri1, false);
GetAffiliations(&third_consumer, facet_uri2, false); GetAffiliations(&third_consumer, facet_uri2, false);
ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri1)); ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri1));
ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest()); ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha()); mock_consumer()->ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassAlpha()); second_consumer.ExpectSuccessWithResult(GetTestEquivalenceClassAlpha());
...@@ -374,11 +555,12 @@ TEST_F(AffiliationBackendTest, ...@@ -374,11 +555,12 @@ TEST_F(AffiliationBackendTest,
FacetURI facet_uri2(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2)); FacetURI facet_uri2(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2));
Prefetch(facet_uri1, base::Time::Max()); Prefetch(facet_uri1, base::Time::Max());
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
Prefetch(facet_uri1, base::Time::Max()); Prefetch(facet_uri1, base::Time::Max());
Prefetch(facet_uri2, base::Time::Max()); Prefetch(facet_uri2, base::Time::Max());
ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri1)); ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(facet_uri1));
ASSERT_FALSE(fake_affiliation_api()->HasPendingRequest()); ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache( ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
GetTestEquivalenceClassAlpha())); GetTestEquivalenceClassAlpha()));
...@@ -427,7 +609,7 @@ TEST_F(AffiliationBackendTest, ...@@ -427,7 +609,7 @@ TEST_F(AffiliationBackendTest,
AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon()); AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon());
EXPECT_FALSE(fake_affiliation_api()->HasPendingRequest()); ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
EXPECT_FALSE(IsCachedDataNearStaleForFacet( EXPECT_FALSE(IsCachedDataNearStaleForFacet(
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))); FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
...@@ -435,6 +617,7 @@ TEST_F(AffiliationBackendTest, ...@@ -435,6 +617,7 @@ TEST_F(AffiliationBackendTest,
EXPECT_TRUE(IsCachedDataNearStaleForFacet( EXPECT_TRUE(IsCachedDataNearStaleForFacet(
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))); FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
ASSERT_NO_FATAL_FAILURE( ASSERT_NO_FATAL_FAILURE(
ExpectAndCompleteFetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))); ExpectAndCompleteFetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
...@@ -452,8 +635,8 @@ TEST_F(AffiliationBackendTest, ...@@ -452,8 +635,8 @@ TEST_F(AffiliationBackendTest,
// The data should be allowed to expire and the FacetManager discarded. // The data should be allowed to expire and the FacetManager discarded.
EXPECT_FALSE(IsCachedDataFreshForFacet( EXPECT_FALSE(IsCachedDataFreshForFacet(
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))); FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
EXPECT_EQ(0u, backend_facet_manager_count()); EXPECT_EQ(0u, backend_facet_manager_count());
EXPECT_FALSE(fake_affiliation_api()->HasPendingRequest());
EXPECT_FALSE(backend_task_runner()->HasPendingTask()); EXPECT_FALSE(backend_task_runner()->HasPendingTask());
ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFailureWithoutFetch( ASSERT_NO_FATAL_FAILURE(GetAffiliationsAndExpectFailureWithoutFetch(
...@@ -479,7 +662,7 @@ TEST_F(AffiliationBackendTest, PrefetchTriggersPeriodicRefetch) { ...@@ -479,7 +662,7 @@ TEST_F(AffiliationBackendTest, PrefetchTriggersPeriodicRefetch) {
AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon()); AdvanceTime(GetCacheSoftExpiryPeriod() - Epsilon());
EXPECT_FALSE(fake_affiliation_api()->HasPendingRequest()); ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
EXPECT_TRUE(IsCachedDataFreshForFacet( EXPECT_TRUE(IsCachedDataFreshForFacet(
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))); FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
EXPECT_FALSE(IsCachedDataNearStaleForFacet( EXPECT_FALSE(IsCachedDataNearStaleForFacet(
...@@ -491,6 +674,7 @@ TEST_F(AffiliationBackendTest, PrefetchTriggersPeriodicRefetch) { ...@@ -491,6 +674,7 @@ TEST_F(AffiliationBackendTest, PrefetchTriggersPeriodicRefetch) {
EXPECT_TRUE(IsCachedDataNearStaleForFacet( EXPECT_TRUE(IsCachedDataNearStaleForFacet(
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))); FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch( ASSERT_NO_FATAL_FAILURE(ExpectAndCompleteFetch(
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))); FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache( ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
...@@ -508,7 +692,7 @@ TEST_F(AffiliationBackendTest, ...@@ -508,7 +692,7 @@ TEST_F(AffiliationBackendTest,
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))); FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max()); Prefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), base::Time::Max());
EXPECT_FALSE(fake_affiliation_api()->HasPendingRequest()); ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache( ASSERT_NO_FATAL_FAILURE(ExpectThatEquivalenceClassIsServedFromCache(
GetTestEquivalenceClassAlpha())); GetTestEquivalenceClassAlpha()));
} }
...@@ -522,8 +706,8 @@ TEST_F(AffiliationBackendTest, CancelPrefetch) { ...@@ -522,8 +706,8 @@ TEST_F(AffiliationBackendTest, CancelPrefetch) {
// Cancel the prefetch the last microsecond before a refetch would take place. // Cancel the prefetch the last microsecond before a refetch would take place.
backend()->CancelPrefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), backend()->CancelPrefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
base::Time::Max()); base::Time::Max());
ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
EXPECT_EQ(0u, backend_facet_manager_count()); EXPECT_EQ(0u, backend_facet_manager_count());
EXPECT_FALSE(fake_affiliation_api()->HasPendingRequest());
EXPECT_TRUE(backend_task_runner()->HasPendingTask()); EXPECT_TRUE(backend_task_runner()->HasPendingTask());
AdvanceTime(GetCacheHardExpiryPeriod() - GetCacheSoftExpiryPeriod() + AdvanceTime(GetCacheHardExpiryPeriod() - GetCacheSoftExpiryPeriod() +
...@@ -557,6 +741,7 @@ TEST_F(AffiliationBackendTest, CancelDuplicatePrefetch) { ...@@ -557,6 +741,7 @@ TEST_F(AffiliationBackendTest, CancelDuplicatePrefetch) {
EXPECT_EQ(1u, backend_facet_manager_count()); EXPECT_EQ(1u, backend_facet_manager_count());
EXPECT_TRUE(IsCachedDataNearStaleForFacet( EXPECT_TRUE(IsCachedDataNearStaleForFacet(
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))); FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
ASSERT_NO_FATAL_FAILURE(ExpectNeedForFetchAndLetItBeSent());
ASSERT_NO_FATAL_FAILURE( ASSERT_NO_FATAL_FAILURE(
ExpectAndCompleteFetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1))); ExpectAndCompleteFetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1)));
...@@ -572,8 +757,8 @@ TEST_F(AffiliationBackendTest, CancelDuplicatePrefetch) { ...@@ -572,8 +757,8 @@ TEST_F(AffiliationBackendTest, CancelDuplicatePrefetch) {
TEST_F(AffiliationBackendTest, CancelingNonExistingPrefetchIsSilentlyIgnored) { TEST_F(AffiliationBackendTest, CancelingNonExistingPrefetchIsSilentlyIgnored) {
CancelPrefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), CancelPrefetch(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
backend_task_runner()->Now() + base::TimeDelta::FromHours(24)); backend_task_runner()->Now() + base::TimeDelta::FromHours(24));
ASSERT_NO_FATAL_FAILURE(ExpectNoFetchNeeded());
EXPECT_EQ(0u, backend_facet_manager_count()); EXPECT_EQ(0u, backend_facet_manager_count());
EXPECT_FALSE(fake_affiliation_api()->HasPendingRequest());
EXPECT_FALSE(backend_task_runner()->HasPendingTask()); EXPECT_FALSE(backend_task_runner()->HasPendingTask());
} }
...@@ -581,8 +766,8 @@ TEST_F(AffiliationBackendTest, NothingExplodesWhenShutDownDuringFetch) { ...@@ -581,8 +766,8 @@ TEST_F(AffiliationBackendTest, NothingExplodesWhenShutDownDuringFetch) {
GetAffiliations(mock_consumer(), GetAffiliations(mock_consumer(),
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2), FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
false /* cached_only */); false /* cached_only */);
ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest()); ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
fake_affiliation_api()->IgnoreNextRequest(); mock_fetch_throttler()->reset_signaled_network_request_needed();
DestroyBackend(); DestroyBackend();
} }
...@@ -591,10 +776,10 @@ TEST_F(AffiliationBackendTest, ...@@ -591,10 +776,10 @@ TEST_F(AffiliationBackendTest,
GetAffiliations(mock_consumer(), GetAffiliations(mock_consumer(),
FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2), FacetURI::FromCanonicalSpec(kTestFacetURIAlpha2),
false /* cached_only */); false /* cached_only */);
ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
// Currently, a GetAffiliations() request can only be blocked due to fetch in // Currently, a GetAffiliations() request can only be blocked due to fetch in
// flight -- so emulate this condition when destroying the backend. // flight -- so emulate this condition when destroying the backend.
fake_affiliation_api()->IgnoreNextRequest(); ASSERT_TRUE(mock_fetch_throttler()->has_signaled_network_request_needed());
mock_fetch_throttler()->reset_signaled_network_request_needed();
DestroyBackend(); DestroyBackend();
mock_consumer()->ExpectFailure(); mock_consumer()->ExpectFailure();
consumer_task_runner()->RunUntilIdle(); consumer_task_runner()->RunUntilIdle();
......
...@@ -75,15 +75,14 @@ const int64_t AffiliationFetchThrottler::kGracePeriodAfterReconnectMs = ...@@ -75,15 +75,14 @@ const int64_t AffiliationFetchThrottler::kGracePeriodAfterReconnectMs =
AffiliationFetchThrottler::AffiliationFetchThrottler( AffiliationFetchThrottler::AffiliationFetchThrottler(
AffiliationFetchThrottlerDelegate* delegate, AffiliationFetchThrottlerDelegate* delegate,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
scoped_ptr<base::TickClock> tick_clock) base::TickClock* tick_clock)
: delegate_(delegate), : delegate_(delegate),
task_runner_(task_runner), task_runner_(task_runner),
tick_clock_(tick_clock),
state_(IDLE), state_(IDLE),
has_network_connectivity_(false), has_network_connectivity_(false),
is_fetch_scheduled_(false), is_fetch_scheduled_(false),
tick_clock_(tick_clock.Pass()), exponential_backoff_(new BackoffEntryImpl(&kBackoffPolicy, tick_clock_)),
exponential_backoff_(
new BackoffEntryImpl(&kBackoffPolicy, tick_clock_.get())),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
DCHECK(delegate); DCHECK(delegate);
// Start observing before querying the current connectivity state, so that if // Start observing before querying the current connectivity state, so that if
......
...@@ -51,11 +51,11 @@ class AffiliationFetchThrottler ...@@ -51,11 +51,11 @@ class AffiliationFetchThrottler
public: public:
// Creates an instance that will use |tick_clock| as its tick source, and will // Creates an instance that will use |tick_clock| as its tick source, and will
// post to |task_runner| to call the |delegate|'s OnSendNetworkRequest(). The // post to |task_runner| to call the |delegate|'s OnSendNetworkRequest(). The
// |delegate| should outlive the throttler. // |delegate| and |tick_clock| should outlive the throttler.
AffiliationFetchThrottler( AffiliationFetchThrottler(
AffiliationFetchThrottlerDelegate* delegate, AffiliationFetchThrottlerDelegate* delegate,
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
scoped_ptr<base::TickClock> tick_clock); base::TickClock* tick_clock);
~AffiliationFetchThrottler() override; ~AffiliationFetchThrottler() override;
// Signals to the throttling logic that a network request is needed, and that // Signals to the throttling logic that a network request is needed, and that
...@@ -66,11 +66,14 @@ class AffiliationFetchThrottler ...@@ -66,11 +66,14 @@ class AffiliationFetchThrottler
// needed or while a request is in flight. To signal that another request will // needed or while a request is in flight. To signal that another request will
// be needed right away after the current one, call this method after calling // be needed right away after the current one, call this method after calling
// InformOfNetworkRequestComplete(). // InformOfNetworkRequestComplete().
void SignalNetworkRequestNeeded(); virtual void SignalNetworkRequestNeeded();
// Informs the back-off logic that the in-flight network request has been // Informs the back-off logic that the in-flight network request has been
// completed, either with |success| or not. // completed, either with |success| or not.
void InformOfNetworkRequestComplete(bool success); virtual void InformOfNetworkRequestComplete(bool success);
protected:
AffiliationFetchThrottlerDelegate* delegate_;
private: private:
FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest, FailedRequests); FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest, FailedRequests);
...@@ -112,12 +115,11 @@ class AffiliationFetchThrottler ...@@ -112,12 +115,11 @@ class AffiliationFetchThrottler
void OnConnectionTypeChanged( void OnConnectionTypeChanged(
net::NetworkChangeNotifier::ConnectionType type) override; net::NetworkChangeNotifier::ConnectionType type) override;
AffiliationFetchThrottlerDelegate* delegate_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
base::TickClock* tick_clock_;
State state_; State state_;
bool has_network_connectivity_; bool has_network_connectivity_;
bool is_fetch_scheduled_; bool is_fetch_scheduled_;
scoped_ptr<base::TickClock> tick_clock_;
scoped_ptr<net::BackoffEntry> exponential_backoff_; scoped_ptr<net::BackoffEntry> exponential_backoff_;
base::WeakPtrFactory<AffiliationFetchThrottler> weak_ptr_factory_; base::WeakPtrFactory<AffiliationFetchThrottler> weak_ptr_factory_;
......
...@@ -27,9 +27,9 @@ namespace { ...@@ -27,9 +27,9 @@ namespace {
class MockAffiliationFetchThrottlerDelegate class MockAffiliationFetchThrottlerDelegate
: public AffiliationFetchThrottlerDelegate { : public AffiliationFetchThrottlerDelegate {
public: public:
explicit MockAffiliationFetchThrottlerDelegate( // The |tick_clock| should outlive this instance.
scoped_ptr<base::TickClock> tick_clock) explicit MockAffiliationFetchThrottlerDelegate(base::TickClock* tick_clock)
: tick_clock_(tick_clock.Pass()), : tick_clock_(tick_clock),
emulated_return_value_(true), emulated_return_value_(true),
can_send_count_(0u) {} can_send_count_(0u) {}
~MockAffiliationFetchThrottlerDelegate() override { ~MockAffiliationFetchThrottlerDelegate() override {
...@@ -49,7 +49,7 @@ class MockAffiliationFetchThrottlerDelegate ...@@ -49,7 +49,7 @@ class MockAffiliationFetchThrottlerDelegate
} }
private: private:
scoped_ptr<base::TickClock> tick_clock_; base::TickClock* tick_clock_;
bool emulated_return_value_; bool emulated_return_value_;
size_t can_send_count_; size_t can_send_count_;
base::TimeTicks last_can_send_time_; base::TimeTicks last_can_send_time_;
...@@ -64,12 +64,13 @@ class AffiliationFetchThrottlerTest : public testing::Test { ...@@ -64,12 +64,13 @@ class AffiliationFetchThrottlerTest : public testing::Test {
AffiliationFetchThrottlerTest() AffiliationFetchThrottlerTest()
: network_change_notifier_(net::NetworkChangeNotifier::CreateMock()), : network_change_notifier_(net::NetworkChangeNotifier::CreateMock()),
task_runner_(new base::TestMockTimeTaskRunner), task_runner_(new base::TestMockTimeTaskRunner),
mock_delegate_(task_runner_->GetMockTickClock()) {} mock_tick_clock_(task_runner_->GetMockTickClock()),
mock_delegate_(mock_tick_clock_.get()) {}
~AffiliationFetchThrottlerTest() override {} ~AffiliationFetchThrottlerTest() override {}
scoped_ptr<AffiliationFetchThrottler> CreateThrottler() { scoped_ptr<AffiliationFetchThrottler> CreateThrottler() {
return make_scoped_ptr(new AffiliationFetchThrottler( return make_scoped_ptr(new AffiliationFetchThrottler(
&mock_delegate_, task_runner_, task_runner_->GetMockTickClock())); &mock_delegate_, task_runner_, mock_tick_clock_.get()));
} }
void SimulateHasNetworkConnectivity(bool has_connectivity) { void SimulateHasNetworkConnectivity(bool has_connectivity) {
...@@ -125,6 +126,7 @@ class AffiliationFetchThrottlerTest : public testing::Test { ...@@ -125,6 +126,7 @@ class AffiliationFetchThrottlerTest : public testing::Test {
base::MessageLoop message_loop_; base::MessageLoop message_loop_;
scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_; scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
scoped_ptr<base::TickClock> mock_tick_clock_;
MockAffiliationFetchThrottlerDelegate mock_delegate_; MockAffiliationFetchThrottlerDelegate mock_delegate_;
DISALLOW_COPY_AND_ASSIGN(AffiliationFetchThrottlerTest); DISALLOW_COPY_AND_ASSIGN(AffiliationFetchThrottlerTest);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#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 "base/time/default_clock.h"
#include "base/time/default_tick_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" #include "net/url_request/url_request_context_getter.h"
...@@ -36,8 +37,12 @@ void AffiliationService::Initialize( ...@@ -36,8 +37,12 @@ void AffiliationService::Initialize(
const base::FilePath& db_path) { const base::FilePath& db_path) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!backend_); DCHECK(!backend_);
backend_ = new AffiliationBackend(request_context_getter, backend_ =
make_scoped_ptr(new base::DefaultClock)); new AffiliationBackend(request_context_getter, backend_task_runner_,
make_scoped_ptr(new base::DefaultClock),
make_scoped_ptr(new base::DefaultTickClock));
scoped_ptr<base::TickClock> tick_clock(new base::DefaultTickClock);
backend_task_runner_->PostTask( backend_task_runner_->PostTask(
FROM_HERE, base::Bind(&AffiliationBackend::Initialize, FROM_HERE, base::Bind(&AffiliationBackend::Initialize,
base::Unretained(backend_), db_path)); base::Unretained(backend_), db_path));
......
...@@ -43,8 +43,8 @@ class AffiliationServiceTest : public testing::Test { ...@@ -43,8 +43,8 @@ class AffiliationServiceTest : public testing::Test {
public: public:
AffiliationServiceTest() AffiliationServiceTest()
: main_task_runner_(new base::TestSimpleTaskRunner), : main_task_runner_(new base::TestSimpleTaskRunner),
background_task_runner_(new base::TestMockTimeTaskRunner), main_task_runner_handle_(main_task_runner_),
main_task_runner_handle_(main_task_runner_) {} background_task_runner_(new base::TestMockTimeTaskRunner) {}
~AffiliationServiceTest() override {} ~AffiliationServiceTest() override {}
protected: protected:
...@@ -86,11 +86,12 @@ class AffiliationServiceTest : public testing::Test { ...@@ -86,11 +86,12 @@ class AffiliationServiceTest : public testing::Test {
background_task_runner_->RunUntilIdle(); background_task_runner_->RunUntilIdle();
} }
ScopedFakeAffiliationAPI fake_affiliation_api_;
MockAffiliationConsumer mock_consumer_;
scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_; scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_;
scoped_refptr<base::TestMockTimeTaskRunner> background_task_runner_;
base::ThreadTaskRunnerHandle main_task_runner_handle_; base::ThreadTaskRunnerHandle main_task_runner_handle_;
scoped_refptr<base::TestMockTimeTaskRunner> background_task_runner_;
ScopedFakeAffiliationAPI fake_affiliation_api_;
MockAffiliationConsumer mock_consumer_;
scoped_ptr<AffiliationService> service_; scoped_ptr<AffiliationService> service_;
...@@ -141,11 +142,10 @@ TEST_F(AffiliationServiceTest, GetAffiliations) { ...@@ -141,11 +142,10 @@ TEST_F(AffiliationServiceTest, GetAffiliations) {
TEST_F(AffiliationServiceTest, ShutdownWhileTasksArePosted) { TEST_F(AffiliationServiceTest, ShutdownWhileTasksArePosted) {
service()->GetAffiliations(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1), service()->GetAffiliations(FacetURI::FromCanonicalSpec(kTestFacetURIAlpha1),
false, mock_consumer()->GetResultCallback()); false, mock_consumer()->GetResultCallback());
DestroyService(); EXPECT_TRUE(background_task_runner()->HasPendingTask());
DestroyService();
background_task_runner()->RunUntilIdle(); background_task_runner()->RunUntilIdle();
ASSERT_TRUE(fake_affiliation_api()->HasPendingRequest());
fake_affiliation_api()->IgnoreNextRequest();
mock_consumer()->ExpectFailure(); mock_consumer()->ExpectFailure();
main_task_runner()->RunUntilIdle(); main_task_runner()->RunUntilIdle();
......
...@@ -60,6 +60,14 @@ void ScopedFakeAffiliationAPI::ServeNextRequest() { ...@@ -60,6 +60,14 @@ void ScopedFakeAffiliationAPI::ServeNextRequest() {
fetcher->SimulateSuccess(fake_response.Pass()); fetcher->SimulateSuccess(fake_response.Pass());
} }
void ScopedFakeAffiliationAPI::FailNextRequest() {
if (!fake_fetcher_factory_.has_pending_fetchers())
return;
FakeAffiliationFetcher* fetcher = fake_fetcher_factory_.PopNextFetcher();
fetcher->SimulateFailure();
}
void ScopedFakeAffiliationAPI::IgnoreNextRequest() { void ScopedFakeAffiliationAPI::IgnoreNextRequest() {
if (!fake_fetcher_factory_.has_pending_fetchers()) if (!fake_fetcher_factory_.has_pending_fetchers())
return; return;
......
...@@ -35,6 +35,9 @@ class ScopedFakeAffiliationAPI { ...@@ -35,6 +35,9 @@ class ScopedFakeAffiliationAPI {
// with success. // with success.
void ServeNextRequest(); void ServeNextRequest();
// Completes the next pending fetch, if any, with failure.
void FailNextRequest();
// Ignores the next pending request, if any, without completing it. // Ignores the next pending request, if any, without completing it.
void IgnoreNextRequest(); void IgnoreNextRequest();
......
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