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/url_request/redirect_info.h"
13 #include "net/url_request/url_request.h"
14 #include "net/url_request/url_request_test_util.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
19 using testing::Property
;
21 namespace predictors
{
23 // Wrapper over the ResourcePrefetcher that stubs out the StartURLRequest call
24 // since we do not want to do network fetches in this unittest.
25 class TestResourcePrefetcher
: public ResourcePrefetcher
{
27 TestResourcePrefetcher(ResourcePrefetcher::Delegate
* delegate
,
28 const ResourcePrefetchPredictorConfig
& config
,
29 const NavigationID
& navigation_id
,
30 PrefetchKeyType key_type
,
31 scoped_ptr
<RequestVector
> requests
)
32 : ResourcePrefetcher(delegate
, config
, navigation_id
,
33 key_type
, requests
.Pass()) { }
35 virtual ~TestResourcePrefetcher() { }
37 MOCK_METHOD1(StartURLRequest
, void(net::URLRequest
* request
));
39 void ReadFullResponse(net::URLRequest
* request
) override
{
40 FinishRequest(request
, Request::PREFETCH_STATUS_FROM_CACHE
);
44 DISALLOW_COPY_AND_ASSIGN(TestResourcePrefetcher
);
48 // Delegate for ResourcePrefetcher.
49 class TestResourcePrefetcherDelegate
: public ResourcePrefetcher::Delegate
{
51 explicit TestResourcePrefetcherDelegate(base::MessageLoop
* loop
)
52 : request_context_getter_(new net::TestURLRequestContextGetter(
53 loop
->message_loop_proxy())) { }
54 ~TestResourcePrefetcherDelegate() { }
56 virtual net::URLRequestContext
* GetURLRequestContext() override
{
57 return request_context_getter_
->GetURLRequestContext();
60 MOCK_METHOD2(ResourcePrefetcherFinished
,
61 void(ResourcePrefetcher
* prefetcher
,
62 ResourcePrefetcher::RequestVector
* requests
));
65 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_getter_
;
67 DISALLOW_COPY_AND_ASSIGN(TestResourcePrefetcherDelegate
);
71 // The following unittest tests most of the ResourcePrefetcher except for:
72 // 1. Call to ReadFullResponse. There does not seem to be a good way to test the
73 // function in a unittest, and probably requires a browser_test.
74 // 2. Setting of the Prefetch status for cache vs non cache.
75 class ResourcePrefetcherTest
: public testing::Test
{
77 ResourcePrefetcherTest();
78 ~ResourcePrefetcherTest() override
;
81 typedef ResourcePrefetcher::Request Request
;
83 void AddStartUrlRequestExpectation(const std::string
& url
) {
84 EXPECT_CALL(*prefetcher_
,
85 StartURLRequest(Property(&net::URLRequest::original_url
,
89 void CheckPrefetcherState(size_t inflight
, size_t queue
, size_t host
) {
90 EXPECT_EQ(prefetcher_
->inflight_requests_
.size(), inflight
);
91 EXPECT_EQ(prefetcher_
->request_queue_
.size(), queue
);
92 EXPECT_EQ(prefetcher_
->host_inflight_counts_
.size(), host
);
95 net::URLRequest
* GetInFlightRequest(const std::string
& url_str
) {
98 for (std::list
<Request
*>::const_iterator it
=
99 prefetcher_
->request_queue_
.begin();
100 it
!= prefetcher_
->request_queue_
.end(); ++it
) {
101 EXPECT_NE((*it
)->resource_url
, url
);
103 for (std::map
<net::URLRequest
*, Request
*>::const_iterator it
=
104 prefetcher_
->inflight_requests_
.begin();
105 it
!= prefetcher_
->inflight_requests_
.end(); ++it
) {
106 if (it
->first
->original_url() == url
)
109 EXPECT_TRUE(false) << "Infligh request not found: " << url_str
;
114 void OnReceivedRedirect(const std::string
& url
) {
115 prefetcher_
->OnReceivedRedirect(
116 GetInFlightRequest(url
), net::RedirectInfo(), NULL
);
118 void OnAuthRequired(const std::string
& url
) {
119 prefetcher_
->OnAuthRequired(GetInFlightRequest(url
), NULL
);
121 void OnCertificateRequested(const std::string
& url
) {
122 prefetcher_
->OnCertificateRequested(GetInFlightRequest(url
), NULL
);
124 void OnSSLCertificateError(const std::string
& url
) {
125 prefetcher_
->OnSSLCertificateError(GetInFlightRequest(url
),
126 net::SSLInfo(), false);
128 void OnResponse(const std::string
& url
) {
129 prefetcher_
->OnResponseStarted(GetInFlightRequest(url
));
132 base::MessageLoop loop_
;
133 content::TestBrowserThread io_thread_
;
134 ResourcePrefetchPredictorConfig config_
;
135 TestResourcePrefetcherDelegate prefetcher_delegate_
;
136 scoped_ptr
<TestResourcePrefetcher
> prefetcher_
;
139 DISALLOW_COPY_AND_ASSIGN(ResourcePrefetcherTest
);
142 ResourcePrefetcherTest::ResourcePrefetcherTest()
143 : loop_(base::MessageLoop::TYPE_IO
),
144 io_thread_(content::BrowserThread::IO
, &loop_
),
145 prefetcher_delegate_(&loop_
) {
146 config_
.max_prefetches_inflight_per_navigation
= 5;
147 config_
.max_prefetches_inflight_per_host_per_navigation
= 2;
150 ResourcePrefetcherTest::~ResourcePrefetcherTest() {
153 TEST_F(ResourcePrefetcherTest
, TestPrefetcherFinishes
) {
154 scoped_ptr
<ResourcePrefetcher::RequestVector
> requests(
155 new ResourcePrefetcher::RequestVector
);
156 requests
->push_back(new ResourcePrefetcher::Request(GURL(
157 "http://www.google.com/resource1.html")));
158 requests
->push_back(new ResourcePrefetcher::Request(GURL(
159 "http://www.google.com/resource2.png")));
160 requests
->push_back(new ResourcePrefetcher::Request(GURL(
161 "http://yahoo.com/resource1.png")));
162 requests
->push_back(new ResourcePrefetcher::Request(GURL(
163 "http://yahoo.com/resource2.png")));
164 requests
->push_back(new ResourcePrefetcher::Request(GURL(
165 "http://yahoo.com/resource3.png")));
166 requests
->push_back(new ResourcePrefetcher::Request(GURL(
167 "http://m.google.com/resource1.jpg")));
168 requests
->push_back(new ResourcePrefetcher::Request(GURL(
169 "http://www.google.com/resource3.html")));
170 requests
->push_back(new ResourcePrefetcher::Request(GURL(
171 "http://m.google.com/resource2.html")));
172 requests
->push_back(new ResourcePrefetcher::Request(GURL(
173 "http://m.google.com/resource3.css")));
174 requests
->push_back(new ResourcePrefetcher::Request(GURL(
175 "http://m.google.com/resource4.png")));
176 requests
->push_back(new ResourcePrefetcher::Request(GURL(
177 "http://yahoo.com/resource4.png")));
178 requests
->push_back(new ResourcePrefetcher::Request(GURL(
179 "http://yahoo.com/resource5.png")));
181 NavigationID navigation_id
;
182 navigation_id
.render_process_id
= 1;
183 navigation_id
.render_frame_id
= 2;
184 navigation_id
.main_frame_url
= GURL("http://www.google.com");
186 // Needed later for comparison.
187 ResourcePrefetcher::RequestVector
* requests_ptr
= requests
.get();
189 prefetcher_
.reset(new TestResourcePrefetcher(&prefetcher_delegate_
,
192 PREFETCH_KEY_TYPE_URL
,
195 // Starting the prefetcher maxes out the number of possible requests.
196 AddStartUrlRequestExpectation("http://www.google.com/resource1.html");
197 AddStartUrlRequestExpectation("http://www.google.com/resource2.png");
198 AddStartUrlRequestExpectation("http://yahoo.com/resource1.png");
199 AddStartUrlRequestExpectation("http://yahoo.com/resource2.png");
200 AddStartUrlRequestExpectation("http://m.google.com/resource1.jpg");
202 prefetcher_
->Start();
203 CheckPrefetcherState(5, 7, 3);
205 AddStartUrlRequestExpectation("http://m.google.com/resource2.html");
206 OnResponse("http://m.google.com/resource1.jpg");
207 CheckPrefetcherState(5, 6, 3);
209 AddStartUrlRequestExpectation("http://www.google.com/resource3.html");
210 OnSSLCertificateError("http://www.google.com/resource1.html");
211 CheckPrefetcherState(5, 5, 3);
213 AddStartUrlRequestExpectation("http://m.google.com/resource3.css");
214 OnResponse("http://m.google.com/resource2.html");
215 CheckPrefetcherState(5, 4, 3);
217 AddStartUrlRequestExpectation("http://m.google.com/resource4.png");
218 OnReceivedRedirect("http://www.google.com/resource3.html");
219 CheckPrefetcherState(5, 3, 3);
221 OnResponse("http://www.google.com/resource2.png");
222 CheckPrefetcherState(4, 3, 2);
224 AddStartUrlRequestExpectation("http://yahoo.com/resource3.png");
225 OnReceivedRedirect("http://yahoo.com/resource2.png");
226 CheckPrefetcherState(4, 2, 2);
228 AddStartUrlRequestExpectation("http://yahoo.com/resource4.png");
229 OnResponse("http://yahoo.com/resource1.png");
230 CheckPrefetcherState(4, 1, 2);
232 AddStartUrlRequestExpectation("http://yahoo.com/resource5.png");
233 OnResponse("http://yahoo.com/resource4.png");
234 CheckPrefetcherState(4, 0, 2);
236 OnResponse("http://yahoo.com/resource5.png");
237 CheckPrefetcherState(3, 0, 2);
239 OnCertificateRequested("http://m.google.com/resource4.png");
240 CheckPrefetcherState(2, 0, 2);
242 OnAuthRequired("http://m.google.com/resource3.css");
243 CheckPrefetcherState(1, 0, 1);
245 // Expect the final call.
246 EXPECT_CALL(prefetcher_delegate_
,
247 ResourcePrefetcherFinished(Eq(prefetcher_
.get()),
250 OnResponse("http://yahoo.com/resource3.png");
251 CheckPrefetcherState(0, 0, 0);
253 // Check the prefetch status.
254 EXPECT_EQ(Request::PREFETCH_STATUS_CERT_ERROR
,
255 (*requests_ptr
)[0]->prefetch_status
);
256 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
257 (*requests_ptr
)[1]->prefetch_status
);
258 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
259 (*requests_ptr
)[2]->prefetch_status
);
260 EXPECT_EQ(Request::PREFETCH_STATUS_REDIRECTED
,
261 (*requests_ptr
)[3]->prefetch_status
);
262 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
263 (*requests_ptr
)[4]->prefetch_status
);
264 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
265 (*requests_ptr
)[5]->prefetch_status
);
266 EXPECT_EQ(Request::PREFETCH_STATUS_REDIRECTED
,
267 (*requests_ptr
)[6]->prefetch_status
);
268 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
269 (*requests_ptr
)[7]->prefetch_status
);
270 EXPECT_EQ(Request::PREFETCH_STATUS_AUTH_REQUIRED
,
271 (*requests_ptr
)[8]->prefetch_status
);
272 EXPECT_EQ(Request::PREFETCH_STATUS_CERT_REQUIRED
,
273 (*requests_ptr
)[9]->prefetch_status
);
274 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
275 (*requests_ptr
)[10]->prefetch_status
);
276 EXPECT_EQ(Request::PREFETCH_STATUS_FROM_CACHE
,
277 (*requests_ptr
)[11]->prefetch_status
);
279 // We need to delete requests_ptr here, though it looks to be managed by the
280 // scoped_ptr requests. The scoped_ptr requests releases itself and the raw
281 // pointer requests_ptr is passed to ResourcePrefetcherFinished(). In the
282 // test, ResourcePrefetcherFinished() is a mock function and does not handle
283 // the raw pointer properly. In the real code, requests_ptr will eventually be
284 // passed to and managed by ResourcePrefetchPredictor::Result::Result.
288 TEST_F(ResourcePrefetcherTest
, TestPrefetcherStopped
) {
289 scoped_ptr
<ResourcePrefetcher::RequestVector
> requests(
290 new ResourcePrefetcher::RequestVector
);
291 requests
->push_back(new ResourcePrefetcher::Request(GURL(
292 "http://www.google.com/resource1.html")));
293 requests
->push_back(new ResourcePrefetcher::Request(GURL(
294 "http://www.google.com/resource2.png")));
295 requests
->push_back(new ResourcePrefetcher::Request(GURL(
296 "http://yahoo.com/resource1.png")));
297 requests
->push_back(new ResourcePrefetcher::Request(GURL(
298 "http://yahoo.com/resource2.png")));
299 requests
->push_back(new ResourcePrefetcher::Request(GURL(
300 "http://yahoo.com/resource3.png")));
301 requests
->push_back(new ResourcePrefetcher::Request(GURL(
302 "http://m.google.com/resource1.jpg")));
304 NavigationID navigation_id
;
305 navigation_id
.render_process_id
= 1;
306 navigation_id
.render_frame_id
= 2;
307 navigation_id
.main_frame_url
= GURL("http://www.google.com");
309 // Needed later for comparison.
310 ResourcePrefetcher::RequestVector
* requests_ptr
= requests
.get();
312 prefetcher_
.reset(new TestResourcePrefetcher(&prefetcher_delegate_
,
315 PREFETCH_KEY_TYPE_HOST
,
318 // Starting the prefetcher maxes out the number of possible requests.
319 AddStartUrlRequestExpectation("http://www.google.com/resource1.html");
320 AddStartUrlRequestExpectation("http://www.google.com/resource2.png");
321 AddStartUrlRequestExpectation("http://yahoo.com/resource1.png");
322 AddStartUrlRequestExpectation("http://yahoo.com/resource2.png");
323 AddStartUrlRequestExpectation("http://m.google.com/resource1.jpg");
325 prefetcher_
->Start();
326 CheckPrefetcherState(5, 1, 3);
328 OnResponse("http://www.google.com/resource1.html");
329 CheckPrefetcherState(4, 1, 3);
331 prefetcher_
->Stop(); // No more queueing.
333 OnResponse("http://www.google.com/resource2.png");
334 CheckPrefetcherState(3, 1, 2);
336 OnResponse("http://yahoo.com/resource1.png");
337 CheckPrefetcherState(2, 1, 2);
339 OnResponse("http://yahoo.com/resource2.png");
340 CheckPrefetcherState(1, 1, 1);
342 // Expect the final call.
343 EXPECT_CALL(prefetcher_delegate_
,
344 ResourcePrefetcherFinished(Eq(prefetcher_
.get()),
347 OnResponse("http://m.google.com/resource1.jpg");
348 CheckPrefetcherState(0, 1, 0);
350 // We need to delete requests_ptr here, though it looks to be managed by the
351 // scoped_ptr requests. The scoped_ptr requests releases itself and the raw
352 // pointer requests_ptr is passed to ResourcePrefetcherFinished(). In the
353 // test, ResourcePrefetcherFinished() is a mock function and does not handle
354 // the raw pointer properly. In the real code, requests_ptr will eventually be
355 // passed to and managed by ResourcePrefetchPredictor::Result::Result.
359 } // namespace predictors