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 <set>
6#include <utility>
7#include <vector>
8
9#include "base/message_loop/message_loop.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/predictors/predictor_database.h"
12#include "chrome/browser/predictors/resource_prefetch_predictor_tables.h"
13#include "chrome/test/base/testing_profile.h"
14#include "content/public/test/test_browser_thread.h"
15#include "sql/statement.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace predictors {
19
20class ResourcePrefetchPredictorTablesTest : public testing::Test {
21 public:
22  ResourcePrefetchPredictorTablesTest();
23  virtual ~ResourcePrefetchPredictorTablesTest();
24  virtual void SetUp() OVERRIDE;
25  virtual void TearDown() OVERRIDE;
26
27 protected:
28  void TestGetAllData();
29  void TestUpdateData();
30  void TestDeleteData();
31  void TestDeleteSingleDataPoint();
32  void TestDeleteAllData();
33
34  base::MessageLoop loop_;
35  content::TestBrowserThread db_thread_;
36  TestingProfile profile_;
37  scoped_ptr<PredictorDatabase> db_;
38  scoped_refptr<ResourcePrefetchPredictorTables> tables_;
39
40 private:
41  typedef ResourcePrefetchPredictorTables::ResourceRow ResourceRow;
42  typedef std::vector<ResourceRow> ResourceRows;
43  typedef ResourcePrefetchPredictorTables::PrefetchData PrefetchData;
44  typedef ResourcePrefetchPredictorTables::PrefetchDataMap PrefetchDataMap;
45
46  // Initializes the tables, |test_url_data_| and |test_host_data_|.
47  void InitializeSampleData();
48
49  // Checks that the input PrefetchData are the same, although the resources
50  // can be in different order.
51  void TestPrefetchDataAreEqual(const PrefetchDataMap& lhs,
52                                const PrefetchDataMap& rhs) const;
53  void TestResourceRowsAreEqual(const ResourceRows& lhs,
54                                const ResourceRows& rhs) const;
55
56  void AddKey(PrefetchDataMap* m, const std::string& key) const;
57
58  // Useful for debugging test.
59  void PrintPrefetchData(const PrefetchData& data) const {
60    LOG(ERROR) << "[" << data.key_type << "," << data.primary_key
61               << "," << data.last_visit.ToInternalValue() << "]";
62    for (ResourceRows::const_iterator it = data.resources.begin();
63         it != data.resources.end(); ++it) {
64      LOG(ERROR) << "\t\t" << it->resource_url << "\t" << it->resource_type
65                 << "\t" << it->number_of_hits << "\t" << it->number_of_misses
66                 << "\t" << it->consecutive_misses
67                 << "\t" << it->average_position
68                 << "\t" << it->score;
69    }
70  }
71
72  PrefetchDataMap test_url_data_;
73  PrefetchDataMap test_host_data_;
74};
75
76class ResourcePrefetchPredictorTablesReopenTest
77    : public ResourcePrefetchPredictorTablesTest {
78 public:
79  virtual void SetUp() OVERRIDE {
80    // Write data to the table, and then reopen the db.
81    ResourcePrefetchPredictorTablesTest::SetUp();
82    ResourcePrefetchPredictorTablesTest::TearDown();
83
84    db_.reset(new PredictorDatabase(&profile_));
85    loop_.RunUntilIdle();
86    tables_ = db_->resource_prefetch_tables();
87  }
88};
89
90ResourcePrefetchPredictorTablesTest::ResourcePrefetchPredictorTablesTest()
91    : loop_(base::MessageLoop::TYPE_DEFAULT),
92      db_thread_(content::BrowserThread::DB, &loop_),
93      db_(new PredictorDatabase(&profile_)),
94      tables_(db_->resource_prefetch_tables()) {
95  loop_.RunUntilIdle();
96}
97
98ResourcePrefetchPredictorTablesTest::~ResourcePrefetchPredictorTablesTest() {
99}
100
101void ResourcePrefetchPredictorTablesTest::SetUp() {
102  tables_->DeleteAllData();
103  InitializeSampleData();
104}
105
106void ResourcePrefetchPredictorTablesTest::TearDown() {
107  tables_ = NULL;
108  db_.reset();
109  loop_.RunUntilIdle();
110}
111
112void ResourcePrefetchPredictorTablesTest::TestGetAllData() {
113  PrefetchDataMap actual_url_data, actual_host_data;
114  tables_->GetAllData(&actual_url_data, &actual_host_data);
115
116  TestPrefetchDataAreEqual(test_url_data_, actual_url_data);
117  TestPrefetchDataAreEqual(test_host_data_, actual_host_data);
118}
119
120void ResourcePrefetchPredictorTablesTest::TestDeleteData() {
121  std::vector<std::string> urls_to_delete, hosts_to_delete;
122  urls_to_delete.push_back("http://www.google.com");
123  urls_to_delete.push_back("http://www.yahoo.com");
124  hosts_to_delete.push_back("www.yahoo.com");
125
126  tables_->DeleteData(urls_to_delete, hosts_to_delete);
127
128  PrefetchDataMap actual_url_data, actual_host_data;
129  tables_->GetAllData(&actual_url_data, &actual_host_data);
130
131  PrefetchDataMap expected_url_data, expected_host_data;
132  AddKey(&expected_url_data, "http://www.reddit.com");
133  AddKey(&expected_host_data, "www.facebook.com");
134
135  TestPrefetchDataAreEqual(expected_url_data, actual_url_data);
136  TestPrefetchDataAreEqual(expected_host_data, actual_host_data);
137}
138
139void ResourcePrefetchPredictorTablesTest::TestDeleteSingleDataPoint() {
140  // Delete a URL.
141  tables_->DeleteSingleDataPoint("http://www.reddit.com",
142                                 PREFETCH_KEY_TYPE_URL);
143
144  PrefetchDataMap actual_url_data, actual_host_data;
145  tables_->GetAllData(&actual_url_data, &actual_host_data);
146
147  PrefetchDataMap expected_url_data;
148  AddKey(&expected_url_data, "http://www.google.com");
149  AddKey(&expected_url_data, "http://www.yahoo.com");
150
151  TestPrefetchDataAreEqual(expected_url_data, actual_url_data);
152  TestPrefetchDataAreEqual(test_host_data_, actual_host_data);
153
154  // Delete a host.
155  tables_->DeleteSingleDataPoint("www.facebook.com", PREFETCH_KEY_TYPE_HOST);
156  actual_url_data.clear();
157  actual_host_data.clear();
158  tables_->GetAllData(&actual_url_data, &actual_host_data);
159
160  PrefetchDataMap expected_host_data;
161  AddKey(&expected_host_data, "www.yahoo.com");
162
163  TestPrefetchDataAreEqual(expected_url_data, actual_url_data);
164  TestPrefetchDataAreEqual(expected_host_data, actual_host_data);
165}
166
167void ResourcePrefetchPredictorTablesTest::TestUpdateData() {
168  PrefetchData google(PREFETCH_KEY_TYPE_URL, "http://www.google.com");
169  google.last_visit = base::Time::FromInternalValue(10);
170  google.resources.push_back(ResourceRow(std::string(),
171                                         "http://www.google.com/style.css",
172                                         content::RESOURCE_TYPE_STYLESHEET,
173                                         6,
174                                         2,
175                                         0,
176                                         1.0));
177  google.resources.push_back(ResourceRow(std::string(),
178                                         "http://www.google.com/image.png",
179                                         content::RESOURCE_TYPE_IMAGE,
180                                         6,
181                                         4,
182                                         1,
183                                         4.2));
184  google.resources.push_back(ResourceRow(std::string(),
185                                         "http://www.google.com/a.xml",
186                                         content::RESOURCE_TYPE_LAST_TYPE,
187                                         1,
188                                         0,
189                                         0,
190                                         6.1));
191  google.resources
192      .push_back(ResourceRow(std::string(),
193                             "http://www.resources.google.com/script.js",
194                             content::RESOURCE_TYPE_SCRIPT,
195                             12,
196                             0,
197                             0,
198                             8.5));
199
200  PrefetchData yahoo(PREFETCH_KEY_TYPE_HOST, "www.yahoo.com");
201  yahoo.last_visit = base::Time::FromInternalValue(7);
202  yahoo.resources.push_back(ResourceRow(std::string(),
203                                        "http://www.yahoo.com/image.png",
204                                        content::RESOURCE_TYPE_IMAGE,
205                                        120,
206                                        1,
207                                        1,
208                                        10.0));
209
210  tables_->UpdateData(google, yahoo);
211
212  PrefetchDataMap actual_url_data, actual_host_data;
213  tables_->GetAllData(&actual_url_data, &actual_host_data);
214
215  PrefetchDataMap expected_url_data, expected_host_data;
216  AddKey(&expected_url_data, "http://www.reddit.com");
217  AddKey(&expected_url_data, "http://www.yahoo.com");
218  expected_url_data.insert(std::make_pair("http://www.google.com", google));
219
220  AddKey(&expected_host_data, "www.facebook.com");
221  expected_host_data.insert(std::make_pair("www.yahoo.com", yahoo));
222
223  TestPrefetchDataAreEqual(expected_url_data, actual_url_data);
224  TestPrefetchDataAreEqual(expected_host_data, actual_host_data);
225}
226
227void ResourcePrefetchPredictorTablesTest::TestDeleteAllData() {
228  tables_->DeleteAllData();
229
230  PrefetchDataMap actual_url_data, actual_host_data;
231  tables_->GetAllData(&actual_url_data, &actual_host_data);
232  EXPECT_TRUE(actual_url_data.empty());
233  EXPECT_TRUE(actual_host_data.empty());
234}
235
236void ResourcePrefetchPredictorTablesTest::TestPrefetchDataAreEqual(
237    const PrefetchDataMap& lhs,
238    const PrefetchDataMap& rhs) const {
239  EXPECT_EQ(lhs.size(), rhs.size());
240
241  for (PrefetchDataMap::const_iterator rhs_it = rhs.begin();
242       rhs_it != rhs.end(); ++rhs_it) {
243    PrefetchDataMap::const_iterator lhs_it = lhs.find(rhs_it->first);
244    ASSERT_TRUE(lhs_it != lhs.end()) << rhs_it->first;
245
246    TestResourceRowsAreEqual(lhs_it->second.resources,
247                             rhs_it->second.resources);
248  }
249}
250
251void ResourcePrefetchPredictorTablesTest::TestResourceRowsAreEqual(
252    const ResourceRows& lhs,
253    const ResourceRows& rhs) const {
254  EXPECT_EQ(lhs.size(), rhs.size());
255
256  std::set<GURL> resources_seen;
257  for (ResourceRows::const_iterator rhs_it = rhs.begin();
258       rhs_it != rhs.end(); ++rhs_it) {
259    const GURL& resource = rhs_it->resource_url;
260    EXPECT_FALSE(ContainsKey(resources_seen, resource));
261
262    for (ResourceRows::const_iterator lhs_it = lhs.begin();
263         lhs_it != lhs.end(); ++lhs_it) {
264      if (*rhs_it == *lhs_it) {
265        resources_seen.insert(resource);
266        break;
267      }
268    }
269    EXPECT_TRUE(ContainsKey(resources_seen, resource));
270  }
271  EXPECT_EQ(lhs.size(), resources_seen.size());
272}
273
274void ResourcePrefetchPredictorTablesTest::AddKey(PrefetchDataMap* m,
275                                                 const std::string& key) const {
276  PrefetchDataMap::const_iterator it = test_url_data_.find(key);
277  if (it != test_url_data_.end()) {
278    m->insert(std::make_pair(it->first, it->second));
279    return;
280  }
281  it = test_host_data_.find(key);
282  ASSERT_TRUE(it != test_host_data_.end());
283  m->insert(std::make_pair(it->first, it->second));
284}
285
286void ResourcePrefetchPredictorTablesTest::InitializeSampleData() {
287  {  // Url data.
288    PrefetchData google(PREFETCH_KEY_TYPE_URL, "http://www.google.com");
289    google.last_visit = base::Time::FromInternalValue(1);
290    google.resources.push_back(ResourceRow(std::string(),
291                                           "http://www.google.com/style.css",
292                                           content::RESOURCE_TYPE_STYLESHEET,
293                                           5,
294                                           2,
295                                           1,
296                                           1.1));
297    google.resources.push_back(ResourceRow(std::string(),
298                                           "http://www.google.com/script.js",
299                                           content::RESOURCE_TYPE_SCRIPT,
300                                           4,
301                                           0,
302                                           1,
303                                           2.1));
304    google.resources.push_back(ResourceRow(std::string(),
305                                           "http://www.google.com/image.png",
306                                           content::RESOURCE_TYPE_IMAGE,
307                                           6,
308                                           3,
309                                           0,
310                                           2.2));
311    google.resources.push_back(ResourceRow(std::string(),
312                                           "http://www.google.com/a.font",
313                                           content::RESOURCE_TYPE_LAST_TYPE,
314                                           2,
315                                           0,
316                                           0,
317                                           5.1));
318    google.resources
319        .push_back(ResourceRow(std::string(),
320                               "http://www.resources.google.com/script.js",
321                               content::RESOURCE_TYPE_SCRIPT,
322                               11,
323                               0,
324                               0,
325                               8.5));
326
327    PrefetchData reddit(PREFETCH_KEY_TYPE_URL, "http://www.reddit.com");
328    reddit.last_visit = base::Time::FromInternalValue(2);
329    reddit.resources
330        .push_back(ResourceRow(std::string(),
331                               "http://reddit-resource.com/script1.js",
332                               content::RESOURCE_TYPE_SCRIPT,
333                               4,
334                               0,
335                               1,
336                               1.0));
337    reddit.resources
338        .push_back(ResourceRow(std::string(),
339                               "http://reddit-resource.com/script2.js",
340                               content::RESOURCE_TYPE_SCRIPT,
341                               2,
342                               0,
343                               0,
344                               2.1));
345
346    PrefetchData yahoo(PREFETCH_KEY_TYPE_URL, "http://www.yahoo.com");
347    yahoo.last_visit = base::Time::FromInternalValue(3);
348    yahoo.resources.push_back(ResourceRow(std::string(),
349                                          "http://www.google.com/image.png",
350                                          content::RESOURCE_TYPE_IMAGE,
351                                          20,
352                                          1,
353                                          0,
354                                          10.0));
355
356    test_url_data_.clear();
357    test_url_data_.insert(std::make_pair("http://www.google.com", google));
358    test_url_data_.insert(std::make_pair("http://www.reddit.com", reddit));
359    test_url_data_.insert(std::make_pair("http://www.yahoo.com", yahoo));
360
361    PrefetchData empty_host_data(PREFETCH_KEY_TYPE_HOST, std::string());
362    tables_->UpdateData(google, empty_host_data);
363    tables_->UpdateData(reddit, empty_host_data);
364    tables_->UpdateData(yahoo, empty_host_data);
365  }
366
367  {  // Host data.
368    PrefetchData facebook(PREFETCH_KEY_TYPE_HOST, "www.facebook.com");
369    facebook.last_visit = base::Time::FromInternalValue(4);
370    facebook.resources
371        .push_back(ResourceRow(std::string(),
372                               "http://www.facebook.com/style.css",
373                               content::RESOURCE_TYPE_STYLESHEET,
374                               5,
375                               2,
376                               1,
377                               1.1));
378    facebook.resources
379        .push_back(ResourceRow(std::string(),
380                               "http://www.facebook.com/script.js",
381                               content::RESOURCE_TYPE_SCRIPT,
382                               4,
383                               0,
384                               1,
385                               2.1));
386    facebook.resources
387        .push_back(ResourceRow(std::string(),
388                               "http://www.facebook.com/image.png",
389                               content::RESOURCE_TYPE_IMAGE,
390                               6,
391                               3,
392                               0,
393                               2.2));
394    facebook.resources.push_back(ResourceRow(std::string(),
395                                             "http://www.facebook.com/a.font",
396                                             content::RESOURCE_TYPE_LAST_TYPE,
397                                             2,
398                                             0,
399                                             0,
400                                             5.1));
401    facebook.resources
402        .push_back(ResourceRow(std::string(),
403                               "http://www.resources.facebook.com/script.js",
404                               content::RESOURCE_TYPE_SCRIPT,
405                               11,
406                               0,
407                               0,
408                               8.5));
409
410    PrefetchData yahoo(PREFETCH_KEY_TYPE_HOST, "www.yahoo.com");
411    yahoo.last_visit = base::Time::FromInternalValue(5);
412    yahoo.resources.push_back(ResourceRow(std::string(),
413                                          "http://www.google.com/image.png",
414                                          content::RESOURCE_TYPE_IMAGE,
415                                          20,
416                                          1,
417                                          0,
418                                          10.0));
419
420    test_host_data_.clear();
421    test_host_data_.insert(std::make_pair("www.facebook.com", facebook));
422    test_host_data_.insert(std::make_pair("www.yahoo.com", yahoo));
423
424    PrefetchData empty_url_data(PREFETCH_KEY_TYPE_URL, std::string());
425    tables_->UpdateData(empty_url_data, facebook);
426    tables_->UpdateData(empty_url_data, yahoo);
427  }
428}
429
430// Test cases.
431
432TEST_F(ResourcePrefetchPredictorTablesTest, GetAllData) {
433  TestGetAllData();
434}
435
436TEST_F(ResourcePrefetchPredictorTablesTest, UpdateData) {
437  TestUpdateData();
438}
439
440TEST_F(ResourcePrefetchPredictorTablesTest, DeleteData) {
441  TestDeleteData();
442}
443
444TEST_F(ResourcePrefetchPredictorTablesTest, DeleteSingleDataPoint) {
445  TestDeleteSingleDataPoint();
446}
447
448TEST_F(ResourcePrefetchPredictorTablesTest, DeleteAllData) {
449  TestDeleteAllData();
450}
451
452TEST_F(ResourcePrefetchPredictorTablesReopenTest, GetAllData) {
453  TestGetAllData();
454}
455
456TEST_F(ResourcePrefetchPredictorTablesReopenTest, UpdateData) {
457  TestUpdateData();
458}
459
460TEST_F(ResourcePrefetchPredictorTablesReopenTest, DeleteData) {
461  TestDeleteData();
462}
463
464TEST_F(ResourcePrefetchPredictorTablesReopenTest, DeleteSingleDataPoint) {
465  TestDeleteSingleDataPoint();
466}
467
468TEST_F(ResourcePrefetchPredictorTablesReopenTest, DeleteAllData) {
469  TestDeleteAllData();
470}
471
472}  // namespace predictors
473