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