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