blob: b9b41576d77a50585b1430cd0c2b9c54cc051b5c [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/preloading/prefetch/prefetch_test_util_internal.h"
#include "base/containers/span.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "content/browser/preloading/prefetch/prefetch_container.h"
#include "content/browser/preloading/prefetch/prefetch_params.h"
#include "content/browser/preloading/prefetch/prefetch_response_reader.h"
#include "content/browser/preloading/prefetch/prefetch_streaming_url_loader.h"
#include "content/browser/preloading/preloading.h"
#include "content/browser/preloading/preloading_data_impl.h"
#include "content/public/browser/prefetch_metrics.h"
#include "content/public/common/content_client.h"
#include "content/public/test/mock_navigation_handle.h"
#include "net/cookies/site_for_cookies.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "url/gurl.h"
namespace content {
using ::testing::_;
namespace {
// Make some broadly reasonable redirect info.
net::RedirectInfo SyntheticRedirect(const GURL& new_url) {
net::RedirectInfo redirect_info;
redirect_info.status_code = 302;
redirect_info.new_method = "GET";
redirect_info.new_url = new_url;
redirect_info.new_site_for_cookies = net::SiteForCookies::FromUrl(new_url);
redirect_info.new_referrer = std::string();
return redirect_info;
}
} // namespace
std::ostream& operator<<(std::ostream& ostream, PrefetchReusableForTests v) {
switch (v) {
case PrefetchReusableForTests::kDisabled:
return ostream << "AllowMultipleUses Disabled";
case PrefetchReusableForTests::kEnabled:
return ostream << "AllowMultipleUses Enabled";
}
}
std::vector<PrefetchReusableForTests> PrefetchReusableValuesForTests() {
return std::vector<PrefetchReusableForTests>{
PrefetchReusableForTests::kDisabled, PrefetchReusableForTests::kEnabled};
}
void MakeServableStreamingURLLoaderForTest(
PrefetchContainer* prefetch_container,
network::mojom::URLResponseHeadPtr head,
const std::string body) {
prefetch_container->SimulatePrefetchStartedForTest();
const GURL kTestUrl = GURL("https://test.com");
network::TestURLLoaderFactory test_url_loader_factory;
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = kTestUrl;
request->method = "GET";
base::RunLoop on_response_received_loop;
base::RunLoop on_response_complete_loop;
base::WeakPtr<PrefetchResponseReader> weak_response_reader =
prefetch_container->GetResponseReaderForCurrentPrefetch();
auto weak_streaming_loader = PrefetchStreamingURLLoader::CreateAndStart(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory),
*request, TRAFFIC_ANNOTATION_FOR_TESTS,
/*timeout_duration=*/base::TimeDelta(),
base::BindOnce(
[](base::RunLoop* on_response_received_loop,
network::mojom::URLResponseHead* head) {
on_response_received_loop->Quit();
return std::optional<PrefetchErrorOnResponseReceived>();
},
&on_response_received_loop),
base::BindOnce(
[](base::RunLoop* on_response_complete_loop,
const network::URLLoaderCompletionStatus& completion_status) {
on_response_complete_loop->Quit();
},
&on_response_complete_loop),
base::BindRepeating([](const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr response_head) {
NOTREACHED();
}),
base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
prefetch_container->GetWeakPtr()),
weak_response_reader);
prefetch_container->SetStreamingURLLoader(weak_streaming_loader);
network::URLLoaderCompletionStatus status(net::OK);
test_url_loader_factory.AddResponse(
kTestUrl, std::move(head), body, status,
network::TestURLLoaderFactory::Redirects(),
network::TestURLLoaderFactory::kResponseDefault);
on_response_received_loop.Run();
on_response_complete_loop.Run();
CHECK(weak_streaming_loader);
CHECK(weak_response_reader);
CHECK(weak_response_reader->Servable(base::TimeDelta::Max()));
}
network::TestURLLoaderFactory::PendingRequest
MakeManuallyServableStreamingURLLoaderForTest(
PrefetchContainer* prefetch_container) {
prefetch_container->SimulatePrefetchStartedForTest();
const GURL kTestUrl = GURL("https://test.com");
network::TestURLLoaderFactory test_url_loader_factory;
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = kTestUrl;
request->method = "GET";
auto weak_streaming_loader = PrefetchStreamingURLLoader::CreateAndStart(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory),
*request, TRAFFIC_ANNOTATION_FOR_TESTS,
/*timeout_duration=*/base::TimeDelta(),
base::BindOnce([](network::mojom::URLResponseHead* head) {
return std::optional<PrefetchErrorOnResponseReceived>();
}),
base::BindOnce(&PrefetchContainer::OnPrefetchComplete,
prefetch_container->GetWeakPtr()),
base::BindRepeating([](const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr response_head) {
NOTREACHED();
}),
base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
prefetch_container->GetWeakPtr()),
prefetch_container->GetResponseReaderForCurrentPrefetch());
prefetch_container->SetStreamingURLLoader(weak_streaming_loader);
CHECK_EQ(test_url_loader_factory.pending_requests()->size(), 1u);
return std::move(test_url_loader_factory.pending_requests()->at(0));
}
OnPrefetchRedirectCallback CreatePrefetchRedirectCallbackForTest(
base::RunLoop* on_receive_redirect_loop,
net::RedirectInfo* out_redirect_info,
network::mojom::URLResponseHeadPtr* out_redirect_head) {
return base::BindRepeating(
[](base::RunLoop* on_receive_redirect_loop,
net::RedirectInfo* out_redirect_info,
network::mojom::URLResponseHeadPtr* out_redirect_head,
const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr redirect_head) {
*out_redirect_info = redirect_info;
*out_redirect_head = std::move(redirect_head);
on_receive_redirect_loop->Quit();
},
on_receive_redirect_loop, out_redirect_info, out_redirect_head);
}
void MakeServableStreamingURLLoaderWithRedirectForTest(
PrefetchContainer* prefetch_container,
const GURL& original_url,
const GURL& redirect_url) {
prefetch_container->SimulatePrefetchStartedForTest();
network::TestURLLoaderFactory test_url_loader_factory;
std::unique_ptr<network::ResourceRequest> request =
std::make_unique<network::ResourceRequest>();
request->url = original_url;
request->method = "GET";
base::RunLoop on_receive_redirect_loop;
base::RunLoop on_response_received_loop;
base::RunLoop on_response_complete_loop;
net::RedirectInfo redirect_info;
network::mojom::URLResponseHeadPtr redirect_head;
auto weak_first_response_reader =
prefetch_container->GetResponseReaderForCurrentPrefetch();
auto weak_streaming_loader = PrefetchStreamingURLLoader::CreateAndStart(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory),
*request, TRAFFIC_ANNOTATION_FOR_TESTS,
/*timeout_duration=*/base::TimeDelta(),
base::BindOnce(
[](base::RunLoop* on_response_received_loop,
network::mojom::URLResponseHead* head) {
on_response_received_loop->Quit();
return std::optional<PrefetchErrorOnResponseReceived>();
},
&on_response_received_loop),
base::BindOnce(
[](base::RunLoop* on_response_complete_loop,
const network::URLLoaderCompletionStatus& completion_status) {
on_response_complete_loop->Quit();
},
&on_response_complete_loop),
CreatePrefetchRedirectCallbackForTest(&on_receive_redirect_loop,
&redirect_info, &redirect_head),
base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
prefetch_container->GetWeakPtr()),
weak_first_response_reader);
prefetch_container->SetStreamingURLLoader(weak_streaming_loader);
network::URLLoaderCompletionStatus status(net::OK);
net::RedirectInfo original_redirect_info = SyntheticRedirect(redirect_url);
network::TestURLLoaderFactory::Redirects redirects;
redirects.emplace_back(original_redirect_info,
network::mojom::URLResponseHead::New());
test_url_loader_factory.AddResponse(
original_url, network::mojom::URLResponseHead::New(), "test body", status,
std::move(redirects), network::TestURLLoaderFactory::kResponseDefault);
on_receive_redirect_loop.Run();
prefetch_container->AddRedirectHop(redirect_info);
CHECK(weak_streaming_loader);
weak_streaming_loader->HandleRedirect(
PrefetchRedirectStatus::kFollow, redirect_info, std::move(redirect_head));
// GetResponseReaderForCurrentPrefetch() now points to a new ResponseReader
// after `AddRedirectHop()` above.
CHECK(weak_streaming_loader);
auto weak_second_response_reader =
prefetch_container->GetResponseReaderForCurrentPrefetch();
weak_streaming_loader->SetResponseReader(weak_second_response_reader);
on_response_received_loop.Run();
on_response_complete_loop.Run();
CHECK(weak_streaming_loader);
CHECK(weak_first_response_reader);
CHECK(weak_second_response_reader);
CHECK(weak_second_response_reader->Servable(base::TimeDelta::Max()));
}
void MakeServableStreamingURLLoadersWithNetworkTransitionRedirectForTest(
PrefetchContainer* prefetch_container,
const GURL& original_url,
const GURL& redirect_url) {
prefetch_container->SimulatePrefetchStartedForTest();
network::TestURLLoaderFactory test_url_loader_factory;
std::unique_ptr<network::ResourceRequest> original_request =
std::make_unique<network::ResourceRequest>();
original_request->url = original_url;
original_request->method = "GET";
base::RunLoop on_receive_redirect_loop;
net::RedirectInfo redirect_info;
network::mojom::URLResponseHeadPtr redirect_head;
// Simulate a PrefetchStreamingURLLoader that receives a redirect that
// requires a change in a network context. When this happens, it will stop its
// request, but can be used to serve the redirect. A new
// PrefetchStreamingURLLoader will be started with a request to the redirect
// URL.
auto weak_first_streaming_loader = PrefetchStreamingURLLoader::CreateAndStart(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory),
*original_request, TRAFFIC_ANNOTATION_FOR_TESTS,
/*timeout_duration=*/base::TimeDelta(),
base::BindOnce([](network::mojom::URLResponseHead* head)
-> std::optional<PrefetchErrorOnResponseReceived> {
NOTREACHED();
}),
base::BindOnce(
[](const network::URLLoaderCompletionStatus& completion_status) {
NOTREACHED();
}),
CreatePrefetchRedirectCallbackForTest(&on_receive_redirect_loop,
&redirect_info, &redirect_head),
base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
prefetch_container->GetWeakPtr()),
prefetch_container->GetResponseReaderForCurrentPrefetch());
prefetch_container->SetStreamingURLLoader(weak_first_streaming_loader);
net::RedirectInfo original_redirect_info = SyntheticRedirect(redirect_url);
network::TestURLLoaderFactory::Redirects redirects;
redirects.emplace_back(original_redirect_info,
network::mojom::URLResponseHead::New());
test_url_loader_factory.AddResponse(
original_url, nullptr, "", network::URLLoaderCompletionStatus(),
std::move(redirects),
network::TestURLLoaderFactory::kResponseOnlyRedirectsNoDestination);
on_receive_redirect_loop.Run();
prefetch_container->AddRedirectHop(redirect_info);
CHECK(weak_first_streaming_loader);
weak_first_streaming_loader->HandleRedirect(
PrefetchRedirectStatus::kSwitchNetworkContext, redirect_info,
std::move(redirect_head));
std::unique_ptr<network::ResourceRequest> redirect_request =
std::make_unique<network::ResourceRequest>();
redirect_request->url = redirect_url;
redirect_request->method = "GET";
base::RunLoop on_response_received_loop;
base::RunLoop on_response_complete_loop;
// Starts the followup PrefetchStreamingURLLoader.
// GetResponseReaderForCurrentPrefetch() now points to a new ResponseReader
// after `AddRedirectHop()` above.
base::WeakPtr<PrefetchResponseReader> weak_second_response_reader =
prefetch_container->GetResponseReaderForCurrentPrefetch();
auto weak_second_streaming_loader =
PrefetchStreamingURLLoader::CreateAndStart(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory),
*redirect_request, TRAFFIC_ANNOTATION_FOR_TESTS,
/*timeout_duration=*/base::TimeDelta(),
base::BindOnce(
[](base::RunLoop* on_response_received_loop,
network::mojom::URLResponseHead* head) {
on_response_received_loop->Quit();
return std::optional<PrefetchErrorOnResponseReceived>();
},
&on_response_received_loop),
base::BindOnce(
[](base::RunLoop* on_response_complete_loop,
const network::URLLoaderCompletionStatus& completion_status) {
on_response_complete_loop->Quit();
},
&on_response_complete_loop),
base::BindRepeating(
[](const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr response_head) {
NOTREACHED();
}),
base::BindOnce(&PrefetchContainer::OnDeterminedHead2,
prefetch_container->GetWeakPtr()),
weak_second_response_reader);
prefetch_container->SetStreamingURLLoader(weak_second_streaming_loader);
network::URLLoaderCompletionStatus status(net::OK);
test_url_loader_factory.AddResponse(
redirect_url, network::mojom::URLResponseHead::New(), "test body", status,
network::TestURLLoaderFactory::Redirects(),
network::TestURLLoaderFactory::kResponseDefault);
on_response_received_loop.Run();
on_response_complete_loop.Run();
// `weak_first_streaming_loader` should be deleted after
// `HandleRedirect(kSwitchNetworkContext)`.
CHECK(!weak_first_streaming_loader);
CHECK(weak_second_streaming_loader);
CHECK(weak_second_response_reader);
CHECK(weak_second_response_reader->Servable(base::TimeDelta::Max()));
}
PrefetchTestURLLoaderClient::PrefetchTestURLLoaderClient() = default;
PrefetchTestURLLoaderClient::~PrefetchTestURLLoaderClient() = default;
mojo::PendingReceiver<network::mojom::URLLoader>
PrefetchTestURLLoaderClient::BindURLloaderAndGetReceiver() {
return remote_.BindNewPipeAndPassReceiver();
}
mojo::PendingRemote<network::mojom::URLLoaderClient>
PrefetchTestURLLoaderClient::BindURLLoaderClientAndGetRemote() {
return receiver_.BindNewPipeAndPassRemote();
}
void PrefetchTestURLLoaderClient::DisconnectMojoPipes() {
remote_.reset();
receiver_.reset();
}
void PrefetchTestURLLoaderClient::OnReceiveEarlyHints(
network::mojom::EarlyHintsPtr early_hints) {
NOTREACHED();
}
void PrefetchTestURLLoaderClient::OnReceiveResponse(
network::mojom::URLResponseHeadPtr head,
mojo::ScopedDataPipeConsumerHandle body,
std::optional<mojo_base::BigBuffer> cached_metadata) {
CHECK(!cached_metadata);
CHECK(!body_);
body_ = std::move(body);
if (auto_draining_) {
StartDraining();
}
}
void PrefetchTestURLLoaderClient::StartDraining() {
// Drains |body_| into |body_content_|
CHECK(body_);
CHECK(!pipe_drainer_);
pipe_drainer_ =
std::make_unique<mojo::DataPipeDrainer>(this, std::move(body_));
}
void PrefetchTestURLLoaderClient::OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr head) {
received_redirects_.emplace_back(redirect_info, std::move(head));
}
void PrefetchTestURLLoaderClient::OnUploadProgress(
int64_t current_position,
int64_t total_size,
OnUploadProgressCallback callback) {
NOTREACHED();
}
void PrefetchTestURLLoaderClient::OnTransferSizeUpdated(
int32_t transfer_size_diff) {
total_transfer_size_diff_ += transfer_size_diff;
}
void PrefetchTestURLLoaderClient::OnComplete(
const network::URLLoaderCompletionStatus& status) {
completion_status_ = status;
}
void PrefetchTestURLLoaderClient::OnDataAvailable(
base::span<const uint8_t> data) {
body_content_.append(base::as_string_view(data));
total_bytes_read_ += data.size();
}
void PrefetchTestURLLoaderClient::OnDataComplete() {
body_finished_ = true;
}
ScopedMockContentBrowserClient::ScopedMockContentBrowserClient() {
old_browser_client_ = SetBrowserClientForTesting(this);
// Ignore `WillCreateURLLoaderFactory(kDocumentSubResource)` calls (triggered
// by e.g. `RenderFrameHostImpl::CommitNavigation()`) to suppress gmock
// warning/failures. This is safe to ignore, because prefetch-related tests
// are only for navigational prefetch and don't care about unrelated
// subresource URLLoaderFactories.
EXPECT_CALL(
*this,
WillCreateURLLoaderFactory(
_, _, _,
ContentBrowserClient::URLLoaderFactoryType::kDocumentSubResource, _,
_, _, _, _, _, _, _, _, _))
.Times(::testing::AnyNumber());
}
ScopedMockContentBrowserClient::~ScopedMockContentBrowserClient() {
EXPECT_EQ(this, SetBrowserClientForTesting(old_browser_client_));
}
PrefetchingMetricsTestBase::PrefetchingMetricsTestBase()
: RenderViewHostTestHarness(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
PrefetchingMetricsTestBase::~PrefetchingMetricsTestBase() = default;
void PrefetchingMetricsTestBase::SetUp() {
RenderViewHostTestHarness::SetUp();
test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
attempt_entry_builder_ =
std::make_unique<test::PreloadingAttemptUkmEntryBuilder>(
content_preloading_predictor::kSpeculationRules);
}
void PrefetchingMetricsTestBase::TearDown() {
test_ukm_recorder_.reset();
attempt_entry_builder_.reset();
RenderViewHostTestHarness::TearDown();
}
void PrefetchingMetricsTestBase::ExpectPrefetchNoNetErrorOrResponseReceived(
const base::HistogramTester& histogram_tester,
bool is_eligible,
bool browser_initiated_prefetch) {
histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode",
0);
histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError",
0);
histogram_tester.ExpectTotalCount(
"PrefetchProxy.Prefetch.Mainframe.BodyLength", 0);
histogram_tester.ExpectTotalCount(
"PrefetchProxy.Prefetch.Mainframe.TotalTime", 0);
histogram_tester.ExpectTotalCount(
"PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0);
if (!browser_initiated_prefetch) {
std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1);
EXPECT_EQ(referring_page_metrics->prefetch_eligible_count,
is_eligible ? 1 : 0);
EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0);
}
}
void PrefetchingMetricsTestBase::ExpectPrefetchNotEligible(
const base::HistogramTester& histogram_tester,
PreloadingEligibility expected_eligibility,
bool is_accurate,
bool browser_initiated_prefetch) {
ExpectPrefetchNoNetErrorOrResponseReceived(histogram_tester,
/*is_eligible=*/false,
browser_initiated_prefetch);
if (!browser_initiated_prefetch) {
ExpectCorrectUkmLogs({.eligibility = expected_eligibility,
.holdback = PreloadingHoldbackStatus::kUnspecified,
.outcome = PreloadingTriggeringOutcome::kUnspecified,
.is_accurate = is_accurate});
}
}
void PrefetchingMetricsTestBase::ExpectPrefetchFailedBeforeResponseReceived(
const base::HistogramTester& histogram_tester,
PrefetchStatus expected_prefetch_status,
bool is_accurate) {
ExpectPrefetchNoNetErrorOrResponseReceived(histogram_tester,
/*is_eligible=*/true);
histogram_tester.ExpectUniqueSample("Preloading.Prefetch.PrefetchStatus",
expected_prefetch_status, 1);
ExpectCorrectUkmLogs(
{.outcome = PreloadingTriggeringOutcome::kFailure,
.failure = ToPreloadingFailureReason(expected_prefetch_status),
.is_accurate = is_accurate});
}
void PrefetchingMetricsTestBase::ExpectPrefetchFailedNetError(
const base::HistogramTester& histogram_tester,
int expected_net_error_code,
blink::mojom::SpeculationEagerness eagerness,
bool is_accurate_triggering,
bool browser_initiated_prefetch) {
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.RespCode",
0);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.NetError",
std::abs(expected_net_error_code), 1);
histogram_tester.ExpectTotalCount(
"PrefetchProxy.Prefetch.Mainframe.BodyLength", 0);
histogram_tester.ExpectTotalCount(
"PrefetchProxy.Prefetch.Mainframe.TotalTime", 0);
histogram_tester.ExpectTotalCount(
"PrefetchProxy.Prefetch.Mainframe.ConnectTime", 0);
histogram_tester.ExpectUniqueSample("Preloading.Prefetch.PrefetchStatus",
PrefetchStatus::kPrefetchFailedNetError,
1);
if (!browser_initiated_prefetch) {
std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1);
EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1);
EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0);
ExpectCorrectUkmLogs({.outcome = PreloadingTriggeringOutcome::kFailure,
.failure = ToPreloadingFailureReason(
PrefetchStatus::kPrefetchFailedNetError),
.is_accurate = is_accurate_triggering,
.eagerness = eagerness});
}
}
void PrefetchingMetricsTestBase::ExpectPrefetchFailedAfterResponseReceived(
const base::HistogramTester& histogram_tester,
net::HttpStatusCode expected_response_code,
int expected_body_length,
PrefetchStatus expected_prefetch_status) {
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.RespCode", expected_response_code, 1);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.BodyLength", expected_body_length, 1);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1);
EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1);
EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0);
histogram_tester.ExpectUniqueSample("Preloading.Prefetch.PrefetchStatus",
expected_prefetch_status, 1);
ExpectCorrectUkmLogs(
{.outcome = PreloadingTriggeringOutcome::kFailure,
.failure = ToPreloadingFailureReason(expected_prefetch_status)});
}
void PrefetchingMetricsTestBase::ExpectPrefetchSuccess(
const base::HistogramTester& histogram_tester,
int expected_body_length,
blink::mojom::SpeculationEagerness eagerness,
bool is_accurate) {
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.BodyLength", expected_body_length, 1);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
histogram_tester.ExpectUniqueSample(
"PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1);
EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1);
EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1);
ExpectCorrectUkmLogs({.is_accurate = is_accurate, .eagerness = eagerness});
}
ukm::SourceId PrefetchingMetricsTestBase::ForceLogsUploadAndGetUkmId(
GURL navigate_url) {
MockNavigationHandle mock_handle;
mock_handle.set_is_in_primary_main_frame(true);
mock_handle.set_is_same_document(false);
mock_handle.set_has_committed(true);
mock_handle.set_url(std::move(navigate_url));
auto* preloading_data =
PreloadingData::GetOrCreateForWebContents(web_contents());
// Sets the accurate bit, and records `TimeToNextNavigation`.
static_cast<PreloadingDataImpl*>(preloading_data)
->DidStartNavigation(&mock_handle);
// Records the UKMs.
static_cast<PreloadingDataImpl*>(preloading_data)
->DidFinishNavigation(&mock_handle);
return mock_handle.GetNextPageUkmSourceId();
}
void PrefetchingMetricsTestBase::ExpectCorrectUkmLogs(
ExpectCorrectUkmLogsArgs args,
GURL navigate_url) {
const auto source_id = ForceLogsUploadAndGetUkmId(std::move(navigate_url));
auto actual_attempts = test_ukm_recorder()->GetEntries(
ukm::builders::Preloading_Attempt::kEntryName,
test::kPreloadingAttemptUkmMetrics);
EXPECT_EQ(actual_attempts.size(), 1u);
std::optional<base::TimeDelta> ready_time = std::nullopt;
if (args.outcome == PreloadingTriggeringOutcome::kReady ||
args.outcome == PreloadingTriggeringOutcome::kSuccess ||
args.expect_ready_time) {
ready_time = base::ScopedMockElapsedTimersForTest::kMockElapsedTime;
}
const auto expected_attempts = {attempt_entry_builder()->BuildEntry(
source_id, PreloadingType::kPrefetch, args.eligibility, args.holdback,
args.outcome, args.failure, args.is_accurate, ready_time,
args.eagerness)};
EXPECT_THAT(actual_attempts,
testing::UnorderedElementsAreArray(expected_attempts))
<< test::ActualVsExpectedUkmEntriesToString(actual_attempts,
expected_attempts);
// We do not test the `PreloadingPrediction` as it is added in
// `PreloadingDecider`.
}
} // namespace content