1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h"
6 
7 #include "base/memory/ptr_util.h"
8 #include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "components/page_load_metrics/browser/page_load_tracker.h"
11 #include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
12 #include "content/public/browser/web_contents.h"
13 #include "content/public/test/navigation_simulator.h"
14 #include "net/base/ip_endpoint.h"
15 #include "services/network/public/cpp/network_quality_tracker.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 
18 using testing::AnyNumber;
19 using testing::Return;
20 
21 class MockNetworkQualityTracker : public network::NetworkQualityTracker {
22  public:
23   MOCK_CONST_METHOD0(GetEffectiveConnectionType,
24                      net::EffectiveConnectionType());
25   MOCK_CONST_METHOD0(GetHttpRTT, base::TimeDelta());
26   MOCK_CONST_METHOD0(GetTransportRTT, base::TimeDelta());
27   MOCK_CONST_METHOD0(GetDownstreamThroughputKbps, int32_t());
28 };
29 
30 class TestAndroidPageLoadMetricsObserver
31     : public AndroidPageLoadMetricsObserver {
32  public:
TestAndroidPageLoadMetricsObserver(network::NetworkQualityTracker * network_quality_tracker)33   TestAndroidPageLoadMetricsObserver(
34       network::NetworkQualityTracker* network_quality_tracker)
35       : AndroidPageLoadMetricsObserver(network_quality_tracker) {}
36 
reported_connection_type() const37   net::EffectiveConnectionType reported_connection_type() const {
38     return reported_connection_type_;
39   }
40 
reported_http_rtt_ms() const41   int64_t reported_http_rtt_ms() const { return reported_http_rtt_ms_; }
42 
reported_transport_rtt_ms() const43   int64_t reported_transport_rtt_ms() const {
44     return reported_transport_rtt_ms_;
45   }
46 
reported_first_contentful_paint_ms() const47   int64_t reported_first_contentful_paint_ms() const {
48     return reported_first_contentful_paint_ms_;
49   }
50 
reported_navigation_start_tick_fcp() const51   int64_t reported_navigation_start_tick_fcp() const {
52     return reported_navigation_start_tick_fcp_;
53   }
54 
reported_navigation_start_tick_load() const55   int64_t reported_navigation_start_tick_load() const {
56     return reported_navigation_start_tick_load_;
57   }
58 
reported_load_event_start_ms() const59   int64_t reported_load_event_start_ms() const {
60     return reported_load_event_start_ms_;
61   }
62 
reported_dns_start_ms() const63   int64_t reported_dns_start_ms() const { return reported_dns_start_ms_; }
64 
65  protected:
ReportNetworkQualityEstimate(net::EffectiveConnectionType connection_type,int64_t http_rtt_ms,int64_t transport_rtt_ms)66   void ReportNetworkQualityEstimate(
67       net::EffectiveConnectionType connection_type,
68       int64_t http_rtt_ms,
69       int64_t transport_rtt_ms) override {
70     reported_connection_type_ = connection_type;
71     reported_http_rtt_ms_ = http_rtt_ms;
72     reported_transport_rtt_ms_ = transport_rtt_ms;
73   }
74 
ReportFirstContentfulPaint(int64_t navigation_start_tick,int64_t first_contentful_paint_ms)75   void ReportFirstContentfulPaint(int64_t navigation_start_tick,
76                                   int64_t first_contentful_paint_ms) override {
77     reported_navigation_start_tick_fcp_ = navigation_start_tick;
78     reported_first_contentful_paint_ms_ = first_contentful_paint_ms;
79   }
80 
ReportLoadEventStart(int64_t navigation_start_tick,int64_t load_event_start_ms)81   void ReportLoadEventStart(int64_t navigation_start_tick,
82                             int64_t load_event_start_ms) override {
83     reported_navigation_start_tick_load_ = navigation_start_tick;
84     reported_load_event_start_ms_ = load_event_start_ms;
85   }
86 
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)87   void ReportLoadedMainResource(int64_t dns_start_ms,
88                                 int64_t dns_end_ms,
89                                 int64_t connect_start_ms,
90                                 int64_t connect_end_ms,
91                                 int64_t request_start_ms,
92                                 int64_t send_start_ms,
93                                 int64_t send_end_ms) override {
94     reported_dns_start_ms_ = dns_start_ms;
95   }
96 
97  private:
98   net::EffectiveConnectionType reported_connection_type_ =
99       net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
100   int64_t reported_http_rtt_ms_ = 0;
101   int64_t reported_transport_rtt_ms_ = 0;
102   int64_t reported_first_contentful_paint_ms_ = 0;
103   int64_t reported_navigation_start_tick_fcp_ = 0;
104   int64_t reported_navigation_start_tick_load_ = 0;
105   int64_t reported_load_event_start_ms_ = 0;
106   int64_t reported_dns_start_ms_ = 0;
107 };
108 
109 class AndroidPageLoadMetricsObserverTest
110     : public page_load_metrics::PageLoadMetricsObserverTestHarness {
111  public:
AndroidPageLoadMetricsObserverTest()112   AndroidPageLoadMetricsObserverTest() {}
113 
SetUp()114   void SetUp() override {
115     PageLoadMetricsObserverTestHarness::SetUp();
116     // Save observer_ptr_ so we can query for test results, while the
117     // PageLoadTracker owns it.
118     observer_ptr_ =
119         new TestAndroidPageLoadMetricsObserver(&mock_network_quality_tracker_);
120     observer_ = base::WrapUnique<page_load_metrics::PageLoadMetricsObserver>(
121         observer_ptr_);
122   }
123 
observer() const124   TestAndroidPageLoadMetricsObserver* observer() const { return observer_ptr_; }
125 
GetNavigationStartMicroseconds() const126   int64_t GetNavigationStartMicroseconds() const {
127     return (navigation_start_ - base::TimeTicks()).InMicroseconds();
128   }
129 
SetNetworkQualityMock()130   void SetNetworkQualityMock() {
131     EXPECT_CALL(mock_network_quality_tracker(), GetEffectiveConnectionType())
132         .Times(AnyNumber())
133         .WillRepeatedly(Return(net::EFFECTIVE_CONNECTION_TYPE_3G));
134     EXPECT_CALL(mock_network_quality_tracker(), GetHttpRTT())
135         .Times(AnyNumber())
136         .WillRepeatedly(Return(base::TimeDelta::FromMilliseconds(3)));
137     EXPECT_CALL(mock_network_quality_tracker(), GetTransportRTT())
138         .Times(AnyNumber())
139         .WillRepeatedly(Return(base::TimeDelta::FromMilliseconds(4)));
140   }
141 
mock_network_quality_tracker()142   MockNetworkQualityTracker& mock_network_quality_tracker() {
143     return mock_network_quality_tracker_;
144   }
145 
RegisterObservers(page_load_metrics::PageLoadTracker * tracker)146   void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
147     navigation_start_ = tracker->navigation_start();
148     tracker->AddObserver(std::move(observer_));
149   }
150 
151  private:
152   std::unique_ptr<page_load_metrics::PageLoadMetricsObserver> observer_;
153   TestAndroidPageLoadMetricsObserver* observer_ptr_;
154   MockNetworkQualityTracker mock_network_quality_tracker_;
155   base::TimeTicks navigation_start_;
156 };
157 
TEST_F(AndroidPageLoadMetricsObserverTest,NetworkQualityEstimate)158 TEST_F(AndroidPageLoadMetricsObserverTest, NetworkQualityEstimate) {
159   SetNetworkQualityMock();
160   NavigateAndCommit(GURL("https://www.example.com"));
161   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_3G,
162             observer()->reported_connection_type());
163   EXPECT_EQ(3L, observer()->reported_http_rtt_ms());
164   EXPECT_EQ(4L, observer()->reported_transport_rtt_ms());
165 }
166 
TEST_F(AndroidPageLoadMetricsObserverTest,MissingNetworkQualityEstimate)167 TEST_F(AndroidPageLoadMetricsObserverTest, MissingNetworkQualityEstimate) {
168   EXPECT_CALL(mock_network_quality_tracker(), GetEffectiveConnectionType())
169       .Times(AnyNumber())
170       .WillRepeatedly(Return(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN));
171   EXPECT_CALL(mock_network_quality_tracker(), GetHttpRTT())
172       .Times(AnyNumber())
173       .WillRepeatedly(Return(base::TimeDelta()));
174   EXPECT_CALL(mock_network_quality_tracker(), GetTransportRTT())
175       .Times(AnyNumber())
176       .WillRepeatedly(Return(base::TimeDelta()));
177   NavigateAndCommit(GURL("https://www.example.com"));
178   EXPECT_EQ(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
179             observer()->reported_connection_type());
180   EXPECT_EQ(0L, observer()->reported_http_rtt_ms());
181   EXPECT_EQ(0L, observer()->reported_transport_rtt_ms());
182 }
183 
TEST_F(AndroidPageLoadMetricsObserverTest,LoadTimingInfo)184 TEST_F(AndroidPageLoadMetricsObserverTest, LoadTimingInfo) {
185   SetNetworkQualityMock();
186   auto navigation_simulator =
187       content::NavigationSimulator::CreateRendererInitiated(
188           GURL("https://www.example.com"), web_contents()->GetMainFrame());
189   navigation_simulator->Start();
190   int frame_tree_node_id =
191       navigation_simulator->GetNavigationHandle()->GetFrameTreeNodeId();
192   navigation_simulator->Commit();
193 
194   auto load_timing_info = std::make_unique<net::LoadTimingInfo>();
195   const base::TimeTicks kNow = base::TimeTicks::Now();
196   load_timing_info->connect_timing.dns_start = kNow;
197   page_load_metrics::ExtraRequestCompleteInfo info(
198       url::Origin::Create(GURL("https://ignored.com")), net::IPEndPoint(),
199       frame_tree_node_id, false, /* cached */
200       10 * 1024 /* size */, 0 /* original_network_content_length */,
201       nullptr /* data_reduction_proxy_data */,
202       network::mojom::RequestDestination::kDocument, 0,
203       std::move(load_timing_info));
204   tester()->SimulateLoadedResource(info,
205                                    navigation_simulator->GetGlobalRequestID());
206   EXPECT_EQ(kNow.since_origin().InMilliseconds(),
207             observer()->reported_dns_start_ms());
208 }
209 
TEST_F(AndroidPageLoadMetricsObserverTest,LoadEvents)210 TEST_F(AndroidPageLoadMetricsObserverTest, LoadEvents) {
211   SetNetworkQualityMock();
212   page_load_metrics::mojom::PageLoadTiming timing;
213   page_load_metrics::InitPageLoadTimingForTest(&timing);
214   // Note this navigation start does not effect the start that is reported to
215   // us.
216   timing.navigation_start = base::Time::FromDoubleT(1);
217   timing.document_timing->load_event_start =
218       base::TimeDelta::FromMilliseconds(30);
219   timing.parse_timing->parse_start = base::TimeDelta::FromMilliseconds(20);
220   timing.paint_timing->first_contentful_paint =
221       base::TimeDelta::FromMilliseconds(20);
222   PopulateRequiredTimingFields(&timing);
223   NavigateAndCommit(GURL("https://www.example.com"));
224   tester()->SimulateTimingUpdate(timing);
225   EXPECT_EQ(30, observer()->reported_load_event_start_ms());
226   EXPECT_EQ(GetNavigationStartMicroseconds(),
227             observer()->reported_navigation_start_tick_load());
228   EXPECT_EQ(20, observer()->reported_first_contentful_paint_ms());
229   EXPECT_EQ(GetNavigationStartMicroseconds(),
230             observer()->reported_navigation_start_tick_fcp());
231 }
232