1 // Copyright 2014 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.
5 #include "base/memory/ref_counted.h"
6 #include "base/memory/scoped_ptr.h"
7 #include "base/message_loop/message_loop.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/base/load_flags.h"
13 #include "net/url_request/redirect_info.h"
14 #include "net/url_request/url_request.h"
15 #include "net/url_request/url_request_test_util.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
20 using testing::Property
;
22 namespace predictors
{
24 // Wrapper over the ResourcePrefetcher that stubs out the StartURLRequest call
25 // since we do not want to do network fetches in this unittest.
26 class TestResourcePrefetcher
: public ResourcePrefetcher
{
28 TestResourcePrefetcher(ResourcePrefetcher::Delegate
* delegate
,
29 const ResourcePrefetchPredictorConfig
& config
,
30 const NavigationID
& navigation_id
,
31 PrefetchKeyType key_type
,
32 scoped_ptr
<RequestVector
> requests
)
33 : ResourcePrefetcher(delegate
, config
, navigation_id
,
34 key_type
, requests
.Pass()) { }
36 ~TestResourcePrefetcher() override
{}
38 MOCK_METHOD1(StartURLRequest
, void(net::URLRequest
* request
));
40 void ReadFullResponse(net::URLRequest
* request
) override
{
41 EXPECT_TRUE(request
->load_flags() & net::LOAD_PREFETCH
);
42 FinishRequest(request
, Request::PREFETCH_STATUS_FROM_CACHE
);
46 DISALLOW_COPY_AND_ASSIGN(TestResourcePrefetcher
);
50 // Delegate for ResourcePrefetcher.
51 class TestResourcePrefetcherDelegate
: public ResourcePrefetcher::Delegate
{
53 explicit TestResourcePrefetcherDelegate(base::MessageLoop
* loop
)
54 : request_context_getter_(
55 new net::TestURLRequestContextGetter(loop
->task_runner())) {}
56 ~TestResourcePrefetcherDelegate() { }
58 net::URLRequestContext
* GetURLRequestContext() override
{
59 return request_context_getter_
->GetURLRequestContext();
62 MOCK_METHOD2(ResourcePrefetcherFinished
,
63 void(ResourcePrefetcher
* prefetcher
,
64 ResourcePrefetcher::RequestVector
* requests
));
67 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_getter_
;
69 DISALLOW_COPY_AND_ASSIGN(TestResourcePrefetcherDelegate
);
73 // The following unittest tests most of the ResourcePrefetcher except for:
74 // 1. Call to ReadFullResponse. There does not seem to be a good way to test the
75 // function in a unittest, and probably requires a browser_test.
76 // 2. Setting of the Prefetch status for cache vs non cache.
77 class ResourcePrefetcherTest
: public testing::Test
{
79 ResourcePrefetcherTest();
80 ~ResourcePrefetcherTest() override
;
83 typedef ResourcePrefetcher::Request Request
;
85 void AddStartUrlRequestExpectation(const std::string
& url
) {
86 EXPECT_CALL(*prefetcher_
,
87 StartURLRequest(Property(&net::URLRequest::original_url
,
91 void CheckPrefetcherState(size_t inflight
, size_t queue
, size_t host
) {
92 EXPECT_EQ(prefetcher_
->inflight_requests_
.size(), inflight
);
93 EXPECT_EQ(prefetcher_
->request_queue_
.size(), queue
);
94 EXPECT_EQ(prefetcher_
->host_inflight_counts_
.size(), host
);
97 net::URLRequest
* GetInFlightRequest(const std::string
& url_str
) {
100 for (std::list
<Request
*>::const_iterator it
=
101 prefetcher_
->request_queue_
.begin();
102 it
!= prefetcher_
->request_queue_
.end(); ++it
) {
103 EXPECT_NE((*it
)->resource_url
, url
);
105 for (std::map
<net::URLRequest
*, Request
*>::const_iterator it
=
106 prefetcher_
->inflight_requests_
.begin();
107 it
!= prefetcher_
->inflight_requests_
.end(); ++it
) {
108 if (it
->first
->original_url() == url
)
111 EXPECT_TRUE(false) << "Infligh request not found: " << url_str
;
116 void OnReceivedRedirect(const std::string
& url
) {
117 prefetcher_
->OnReceivedRedirect(
118 GetInFlightRequest(url
), net::RedirectInfo(), NULL
);
120 void OnAuthRequired(const std::string
& url
) {
121 prefetcher_
->OnAuthRequired(GetInFlightRequest(url
), NULL
);
123 void OnCertificateRequested(const std::string
& url
) {
124 prefetcher_
->OnCertificateRequested(GetInFlightRequest(url
), NULL
);
126 void OnSSLCertificateError(const std::string
& url
) {
127 prefetcher_
->OnSSLCertificateError(GetInFlightRequest(url
),
128 net::SSLInfo(), false);
130 void OnResponse(const std::string
& url
) {
131 prefetcher_
->OnResponseStarted(GetInFlightRequest(url
));
134 base::MessageLoop loop_
;
135 content::TestBrowserThread io_thread_
;
136 ResourcePrefetchPredictorConfig config_
;
137 TestResourcePrefetcherDelegate prefetcher_delegate_
;
138 scoped_ptr
<TestResourcePrefetcher
> prefetcher_
;
141 DISALLOW_COPY_AND_ASSIGN(ResourcePrefetcherTest
);
144 ResourcePrefetcherTest::ResourcePrefetcherTest()
145 : loop_(base::MessageLoop::TYPE_IO
),
146 io_thread_(content::BrowserThread::IO
, &loop_
),
147 prefetcher_delegate_(&loop_
) {
148 config_
.max_prefetches_inflight_per_navigation
= 5;
149 config_
.max_prefetches_inflight_per_host_per_navigation
= 2;
152 ResourcePrefetcherTest::~ResourcePrefetcherTest() {
155 TEST_F(ResourcePrefetcherTest
, TestPrefetcherFinishes
) {
156 scoped_ptr
<ResourcePrefetcher::RequestVector
> requests(
157 new ResourcePrefetcher::RequestVector
);
158 requests
->push_back(new ResourcePrefetcher::Request(GURL(
159 "http://www.google.com/resource1.html")));
160 requests
->push_back(new ResourcePrefetcher::Request(GURL(
161 "http://www.google.com/resource2.png")));
162 requests
->push_back(new ResourcePrefetcher::Request(GURL(
163 "http://yahoo.com/resource1.png")));
164 requests
->push_back(new ResourcePrefetcher::Request(GURL(
165 "http://yahoo.com/resource2.png")));
166 requests
->push_back(new ResourcePrefetcher::Request(GURL(
167 "http://yahoo.com/resource3.png")));
168 requests
->push_back(new ResourcePrefetcher::Request(GURL(
169 "http://m.google.com/resource1.jpg")));
170 requests
->push_back(new ResourcePrefetcher::Request(GURL(
171 "http://www.google.com/resource3.html")));
172 requests
->push_back(new ResourcePrefetcher::Request(GURL(
173 "http://m.google.com/resource2.html")));
174 requests
->push_back(new ResourcePrefetcher::Request(GURL(
175 "http://m.google.com/resource3.css")));
176 requests
->push_back(new ResourcePrefetcher::Request(GURL(
177 "http://m.google.com/resource4.png")));
178 requests
->push_back(new ResourcePrefetcher::Request(GURL(
179 "http://yahoo.com/resource4.png")));
180 requests
->push_back(new ResourcePrefetcher::Request(GURL(
181 "http://yahoo.com/resource5.png")));
183 NavigationID navigation_id
;
184 navigation_id
.render_process_id
= 1;
185 navigation_id
.render_frame_id
= 2;
186 navigation_id
.main_frame_url
= GURL("http://www.google.com");
188 // Needed later for comparison.
189 ResourcePrefetcher::RequestVector
* requests_ptr
= requests
.get();
191 prefetcher_
.reset(new TestResourcePrefetcher(&prefetcher_delegate_
,
194 PREFETCH_KEY_TYPE_URL
,
197 // Starting the prefetcher maxes out the number of possible requests.
198 AddStartUrlRequestExpectation("http://www.google.com/resource1.html");
199 AddStartUrlRequestExpectation("http://www.google.com/resource2.png");
200 AddStartUrlRequestExpectation("http://yahoo.com/resource1.png");
201 AddStartUrlRequestExpectation("http://yahoo.com/resource2.png");
202 AddStartUrlRequestExpectation("http://m.google.com/resource1.jpg");
204 prefetcher_
->Start();
205 CheckPrefetcherState(5, 7, 3);
207 AddStartUrlRequestExpectation("http://m.google.com/resource2.html");
208 OnResponse("http://m.google.com/resource1.jpg");
209 CheckPrefetcherState(5, 6, 3);
211 AddStartUrlRequestExpectation("http://www.google.com/resource3.html");
212 OnSSLCertificateError("http://www.google.com/resource1.html");
213 CheckPrefetcherState(5, 5, 3);
215 AddStartUrlRequestExpectation("http://m.google.com/resource3.css");
216 OnResponse("http://m.google.com/resource2.html");
217 CheckPrefetcherState(5, 4, 3);
219 AddStartUrlRequestExpectation("http://m.google.com/resource4.png");
220 OnReceivedRedirect("http://www.google.com/resource3.html");
221 CheckPrefetcherState(5, 3, 3);
223 OnResponse("http://www.google.com/resource2.png");
224 CheckPrefetcherState(4, 3, 2);
226 AddStartUrlRequestExpectation("http://yahoo.com/resource3.png");
227 OnReceivedRedirect("http://yahoo.com/resource2.png");
228 CheckPrefetcherState(4, 2, 2);
230 AddStartUrlRequestExpectation("http://yahoo.com/resource4.png");
231 OnResponse("http://yahoo.com/resource1.png");
232 CheckPrefetcherState(4, 1, 2);
234 AddStartUrlRequestExpectation("http://yahoo.com/resource5.png");
235 OnResponse("http://yahoo.com/resource4.png");
236 CheckPrefetcherState(4, 0, 2);
238 OnResponse("http://yahoo.com/resource5.png");
239 CheckPrefetcherState(3, 0, 2);
241 OnCertificateRequested("http://m.google.com/resource4.png");
242 CheckPrefetcherState(2, 0, 2);
244 OnAuthRequired("http://m.google.com/resource3.css");
245 CheckPrefetcherState(1, 0, 1);
247 // Expect the final call.
248 EXPECT_CALL(prefetcher_delegate_
,
249 ResourcePrefetcherFinished(Eq(prefetcher_
.get()),
252 OnResponse("http://yahoo.com/resource3.png");
253 CheckPrefetcherState(0, 0, 0);
255 // Check the prefetch status.
256 EXPECT_EQ(Request::PREFETCH_STATUS_CERT_ERROR
,
257 (*requests_ptr
)[0]->prefetch_status
);
258 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
259 (*requests_ptr
)[1]->prefetch_status
);
260 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
261 (*requests_ptr
)[2]->prefetch_status
);
262 EXPECT_EQ(Request::PREFETCH_STATUS_REDIRECTED
,
263 (*requests_ptr
)[3]->prefetch_status
);
264 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
265 (*requests_ptr
)[4]->prefetch_status
);
266 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
267 (*requests_ptr
)[5]->prefetch_status
);
268 EXPECT_EQ(Request::PREFETCH_STATUS_REDIRECTED
,
269 (*requests_ptr
)[6]->prefetch_status
);
270 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
271 (*requests_ptr
)[7]->prefetch_status
);
272 EXPECT_EQ(Request::PREFETCH_STATUS_AUTH_REQUIRED
,
273 (*requests_ptr
)[8]->prefetch_status
);
274 EXPECT_EQ(Request::PREFETCH_STATUS_CERT_REQUIRED
,
275 (*requests_ptr
)[9]->prefetch_status
);
276 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
277 (*requests_ptr
)[10]->prefetch_status
);
278 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
279 (*requests_ptr
)[11]->prefetch_status
);
281 // We need to delete requests_ptr here, though it looks to be managed by the
282 // scoped_ptr requests. The scoped_ptr requests releases itself and the raw
283 // pointer requests_ptr is passed to ResourcePrefetcherFinished(). In the
284 // test, ResourcePrefetcherFinished() is a mock function and does not handle
285 // the raw pointer properly. In the real code, requests_ptr will eventually be
286 // passed to and managed by ResourcePrefetchPredictor::Result::Result.
290 TEST_F(ResourcePrefetcherTest
, TestPrefetcherStopped
) {
291 scoped_ptr
<ResourcePrefetcher::RequestVector
> requests(
292 new ResourcePrefetcher::RequestVector
);
293 requests
->push_back(new ResourcePrefetcher::Request(GURL(
294 "http://www.google.com/resource1.html")));
295 requests
->push_back(new ResourcePrefetcher::Request(GURL(
296 "http://www.google.com/resource2.png")));
297 requests
->push_back(new ResourcePrefetcher::Request(GURL(
298 "http://yahoo.com/resource1.png")));
299 requests
->push_back(new ResourcePrefetcher::Request(GURL(
300 "http://yahoo.com/resource2.png")));
301 requests
->push_back(new ResourcePrefetcher::Request(GURL(
302 "http://yahoo.com/resource3.png")));
303 requests
->push_back(new ResourcePrefetcher::Request(GURL(
304 "http://m.google.com/resource1.jpg")));
306 NavigationID navigation_id
;
307 navigation_id
.render_process_id
= 1;
308 navigation_id
.render_frame_id
= 2;
309 navigation_id
.main_frame_url
= GURL("http://www.google.com");
311 // Needed later for comparison.
312 ResourcePrefetcher::RequestVector
* requests_ptr
= requests
.get();
314 prefetcher_
.reset(new TestResourcePrefetcher(&prefetcher_delegate_
,
317 PREFETCH_KEY_TYPE_HOST
,
320 // Starting the prefetcher maxes out the number of possible requests.
321 AddStartUrlRequestExpectation("http://www.google.com/resource1.html");
322 AddStartUrlRequestExpectation("http://www.google.com/resource2.png");
323 AddStartUrlRequestExpectation("http://yahoo.com/resource1.png");
324 AddStartUrlRequestExpectation("http://yahoo.com/resource2.png");
325 AddStartUrlRequestExpectation("http://m.google.com/resource1.jpg");
327 prefetcher_
->Start();
328 CheckPrefetcherState(5, 1, 3);
330 OnResponse("http://www.google.com/resource1.html");
331 CheckPrefetcherState(4, 1, 3);
333 prefetcher_
->Stop(); // No more queueing.
335 OnResponse("http://www.google.com/resource2.png");
336 CheckPrefetcherState(3, 1, 2);
338 OnResponse("http://yahoo.com/resource1.png");
339 CheckPrefetcherState(2, 1, 2);
341 OnResponse("http://yahoo.com/resource2.png");
342 CheckPrefetcherState(1, 1, 1);
344 // Expect the final call.
345 EXPECT_CALL(prefetcher_delegate_
,
346 ResourcePrefetcherFinished(Eq(prefetcher_
.get()),
349 OnResponse("http://m.google.com/resource1.jpg");
350 CheckPrefetcherState(0, 1, 0);
352 // We need to delete requests_ptr here, though it looks to be managed by the
353 // scoped_ptr requests. The scoped_ptr requests releases itself and the raw
354 // pointer requests_ptr is passed to ResourcePrefetcherFinished(). In the
355 // test, ResourcePrefetcherFinished() is a mock function and does not handle
356 // the raw pointer properly. In the real code, requests_ptr will eventually be
357 // passed to and managed by ResourcePrefetchPredictor::Result::Result.
361 } // namespace predictors