1// Copyright (c) 2011 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/time.h" 6#include "chrome/browser/prerender/prerender_contents.h" 7#include "chrome/browser/prerender/prerender_manager.h" 8#include "content/browser/browser_thread.h" 9#include "content/browser/renderer_host/render_view_host.h" 10#include "content/browser/renderer_host/render_process_host.h" 11#include "googleurl/src/gurl.h" 12#include "testing/gtest/include/gtest/gtest.h" 13 14namespace prerender { 15 16namespace { 17 18class DummyPrerenderContents : public PrerenderContents { 19 public: 20 DummyPrerenderContents(PrerenderManager* prerender_manager, 21 const GURL& url, 22 FinalStatus expected_final_status) 23 : PrerenderContents(prerender_manager, NULL, url, 24 std::vector<GURL>(), GURL()), 25 has_started_(false), 26 expected_final_status_(expected_final_status) { 27 } 28 29 DummyPrerenderContents(PrerenderManager* prerender_manager, 30 const GURL& url, 31 const std::vector<GURL> alias_urls, 32 FinalStatus expected_final_status) 33 : PrerenderContents(prerender_manager, NULL, url, alias_urls, GURL()), 34 has_started_(false), 35 expected_final_status_(expected_final_status) { 36 } 37 38 virtual ~DummyPrerenderContents() { 39 EXPECT_EQ(expected_final_status_, final_status()); 40 } 41 42 virtual void StartPrerendering() OVERRIDE { 43 has_started_ = true; 44 } 45 46 virtual bool GetChildId(int* child_id) const OVERRIDE { 47 *child_id = 0; 48 return true; 49 } 50 51 virtual bool GetRouteId(int* route_id) const OVERRIDE { 52 *route_id = 0; 53 return true; 54 } 55 56 bool has_started() const { return has_started_; } 57 58 private: 59 bool has_started_; 60 FinalStatus expected_final_status_; 61}; 62 63class TestPrerenderManager : public PrerenderManager { 64 public: 65 TestPrerenderManager() 66 : PrerenderManager(NULL), 67 time_(base::Time::Now()), 68 time_ticks_(base::TimeTicks::Now()), 69 next_pc_(NULL) { 70 rate_limit_enabled_ = false; 71 } 72 73 void AdvanceTime(base::TimeDelta delta) { 74 time_ += delta; 75 } 76 77 void AdvanceTimeTicks(base::TimeDelta delta) { 78 time_ticks_ += delta; 79 } 80 81 void SetNextPrerenderContents(PrerenderContents* pc) { 82 next_pc_.reset(pc); 83 } 84 85 // Shorthand to add a simple preload with no aliases. 86 bool AddSimplePreload(const GURL& url) { 87 return AddPreload(url, std::vector<GURL>(), GURL()); 88 } 89 90 bool IsPendingEntry(const GURL& url) { 91 return (PrerenderManager::FindPendingEntry(url) != NULL); 92 } 93 94 void set_rate_limit_enabled(bool enabled) { rate_limit_enabled_ = true; } 95 96 PrerenderContents* next_pc() { return next_pc_.get(); } 97 98 protected: 99 virtual ~TestPrerenderManager() { 100 if (next_pc()) { 101 next_pc()->set_final_status( 102 FINAL_STATUS_MANAGER_SHUTDOWN); 103 } 104 } 105 106 private: 107 virtual base::Time GetCurrentTime() const OVERRIDE { 108 return time_; 109 } 110 111 virtual base::TimeTicks GetCurrentTimeTicks() const OVERRIDE { 112 return time_ticks_; 113 } 114 115 virtual PrerenderContents* CreatePrerenderContents( 116 const GURL& url, 117 const std::vector<GURL>& alias_urls, 118 const GURL& referrer) OVERRIDE { 119 DCHECK(next_pc_.get()); 120 return next_pc_.release(); 121 } 122 123 base::Time time_; 124 base::TimeTicks time_ticks_; 125 scoped_ptr<PrerenderContents> next_pc_; 126}; 127 128} // namespace 129 130class PrerenderManagerTest : public testing::Test { 131 public: 132 PrerenderManagerTest() : prerender_manager_(new TestPrerenderManager()), 133 ui_thread_(BrowserThread::UI, &message_loop_) { 134 } 135 136 protected: 137 scoped_refptr<TestPrerenderManager> prerender_manager_; 138 139 private: 140 // Needed to pass PrerenderManager's DCHECKs. 141 MessageLoop message_loop_; 142 BrowserThread ui_thread_; 143}; 144 145TEST_F(PrerenderManagerTest, EmptyTest) { 146 GURL url("http://www.google.com/"); 147 EXPECT_FALSE(prerender_manager_->MaybeUsePreloadedPage(NULL, url)); 148} 149 150TEST_F(PrerenderManagerTest, FoundTest) { 151 GURL url("http://www.google.com/"); 152 DummyPrerenderContents* pc = 153 new DummyPrerenderContents(prerender_manager_.get(), 154 url, 155 FINAL_STATUS_USED); 156 prerender_manager_->SetNextPrerenderContents(pc); 157 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); 158 EXPECT_TRUE(pc->has_started()); 159 ASSERT_EQ(pc, prerender_manager_->GetEntry(url)); 160 pc->set_final_status(FINAL_STATUS_USED); 161 delete pc; 162} 163 164// Make sure that if queue a request, and a second prerender request for the 165// same URL comes in, that we drop the second request and keep the first one. 166TEST_F(PrerenderManagerTest, DropSecondRequestTest) { 167 GURL url("http://www.google.com/"); 168 DummyPrerenderContents* pc = 169 new DummyPrerenderContents(prerender_manager_.get(), url, 170 FINAL_STATUS_USED); 171 DummyPrerenderContents* null = NULL; 172 prerender_manager_->SetNextPrerenderContents(pc); 173 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); 174 EXPECT_EQ(null, prerender_manager_->next_pc()); 175 EXPECT_TRUE(pc->has_started()); 176 DummyPrerenderContents* pc1 = 177 new DummyPrerenderContents( 178 prerender_manager_.get(), url, 179 FINAL_STATUS_MANAGER_SHUTDOWN); 180 prerender_manager_->SetNextPrerenderContents(pc1); 181 EXPECT_FALSE(prerender_manager_->AddSimplePreload(url)); 182 EXPECT_EQ(pc1, prerender_manager_->next_pc()); 183 EXPECT_FALSE(pc1->has_started()); 184 ASSERT_EQ(pc, prerender_manager_->GetEntry(url)); 185 pc->set_final_status(FINAL_STATUS_USED); 186 delete pc; 187} 188 189// Ensure that we expire a prerendered page after the max. permitted time. 190TEST_F(PrerenderManagerTest, ExpireTest) { 191 GURL url("http://www.google.com/"); 192 DummyPrerenderContents* pc = 193 new DummyPrerenderContents(prerender_manager_.get(), url, 194 FINAL_STATUS_TIMED_OUT); 195 DummyPrerenderContents* null = NULL; 196 prerender_manager_->SetNextPrerenderContents(pc); 197 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); 198 EXPECT_EQ(null, prerender_manager_->next_pc()); 199 EXPECT_TRUE(pc->has_started()); 200 prerender_manager_->AdvanceTime(prerender_manager_->max_prerender_age() 201 + base::TimeDelta::FromSeconds(1)); 202 ASSERT_EQ(null, prerender_manager_->GetEntry(url)); 203} 204 205// LRU Test. Make sure that if we prerender more than one request, that 206// the oldest one will be dropped. 207TEST_F(PrerenderManagerTest, DropOldestRequestTest) { 208 GURL url("http://www.google.com/"); 209 DummyPrerenderContents* pc = 210 new DummyPrerenderContents(prerender_manager_.get(), url, 211 FINAL_STATUS_EVICTED); 212 DummyPrerenderContents* null = NULL; 213 prerender_manager_->SetNextPrerenderContents(pc); 214 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); 215 EXPECT_EQ(null, prerender_manager_->next_pc()); 216 EXPECT_TRUE(pc->has_started()); 217 GURL url1("http://news.google.com/"); 218 DummyPrerenderContents* pc1 = 219 new DummyPrerenderContents(prerender_manager_.get(), url1, 220 FINAL_STATUS_USED); 221 prerender_manager_->SetNextPrerenderContents(pc1); 222 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url1)); 223 EXPECT_EQ(null, prerender_manager_->next_pc()); 224 EXPECT_TRUE(pc1->has_started()); 225 ASSERT_EQ(null, prerender_manager_->GetEntry(url)); 226 ASSERT_EQ(pc1, prerender_manager_->GetEntry(url1)); 227 pc1->set_final_status(FINAL_STATUS_USED); 228 delete pc1; 229} 230 231// Two element prerender test. Ensure that the LRU operates correctly if we 232// permit 2 elements to be kept prerendered. 233TEST_F(PrerenderManagerTest, TwoElementPrerenderTest) { 234 prerender_manager_->set_max_elements(2); 235 GURL url("http://www.google.com/"); 236 DummyPrerenderContents* pc = 237 new DummyPrerenderContents(prerender_manager_.get(), url, 238 FINAL_STATUS_EVICTED); 239 DummyPrerenderContents* null = NULL; 240 prerender_manager_->SetNextPrerenderContents(pc); 241 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); 242 EXPECT_EQ(null, prerender_manager_->next_pc()); 243 EXPECT_TRUE(pc->has_started()); 244 GURL url1("http://news.google.com/"); 245 DummyPrerenderContents* pc1 = 246 new DummyPrerenderContents(prerender_manager_.get(), url1, 247 FINAL_STATUS_USED); 248 prerender_manager_->SetNextPrerenderContents(pc1); 249 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url1)); 250 EXPECT_EQ(null, prerender_manager_->next_pc()); 251 EXPECT_TRUE(pc1->has_started()); 252 GURL url2("http://images.google.com/"); 253 DummyPrerenderContents* pc2 = 254 new DummyPrerenderContents(prerender_manager_.get(), url2, 255 FINAL_STATUS_USED); 256 prerender_manager_->SetNextPrerenderContents(pc2); 257 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url2)); 258 EXPECT_EQ(null, prerender_manager_->next_pc()); 259 EXPECT_TRUE(pc2->has_started()); 260 ASSERT_EQ(null, prerender_manager_->GetEntry(url)); 261 ASSERT_EQ(pc1, prerender_manager_->GetEntry(url1)); 262 ASSERT_EQ(pc2, prerender_manager_->GetEntry(url2)); 263 pc1->set_final_status(FINAL_STATUS_USED); 264 delete pc1; 265 pc2->set_final_status(FINAL_STATUS_USED); 266 delete pc2; 267} 268 269TEST_F(PrerenderManagerTest, AliasURLTest) { 270 GURL url("http://www.google.com/"); 271 GURL alias_url1("http://www.google.com/index.html"); 272 GURL alias_url2("http://google.com/"); 273 GURL not_an_alias_url("http://google.com/index.html"); 274 std::vector<GURL> alias_urls; 275 alias_urls.push_back(alias_url1); 276 alias_urls.push_back(alias_url2); 277 DummyPrerenderContents* pc = 278 new DummyPrerenderContents(prerender_manager_.get(), url, alias_urls, 279 FINAL_STATUS_USED); 280 // Test that all of the aliases work, but nont_an_alias_url does not. 281 prerender_manager_->SetNextPrerenderContents(pc); 282 EXPECT_TRUE(prerender_manager_->AddPreload(url, alias_urls, GURL())); 283 ASSERT_EQ(NULL, prerender_manager_->GetEntry(not_an_alias_url)); 284 ASSERT_EQ(pc, prerender_manager_->GetEntry(alias_url1)); 285 prerender_manager_->SetNextPrerenderContents(pc); 286 EXPECT_TRUE(prerender_manager_->AddPreload(url, alias_urls, GURL())); 287 ASSERT_EQ(pc, prerender_manager_->GetEntry(alias_url2)); 288 prerender_manager_->SetNextPrerenderContents(pc); 289 EXPECT_TRUE(prerender_manager_->AddPreload(url, alias_urls, GURL())); 290 ASSERT_EQ(pc, prerender_manager_->GetEntry(url)); 291 292 // Test that alias URLs can not be added. 293 prerender_manager_->SetNextPrerenderContents(pc); 294 EXPECT_TRUE(prerender_manager_->AddPreload(url, alias_urls, GURL())); 295 EXPECT_FALSE(prerender_manager_->AddSimplePreload(url)); 296 EXPECT_FALSE(prerender_manager_->AddSimplePreload(alias_url1)); 297 EXPECT_FALSE(prerender_manager_->AddSimplePreload(alias_url2)); 298 ASSERT_EQ(pc, prerender_manager_->GetEntry(url)); 299 300 pc->set_final_status(FINAL_STATUS_USED); 301 delete pc; 302} 303 304// Ensure that we ignore prerender requests within the rate limit. 305TEST_F(PrerenderManagerTest, RateLimitInWindowTest) { 306 GURL url("http://www.google.com/"); 307 DummyPrerenderContents* pc = 308 new DummyPrerenderContents(prerender_manager_.get(), url, 309 FINAL_STATUS_MANAGER_SHUTDOWN); 310 DummyPrerenderContents* null = NULL; 311 prerender_manager_->SetNextPrerenderContents(pc); 312 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); 313 EXPECT_EQ(null, prerender_manager_->next_pc()); 314 EXPECT_TRUE(pc->has_started()); 315 316 prerender_manager_->set_rate_limit_enabled(true); 317 prerender_manager_->AdvanceTimeTicks(base::TimeDelta::FromMilliseconds(1)); 318 319 GURL url1("http://news.google.com/"); 320 DummyPrerenderContents* rate_limit_pc = 321 new DummyPrerenderContents(prerender_manager_.get(), url1, 322 FINAL_STATUS_MANAGER_SHUTDOWN); 323 prerender_manager_->SetNextPrerenderContents(rate_limit_pc); 324 EXPECT_FALSE(prerender_manager_->AddSimplePreload(url1)); 325 prerender_manager_->set_rate_limit_enabled(false); 326} 327 328// Ensure that we don't ignore prerender requests outside the rate limit. 329TEST_F(PrerenderManagerTest, RateLimitOutsideWindowTest) { 330 GURL url("http://www.google.com/"); 331 DummyPrerenderContents* pc = 332 new DummyPrerenderContents(prerender_manager_.get(), url, 333 FINAL_STATUS_EVICTED); 334 DummyPrerenderContents* null = NULL; 335 prerender_manager_->SetNextPrerenderContents(pc); 336 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); 337 EXPECT_EQ(null, prerender_manager_->next_pc()); 338 EXPECT_TRUE(pc->has_started()); 339 340 prerender_manager_->set_rate_limit_enabled(true); 341 prerender_manager_->AdvanceTimeTicks(base::TimeDelta::FromMilliseconds(2000)); 342 343 GURL url1("http://news.google.com/"); 344 DummyPrerenderContents* rate_limit_pc = 345 new DummyPrerenderContents(prerender_manager_.get(), url1, 346 FINAL_STATUS_MANAGER_SHUTDOWN); 347 prerender_manager_->SetNextPrerenderContents(rate_limit_pc); 348 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url1)); 349 EXPECT_EQ(null, prerender_manager_->next_pc()); 350 EXPECT_TRUE(rate_limit_pc->has_started()); 351 prerender_manager_->set_rate_limit_enabled(false); 352} 353 354TEST_F(PrerenderManagerTest, PendingPreloadTest) { 355 GURL url("http://www.google.com/"); 356 DummyPrerenderContents* pc = 357 new DummyPrerenderContents(prerender_manager_.get(), 358 url, 359 FINAL_STATUS_USED); 360 prerender_manager_->SetNextPrerenderContents(pc); 361 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); 362 363 int child_id; 364 int route_id; 365 ASSERT_TRUE(pc->GetChildId(&child_id)); 366 ASSERT_TRUE(pc->GetRouteId(&route_id)); 367 368 GURL pending_url("http://news.google.com/"); 369 370 prerender_manager_->AddPendingPreload(std::make_pair(child_id, route_id), 371 pending_url, 372 std::vector<GURL>(), 373 url); 374 375 EXPECT_TRUE(prerender_manager_->IsPendingEntry(pending_url)); 376 EXPECT_TRUE(pc->has_started()); 377 ASSERT_EQ(pc, prerender_manager_->GetEntry(url)); 378 pc->set_final_status(FINAL_STATUS_USED); 379 380 delete pc; 381} 382 383TEST_F(PrerenderManagerTest, PendingPreloadSkippedTest) { 384 GURL url("http://www.google.com/"); 385 DummyPrerenderContents* pc = 386 new DummyPrerenderContents(prerender_manager_.get(), 387 url, 388 FINAL_STATUS_TIMED_OUT); 389 prerender_manager_->SetNextPrerenderContents(pc); 390 391 int child_id; 392 int route_id; 393 ASSERT_TRUE(pc->GetChildId(&child_id)); 394 ASSERT_TRUE(pc->GetRouteId(&route_id)); 395 396 EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); 397 prerender_manager_->AdvanceTime(prerender_manager_->max_prerender_age() 398 + base::TimeDelta::FromSeconds(1)); 399 // GetEntry will cull old entries which should now include pc. 400 ASSERT_EQ(NULL, prerender_manager_->GetEntry(url)); 401 402 GURL pending_url("http://news.google.com/"); 403 404 prerender_manager_->AddPendingPreload(std::make_pair(child_id, route_id), 405 pending_url, 406 std::vector<GURL>(), 407 url); 408 EXPECT_FALSE(prerender_manager_->IsPendingEntry(pending_url)); 409} 410 411// Ensure that extracting a urlencoded URL in the url= query string component 412// works. 413TEST_F(PrerenderManagerTest, ExtractURLInQueryStringTest) { 414 GURL result; 415 EXPECT_TRUE(PrerenderManager::MaybeGetQueryStringBasedAliasURL( 416 GURL("http://www.google.com/url?sa=t&source=web&cd=1&ved=0CBcQFjAA&url=http%3A%2F%2Fwww.abercrombie.com%2Fwebapp%2Fwcs%2Fstores%2Fservlet%2FStoreLocator%3FcatalogId%3D%26storeId%3D10051%26langId%3D-1&rct=j&q=allinurl%3A%26&ei=KLyUTYGSEdTWiAKUmLCdCQ&usg=AFQjCNF8nJ2MpBFfr1ijO39_f22bcKyccw&sig2=2ymyGpO0unJwU1d4kdCUjQ"), 417 &result)); 418 ASSERT_EQ(GURL("http://www.abercrombie.com/webapp/wcs/stores/servlet/StoreLocator?catalogId=&storeId=10051&langId=-1").spec(), result.spec()); 419 EXPECT_FALSE(PrerenderManager::MaybeGetQueryStringBasedAliasURL( 420 GURL("http://www.google.com/url?sadf=test&blah=blahblahblah"), &result)); 421 EXPECT_FALSE(PrerenderManager::MaybeGetQueryStringBasedAliasURL( 422 GURL("http://www.google.com/?url=INVALIDurlsAREsoMUCHfun.com"), &result)); 423 EXPECT_TRUE(PrerenderManager::MaybeGetQueryStringBasedAliasURL( 424 GURL("http://www.google.com/?url=http://validURLSareGREAT.com"), 425 &result)); 426 ASSERT_EQ(GURL("http://validURLSareGREAT.com").spec(), result.spec()); 427} 428 429} // namespace prerender 430