blob: 11d21eae08716015ec23b50587249b0e73863604 [file] [log] [blame]
// Copyright 2017 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/previews/content/previews_optimization_guide.h"
#include <memory>
#include "base/base64.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/gtest_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_clock.h"
#include "components/optimization_guide/hints_component_info.h"
#include "components/optimization_guide/optimization_guide_service.h"
#include "components/optimization_guide/proto/hints.pb.h"
#include "components/previews/content/hints_fetcher.h"
#include "components/previews/content/previews_hints.h"
#include "components/previews/content/previews_top_host_provider.h"
#include "components/previews/content/previews_user_data.h"
#include "components/previews/core/bloom_filter.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_features.h"
#include "components/previews/core/previews_switches.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"
#include "url/gurl.h"
namespace previews {
namespace {
// A fake default page_id for testing.
const uint64_t kDefaultPageId = 123456;
enum class HintsFetcherEndState {
kFetchFailed = 0,
kFetchSuccessWithHints = 1,
kFetchSuccessWithNoHints = 2,
};
// 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.
} // namespace
class TestOptimizationGuideService
: public optimization_guide::OptimizationGuideService {
public:
explicit TestOptimizationGuideService(
const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
: OptimizationGuideService(ui_task_runner),
add_observer_called_(false),
remove_observer_called_(false) {}
void AddObserver(
optimization_guide::OptimizationGuideServiceObserver* observer) override {
add_observer_called_ = true;
}
void RemoveObserver(
optimization_guide::OptimizationGuideServiceObserver* observer) override {
remove_observer_called_ = true;
}
bool AddObserverCalled() { return add_observer_called_; }
bool RemoveObserverCalled() { return remove_observer_called_; }
private:
bool add_observer_called_;
bool remove_observer_called_;
};
// A mock class implementation for unittesting previews_optimization_guide.
class MockPreviewsTopHostProvider : public PreviewsTopHostProvider {
public:
MOCK_CONST_METHOD1(GetTopHosts, std::vector<std::string>(size_t max_sites));
};
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;
}
// A mock class implementation of HintsFetcher for unittesting
// previews_optimization_guide.
class TestHintsFetcher : public HintsFetcher {
using HintsFetchedCallback = base::OnceCallback<void(
base::Optional<
std::unique_ptr<optimization_guide::proto::GetHintsResponse>>)>;
using HintsFetcher::FetchOptimizationGuideServiceHints;
public:
TestHintsFetcher(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
GURL optimization_guide_service_url,
HintsFetcherEndState fetch_state)
: HintsFetcher(url_loader_factory, optimization_guide_service_url),
fetch_state_(fetch_state) {}
bool FetchOptimizationGuideServiceHints(
const std::vector<std::string>& hosts,
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_;
};
// A Test PreviewsOptimizationGuide to observe and record when callbacks
// from hints fetching and storing occur.
class TestPreviewsOptimizationGuide : public PreviewsOptimizationGuide {
public:
TestPreviewsOptimizationGuide(
optimization_guide::OptimizationGuideService* optimization_guide_service,
const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner,
const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
const base::FilePath& profile_path,
PreviewsTopHostProvider* previews_top_host_provider,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: PreviewsOptimizationGuide(optimization_guide_service,
ui_task_runner,
background_task_runner,
profile_path,
previews_top_host_provider,
url_loader_factory) {}
bool fetched_hints_stored() { return fetched_hints_stored_; }
private:
void OnHintsFetched(
base::Optional<
std::unique_ptr<optimization_guide::proto::GetHintsResponse>>
get_hints_response) override {
fetched_hints_stored_ = false;
PreviewsOptimizationGuide::OnHintsFetched(std::move(get_hints_response));
}
void OnFetchedHintsStored() override {
fetched_hints_stored_ = true;
PreviewsOptimizationGuide::OnFetchedHintsStored();
}
bool fetched_hints_stored_ = false;
};
class PreviewsOptimizationGuideTest : public testing::Test {
public:
PreviewsOptimizationGuideTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI_MOCK_TIME) {}
~PreviewsOptimizationGuideTest() override {}
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
CreateServiceAndGuide();
}
// Delete |guide_| if it hasn't been deleted.
void TearDown() override { ResetGuide(); }
PreviewsOptimizationGuide* guide() { return guide_.get(); }
MockPreviewsTopHostProvider* top_host_provider() {
return previews_top_host_provider_.get();
}
TestOptimizationGuideService* optimization_guide_service() {
return optimization_guide_service_.get();
}
network::SharedURLLoaderFactory* url_loader_factory() {
return url_loader_factory_.get();
}
TestHintsFetcher* hints_fetcher() {
return static_cast<TestHintsFetcher*>(guide_->GetHintsFetcherForTesting());
}
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));
guide_->OnHintsComponentAvailable(info);
RunUntilIdle();
}
void CreateServiceAndGuide() {
if (guide_) {
ResetGuide();
}
url_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
if (!previews_top_host_provider_) {
previews_top_host_provider_ =
std::make_unique<MockPreviewsTopHostProvider>();
}
optimization_guide_service_ =
std::make_unique<TestOptimizationGuideService>(
scoped_task_environment_.GetMainThreadTaskRunner());
guide_ = std::make_unique<TestPreviewsOptimizationGuide>(
optimization_guide_service_.get(),
scoped_task_environment_.GetMainThreadTaskRunner(),
scoped_task_environment_.GetMainThreadTaskRunner(), temp_dir(),
previews_top_host_provider_.get(), url_loader_factory_);
guide_->SetTimeClockForTesting(scoped_task_environment_.GetMockClock());
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching);
// Add observer is called after the HintCache is fully initialized,
// indicating that the PreviewsOptimizationGuide is ready to process hints.
while (!optimization_guide_service_->AddObserverCalled()) {
RunUntilIdle();
}
}
void ResetGuide() {
guide_.reset();
RunUntilIdle();
}
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);
return hints_fetcher;
}
base::FilePath temp_dir() const { return temp_dir_.GetPath(); }
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
void MoveClockForwardBy(base::TimeDelta time_delta) {
scoped_task_environment_.FastForwardBy(time_delta);
base::RunLoop().RunUntilIdle();
}
void RunUntilIdle() {
scoped_task_environment_.RunUntilIdle();
base::RunLoop().RunUntilIdle();
}
void DoExperimentFlagTest(base::Optional<std::string> experiment_name,
bool expect_enabled);
// This is a helper function for initializing fixed number of ResourceLoading
// hints.
void InitializeFixedCountResourceLoadingHints();
// This is a helper function for initializing fixed number of ResourceLoading
// hints that contain an experiment
void InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
// This is a helper function for initializing multiple ResourceLoading hints.
// The generated hint proto contains hints for |key_count| keys.
// |page_patterns_per_key| page patterns are specified per key.
// For each page pattern, 2 resource loading hints are specified in the proto.
void InitializeMultipleResourceLoadingHints(size_t key_count,
size_t page_patterns_per_key);
// This is a helper function for initializing with a LITE_PAGE_REDIRECT
// server blacklist.
void InitializeWithLitePageRedirectBlacklist();
// This function guarantees that all of the asynchronous processing required
// to load the specified hint has occurred prior to calling IsWhitelisted.
// It accomplishes this by calling MaybeLoadOptimizationHints() and waiting
// until OnLoadOptimizationHints runs before calling IsWhitelisted().
bool MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
PreviewsUserData* previews_data,
const GURL& url,
PreviewsType type,
net::EffectiveConnectionType* out_ect_threshold);
void RunUntilFetchedHintsStored() {
while (!guide_->fetched_hints_stored()) {
}
}
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.length()),
base::WriteFile(filePath, serialized_config.data(),
serialized_config.length()));
}
void TestOnHintsFetched(
std::unique_ptr<optimization_guide::proto::GetHintsResponse>
get_hints_response) {}
std::unique_ptr<TestHintsFetcher> test_fetcher_;
// Callback used to indicate that the asynchronous call to
// MaybeLoadOptimizationHints() has completed its processing.
void OnLoadOptimizationHints();
base::ScopedTempDir temp_dir_;
// std::unique_ptr<PreviewsOptimizationGuide> guide_;
std::unique_ptr<TestPreviewsOptimizationGuide> guide_;
std::unique_ptr<TestOptimizationGuideService> optimization_guide_service_;
std::unique_ptr<MockPreviewsTopHostProvider> previews_top_host_provider_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
network::TestURLLoaderFactory test_url_loader_factory_;
// Flag set when the OnLoadOptimizationHints callback runs. This indicates
// that MaybeLoadOptimizationHints() has completed its processing.
bool requested_hints_loaded_;
GURL loaded_hints_document_gurl_;
std::vector<std::string> loaded_hints_resource_patterns_;
// const base::SimpleTestClock* test_clock_;
DISALLOW_COPY_AND_ASSIGN(PreviewsOptimizationGuideTest);
};
void PreviewsOptimizationGuideTest::InitializeFixedCountResourceLoadingHints() {
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);
// Page hint for "/news/"
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");
// Page hint for "football"
optimization_guide::proto::PageHint* page_hint2 = hint1->add_page_hints();
page_hint2->set_page_pattern("football");
optimization_guide::proto::Optimization* optimization2 =
page_hint2->add_whitelisted_optimizations();
optimization2->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
optimization_guide::proto::ResourceLoadingHint* resource_loading_hint2 =
optimization2->add_resource_loading_hints();
resource_loading_hint2->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_loading_hint2->set_resource_pattern("football_cruft.js");
optimization_guide::proto::ResourceLoadingHint* resource_loading_hint3 =
optimization2->add_resource_loading_hints();
resource_loading_hint3->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_loading_hint3->set_resource_pattern("barball_cruft.js");
ProcessHints(config, "2.0.0");
}
void PreviewsOptimizationGuideTest::
InitializeFixedCountResourceLoadingHintsWithTwoExperiments() {
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);
// Page hint for "/news/"
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_experiment_name("experiment_1");
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");
optimization_guide::proto::Optimization* optimization2 =
page_hint1->add_whitelisted_optimizations();
optimization2->set_experiment_name("experiment_2");
optimization2->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
optimization_guide::proto::ResourceLoadingHint* resource_loading_hint2 =
optimization2->add_resource_loading_hints();
resource_loading_hint2->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_loading_hint2->set_resource_pattern("football_cruft.js");
ProcessHints(config, "2.0.0");
}
void PreviewsOptimizationGuideTest::InitializeMultipleResourceLoadingHints(
size_t key_count,
size_t page_patterns_per_key) {
optimization_guide::proto::Configuration config;
for (size_t key_index = 0; key_index < key_count; ++key_index) {
optimization_guide::proto::Hint* hint = config.add_hints();
hint->set_key("somedomain" + base::NumberToString(key_index) + ".org");
hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
for (size_t page_pattern_index = 0;
page_pattern_index < page_patterns_per_key; ++page_pattern_index) {
// Page hint for "/news/"
optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern(
"/news" + base::NumberToString(page_pattern_index) + "/");
optimization_guide::proto::Optimization* optimization1 =
page_hint->add_whitelisted_optimizations();
optimization1->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
optimization_guide::proto::ResourceLoadingHint* resource_loading_hint_1 =
optimization1->add_resource_loading_hints();
resource_loading_hint_1->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_loading_hint_1->set_resource_pattern("news_cruft_1.js");
optimization_guide::proto::ResourceLoadingHint* resource_loading_hint_2 =
optimization1->add_resource_loading_hints();
resource_loading_hint_2->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_loading_hint_2->set_resource_pattern("news_cruft_2.js");
}
}
ProcessHints(config, "2.0.0");
}
void PreviewsOptimizationGuideTest::InitializeWithLitePageRedirectBlacklist() {
previews::BloomFilter blacklist_bloom_filter(7, 511);
blacklist_bloom_filter.Add("blacklisteddomain.com");
blacklist_bloom_filter.Add("blacklistedsubdomain.maindomain.co.in");
std::string blacklist_data((char*)&blacklist_bloom_filter.bytes()[0],
blacklist_bloom_filter.bytes().size());
optimization_guide::proto::Configuration config;
optimization_guide::proto::OptimizationFilter* blacklist_proto =
config.add_optimization_blacklists();
blacklist_proto->set_optimization_type(
optimization_guide::proto::LITE_PAGE_REDIRECT);
std::unique_ptr<optimization_guide::proto::BloomFilter> bloom_filter_proto =
std::make_unique<optimization_guide::proto::BloomFilter>();
bloom_filter_proto->set_num_hash_functions(7);
bloom_filter_proto->set_num_bits(511);
bloom_filter_proto->set_data(blacklist_data);
blacklist_proto->set_allocated_bloom_filter(bloom_filter_proto.release());
ProcessHints(config, "2.0.0");
}
bool PreviewsOptimizationGuideTest::
MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
PreviewsUserData* previews_data,
const GURL& url,
PreviewsType type,
net::EffectiveConnectionType* out_ect_threshold) {
// Ensure that all asynchronous MaybeLoadOptimizationHints processing
// finishes prior to calling IsWhitelisted. This is accomplished by waiting
// for the OnLoadOptimizationHints callback to set |requested_hints_loaded_|
// to true.
requested_hints_loaded_ = false;
if (guide()->MaybeLoadOptimizationHints(
url, base::BindOnce(
&PreviewsOptimizationGuideTest::OnLoadOptimizationHints,
base::Unretained(this)))) {
while (!requested_hints_loaded_) {
RunUntilIdle();
}
}
return guide()->IsWhitelisted(previews_data, url, type, out_ect_threshold);
}
void PreviewsOptimizationGuideTest::OnLoadOptimizationHints() {
requested_hints_loaded_ = true;
}
TEST_F(PreviewsOptimizationGuideTest, IsWhitelistedWithoutHints) {
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest,
ProcessHintsForNoScriptPageHintsPopulatedCorrectly) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
// Configure somedomain.org with 2 page patterns, different ECT thresholds.
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);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("noscript_default_2g");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
optimization_guide::proto::PageHint* page_hint2 = hint1->add_page_hints();
page_hint2->set_page_pattern("noscript_3g");
page_hint2->set_max_ect_trigger(
optimization_guide::proto::EffectiveConnectionType::
EFFECTIVE_CONNECTION_TYPE_3G);
optimization_guide::proto::Optimization* optimization2 =
page_hint2->add_whitelisted_optimizations();
optimization2->set_optimization_type(optimization_guide::proto::NOSCRIPT);
// Configure anypage.com with * page pattern.
optimization_guide::proto::Hint* hint2 = config.add_hints();
hint2->set_key("anypage.com");
hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint3 = hint2->add_page_hints();
page_hint3->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization3 =
page_hint3->add_whitelisted_optimizations();
optimization3->set_optimization_type(optimization_guide::proto::NOSCRIPT);
ProcessHints(config, "2.0.0");
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Verify page matches and ECT thresholds.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://somedomain.org/noscript_default_2g"),
PreviewsType::NOSCRIPT, &ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://somedomain.org/noscript_3g"),
PreviewsType::NOSCRIPT, &ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_3G, ect_threshold);
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://somedomain.org/no_pattern_match"),
PreviewsType::NOSCRIPT, &ect_threshold));
// Verify * matches any page.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://anypage.com/noscript_for_all"),
PreviewsType::NOSCRIPT, &ect_threshold));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://anypage.com/"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://anypage.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest,
ProcessHintsWithValidCommandLineOverride) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
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(
switches::kHintsProtoOverride, encoded_config);
CreateServiceAndGuide();
// Verify page matches and ECT thresholds.
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
EXPECT_TRUE(guide()->GetHintsForTesting());
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://somedomain.org/noscript_default_2g"),
PreviewsType::NOSCRIPT, &ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
}
TEST_F(PreviewsOptimizationGuideTest,
ProcessHintsWithValidCommandLineOverrideAndPreexistingData) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint = config.add_hints();
hint->set_key("otherdomain.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(
switches::kHintsProtoOverride, encoded_config);
CreateServiceAndGuide();
// Verify page matches and ECT thresholds.
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
EXPECT_TRUE(guide()->GetHintsForTesting());
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://otherdomain.org/noscript_default_2g"),
PreviewsType::NOSCRIPT, &ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
}
TEST_F(PreviewsOptimizationGuideTest,
ProcessHintsWithInvalidCommandLineOverride) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
switches::kHintsProtoOverride, "this-is-not-a-proto");
CreateServiceAndGuide();
EXPECT_FALSE(guide()->GetHintsForTesting());
}
TEST_F(PreviewsOptimizationGuideTest,
ProcessHintsWithPurgeHintCacheStoreCommandLineAndNoPreexistingData) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kPurgeHintCacheStore);
CreateServiceAndGuide();
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain.org/"), base::DoNothing()));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
}
TEST_F(PreviewsOptimizationGuideTest,
ProcessHintsWithPurgeHintCacheStoreCommandLineAndPreexistingData) {
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kPurgeHintCacheStore);
CreateServiceAndGuide();
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain.org/"), base::DoNothing()));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
}
// Test when resource loading hints are enabled.
TEST_F(PreviewsOptimizationGuideTest,
ProcessHintsForResourceLoadingHintsPopulatedCorrectly) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
// Add first hint.
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("facebook.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
optimization_guide::proto::ResourceLoadingHint* resource_hint1 =
optimization1->add_resource_loading_hints();
resource_hint1->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_hint1->set_resource_pattern("resource.js");
// Add an additional optimization to first hint to verify that the applicable
// optimizations are still whitelisted.
optimization_guide::proto::PageHint* page_hint2 = hint1->add_page_hints();
page_hint2->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization2 =
page_hint2->add_whitelisted_optimizations();
optimization2->set_optimization_type(optimization_guide::proto::NOSCRIPT);
// Add second hint.
optimization_guide::proto::Hint* hint2 = config.add_hints();
hint2->set_key("twitter.com");
hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint3 = hint2->add_page_hints();
page_hint3->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization3 =
page_hint3->add_whitelisted_optimizations();
optimization3->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
optimization_guide::proto::ResourceLoadingHint* resource_hint2 =
optimization3->add_resource_loading_hints();
resource_hint2->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_hint2->set_resource_pattern("resource.js");
ProcessHints(config, "2.0.0");
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Twitter and Facebook should be whitelisted but not Google.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.twitter.com/example"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://google.com"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
}
// Test when both NoScript and resource loading hints are enabled.
TEST_F(
PreviewsOptimizationGuideTest,
ProcessHintsWhitelistForNoScriptAndResourceLoadingHintsPopulatedCorrectly) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
// Add first hint.
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("facebook.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
// Add additional optimization to second hint to verify that the applicable
// optimizations are still whitelisted.
optimization_guide::proto::Optimization* optimization2 =
page_hint1->add_whitelisted_optimizations();
optimization2->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
optimization_guide::proto::ResourceLoadingHint* resource_hint1 =
optimization2->add_resource_loading_hints();
resource_hint1->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_hint1->set_resource_pattern("resource.js");
// Add second hint.
optimization_guide::proto::Hint* hint2 = config.add_hints();
hint2->set_key("twitter.com");
hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint2 = hint2->add_page_hints();
page_hint2->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization3 =
page_hint2->add_whitelisted_optimizations();
optimization3->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
optimization_guide::proto::ResourceLoadingHint* resource_hint2 =
optimization3->add_resource_loading_hints();
resource_hint2->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_hint2->set_resource_pattern("resource.js");
ProcessHints(config, "2.0.0");
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Twitter and Facebook should be whitelisted but not Google.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com/example.html"),
PreviewsType::NOSCRIPT, &ect_threshold));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.twitter.com/example"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://google.com"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest,
ProcessHintsForResourceLoadingHintsWithSlowPageTriggering) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
// Hint with 3G threshold.
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("3g.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("*");
page_hint1->set_max_ect_trigger(
optimization_guide::proto::EffectiveConnectionType::
EFFECTIVE_CONNECTION_TYPE_3G);
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
// Hint with 4G threshold.
optimization_guide::proto::Hint* hint2 = config.add_hints();
hint2->set_key("4g.com");
hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint2 = hint2->add_page_hints();
page_hint2->set_page_pattern("*");
page_hint2->set_max_ect_trigger(
optimization_guide::proto::EffectiveConnectionType::
EFFECTIVE_CONNECTION_TYPE_4G);
optimization_guide::proto::Optimization* optimization2 =
page_hint2->add_whitelisted_optimizations();
optimization2->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
// Hint with no threshold (default case).
optimization_guide::proto::Hint* hint3 = config.add_hints();
hint3->set_key("default2g.com");
hint3->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint3 = hint3->add_page_hints();
page_hint3->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization3 =
page_hint3->add_whitelisted_optimizations();
optimization3->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
ProcessHints(config, "2.0.0");
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://3g.com"), PreviewsType::RESOURCE_LOADING_HINTS,
&ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_3G, ect_threshold);
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://4g.com/example"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_4G, ect_threshold);
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://default2g.com"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
}
// This is a helper function for testing the experiment flags on the config for
// the optimization guide. It creates a test config with a hint containing
// multiple optimizations. The optimization under test will be marked with an
// experiment name if one is provided in |experiment_name|. It will then be
// tested to see if it's enabled, the expectation found in |expect_enabled|.
void PreviewsOptimizationGuideTest::DoExperimentFlagTest(
base::Optional<std::string> experiment_name,
bool expect_enabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
// Create a hint with two optimizations. One may be marked experimental
// depending on test configuration. The other is never marked experimental.
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("facebook.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
// NOSCRIPT is the optimization under test and may be marked experimental.
if (experiment_name.has_value()) {
optimization1->set_experiment_name(experiment_name.value());
}
optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
// RESOURCE_LOADING is not marked experimental.
optimization_guide::proto::Optimization* optimization2 =
page_hint1->add_whitelisted_optimizations();
optimization2->set_optimization_type(
optimization_guide::proto::RESOURCE_LOADING);
optimization_guide::proto::ResourceLoadingHint* resource_hint1 =
optimization2->add_resource_loading_hints();
resource_hint1->set_loading_optimization_type(
optimization_guide::proto::LOADING_BLOCK_RESOURCE);
resource_hint1->set_resource_pattern("resource.js");
// Add a second, non-experimental hint.
optimization_guide::proto::Hint* hint2 = config.add_hints();
hint2->set_key("twitter.com");
hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint3 = hint2->add_page_hints();
page_hint3->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization3 =
page_hint3->add_whitelisted_optimizations();
optimization3->set_optimization_type(optimization_guide::proto::NOSCRIPT);
ProcessHints(config, "2.0.0");
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Check to ensure the optimization under test (facebook noscript) is either
// enabled or disabled, depending on what the caller told us to expect.
EXPECT_EQ(expect_enabled, MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"),
PreviewsType::NOSCRIPT, &ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
// RESOURCE_LOADING_HINTS for facebook should always be enabled.
ect_threshold = net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
EXPECT_EQ(!expect_enabled,
MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
// Twitter's NOSCRIPT should always be enabled; RESOURCE_LOADING_HINTS is not
// configured and should be disabled.
ect_threshold = net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.twitter.com/example"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
// Google (which is not configured at all) should always have both NOSCRIPT
// and RESOURCE_LOADING_HINTS disabled.
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://google.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest,
HandlesExperimentalFlagWithNoExperimentFlaggedOrEnabled) {
// With the optimization NOT flagged as experimental and no experiment
// enabled, the optimization should be enabled.
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(features::kOptimizationHintsExperiments);
DoExperimentFlagTest(base::nullopt, true);
}
TEST_F(PreviewsOptimizationGuideTest,
HandlesExperimentalFlagWithEmptyExperimentName) {
// Empty experiment names should be equivalent to no experiment flag set.
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(features::kOptimizationHintsExperiments);
DoExperimentFlagTest("", true);
}
TEST_F(PreviewsOptimizationGuideTest,
HandlesExperimentalFlagWithExperimentConfiguredAndNotRunning) {
// With the optimization flagged as experimental and no experiment
// enabled, the optimization should be disabled.
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(features::kOptimizationHintsExperiments);
DoExperimentFlagTest("foo_experiment", false);
}
TEST_F(PreviewsOptimizationGuideTest,
HandlesExperimentalFlagWithExperimentConfiguredAndSameOneRunning) {
// With the optimization flagged as experimental and an experiment with that
// name running, the optimization should be enabled.
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeatureWithParameters(
features::kOptimizationHintsExperiments,
{{"experiment_name", "foo_experiment"}});
DoExperimentFlagTest("foo_experiment", true);
}
TEST_F(PreviewsOptimizationGuideTest,
HandlesExperimentalFlagWithExperimentConfiguredAndDifferentOneRunning) {
// With the optimization flagged as experimental and a *different* experiment
// enabled, the optimization should be disabled.
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeatureWithParameters(
features::kOptimizationHintsExperiments,
{{"experiment_name", "bar_experiment"}});
DoExperimentFlagTest("foo_experiment", false);
}
TEST_F(PreviewsOptimizationGuideTest, EnsureExperimentsDisabledByDefault) {
// Mark an optimization as experiment, and ensure it's disabled even though we
// don't explicitly enable or disable the feature as part of the test. This
// ensures the experiments feature is disabled by default.
DoExperimentFlagTest("foo_experiment", false);
}
TEST_F(PreviewsOptimizationGuideTest, ProcessHintsUnsupportedKeyRepIsIgnored) {
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint = config.add_hints();
hint->set_key("facebook.com");
hint->set_key_representation(
optimization_guide::proto::REPRESENTATION_UNSPECIFIED);
optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
page_hint->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization =
page_hint->add_whitelisted_optimizations();
optimization->set_optimization_type(optimization_guide::proto::NOSCRIPT);
ProcessHints(config, "2.0.0");
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest,
ProcessHintsUnsupportedOptimizationIsIgnored) {
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint = config.add_hints();
hint->set_key("facebook.com");
hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(
optimization_guide::proto::TYPE_UNSPECIFIED);
ProcessHints(config, "2.0.0");
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest, ProcessHintsWithExistingSentinel) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
base::HistogramTester histogram_tester;
// Create valid config.
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("facebook.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
// Create sentinel file for version 2.0.0.
const base::FilePath sentinel_path =
temp_dir().Append(FILE_PATH_LITERAL("previews_config_sentinel.txt"));
base::WriteFile(sentinel_path, "2.0.0", 5);
// Verify config not processed for version 2.0.0 (same as sentinel).
ProcessHints(config, "2.0.0");
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_TRUE(base::PathExists(sentinel_path));
histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
2 /* kFailedFinishProcessing */, 1);
// Now verify config is processed for different version and sentinel cleared.
ProcessHints(config, "3.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_2G, ect_threshold);
EXPECT_FALSE(base::PathExists(sentinel_path));
histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
1 /* kProcessedPreviewsHints */, 1);
}
TEST_F(PreviewsOptimizationGuideTest, ProcessHintsWithInvalidSentinelFile) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
base::HistogramTester histogram_tester;
// Create valid config.
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("facebook.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
// Create sentinel file with invalid contents.
const base::FilePath sentinel_path =
temp_dir().Append(FILE_PATH_LITERAL("previews_config_sentinel.txt"));
base::WriteFile(sentinel_path, "bad-2.0.0", 5);
// Verify config not processed for existing sentinel with bad value but
// that the existinel sentinel file is deleted.
ProcessHints(config, "2.0.0");
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_FALSE(base::PathExists(sentinel_path));
histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
2 /* kFailedFinishProcessing */, 1);
// Now verify config is processed with sentinel cleared.
ProcessHints(config, "2.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_FALSE(base::PathExists(sentinel_path));
histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
1 /* kProcessedPreviewsHints */, 1);
}
TEST_F(PreviewsOptimizationGuideTest, SkipHintProcessingForSameConfigVersion) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
base::HistogramTester histogram_tester;
optimization_guide::proto::Configuration config1;
optimization_guide::proto::Hint* hint1 = config1.add_hints();
hint1->set_key("facebook.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
optimization_guide::proto::Configuration config2;
optimization_guide::proto::Hint* hint2 = config2.add_hints();
hint2->set_key("google.com");
hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint2 = hint2->add_page_hints();
page_hint2->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization2 =
page_hint2->add_whitelisted_optimizations();
optimization2->set_optimization_type(optimization_guide::proto::NOSCRIPT);
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Process the new hints config and verify that they are available.
ProcessHints(config1, "2.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
1 /* kProcessedPreviewsHints */, 1);
// Verify hint config with the same version as the previously processed config
// is skipped.
ProcessHints(config2, "2.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
3 /* kSkippedProcessingPreviewsHints */,
1);
}
TEST_F(PreviewsOptimizationGuideTest,
SkipHintProcessingForEarlierConfigVersion) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
base::HistogramTester histogram_tester;
optimization_guide::proto::Configuration config1;
optimization_guide::proto::Hint* hint1 = config1.add_hints();
hint1->set_key("facebook.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
optimization_guide::proto::Configuration config2;
optimization_guide::proto::Hint* hint2 = config2.add_hints();
hint2->set_key("google.com");
hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint2 = hint2->add_page_hints();
page_hint2->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization2 =
page_hint2->add_whitelisted_optimizations();
optimization2->set_optimization_type(optimization_guide::proto::NOSCRIPT);
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Process the new hints config and verify that they are available.
ProcessHints(config1, "2.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
1 /* kProcessedPreviewsHints */, 1);
// Verify hint config with an earlier version than the previously processed
// one is skipped.
ProcessHints(config2, "1.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
3 /* kSkippedProcessingPreviewsHints */,
1);
}
TEST_F(PreviewsOptimizationGuideTest, ProcessMultipleNewConfigs) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
optimization_guide::proto::Configuration config1;
optimization_guide::proto::Hint* hint1 = config1.add_hints();
hint1->set_key("facebook.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
optimization_guide::proto::Configuration config2;
optimization_guide::proto::Hint* hint2 = config2.add_hints();
hint2->set_key("google.com");
hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint2 = hint2->add_page_hints();
page_hint2->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization2 =
page_hint2->add_whitelisted_optimizations();
optimization2->set_optimization_type(optimization_guide::proto::NOSCRIPT);
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Process the new hints config and verify that they are available.
ProcessHints(config1, "2.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
histogram_tester.ExpectUniqueSample("Previews.ProcessHintsResult",
1 /* kProcessedPreviewsHints */, 1);
// Verify hint config with a newer version then the previously processed one
// is processed.
ProcessHints(config2, "3.0.0");
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
&ect_threshold));
histogram_tester.ExpectBucketCount("Previews.ProcessHintsResult",
1 /* kProcessedPreviewsHints */, 2);
}
TEST_F(PreviewsOptimizationGuideTest, ProcessHintConfigWithNoKeyFailsDcheck) {
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint = config.add_hints();
hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
EXPECT_DCHECK_DEATH({
ProcessHints(config, "2.0.0");
});
}
TEST_F(PreviewsOptimizationGuideTest,
ProcessHintsConfigWithDuplicateKeysFailsDcheck) {
optimization_guide::proto::Configuration config;
optimization_guide::proto::Hint* hint1 = config.add_hints();
hint1->set_key("facebook.com");
hint1->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint1 = hint1->add_page_hints();
page_hint1->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization1 =
page_hint1->add_whitelisted_optimizations();
optimization1->set_optimization_type(optimization_guide::proto::NOSCRIPT);
optimization_guide::proto::Hint* hint2 = config.add_hints();
hint2->set_key("facebook.com");
hint2->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
optimization_guide::proto::PageHint* page_hint2 = hint1->add_page_hints();
page_hint2->set_page_pattern("*");
optimization_guide::proto::Optimization* optimization2 =
page_hint2->add_whitelisted_optimizations();
optimization2->set_optimization_type(optimization_guide::proto::NOSCRIPT);
EXPECT_DCHECK_DEATH({
ProcessHints(config, "2.0.0");
});
}
TEST_F(PreviewsOptimizationGuideTest, MaybeLoadOptimizationHints) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.unknown.com"), base::DoNothing()));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Verify whitelisting from loaded page hints.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest,
MaybeLoadPageHintsWithTwoExperimentsDisabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.unknown.com"), base::DoNothing()));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Verify whitelisting from loaded page hints.
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest,
MaybeLoadPageHintsWithFirstExperimentEnabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
base::test::ScopedFeatureList scoped_list2;
scoped_list2.InitAndEnableFeatureWithParameters(
features::kOptimizationHintsExperiments,
{{"experiment_name", "experiment_1"}});
InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.unknown.com"), base::DoNothing()));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Verify whitelisting from loaded page hints.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest,
MaybeLoadPageHintsWithSecondExperimentEnabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
base::test::ScopedFeatureList scoped_list2;
scoped_list2.InitAndEnableFeatureWithParameters(
features::kOptimizationHintsExperiments,
{{"experiment_name", "experiment_2"}});
InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.unknown.com"), base::DoNothing()));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Verify whitelisting from loaded page hints.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest,
MaybeLoadPageHintsWithBothExperimentEnabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
base::test::ScopedFeatureList scoped_list2;
scoped_list2.InitAndEnableFeatureWithParameters(
features::kOptimizationHintsExperiments,
{{"experiment_name", "experiment_1"},
{"experiment_name", "experiment_2"}});
InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football"), base::DoNothing()));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.unknown.com"), base::DoNothing()));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
// Verify whitelisting from loaded page hints.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
}
// Test that optimization hints with multiple page patterns is processed
// correctly.
TEST_F(PreviewsOptimizationGuideTest,
LoadManyResourceLoadingOptimizationHints) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
const size_t key_count = 50;
const size_t page_patterns_per_key = 25;
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
InitializeMultipleResourceLoadingHints(key_count, page_patterns_per_key);
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain0.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain0.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain0.org/news0/football"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain49.org/"), base::DoNothing()));
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain49.org/news0/football"), base::DoNothing()));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain50.org/"), base::DoNothing()));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://somedomain50.org/news0/football"), base::DoNothing()));
EXPECT_FALSE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.unknown.com"), base::DoNothing()));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain0.org/news0/football"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain0.org/news24/football"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain0.org/news25/football"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain49.org/news0/football"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain49.org/news24/football"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain49.org/news25/football"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain50.org/news0/football"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain50.org/news24/football"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data, GURL("https://www.somedomain50.org/news25/football"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
RunUntilIdle();
}
TEST_F(PreviewsOptimizationGuideTest,
MaybeLoadOptimizationHintsWithoutEnabledPageHintsFeature) {
// Without PageHints-oriented feature enabled, never see
// enabled, the optimization should be disabled.
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(features::kResourceLoadingHints);
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(guide()->MaybeLoadOptimizationHints(
GURL("https://www.somedomain.org"), base::DoNothing()));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
net::EffectiveConnectionType ect_threshold;
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIsWhitelisted(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS, &ect_threshold));
}
TEST_F(PreviewsOptimizationGuideTest, IsBlacklisted) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kLitePageServerPreviews);
EXPECT_TRUE(
guide()->IsBlacklisted(GURL("https://m.blacklisteddomain.com/path"),
PreviewsType::LITE_PAGE_REDIRECT));
InitializeWithLitePageRedirectBlacklist();
EXPECT_TRUE(
guide()->IsBlacklisted(GURL("https://m.blacklisteddomain.com/path"),
PreviewsType::LITE_PAGE_REDIRECT));
EXPECT_DCHECK_DEATH(guide()->IsBlacklisted(
GURL("https://m.blacklisteddomain.com/path"), PreviewsType::NOSCRIPT));
EXPECT_TRUE(guide()->IsBlacklisted(
GURL("https://blacklistedsubdomain.maindomain.co.in"),
PreviewsType::LITE_PAGE_REDIRECT));
EXPECT_FALSE(guide()->IsBlacklisted(GURL("https://maindomain.co.in"),
PreviewsType::LITE_PAGE_REDIRECT));
}
TEST_F(PreviewsOptimizationGuideTest, LitePageRedirectSkipIsBlacklistedCheck) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kLitePageServerPreviews);
InitializeWithLitePageRedirectBlacklist();
EXPECT_TRUE(
guide()->IsBlacklisted(GURL("https://m.blacklisteddomain.com/path"),
PreviewsType::LITE_PAGE_REDIRECT));
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kIgnoreLitePageRedirectOptimizationBlacklist);
EXPECT_FALSE(
guide()->IsBlacklisted(GURL("https://m.blacklisteddomain.com/path"),
PreviewsType::LITE_PAGE_REDIRECT));
}
TEST_F(PreviewsOptimizationGuideTest,
IsBlacklistedWithLitePageServerPreviewsDisabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(features::kLitePageServerPreviews);
InitializeWithLitePageRedirectBlacklist();
EXPECT_TRUE(
guide()->IsBlacklisted(GURL("https://m.blacklisteddomain.com/path"),
PreviewsType::LITE_PAGE_REDIRECT));
}
TEST_F(PreviewsOptimizationGuideTest, RemoveObserverCalledAtDestruction) {
EXPECT_FALSE(optimization_guide_service()->RemoveObserverCalled());
ResetGuide();
EXPECT_TRUE(optimization_guide_service()->RemoveObserverCalled());
}
TEST_F(PreviewsOptimizationGuideTest, HintsFetcherEnabledNoHosts) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching);
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitchASCII("optimization_guide_service_url",
"https://hintsserver.com");
guide()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)).Times(1);
// Load hints so that OnHintsUpdated is called. This will force FetchHints to
// be triggered if OptimizationHintsFetching is enabled.
InitializeFixedCountResourceLoadingHints();
// Cause the timer to fire the fetch event.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_FALSE(hints_fetcher()->hints_fetched());
}
TEST_F(PreviewsOptimizationGuideTest, HintsFetcherEnabledWithHosts) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching);
std::string opt_guide_url = "https://hintsserver.com";
guide()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
std::vector<std::string> hosts = {"example1.com", "example2.com"};
EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_))
.Times(1)
.WillRepeatedly(testing::Return(hosts));
// Load hints so that OnHintsUpdated is called. This will force FetchHints to
// be triggered if OptimizationHintsFetching is enabled.
InitializeFixedCountResourceLoadingHints();
// Force timer to expire and schedule a hints fetch.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_TRUE(hints_fetcher()->hints_fetched());
}
TEST_F(PreviewsOptimizationGuideTest, HintsFetcherTimerRetryDelay) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching);
std::string opt_guide_url = "https://hintsserver.com";
guide()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchFailed));
std::vector<std::string> hosts = {"example1.com", "example2.com"};
EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_))
.Times(2)
.WillRepeatedly(testing::Return(hosts));
// Force hints fetch scheduling.
guide()->ScheduleHintsFetch();
// Force timer to expire and schedule a hints fetch - first time.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_FALSE(hints_fetcher()->hints_fetched());
// Force speculative timer to expire after fetch fails first time, update
// hints fetcher so it succeeds this time.
guide()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_TRUE(hints_fetcher()->hints_fetched());
}
TEST_F(PreviewsOptimizationGuideTest, HintsFetcherTimerFetchSucceeds) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching);
std::string opt_guide_url = "https://hintsserver.com";
guide()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
std::vector<std::string> hosts = {"example1.com", "example2.com"};
EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_))
.WillRepeatedly(testing::Return(hosts));
// Force hints fetch scheduling.
guide()->ScheduleHintsFetch();
// 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.
guide()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_FALSE(hints_fetcher()->hints_fetched());
MoveClockForwardBy(base::TimeDelta::FromSeconds(kUpdateFetchHintsTimeSecs));
RunUntilFetchedHintsStored();
EXPECT_TRUE(hints_fetcher()->hints_fetched());
}
TEST_F(PreviewsOptimizationGuideTest, HintsFetcherDisabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(features::kOptimizationHintsFetching);
EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)).Times(0);
CreateServiceAndGuide();
// Load hints so that OnHintsUpdated is called. This will
// check that FetcHints is not triggered by making sure that top_host_provider
// is not called.
InitializeFixedCountResourceLoadingHints();
}
} // namespace previews