load_timing_browsertest.cc revision 9ab5563a3196760eb381d102cbb2bc0f7abc6a50
1// Copyright (c) 2013 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 <string>
6
7#include "base/basictypes.h"
8#include "base/bind.h"
9#include "base/compiler_specific.h"
10#include "base/files/file_path.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/memory/weak_ptr.h"
13#include "base/message_loop/message_loop.h"
14#include "base/path_service.h"
15#include "base/strings/stringprintf.h"
16#include "chrome/browser/ui/browser.h"
17#include "chrome/browser/ui/tabs/tab_strip_model.h"
18#include "chrome/common/chrome_paths.h"
19#include "chrome/test/base/in_process_browser_test.h"
20#include "chrome/test/base/ui_test_utils.h"
21#include "content/public/browser/browser_thread.h"
22#include "content/public/test/browser_test_utils.h"
23#include "net/base/load_timing_info.h"
24#include "net/test/spawned_test_server/spawned_test_server.h"
25#include "net/url_request/url_request_file_job.h"
26#include "net/url_request/url_request_filter.h"
27#include "net/url_request/url_request_job_factory.h"
28#include "url/gurl.h"
29
30// This file tests that net::LoadTimingInfo is correctly hooked up to the
31// NavigationTiming API.  It depends on behavior in a large number of files
32// spread across multiple projects, so is somewhat arbitrarily put in
33// chrome/browser/net.
34
35using content::BrowserThread;
36
37namespace {
38
39const char kTestDomain[] = "test.com";
40const char kTestUrl[] = "http://test.com/";
41
42// Relative times need to be used because:
43// 1)  ExecuteScriptAndExtractInt does not support 64-bit integers.
44// 2)  Times for tests are set before the request has been started, but need to
45//     be set relative to the start time.
46//
47// Since some tests need to test negative time deltas (preconnected sockets)
48// and others need to test NULL times (events that don't apply), this class has
49// to be able to handle all cases:  positive deltas, negative deltas, no
50// delta, and null times.
51class RelativeTime {
52 public:
53  // Constructor for null RelativeTimes.
54  RelativeTime() : is_null_(true) {
55  }
56
57  // Constructor for non-null RelativeTimes.
58  explicit RelativeTime(int delta_ms)
59      : is_null_(false),
60        delta_(base::TimeDelta::FromMilliseconds(delta_ms)) {
61  }
62
63  // Given a base time, returns the TimeTicks |this| identifies.
64  base::TimeTicks ToTimeTicks(base::TimeTicks base_time) const {
65    if (is_null_)
66      return base::TimeTicks();
67    return base_time + delta_;
68  }
69
70  bool is_null() const { return is_null_; }
71
72  base::TimeDelta GetDelta() const {
73    // This allows tests to compare times that shouldn't be null without
74    // explicitly null-testing them all first.
75    EXPECT_FALSE(is_null_);
76    return delta_;
77  }
78
79 private:
80  bool is_null_;
81
82  // Must be 0 when |is_null| is true.
83  base::TimeDelta delta_;
84
85  // This class is copyable and assignable.
86};
87
88// Structure used for both setting the LoadTimingInfo used by mock requests
89// and for times retrieved from the renderer process.
90//
91// Times used for mock requests are all expressed as TimeDeltas relative to
92// when the Job starts.  Null RelativeTimes correspond to null TimeTicks().
93//
94// Times read from the renderer are expressed relative to fetchStart (Which is
95// not the same as request_start).  Null RelativeTimes correspond to times that
96// either cannot be retrieved (proxy times, send end) or times that are 0 (SSL
97// time when no new SSL connection was established).
98struct TimingDeltas {
99  RelativeTime proxy_resolve_start;
100  RelativeTime proxy_resolve_end;
101  RelativeTime dns_start;
102  RelativeTime dns_end;
103  RelativeTime connect_start;
104  RelativeTime ssl_start;
105  RelativeTime connect_end;
106  RelativeTime send_start;
107  RelativeTime send_end;
108
109  // Must be non-negative and greater than all other times.  May only be null if
110  // all other times are as well.
111  RelativeTime receive_headers_end;
112};
113
114// Mock UrlRequestJob that returns the contents of a specified file and
115// provides the specified load timing information when queried.
116class MockUrlRequestJobWithTiming : public net::URLRequestFileJob {
117 public:
118  MockUrlRequestJobWithTiming(net::URLRequest* request,
119                              net::NetworkDelegate* network_delegate,
120                              const base::FilePath& path,
121                              const TimingDeltas& load_timing_deltas)
122      : net::URLRequestFileJob(request, network_delegate, path),
123        load_timing_deltas_(load_timing_deltas),
124        weak_factory_(this) {
125  }
126
127  // net::URLRequestFileJob implementation:
128  virtual void Start() OVERRIDE {
129    base::TimeDelta time_to_wait;
130    start_time_ = base::TimeTicks::Now();
131    if (!load_timing_deltas_.receive_headers_end.is_null()) {
132      // Need to delay starting until the largest of the times has elapsed.
133      // Wait a little longer than necessary, to be on the safe side.
134      time_to_wait = load_timing_deltas_.receive_headers_end.GetDelta() +
135                         base::TimeDelta::FromMilliseconds(100);
136    }
137
138    base::MessageLoop::current()->PostDelayedTask(
139        FROM_HERE,
140        base::Bind(&MockUrlRequestJobWithTiming::DelayedStart,
141                   weak_factory_.GetWeakPtr()),
142        time_to_wait);
143  }
144
145  virtual void GetLoadTimingInfo(
146      net::LoadTimingInfo* load_timing_info) const OVERRIDE {
147    // Make sure enough time has elapsed since start was called.  If this
148    // fails, the test fixture itself is flaky.
149    if (!load_timing_deltas_.receive_headers_end.is_null()) {
150      EXPECT_LE(
151          start_time_ + load_timing_deltas_.receive_headers_end.GetDelta(),
152          base::TimeTicks::Now());
153    }
154
155    // If there are no connect times, but there is a receive headers end time,
156    // then assume the socket is reused.  This shouldn't affect the load timing
157    // information the test checks, just done for completeness.
158    load_timing_info->socket_reused = false;
159    if (load_timing_deltas_.connect_start.is_null() &&
160        !load_timing_deltas_.receive_headers_end.is_null()) {
161      load_timing_info->socket_reused = true;
162    }
163
164    load_timing_info->proxy_resolve_start =
165        load_timing_deltas_.proxy_resolve_start.ToTimeTicks(start_time_);
166    load_timing_info->proxy_resolve_end =
167        load_timing_deltas_.proxy_resolve_end.ToTimeTicks(start_time_);
168
169    load_timing_info->connect_timing.dns_start =
170        load_timing_deltas_.dns_start.ToTimeTicks(start_time_);
171    load_timing_info->connect_timing.dns_end =
172        load_timing_deltas_.dns_end.ToTimeTicks(start_time_);
173    load_timing_info->connect_timing.connect_start =
174        load_timing_deltas_.connect_start.ToTimeTicks(start_time_);
175    load_timing_info->connect_timing.ssl_start =
176        load_timing_deltas_.ssl_start.ToTimeTicks(start_time_);
177    load_timing_info->connect_timing.connect_end =
178        load_timing_deltas_.connect_end.ToTimeTicks(start_time_);
179
180    // If there's an SSL start time, use connect end as the SSL end time.
181    // The NavigationTiming API does not have a corresponding field, and there's
182    // no need to test the case when the values are both non-NULL and different.
183    if (!load_timing_deltas_.ssl_start.is_null()) {
184      load_timing_info->connect_timing.ssl_end =
185          load_timing_info->connect_timing.connect_end;
186    }
187
188    load_timing_info->send_start =
189        load_timing_deltas_.send_start.ToTimeTicks(start_time_);
190    load_timing_info->send_end=
191        load_timing_deltas_.send_end.ToTimeTicks(start_time_);
192    load_timing_info->receive_headers_end =
193        load_timing_deltas_.receive_headers_end.ToTimeTicks(start_time_);
194  }
195
196 private:
197  // Parent class is reference counted, so need to have a private destructor.
198  virtual ~MockUrlRequestJobWithTiming() {}
199
200  void DelayedStart() {
201    net::URLRequestFileJob::Start();
202  }
203
204  // Load times to use, relative to |start_time_|.
205  const TimingDeltas load_timing_deltas_;
206  base::TimeTicks start_time_;
207
208  base::WeakPtrFactory<MockUrlRequestJobWithTiming> weak_factory_;
209
210  DISALLOW_COPY_AND_ASSIGN(MockUrlRequestJobWithTiming);
211};
212
213// A protocol handler that returns mock URLRequestJobs that return the specified
214// file with the given timings.  Constructed on the UI thread, but after that,
215// lives and is destroyed on the IO thread.
216class TestProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler {
217 public:
218  TestProtocolHandler(const base::FilePath& path,
219                      const TimingDeltas& load_timing_deltas)
220      : path_(path), load_timing_deltas_(load_timing_deltas) {
221    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
222  }
223
224  virtual ~TestProtocolHandler() {
225    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
226  }
227
228  // Registers |this| with the URLRequestFilter, which takes ownership of it.
229  void Register() {
230    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
231    net::URLRequestFilter::GetInstance()->AddHostnameProtocolHandler(
232        "http", kTestDomain,
233        scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>(this));
234  }
235
236  // Unregisters |this| with the URLRequestFilter, which should then delete
237  // |this|.
238  void Unregister() {
239    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
240    net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(
241        "http", kTestDomain);
242  }
243
244  // net::URLRequestJobFactory::ProtocolHandler implementation:
245  virtual net::URLRequestJob* MaybeCreateJob(
246      net::URLRequest* request,
247      net::NetworkDelegate* network_delegate) const OVERRIDE {
248    EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
249
250    return new MockUrlRequestJobWithTiming(request, network_delegate, path_,
251                                           load_timing_deltas_);
252  }
253
254 private:
255  // Path of the file to use as the response body.
256  const base::FilePath path_;
257
258  // Load times for each request to use, relative to when the Job starts.
259  const TimingDeltas load_timing_deltas_;
260
261  DISALLOW_COPY_AND_ASSIGN(TestProtocolHandler);
262};
263
264class LoadTimingBrowserTest : public InProcessBrowserTest {
265 public:
266  LoadTimingBrowserTest() {
267  }
268
269  virtual ~LoadTimingBrowserTest() {
270  }
271
272  // Navigates to |url| and writes the resulting navigation timings to
273  // |navigation_deltas|.
274  void RunTestWithUrl(const GURL& url, TimingDeltas* navigation_deltas) {
275    ui_test_utils::NavigateToURL(browser(), url);
276    GetResultDeltas(navigation_deltas);
277  }
278
279  // Navigates to a url that returns the timings indicated by
280  // |load_timing_deltas| and writes the resulting navigation timings to
281  // |navigation_deltas|.  Uses a generic test page.
282  void RunTest(const TimingDeltas& load_timing_deltas,
283               TimingDeltas* navigation_deltas) {
284    // None of the tests care about the contents of the test page.  Just do
285    // this here because PathService has thread restrictions on some platforms.
286    base::FilePath path;
287    PathService::Get(chrome::DIR_TEST_DATA, &path);
288    path = path.AppendASCII("title1.html");
289
290    // Create and register protocol handler.
291    TestProtocolHandler* protocol_handler =
292        new TestProtocolHandler(path, load_timing_deltas);
293    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
294                            base::Bind(&TestProtocolHandler::Register,
295                                       base::Unretained(protocol_handler)));
296
297    // Navigate to the page.
298    RunTestWithUrl(GURL(kTestUrl), navigation_deltas);
299
300    // Once navigation is complete, unregister the protocol handler.
301    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
302                            base::Bind(&TestProtocolHandler::Unregister,
303                                        base::Unretained(protocol_handler)));
304  }
305
306 private:
307  // Reads applicable times from performance.timing and writes them to
308  // |navigation_deltas|.  Proxy times and send end cannot be read from the
309  // Navigation Timing API, so those are all left as null.
310  void GetResultDeltas(TimingDeltas* navigation_deltas) {
311    *navigation_deltas = TimingDeltas();
312
313    navigation_deltas->dns_start = GetResultDelta("domainLookupStart");
314    navigation_deltas->dns_end = GetResultDelta("domainLookupEnd");
315    navigation_deltas->connect_start = GetResultDelta("connectStart");
316    navigation_deltas->connect_end = GetResultDelta("connectEnd");
317    navigation_deltas->send_start = GetResultDelta("requestStart");
318    navigation_deltas->receive_headers_end = GetResultDelta("responseStart");
319
320    // Unlike the above times, secureConnectionStart will be zero when not
321    // applicable.  In that case, leave ssl_start as null.
322    bool ssl_start_zero = false;
323    ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
324                    browser()->tab_strip_model()->GetActiveWebContents(),
325                    "window.domAutomationController.send("
326                        "performance.timing.secureConnectionStart == 0);",
327                    &ssl_start_zero));
328    if (!ssl_start_zero)
329      navigation_deltas->ssl_start = GetResultDelta("secureConnectionStart");
330
331    // Simple sanity checks.  Make sure times that correspond to LoadTimingInfo
332    // occur between fetchStart and loadEventEnd.  Relationships between
333    // intervening times are handled by the test bodies.
334
335    RelativeTime fetch_start = GetResultDelta("fetchStart");
336    // While the input dns_start is sometimes null, when read from the
337    // NavigationTiming API, it's always non-null.
338    EXPECT_LE(fetch_start.GetDelta(), navigation_deltas->dns_start.GetDelta());
339
340    RelativeTime load_event_end = GetResultDelta("loadEventEnd");
341    EXPECT_LE(navigation_deltas->receive_headers_end.GetDelta(),
342              load_event_end.GetDelta());
343  }
344
345  // Returns the time between performance.timing.fetchStart and the time with
346  // the specified name.  This time must be non-negative.
347  RelativeTime GetResultDelta(const std::string& name) {
348    int time_ms = 0;
349    std::string command(base::StringPrintf(
350        "window.domAutomationController.send("
351            "performance.timing.%s - performance.timing.fetchStart);",
352        name.c_str()));
353    EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
354                    browser()->tab_strip_model()->GetActiveWebContents(),
355                    command.c_str(),
356                    &time_ms));
357
358    // Basic sanity check.
359    EXPECT_GE(time_ms, 0);
360
361    return RelativeTime(time_ms);
362  }
363};
364
365// Test case when no times are given, except the request start times.  This
366// happens with FTP, cached responses, responses handled by something other than
367// the network stack, RedirectJobs, HSTs, etc.
368IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, NoTimes) {
369  TimingDeltas load_timing_deltas;
370  TimingDeltas navigation_deltas;
371  RunTest(load_timing_deltas, &navigation_deltas);
372
373  // When there are no times, all read times should be the same as fetchStart,
374  // except SSL start, which should be 0.
375  EXPECT_EQ(base::TimeDelta(), navigation_deltas.dns_start.GetDelta());
376  EXPECT_EQ(base::TimeDelta(), navigation_deltas.dns_end.GetDelta());
377  EXPECT_EQ(base::TimeDelta(), navigation_deltas.connect_start.GetDelta());
378  EXPECT_EQ(base::TimeDelta(), navigation_deltas.connect_end.GetDelta());
379  EXPECT_EQ(base::TimeDelta(), navigation_deltas.send_start.GetDelta());
380  EXPECT_EQ(base::TimeDelta(),
381            navigation_deltas.receive_headers_end.GetDelta());
382
383  EXPECT_TRUE(navigation_deltas.ssl_start.is_null());
384}
385
386// Standard case - new socket, no PAC, no preconnect, no SSL.
387IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, Basic) {
388  TimingDeltas load_timing_deltas;
389  load_timing_deltas.dns_start = RelativeTime(0);
390  load_timing_deltas.dns_end = RelativeTime(100);
391  load_timing_deltas.connect_start = RelativeTime(200);
392  load_timing_deltas.connect_end = RelativeTime(300);
393  load_timing_deltas.send_start = RelativeTime(400);
394  load_timing_deltas.send_end = RelativeTime(500);
395  load_timing_deltas.receive_headers_end = RelativeTime(600);
396
397  TimingDeltas navigation_deltas;
398  RunTest(load_timing_deltas, &navigation_deltas);
399
400  // Due to potential roundoff issues, never check exact differences.
401  EXPECT_LT(navigation_deltas.dns_start.GetDelta(),
402            navigation_deltas.dns_end.GetDelta());
403  EXPECT_LT(navigation_deltas.dns_end.GetDelta(),
404            navigation_deltas.connect_start.GetDelta());
405  EXPECT_LT(navigation_deltas.connect_start.GetDelta(),
406            navigation_deltas.connect_end.GetDelta());
407  EXPECT_LT(navigation_deltas.connect_end.GetDelta(),
408            navigation_deltas.send_start.GetDelta());
409  EXPECT_LT(navigation_deltas.send_start.GetDelta(),
410            navigation_deltas.receive_headers_end.GetDelta());
411
412  EXPECT_TRUE(navigation_deltas.ssl_start.is_null());
413}
414
415// Basic SSL case.
416IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, Ssl) {
417  TimingDeltas load_timing_deltas;
418  load_timing_deltas.dns_start = RelativeTime(0);
419  load_timing_deltas.dns_end = RelativeTime(100);
420  load_timing_deltas.connect_start = RelativeTime(200);
421  load_timing_deltas.ssl_start = RelativeTime(300);
422  load_timing_deltas.connect_end = RelativeTime(400);
423  load_timing_deltas.send_start = RelativeTime(500);
424  load_timing_deltas.send_end = RelativeTime(600);
425  load_timing_deltas.receive_headers_end = RelativeTime(700);
426
427  TimingDeltas navigation_deltas;
428  RunTest(load_timing_deltas, &navigation_deltas);
429
430  // Due to potential roundoff issues, never check exact differences.
431  EXPECT_LT(navigation_deltas.dns_start.GetDelta(),
432            navigation_deltas.dns_end.GetDelta());
433  EXPECT_LT(navigation_deltas.dns_end.GetDelta(),
434            navigation_deltas.connect_start.GetDelta());
435  EXPECT_LT(navigation_deltas.connect_start.GetDelta(),
436            navigation_deltas.ssl_start.GetDelta());
437  EXPECT_LT(navigation_deltas.ssl_start.GetDelta(),
438            navigation_deltas.connect_end.GetDelta());
439  EXPECT_LT(navigation_deltas.connect_end.GetDelta(),
440            navigation_deltas.send_start.GetDelta());
441  EXPECT_LT(navigation_deltas.send_start.GetDelta(),
442            navigation_deltas.receive_headers_end.GetDelta());
443}
444
445// All times are the same.
446IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, EverythingAtOnce) {
447  TimingDeltas load_timing_deltas;
448  load_timing_deltas.dns_start = RelativeTime(100);
449  load_timing_deltas.dns_end = RelativeTime(100);
450  load_timing_deltas.connect_start = RelativeTime(100);
451  load_timing_deltas.ssl_start = RelativeTime(100);
452  load_timing_deltas.connect_end = RelativeTime(100);
453  load_timing_deltas.send_start = RelativeTime(100);
454  load_timing_deltas.send_end = RelativeTime(100);
455  load_timing_deltas.receive_headers_end = RelativeTime(100);
456
457  TimingDeltas navigation_deltas;
458  RunTest(load_timing_deltas, &navigation_deltas);
459
460  EXPECT_EQ(navigation_deltas.dns_start.GetDelta(),
461            navigation_deltas.dns_end.GetDelta());
462  EXPECT_EQ(navigation_deltas.dns_end.GetDelta(),
463            navigation_deltas.connect_start.GetDelta());
464  EXPECT_EQ(navigation_deltas.connect_start.GetDelta(),
465            navigation_deltas.ssl_start.GetDelta());
466  EXPECT_EQ(navigation_deltas.ssl_start.GetDelta(),
467            navigation_deltas.connect_end.GetDelta());
468  EXPECT_EQ(navigation_deltas.connect_end.GetDelta(),
469            navigation_deltas.send_start.GetDelta());
470  EXPECT_EQ(navigation_deltas.send_start.GetDelta(),
471            navigation_deltas.receive_headers_end.GetDelta());
472}
473
474// Reuse case.
475IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, ReuseSocket) {
476  TimingDeltas load_timing_deltas;
477  load_timing_deltas.send_start = RelativeTime(0);
478  load_timing_deltas.send_end = RelativeTime(100);
479  load_timing_deltas.receive_headers_end = RelativeTime(200);
480
481  TimingDeltas navigation_deltas;
482  RunTest(load_timing_deltas, &navigation_deltas);
483
484  // Connect times should all be the same as fetchStart.
485  EXPECT_EQ(base::TimeDelta(), navigation_deltas.dns_start.GetDelta());
486  EXPECT_EQ(base::TimeDelta(), navigation_deltas.dns_end.GetDelta());
487  EXPECT_EQ(base::TimeDelta(), navigation_deltas.connect_start.GetDelta());
488  EXPECT_EQ(base::TimeDelta(), navigation_deltas.connect_end.GetDelta());
489
490  // Connect end may be less than send start, since connect end defaults to
491  // fetchStart, which is often less than request_start.
492  EXPECT_LE(navigation_deltas.connect_end.GetDelta(),
493            navigation_deltas.send_start.GetDelta());
494
495  EXPECT_LT(navigation_deltas.send_start.GetDelta(),
496            navigation_deltas.receive_headers_end.GetDelta());
497
498  EXPECT_TRUE(navigation_deltas.ssl_start.is_null());
499}
500
501// Preconnect case.  Connect times are all before the request was started.
502IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, Preconnect) {
503  TimingDeltas load_timing_deltas;
504  load_timing_deltas.dns_start = RelativeTime(-1000300);
505  load_timing_deltas.dns_end = RelativeTime(-1000200);
506  load_timing_deltas.connect_start = RelativeTime(-1000100);
507  load_timing_deltas.connect_end = RelativeTime(-1000000);
508  load_timing_deltas.send_start = RelativeTime(0);
509  load_timing_deltas.send_end = RelativeTime(100);
510  load_timing_deltas.receive_headers_end = RelativeTime(200);
511
512  TimingDeltas navigation_deltas;
513  RunTest(load_timing_deltas, &navigation_deltas);
514
515  // Connect times should all be the same as request_start.
516  EXPECT_EQ(navigation_deltas.dns_start.GetDelta(),
517            navigation_deltas.dns_end.GetDelta());
518  EXPECT_EQ(navigation_deltas.dns_start.GetDelta(),
519            navigation_deltas.connect_start.GetDelta());
520  EXPECT_EQ(navigation_deltas.dns_start.GetDelta(),
521            navigation_deltas.connect_end.GetDelta());
522
523  EXPECT_LE(navigation_deltas.dns_start.GetDelta(),
524            navigation_deltas.send_start.GetDelta());
525
526  EXPECT_LT(navigation_deltas.send_start.GetDelta(),
527            navigation_deltas.receive_headers_end.GetDelta());
528  EXPECT_LT(navigation_deltas.send_start.GetDelta(),
529            navigation_deltas.receive_headers_end.GetDelta());
530
531  EXPECT_TRUE(navigation_deltas.ssl_start.is_null());
532}
533
534// Preconnect case with a proxy.  Connect times are all before the proxy lookup
535// finished (Or at the same time).
536IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, PreconnectProxySsl) {
537  TimingDeltas load_timing_deltas;
538  load_timing_deltas.proxy_resolve_start = RelativeTime(0);
539  load_timing_deltas.proxy_resolve_end = RelativeTime(100);
540  load_timing_deltas.dns_start = RelativeTime(-3000000);
541  load_timing_deltas.dns_end = RelativeTime(-2000000);
542  load_timing_deltas.connect_start = RelativeTime(-1000000);
543  load_timing_deltas.ssl_start = RelativeTime(0);
544  load_timing_deltas.connect_end = RelativeTime(100);
545  load_timing_deltas.send_start = RelativeTime(100);
546  load_timing_deltas.send_end = RelativeTime(200);
547  load_timing_deltas.receive_headers_end = RelativeTime(300);
548
549  TimingDeltas navigation_deltas;
550  RunTest(load_timing_deltas, &navigation_deltas);
551
552  // Connect times should all be the same as proxy_end, which is also the
553  // same as send_start.
554  EXPECT_EQ(navigation_deltas.dns_start.GetDelta(),
555            navigation_deltas.dns_end.GetDelta());
556  EXPECT_EQ(navigation_deltas.dns_start.GetDelta(),
557            navigation_deltas.connect_start.GetDelta());
558  EXPECT_EQ(navigation_deltas.dns_start.GetDelta(),
559            navigation_deltas.ssl_start.GetDelta());
560  EXPECT_EQ(navigation_deltas.dns_start.GetDelta(),
561            navigation_deltas.connect_end.GetDelta());
562  EXPECT_EQ(navigation_deltas.dns_start.GetDelta(),
563            navigation_deltas.send_start.GetDelta());
564
565  EXPECT_LT(navigation_deltas.send_start.GetDelta(),
566            navigation_deltas.receive_headers_end.GetDelta());
567  EXPECT_LT(navigation_deltas.send_start.GetDelta(),
568            navigation_deltas.receive_headers_end.GetDelta());
569}
570
571// Integration test with a real network response.
572IN_PROC_BROWSER_TEST_F(LoadTimingBrowserTest, Integration) {
573  ASSERT_TRUE(test_server()->Start());
574  TimingDeltas navigation_deltas;
575  RunTestWithUrl(test_server()->GetURL("chunked?waitBeforeHeaders=100"),
576                 &navigation_deltas);
577
578  // Due to potential roundoff issues, never check exact differences.
579  EXPECT_LE(navigation_deltas.dns_start.GetDelta(),
580            navigation_deltas.dns_end.GetDelta());
581  EXPECT_LE(navigation_deltas.dns_end.GetDelta(),
582            navigation_deltas.connect_start.GetDelta());
583  EXPECT_LE(navigation_deltas.connect_start.GetDelta(),
584            navigation_deltas.connect_end.GetDelta());
585  EXPECT_LE(navigation_deltas.connect_end.GetDelta(),
586            navigation_deltas.send_start.GetDelta());
587  // The only times that are guaranteed to be distinct are send_start and
588  // received_headers_end.
589  EXPECT_LT(navigation_deltas.send_start.GetDelta(),
590            navigation_deltas.receive_headers_end.GetDelta());
591
592  EXPECT_TRUE(navigation_deltas.ssl_start.is_null());
593}
594
595}  // namespace
596