blob: 22188299815f4dacde8e8b49c2bd26ae87269123 [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_impl.h"
#include <memory>
#include <utility>
#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/simple_test_clock.h"
#include "base/test/task_environment.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
#include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
#include "components/optimization_guide/bloom_filter.h"
#include "components/optimization_guide/hints_component_info.h"
#include "components/optimization_guide/hints_component_util.h"
#include "components/optimization_guide/hints_fetcher.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/hints.pb.h"
#include "components/optimization_guide/proto_database_provider_test_base.h"
#include "components/optimization_guide/top_host_provider.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/previews/content/previews_hints.h"
#include "components/previews/content/previews_user_data.h"
#include "components/previews/core/previews_experiments.h"
#include "components/previews/core/previews_features.h"
#include "content/public/test/mock_navigation_handle.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_network_quality_tracker.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 MockTopHostProvider : public optimization_guide::TopHostProvider {
public:
MOCK_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 optimization_guide::HintsFetcher {
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_;
};
// A Test PreviewsOptimizationGuide to observe and record when callbacks
// from hints fetching and storing occur.
class TestPreviewsOptimizationGuide : public PreviewsOptimizationGuideImpl {
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,
PrefService* pref_service,
leveldb_proto::ProtoDatabaseProvider* database_provider,
optimization_guide::TopHostProvider* optimization_guide_top_host_provider,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
network::TestNetworkQualityTracker* network_quality_tracker)
: PreviewsOptimizationGuideImpl(optimization_guide_service,
ui_task_runner,
background_task_runner,
profile_path,
pref_service,
database_provider,
optimization_guide_top_host_provider,
url_loader_factory,
network_quality_tracker) {}
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;
PreviewsOptimizationGuideImpl::OnHintsFetched(
std::move(get_hints_response));
}
void OnFetchedHintsStored() override {
fetched_hints_stored_ = true;
PreviewsOptimizationGuideImpl::OnFetchedHintsStored();
}
bool fetched_hints_stored_ = false;
};
class PreviewsOptimizationGuideImplTest
: public optimization_guide::ProtoDatabaseProviderTestBase {
public:
PreviewsOptimizationGuideImplTest()
: task_environment_(base::test::TaskEnvironment::MainThreadType::UI,
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
~PreviewsOptimizationGuideImplTest() override {}
void SetUp() override {
ProtoDatabaseProviderTestBase::SetUp();
EnableDataSaver();
CreateServiceAndGuide();
}
// Delete |guide_| if it hasn't been deleted.
void TearDown() override {
ProtoDatabaseProviderTestBase::TearDown();
ResetGuide();
}
PreviewsOptimizationGuideImpl* guide() { return guide_.get(); }
MockTopHostProvider* top_host_provider() {
return optimization_guide_top_host_provider_.get();
}
TestOptimizationGuideService* optimization_guide_service() {
return optimization_guide_service_.get();
}
network::SharedURLLoaderFactory* url_loader_factory() {
return url_loader_factory_.get();
}
PrefService* pref_service() { return pref_service_.get(); }
TestHintsFetcher* hints_fetcher() {
return static_cast<TestHintsFetcher*>(guide_->GetHintsFetcherForTesting());
}
network::TestNetworkQualityTracker* network_quality_tracker() {
return network_quality_tracker_.get();
}
void ProcessHints(const optimization_guide::proto::Configuration& config,
const std::string& version) {
base::HistogramTester histogram_tester;
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;
guide_->ListenForNextUpdateForTesting(run_loop.QuitClosure());
guide_->OnHintsComponentAvailable(info);
run_loop.Run();
// Check for histogram to ensure that we do not remove this histogram since
// it's relied on in release tools.
histogram_tester.ExpectTotalCount(
"OptimizationGuide.UpdateComponentHints.Result", 1);
}
void EnableDataSaver() {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
data_reduction_proxy::switches::kEnableDataReductionProxy);
}
void DisableDataSaver() {
base::CommandLine::ForCurrentProcess()->RemoveSwitch(
data_reduction_proxy::switches::kEnableDataReductionProxy);
}
void CreateServiceAndGuide() {
if (guide_) {
ResetGuide();
}
url_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
if (!optimization_guide_top_host_provider_) {
optimization_guide_top_host_provider_ =
std::make_unique<MockTopHostProvider>();
}
optimization_guide_service_ =
std::make_unique<TestOptimizationGuideService>(
task_environment_.GetMainThreadTaskRunner());
pref_service_ = std::make_unique<TestingPrefServiceSimple>();
// Registry pref for DataSaver with default off.
pref_service_->registry()->RegisterBooleanPref(
data_reduction_proxy::prefs::kDataSaverEnabled, false);
optimization_guide::prefs::RegisterProfilePrefs(pref_service_->registry());
network_quality_tracker_ =
std::make_unique<network::TestNetworkQualityTracker>();
guide_ = std::make_unique<TestPreviewsOptimizationGuide>(
optimization_guide_service_.get(),
task_environment_.GetMainThreadTaskRunner(),
task_environment_.GetMainThreadTaskRunner(), temp_dir(),
pref_service_.get(), db_provider_.get(),
optimization_guide_top_host_provider_.get(), url_loader_factory_,
network_quality_tracker_.get());
guide_->SetTimeClockForTesting(task_environment_.GetMockClock());
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(
optimization_guide::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();
}
}
const base::Clock* GetMockClock() const {
return task_environment_.GetMockClock();
}
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, pref_service());
return hints_fetcher;
}
base::FilePath temp_dir() const { return temp_dir_.GetPath(); }
protected:
base::test::TaskEnvironment task_environment_;
void MoveClockForwardBy(base::TimeDelta time_delta) {
task_environment_.FastForwardBy(time_delta);
base::RunLoop().RunUntilIdle();
}
void RunUntilIdle() {
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 is a wrapper around MaybeLoadOptimizationHints that wraps |url| in a
// navigation handle to call the method.
bool CallMaybeLoadOptimizationHints(const GURL& url);
// This function guarantees that all of the asynchronous processing required
// to load the specified hint has occurred prior to calling
// CanApplyPreview. It accomplishes this by calling
// MaybeLoadOptimizationHints() and waiting until OnLoadOptimizationHints runs
// before calling CanApplyPreview().
bool MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
PreviewsUserData* previews_data,
const GURL& url,
PreviewsType type,
net::EffectiveConnectionType ect);
void RunUntilFetchedHintsStored() {
while (!guide_->fetched_hints_stored()) {
}
}
// Flag set when the OnLoadOptimizationHints callback runs. This indicates
// that MaybeLoadOptimizationHints() has completed its processing.
bool requested_hints_loaded_;
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();
std::unique_ptr<TestPreviewsOptimizationGuide> guide_;
std::unique_ptr<TestOptimizationGuideService> optimization_guide_service_;
std::unique_ptr<MockTopHostProvider> optimization_guide_top_host_provider_;
std::unique_ptr<TestingPrefServiceSimple> pref_service_;
std::unique_ptr<network::TestNetworkQualityTracker> network_quality_tracker_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
network::TestURLLoaderFactory test_url_loader_factory_;
GURL loaded_hints_document_gurl_;
std::vector<std::string> loaded_hints_resource_patterns_;
DISALLOW_COPY_AND_ASSIGN(PreviewsOptimizationGuideImplTest);
};
void PreviewsOptimizationGuideImplTest::
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);
hint1->set_version("someversion");
// 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 PreviewsOptimizationGuideImplTest::
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 PreviewsOptimizationGuideImplTest::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 PreviewsOptimizationGuideImplTest::
InitializeWithLitePageRedirectBlacklist() {
optimization_guide::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 PreviewsOptimizationGuideImplTest::CallMaybeLoadOptimizationHints(
const GURL& url) {
content::MockNavigationHandle navigation_handle;
navigation_handle.set_url(url);
return guide()->MaybeLoadOptimizationHints(&navigation_handle,
base::DoNothing());
}
bool PreviewsOptimizationGuideImplTest::
MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
PreviewsUserData* previews_data,
const GURL& url,
PreviewsType type,
net::EffectiveConnectionType ect) {
content::MockNavigationHandle navigation_handle;
navigation_handle.set_url(url);
// Ensure that all asynchronous MaybeLoadOptimizationHints processing
// finishes prior to calling CanApplyPreview. This is accomplished by
// waiting for the OnLoadOptimizationHints callback to set
// |requested_hints_loaded_| to true.
requested_hints_loaded_ = false;
if (guide()->MaybeLoadOptimizationHints(
&navigation_handle,
base::BindOnce(
&PreviewsOptimizationGuideImplTest::OnLoadOptimizationHints,
base::Unretained(this)))) {
while (!requested_hints_loaded_) {
RunUntilIdle();
}
}
network_quality_tracker()->ReportEffectiveConnectionTypeForTesting(ect);
return guide()->CanApplyPreview(previews_data, &navigation_handle, type);
}
void PreviewsOptimizationGuideImplTest::OnLoadOptimizationHints() {
requested_hints_loaded_ = true;
}
TEST_F(PreviewsOptimizationGuideImplTest, CanApplyPreviewWithoutHints) {
PreviewsUserData user_data(kDefaultPageId);
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
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::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);
// Verify page matches and ECT thresholds.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://somedomain.org/noscript_default_2g"),
PreviewsType::NOSCRIPT, net::EFFECTIVE_CONNECTION_TYPE_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://somedomain.org/noscript_3g"),
PreviewsType::NOSCRIPT, net::EFFECTIVE_CONNECTION_TYPE_3G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://somedomain.org/no_pattern_match"),
PreviewsType::NOSCRIPT, net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
// Verify * matches any page.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://anypage.com/noscript_for_all"),
PreviewsType::NOSCRIPT, net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://anypage.com/"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://anypage.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
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(
optimization_guide::switches::kHintsProtoOverride, encoded_config);
CreateServiceAndGuide();
// Verify page matches and ECT thresholds.
PreviewsUserData user_data(kDefaultPageId);
EXPECT_TRUE(guide()->GetHintsForTesting());
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://somedomain.org/noscript_default_2g"),
PreviewsType::NOSCRIPT, net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
ProcessHintsWithValidCommandLineOverrideForDeferAllScript) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures({features::kDeferAllScriptPreviews}, {});
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("defer_default_2g");
optimization_guide::proto::Optimization* optimization =
page_hint->add_whitelisted_optimizations();
optimization->set_optimization_type(
optimization_guide::proto::DEFER_ALL_SCRIPT);
std::string encoded_config;
config.SerializeToString(&encoded_config);
base::Base64Encode(encoded_config, &encoded_config);
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
optimization_guide::switches::kHintsProtoOverride, encoded_config);
CreateServiceAndGuide();
// Verify page matches and ECT thresholds.
PreviewsUserData user_data(kDefaultPageId);
EXPECT_TRUE(guide()->GetHintsForTesting());
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://somedomain.org/defer_default_2g"),
PreviewsType::DEFER_ALL_SCRIPT, net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
ProcessHintsWithValidCommandLineOverrideAndPreexistingData) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitWithFeatures(
{features::kNoScriptPreviews, features::kResourceLoadingHints}, {});
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
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(
optimization_guide::switches::kHintsProtoOverride, encoded_config);
CreateServiceAndGuide();
// Verify page matches and ECT thresholds.
PreviewsUserData user_data(kDefaultPageId);
EXPECT_TRUE(guide()->GetHintsForTesting());
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://otherdomain.org/noscript_default_2g"),
PreviewsType::NOSCRIPT, net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
}
TEST_F(PreviewsOptimizationGuideImplTest,
ProcessHintsWithInvalidCommandLineOverride) {
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
optimization_guide::switches::kHintsProtoOverride, "this-is-not-a-proto");
CreateServiceAndGuide();
EXPECT_FALSE(guide()->GetHintsForTesting());
}
TEST_F(PreviewsOptimizationGuideImplTest,
ProcessHintsWithPurgeHintCacheStoreCommandLineAndNoPreexistingData) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
optimization_guide::switches::kPurgeHintCacheStore);
CreateServiceAndGuide();
EXPECT_FALSE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
}
TEST_F(PreviewsOptimizationGuideImplTest,
ProcessHintsWithPurgeHintCacheStoreCommandLineAndPreexistingData) {
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
base::CommandLine::ForCurrentProcess()->AppendSwitch(
optimization_guide::switches::kPurgeHintCacheStore);
CreateServiceAndGuide();
EXPECT_FALSE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
}
// Test when resource loading hints are enabled.
TEST_F(PreviewsOptimizationGuideImplTest,
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);
// Twitter and Facebook should be whitelisted but not Google.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.twitter.com/example"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://google.com"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
// Test when both NoScript and resource loading hints are enabled.
TEST_F(
PreviewsOptimizationGuideImplTest,
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);
// Twitter and Facebook should be whitelisted but not Google.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com/example.html"),
PreviewsType::NOSCRIPT, net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.twitter.com/example"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://google.com"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
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);
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://3g.com"), PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_3G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://4g.com/example"),
PreviewsType::RESOURCE_LOADING_HINTS, net::EFFECTIVE_CONNECTION_TYPE_4G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://default2g.com"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
// 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 PreviewsOptimizationGuideImplTest::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);
// 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, MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"),
PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
// RESOURCE_LOADING_HINTS for facebook should always be enabled.
EXPECT_EQ(!expect_enabled,
MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
// Twitter's NOSCRIPT should always be enabled; RESOURCE_LOADING_HINTS is not
// configured and should be disabled.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.twitter.com/example"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
// Google (which is not configured at all) should always have both NOSCRIPT
// and RESOURCE_LOADING_HINTS disabled.
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://google.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
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(
optimization_guide::features::kOptimizationHintsExperiments);
DoExperimentFlagTest(base::nullopt, true);
}
TEST_F(PreviewsOptimizationGuideImplTest,
HandlesExperimentalFlagWithEmptyExperimentName) {
// Empty experiment names should be equivalent to no experiment flag set.
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(
optimization_guide::features::kOptimizationHintsExperiments);
DoExperimentFlagTest("", true);
}
TEST_F(PreviewsOptimizationGuideImplTest,
HandlesExperimentalFlagWithExperimentConfiguredAndNotRunning) {
// With the optimization flagged as experimental and no experiment
// enabled, the optimization should be disabled.
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(
optimization_guide::features::kOptimizationHintsExperiments);
DoExperimentFlagTest("foo_experiment", false);
}
TEST_F(PreviewsOptimizationGuideImplTest,
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(
optimization_guide::features::kOptimizationHintsExperiments,
{{"experiment_name", "foo_experiment"}});
DoExperimentFlagTest("foo_experiment", true);
}
TEST_F(PreviewsOptimizationGuideImplTest,
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(
optimization_guide::features::kOptimizationHintsExperiments,
{{"experiment_name", "bar_experiment"}});
DoExperimentFlagTest("foo_experiment", false);
}
TEST_F(PreviewsOptimizationGuideImplTest, 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(PreviewsOptimizationGuideImplTest,
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);
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
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);
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest, 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);
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(base::PathExists(sentinel_path));
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
static_cast<int>(optimization_guide::ProcessHintsComponentResult::
kFailedFinishProcessing),
1);
// Now verify config is processed for different version and sentinel cleared.
ProcessHints(config, "3.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_2G));
EXPECT_FALSE(base::PathExists(sentinel_path));
histogram_tester.ExpectBucketCount(
"OptimizationGuide.ProcessHintsResult",
static_cast<int>(
optimization_guide::ProcessHintsComponentResult::kSuccess),
1);
}
TEST_F(PreviewsOptimizationGuideImplTest, 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);
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(base::PathExists(sentinel_path));
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
static_cast<int>(optimization_guide::ProcessHintsComponentResult::
kFailedFinishProcessing),
1);
// Now verify config is processed with sentinel cleared.
ProcessHints(config, "2.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(base::PathExists(sentinel_path));
histogram_tester.ExpectBucketCount(
"OptimizationGuide.ProcessHintsResult",
static_cast<int>(
optimization_guide::ProcessHintsComponentResult::kSuccess),
1);
}
TEST_F(PreviewsOptimizationGuideImplTest,
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);
// Process the new hints config and verify that they are available.
ProcessHints(config1, "2.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
static_cast<int>(
optimization_guide::ProcessHintsComponentResult::kSuccess),
1);
// Verify hint config with the same version as the previously processed config
// is skipped.
ProcessHints(config2, "2.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
histogram_tester.ExpectBucketCount(
"OptimizationGuide.ProcessHintsResult",
static_cast<int>(optimization_guide::ProcessHintsComponentResult::
kSkippedProcessingHints),
1);
}
TEST_F(PreviewsOptimizationGuideImplTest,
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);
// Process the new hints config and verify that they are available.
ProcessHints(config1, "2.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
static_cast<int>(
optimization_guide::ProcessHintsComponentResult::kSuccess),
1);
// Verify hint config with an earlier version than the previously processed
// one is skipped.
ProcessHints(config2, "1.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
histogram_tester.ExpectBucketCount(
"OptimizationGuide.ProcessHintsResult",
static_cast<int>(optimization_guide::ProcessHintsComponentResult::
kSkippedProcessingHints),
1);
}
TEST_F(PreviewsOptimizationGuideImplTest, 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);
// Process the new hints config and verify that they are available.
ProcessHints(config1, "2.0.0");
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
histogram_tester.ExpectUniqueSample(
"OptimizationGuide.ProcessHintsResult",
static_cast<int>(
optimization_guide::ProcessHintsComponentResult::kSuccess),
1);
// Verify hint config with a newer version then the previously processed one
// is processed.
ProcessHints(config2, "3.0.0");
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.facebook.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://m.google.com"), PreviewsType::NOSCRIPT,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
histogram_tester.ExpectBucketCount(
"OptimizationGuide.ProcessHintsResult",
static_cast<int>(
optimization_guide::ProcessHintsComponentResult::kSuccess),
2);
}
TEST_F(PreviewsOptimizationGuideImplTest,
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(PreviewsOptimizationGuideImplTest,
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(PreviewsOptimizationGuideImplTest, MaybeLoadOptimizationHints) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
// Verify whitelisting from loaded page hints.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
MaybeLoadPageHintsWithTwoExperimentsDisabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
// Verify whitelisting from loaded page hints.
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
MaybeLoadPageHintsWithFirstExperimentEnabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
base::test::ScopedFeatureList scoped_list2;
scoped_list2.InitAndEnableFeatureWithParameters(
optimization_guide::features::kOptimizationHintsExperiments,
{{"experiment_name", "experiment_1"}});
InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
// Verify whitelisting from loaded page hints.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
MaybeLoadPageHintsWithSecondExperimentEnabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
base::test::ScopedFeatureList scoped_list2;
scoped_list2.InitAndEnableFeatureWithParameters(
optimization_guide::features::kOptimizationHintsExperiments,
{{"experiment_name", "experiment_2"}});
InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
// Verify whitelisting from loaded page hints.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest,
MaybeLoadPageHintsWithBothExperimentEnabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
base::test::ScopedFeatureList scoped_list2;
scoped_list2.InitAndEnableFeatureWithParameters(
optimization_guide::features::kOptimizationHintsExperiments,
{{"experiment_name", "experiment_1"},
{"experiment_name", "experiment_2"}});
InitializeFixedCountResourceLoadingHintsWithTwoExperiments();
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
// Verify whitelisting from loaded page hints.
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/football/seahawksrebuildingyear"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
// Test that optimization hints with multiple page patterns is processed
// correctly.
TEST_F(PreviewsOptimizationGuideImplTest,
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);
InitializeMultipleResourceLoadingHints(key_count, page_patterns_per_key);
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain0.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain0.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://somedomain0.org/news0/football")));
EXPECT_TRUE(
CallMaybeLoadOptimizationHints(GURL("https://somedomain49.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://somedomain49.org/news0/football")));
EXPECT_FALSE(
CallMaybeLoadOptimizationHints(GURL("https://somedomain50.org/")));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(
GURL("https://somedomain50.org/news0/football")));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain0.org/news0/football"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain0.org/news24/football"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain0.org/news25/football"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain49.org/news0/football"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain49.org/news24/football"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain49.org/news25/football"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain50.org/news0/football"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain50.org/news24/football"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain50.org/news25/football"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
RunUntilIdle();
}
TEST_F(PreviewsOptimizationGuideImplTest,
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(
CallMaybeLoadOptimizationHints(GURL("https://www.somedomain.org")));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
}
TEST_F(PreviewsOptimizationGuideImplTest, PreviewsUserDataPopulatedCorrectly) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kResourceLoadingHints);
InitializeFixedCountResourceLoadingHints();
EXPECT_TRUE(CallMaybeLoadOptimizationHints(GURL("https://somedomain.org/")));
EXPECT_TRUE(CallMaybeLoadOptimizationHints(
GURL("https://www.somedomain.org/news/football")));
EXPECT_FALSE(CallMaybeLoadOptimizationHints(GURL("https://www.unknown.com")));
RunUntilIdle();
PreviewsUserData user_data(kDefaultPageId);
// Verify whitelisting from loaded page hints.
EXPECT_FALSE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data, GURL("https://www.somedomain.org/unhinted"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_EQ(base::nullopt, user_data.serialized_hint_version_string());
EXPECT_TRUE(MaybeLoadOptimizationHintsAndCheckIfCanApplyPreview(
&user_data,
GURL("https://www.somedomain.org/news/weather/raininginseattle"),
PreviewsType::RESOURCE_LOADING_HINTS,
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G));
EXPECT_EQ("someversion", user_data.serialized_hint_version_string());
}
TEST_F(PreviewsOptimizationGuideImplTest,
CanApplyPreviewWithLitePageServerPreviewsEnabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(features::kLitePageServerPreviews);
network_quality_tracker()->ReportEffectiveConnectionTypeForTesting(
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
PreviewsUserData user_data(kDefaultPageId);
content::MockNavigationHandle navigation_handle;
navigation_handle.set_url(GURL("https://m.blacklisteddomain.com/path"));
EXPECT_FALSE(guide()->CanApplyPreview(&user_data, &navigation_handle,
PreviewsType::LITE_PAGE_REDIRECT));
InitializeWithLitePageRedirectBlacklist();
EXPECT_FALSE(guide()->CanApplyPreview(&user_data, &navigation_handle,
PreviewsType::LITE_PAGE_REDIRECT));
content::MockNavigationHandle blacklisted_subdomain_navigation_handle;
blacklisted_subdomain_navigation_handle.set_url(
GURL("https://blacklistedsubdomain.maindomain.co.in"));
EXPECT_FALSE(guide()->CanApplyPreview(
&user_data, &blacklisted_subdomain_navigation_handle,
PreviewsType::LITE_PAGE_REDIRECT));
content::MockNavigationHandle main_domain_navigation_handle;
main_domain_navigation_handle.set_url(GURL("https://maindomain.co.in"));
EXPECT_TRUE(guide()->CanApplyPreview(&user_data,
&main_domain_navigation_handle,
PreviewsType::LITE_PAGE_REDIRECT));
}
TEST_F(PreviewsOptimizationGuideImplTest,
CanApplyPreviewWithLitePageServerPreviewsDisabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(features::kLitePageServerPreviews);
network_quality_tracker()->ReportEffectiveConnectionTypeForTesting(
net::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
InitializeWithLitePageRedirectBlacklist();
PreviewsUserData user_data(kDefaultPageId);
content::MockNavigationHandle navigation_handle;
navigation_handle.set_url(GURL("https://m.blacklisteddomain.com/path"));
EXPECT_FALSE(guide()->CanApplyPreview(&user_data, &navigation_handle,
PreviewsType::LITE_PAGE_REDIRECT));
}
TEST_F(PreviewsOptimizationGuideImplTest, RemoveObserverCalledAtDestruction) {
EXPECT_FALSE(optimization_guide_service()->RemoveObserverCalled());
ResetGuide();
EXPECT_TRUE(optimization_guide_service()->RemoveObserverCalled());
}
TEST_F(PreviewsOptimizationGuideImplTest, HintsFetcherEnabledNoHosts) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(
optimization_guide::features::kOptimizationHintsFetching);
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(PreviewsOptimizationGuideImplTest,
HintsFetcherEnabledWithHostsNoHintsInResponse) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(
optimization_guide::features::kOptimizationHintsFetching);
std::vector<std::string> hosts = {"example1.com", "example2.com"};
// This should be called exactly once, confirming that hints are not fetched
// again after |kTestFetchRetryDelaySecs|.
EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_))
.Times(1)
.WillRepeatedly(testing::Return(hosts));
guide()->SetHintsFetcherForTesting(
BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithNoHints));
// 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_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));
EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)).Times(0);
}
TEST_F(PreviewsOptimizationGuideImplTest, HintsFetcherEnabledWithHosts) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(
optimization_guide::features::kOptimizationHintsFetching);
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(PreviewsOptimizationGuideImplTest, HintsFetcherTimerRetryDelay) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(
optimization_guide::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(PreviewsOptimizationGuideImplTest, HintsFetcherTimerFetchSucceeds) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(
optimization_guide::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(PreviewsOptimizationGuideImplTest, HintsFetcherDisabled) {
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndDisableFeature(
optimization_guide::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();
}
TEST_F(PreviewsOptimizationGuideImplTest, HintsFetcherLastFetchAtttempt) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(
optimization_guide::features::kOptimizationHintsFetching);
// Set the last fetch attempt to 5 minutes ago, simulating a short duration
// since last execution (simulating browser crash/close-reopen).
base::TimeDelta minutes_since_attempt = base::TimeDelta::FromMinutes(5);
base::Time last_attempt_time = GetMockClock()->Now() - minutes_since_attempt;
pref_service()->SetInt64(
optimization_guide::prefs::kHintsFetcherLastFetchAttempt,
last_attempt_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
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();
// Move the clock forward a short duration to ensure that the hints fetch does
// not occur too quickly after the simulated short relaunch.
MoveClockForwardBy(base::TimeDelta::FromMinutes(5));
EXPECT_FALSE(hints_fetcher()->hints_fetched());
// Move clock forward by the maximum delay, |kTestFetchRetryDelaySeconds to
// trigger the hints fetch.
MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
EXPECT_TRUE(hints_fetcher()->hints_fetched());
}
class PreviewsOptimizationGuideDataSaverOffTest
: public PreviewsOptimizationGuideImplTest {
public:
PreviewsOptimizationGuideDataSaverOffTest() {}
~PreviewsOptimizationGuideDataSaverOffTest() override {}
void SetUp() override {
PreviewsOptimizationGuideImplTest::SetUp();
DisableDataSaver();
}
private:
DISALLOW_COPY_AND_ASSIGN(PreviewsOptimizationGuideDataSaverOffTest);
};
TEST_F(PreviewsOptimizationGuideDataSaverOffTest,
HintsFetcherEnabledDataSaverDisabled) {
base::HistogramTester histogram_tester;
base::test::ScopedFeatureList scoped_list;
scoped_list.InitAndEnableFeature(
optimization_guide::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(0);
// 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_FALSE(hints_fetcher()->hints_fetched());
}
} // namespace previews