| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/time.h" |
| #include "chrome/browser/prerender/prerender_contents.h" |
| #include "chrome/browser/prerender/prerender_manager.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/renderer_host/render_view_host.h" |
| #include "content/browser/renderer_host/render_process_host.h" |
| #include "googleurl/src/gurl.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace prerender { |
| |
| namespace { |
| |
| class DummyPrerenderContents : public PrerenderContents { |
| public: |
| DummyPrerenderContents(PrerenderManager* prerender_manager, |
| const GURL& url, |
| FinalStatus expected_final_status) |
| : PrerenderContents(prerender_manager, NULL, url, |
| std::vector<GURL>(), GURL()), |
| has_started_(false), |
| expected_final_status_(expected_final_status) { |
| } |
| |
| DummyPrerenderContents(PrerenderManager* prerender_manager, |
| const GURL& url, |
| const std::vector<GURL> alias_urls, |
| FinalStatus expected_final_status) |
| : PrerenderContents(prerender_manager, NULL, url, alias_urls, GURL()), |
| has_started_(false), |
| expected_final_status_(expected_final_status) { |
| } |
| |
| virtual ~DummyPrerenderContents() { |
| EXPECT_EQ(expected_final_status_, final_status()); |
| } |
| |
| virtual void StartPrerendering() OVERRIDE { |
| has_started_ = true; |
| } |
| |
| virtual bool GetChildId(int* child_id) const OVERRIDE { |
| *child_id = 0; |
| return true; |
| } |
| |
| virtual bool GetRouteId(int* route_id) const OVERRIDE { |
| *route_id = 0; |
| return true; |
| } |
| |
| bool has_started() const { return has_started_; } |
| |
| private: |
| bool has_started_; |
| FinalStatus expected_final_status_; |
| }; |
| |
| class TestPrerenderManager : public PrerenderManager { |
| public: |
| TestPrerenderManager() |
| : PrerenderManager(NULL), |
| time_(base::Time::Now()), |
| time_ticks_(base::TimeTicks::Now()), |
| next_pc_(NULL) { |
| rate_limit_enabled_ = false; |
| } |
| |
| void AdvanceTime(base::TimeDelta delta) { |
| time_ += delta; |
| } |
| |
| void AdvanceTimeTicks(base::TimeDelta delta) { |
| time_ticks_ += delta; |
| } |
| |
| void SetNextPrerenderContents(PrerenderContents* pc) { |
| next_pc_.reset(pc); |
| } |
| |
| // Shorthand to add a simple preload with no aliases. |
| bool AddSimplePreload(const GURL& url) { |
| return AddPreload(url, std::vector<GURL>(), GURL()); |
| } |
| |
| bool IsPendingEntry(const GURL& url) { |
| return (PrerenderManager::FindPendingEntry(url) != NULL); |
| } |
| |
| void set_rate_limit_enabled(bool enabled) { rate_limit_enabled_ = true; } |
| |
| PrerenderContents* next_pc() { return next_pc_.get(); } |
| |
| protected: |
| virtual ~TestPrerenderManager() { |
| if (next_pc()) { |
| next_pc()->set_final_status( |
| FINAL_STATUS_MANAGER_SHUTDOWN); |
| } |
| } |
| |
| private: |
| virtual base::Time GetCurrentTime() const OVERRIDE { |
| return time_; |
| } |
| |
| virtual base::TimeTicks GetCurrentTimeTicks() const OVERRIDE { |
| return time_ticks_; |
| } |
| |
| virtual PrerenderContents* CreatePrerenderContents( |
| const GURL& url, |
| const std::vector<GURL>& alias_urls, |
| const GURL& referrer) OVERRIDE { |
| DCHECK(next_pc_.get()); |
| return next_pc_.release(); |
| } |
| |
| base::Time time_; |
| base::TimeTicks time_ticks_; |
| scoped_ptr<PrerenderContents> next_pc_; |
| }; |
| |
| } // namespace |
| |
| class PrerenderManagerTest : public testing::Test { |
| public: |
| PrerenderManagerTest() : prerender_manager_(new TestPrerenderManager()), |
| ui_thread_(BrowserThread::UI, &message_loop_) { |
| } |
| |
| protected: |
| scoped_refptr<TestPrerenderManager> prerender_manager_; |
| |
| private: |
| // Needed to pass PrerenderManager's DCHECKs. |
| MessageLoop message_loop_; |
| BrowserThread ui_thread_; |
| }; |
| |
| TEST_F(PrerenderManagerTest, EmptyTest) { |
| GURL url("http://www.google.com/"); |
| EXPECT_FALSE(prerender_manager_->MaybeUsePreloadedPage(NULL, url)); |
| } |
| |
| TEST_F(PrerenderManagerTest, FoundTest) { |
| GURL url("http://www.google.com/"); |
| DummyPrerenderContents* pc = |
| new DummyPrerenderContents(prerender_manager_.get(), |
| url, |
| FINAL_STATUS_USED); |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); |
| EXPECT_TRUE(pc->has_started()); |
| ASSERT_EQ(pc, prerender_manager_->GetEntry(url)); |
| pc->set_final_status(FINAL_STATUS_USED); |
| delete pc; |
| } |
| |
| // Make sure that if queue a request, and a second prerender request for the |
| // same URL comes in, that we drop the second request and keep the first one. |
| TEST_F(PrerenderManagerTest, DropSecondRequestTest) { |
| GURL url("http://www.google.com/"); |
| DummyPrerenderContents* pc = |
| new DummyPrerenderContents(prerender_manager_.get(), url, |
| FINAL_STATUS_USED); |
| DummyPrerenderContents* null = NULL; |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); |
| EXPECT_EQ(null, prerender_manager_->next_pc()); |
| EXPECT_TRUE(pc->has_started()); |
| DummyPrerenderContents* pc1 = |
| new DummyPrerenderContents( |
| prerender_manager_.get(), url, |
| FINAL_STATUS_MANAGER_SHUTDOWN); |
| prerender_manager_->SetNextPrerenderContents(pc1); |
| EXPECT_FALSE(prerender_manager_->AddSimplePreload(url)); |
| EXPECT_EQ(pc1, prerender_manager_->next_pc()); |
| EXPECT_FALSE(pc1->has_started()); |
| ASSERT_EQ(pc, prerender_manager_->GetEntry(url)); |
| pc->set_final_status(FINAL_STATUS_USED); |
| delete pc; |
| } |
| |
| // Ensure that we expire a prerendered page after the max. permitted time. |
| TEST_F(PrerenderManagerTest, ExpireTest) { |
| GURL url("http://www.google.com/"); |
| DummyPrerenderContents* pc = |
| new DummyPrerenderContents(prerender_manager_.get(), url, |
| FINAL_STATUS_TIMED_OUT); |
| DummyPrerenderContents* null = NULL; |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); |
| EXPECT_EQ(null, prerender_manager_->next_pc()); |
| EXPECT_TRUE(pc->has_started()); |
| prerender_manager_->AdvanceTime(prerender_manager_->max_prerender_age() |
| + base::TimeDelta::FromSeconds(1)); |
| ASSERT_EQ(null, prerender_manager_->GetEntry(url)); |
| } |
| |
| // LRU Test. Make sure that if we prerender more than one request, that |
| // the oldest one will be dropped. |
| TEST_F(PrerenderManagerTest, DropOldestRequestTest) { |
| GURL url("http://www.google.com/"); |
| DummyPrerenderContents* pc = |
| new DummyPrerenderContents(prerender_manager_.get(), url, |
| FINAL_STATUS_EVICTED); |
| DummyPrerenderContents* null = NULL; |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); |
| EXPECT_EQ(null, prerender_manager_->next_pc()); |
| EXPECT_TRUE(pc->has_started()); |
| GURL url1("http://news.google.com/"); |
| DummyPrerenderContents* pc1 = |
| new DummyPrerenderContents(prerender_manager_.get(), url1, |
| FINAL_STATUS_USED); |
| prerender_manager_->SetNextPrerenderContents(pc1); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url1)); |
| EXPECT_EQ(null, prerender_manager_->next_pc()); |
| EXPECT_TRUE(pc1->has_started()); |
| ASSERT_EQ(null, prerender_manager_->GetEntry(url)); |
| ASSERT_EQ(pc1, prerender_manager_->GetEntry(url1)); |
| pc1->set_final_status(FINAL_STATUS_USED); |
| delete pc1; |
| } |
| |
| // Two element prerender test. Ensure that the LRU operates correctly if we |
| // permit 2 elements to be kept prerendered. |
| TEST_F(PrerenderManagerTest, TwoElementPrerenderTest) { |
| prerender_manager_->set_max_elements(2); |
| GURL url("http://www.google.com/"); |
| DummyPrerenderContents* pc = |
| new DummyPrerenderContents(prerender_manager_.get(), url, |
| FINAL_STATUS_EVICTED); |
| DummyPrerenderContents* null = NULL; |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); |
| EXPECT_EQ(null, prerender_manager_->next_pc()); |
| EXPECT_TRUE(pc->has_started()); |
| GURL url1("http://news.google.com/"); |
| DummyPrerenderContents* pc1 = |
| new DummyPrerenderContents(prerender_manager_.get(), url1, |
| FINAL_STATUS_USED); |
| prerender_manager_->SetNextPrerenderContents(pc1); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url1)); |
| EXPECT_EQ(null, prerender_manager_->next_pc()); |
| EXPECT_TRUE(pc1->has_started()); |
| GURL url2("http://images.google.com/"); |
| DummyPrerenderContents* pc2 = |
| new DummyPrerenderContents(prerender_manager_.get(), url2, |
| FINAL_STATUS_USED); |
| prerender_manager_->SetNextPrerenderContents(pc2); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url2)); |
| EXPECT_EQ(null, prerender_manager_->next_pc()); |
| EXPECT_TRUE(pc2->has_started()); |
| ASSERT_EQ(null, prerender_manager_->GetEntry(url)); |
| ASSERT_EQ(pc1, prerender_manager_->GetEntry(url1)); |
| ASSERT_EQ(pc2, prerender_manager_->GetEntry(url2)); |
| pc1->set_final_status(FINAL_STATUS_USED); |
| delete pc1; |
| pc2->set_final_status(FINAL_STATUS_USED); |
| delete pc2; |
| } |
| |
| TEST_F(PrerenderManagerTest, AliasURLTest) { |
| GURL url("http://www.google.com/"); |
| GURL alias_url1("http://www.google.com/index.html"); |
| GURL alias_url2("http://google.com/"); |
| GURL not_an_alias_url("http://google.com/index.html"); |
| std::vector<GURL> alias_urls; |
| alias_urls.push_back(alias_url1); |
| alias_urls.push_back(alias_url2); |
| DummyPrerenderContents* pc = |
| new DummyPrerenderContents(prerender_manager_.get(), url, alias_urls, |
| FINAL_STATUS_USED); |
| // Test that all of the aliases work, but nont_an_alias_url does not. |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddPreload(url, alias_urls, GURL())); |
| ASSERT_EQ(NULL, prerender_manager_->GetEntry(not_an_alias_url)); |
| ASSERT_EQ(pc, prerender_manager_->GetEntry(alias_url1)); |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddPreload(url, alias_urls, GURL())); |
| ASSERT_EQ(pc, prerender_manager_->GetEntry(alias_url2)); |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddPreload(url, alias_urls, GURL())); |
| ASSERT_EQ(pc, prerender_manager_->GetEntry(url)); |
| |
| // Test that alias URLs can not be added. |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddPreload(url, alias_urls, GURL())); |
| EXPECT_FALSE(prerender_manager_->AddSimplePreload(url)); |
| EXPECT_FALSE(prerender_manager_->AddSimplePreload(alias_url1)); |
| EXPECT_FALSE(prerender_manager_->AddSimplePreload(alias_url2)); |
| ASSERT_EQ(pc, prerender_manager_->GetEntry(url)); |
| |
| pc->set_final_status(FINAL_STATUS_USED); |
| delete pc; |
| } |
| |
| // Ensure that we ignore prerender requests within the rate limit. |
| TEST_F(PrerenderManagerTest, RateLimitInWindowTest) { |
| GURL url("http://www.google.com/"); |
| DummyPrerenderContents* pc = |
| new DummyPrerenderContents(prerender_manager_.get(), url, |
| FINAL_STATUS_MANAGER_SHUTDOWN); |
| DummyPrerenderContents* null = NULL; |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); |
| EXPECT_EQ(null, prerender_manager_->next_pc()); |
| EXPECT_TRUE(pc->has_started()); |
| |
| prerender_manager_->set_rate_limit_enabled(true); |
| prerender_manager_->AdvanceTimeTicks(base::TimeDelta::FromMilliseconds(1)); |
| |
| GURL url1("http://news.google.com/"); |
| DummyPrerenderContents* rate_limit_pc = |
| new DummyPrerenderContents(prerender_manager_.get(), url1, |
| FINAL_STATUS_MANAGER_SHUTDOWN); |
| prerender_manager_->SetNextPrerenderContents(rate_limit_pc); |
| EXPECT_FALSE(prerender_manager_->AddSimplePreload(url1)); |
| prerender_manager_->set_rate_limit_enabled(false); |
| } |
| |
| // Ensure that we don't ignore prerender requests outside the rate limit. |
| TEST_F(PrerenderManagerTest, RateLimitOutsideWindowTest) { |
| GURL url("http://www.google.com/"); |
| DummyPrerenderContents* pc = |
| new DummyPrerenderContents(prerender_manager_.get(), url, |
| FINAL_STATUS_EVICTED); |
| DummyPrerenderContents* null = NULL; |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); |
| EXPECT_EQ(null, prerender_manager_->next_pc()); |
| EXPECT_TRUE(pc->has_started()); |
| |
| prerender_manager_->set_rate_limit_enabled(true); |
| prerender_manager_->AdvanceTimeTicks(base::TimeDelta::FromMilliseconds(2000)); |
| |
| GURL url1("http://news.google.com/"); |
| DummyPrerenderContents* rate_limit_pc = |
| new DummyPrerenderContents(prerender_manager_.get(), url1, |
| FINAL_STATUS_MANAGER_SHUTDOWN); |
| prerender_manager_->SetNextPrerenderContents(rate_limit_pc); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url1)); |
| EXPECT_EQ(null, prerender_manager_->next_pc()); |
| EXPECT_TRUE(rate_limit_pc->has_started()); |
| prerender_manager_->set_rate_limit_enabled(false); |
| } |
| |
| TEST_F(PrerenderManagerTest, PendingPreloadTest) { |
| GURL url("http://www.google.com/"); |
| DummyPrerenderContents* pc = |
| new DummyPrerenderContents(prerender_manager_.get(), |
| url, |
| FINAL_STATUS_USED); |
| prerender_manager_->SetNextPrerenderContents(pc); |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); |
| |
| int child_id; |
| int route_id; |
| ASSERT_TRUE(pc->GetChildId(&child_id)); |
| ASSERT_TRUE(pc->GetRouteId(&route_id)); |
| |
| GURL pending_url("http://news.google.com/"); |
| |
| prerender_manager_->AddPendingPreload(std::make_pair(child_id, route_id), |
| pending_url, |
| std::vector<GURL>(), |
| url); |
| |
| EXPECT_TRUE(prerender_manager_->IsPendingEntry(pending_url)); |
| EXPECT_TRUE(pc->has_started()); |
| ASSERT_EQ(pc, prerender_manager_->GetEntry(url)); |
| pc->set_final_status(FINAL_STATUS_USED); |
| |
| delete pc; |
| } |
| |
| TEST_F(PrerenderManagerTest, PendingPreloadSkippedTest) { |
| GURL url("http://www.google.com/"); |
| DummyPrerenderContents* pc = |
| new DummyPrerenderContents(prerender_manager_.get(), |
| url, |
| FINAL_STATUS_TIMED_OUT); |
| prerender_manager_->SetNextPrerenderContents(pc); |
| |
| int child_id; |
| int route_id; |
| ASSERT_TRUE(pc->GetChildId(&child_id)); |
| ASSERT_TRUE(pc->GetRouteId(&route_id)); |
| |
| EXPECT_TRUE(prerender_manager_->AddSimplePreload(url)); |
| prerender_manager_->AdvanceTime(prerender_manager_->max_prerender_age() |
| + base::TimeDelta::FromSeconds(1)); |
| // GetEntry will cull old entries which should now include pc. |
| ASSERT_EQ(NULL, prerender_manager_->GetEntry(url)); |
| |
| GURL pending_url("http://news.google.com/"); |
| |
| prerender_manager_->AddPendingPreload(std::make_pair(child_id, route_id), |
| pending_url, |
| std::vector<GURL>(), |
| url); |
| EXPECT_FALSE(prerender_manager_->IsPendingEntry(pending_url)); |
| } |
| |
| // Ensure that extracting a urlencoded URL in the url= query string component |
| // works. |
| TEST_F(PrerenderManagerTest, ExtractURLInQueryStringTest) { |
| GURL result; |
| EXPECT_TRUE(PrerenderManager::MaybeGetQueryStringBasedAliasURL( |
| 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"), |
| &result)); |
| ASSERT_EQ(GURL("http://www.abercrombie.com/webapp/wcs/stores/servlet/StoreLocator?catalogId=&storeId=10051&langId=-1").spec(), result.spec()); |
| EXPECT_FALSE(PrerenderManager::MaybeGetQueryStringBasedAliasURL( |
| GURL("http://www.google.com/url?sadf=test&blah=blahblahblah"), &result)); |
| EXPECT_FALSE(PrerenderManager::MaybeGetQueryStringBasedAliasURL( |
| GURL("http://www.google.com/?url=INVALIDurlsAREsoMUCHfun.com"), &result)); |
| EXPECT_TRUE(PrerenderManager::MaybeGetQueryStringBasedAliasURL( |
| GURL("http://www.google.com/?url=http://validURLSareGREAT.com"), |
| &result)); |
| ASSERT_EQ(GURL("http://validURLSareGREAT.com").spec(), result.spec()); |
| } |
| |
| } // namespace prerender |