resource_prefetcher_unittest.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/memory/ref_counted.h"
6#include "base/message_loop.h"
7#include "base/memory/scoped_ptr.h"
8#include "chrome/browser/predictors/resource_prefetcher.h"
9#include "chrome/browser/predictors/resource_prefetcher_manager.h"
10#include "chrome/test/base/testing_profile.h"
11#include "content/public/test/test_browser_thread.h"
12#include "net/url_request/url_request.h"
13#include "net/url_request/url_request_test_util.h"
14#include "testing/gtest/include/gtest/gtest.h"
15#include "testing/gmock/include/gmock/gmock.h"
16
17using testing::Eq;
18using testing::Property;
19
20namespace predictors {
21
22// Wrapper over the ResourcePrefetcher that stubs out the StartURLRequest call
23// since we do not want to do network fetches in this unittest.
24class TestResourcePrefetcher : public ResourcePrefetcher {
25 public:
26  TestResourcePrefetcher(ResourcePrefetcher::Delegate* delegate,
27                         const ResourcePrefetchPredictorConfig& config,
28                         const NavigationID& navigation_id,
29                         PrefetchKeyType key_type,
30                         scoped_ptr<RequestVector> requests)
31      : ResourcePrefetcher(delegate, config, navigation_id,
32                           key_type, requests.Pass()) { }
33
34  virtual ~TestResourcePrefetcher() { }
35
36  MOCK_METHOD1(StartURLRequest, void(net::URLRequest* request));
37
38  void ReadFullResponse(net::URLRequest* request) OVERRIDE {
39    FinishRequest(request, Request::PREFETCH_STATUS_FROM_CACHE);
40  }
41
42 private:
43  DISALLOW_COPY_AND_ASSIGN(TestResourcePrefetcher);
44};
45
46
47// Delegate for ResourcePrefetcher.
48class TestResourcePrefetcherDelegate : public ResourcePrefetcher::Delegate {
49 public:
50  explicit TestResourcePrefetcherDelegate(MessageLoop* loop)
51      : request_context_getter_(new net::TestURLRequestContextGetter(
52          loop->message_loop_proxy())) { }
53  ~TestResourcePrefetcherDelegate() { }
54
55  virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE {
56    return request_context_getter_->GetURLRequestContext();
57  }
58
59  MOCK_METHOD2(ResourcePrefetcherFinished,
60               void(ResourcePrefetcher* prefetcher,
61                    ResourcePrefetcher::RequestVector* requests));
62
63 private:
64  scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
65
66  DISALLOW_COPY_AND_ASSIGN(TestResourcePrefetcherDelegate);
67};
68
69
70// The following unittest tests most of the ResourcePrefetcher except for:
71// 1. Call to ReadFullResponse. There does not seem to be a good way to test the
72//    function in a unittest, and probably requires a browser_test.
73// 2. Setting of the Prefetch status for cache vs non cache.
74class ResourcePrefetcherTest : public testing::Test {
75 public:
76  ResourcePrefetcherTest();
77  virtual ~ResourcePrefetcherTest();
78
79 protected:
80  typedef ResourcePrefetcher::Request Request;
81
82  void AddStartUrlRequestExpectation(const std::string& url) {
83    EXPECT_CALL(*prefetcher_,
84                StartURLRequest(Property(&net::URLRequest::original_url,
85                                         Eq(GURL(url)))));
86  }
87
88  void CheckPrefetcherState(size_t inflight, size_t queue, size_t host) {
89    EXPECT_EQ(prefetcher_->inflight_requests_.size(), inflight);
90    EXPECT_EQ(prefetcher_->request_queue_.size(), queue);
91    EXPECT_EQ(prefetcher_->host_inflight_counts_.size(), host);
92  }
93
94  net::URLRequest* GetInFlightRequest(const std::string& url_str) {
95    GURL url(url_str);
96
97    for (std::list<Request*>::const_iterator it =
98         prefetcher_->request_queue_.begin();
99         it != prefetcher_->request_queue_.end(); ++it) {
100      EXPECT_NE((*it)->resource_url, url);
101    }
102    for (std::map<net::URLRequest*, Request*>::const_iterator it =
103         prefetcher_->inflight_requests_.begin();
104         it != prefetcher_->inflight_requests_.end(); ++it) {
105      if (it->first->original_url() == url)
106        return it->first;
107    }
108    EXPECT_TRUE(false) << "Infligh request not found: " << url_str;
109    return NULL;
110  }
111
112
113  void OnReceivedRedirect(const std::string& url) {
114    prefetcher_->OnReceivedRedirect(GetInFlightRequest(url), GURL(""), NULL);
115  }
116  void OnAuthRequired(const std::string& url) {
117    prefetcher_->OnAuthRequired(GetInFlightRequest(url), NULL);
118  }
119  void OnCertificateRequested(const std::string& url) {
120    prefetcher_->OnCertificateRequested(GetInFlightRequest(url), NULL);
121  }
122  void OnSSLCertificateError(const std::string& url) {
123    prefetcher_->OnSSLCertificateError(GetInFlightRequest(url),
124                                       net::SSLInfo(), false);
125  }
126  void OnResponse(const std::string& url) {
127    prefetcher_->OnResponseStarted(GetInFlightRequest(url));
128  }
129
130  MessageLoop loop_;
131  content::TestBrowserThread io_thread_;
132  ResourcePrefetchPredictorConfig config_;
133  TestResourcePrefetcherDelegate prefetcher_delegate_;
134  scoped_ptr<TestResourcePrefetcher> prefetcher_;
135
136 private:
137  DISALLOW_COPY_AND_ASSIGN(ResourcePrefetcherTest);
138};
139
140ResourcePrefetcherTest::ResourcePrefetcherTest()
141    : loop_(MessageLoop::TYPE_IO),
142      io_thread_(content::BrowserThread::IO, &loop_),
143      prefetcher_delegate_(&loop_) {
144  config_.max_prefetches_inflight_per_navigation = 5;
145  config_.max_prefetches_inflight_per_host_per_navigation = 2;
146}
147
148ResourcePrefetcherTest::~ResourcePrefetcherTest() {
149}
150
151TEST_F(ResourcePrefetcherTest, TestPrefetcherFinishes) {
152  scoped_ptr<ResourcePrefetcher::RequestVector> requests(
153      new ResourcePrefetcher::RequestVector);
154  requests->push_back(new ResourcePrefetcher::Request(GURL(
155      "http://www.google.com/resource1.html")));
156  requests->push_back(new ResourcePrefetcher::Request(GURL(
157      "http://www.google.com/resource2.png")));
158  requests->push_back(new ResourcePrefetcher::Request(GURL(
159      "http://yahoo.com/resource1.png")));
160  requests->push_back(new ResourcePrefetcher::Request(GURL(
161      "http://yahoo.com/resource2.png")));
162  requests->push_back(new ResourcePrefetcher::Request(GURL(
163      "http://yahoo.com/resource3.png")));
164  requests->push_back(new ResourcePrefetcher::Request(GURL(
165      "http://m.google.com/resource1.jpg")));
166  requests->push_back(new ResourcePrefetcher::Request(GURL(
167      "http://www.google.com/resource3.html")));
168  requests->push_back(new ResourcePrefetcher::Request(GURL(
169      "http://m.google.com/resource2.html")));
170  requests->push_back(new ResourcePrefetcher::Request(GURL(
171      "http://m.google.com/resource3.css")));
172  requests->push_back(new ResourcePrefetcher::Request(GURL(
173      "http://m.google.com/resource4.png")));
174  requests->push_back(new ResourcePrefetcher::Request(GURL(
175      "http://yahoo.com/resource4.png")));
176  requests->push_back(new ResourcePrefetcher::Request(GURL(
177      "http://yahoo.com/resource5.png")));
178
179  NavigationID navigation_id;
180  navigation_id.render_process_id = 1;
181  navigation_id.render_view_id = 2;
182  navigation_id.main_frame_url = GURL("http://www.google.com");
183
184  // Needed later for comparison.
185  ResourcePrefetcher::RequestVector* requests_ptr = requests.get();
186
187  prefetcher_.reset(new TestResourcePrefetcher(&prefetcher_delegate_,
188                                               config_,
189                                               navigation_id,
190                                               PREFETCH_KEY_TYPE_URL,
191                                               requests.Pass()));
192
193  // Starting the prefetcher maxes out the number of possible requests.
194  AddStartUrlRequestExpectation("http://www.google.com/resource1.html");
195  AddStartUrlRequestExpectation("http://www.google.com/resource2.png");
196  AddStartUrlRequestExpectation("http://yahoo.com/resource1.png");
197  AddStartUrlRequestExpectation("http://yahoo.com/resource2.png");
198  AddStartUrlRequestExpectation("http://m.google.com/resource1.jpg");
199
200  prefetcher_->Start();
201  CheckPrefetcherState(5, 7, 3);
202
203  AddStartUrlRequestExpectation("http://m.google.com/resource2.html");
204  OnResponse("http://m.google.com/resource1.jpg");
205  CheckPrefetcherState(5, 6, 3);
206
207  AddStartUrlRequestExpectation("http://www.google.com/resource3.html");
208  OnSSLCertificateError("http://www.google.com/resource1.html");
209  CheckPrefetcherState(5, 5, 3);
210
211  AddStartUrlRequestExpectation("http://m.google.com/resource3.css");
212  OnResponse("http://m.google.com/resource2.html");
213  CheckPrefetcherState(5, 4, 3);
214
215  AddStartUrlRequestExpectation("http://m.google.com/resource4.png");
216  OnReceivedRedirect("http://www.google.com/resource3.html");
217  CheckPrefetcherState(5, 3, 3);
218
219  OnResponse("http://www.google.com/resource2.png");
220  CheckPrefetcherState(4, 3, 2);
221
222  AddStartUrlRequestExpectation("http://yahoo.com/resource3.png");
223  OnReceivedRedirect("http://yahoo.com/resource2.png");
224  CheckPrefetcherState(4, 2, 2);
225
226  AddStartUrlRequestExpectation("http://yahoo.com/resource4.png");
227  OnResponse("http://yahoo.com/resource1.png");
228  CheckPrefetcherState(4, 1, 2);
229
230  AddStartUrlRequestExpectation("http://yahoo.com/resource5.png");
231  OnResponse("http://yahoo.com/resource4.png");
232  CheckPrefetcherState(4, 0, 2);
233
234  OnResponse("http://yahoo.com/resource5.png");
235  CheckPrefetcherState(3, 0, 2);
236
237  OnCertificateRequested("http://m.google.com/resource4.png");
238  CheckPrefetcherState(2, 0, 2);
239
240  OnAuthRequired("http://m.google.com/resource3.css");
241  CheckPrefetcherState(1, 0, 1);
242
243  // Expect the final call.
244  EXPECT_CALL(prefetcher_delegate_,
245              ResourcePrefetcherFinished(Eq(prefetcher_.get()),
246                                         Eq(requests_ptr)));
247
248  OnResponse("http://yahoo.com/resource3.png");
249  CheckPrefetcherState(0, 0, 0);
250
251  // Check the prefetch status.
252  EXPECT_EQ((*requests_ptr)[0]->prefetch_status,
253            Request::PREFETCH_STATUS_CERT_ERROR);
254  EXPECT_EQ((*requests_ptr)[1]->prefetch_status,
255            Request::PREFETCH_STATUS_FROM_CACHE);
256  EXPECT_EQ((*requests_ptr)[2]->prefetch_status,
257            Request::PREFETCH_STATUS_FROM_CACHE);
258  EXPECT_EQ((*requests_ptr)[3]->prefetch_status,
259            Request::PREFETCH_STATUS_REDIRECTED);
260  EXPECT_EQ((*requests_ptr)[4]->prefetch_status,
261            Request::PREFETCH_STATUS_FROM_CACHE);
262  EXPECT_EQ((*requests_ptr)[5]->prefetch_status,
263            Request::PREFETCH_STATUS_FROM_CACHE);
264  EXPECT_EQ((*requests_ptr)[6]->prefetch_status,
265            Request::PREFETCH_STATUS_REDIRECTED);
266  EXPECT_EQ((*requests_ptr)[7]->prefetch_status,
267            Request::PREFETCH_STATUS_FROM_CACHE);
268  EXPECT_EQ((*requests_ptr)[8]->prefetch_status,
269            Request::PREFETCH_STATUS_AUTH_REQUIRED);
270  EXPECT_EQ((*requests_ptr)[9]->prefetch_status,
271            Request::PREFETCH_STATUS_CERT_REQUIRED);
272  EXPECT_EQ((*requests_ptr)[10]->prefetch_status,
273            Request::PREFETCH_STATUS_FROM_CACHE);
274  EXPECT_EQ((*requests_ptr)[11]->prefetch_status,
275            Request::PREFETCH_STATUS_FROM_CACHE);
276
277  delete requests_ptr;
278}
279
280TEST_F(ResourcePrefetcherTest, TestPrefetcherStopped) {
281  scoped_ptr<ResourcePrefetcher::RequestVector> requests(
282      new ResourcePrefetcher::RequestVector);
283  requests->push_back(new ResourcePrefetcher::Request(GURL(
284      "http://www.google.com/resource1.html")));
285  requests->push_back(new ResourcePrefetcher::Request(GURL(
286      "http://www.google.com/resource2.png")));
287  requests->push_back(new ResourcePrefetcher::Request(GURL(
288      "http://yahoo.com/resource1.png")));
289  requests->push_back(new ResourcePrefetcher::Request(GURL(
290      "http://yahoo.com/resource2.png")));
291  requests->push_back(new ResourcePrefetcher::Request(GURL(
292      "http://yahoo.com/resource3.png")));
293  requests->push_back(new ResourcePrefetcher::Request(GURL(
294      "http://m.google.com/resource1.jpg")));
295
296  NavigationID navigation_id;
297  navigation_id.render_process_id = 1;
298  navigation_id.render_view_id = 2;
299  navigation_id.main_frame_url = GURL("http://www.google.com");
300
301  // Needed later for comparison.
302  ResourcePrefetcher::RequestVector* requests_ptr = requests.get();
303
304  prefetcher_.reset(new TestResourcePrefetcher(&prefetcher_delegate_,
305                                               config_,
306                                               navigation_id,
307                                               PREFETCH_KEY_TYPE_HOST,
308                                               requests.Pass()));
309
310  // Starting the prefetcher maxes out the number of possible requests.
311  AddStartUrlRequestExpectation("http://www.google.com/resource1.html");
312  AddStartUrlRequestExpectation("http://www.google.com/resource2.png");
313  AddStartUrlRequestExpectation("http://yahoo.com/resource1.png");
314  AddStartUrlRequestExpectation("http://yahoo.com/resource2.png");
315  AddStartUrlRequestExpectation("http://m.google.com/resource1.jpg");
316
317  prefetcher_->Start();
318  CheckPrefetcherState(5, 1, 3);
319
320  OnResponse("http://www.google.com/resource1.html");
321  CheckPrefetcherState(4, 1, 3);
322
323  prefetcher_->Stop();  // No more queueing.
324
325  OnResponse("http://www.google.com/resource2.png");
326  CheckPrefetcherState(3, 1, 2);
327
328  OnResponse("http://yahoo.com/resource1.png");
329  CheckPrefetcherState(2, 1, 2);
330
331  OnResponse("http://yahoo.com/resource2.png");
332  CheckPrefetcherState(1, 1, 1);
333
334  // Expect the final call.
335  EXPECT_CALL(prefetcher_delegate_,
336              ResourcePrefetcherFinished(Eq(prefetcher_.get()),
337                                         Eq(requests_ptr)));
338
339  OnResponse("http://m.google.com/resource1.jpg");
340  CheckPrefetcherState(0, 1, 0);
341
342  delete requests_ptr;
343}
344
345}  // namespace predictors
346