1// Copyright (c) 2012 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 "base/json/json_string_value_serializer.h"
6#include "base/prefs/pref_service.h"
7#include "chrome/browser/net/predictor.h"
8#include "chrome/browser/profiles/profile.h"
9#include "chrome/browser/ui/browser.h"
10#include "chrome/common/pref_names.h"
11#include "chrome/test/base/in_process_browser_test.h"
12#include "chrome/test/base/ui_test_utils.h"
13#include "net/base/net_errors.h"
14#include "net/dns/host_resolver_proc.h"
15#include "net/dns/mock_host_resolver.h"
16#include "testing/gmock/include/gmock/gmock.h"
17
18using content::BrowserThread;
19using testing::HasSubstr;
20
21namespace {
22
23// Records a history of all hostnames for which resolving has been requested,
24// and immediately fails the resolution requests themselves.
25class HostResolutionRequestRecorder : public net::HostResolverProc {
26 public:
27  HostResolutionRequestRecorder()
28      : HostResolverProc(NULL),
29        is_waiting_for_hostname_(false) {
30  }
31
32  virtual int Resolve(const std::string& host,
33                      net::AddressFamily address_family,
34                      net::HostResolverFlags host_resolver_flags,
35                      net::AddressList* addrlist,
36                      int* os_error) OVERRIDE {
37    BrowserThread::PostTask(
38        BrowserThread::UI,
39        FROM_HERE,
40        base::Bind(&HostResolutionRequestRecorder::AddToHistory,
41                   base::Unretained(this),
42                   host));
43    return net::ERR_NAME_NOT_RESOLVED;
44  }
45
46  bool HasHostBeenRequested(const std::string& hostname) {
47    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
48    return std::find(requested_hostnames_.begin(),
49                     requested_hostnames_.end(),
50                     hostname) != requested_hostnames_.end();
51  }
52
53  void WaitUntilHostHasBeenRequested(const std::string& hostname) {
54    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
55    DCHECK(!is_waiting_for_hostname_);
56    if (HasHostBeenRequested(hostname))
57      return;
58    waiting_for_hostname_ = hostname;
59    is_waiting_for_hostname_ = true;
60    content::RunMessageLoop();
61  }
62
63 private:
64  virtual ~HostResolutionRequestRecorder() {}
65
66  void AddToHistory(const std::string& hostname) {
67    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
68    requested_hostnames_.push_back(hostname);
69    if (is_waiting_for_hostname_ && waiting_for_hostname_ == hostname) {
70      is_waiting_for_hostname_ = false;
71      waiting_for_hostname_.clear();
72      base::MessageLoop::current()->Quit();
73    }
74  }
75
76  // The hostname which WaitUntilHostHasBeenRequested is currently waiting for
77  // to be requested.
78  std::string waiting_for_hostname_;
79
80  // Whether WaitUntilHostHasBeenRequested is waiting for a hostname to be
81  // requested and thus is running a nested message loop.
82  bool is_waiting_for_hostname_;
83
84  // A list of hostnames for which resolution has already been requested. Only
85  // to be accessed from the UI thread.
86  std::vector<std::string> requested_hostnames_;
87
88  DISALLOW_COPY_AND_ASSIGN(HostResolutionRequestRecorder);
89};
90
91}  // namespace
92
93namespace chrome_browser_net {
94
95class PredictorBrowserTest : public InProcessBrowserTest {
96 public:
97  PredictorBrowserTest()
98      : startup_url_("http://host1:1"),
99        referring_url_("http://host2:1"),
100        target_url_("http://host3:1"),
101        host_resolution_request_recorder_(new HostResolutionRequestRecorder) {
102  }
103
104 protected:
105  virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
106    scoped_host_resolver_proc_.reset(new net::ScopedDefaultHostResolverProc(
107        host_resolution_request_recorder_.get()));
108    InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
109  }
110
111  virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
112    InProcessBrowserTest::TearDownInProcessBrowserTestFixture();
113    scoped_host_resolver_proc_.reset();
114  }
115
116  void LearnAboutInitialNavigation(const GURL& url) {
117    Predictor* predictor = browser()->profile()->GetNetworkPredictor();
118    BrowserThread::PostTask(BrowserThread::IO,
119                            FROM_HERE,
120                            base::Bind(&Predictor::LearnAboutInitialNavigation,
121                                       base::Unretained(predictor),
122                                       url));
123    content::RunAllPendingInMessageLoop(BrowserThread::IO);
124  }
125
126  void LearnFromNavigation(const GURL& referring_url, const GURL& target_url) {
127    Predictor* predictor = browser()->profile()->GetNetworkPredictor();
128    BrowserThread::PostTask(BrowserThread::IO,
129                            FROM_HERE,
130                            base::Bind(&Predictor::LearnFromNavigation,
131                                       base::Unretained(predictor),
132                                       referring_url,
133                                       target_url));
134    content::RunAllPendingInMessageLoop(BrowserThread::IO);
135  }
136
137  void PrepareFrameSubresources(const GURL& url) {
138    Predictor* predictor = browser()->profile()->GetNetworkPredictor();
139    predictor->PredictFrameSubresources(url, GURL());
140  }
141
142  void GetListFromPrefsAsString(const char* list_path,
143                                std::string* value_as_string) const {
144    PrefService* prefs = browser()->profile()->GetPrefs();
145    const base::ListValue* list_value = prefs->GetList(list_path);
146    JSONStringValueSerializer serializer(value_as_string);
147    serializer.Serialize(*list_value);
148  }
149
150  void WaitUntilHostHasBeenRequested(const std::string& hostname) {
151    host_resolution_request_recorder_->WaitUntilHostHasBeenRequested(hostname);
152  }
153
154  const GURL startup_url_;
155  const GURL referring_url_;
156  const GURL target_url_;
157
158 private:
159  scoped_refptr<HostResolutionRequestRecorder>
160      host_resolution_request_recorder_;
161  scoped_ptr<net::ScopedDefaultHostResolverProc> scoped_host_resolver_proc_;
162};
163
164IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PRE_ShutdownStartupCycle) {
165  // Prepare state that will be serialized on this shut-down and read on next
166  // start-up.
167  LearnAboutInitialNavigation(startup_url_);
168  LearnFromNavigation(referring_url_, target_url_);
169}
170
171IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ShutdownStartupCycle) {
172  // Make sure that the Preferences file is actually wiped of all DNS prefetch
173  // related data after start-up.
174  std::string cleared_startup_list;
175  std::string cleared_referral_list;
176  GetListFromPrefsAsString(prefs::kDnsPrefetchingStartupList,
177                           &cleared_startup_list);
178  GetListFromPrefsAsString(prefs::kDnsPrefetchingHostReferralList,
179                           &cleared_referral_list);
180
181  EXPECT_THAT(cleared_startup_list, Not(HasSubstr(startup_url_.host())));
182  EXPECT_THAT(cleared_referral_list, Not(HasSubstr(referring_url_.host())));
183  EXPECT_THAT(cleared_referral_list, Not(HasSubstr(target_url_.host())));
184
185  // But also make sure this data has been first loaded into the Predictor, by
186  // inspecting that the Predictor starts making the expected hostname requests.
187  PrepareFrameSubresources(referring_url_);
188  WaitUntilHostHasBeenRequested(startup_url_.host());
189  WaitUntilHostHasBeenRequested(target_url_.host());
190}
191
192}  // namespace chrome_browser_net
193
194