resource_prefetch_predictor_unittest.cc revision bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3
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/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/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      mock_tables_(new StrictMock<MockResourcePrefetchPredictorTables>()),
178      empty_url_data_(PREFETCH_KEY_TYPE_URL, std::string()),
179      empty_host_data_(PREFETCH_KEY_TYPE_HOST, std::string()) {}
180
181ResourcePrefetchPredictorTest::~ResourcePrefetchPredictorTest() {
182  profile_.reset(NULL);
183  loop_.RunUntilIdle();
184}
185
186void ResourcePrefetchPredictorTest::SetUp() {
187  InitializeSampleData();
188
189  ASSERT_TRUE(profile_->CreateHistoryService(true, false));
190  profile_->BlockUntilHistoryProcessesPendingRequests();
191  EXPECT_TRUE(HistoryServiceFactory::GetForProfile(profile_.get(),
192                                                   Profile::EXPLICIT_ACCESS));
193  // Initialize the predictor with empty data.
194  ResetPredictor();
195  EXPECT_EQ(predictor_->initialization_state_,
196            ResourcePrefetchPredictor::NOT_INITIALIZED);
197  EXPECT_CALL(*mock_tables_.get(),
198              GetAllData(Pointee(ContainerEq(PrefetchDataMap())),
199                         Pointee(ContainerEq(PrefetchDataMap()))));
200  InitializePredictor();
201  EXPECT_TRUE(predictor_->inflight_navigations_.empty());
202  EXPECT_EQ(predictor_->initialization_state_,
203            ResourcePrefetchPredictor::INITIALIZED);
204}
205
206void ResourcePrefetchPredictorTest::TearDown() {
207  predictor_.reset(NULL);
208  profile_->DestroyHistoryService();
209}
210
211void ResourcePrefetchPredictorTest::InitializeSampleData() {
212  {  // Url data.
213    PrefetchData google(PREFETCH_KEY_TYPE_URL, "http://www.google.com/");
214    google.last_visit = base::Time::FromInternalValue(1);
215    google.resources.push_back(ResourceRow(std::string(),
216                                           "http://google.com/style1.css",
217                                           ResourceType::STYLESHEET,
218                                           3,
219                                           2,
220                                           1,
221                                           1.0));
222    google.resources.push_back(ResourceRow(std::string(),
223                                           "http://google.com/script3.js",
224                                           ResourceType::SCRIPT,
225                                           4,
226                                           0,
227                                           1,
228                                           2.1));
229    google.resources.push_back(ResourceRow(std::string(),
230                                           "http://google.com/script4.js",
231                                           ResourceType::SCRIPT,
232                                           11,
233                                           0,
234                                           0,
235                                           2.1));
236    google.resources.push_back(ResourceRow(std::string(),
237                                           "http://google.com/image1.png",
238                                           ResourceType::IMAGE,
239                                           6,
240                                           3,
241                                           0,
242                                           2.2));
243    google.resources.push_back(ResourceRow(std::string(),
244                                           "http://google.com/a.font",
245                                           ResourceType::LAST_TYPE,
246                                           2,
247                                           0,
248                                           0,
249                                           5.1));
250
251    PrefetchData reddit(PREFETCH_KEY_TYPE_URL, "http://www.reddit.com/");
252    reddit.last_visit = base::Time::FromInternalValue(2);
253    reddit.resources
254        .push_back(ResourceRow(std::string(),
255                               "http://reddit-resource.com/script1.js",
256                               ResourceType::SCRIPT,
257                               4,
258                               0,
259                               1,
260                               1.0));
261    reddit.resources
262        .push_back(ResourceRow(std::string(),
263                               "http://reddit-resource.com/script2.js",
264                               ResourceType::SCRIPT,
265                               2,
266                               0,
267                               0,
268                               2.1));
269
270    PrefetchData yahoo(PREFETCH_KEY_TYPE_URL, "http://www.yahoo.com/");
271    yahoo.last_visit = base::Time::FromInternalValue(3);
272    yahoo.resources.push_back(ResourceRow(std::string(),
273                                          "http://google.com/image.png",
274                                          ResourceType::IMAGE,
275                                          20,
276                                          1,
277                                          0,
278                                          10.0));
279
280    test_url_data_.clear();
281    test_url_data_.insert(std::make_pair("http://www.google.com/", google));
282    test_url_data_.insert(std::make_pair("http://www.reddit.com/", reddit));
283    test_url_data_.insert(std::make_pair("http://www.yahoo.com/", yahoo));
284  }
285
286  {  // Host data.
287    PrefetchData facebook(PREFETCH_KEY_TYPE_HOST, "www.facebook.com");
288    facebook.last_visit = base::Time::FromInternalValue(4);
289    facebook.resources
290        .push_back(ResourceRow(std::string(),
291                               "http://www.facebook.com/style.css",
292                               ResourceType::STYLESHEET,
293                               5,
294                               2,
295                               1,
296                               1.1));
297    facebook.resources
298        .push_back(ResourceRow(std::string(),
299                               "http://www.facebook.com/script.js",
300                               ResourceType::SCRIPT,
301                               4,
302                               0,
303                               1,
304                               2.1));
305    facebook.resources
306        .push_back(ResourceRow(std::string(),
307                               "http://www.facebook.com/image.png",
308                               ResourceType::IMAGE,
309                               6,
310                               3,
311                               0,
312                               2.2));
313    facebook.resources.push_back(ResourceRow(std::string(),
314                                             "http://www.facebook.com/a.font",
315                                             ResourceType::LAST_TYPE,
316                                             2,
317                                             0,
318                                             0,
319                                             5.1));
320    facebook.resources
321        .push_back(ResourceRow(std::string(),
322                               "http://www.resources.facebook.com/script.js",
323                               ResourceType::SCRIPT,
324                               11,
325                               0,
326                               0,
327                               8.5));
328
329    PrefetchData yahoo(PREFETCH_KEY_TYPE_HOST, "www.yahoo.com");
330    yahoo.last_visit = base::Time::FromInternalValue(5);
331    yahoo.resources.push_back(ResourceRow(std::string(),
332                                          "http://google.com/image.png",
333                                          ResourceType::IMAGE,
334                                          20,
335                                          1,
336                                          0,
337                                          10.0));
338
339    test_host_data_.clear();
340    test_host_data_.insert(std::make_pair("www.facebook.com", facebook));
341    test_host_data_.insert(std::make_pair("www.yahoo.com", yahoo));
342  }
343}
344
345TEST_F(ResourcePrefetchPredictorTest, LazilyInitializeEmpty) {
346  // Tests that the predictor initializes correctly without any data.
347  EXPECT_TRUE(predictor_->url_table_cache_->empty());
348  EXPECT_TRUE(predictor_->host_table_cache_->empty());
349}
350
351TEST_F(ResourcePrefetchPredictorTest, LazilyInitializeWithData) {
352  // Tests that the history and the db tables data are loaded correctly.
353  AddUrlToHistory("http://www.google.com/", 4);
354  AddUrlToHistory("http://www.yahoo.com/", 2);
355
356  EXPECT_CALL(*mock_tables_.get(),
357              GetAllData(Pointee(ContainerEq(PrefetchDataMap())),
358                         Pointee(ContainerEq(PrefetchDataMap()))))
359      .WillOnce(DoAll(SetArgPointee<0>(test_url_data_),
360                      SetArgPointee<1>(test_host_data_)));
361
362  ResetPredictor();
363  InitializePredictor();
364
365  // Test that the internal variables correctly initialized.
366  EXPECT_EQ(predictor_->initialization_state_,
367            ResourcePrefetchPredictor::INITIALIZED);
368  EXPECT_TRUE(predictor_->inflight_navigations_.empty());
369
370  EXPECT_EQ(test_url_data_, *predictor_->url_table_cache_);
371  EXPECT_EQ(test_host_data_, *predictor_->host_table_cache_);
372}
373
374TEST_F(ResourcePrefetchPredictorTest, NavigationNotRecorded) {
375  // Single navigation but history count is low, so should not record.
376  AddUrlToHistory("http://www.google.com", 1);
377
378  URLRequestSummary main_frame =
379      CreateURLRequestSummary(1,
380                              1,
381                              "http://www.google.com",
382                              "http://www.google.com",
383                              ResourceType::MAIN_FRAME,
384                              std::string(),
385                              false);
386  predictor_->RecordURLRequest(main_frame);
387  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
388
389  // Now add a few subresources.
390  URLRequestSummary resource1 = CreateURLRequestSummary(
391      1, 1, "http://www.google.com",  "http://google.com/style1.css",
392      ResourceType::STYLESHEET, "text/css", false);
393  predictor_->RecordUrlResponse(resource1);
394  URLRequestSummary resource2 = CreateURLRequestSummary(
395      1, 1, "http://www.google.com",  "http://google.com/script1.js",
396      ResourceType::SCRIPT, "text/javascript", false);
397  predictor_->RecordUrlResponse(resource2);
398  URLRequestSummary resource3 = CreateURLRequestSummary(
399      1, 1, "http://www.google.com",  "http://google.com/script2.js",
400      ResourceType::SCRIPT, "text/javascript", false);
401  predictor_->RecordUrlResponse(resource3);
402
403  PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.google.com");
404  host_data.resources.push_back(ResourceRow(std::string(),
405                                            "http://google.com/style1.css",
406                                            ResourceType::STYLESHEET,
407                                            1,
408                                            0,
409                                            0,
410                                            1.0));
411  host_data.resources.push_back(ResourceRow(std::string(),
412                                            "http://google.com/script1.js",
413                                            ResourceType::SCRIPT,
414                                            1,
415                                            0,
416                                            0,
417                                            2.0));
418  host_data.resources.push_back(ResourceRow(std::string(),
419                                            "http://google.com/script2.js",
420                                            ResourceType::SCRIPT,
421                                            1,
422                                            0,
423                                            0,
424                                            3.0));
425  EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data));
426
427  predictor_->OnNavigationComplete(main_frame.navigation_id);
428  profile_->BlockUntilHistoryProcessesPendingRequests();
429}
430
431TEST_F(ResourcePrefetchPredictorTest, NavigationUrlNotInDB) {
432  // Single navigation that will be recorded. Will check for duplicate
433  // resources and also for number of resources saved.
434  AddUrlToHistory("http://www.google.com", 4);
435
436  URLRequestSummary main_frame =
437      CreateURLRequestSummary(1,
438                              1,
439                              "http://www.google.com",
440                              "http://www.google.com",
441                              ResourceType::MAIN_FRAME,
442                              std::string(),
443                              false);
444  predictor_->RecordURLRequest(main_frame);
445  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
446
447  URLRequestSummary resource1 = CreateURLRequestSummary(
448      1, 1, "http://www.google.com",  "http://google.com/style1.css",
449      ResourceType::STYLESHEET, "text/css", false);
450  predictor_->RecordUrlResponse(resource1);
451  URLRequestSummary resource2 = CreateURLRequestSummary(
452      1, 1, "http://www.google.com",  "http://google.com/script1.js",
453      ResourceType::SCRIPT, "text/javascript", false);
454  predictor_->RecordUrlResponse(resource2);
455  URLRequestSummary resource3 = CreateURLRequestSummary(
456      1, 1, "http://www.google.com",  "http://google.com/script2.js",
457      ResourceType::SCRIPT, "text/javascript", false);
458  predictor_->RecordUrlResponse(resource3);
459  URLRequestSummary resource4 = CreateURLRequestSummary(
460      1, 1, "http://www.google.com",  "http://google.com/script1.js",
461      ResourceType::SCRIPT, "text/javascript", true);
462  predictor_->RecordUrlResponse(resource4);
463  URLRequestSummary resource5 = CreateURLRequestSummary(
464      1, 1, "http://www.google.com",  "http://google.com/image1.png",
465      ResourceType::IMAGE, "image/png", false);
466  predictor_->RecordUrlResponse(resource5);
467  URLRequestSummary resource6 = CreateURLRequestSummary(
468      1, 1, "http://www.google.com",  "http://google.com/image2.png",
469      ResourceType::IMAGE, "image/png", false);
470  predictor_->RecordUrlResponse(resource6);
471  URLRequestSummary resource7 = CreateURLRequestSummary(
472      1, 1, "http://www.google.com",  "http://google.com/style2.css",
473      ResourceType::STYLESHEET, "text/css", false);
474  predictor_->OnSubresourceLoadedFromMemory(
475      resource7.navigation_id,
476      resource7.resource_url,
477      resource7.mime_type,
478      resource7.resource_type);
479
480  PrefetchData url_data(PREFETCH_KEY_TYPE_URL, "http://www.google.com/");
481  url_data.resources.push_back(ResourceRow(std::string(),
482                                           "http://google.com/style1.css",
483                                           ResourceType::STYLESHEET,
484                                           1,
485                                           0,
486                                           0,
487                                           1.0));
488  url_data.resources.push_back(ResourceRow(std::string(),
489                                           "http://google.com/script1.js",
490                                           ResourceType::SCRIPT,
491                                           1,
492                                           0,
493                                           0,
494                                           2.0));
495  url_data.resources.push_back(ResourceRow(std::string(),
496                                           "http://google.com/script2.js",
497                                           ResourceType::SCRIPT,
498                                           1,
499                                           0,
500                                           0,
501                                           3.0));
502  url_data.resources.push_back(ResourceRow(std::string(),
503                                           "http://google.com/style2.css",
504                                           ResourceType::STYLESHEET,
505                                           1,
506                                           0,
507                                           0,
508                                           7.0));
509  EXPECT_CALL(*mock_tables_.get(), UpdateData(url_data, empty_host_data_));
510
511  PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.google.com");
512  host_data.resources = url_data.resources;
513  EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data));
514
515  predictor_->OnNavigationComplete(main_frame.navigation_id);
516  profile_->BlockUntilHistoryProcessesPendingRequests();
517}
518
519TEST_F(ResourcePrefetchPredictorTest, NavigationUrlInDB) {
520  // Tests that navigation is recorded correctly for URL already present in
521  // the database cache.
522  AddUrlToHistory("http://www.google.com", 4);
523
524  EXPECT_CALL(*mock_tables_.get(),
525              GetAllData(Pointee(ContainerEq(PrefetchDataMap())),
526                         Pointee(ContainerEq(PrefetchDataMap()))))
527      .WillOnce(DoAll(SetArgPointee<0>(test_url_data_),
528                      SetArgPointee<1>(test_host_data_)));
529  ResetPredictor();
530  InitializePredictor();
531  EXPECT_EQ(3, static_cast<int>(predictor_->url_table_cache_->size()));
532  EXPECT_EQ(2, static_cast<int>(predictor_->host_table_cache_->size()));
533
534  URLRequestSummary main_frame =
535      CreateURLRequestSummary(1,
536                              1,
537                              "http://www.google.com",
538                              "http://www.google.com",
539                              ResourceType::MAIN_FRAME,
540                              std::string(),
541                              false);
542  predictor_->RecordURLRequest(main_frame);
543  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
544
545  URLRequestSummary resource1 = CreateURLRequestSummary(
546      1, 1, "http://www.google.com",  "http://google.com/style1.css",
547      ResourceType::STYLESHEET, "text/css", false);
548  predictor_->RecordUrlResponse(resource1);
549  URLRequestSummary resource2 = CreateURLRequestSummary(
550      1, 1, "http://www.google.com",  "http://google.com/script1.js",
551      ResourceType::SCRIPT, "text/javascript", false);
552  predictor_->RecordUrlResponse(resource2);
553  URLRequestSummary resource3 = CreateURLRequestSummary(
554      1, 1, "http://www.google.com",  "http://google.com/script2.js",
555      ResourceType::SCRIPT, "text/javascript", false);
556  predictor_->RecordUrlResponse(resource3);
557  URLRequestSummary resource4 = CreateURLRequestSummary(
558      1, 1, "http://www.google.com",  "http://google.com/script1.js",
559      ResourceType::SCRIPT, "text/javascript", true);
560  predictor_->RecordUrlResponse(resource4);
561  URLRequestSummary resource5 = CreateURLRequestSummary(
562      1, 1, "http://www.google.com",  "http://google.com/image1.png",
563      ResourceType::IMAGE, "image/png", false);
564  predictor_->RecordUrlResponse(resource5);
565  URLRequestSummary resource6 = CreateURLRequestSummary(
566      1, 1, "http://www.google.com",  "http://google.com/image2.png",
567      ResourceType::IMAGE, "image/png", false);
568  predictor_->RecordUrlResponse(resource6);
569  URLRequestSummary resource7 = CreateURLRequestSummary(
570      1, 1, "http://www.google.com",  "http://google.com/style2.css",
571      ResourceType::STYLESHEET, "text/css", false);
572  predictor_->OnSubresourceLoadedFromMemory(
573      resource7.navigation_id,
574      resource7.resource_url,
575      resource7.mime_type,
576      resource7.resource_type);
577
578  PrefetchData url_data(PREFETCH_KEY_TYPE_URL, "http://www.google.com/");
579  url_data.resources.push_back(ResourceRow(std::string(),
580                                           "http://google.com/style1.css",
581                                           ResourceType::STYLESHEET,
582                                           4,
583                                           2,
584                                           0,
585                                           1.0));
586  url_data.resources.push_back(ResourceRow(std::string(),
587                                           "http://google.com/script1.js",
588                                           ResourceType::SCRIPT,
589                                           1,
590                                           0,
591                                           0,
592                                           2.0));
593  url_data.resources.push_back(ResourceRow(std::string(),
594                                           "http://google.com/script4.js",
595                                           ResourceType::SCRIPT,
596                                           11,
597                                           1,
598                                           1,
599                                           2.1));
600  url_data.resources.push_back(ResourceRow(std::string(),
601                                           "http://google.com/script2.js",
602                                           ResourceType::SCRIPT,
603                                           1,
604                                           0,
605                                           0,
606                                           3.0));
607  EXPECT_CALL(*mock_tables_.get(), UpdateData(url_data, empty_host_data_));
608
609  EXPECT_CALL(
610      *mock_tables_.get(),
611      DeleteSingleDataPoint("www.facebook.com", PREFETCH_KEY_TYPE_HOST));
612
613  PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.google.com");
614  host_data.resources.push_back(ResourceRow(std::string(),
615                                            "http://google.com/style1.css",
616                                            ResourceType::STYLESHEET,
617                                            1,
618                                            0,
619                                            0,
620                                            1.0));
621  host_data.resources.push_back(ResourceRow(std::string(),
622                                            "http://google.com/script1.js",
623                                            ResourceType::SCRIPT,
624                                            1,
625                                            0,
626                                            0,
627                                            2.0));
628  host_data.resources.push_back(ResourceRow(std::string(),
629                                            "http://google.com/script2.js",
630                                            ResourceType::SCRIPT,
631                                            1,
632                                            0,
633                                            0,
634                                            3.0));
635  host_data.resources.push_back(ResourceRow(std::string(),
636                                            "http://google.com/style2.css",
637                                            ResourceType::STYLESHEET,
638                                            1,
639                                            0,
640                                            0,
641                                            7.0));
642  EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data));
643
644  predictor_->OnNavigationComplete(main_frame.navigation_id);
645  profile_->BlockUntilHistoryProcessesPendingRequests();
646}
647
648TEST_F(ResourcePrefetchPredictorTest, NavigationUrlNotInDBAndDBFull) {
649  // Tests that a URL is deleted before another is added if the cache is full.
650  AddUrlToHistory("http://www.nike.com/", 4);
651
652  EXPECT_CALL(*mock_tables_.get(),
653              GetAllData(Pointee(ContainerEq(PrefetchDataMap())),
654                         Pointee(ContainerEq(PrefetchDataMap()))))
655      .WillOnce(DoAll(SetArgPointee<0>(test_url_data_),
656                      SetArgPointee<1>(test_host_data_)));
657  ResetPredictor();
658  InitializePredictor();
659  EXPECT_EQ(3, static_cast<int>(predictor_->url_table_cache_->size()));
660  EXPECT_EQ(2, static_cast<int>(predictor_->host_table_cache_->size()));
661
662  URLRequestSummary main_frame =
663      CreateURLRequestSummary(1,
664                              1,
665                              "http://www.nike.com",
666                              "http://www.nike.com",
667                              ResourceType::MAIN_FRAME,
668                              std::string(),
669                              false);
670  predictor_->RecordURLRequest(main_frame);
671  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
672
673  URLRequestSummary resource1 = CreateURLRequestSummary(
674      1, 1, "http://www.nike.com",  "http://nike.com/style1.css",
675      ResourceType::STYLESHEET, "text/css", false);
676  predictor_->RecordUrlResponse(resource1);
677  URLRequestSummary resource2 = CreateURLRequestSummary(
678      1, 1, "http://www.nike.com",  "http://nike.com/image2.png",
679      ResourceType::IMAGE, "image/png", false);
680  predictor_->RecordUrlResponse(resource2);
681
682  EXPECT_CALL(
683      *mock_tables_.get(),
684      DeleteSingleDataPoint("http://www.google.com/", PREFETCH_KEY_TYPE_URL));
685  EXPECT_CALL(
686      *mock_tables_.get(),
687      DeleteSingleDataPoint("www.facebook.com", PREFETCH_KEY_TYPE_HOST));
688
689  PrefetchData url_data(PREFETCH_KEY_TYPE_URL, "http://www.nike.com/");
690  url_data.resources.push_back(ResourceRow(std::string(),
691                                           "http://nike.com/style1.css",
692                                           ResourceType::STYLESHEET,
693                                           1,
694                                           0,
695                                           0,
696                                           1.0));
697  url_data.resources.push_back(ResourceRow(std::string(),
698                                           "http://nike.com/image2.png",
699                                           ResourceType::IMAGE,
700                                           1,
701                                           0,
702                                           0,
703                                           2.0));
704  EXPECT_CALL(*mock_tables_.get(), UpdateData(url_data, empty_host_data_));
705
706  PrefetchData host_data(PREFETCH_KEY_TYPE_HOST, "www.nike.com");
707  host_data.resources = url_data.resources;
708  EXPECT_CALL(*mock_tables_.get(), UpdateData(empty_url_data_, host_data));
709
710  predictor_->OnNavigationComplete(main_frame.navigation_id);
711  profile_->BlockUntilHistoryProcessesPendingRequests();
712}
713
714TEST_F(ResourcePrefetchPredictorTest, DeleteUrls) {
715  // Add some dummy entries to cache.
716  predictor_->url_table_cache_->insert(std::make_pair(
717      "http://www.google.com/page1.html",
718      PrefetchData(PREFETCH_KEY_TYPE_URL, "http://www.google.com/page1.html")));
719  predictor_->url_table_cache_->insert(std::make_pair(
720      "http://www.google.com/page2.html",
721      PrefetchData(PREFETCH_KEY_TYPE_URL, "http://www.google.com/page2.html")));
722  predictor_->url_table_cache_->insert(std::make_pair(
723      "http://www.yahoo.com/",
724      PrefetchData(PREFETCH_KEY_TYPE_URL, "http://www.yahoo.com/")));
725  predictor_->url_table_cache_->insert(std::make_pair(
726      "http://www.apple.com/",
727      PrefetchData(PREFETCH_KEY_TYPE_URL, "http://www.apple.com/")));
728  predictor_->url_table_cache_->insert(std::make_pair(
729      "http://www.nike.com/",
730      PrefetchData(PREFETCH_KEY_TYPE_URL, "http://www.nike.com/")));
731
732  predictor_->host_table_cache_->insert(std::make_pair(
733      "www.google.com",
734      PrefetchData(PREFETCH_KEY_TYPE_HOST, "www.google.com")));
735  predictor_->host_table_cache_->insert(std::make_pair(
736      "www.yahoo.com",
737      PrefetchData(PREFETCH_KEY_TYPE_HOST, "www.yahoo.com")));
738  predictor_->host_table_cache_->insert(std::make_pair(
739      "www.apple.com",
740      PrefetchData(PREFETCH_KEY_TYPE_HOST, "www.apple.com")));
741
742  history::URLRows rows;
743  rows.push_back(history::URLRow(GURL("http://www.google.com/page2.html")));
744  rows.push_back(history::URLRow(GURL("http://www.apple.com")));
745  rows.push_back(history::URLRow(GURL("http://www.nike.com")));
746
747  std::vector<std::string> urls_to_delete, hosts_to_delete;
748  urls_to_delete.push_back("http://www.google.com/page2.html");
749  urls_to_delete.push_back("http://www.apple.com/");
750  urls_to_delete.push_back("http://www.nike.com/");
751  hosts_to_delete.push_back("www.google.com");
752  hosts_to_delete.push_back("www.apple.com");
753
754  EXPECT_CALL(
755      *mock_tables_.get(),
756      DeleteData(ContainerEq(urls_to_delete), ContainerEq(hosts_to_delete)));
757
758  predictor_->DeleteUrls(rows);
759  EXPECT_EQ(2, static_cast<int>(predictor_->url_table_cache_->size()));
760  EXPECT_EQ(1, static_cast<int>(predictor_->host_table_cache_->size()));
761
762  EXPECT_CALL(*mock_tables_.get(), DeleteAllData());
763
764  predictor_->DeleteAllUrls();
765  EXPECT_TRUE(predictor_->url_table_cache_->empty());
766  EXPECT_TRUE(predictor_->host_table_cache_->empty());
767}
768
769TEST_F(ResourcePrefetchPredictorTest, OnMainFrameRequest) {
770  URLRequestSummary summary1 = CreateURLRequestSummary(1,
771                                                       1,
772                                                       "http://www.google.com",
773                                                       "http://www.google.com",
774                                                       ResourceType::MAIN_FRAME,
775                                                       std::string(),
776                                                       false);
777  URLRequestSummary summary2 = CreateURLRequestSummary(1,
778                                                       2,
779                                                       "http://www.google.com",
780                                                       "http://www.google.com",
781                                                       ResourceType::MAIN_FRAME,
782                                                       std::string(),
783                                                       false);
784  URLRequestSummary summary3 = CreateURLRequestSummary(2,
785                                                       1,
786                                                       "http://www.yahoo.com",
787                                                       "http://www.yahoo.com",
788                                                       ResourceType::MAIN_FRAME,
789                                                       std::string(),
790                                                       false);
791
792  predictor_->OnMainFrameRequest(summary1);
793  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
794  predictor_->OnMainFrameRequest(summary2);
795  EXPECT_EQ(2, static_cast<int>(predictor_->inflight_navigations_.size()));
796  predictor_->OnMainFrameRequest(summary3);
797  EXPECT_EQ(3, static_cast<int>(predictor_->inflight_navigations_.size()));
798
799  // Insert anther with same navigation id. It should replace.
800  URLRequestSummary summary4 = CreateURLRequestSummary(1,
801                                                       1,
802                                                       "http://www.nike.com",
803                                                       "http://www.nike.com",
804                                                       ResourceType::MAIN_FRAME,
805                                                       std::string(),
806                                                       false);
807  URLRequestSummary summary5 = CreateURLRequestSummary(1,
808                                                       2,
809                                                       "http://www.google.com",
810                                                       "http://www.google.com",
811                                                       ResourceType::MAIN_FRAME,
812                                                       std::string(),
813                                                       false);
814
815  predictor_->OnMainFrameRequest(summary4);
816  EXPECT_EQ(3, static_cast<int>(predictor_->inflight_navigations_.size()));
817
818  // Change this creation time so that it will go away on the next insert.
819  summary5.navigation_id.creation_time = base::TimeTicks::Now() -
820      base::TimeDelta::FromDays(1);
821  predictor_->OnMainFrameRequest(summary5);
822  EXPECT_EQ(3, static_cast<int>(predictor_->inflight_navigations_.size()));
823
824  URLRequestSummary summary6 = CreateURLRequestSummary(3,
825                                                       1,
826                                                       "http://www.shoes.com",
827                                                       "http://www.shoes.com",
828                                                       ResourceType::MAIN_FRAME,
829                                                       std::string(),
830                                                       false);
831  predictor_->OnMainFrameRequest(summary6);
832  EXPECT_EQ(3, static_cast<int>(predictor_->inflight_navigations_.size()));
833
834  EXPECT_TRUE(predictor_->inflight_navigations_.find(summary3.navigation_id) !=
835              predictor_->inflight_navigations_.end());
836  EXPECT_TRUE(predictor_->inflight_navigations_.find(summary4.navigation_id) !=
837              predictor_->inflight_navigations_.end());
838  EXPECT_TRUE(predictor_->inflight_navigations_.find(summary6.navigation_id) !=
839              predictor_->inflight_navigations_.end());
840}
841
842TEST_F(ResourcePrefetchPredictorTest, OnMainFrameRedirect) {
843  URLRequestSummary summary1 = CreateURLRequestSummary(1,
844                                                       1,
845                                                       "http://www.google.com",
846                                                       "http://www.google.com",
847                                                       ResourceType::MAIN_FRAME,
848                                                       std::string(),
849                                                       false);
850  URLRequestSummary summary2 = CreateURLRequestSummary(1,
851                                                       2,
852                                                       "http://www.google.com",
853                                                       "http://www.google.com",
854                                                       ResourceType::MAIN_FRAME,
855                                                       std::string(),
856                                                       false);
857  URLRequestSummary summary3 = CreateURLRequestSummary(2,
858                                                       1,
859                                                       "http://www.yahoo.com",
860                                                       "http://www.yahoo.com",
861                                                       ResourceType::MAIN_FRAME,
862                                                       std::string(),
863                                                       false);
864
865  predictor_->OnMainFrameRedirect(summary1);
866  EXPECT_TRUE(predictor_->inflight_navigations_.empty());
867
868  predictor_->OnMainFrameRequest(summary1);
869  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
870  predictor_->OnMainFrameRequest(summary2);
871  EXPECT_EQ(2, static_cast<int>(predictor_->inflight_navigations_.size()));
872
873  predictor_->OnMainFrameRedirect(summary3);
874  EXPECT_EQ(2, static_cast<int>(predictor_->inflight_navigations_.size()));
875  predictor_->OnMainFrameRedirect(summary1);
876  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
877  predictor_->OnMainFrameRedirect(summary2);
878  EXPECT_TRUE(predictor_->inflight_navigations_.empty());
879}
880
881TEST_F(ResourcePrefetchPredictorTest, OnSubresourceResponse) {
882  // If there is no inflight navigation, nothing happens.
883  URLRequestSummary resource1 = CreateURLRequestSummary(
884      1, 1, "http://www.google.com",  "http://google.com/style1.css",
885      ResourceType::STYLESHEET, "text/css", false);
886  predictor_->OnSubresourceResponse(resource1);
887  EXPECT_TRUE(predictor_->inflight_navigations_.empty());
888
889  // Add an inflight navigation.
890  URLRequestSummary main_frame1 =
891      CreateURLRequestSummary(1,
892                              1,
893                              "http://www.google.com",
894                              "http://www.google.com",
895                              ResourceType::MAIN_FRAME,
896                              std::string(),
897                              false);
898  predictor_->OnMainFrameRequest(main_frame1);
899  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
900
901  // Now add a few subresources.
902  URLRequestSummary resource2 = CreateURLRequestSummary(
903      1, 1, "http://www.google.com",  "http://google.com/script1.js",
904      ResourceType::SCRIPT, "text/javascript", false);
905  URLRequestSummary resource3 = CreateURLRequestSummary(
906      1, 1, "http://www.google.com",  "http://google.com/script2.js",
907      ResourceType::SCRIPT, "text/javascript", false);
908  predictor_->OnSubresourceResponse(resource1);
909  predictor_->OnSubresourceResponse(resource2);
910  predictor_->OnSubresourceResponse(resource3);
911
912  EXPECT_EQ(1, static_cast<int>(predictor_->inflight_navigations_.size()));
913  EXPECT_EQ(3, static_cast<int>(
914      predictor_->inflight_navigations_[main_frame1.navigation_id]->size()));
915  EXPECT_TRUE(URLRequestSummaryAreEqual(
916      resource1,
917      predictor_->inflight_navigations_[main_frame1.navigation_id]->at(0)));
918  EXPECT_TRUE(URLRequestSummaryAreEqual(
919      resource2,
920      predictor_->inflight_navigations_[main_frame1.navigation_id]->at(1)));
921  EXPECT_TRUE(URLRequestSummaryAreEqual(
922      resource3,
923      predictor_->inflight_navigations_[main_frame1.navigation_id]->at(2)));
924}
925
926}  // namespace predictors
927