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