blob: 5f4fd3b8383a47e076d10e371f2c3eb54cd72f8e [file] [log] [blame]
// Copyright 2019 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/optimization_guide/core/hints_manager.h"
#include <string>
#include <utility>
#include "base/base64.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/test/gtest_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
#include "components/optimization_guide/core/bloom_filter.h"
#include "components/optimization_guide/core/hint_cache.h"
#include "components/optimization_guide/core/hints_component_util.h"
#include "components/optimization_guide/core/hints_fetcher.h"
#include "components/optimization_guide/core/hints_fetcher_factory.h"
#include "components/optimization_guide/core/optimization_guide_constants.h"
#include "components/optimization_guide/core/optimization_guide_enums.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/optimization_guide/core/optimization_guide_navigation_data.h"
#include "components/optimization_guide/core/optimization_guide_prefs.h"
#include "components/optimization_guide/core/optimization_guide_store.h"
#include "components/optimization_guide/core/optimization_guide_switches.h"
#include "components/optimization_guide/core/proto_database_provider_test_base.h"
#include "components/optimization_guide/core/tab_url_provider.h"
#include "components/optimization_guide/core/top_host_provider.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "components/ukm/test_ukm_recorder.h"
#include "components/unified_consent/unified_consent_service.h"
#include "components/variations/scoped_variations_ids_provider.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "services/network/public/cpp/network_connection_tracker.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_network_connection_tracker.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace optimization_guide {
namespace {
// Allows for default hour to pass + random delay between 30 and 60 seconds.
constexpr int kUpdateFetchHintsTimeSecs = 61 * 60; // 1 hours and 1 minutes.
const int kDefaultHostBloomFilterNumHashFunctions = 7;
const int kDefaultHostBloomFilterNumBits = 511;
void PopulateBloomFilterWithDefaultHost(BloomFilter* bloom_filter) {
bloom_filter->Add("host.com");
}
void AddBloomFilterToConfig(proto::OptimizationType optimization_type,
const BloomFilter& bloom_filter,
int num_hash_functions,
int num_bits,
bool is_allowlist,
proto::Configuration* config) {
std::string bloom_filter_data(
reinterpret_cast<const char*>(&bloom_filter.bytes()[0]),
bloom_filter.bytes().size());
proto::OptimizationFilter* of_proto =
is_allowlist ? config->add_optimization_allowlists()
: config->add_optimization_blocklists();
of_proto->set_optimization_type(optimization_type);
std::unique_ptr<proto::BloomFilter> bloom_filter_proto =
std::make_unique<proto::BloomFilter>();
bloom_filter_proto->set_num_hash_functions(num_hash_functions);
bloom_filter_proto->set_num_bits(num_bits);
bloom_filter_proto->set_data(bloom_filter_data);
of_proto->set_allocated_bloom_filter(bloom_filter_proto.release());
}
std::unique_ptr<proto::GetHintsResponse> BuildHintsResponse(
const std::vector<std::string>& hosts,
const std::vector<std::string>& urls) {
std::unique_ptr<proto::GetHintsResponse> get_hints_response =
std::make_unique<proto::GetHintsResponse>();
for (const auto& host : hosts) {
proto::Hint* hint = get_hints_response->add_hints();
hint->set_key_representation(proto::HOST);
hint->set_key(host);
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("page pattern");
proto::Optimization* opt = page_hint->add_allowlisted_optimizations();
opt->set_optimization_type(proto::DEFER_ALL_SCRIPT);
}
for (const auto& url : urls) {
proto::Hint* hint = get_hints_response->add_hints();
hint->set_key_representation(proto::FULL_URL);
hint->set_key(url);
hint->mutable_max_cache_duration()->set_seconds(60 * 60);
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern(url);
proto::Optimization* opt = page_hint->add_allowlisted_optimizations();
opt->set_optimization_type(proto::COMPRESS_PUBLIC_IMAGES);
opt->mutable_public_image_metadata()->add_url("someurl");
}
return get_hints_response;
}
void RunHintsFetchedCallbackWithResponse(
HintsFetchedCallback hints_fetched_callback,
std::unique_ptr<proto::GetHintsResponse> response) {
std::move(hints_fetched_callback).Run(std::move(response));
}
// Returns the default params used for the kOptimizationHints feature.
base::FieldTrialParams GetOptimizationHintsDefaultFeatureParams() {
return {{
"max_host_keyed_hint_cache_size",
"1",
}};
}
std::unique_ptr<base::test::ScopedFeatureList>
SetUpDeferStartupActiveTabsHintsFetch(bool is_enabled) {
std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list =
std::make_unique<base::test::ScopedFeatureList>();
auto params = GetOptimizationHintsDefaultFeatureParams();
params["defer_startup_active_tabs_hints_fetch"] =
is_enabled ? "true" : "false";
scoped_feature_list->InitAndEnableFeatureWithParameters(
features::kOptimizationHints, params);
return scoped_feature_list;
}
} // namespace
// A mock class implementation of TopHostProvider.
class FakeTopHostProvider : public TopHostProvider {
public:
explicit FakeTopHostProvider(const std::vector<std::string>& top_hosts)
: top_hosts_(top_hosts) {}
std::vector<std::string> GetTopHosts() override {
num_top_hosts_called_++;
return top_hosts_;
}
int get_num_top_hosts_called() const { return num_top_hosts_called_; }
private:
std::vector<std::string> top_hosts_;
int num_top_hosts_called_ = 0;
};
// A mock class implementation of TabUrlProvider.
class FakeTabUrlProvider : public TabUrlProvider {
public:
const std::vector<GURL> GetUrlsOfActiveTabs(
const base::TimeDelta& duration_since_last_shown) override {
num_urls_called_++;
return urls_;
}
void SetUrls(const std::vector<GURL>& urls) { urls_ = urls; }
int get_num_urls_called() const { return num_urls_called_; }
private:
std::vector<GURL> urls_;
int num_urls_called_ = 0;
};
enum class HintsFetcherEndState {
kFetchFailed = 0,
kFetchSuccessWithHostHints = 1,
kFetchSuccessWithNoHints = 2,
kFetchSuccessWithURLHints = 3,
};
// A mock class implementation of HintsFetcher. It will iterate through the
// provided fetch states each time it is called. If it reaches the end of the
// loop, it will just continue using the last fetch state.
class TestHintsFetcher : public HintsFetcher {
public:
TestHintsFetcher(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
GURL optimization_guide_service_url,
PrefService* pref_service,
network::NetworkConnectionTracker* network_connection_tracker,
const std::vector<HintsFetcherEndState>& fetch_states)
: HintsFetcher(url_loader_factory,
optimization_guide_service_url,
pref_service,
network_connection_tracker),
fetch_states_(fetch_states) {
DCHECK(!fetch_states_.empty());
}
bool FetchOptimizationGuideServiceHints(
const std::vector<std::string>& hosts,
const std::vector<GURL>& urls,
const base::flat_set<proto::OptimizationType>& optimization_types,
proto::RequestContext request_context,
const std::string& locale,
HintsFetchedCallback hints_fetched_callback) override {
HintsFetcherEndState fetch_state =
num_fetches_requested_ < static_cast<int>(fetch_states_.size())
? fetch_states_[num_fetches_requested_]
: fetch_states_.back();
num_fetches_requested_++;
locale_requested_ = locale;
request_context_requested_ = request_context;
switch (fetch_state) {
case HintsFetcherEndState::kFetchFailed:
std::move(hints_fetched_callback).Run(absl::nullopt);
return false;
case HintsFetcherEndState::kFetchSuccessWithHostHints:
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&RunHintsFetchedCallbackWithResponse,
std::move(hints_fetched_callback),
BuildHintsResponse({"host.com"}, {})));
return true;
case HintsFetcherEndState::kFetchSuccessWithURLHints:
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&RunHintsFetchedCallbackWithResponse,
std::move(hints_fetched_callback),
BuildHintsResponse(
{}, {"https://somedomain.org/news/whatever"})));
return true;
case HintsFetcherEndState::kFetchSuccessWithNoHints:
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&RunHintsFetchedCallbackWithResponse,
std::move(hints_fetched_callback),
BuildHintsResponse({}, {})));
return true;
}
return true;
}
int num_fetches_requested() const { return num_fetches_requested_; }
std::string locale_requested() const { return locale_requested_; }
proto::RequestContext request_context_requested() const {
return request_context_requested_;
}
private:
std::vector<HintsFetcherEndState> fetch_states_;
int num_fetches_requested_ = 0;
std::string locale_requested_;
proto::RequestContext request_context_requested_ =
proto::RequestContext::CONTEXT_UNSPECIFIED;
};
// A mock class of HintsFetcherFactory that returns instances of
// TestHintsFetchers with the provided fetch state.
class TestHintsFetcherFactory : public HintsFetcherFactory {
public:
TestHintsFetcherFactory(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
GURL optimization_guide_service_url,
PrefService* pref_service,
const std::vector<HintsFetcherEndState>& fetch_states,
network::NetworkConnectionTracker* network_connection_tracker)
: HintsFetcherFactory(url_loader_factory,
optimization_guide_service_url,
pref_service,
network_connection_tracker),
fetch_states_(fetch_states) {}
std::unique_ptr<HintsFetcher> BuildInstance() override {
return std::make_unique<TestHintsFetcher>(
url_loader_factory_, optimization_guide_service_url_, pref_service_,
network_connection_tracker_, fetch_states_);
}
private:
std::vector<HintsFetcherEndState> fetch_states_;
};
class HintsManagerTest : public ProtoDatabaseProviderTestBase {
public:
HintsManagerTest() {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
features::kOptimizationHints,
GetOptimizationHintsDefaultFeatureParams());
}
~HintsManagerTest() override = default;
HintsManagerTest(const HintsManagerTest&) = delete;
HintsManagerTest& operator=(const HintsManagerTest&) = delete;
void SetUp() override {
ProtoDatabaseProviderTestBase::SetUp();
CreateHintsManager(/*top_host_provider=*/nullptr);
}
void TearDown() override {
ResetHintsManager();
ProtoDatabaseProviderTestBase::TearDown();
}
void CreateHintsManager(TopHostProvider* top_host_provider) {
if (hints_manager_)
ResetHintsManager();
pref_service_ =
std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
prefs::RegisterProfilePrefs(pref_service_->registry());
pref_service_->registry()->RegisterBooleanPref(
data_reduction_proxy::prefs::kDataSaverEnabled, false);
unified_consent::UnifiedConsentService::RegisterPrefs(
pref_service_->registry());
url_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
hint_store_ = std::make_unique<OptimizationGuideStore>(
db_provider_.get(), temp_dir(),
task_environment_.GetMainThreadTaskRunner());
tab_url_provider_ = std::make_unique<FakeTabUrlProvider>();
hints_manager_ = std::make_unique<HintsManager>(
/*is_off_the_record=*/false, /*application_locale=*/"en-US",
pref_service(), hint_store_->AsWeakPtr(), top_host_provider,
tab_url_provider_.get(), url_loader_factory_,
network::TestNetworkConnectionTracker::GetInstance(),
/*push_notification_manager=*/nullptr);
hints_manager_->SetClockForTesting(task_environment_.GetMockClock());
// Run until hint cache is initialized and the HintsManager is ready to
// process hints.
RunUntilIdle();
}
void ResetHintsManager() {
hints_manager_->Shutdown();
hints_manager_.reset();
tab_url_provider_.reset();
hint_store_.reset();
pref_service_.reset();
RunUntilIdle();
}
void ProcessInvalidHintsComponentInfo(const std::string& version) {
HintsComponentInfo info(
base::Version(version),
temp_dir().Append(FILE_PATH_LITERAL("notaconfigfile")));
base::RunLoop run_loop;
hints_manager_->ListenForNextUpdateForTesting(run_loop.QuitClosure());
hints_manager_->OnHintsComponentAvailable(info);
run_loop.Run();
}
void ProcessHintsComponentInfoWithBadConfig(const std::string& version) {
HintsComponentInfo info(
base::Version(version),
temp_dir().Append(FILE_PATH_LITERAL("badconfig.pb")));
ASSERT_EQ(7, base::WriteFile(info.path, "garbage", 7));
hints_manager_->OnHintsComponentAvailable(info);
RunUntilIdle();
}
void ProcessHints(const proto::Configuration& config,
const std::string& version,
bool should_wait = true) {
HintsComponentInfo info(
base::Version(version),
temp_dir().Append(FILE_PATH_LITERAL("somefile.pb")));
ASSERT_NO_FATAL_FAILURE(WriteConfigToFile(config, info.path));
base::RunLoop run_loop;
if (should_wait)
hints_manager_->ListenForNextUpdateForTesting(run_loop.QuitClosure());
hints_manager_->OnHintsComponentAvailable(info);
if (should_wait)
run_loop.Run();
}
void InitializeWithDefaultConfig(const std::string& version,
bool should_wait = true) {
proto::Configuration config;
proto::Hint* hint1 = config.add_hints();
hint1->set_key("somedomain.org");
hint1->set_key_representation(proto::HOST);
hint1->set_version("someversion");
proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("/news/");
proto::Optimization* default_opt =
page_hint1->add_allowlisted_optimizations();
default_opt->set_optimization_type(proto::NOSCRIPT);
// Add another hint so somedomain.org hint is not in-memory initially.
proto::Hint* hint2 = config.add_hints();
hint2->set_key("somedomain2.org");
hint2->set_key_representation(proto::HOST);
hint2->set_version("someversion");
proto::Optimization* opt = hint2->add_allowlisted_optimizations();
opt->set_optimization_type(proto::NOSCRIPT);
ProcessHints(config, version, should_wait);
}
std::unique_ptr<HintsFetcherFactory> BuildTestHintsFetcherFactory(
const std::vector<HintsFetcherEndState>& fetch_states) {
return std::make_unique<TestHintsFetcherFactory>(
url_loader_factory_, GURL("https://hintsserver.com"), pref_service(),
fetch_states, network::TestNetworkConnectionTracker::GetInstance());
}
void MoveClockForwardBy(base::TimeDelta time_delta) {
task_environment_.FastForwardBy(time_delta);
RunUntilIdle();
}
// Creates navigation data for a navigation to |url| with registered
// |optimization_types|.
std::unique_ptr<OptimizationGuideNavigationData> CreateTestNavigationData(
const GURL& url,
const std::vector<proto::OptimizationType>& optimization_types) {
auto navigation_data = std::make_unique<OptimizationGuideNavigationData>(
/*navigation_id=*/1, /*navigation_start*/ base::TimeTicks::Now());
navigation_data->set_navigation_url(url);
navigation_data->set_registered_optimization_types(optimization_types);
return navigation_data;
}
void CallOnNavigationStartOrRedirect(
OptimizationGuideNavigationData* navigation_data,
base::OnceClosure callback) {
hints_manager()->OnNavigationStartOrRedirect(navigation_data,
std::move(callback));
}
void SetConnectionOffline() {
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_NONE);
}
void SetConnectionOnline() {
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_4G);
}
HintsManager* hints_manager() const { return hints_manager_.get(); }
int32_t num_batch_update_hints_fetches_initiated() const {
return hints_manager()->num_batch_update_hints_fetches_initiated();
}
TestHintsFetcher* active_tabs_batch_update_hints_fetcher() const {
return static_cast<TestHintsFetcher*>(
hints_manager()->active_tabs_batch_update_hints_fetcher());
}
GURL url_with_hints() const {
return GURL("https://somedomain.org/news/whatever");
}
GURL url_with_url_keyed_hint() const {
return GURL("https://somedomain.org/news/whatever");
}
GURL url_without_hints() const {
return GURL("https://url_without_hints.org/");
}
base::FilePath temp_dir() const { return temp_dir_.GetPath(); }
PrefService* pref_service() const { return pref_service_.get(); }
FakeTabUrlProvider* tab_url_provider() const {
return tab_url_provider_.get();
}
void RunUntilIdle() {
task_environment_.RunUntilIdle();
base::RunLoop().RunUntilIdle();
}
private:
void WriteConfigToFile(const proto::Configuration& config,
const base::FilePath& filePath) {
std::string serialized_config;
ASSERT_TRUE(config.SerializeToString(&serialized_config));
ASSERT_EQ(static_cast<int32_t>(serialized_config.size()),
base::WriteFile(filePath, serialized_config.data(),
serialized_config.size()));
}
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<OptimizationGuideStore> hint_store_;
std::unique_ptr<FakeTabUrlProvider> tab_url_provider_;
std::unique_ptr<HintsManager> hints_manager_;
std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
network::TestURLLoaderFactory test_url_loader_factory_;
};
TEST_F(HintsManagerTest, ProcessHintsWithValidCommandLineOverride) {
base::HistogramTester histogram_tester;
proto::Configuration config;
proto::Hint* hint = config.add_hints();
hint->set_key("somedomain.org");
hint->set_key_representation(proto::HOST);
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("noscript_default_2g");
proto::Optimization* optimization =
page_hint->add_allowlisted_optimizations();
optimization->set_optimization_type(proto::NOSCRIPT);
BloomFilter bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
AddBloomFilterToConfig(proto::PERFORMANCE_HINTS, bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/true, &config);
std::string encoded_config;
config.SerializeToString(&encoded_config);
base::Base64Encode(encoded_config, &encoded_config);
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kHintsProtoOverride, encoded_config);
CreateHintsManager(/*top_host_provider=*/nullptr);
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
// The below histogram should not be recorded since hints weren't coming
// directly from the component.
histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSuccess, 1);
// However, we still expect the local histogram for the hints being updated to
// be recorded.
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.UpdateComponentHints.Result", true, 1);
// Bloom filters passed via command line are processed on the background
// thread so make sure everything has finished before checking if it has been
// loaded.
RunUntilIdle();
EXPECT_TRUE(hints_manager()->HasLoadedOptimizationBlocklist(
proto::LITE_PAGE_REDIRECT));
EXPECT_FALSE(hints_manager()->HasLoadedOptimizationAllowlist(
proto::PERFORMANCE_HINTS));
// Now register a new type with an allowlist that has not yet been loaded.
hints_manager()->RegisterOptimizationTypes({proto::PERFORMANCE_HINTS});
RunUntilIdle();
EXPECT_TRUE(hints_manager()->HasLoadedOptimizationAllowlist(
proto::PERFORMANCE_HINTS));
}
TEST_F(HintsManagerTest, ProcessHintsWithInvalidCommandLineOverride) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kHintsProtoOverride, "this-is-not-a-proto");
CreateHintsManager(/*top_host_provider=*/nullptr);
// The below histogram should not be recorded since hints weren't coming
// directly from the component.
histogram_tester.ExpectTotalCount("OptimizationGuide.ProcessHintsResult", 0);
// We also do not expect to update the component hints with bad hints either.
histogram_tester.ExpectTotalCount(
"OptimizationGuide.UpdateComponentHints.Result", 0);
}
TEST_F(HintsManagerTest,
ProcessHintsWithCommandLineOverrideShouldNotBeOverriddenByNewComponent) {
proto::Configuration config;
proto::Hint* hint = config.add_hints();
hint->set_key("somedomain.org");
hint->set_key_representation(proto::HOST);
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("noscript_default_2g");
proto::Optimization* optimization =
page_hint->add_allowlisted_optimizations();
optimization->set_optimization_type(proto::NOSCRIPT);
std::string encoded_config;
config.SerializeToString(&encoded_config);
base::Base64Encode(encoded_config, &encoded_config);
{
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kHintsProtoOverride, encoded_config);
CreateHintsManager(/*top_host_provider=*/nullptr);
histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSuccess,
1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.UpdateComponentHints.Result", true, 1);
}
// Test that a new component coming in does not update the component hints.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("3.0.0.0");
// The below histograms should not be recorded since component hints
// processing is disabled.
histogram_tester.ExpectTotalCount("OptimizationGuide.ProcessHintsResult",
0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.UpdateComponentHints.Result", 0);
}
}
TEST_F(HintsManagerTest, ParseTwoConfigVersions) {
proto::Configuration config;
proto::Hint* hint1 = config.add_hints();
hint1->set_key("somedomain.org");
hint1->set_key_representation(proto::HOST);
hint1->set_version("someversion");
proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("/news/");
proto::Optimization* optimization1 =
page_hint1->add_allowlisted_optimizations();
optimization1->set_optimization_type(proto::RESOURCE_LOADING);
// Test the first time parsing the config.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("1.0.0.0");
histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSuccess,
1);
}
// Test the second time parsing the config. This should also update the hints.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("2.0.0.0");
histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSuccess,
1);
}
}
TEST_F(HintsManagerTest, ParseInvalidConfigVersions) {
// Test the first time parsing the config.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("1.0.0.0");
histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSuccess,
1);
}
{
base::HistogramTester histogram_tester;
ProcessHintsComponentInfoWithBadConfig("2.0.0.0");
// If we have already parsed a version later than this version, we expect
// for the hints to not be updated.
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kFailedInvalidConfiguration, 1);
}
}
TEST_F(HintsManagerTest, ComponentProcessingWhileShutdown) {
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("10.0.0.0", /*should_wait=*/false);
hints_manager()->Shutdown();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessingComponentAtShutdown", true, 1);
EXPECT_TRUE(
pref_service()->GetString(prefs::kPendingHintsProcessingVersion).empty());
}
TEST_F(HintsManagerTest, ParseOlderConfigVersions) {
// Test the first time parsing the config.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("10.0.0.0");
histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSuccess,
1);
}
// Test the second time parsing the config. This will be treated by the cache
// as an older version.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("2.0.0.0");
// If we have already parsed a version later than this version, we expect
// for the hints to not be updated.
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSkippedProcessingHints, 1);
}
}
TEST_F(HintsManagerTest, ParseDuplicateConfigVersions) {
const std::string version = "3.0.0.0";
// Test the first time parsing the config.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig(version);
histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSuccess,
1);
}
// Test the second time parsing the config. This will be treated by the cache
// as a duplicate version.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig(version);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSkippedProcessingHints, 1);
}
}
TEST_F(HintsManagerTest, ComponentInfoDidNotContainConfig) {
base::HistogramTester histogram_tester;
ProcessInvalidHintsComponentInfo("1.0.0.0");
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kFailedReadingFile, 1);
}
TEST_F(HintsManagerTest, ProcessHintsWithExistingPref) {
// Write hints processing pref for version 2.0.0.
pref_service()->SetString(prefs::kPendingHintsProcessingVersion, "2.0.0");
// Verify config not processed for same version (2.0.0) and pref not cleared.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("2.0.0");
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kFailedFinishProcessing, 1);
EXPECT_FALSE(pref_service()
->GetString(prefs::kPendingHintsProcessingVersion)
.empty());
}
// Now verify config is processed for different version and pref cleared.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("3.0.0");
EXPECT_TRUE(pref_service()
->GetString(prefs::kPendingHintsProcessingVersion)
.empty());
histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSuccess,
1);
}
}
TEST_F(HintsManagerTest,
ProcessHintsWithExistingPrefDoesNotClearOrCountAsMidProcessing) {
// Write hints processing pref for version 2.0.0.
pref_service()->SetString(prefs::kPendingHintsProcessingVersion, "2.0.0");
// Verify component for same version counts as "failed".
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("2.0.0", /*should_wait=*/false);
hints_manager()->Shutdown();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kFailedFinishProcessing, 1);
// Verify that pref still not cleared at shutdown and was not counted as
// mid-processing.
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessingComponentAtShutdown", false, 1);
EXPECT_FALSE(
pref_service()->GetString(prefs::kPendingHintsProcessingVersion).empty());
}
TEST_F(HintsManagerTest, ProcessHintsWithInvalidPref) {
// Create pref file with invalid version.
pref_service()->SetString(prefs::kPendingHintsProcessingVersion, "bad-2.0.0");
// Verify config not processed for existing pref with bad value but
// that the pref is cleared.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("2.0.0");
EXPECT_TRUE(pref_service()
->GetString(prefs::kPendingHintsProcessingVersion)
.empty());
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kFailedPreviouslyAttemptedVersionInvalid,
1);
}
// Now verify config is processed with pref cleared.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("2.0.0");
EXPECT_TRUE(pref_service()
->GetString(prefs::kPendingHintsProcessingVersion)
.empty());
histogram_tester.ExpectUniqueSample("OptimizationGuide.ProcessHintsResult",
ProcessHintsComponentResult::kSuccess,
1);
}
}
TEST_F(HintsManagerTest, OnNavigationStartOrRedirectWithHint) {
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("3.0.0.0");
auto navigation_data = CreateTestNavigationData(url_with_hints(), {});
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
histogram_tester.ExpectUniqueSample("OptimizationGuide.LoadedHint.Result",
true, 1);
}
TEST_F(HintsManagerTest, OnNavigationStartOrRedirectNoHint) {
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("3.0.0.0");
auto navigation_data =
CreateTestNavigationData(GURL("https://notinhints.com"), {});
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
histogram_tester.ExpectUniqueSample("OptimizationGuide.LoadedHint.Result",
false, 1);
}
TEST_F(HintsManagerTest, OnNavigationStartOrRedirectNoHost) {
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("3.0.0.0");
auto navigation_data = CreateTestNavigationData(GURL("blargh"), {});
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
histogram_tester.ExpectTotalCount("OptimizationGuide.LoadedHint.Result", 0);
}
TEST_F(HintsManagerTest, OptimizationFiltersAreOnlyLoadedIfTypeIsRegistered) {
proto::Configuration config;
BloomFilter bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
AddBloomFilterToConfig(proto::NOSCRIPT, bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
AddBloomFilterToConfig(proto::DEFER_ALL_SCRIPT, bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/true, &config);
{
base::HistogramTester histogram_tester;
ProcessHints(config, "1.0.0.0");
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.DeferAllScript", 0);
}
// Now register the optimization type and see that it is loaded.
{
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
hints_manager()->ListenForNextUpdateForTesting(run_loop.QuitClosure());
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
run_loop.Run();
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
OptimizationFilterStatus::kFoundServerFilterConfig, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
OptimizationFilterStatus::kCreatedServerFilter, 1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.DeferAllScript", 0);
EXPECT_TRUE(hints_manager()->HasLoadedOptimizationBlocklist(
proto::LITE_PAGE_REDIRECT));
EXPECT_FALSE(
hints_manager()->HasLoadedOptimizationBlocklist(proto::NOSCRIPT));
EXPECT_FALSE(hints_manager()->HasLoadedOptimizationAllowlist(
proto::DEFER_ALL_SCRIPT));
}
// Re-registering the same optimization type does not re-load the filter.
{
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
hints_manager()->ListenForNextUpdateForTesting(run_loop.QuitClosure());
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
run_loop.Run();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.DeferAllScript", 0);
}
// Registering a new optimization type without a filter does not trigger a
// reload of the filter.
{
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
hints_manager()->ListenForNextUpdateForTesting(run_loop.QuitClosure());
hints_manager()->RegisterOptimizationTypes({proto::PERFORMANCE_HINTS});
run_loop.Run();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.DeferAllScript", 0);
}
// Registering a new optimization types with filters does trigger a
// reload of the filters.
{
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
hints_manager()->ListenForNextUpdateForTesting(run_loop.QuitClosure());
hints_manager()->RegisterOptimizationTypes(
{proto::NOSCRIPT, proto::DEFER_ALL_SCRIPT});
run_loop.Run();
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
OptimizationFilterStatus::kFoundServerFilterConfig, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
OptimizationFilterStatus::kCreatedServerFilter, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript",
OptimizationFilterStatus::kFoundServerFilterConfig, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript",
OptimizationFilterStatus::kCreatedServerFilter, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.DeferAllScript",
OptimizationFilterStatus::kFoundServerFilterConfig, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.DeferAllScript",
OptimizationFilterStatus::kCreatedServerFilter, 1);
EXPECT_TRUE(hints_manager()->HasLoadedOptimizationBlocklist(
proto::LITE_PAGE_REDIRECT));
EXPECT_TRUE(
hints_manager()->HasLoadedOptimizationBlocklist(proto::NOSCRIPT));
EXPECT_TRUE(hints_manager()->HasLoadedOptimizationAllowlist(
proto::DEFER_ALL_SCRIPT));
}
}
TEST_F(HintsManagerTest, OptimizationFiltersOnlyLoadOncePerType) {
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
base::HistogramTester histogram_tester;
proto::Configuration config;
BloomFilter blocklist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&blocklist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
// Make sure it will only load one of an allowlist or a blocklist.
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/true, &config);
ProcessHints(config, "1.0.0.0");
// We found 2 LPR blocklists: parsed one and duped the other.
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
OptimizationFilterStatus::kFoundServerFilterConfig, 3);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
OptimizationFilterStatus::kCreatedServerFilter, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
OptimizationFilterStatus::kFailedServerFilterDuplicateConfig, 2);
}
TEST_F(HintsManagerTest, InvalidOptimizationFilterNotLoaded) {
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
base::HistogramTester histogram_tester;
int too_many_bits = features::MaxServerBloomFilterByteSize() * 8 + 1;
proto::Configuration config;
BloomFilter blocklist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
too_many_bits);
PopulateBloomFilterWithDefaultHost(&blocklist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions, too_many_bits,
/*is_allowlist=*/false, &config);
ProcessHints(config, "1.0.0.0");
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
OptimizationFilterStatus::kFoundServerFilterConfig, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
OptimizationFilterStatus::kFailedServerFilterTooBig, 1);
EXPECT_FALSE(hints_manager()->HasLoadedOptimizationBlocklist(
proto::LITE_PAGE_REDIRECT));
}
TEST_F(HintsManagerTest, CanApplyOptimizationUrlWithNoHost) {
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
proto::Configuration config;
BloomFilter blocklist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&blocklist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
ProcessHints(config, "1.0.0.0");
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(GURL("urlwithnohost"),
proto::LITE_PAGE_REDIRECT,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(OptimizationTypeDecision::kNoHintAvailable,
optimization_type_decision);
}
TEST_F(HintsManagerTest, CanApplyOptimizationHasFilterForTypeButNotLoadedYet) {
proto::Configuration config;
BloomFilter blocklist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&blocklist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
ProcessHints(config, "1.0.0.0");
// Append the switch for processing hints to force the filter to not get
// loaded.
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kHintsProtoOverride);
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(GURL("https://whatever.com/123"),
proto::LITE_PAGE_REDIRECT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(OptimizationTypeDecision::kHadOptimizationFilterButNotLoadedInTime,
optimization_type_decision);
// Run until idle to ensure we don't crash because the test object has gone
// away.
RunUntilIdle();
}
TEST_F(HintsManagerTest,
CanApplyOptimizationHasLoadedFilterForTypeUrlInAllowlist) {
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
proto::Configuration config;
BloomFilter allowlist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&allowlist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, allowlist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/true, &config);
ProcessHints(config, "1.0.0.0");
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(GURL("https://m.host.com/123"),
proto::LITE_PAGE_REDIRECT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(OptimizationTypeDecision::kAllowedByOptimizationFilter,
optimization_type_decision);
}
TEST_F(HintsManagerTest,
CanApplyOptimizationHasLoadedFilterForTypeUrlInBlocklist) {
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
proto::Configuration config;
BloomFilter blocklist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&blocklist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
ProcessHints(config, "1.0.0.0");
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(GURL("https://m.host.com/123"),
proto::LITE_PAGE_REDIRECT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(OptimizationTypeDecision::kNotAllowedByOptimizationFilter,
optimization_type_decision);
}
TEST_F(HintsManagerTest,
CanApplyOptimizationHasLoadedFilterForTypeUrlNotInAllowlistFilter) {
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
proto::Configuration config;
BloomFilter allowlist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&allowlist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, allowlist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/true, &config);
ProcessHints(config, "1.0.0.0");
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(GURL("https://whatever.com/123"),
proto::LITE_PAGE_REDIRECT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(OptimizationTypeDecision::kNotAllowedByOptimizationFilter,
optimization_type_decision);
}
TEST_F(HintsManagerTest,
CanApplyOptimizationHasLoadedFilterForTypeUrlNotInBlocklistFilter) {
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
proto::Configuration config;
BloomFilter blocklist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&blocklist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
ProcessHints(config, "1.0.0.0");
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(GURL("https://whatever.com/123"),
proto::LITE_PAGE_REDIRECT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(OptimizationTypeDecision::kAllowedByOptimizationFilter,
optimization_type_decision);
}
TEST_F(HintsManagerTest,
CanApplyOptimizationOptimizationTypeAllowlistedAtTopLevel) {
proto::Configuration config;
proto::Hint* hint1 = config.add_hints();
hint1->set_key("somedomain.org");
hint1->set_key_representation(proto::HOST);
hint1->set_version("someversion");
proto::Optimization* opt1 = hint1->add_allowlisted_optimizations();
opt1->set_optimization_type(proto::RESOURCE_LOADING);
ProcessHints(config, "1.0.0.0");
hints_manager()->RegisterOptimizationTypes({proto::RESOURCE_LOADING});
auto navigation_data =
CreateTestNavigationData(url_with_hints(), {proto::RESOURCE_LOADING});
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::RESOURCE_LOADING,
&optimization_metadata);
EXPECT_EQ(OptimizationTypeDecision::kAllowedByHint,
optimization_type_decision);
}
TEST_F(HintsManagerTest, CanApplyOptimizationHasPageHintButNoMatchingOptType) {
InitializeWithDefaultConfig("1.0.0.0");
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
auto navigation_data =
CreateTestNavigationData(url_with_hints(), {proto::DEFER_ALL_SCRIPT});
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::DEFER_ALL_SCRIPT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(OptimizationTypeDecision::kNotAllowedByHint,
optimization_type_decision);
}
TEST_F(HintsManagerTest,
CanApplyOptimizationAndPopulatesPerformanceHintsMetadata) {
hints_manager()->RegisterOptimizationTypes({proto::PERFORMANCE_HINTS});
proto::Configuration config;
proto::Hint* hint = config.add_hints();
hint->set_key("somedomain.org");
hint->set_key_representation(proto::HOST);
hint->set_version("someversion");
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("/news/");
proto::Optimization* opt = page_hint->add_allowlisted_optimizations();
opt->set_optimization_type(proto::PERFORMANCE_HINTS);
proto::PerformanceHint* performance_hint =
opt->mutable_performance_hints_metadata()->add_performance_hints();
performance_hint->set_wildcard_pattern("somedomain.org");
performance_hint->set_performance_class(proto::PERFORMANCE_SLOW);
ProcessHints(config, "1.0.0.0");
base::RunLoop run_loop;
auto navigation_data =
CreateTestNavigationData(url_with_hints(), {proto::PERFORMANCE_HINTS});
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::PERFORMANCE_HINTS,
&optimization_metadata);
// Make sure performance hints metadata is populated.
EXPECT_TRUE(optimization_metadata.performance_hints_metadata().has_value());
EXPECT_EQ(OptimizationTypeDecision::kAllowedByHint,
optimization_type_decision);
}
TEST_F(HintsManagerTest, CanApplyOptimizationAndPopulatesPublicImageMetadata) {
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
proto::Configuration config;
proto::Hint* hint = config.add_hints();
hint->set_key("somedomain.org");
hint->set_key_representation(proto::HOST);
hint->set_version("someversion");
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("/news/");
proto::Optimization* opt = page_hint->add_allowlisted_optimizations();
opt->set_optimization_type(proto::COMPRESS_PUBLIC_IMAGES);
opt->mutable_public_image_metadata()->add_url("someimage");
ProcessHints(config, "1.0.0.0");
auto navigation_data = CreateTestNavigationData(
url_with_hints(), {proto::COMPRESS_PUBLIC_IMAGES});
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::COMPRESS_PUBLIC_IMAGES,
&optimization_metadata);
// Make sure public images metadata is populated.
EXPECT_TRUE(optimization_metadata.public_image_metadata().has_value());
EXPECT_EQ(OptimizationTypeDecision::kAllowedByHint,
optimization_type_decision);
}
TEST_F(HintsManagerTest,
CanApplyOptimizationAndPopulatesLoadingPredictorMetadata) {
hints_manager()->RegisterOptimizationTypes({proto::LOADING_PREDICTOR});
proto::Configuration config;
proto::Hint* hint = config.add_hints();
hint->set_key("somedomain.org");
hint->set_key_representation(proto::HOST);
hint->set_version("someversion");
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("/news/");
proto::Optimization* opt = page_hint->add_allowlisted_optimizations();
opt->set_optimization_type(proto::LOADING_PREDICTOR);
opt->mutable_loading_predictor_metadata()->add_subresources()->set_url(
"https://resource.com/");
ProcessHints(config, "1.0.0.0");
auto navigation_data =
CreateTestNavigationData(url_with_hints(), {proto::LOADING_PREDICTOR});
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::LOADING_PREDICTOR,
&optimization_metadata);
// Make sure loading predictor metadata is populated.
EXPECT_TRUE(optimization_metadata.loading_predictor_metadata().has_value());
EXPECT_EQ(OptimizationTypeDecision::kAllowedByHint,
optimization_type_decision);
}
TEST_F(HintsManagerTest, CanApplyOptimizationAndPopulatesAnyMetadata) {
hints_manager()->RegisterOptimizationTypes({proto::LOADING_PREDICTOR});
proto::Configuration config;
proto::Hint* hint = config.add_hints();
hint->set_key("somedomain.org");
hint->set_key_representation(proto::HOST);
hint->set_version("someversion");
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("/news/");
proto::Optimization* opt = page_hint->add_allowlisted_optimizations();
opt->set_optimization_type(proto::LOADING_PREDICTOR);
proto::LoadingPredictorMetadata lp_metadata;
lp_metadata.add_subresources()->set_url("https://resource.com/");
lp_metadata.SerializeToString(opt->mutable_any_metadata()->mutable_value());
opt->mutable_any_metadata()->set_type_url(
"type.googleapis.com/com.foo.LoadingPredictorMetadata");
ProcessHints(config, "1.0.0.0");
auto navigation_data =
CreateTestNavigationData(url_with_hints(), {proto::LOADING_PREDICTOR});
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::LOADING_PREDICTOR,
&optimization_metadata);
// Make sure loading predictor metadata is populated.
EXPECT_TRUE(
optimization_metadata.ParsedMetadata<proto::LoadingPredictorMetadata>()
.has_value());
EXPECT_EQ(OptimizationTypeDecision::kAllowedByHint,
optimization_type_decision);
}
TEST_F(HintsManagerTest, CanApplyOptimizationNoMatchingPageHint) {
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
auto navigation_data =
CreateTestNavigationData(GURL("https://somedomain.org/nomatch"), {});
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
hints_manager()->RegisterOptimizationTypes({proto::NOSCRIPT});
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::NOSCRIPT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(OptimizationTypeDecision::kNotAllowedByHint,
optimization_type_decision);
}
TEST_F(HintsManagerTest,
CanApplyOptimizationNoHintForNavigationMetadataClearedAnyway) {
InitializeWithDefaultConfig("1.0.0.0");
auto navigation_data = CreateTestNavigationData(
GURL("https://nohint.com"), {proto::COMPRESS_PUBLIC_IMAGES});
hints_manager()->RegisterOptimizationTypes({proto::NOSCRIPT});
OptimizationMetadata optimization_metadata;
proto::PerformanceHintsMetadata hints_metadata;
auto* hint = hints_metadata.add_performance_hints();
hint->set_wildcard_pattern("test.com");
hint->set_performance_class(proto::PERFORMANCE_SLOW);
OptimizationMetadata metadata;
optimization_metadata.set_performance_hints_metadata(hints_metadata);
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::NOSCRIPT,
&optimization_metadata);
EXPECT_FALSE(optimization_metadata.performance_hints_metadata().has_value());
EXPECT_EQ(OptimizationTypeDecision::kNoHintAvailable,
optimization_type_decision);
}
TEST_F(HintsManagerTest, CanApplyOptimizationHasHintInCacheButNotLoaded) {
InitializeWithDefaultConfig("1.0.0.0");
hints_manager()->RegisterOptimizationTypes({proto::NOSCRIPT});
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(url_with_hints(), proto::NOSCRIPT,
&optimization_metadata);
EXPECT_EQ(OptimizationTypeDecision::kHadHintButNotLoadedInTime,
optimization_type_decision);
}
TEST_F(HintsManagerTest, CanApplyOptimizationFilterTakesPrecedence) {
auto navigation_data = CreateTestNavigationData(
GURL("https://m.host.com/urlinfilterandhints"), {});
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
proto::Configuration config;
proto::Hint* hint1 = config.add_hints();
hint1->set_key("host.com");
hint1->set_key_representation(proto::HOST);
hint1->set_version("someversion");
proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("https://m.host.com");
proto::Optimization* optimization1 =
page_hint1->add_allowlisted_optimizations();
optimization1->set_optimization_type(proto::LITE_PAGE_REDIRECT);
BloomFilter blocklist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&blocklist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
ProcessHints(config, "1.0.0.0");
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::LITE_PAGE_REDIRECT,
/*optimization_metadata=*/nullptr);
// Make sure decision points logged correctly.
EXPECT_EQ(OptimizationTypeDecision::kNotAllowedByOptimizationFilter,
optimization_type_decision);
}
TEST_F(HintsManagerTest,
CanApplyOptimizationFilterTakesPrecedenceMatchesFilter) {
auto navigation_data =
CreateTestNavigationData(GURL("https://notfiltered.com/whatever"),
{proto::COMPRESS_PUBLIC_IMAGES});
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
proto::Configuration config;
proto::Hint* hint1 = config.add_hints();
hint1->set_key("notfiltered.com");
hint1->set_key_representation(proto::HOST);
hint1->set_version("someversion");
proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("https://notfiltered.com");
proto::Optimization* optimization1 =
page_hint1->add_allowlisted_optimizations();
optimization1->set_optimization_type(proto::LITE_PAGE_REDIRECT);
BloomFilter blocklist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&blocklist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
ProcessHints(config, "1.0.0.0");
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::LITE_PAGE_REDIRECT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(OptimizationTypeDecision::kAllowedByOptimizationFilter,
optimization_type_decision);
}
class HintsManagerFetchingDisabledTest : public HintsManagerTest {
public:
HintsManagerFetchingDisabledTest() {
scoped_list_.InitAndDisableFeature(
features::kRemoteOptimizationGuideFetching);
}
private:
base::test::ScopedFeatureList scoped_list_;
};
TEST_F(HintsManagerFetchingDisabledTest,
HintsFetchNotAllowedIfFeatureIsNotEnabled) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(
std::vector<std::string>({"example1.com", "example2.com"}));
CreateHintsManager(top_host_provider.get());
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch.
MoveClockForwardBy(base::Seconds(kUpdateFetchHintsTimeSecs));
EXPECT_EQ(0, top_host_provider->get_num_top_hosts_called());
// Hints fetcher should not even be created.
EXPECT_FALSE(active_tabs_batch_update_hints_fetcher());
}
TEST_F(HintsManagerTest,
CanApplyOptimizationAsyncReturnsRightAwayIfNotAllowedToFetch) {
base::HistogramTester histogram_tester;
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
auto navigation_data = CreateTestNavigationData(
url_without_hints(), {proto::COMPRESS_PUBLIC_IMAGES});
hints_manager()->CanApplyOptimizationAsync(
url_without_hints(), proto::COMPRESS_PUBLIC_IMAGES,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kFalse, decision);
}));
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.CompressPublicImages",
OptimizationTypeDecision::kNoHintAvailable, 1);
}
TEST_F(
HintsManagerTest,
CanApplyOptimizationAsyncReturnsRightAwayIfNotAllowedToFetchAndNotAllowlistedByAvailableHint) {
base::HistogramTester histogram_tester;
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
auto navigation_data = CreateTestNavigationData(
url_with_hints(), {proto::COMPRESS_PUBLIC_IMAGES});
// Wait for hint to be loaded.
base::RunLoop run_loop;
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
hints_manager()->CanApplyOptimizationAsync(
url_with_hints(), proto::COMPRESS_PUBLIC_IMAGES,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kFalse, decision);
}));
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.CompressPublicImages",
OptimizationTypeDecision::kNotAllowedByHint, 1);
}
TEST_F(HintsManagerTest, RemoveFetchedEntriesByHintKeys_Host) {
int cache_duration_in_secs = 60;
GURL url("https://host.com/r/cats");
std::unique_ptr<proto::GetHintsResponse> get_hints_response =
std::make_unique<proto::GetHintsResponse>();
proto::Hint* hint = get_hints_response->add_hints();
hint->set_key(url.spec());
hint->set_key_representation(proto::FULL_URL);
hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->add_allowlisted_optimizations()->set_optimization_type(
proto::PERFORMANCE_HINTS);
page_hint->set_page_pattern("whatever/*");
hint = get_hints_response->add_hints();
hint->set_key_representation(proto::HOST);
hint->set_key(url.host());
page_hint = hint->add_page_hints();
page_hint->set_page_pattern("anything/*");
std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
hints_manager()->hint_cache()->UpdateFetchedHints(
std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
run_loop->QuitClosure());
EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
run_loop = std::make_unique<base::RunLoop>();
hints_manager()->RemoveFetchedEntriesByHintKeys(
run_loop->QuitClosure(), proto::KeyRepresentation::HOST, {url.host()});
run_loop->Run();
EXPECT_FALSE(hints_manager()->hint_cache()->HasHint(url.host()));
EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
}
TEST_F(HintsManagerTest, RemoveFetchedEntriesByHintKeys_URL) {
int cache_duration_in_secs = 60;
GURL url("https://host.com/r/cats");
std::unique_ptr<proto::GetHintsResponse> get_hints_response =
std::make_unique<proto::GetHintsResponse>();
proto::Hint* hint = get_hints_response->add_hints();
hint->set_key(url.spec());
hint->set_key_representation(proto::FULL_URL);
hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->add_allowlisted_optimizations()->set_optimization_type(
proto::PERFORMANCE_HINTS);
page_hint->set_page_pattern("whatever/*");
hint = get_hints_response->add_hints();
hint->set_key_representation(proto::HOST);
hint->set_key(url.host());
page_hint = hint->add_page_hints();
page_hint->set_page_pattern("anything/*");
std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
hints_manager()->hint_cache()->UpdateFetchedHints(
std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
run_loop->QuitClosure());
EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
run_loop = std::make_unique<base::RunLoop>();
hints_manager()->RemoveFetchedEntriesByHintKeys(
run_loop->QuitClosure(), proto::KeyRepresentation::FULL_URL,
{url.spec()});
run_loop->Run();
// Both the host and url entries should have been removed to support upgrading
// hint keys from HOST to FULL_URL.
EXPECT_FALSE(hints_manager()->hint_cache()->HasHint(url.host()));
EXPECT_FALSE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
}
TEST_F(HintsManagerTest, PurgeFetchedEntries) {
int cache_duration_in_secs = 60;
GURL url("https://host.com/r/cats");
std::unique_ptr<proto::GetHintsResponse> get_hints_response =
std::make_unique<proto::GetHintsResponse>();
proto::Hint* hint = get_hints_response->add_hints();
hint->set_key(url.spec());
hint->set_key_representation(proto::FULL_URL);
hint->mutable_max_cache_duration()->set_seconds(cache_duration_in_secs);
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->add_allowlisted_optimizations()->set_optimization_type(
proto::PERFORMANCE_HINTS);
page_hint->set_page_pattern("whatever/*");
hint = get_hints_response->add_hints();
hint->set_key_representation(proto::HOST);
hint->set_key(url.host());
page_hint = hint->add_page_hints();
page_hint->set_page_pattern("anything/*");
std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
hints_manager()->hint_cache()->UpdateFetchedHints(
std::move(get_hints_response), base::Time().Now(), {url.host()}, {url},
run_loop->QuitClosure());
EXPECT_TRUE(hints_manager()->hint_cache()->HasHint(url.host()));
EXPECT_TRUE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
run_loop = std::make_unique<base::RunLoop>();
hints_manager()->PurgeFetchedEntries(run_loop->QuitClosure());
run_loop->Run();
EXPECT_FALSE(hints_manager()->hint_cache()->HasHint(url.host()));
EXPECT_FALSE(hints_manager()->hint_cache()->HasURLKeyedEntryForURL(url));
}
TEST_F(HintsManagerTest, HintFetcherPrefUpdated_URL) {
base::Time expiry = base::Time::Now() + base::Hours(1);
HintsFetcher::AddFetchedHostForTesting(pref_service(), "host-key.com",
expiry);
HintsFetcher::AddFetchedHostForTesting(pref_service(), "url-key.com", expiry);
ASSERT_TRUE(
HintsFetcher::WasHostCoveredByFetch(pref_service(), "host-key.com"));
ASSERT_TRUE(
HintsFetcher::WasHostCoveredByFetch(pref_service(), "url-key.com"));
std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
hints_manager()->RemoveFetchedEntriesByHintKeys(
run_loop->QuitClosure(), proto::KeyRepresentation::FULL_URL,
{
GURL("https://host-key.com/page").spec(),
GURL("https://url-key.com/page").spec(),
});
run_loop->Run();
EXPECT_FALSE(
HintsFetcher::WasHostCoveredByFetch(pref_service(), "host-key.com"));
EXPECT_FALSE(
HintsFetcher::WasHostCoveredByFetch(pref_service(), "url-key.com"));
}
TEST_F(HintsManagerTest, HintFetcherPrefUpdated_Hosts) {
base::Time expiry = base::Time::Now() + base::Hours(1);
HintsFetcher::AddFetchedHostForTesting(pref_service(), "host-key.com",
expiry);
HintsFetcher::AddFetchedHostForTesting(pref_service(), "url-key.com", expiry);
ASSERT_TRUE(
HintsFetcher::WasHostCoveredByFetch(pref_service(), "host-key.com"));
ASSERT_TRUE(
HintsFetcher::WasHostCoveredByFetch(pref_service(), "url-key.com"));
std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
hints_manager()->RemoveFetchedEntriesByHintKeys(
run_loop->QuitClosure(), proto::KeyRepresentation::HOST,
{
"host-key.com",
"url-key.com",
});
run_loop->Run();
EXPECT_FALSE(
HintsFetcher::WasHostCoveredByFetch(pref_service(), "host-key.com"));
EXPECT_FALSE(
HintsFetcher::WasHostCoveredByFetch(pref_service(), "url-key.com"));
}
class HintsManagerFetchingTest : public HintsManagerTest {
public:
HintsManagerFetchingTest() {
scoped_list_.InitWithFeaturesAndParameters(
{
{
features::kRemoteOptimizationGuideFetching,
{{"max_concurrent_page_navigation_fetches", "2"},
{"max_concurrent_batch_update_fetches", "2"}},
},
},
{features::kRemoteOptimizationGuideFetchingAnonymousDataConsent});
}
private:
variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
variations::VariationsIdsProvider::Mode::kUseSignedInState};
base::test::ScopedFeatureList scoped_list_;
};
TEST_F(HintsManagerFetchingTest,
HintsFetchNotAllowedIfFeatureIsEnabledButUserNotAllowed) {
base::CommandLine::ForCurrentProcess()->RemoveSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
CreateHintsManager(/*top_host_provider=*/nullptr);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch.
MoveClockForwardBy(base::Seconds(kUpdateFetchHintsTimeSecs));
// Hints fetcher should not even be created.
EXPECT_FALSE(active_tabs_batch_update_hints_fetcher());
}
TEST_F(HintsManagerFetchingTest,
NoRegisteredOptimizationTypesAndHintsFetchNotAttempted) {
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(
std::vector<std::string>({"example1.com", "example2.com"}));
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
CreateHintsManager(top_host_provider.get());
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch but the fetch is not made.
MoveClockForwardBy(base::Seconds(kUpdateFetchHintsTimeSecs));
EXPECT_EQ(0, top_host_provider->get_num_top_hosts_called());
// Hints fetcher should not even be created.
EXPECT_FALSE(active_tabs_batch_update_hints_fetcher());
}
TEST_F(HintsManagerFetchingTest,
OnlyFilterTypesRegisteredHintsFetchNotAttempted) {
proto::Configuration config;
BloomFilter allowlist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&allowlist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, allowlist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/true, &config);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(
std::vector<std::string>({"example1.com", "example2.com"}));
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
CreateHintsManager(top_host_provider.get());
ProcessHints(config, "1.0.0.0");
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
// Force timer to expire after random delay and schedule a hints fetch.
MoveClockForwardBy(base::Seconds(60 * 2));
EXPECT_EQ(0, top_host_provider->get_num_top_hosts_called());
// Hints fetcher should not even be created.
EXPECT_FALSE(active_tabs_batch_update_hints_fetcher());
}
TEST_F(HintsManagerFetchingTest, HintsFetcherEnabledNoHostsOrUrlsToFetch) {
auto scoped_feature_list = SetUpDeferStartupActiveTabsHintsFetch(false);
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(std::vector<std::string>({}));
CreateHintsManager(top_host_provider.get());
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
InitializeWithDefaultConfig("1.0.0");
// No hints fetch should happen on startup.
RunUntilIdle();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsManager.ActiveTabUrlsToFetchFor", 0);
// Force timer to expire after random delay and schedule a hints fetch.
MoveClockForwardBy(base::Seconds(60 * 2));
EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
EXPECT_EQ(1, tab_url_provider()->get_num_urls_called());
// Hints fetcher should not even be created.
EXPECT_FALSE(active_tabs_batch_update_hints_fetcher());
// Move it forward again to make sure timer is scheduled.
MoveClockForwardBy(base::Seconds(kUpdateFetchHintsTimeSecs));
EXPECT_EQ(2, top_host_provider->get_num_top_hosts_called());
EXPECT_EQ(2, tab_url_provider()->get_num_urls_called());
// Hints fetcher should not even be created.
EXPECT_FALSE(active_tabs_batch_update_hints_fetcher());
}
TEST_F(HintsManagerFetchingTest, HintsFetcherEnabledNoHostsButHasUrlsToFetch) {
auto scoped_feature_list = SetUpDeferStartupActiveTabsHintsFetch(false);
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(std::vector<std::string>({}));
CreateHintsManager(top_host_provider.get());
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
InitializeWithDefaultConfig("1.0.0");
tab_url_provider()->SetUrls(
{GURL("https://a.com"), GURL("https://b.com"), GURL("chrome://new-tab")});
// No hints fetch should happen on startup.
RunUntilIdle();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsManager.ActiveTabUrlsToFetchFor", 0);
// Force timer to expire after random delay and schedule a hints fetch that
// succeeds.
MoveClockForwardBy(base::Seconds(60 * 2));
EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
EXPECT_EQ(1, tab_url_provider()->get_num_urls_called());
EXPECT_EQ(1,
active_tabs_batch_update_hints_fetcher()->num_fetches_requested());
EXPECT_EQ("en-US",
active_tabs_batch_update_hints_fetcher()->locale_requested());
EXPECT_EQ(
proto::RequestContext::CONTEXT_BATCH_UPDATE_ACTIVE_TABS,
active_tabs_batch_update_hints_fetcher()->request_context_requested());
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.ActiveTabUrlsToFetchFor", 2, 1);
// Move it forward again to make sure timer is scheduled.
MoveClockForwardBy(base::Seconds(kUpdateFetchHintsTimeSecs));
EXPECT_EQ(2, top_host_provider->get_num_top_hosts_called());
EXPECT_EQ(2, tab_url_provider()->get_num_urls_called());
// Urls didn't change and we have all URLs cached in store.
EXPECT_EQ(1,
active_tabs_batch_update_hints_fetcher()->num_fetches_requested());
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.ActiveTabUrlsToFetchFor", 0, 1);
}
// Verifies hints for active tab URLs is not fetched immediately on startup. It
// should be fetched after a random delay for the first time, and then continue
// to be fetched.
TEST_F(HintsManagerFetchingTest, HintsFetcherTimerFetchOnStartup) {
auto scoped_feature_list = SetUpDeferStartupActiveTabsHintsFetch(false);
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(std::vector<std::string>({}));
CreateHintsManager(top_host_provider.get());
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
InitializeWithDefaultConfig("1.0.0");
tab_url_provider()->SetUrls(
{GURL("https://a.com"), GURL("https://b.com"), GURL("chrome://new-tab")});
// No hints fetch should happen on startup.
RunUntilIdle();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsManager.ActiveTabUrlsToFetchFor", 0);
EXPECT_FALSE(active_tabs_batch_update_hints_fetcher());
EXPECT_EQ(0, tab_url_provider()->get_num_urls_called());
// Force timer to expire after random delay and schedule a hints fetch that
// succeeds.
MoveClockForwardBy(base::Seconds(60 * 2));
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.ActiveTabUrlsToFetchFor", 2, 1);
EXPECT_EQ(1, tab_url_provider()->get_num_urls_called());
EXPECT_EQ(1,
active_tabs_batch_update_hints_fetcher()->num_fetches_requested());
// Move it forward again to make sure timer is scheduled.
MoveClockForwardBy(base::Seconds(kUpdateFetchHintsTimeSecs));
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.ActiveTabUrlsToFetchFor", 0, 1);
EXPECT_EQ(2, tab_url_provider()->get_num_urls_called());
EXPECT_EQ(1,
active_tabs_batch_update_hints_fetcher()->num_fetches_requested());
}
// Verifies the deferred startup mode that fetches hints for active tab URLs on
// deferred startup (but not on immediate startup). It should continue to be
// fetched after a refresh duration.
TEST_F(HintsManagerFetchingTest, HintsFetcherDeferredStartup) {
auto scoped_feature_list = SetUpDeferStartupActiveTabsHintsFetch(true);
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(std::vector<std::string>({}));
CreateHintsManager(top_host_provider.get());
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
InitializeWithDefaultConfig("1.0.0");
tab_url_provider()->SetUrls(
{GURL("https://a.com"), GURL("https://b.com"), GURL("chrome://new-tab")});
// No hints fetch should happen on startup.
RunUntilIdle();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsManager.ActiveTabUrlsToFetchFor", 0);
EXPECT_EQ(0, tab_url_provider()->get_num_urls_called());
// Hints fetch should be triggered on deferred startup.
hints_manager()->OnDeferredStartup();
RunUntilIdle();
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.ActiveTabUrlsToFetchFor", 2, 1);
EXPECT_EQ(1, tab_url_provider()->get_num_urls_called());
EXPECT_EQ(1,
active_tabs_batch_update_hints_fetcher()->num_fetches_requested());
// Move it forward again to make sure timer is scheduled.
MoveClockForwardBy(base::Seconds(kUpdateFetchHintsTimeSecs));
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.ActiveTabUrlsToFetchFor", 0, 1);
EXPECT_EQ(2, tab_url_provider()->get_num_urls_called());
EXPECT_EQ(1,
active_tabs_batch_update_hints_fetcher()->num_fetches_requested());
}
TEST_F(HintsManagerFetchingTest,
HintsFetched_RegisteredOptimizationTypes_AllWithOptFilter) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
proto::Configuration config;
BloomFilter allowlist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&allowlist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, allowlist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/true, &config);
ProcessHints(config, "1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
auto navigation_data = CreateTestNavigationData(url_without_hints(),
{proto::LITE_PAGE_REDIRECT});
base::HistogramTester histogram_tester;
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsFetcher.GetHintsRequest.UrlCount", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus", 0);
}
TEST_F(HintsManagerFetchingTest, HintsFetchedAtNavigationTime) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
auto navigation_data =
CreateTestNavigationData(url_without_hints(), {proto::DEFER_ALL_SCRIPT});
base::HistogramTester histogram_tester;
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 1, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsFetcher.GetHintsRequest.UrlCount", 1, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHostAndURL, 1);
}
TEST_F(HintsManagerFetchingTest,
HintsFetchedAtNavigationTime_FetchNotAttempted) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
auto navigation_data =
CreateTestNavigationData(url_without_hints(), {proto::DEFER_ALL_SCRIPT});
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory({HintsFetcherEndState::kFetchFailed}));
base::HistogramTester histogram_tester;
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsFetcher.GetHintsRequest.UrlCount", 0);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchNotAttempted, 1);
}
TEST_F(HintsManagerFetchingTest,
HintsFetchedAtNavigationTime_HasComponentHintButNotFetched) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
InitializeWithDefaultConfig("1.0.0.0");
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
// Set to online so fetch is activated.
SetConnectionOnline();
auto navigation_data =
CreateTestNavigationData(url_with_hints(), {proto::DEFER_ALL_SCRIPT});
base::HistogramTester histogram_tester;
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 0);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchURL, 1);
}
TEST_F(HintsManagerFetchingTest,
HintsFetchedAtNavigationTime_DoesNotRemoveManualOverride) {
GURL example_url("http://www.example.com/hasoverride");
proto::Configuration config;
proto::Hint* hint = config.add_hints();
hint->set_key(example_url.spec());
hint->set_key_representation(proto::FULL_URL);
proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("*");
proto::Optimization* opt = page_hint->add_allowlisted_optimizations();
opt->set_optimization_type(proto::DEFER_ALL_SCRIPT);
std::string encoded_config;
config.SerializeToString(&encoded_config);
base::Base64Encode(encoded_config, &encoded_config);
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kHintsProtoOverride, encoded_config);
// Re-create hints manager with override.
CreateHintsManager(/*top_host_provider=*/nullptr);
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
// Set to online so fetch is activated.
SetConnectionOnline();
auto navigation_data =
CreateTestNavigationData(example_url, {proto::DEFER_ALL_SCRIPT});
base::HistogramTester histogram_tester;
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 1, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsFetcher.GetHintsRequest.UrlCount", 0, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHost, 1);
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::DEFER_ALL_SCRIPT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(optimization_type_decision,
OptimizationTypeDecision::kAllowedByHint);
}
TEST_F(HintsManagerFetchingTest, URLHintsNotFetchedAtNavigationTime) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
InitializeWithDefaultConfig("1.0.0.0");
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
// Set to online so fetch is activated.
SetConnectionOnline();
{
base::HistogramTester histogram_tester;
auto navigation_data =
CreateTestNavigationData(url_with_hints(), {proto::DEFER_ALL_SCRIPT});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 0);
// Make sure navigation data is populated correctly.
EXPECT_TRUE(navigation_data->hints_fetch_latency().has_value());
EXPECT_EQ(navigation_data->hints_fetch_attempt_status(),
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchURL);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchURL, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager."
"PageNavigationHintsReturnedBeforeDataFlushed",
true, 1);
RunUntilIdle();
}
{
base::HistogramTester histogram_tester;
auto navigation_data =
CreateTestNavigationData(url_with_hints(), {proto::DEFER_ALL_SCRIPT});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchNotAttempted, 1);
// Make sure navigation data is populated correctly.
EXPECT_FALSE(navigation_data->hints_fetch_latency().has_value());
EXPECT_EQ(
navigation_data->hints_fetch_attempt_status(),
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchNotAttempted);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsManager."
"PageNavigationHintsReturnedBeforeDataFlushed",
0);
}
}
TEST_F(HintsManagerFetchingTest, URLWithNoHintsNotRefetchedAtNavigationTime) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
InitializeWithDefaultConfig("1.0.0.0");
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
// Set to online so fetch is activated.
SetConnectionOnline();
base::HistogramTester histogram_tester;
{
auto navigation_data = CreateTestNavigationData(url_without_hints(),
{proto::DEFER_ALL_SCRIPT});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount", 0);
// Make sure navigation data is populated correctly.
EXPECT_TRUE(navigation_data->hints_fetch_latency().has_value());
EXPECT_EQ(navigation_data->hints_fetch_attempt_status(),
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHostAndURL);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHostAndURL, 1);
RunUntilIdle();
}
{
auto navigation_data = CreateTestNavigationData(url_without_hints(),
{proto::DEFER_ALL_SCRIPT});
base::RunLoop run_loop;
navigation_data = CreateTestNavigationData(url_without_hints(),
{proto::DEFER_ALL_SCRIPT});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHost, 1);
EXPECT_TRUE(navigation_data->hints_fetch_latency().has_value());
EXPECT_EQ(navigation_data->hints_fetch_attempt_status(),
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHost);
}
}
TEST_F(HintsManagerFetchingTest, CanApplyOptimizationCalledMidFetch) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
auto navigation_data =
CreateTestNavigationData(url_without_hints(), {proto::DEFER_ALL_SCRIPT});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::DEFER_ALL_SCRIPT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(optimization_type_decision,
OptimizationTypeDecision::kHintFetchStartedButNotAvailableInTime);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationCalledPostFetchButNoHintsCameBack) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
InitializeWithDefaultConfig("1.0.0.0");
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithNoHints}));
// Set to online so fetch is activated.
SetConnectionOnline();
auto navigation_data =
CreateTestNavigationData(url_without_hints(), {proto::DEFER_ALL_SCRIPT});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::DEFER_ALL_SCRIPT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(optimization_type_decision,
OptimizationTypeDecision::kNoHintAvailable);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationCalledPostFetchButFetchFailed) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
InitializeWithDefaultConfig("1.0.0.0");
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory({HintsFetcherEndState::kFetchFailed}));
// Set to online so fetch is activated.
SetConnectionOnline();
auto navigation_data =
CreateTestNavigationData(url_without_hints(), {proto::DEFER_ALL_SCRIPT});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::DEFER_ALL_SCRIPT,
/*optimization_metadata=*/nullptr);
EXPECT_EQ(optimization_type_decision,
OptimizationTypeDecision::kNoHintAvailable);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationWithURLKeyedHintApplicableForOptimizationType) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0");
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
// Set to online so fetch is activated.
SetConnectionOnline();
auto navigation_data = CreateTestNavigationData(url_with_url_keyed_hint(),
{proto::DEFER_ALL_SCRIPT});
// Make sure URL-keyed hint is fetched and processed.
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::COMPRESS_PUBLIC_IMAGES,
&optimization_metadata);
// Make sure decisions are logged correctly and metadata is populated off
// a URL-keyed hint.
EXPECT_EQ(OptimizationTypeDecision::kAllowedByHint,
optimization_type_decision);
EXPECT_TRUE(optimization_metadata.public_image_metadata().has_value());
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationNotAllowedByURLButAllowedByHostKeyedHint) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::NOSCRIPT});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
// Make sure both URL-Keyed and host-keyed hints are processed and cached.
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
auto navigation_data =
CreateTestNavigationData(url_with_url_keyed_hint(), {proto::NOSCRIPT});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::NOSCRIPT,
&optimization_metadata);
EXPECT_EQ(OptimizationTypeDecision::kAllowedByHint,
optimization_type_decision);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationNotAllowedByURLOrHostKeyedHint) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::RESOURCE_LOADING});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
// Make sure both URL-Keyed and host-keyed hints are processed and cached.
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
auto navigation_data = CreateTestNavigationData(url_with_url_keyed_hint(),
{proto::RESOURCE_LOADING});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::RESOURCE_LOADING,
&optimization_metadata);
EXPECT_EQ(OptimizationTypeDecision::kNotAllowedByHint,
optimization_type_decision);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationNoURLKeyedHintOrHostKeyedHint) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithNoHints}));
auto navigation_data = CreateTestNavigationData(
url_without_hints(), {proto::COMPRESS_PUBLIC_IMAGES});
// Attempt to fetch a hint but ensure nothing comes back.
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::COMPRESS_PUBLIC_IMAGES,
&optimization_metadata);
EXPECT_EQ(OptimizationTypeDecision::kNoHintAvailable,
optimization_type_decision);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationCalledMidFetchForURLKeyedOptimization) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
// Attempt to fetch a hint but call CanApplyOptimization right away to
// simulate being mid-fetch.
auto navigation_data = CreateTestNavigationData(
url_without_hints(), {proto::COMPRESS_PUBLIC_IMAGES});
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::COMPRESS_PUBLIC_IMAGES,
&optimization_metadata);
EXPECT_EQ(OptimizationTypeDecision::kHintFetchStartedButNotAvailableInTime,
optimization_type_decision);
}
TEST_F(HintsManagerFetchingTest,
OnNavigationStartOrRedirectWontInitiateFetchIfAlreadyStartedForTheURL) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::RESOURCE_LOADING});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
// Attempt to fetch a hint but initiate the next navigation right away to
// simulate being mid-fetch.
auto navigation_data =
CreateTestNavigationData(url_without_hints(), {proto::RESOURCE_LOADING});
{
base::HistogramTester histogram_tester;
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHostAndURL, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.ConcurrentPageNavigationFetches", 1, 1);
}
{
base::HistogramTester histogram_tester;
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchAlreadyInProgress,
1);
// Should not be recorded since we are not attempting a new fetch.
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsManager.ConcurrentPageNavigationFetches", 0);
// Set hints fetch end.so we can figure out if hints fetch start was set.
navigation_data->set_hints_fetch_end(base::TimeTicks::Now());
EXPECT_TRUE(navigation_data->hints_fetch_latency().has_value());
EXPECT_EQ(navigation_data->hints_fetch_attempt_status(),
RaceNavigationFetchAttemptStatus::
kRaceNavigationFetchAlreadyInProgress);
}
}
TEST_F(HintsManagerFetchingTest,
PageNavigationHintsFetcherGetsCleanedUpOnceHintsAreStored) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::RESOURCE_LOADING});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithNoHints}));
// Attempt to fetch a hint but initiate the next navigation right away to
// simulate being mid-fetch.
auto navigation_data =
CreateTestNavigationData(url_without_hints(), {proto::RESOURCE_LOADING});
{
base::HistogramTester histogram_tester;
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHostAndURL, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.ConcurrentPageNavigationFetches", 1, 1);
// Make sure hints are stored (i.e. fetcher is cleaned up).
RunUntilIdle();
}
{
base::HistogramTester histogram_tester;
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHost, 1);
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.ConcurrentPageNavigationFetches", 1, 1);
}
}
TEST_F(HintsManagerFetchingTest,
PageNavigationHintsFetcherCanFetchMultipleThingsConcurrently) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
auto navigation_data_with_hints = CreateTestNavigationData(
url_with_url_keyed_hint(), {proto::COMPRESS_PUBLIC_IMAGES});
auto navigation_data_without_hints = CreateTestNavigationData(
GURL("https://doesntmatter.com/"), {proto::COMPRESS_PUBLIC_IMAGES});
auto navigation_data_without_hints2 = CreateTestNavigationData(
url_without_hints(), {proto::COMPRESS_PUBLIC_IMAGES});
// Attempt to fetch a hint but initiate the next navigations right away to
// simulate being mid-fetch.
base::HistogramTester histogram_tester;
CallOnNavigationStartOrRedirect(navigation_data_with_hints.get(),
base::DoNothing());
CallOnNavigationStartOrRedirect(navigation_data_without_hints.get(),
base::DoNothing());
CallOnNavigationStartOrRedirect(navigation_data_without_hints2.get(),
base::DoNothing());
// The third one is over the max and should evict another one.
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsManager.ConcurrentPageNavigationFetches", 3);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.ConcurrentPageNavigationFetches", 1, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.ConcurrentPageNavigationFetches", 2, 2);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationAsyncDecisionComesFromInFlightURLHint) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
auto navigation_data = CreateTestNavigationData(
url_with_url_keyed_hint(), {proto::COMPRESS_PUBLIC_IMAGES});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
hints_manager()->CanApplyOptimizationAsync(
url_with_url_keyed_hint(), proto::COMPRESS_PUBLIC_IMAGES,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kTrue, decision);
EXPECT_TRUE(metadata.public_image_metadata().has_value());
}));
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.CompressPublicImages",
OptimizationTypeDecision::kAllowedByHint, 1);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationAsyncMultipleCallbacksRegisteredForSameTypeAndURL) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
auto navigation_data = CreateTestNavigationData(
url_with_url_keyed_hint(), {proto::COMPRESS_PUBLIC_IMAGES});
hints_manager()->CanApplyOptimizationAsync(
url_with_url_keyed_hint(), proto::COMPRESS_PUBLIC_IMAGES,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kTrue, decision);
EXPECT_TRUE(metadata.public_image_metadata().has_value());
}));
hints_manager()->CanApplyOptimizationAsync(
url_with_url_keyed_hint(), proto::COMPRESS_PUBLIC_IMAGES,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kTrue, decision);
EXPECT_TRUE(metadata.public_image_metadata().has_value());
}));
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.CompressPublicImages",
OptimizationTypeDecision::kAllowedByHint, 2);
}
TEST_F(
HintsManagerFetchingTest,
CanApplyOptimizationAsyncDecisionComesFromInFlightURLHintNotAllowlisted) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::RESOURCE_LOADING});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
auto navigation_data = CreateTestNavigationData(url_with_url_keyed_hint(),
{proto::RESOURCE_LOADING});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
hints_manager()->CanApplyOptimizationAsync(
url_with_url_keyed_hint(), proto::RESOURCE_LOADING,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kFalse, decision);
}));
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.ResourceLoading",
OptimizationTypeDecision::kNotAllowedByHint, 1);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationAsyncFetchFailsDoesNotStrandCallbacks) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory({HintsFetcherEndState::kFetchFailed}));
auto navigation_data = CreateTestNavigationData(
url_with_url_keyed_hint(), {proto::COMPRESS_PUBLIC_IMAGES});
hints_manager()->CanApplyOptimizationAsync(
url_with_url_keyed_hint(), proto::COMPRESS_PUBLIC_IMAGES,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kFalse, decision);
}));
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.CompressPublicImages",
OptimizationTypeDecision::kNotAllowedByHint, 1);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationAsyncInfoAlreadyInPriorToCall) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
auto navigation_data = CreateTestNavigationData(
url_with_url_keyed_hint(), {proto::COMPRESS_PUBLIC_IMAGES});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
hints_manager()->CanApplyOptimizationAsync(
url_with_url_keyed_hint(), proto::COMPRESS_PUBLIC_IMAGES,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kTrue, decision);
EXPECT_TRUE(metadata.public_image_metadata().has_value());
}));
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.CompressPublicImages",
OptimizationTypeDecision::kAllowedByHint, 1);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationAsyncInfoAlreadyInPriorToCallAndNotAllowlisted) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::PERFORMANCE_HINTS});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
auto navigation_data = CreateTestNavigationData(url_with_url_keyed_hint(),
{proto::PERFORMANCE_HINTS});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
hints_manager()->CanApplyOptimizationAsync(
url_with_url_keyed_hint(), proto::PERFORMANCE_HINTS,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kFalse, decision);
}));
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.PerformanceHints",
OptimizationTypeDecision::kNotAllowedByHint, 1);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationAsyncHintComesInAndNotAllowlisted) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::PERFORMANCE_HINTS});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithNoHints}));
auto navigation_data =
CreateTestNavigationData(url_without_hints(), {proto::PERFORMANCE_HINTS});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
hints_manager()->CanApplyOptimizationAsync(
url_without_hints(), proto::PERFORMANCE_HINTS,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kFalse, decision);
}));
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.PerformanceHints",
OptimizationTypeDecision::kNoHintAvailable, 1);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationAsyncDoesNotStrandCallbacksAtBeginningOfChain) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to offline so fetch is NOT activated.
SetConnectionOffline();
GURL url_that_redirected("https://urlthatredirected.com");
auto navigation_data_redirect = CreateTestNavigationData(
url_that_redirected, {proto::COMPRESS_PUBLIC_IMAGES});
hints_manager()->CanApplyOptimizationAsync(
url_that_redirected, proto::COMPRESS_PUBLIC_IMAGES,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kFalse, decision);
}));
hints_manager()->OnNavigationFinish(
{url_that_redirected, GURL("https://otherurl.com/")});
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.CompressPublicImages",
OptimizationTypeDecision::kNoHintAvailable, 1);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationAsyncDoesNotStrandCallbacksIfFetchNotPending) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to offline so fetch is NOT activated.
SetConnectionOffline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithNoHints}));
auto navigation_data = CreateTestNavigationData(
url_with_url_keyed_hint(), {proto::COMPRESS_PUBLIC_IMAGES});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
hints_manager()->CanApplyOptimizationAsync(
url_with_url_keyed_hint(), proto::COMPRESS_PUBLIC_IMAGES,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kFalse, decision);
}));
hints_manager()->OnNavigationFinish({url_with_url_keyed_hint()});
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.CompressPublicImages",
OptimizationTypeDecision::kNotAllowedByHint, 1);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationAsyncWithDecisionFromAllowlistReturnsRightAway) {
base::HistogramTester histogram_tester;
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
proto::Configuration config;
BloomFilter allowlist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&allowlist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, allowlist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/true, &config);
ProcessHints(config, "1.0.0.0");
auto navigation_data = CreateTestNavigationData(
GURL("https://notallowed.com/123"), {proto::LITE_PAGE_REDIRECT});
hints_manager()->CanApplyOptimizationAsync(
navigation_data->navigation_url(), proto::LITE_PAGE_REDIRECT,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kFalse, decision);
}));
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.LitePageRedirect",
OptimizationTypeDecision::kNotAllowedByOptimizationFilter, 1);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationAsyncWithDecisionFromBlocklistReturnsRightAway) {
base::HistogramTester histogram_tester;
hints_manager()->RegisterOptimizationTypes({proto::LITE_PAGE_REDIRECT});
proto::Configuration config;
BloomFilter blocklist_bloom_filter(kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits);
PopulateBloomFilterWithDefaultHost(&blocklist_bloom_filter);
AddBloomFilterToConfig(proto::LITE_PAGE_REDIRECT, blocklist_bloom_filter,
kDefaultHostBloomFilterNumHashFunctions,
kDefaultHostBloomFilterNumBits,
/*is_allowlist=*/false, &config);
ProcessHints(config, "1.0.0.0");
auto navigation_data = CreateTestNavigationData(
GURL("https://m.host.com/123"), {proto::LITE_PAGE_REDIRECT});
hints_manager()->CanApplyOptimizationAsync(
navigation_data->navigation_url(), proto::LITE_PAGE_REDIRECT,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kFalse, decision);
}));
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.LitePageRedirect",
OptimizationTypeDecision::kNotAllowedByOptimizationFilter, 1);
}
TEST_F(HintsManagerFetchingTest,
OnNavigationFinishDoesNotPrematurelyInvokeRegisteredCallbacks) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
auto navigation_data = CreateTestNavigationData(url_with_url_keyed_hint(),
{proto::LITE_PAGE_REDIRECT});
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
hints_manager()->CanApplyOptimizationAsync(
url_with_url_keyed_hint(), proto::COMPRESS_PUBLIC_IMAGES,
base::BindOnce([](OptimizationGuideDecision decision,
const OptimizationMetadata& metadata) {
EXPECT_EQ(OptimizationGuideDecision::kTrue, decision);
EXPECT_TRUE(metadata.public_image_metadata().has_value());
}));
hints_manager()->OnNavigationFinish({url_with_url_keyed_hint()});
RunUntilIdle();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ApplyDecisionAsync.CompressPublicImages",
OptimizationTypeDecision::kAllowedByHint, 1);
}
TEST_F(HintsManagerFetchingTest,
OnNavigationFinishDoesNotCrashWithoutAnyCallbacksRegistered) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
hints_manager()->OnNavigationFinish({url_with_url_keyed_hint()});
RunUntilIdle();
}
TEST_F(HintsManagerFetchingTest, NewOptTypeRegisteredClearsHintCache) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
InitializeWithDefaultConfig("1.0.0.0");
GURL url("https://host.com/fetched_hint_host");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
auto navigation_data =
CreateTestNavigationData(url, {proto::DEFER_ALL_SCRIPT});
// Attempt to fetch a hint but ensure nothing comes back.
CallOnNavigationStartOrRedirect(navigation_data.get(), base::DoNothing());
RunUntilIdle();
OptimizationMetadata optimization_metadata;
OptimizationTypeDecision optimization_type_decision =
hints_manager()->CanApplyOptimization(navigation_data->navigation_url(),
proto::DEFER_ALL_SCRIPT,
&optimization_metadata);
EXPECT_EQ(OptimizationTypeDecision::kNotAllowedByHint,
optimization_type_decision);
// Register a new type that is unlaunched - this should clear the Fetched
// hints.
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
RunUntilIdle();
base::RunLoop run_loop;
// Set to offline so fetch is NOT activated, so the cache state is known and
// empty.
SetConnectionOffline();
base::HistogramTester histogram_tester;
navigation_data = CreateTestNavigationData(url, {proto::DEFER_ALL_SCRIPT});
CallOnNavigationStartOrRedirect(navigation_data.get(),
run_loop.QuitClosure());
run_loop.Run();
optimization_type_decision = hints_manager()->CanApplyOptimization(
navigation_data->navigation_url(), proto::DEFER_ALL_SCRIPT,
&optimization_metadata);
// The previously fetched hints for the host should not be available after
// registering a new optimization type.
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.RaceNavigationFetchAttemptStatus",
RaceNavigationFetchAttemptStatus::kRaceNavigationFetchHost, 1);
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationOnDemandDecisionMetadataComesFromFetch) {
base::HistogramTester histogram_tester;
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
hints_manager()->CanApplyOptimizationOnDemand(
{url_with_url_keyed_hint()}, {proto::COMPRESS_PUBLIC_IMAGES},
proto::RequestContext::CONTEXT_BOOKMARKS,
base::BindRepeating(
[](base::RunLoop* run_loop, const GURL& url,
const base::flat_map<proto::OptimizationType,
OptimizationGuideDecisionWithMetadata>&
decisions) {
ASSERT_EQ(decisions.size(), 1u);
auto it = decisions.find(proto::COMPRESS_PUBLIC_IMAGES);
ASSERT_TRUE(it != decisions.end());
EXPECT_EQ(OptimizationGuideDecision::kTrue, it->second.decision);
EXPECT_TRUE(
it->second.metadata.public_image_metadata().has_value());
run_loop->Quit();
},
run_loop.get()));
run_loop->Run();
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.HintsManager.ConcurrentBatchUpdateFetches", 1, 1);
}
TEST_F(HintsManagerFetchingTest, BatchUpdateCalledMoreThanMaxConcurrent) {
base::HistogramTester histogram_tester;
hints_manager()->RegisterOptimizationTypes({proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
// Call this over the max count.
hints_manager()->CanApplyOptimizationOnDemand(
{url_with_url_keyed_hint()}, {proto::COMPRESS_PUBLIC_IMAGES},
proto::RequestContext::CONTEXT_BOOKMARKS,
base::DoNothingAs<void(
const GURL&,
const base::flat_map<proto::OptimizationType,
OptimizationGuideDecisionWithMetadata>&)>());
hints_manager()->CanApplyOptimizationOnDemand(
{url_with_url_keyed_hint()}, {proto::COMPRESS_PUBLIC_IMAGES},
proto::RequestContext::CONTEXT_BOOKMARKS,
base::DoNothingAs<void(
const GURL&,
const base::flat_map<proto::OptimizationType,
OptimizationGuideDecisionWithMetadata>&)>());
hints_manager()->CanApplyOptimizationOnDemand(
{url_with_url_keyed_hint()}, {proto::COMPRESS_PUBLIC_IMAGES},
proto::RequestContext::CONTEXT_BOOKMARKS,
base::DoNothingAs<void(
const GURL&,
const base::flat_map<proto::OptimizationType,
OptimizationGuideDecisionWithMetadata>&)>());
// The third one is over the max and should evict another one.
histogram_tester.ExpectTotalCount(
"OptimizationGuide.HintsManager.ConcurrentBatchUpdateFetches", 3);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.ConcurrentBatchUpdateFetches", 1, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.HintsManager.ConcurrentBatchUpdateFetches", 2, 2);
}
TEST_F(
HintsManagerFetchingTest,
CanApplyOptimizationOnDemandDecisionMultipleTypesBothHostAndURLKeyedMixedFetch) {
base::HistogramTester histogram_tester;
hints_manager()->RegisterOptimizationTypes(
{proto::NOSCRIPT, proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithURLHints}));
std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
hints_manager()->CanApplyOptimizationOnDemand(
{url_with_url_keyed_hint()},
{proto::NOSCRIPT, proto::COMPRESS_PUBLIC_IMAGES},
proto::RequestContext::CONTEXT_BOOKMARKS,
base::BindRepeating(
[](base::RunLoop* run_loop, const GURL& url,
const base::flat_map<proto::OptimizationType,
OptimizationGuideDecisionWithMetadata>&
decisions) {
ASSERT_EQ(decisions.size(), 2u);
auto it = decisions.find(proto::COMPRESS_PUBLIC_IMAGES);
ASSERT_TRUE(it != decisions.end());
EXPECT_EQ(OptimizationGuideDecision::kTrue, it->second.decision);
EXPECT_TRUE(
it->second.metadata.public_image_metadata().has_value());
it = decisions.find(proto::NOSCRIPT);
ASSERT_TRUE(it != decisions.end());
EXPECT_EQ(OptimizationGuideDecision::kTrue, it->second.decision);
run_loop->Quit();
},
run_loop.get()));
run_loop->Run();
}
TEST_F(HintsManagerFetchingTest,
CanApplyOptimizationOnDemandDecisionFailedFetchDoesNotStrandCallback) {
base::HistogramTester histogram_tester;
hints_manager()->RegisterOptimizationTypes(
{proto::NOSCRIPT, proto::COMPRESS_PUBLIC_IMAGES});
InitializeWithDefaultConfig("1.0.0.0");
// Set to online so fetch is activated.
SetConnectionOnline();
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory({HintsFetcherEndState::kFetchFailed}));
std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
hints_manager()->CanApplyOptimizationOnDemand(
{url_with_url_keyed_hint()},
{proto::NOSCRIPT, proto::COMPRESS_PUBLIC_IMAGES},
proto::RequestContext::CONTEXT_BOOKMARKS,
base::BindRepeating(
[](base::RunLoop* run_loop, const GURL& url,
const base::flat_map<proto::OptimizationType,
OptimizationGuideDecisionWithMetadata>&
decisions) {
ASSERT_EQ(decisions.size(), 2u);
auto it = decisions.find(proto::COMPRESS_PUBLIC_IMAGES);
ASSERT_TRUE(it != decisions.end());
EXPECT_EQ(OptimizationGuideDecision::kFalse, it->second.decision);
// Should still populate with what's on device.
it = decisions.find(proto::NOSCRIPT);
ASSERT_TRUE(it != decisions.end());
EXPECT_EQ(OptimizationGuideDecision::kTrue, it->second.decision);
run_loop->Quit();
},
run_loop.get()));
run_loop->Run();
}
class HintsManagerFetchingNoBatchUpdateTest : public HintsManagerTest {
public:
HintsManagerFetchingNoBatchUpdateTest() {
scoped_list_.InitAndEnableFeatureWithParameters(
features::kRemoteOptimizationGuideFetching,
{{"batch_update_hints_for_top_hosts", "false"}});
}
private:
base::test::ScopedFeatureList scoped_list_;
};
TEST_F(HintsManagerFetchingNoBatchUpdateTest,
BatchUpdateHintsFetchNotScheduledIfNotAllowed) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kDisableCheckingUserPermissionsForTesting);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(
std::vector<std::string>({"example1.com", "example2.com"}));
// Force hints fetch scheduling.
CreateHintsManager(top_host_provider.get());
hints_manager()->RegisterOptimizationTypes({proto::DEFER_ALL_SCRIPT});
hints_manager()->SetHintsFetcherFactoryForTesting(
BuildTestHintsFetcherFactory(
{HintsFetcherEndState::kFetchSuccessWithHostHints}));
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch.
MoveClockForwardBy(base::Seconds(kUpdateFetchHintsTimeSecs));
// Hints fetcher should not even be created.
EXPECT_FALSE(active_tabs_batch_update_hints_fetcher());
}
} // namespace optimization_guide