Commit db78e67f authored by michaeln's avatar michaeln Committed by Commit bot

Change how the quota system computes the poolsize for temporary storage.

Adds a QuotaSettings datastructure and changes how the total poolsize
for temporary storage is determined. This CL adds a public content API
that the embedder must use to inform the quota system of the desired
QuotaSettings.

The old computation: [available_space + current_temp_usage] / 3
The new computation: [total_volume_size - os_overhead] / 3

The new computation is simplified to be a function of the size of the
volume, and no longer takes as input how much chrome is currently using.
This also reduces the time take to initialize the quota system.

To defend against filling the drive, as available space gets "too low",
the calculated per-host quota is clamped to the hosts current usage. This
prevents the writing of new data because quota clients query
GetUsageAndQuota() prior to performing a write. Also the eviction logic
deletes existing data to keep |must_remain_available| disk space free.

BUG=520318

Review-Url: https://codereview.chromium.org/1782053004
Cr-Commit-Position: refs/heads/master@{#438741}
parent 0ef90d0b
......@@ -44,6 +44,8 @@ include_rules = [
"+services/service_manager/public/cpp",
"+storage/browser/quota",
"+ui/gfx",
"+ui/gl",
......
......@@ -47,6 +47,7 @@
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/service_names.mojom.h"
......@@ -58,6 +59,7 @@
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_info.h"
#include "services/service_manager/public/cpp/interface_registry.h"
#include "storage/browser/quota/quota_settings.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/resource/resource_bundle_android.h"
#include "ui/resources/grit/ui_resources.h"
......@@ -353,6 +355,17 @@ AwContentBrowserClient::CreateQuotaPermissionContext() {
return new AwQuotaPermissionContext;
}
void AwContentBrowserClient::GetQuotaSettings(
content::BrowserContext* context,
content::StoragePartition* partition,
const storage::OptionalQuotaSettingsCallback& callback) {
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::FILE, FROM_HERE,
base::Bind(&storage::CalculateNominalDynamicSettings,
partition->GetPath(), context->IsOffTheRecord()),
callback);
}
void AwContentBrowserClient::AllowCertificateError(
content::WebContents* web_contents,
int cert_error,
......
......@@ -74,6 +74,10 @@ class AwContentBrowserClient : public content::ContentBrowserClient {
content::ResourceContext* context,
const std::vector<std::pair<int, int>>& render_frames) override;
content::QuotaPermissionContext* CreateQuotaPermissionContext() override;
void GetQuotaSettings(
content::BrowserContext* context,
content::StoragePartition* partition,
const storage::OptionalQuotaSettingsCallback& callback) override;
void AllowCertificateError(
content::WebContents* web_contents,
int cert_error,
......
include_rules = [
"+content/public",
"+content/shell",
"+storage/browser/quota",
]
......@@ -8,6 +8,10 @@
#include "ash/shell/content/client/shell_browser_main_parts.h"
#include "base/command_line.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "storage/browser/quota/quota_settings.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace ash {
......@@ -24,5 +28,16 @@ content::BrowserMainParts* ShellContentBrowserClient::CreateBrowserMainParts(
return shell_browser_main_parts_;
}
void ShellContentBrowserClient::GetQuotaSettings(
content::BrowserContext* context,
content::StoragePartition* partition,
const storage::OptionalQuotaSettingsCallback& callback) {
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::FILE, FROM_HERE,
base::Bind(&storage::CalculateNominalDynamicSettings,
partition->GetPath(), context->IsOffTheRecord()),
callback);
}
} // namespace examples
} // namespace views
......@@ -28,6 +28,10 @@ class ShellContentBrowserClient : public content::ContentBrowserClient {
// Overridden from content::ContentBrowserClient:
content::BrowserMainParts* CreateBrowserMainParts(
const content::MainFunctionParams& parameters) override;
void GetQuotaSettings(
content::BrowserContext* context,
content::StoragePartition* partition,
const storage::OptionalQuotaSettingsCallback& callback) override;
private:
ShellBrowserMainParts* shell_browser_main_parts_;
......
......@@ -21,11 +21,6 @@ namespace base {
// TimeDelta ------------------------------------------------------------------
// static
TimeDelta TimeDelta::Max() {
return TimeDelta(std::numeric_limits<int64_t>::max());
}
int TimeDelta::InDays() const {
if (is_max()) {
// Preserve max to prevent overflow.
......
......@@ -130,7 +130,7 @@ class BASE_EXPORT TimeDelta {
// Returns the maximum time delta, which should be greater than any reasonable
// time delta we might compare it to. Adding or subtracting the maximum time
// delta to a time or another time delta has an undefined result.
static TimeDelta Max();
static constexpr TimeDelta Max();
// Returns the internal numeric value of the TimeDelta object. Please don't
// use this and do arithmetic on it, as it is more error prone than using the
......@@ -670,6 +670,11 @@ constexpr TimeDelta TimeDelta::FromMicroseconds(int64_t us) {
return TimeDelta(us);
}
// static
constexpr TimeDelta TimeDelta::Max() {
return TimeDelta(std::numeric_limits<int64_t>::max());
}
// static
constexpr TimeDelta TimeDelta::FromDouble(double value) {
// TODO(crbug.com/612601): Use saturated_cast<int64_t>(value) once we sort out
......
......@@ -14,6 +14,7 @@ include_rules = [
"+mojo/public",
"+net",
"+services/service_manager/public/cpp",
"+storage/browser/quota",
"+third_party/blimp_fonts",
"+third_party/khronos/GLES2/gl2.h",
"+third_party/WebKit/public/platform/WebGestureEvent.h",
......
......@@ -8,9 +8,12 @@
#include "blimp/engine/app/settings_manager.h"
#include "blimp/engine/grit/blimp_browser_resources.h"
#include "blimp/engine/mojo/blob_channel_service.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/service_names.mojom.h"
#include "services/service_manager/public/cpp/interface_registry.h"
#include "storage/browser/quota/quota_settings.h"
#include "ui/base/resource/resource_bundle.h"
namespace blimp {
......@@ -69,5 +72,16 @@ BlimpContentBrowserClient::GetServiceManifestOverlay(
return base::JSONReader::Read(manifest_contents);
}
void BlimpContentBrowserClient::GetQuotaSettings(
content::BrowserContext* context,
content::StoragePartition* partition,
const storage::OptionalQuotaSettingsCallback& callback) {
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::FILE, FROM_HERE,
base::Bind(&storage::CalculateNominalDynamicSettings,
partition->GetPath(), context->IsOffTheRecord()),
callback);
}
} // namespace engine
} // namespace blimp
......@@ -33,6 +33,10 @@ class BlimpContentBrowserClient : public content::ContentBrowserClient {
content::RenderProcessHost* render_process_host) override;
std::unique_ptr<base::Value> GetServiceManifestOverlay(
const std::string& name) override;
void GetQuotaSettings(
content::BrowserContext* context,
content::StoragePartition* partition,
const storage::OptionalQuotaSettingsCallback& callback) override;
BlimpBrowserContext* GetBrowserContext();
......
......@@ -354,8 +354,6 @@ split_static_library("browser") {
"download/save_package_file_picker.h",
"engagement/important_sites_util.cc",
"engagement/important_sites_util.h",
"engagement/site_engagement_eviction_policy.cc",
"engagement/site_engagement_eviction_policy.h",
"engagement/site_engagement_helper.cc",
"engagement/site_engagement_helper.h",
"engagement/site_engagement_metrics.cc",
......
......@@ -37,8 +37,8 @@ class BrowsingDataQuotaHelperTest : public testing::Test {
quota_manager_ = new storage::QuotaManager(
false, dir_.GetPath(),
BrowserThread::GetTaskRunnerForThread(BrowserThread::IO).get(),
BrowserThread::GetTaskRunnerForThread(BrowserThread::DB).get(),
nullptr);
BrowserThread::GetTaskRunnerForThread(BrowserThread::DB).get(), nullptr,
storage::GetQuotaSettingsFunc());
helper_ = new BrowsingDataQuotaHelperImpl(quota_manager_.get());
}
......
......@@ -48,7 +48,6 @@
#include "chrome/browser/content_settings/tab_specific_content_settings.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/engagement/site_engagement_eviction_policy.h"
#include "chrome/browser/field_trial_recorder.h"
#include "chrome/browser/font_family_cache.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
......@@ -405,6 +404,8 @@ namespace {
// thread.
base::LazyInstance<std::string> g_io_thread_application_locale;
const storage::QuotaSettings* g_default_quota_settings;
#if BUILDFLAG(ENABLE_PLUGINS)
// TODO(teravest): Add renderer-side API-specific checking for these APIs so
// that blanket permission isn't granted to all dev channel APIs for these.
......@@ -2164,12 +2165,20 @@ ChromeContentBrowserClient::CreateQuotaPermissionContext() {
return new ChromeQuotaPermissionContext();
}
std::unique_ptr<storage::QuotaEvictionPolicy>
ChromeContentBrowserClient::GetTemporaryStorageEvictionPolicy(
content::BrowserContext* context) {
return SiteEngagementEvictionPolicy::IsEnabled()
? base::MakeUnique<SiteEngagementEvictionPolicy>(context)
: nullptr;
void ChromeContentBrowserClient::GetQuotaSettings(
content::BrowserContext* context,
content::StoragePartition* partition,
const storage::OptionalQuotaSettingsCallback& callback) {
if (g_default_quota_settings) {
// For debugging tests harness can inject settings.
callback.Run(*g_default_quota_settings);
return;
}
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::FILE, FROM_HERE,
base::Bind(&storage::CalculateNominalDynamicSettings,
partition->GetPath(), context->IsOffTheRecord()),
callback);
}
void ChromeContentBrowserClient::AllowCertificateError(
......@@ -3356,3 +3365,9 @@ void ChromeContentBrowserClient::
task_scheduler_util::variations::
MaybePerformBrowserTaskSchedulerRedirection();
}
//static
void ChromeContentBrowserClient::SetDefaultQuotaSettingsForTesting(
const storage::QuotaSettings* settings) {
g_default_quota_settings = settings;
}
......@@ -171,8 +171,11 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
const GURL& url,
content::ResourceContext* context) override;
content::QuotaPermissionContext* CreateQuotaPermissionContext() override;
std::unique_ptr<storage::QuotaEvictionPolicy>
GetTemporaryStorageEvictionPolicy(content::BrowserContext* context) override;
void GetQuotaSettings(
content::BrowserContext* context,
content::StoragePartition* partition,
const storage::OptionalQuotaSettingsCallback& callback) override;
void AllowCertificateError(
content::WebContents* web_contents,
int cert_error,
......@@ -326,6 +329,7 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
private:
friend class DisableWebRtcEncryptionFlagTest;
friend class InProcessBrowserTest;
#if BUILDFLAG(ENABLE_WEBRTC)
// Copies disable WebRTC encryption switch depending on the channel.
......@@ -356,6 +360,11 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
const base::Callback<void(bool)>& callback);
#endif
// The value pointed to by |settings| should remain valid until the
// the function is called again with a new value or a nullptr.
static void SetDefaultQuotaSettingsForTesting(
const storage::QuotaSettings *settings);
#if BUILDFLAG(ENABLE_PLUGINS)
// Set of origins that can use TCP/UDP private APIs from NaCl.
std::set<std::string> allowed_socket_origins_;
......
// 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 "chrome/browser/engagement/site_engagement_eviction_policy.h"
#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/engagement/site_engagement_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/browser_thread.h"
namespace {
const int kExpectedEngagementSites = 200;
// Gets the quota that an origin deserves based on its site engagement.
int64_t GetSoftQuotaForOrigin(const GURL& origin,
int score,
int total_engagement_points,
int64_t global_quota) {
double quota_per_point =
global_quota /
std::max(kExpectedEngagementSites * SiteEngagementService::GetMaxPoints(),
static_cast<double>(total_engagement_points));
return score * quota_per_point;
}
GURL DoCalculateEvictionOrigin(
const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
SiteEngagementScoreProvider* score_provider,
const std::set<GURL>& exceptions,
const std::map<GURL, int64_t>& usage_map,
int64_t global_quota) {
// TODO(calamity): Integrate storage access frequency as an input to this
// heuristic.
// This heuristic is intended to optimize for two criteria:
// - evict the site that the user cares about least
// - evict the least number of sites to get under the quota limit
//
// The heuristic for deciding the next eviction origin calculates a soft
// quota for each origin which is the amount the origin should be allowed to
// use based on its engagement and the global quota. The origin that most
// exceeds its soft quota is chosen.
GURL origin_to_evict;
int64_t max_overuse = std::numeric_limits<int64_t>::min();
int total_engagement_points = score_provider->GetTotalEngagementPoints();
for (const auto& usage : usage_map) {
GURL origin = usage.first;
if (special_storage_policy &&
(special_storage_policy->IsStorageUnlimited(origin) ||
special_storage_policy->IsStorageDurable(origin))) {
continue;
}
// |overuse| can be negative if the soft quota exceeds the usage.
int64_t overuse =
usage.second -
GetSoftQuotaForOrigin(origin, score_provider->GetScore(origin),
total_engagement_points, global_quota);
if (overuse > max_overuse && !base::ContainsKey(exceptions, origin)) {
max_overuse = overuse;
origin_to_evict = origin;
}
}
return origin_to_evict;
}
GURL GetSiteEngagementEvictionOriginOnUIThread(
const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
content::BrowserContext* browser_context,
const std::set<GURL>& exceptions,
const std::map<GURL, int64_t>& usage_map,
int64_t global_quota) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Profile* profile = Profile::FromBrowserContext(browser_context);
SiteEngagementScoreProvider* score_provider =
g_browser_process->profile_manager()->IsValidProfile(profile)
? SiteEngagementService::Get(profile)
: nullptr;
if (!score_provider)
return GURL();
return DoCalculateEvictionOrigin(special_storage_policy, score_provider,
exceptions, usage_map, global_quota);
}
} // namespace
// static
bool SiteEngagementEvictionPolicy::IsEnabled() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableSiteEngagementEvictionPolicy)) {
return true;
}
const std::string group_name = base::FieldTrialList::FindFullName(
SiteEngagementService::kEngagementParams);
return base::StartsWith(group_name, "StorageEvictionEnabled",
base::CompareCase::SENSITIVE);
}
SiteEngagementEvictionPolicy::SiteEngagementEvictionPolicy(
content::BrowserContext* browser_context)
: browser_context_(browser_context) {}
SiteEngagementEvictionPolicy::~SiteEngagementEvictionPolicy() {}
void SiteEngagementEvictionPolicy::GetEvictionOrigin(
const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
const std::set<GURL>& exceptions,
const std::map<GURL, int64_t>& usage_map,
int64_t global_quota,
const storage::GetOriginCallback& callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::UI, FROM_HERE,
base::Bind(&GetSiteEngagementEvictionOriginOnUIThread,
special_storage_policy, browser_context_, exceptions,
usage_map, global_quota),
callback);
}
// static
GURL SiteEngagementEvictionPolicy::CalculateEvictionOriginForTests(
const scoped_refptr<storage::SpecialStoragePolicy>& special_storage_policy,
SiteEngagementScoreProvider* score_provider,
const std::set<GURL>& exceptions,
const std::map<GURL, int64_t>& usage_map,
int64_t global_quota) {
return DoCalculateEvictionOrigin(special_storage_policy, score_provider,
exceptions, usage_map, global_quota);
}
// 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 CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_EVICTION_POLICY_H_
#define CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_EVICTION_POLICY_H_
#include <stdint.h>
#include <map>
#include <memory>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "storage/browser/quota/quota_manager.h"
#include "url/gurl.h"
namespace content {
class BrowserContext;
}
class SiteEngagementScoreProvider;
class SiteEngagementEvictionPolicy : public storage::QuotaEvictionPolicy {
public:
static bool IsEnabled();
explicit SiteEngagementEvictionPolicy(
content::BrowserContext* browser_context);
~SiteEngagementEvictionPolicy() override;
// Overridden from storage::QuotaEvictionPolicy:
void GetEvictionOrigin(const scoped_refptr<storage::SpecialStoragePolicy>&
special_storage_policy,
const std::set<GURL>& exceptions,
const std::map<GURL, int64_t>& usage_map,
int64_t global_quota,
const storage::GetOriginCallback& callback) override;
private:
friend class SiteEngagementEvictionPolicyTest;
static GURL CalculateEvictionOriginForTests(
const scoped_refptr<storage::SpecialStoragePolicy>&
special_storage_policy,
SiteEngagementScoreProvider* score_provider,
const std::set<GURL>& exceptions,
const std::map<GURL, int64_t>& usage_map,
int64_t global_quota);
content::BrowserContext* const browser_context_;
DISALLOW_COPY_AND_ASSIGN(SiteEngagementEvictionPolicy);
};
#endif // CHROME_BROWSER_ENGAGEMENT_SITE_ENGAGEMENT_EVICTION_POLICY_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 "chrome/browser/engagement/site_engagement_eviction_policy.h"
#include <stdint.h>
#include <memory>
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/engagement/site_engagement_service.h"
#include "content/public/test/mock_special_storage_policy.h"
#include "content/public/test/mock_storage_client.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "storage/browser/quota/quota_manager.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const int64_t kGlobalQuota = 25 * 1024;
} // namespace
class TestSiteEngagementScoreProvider : public SiteEngagementScoreProvider {
public:
TestSiteEngagementScoreProvider() {}
virtual ~TestSiteEngagementScoreProvider() {}
double GetScore(const GURL& url) const override {
const auto& it = engagement_score_map_.find(url);
if (it != engagement_score_map_.end())
return it->second;
return 0.0;
}
double GetTotalEngagementPoints() const override {
double total = 0;
for (const auto& site : engagement_score_map_)
total += site.second;
return total;
}
void SetScore(const GURL& origin, double score) {
engagement_score_map_[origin] = score;
}
private:
std::map<GURL, double> engagement_score_map_;
DISALLOW_COPY_AND_ASSIGN(TestSiteEngagementScoreProvider);
};
class SiteEngagementEvictionPolicyTest : public testing::Test {
public:
SiteEngagementEvictionPolicyTest()
: score_provider_(new TestSiteEngagementScoreProvider()),
storage_policy_(new content::MockSpecialStoragePolicy()) {}
~SiteEngagementEvictionPolicyTest() override {}
GURL CalculateEvictionOriginWithExceptions(
const std::map<GURL, int64_t>& usage,
const std::set<GURL>& exceptions) {
return SiteEngagementEvictionPolicy::CalculateEvictionOriginForTests(
storage_policy_, score_provider_.get(), exceptions, usage,
kGlobalQuota);
}
GURL CalculateEvictionOrigin(const std::map<GURL, int64_t>& usage) {
return CalculateEvictionOriginWithExceptions(usage, std::set<GURL>());
}
TestSiteEngagementScoreProvider* score_provider() {
return score_provider_.get();
}
content::MockSpecialStoragePolicy* storage_policy() {
return storage_policy_.get();
}
private:
std::unique_ptr<TestSiteEngagementScoreProvider> score_provider_;
scoped_refptr<content::MockSpecialStoragePolicy> storage_policy_;
DISALLOW_COPY_AND_ASSIGN(SiteEngagementEvictionPolicyTest);
};
TEST_F(SiteEngagementEvictionPolicyTest, GetEvictionOrigin) {
GURL url1("http://www.google.com");
GURL url2("http://www.example.com");
GURL url3("http://www.spam.me");
std::map<GURL, int64_t> usage;
usage[url1] = 10 * 1024;
usage[url2] = 10 * 1024;
usage[url3] = 10 * 1024;
score_provider()->SetScore(url1, 50);
score_provider()->SetScore(url2, 25);
// When 3 sites have equal usage, evict the site with the least engagement.
EXPECT_EQ(url3, CalculateEvictionOrigin(usage));
usage[url2] = usage[url3] + 10;
// Now |url2| has the most usage but |url3| has the least engagement score so
// one of them should be evicted. In this case the heuristic chooses |url3|.
EXPECT_EQ(url3, CalculateEvictionOrigin(usage));
// But exceeding allocated usage too much will still result in being evicted
// even though the engagement with |url2| is higher.
usage[url2] = 15 * 1024;
EXPECT_EQ(url2, CalculateEvictionOrigin(usage));
// When all origins have the same engagement, the origin with the highest
// usage is evicted.
score_provider()->SetScore(url1, 50);
score_provider()->SetScore(url2, 50);
score_provider()->SetScore(url3, 50);
usage[url2] = 10 * 1024;
usage[url3] = 20 * 1024;
EXPECT_EQ(url3, CalculateEvictionOrigin(usage));
}
// Test that durable and unlimited storage origins are exempt from eviction.
TEST_F(SiteEngagementEvictionPolicyTest, SpecialStoragePolicy) {
GURL url1("http://www.google.com");
GURL url2("http://www.example.com");
std::map<GURL, int64_t> usage;
usage[url1] = 10 * 1024;
usage[url2] = 10 * 1024;
score_provider()->SetScore(url1, 50);
score_provider()->SetScore(url2, 25);
EXPECT_EQ(url2, CalculateEvictionOrigin(usage));
// Durable storage doesn't get evicted.
storage_policy()->AddDurable(url2);
EXPECT_EQ(url1, CalculateEvictionOrigin(usage));
// Unlimited storage doesn't get evicted.
storage_policy()->AddUnlimited(url1);
EXPECT_EQ(GURL(), CalculateEvictionOrigin(usage));
}
TEST_F(SiteEngagementEvictionPolicyTest, Exceptions) {
GURL url1("http://www.google.com");
GURL url2("http://www.example.com");
std::map<GURL, int64_t> usage;
usage[url1] = 10 * 1024;
usage[url2] = 10 * 1024;
score_provider()->SetScore(url1, 50);
score_provider()->SetScore(url2, 25);
EXPECT_EQ(url2, CalculateEvictionOrigin(usage));
// The policy should respect exceptions.
std::set<GURL> exceptions;
exceptions.insert(url2);
EXPECT_EQ(url1, CalculateEvictionOriginWithExceptions(usage, exceptions));
}