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