#ifndef CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_TEST_UTIL_INTERNAL_H_
#define CONTENT_BROWSER_PRELOADING_PREFETCH_PREFETCH_TEST_UTIL_INTERNAL_H_
#include <string>
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "content/browser/preloading/prefetch/prefetch_service.h"
#include "content/public/test/preloading_test_util.h"
#include "content/public/test/test_renderer_host.h"
#include "content/test/test_content_browser_client.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/public/cpp/system/data_pipe_drainer.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/mojom/early_hints.mojom.h"
#include "services/network/public/mojom/url_loader.mojom.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 base {
class RunLoop;
}
namespace content {
using OnPrefetchCompleteTestFuture =
base::test::TestFuture<network::URLLoaderCompletionStatus>;
using OnPrefetchReceiveRedirectTestFuture =
base::test::TestFuture<net::RedirectInfo,
network::mojom::URLResponseHeadPtr>;
struct NotReachedTagForTests {};
template <typename T>
using NotReachedTagForTestsOr = std::variant<T, NotReachedTagForTests>;
std::tuple<scoped_refptr<PrefetchResponseReader>,
base::WeakPtr<PrefetchStreamingURLLoader>>
CreateStreamingURLLoaderWithoutPrefetchContainerForTests(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const network::ResourceRequest& prefetch_request,
NotReachedTagForTestsOr<base::RunLoop*> on_response_received,
NotReachedTagForTestsOr<OnPrefetchCompleteTestFuture*> on_complete,
NotReachedTagForTestsOr<OnPrefetchReceiveRedirectTestFuture*>
on_receive_redirect,
NotReachedTagForTestsOr<base::RunLoop*> on_head_received,
std::optional<PrefetchErrorOnResponseReceived> error_on_response_received =
std::nullopt,
base::TimeDelta timeout_duration = {});
base::WeakPtr<PrefetchStreamingURLLoader> CreateStreamingURLLoaderForTests(
base::WeakPtr<PrefetchContainer> prefetch_container,
base::WeakPtr<PrefetchResponseReader> response_reader,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const network::ResourceRequest& prefetch_request,
NotReachedTagForTestsOr<base::RunLoop*> on_response_received,
NotReachedTagForTestsOr<OnPrefetchReceiveRedirectTestFuture*>
on_receive_redirect,
std::optional<PrefetchErrorOnResponseReceived> error_on_response_received =
std::nullopt,
base::TimeDelta timeout_duration = {});
void MakeServableStreamingURLLoaderForTest(
PrefetchContainer* prefetch_container,
network::mojom::URLResponseHeadPtr head,
const std::string body,
network::URLLoaderCompletionStatus status =
network::URLLoaderCompletionStatus(net::OK));
network::TestURLLoaderFactory::PendingRequest
MakeManuallyServableStreamingURLLoaderForTest(
PrefetchContainer* prefetch_container);
void MakeServableStreamingURLLoaderWithRedirectForTest(
PrefetchContainer* prefetch_container,
const GURL& original_url,
const GURL& redirect_url);
void MakeServableStreamingURLLoadersWithNetworkTransitionRedirectForTest(
PrefetchContainer* prefetch_container,
const GURL& original_url,
const GURL& redirect_url);
class PrefetchTestURLLoaderClient : public network::mojom::URLLoaderClient,
public mojo::DataPipeDrainer::Client {
public:
PrefetchTestURLLoaderClient();
~PrefetchTestURLLoaderClient() override;
PrefetchTestURLLoaderClient(const PrefetchTestURLLoaderClient&) = delete;
PrefetchTestURLLoaderClient& operator=(const PrefetchTestURLLoaderClient&) =
delete;
mojo::PendingReceiver<network::mojom::URLLoader>
BindURLloaderAndGetReceiver();
mojo::PendingRemote<network::mojom::URLLoaderClient>
BindURLLoaderClientAndGetRemote();
void DisconnectMojoPipes();
void SetAutoDraining(bool auto_draining) { auto_draining_ = auto_draining; }
void StartDraining();
std::string body_content() { return body_content_; }
uint32_t total_bytes_read() { return total_bytes_read_; }
bool body_finished() { return body_finished_; }
int32_t total_transfer_size_diff() { return total_transfer_size_diff_; }
std::optional<network::URLLoaderCompletionStatus> completion_status() {
return completion_status_;
}
const std::vector<
std::pair<net::RedirectInfo, network::mojom::URLResponseHeadPtr>>&
received_redirects() {
return received_redirects_;
}
private:
void OnReceiveEarlyHints(network::mojom::EarlyHintsPtr early_hints) override;
void OnReceiveResponse(
network::mojom::URLResponseHeadPtr head,
mojo::ScopedDataPipeConsumerHandle body,
std::optional<mojo_base::BigBuffer> cached_metadata) override;
void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr head) override;
void OnUploadProgress(int64_t current_position,
int64_t total_size,
OnUploadProgressCallback callback) override;
void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
void OnComplete(const network::URLLoaderCompletionStatus& status) override;
void OnDataAvailable(base::span<const uint8_t> data) override;
void OnDataComplete() override;
#if BUILDFLAG(ARKWEB_UNITTESTS)
void OnTransferDataWithSharedMemory(
::base::ReadOnlySharedMemoryRegion region,
uint64_t buffer_size) override;
#endif
mojo::Remote<network::mojom::URLLoader> remote_;
mojo::Receiver<network::mojom::URLLoaderClient> receiver_{this};
std::unique_ptr<mojo::DataPipeDrainer> pipe_drainer_;
bool auto_draining_{true};
mojo::ScopedDataPipeConsumerHandle body_;
std::string body_content_;
uint32_t total_bytes_read_{0};
bool body_finished_{false};
int32_t total_transfer_size_diff_{0};
std::optional<network::URLLoaderCompletionStatus> completion_status_;
std::vector<std::pair<net::RedirectInfo, network::mojom::URLResponseHeadPtr>>
received_redirects_;
};
class ScopedMockContentBrowserClient : public TestContentBrowserClient {
public:
ScopedMockContentBrowserClient();
~ScopedMockContentBrowserClient() override;
MOCK_METHOD(
void,
WillCreateURLLoaderFactory,
(BrowserContext * browser_context,
RenderFrameHost* frame,
int render_process_id,
URLLoaderFactoryType type,
const url::Origin& request_initiator,
const net::IsolationInfo& isolation_info,
std::optional<int64_t> navigation_id,
ukm::SourceIdObj ukm_source_id,
network::URLLoaderFactoryBuilder& factory_builder,
mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>*
header_client,
bool* bypass_redirect_checks,
bool* disable_secure_dns,
network::mojom::URLLoaderFactoryOverridePtr* factory_override,
scoped_refptr<base::SequencedTaskRunner>
navigation_response_task_runner),
(override));
private:
raw_ptr<ContentBrowserClient> old_browser_client_;
};
class TestPrefetchService final : public PrefetchService {
public:
explicit TestPrefetchService(BrowserContext* browser_context);
~TestPrefetchService() override;
void PrefetchUrl(
base::WeakPtr<PrefetchContainer> prefetch_container) override;
void OnPrefetchCompletedOrFailed(
PrefetchContainer& prefetch_container,
const network::URLLoaderCompletionStatus& completion_status,
const std::optional<int>& response_code) override;
void EvictPrefetch(size_t index);
std::vector<base::WeakPtr<PrefetchContainer>> prefetches_;
};
class PrefetchingMetricsTestBase : public RenderViewHostTestHarness {
public:
PrefetchingMetricsTestBase();
~PrefetchingMetricsTestBase() override;
const int kTotalTimeDuration = 4321;
const int kConnectTimeDuration = 123;
ukm::TestAutoSetUkmRecorder* test_ukm_recorder() {
return test_ukm_recorder_.get();
}
const test::PreloadingAttemptUkmEntryBuilder* attempt_entry_builder() {
return attempt_entry_builder_.get();
}
void SetUp() override;
void TearDown() override;
void ExpectPrefetchNoNetErrorOrResponseReceived(
const base::HistogramTester& histogram_tester,
bool is_eligible,
bool browser_initiated_prefetch = false);
void ExpectPrefetchNotEligible(const base::HistogramTester& histogram_tester,
PreloadingEligibility expected_eligibility,
bool is_accurate = false,
bool browser_initiated_prefetch = false);
void ExpectPrefetchFailedBeforeResponseReceived(
const base::HistogramTester& histogram_tester,
PrefetchStatus expected_prefetch_status,
bool is_accurate = false);
void ExpectPrefetchFailedNetError(
const base::HistogramTester& histogram_tester,
int expected_net_error_code,
blink::mojom::SpeculationEagerness eagerness =
blink::mojom::SpeculationEagerness::kImmediate,
bool is_accurate_triggering = false,
bool browser_initiated_prefetch = false);
void ExpectPrefetchFailedAfterResponseReceived(
const base::HistogramTester& histogram_tester,
net::HttpStatusCode expected_response_code,
int expected_body_length,
PrefetchStatus expected_prefetch_status);
void ExpectPrefetchSuccess(const base::HistogramTester& histogram_tester,
int expected_body_length,
blink::mojom::SpeculationEagerness eagerness =
blink::mojom::SpeculationEagerness::kImmediate,
bool is_accurate = false);
ukm::SourceId ForceLogsUploadAndGetUkmId(
GURL navigate_url = GURL("http://Not.Accurate.Trigger.Url/"));
struct ExpectCorrectUkmLogsArgs {
PreloadingEligibility eligibility = PreloadingEligibility::kEligible;
PreloadingHoldbackStatus holdback = PreloadingHoldbackStatus::kAllowed;
PreloadingTriggeringOutcome outcome = PreloadingTriggeringOutcome::kReady;
PreloadingFailureReason failure = PreloadingFailureReason::kUnspecified;
bool is_accurate = false;
bool expect_ready_time = false;
blink::mojom::SpeculationEagerness eagerness =
blink::mojom::SpeculationEagerness::kImmediate;
};
void ExpectCorrectUkmLogs(
ExpectCorrectUkmLogsArgs args,
GURL navigate_url = GURL("http://Not.Accurate.Trigger.Url/"));
private:
std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
std::unique_ptr<test::PreloadingAttemptUkmEntryBuilder>
attempt_entry_builder_;
};
struct PrefetchRearchParam final {
public:
static std::vector<PrefetchRearchParam> Params();
bool prefetch_scheduler;
bool prefetch_scheduler_progress_sync_best_effort;
bool graceful_notification;
};
class WithPrefetchRearchParam {
public:
explicit WithPrefetchRearchParam(PrefetchRearchParam param);
virtual ~WithPrefetchRearchParam();
void InitRearchFeatures();
const PrefetchRearchParam& rearch_param() { return param_; }
private:
PrefetchRearchParam param_;
base::test::ScopedFeatureList feature_list_prefetch_scheduler_;
base::test::ScopedFeatureList feature_list_graceful_notification_;
};
class PrefetchServiceInjectedEligibilityCheckFuture final {
public:
explicit PrefetchServiceInjectedEligibilityCheckFuture(
PrefetchService& prefetch_service);
~PrefetchServiceInjectedEligibilityCheckFuture();
using TestFutureType = base::test::TestFuture<
PrefetchService::InjectedEligibilityCheckResultCallbackForTesting>;
TestFutureType* operator->() { return &result_callback_future_; }
private:
raw_ref<PrefetchService> prefetch_service_;
TestFutureType result_callback_future_;
};
}
#endif