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 <iostream>
6#include "base/memory/ref_counted.h"
7#include "base/memory/scoped_ptr.h"
8#include "base/message_loop/message_loop.h"
9#include "base/run_loop.h"
10#include "base/time/time.h"
11#include "chrome/browser/history/history_service.h"
12#include "chrome/browser/history/history_service_factory.h"
13#include "chrome/browser/predictors/resource_prefetch_predictor.h"
14#include "chrome/browser/predictors/resource_prefetch_predictor_tables.h"
15#include "chrome/test/base/testing_profile.h"
16#include "components/history/core/browser/history_types.h"
17#include "content/public/test/test_browser_thread.h"
18#include "testing/gmock/include/gmock/gmock.h"
19#include "testing/gtest/include/gtest/gtest.h"
20
21using testing::ContainerEq;
22using testing::Pointee;
23using testing::SetArgPointee;
24using testing::StrictMock;
25
26namespace predictors {
27
28typedef ResourcePrefetchPredictor::URLRequestSummary URLRequestSummary;
29typedef ResourcePrefetchPredictorTables::ResourceRow ResourceRow;
30typedef std::vector<ResourceRow> ResourceRows;
31typedef ResourcePrefetchPredictorTables::PrefetchData PrefetchData;
32typedef ResourcePrefetchPredictorTables::PrefetchDataMap PrefetchDataMap;
33
34// For printing failures nicely.
35void PrintTo(const ResourceRow& row, ::std::ostream* os) {
36  *os << "[" << row.primary_key << "," << row.resource_url
37      << "," << row.resource_type << "," << row.number_of_hits
38      << "," << row.number_of_misses << "," << row.consecutive_misses
39      << "," << row.average_position << "," << row.score << "]";
40}
41
42void PrintTo(const PrefetchData& data, ::std::ostream* os) {
43  *os << "[" << data.key_type << "," << data.primary_key
44      << "," << data.last_visit.ToInternalValue() << "]\n";
45  for (ResourceRows::const_iterator it = data.resources.begin();
46       it != data.resources.end(); ++it) {
47    *os << "\t\t";
48    PrintTo(*it, os);
49    *os << "\n";
50  }
51}
52
53class MockResourcePrefetchPredictorTables
54    : public ResourcePrefetchPredictorTables {
55 public:
56  MockResourcePrefetchPredictorTables() { }
57
58  MOCK_METHOD2(GetAllData, void(PrefetchDataMap* url_data_map,
59                                PrefetchDataMap* host_data_map));
60  MOCK_METHOD2(UpdateData, void(const PrefetchData& url_data,
61                                const PrefetchData& host_data));
62  MOCK_METHOD2(DeleteData, void(const std::vector<std::string>& urls,
63                                const std::vector<std::string>& hosts));
64  MOCK_METHOD2(DeleteSingleDataPoint, void(const std::string& key,
65                                           PrefetchKeyType key_type));
66  MOCK_METHOD0(DeleteAllData, void());
67
68 protected:
69  ~MockResourcePrefetchPredictorTables() { }
70};
71
72class ResourcePrefetchPredictorTest : public testing::Test {
73 public:
74  ResourcePrefetchPredictorTest();
75  virtual ~ResourcePrefetchPredictorTest();
76  virtual void SetUp() OVERRIDE;
77  virtual void TearDown() OVERRIDE;
78
79 protected:
80  void AddUrlToHistory(const std::string& url, int visit_count) {
81    HistoryServiceFactory::GetForProfile(profile_.get(),
82                                         Profile::EXPLICIT_ACCESS)->
83        AddPageWithDetails(
84            GURL(url),
85            base::string16(),
86            visit_count,
87            0,
88            base::Time::Now(),
89            false,
90            history::SOURCE_BROWSED);
91    profile_->BlockUntilHistoryProcessesPendingRequests();
92  }
93
94  NavigationID CreateNavigationID(int process_id,
95                                  int render_frame_id,
96                                  const std::string& main_frame_url) {
97    NavigationID navigation_id;
98    navigation_id.render_process_id = process_id;
99    navigation_id.render_frame_id = render_frame_id;
100    navigation_id.main_frame_url = GURL(main_frame_url);
101    navigation_id.creation_time = base::TimeTicks::Now();
102    return navigation_id;
103  }
104
105  ResourcePrefetchPredictor::URLRequestSummary CreateURLRequestSummary(
106      int process_id,
107      int render_frame_id,
108      const std::string& main_frame_url,
109      const std::string& resource_url,
110      content::ResourceType resource_type,
111      const std::string& mime_type,
112      bool was_cached) {
113    ResourcePrefetchPredictor::URLRequestSummary summary;
114    summary.navigation_id = CreateNavigationID(process_id, render_frame_id,
115                                               main_frame_url);
116    summary.resource_url = GURL(resource_url);
117    summary.resource_type = resource_type;
118    summary.mime_type = mime_type;
119    summary.was_cached = was_cached;
120    return summary;
121  }
122
123  void InitializePredictor() {
124    predictor_->StartInitialization();
125    base::RunLoop loop;
126    loop.RunUntilIdle();  // Runs the DB lookup.
127    profile_->BlockUntilHistoryProcessesPendingRequests();
128  }
129
130  bool URLRequestSummaryAreEqual(const URLRequestSummary& lhs,
131                                 const URLRequestSummary& rhs) {
132    return lhs.navigation_id == rhs.navigation_id &&
133        lhs.resource_url == rhs.resource_url &&
134        lhs.resource_type == rhs.resource_type &&
135        lhs.mime_type == rhs.mime_type &&
136        lhs.was_cached == rhs.was_cached;
137  }
138
139  void ResetPredictor() {
140    ResourcePrefetchPredictorConfig config;
141    config.max_urls_to_track = 3;
142    config.max_hosts_to_track = 2;
143    config.min_url_visit_count = 2;
144    config.max_resources_per_entry = 4;
145    config.max_consecutive_misses = 2;
146
147    // TODO(shishir): Enable the prefetching mode in the tests.
148    config.mode |= ResourcePrefetchPredictorConfig::URL_LEARNING;
149    config.mode |= ResourcePrefetchPredictorConfig::HOST_LEARNING;
150    predictor_.reset(new ResourcePrefetchPredictor(config, profile_.get()));
151    predictor_->set_mock_tables(mock_tables_);
152  }
153
154  void InitializeSampleData();
155
156  base::MessageLoop loop_;
157  content::TestBrowserThread ui_thread_;
158  content::TestBrowserThread db_thread_;
159  scoped_ptr<TestingProfile> profile_;
160
161  scoped_ptr<ResourcePrefetchPredictor> predictor_;
162  scoped_refptr<StrictMock<MockResourcePrefetchPredictorTables> > mock_tables_;
163
164  PrefetchDataMap test_url_data_;
165  PrefetchDataMap test_host_data_;
166  PrefetchData empty_url_data_;
167  PrefetchData empty_host_data_;
168};
169
170ResourcePrefetchPredictorTest::ResourcePrefetchPredictorTest()
171    : loop_(base::MessageLoop::TYPE_DEFAULT),
172      ui_thread_(content::BrowserThread::UI, &loop_),
173      db_thread_(content::BrowserThread::DB, &loop_),
174      profile_(new TestingProfile()),
175      mock_tables_(new StrictMock<MockResourcePrefetchPredictorTables>()),
176      empty_url_data_(PREFETCH_KEY_TYPE_URL, std::string()),
177      empty_host_data_(PREFETCH_KEY_TYPE_HOST, std::string()) {}
178
179ResourcePrefetchPredictorTest::~ResourcePrefetchPredictorTest() {
180  profile_.reset(NULL);
181  loop_.RunUntilIdle();
182}
183
184void ResourcePrefetchPredictorTest::SetUp() {
185  InitializeSampleData();
186
187  ASSERT_TRUE(profile_->CreateHistoryService(true, false));
188  profile_->BlockUntilHistoryProcessesPendingRequests();
189  EXPECT_TRUE(HistoryServiceFactory::GetForProfile(profile_.get(),
190                                                   Profile::EXPLICIT_ACCESS));
191  // Initialize the predictor with empty data.
192  ResetPredictor();
193  EXPECT_EQ(predictor_->initialization_state_,
194            ResourcePrefetchPredictor::NOT_INITIALIZED);
195  EXPECT_CALL(*mock_tables_.get(),
196              GetAllData(Pointee(ContainerEq(PrefetchDataMap())),
197                         Pointee(ContainerEq(PrefetchDataMap()))));
198  InitializePredictor();
199  EXPECT_TRUE(predictor_->inflight_navigations_.empty());
200  EXPECT_EQ(predictor_->initialization_state_,
201            ResourcePrefetchPredictor::INITIALIZED);
202}
203
204void ResourcePrefetchPredictorTest::TearDown() {
205  predictor_.reset(NULL);
206  profile_->DestroyHistoryService();
207}
208
209void ResourcePrefetchPredictorTest::InitializeSampleData() {
210  {  // Url data.
211    PrefetchData google(PREFETCH_KEY_TYPE_URL, "http://www.google.com/");
212    google.last_visit = base::Time::FromInternalValue(1);
213    google.resources.push_back(ResourceRow(std::string(),
214                                           "http://google.com/style1.css",
215                                           content::RESOURCE_TYPE_STYLESHEET,
216                                           3,
217                                           2,
218                                           1,
219                                           1.0));
220    google.resources.push_back(ResourceRow(std::string(),
221                                           "http://google.com/script3.js",
222                                           content::RESOURCE_TYPE_SCRIPT,
223                                           4,
224                                           0,
225                                           1,
226                                           2.1));
227    google.resources.push_back(ResourceRow(std::string(),
228                                           "http://google.com/script4.js",
229                                           content::RESOURCE_TYPE_SCRIPT,
230                                           11,
231                                           0,
232                                           0,
233                                           2.1));
234    google.resources.push_back(ResourceRow(std::string(),
235                                           "http://google.com/image1.png",
236                                           content::RESOURCE_TYPE_IMAGE,
237                                           6,
238                                           3,
239                                           0,
240                                           2.2));
241    google.resources.push_back(ResourceRow(std::string(),
242                                           "http://google.com/a.font",
243                                           content::RESOURCE_TYPE_LAST_TYPE,
244                                           2,
245                                           0,
246                                           0,
247                                           5.1));
248
249    PrefetchData reddit(PREFETCH_KEY_TYPE_URL, "http://www.reddit.com/");
250    reddit.last_visit = base::Time::FromInternalValue(2);
251    reddit.resources
252        .push_back(ResourceRow(std::string(),
253                               "http://reddit-resource.com/script1.js",
254                               content::RESOURCE_TYPE_SCRIPT,
255                               4,
256                               0,
257                               1,
258                               1.0));
259    reddit.resources
260        .push_back(ResourceRow(std::string(),
261                               "http://reddit-resource.com/script2.js",
262                               content::RESOURCE_TYPE_SCRIPT,
263                               2,
264                               0,
265                               0,
266                               2.1));
267
268    PrefetchData yahoo(PREFETCH_KEY_TYPE_URL, "http://www.yahoo.com/");
269    yahoo.last_visit = base::Time::FromInternalValue(3);
270    yahoo.resources.push_back(ResourceRow(std::string(),
271                                          "http://google.com/image.png",
272                                          content::RESOURCE_TYPE_IMAGE,
273                                          20,
274                                          1,
275                                          0,
276                                          10.0));
277
278    test_url_data_.clear();
279    test_url_data_.insert(std::make_pair("http://www.google.com/", google));
280    test_url_data_.insert(std::make_pair("http://www.reddit.com/", reddit));
281    test_url_data_.insert(std::make_pair("http://www.yahoo.com/", yahoo));
282  }
283
284  {  // Host data.
285    PrefetchData facebook(PREFETCH_KEY_TYPE_HOST, "www.facebook.com");
286    facebook.last_visit = base::Time::FromInternalValue(4);
287    facebook.resources
288        .push_back(ResourceRow(std::string(),
289                               "http://www.facebook.com/style.css",
290                               content::RESOURCE_TYPE_STYLESHEET,
291                               5,
292                               2,
293                               1,
294                               1.1));
295    facebook.resources
296        .push_back(ResourceRow(std::string(),
297                               "http://www.facebook.com/script.js",
298                               content::RESOURCE_TYPE_SCRIPT,
299                               4,
300                               0,
301                               1,
302                               2.1));
303    facebook.resources
304        .push_back(ResourceRow(std::string(),
305                               "http://www.facebook.com/image.png",
306                               content::RESOURCE_TYPE_IMAGE,
307                               6,
308                               3,
309                               0,
310                               2.2));
311    facebook.resources.push_back(ResourceRow(std::string(),
312                                             "http://www.facebook.com/a.font",
313                                             content::RESOURCE_TYPE_LAST_TYPE,
314                                             2,
315                                             0,
316                                             0,
317                                             5.1));
318    facebook.resources
319        .push_back(ResourceRow(std::string(),
320                               "http://www.resources.facebook.com/script.js",
321                               content::RESOURCE_TYPE_SCRIPT,
322                               11,
323                               0,
324                               0,
325                               8.5));
326
327    PrefetchData yahoo(PREFETCH_KEY_TYPE_HOST, "www.yahoo.com");
328    yahoo.last_visit = base::Time::FromInternalValue(5);
329    yahoo.resources.push_back(ResourceRow(std::string(),
330                                          "http://google.com/image.png",
331                                          content::RESOURCE_TYPE_IMAGE,
332                                          20,
333                                          1,
334                                          0,
335                                          10.0));
336
337    test_host_data_.clear();
338    test_host_data_.insert(std::make_pair("www.facebook.com", facebook));
339    test_host_data_.insert(std::make_pair("www.yahoo.com", yahoo));
340  }
341}
342
343TEST_F(ResourcePrefetchPredictorTest, LazilyInitializeEmpty) {
344  // Tests that the predictor initializes correctly without any data.
345  EXPECT_TRUE(predictor_->url_table_cache_->empty());
346  EXPECT_TRUE(predictor_->host_table_cache_->empty());
347}
348
349TEST_F(ResourcePrefetchPredictorTest, LazilyInitializeWithData) {
350  // Tests that the history and the db tables data are loaded correctly.
351  AddUrlToHistory("http://www.google.com/", 4);
352  AddUrlToHistory("http://www.yahoo.com/", 2);
353
354  EXPECT_CALL(*mock_tables_.get(),
355              GetAllData(Pointee(ContainerEq(PrefetchDataMap())),
356                         Pointee(ContainerEq(PrefetchDataMap()))))
357      .WillOnce(DoAll(SetArgPointee<0>(test_url_data_),
358                      SetArgPointee<1>(test_host_data_)));
359
360  ResetPredictor();
361  InitializePredictor();
362
363  // Test that the internal variables correctly initialized.
364  EXPECT_EQ(predictor_->initialization_state_,
365            ResourcePrefetchPredictor::INITIALIZED);
366  EXPECT_TRUE(predictor_->inflight_navigations_.empty());
367
368  EXPECT_EQ(test_url_data_, *predictor_->url_table_cache_);
369  EXPECT_EQ(test_host_data_, *predictor_->host_table_cache_);
370}
371
372TEST_F(ResourcePrefetchPredictorTest, NavigationNotRecorded) {
373  // Single navigation but history count is low, so should not record.
374  AddUrlToHistory("http://www.google.com", 1);
375
376  URLRequestSummary main_frame =
377      CreateURLRequestSummary(1,
378                              1,
379                              "http://www.google.com",
380                              "http://www.google.com",
381                              content::RESOURCE_TYPE_MAIN_FRAME,
382                              std::string(),
383                              false);
384  predictor_->RecordURLRequest(main_frame);
385  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
386
387  // Now add a few subresources.
388  URLRequestSummary resource1 = CreateURLRequestSummary(
389      1, 1, "http://www.google.com",  "http://google.com/style1.css",
390      content::RESOURCE_TYPE_STYLESHEET, "text/css", false);
391  predictor_->RecordURLResponse(resource1);
392  URLRequestSummary resource2 = CreateURLRequestSummary(
393      1, 1, "http://www.google.com",  "http://google.com/script1.js",
394      content::RESOURCE_TYPE_SCRIPT, "text/javascript", false);
395  predictor_->RecordURLResponse(resource2);
396  URLRequestSummary resource3 = CreateURLRequestSummary(
397      1, 1, "http://www.google.com",  "http://google.com/script2.js",
398      content::RESOURCE_TYPE_SCRIPT, "text/javascript", false);
399  predictor_->RecordURLResponse(resource3);
400
401  PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.google.com");
402  host_data.resources.push_back(ResourceRow(std::string(),
403                                            "http://google.com/style1.css",
404                                            content::RESOURCE_TYPE_STYLESHEET,
405                                            1,
406                                            0,
407                                            0,
408                                            1.0));
409  host_data.resources.push_back(ResourceRow(std::string(),
410                                            "http://google.com/script1.js",
411                                            content::RESOURCE_TYPE_SCRIPT,
412                                            1,
413                                            0,
414                                            0,
415                                            2.0));
416  host_data.resources.push_back(ResourceRow(std::string(),
417                                            "http://google.com/script2.js",
418                                            content::RESOURCE_TYPE_SCRIPT,
419                                            1,
420                                            0,
421                                            0,
422                                            3.0));
423  EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data));
424
425  predictor_->OnNavigationComplete(main_frame.navigation_id);
426  profile_->BlockUntilHistoryProcessesPendingRequests();
427}
428
429TEST_F(ResourcePrefetchPredictorTest, NavigationUrlNotInDB) {
430  // Single navigation that will be recorded. Will check for duplicate
431  // resources and also for number of resources saved.
432  AddUrlToHistory("http://www.google.com", 4);
433
434  URLRequestSummary main_frame =
435      CreateURLRequestSummary(1,
436                              1,
437                              "http://www.google.com",
438                              "http://www.google.com",
439                              content::RESOURCE_TYPE_MAIN_FRAME,
440                              std::string(),
441                              false);
442  predictor_->RecordURLRequest(main_frame);
443  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
444
445  URLRequestSummary resource1 = CreateURLRequestSummary(
446      1, 1, "http://www.google.com",  "http://google.com/style1.css",
447      content::RESOURCE_TYPE_STYLESHEET, "text/css", false);
448  predictor_->RecordURLResponse(resource1);
449  URLRequestSummary resource2 = CreateURLRequestSummary(
450      1, 1, "http://www.google.com",  "http://google.com/script1.js",
451      content::RESOURCE_TYPE_SCRIPT, "text/javascript", false);
452  predictor_->RecordURLResponse(resource2);
453  URLRequestSummary resource3 = CreateURLRequestSummary(
454      1, 1, "http://www.google.com",  "http://google.com/script2.js",
455      content::RESOURCE_TYPE_SCRIPT, "text/javascript", false);
456  predictor_->RecordURLResponse(resource3);
457  URLRequestSummary resource4 = CreateURLRequestSummary(
458      1, 1, "http://www.google.com",  "http://google.com/script1.js",
459      content::RESOURCE_TYPE_SCRIPT, "text/javascript", true);
460  predictor_->RecordURLResponse(resource4);
461  URLRequestSummary resource5 = CreateURLRequestSummary(
462      1, 1, "http://www.google.com",  "http://google.com/image1.png",
463      content::RESOURCE_TYPE_IMAGE, "image/png", false);
464  predictor_->RecordURLResponse(resource5);
465  URLRequestSummary resource6 = CreateURLRequestSummary(
466      1, 1, "http://www.google.com",  "http://google.com/image2.png",
467      content::RESOURCE_TYPE_IMAGE, "image/png", false);
468  predictor_->RecordURLResponse(resource6);
469  URLRequestSummary resource7 = CreateURLRequestSummary(
470      1, 1, "http://www.google.com",  "http://google.com/style2.css",
471      content::RESOURCE_TYPE_STYLESHEET, "text/css", true);
472  predictor_->RecordURLResponse(resource7);
473
474  PrefetchData url_data(PREFETCH_KEY_TYPE_URL, "http://www.google.com/");
475  url_data.resources.push_back(ResourceRow(std::string(),
476                                           "http://google.com/style1.css",
477                                           content::RESOURCE_TYPE_STYLESHEET,
478                                           1,
479                                           0,
480                                           0,
481                                           1.0));
482  url_data.resources.push_back(ResourceRow(std::string(),
483                                           "http://google.com/script1.js",
484                                           content::RESOURCE_TYPE_SCRIPT,
485                                           1,
486                                           0,
487                                           0,
488                                           2.0));
489  url_data.resources.push_back(ResourceRow(std::string(),
490                                           "http://google.com/script2.js",
491                                           content::RESOURCE_TYPE_SCRIPT,
492                                           1,
493                                           0,
494                                           0,
495                                           3.0));
496  url_data.resources.push_back(ResourceRow(std::string(),
497                                           "http://google.com/style2.css",
498                                           content::RESOURCE_TYPE_STYLESHEET,
499                                           1,
500                                           0,
501                                           0,
502                                           7.0));
503  EXPECT_CALL(*mock_tables_.get(), UpdateData(url_data, empty_host_data_));
504
505  PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.google.com");
506  host_data.resources = url_data.resources;
507  EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data));
508
509  predictor_->OnNavigationComplete(main_frame.navigation_id);
510  profile_->BlockUntilHistoryProcessesPendingRequests();
511}
512
513TEST_F(ResourcePrefetchPredictorTest, NavigationUrlInDB) {
514  // Tests that navigation is recorded correctly for URL already present in
515  // the database cache.
516  AddUrlToHistory("http://www.google.com", 4);
517
518  EXPECT_CALL(*mock_tables_.get(),
519              GetAllData(Pointee(ContainerEq(PrefetchDataMap())),
520                         Pointee(ContainerEq(PrefetchDataMap()))))
521      .WillOnce(DoAll(SetArgPointee<0>(test_url_data_),
522                      SetArgPointee<1>(test_host_data_)));
523  ResetPredictor();
524  InitializePredictor();
525  EXPECT_EQ(3, static_cast<int>(predictor_->url_table_cache_->size()));
526  EXPECT_EQ(2, static_cast<int>(predictor_->host_table_cache_->size()));
527
528  URLRequestSummary main_frame =
529      CreateURLRequestSummary(1,
530                              1,
531                              "http://www.google.com",
532                              "http://www.google.com",
533                              content::RESOURCE_TYPE_MAIN_FRAME,
534                              std::string(),
535                              false);
536  predictor_->RecordURLRequest(main_frame);
537  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
538
539  URLRequestSummary resource1 = CreateURLRequestSummary(
540      1, 1, "http://www.google.com",  "http://google.com/style1.css",
541      content::RESOURCE_TYPE_STYLESHEET, "text/css", false);
542  predictor_->RecordURLResponse(resource1);
543  URLRequestSummary resource2 = CreateURLRequestSummary(
544      1, 1, "http://www.google.com",  "http://google.com/script1.js",
545      content::RESOURCE_TYPE_SCRIPT, "text/javascript", false);
546  predictor_->RecordURLResponse(resource2);
547  URLRequestSummary resource3 = CreateURLRequestSummary(
548      1, 1, "http://www.google.com",  "http://google.com/script2.js",
549      content::RESOURCE_TYPE_SCRIPT, "text/javascript", false);
550  predictor_->RecordURLResponse(resource3);
551  URLRequestSummary resource4 = CreateURLRequestSummary(
552      1, 1, "http://www.google.com",  "http://google.com/script1.js",
553      content::RESOURCE_TYPE_SCRIPT, "text/javascript", true);
554  predictor_->RecordURLResponse(resource4);
555  URLRequestSummary resource5 = CreateURLRequestSummary(
556      1, 1, "http://www.google.com",  "http://google.com/image1.png",
557      content::RESOURCE_TYPE_IMAGE, "image/png", false);
558  predictor_->RecordURLResponse(resource5);
559  URLRequestSummary resource6 = CreateURLRequestSummary(
560      1, 1, "http://www.google.com",  "http://google.com/image2.png",
561      content::RESOURCE_TYPE_IMAGE, "image/png", false);
562  predictor_->RecordURLResponse(resource6);
563  URLRequestSummary resource7 = CreateURLRequestSummary(
564      1, 1, "http://www.google.com",  "http://google.com/style2.css",
565      content::RESOURCE_TYPE_STYLESHEET, "text/css", true);
566  predictor_->RecordURLResponse(resource7);
567
568  PrefetchData url_data(PREFETCH_KEY_TYPE_URL, "http://www.google.com/");
569  url_data.resources.push_back(ResourceRow(std::string(),
570                                           "http://google.com/style1.css",
571                                           content::RESOURCE_TYPE_STYLESHEET,
572                                           4,
573                                           2,
574                                           0,
575                                           1.0));
576  url_data.resources.push_back(ResourceRow(std::string(),
577                                           "http://google.com/script1.js",
578                                           content::RESOURCE_TYPE_SCRIPT,
579                                           1,
580                                           0,
581                                           0,
582                                           2.0));
583  url_data.resources.push_back(ResourceRow(std::string(),
584                                           "http://google.com/script4.js",
585                                           content::RESOURCE_TYPE_SCRIPT,
586                                           11,
587                                           1,
588                                           1,
589                                           2.1));
590  url_data.resources.push_back(ResourceRow(std::string(),
591                                           "http://google.com/script2.js",
592                                           content::RESOURCE_TYPE_SCRIPT,
593                                           1,
594                                           0,
595                                           0,
596                                           3.0));
597  EXPECT_CALL(*mock_tables_.get(), UpdateData(url_data, empty_host_data_));
598
599  EXPECT_CALL(
600      *mock_tables_.get(),
601      DeleteSingleDataPoint("www.facebook.com", PREFETCH_KEY_TYPE_HOST));
602
603  PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.google.com");
604  host_data.resources.push_back(ResourceRow(std::string(),
605                                            "http://google.com/style1.css",
606                                            content::RESOURCE_TYPE_STYLESHEET,
607                                            1,
608                                            0,
609                                            0,
610                                            1.0));
611  host_data.resources.push_back(ResourceRow(std::string(),
612                                            "http://google.com/script1.js",
613                                            content::RESOURCE_TYPE_SCRIPT,
614                                            1,
615                                            0,
616                                            0,
617                                            2.0));
618  host_data.resources.push_back(ResourceRow(std::string(),
619                                            "http://google.com/script2.js",
620                                            content::RESOURCE_TYPE_SCRIPT,
621                                            1,
622                                            0,
623                                            0,
624                                            3.0));
625  host_data.resources.push_back(ResourceRow(std::string(),
626                                            "http://google.com/style2.css",
627                                            content::RESOURCE_TYPE_STYLESHEET,
628                                            1,
629                                            0,
630                                            0,
631                                            7.0));
632  EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data));
633
634  predictor_->OnNavigationComplete(main_frame.navigation_id);
635  profile_->BlockUntilHistoryProcessesPendingRequests();
636}
637
638TEST_F(ResourcePrefetchPredictorTest, NavigationUrlNotInDBAndDBFull) {
639  // Tests that a URL is deleted before another is added if the cache is full.
640  AddUrlToHistory("http://www.nike.com/", 4);
641
642  EXPECT_CALL(*mock_tables_.get(),
643              GetAllData(Pointee(ContainerEq(PrefetchDataMap())),
644                         Pointee(ContainerEq(PrefetchDataMap()))))
645      .WillOnce(DoAll(SetArgPointee<0>(test_url_data_),
646                      SetArgPointee<1>(test_host_data_)));
647  ResetPredictor();
648  InitializePredictor();
649  EXPECT_EQ(3, static_cast<int>(predictor_->url_table_cache_->size()));
650  EXPECT_EQ(2, static_cast<int>(predictor_->host_table_cache_->size()));
651
652  URLRequestSummary main_frame =
653      CreateURLRequestSummary(1,
654                              1,
655                              "http://www.nike.com",
656                              "http://www.nike.com",
657                              content::RESOURCE_TYPE_MAIN_FRAME,
658                              std::string(),
659                              false);
660  predictor_->RecordURLRequest(main_frame);
661  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
662
663  URLRequestSummary resource1 = CreateURLRequestSummary(
664      1, 1, "http://www.nike.com",  "http://nike.com/style1.css",
665      content::RESOURCE_TYPE_STYLESHEET, "text/css", false);
666  predictor_->RecordURLResponse(resource1);
667  URLRequestSummary resource2 = CreateURLRequestSummary(
668      1, 1, "http://www.nike.com",  "http://nike.com/image2.png",
669      content::RESOURCE_TYPE_IMAGE, "image/png", false);
670  predictor_->RecordURLResponse(resource2);
671
672  EXPECT_CALL(
673      *mock_tables_.get(),
674      DeleteSingleDataPoint("http://www.google.com/", PREFETCH_KEY_TYPE_URL));
675  EXPECT_CALL(
676      *mock_tables_.get(),
677      DeleteSingleDataPoint("www.facebook.com", PREFETCH_KEY_TYPE_HOST));
678
679  PrefetchData url_data(PREFETCH_KEY_TYPE_URL, "http://www.nike.com/");
680  url_data.resources.push_back(ResourceRow(std::string(),
681                                           "http://nike.com/style1.css",
682                                           content::RESOURCE_TYPE_STYLESHEET,
683                                           1,
684                                           0,
685                                           0,
686                                           1.0));
687  url_data.resources.push_back(ResourceRow(std::string(),
688                                           "http://nike.com/image2.png",
689                                           content::RESOURCE_TYPE_IMAGE,
690                                           1,
691                                           0,
692                                           0,
693                                           2.0));
694  EXPECT_CALL(*mock_tables_.get(), UpdateData(url_data, empty_host_data_));
695
696  PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.nike.com");
697  host_data.resources = url_data.resources;
698  EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data));
699
700  predictor_->OnNavigationComplete(main_frame.navigation_id);
701  profile_->BlockUntilHistoryProcessesPendingRequests();
702}
703
704TEST_F(ResourcePrefetchPredictorTest, DeleteUrls) {
705  // Add some dummy entries to cache.
706  predictor_->url_table_cache_->insert(std::make_pair(
707      "http://www.google.com/page1.html",
708      PrefetchData(PREFETCH_KEY_TYPE_URL, "http://www.google.com/page1.html")));
709  predictor_->url_table_cache_->insert(std::make_pair(
710      "http://www.google.com/page2.html",
711      PrefetchData(PREFETCH_KEY_TYPE_URL, "http://www.google.com/page2.html")));
712  predictor_->url_table_cache_->insert(std::make_pair(
713      "http://www.yahoo.com/",
714      PrefetchData(PREFETCH_KEY_TYPE_URL, "http://www.yahoo.com/")));
715  predictor_->url_table_cache_->insert(std::make_pair(
716      "http://www.apple.com/",
717      PrefetchData(PREFETCH_KEY_TYPE_URL, "http://www.apple.com/")));
718  predictor_->url_table_cache_->insert(std::make_pair(
719      "http://www.nike.com/",
720      PrefetchData(PREFETCH_KEY_TYPE_URL, "http://www.nike.com/")));
721
722  predictor_->host_table_cache_->insert(std::make_pair(
723      "www.google.com",
724      PrefetchData(PREFETCH_KEY_TYPE_HOST, "www.google.com")));
725  predictor_->host_table_cache_->insert(std::make_pair(
726      "www.yahoo.com",
727      PrefetchData(PREFETCH_KEY_TYPE_HOST, "www.yahoo.com")));
728  predictor_->host_table_cache_->insert(std::make_pair(
729      "www.apple.com",
730      PrefetchData(PREFETCH_KEY_TYPE_HOST, "www.apple.com")));
731
732  history::URLRows rows;
733  rows.push_back(history::URLRow(GURL("http://www.google.com/page2.html")));
734  rows.push_back(history::URLRow(GURL("http://www.apple.com")));
735  rows.push_back(history::URLRow(GURL("http://www.nike.com")));
736
737  std::vector<std::string> urls_to_delete, hosts_to_delete;
738  urls_to_delete.push_back("http://www.google.com/page2.html");
739  urls_to_delete.push_back("http://www.apple.com/");
740  urls_to_delete.push_back("http://www.nike.com/");
741  hosts_to_delete.push_back("www.google.com");
742  hosts_to_delete.push_back("www.apple.com");
743
744  EXPECT_CALL(
745      *mock_tables_.get(),
746      DeleteData(ContainerEq(urls_to_delete), ContainerEq(hosts_to_delete)));
747
748  predictor_->DeleteUrls(rows);
749  EXPECT_EQ(2, static_cast<int>(predictor_->url_table_cache_->size()));
750  EXPECT_EQ(1, static_cast<int>(predictor_->host_table_cache_->size()));
751
752  EXPECT_CALL(*mock_tables_.get(), DeleteAllData());
753
754  predictor_->DeleteAllUrls();
755  EXPECT_TRUE(predictor_->url_table_cache_->empty());
756  EXPECT_TRUE(predictor_->host_table_cache_->empty());
757}
758
759TEST_F(ResourcePrefetchPredictorTest, OnMainFrameRequest) {
760  URLRequestSummary summary1 =
761      CreateURLRequestSummary(1,
762                              1,
763                              "http://www.google.com",
764                              "http://www.google.com",
765                              content::RESOURCE_TYPE_MAIN_FRAME,
766                              std::string(),
767                              false);
768  URLRequestSummary summary2 =
769      CreateURLRequestSummary(1,
770                              2,
771                              "http://www.google.com",
772                              "http://www.google.com",
773                              content::RESOURCE_TYPE_MAIN_FRAME,
774                              std::string(),
775                              false);
776  URLRequestSummary summary3 =
777      CreateURLRequestSummary(2,
778                              1,
779                              "http://www.yahoo.com",
780                              "http://www.yahoo.com",
781                              content::RESOURCE_TYPE_MAIN_FRAME,
782                              std::string(),
783                              false);
784
785  predictor_->OnMainFrameRequest(summary1);
786  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
787  predictor_->OnMainFrameRequest(summary2);
788  EXPECT_EQ(2, static_cast<int>(predictor_->inflight_navigations_.size()));
789  predictor_->OnMainFrameRequest(summary3);
790  EXPECT_EQ(3, static_cast<int>(predictor_->inflight_navigations_.size()));
791
792  // Insert anther with same navigation id. It should replace.
793  URLRequestSummary summary4 =
794      CreateURLRequestSummary(1,
795                              1,
796                              "http://www.nike.com",
797                              "http://www.nike.com",
798                              content::RESOURCE_TYPE_MAIN_FRAME,
799                              std::string(),
800                              false);
801  URLRequestSummary summary5 =
802      CreateURLRequestSummary(1,
803                              2,
804                              "http://www.google.com",
805                              "http://www.google.com",
806                              content::RESOURCE_TYPE_MAIN_FRAME,
807                              std::string(),
808                              false);
809
810  predictor_->OnMainFrameRequest(summary4);
811  EXPECT_EQ(3, static_cast<int>(predictor_->inflight_navigations_.size()));
812
813  // Change this creation time so that it will go away on the next insert.
814  summary5.navigation_id.creation_time = base::TimeTicks::Now() -
815      base::TimeDelta::FromDays(1);
816  predictor_->OnMainFrameRequest(summary5);
817  EXPECT_EQ(3, static_cast<int>(predictor_->inflight_navigations_.size()));
818
819  URLRequestSummary summary6 =
820      CreateURLRequestSummary(3,
821                              1,
822                              "http://www.shoes.com",
823                              "http://www.shoes.com",
824                              content::RESOURCE_TYPE_MAIN_FRAME,
825                              std::string(),
826                              false);
827  predictor_->OnMainFrameRequest(summary6);
828  EXPECT_EQ(3, static_cast<int>(predictor_->inflight_navigations_.size()));
829
830  EXPECT_TRUE(predictor_->inflight_navigations_.find(summary3.navigation_id) !=
831              predictor_->inflight_navigations_.end());
832  EXPECT_TRUE(predictor_->inflight_navigations_.find(summary4.navigation_id) !=
833              predictor_->inflight_navigations_.end());
834  EXPECT_TRUE(predictor_->inflight_navigations_.find(summary6.navigation_id) !=
835              predictor_->inflight_navigations_.end());
836}
837
838TEST_F(ResourcePrefetchPredictorTest, OnMainFrameRedirect) {
839  URLRequestSummary summary1 =
840      CreateURLRequestSummary(1,
841                              1,
842                              "http://www.google.com",
843                              "http://www.google.com",
844                              content::RESOURCE_TYPE_MAIN_FRAME,
845                              std::string(),
846                              false);
847  URLRequestSummary summary2 =
848      CreateURLRequestSummary(1,
849                              2,
850                              "http://www.google.com",
851                              "http://www.google.com",
852                              content::RESOURCE_TYPE_MAIN_FRAME,
853                              std::string(),
854                              false);
855  URLRequestSummary summary3 =
856      CreateURLRequestSummary(2,
857                              1,
858                              "http://www.yahoo.com",
859                              "http://www.yahoo.com",
860                              content::RESOURCE_TYPE_MAIN_FRAME,
861                              std::string(),
862                              false);
863
864  predictor_->OnMainFrameRedirect(summary1);
865  EXPECT_TRUE(predictor_->inflight_navigations_.empty());
866
867  predictor_->OnMainFrameRequest(summary1);
868  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
869  predictor_->OnMainFrameRequest(summary2);
870  EXPECT_EQ(2, static_cast<int>(predictor_->inflight_navigations_.size()));
871
872  predictor_->OnMainFrameRedirect(summary3);
873  EXPECT_EQ(2, static_cast<int>(predictor_->inflight_navigations_.size()));
874  predictor_->OnMainFrameRedirect(summary1);
875  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
876  predictor_->OnMainFrameRedirect(summary2);
877  EXPECT_TRUE(predictor_->inflight_navigations_.empty());
878}
879
880TEST_F(ResourcePrefetchPredictorTest, OnSubresourceResponse) {
881  // If there is no inflight navigation, nothing happens.
882  URLRequestSummary resource1 = CreateURLRequestSummary(
883      1, 1, "http://www.google.com",  "http://google.com/style1.css",
884      content::RESOURCE_TYPE_STYLESHEET, "text/css", false);
885  predictor_->OnSubresourceResponse(resource1);
886  EXPECT_TRUE(predictor_->inflight_navigations_.empty());
887
888  // Add an inflight navigation.
889  URLRequestSummary main_frame1 =
890      CreateURLRequestSummary(1,
891                              1,
892                              "http://www.google.com",
893                              "http://www.google.com",
894                              content::RESOURCE_TYPE_MAIN_FRAME,
895                              std::string(),
896                              false);
897  predictor_->OnMainFrameRequest(main_frame1);
898  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
899
900  // Now add a few subresources.
901  URLRequestSummary resource2 = CreateURLRequestSummary(
902      1, 1, "http://www.google.com",  "http://google.com/script1.js",
903      content::RESOURCE_TYPE_SCRIPT, "text/javascript", false);
904  URLRequestSummary resource3 = CreateURLRequestSummary(
905      1, 1, "http://www.google.com",  "http://google.com/script2.js",
906      content::RESOURCE_TYPE_SCRIPT, "text/javascript", false);
907  predictor_->OnSubresourceResponse(resource1);
908  predictor_->OnSubresourceResponse(resource2);
909  predictor_->OnSubresourceResponse(resource3);
910
911  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
912  EXPECT_EQ(3, static_cast<int>(
913      predictor_->inflight_navigations_[main_frame1.navigation_id]->size()));
914  EXPECT_TRUE(URLRequestSummaryAreEqual(
915      resource1,
916      predictor_->inflight_navigations_[main_frame1.navigation_id]->at(0)));
917  EXPECT_TRUE(URLRequestSummaryAreEqual(
918      resource2,
919      predictor_->inflight_navigations_[main_frame1.navigation_id]->at(1)));
920  EXPECT_TRUE(URLRequestSummaryAreEqual(
921      resource3,
922      predictor_->inflight_navigations_[main_frame1.navigation_id]->at(2)));
923}
924
925}  // namespace predictors
926