blob: b279ecbac67cd3ab0a5f0c30f67a21dfb2858644 [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 "chrome/browser/optimization_guide/optimization_guide_hints_manager.h"
#include <string>
#include <utility>
#include "base/base64.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/optimization_guide/optimization_guide_navigation_data.h"
#include "chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h"
#include "chrome/test/base/testing_profile.h"
#include "components/optimization_guide/bloom_filter.h"
#include "components/optimization_guide/hints_component_util.h"
#include "components/optimization_guide/hints_fetcher.h"
#include "components/optimization_guide/optimization_guide_decider.h"
#include "components/optimization_guide/optimization_guide_enums.h"
#include "components/optimization_guide/optimization_guide_features.h"
#include "components/optimization_guide/optimization_guide_prefs.h"
#include "components/optimization_guide/optimization_guide_service.h"
#include "components/optimization_guide/optimization_guide_switches.h"
#include "components/optimization_guide/proto_database_provider_test_base.h"
#include "components/optimization_guide/top_host_provider.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/test_web_contents_factory.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_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Retry delay is 16 minutes to allow for kFetchRetryDelaySecs +
// kFetchRandomMaxDelaySecs to pass.
constexpr int kTestFetchRetryDelaySecs = 60 * 16;
constexpr int kUpdateFetchHintsTimeSecs = 24 * 60 * 60; // 24 hours.
const int kBlackBlacklistBloomFilterNumHashFunctions = 7;
const int kBlackBlacklistBloomFilterNumBits = 511;
void PopulateBlackBlacklistBloomFilter(
optimization_guide::BloomFilter* bloom_filter) {
bloom_filter->Add("black.com");
}
void AddBlacklistBloomFilterToConfig(
optimization_guide::proto::OptimizationType optimization_type,
const optimization_guide::BloomFilter& blacklist_bloom_filter,
int num_hash_functions,
int num_bits,
optimization_guide::proto::Configuration* config) {
std::string blacklist_data(
reinterpret_cast<const char*>(&blacklist_bloom_filter.bytes()[0]),
blacklist_bloom_filter.bytes().size());
optimization_guide::proto::OptimizationFilter* blacklist_proto =
config->add_optimization_blacklists();
blacklist_proto->set_optimization_type(optimization_type);
std::unique_ptr<optimization_guide::proto::BloomFilter> bloom_filter_proto =
std::make_unique<optimization_guide::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(blacklist_data);
blacklist_proto->set_allocated_bloom_filter(bloom_filter_proto.release());
}
std::unique_ptr<optimization_guide::proto::GetHintsResponse> BuildHintsResponse(
std::vector<std::string> hosts) {
std::unique_ptr<optimization_guide::proto::GetHintsResponse>
get_hints_response =
std::make_unique<optimization_guide::proto::GetHintsResponse>();
for (const auto& host : hosts) {
optimization_guide::proto::Hint* hint = get_hints_response->add_hints();
hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
hint->set_key(host);
optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("page pattern");
}
return get_hints_response;
}
} // namespace
class TestOptimizationGuideService
: public optimization_guide::OptimizationGuideService {
public:
explicit TestOptimizationGuideService(
const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
: OptimizationGuideService(ui_task_runner) {}
~TestOptimizationGuideService() override = default;
void AddObserver(
optimization_guide::OptimizationGuideServiceObserver* observer) override {
add_observer_called_ = true;
}
void RemoveObserver(
optimization_guide::OptimizationGuideServiceObserver* observer) override {
remove_observer_called_ = true;
}
bool AddObserverCalled() const { return add_observer_called_; }
bool RemoveObserverCalled() const { return remove_observer_called_; }
private:
bool add_observer_called_ = false;
bool remove_observer_called_ = false;
};
// A mock class implementation of TopHostProvider.
class FakeTopHostProvider : public optimization_guide::TopHostProvider {
public:
explicit FakeTopHostProvider(std::vector<std::string> top_hosts)
: top_hosts_(top_hosts) {}
std::vector<std::string> GetTopHosts(size_t max_sites) override {
num_top_hosts_called_++;
if (top_hosts_.size() <= max_sites) {
return top_hosts_;
}
std::vector<std::string> top_hosts;
top_hosts.reserve(max_sites);
for (const auto& top_host : top_hosts_) {
if (top_hosts.size() >= max_sites)
return top_hosts;
top_hosts.push_back(top_host);
}
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;
};
enum class HintsFetcherEndState {
kFetchFailed = 0,
kFetchSuccessWithHints = 1,
kFetchSuccessWithNoHints = 2,
};
// A mock class implementation of HintsFetcher.
class TestHintsFetcher : public optimization_guide::HintsFetcher {
using HintsFetcher::FetchOptimizationGuideServiceHints;
public:
TestHintsFetcher(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
GURL optimization_guide_service_url,
HintsFetcherEndState fetch_state,
PrefService* pref_service)
: HintsFetcher(url_loader_factory,
optimization_guide_service_url,
pref_service),
fetch_state_(fetch_state) {}
bool FetchOptimizationGuideServiceHints(
const std::vector<std::string>& hosts,
optimization_guide::HintsFetchedCallback hints_fetched_callback)
override {
switch (fetch_state_) {
case HintsFetcherEndState::kFetchFailed:
std::move(hints_fetched_callback).Run(base::nullopt);
return false;
case HintsFetcherEndState::kFetchSuccessWithHints:
hints_fetched_ = true;
std::move(hints_fetched_callback).Run(BuildHintsResponse({"host.com"}));
return true;
case HintsFetcherEndState::kFetchSuccessWithNoHints:
hints_fetched_ = true;
std::move(hints_fetched_callback).Run(BuildHintsResponse({}));
return true;
}
return true;
}
bool hints_fetched() { return hints_fetched_; }
private:
bool hints_fetched_ = false;
HintsFetcherEndState fetch_state_;
};
class OptimizationGuideHintsManagerTest
: public optimization_guide::ProtoDatabaseProviderTestBase {
public:
OptimizationGuideHintsManagerTest() = default;
~OptimizationGuideHintsManagerTest() override = default;
void SetUp() override {
optimization_guide::ProtoDatabaseProviderTestBase::SetUp();
web_contents_factory_.reset(new content::TestWebContentsFactory);
CreateServiceAndHintsManager();
}
void TearDown() override {
optimization_guide::ProtoDatabaseProviderTestBase::TearDown();
ResetHintsManager();
}
void CreateServiceAndHintsManager(
optimization_guide::TopHostProvider* top_host_provider = nullptr) {
if (hints_manager_) {
ResetHintsManager();
}
optimization_guide_service_ =
std::make_unique<TestOptimizationGuideService>(
task_environment_.GetMainThreadTaskRunner());
pref_service_ = std::make_unique<TestingPrefServiceSimple>();
optimization_guide::prefs::RegisterProfilePrefs(pref_service_->registry());
url_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
hints_manager_ = std::make_unique<OptimizationGuideHintsManager>(
optimization_guide_service_.get(), temp_dir(), pref_service_.get(),
db_provider_.get(), top_host_provider, url_loader_factory_);
hints_manager_->SetClockForTesting(task_environment_.GetMockClock());
// Add observer is called after the HintCache is fully initialized,
// indicating that the OptimizationGuideHintsManager is ready to process
// hints.
while (!optimization_guide_service_->AddObserverCalled()) {
RunUntilIdle();
}
}
void ResetHintsManager() {
hints_manager_.reset();
RunUntilIdle();
}
void ProcessInvalidHintsComponentInfo(const std::string& version) {
optimization_guide::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 ProcessHints(const optimization_guide::proto::Configuration& config,
const std::string& version) {
optimization_guide::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;
hints_manager_->ListenForNextUpdateForTesting(run_loop.QuitClosure());
hints_manager_->OnHintsComponentAvailable(info);
run_loop.Run();
}
void InitializeWithDefaultConfig(const std::string& version) {
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("somedomain.org");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
hint1->set_version("someversion");
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("/news/");
page_hint1->set_max_ect_trigger(
optimization_guide::proto::EFFECTIVE_CONNECTION_TYPE_3G);
optimization_guide::proto::Optimization* experimental_opt =
page_hint1->add_whitelisted_optimizations();
experimental_opt->set_optimization_type(
optimization_guide::proto::NOSCRIPT);
experimental_opt->set_experiment_name("experiment");
optimization_guide::proto::PreviewsMetadata* experimental_opt_metadata =
experimental_opt->mutable_previews_metadata();
experimental_opt_metadata->set_inflation_percent(12345);
optimization_guide::proto::Optimization* default_opt =
page_hint1->add_whitelisted_optimizations();
default_opt->set_optimization_type(optimization_guide::proto::NOSCRIPT);
optimization_guide::proto::PreviewsMetadata* default_opt_metadata =
default_opt->mutable_previews_metadata();
default_opt_metadata->set_inflation_percent(1234);
ProcessHints(config, version);
}
std::unique_ptr<TestHintsFetcher> BuildTestHintsFetcher(
HintsFetcherEndState end_state) {
std::unique_ptr<TestHintsFetcher> hints_fetcher =
std::make_unique<TestHintsFetcher>(url_loader_factory_,
GURL("https://hintsserver.com"),
end_state, pref_service());
return hints_fetcher;
}
void MoveClockForwardBy(base::TimeDelta time_delta) {
task_environment_.FastForwardBy(time_delta);
RunUntilIdle();
}
// Creates a navigation handle with the OptimizationGuideWebContentsObserver
// attached.
std::unique_ptr<content::MockNavigationHandle>
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
const GURL& url) {
if (!testing_profile_) {
testing_profile_ = std::make_unique<TestingProfile>();
}
content::WebContents* web_contents =
web_contents_factory_->CreateWebContents(testing_profile_.get());
OptimizationGuideWebContentsObserver::CreateForWebContents(web_contents);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
std::make_unique<content::MockNavigationHandle>(web_contents);
navigation_handle->set_url(url);
return navigation_handle;
}
// Returns the optimization guide navigation data attached to
// |navigation_handle|.
OptimizationGuideNavigationData* GetOptimizationGuideNavigationData(
content::NavigationHandle* navigation_handle) {
OptimizationGuideWebContentsObserver* observer =
OptimizationGuideWebContentsObserver::FromWebContents(
navigation_handle->GetWebContents());
return observer->GetOrCreateOptimizationGuideNavigationData(
navigation_handle);
}
OptimizationGuideHintsManager* hints_manager() const {
return hints_manager_.get();
}
TestHintsFetcher* hints_fetcher() const {
return static_cast<TestHintsFetcher*>(hints_manager()->hints_fetcher());
}
GURL url_with_hints() const {
return GURL("https://somedomain.org/news/whatever");
}
base::FilePath temp_dir() const { return temp_dir_.GetPath(); }
TestingPrefServiceSimple* pref_service() const { return pref_service_.get(); }
protected:
void RunUntilIdle() {
task_environment_.RunUntilIdle();
base::RunLoop().RunUntilIdle();
}
private:
void WriteConfigToFile(const optimization_guide::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()));
}
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::UI,
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
std::unique_ptr<TestingProfile> testing_profile_;
std::unique_ptr<content::TestWebContentsFactory> web_contents_factory_;
std::unique_ptr<OptimizationGuideHintsManager> hints_manager_;
std::unique_ptr<TestOptimizationGuideService> optimization_guide_service_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
network::TestURLLoaderFactory test_url_loader_factory_;
DISALLOW_COPY_AND_ASSIGN(OptimizationGuideHintsManagerTest);
};
TEST_F(OptimizationGuideHintsManagerTest,
ProcessHintsWithValidCommandLineOverride) {
base::HistogramTester histogram_tester;
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint = config.add_hints();
hint->set_key("somedomain.org");
hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("noscript_default_2g");
optimization_guide::proto::Optimization* optimization =
page_hint->add_whitelisted_optimizations();
optimization->set_optimization_type(optimization_guide::proto::NOSCRIPT);
std::string encoded_config;
config.SerializeToString(&encoded_config);
base::Base64Encode(encoded_config, &encoded_config);
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
optimization_guide::switches::kHintsProtoOverride, encoded_config);
CreateServiceAndHintsManager();
// The below histogram should not be recorded since hints weren't coming
// directly from the component.
histogram_tester.ExpectTotalCount("OptimizationGuide.ProcessHintsResult", 0);
// However, we still expect the local histogram for the hints being updated to
// be recorded.
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.UpdateComponentHints.Result", true, 1);
}
TEST_F(OptimizationGuideHintsManagerTest,
ProcessHintsWithInvalidCommandLineOverride) {
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
optimization_guide::switches::kHintsProtoOverride, "this-is-not-a-proto");
CreateServiceAndHintsManager();
// 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(OptimizationGuideHintsManagerTest,
ProcessHintsWithCommandLineOverrideShouldNotBeOverriddenByNewComponent) {
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint = config.add_hints();
hint->set_key("somedomain.org");
hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("noscript_default_2g");
optimization_guide::proto::Optimization* optimization =
page_hint->add_whitelisted_optimizations();
optimization->set_optimization_type(optimization_guide::proto::NOSCRIPT);
std::string encoded_config;
config.SerializeToString(&encoded_config);
base::Base64Encode(encoded_config, &encoded_config);
{
base::HistogramTester histogram_tester;
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
optimization_guide::switches::kHintsProtoOverride, encoded_config);
CreateServiceAndHintsManager();
// The below histogram should not be recorded since hints weren't coming
// directly from the component.
histogram_tester.ExpectTotalCount("OptimizationGuide.ProcessHintsResult",
0);
// However, we still expect the local histogram for the hints being updated
// to be recorded.
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(OptimizationGuideHintsManagerTest, ParseTwoConfigVersions) {
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("somedomain.org");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
hint1->set_version("someversion");
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("/news/");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
optimization_guide::proto::ResourceLoadingHint* resource_loading_hint1 =
optimization1->add_resource_loading_hints();
resource_loading_hint1->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_loading_hint1->set_resource_pattern("news_cruft.js");
// Test the first time parsing the config.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("1.0.0.0");
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
optimization_guide::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",
optimization_guide::ProcessHintsComponentResult::kSuccess, 1);
}
}
TEST_F(OptimizationGuideHintsManagerTest, ParseOlderConfigVersions) {
// Test the first time parsing the config.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("10.0.0.0");
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
optimization_guide::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",
optimization_guide::ProcessHintsComponentResult::
kSkippedProcessingHints,
1);
}
}
TEST_F(OptimizationGuideHintsManagerTest, 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",
optimization_guide::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",
optimization_guide::ProcessHintsComponentResult::
kSkippedProcessingHints,
1);
}
}
TEST_F(OptimizationGuideHintsManagerTest, ComponentInfoDidNotContainConfig) {
base::HistogramTester histogram_tester;
ProcessInvalidHintsComponentInfo("1.0.0.0");
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
optimization_guide::ProcessHintsComponentResult::kFailedReadingFile, 1);
}
TEST_F(OptimizationGuideHintsManagerTest, ProcessHintsWithExistingPref) {
// Write hints processing pref for version 2.0.0.
pref_service()->SetString(
optimization_guide::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",
optimization_guide::ProcessHintsComponentResult::
kFailedFinishProcessing,
1);
EXPECT_FALSE(
pref_service()
->GetString(
optimization_guide::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(
optimization_guide::prefs::kPendingHintsProcessingVersion)
.empty());
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
optimization_guide::ProcessHintsComponentResult::kSuccess, 1);
}
}
TEST_F(OptimizationGuideHintsManagerTest, ProcessHintsWithInvalidPref) {
// Create pref file with invalid version.
pref_service()->SetString(
optimization_guide::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(
optimization_guide::prefs::kPendingHintsProcessingVersion)
.empty());
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
optimization_guide::ProcessHintsComponentResult::
kFailedFinishProcessing,
1);
}
// Now verify config is processed with pref cleared.
{
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("2.0.0");
EXPECT_TRUE(
pref_service()
->GetString(
optimization_guide::prefs::kPendingHintsProcessingVersion)
.empty());
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
optimization_guide::ProcessHintsComponentResult::kSuccess, 1);
}
}
TEST_F(OptimizationGuideHintsManagerTest,
LoadHintForNavigationWithHintAfterCommit) {
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("3.0.0.0");
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
url_with_hints());
navigation_handle->set_has_committed(true);
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
histogram_tester.ExpectUniqueSample("OptimizationGuide.LoadedHint.Result",
true, 1);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest, LoadHintForNavigationWithHint) {
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("3.0.0.0");
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
url_with_hints());
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
histogram_tester.ExpectUniqueSample("OptimizationGuide.LoadedHint.Result",
true, 1);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest, LoadHintForNavigationNoHint) {
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("3.0.0.0");
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("https://notinhints.com"));
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
histogram_tester.ExpectUniqueSample("OptimizationGuide.LoadedHint.Result",
false, 1);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_FALSE(navigation_data->has_hint_before_commit().value());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest, LoadHintForNavigationNoHost) {
base::HistogramTester histogram_tester;
InitializeWithDefaultConfig("3.0.0.0");
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("blargh"));
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
histogram_tester.ExpectTotalCount("OptimizationGuide.LoadedHint.Result", 0);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest,
OptimizationFiltersAreOnlyLoadedIfTypeIsRegistered) {
optimization_guide::proto::Configuration config;
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::NOSCRIPT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &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);
}
// 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(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
run_loop.Run();
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
optimization_guide::OptimizationFilterStatus::
kFoundServerBlacklistConfig,
1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
optimization_guide::OptimizationFilterStatus::kCreatedServerBlacklist,
1);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript", 0);
EXPECT_TRUE(hints_manager()->HasLoadedOptimizationFilter(
optimization_guide::proto::LITE_PAGE_REDIRECT));
EXPECT_FALSE(hints_manager()->HasLoadedOptimizationFilter(
optimization_guide::proto::NOSCRIPT));
}
// 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(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
run_loop.Run();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript", 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(
{optimization_guide::proto::DEFER_ALL_SCRIPT});
run_loop.Run();
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect", 0);
histogram_tester.ExpectTotalCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript", 0);
}
// Registering a new optimization type with a filter does trigger a
// reload of the filters.
{
base::HistogramTester histogram_tester;
base::RunLoop run_loop;
hints_manager()->ListenForNextUpdateForTesting(run_loop.QuitClosure());
hints_manager()->RegisterOptimizationTypes(
{optimization_guide::proto::NOSCRIPT});
run_loop.Run();
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
optimization_guide::OptimizationFilterStatus::
kFoundServerBlacklistConfig,
1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
optimization_guide::OptimizationFilterStatus::kCreatedServerBlacklist,
1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript",
optimization_guide::OptimizationFilterStatus::
kFoundServerBlacklistConfig,
1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.NoScript",
optimization_guide::OptimizationFilterStatus::kCreatedServerBlacklist,
1);
EXPECT_TRUE(hints_manager()->HasLoadedOptimizationFilter(
optimization_guide::proto::LITE_PAGE_REDIRECT));
EXPECT_TRUE(hints_manager()->HasLoadedOptimizationFilter(
optimization_guide::proto::NOSCRIPT));
}
}
TEST_F(OptimizationGuideHintsManagerTest,
OptimizationFiltersOnlyLoadOncePerType) {
hints_manager()->RegisterOptimizationTypes(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
base::HistogramTester histogram_tester;
optimization_guide::proto::Configuration config;
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
ProcessHints(config, "1.0.0.0");
// We found 2 LPR blacklists: parsed one and duped the other.
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
optimization_guide::OptimizationFilterStatus::kFoundServerBlacklistConfig,
2);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
optimization_guide::OptimizationFilterStatus::kCreatedServerBlacklist, 1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
optimization_guide::OptimizationFilterStatus::
kFailedServerBlacklistDuplicateConfig,
1);
}
TEST_F(OptimizationGuideHintsManagerTest, InvalidOptimizationFilterNotLoaded) {
hints_manager()->RegisterOptimizationTypes(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
base::HistogramTester histogram_tester;
int too_many_bits =
optimization_guide::features::MaxServerBloomFilterByteSize() * 8 + 1;
optimization_guide::proto::Configuration config;
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions, too_many_bits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(
optimization_guide::proto::LITE_PAGE_REDIRECT, blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions, too_many_bits, &config);
ProcessHints(config, "1.0.0.0");
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
optimization_guide::OptimizationFilterStatus::kFoundServerBlacklistConfig,
1);
histogram_tester.ExpectBucketCount(
"OptimizationGuide.OptimizationFilterStatus.LitePageRedirect",
optimization_guide::OptimizationFilterStatus::
kFailedServerBlacklistTooBig,
1);
EXPECT_FALSE(hints_manager()->HasLoadedOptimizationFilter(
optimization_guide::proto::LITE_PAGE_REDIRECT));
}
TEST_F(OptimizationGuideHintsManagerTest,
HintsFetchNotAllowedIfFeatureIsEnabledButTopHostProviderIsNotProvided) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
{optimization_guide::features::kOptimizationHintsFetching}, {});
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(std::vector<std::string>({}));
CreateServiceAndHintsManager(/*top_host_provider=*/nullptr);
hints_manager()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_EQ(0, top_host_provider->get_num_top_hosts_called());
EXPECT_FALSE(hints_fetcher()->hints_fetched());
}
TEST_F(OptimizationGuideHintsManagerTest,
HintsFetchNotAllowedIfFeatureIsNotEnabledButTopHostProviderIsProvided) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
{}, {optimization_guide::features::kOptimizationHintsFetching});
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(
std::vector<std::string>({"example1.com", "example2.com"}));
CreateServiceAndHintsManager(top_host_provider.get());
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_EQ(0, top_host_provider->get_num_top_hosts_called());
}
TEST_F(OptimizationGuideHintsManagerTest,
HintsFetchAllowedIfFeatureIsEnabledAndTopHostProviderIsProvided) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
{optimization_guide::features::kOptimizationHintsFetching}, {});
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(
std::vector<std::string>({"example1.com", "example2.com"}));
CreateServiceAndHintsManager(top_host_provider.get());
hints_manager()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
EXPECT_TRUE(hints_fetcher()->hints_fetched());
}
TEST_F(OptimizationGuideHintsManagerTest, HintsFetcherEnabledNoHostsToFetch) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
optimization_guide::features::kOptimizationHintsFetching);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(std::vector<std::string>({}));
CreateServiceAndHintsManager(top_host_provider.get());
hints_manager()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
EXPECT_FALSE(hints_fetcher()->hints_fetched());
}
TEST_F(OptimizationGuideHintsManagerTest,
HintsFetcherEnabledWithHostsNoHintsInResponse) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
optimization_guide::features::kOptimizationHintsFetching);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(
std::vector<std::string>({"example1.com", "example2.com"}));
CreateServiceAndHintsManager(top_host_provider.get());
hints_manager()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithNoHints));
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
EXPECT_TRUE(hints_fetcher()->hints_fetched());
// Check that hints should not be fetched again after the delay for a failed
// hints fetch attempt.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
// This should be called exactly once, confirming that hints are not fetched
// again after |kTestFetchRetryDelaySecs|.
EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
}
TEST_F(OptimizationGuideHintsManagerTest, HintsFetcherTimerRetryDelay) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
optimization_guide::features::kOptimizationHintsFetching);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(
std::vector<std::string>({"example1.com", "example2.com"}));
CreateServiceAndHintsManager(top_host_provider.get());
hints_manager()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchFailed));
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch - first time.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_EQ(1, top_host_provider->get_num_top_hosts_called());
EXPECT_FALSE(hints_fetcher()->hints_fetched());
// Force speculative timer to expire after fetch fails first time, update
// hints fetcher so it succeeds this time.
hints_manager()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_EQ(2, top_host_provider->get_num_top_hosts_called());
EXPECT_TRUE(hints_fetcher()->hints_fetched());
}
TEST_F(OptimizationGuideHintsManagerTest, HintsFetcherTimerFetchSucceeds) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
optimization_guide::features::kOptimizationHintsFetching);
std::unique_ptr<FakeTopHostProvider> top_host_provider =
std::make_unique<FakeTopHostProvider>(
std::vector<std::string>({"example1.com", "example2.com"}));
// Force hints fetch scheduling.
CreateServiceAndHintsManager(top_host_provider.get());
hints_manager()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
InitializeWithDefaultConfig("1.0.0");
// Force timer to expire and schedule a hints fetch that succeeds.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_TRUE(hints_fetcher()->hints_fetched());
// TODO(mcrouse): Make sure timer is triggered by metadata entry,
// |hint_cache| control needed.
hints_manager()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_FALSE(hints_fetcher()->hints_fetched());
MoveClockForwardBy(base::TimeDelta::FromSeconds(kUpdateFetchHintsTimeSecs));
EXPECT_TRUE(hints_fetcher()->hints_fetched());
}
TEST_F(OptimizationGuideHintsManagerTest, CanApplyOptimizationUrlWithNoHost) {
hints_manager()->RegisterOptimizationTypes(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
optimization_guide::proto::Configuration config;
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
ProcessHints(config, "1.0.0.0");
// Set ECT estimate to be "painful".
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("urlwithnohost"));
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::LITE_PAGE_REDIRECT,
&optimization_target_decision, &optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(
optimization_guide::OptimizationTargetDecision::kPageLoadDoesNotMatch,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNoHintAvailable,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationHasFilterForTypeButNotLoadedYet) {
optimization_guide::proto::Configuration config;
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
ProcessHints(config, "1.0.0.0");
// Set ECT estimate to be "painful".
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("https://whatever.com/123"));
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::LITE_PAGE_REDIRECT,
&optimization_target_decision, &optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::
kHadOptimizationFilterButNotLoadedInTime,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationHasLoadedFilterForTypeUrlInBlacklistFilter) {
hints_manager()->RegisterOptimizationTypes(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
optimization_guide::proto::Configuration config;
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
ProcessHints(config, "1.0.0.0");
// Set ECT estimate to be "painful".
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("https://m.black.com/123"));
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::LITE_PAGE_REDIRECT,
&optimization_target_decision, &optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::
kNotAllowedByOptimizationFilter,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationHasLoadedFilterForTypeUrlNotInBlacklistFilter) {
hints_manager()->RegisterOptimizationTypes(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
optimization_guide::proto::Configuration config;
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
ProcessHints(config, "1.0.0.0");
// Set ECT estimate to be "painful".
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("https://whatever.com/123"));
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::LITE_PAGE_REDIRECT,
&optimization_target_decision, &optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::
kAllowedByOptimizationFilter,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest, CanApplyOptimizationNoECTEstimate) {
hints_manager()->RegisterOptimizationTypes(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
optimization_guide::proto::Configuration config;
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
ProcessHints(config, "1.0.0.0");
// Explicitly set ECT estimate to be unknown.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_UNKNOWN);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("https://whatever.com/123"));
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::LITE_PAGE_REDIRECT,
&optimization_target_decision, &optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(
optimization_guide::OptimizationTargetDecision::kPageLoadDoesNotMatch,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::
kAllowedByOptimizationFilter,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationNoHintToTriggerHigherThan2G) {
hints_manager()->RegisterOptimizationTypes(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
optimization_guide::proto::Configuration config;
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
ProcessHints(config, "1.0.0.0");
// Explicitly set ECT estimate to be unknown.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("https://whatever.com/123"));
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::LITE_PAGE_REDIRECT,
&optimization_target_decision, &optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(
optimization_guide::OptimizationTargetDecision::kPageLoadDoesNotMatch,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::
kAllowedByOptimizationFilter,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationAndPopulatesMetadataWithFirstOptThatMatchesWithExp) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeatureWithParameters(
optimization_guide::features::kOptimizationHintsExperiments,
{{"experiment_name", "experiment"}});
InitializeWithDefaultConfig("1.0.0.0");
// Set ECT estimate so hint is activated.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
url_with_hints());
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
optimization_guide::OptimizationMetadata optimization_metadata;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::NOSCRIPT, &optimization_target_decision,
&optimization_type_decision, &optimization_metadata);
EXPECT_EQ(12345, optimization_metadata.previews_metadata.inflation_percent());
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kAllowedByHint,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
ASSERT_TRUE(navigation_data->page_hint());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationAndPopulatesMetadataWithFirstOptThatMatchesNoExp) {
InitializeWithDefaultConfig("1.0.0.0");
// Set ECT estimate so hint is activated.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
url_with_hints());
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
optimization_guide::OptimizationMetadata optimization_metadata;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::NOSCRIPT, &optimization_target_decision,
&optimization_type_decision, &optimization_metadata);
EXPECT_EQ(1234, optimization_metadata.previews_metadata.inflation_percent());
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kAllowedByHint,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
ASSERT_TRUE(navigation_data->page_hint());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationHasHintButNotSlowEnough) {
InitializeWithDefaultConfig("1.0.0.0");
// Set ECT estimate so hint is activated.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_4G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
url_with_hints());
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
optimization_guide::OptimizationMetadata optimization_metadata;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::NOSCRIPT, &optimization_target_decision,
&optimization_type_decision, &optimization_metadata);
EXPECT_EQ(1234, optimization_metadata.previews_metadata.inflation_percent());
// Make sure decisions are logged correctly.
EXPECT_EQ(
optimization_guide::OptimizationTargetDecision::kPageLoadDoesNotMatch,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kAllowedByHint,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
ASSERT_TRUE(navigation_data->page_hint());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationWithNonPainfulPageLoadTarget) {
InitializeWithDefaultConfig("1.0.0.0");
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
url_with_hints());
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
optimization_guide::OptimizationMetadata optimization_metadata;
hints_manager()->CanApplyOptimization(
navigation_handle.get(), optimization_guide::OptimizationTarget::kUnknown,
optimization_guide::proto::NOSCRIPT, &optimization_target_decision,
&optimization_type_decision, &optimization_metadata);
// Make sure metadata is cleared.
EXPECT_EQ(0, optimization_metadata.previews_metadata.inflation_percent());
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kUnknown,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kUnknown,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_after_commit());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationHasPageHintButNoMatchingOptType) {
InitializeWithDefaultConfig("1.0.0.0");
// Set ECT estimate so hint is activated.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
url_with_hints());
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
optimization_guide::OptimizationMetadata optimization_metadata;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::DEFER_ALL_SCRIPT,
&optimization_target_decision, &optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNotAllowedByHint,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
ASSERT_TRUE(navigation_data->page_hint());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationUsesCachedPageHintFromNavigationData) {
InitializeWithDefaultConfig("1.0.0.0");
// Set ECT estimate so hint is activated.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
url_with_hints());
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
// Purposely set the page hint to be null to show that we override the page
// hint information from the navigation handle.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
navigation_data->set_page_hint(nullptr);
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
optimization_guide::OptimizationMetadata optimization_metadata;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::DEFER_ALL_SCRIPT,
&optimization_target_decision, &optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNoMatchingPageHint,
optimization_type_decision);
// Make sure navigation data is populated correctly.
EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
EXPECT_EQ(nullptr, navigation_data->page_hint());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationNoMatchingPageHint) {
InitializeWithDefaultConfig("1.0.0.0");
// Set ECT estimate so hint is activated.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("https://somedomain.org/nomatch"));
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
optimization_guide::OptimizationMetadata optimization_metadata;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::NOSCRIPT, &optimization_target_decision,
&optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNoMatchingPageHint,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
EXPECT_EQ(nullptr, navigation_data->page_hint());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationNoHintForNavigationMetadataClearedAnyway) {
InitializeWithDefaultConfig("1.0.0.0");
// Set ECT estimate so hint is activated.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("https://nohint.com"));
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
optimization_guide::OptimizationMetadata optimization_metadata;
optimization_metadata.previews_metadata.set_inflation_percent(12345);
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::NOSCRIPT, &optimization_target_decision,
&optimization_type_decision, &optimization_metadata);
EXPECT_EQ(0, optimization_metadata.previews_metadata.inflation_percent());
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::kNoHintAvailable,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
EXPECT_FALSE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationHasHintInCacheButNotLoaded) {
InitializeWithDefaultConfig("1.0.0.0");
// Set ECT estimate so hint is activated.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
url_with_hints());
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
optimization_guide::OptimizationMetadata optimization_metadata;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::NOSCRIPT, &optimization_target_decision,
&optimization_type_decision, &optimization_metadata);
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(
optimization_guide::OptimizationTypeDecision::kHadHintButNotLoadedInTime,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_EQ(base::nullopt, navigation_data->has_hint_before_commit());
EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ(base::nullopt, navigation_data->serialized_hint_version_string());
EXPECT_FALSE(navigation_data->has_page_hint_value());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationFilterTakesPrecedence) {
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("https://m.black.com/urlinfilterandhints"));
hints_manager()->RegisterOptimizationTypes(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("black.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
hint1->set_version("someversion");
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("https://m.black.com");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(
optimization_guide::proto::LITE_PAGE_REDIRECT);
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
ProcessHints(config, "1.0.0.0");
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
// Set ECT estimate so hint is activated.
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::LITE_PAGE_REDIRECT,
&optimization_target_decision, &optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decision points logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::
kNotAllowedByOptimizationFilter,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
ASSERT_TRUE(navigation_data->page_hint());
}
TEST_F(OptimizationGuideHintsManagerTest,
CanApplyOptimizationFilterTakesPrecedenceWithECTComingFromHint) {
std::unique_ptr<content::MockNavigationHandle> navigation_handle =
CreateMockNavigationHandleWithOptimizationGuideWebContentsObserver(
GURL("https://notfiltered.com/whatever"));
hints_manager()->RegisterOptimizationTypes(
{optimization_guide::proto::LITE_PAGE_REDIRECT});
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("notfiltered.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
hint1->set_version("someversion");
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("https://notfiltered.com");
page_hint1->set_max_ect_trigger(
optimization_guide::proto::EFFECTIVE_CONNECTION_TYPE_3G);
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(
optimization_guide::proto::LITE_PAGE_REDIRECT);
optimization_guide::BloomFilter blacklist_bloom_filter(
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits);
PopulateBlackBlacklistBloomFilter(&blacklist_bloom_filter);
AddBlacklistBloomFilterToConfig(optimization_guide::proto::LITE_PAGE_REDIRECT,
blacklist_bloom_filter,
kBlackBlacklistBloomFilterNumHashFunctions,
kBlackBlacklistBloomFilterNumBits, &config);
ProcessHints(config, "1.0.0.0");
base::RunLoop run_loop;
hints_manager()->LoadHintForNavigation(navigation_handle.get(),
run_loop.QuitClosure());
run_loop.Run();
hints_manager()->OnEffectiveConnectionTypeChanged(
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_3G);
optimization_guide::OptimizationTargetDecision optimization_target_decision;
optimization_guide::OptimizationTypeDecision optimization_type_decision;
hints_manager()->CanApplyOptimization(
navigation_handle.get(),
optimization_guide::OptimizationTarget::kPainfulPageLoad,
optimization_guide::proto::LITE_PAGE_REDIRECT,
&optimization_target_decision, &optimization_type_decision,
/*optimization_metadata=*/nullptr);
// Make sure decisions are logged correctly.
EXPECT_EQ(optimization_guide::OptimizationTargetDecision::kPageLoadMatches,
optimization_target_decision);
EXPECT_EQ(optimization_guide::OptimizationTypeDecision::
kAllowedByOptimizationFilter,
optimization_type_decision);
// Make sure navigation data is populated correctly.
OptimizationGuideNavigationData* navigation_data =
GetOptimizationGuideNavigationData(navigation_handle.get());
EXPECT_TRUE(navigation_data->has_hint_before_commit().value());
EXPECT_TRUE(navigation_data->has_hint_after_commit().value());
EXPECT_EQ("someversion", navigation_data->serialized_hint_version_string());
ASSERT_TRUE(navigation_data->page_hint());
}