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. 4 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" 17 18 using testing::Eq; 19 using testing::Property; 20 21 namespace predictors { 22 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 { 26 public: 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()) { } 34 35 virtual ~TestResourcePrefetcher() { } 36 37 MOCK_METHOD1(StartURLRequest, void(net::URLRequest* request)); 38 39 void ReadFullResponse(net::URLRequest* request) OVERRIDE { 40 FinishRequest(request, Request::PREFETCH_STATUS_FROM_CACHE); 41 } 42 43 private: 44 DISALLOW_COPY_AND_ASSIGN(TestResourcePrefetcher); 45 }; 46 47 48 // Delegate for ResourcePrefetcher. 49 class TestResourcePrefetcherDelegate : public ResourcePrefetcher::Delegate { 50 public: 51 explicit TestResourcePrefetcherDelegate(base::MessageLoop* loop) 52 : request_context_getter_(new net::TestURLRequestContextGetter( 53 loop->message_loop_proxy())) { } 54 ~TestResourcePrefetcherDelegate() { } 55 56 virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE { 57 return request_context_getter_->GetURLRequestContext(); 58 } 59 60 MOCK_METHOD2(ResourcePrefetcherFinished, 61 void(ResourcePrefetcher* prefetcher, 62 ResourcePrefetcher::RequestVector* requests)); 63 64 private: 65 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_; 66 67 DISALLOW_COPY_AND_ASSIGN(TestResourcePrefetcherDelegate); 68 }; 69 70 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 { 76 public: 77 ResourcePrefetcherTest(); 78 virtual ~ResourcePrefetcherTest(); 79 80 protected: 81 typedef ResourcePrefetcher::Request Request; 82 83 void AddStartUrlRequestExpectation(const std::string& url) { 84 EXPECT_CALL(*prefetcher_, 85 StartURLRequest(Property(&net::URLRequest::original_url, 86 Eq(GURL(url))))); 87 } 88 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); 93 } 94 95 net::URLRequest* GetInFlightRequest(const std::string& url_str) { 96 GURL url(url_str); 97 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); 102 } 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) 107 return it->first; 108 } 109 EXPECT_TRUE(false) << "Infligh request not found: " << url_str; 110 return NULL; 111 } 112 113 114 void OnReceivedRedirect(const std::string& url) { 115 prefetcher_->OnReceivedRedirect( 116 GetInFlightRequest(url), net::RedirectInfo(), NULL); 117 } 118 void OnAuthRequired(const std::string& url) { 119 prefetcher_->OnAuthRequired(GetInFlightRequest(url), NULL); 120 } 121 void OnCertificateRequested(const std::string& url) { 122 prefetcher_->OnCertificateRequested(GetInFlightRequest(url), NULL); 123 } 124 void OnSSLCertificateError(const std::string& url) { 125 prefetcher_->OnSSLCertificateError(GetInFlightRequest(url), 126 net::SSLInfo(), false); 127 } 128 void OnResponse(const std::string& url) { 129 prefetcher_->OnResponseStarted(GetInFlightRequest(url)); 130 } 131 132 base::MessageLoop loop_; 133 content::TestBrowserThread io_thread_; 134 ResourcePrefetchPredictorConfig config_; 135 TestResourcePrefetcherDelegate prefetcher_delegate_; 136 scoped_ptr<TestResourcePrefetcher> prefetcher_; 137 138 private: 139 DISALLOW_COPY_AND_ASSIGN(ResourcePrefetcherTest); 140 }; 141 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; 148 } 149 150 ResourcePrefetcherTest::~ResourcePrefetcherTest() { 151 } 152 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"))); 180 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"); 185 186 // Needed later for comparison. 187 ResourcePrefetcher::RequestVector* requests_ptr = requests.get(); 188 189 prefetcher_.reset(new TestResourcePrefetcher(&prefetcher_delegate_, 190 config_, 191 navigation_id, 192 PREFETCH_KEY_TYPE_URL, 193 requests.Pass())); 194 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"); 201 202 prefetcher_->Start(); 203 CheckPrefetcherState(5, 7, 3); 204 205 AddStartUrlRequestExpectation("http://m.google.com/resource2.html"); 206 OnResponse("http://m.google.com/resource1.jpg"); 207 CheckPrefetcherState(5, 6, 3); 208 209 AddStartUrlRequestExpectation("http://www.google.com/resource3.html"); 210 OnSSLCertificateError("http://www.google.com/resource1.html"); 211 CheckPrefetcherState(5, 5, 3); 212 213 AddStartUrlRequestExpectation("http://m.google.com/resource3.css"); 214 OnResponse("http://m.google.com/resource2.html"); 215 CheckPrefetcherState(5, 4, 3); 216 217 AddStartUrlRequestExpectation("http://m.google.com/resource4.png"); 218 OnReceivedRedirect("http://www.google.com/resource3.html"); 219 CheckPrefetcherState(5, 3, 3); 220 221 OnResponse("http://www.google.com/resource2.png"); 222 CheckPrefetcherState(4, 3, 2); 223 224 AddStartUrlRequestExpectation("http://yahoo.com/resource3.png"); 225 OnReceivedRedirect("http://yahoo.com/resource2.png"); 226 CheckPrefetcherState(4, 2, 2); 227 228 AddStartUrlRequestExpectation("http://yahoo.com/resource4.png"); 229 OnResponse("http://yahoo.com/resource1.png"); 230 CheckPrefetcherState(4, 1, 2); 231 232 AddStartUrlRequestExpectation("http://yahoo.com/resource5.png"); 233 OnResponse("http://yahoo.com/resource4.png"); 234 CheckPrefetcherState(4, 0, 2); 235 236 OnResponse("http://yahoo.com/resource5.png"); 237 CheckPrefetcherState(3, 0, 2); 238 239 OnCertificateRequested("http://m.google.com/resource4.png"); 240 CheckPrefetcherState(2, 0, 2); 241 242 OnAuthRequired("http://m.google.com/resource3.css"); 243 CheckPrefetcherState(1, 0, 1); 244 245 // Expect the final call. 246 EXPECT_CALL(prefetcher_delegate_, 247 ResourcePrefetcherFinished(Eq(prefetcher_.get()), 248 Eq(requests_ptr))); 249 250 OnResponse("http://yahoo.com/resource3.png"); 251 CheckPrefetcherState(0, 0, 0); 252 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); 278 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. 285 delete requests_ptr; 286 } 287 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"))); 303 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"); 308 309 // Needed later for comparison. 310 ResourcePrefetcher::RequestVector* requests_ptr = requests.get(); 311 312 prefetcher_.reset(new TestResourcePrefetcher(&prefetcher_delegate_, 313 config_, 314 navigation_id, 315 PREFETCH_KEY_TYPE_HOST, 316 requests.Pass())); 317 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"); 324 325 prefetcher_->Start(); 326 CheckPrefetcherState(5, 1, 3); 327 328 OnResponse("http://www.google.com/resource1.html"); 329 CheckPrefetcherState(4, 1, 3); 330 331 prefetcher_->Stop(); // No more queueing. 332 333 OnResponse("http://www.google.com/resource2.png"); 334 CheckPrefetcherState(3, 1, 2); 335 336 OnResponse("http://yahoo.com/resource1.png"); 337 CheckPrefetcherState(2, 1, 2); 338 339 OnResponse("http://yahoo.com/resource2.png"); 340 CheckPrefetcherState(1, 1, 1); 341 342 // Expect the final call. 343 EXPECT_CALL(prefetcher_delegate_, 344 ResourcePrefetcherFinished(Eq(prefetcher_.get()), 345 Eq(requests_ptr))); 346 347 OnResponse("http://m.google.com/resource1.jpg"); 348 CheckPrefetcherState(0, 1, 0); 349 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. 356 delete requests_ptr; 357 } 358 359 } // namespace predictors 360