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 <set>
6#include <vector>
7
8#include "base/command_line.h"
9#include "base/file_path.h"
10#include "base/file_util.h"
11#include "base/memory/ref_counted.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/path_service.h"
14#include "base/string16.h"
15#include "base/utf_string_conversions.h"
16#include "chrome/browser/bookmarks/bookmark_model.h"
17#include "chrome/browser/history/history_backend.h"
18#include "chrome/browser/history/history_notifications.h"
19#include "chrome/browser/history/in_memory_database.h"
20#include "chrome/browser/history/in_memory_history_backend.h"
21#include "chrome/common/chrome_constants.h"
22#include "chrome/common/chrome_paths.h"
23#include "chrome/common/thumbnail_score.h"
24#include "chrome/tools/profiles/thumbnail-inl.h"
25#include "content/common/notification_details.h"
26#include "content/common/notification_source.h"
27#include "googleurl/src/gurl.h"
28#include "testing/gtest/include/gtest/gtest.h"
29#include "ui/gfx/codec/jpeg_codec.h"
30
31using base::Time;
32
33// This file only tests functionality where it is most convenient to call the
34// backend directly. Most of the history backend functions are tested by the
35// history unit test. Because of the elaborate callbacks involved, this is no
36// harder than calling it directly for many things.
37
38namespace {
39
40// data we'll put into the thumbnail database
41static const unsigned char blob1[] =
42    "12346102356120394751634516591348710478123649165419234519234512349134";
43
44}  // namepace
45
46namespace history {
47
48class HistoryBackendTest;
49
50// This must be a separate object since HistoryBackend manages its lifetime.
51// This just forwards the messages we're interested in to the test object.
52class HistoryBackendTestDelegate : public HistoryBackend::Delegate {
53 public:
54  explicit HistoryBackendTestDelegate(HistoryBackendTest* test) : test_(test) {}
55
56  virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {}
57  virtual void SetInMemoryBackend(InMemoryHistoryBackend* backend) OVERRIDE;
58  virtual void BroadcastNotifications(NotificationType type,
59                                      HistoryDetails* details) OVERRIDE;
60  virtual void DBLoaded() OVERRIDE;
61  virtual void StartTopSitesMigration() OVERRIDE;
62
63 private:
64  // Not owned by us.
65  HistoryBackendTest* test_;
66
67  DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
68};
69
70class HistoryBackendTest : public testing::Test {
71 public:
72  HistoryBackendTest() : bookmark_model_(NULL), loaded_(false) {}
73  virtual ~HistoryBackendTest() {
74  }
75
76 protected:
77  scoped_refptr<HistoryBackend> backend_;  // Will be NULL on init failure.
78  scoped_ptr<InMemoryHistoryBackend> mem_backend_;
79
80  void AddRedirectChain(const char* sequence[], int page_id) {
81    history::RedirectList redirects;
82    for (int i = 0; sequence[i] != NULL; ++i)
83      redirects.push_back(GURL(sequence[i]));
84
85    int int_scope = 1;
86    void* scope = 0;
87    memcpy(&scope, &int_scope, sizeof(int_scope));
88    scoped_refptr<history::HistoryAddPageArgs> request(
89        new history::HistoryAddPageArgs(
90            redirects.back(), Time::Now(), scope, page_id, GURL(),
91            redirects, PageTransition::LINK, history::SOURCE_BROWSED, true));
92    backend_->AddPage(request);
93  }
94
95  // Adds CLIENT_REDIRECT page transition.
96  // |url1| is the source URL and |url2| is the destination.
97  // |did_replace| is true if the transition is non-user initiated and the
98  // navigation entry for |url2| has replaced that for |url1|. The possibly
99  // updated transition code of the visit records for |url1| and |url2| is
100  // returned by filling in |*transition1| and |*transition2|, respectively.
101  void  AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace,
102                          int* transition1, int* transition2) {
103    void* const dummy_scope = reinterpret_cast<void*>(0x87654321);
104    history::RedirectList redirects;
105    if (url1.is_valid())
106      redirects.push_back(url1);
107    if (url2.is_valid())
108      redirects.push_back(url2);
109    scoped_refptr<HistoryAddPageArgs> request(
110        new HistoryAddPageArgs(url2, base::Time(), dummy_scope, 0, url1,
111            redirects, PageTransition::CLIENT_REDIRECT,
112            history::SOURCE_BROWSED, did_replace));
113    backend_->AddPage(request);
114
115    *transition1 = getTransition(url1);
116    *transition2 = getTransition(url2);
117  }
118
119  int getTransition(const GURL& url) {
120    if (!url.is_valid())
121      return 0;
122    URLRow row;
123    URLID id = backend_->db()->GetRowForURL(url, &row);
124    VisitVector visits;
125    EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
126    return visits[0].transition;
127  }
128
129  FilePath getTestDir() {
130    return test_dir_;
131  }
132
133  FaviconID GetFavicon(const GURL& url, IconType icon_type) {
134    IconMapping icon_mapping;
135    if (backend_->thumbnail_db_->GetIconMappingForPageURL(url, icon_type,
136                                                          &icon_mapping))
137      return icon_mapping.icon_id;
138    else
139      return 0;
140  }
141
142  BookmarkModel bookmark_model_;
143
144 protected:
145  bool loaded_;
146
147 private:
148  friend class HistoryBackendTestDelegate;
149
150  // testing::Test
151  virtual void SetUp() {
152    if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"),
153                                           &test_dir_))
154      return;
155    backend_ = new HistoryBackend(test_dir_,
156                                  new HistoryBackendTestDelegate(this),
157                                  &bookmark_model_);
158    backend_->Init(std::string(), false);
159  }
160  virtual void TearDown() {
161    if (backend_.get())
162      backend_->Closing();
163    backend_ = NULL;
164    mem_backend_.reset();
165    file_util::Delete(test_dir_, true);
166  }
167
168  void SetInMemoryBackend(InMemoryHistoryBackend* backend) {
169    mem_backend_.reset(backend);
170  }
171
172  void BroadcastNotifications(NotificationType type,
173                                      HistoryDetails* details) {
174    // Send the notifications directly to the in-memory database.
175    Details<HistoryDetails> det(details);
176    mem_backend_->Observe(type, Source<HistoryBackendTest>(NULL), det);
177
178    // The backend passes ownership of the details pointer to us.
179    delete details;
180  }
181
182  MessageLoop message_loop_;
183  FilePath test_dir_;
184};
185
186void HistoryBackendTestDelegate::SetInMemoryBackend(
187    InMemoryHistoryBackend* backend) {
188  test_->SetInMemoryBackend(backend);
189}
190
191void HistoryBackendTestDelegate::BroadcastNotifications(
192    NotificationType type,
193    HistoryDetails* details) {
194  test_->BroadcastNotifications(type, details);
195}
196
197void HistoryBackendTestDelegate::DBLoaded() {
198  test_->loaded_ = true;
199}
200
201void HistoryBackendTestDelegate::StartTopSitesMigration() {
202  test_->backend_->MigrateThumbnailsDatabase();
203}
204
205TEST_F(HistoryBackendTest, Loaded) {
206  ASSERT_TRUE(backend_.get());
207  ASSERT_TRUE(loaded_);
208}
209
210TEST_F(HistoryBackendTest, DeleteAll) {
211  ASSERT_TRUE(backend_.get());
212
213  // Add two favicons, use the characters '1' and '2' for the image data. Note
214  // that we do these in the opposite order. This is so the first one gets ID
215  // 2 autoassigned to the database, which will change when the other one is
216  // deleted. This way we can test that updating works properly.
217  GURL favicon_url1("http://www.google.com/favicon.ico");
218  GURL favicon_url2("http://news.google.com/favicon.ico");
219  FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2,
220                                                           FAVICON);
221  FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
222                                                           FAVICON);
223
224  std::vector<unsigned char> data;
225  data.push_back('1');
226  EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(favicon1,
227      new RefCountedBytes(data), Time::Now()));
228
229  data[0] = '2';
230  EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
231                  favicon2, new RefCountedBytes(data), Time::Now()));
232
233  // First visit two URLs.
234  URLRow row1(GURL("http://www.google.com/"));
235  row1.set_visit_count(2);
236  row1.set_typed_count(1);
237  row1.set_last_visit(Time::Now());
238  backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1);
239
240  URLRow row2(GURL("http://news.google.com/"));
241  row2.set_visit_count(1);
242  row2.set_last_visit(Time::Now());
243  backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2);
244
245  std::vector<URLRow> rows;
246  rows.push_back(row2);  // Reversed order for the same reason as favicons.
247  rows.push_back(row1);
248  backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
249
250  URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
251  URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
252
253  // Get the two visits for the URLs we just added.
254  VisitVector visits;
255  backend_->db_->GetVisitsForURL(row1_id, &visits);
256  ASSERT_EQ(1U, visits.size());
257  VisitID visit1_id = visits[0].visit_id;
258
259  visits.clear();
260  backend_->db_->GetVisitsForURL(row2_id, &visits);
261  ASSERT_EQ(1U, visits.size());
262  VisitID visit2_id = visits[0].visit_id;
263
264  // The in-memory backend should have been set and it should have gotten the
265  // typed URL.
266  ASSERT_TRUE(mem_backend_.get());
267  URLRow outrow1;
268  EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL));
269
270  // Add thumbnails for each page.
271  ThumbnailScore score(0.25, true, true);
272  scoped_ptr<SkBitmap> google_bitmap(
273      gfx::JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail)));
274
275  Time time;
276  GURL gurl;
277  backend_->thumbnail_db_->SetPageThumbnail(gurl, row1_id, *google_bitmap,
278                                            score, time);
279  scoped_ptr<SkBitmap> weewar_bitmap(
280     gfx::JPEGCodec::Decode(kWeewarThumbnail, sizeof(kWeewarThumbnail)));
281  backend_->thumbnail_db_->SetPageThumbnail(gurl, row2_id, *weewar_bitmap,
282                                            score, time);
283
284  // Star row1.
285  bookmark_model_.AddURL(
286      bookmark_model_.GetBookmarkBarNode(), 0, string16(), row1.url());
287
288  // Set full text index for each one.
289  backend_->text_database_->AddPageData(row1.url(), row1_id, visit1_id,
290                                        row1.last_visit(),
291                                        UTF8ToUTF16("Title 1"),
292                                        UTF8ToUTF16("Body 1"));
293  backend_->text_database_->AddPageData(row2.url(), row2_id, visit2_id,
294                                        row2.last_visit(),
295                                        UTF8ToUTF16("Title 2"),
296                                        UTF8ToUTF16("Body 2"));
297
298  // Now finally clear all history.
299  backend_->DeleteAllHistory();
300
301  // The first URL should be preserved but the time should be cleared.
302  EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1));
303  EXPECT_EQ(row1.url(), outrow1.url());
304  EXPECT_EQ(0, outrow1.visit_count());
305  EXPECT_EQ(0, outrow1.typed_count());
306  EXPECT_TRUE(Time() == outrow1.last_visit());
307
308  // The second row should be deleted.
309  URLRow outrow2;
310  EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2));
311
312  // All visits should be deleted for both URLs.
313  VisitVector all_visits;
314  backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
315  ASSERT_EQ(0U, all_visits.size());
316
317  // All thumbnails should be deleted.
318  std::vector<unsigned char> out_data;
319  EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(outrow1.id(),
320                                                         &out_data));
321  EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(row2_id, &out_data));
322
323  // We should have a favicon for the first URL only. We look them up by favicon
324  // URL since the IDs may hav changed.
325  FaviconID out_favicon1 = backend_->thumbnail_db_->
326      GetFaviconIDForFaviconURL(favicon_url1, FAVICON, NULL);
327  EXPECT_TRUE(out_favicon1);
328  FaviconID out_favicon2 = backend_->thumbnail_db_->
329      GetFaviconIDForFaviconURL(favicon_url2, FAVICON, NULL);
330  EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
331
332  // The remaining URL should still reference the same favicon, even if its
333  // ID has changed.
334  EXPECT_EQ(out_favicon1, GetFavicon(outrow1.url(), FAVICON));
335
336  // The first URL should still be bookmarked.
337  EXPECT_TRUE(bookmark_model_.IsBookmarked(row1.url()));
338
339  // The full text database should have no data.
340  std::vector<TextDatabase::Match> text_matches;
341  Time first_time_searched;
342  backend_->text_database_->GetTextMatches(UTF8ToUTF16("Body"),
343                                           QueryOptions(),
344                                           &text_matches,
345                                           &first_time_searched);
346  EXPECT_EQ(0U, text_matches.size());
347}
348
349TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
350  GURL favicon_url1("http://www.google.com/favicon.ico");
351  GURL favicon_url2("http://news.google.com/favicon.ico");
352  FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2,
353                                                           FAVICON);
354  FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
355                                                           FAVICON);
356
357  std::vector<unsigned char> data;
358  data.push_back('1');
359  EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
360                  favicon1, new RefCountedBytes(data), Time::Now()));
361
362  data[0] = '2';
363  EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
364                  favicon2, new RefCountedBytes(data), Time::Now()));
365
366  // First visit two URLs.
367  URLRow row1(GURL("http://www.google.com/"));
368  row1.set_visit_count(2);
369  row1.set_typed_count(1);
370  row1.set_last_visit(Time::Now());
371  EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
372
373  URLRow row2(GURL("http://news.google.com/"));
374  row2.set_visit_count(1);
375  row2.set_last_visit(Time::Now());
376  EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2));
377
378  std::vector<URLRow> rows;
379  rows.push_back(row2);  // Reversed order for the same reason as favicons.
380  rows.push_back(row1);
381  backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
382
383  URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
384  URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
385
386  // Star the two URLs.
387  bookmark_model_.SetURLStarred(row1.url(), string16(), true);
388  bookmark_model_.SetURLStarred(row2.url(), string16(), true);
389
390  // Delete url 2. Because url 2 is starred this won't delete the URL, only
391  // the visits.
392  backend_->expirer_.DeleteURL(row2.url());
393
394  // Make sure url 2 is still valid, but has no visits.
395  URLRow tmp_url_row;
396  EXPECT_EQ(row2_id, backend_->db_->GetRowForURL(row2.url(), NULL));
397  VisitVector visits;
398  backend_->db_->GetVisitsForURL(row2_id, &visits);
399  EXPECT_EQ(0U, visits.size());
400  // The favicon should still be valid.
401  EXPECT_EQ(favicon2,
402      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
403                                                         FAVICON,
404                                                         NULL));
405
406  // Unstar row2.
407  bookmark_model_.SetURLStarred(row2.url(), string16(), false);
408  // Tell the backend it was unstarred. We have to explicitly do this as
409  // BookmarkModel isn't wired up to the backend during testing.
410  std::set<GURL> unstarred_urls;
411  unstarred_urls.insert(row2.url());
412  backend_->URLsNoLongerBookmarked(unstarred_urls);
413
414  // The URL should no longer exist.
415  EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row));
416  // And the favicon should be deleted.
417  EXPECT_EQ(0,
418      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
419                                                         FAVICON,
420                                                         NULL));
421
422  // Unstar row 1.
423  bookmark_model_.SetURLStarred(row1.url(), string16(), false);
424  // Tell the backend it was unstarred. We have to explicitly do this as
425  // BookmarkModel isn't wired up to the backend during testing.
426  unstarred_urls.clear();
427  unstarred_urls.insert(row1.url());
428  backend_->URLsNoLongerBookmarked(unstarred_urls);
429
430  // The URL should still exist (because there were visits).
431  EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL));
432
433  // There should still be visits.
434  visits.clear();
435  backend_->db_->GetVisitsForURL(row1_id, &visits);
436  EXPECT_EQ(1U, visits.size());
437
438  // The favicon should still be valid.
439  EXPECT_EQ(favicon1,
440      backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url1,
441                                                         FAVICON,
442                                                         NULL));
443}
444
445// Tests a handful of assertions for a navigation with a type of
446// KEYWORD_GENERATED.
447TEST_F(HistoryBackendTest, KeywordGenerated) {
448  ASSERT_TRUE(backend_.get());
449
450  GURL url("http://google.com");
451
452  Time visit_time = Time::Now() - base::TimeDelta::FromDays(1);
453  scoped_refptr<HistoryAddPageArgs> request(
454      new HistoryAddPageArgs(url, visit_time, NULL, 0, GURL(),
455                             history::RedirectList(),
456                             PageTransition::KEYWORD_GENERATED,
457                             history::SOURCE_BROWSED, false));
458  backend_->AddPage(request);
459
460  // A row should have been added for the url.
461  URLRow row;
462  URLID url_id = backend_->db()->GetRowForURL(url, &row);
463  ASSERT_NE(0, url_id);
464
465  // The typed count should be 1.
466  ASSERT_EQ(1, row.typed_count());
467
468  // KEYWORD_GENERATED urls should not be added to the segment db.
469  std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url);
470  EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name));
471
472  // One visit should be added.
473  VisitVector visits;
474  EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
475  EXPECT_EQ(1U, visits.size());
476
477  // But no visible visits.
478  visits.clear();
479  backend_->db()->GetVisibleVisitsInRange(base::Time(), base::Time(), 1,
480                                          &visits);
481  EXPECT_TRUE(visits.empty());
482
483  // Expire the visits.
484  std::set<GURL> restrict_urls;
485  backend_->expire_backend()->ExpireHistoryBetween(restrict_urls,
486                                                   visit_time, Time::Now());
487
488  // The visit should have been nuked.
489  visits.clear();
490  EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
491  EXPECT_TRUE(visits.empty());
492
493  // As well as the url.
494  ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row));
495}
496
497TEST_F(HistoryBackendTest, ClientRedirect) {
498  ASSERT_TRUE(backend_.get());
499
500  int transition1;
501  int transition2;
502
503  // Initial transition to page A.
504  GURL url_a("http://google.com/a");
505  AddClientRedirect(GURL(), url_a, false, &transition1, &transition2);
506  EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
507
508  // User initiated redirect to page B.
509  GURL url_b("http://google.com/b");
510  AddClientRedirect(url_a, url_b, false, &transition1, &transition2);
511  EXPECT_TRUE(transition1 & PageTransition::CHAIN_END);
512  EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
513
514  // Non-user initiated redirect to page C.
515  GURL url_c("http://google.com/c");
516  AddClientRedirect(url_b, url_c, true, &transition1, &transition2);
517  EXPECT_FALSE(transition1 & PageTransition::CHAIN_END);
518  EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
519}
520
521TEST_F(HistoryBackendTest, ImportedFaviconsTest) {
522  // Setup test data - two Urls in the history, one with favicon assigned and
523  // one without.
524  GURL favicon_url1("http://www.google.com/favicon.ico");
525  FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
526                                                           FAVICON);
527  std::vector<unsigned char> data;
528  data.push_back('1');
529  EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(favicon1,
530      RefCountedBytes::TakeVector(&data), Time::Now()));
531  URLRow row1(GURL("http://www.google.com/"));
532  row1.set_visit_count(1);
533  row1.set_last_visit(Time::Now());
534  EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
535
536  URLRow row2(GURL("http://news.google.com/"));
537  row2.set_visit_count(1);
538  row2.set_last_visit(Time::Now());
539  std::vector<URLRow> rows;
540  rows.push_back(row1);
541  rows.push_back(row2);
542  backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
543  URLRow url_row1, url_row2;
544  EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
545  EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
546  EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 0);
547  EXPECT_TRUE(GetFavicon(row2.url(), FAVICON) == 0);
548
549  // Now provide one imported favicon for both URLs already in the registry.
550  // The new favicon should only be used with the URL that doesn't already have
551  // a favicon.
552  std::vector<history::ImportedFaviconUsage> favicons;
553  history::ImportedFaviconUsage favicon;
554  favicon.favicon_url = GURL("http://news.google.com/favicon.ico");
555  favicon.png_data.push_back('2');
556  favicon.urls.insert(row1.url());
557  favicon.urls.insert(row2.url());
558  favicons.push_back(favicon);
559  backend_->SetImportedFavicons(favicons);
560  EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
561  EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
562  EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 0);
563  EXPECT_FALSE(GetFavicon(row2.url(), FAVICON) == 0);
564  EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) ==
565      GetFavicon(row2.url(), FAVICON));
566
567  // A URL should not be added to history (to store favicon), if
568  // the URL is not bookmarked.
569  GURL url3("http://mail.google.com");
570  favicons.clear();
571  favicon.favicon_url = GURL("http://mail.google.com/favicon.ico");
572  favicon.png_data.push_back('3');
573  favicon.urls.insert(url3);
574  favicons.push_back(favicon);
575  backend_->SetImportedFavicons(favicons);
576  URLRow url_row3;
577  EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
578
579  // If the URL is bookmarked, it should get added to history with 0 visits.
580  bookmark_model_.AddURL(bookmark_model_.GetBookmarkBarNode(), 0, string16(),
581                         url3);
582  backend_->SetImportedFavicons(favicons);
583  EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
584  EXPECT_TRUE(url_row3.visit_count() == 0);
585}
586
587TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {
588  ASSERT_TRUE(backend_.get());
589
590  GURL url("http://anyuser:anypass@www.google.com");
591  GURL stripped_url("http://www.google.com");
592
593  // Clear all history.
594  backend_->DeleteAllHistory();
595
596  // Visit the url with username, password.
597  backend_->AddPageVisit(url, base::Time::Now(), 0,
598    PageTransition::GetQualifier(PageTransition::TYPED),
599    history::SOURCE_BROWSED);
600
601  // Fetch the row information about stripped url from history db.
602  VisitVector visits;
603  URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL);
604  backend_->db_->GetVisitsForURL(row_id, &visits);
605
606  // Check if stripped url is stored in database.
607  ASSERT_EQ(1U, visits.size());
608}
609
610TEST_F(HistoryBackendTest, AddPageVisitSource) {
611  ASSERT_TRUE(backend_.get());
612
613  GURL url("http://www.google.com");
614
615  // Clear all history.
616  backend_->DeleteAllHistory();
617
618  // Assume visiting the url from an externsion.
619  backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
620                         history::SOURCE_EXTENSION);
621  // Assume the url is imported from Firefox.
622  backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
623                         history::SOURCE_FIREFOX_IMPORTED);
624  // Assume this url is also synced.
625  backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
626                         history::SOURCE_SYNCED);
627
628  // Fetch the row information about the url from history db.
629  VisitVector visits;
630  URLID row_id = backend_->db_->GetRowForURL(url, NULL);
631  backend_->db_->GetVisitsForURL(row_id, &visits);
632
633  // Check if all the visits to the url are stored in database.
634  ASSERT_EQ(3U, visits.size());
635  VisitSourceMap visit_sources;
636  backend_->db_->GetVisitsSource(visits, &visit_sources);
637  ASSERT_EQ(3U, visit_sources.size());
638  int sources = 0;
639  for (int i = 0; i < 3; i++) {
640    switch (visit_sources[visits[i].visit_id]) {
641      case history::SOURCE_EXTENSION:
642        sources |= 0x1;
643        break;
644      case history::SOURCE_FIREFOX_IMPORTED:
645        sources |= 0x2;
646        break;
647      case history::SOURCE_SYNCED:
648        sources |= 0x4;
649      default:
650        break;
651    }
652  }
653  EXPECT_EQ(0x7, sources);
654}
655
656TEST_F(HistoryBackendTest, AddPageArgsSource) {
657  ASSERT_TRUE(backend_.get());
658
659  GURL url("http://testpageargs.com");
660
661  // Assume this page is browsed by user.
662  scoped_refptr<HistoryAddPageArgs> request1(
663      new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
664                             history::RedirectList(),
665                             PageTransition::KEYWORD_GENERATED,
666                             history::SOURCE_BROWSED, false));
667  backend_->AddPage(request1);
668  // Assume this page is synced.
669  scoped_refptr<HistoryAddPageArgs> request2(
670      new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
671                             history::RedirectList(),
672                             PageTransition::LINK,
673                             history::SOURCE_SYNCED, false));
674  backend_->AddPage(request2);
675  // Assume this page is browsed again.
676  scoped_refptr<HistoryAddPageArgs> request3(
677      new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
678                             history::RedirectList(),
679                             PageTransition::TYPED,
680                             history::SOURCE_BROWSED, false));
681  backend_->AddPage(request3);
682
683  // Three visits should be added with proper sources.
684  VisitVector visits;
685  URLRow row;
686  URLID id = backend_->db()->GetRowForURL(url, &row);
687  ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
688  ASSERT_EQ(3U, visits.size());
689  VisitSourceMap visit_sources;
690  backend_->db_->GetVisitsSource(visits, &visit_sources);
691  ASSERT_EQ(1U, visit_sources.size());
692  EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second);
693}
694
695TEST_F(HistoryBackendTest, AddVisitsSource) {
696  ASSERT_TRUE(backend_.get());
697
698  GURL url1("http://www.cnn.com");
699  std::vector<base::Time> visits1;
700  visits1.push_back(Time::Now() - base::TimeDelta::FromDays(5));
701  visits1.push_back(Time::Now() - base::TimeDelta::FromDays(1));
702  visits1.push_back(Time::Now());
703
704  GURL url2("http://www.example.com");
705  std::vector<base::Time> visits2;
706  visits2.push_back(Time::Now() - base::TimeDelta::FromDays(10));
707  visits2.push_back(Time::Now());
708
709  // Clear all history.
710  backend_->DeleteAllHistory();
711
712  // Add the visits.
713  backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
714  backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
715
716  // Verify the visits were added with their sources.
717  VisitVector visits;
718  URLRow row;
719  URLID id = backend_->db()->GetRowForURL(url1, &row);
720  ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
721  ASSERT_EQ(3U, visits.size());
722  VisitSourceMap visit_sources;
723  backend_->db_->GetVisitsSource(visits, &visit_sources);
724  ASSERT_EQ(3U, visit_sources.size());
725  for (int i = 0; i < 3; i++)
726    EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]);
727  id = backend_->db()->GetRowForURL(url2, &row);
728  ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
729  ASSERT_EQ(2U, visits.size());
730  backend_->db_->GetVisitsSource(visits, &visit_sources);
731  ASSERT_EQ(2U, visit_sources.size());
732  for (int i = 0; i < 2; i++)
733    EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
734}
735
736TEST_F(HistoryBackendTest, RemoveVisitsSource) {
737  ASSERT_TRUE(backend_.get());
738
739  GURL url1("http://www.cnn.com");
740  std::vector<base::Time> visits1;
741  visits1.push_back(Time::Now() - base::TimeDelta::FromDays(5));
742  visits1.push_back(Time::Now());
743
744  GURL url2("http://www.example.com");
745  std::vector<base::Time> visits2;
746  visits2.push_back(Time::Now() - base::TimeDelta::FromDays(10));
747  visits2.push_back(Time::Now());
748
749  // Clear all history.
750  backend_->DeleteAllHistory();
751
752  // Add the visits.
753  backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
754  backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
755
756  // Verify the visits of url1 were added.
757  VisitVector visits;
758  URLRow row;
759  URLID id = backend_->db()->GetRowForURL(url1, &row);
760  ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
761  ASSERT_EQ(2U, visits.size());
762  // Remove these visits.
763  ASSERT_TRUE(backend_->RemoveVisits(visits));
764
765  // Now check only url2's source in visit_source table.
766  VisitSourceMap visit_sources;
767  backend_->db_->GetVisitsSource(visits, &visit_sources);
768  ASSERT_EQ(0U, visit_sources.size());
769  id = backend_->db()->GetRowForURL(url2, &row);
770  ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
771  ASSERT_EQ(2U, visits.size());
772  backend_->db_->GetVisitsSource(visits, &visit_sources);
773  ASSERT_EQ(2U, visit_sources.size());
774  for (int i = 0; i < 2; i++)
775    EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
776}
777
778// Test for migration of adding visit_source table.
779TEST_F(HistoryBackendTest, MigrationVisitSource) {
780  ASSERT_TRUE(backend_.get());
781  backend_->Closing();
782  backend_ = NULL;
783
784  FilePath old_history_path;
785  ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
786  old_history_path = old_history_path.AppendASCII("History");
787  old_history_path = old_history_path.AppendASCII("HistoryNoSource");
788
789  // Copy history database file to current directory so that it will be deleted
790  // in Teardown.
791  FilePath new_history_path(getTestDir());
792  file_util::Delete(new_history_path, true);
793  file_util::CreateDirectory(new_history_path);
794  FilePath new_history_file = new_history_path.Append(chrome::kHistoryFilename);
795  ASSERT_TRUE(file_util::CopyFile(old_history_path, new_history_file));
796
797  backend_ = new HistoryBackend(new_history_path,
798                                new HistoryBackendTestDelegate(this),
799                                &bookmark_model_);
800  backend_->Init(std::string(), false);
801  backend_->Closing();
802  backend_ = NULL;
803
804  // Now the database should already be migrated.
805  // Check version first.
806  int cur_version = HistoryDatabase::GetCurrentVersion();
807  sql::Connection db;
808  ASSERT_TRUE(db.Open(new_history_file));
809  sql::Statement s(db.GetUniqueStatement(
810      "SELECT value FROM meta WHERE key = 'version'"));
811  ASSERT_TRUE(s.Step());
812  int file_version = s.ColumnInt(0);
813  EXPECT_EQ(cur_version, file_version);
814
815  // Check visit_source table is created and empty.
816  s.Assign(db.GetUniqueStatement(
817      "SELECT name FROM sqlite_master WHERE name=\"visit_source\""));
818  ASSERT_TRUE(s.Step());
819  s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10"));
820  EXPECT_FALSE(s.Step());
821}
822
823TEST_F(HistoryBackendTest, SetFaviconMapping) {
824  // Init recent_redirects_
825  const GURL url1("http://www.google.com");
826  const GURL url2("http://www.google.com/m");
827  URLRow url_info1(url1);
828  url_info1.set_visit_count(0);
829  url_info1.set_typed_count(0);
830  url_info1.set_last_visit(base::Time());
831  url_info1.set_hidden(false);
832  backend_->db_->AddURL(url_info1);
833
834  URLRow url_info2(url2);
835  url_info2.set_visit_count(0);
836  url_info2.set_typed_count(0);
837  url_info2.set_last_visit(base::Time());
838  url_info2.set_hidden(false);
839  backend_->db_->AddURL(url_info2);
840
841  history::RedirectList redirects;
842  redirects.push_back(url2);
843  redirects.push_back(url1);
844  backend_->recent_redirects_.Put(url1, redirects);
845
846  const GURL icon_url("http://www.google.com/icon");
847  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
848  // Add a favicon
849  backend_->SetFavicon(
850      url1, icon_url, RefCountedBytes::TakeVector(&data), FAVICON);
851  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
852      url1, FAVICON, NULL));
853  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
854      url2, FAVICON, NULL));
855
856  // Add a touch_icon
857  backend_->SetFavicon(
858      url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON);
859  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
860      url1, TOUCH_ICON, NULL));
861  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
862      url2, TOUCH_ICON, NULL));
863  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
864      url1, FAVICON, NULL));
865
866  // Add a TOUCH_PRECOMPOSED_ICON
867  backend_->SetFavicon(url1,
868                       icon_url,
869                       RefCountedBytes::TakeVector(&data),
870                       TOUCH_PRECOMPOSED_ICON);
871  // The touch_icon was replaced.
872  EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL(
873      url1, TOUCH_ICON, NULL));
874  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
875      url1, FAVICON, NULL));
876  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
877      url1, TOUCH_PRECOMPOSED_ICON, NULL));
878  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
879      url2, TOUCH_PRECOMPOSED_ICON, NULL));
880
881  // Add a touch_icon
882  backend_->SetFavicon(
883      url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON);
884  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
885      url1, TOUCH_ICON, NULL));
886  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
887      url1, FAVICON, NULL));
888  // The TOUCH_PRECOMPOSED_ICON was replaced.
889  EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL(
890      url1, TOUCH_PRECOMPOSED_ICON, NULL));
891
892  // Add a favicon
893  const GURL icon_url2("http://www.google.com/icon2");
894  backend_->SetFavicon(
895      url1, icon_url2, RefCountedBytes::TakeVector(&data), FAVICON);
896  FaviconID icon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
897      icon_url2, FAVICON, NULL);
898  EXPECT_NE(0, icon_id);
899  std::vector<IconMapping> icon_mapping;
900  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
901      url1, &icon_mapping));
902  // The old icon was replaced.
903  EXPECT_TRUE(icon_mapping.size() > 1);
904  EXPECT_EQ(icon_id, icon_mapping[1].icon_id);
905}
906
907TEST_F(HistoryBackendTest, AddOrUpdateIconMapping) {
908  // Test the same icon and page mapping will not be added twice. other case
909  // should be covered in TEST_F(HistoryBackendTest, SetFaviconMapping)
910  const GURL url("http://www.google.com/");
911  const GURL icon_url("http://www.google.com/icon");
912  std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
913
914  backend_->SetFavicon(
915      url, icon_url, RefCountedBytes::TakeVector(&data), FAVICON);
916  FaviconID icon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
917      icon_url, FAVICON, NULL);
918
919  // Add the same mapping
920  FaviconID replaced;
921  EXPECT_FALSE(backend_->AddOrUpdateIconMapping(
922      url, icon_id, FAVICON, &replaced));
923  EXPECT_EQ(0, replaced);
924
925  std::vector<IconMapping> icon_mapping;
926  EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
927      url, &icon_mapping));
928  EXPECT_EQ(1u, icon_mapping.size());
929}
930
931}  // namespace history
932