blob: b3508a5c36e3a655948ca3991ca5089906b96082 [file] [log] [blame]
// Copyright (c) 2012 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 "chrome/browser/history/history_backend.h"
#include <algorithm>
#include <set>
#include <vector>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/bookmarks/bookmark_test_helpers.h"
#include "chrome/browser/bookmarks/bookmark_utils.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/browser/history/history_service.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/history/in_memory_database.h"
#include "chrome/browser/history/in_memory_history_backend.h"
#include "chrome/browser/history/visit_filter.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/importer/imported_favicon_usage.h"
#include "chrome/test/base/testing_profile.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/test/test_browser_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using base::Time;
using base::TimeDelta;
// 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 {
static const gfx::Size kTinySize = gfx::Size(10, 10);
static const gfx::Size kSmallSize = gfx::Size(16, 16);
static const gfx::Size kLargeSize = gfx::Size(32, 32);
// Comparison functions as to make it easier to check results of
// GetFaviconBitmaps() and GetIconMappingsForPageURL().
bool IconMappingLessThan(const history::IconMapping& a,
const history::IconMapping& b) {
return a.icon_url < b.icon_url;
}
bool FaviconBitmapLessThan(const history::FaviconBitmap& a,
const history::FaviconBitmap& b) {
return a.pixel_size.GetArea() < b.pixel_size.GetArea();
}
} // namespace
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(int backend_id,
sql::InitStatus init_status) OVERRIDE {}
virtual void SetInMemoryBackend(int backend_id,
InMemoryHistoryBackend* backend) OVERRIDE;
virtual void BroadcastNotifications(int type,
HistoryDetails* details) OVERRIDE;
virtual void DBLoaded(int backend_id) OVERRIDE;
virtual void NotifyVisitDBObserversOnAddVisit(
const BriefVisitInfo& info) OVERRIDE {}
private:
// Not owned by us.
HistoryBackendTest* test_;
DISALLOW_COPY_AND_ASSIGN(HistoryBackendTestDelegate);
};
class HistoryBackendCancelableRequest
: public CancelableRequestProvider,
public CancelableRequestConsumerTSimple<int> {
public:
HistoryBackendCancelableRequest() {}
template<class RequestType>
CancelableRequestProvider::Handle MockScheduleOfRequest(
RequestType* request) {
AddRequest(request, this);
return request->handle();
}
};
class HistoryBackendTest : public testing::Test {
public:
HistoryBackendTest()
: bookmark_model_(NULL),
loaded_(false),
num_broadcasted_notifications_(0),
ui_thread_(content::BrowserThread::UI, &message_loop_) {
}
virtual ~HistoryBackendTest() {
}
// Callback for QueryMostVisited.
void OnQueryMostVisited(CancelableRequestProvider::Handle handle,
history::MostVisitedURLList data) {
most_visited_list_.swap(data);
}
// Callback for QueryFiltered.
void OnQueryFiltered(CancelableRequestProvider::Handle handle,
const history::FilteredURLList& data) {
filtered_list_ = data;
}
const history::MostVisitedURLList& get_most_visited_list() const {
return most_visited_list_;
}
const history::FilteredURLList& get_filtered_list() const {
return filtered_list_;
}
int num_broadcasted_notifications() const {
return num_broadcasted_notifications_;
}
protected:
scoped_refptr<HistoryBackend> backend_; // Will be NULL on init failure.
scoped_ptr<InMemoryHistoryBackend> mem_backend_;
void AddRedirectChain(const char* sequence[], int page_id) {
AddRedirectChainWithTransitionAndTime(sequence, page_id,
content::PAGE_TRANSITION_LINK,
Time::Now());
}
void AddRedirectChainWithTransitionAndTime(
const char* sequence[],
int page_id,
content::PageTransition transition,
base::Time time) {
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));
history::HistoryAddPageArgs request(
redirects.back(), time, scope, page_id, GURL(),
redirects, transition, 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.
// |time| is a time of the redirect.
void AddClientRedirect(const GURL& url1, const GURL& url2, bool did_replace,
base::Time time,
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);
HistoryAddPageArgs request(
url2, time, dummy_scope, 0, url1,
redirects, content::PAGE_TRANSITION_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;
}
base::FilePath getTestDir() {
return test_dir_;
}
// Returns a gfx::Size vector with small size.
const std::vector<gfx::Size> GetSizesSmall() {
std::vector<gfx::Size> sizes_small;
sizes_small.push_back(kSmallSize);
return sizes_small;
}
// Returns a gfx::Size vector with large size.
const std::vector<gfx::Size> GetSizesLarge() {
std::vector<gfx::Size> sizes_large;
sizes_large.push_back(kLargeSize);
return sizes_large;
}
// Returns a gfx::Size vector with small and large sizes.
const std::vector<gfx::Size> GetSizesSmallAndLarge() {
std::vector<gfx::Size> sizes_small_and_large;
sizes_small_and_large.push_back(kSmallSize);
sizes_small_and_large.push_back(kLargeSize);
return sizes_small_and_large;
}
// Returns a gfx::Size vector with tiny, small and large sizes.
const std::vector<gfx::Size> GetSizesTinySmallAndLarge() {
std::vector<gfx::Size> sizes_tiny_small_and_large;
sizes_tiny_small_and_large.push_back(kTinySize);
sizes_tiny_small_and_large.push_back(kSmallSize);
sizes_tiny_small_and_large.push_back(kLargeSize);
return sizes_tiny_small_and_large;
}
// Returns 1x and 2x scale factors.
const std::vector<ui::ScaleFactor> GetScaleFactors1x2x() {
std::vector<ui::ScaleFactor> scale_factors_1x_2x;
scale_factors_1x_2x.push_back(ui::SCALE_FACTOR_100P);
scale_factors_1x_2x.push_back(ui::SCALE_FACTOR_200P);
return scale_factors_1x_2x;
}
// Returns the number of icon mappings of |icon_type| to |page_url|.
size_t NumIconMappingsForPageURL(const GURL& page_url,
chrome::IconType icon_type) {
std::vector<IconMapping> icon_mappings;
backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url, icon_type,
&icon_mappings);
return icon_mappings.size();
}
// Returns the icon mappings for |page_url| sorted alphabetically by icon
// URL in ascending order. Returns true if there is at least one icon
// mapping.
bool GetSortedIconMappingsForPageURL(
const GURL& page_url,
std::vector<IconMapping>* icon_mappings) {
if (!backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
icon_mappings)) {
return false;
}
std::sort(icon_mappings->begin(), icon_mappings->end(),
IconMappingLessThan);
return true;
}
// Returns the favicon bitmaps for |icon_id| sorted by pixel size in
// ascending order. Returns true if there is at least one favicon bitmap.
bool GetSortedFaviconBitmaps(chrome::FaviconID icon_id,
std::vector<FaviconBitmap>* favicon_bitmaps) {
if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, favicon_bitmaps))
return false;
std::sort(favicon_bitmaps->begin(), favicon_bitmaps->end(),
FaviconBitmapLessThan);
return true;
}
// Returns true if there is exactly one favicon bitmap associated to
// |favicon_id|. If true, returns favicon bitmap in output parameter.
bool GetOnlyFaviconBitmap(const chrome::FaviconID icon_id,
FaviconBitmap* favicon_bitmap) {
std::vector<FaviconBitmap> favicon_bitmaps;
if (!backend_->thumbnail_db_->GetFaviconBitmaps(icon_id, &favicon_bitmaps))
return false;
if (favicon_bitmaps.size() != 1)
return false;
*favicon_bitmap = favicon_bitmaps[0];
return true;
}
// Generates |favicon_bitmap_data| with entries for the icon_urls and sizes
// specified. The bitmap_data for entries are lowercase letters of the
// alphabet starting at 'a' for the entry at index 0.
void GenerateFaviconBitmapData(
const GURL& icon_url1,
const std::vector<gfx::Size>& icon_url1_sizes,
std::vector<chrome::FaviconBitmapData>* favicon_bitmap_data) {
GenerateFaviconBitmapData(icon_url1, icon_url1_sizes, GURL(),
std::vector<gfx::Size>(), favicon_bitmap_data);
}
void GenerateFaviconBitmapData(
const GURL& icon_url1,
const std::vector<gfx::Size>& icon_url1_sizes,
const GURL& icon_url2,
const std::vector<gfx::Size>& icon_url2_sizes,
std::vector<chrome::FaviconBitmapData>* favicon_bitmap_data) {
favicon_bitmap_data->clear();
char bitmap_char = 'a';
for (size_t i = 0; i < icon_url1_sizes.size(); ++i) {
std::vector<unsigned char> data;
data.push_back(bitmap_char);
chrome::FaviconBitmapData bitmap_data_element;
bitmap_data_element.bitmap_data =
base::RefCountedBytes::TakeVector(&data);
bitmap_data_element.pixel_size = icon_url1_sizes[i];
bitmap_data_element.icon_url = icon_url1;
favicon_bitmap_data->push_back(bitmap_data_element);
++bitmap_char;
}
for (size_t i = 0; i < icon_url2_sizes.size(); ++i) {
std::vector<unsigned char> data;
data.push_back(bitmap_char);
chrome::FaviconBitmapData bitmap_data_element;
bitmap_data_element.bitmap_data =
base::RefCountedBytes::TakeVector(&data);
bitmap_data_element.pixel_size = icon_url2_sizes[i];
bitmap_data_element.icon_url = icon_url2;
favicon_bitmap_data->push_back(bitmap_data_element);
++bitmap_char;
}
}
// Returns true if |bitmap_data| is equal to |expected_data|.
bool BitmapDataEqual(char expected_data,
scoped_refptr<base::RefCountedMemory> bitmap_data) {
return bitmap_data.get() &&
bitmap_data->size() == 1u &&
*bitmap_data->front() == expected_data;
}
BookmarkModel bookmark_model_;
protected:
// testing::Test
virtual void SetUp() {
if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("BackendTest"),
&test_dir_))
return;
backend_ = new HistoryBackend(test_dir_,
0,
new HistoryBackendTestDelegate(this),
&bookmark_model_);
backend_->Init(std::string(), false);
}
bool loaded_;
private:
friend class HistoryBackendTestDelegate;
virtual void TearDown() {
if (backend_.get())
backend_->Closing();
backend_ = NULL;
mem_backend_.reset();
base::DeleteFile(test_dir_, true);
base::RunLoop().RunUntilIdle();
}
void SetInMemoryBackend(int backend_id, InMemoryHistoryBackend* backend) {
mem_backend_.reset(backend);
}
void BroadcastNotifications(int type,
HistoryDetails* details) {
++num_broadcasted_notifications_;
// Send the notifications directly to the in-memory database.
content::Details<HistoryDetails> det(details);
mem_backend_->Observe(type, content::Source<HistoryBackendTest>(NULL), det);
// The backend passes ownership of the details pointer to us.
delete details;
}
// The number of notifications which were broadcasted.
int num_broadcasted_notifications_;
base::MessageLoop message_loop_;
base::FilePath test_dir_;
history::MostVisitedURLList most_visited_list_;
history::FilteredURLList filtered_list_;
content::TestBrowserThread ui_thread_;
};
void HistoryBackendTestDelegate::SetInMemoryBackend(int backend_id,
InMemoryHistoryBackend* backend) {
test_->SetInMemoryBackend(backend_id, backend);
}
void HistoryBackendTestDelegate::BroadcastNotifications(
int type,
HistoryDetails* details) {
test_->BroadcastNotifications(type, details);
}
void HistoryBackendTestDelegate::DBLoaded(int backend_id) {
test_->loaded_ = true;
}
// http://crbug.com/114287
#if defined(OS_WIN)
#define MAYBE_Loaded DISABLED_Loaded
#else
#define MAYBE_Loaded Loaded
#endif // defined(OS_WIN)
TEST_F(HistoryBackendTest, MAYBE_Loaded) {
ASSERT_TRUE(backend_.get());
ASSERT_TRUE(loaded_);
}
TEST_F(HistoryBackendTest, DeleteAll) {
ASSERT_TRUE(backend_.get());
// Add two favicons, each with two bitmaps. Note that we add favicon2 before
// adding favicon1. This is so that favicon1 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");
chrome::FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(favicon_url2,
chrome::FAVICON);
chrome::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(favicon_url1,
chrome::FAVICON);
std::vector<unsigned char> data;
data.push_back('a');
EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
new base::RefCountedBytes(data), Time::Now(), kSmallSize));
data[0] = 'b';
EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon1,
new base::RefCountedBytes(data), Time::Now(), kLargeSize));
data[0] = 'c';
EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
new base::RefCountedBytes(data), Time::Now(), kSmallSize));
data[0] = 'd';
EXPECT_TRUE(backend_->thumbnail_db_->AddFaviconBitmap(favicon2,
new base::RefCountedBytes(data), Time::Now(), kLargeSize));
// 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);
URLRows 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());
visits.clear();
backend_->db_->GetVisitsForURL(row2_id, &visits);
ASSERT_EQ(1U, visits.size());
// 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));
// Star row1.
bookmark_model_.AddURL(
bookmark_model_.bookmark_bar_node(), 0, base::string16(), row1.url());
// 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());
// We should have a favicon and favicon bitmaps for the first URL only. We
// look them up by favicon URL since the IDs may have changed.
chrome::FaviconID out_favicon1 = backend_->thumbnail_db_->
GetFaviconIDForFaviconURL(favicon_url1, chrome::FAVICON, NULL);
EXPECT_TRUE(out_favicon1);
std::vector<FaviconBitmap> favicon_bitmaps;
EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
out_favicon1, &favicon_bitmaps));
ASSERT_EQ(2u, favicon_bitmaps.size());
FaviconBitmap favicon_bitmap1 = favicon_bitmaps[0];
FaviconBitmap favicon_bitmap2 = favicon_bitmaps[1];
// Favicon bitmaps do not need to be in particular order.
if (favicon_bitmap1.pixel_size == kLargeSize) {
FaviconBitmap tmp_favicon_bitmap = favicon_bitmap1;
favicon_bitmap1 = favicon_bitmap2;
favicon_bitmap2 = tmp_favicon_bitmap;
}
EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap1.bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmap1.pixel_size);
EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap2.bitmap_data));
EXPECT_EQ(kLargeSize, favicon_bitmap2.pixel_size);
chrome::FaviconID out_favicon2 = backend_->thumbnail_db_->
GetFaviconIDForFaviconURL(favicon_url2, chrome::FAVICON, NULL);
EXPECT_FALSE(out_favicon2) << "Favicon not deleted";
// The remaining URL should still reference the same favicon, even if its
// ID has changed.
std::vector<IconMapping> mappings;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
outrow1.url(), chrome::FAVICON, &mappings));
EXPECT_EQ(1u, mappings.size());
EXPECT_EQ(out_favicon1, mappings[0].icon_id);
// The first URL should still be bookmarked.
EXPECT_TRUE(bookmark_model_.IsBookmarked(row1.url()));
}
// Checks that adding a visit, then calling DeleteAll, and then trying to add
// data for the visited page works. This can happen when clearing the history
// immediately after visiting a page.
TEST_F(HistoryBackendTest, DeleteAllThenAddData) {
ASSERT_TRUE(backend_.get());
Time visit_time = Time::Now();
GURL url("http://www.google.com/");
HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
history::RedirectList(),
content::PAGE_TRANSITION_KEYWORD_GENERATED,
history::SOURCE_BROWSED, false);
backend_->AddPage(request);
// Check that a row was added.
URLRow outrow;
EXPECT_TRUE(backend_->db_->GetRowForURL(url, &outrow));
// Check that the visit was added.
VisitVector all_visits;
backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
ASSERT_EQ(1U, all_visits.size());
// Clear all history.
backend_->DeleteAllHistory();
// The row should be deleted.
EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
// The visit should be deleted.
backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
ASSERT_EQ(0U, all_visits.size());
// Try and set the title.
backend_->SetPageTitle(url, UTF8ToUTF16("Title"));
// The row should still be deleted.
EXPECT_FALSE(backend_->db_->GetRowForURL(url, &outrow));
// The visit should still be deleted.
backend_->db_->GetAllVisitsInRange(Time(), Time(), 0, &all_visits);
ASSERT_EQ(0U, all_visits.size());
}
TEST_F(HistoryBackendTest, URLsNoLongerBookmarked) {
GURL favicon_url1("http://www.google.com/favicon.ico");
GURL favicon_url2("http://news.google.com/favicon.ico");
std::vector<unsigned char> data;
data.push_back('1');
chrome::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
favicon_url1,
chrome::FAVICON,
new base::RefCountedBytes(data),
Time::Now(),
gfx::Size());
data[0] = '2';
chrome::FaviconID favicon2 = backend_->thumbnail_db_->AddFavicon(
favicon_url2,
chrome::FAVICON,
new base::RefCountedBytes(data),
Time::Now(),
gfx::Size());
// 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));
URLRows 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_utils::AddIfNotBookmarked(&bookmark_model_, row1.url(),
base::string16());
bookmark_utils::AddIfNotBookmarked(&bookmark_model_, row2.url(),
base::string16());
// 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,
chrome::FAVICON,
NULL));
// Unstar row2.
bookmark_utils::RemoveAllBookmarks(&bookmark_model_, row2.url());
// 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,
chrome::FAVICON,
NULL));
// Unstar row 1.
bookmark_utils::RemoveAllBookmarks(&bookmark_model_, row1.url());
// 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,
chrome::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);
HistoryAddPageArgs request(url, visit_time, NULL, 0, GURL(),
history::RedirectList(),
content::PAGE_TRANSITION_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();
QueryOptions query_options;
query_options.max_count = 1;
backend_->db()->GetVisibleVisitsInRange(query_options, &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, base::Time(),
&transition1, &transition2);
EXPECT_TRUE(transition2 & content::PAGE_TRANSITION_CHAIN_END);
// User initiated redirect to page B.
GURL url_b("http://google.com/b");
AddClientRedirect(url_a, url_b, false, base::Time(),
&transition1, &transition2);
EXPECT_TRUE(transition1 & content::PAGE_TRANSITION_CHAIN_END);
EXPECT_TRUE(transition2 & content::PAGE_TRANSITION_CHAIN_END);
// Non-user initiated redirect to page C.
GURL url_c("http://google.com/c");
AddClientRedirect(url_b, url_c, true, base::Time(),
&transition1, &transition2);
EXPECT_FALSE(transition1 & content::PAGE_TRANSITION_CHAIN_END);
EXPECT_TRUE(transition2 & content::PAGE_TRANSITION_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");
std::vector<unsigned char> data;
data.push_back('1');
chrome::FaviconID favicon1 = backend_->thumbnail_db_->AddFavicon(
favicon_url1,
chrome::FAVICON,
base::RefCountedBytes::TakeVector(&data),
Time::Now(),
gfx::Size());
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());
URLRows 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_EQ(1u, NumIconMappingsForPageURL(row1.url(), chrome::FAVICON));
EXPECT_EQ(0u, NumIconMappingsForPageURL(row2.url(), chrome::FAVICON));
// 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<ImportedFaviconUsage> favicons;
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);
std::vector<IconMapping> mappings;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
row1.url(), chrome::FAVICON, &mappings));
EXPECT_EQ(1u, mappings.size());
EXPECT_EQ(favicon1, mappings[0].icon_id);
EXPECT_EQ(favicon_url1, mappings[0].icon_url);
mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
row2.url(), chrome::FAVICON, &mappings));
EXPECT_EQ(1u, mappings.size());
EXPECT_EQ(favicon.favicon_url, mappings[0].icon_url);
// 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_.bookmark_bar_node(), 0,
base::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,
content::PageTransitionFromInt(
content::PageTransitionGetQualifier(content::PAGE_TRANSITION_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, content::PAGE_TRANSITION_TYPED,
history::SOURCE_EXTENSION);
// Assume the url is imported from Firefox.
backend_->AddPageVisit(url, base::Time::Now(), 0,
content::PAGE_TRANSITION_TYPED,
history::SOURCE_FIREFOX_IMPORTED);
// Assume this url is also synced.
backend_->AddPageVisit(url, base::Time::Now(), 0,
content::PAGE_TRANSITION_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;
ASSERT_TRUE(backend_->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, AddPageVisitNotLastVisit) {
ASSERT_TRUE(backend_.get());
GURL url("http://www.google.com");
// Clear all history.
backend_->DeleteAllHistory();
// Create visit times
base::Time recent_time = base::Time::Now();
base::TimeDelta visit_age = base::TimeDelta::FromDays(3);
base::Time older_time = recent_time - visit_age;
// Visit the url with recent time.
backend_->AddPageVisit(url, recent_time, 0,
content::PageTransitionFromInt(
content::PageTransitionGetQualifier(content::PAGE_TRANSITION_TYPED)),
history::SOURCE_BROWSED);
// Add to the url a visit with older time (could be syncing from another
// client, etc.).
backend_->AddPageVisit(url, older_time, 0,
content::PageTransitionFromInt(
content::PageTransitionGetQualifier(content::PAGE_TRANSITION_TYPED)),
history::SOURCE_SYNCED);
// Fetch the row information about url from history db.
VisitVector visits;
URLRow row;
URLID row_id = backend_->db_->GetRowForURL(url, &row);
backend_->db_->GetVisitsForURL(row_id, &visits);
// Last visit time should be the most recent time, not the most recently added
// visit.
ASSERT_EQ(2U, visits.size());
ASSERT_EQ(recent_time, row.last_visit());
}
TEST_F(HistoryBackendTest, AddPageArgsSource) {
ASSERT_TRUE(backend_.get());
GURL url("http://testpageargs.com");
// Assume this page is browsed by user.
HistoryAddPageArgs request1(url, base::Time::Now(), NULL, 0, GURL(),
history::RedirectList(),
content::PAGE_TRANSITION_KEYWORD_GENERATED,
history::SOURCE_BROWSED, false);
backend_->AddPage(request1);
// Assume this page is synced.
HistoryAddPageArgs request2(url, base::Time::Now(), NULL, 0, GURL(),
history::RedirectList(),
content::PAGE_TRANSITION_LINK,
history::SOURCE_SYNCED, false);
backend_->AddPage(request2);
// Assume this page is browsed again.
HistoryAddPageArgs request3(url, base::Time::Now(), NULL, 0, GURL(),
history::RedirectList(),
content::PAGE_TRANSITION_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;
ASSERT_TRUE(backend_->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<VisitInfo> visits1, visits2;
visits1.push_back(VisitInfo(
Time::Now() - base::TimeDelta::FromDays(5),
content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(
Time::Now() - base::TimeDelta::FromDays(1),
content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(
Time::Now(), content::PAGE_TRANSITION_LINK));
GURL url2("http://www.example.com");
visits2.push_back(VisitInfo(
Time::Now() - base::TimeDelta::FromDays(10),
content::PAGE_TRANSITION_LINK));
visits2.push_back(VisitInfo(Time::Now(), content::PAGE_TRANSITION_LINK));
// 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;
ASSERT_TRUE(backend_->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());
ASSERT_TRUE(backend_->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, GetMostRecentVisits) {
ASSERT_TRUE(backend_.get());
GURL url1("http://www.cnn.com");
std::vector<VisitInfo> visits1;
visits1.push_back(VisitInfo(
Time::Now() - base::TimeDelta::FromDays(5),
content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(
Time::Now() - base::TimeDelta::FromDays(1),
content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(
Time::Now(), content::PAGE_TRANSITION_LINK));
// Clear all history.
backend_->DeleteAllHistory();
// Add the visits.
backend_->AddVisits(url1, visits1, history::SOURCE_IE_IMPORTED);
// Verify the visits were added with their sources.
VisitVector visits;
URLRow row;
URLID id = backend_->db()->GetRowForURL(url1, &row);
ASSERT_TRUE(backend_->db()->GetMostRecentVisitsForURL(id, 1, &visits));
ASSERT_EQ(1U, visits.size());
EXPECT_EQ(visits1[2].first, visits[0].visit_time);
}
TEST_F(HistoryBackendTest, RemoveVisitsTransitions) {
ASSERT_TRUE(backend_.get());
// Clear all history.
backend_->DeleteAllHistory();
GURL url1("http://www.cnn.com");
VisitInfo typed_visit(
Time::Now() - base::TimeDelta::FromDays(6),
content::PAGE_TRANSITION_TYPED);
VisitInfo reload_visit(
Time::Now() - base::TimeDelta::FromDays(5),
content::PAGE_TRANSITION_RELOAD);
VisitInfo link_visit(
Time::Now() - base::TimeDelta::FromDays(4),
content::PAGE_TRANSITION_LINK);
std::vector<VisitInfo> visits_to_add;
visits_to_add.push_back(typed_visit);
visits_to_add.push_back(reload_visit);
visits_to_add.push_back(link_visit);
// Add the visits.
backend_->AddVisits(url1, visits_to_add, history::SOURCE_SYNCED);
// Verify that the various counts are what we expect.
VisitVector visits;
URLRow row;
URLID id = backend_->db()->GetRowForURL(url1, &row);
ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
ASSERT_EQ(3U, visits.size());
ASSERT_EQ(1, row.typed_count());
ASSERT_EQ(2, row.visit_count());
// Now, delete the typed visit and verify that typed_count is updated.
ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
id = backend_->db()->GetRowForURL(url1, &row);
ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
ASSERT_EQ(2U, visits.size());
ASSERT_EQ(0, row.typed_count());
ASSERT_EQ(1, row.visit_count());
// Delete the reload visit now and verify that none of the counts have
// changed.
ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
id = backend_->db()->GetRowForURL(url1, &row);
ASSERT_TRUE(backend_->db()->GetVisitsForURL(id, &visits));
ASSERT_EQ(1U, visits.size());
ASSERT_EQ(0, row.typed_count());
ASSERT_EQ(1, row.visit_count());
// Delete the last visit and verify that we delete the URL.
ASSERT_TRUE(backend_->RemoveVisits(VisitVector(1, visits[0])));
ASSERT_EQ(0, backend_->db()->GetRowForURL(url1, &row));
}
TEST_F(HistoryBackendTest, RemoveVisitsSource) {
ASSERT_TRUE(backend_.get());
GURL url1("http://www.cnn.com");
std::vector<VisitInfo> visits1, visits2;
visits1.push_back(VisitInfo(
Time::Now() - base::TimeDelta::FromDays(5),
content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(Time::Now(),
content::PAGE_TRANSITION_LINK));
GURL url2("http://www.example.com");
visits2.push_back(VisitInfo(
Time::Now() - base::TimeDelta::FromDays(10),
content::PAGE_TRANSITION_LINK));
visits2.push_back(VisitInfo(Time::Now(), content::PAGE_TRANSITION_LINK));
// 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;
ASSERT_TRUE(backend_->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());
ASSERT_TRUE(backend_->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;
base::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.
base::FilePath new_history_path(getTestDir());
base::DeleteFile(new_history_path, true);
base::CreateDirectory(new_history_path);
base::FilePath new_history_file =
new_history_path.Append(chrome::kHistoryFilename);
ASSERT_TRUE(base::CopyFile(old_history_path, new_history_file));
backend_ = new HistoryBackend(new_history_path,
0,
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 that SetFaviconMappingsForPageAndRedirects correctly updates icon
// mappings based on redirects, icon URLs and icon types.
TEST_F(HistoryBackendTest, SetFaviconMappingsForPageAndRedirects) {
// 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_url1("http://www.google.com/icon");
const GURL icon_url2("http://www.google.com/icon2");
// Generate bitmap data for a page with two favicons.
std::vector<chrome::FaviconBitmapData> two_favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url1, GetSizesSmallAndLarge(),
icon_url2, GetSizesSmallAndLarge(), &two_favicon_bitmap_data);
// Generate bitmap data for a page with a single favicon.
std::vector<chrome::FaviconBitmapData> one_favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url1, GetSizesSmallAndLarge(),
&one_favicon_bitmap_data);
// Add two favicons
backend_->SetFavicons(url1, chrome::FAVICON, two_favicon_bitmap_data);
EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
EXPECT_EQ(2u, NumIconMappingsForPageURL(url2, chrome::FAVICON));
// Add one touch_icon
backend_->SetFavicons(url1, chrome::TOUCH_ICON, one_favicon_bitmap_data);
EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, chrome::TOUCH_ICON));
EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, chrome::TOUCH_ICON));
EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
// Add one TOUCH_PRECOMPOSED_ICON
backend_->SetFavicons(
url1, chrome::TOUCH_PRECOMPOSED_ICON, one_favicon_bitmap_data);
// The touch_icon was replaced.
EXPECT_EQ(0u, NumIconMappingsForPageURL(url1, chrome::TOUCH_ICON));
EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
EXPECT_EQ(1u,
NumIconMappingsForPageURL(url1, chrome::TOUCH_PRECOMPOSED_ICON));
EXPECT_EQ(1u,
NumIconMappingsForPageURL(url2, chrome::TOUCH_PRECOMPOSED_ICON));
// Add a touch_icon.
backend_->SetFavicons(url1, chrome::TOUCH_ICON, one_favicon_bitmap_data);
EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, chrome::TOUCH_ICON));
EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
// The TOUCH_PRECOMPOSED_ICON was replaced.
EXPECT_EQ(0u,
NumIconMappingsForPageURL(url1, chrome::TOUCH_PRECOMPOSED_ICON));
// Add a single favicon.
backend_->SetFavicons(url1, chrome::FAVICON, one_favicon_bitmap_data);
EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, chrome::TOUCH_ICON));
EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
EXPECT_EQ(1u, NumIconMappingsForPageURL(url2, chrome::FAVICON));
// Add two favicons.
backend_->SetFavicons(url1, chrome::FAVICON, two_favicon_bitmap_data);
EXPECT_EQ(1u, NumIconMappingsForPageURL(url1, chrome::TOUCH_ICON));
EXPECT_EQ(2u, NumIconMappingsForPageURL(url1, chrome::FAVICON));
}
// Test that there is no churn in icon mappings from calling
// SetFavicons() twice with the same |favicon_bitmap_data| parameter.
TEST_F(HistoryBackendTest, SetFaviconMappingsForPageDuplicates) {
const GURL url("http://www.google.com/");
const GURL icon_url("http://www.google.com/icon");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url, GetSizesSmallAndLarge(),
&favicon_bitmap_data);
backend_->SetFavicons(url, chrome::FAVICON, favicon_bitmap_data);
std::vector<IconMapping> icon_mappings;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
url, chrome::FAVICON, &icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
IconMappingID mapping_id = icon_mappings[0].mapping_id;
backend_->SetFavicons(url, chrome::FAVICON, favicon_bitmap_data);
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
url, chrome::FAVICON, &icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
// The same row in the icon_mapping table should be used for the mapping as
// before.
EXPECT_EQ(mapping_id, icon_mappings[0].mapping_id);
}
// Test that calling SetFavicons() with FaviconBitmapData of different pixel
// sizes than the initially passed in FaviconBitmapData deletes the no longer
// used favicon bitmaps.
TEST_F(HistoryBackendTest, SetFaviconsDeleteBitmaps) {
const GURL page_url("http://www.google.com/");
const GURL icon_url("http://www.google.com/icon");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url, GetSizesSmallAndLarge(),
&favicon_bitmap_data);
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
// Test initial state.
std::vector<IconMapping> icon_mappings;
EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url, &icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
EXPECT_EQ(chrome::FAVICON, icon_mappings[0].icon_type);
chrome::FaviconID favicon_id = icon_mappings[0].icon_id;
std::vector<FaviconBitmap> favicon_bitmaps;
EXPECT_TRUE(GetSortedFaviconBitmaps(favicon_id, &favicon_bitmaps));
EXPECT_EQ(2u, favicon_bitmaps.size());
FaviconBitmapID small_bitmap_id = favicon_bitmaps[0].bitmap_id;
EXPECT_NE(0, small_bitmap_id);
EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmaps[0].bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmaps[0].pixel_size);
FaviconBitmapID large_bitmap_id = favicon_bitmaps[1].bitmap_id;
EXPECT_NE(0, large_bitmap_id);
EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data));
EXPECT_EQ(kLargeSize, favicon_bitmaps[1].pixel_size);
// Call SetFavicons() with bitmap data for only the large bitmap. Check that
// the small bitmap is in fact deleted.
GenerateFaviconBitmapData(icon_url, GetSizesLarge(), &favicon_bitmap_data);
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
scoped_refptr<base::RefCountedMemory> bitmap_data_out;
gfx::Size pixel_size_out;
EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(small_bitmap_id,
NULL, &bitmap_data_out, &pixel_size_out));
EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmap(large_bitmap_id,
NULL, &bitmap_data_out, &pixel_size_out));
EXPECT_TRUE(BitmapDataEqual('a', bitmap_data_out));
EXPECT_EQ(kLargeSize, pixel_size_out);
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
// Call SetFavicons() with no bitmap data. Check that the bitmaps and icon
// mappings are deleted.
favicon_bitmap_data.clear();
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
EXPECT_FALSE(backend_->thumbnail_db_->GetFaviconBitmap(large_bitmap_id, NULL,
NULL, NULL));
icon_mappings.clear();
EXPECT_FALSE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
&icon_mappings));
// Notifications should have been broadcast for each call to SetFavicons().
EXPECT_EQ(3, num_broadcasted_notifications());
}
// Test updating a single favicon bitmap's data via SetFavicons.
TEST_F(HistoryBackendTest, SetFaviconsReplaceBitmapData) {
const GURL page_url("http://www.google.com/");
const GURL icon_url("http://www.google.com/icon");
std::vector<unsigned char> data_initial;
data_initial.push_back('a');
chrome::FaviconBitmapData bitmap_data_element;
bitmap_data_element.bitmap_data =
base::RefCountedBytes::TakeVector(&data_initial);
bitmap_data_element.pixel_size = kSmallSize;
bitmap_data_element.icon_url = icon_url;
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
favicon_bitmap_data.push_back(bitmap_data_element);
// Add bitmap to the database.
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
chrome::FaviconID original_favicon_id =
backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
icon_url, chrome::FAVICON, NULL);
EXPECT_NE(0, original_favicon_id);
FaviconBitmap original_favicon_bitmap;
EXPECT_TRUE(
GetOnlyFaviconBitmap(original_favicon_id, &original_favicon_bitmap));
EXPECT_TRUE(BitmapDataEqual('a', original_favicon_bitmap.bitmap_data));
EXPECT_EQ(1, num_broadcasted_notifications());
// Call SetFavicons() with completely identical data.
std::vector<unsigned char> updated_data;
updated_data.push_back('a');
favicon_bitmap_data[0].bitmap_data = new base::RefCountedBytes(updated_data);
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
chrome::FaviconID updated_favicon_id =
backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
icon_url, chrome::FAVICON, NULL);
EXPECT_NE(0, updated_favicon_id);
FaviconBitmap updated_favicon_bitmap;
EXPECT_TRUE(
GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
EXPECT_TRUE(BitmapDataEqual('a', updated_favicon_bitmap.bitmap_data));
// Because the bitmap data is byte equivalent, no notifications should have
// been broadcasted.
EXPECT_EQ(1, num_broadcasted_notifications());
// Call SetFavicons() with identical data but a different bitmap.
updated_data[0] = 'b';
favicon_bitmap_data[0].bitmap_data = new base::RefCountedBytes(updated_data);
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
updated_favicon_id =
backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
icon_url, chrome::FAVICON, NULL);
EXPECT_NE(0, updated_favicon_id);
EXPECT_TRUE(
GetOnlyFaviconBitmap(updated_favicon_id, &updated_favicon_bitmap));
EXPECT_TRUE(BitmapDataEqual('b', updated_favicon_bitmap.bitmap_data));
// There should be no churn in FaviconIDs or FaviconBitmapIds even though
// the bitmap data changed.
EXPECT_EQ(original_favicon_bitmap.icon_id, updated_favicon_bitmap.icon_id);
EXPECT_EQ(original_favicon_bitmap.bitmap_id,
updated_favicon_bitmap.bitmap_id);
// A notification should have been broadcasted as the favicon bitmap data has
// changed.
EXPECT_EQ(2, num_broadcasted_notifications());
}
// Test that if two pages share the same FaviconID, changing the favicon for
// one page does not affect the other.
TEST_F(HistoryBackendTest, SetFaviconsSameFaviconURLForTwoPages) {
GURL icon_url("http://www.google.com/favicon.ico");
GURL icon_url_new("http://www.google.com/favicon2.ico");
GURL page_url1("http://www.google.com");
GURL page_url2("http://www.google.ca");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url, GetSizesSmallAndLarge(),
&favicon_bitmap_data);
backend_->SetFavicons(page_url1, chrome::FAVICON, favicon_bitmap_data);
std::vector<GURL> icon_urls;
icon_urls.push_back(icon_url);
std::vector<chrome::FaviconBitmapResult> bitmap_results;
backend_->UpdateFaviconMappingsAndFetch(
page_url2, icon_urls, chrome::FAVICON, kSmallSize.width(),
GetScaleFactors1x2x(), &bitmap_results);
// Check that the same FaviconID is mapped to both page URLs.
std::vector<IconMapping> icon_mappings;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
page_url1, &icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
chrome::FaviconID favicon_id = icon_mappings[0].icon_id;
EXPECT_NE(0, favicon_id);
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
page_url2, &icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
// Change the icon URL that |page_url1| is mapped to.
GenerateFaviconBitmapData(icon_url_new, GetSizesSmall(),
&favicon_bitmap_data);
backend_->SetFavicons(page_url1, chrome::FAVICON, favicon_bitmap_data);
// |page_url1| should map to a new FaviconID and have valid bitmap data.
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
page_url1, &icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_url_new, icon_mappings[0].icon_url);
EXPECT_NE(favicon_id, icon_mappings[0].icon_id);
std::vector<FaviconBitmap> favicon_bitmaps;
EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
icon_mappings[0].icon_id, &favicon_bitmaps));
EXPECT_EQ(1u, favicon_bitmaps.size());
// |page_url2| should still map to the same FaviconID and have valid bitmap
// data.
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
page_url2, &icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
favicon_bitmaps.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(favicon_id,
&favicon_bitmaps));
EXPECT_EQ(2u, favicon_bitmaps.size());
// A notification should have been broadcast for each call to SetFavicons()
// and each call to UpdateFaviconMappingsAndFetch().
EXPECT_EQ(3, num_broadcasted_notifications());
}
// Test that no notifications are broadcast as a result of calling
// UpdateFaviconMappingsAndFetch() for an icon URL which is already
// mapped to the passed in page URL.
TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoChange) {
GURL page_url("http://www.google.com");
GURL icon_url("http://www.google.com/favicon.ico");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url, GetSizesSmall(), &favicon_bitmap_data);
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
chrome::FaviconID icon_id =
backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
icon_url, chrome::FAVICON, NULL);
EXPECT_NE(0, icon_id);
EXPECT_EQ(1, num_broadcasted_notifications());
std::vector<GURL> icon_urls;
icon_urls.push_back(icon_url);
std::vector<chrome::FaviconBitmapResult> bitmap_results;
backend_->UpdateFaviconMappingsAndFetch(
page_url, icon_urls, chrome::FAVICON, kSmallSize.width(),
GetScaleFactors1x2x(), &bitmap_results);
EXPECT_EQ(icon_id, backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
icon_url, chrome::FAVICON, NULL));
// No notification should have been broadcast as no icon mapping, favicon,
// or favicon bitmap was updated, added or removed.
EXPECT_EQ(1, num_broadcasted_notifications());
}
// Test repeatedly calling MergeFavicon(). |page_url| is initially not known
// to the database.
TEST_F(HistoryBackendTest, MergeFaviconPageURLNotInDB) {
GURL page_url("http://www.google.com");
GURL icon_url("http:/www.google.com/favicon.ico");
std::vector<unsigned char> data;
data.push_back('a');
scoped_refptr<base::RefCountedBytes> bitmap_data(
new base::RefCountedBytes(data));
backend_->MergeFavicon(
page_url, icon_url, chrome::FAVICON, bitmap_data, kSmallSize);
// |page_url| should now be mapped to |icon_url| and the favicon bitmap should
// not be expired.
std::vector<IconMapping> icon_mappings;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
FaviconBitmap favicon_bitmap;
EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
data[0] = 'b';
bitmap_data = new base::RefCountedBytes(data);
backend_->MergeFavicon(
page_url, icon_url, chrome::FAVICON, bitmap_data, kSmallSize);
// |page_url| should still have a single favicon bitmap. The bitmap data
// should be updated.
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
}
// Test calling MergeFavicon() when |page_url| is known to the database.
TEST_F(HistoryBackendTest, MergeFaviconPageURLInDB) {
GURL page_url("http://www.google.com");
GURL icon_url1("http:/www.google.com/favicon.ico");
GURL icon_url2("http://www.google.com/favicon2.ico");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url1, GetSizesSmall(),
&favicon_bitmap_data);
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
// Test initial state.
std::vector<IconMapping> icon_mappings;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
FaviconBitmap favicon_bitmap;
EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
EXPECT_EQ(1, num_broadcasted_notifications());
// 1) Merge identical favicon bitmap.
std::vector<unsigned char> data;
data.push_back('a');
scoped_refptr<base::RefCountedBytes> bitmap_data(
new base::RefCountedBytes(data));
backend_->MergeFavicon(
page_url, icon_url1, chrome::FAVICON, bitmap_data, kSmallSize);
// All the data should stay the same and no notifications should have been
// sent.
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
EXPECT_EQ(1, num_broadcasted_notifications());
// 2) Merge favicon bitmap of the same size.
data[0] = 'b';
bitmap_data = new base::RefCountedBytes(data);
backend_->MergeFavicon(
page_url, icon_url1, chrome::FAVICON, bitmap_data, kSmallSize);
// The small favicon bitmap at |icon_url1| should be overwritten.
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
// 3) Merge favicon for the same icon URL, but a pixel size for which there is
// no favicon bitmap.
data[0] = 'c';
bitmap_data = new base::RefCountedBytes(data);
backend_->MergeFavicon(
page_url, icon_url1, chrome::FAVICON, bitmap_data, kTinySize);
// A new favicon bitmap should be created and the preexisting favicon bitmap
// ('b') should be expired.
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_url1, icon_mappings[0].icon_url);
std::vector<FaviconBitmap> favicon_bitmaps;
EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
&favicon_bitmaps));
EXPECT_NE(base::Time(), favicon_bitmaps[0].last_updated);
EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
EXPECT_EQ(base::Time(), favicon_bitmaps[1].last_updated);
EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmaps[1].bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
// 4) Merge favicon for an icon URL different from the icon URLs already
// mapped to page URL.
data[0] = 'd';
bitmap_data = new base::RefCountedBytes(data);
backend_->MergeFavicon(
page_url, icon_url2, chrome::FAVICON, bitmap_data, kSmallSize);
// The existing favicon bitmaps should be copied over to the newly created
// favicon at |icon_url2|. |page_url| should solely be mapped to |icon_url2|.
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_url2, icon_mappings[0].icon_url);
favicon_bitmaps.clear();
EXPECT_TRUE(GetSortedFaviconBitmaps(icon_mappings[0].icon_id,
&favicon_bitmaps));
EXPECT_EQ(base::Time(), favicon_bitmaps[0].last_updated);
EXPECT_TRUE(BitmapDataEqual('c', favicon_bitmaps[0].bitmap_data));
EXPECT_EQ(kTinySize, favicon_bitmaps[0].pixel_size);
// The favicon being merged should take precedence over the preexisting
// favicon bitmaps.
EXPECT_NE(base::Time(), favicon_bitmaps[1].last_updated);
EXPECT_TRUE(BitmapDataEqual('d', favicon_bitmaps[1].bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmaps[1].pixel_size);
// A notification should have been broadcast for each call to SetFavicons()
// and MergeFavicon().
EXPECT_EQ(4, num_broadcasted_notifications());
}
// Test calling MergeFavicon() when |icon_url| is known to the database but not
// mapped to |page_url|.
TEST_F(HistoryBackendTest, MergeFaviconIconURLMappedToDifferentPageURL) {
GURL page_url1("http://www.google.com");
GURL page_url2("http://news.google.com");
GURL page_url3("http://maps.google.com");
GURL icon_url("http:/www.google.com/favicon.ico");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url, GetSizesSmall(),
&favicon_bitmap_data);
backend_->SetFavicons(page_url1, chrome::FAVICON, favicon_bitmap_data);
// Test initial state.
std::vector<IconMapping> icon_mappings;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_url, icon_mappings[0].icon_url);
FaviconBitmap favicon_bitmap;
EXPECT_TRUE(GetOnlyFaviconBitmap(icon_mappings[0].icon_id, &favicon_bitmap));
EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
// 1) Merge in an identical favicon bitmap data but for a different page URL.
std::vector<unsigned char> data;
data.push_back('a');
scoped_refptr<base::RefCountedBytes> bitmap_data(
new base::RefCountedBytes(data));
backend_->MergeFavicon(
page_url2, icon_url, chrome::FAVICON, bitmap_data, kSmallSize);
chrome::FaviconID favicon_id =
backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
icon_url, chrome::FAVICON, NULL);
EXPECT_NE(0, favicon_id);
EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
EXPECT_TRUE(BitmapDataEqual('a', favicon_bitmap.bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
// 2) Merging a favicon bitmap with different bitmap data for the same icon
// URL should overwrite the small favicon bitmap at |icon_url|.
bitmap_data->data()[0] = 'b';
backend_->MergeFavicon(
page_url3, icon_url, chrome::FAVICON, bitmap_data, kSmallSize);
favicon_id = backend_->thumbnail_db_->GetFaviconIDForFaviconURL(
icon_url, chrome::FAVICON, NULL);
EXPECT_NE(0, favicon_id);
EXPECT_TRUE(GetOnlyFaviconBitmap(favicon_id, &favicon_bitmap));
EXPECT_NE(base::Time(), favicon_bitmap.last_updated);
EXPECT_TRUE(BitmapDataEqual('b', favicon_bitmap.bitmap_data));
EXPECT_EQ(kSmallSize, favicon_bitmap.pixel_size);
// |icon_url| should be mapped to all three page URLs.
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url2,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
icon_mappings.clear();
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url3,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(favicon_id, icon_mappings[0].icon_id);
// A notification should have been broadcast for each call to SetFavicons()
// and MergeFavicon().
EXPECT_EQ(3, num_broadcasted_notifications());
}
// Test that MergeFavicon() does not add more than
// |kMaxFaviconBitmapsPerIconURL| to a favicon.
TEST_F(HistoryBackendTest, MergeFaviconMaxFaviconBitmapsPerIconURL) {
GURL page_url("http://www.google.com");
std::string icon_url_string("http://www.google.com/favicon.ico");
size_t replace_index = icon_url_string.size() - 1;
std::vector<unsigned char> data;
data.push_back('a');
scoped_refptr<base::RefCountedMemory> bitmap_data =
base::RefCountedBytes::TakeVector(&data);
int pixel_size = 1;
for (size_t i = 0; i < kMaxFaviconBitmapsPerIconURL + 1; ++i) {
icon_url_string[replace_index] = '0' + i;
GURL icon_url(icon_url_string);
backend_->MergeFavicon(page_url, icon_url, chrome::FAVICON, bitmap_data,
gfx::Size(pixel_size, pixel_size));
++pixel_size;
}
// There should be a single favicon mapped to |page_url| with exactly
// kMaxFaviconBitmapsPerIconURL favicon bitmaps.
std::vector<IconMapping> icon_mappings;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
std::vector<FaviconBitmap> favicon_bitmaps;
EXPECT_TRUE(backend_->thumbnail_db_->GetFaviconBitmaps(
icon_mappings[0].icon_id, &favicon_bitmaps));
EXPECT_EQ(kMaxFaviconBitmapsPerIconURL, favicon_bitmaps.size());
}
// Tests that the favicon set by MergeFavicon() shows up in the result of
// GetFaviconsForURL().
TEST_F(HistoryBackendTest, MergeFaviconShowsUpInGetFaviconsForURLResult) {
GURL page_url("http://www.google.com");
GURL icon_url("http://www.google.com/favicon.ico");
GURL merged_icon_url("http://wwww.google.com/favicon2.ico");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url, GetSizesSmallAndLarge(),
&favicon_bitmap_data);
// Set some preexisting favicons for |page_url|.
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
// Merge small favicon.
std::vector<unsigned char> data;
data.push_back('c');
scoped_refptr<base::RefCountedBytes> bitmap_data(
new base::RefCountedBytes(data));
backend_->MergeFavicon(
page_url, merged_icon_url, chrome::FAVICON, bitmap_data, kSmallSize);
// Request favicon bitmaps for both 1x and 2x to simulate request done by
// BookmarkModel::GetFavicon().
std::vector<chrome::FaviconBitmapResult> bitmap_results;
backend_->GetFaviconsForURL(page_url, chrome::FAVICON, kSmallSize.width(),
GetScaleFactors1x2x(), &bitmap_results);
EXPECT_EQ(2u, bitmap_results.size());
const chrome::FaviconBitmapResult& first_result = bitmap_results[0];
const chrome::FaviconBitmapResult& result =
(first_result.pixel_size == kSmallSize) ? first_result
: bitmap_results[1];
EXPECT_TRUE(BitmapDataEqual('c', result.bitmap_data));
}
// Tests GetFaviconsForURL with icon_types priority,
TEST_F(HistoryBackendTest, TestGetFaviconsForURLWithIconTypesPriority) {
GURL page_url("http://www.google.com");
GURL icon_url("http://www.google.com/favicon.ico");
GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
std::vector<gfx::Size> favicon_size;
favicon_size.push_back(gfx::Size(16, 16));
favicon_size.push_back(gfx::Size(32, 32));
GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data);
ASSERT_EQ(2u, favicon_bitmap_data.size());
std::vector<chrome::FaviconBitmapData> touch_icon_bitmap_data;
std::vector<gfx::Size> touch_icon_size;
touch_icon_size.push_back(gfx::Size(64, 64));
GenerateFaviconBitmapData(icon_url, touch_icon_size, &touch_icon_bitmap_data);
ASSERT_EQ(1u, touch_icon_bitmap_data.size());
// Set some preexisting favicons for |page_url|.
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
backend_->SetFavicons(page_url, chrome::TOUCH_ICON, touch_icon_bitmap_data);
chrome::FaviconBitmapResult result;
std::vector<int> icon_types;
icon_types.push_back(chrome::FAVICON);
icon_types.push_back(chrome::TOUCH_ICON);
backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
// Verify the result icon is 32x32 favicon.
EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
EXPECT_EQ(chrome::FAVICON, result.icon_type);
// Change Minimal size to 32x32 and verify the 64x64 touch icon returned.
backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
EXPECT_EQ(gfx::Size(64, 64), result.pixel_size);
EXPECT_EQ(chrome::TOUCH_ICON, result.icon_type);
}
// Test the the first types of icon is returned if its size equal to the
// second types icon.
TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFavicon) {
GURL page_url("http://www.google.com");
GURL icon_url("http://www.google.com/favicon.ico");
GURL touch_icon_url("http://wwww.google.com/touch_icon.ico");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
std::vector<gfx::Size> favicon_size;
favicon_size.push_back(gfx::Size(16, 16));
favicon_size.push_back(gfx::Size(32, 32));
GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data);
ASSERT_EQ(2u, favicon_bitmap_data.size());
std::vector<chrome::FaviconBitmapData> touch_icon_bitmap_data;
std::vector<gfx::Size> touch_icon_size;
touch_icon_size.push_back(gfx::Size(32, 32));
GenerateFaviconBitmapData(icon_url, touch_icon_size, &touch_icon_bitmap_data);
ASSERT_EQ(1u, touch_icon_bitmap_data.size());
// Set some preexisting favicons for |page_url|.
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
backend_->SetFavicons(page_url, chrome::TOUCH_ICON, touch_icon_bitmap_data);
chrome::FaviconBitmapResult result;
std::vector<int> icon_types;
icon_types.push_back(chrome::FAVICON);
icon_types.push_back(chrome::TOUCH_ICON);
backend_->GetLargestFaviconForURL(page_url, icon_types, 16, &result);
// Verify the result icon is 32x32 favicon.
EXPECT_EQ(gfx::Size(32, 32), result.pixel_size);
EXPECT_EQ(chrome::FAVICON, result.icon_type);
// Change minimal size to 32x32 and verify the 32x32 favicon returned.
chrome::FaviconBitmapResult result1;
backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result1);
EXPECT_EQ(gfx::Size(32, 32), result1.pixel_size);
EXPECT_EQ(chrome::FAVICON, result1.icon_type);
}
// Test the favicon is returned if its size is smaller than minimal size,
// because it is only one available.
TEST_F(HistoryBackendTest, TestGetFaviconsForURLReturnFaviconEvenItSmaller) {
GURL page_url("http://www.google.com");
GURL icon_url("http://www.google.com/favicon.ico");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
std::vector<gfx::Size> favicon_size;
favicon_size.push_back(gfx::Size(16, 16));
GenerateFaviconBitmapData(icon_url, favicon_size, &favicon_bitmap_data);
ASSERT_EQ(1u, favicon_bitmap_data.size());
// Set preexisting favicons for |page_url|.
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
chrome::FaviconBitmapResult result;
std::vector<int> icon_types;
icon_types.push_back(chrome::FAVICON);
icon_types.push_back(chrome::TOUCH_ICON);
backend_->GetLargestFaviconForURL(page_url, icon_types, 32, &result);
// Verify 16x16 icon is returned, even it small than minimal_size.
EXPECT_EQ(gfx::Size(16, 16), result.pixel_size);
EXPECT_EQ(chrome::FAVICON, result.icon_type);
}
// Test UpdateFaviconMapingsAndFetch() when multiple icon types are passed in.
TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchMultipleIconTypes) {
GURL page_url1("http://www.google.com");
GURL page_url2("http://news.google.com");
GURL page_url3("http://mail.google.com");
GURL icon_urla("http://www.google.com/favicon1.ico");
GURL icon_urlb("http://www.google.com/favicon2.ico");
GURL icon_urlc("http://www.google.com/favicon3.ico");
// |page_url1| is mapped to |icon_urla| which if of type TOUCH_ICON.
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_urla, GetSizesSmall(), &favicon_bitmap_data);
backend_->SetFavicons(page_url1, chrome::TOUCH_ICON, favicon_bitmap_data);
// |page_url2| is mapped to |icon_urlb| and |icon_urlc| which are of type
// TOUCH_PRECOMPOSED_ICON.
GenerateFaviconBitmapData(icon_urlb, GetSizesSmall(), icon_urlc,
GetSizesSmall(), &favicon_bitmap_data);
backend_->SetFavicons(
page_url2, chrome::TOUCH_PRECOMPOSED_ICON, favicon_bitmap_data);
std::vector<GURL> icon_urls;
icon_urls.push_back(icon_urla);
icon_urls.push_back(icon_urlb);
icon_urls.push_back(icon_urlc);
std::vector<chrome::FaviconBitmapResult> bitmap_results;
backend_->UpdateFaviconMappingsAndFetch(
page_url3,
icon_urls,
(chrome::TOUCH_ICON | chrome::TOUCH_PRECOMPOSED_ICON),
kSmallSize.width(),
GetScaleFactors1x2x(),
&bitmap_results);
// |page_url1| and |page_url2| should still be mapped to the same icon URLs.
std::vector<IconMapping> icon_mappings;
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(page_url1,
&icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
EXPECT_EQ(icon_urla, icon_mappings[0].icon_url);
EXPECT_EQ(chrome::TOUCH_ICON, icon_mappings[0].icon_type);
icon_mappings.clear();
EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url2, &icon_mappings));
EXPECT_EQ(2u, icon_mappings.size());
EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
EXPECT_EQ(chrome::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
EXPECT_EQ(icon_urlc, icon_mappings[1].icon_url);
EXPECT_EQ(chrome::TOUCH_PRECOMPOSED_ICON, icon_mappings[1].icon_type);
// |page_url3| should be mapped only to |icon_urlb| and |icon_urlc| as
// TOUCH_PRECOMPOSED_ICON is the largest IconType.
icon_mappings.clear();
EXPECT_TRUE(GetSortedIconMappingsForPageURL(page_url3, &icon_mappings));
EXPECT_EQ(2u, icon_mappings.size());
EXPECT_EQ(icon_urlb, icon_mappings[0].icon_url);
EXPECT_EQ(chrome::TOUCH_PRECOMPOSED_ICON, icon_mappings[0].icon_type);
EXPECT_EQ(icon_urlc, icon_mappings[1].icon_url);
EXPECT_EQ(chrome::TOUCH_PRECOMPOSED_ICON, icon_mappings[1].icon_type);
}
// Test the results of GetFaviconsFromDB() when there are no found
// favicons.
TEST_F(HistoryBackendTest, GetFaviconsFromDBEmpty) {
const GURL page_url("http://www.google.com/");
std::vector<chrome::FaviconBitmapResult> bitmap_results;
EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url, chrome::FAVICON,
kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results));
EXPECT_TRUE(bitmap_results.empty());
}
// Test the results of GetFaviconsFromDB() when there are matching favicons
// but there are no associated favicon bitmaps.
TEST_F(HistoryBackendTest, GetFaviconsFromDBNoFaviconBitmaps) {
const GURL page_url("http://www.google.com/");
const GURL icon_url("http://www.google.com/icon1");
chrome::FaviconID icon_id = backend_->thumbnail_db_->AddFavicon(
icon_url, chrome::FAVICON);
EXPECT_NE(0, icon_id);
EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
EXPECT_FALSE(backend_->GetFaviconsFromDB(page_url, chrome::FAVICON,
kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
EXPECT_TRUE(bitmap_results_out.empty());
}
// Test that GetFaviconsFromDB() returns results for the bitmaps which most
// closely match the passed in desired size and scale factors.
TEST_F(HistoryBackendTest, GetFaviconsFromDBSelectClosestMatch) {
const GURL page_url("http://www.google.com/");
const GURL icon_url("http://www.google.com/icon1");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url, GetSizesTinySmallAndLarge(),
&favicon_bitmap_data);
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
chrome::FAVICON,
kSmallSize.width(),
GetScaleFactors1x2x(),
&bitmap_results_out));
// The bitmap data for the small and large bitmaps should be returned as their
// sizes match exactly.
EXPECT_EQ(2u, bitmap_results_out.size());
// No required order for results.
if (bitmap_results_out[0].pixel_size == kLargeSize) {
chrome::FaviconBitmapResult tmp_result = bitmap_results_out[0];
bitmap_results_out[0] = bitmap_results_out[1];
bitmap_results_out[1] = tmp_result;
}
EXPECT_FALSE(bitmap_results_out[0].expired);
EXPECT_TRUE(BitmapDataEqual('b', bitmap_results_out[0].bitmap_data));
EXPECT_EQ(kSmallSize, bitmap_results_out[0].pixel_size);
EXPECT_EQ(icon_url, bitmap_results_out[0].icon_url);
EXPECT_EQ(chrome::FAVICON, bitmap_results_out[0].icon_type);
EXPECT_FALSE(bitmap_results_out[1].expired);
EXPECT_TRUE(BitmapDataEqual('c', bitmap_results_out[1].bitmap_data));
EXPECT_EQ(kLargeSize, bitmap_results_out[1].pixel_size);
EXPECT_EQ(icon_url, bitmap_results_out[1].icon_url);
EXPECT_EQ(chrome::FAVICON, bitmap_results_out[1].icon_type);
}
// Test that GetFaviconsFromDB() returns results from the icon URL whose
// bitmaps most closely match the passed in desired size and scale factors.
TEST_F(HistoryBackendTest, GetFaviconsFromDBSingleIconURL) {
const GURL page_url("http://www.google.com/");
const GURL icon_url1("http://www.google.com/icon1");
const GURL icon_url2("http://www.google.com/icon2");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url1, GetSizesSmall(), icon_url2,
GetSizesLarge(), &favicon_bitmap_data);
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url,
chrome::FAVICON,
kSmallSize.width(),
GetScaleFactors1x2x(),
&bitmap_results_out));
// The results should have results for the icon URL with the large bitmap as
// downscaling is preferred to upscaling.
EXPECT_EQ(1u, bitmap_results_out.size());
EXPECT_EQ(kLargeSize, bitmap_results_out[0].pixel_size);
EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url);
}
// Test the results of GetFaviconsFromDB() when called with different
// |icon_types|.
TEST_F(HistoryBackendTest, GetFaviconsFromDBIconType) {
const GURL page_url("http://www.google.com/");
const GURL icon_url1("http://www.google.com/icon1.png");
const GURL icon_url2("http://www.google.com/icon2.png");
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url1, GetSizesSmall(), &favicon_bitmap_data);
backend_->SetFavicons(page_url, chrome::FAVICON, favicon_bitmap_data);
GenerateFaviconBitmapData(icon_url2, GetSizesSmall(), &favicon_bitmap_data);
backend_->SetFavicons(page_url, chrome::TOUCH_ICON, favicon_bitmap_data);
std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, chrome::FAVICON,
kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
EXPECT_EQ(1u, bitmap_results_out.size());
EXPECT_EQ(chrome::FAVICON, bitmap_results_out[0].icon_type);
EXPECT_EQ(icon_url1, bitmap_results_out[0].icon_url);
bitmap_results_out.clear();
EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, chrome::TOUCH_ICON,
kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
EXPECT_EQ(1u, bitmap_results_out.size());
EXPECT_EQ(chrome::TOUCH_ICON, bitmap_results_out[0].icon_type);
EXPECT_EQ(icon_url2, bitmap_results_out[0].icon_url);
}
// Test that GetFaviconsFromDB() correctly sets the expired flag for bitmap
// reults.
TEST_F(HistoryBackendTest, GetFaviconsFromDBExpired) {
const GURL page_url("http://www.google.com/");
const GURL icon_url("http://www.google.com/icon.png");
std::vector<unsigned char> data;
data.push_back('a');
scoped_refptr<base::RefCountedBytes> bitmap_data(
base::RefCountedBytes::TakeVector(&data));
base::Time last_updated = base::Time::FromTimeT(0);
chrome::FaviconID icon_id =
backend_->thumbnail_db_->AddFavicon(icon_url,
chrome::FAVICON,
bitmap_data,
last_updated,
kSmallSize);
EXPECT_NE(0, icon_id);
EXPECT_NE(0, backend_->thumbnail_db_->AddIconMapping(page_url, icon_id));
std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
EXPECT_TRUE(backend_->GetFaviconsFromDB(page_url, chrome::FAVICON,
kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
EXPECT_EQ(1u, bitmap_results_out.size());
EXPECT_TRUE(bitmap_results_out[0].expired);
}
// Check that UpdateFaviconMappingsAndFetch() call back to the UI when there is
// no valid thumbnail database.
TEST_F(HistoryBackendTest, UpdateFaviconMappingsAndFetchNoDB) {
// Make the thumbnail database invalid.
backend_->thumbnail_db_.reset();
std::vector<chrome::FaviconBitmapResult> bitmap_results;
backend_->UpdateFaviconMappingsAndFetch(
GURL(), std::vector<GURL>(), chrome::FAVICON, kSmallSize.width(),
GetScaleFactors1x2x(), &bitmap_results);
EXPECT_TRUE(bitmap_results.empty());
}
TEST_F(HistoryBackendTest, CloneFaviconIsRestrictedToSameDomain) {
const GURL url("http://www.google.com/");
const GURL same_domain_url("http://www.google.com/subdir/index.html");
const GURL foreign_domain_url("http://www.not-google.com/");
const GURL icon_url("http://www.google.com/icon.png");
// Add a favicon
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
GenerateFaviconBitmapData(icon_url, GetSizesSmall(), &favicon_bitmap_data);
backend_->SetFavicons(url, chrome::FAVICON, favicon_bitmap_data);
EXPECT_TRUE(backend_->thumbnail_db_->GetIconMappingsForPageURL(
url, chrome::FAVICON, NULL));
// Validate starting state.
std::vector<chrome::FaviconBitmapResult> bitmap_results_out;
EXPECT_TRUE(backend_->GetFaviconsFromDB(url, chrome::FAVICON,
kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
EXPECT_FALSE(backend_->GetFaviconsFromDB(same_domain_url, chrome::FAVICON,
kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
EXPECT_FALSE(backend_->GetFaviconsFromDB(foreign_domain_url, chrome::FAVICON,
kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
// Same-domain cloning should work.
backend_->CloneFavicons(url, same_domain_url);
EXPECT_TRUE(backend_->GetFaviconsFromDB(same_domain_url, chrome::FAVICON,
kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
// Foreign-domain cloning is forbidden.
backend_->CloneFavicons(url, foreign_domain_url);
EXPECT_FALSE(backend_->GetFaviconsFromDB(foreign_domain_url, chrome::FAVICON,
kSmallSize.width(), GetScaleFactors1x2x(), &bitmap_results_out));
}
TEST_F(HistoryBackendTest, QueryFilteredURLs) {
const char* google = "http://www.google.com/";
const char* yahoo = "http://www.yahoo.com/";
const char* yahoo_sports = "http://sports.yahoo.com/";
const char* yahoo_sports_with_article1 =
"http://sports.yahoo.com/article1.htm";
const char* yahoo_sports_with_article2 =
"http://sports.yahoo.com/article2.htm";
const char* yahoo_sports_soccer = "http://sports.yahoo.com/soccer";
const char* apple = "http://www.apple.com/";
// Clear all history.
backend_->DeleteAllHistory();
Time tested_time = Time::Now().LocalMidnight() +
base::TimeDelta::FromHours(4);
base::TimeDelta half_an_hour = base::TimeDelta::FromMinutes(30);
base::TimeDelta one_hour = base::TimeDelta::FromHours(1);
base::TimeDelta one_day = base::TimeDelta::FromDays(1);
const content::PageTransition kTypedTransition =
content::PAGE_TRANSITION_TYPED;
const content::PageTransition kKeywordGeneratedTransition =
content::PAGE_TRANSITION_KEYWORD_GENERATED;
const char* redirect_sequence[2];
redirect_sequence[1] = NULL;
redirect_sequence[0] = google;
AddRedirectChainWithTransitionAndTime(
redirect_sequence, 0, kTypedTransition,
tested_time - one_day - half_an_hour * 2);
AddRedirectChainWithTransitionAndTime(
redirect_sequence, 0,
kTypedTransition, tested_time - one_day);
AddRedirectChainWithTransitionAndTime(
redirect_sequence, 0,
kTypedTransition, tested_time - half_an_hour / 2);
AddRedirectChainWithTransitionAndTime(
redirect_sequence, 0,
kTypedTransition, tested_time);
// Add a visit with a transition that will make sure that no segment gets
// created for this page (so the subsequent entries will have different URLIDs
// and SegmentIDs).
redirect_sequence[0] = apple;
AddRedirectChainWithTransitionAndTime(
redirect_sequence, 0, kKeywordGeneratedTransition,
tested_time - one_day + one_hour * 6);
redirect_sequence[0] = yahoo;
AddRedirectChainWithTransitionAndTime(
redirect_sequence, 0, kTypedTransition,
tested_time - one_day + half_an_hour);
AddRedirectChainWithTransitionAndTime(
redirect_sequence, 0, kTypedTransition,
tested_time - one_day + half_an_hour * 2);
redirect_sequence[0] = yahoo_sports;
AddRedirectChainWithTransitionAndTime(
redirect_sequence, 0, kTypedTransition,
tested_time - one_day - half_an_hour * 2);
AddRedirectChainWithTransitionAndTime(
redirect_sequence, 0, kTypedTransition,
tested_time - one_day);
int transition1, transition2;
AddClientRedirect(GURL(yahoo_sports), GURL(yahoo_sports_with_article1), false,
tested_time - one_day + half_an_hour,
&transition1, &transition2);
AddClientRedirect(GURL(yahoo_sports_with_article1),
GURL(yahoo_sports_with_article2),
false,
tested_time - one_day + half_an_hour * 2,
&transition1, &transition2);
redirect_sequence[0] = yahoo_sports_soccer;
AddRedirectChainWithTransitionAndTime(redirect_sequence, 0,
kTypedTransition,
tested_time - half_an_hour);
backend_->Commit();
scoped_refptr<QueryFilteredURLsRequest> request1 =
new history::QueryFilteredURLsRequest(
base::Bind(&HistoryBackendTest::OnQueryFiltered,
base::Unretained(static_cast<HistoryBackendTest*>(this))));
HistoryBackendCancelableRequest cancellable_request;
cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
request1.get());
VisitFilter filter;
// Time limit is |tested_time| +/- 45 min.
base::TimeDelta three_quarters_of_an_hour = base::TimeDelta::FromMinutes(45);
filter.SetFilterTime(tested_time);
filter.SetFilterWidth(three_quarters_of_an_hour);
backend_->QueryFilteredURLs(request1, 100, filter, false);
ASSERT_EQ(4U, get_filtered_list().size());
EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
EXPECT_EQ(std::string(yahoo_sports_soccer),
get_filtered_list()[1].url.spec());
EXPECT_EQ(std::string(yahoo), get_filtered_list()[2].url.spec());
EXPECT_EQ(std::string(yahoo_sports),
get_filtered_list()[3].url.spec());
// Time limit is between |tested_time| and |tested_time| + 2 hours.
scoped_refptr<QueryFilteredURLsRequest> request2 =
new history::QueryFilteredURLsRequest(
base::Bind(&HistoryBackendTest::OnQueryFiltered,
base::Unretained(static_cast<HistoryBackendTest*>(this))));
cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
request2.get());
filter.SetFilterTime(tested_time + one_hour);
filter.SetFilterWidth(one_hour);
backend_->QueryFilteredURLs(request2, 100, filter, false);
ASSERT_EQ(3U, get_filtered_list().size());
EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
EXPECT_EQ(std::string(yahoo), get_filtered_list()[1].url.spec());
EXPECT_EQ(std::string(yahoo_sports), get_filtered_list()[2].url.spec());
// Time limit is between |tested_time| - 2 hours and |tested_time|.
scoped_refptr<QueryFilteredURLsRequest> request3 =
new history::QueryFilteredURLsRequest(
base::Bind(&HistoryBackendTest::OnQueryFiltered,
base::Unretained(static_cast<HistoryBackendTest*>(this))));
cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
request3.get());
filter.SetFilterTime(tested_time - one_hour);
filter.SetFilterWidth(one_hour);
backend_->QueryFilteredURLs(request3, 100, filter, false);
ASSERT_EQ(3U, get_filtered_list().size());
EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
EXPECT_EQ(std::string(yahoo_sports_soccer),
get_filtered_list()[1].url.spec());
EXPECT_EQ(std::string(yahoo_sports), get_filtered_list()[2].url.spec());
filter.ClearFilters();
base::Time::Exploded exploded_time;
tested_time.LocalExplode(&exploded_time);
// Today.
scoped_refptr<QueryFilteredURLsRequest> request4 =
new history::QueryFilteredURLsRequest(
base::Bind(&HistoryBackendTest::OnQueryFiltered,
base::Unretained(static_cast<HistoryBackendTest*>(this))));
cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
request4.get());
filter.SetFilterTime(tested_time);
filter.SetDayOfTheWeekFilter(static_cast<int>(exploded_time.day_of_week));
backend_->QueryFilteredURLs(request4, 100, filter, false);
ASSERT_EQ(2U, get_filtered_list().size());
EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
EXPECT_EQ(std::string(yahoo_sports_soccer),
get_filtered_list()[1].url.spec());
// Today + time limit - only yahoo_sports_soccer should fit.
scoped_refptr<QueryFilteredURLsRequest> request5 =
new history::QueryFilteredURLsRequest(
base::Bind(&HistoryBackendTest::OnQueryFiltered,
base::Unretained(static_cast<HistoryBackendTest*>(this))));
cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
request5.get());
filter.SetFilterTime(tested_time - base::TimeDelta::FromMinutes(40));
filter.SetFilterWidth(base::TimeDelta::FromMinutes(20));
backend_->QueryFilteredURLs(request5, 100, filter, false);
ASSERT_EQ(1U, get_filtered_list().size());
EXPECT_EQ(std::string(yahoo_sports_soccer),
get_filtered_list()[0].url.spec());
// Make sure we get debug data if we request it.
scoped_refptr<QueryFilteredURLsRequest> request6 =
new history::QueryFilteredURLsRequest(
base::Bind(&HistoryBackendTest::OnQueryFiltered,
base::Unretained(static_cast<HistoryBackendTest*>(this))));
cancellable_request.MockScheduleOfRequest<QueryFilteredURLsRequest>(
request6.get());
filter.SetFilterTime(tested_time);
filter.SetFilterWidth(one_hour * 2);
backend_->QueryFilteredURLs(request6, 100, filter, true);
// If the SegmentID is used by QueryFilteredURLs when generating the debug
// data instead of the URLID, the |total_visits| for the |yahoo_sports_soccer|
// entry will be zero instead of 1.
ASSERT_GE(get_filtered_list().size(), 2U);
EXPECT_EQ(std::string(google), get_filtered_list()[0].url.spec());
EXPECT_EQ(std::string(yahoo_sports_soccer),
get_filtered_list()[1].url.spec());
EXPECT_EQ(4U, get_filtered_list()[0].extended_info.total_visits);
EXPECT_EQ(1U, get_filtered_list()[1].extended_info.total_visits);
}
TEST_F(HistoryBackendTest, UpdateVisitDuration) {
// This unit test will test adding and deleting visit details information.
ASSERT_TRUE(backend_.get());
GURL url1("http://www.cnn.com");
std::vector<VisitInfo> visit_info1, visit_info2;
Time start_ts = Time::Now() - base::TimeDelta::FromDays(5);
Time end_ts = start_ts + base::TimeDelta::FromDays(2);
visit_info1.push_back(VisitInfo(start_ts, content::PAGE_TRANSITION_LINK));
GURL url2("http://www.example.com");
visit_info2.push_back(VisitInfo(Time::Now() - base::TimeDelta::FromDays(10),
content::PAGE_TRANSITION_LINK));
// Clear all history.
backend_->DeleteAllHistory();
// Add the visits.
backend_->AddVisits(url1, visit_info1, history::SOURCE_BROWSED);
backend_->AddVisits(url2, visit_info2, history::SOURCE_BROWSED);
// Verify the entries for both visits were added in visit_details.
VisitVector visits1, visits2;
URLRow row;
URLID url_id1 = backend_->db()->GetRowForURL(url1, &row);
ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
ASSERT_EQ(1U, visits1.size());
EXPECT_EQ(0, visits1[0].visit_duration.ToInternalValue());
URLID url_id2 = backend_->db()->GetRowForURL(url2, &row);
ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id2, &visits2));
ASSERT_EQ(1U, visits2.size());
EXPECT_EQ(0, visits2[0].visit_duration.ToInternalValue());
// Update the visit to cnn.com.
backend_->UpdateVisitDuration(visits1[0].visit_id, end_ts);
// Check the duration for visiting cnn.com was correctly updated.
ASSERT_TRUE(backend_->db()->GetVisitsForURL(url_id1, &visits1));
ASSERT_EQ(1U, visits1.size());
base::TimeDelta expected_duration = end_ts - start_ts;
EXPECT_EQ(expected_duration.ToInternalValue(),
visits1[0].visit_duration.ToInternalValue());
// Remove the visit to cnn.com.
ASSERT_TRUE(backend_->RemoveVisits(visits1));
}
// Test for migration of adding visit_duration column.
TEST_F(HistoryBackendTest, MigrationVisitDuration) {
ASSERT_TRUE(backend_.get());
backend_->Closing();
backend_ = NULL;
base::FilePath old_history_path, old_history, old_archived;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &old_history_path));
old_history_path = old_history_path.AppendASCII("History");
old_history = old_history_path.AppendASCII("HistoryNoDuration");
old_archived = old_history_path.AppendASCII("ArchivedNoDuration");
// Copy history database file to current directory so that it will be deleted
// in Teardown.
base::FilePath new_history_path(getTestDir());
base::DeleteFile(new_history_path, true);
base::CreateDirectory(new_history_path);
base::FilePath new_history_file =
new_history_path.Append(chrome::kHistoryFilename);
base::FilePath new_archived_file =
new_history_path.Append(chrome::kArchivedHistoryFilename);
ASSERT_TRUE(base::CopyFile(old_history, new_history_file));
ASSERT_TRUE(base::CopyFile(old_archived, new_archived_file));
backend_ = new HistoryBackend(new_history_path,
0,
new HistoryBackendTestDelegate(this),
&bookmark_model_);
backend_->Init(std::string(), false);
backend_->Closing();
backend_ = NULL;
// Now both history and archived_history databases should already be migrated.
// Check version in history database 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_duration column in visits table is created and set to 0.
s.Assign(db.GetUniqueStatement(
"SELECT visit_duration FROM visits LIMIT 1"));
ASSERT_TRUE(s.Step());
EXPECT_EQ(0, s.ColumnInt(0));
// Repeat version and visit_duration checks in archived history database
// also.
cur_version = ArchivedDatabase::GetCurrentVersion();
sql::Connection archived_db;
ASSERT_TRUE(archived_db.Open(new_archived_file));
sql::Statement s1(archived_db.GetUniqueStatement(
"SELECT value FROM meta WHERE key = 'version'"));
ASSERT_TRUE(s1.Step());
file_version = s1.ColumnInt(0);
EXPECT_EQ(cur_version, file_version);
// Check visit_duration column in visits table is created and set to 0.
s1.Assign(archived_db.GetUniqueStatement(
"SELECT visit_duration FROM visits LIMIT 1"));
ASSERT_TRUE(s1.Step());
EXPECT_EQ(0, s1.ColumnInt(0));
}
TEST_F(HistoryBackendTest, AddPageNoVisitForBookmark) {
ASSERT_TRUE(backend_.get());
GURL url("http://www.google.com");
base::string16 title(UTF8ToUTF16("Bookmark title"));
backend_->AddPageNoVisitForBookmark(url, title);
URLRow row;
backend_->GetURL(url, &row);
EXPECT_EQ(url, row.url());
EXPECT_EQ(title, row.title());
EXPECT_EQ(0, row.visit_count());
backend_->DeleteURL(url);
backend_->AddPageNoVisitForBookmark(url, base::string16());
backend_->GetURL(url, &row);
EXPECT_EQ(url, row.url());
EXPECT_EQ(UTF8ToUTF16(url.spec()), row.title());
EXPECT_EQ(0, row.visit_count());
}
TEST_F(HistoryBackendTest, ExpireHistoryForTimes) {
ASSERT_TRUE(backend_.get());
HistoryAddPageArgs args[10];
for (size_t i = 0; i < arraysize(args); ++i) {
args[i].url = GURL("http://example" +
std::string((i % 2 == 0 ? ".com" : ".net")));
args[i].time = base::Time::FromInternalValue(i);
backend_->AddPage(args[i]);
}
EXPECT_EQ(base::Time(), backend_->GetFirstRecordedTimeForTest());
URLRow row;
for (size_t i = 0; i < arraysize(args); ++i) {
EXPECT_TRUE(backend_->GetURL(args[i].url, &row));
}
std::set<base::Time> times;
times.insert(args[5].time);
backend_->ExpireHistoryForTimes(times,
base::Time::FromInternalValue(2),
base::Time::FromInternalValue(8));
EXPECT_EQ(base::Time::FromInternalValue(0),
backend_->GetFirstRecordedTimeForTest());
// Visits to http://example.com are untouched.
VisitVector visit_vector;
EXPECT_TRUE(backend_->GetVisitsForURL(
backend_->db_->GetRowForURL(GURL("http://example.com"), NULL),
&visit_vector));
ASSERT_EQ(5u, visit_vector.size());
EXPECT_EQ(base::Time::FromInternalValue(0), visit_vector[0].visit_time);
EXPECT_EQ(base::Time::FromInternalValue(2), visit_vector[1].visit_time);
EXPECT_EQ(base::Time::FromInternalValue(4), visit_vector[2].visit_time);
EXPECT_EQ(base::Time::FromInternalValue(6), visit_vector[3].visit_time);
EXPECT_EQ(base::Time::FromInternalValue(8), visit_vector[4].visit_time);
// Visits to http://example.net between [2,8] are removed.
visit_vector.clear();
EXPECT_TRUE(backend_->GetVisitsForURL(
backend_->db_->GetRowForURL(GURL("http://example.net"), NULL),
&visit_vector));
ASSERT_EQ(2u, visit_vector.size());
EXPECT_EQ(base::Time::FromInternalValue(1), visit_vector[0].visit_time);
EXPECT_EQ(base::Time::FromInternalValue(9), visit_vector[1].visit_time);
EXPECT_EQ(base::Time::FromInternalValue(0),
backend_->GetFirstRecordedTimeForTest());
}
TEST_F(HistoryBackendTest, ExpireHistory) {
ASSERT_TRUE(backend_.get());
// Since history operations are dependent on the local timezone, make all
// entries relative to a fixed, local reference time.
base::Time reference_time = base::Time::UnixEpoch().LocalMidnight() +
base::TimeDelta::FromHours(12);
// Insert 4 entries into the database.
HistoryAddPageArgs args[4];
for (size_t i = 0; i < arraysize(args); ++i) {
args[i].url = GURL("http://example" + base::IntToString(i) + ".com");
args[i].time = reference_time + base::TimeDelta::FromDays(i);
backend_->AddPage(args[i]);
}
URLRow url_rows[4];
for (unsigned int i = 0; i < arraysize(args); ++i)
ASSERT_TRUE(backend_->GetURL(args[i].url, &url_rows[i]));
std::vector<ExpireHistoryArgs> expire_list;
VisitVector visits;
// Passing an empty map should be a no-op.
backend_->ExpireHistory(expire_list);
backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
EXPECT_EQ(4U, visits.size());
// Trying to delete an unknown URL with the time of the first visit should
// also be a no-op.
expire_list.resize(expire_list.size() + 1);
expire_list[0].SetTimeRangeForOneDay(args[0].time);
expire_list[0].urls.insert(GURL("http://google.does-not-exist"));
backend_->ExpireHistory(expire_list);
backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
EXPECT_EQ(4U, visits.size());
// Now add the first URL with the same time -- it should get deleted.
expire_list.back().urls.insert(url_rows[0].url());
backend_->ExpireHistory(expire_list);
backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
ASSERT_EQ(3U, visits.size());
EXPECT_EQ(visits[0].url_id, url_rows[1].id());
EXPECT_EQ(visits[1].url_id, url_rows[2].id());
EXPECT_EQ(visits[2].url_id, url_rows[3].id());
// The first recorded time should also get updated.
EXPECT_EQ(backend_->GetFirstRecordedTimeForTest(), args[1].time);
// Now delete the rest of the visits in one call.
for (unsigned int i = 1; i < arraysize(args); ++i) {
expire_list.resize(expire_list.size() + 1);
expire_list[i].SetTimeRangeForOneDay(args[i].time);
expire_list[i].urls.insert(args[i].url);
}
backend_->ExpireHistory(expire_list);
backend_->db()->GetAllVisitsInRange(base::Time(), base::Time(), 0, &visits);
ASSERT_EQ(0U, visits.size());
}
TEST_F(HistoryBackendTest, DeleteMatchingUrlsForKeyword) {
// Set up urls and keyword_search_terms
GURL url1("https://www.bing.com/?q=bar");
URLRow url_info1(url1);
url_info1.set_visit_count(0);
url_info1.set_typed_count(0);
url_info1.set_last_visit(Time());
url_info1.set_hidden(false);
const URLID url1_id = backend_->db()->AddURL(url_info1);
EXPECT_NE(0, url1_id);
TemplateURLID keyword_id = 1;
base::string16 keyword = UTF8ToUTF16("bar");
ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
url1_id, keyword_id, keyword));
GURL url2("https://www.google.com/?q=bar");
URLRow url_info2(url2);
url_info2.set_visit_count(0);
url_info2.set_typed_count(0);
url_info2.set_last_visit(Time());
url_info2.set_hidden(false);
const URLID url2_id = backend_->db()->AddURL(url_info2);
EXPECT_NE(0, url2_id);
TemplateURLID keyword_id2 = 2;
ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
url2_id, keyword_id2, keyword));
// Add another visit to the same URL
URLRow url_info3(url2);
url_info3.set_visit_count(0);
url_info3.set_typed_count(0);
url_info3.set_last_visit(Time());
url_info3.set_hidden(false);
const URLID url3_id = backend_->db()->AddURL(url_info3);
EXPECT_NE(0, url3_id);
ASSERT_TRUE(backend_->db()->SetKeywordSearchTermsForURL(
url3_id, keyword_id2, keyword));
// Test that deletion works correctly
backend_->DeleteMatchingURLsForKeyword(keyword_id2, keyword);
// Test that rows 2 and 3 are deleted, while 1 is intact
URLRow row;
EXPECT_TRUE(backend_->db()->GetURLRow(url1_id, &row));
EXPECT_EQ(url1.spec(), row.url().spec());
EXPECT_FALSE(backend_->db()->GetURLRow(url2_id, &row));
EXPECT_FALSE(backend_->db()->GetURLRow(url3_id, &row));
// Test that corresponding keyword search terms are deleted for rows 2 & 3,
// but not for row 1
EXPECT_TRUE(backend_->db()->GetKeywordSearchTermRow(url1_id, NULL));
EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url2_id, NULL));
EXPECT_FALSE(backend_->db()->GetKeywordSearchTermRow(url3_id, NULL));
}
class HistoryBackendSegmentDurationTest : public HistoryBackendTest {
public:
HistoryBackendSegmentDurationTest() {}
virtual void SetUp() {
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kTrackActiveVisitTime);
HistoryBackendTest::SetUp();
}
private:
DISALLOW_COPY_AND_ASSIGN(HistoryBackendSegmentDurationTest);
};
// Assertions around segment durations.
TEST_F(HistoryBackendSegmentDurationTest, SegmentDuration) {
const GURL url1("http://www.google.com");
const GURL url2("http://www.foo.com/m");
const std::string segment1(VisitSegmentDatabase::ComputeSegmentName(url1));
const std::string segment2(VisitSegmentDatabase::ComputeSegmentName(url2));
Time segment_time(VisitSegmentDatabase::SegmentTime(Time::Now()));
URLRow url_info1(url1);
url_info1.set_visit_count(0);
url_info1.set_typed_count(0);
url_info1.set_last_visit(segment_time);
url_info1.set_hidden(false);
const URLID url1_id = backend_->db()->AddURL(url_info1);
EXPECT_NE(0, url1_id);
URLRow url_info2(url2);
url_info2.set_visit_count(0);
url_info2.set_typed_count(0);
url_info2.set_last_visit(Time());
url_info2.set_hidden(false);
const URLID url2_id = backend_->db()->AddURL(url_info2);
EXPECT_NE(0, url2_id);
EXPECT_NE(url1_id, url2_id);
// Should not have any segments for the urls.
EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment1));
EXPECT_EQ(0, backend_->db()->GetSegmentNamed(segment2));
// Update the duration, which should implicitly create the segments.
const TimeDelta segment1_time_delta(TimeDelta::FromHours(1));
const TimeDelta segment2_time_delta(TimeDelta::FromHours(2));
backend_->IncreaseSegmentDuration(url1, segment_time, segment1_time_delta);
backend_->IncreaseSegmentDuration(url2, segment_time, segment2_time_delta);
// Get the ids of the segments that were created.
const SegmentID segment1_id = backend_->db()->GetSegmentNamed(segment1);
EXPECT_NE(0, segment1_id);
const SegmentID segment2_id = backend_->db()->GetSegmentNamed(segment2);
EXPECT_NE(0, segment2_id);
EXPECT_NE(segment1_id, segment2_id);
// Make sure the values made it to the db.
SegmentDurationID segment1_duration_id;
TimeDelta fetched_delta;
EXPECT_TRUE(backend_->db()->GetSegmentDuration(
segment1_id, segment_time, &segment1_duration_id,
&fetched_delta));
EXPECT_NE(0, segment1_duration_id);
EXPECT_EQ(segment1_time_delta.InHours(), fetched_delta.InHours());
SegmentDurationID segment2_duration_id;
EXPECT_TRUE(backend_->db()->GetSegmentDuration(
segment2_id, segment_time, &segment2_duration_id,
&fetched_delta));
EXPECT_NE(0, segment2_duration_id);
EXPECT_NE(segment1_duration_id, segment2_duration_id);
EXPECT_EQ(segment2_time_delta.InHours(), fetched_delta.InHours());
// Query by duration. |url2| should be first as it has a longer view time.
ScopedVector<PageUsageData> data;
backend_->db()->QuerySegmentDuration(segment_time, 10, &data.get());
ASSERT_EQ(2u, data.size());
EXPECT_EQ(url2.spec(), data[0]->GetURL().spec());
EXPECT_EQ(url2_id, data[0]->GetID());
EXPECT_EQ(segment2_time_delta.InHours(), data[0]->duration().InHours());
EXPECT_EQ(url1.spec(), data[1]->GetURL().spec());
EXPECT_EQ(url1_id, data[1]->GetID());
EXPECT_EQ(segment1_time_delta.InHours(), data[1]->duration().InHours());
}
// Simple test that removes a bookmark. This test exercises the code paths in
// History that block till bookmark bar model is loaded.
TEST_F(HistoryBackendTest, RemoveNotification) {
scoped_ptr<TestingProfile> profile(new TestingProfile());
ASSERT_TRUE(profile->CreateHistoryService(false, false));
profile->CreateBookmarkModel(true);
BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile.get());
test::WaitForBookmarkModelToLoad(model);
// Add a URL.
GURL url("http://www.google.com");
bookmark_utils::AddIfNotBookmarked(model, url, base::string16());
HistoryService* service = HistoryServiceFactory::GetForProfile(
profile.get(), Profile::EXPLICIT_ACCESS);
service->AddPage(
url, base::Time::Now(), NULL, 1, GURL(), RedirectList(),
content::PAGE_TRANSITION_TYPED, SOURCE_BROWSED, false);
// This won't actually delete the URL, rather it'll empty out the visits.
// This triggers blocking on the BookmarkModel.
service->DeleteURL(url);
}
// Test DeleteFTSIndexDatabases deletes expected files.
TEST_F(HistoryBackendTest, DeleteFTSIndexDatabases) {
ASSERT_TRUE(backend_.get());
base::FilePath history_path(getTestDir());
base::FilePath db1(history_path.AppendASCII("History Index 2013-05"));
base::FilePath db1_journal(db1.InsertBeforeExtensionASCII("-journal"));
base::FilePath db1_wal(db1.InsertBeforeExtensionASCII("-wal"));
base::FilePath db2_symlink(history_path.AppendASCII("History Index 2013-06"));
base::FilePath db2_actual(history_path.AppendASCII("Underlying DB"));
// Setup dummy index database files.
const char* data = "Dummy";
const size_t data_len = 5;
ASSERT_TRUE(file_util::WriteFile(db1, data, data_len));
ASSERT_TRUE(file_util::WriteFile(db1_journal, data, data_len));
ASSERT_TRUE(file_util::WriteFile(db1_wal, data, data_len));
ASSERT_TRUE(file_util::WriteFile(db2_actual, data, data_len));
#if defined(OS_POSIX)
EXPECT_TRUE(base::CreateSymbolicLink(db2_actual, db2_symlink));
#endif
// Delete all DTS index databases.
backend_->DeleteFTSIndexDatabases();
EXPECT_FALSE(base::PathExists(db1));
EXPECT_FALSE(base::PathExists(db1_wal));
EXPECT_FALSE(base::PathExists(db1_journal));
EXPECT_FALSE(base::PathExists(db2_symlink));
EXPECT_TRUE(base::PathExists(db2_actual)); // Symlinks shouldn't be followed.
}
} // namespace history