1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <algorithm>
6#include <vector>
7
8#include "base/basictypes.h"
9#include "base/command_line.h"
10#include "base/file_path.h"
11#include "base/file_util.h"
12#include "base/memory/ref_counted_memory.h"
13#include "base/memory/scoped_temp_dir.h"
14#include "base/path_service.h"
15#include "chrome/browser/history/history_database.h"
16#include "chrome/browser/history/history_unittest_base.h"
17#include "chrome/browser/history/thumbnail_database.h"
18#include "chrome/common/chrome_constants.h"
19#include "chrome/common/chrome_paths.h"
20#include "chrome/common/thumbnail_score.h"
21#include "chrome/test/testing_profile.h"
22#include "chrome/tools/profiles/thumbnail-inl.h"
23#include "googleurl/src/gurl.h"
24#include "testing/gtest/include/gtest/gtest.h"
25#include "third_party/skia/include/core/SkBitmap.h"
26#include "ui/gfx/codec/jpeg_codec.h"
27
28using base::Time;
29using base::TimeDelta;
30
31namespace history {
32
33namespace {
34
35// data we'll put into the thumbnail database
36static const unsigned char blob1[] =
37    "12346102356120394751634516591348710478123649165419234519234512349134";
38static const unsigned char blob2[] =
39    "goiwuegrqrcomizqyzkjalitbahxfjytrqvpqeroicxmnlkhlzunacxaneviawrtxcywhgef";
40static const unsigned char blob3[] =
41    "3716871354098370776510470746794707624107647054607467847164027";
42const double kBoringness = 0.25;
43const double kWorseBoringness = 0.50;
44const double kBetterBoringness = 0.10;
45const double kTotallyBoring = 1.0;
46
47const int64 kPage1 = 1234;
48
49}  // namespace
50
51class ThumbnailDatabaseTest : public testing::Test {
52 public:
53  ThumbnailDatabaseTest() {
54  }
55  ~ThumbnailDatabaseTest() {
56  }
57
58 protected:
59  virtual void SetUp() {
60    // Get a temporary directory for the test DB files.
61    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
62
63    file_name_ = temp_dir_.path().AppendASCII("TestThumbnails.db");
64    new_file_name_ = temp_dir_.path().AppendASCII("TestFavicons.db");
65    history_db_name_ = temp_dir_.path().AppendASCII("TestHistory.db");
66    google_bitmap_.reset(
67        gfx::JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail)));
68  }
69
70  scoped_ptr<SkBitmap> google_bitmap_;
71
72  ScopedTempDir temp_dir_;
73  FilePath file_name_;
74  FilePath new_file_name_;
75  FilePath history_db_name_;
76};
77
78class IconMappingMigrationTest : public HistoryUnitTestBase {
79 public:
80  IconMappingMigrationTest() {
81  }
82  ~IconMappingMigrationTest() {
83  }
84
85 protected:
86  virtual void SetUp() {
87    profile_.reset(new TestingProfile);
88
89    FilePath data_path;
90    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_path));
91    data_path = data_path.AppendASCII("History");
92
93    history_db_name_ = profile_->GetPath().Append(chrome::kHistoryFilename);
94    // Set up history and thumbnails as they would be before migration.
95    ASSERT_NO_FATAL_FAILURE(
96        ExecuteSQLScript(data_path.AppendASCII("history.20.sql"),
97                         history_db_name_));
98    thumbnail_db_name_ =
99        profile_->GetPath().Append(chrome::kThumbnailsFilename);
100    ASSERT_NO_FATAL_FAILURE(
101        ExecuteSQLScript(data_path.AppendASCII("thumbnails.3.sql"),
102                         thumbnail_db_name_));
103  }
104
105 protected:
106  FilePath history_db_name_;
107  FilePath thumbnail_db_name_;
108
109 private:
110  scoped_ptr<TestingProfile> profile_;
111};
112
113TEST_F(ThumbnailDatabaseTest, GetFaviconAfterMigrationToTopSites) {
114  ThumbnailDatabase db;
115  ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL));
116  db.BeginTransaction();
117
118  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
119  scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data));
120
121  GURL url("http://google.com");
122  FaviconID id = db.AddFavicon(url, FAVICON);
123  base::Time time = base::Time::Now();
124  db.SetFavicon(id, favicon, time);
125  EXPECT_TRUE(db.RenameAndDropThumbnails(file_name_, new_file_name_));
126
127  base::Time time_out;
128  std::vector<unsigned char> favicon_out;
129  GURL url_out;
130  EXPECT_TRUE(db.GetFavicon(id, &time_out, &favicon_out, &url_out));
131  EXPECT_EQ(url, url_out);
132  EXPECT_EQ(time.ToTimeT(), time_out.ToTimeT());
133  ASSERT_EQ(data.size(), favicon_out.size());
134  EXPECT_TRUE(std::equal(data.begin(),
135                         data.end(),
136                         favicon_out.begin()));
137}
138
139TEST_F(ThumbnailDatabaseTest, AddIconMapping) {
140  ThumbnailDatabase db;
141  ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL));
142  db.BeginTransaction();
143
144  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
145  scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data));
146
147  GURL url("http://google.com");
148  FaviconID id = db.AddFavicon(url, TOUCH_ICON);
149  EXPECT_NE(0, id);
150  base::Time time = base::Time::Now();
151  db.SetFavicon(id, favicon, time);
152
153  EXPECT_NE(0, db.AddIconMapping(url, id));
154  std::vector<IconMapping> icon_mapping;
155  EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
156  EXPECT_EQ(1u, icon_mapping.size());
157  EXPECT_EQ(url, icon_mapping.front().page_url);
158  EXPECT_EQ(id, icon_mapping.front().icon_id);
159}
160
161TEST_F(ThumbnailDatabaseTest, UpdateIconMapping) {
162  ThumbnailDatabase db;
163  ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL));
164  db.BeginTransaction();
165
166  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
167  scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data));
168
169  GURL url("http://google.com");
170  FaviconID id = db.AddFavicon(url, TOUCH_ICON);
171  base::Time time = base::Time::Now();
172  db.SetFavicon(id, favicon, time);
173
174  EXPECT_TRUE(0 < db.AddIconMapping(url, id));
175  std::vector<IconMapping> icon_mapping;
176  EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
177  ASSERT_EQ(1u, icon_mapping.size());
178  EXPECT_EQ(url, icon_mapping.front().page_url);
179  EXPECT_EQ(id, icon_mapping.front().icon_id);
180
181  GURL url1("http://www.google.com/");
182  FaviconID new_id = db.AddFavicon(url1, TOUCH_ICON);
183  EXPECT_TRUE(db.UpdateIconMapping(icon_mapping.front().mapping_id, new_id));
184
185  icon_mapping.clear();
186  EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
187  ASSERT_EQ(1u, icon_mapping.size());
188  EXPECT_EQ(url, icon_mapping.front().page_url);
189  EXPECT_EQ(new_id, icon_mapping.front().icon_id);
190  EXPECT_NE(id, icon_mapping.front().icon_id);
191}
192
193TEST_F(ThumbnailDatabaseTest, DeleteIconMappings) {
194  ThumbnailDatabase db;
195  ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL));
196  db.BeginTransaction();
197
198  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
199  scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data));
200
201  GURL url("http://google.com");
202  FaviconID id = db.AddFavicon(url, TOUCH_ICON);
203  base::Time time = base::Time::Now();
204  db.SetFavicon(id, favicon, time);
205  EXPECT_TRUE(0 < db.AddIconMapping(url, id));
206
207  FaviconID id2 = db.AddFavicon(url, FAVICON);
208  db.SetFavicon(id2, favicon, time);
209  EXPECT_TRUE(0 < db.AddIconMapping(url, id2));
210  ASSERT_NE(id, id2);
211
212  std::vector<IconMapping> icon_mapping;
213  EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
214  ASSERT_EQ(2u, icon_mapping.size());
215  EXPECT_EQ(icon_mapping.front().icon_type, TOUCH_ICON);
216  EXPECT_TRUE(db.GetIconMappingForPageURL(url, FAVICON, NULL));
217
218  db.DeleteIconMappings(url);
219
220  EXPECT_FALSE(db.GetIconMappingsForPageURL(url, NULL));
221  EXPECT_FALSE(db.GetIconMappingForPageURL(url, FAVICON, NULL));
222}
223
224TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURL) {
225  ThumbnailDatabase db;
226  ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL));
227  db.BeginTransaction();
228
229  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
230  scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data));
231
232  GURL url("http://google.com");
233
234  FaviconID id1 = db.AddFavicon(url, TOUCH_ICON);
235  base::Time time = base::Time::Now();
236  db.SetFavicon(id1, favicon, time);
237  EXPECT_TRUE(0 < db.AddIconMapping(url, id1));
238
239  FaviconID id2 = db.AddFavicon(url, FAVICON);
240  EXPECT_NE(id1, id2);
241  db.SetFavicon(id2, favicon, time);
242  EXPECT_TRUE(0 < db.AddIconMapping(url, id2));
243
244  std::vector<IconMapping> icon_mapping;
245  EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
246  ASSERT_EQ(2u, icon_mapping.size());
247  EXPECT_NE(icon_mapping[0].icon_id, icon_mapping[1].icon_id);
248  EXPECT_TRUE(icon_mapping[0].icon_id == id1 && icon_mapping[1].icon_id == id2);
249}
250
251TEST_F(ThumbnailDatabaseTest, UpgradeToVersion4) {
252  ThumbnailDatabase db;
253  ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL));
254  db.BeginTransaction();
255
256  const char* name = "favicons";
257  std::string sql;
258  sql.append("DROP TABLE IF EXISTS ");
259  sql.append(name);
260  EXPECT_TRUE(db.db_.Execute(sql.c_str()));
261
262  sql.resize(0);
263  sql.append("CREATE TABLE ");
264  sql.append(name);
265  sql.append("("
266             "id INTEGER PRIMARY KEY,"
267             "url LONGVARCHAR NOT NULL,"
268             "last_updated INTEGER DEFAULT 0,"
269             "image_data BLOB)");
270  EXPECT_TRUE(db.db_.Execute(sql.c_str()));
271
272  EXPECT_TRUE(db.UpgradeToVersion4());
273
274  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
275  scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data));
276
277  GURL url("http://google.com");
278  FaviconID id = db.AddFavicon(url, TOUCH_ICON);
279  base::Time time = base::Time::Now();
280  db.SetFavicon(id, favicon, time);
281
282  EXPECT_TRUE(0 < db.AddIconMapping(url, id));
283  IconMapping icon_mapping;
284  EXPECT_TRUE(db.GetIconMappingForPageURL(url, TOUCH_ICON, &icon_mapping));
285  EXPECT_EQ(url, icon_mapping.page_url);
286  EXPECT_EQ(id, icon_mapping.icon_id);
287}
288
289TEST_F(ThumbnailDatabaseTest, TemporayIconMapping) {
290  ThumbnailDatabase db;
291
292  ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL));
293
294  db.BeginTransaction();
295
296  EXPECT_TRUE(db.InitTemporaryIconMappingTable());
297
298  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
299  scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data));
300
301  GURL url("http://google.com");
302  FaviconID id = db.AddFavicon(url, FAVICON);
303  base::Time time = base::Time::Now();
304  db.SetFavicon(id, favicon, time);
305
306  db.AddToTemporaryIconMappingTable(url, id);
307  db.CommitTemporaryIconMappingTable();
308  IconMapping icon_mapping;
309  EXPECT_TRUE(db.GetIconMappingForPageURL(url, FAVICON, &icon_mapping));
310  EXPECT_EQ(id, icon_mapping.icon_id);
311  EXPECT_EQ(url, icon_mapping.page_url);
312}
313
314TEST_F(ThumbnailDatabaseTest, GetIconMappingsForPageURLForReturnOrder) {
315  ThumbnailDatabase db;
316  ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL));
317  db.BeginTransaction();
318
319  // Add a favicon
320  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
321  scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data));
322
323  GURL url("http://google.com");
324  FaviconID id = db.AddFavicon(url, FAVICON);
325  base::Time time = base::Time::Now();
326  db.SetFavicon(id, favicon, time);
327
328  EXPECT_NE(0, db.AddIconMapping(url, id));
329  std::vector<IconMapping> icon_mapping;
330  EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
331
332  EXPECT_EQ(url, icon_mapping.front().page_url);
333  EXPECT_EQ(id, icon_mapping.front().icon_id);
334  EXPECT_EQ(FAVICON, icon_mapping.front().icon_type);
335
336  // Add a touch icon
337  std::vector<unsigned char> data2(blob2, blob2 + sizeof(blob2));
338  scoped_refptr<RefCountedBytes> favicon2(new RefCountedBytes(data));
339
340  FaviconID id2 = db.AddFavicon(url, TOUCH_ICON);
341  db.SetFavicon(id2, favicon2, time);
342  EXPECT_NE(0, db.AddIconMapping(url, id2));
343
344  icon_mapping.clear();
345  EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
346
347  EXPECT_EQ(url, icon_mapping.front().page_url);
348  EXPECT_EQ(id2, icon_mapping.front().icon_id);
349  EXPECT_EQ(TOUCH_ICON, icon_mapping.front().icon_type);
350
351  // Add a touch precomposed icon
352  scoped_refptr<RefCountedBytes> favicon3(new RefCountedBytes(data2));
353
354  FaviconID id3 = db.AddFavicon(url, TOUCH_PRECOMPOSED_ICON);
355  db.SetFavicon(id3, favicon3, time);
356  EXPECT_NE(0, db.AddIconMapping(url, id3));
357
358  icon_mapping.clear();
359  EXPECT_TRUE(db.GetIconMappingsForPageURL(url, &icon_mapping));
360
361  EXPECT_EQ(url, icon_mapping.front().page_url);
362  EXPECT_EQ(id3, icon_mapping.front().icon_id);
363  EXPECT_EQ(TOUCH_PRECOMPOSED_ICON, icon_mapping.front().icon_type);
364}
365
366TEST_F(ThumbnailDatabaseTest, HasMappingFor) {
367  ThumbnailDatabase db;
368  ASSERT_EQ(sql::INIT_OK, db.Init(file_name_, NULL, NULL));
369  db.BeginTransaction();
370
371  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
372  scoped_refptr<RefCountedBytes> favicon(new RefCountedBytes(data));
373
374  // Add a favicon which will have icon_mappings
375  FaviconID id1 = db.AddFavicon(GURL("http://google.com"), FAVICON);
376  EXPECT_NE(id1, 0);
377  base::Time time = base::Time::Now();
378  db.SetFavicon(id1, favicon, time);
379
380  // Add another type of favicon
381  FaviconID id2 = db.AddFavicon(GURL("http://www.google.com/icon"), TOUCH_ICON);
382  EXPECT_NE(id2, 0);
383  time = base::Time::Now();
384  db.SetFavicon(id2, favicon, time);
385
386  // Add 3rd favicon
387  FaviconID id3 = db.AddFavicon(GURL("http://www.google.com/icon"), TOUCH_ICON);
388  EXPECT_NE(id3, 0);
389  time = base::Time::Now();
390  db.SetFavicon(id3, favicon, time);
391
392  // Add 2 icon mapping
393  GURL page_url("http://www.google.com");
394  EXPECT_TRUE(db.AddIconMapping(page_url, id1));
395  EXPECT_TRUE(db.AddIconMapping(page_url, id2));
396
397  EXPECT_TRUE(db.HasMappingFor(id1));
398  EXPECT_TRUE(db.HasMappingFor(id2));
399  EXPECT_FALSE(db.HasMappingFor(id3));
400
401  // Remove all mappings
402  db.DeleteIconMappings(page_url);
403  EXPECT_FALSE(db.HasMappingFor(id1));
404  EXPECT_FALSE(db.HasMappingFor(id2));
405  EXPECT_FALSE(db.HasMappingFor(id3));
406}
407
408TEST_F(IconMappingMigrationTest, TestIconMappingMigration) {
409  HistoryDatabase history_db;
410  ASSERT_TRUE(history_db.db_.Open(history_db_name_));
411  history_db.BeginTransaction();
412
413  const GURL icon1 = GURL("http://www.google.com/favicon.ico");
414  const GURL icon2 = GURL("http://www.yahoo.com/favicon.ico");
415
416  ThumbnailDatabase db;
417  ASSERT_EQ(sql::INIT_OK, db.Init(thumbnail_db_name_, NULL, &history_db));
418  db.BeginTransaction();
419
420  // Migration should be done.
421  // Test one icon_mapping.
422  GURL page_url1 = GURL("http://google.com/");
423  std::vector<IconMapping> icon_mappings;
424  EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url1, &icon_mappings));
425  ASSERT_EQ(1u, icon_mappings.size());
426  EXPECT_EQ(FAVICON, icon_mappings[0].icon_type);
427  EXPECT_EQ(page_url1, icon_mappings[0].page_url);
428  EXPECT_EQ(1, icon_mappings[0].icon_id);
429  base::Time time;
430  std::vector<unsigned char> out_data;
431  GURL out_icon_url;
432  ASSERT_TRUE(db.GetFavicon(
433      icon_mappings[0].icon_id, &time, &out_data, &out_icon_url));
434  EXPECT_EQ(icon1, out_icon_url);
435
436  // Test a page which has the same icon.
437  GURL page_url3 = GURL("http://www.google.com/");
438  icon_mappings.clear();
439  EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url3, &icon_mappings));
440  ASSERT_EQ(1u, icon_mappings.size());
441  EXPECT_EQ(FAVICON, icon_mappings[0].icon_type);
442  EXPECT_EQ(page_url3, icon_mappings[0].page_url);
443  EXPECT_EQ(1, icon_mappings[0].icon_id);
444
445  // Test a icon_mapping with different IconID.
446  GURL page_url2 = GURL("http://yahoo.com/");
447  icon_mappings.clear();
448  EXPECT_TRUE(db.GetIconMappingsForPageURL(page_url2, &icon_mappings));
449  ASSERT_EQ(1u, icon_mappings.size());
450  EXPECT_EQ(FAVICON, icon_mappings[0].icon_type);
451  EXPECT_EQ(page_url2, icon_mappings[0].page_url);
452  EXPECT_EQ(2, icon_mappings[0].icon_id);
453  ASSERT_TRUE(db.GetFavicon(
454      icon_mappings[0].icon_id, &time, &out_data, &out_icon_url));
455  EXPECT_EQ(icon2, out_icon_url);
456
457  // Test a page without icon
458  GURL page_url4 = GURL("http://www.google.com/blank.html");
459  EXPECT_FALSE(db.GetIconMappingsForPageURL(page_url4, NULL));
460}
461
462}  // namespace history
463