// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <set>
#include <vector>
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/history/history_backend.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/browser/history/in_memory_database.h"
#include "chrome/browser/history/in_memory_history_backend.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/thumbnail_score.h"
#include "chrome/tools/profiles/thumbnail-inl.h"
#include "content/common/notification_details.h"
#include "content/common/notification_source.h"
#include "googleurl/src/gurl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/codec/jpeg_codec.h"
using base::Time;
// This file only tests functionality where it is most convenient to call the
// backend directly. Most of the history backend functions are tested by the
// history unit test. Because of the elaborate callbacks involved, this is no
// harder than calling it directly for many things.
namespace {
// data we'll put into the thumbnail database
static const unsigned char blob1[] =
"12346102356120394751634516591348710478123649165419234519234512349134";
} // namepace
namespace history {
class HistoryBackendTest;
// This must be a separate object since HistoryBackend manages its lifetime.
// This just forwards the messages we're interested in to the test object.
class HistoryBackendTestDelegate : public HistoryBackend::Delegate {
public:
explicit HistoryBackendTestDelegate(HistoryBackendTest* test) : test_(test) {}
virtual void NotifyProfileError(sql::InitStatus init_status) OVERRIDE {}
virtual void SetInMemoryBackend(InMemoryHistoryBackend* backend) OVERRIDE;
virtual void BroadcastNotifications(NotificationType type,
HistoryDetails* details) OVERRIDE;
virtual void DBLoaded() OVERRIDE;
virtual void StartTopSitesMigration() OVERRIDE;
private:
// Not owned by us.
HistoryBackendTest* test_;
DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
};
class HistoryBackendTest : public testing::Test {
public:
HistoryBackendTest() : bookmark_model_(NULL), loaded_(false) {}
virtual ~HistoryBackendTest() {
}
protected:
scoped_refptr<HistoryBackend> backend_; // Will be NULL on init failure.
scoped_ptr<InMemoryHistoryBackend> mem_backend_;
void AddRedirectChain(const char* sequence[], int page_id) {
history::RedirectList redirects;
for (int i = 0; sequence[i] != NULL; ++i)
redirects.push_back(GURL(sequence[i]));
int int_scope = 1;
void* scope = 0;
memcpy(&scope, &int_scope, sizeof(int_scope));
scoped_refptr<history::HistoryAddPageArgs> request(
new history::HistoryAddPageArgs(
redirects.back(), Time::Now(), scope, page_id, GURL(),
redirects, PageTransition::LINK, history::SOURCE_BROWSED, true));
backend_->AddPage(request);
}
// Adds CLIENT_REDIRECT page transition.
// |url1| is the source URL and |url2| is the destination.
// |did_replace| is true if the transition is non-user initiated and the
// navigation entry for |url2| has replaced that for |url1|. The possibly
// updated transition code of the visit records for |url1| and |url2| is
// returned by filling in |*transition1| and |*transition2|, respectively.
void AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace,
int* transition1, int* transition2) {
void* const dummy_scope = reinterpret_cast<void*>(0x87654321);
history::RedirectList redirects;
if (url1.is_valid())
redirects.push_back(url1);
if (url2.is_valid())
redirects.push_back(url2);
scoped_refptr<HistoryAddPageArgs> request(
new HistoryAddPageArgs(url2, base::Time(), dummy_scope, 0, url1,
redirects, PageTransition::CLIENT_REDIRECT,
history::SOURCE_BROWSED, did_replace));
backend_->AddPage(request);
*transition1 = getTransition(url1);
*transition2 = getTransition(url2);
}
int getTransition(const GURL& url) {
if (!url.is_valid())
return 0;
URLRow row;
URLID id = backend_->db()->GetRowForURL(url, &row);
VisitVector visits;
EXPECT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
return visits[0].transition;
}
FilePath getTestDir() {
return test_dir_;
}
FaviconID GetFavicon(const GURL& url, IconType icon_type) {
IconMapping icon_mapping;
if (backend_->thumbnail_db_->GetIconMappingForPageURL(url, icon_type,
&icon_mapping))
return icon_mapping.icon_id;
else
return 0;
}
BookmarkModel bookmark_model_;
protected:
bool loaded_;
private:
friend class HistoryBackendTestDelegate;
// testing::Test
virtual void SetUp() {
if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"),
&test_dir_))
return;
backend_ = new HistoryBackend(test_dir_,
new HistoryBackendTestDelegate(this),
&bookmark_model_);
backend_->Init(std::string(), false);
}
virtual void TearDown() {
if (backend_.get())
backend_->Closing();
backend_ = NULL;
mem_backend_.reset();
file_util::Delete(test_dir_, true);
}
void SetInMemoryBackend(InMemoryHistoryBackend* backend) {
mem_backend_.reset(backend);
}
void BroadcastNotifications(NotificationType type,
HistoryDetails* details) {
// Send the notifications directly to the in-memory database.
Details<HistoryDetails> det(details);
mem_backend_->Observe(type, Source<HistoryBackendTest>(NULL), det);
// The backend passes ownership of the details pointer to us.
delete details;
}
MessageLoop message_loop_;
FilePath test_dir_;
};
void HistoryBackendTestDelegate::SetInMemoryBackend(
InMemoryHistoryBackend* backend) {
test_->SetInMemoryBackend(backend);
}
void HistoryBackendTestDelegate::BroadcastNotifications(
NotificationType type,
HistoryDetails* details) {
test_->BroadcastNotifications(type, details);
}
void HistoryBackendTestDelegate::DBLoaded() {
test_->loaded_ = true;
}
void HistoryBackendTestDelegate::StartTopSitesMigration() {
test_->backend_->MigrateThumbnailsDatabase();
}
TEST_F(HistoryBackendTest, Loaded) {
ASSERT_TRUE(backend_.get());
ASSERT_TRUE(loaded_);
}
TEST_F(HistoryBackendTest, DeleteAll) {
ASSERT_TRUE(backend_.get());
// Add two favicons, use the characters '1' and '2' for the image data. Note
// that we do these in the opposite order. This is so the first one gets ID
// 2 autoassigned to the database, which will change when the other one is
// deleted. This way we can test that updating works properly.
GURL favicon_url1("http://www.google.com/favicon.ico");
GURL favicon_url2("http://news.google.com/favicon.ico");
FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2,
FAVICON);
FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
FAVICON);
std::vector<unsigned char> data;
data.push_back('1');
EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(favicon1,
new RefCountedBytes(data), Time::Now()));
data[0] = '2';
EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
favicon2, new RefCountedBytes(data), Time::Now()));
// First visit two URLs.
URLRow row1(GURL("http://www.google.com/"));
row1.set_visit_count(2);
row1.set_typed_count(1);
row1.set_last_visit(Time::Now());
backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1);
URLRow row2(GURL("http://news.google.com/"));
row2.set_visit_count(1);
row2.set_last_visit(Time::Now());
backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2);
std::vector<URLRow> rows;
rows.push_back(row2); // Reversed order for the same reason as favicons.
rows.push_back(row1);
backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
// Get the two visits for the URLs we just added.
VisitVector visits;
backend_->db_->GetVisitsForURL(row1_id, &visits);
ASSERT_EQ(1U, visits.size());
VisitID visit1_id = visits[0].visit_id;
visits.clear();
backend_->db_->GetVisitsForURL(row2_id, &visits);
ASSERT_EQ(1U, visits.size());
VisitID visit2_id = visits[0].visit_id;
// The in-memory backend should have been set and it should have gotten the
// typed URL.
ASSERT_TRUE(mem_backend_.get());
URLRow outrow1;
EXPECT_TRUE(mem_backend_->db_->GetRowForURL(row1.url(), NULL));
// Add thumbnails for each page.
ThumbnailScore score(0.25, true, true);
scoped_ptr<SkBitmap> google_bitmap(
gfx::JPEGCodec::Decode(kGoogleThumbnail, sizeof(kGoogleThumbnail)));
Time time;
GURL gurl;
backend_->thumbnail_db_->SetPageThumbnail(gurl, row1_id, *google_bitmap,
score, time);
scoped_ptr<SkBitmap> weewar_bitmap(
gfx::JPEGCodec::Decode(kWeewarThumbnail, sizeof(kWeewarThumbnail)));
backend_->thumbnail_db_->SetPageThumbnail(gurl, row2_id, *weewar_bitmap,
score, time);
// Star row1.
bookmark_model_.AddURL(
bookmark_model_.GetBookmarkBarNode(), 0, string16(), row1.url());
// Set full text index for each one.
backend_->text_database_->AddPageData(row1.url(), row1_id, visit1_id,
row1.last_visit(),
UTF8ToUTF16("Title 1"),
UTF8ToUTF16("Body 1"));
backend_->text_database_->AddPageData(row2.url(), row2_id, visit2_id,
row2.last_visit(),
UTF8ToUTF16("Title 2"),
UTF8ToUTF16("Body 2"));
// Now finally clear all history.
backend_->DeleteAllHistory();
// The first URL should be preserved but the time should be cleared.
EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1));
EXPECT_EQ(row1.url(), outrow1.url());
EXPECT_EQ(0, outrow1.visit_count());
EXPECT_EQ(0, outrow1.typed_count());
EXPECT_TRUE(Time() == outrow1.last_visit());
// The second row should be deleted.
URLRow outrow2;
EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &outrow2));
// All visits should be deleted for both URLs.
VisitVector all_visits;
backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
ASSERT_EQ(0U, all_visits.size());
// All thumbnails should be deleted.
std::vector<unsigned char> out_data;
EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(outrow1.id(),
&out_data));
EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(row2_id, &out_data));
// We should have a favicon for the first URL only. We look them up by favicon
// URL since the IDs may hav changed.
FaviconID out_favicon1 = backend_->thumbnail_db_->
GetFaviconIDForFaviconURL(favicon_url1, FAVICON, NULL);
EXPECT_TRUE(out_favicon1);
FaviconID out_favicon2 = backend_->thumbnail_db_->
GetFaviconIDForFaviconURL(favicon_url2, FAVICON, NULL);
EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
// The remaining URL should still reference the same favicon, even if its
// ID has changed.
EXPECT_EQ(out_favicon1, GetFavicon(outrow1.url(), FAVICON));
// The first URL should still be bookmarked.
EXPECT_TRUE(bookmark_model_.IsBookmarked(row1.url()));
// The full text database should have no data.
std::vector<TextDatabase::Match> text_matches;
Time first_time_searched;
backend_->text_database_->GetTextMatches(UTF8ToUTF16("Body"),
QueryOptions(),
&text_matches,
&first_time_searched);
EXPECT_EQ(0U, text_matches.size());
}
TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
GURL favicon_url1("http://www.google.com/favicon.ico");
GURL favicon_url2("http://news.google.com/favicon.ico");
FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2,
FAVICON);
FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
FAVICON);
std::vector<unsigned char> data;
data.push_back('1');
EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
favicon1, new RefCountedBytes(data), Time::Now()));
data[0] = '2';
EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(
favicon2, new RefCountedBytes(data), Time::Now()));
// First visit two URLs.
URLRow row1(GURL("http://www.google.com/"));
row1.set_visit_count(2);
row1.set_typed_count(1);
row1.set_last_visit(Time::Now());
EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
URLRow row2(GURL("http://news.google.com/"));
row2.set_visit_count(1);
row2.set_last_visit(Time::Now());
EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row2.url(), favicon2));
std::vector<URLRow> rows;
rows.push_back(row2); // Reversed order for the same reason as favicons.
rows.push_back(row1);
backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
URLID row1_id = backend_->db_->GetRowForURL(row1.url(), NULL);
URLID row2_id = backend_->db_->GetRowForURL(row2.url(), NULL);
// Star the two URLs.
bookmark_model_.SetURLStarred(row1.url(), string16(), true);
bookmark_model_.SetURLStarred(row2.url(), string16(), true);
// Delete url 2. Because url 2 is starred this won't delete the URL, only
// the visits.
backend_->expirer_.DeleteURL(row2.url());
// Make sure url 2 is still valid, but has no visits.
URLRow tmp_url_row;
EXPECT_EQ(row2_id, backend_->db_->GetRowForURL(row2.url(), NULL));
VisitVector visits;
backend_->db_->GetVisitsForURL(row2_id, &visits);
EXPECT_EQ(0U, visits.size());
// The favicon should still be valid.
EXPECT_EQ(favicon2,
backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
FAVICON,
NULL));
// Unstar row2.
bookmark_model_.SetURLStarred(row2.url(), string16(), false);
// Tell the backend it was unstarred. We have to explicitly do this as
// BookmarkModel isn't wired up to the backend during testing.
std::set<GURL> unstarred_urls;
unstarred_urls.insert(row2.url());
backend_->URLsNoLongerBookmarked(unstarred_urls);
// The URL should no longer exist.
EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &tmp_url_row));
// And the favicon should be deleted.
EXPECT_EQ(0,
backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url2,
FAVICON,
NULL));
// Unstar row 1.
bookmark_model_.SetURLStarred(row1.url(), string16(), false);
// Tell the backend it was unstarred. We have to explicitly do this as
// BookmarkModel isn't wired up to the backend during testing.
unstarred_urls.clear();
unstarred_urls.insert(row1.url());
backend_->URLsNoLongerBookmarked(unstarred_urls);
// The URL should still exist (because there were visits).
EXPECT_EQ(row1_id, backend_->db_->GetRowForURL(row1.url(), NULL));
// There should still be visits.
visits.clear();
backend_->db_->GetVisitsForURL(row1_id, &visits);
EXPECT_EQ(1U, visits.size());
// The favicon should still be valid.
EXPECT_EQ(favicon1,
backend_->thumbnail_db_->GetFaviconIDForFaviconURL(favicon_url1,
FAVICON,
NULL));
}
// Tests a handful of assertions for a navigation with a type of
// KEYWORD_GENERATED.
TEST_F(HistoryBackendTest, KeywordGenerated) {
ASSERT_TRUE(backend_.get());
GURL url("http://google.com");
Time visit_time = Time::Now() - base::TimeDelta::FromDays(1);
scoped_refptr<HistoryAddPageArgs> request(
new HistoryAddPageArgs(url, visit_time, NULL, 0, GURL(),
history::RedirectList(),
PageTransition::KEYWORD_GENERATED,
history::SOURCE_BROWSED, false));
backend_->AddPage(request);
// A row should have been added for the url.
URLRow row;
URLID url_id = backend_->db()->GetRowForURL(url, &row);
ASSERT_NE(0, url_id);
// The typed count should be 1.
ASSERT_EQ(1, row.typed_count());
// KEYWORD_GENERATED urls should not be added to the segment db.
std::string segment_name = VisitSegmentDatabase::ComputeSegmentName(url);
EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment_name));
// One visit should be added.
VisitVector visits;
EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
EXPECT_EQ(1U, visits.size());
// But no visible visits.
visits.clear();
backend_->db()->GetVisibleVisitsInRange(base::Time(), base::Time(), 1,
&visits);
EXPECT_TRUE(visits.empty());
// Expire the visits.
std::set<GURL> restrict_urls;
backend_->expire_backend()->ExpireHistoryBetween(restrict_urls,
visit_time, Time::Now());
// The visit should have been nuked.
visits.clear();
EXPECT_TRUE(backend_->db()->GetVisitsForURL(url_id, &visits));
EXPECT_TRUE(visits.empty());
// As well as the url.
ASSERT_EQ(0, backend_->db()->GetRowForURL(url, &row));
}
TEST_F(HistoryBackendTest, ClientRedirect) {
ASSERT_TRUE(backend_.get());
int transition1;
int transition2;
// Initial transition to page A.
GURL url_a("http://google.com/a");
AddClientRedirect(GURL(), url_a, false, &transition1, &transition2);
EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
// User initiated redirect to page B.
GURL url_b("http://google.com/b");
AddClientRedirect(url_a, url_b, false, &transition1, &transition2);
EXPECT_TRUE(transition1 & PageTransition::CHAIN_END);
EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
// Non-user initiated redirect to page C.
GURL url_c("http://google.com/c");
AddClientRedirect(url_b, url_c, true, &transition1, &transition2);
EXPECT_FALSE(transition1 & PageTransition::CHAIN_END);
EXPECT_TRUE(transition2 & PageTransition::CHAIN_END);
}
TEST_F(HistoryBackendTest, ImportedFaviconsTest) {
// Setup test data - two Urls in the history, one with favicon assigned and
// one without.
GURL favicon_url1("http://www.google.com/favicon.ico");
FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
FAVICON);
std::vector<unsigned char> data;
data.push_back('1');
EXPECT_TRUE(backend_->thumbnail_db_->SetFavicon(favicon1,
RefCountedBytes::TakeVector(&data), Time::Now()));
URLRow row1(GURL("http://www.google.com/"));
row1.set_visit_count(1);
row1.set_last_visit(Time::Now());
EXPECT_TRUE(backend_->thumbnail_db_->AddIconMapping(row1.url(), favicon1));
URLRow row2(GURL("http://news.google.com/"));
row2.set_visit_count(1);
row2.set_last_visit(Time::Now());
std::vector<URLRow> rows;
rows.push_back(row1);
rows.push_back(row2);
backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
URLRow url_row1, url_row2;
EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 0);
EXPECT_TRUE(GetFavicon(row2.url(), FAVICON) == 0);
// Now provide one imported favicon for both URLs already in the registry.
// The new favicon should only be used with the URL that doesn't already have
// a favicon.
std::vector<history::ImportedFaviconUsage> favicons;
history::ImportedFaviconUsage favicon;
favicon.favicon_url = GURL("http://news.google.com/favicon.ico");
favicon.png_data.push_back('2');
favicon.urls.insert(row1.url());
favicon.urls.insert(row2.url());
favicons.push_back(favicon);
backend_->SetImportedFavicons(favicons);
EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &url_row1) == 0);
EXPECT_FALSE(backend_->db_->GetRowForURL(row2.url(), &url_row2) == 0);
EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) == 0);
EXPECT_FALSE(GetFavicon(row2.url(), FAVICON) == 0);
EXPECT_FALSE(GetFavicon(row1.url(), FAVICON) ==
GetFavicon(row2.url(), FAVICON));
// A URL should not be added to history (to store favicon), if
// the URL is not bookmarked.
GURL url3("http://mail.google.com");
favicons.clear();
favicon.favicon_url = GURL("http://mail.google.com/favicon.ico");
favicon.png_data.push_back('3');
favicon.urls.insert(url3);
favicons.push_back(favicon);
backend_->SetImportedFavicons(favicons);
URLRow url_row3;
EXPECT_TRUE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
// If the URL is bookmarked, it should get added to history with 0 visits.
bookmark_model_.AddURL(bookmark_model_.GetBookmarkBarNode(), 0, string16(),
url3);
backend_->SetImportedFavicons(favicons);
EXPECT_FALSE(backend_->db_->GetRowForURL(url3, &url_row3) == 0);
EXPECT_TRUE(url_row3.visit_count() == 0);
}
TEST_F(HistoryBackendTest, StripUsernamePasswordTest) {
ASSERT_TRUE(backend_.get());
GURL url("http://anyuser:anypass@www.google.com");
GURL stripped_url("http://www.google.com");
// Clear all history.
backend_->DeleteAllHistory();
// Visit the url with username, password.
backend_->AddPageVisit(url, base::Time::Now(), 0,
PageTransition::GetQualifier(PageTransition::TYPED),
history::SOURCE_BROWSED);
// Fetch the row information about stripped url from history db.
VisitVector visits;
URLID row_id = backend_->db_->GetRowForURL(stripped_url, NULL);
backend_->db_->GetVisitsForURL(row_id, &visits);
// Check if stripped url is stored in database.
ASSERT_EQ(1U, visits.size());
}
TEST_F(HistoryBackendTest, AddPageVisitSource) {
ASSERT_TRUE(backend_.get());
GURL url("http://www.google.com");
// Clear all history.
backend_->DeleteAllHistory();
// Assume visiting the url from an externsion.
backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
history::SOURCE_EXTENSION);
// Assume the url is imported from Firefox.
backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
history::SOURCE_FIREFOX_IMPORTED);
// Assume this url is also synced.
backend_->AddPageVisit(url, base::Time::Now(), 0, PageTransition::TYPED,
history::SOURCE_SYNCED);
// Fetch the row information about the url from history db.
VisitVector visits;
URLID row_id = backend_->db_->GetRowForURL(url, NULL);
backend_->db_->GetVisitsForURL(row_id, &visits);
// Check if all the visits to the url are stored in database.
ASSERT_EQ(3U, visits.size());
VisitSourceMap visit_sources;
backend_->db_->GetVisitsSource(visits, &visit_sources);
ASSERT_EQ(3U, visit_sources.size());
int sources = 0;
for (int i = 0; i < 3; i++) {
switch (visit_sources[visits[i].visit_id]) {
case history::SOURCE_EXTENSION:
sources |= 0x1;
break;
case history::SOURCE_FIREFOX_IMPORTED:
sources |= 0x2;
break;
case history::SOURCE_SYNCED:
sources |= 0x4;
default:
break;
}
}
EXPECT_EQ(0x7, sources);
}
TEST_F(HistoryBackendTest, AddPageArgsSource) {
ASSERT_TRUE(backend_.get());
GURL url("http://testpageargs.com");
// Assume this page is browsed by user.
scoped_refptr<HistoryAddPageArgs> request1(
new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
history::RedirectList(),
PageTransition::KEYWORD_GENERATED,
history::SOURCE_BROWSED, false));
backend_->AddPage(request1);
// Assume this page is synced.
scoped_refptr<HistoryAddPageArgs> request2(
new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
history::RedirectList(),
PageTransition::LINK,
history::SOURCE_SYNCED, false));
backend_->AddPage(request2);
// Assume this page is browsed again.
scoped_refptr<HistoryAddPageArgs> request3(
new HistoryAddPageArgs(url, base::Time::Now(), NULL, 0, GURL(),
history::RedirectList(),
PageTransition::TYPED,
history::SOURCE_BROWSED, false));
backend_->AddPage(request3);
// Three visits should be added with proper sources.
VisitVector visits;
URLRow row;
URLID id = backend_->db()->GetRowForURL(url, &row);
ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
ASSERT_EQ(3U, visits.size());
VisitSourceMap visit_sources;
backend_->db_->GetVisitsSource(visits, &visit_sources);
ASSERT_EQ(1U, visit_sources.size());
EXPECT_EQ(history::SOURCE_SYNCED, visit_sources.begin()->second);
}
TEST_F(HistoryBackendTest, AddVisitsSource) {
ASSERT_TRUE(backend_.get());
GURL url1("http://www.cnn.com");
std::vector<base::Time> visits1;
visits1.push_back(Time::Now() - base::TimeDelta::FromDays(5));
visits1.push_back(Time::Now() - base::TimeDelta::FromDays(1));
visits1.push_back(Time::Now());
GURL url2("http://www.example.com");
std::vector<base::Time> visits2;
visits2.push_back(Time::Now() - base::TimeDelta::FromDays(10));
visits2.push_back(Time::Now());
// Clear all history.
backend_->DeleteAllHistory();
// Add the visits.
backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
// Verify the visits were added with their sources.
VisitVector visits;
URLRow row;
URLID id = backend_->db()->GetRowForURL(url1, &row);
ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
ASSERT_EQ(3U, visits.size());
VisitSourceMap visit_sources;
backend_->db_->GetVisitsSource(visits, &visit_sources);
ASSERT_EQ(3U, visit_sources.size());
for (int i = 0; i < 3; i++)
EXPECT_EQ(history::SOURCE_IE_IMPORTED, visit_sources[visits[i].visit_id]);
id = backend_->db()->GetRowForURL(url2, &row);
ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
ASSERT_EQ(2U, visits.size());
backend_->db_->GetVisitsSource(visits, &visit_sources);
ASSERT_EQ(2U, visit_sources.size());
for (int i = 0; i < 2; i++)
EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
}
TEST_F(HistoryBackendTest, RemoveVisitsSource) {
ASSERT_TRUE(backend_.get());
GURL url1("http://www.cnn.com");
std::vector<base::Time> visits1;
visits1.push_back(Time::Now() - base::TimeDelta::FromDays(5));
visits1.push_back(Time::Now());
GURL url2("http://www.example.com");
std::vector<base::Time> visits2;
visits2.push_back(Time::Now() - base::TimeDelta::FromDays(10));
visits2.push_back(Time::Now());
// Clear all history.
backend_->DeleteAllHistory();
// Add the visits.
backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
backend_->AddVisits(url2, visits2, history::SOURCE_SYNCED);
// Verify the visits of url1 were added.
VisitVector visits;
URLRow row;
URLID id = backend_->db()->GetRowForURL(url1, &row);
ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
ASSERT_EQ(2U, visits.size());
// Remove these visits.
ASSERT_TRUE(backend_->RemoveVisits(visits));
// Now check only url2's source in visit_source table.
VisitSourceMap visit_sources;
backend_->db_->GetVisitsSource(visits, &visit_sources);
ASSERT_EQ(0U, visit_sources.size());
id = backend_->db()->GetRowForURL(url2, &row);
ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
ASSERT_EQ(2U, visits.size());
backend_->db_->GetVisitsSource(visits, &visit_sources);
ASSERT_EQ(2U, visit_sources.size());
for (int i = 0; i < 2; i++)
EXPECT_EQ(history::SOURCE_SYNCED, visit_sources[visits[i].visit_id]);
}
// Test for migration of adding visit_source table.
TEST_F(HistoryBackendTest, MigrationVisitSource) {
ASSERT_TRUE(backend_.get());
backend_->Closing();
backend_ = NULL;
FilePath old_history_path;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
old_history_path = old_history_path.AppendASCII("History");
old_history_path = old_history_path.AppendASCII("HistoryNoSource");
// Copy history database file to current directory so that it will be deleted
// in Teardown.
FilePath new_history_path(getTestDir());
file_util::Delete(new_history_path, true);
file_util::CreateDirectory(new_history_path);
FilePath new_history_file = new_history_path.Append(chrome::kHistoryFilename);
ASSERT_TRUE(file_util::CopyFile(old_history_path, new_history_file));
backend_ = new HistoryBackend(new_history_path,
new HistoryBackendTestDelegate(this),
&bookmark_model_);
backend_->Init(std::string(), false);
backend_->Closing();
backend_ = NULL;
// Now the database should already be migrated.
// Check version first.
int cur_version = HistoryDatabase::GetCurrentVersion();
sql::Connection db;
ASSERT_TRUE(db.Open(new_history_file));
sql::Statement s(db.GetUniqueStatement(
"SELECT value FROM meta WHERE key = 'version'"));
ASSERT_TRUE(s.Step());
int file_version = s.ColumnInt(0);
EXPECT_EQ(cur_version, file_version);
// Check visit_source table is created and empty.
s.Assign(db.GetUniqueStatement(
"SELECT name FROM sqlite_master WHERE name=\"visit_source\""));
ASSERT_TRUE(s.Step());
s.Assign(db.GetUniqueStatement("SELECT * FROM visit_source LIMIT 10"));
EXPECT_FALSE(s.Step());
}
TEST_F(HistoryBackendTest, SetFaviconMapping) {
// Init recent_redirects_
const GURL url1("http://www.google.com");
const GURL url2("http://www.google.com/m");
URLRow url_info1(url1);
url_info1.set_visit_count(0);
url_info1.set_typed_count(0);
url_info1.set_last_visit(base::Time());
url_info1.set_hidden(false);
backend_->db_->AddURL(url_info1);
URLRow url_info2(url2);
url_info2.set_visit_count(0);
url_info2.set_typed_count(0);
url_info2.set_last_visit(base::Time());
url_info2.set_hidden(false);
backend_->db_->AddURL(url_info2);
history::RedirectList redirects;
redirects.push_back(url2);
redirects.push_back(url1);
backend_->recent_redirects_.Put(url1, redirects);
const GURL icon_url("http://www.google.com/icon");
std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
// Add a favicon
backend_->SetFavicon(
url1, icon_url, RefCountedBytes::TakeVector(&data), FAVICON);
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url1, FAVICON, NULL));
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url2, FAVICON, NULL));
// Add a touch_icon
backend_->SetFavicon(
url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON);
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url1, TOUCH_ICON, NULL));
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url2, TOUCH_ICON, NULL));
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url1, FAVICON, NULL));
// Add a TOUCH_PRECOMPOSED_ICON
backend_->SetFavicon(url1,
icon_url,
RefCountedBytes::TakeVector(&data),
TOUCH_PRECOMPOSED_ICON);
// The touch_icon was replaced.
EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url1, TOUCH_ICON, NULL));
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url1, FAVICON, NULL));
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url1, TOUCH_PRECOMPOSED_ICON, NULL));
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url2, TOUCH_PRECOMPOSED_ICON, NULL));
// Add a touch_icon
backend_->SetFavicon(
url1, icon_url, RefCountedBytes::TakeVector(&data), TOUCH_ICON);
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url1, TOUCH_ICON, NULL));
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url1, FAVICON, NULL));
// The TOUCH_PRECOMPOSED_ICON was replaced.
EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingForPageURL(
url1, TOUCH_PRECOMPOSED_ICON, NULL));
// Add a favicon
const GURL icon_url2("http://www.google.com/icon2");
backend_->SetFavicon(
url1, icon_url2, RefCountedBytes::TakeVector(&data), FAVICON);
FaviconID icon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
icon_url2, FAVICON, NULL);
EXPECT_NE(0, icon_id);
std::vector<IconMapping> icon_mapping;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
url1, &icon_mapping));
// The old icon was replaced.
EXPECT_TRUE(icon_mapping.size() > 1);
EXPECT_EQ(icon_id, icon_mapping[1].icon_id);
}
TEST_F(HistoryBackendTest, AddOrUpdateIconMapping) {
// Test the same icon and page mapping will not be added twice. other case
// should be covered in TEST_F(HistoryBackendTest, SetFaviconMapping)
const GURL url("http://www.google.com/");
const GURL icon_url("http://www.google.com/icon");
std::vector<unsigned char> data(blob1, blob1 + sizeof(blob1));
backend_->SetFavicon(
url, icon_url, RefCountedBytes::TakeVector(&data), FAVICON);
FaviconID icon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
icon_url, FAVICON, NULL);
// Add the same mapping
FaviconID replaced;
EXPECT_FALSE(backend_->AddOrUpdateIconMapping(
url, icon_id, FAVICON, &replaced));
EXPECT_EQ(0, replaced);
std::vector<IconMapping> icon_mapping;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
url, &icon_mapping));
EXPECT_EQ(1u, icon_mapping.size());
}
} // namespace history