blob: e171117339e70fd2a5af0fcef95833be04e68625 [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 "chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h"
#include "chrome/browser/net/nqe/ui_network_quality_estimator_service_factory.h"
#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
#include "chrome/browser/page_load_metrics/page_load_tracker.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/navigation_simulator.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::AnyNumber;
using testing::Return;
class MockNetworkQualityProvider : public net::NetworkQualityProvider {
public:
MOCK_CONST_METHOD0(GetEffectiveConnectionType,
net::EffectiveConnectionType());
MOCK_CONST_METHOD0(GetHttpRTT, base::Optional<base::TimeDelta>());
MOCK_CONST_METHOD0(GetTransportRTT, base::Optional<base::TimeDelta>());
MOCK_CONST_METHOD0(GetDownstreamThroughputKbps, base::Optional<int32_t>());
};
class TestAndroidPageLoadMetricsObserver
: public AndroidPageLoadMetricsObserver {
public:
TestAndroidPageLoadMetricsObserver(
content::WebContents* web_contents,
net::NetworkQualityEstimator::NetworkQualityProvider*
network_quality_provider)
: AndroidPageLoadMetricsObserver(web_contents, network_quality_provider) {
}
net::EffectiveConnectionType reported_connection_type() const {
return reported_connection_type_;
}
int64_t reported_http_rtt_ms() const { return reported_http_rtt_ms_; }
int64_t reported_transport_rtt_ms() const {
return reported_transport_rtt_ms_;
}
int64_t reported_first_contentful_paint_ms() const {
return reported_first_contentful_paint_ms_;
}
int64_t reported_navigation_start_tick_fcp() const {
return reported_navigation_start_tick_fcp_;
}
int64_t reported_navigation_start_tick_load() const {
return reported_navigation_start_tick_load_;
}
int64_t reported_load_event_start_ms() const {
return reported_load_event_start_ms_;
}
int64_t reported_dns_start_ms() const { return reported_dns_start_ms_; }
protected:
void ReportNetworkQualityEstimate(
net::EffectiveConnectionType connection_type,
int64_t http_rtt_ms,
int64_t transport_rtt_ms) override {
reported_connection_type_ = connection_type;
reported_http_rtt_ms_ = http_rtt_ms;
reported_transport_rtt_ms_ = transport_rtt_ms;
}
void ReportFirstContentfulPaint(int64_t navigation_start_tick,
int64_t first_contentful_paint_ms) override {
reported_navigation_start_tick_fcp_ = navigation_start_tick;
reported_first_contentful_paint_ms_ = first_contentful_paint_ms;
}
void ReportLoadEventStart(int64_t navigation_start_tick,
int64_t load_event_start_ms) override {
reported_navigation_start_tick_load_ = navigation_start_tick;
reported_load_event_start_ms_ = load_event_start_ms;
}
void ReportLoadedMainResource(int64_t dns_start_ms,
int64_t dns_end_ms,
int64_t connect_start_ms,
int64_t connect_end_ms,
int64_t request_start_ms,
int64_t send_start_ms,
int64_t send_end_ms) override {
reported_dns_start_ms_ = dns_start_ms;
}
private:
net::EffectiveConnectionType reported_connection_type_ =
net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
int64_t reported_http_rtt_ms_ = 0;
int64_t reported_transport_rtt_ms_ = 0;
int64_t reported_first_contentful_paint_ms_ = 0;
int64_t reported_navigation_start_tick_fcp_ = 0;
int64_t reported_navigation_start_tick_load_ = 0;
int64_t reported_load_event_start_ms_ = 0;
int64_t reported_dns_start_ms_ = 0;
};
class AndroidPageLoadMetricsObserverTest
: public page_load_metrics::PageLoadMetricsObserverTestHarness {
public:
AndroidPageLoadMetricsObserverTest() {}
void SetUp() override {
PageLoadMetricsObserverTestHarness::SetUp();
// Save observer_ptr_ so we can query for test results, while the
// PageLoadTracker owns it.
observer_ptr_ = new TestAndroidPageLoadMetricsObserver(
web_contents(), &mock_network_quality_provider_);
observer_ = base::WrapUnique<page_load_metrics::PageLoadMetricsObserver>(
observer_ptr_);
}
TestAndroidPageLoadMetricsObserver* observer() const { return observer_ptr_; }
int64_t GetNavigationStartMicroseconds() const {
return (navigation_start_ - base::TimeTicks()).InMicroseconds();
}
void SetNetworkQualityMock() {
EXPECT_CALL(mock_network_quality_provider(), GetEffectiveConnectionType())
.Times(AnyNumber())
.WillRepeatedly(Return(net::EFFECTIVE_CONNECTION_TYPE_3G));
EXPECT_CALL(mock_network_quality_provider(), GetHttpRTT())
.Times(AnyNumber())
.WillRepeatedly(Return(base::Optional<base::TimeDelta>(
base::TimeDelta::FromMilliseconds(3))));
EXPECT_CALL(mock_network_quality_provider(), GetTransportRTT())
.Times(AnyNumber())
.WillRepeatedly(Return(base::Optional<base::TimeDelta>(
base::TimeDelta::FromMilliseconds(4))));
}
MockNetworkQualityProvider& mock_network_quality_provider() {
return mock_network_quality_provider_;
}
UINetworkQualityEstimatorService* GetNetworkQualityEstimator() {
return UINetworkQualityEstimatorServiceFactory::GetForProfile(
Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
}
void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
navigation_start_ = tracker->navigation_start();
tracker->AddObserver(std::move(observer_));
}
private:
std::unique_ptr<page_load_metrics::PageLoadMetricsObserver> observer_;
TestAndroidPageLoadMetricsObserver* observer_ptr_;
MockNetworkQualityProvider mock_network_quality_provider_;
base::TimeTicks navigation_start_;
};
TEST_F(AndroidPageLoadMetricsObserverTest, NetworkQualityEstimate) {
SetNetworkQualityMock();
NavigateAndCommit(GURL("https://www.example.com"));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_3G,
observer()->reported_connection_type());
EXPECT_EQ(3L, observer()->reported_http_rtt_ms());
EXPECT_EQ(4L, observer()->reported_transport_rtt_ms());
}
TEST_F(AndroidPageLoadMetricsObserverTest, MissingNetworkQualityEstimate) {
EXPECT_CALL(mock_network_quality_provider(), GetEffectiveConnectionType())
.Times(AnyNumber())
.WillRepeatedly(Return(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN));
EXPECT_CALL(mock_network_quality_provider(), GetHttpRTT())
.Times(AnyNumber())
.WillRepeatedly(Return(base::Optional<base::TimeDelta>()));
EXPECT_CALL(mock_network_quality_provider(), GetTransportRTT())
.Times(AnyNumber())
.WillRepeatedly(Return(base::Optional<base::TimeDelta>()));
NavigateAndCommit(GURL("https://www.example.com"));
EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
observer()->reported_connection_type());
EXPECT_EQ(0L, observer()->reported_http_rtt_ms());
EXPECT_EQ(0L, observer()->reported_transport_rtt_ms());
}
TEST_F(AndroidPageLoadMetricsObserverTest, LoadTimingInfo) {
SetNetworkQualityMock();
auto navigation_simulator =
content::NavigationSimulator::CreateRendererInitiated(
GURL("https://www.example.com"), web_contents()->GetMainFrame());
navigation_simulator->Start();
int frame_tree_node_id =
navigation_simulator->GetNavigationHandle()->GetFrameTreeNodeId();
navigation_simulator->Commit();
auto load_timing_info = std::make_unique<net::LoadTimingInfo>();
const base::TimeTicks kNow = base::TimeTicks::Now();
load_timing_info->connect_timing.dns_start = kNow;
page_load_metrics::ExtraRequestCompleteInfo info(
GURL("https://ignored.com"), net::HostPortPair(), frame_tree_node_id,
false, /* cached */
10 * 1024 /* size */, 0 /* original_network_content_length */,
nullptr
/* data_reduction_proxy_data */,
content::RESOURCE_TYPE_MAIN_FRAME, 0, std::move(load_timing_info));
SimulateLoadedResource(info, navigation_simulator->GetGlobalRequestID());
EXPECT_EQ(kNow.since_origin().InMilliseconds(),
observer()->reported_dns_start_ms());
}
TEST_F(AndroidPageLoadMetricsObserverTest, LoadEvents) {
SetNetworkQualityMock();
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
// Note this navigation start does not effect the start that is reported to
// us.
timing.navigation_start = base::Time::FromDoubleT(1);
timing.document_timing->load_event_start =
base::TimeDelta::FromMilliseconds(30);
timing.paint_timing->first_contentful_paint =
base::TimeDelta::FromMilliseconds(20);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL("https://www.example.com"));
SimulateTimingUpdate(timing);
EXPECT_EQ(30, observer()->reported_load_event_start_ms());
EXPECT_EQ(GetNavigationStartMicroseconds(),
observer()->reported_navigation_start_tick_load());
EXPECT_EQ(20, observer()->reported_first_contentful_paint_ms());
EXPECT_EQ(GetNavigationStartMicroseconds(),
observer()->reported_navigation_start_tick_fcp());
}