blob: 0d3d1c7aa02f91ef096aa7fe66b1d87d6d059900 [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/android/android_provider_backend.h"
#include <vector>
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted.h"
#include "base/strings/stringprintf.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_service.h"
#include "chrome/browser/bookmarks/bookmark_test_helpers.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/favicon/favicon_changed_details.h"
#include "chrome/browser/history/android/android_time.h"
#include "chrome/browser/history/history_backend.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::Time;
using base::TimeDelta;
using content::BrowserThread;
namespace history {
namespace {
struct BookmarkCacheRow {
public:
BookmarkCacheRow()
: url_id_(0),
bookmark_(false),
favicon_id_(0) {
}
URLID url_id_;
Time create_time_;
Time last_visit_time_;
bool bookmark_;
chrome::FaviconID favicon_id_;
};
} // namespace
class AndroidProviderBackendDelegate : public HistoryBackend::Delegate {
public:
AndroidProviderBackendDelegate() {}
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 {
switch (type) {
case chrome::NOTIFICATION_HISTORY_URLS_DELETED:
deleted_details_.reset(static_cast<URLsDeletedDetails*>(details));
break;
case chrome::NOTIFICATION_FAVICON_CHANGED:
favicon_details_.reset(static_cast<FaviconChangedDetails*>(details));
break;
case chrome::NOTIFICATION_HISTORY_URLS_MODIFIED:
modified_details_.reset(static_cast<URLsModifiedDetails*>(details));
break;
}
}
virtual void DBLoaded(int backend_id) OVERRIDE {}
virtual void NotifyVisitDBObserversOnAddVisit(
const history::BriefVisitInfo& info) OVERRIDE {}
URLsDeletedDetails* deleted_details() const {
return deleted_details_.get();
}
URLsModifiedDetails* modified_details() const {
return modified_details_.get();
}
FaviconChangedDetails* favicon_details() const {
return favicon_details_.get();
}
void ResetDetails() {
deleted_details_.reset();
modified_details_.reset();
favicon_details_.reset();
}
private:
scoped_ptr<URLsDeletedDetails> deleted_details_;
scoped_ptr<URLsModifiedDetails> modified_details_;
scoped_ptr<FaviconChangedDetails> favicon_details_;
DISALLOW_COPY_AND_ASSIGN(AndroidProviderBackendDelegate);
};
class AndroidProviderBackendTest : public testing::Test {
public:
AndroidProviderBackendTest()
: profile_manager_(
TestingBrowserProcess::GetGlobal()),
bookmark_model_(NULL),
ui_thread_(BrowserThread::UI, &message_loop_),
file_thread_(BrowserThread::FILE, &message_loop_) {
}
virtual ~AndroidProviderBackendTest() {}
protected:
virtual void SetUp() OVERRIDE {
// Setup the testing profile, so the bookmark_model_sql_handler could
// get the bookmark model from it.
ASSERT_TRUE(profile_manager_.SetUp());
// It seems that the name has to be chrome::kInitialProfile, so it
// could be found by ProfileManager::GetLastUsedProfile().
TestingProfile* testing_profile = profile_manager_.CreateTestingProfile(
chrome::kInitialProfile);
testing_profile->CreateBookmarkModel(true);
bookmark_model_ = BookmarkModelFactory::GetForProfile(testing_profile);
test::WaitForBookmarkModelToLoad(bookmark_model_);
ASSERT_TRUE(bookmark_model_);
// Get the BookmarkModel from LastUsedProfile, this is the same way that
// how the BookmarkModelSQLHandler gets the BookmarkModel.
Profile* profile = ProfileManager::GetLastUsedProfile();
ASSERT_TRUE(profile);
// Setup the database directory and files.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
history_db_name_ = temp_dir_.path().AppendASCII(chrome::kHistoryFilename);
thumbnail_db_name_ = temp_dir_.path().AppendASCII(
chrome::kFaviconsFilename);
android_cache_db_name_ = temp_dir_.path().AppendASCII(
"TestAndroidCache.db");
}
void AddBookmark(const GURL& url) {
const BookmarkNode* mobile_node = bookmark_model_->mobile_node();
ASSERT_TRUE(mobile_node);
ASSERT_TRUE(bookmark_model_->AddURL(mobile_node, 0, string16(), url));
}
bool GetAndroidURLsRows(std::vector<AndroidURLRow>* rows,
AndroidProviderBackend* backend) {
sql::Statement statement(backend->db_->GetCachedStatement(SQL_FROM_HERE,
"SELECT id, raw_url, url_id FROM android_urls ORDER BY url_id ASC"));
while (statement.Step()) {
AndroidURLRow row;
row.id = statement.ColumnInt64(0);
row.raw_url = statement.ColumnString(1);
row.url_id = statement.ColumnInt64(2);
rows->push_back(row);
}
return true;
}
bool GetBookmarkCacheRows(std::vector<BookmarkCacheRow>* rows,
AndroidProviderBackend* backend) {
sql::Statement statement(backend->db_->GetCachedStatement(SQL_FROM_HERE,
"SELECT created_time, last_visit_time, url_id, bookmark, favicon_id "
"FROM android_cache_db.bookmark_cache ORDER BY url_id ASC"));
while (statement.Step()) {
BookmarkCacheRow row;
row.create_time_ = FromDatabaseTime(statement.ColumnInt64(0));
row.last_visit_time_ = FromDatabaseTime(statement.ColumnInt64(1));
row.url_id_ = statement.ColumnInt64(2);
row.bookmark_ = statement.ColumnBool(3);
row.favicon_id_ = statement.ColumnInt64(4);
rows->push_back(row);
}
return true;
}
AndroidProviderBackendDelegate delegate_;
scoped_refptr<HistoryBackend> history_backend_;
HistoryDatabase history_db_;
ThumbnailDatabase thumbnail_db_;
base::ScopedTempDir temp_dir_;
base::FilePath android_cache_db_name_;
base::FilePath history_db_name_;
base::FilePath thumbnail_db_name_;
TestingProfileManager profile_manager_;
BookmarkModel* bookmark_model_;
base::MessageLoopForUI message_loop_;
content::TestBrowserThread ui_thread_;
content::TestBrowserThread file_thread_;
DISALLOW_COPY_AND_ASSIGN(AndroidProviderBackendTest);
};
TEST_F(AndroidProviderBackendTest, UpdateTables) {
GURL url1("http://www.cnn.com");
URLID url_id1 = 0;
std::vector<VisitInfo> visits1;
Time last_visited1 = Time::Now() - TimeDelta::FromDays(1);
Time created1 = last_visited1 - TimeDelta::FromDays(20);
visits1.push_back(VisitInfo(created1, content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(last_visited1 - TimeDelta::FromDays(1),
content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(last_visited1, content::PAGE_TRANSITION_LINK));
GURL url2("http://www.example.com");
URLID url_id2 = 0;
std::vector<VisitInfo> visits2;
Time last_visited2 = Time::Now();
Time created2 = last_visited2 - TimeDelta::FromDays(10);
visits2.push_back(VisitInfo(created2, content::PAGE_TRANSITION_LINK));
visits2.push_back(VisitInfo(last_visited2 - TimeDelta::FromDays(5),
content::PAGE_TRANSITION_LINK));
visits2.push_back(VisitInfo(last_visited2, content::PAGE_TRANSITION_LINK));
// Add a bookmark which is not in the history.
GURL url3("http://www.bookmark.com");
string16 title3(UTF8ToUTF16("bookmark"));
ASSERT_TRUE(bookmark_model_->AddURL(bookmark_model_->bookmark_bar_node(), 0,
title3, url3));
// Only use the HistoryBackend to generate the test data.
// HistoryBackend will shutdown after that.
{
scoped_refptr<HistoryBackend> history_backend;
history_backend = new HistoryBackend(temp_dir_.path(), 0,
new AndroidProviderBackendDelegate(), bookmark_model_);
history_backend->Init(std::string(), false);
history_backend->AddVisits(url1, visits1, history::SOURCE_SYNCED);
history_backend->AddVisits(url2, visits2, history::SOURCE_SYNCED);
URLRow url_row;
ASSERT_TRUE(history_backend->GetURL(url1, &url_row));
url_id1 = url_row.id();
ASSERT_TRUE(history_backend->GetURL(url2, &url_row));
url_id2 = url_row.id();
// Set favicon to url2.
std::vector<unsigned char> data;
data.push_back('1');
chrome::FaviconBitmapData bitmap_data_element;
bitmap_data_element.bitmap_data = new base::RefCountedBytes(data);
bitmap_data_element.pixel_size = gfx::Size();
bitmap_data_element.icon_url = GURL();
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
favicon_bitmap_data.push_back(bitmap_data_element);
history_backend->SetFavicons(url2, chrome::FAVICON, favicon_bitmap_data);
history_backend->Closing();
}
// The history_db_name and thumbnail_db_name files should be created by
// HistoryBackend. We need to open the same database files.
ASSERT_TRUE(base::PathExists(history_db_name_));
ASSERT_TRUE(base::PathExists(thumbnail_db_name_));
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
// Set url1 as bookmark.
AddBookmark(url1);
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
ASSERT_TRUE(backend->EnsureInitializedAndUpdated());
// First verify that the bookmark which was not in the history has been added
// to history database.
URLRow url_row;
ASSERT_TRUE(history_db_.GetRowForURL(url3, &url_row));
URLID url_id3 = url_row.id();
ASSERT_EQ(url3, url_row.url());
ASSERT_EQ(title3, url_row.title());
std::vector<AndroidURLRow> android_url_rows;
ASSERT_TRUE(GetAndroidURLsRows(&android_url_rows, backend.get()));
ASSERT_EQ(3u, android_url_rows.size());
std::vector<AndroidURLRow>::iterator i = android_url_rows.begin();
EXPECT_EQ(url_id1, i->url_id);
EXPECT_EQ(url1.spec(), i->raw_url);
i++;
EXPECT_EQ(url_id2, i->url_id);
EXPECT_EQ(url2.spec(), i->raw_url);
i++;
EXPECT_EQ(url_id3, i->url_id);
EXPECT_EQ(url3.spec(), i->raw_url);
std::vector<BookmarkCacheRow> bookmark_cache_rows;
ASSERT_TRUE(GetBookmarkCacheRows(&bookmark_cache_rows, backend.get()));
ASSERT_EQ(3u, bookmark_cache_rows.size());
std::vector<BookmarkCacheRow>::const_iterator j = bookmark_cache_rows.begin();
EXPECT_EQ(url_id1, j->url_id_);
EXPECT_EQ(ToDatabaseTime(last_visited1), ToDatabaseTime(j->last_visit_time_));
EXPECT_EQ(ToDatabaseTime(created1), ToDatabaseTime(j->create_time_));
EXPECT_EQ(0, j->favicon_id_);
EXPECT_TRUE(j->bookmark_);
j++;
EXPECT_EQ(url_id2, j->url_id_);
EXPECT_EQ(ToDatabaseTime(last_visited2), ToDatabaseTime(j->last_visit_time_));
EXPECT_EQ(ToDatabaseTime(created2), ToDatabaseTime(j->create_time_));
EXPECT_NE(0, j->favicon_id_);
EXPECT_FALSE(j->bookmark_);
// Delete url2 from database.
ASSERT_TRUE(history_db_.DeleteURLRow(url_id2));
VisitVector visit_rows;
ASSERT_TRUE(history_db_.GetMostRecentVisitsForURL(url_id2, 10,
&visit_rows));
ASSERT_EQ(3u, visit_rows.size());
for (VisitVector::const_iterator v = visit_rows.begin();
v != visit_rows.end(); v++)
history_db_.DeleteVisit(*v);
backend->UpdateTables();
android_url_rows.clear();
ASSERT_TRUE(GetAndroidURLsRows(&android_url_rows, backend.get()));
ASSERT_EQ(2u, android_url_rows.size());
i = android_url_rows.begin();
EXPECT_EQ(url_id1, i->url_id);
EXPECT_EQ(url1.spec(), i->raw_url);
++i;
EXPECT_EQ(url_id3, i->url_id);
EXPECT_EQ(url3.spec(), i->raw_url);
bookmark_cache_rows.clear();
ASSERT_TRUE(GetBookmarkCacheRows(&bookmark_cache_rows, backend.get()));
ASSERT_EQ(2u, bookmark_cache_rows.size());
j = bookmark_cache_rows.begin();
EXPECT_EQ(url_id1, j->url_id_);
EXPECT_EQ(ToDatabaseTime(last_visited1), ToDatabaseTime(j->last_visit_time_));
EXPECT_EQ(ToDatabaseTime(created1), ToDatabaseTime(j->create_time_));
EXPECT_EQ(0, j->favicon_id_);
EXPECT_TRUE(j->bookmark_);
++j;
EXPECT_EQ(url_id3, j->url_id_);
EXPECT_EQ(base::Time::UnixEpoch(), j->last_visit_time_);
EXPECT_EQ(base::Time::UnixEpoch(), j->create_time_);
EXPECT_EQ(0, j->favicon_id_);
EXPECT_TRUE(j->bookmark_);
}
TEST_F(AndroidProviderBackendTest, QueryHistoryAndBookmarks) {
GURL url1("http://www.cnn.com");
URLID url_id1 = 0;
const string16 title1(UTF8ToUTF16("cnn"));
std::vector<VisitInfo> visits1;
Time last_visited1 = Time::Now() - TimeDelta::FromDays(1);
Time created1 = last_visited1 - TimeDelta::FromDays(20);
visits1.push_back(VisitInfo(created1, content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(last_visited1 - TimeDelta::FromDays(1),
content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(last_visited1, content::PAGE_TRANSITION_LINK));
GURL url2("http://www.example.com");
URLID url_id2 = 0;
std::vector<VisitInfo> visits2;
const string16 title2(UTF8ToUTF16("example"));
Time last_visited2 = Time::Now();
Time created2 = last_visited2 - TimeDelta::FromDays(10);
visits2.push_back(VisitInfo(created2, content::PAGE_TRANSITION_LINK));
visits2.push_back(VisitInfo(last_visited2 - TimeDelta::FromDays(5),
content::PAGE_TRANSITION_LINK));
visits2.push_back(VisitInfo(last_visited2, content::PAGE_TRANSITION_LINK));
// Only use the HistoryBackend to generate the test data.
// HistoryBackend will shutdown after that.
{
scoped_refptr<HistoryBackend> history_backend;
history_backend = new HistoryBackend(temp_dir_.path(), 0,
new AndroidProviderBackendDelegate(), bookmark_model_);
history_backend->Init(std::string(), false);
history_backend->AddVisits(url1, visits1, history::SOURCE_SYNCED);
history_backend->AddVisits(url2, visits2, history::SOURCE_SYNCED);
URLRow url_row;
ASSERT_TRUE(history_backend->GetURL(url1, &url_row));
url_id1 = url_row.id();
url_row.set_title(title1);
ASSERT_TRUE(history_backend->UpdateURL(url_id1, url_row));
ASSERT_TRUE(history_backend->GetURL(url2, &url_row));
url_id2 = url_row.id();
url_row.set_title(title2);
ASSERT_TRUE(history_backend->UpdateURL(url_id2, url_row));
// Set favicon to url2.
std::vector<unsigned char> data;
data.push_back('1');
chrome::FaviconBitmapData bitmap_data_element;
bitmap_data_element.bitmap_data = new base::RefCountedBytes(data);
bitmap_data_element.pixel_size = gfx::Size();
bitmap_data_element.icon_url = GURL();
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
favicon_bitmap_data.push_back(bitmap_data_element);
history_backend->SetFavicons(url2, chrome::FAVICON, favicon_bitmap_data);
history_backend->Closing();
}
// The history_db_name and thumbnail_db_name files should be created by
// HistoryBackend. We need to open the same database files.
ASSERT_TRUE(base::PathExists(history_db_name_));
ASSERT_TRUE(base::PathExists(thumbnail_db_name_));
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
// Set url1 as bookmark.
AddBookmark(url1);
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
std::vector<HistoryAndBookmarkRow::ColumnID> projections;
projections.push_back(HistoryAndBookmarkRow::ID);
projections.push_back(HistoryAndBookmarkRow::URL);
projections.push_back(HistoryAndBookmarkRow::TITLE);
projections.push_back(HistoryAndBookmarkRow::CREATED);
projections.push_back(HistoryAndBookmarkRow::LAST_VISIT_TIME);
projections.push_back(HistoryAndBookmarkRow::VISIT_COUNT);
projections.push_back(HistoryAndBookmarkRow::FAVICON);
projections.push_back(HistoryAndBookmarkRow::BOOKMARK);
scoped_ptr<AndroidStatement> statement(backend->QueryHistoryAndBookmarks(
projections, std::string(), std::vector<string16>(),
std::string("url ASC")));
ASSERT_TRUE(statement->statement()->Step());
ASSERT_EQ(url1, GURL(statement->statement()->ColumnString(1)));
EXPECT_EQ(title1, statement->statement()->ColumnString16(2));
EXPECT_EQ(ToDatabaseTime(created1),
statement->statement()->ColumnInt64(3));
EXPECT_EQ(ToDatabaseTime(last_visited1),
statement->statement()->ColumnInt64(4));
EXPECT_EQ(3, statement->statement()->ColumnInt(5));
EXPECT_EQ(6, statement->favicon_index());
// No favicon.
EXPECT_EQ(0, statement->statement()->ColumnByteLength(6));
EXPECT_TRUE(statement->statement()->ColumnBool(7));
ASSERT_TRUE(statement->statement()->Step());
EXPECT_EQ(title2, statement->statement()->ColumnString16(2));
ASSERT_EQ(url2, GURL(statement->statement()->ColumnString(1)));
EXPECT_EQ(ToDatabaseTime(created2),
statement->statement()->ColumnInt64(3));
EXPECT_EQ(ToDatabaseTime(last_visited2),
statement->statement()->ColumnInt64(4));
EXPECT_EQ(3, statement->statement()->ColumnInt(5));
std::vector<unsigned char> favicon2;
EXPECT_EQ(6, statement->favicon_index());
// Has favicon.
EXPECT_NE(0, statement->statement()->ColumnByteLength(6));
EXPECT_FALSE(statement->statement()->ColumnBool(7));
// No more row.
EXPECT_FALSE(statement->statement()->Step());
// Query by bookmark
statement.reset(backend->QueryHistoryAndBookmarks(projections, "bookmark=1",
std::vector<string16>(), std::string("url ASC")));
// Only URL1 is returned.
ASSERT_TRUE(statement->statement()->Step());
ASSERT_EQ(url1, GURL(statement->statement()->ColumnString(1)));
EXPECT_FALSE(statement->statement()->Step());
statement.reset(backend->QueryHistoryAndBookmarks(projections, "bookmark=0",
std::vector<string16>(), std::string("url ASC")));
// Only URL2 is returned.
ASSERT_TRUE(statement->statement()->Step());
ASSERT_EQ(url2, GURL(statement->statement()->ColumnString(1)));
EXPECT_FALSE(statement->statement()->Step());
}
TEST_F(AndroidProviderBackendTest, InsertHistoryAndBookmark) {
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_is_bookmark(false);
row2.set_title(UTF8ToUTF16("example"));
std::vector<unsigned char> data;
data.push_back('1');
row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
EXPECT_FALSE(delegate_.deleted_details());
ASSERT_TRUE(delegate_.modified_details());
ASSERT_EQ(1u, delegate_.modified_details()->changed_urls.size());
EXPECT_EQ(row1.url(), delegate_.modified_details()->changed_urls[0].url());
EXPECT_EQ(row1.last_visit_time(),
delegate_.modified_details()->changed_urls[0].last_visit());
EXPECT_EQ(row1.visit_count(),
delegate_.modified_details()->changed_urls[0].visit_count());
EXPECT_EQ(row1.title(),
delegate_.modified_details()->changed_urls[0].title());
EXPECT_FALSE(delegate_.favicon_details());
content::RunAllPendingInMessageLoop();
ASSERT_EQ(1, bookmark_model_->mobile_node()->child_count());
const BookmarkNode* child = bookmark_model_->mobile_node()->GetChild(0);
ASSERT_TRUE(child);
EXPECT_EQ(row1.title(), child->GetTitle());
EXPECT_EQ(row1.url(), child->url());
delegate_.ResetDetails();
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row2));
EXPECT_FALSE(delegate_.deleted_details());
ASSERT_TRUE(delegate_.modified_details());
ASSERT_EQ(1u, delegate_.modified_details()->changed_urls.size());
EXPECT_EQ(row2.url(), delegate_.modified_details()->changed_urls[0].url());
EXPECT_EQ(row2.last_visit_time(),
delegate_.modified_details()->changed_urls[0].last_visit());
EXPECT_EQ(row2.title(),
delegate_.modified_details()->changed_urls[0].title());
ASSERT_TRUE(delegate_.favicon_details());
ASSERT_EQ(1u, delegate_.favicon_details()->urls.size());
ASSERT_TRUE(delegate_.favicon_details()->urls.end() !=
delegate_.favicon_details()->urls.find(row2.url()));
std::vector<HistoryAndBookmarkRow::ColumnID> projections;
projections.push_back(HistoryAndBookmarkRow::ID);
projections.push_back(HistoryAndBookmarkRow::URL);
projections.push_back(HistoryAndBookmarkRow::TITLE);
projections.push_back(HistoryAndBookmarkRow::CREATED);
projections.push_back(HistoryAndBookmarkRow::LAST_VISIT_TIME);
projections.push_back(HistoryAndBookmarkRow::VISIT_COUNT);
projections.push_back(HistoryAndBookmarkRow::FAVICON);
projections.push_back(HistoryAndBookmarkRow::BOOKMARK);
scoped_ptr<AndroidStatement> statement(backend->QueryHistoryAndBookmarks(
projections, std::string(), std::vector<string16>(),
std::string("url ASC")));
ASSERT_TRUE(statement->statement()->Step());
ASSERT_EQ(row1.raw_url(), statement->statement()->ColumnString(1));
EXPECT_EQ(row1.title(), statement->statement()->ColumnString16(2));
EXPECT_EQ(ToDatabaseTime(row1.created()),
statement->statement()->ColumnInt64(3));
EXPECT_EQ(ToDatabaseTime(row1.last_visit_time()),
statement->statement()->ColumnInt64(4));
EXPECT_EQ(row1.visit_count(), statement->statement()->ColumnInt(5));
EXPECT_EQ(6, statement->favicon_index());
// No favicon.
EXPECT_EQ(0, statement->statement()->ColumnByteLength(6));
// TODO: Find a way to test the bookmark was added in BookmarkModel.
// The bookmark was added in UI thread, there is no good way to test it.
EXPECT_TRUE(statement->statement()->ColumnBool(7));
ASSERT_TRUE(statement->statement()->Step());
EXPECT_EQ(row2.title(), statement->statement()->ColumnString16(2));
EXPECT_EQ(row2.url(), GURL(statement->statement()->ColumnString(1)));
EXPECT_EQ(ToDatabaseTime(row2.last_visit_time()),
statement->statement()->ColumnInt64(3));
EXPECT_EQ(ToDatabaseTime(row2.last_visit_time()),
statement->statement()->ColumnInt64(4));
EXPECT_EQ(1, statement->statement()->ColumnInt(5));
EXPECT_EQ(6, statement->favicon_index());
// Has favicon.
EXPECT_NE(0, statement->statement()->ColumnByteLength(6));
// TODO: Find a way to test the bookmark was added in BookmarkModel.
// The bookmark was added in UI thread, there is no good way to test it.
EXPECT_FALSE(statement->statement()->ColumnBool(7));
// No more row.
EXPECT_FALSE(statement->statement()->Step());
}
TEST_F(AndroidProviderBackendTest, DeleteHistoryAndBookmarks) {
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_is_bookmark(false);
row2.set_title(UTF8ToUTF16("example"));
std::vector<unsigned char> data;
data.push_back('1');
row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row2));
// Verify the row1 has been added in bookmark model.
content::RunAllPendingInMessageLoop();
ASSERT_EQ(1, bookmark_model_->mobile_node()->child_count());
const BookmarkNode* child = bookmark_model_->mobile_node()->GetChild(0);
ASSERT_TRUE(child);
EXPECT_EQ(row1.title(), child->GetTitle());
EXPECT_EQ(row1.url(), child->url());
// Delete the row1.
std::vector<string16> args;
int deleted_count = 0;
delegate_.ResetDetails();
ASSERT_TRUE(backend->DeleteHistoryAndBookmarks("Favicon IS NULL", args,
&deleted_count));
EXPECT_EQ(1, deleted_count);
// Verify the row1 was removed from bookmark model.
content::RunAllPendingInMessageLoop();
ASSERT_EQ(0, bookmark_model_->mobile_node()->child_count());
// Verify notifications
ASSERT_TRUE(delegate_.deleted_details());
EXPECT_FALSE(delegate_.modified_details());
EXPECT_EQ(1u, delegate_.deleted_details()->rows.size());
EXPECT_EQ(row1.url(), delegate_.deleted_details()->rows[0].url());
EXPECT_EQ(row1.last_visit_time(),
delegate_.deleted_details()->rows[0].last_visit());
EXPECT_EQ(row1.title(),
delegate_.deleted_details()->rows[0].title());
EXPECT_FALSE(delegate_.favicon_details());
std::vector<HistoryAndBookmarkRow::ColumnID> projections;
projections.push_back(HistoryAndBookmarkRow::ID);
projections.push_back(HistoryAndBookmarkRow::URL);
projections.push_back(HistoryAndBookmarkRow::TITLE);
projections.push_back(HistoryAndBookmarkRow::CREATED);
projections.push_back(HistoryAndBookmarkRow::LAST_VISIT_TIME);
projections.push_back(HistoryAndBookmarkRow::VISIT_COUNT);
projections.push_back(HistoryAndBookmarkRow::FAVICON);
projections.push_back(HistoryAndBookmarkRow::BOOKMARK);
scoped_ptr<AndroidStatement> statement(backend->QueryHistoryAndBookmarks(
projections, std::string(), std::vector<string16>(),
std::string("url ASC")));
ASSERT_TRUE(statement->statement()->Step());
EXPECT_EQ(row2.title(), statement->statement()->ColumnString16(2));
EXPECT_EQ(row2.url(), GURL(statement->statement()->ColumnString(1)));
EXPECT_EQ(ToDatabaseTime(row2.last_visit_time()),
statement->statement()->ColumnInt64(3));
EXPECT_EQ(ToDatabaseTime(row2.last_visit_time()),
statement->statement()->ColumnInt64(4));
EXPECT_EQ(1, statement->statement()->ColumnInt(5));
EXPECT_EQ(6, statement->favicon_index());
// Has favicon.
EXPECT_NE(0, statement->statement()->ColumnByteLength(6));
// TODO: Find a way to test the bookmark was added in BookmarkModel.
// The bookmark was added in UI thread, there is no good way to test it.
EXPECT_FALSE(statement->statement()->ColumnBool(7));
// No more row.
EXPECT_FALSE(statement->statement()->Step());
deleted_count = 0;
// Delete row2.
delegate_.ResetDetails();
ASSERT_TRUE(backend->DeleteHistoryAndBookmarks("bookmark = 0",
std::vector<string16>(), &deleted_count));
// Verify notifications
ASSERT_TRUE(delegate_.deleted_details());
EXPECT_FALSE(delegate_.modified_details());
EXPECT_EQ(1u, delegate_.deleted_details()->rows.size());
EXPECT_EQ(row2.url(), delegate_.deleted_details()->rows[0].url());
EXPECT_EQ(row2.last_visit_time(),
delegate_.deleted_details()->rows[0].last_visit());
EXPECT_EQ(row2.title(),
delegate_.deleted_details()->rows[0].title());
ASSERT_TRUE(delegate_.favicon_details());
ASSERT_EQ(1u, delegate_.favicon_details()->urls.size());
ASSERT_TRUE(delegate_.favicon_details()->urls.end() !=
delegate_.favicon_details()->urls.find(row2.url()));
ASSERT_EQ(1, deleted_count);
scoped_ptr<AndroidStatement> statement1(backend->QueryHistoryAndBookmarks(
projections, std::string(), std::vector<string16>(),
std::string("url ASC")));
ASSERT_FALSE(statement1->statement()->Step());
}
TEST_F(AndroidProviderBackendTest, IsValidHistoryAndBookmarkRow) {
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
// The created time and last visit time are too close to have required visit
// count.
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::FromInternalValue(
row1.last_visit_time().ToInternalValue() - 1));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
EXPECT_FALSE(backend->InsertHistoryAndBookmark(row1));
// Have different created time and last visit time, but only have 1 visit
// count.
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_created(Time::Now() - TimeDelta::FromDays(11));
row2.set_visit_count(1);
EXPECT_FALSE(backend->InsertHistoryAndBookmark(row2));
// Have created time in the future.
HistoryAndBookmarkRow row3;
row3.set_raw_url("http://www.example.com");
row3.set_url(GURL("http://www.example.com"));
row3.set_created(Time::Now() + TimeDelta::FromDays(11));
EXPECT_FALSE(backend->InsertHistoryAndBookmark(row3));
// Have last vist time in the future.
HistoryAndBookmarkRow row4;
row4.set_raw_url("http://www.example.com");
row4.set_url(GURL("http://www.example.com"));
row4.set_last_visit_time(Time::Now() + TimeDelta::FromDays(11));
EXPECT_FALSE(backend->InsertHistoryAndBookmark(row4));
// Created time is larger than last visit time.
HistoryAndBookmarkRow row5;
row5.set_raw_url("http://www.example.com");
row5.set_url(GURL("http://www.example.com"));
row5.set_last_visit_time(Time::Now());
row5.set_created(Time::Now() + TimeDelta::FromDays(11));
EXPECT_FALSE(backend->InsertHistoryAndBookmark(row5));
// Visit count is zero, and last visit time is not zero.
HistoryAndBookmarkRow row6;
row6.set_raw_url("http://www.example.com");
row6.set_url(GURL("http://www.example.com"));
row6.set_visit_count(0);
row6.set_last_visit_time(Time::Now());
row6.set_created(Time::Now() - TimeDelta::FromDays(1));
EXPECT_FALSE(backend->InsertHistoryAndBookmark(row6));
// Visit count is zero, and create time is not zero.
HistoryAndBookmarkRow row7;
row7.set_raw_url("http://www.example.com");
row7.set_url(GURL("http://www.example.com"));
row7.set_visit_count(0);
row7.set_last_visit_time(Time::Now());
row7.set_created(Time::UnixEpoch());
EXPECT_TRUE(backend->InsertHistoryAndBookmark(row7));
}
TEST_F(AndroidProviderBackendTest, UpdateURL) {
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_is_bookmark(false);
row2.set_title(UTF8ToUTF16("example"));
std::vector<unsigned char> data;
data.push_back('1');
row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
ASSERT_TRUE(id1);
AndroidURLID id2 = backend->InsertHistoryAndBookmark(row2);
ASSERT_TRUE(id2);
// Verify the row1 has been added in bookmark model.
content::RunAllPendingInMessageLoop();
ASSERT_EQ(1, bookmark_model_->mobile_node()->child_count());
const BookmarkNode* child = bookmark_model_->mobile_node()->GetChild(0);
ASSERT_TRUE(child);
EXPECT_EQ(row1.title(), child->GetTitle());
EXPECT_EQ(row1.url(), child->url());
// Make sure the url has correctly insertted.
URLID url_id1 = history_db_.GetRowForURL(row1.url(), NULL);
ASSERT_TRUE(url_id1);
URLID url_id2 = history_db_.GetRowForURL(row2.url(), NULL);
ASSERT_TRUE(url_id2);
// Make sure we have the correct visit rows in visit table.
VisitVector visits;
ASSERT_TRUE(history_db_.GetVisitsForURL(url_id1, &visits));
ASSERT_EQ(10u, visits.size());
visits.clear();
ASSERT_TRUE(history_db_.GetVisitsForURL(url_id2, &visits));
ASSERT_EQ(1u, visits.size());
int update_count;
std::vector<string16> update_args;
// Try to update the mutiple rows with the same URL, this should failed.
HistoryAndBookmarkRow update_row1;
update_row1.set_raw_url("newwebiste.com");
update_row1.set_url(GURL("http://newwebsite.com"));
update_args.clear();
ASSERT_FALSE(backend->UpdateHistoryAndBookmarks(update_row1, std::string(),
update_args, &update_count));
// Only update one URL.
update_args.clear();
update_args.push_back(UTF8ToUTF16(row1.raw_url()));
delegate_.ResetDetails();
ASSERT_TRUE(backend->UpdateHistoryAndBookmarks(update_row1, "url = ?",
update_args, &update_count));
// Verify notifications, Update involves insert and delete URLS.
ASSERT_TRUE(delegate_.deleted_details());
EXPECT_EQ(1u, delegate_.deleted_details()->rows.size());
EXPECT_EQ(row1.url(), delegate_.deleted_details()->rows[0].url());
EXPECT_EQ(row1.last_visit_time(),
delegate_.deleted_details()->rows[0].last_visit());
EXPECT_EQ(row1.title(),
delegate_.deleted_details()->rows[0].title());
ASSERT_TRUE(delegate_.modified_details());
ASSERT_EQ(1u, delegate_.modified_details()->changed_urls.size());
EXPECT_EQ(update_row1.url(),
delegate_.modified_details()->changed_urls[0].url());
EXPECT_EQ(ToDatabaseTime(row1.last_visit_time()),
ToDatabaseTime(
delegate_.modified_details()->changed_urls[0].last_visit()));
EXPECT_EQ(row1.title(),
delegate_.modified_details()->changed_urls[0].title());
EXPECT_FALSE(delegate_.favicon_details());
EXPECT_EQ(1, update_count);
// We shouldn't find orignal url anymore.
EXPECT_FALSE(history_db_.GetRowForURL(row1.url(), NULL));
visits.clear();
EXPECT_TRUE(history_db_.GetVisitsForURL(url_id1, &visits));
EXPECT_EQ(0u, visits.size());
// Verify new URL.
URLRow new_row;
EXPECT_TRUE(history_db_.GetRowForURL(update_row1.url(), &new_row));
EXPECT_EQ(10, new_row.visit_count());
EXPECT_EQ(ToDatabaseTime(row1.last_visit_time()),
ToDatabaseTime(new_row.last_visit()));
visits.clear();
EXPECT_TRUE(history_db_.GetVisitsForURL(new_row.id(), &visits));
EXPECT_EQ(10u, visits.size());
AndroidURLRow android_url_row1;
ASSERT_TRUE(history_db_.GetAndroidURLRow(new_row.id(), &android_url_row1));
// Android URL ID shouldn't change.
EXPECT_EQ(id1, android_url_row1.id);
// Verify the bookmark model was updated.
content::RunAllPendingInMessageLoop();
ASSERT_EQ(1, bookmark_model_->mobile_node()->child_count());
const BookmarkNode* child1 = bookmark_model_->mobile_node()->GetChild(0);
ASSERT_TRUE(child1);
EXPECT_EQ(row1.title(), child1->GetTitle());
EXPECT_EQ(update_row1.url(), child1->url());
// Update the URL with visit count, created time, and last visit time.
HistoryAndBookmarkRow update_row2;
update_row2.set_raw_url("somethingelse.com");
update_row2.set_url(GURL("http://somethingelse.com"));
update_row2.set_last_visit_time(Time::Now());
update_row2.set_created(Time::Now() - TimeDelta::FromDays(20));
update_row2.set_visit_count(10);
update_args.clear();
update_args.push_back(UTF8ToUTF16(row2.raw_url()));
delegate_.ResetDetails();
ASSERT_TRUE(backend->UpdateHistoryAndBookmarks(update_row2, "url = ?",
update_args, &update_count));
// Verify notifications, Update involves insert and delete URLS.
ASSERT_TRUE(delegate_.deleted_details());
EXPECT_EQ(1u, delegate_.deleted_details()->rows.size());
EXPECT_EQ(row2.url(), delegate_.deleted_details()->rows[0].url());
EXPECT_EQ(row2.last_visit_time(),
delegate_.deleted_details()->rows[0].last_visit());
EXPECT_EQ(row2.title(),
delegate_.deleted_details()->rows[0].title());
ASSERT_TRUE(delegate_.modified_details());
ASSERT_EQ(1u, delegate_.modified_details()->changed_urls.size());
EXPECT_EQ(update_row2.url(),
delegate_.modified_details()->changed_urls[0].url());
EXPECT_EQ(ToDatabaseTime(update_row2.last_visit_time()),
ToDatabaseTime(
delegate_.modified_details()->changed_urls[0].last_visit()));
EXPECT_EQ(update_row2.visit_count(),
delegate_.modified_details()->changed_urls[0].visit_count());
ASSERT_TRUE(delegate_.favicon_details());
ASSERT_EQ(2u, delegate_.favicon_details()->urls.size());
ASSERT_TRUE(delegate_.favicon_details()->urls.end() !=
delegate_.favicon_details()->urls.find(row2.url()));
ASSERT_TRUE(delegate_.favicon_details()->urls.end() !=
delegate_.favicon_details()->urls.find(update_row2.url()));
EXPECT_EQ(1, update_count);
// We shouldn't find orignal url anymore.
EXPECT_FALSE(history_db_.GetRowForURL(row2.url(), NULL));
visits.clear();
EXPECT_TRUE(history_db_.GetVisitsForURL(url_id2, &visits));
EXPECT_EQ(0u, visits.size());
// Verify new URL.
URLRow new_row2;
ASSERT_TRUE(history_db_.GetRowForURL(update_row2.url(), &new_row2));
EXPECT_EQ(10, new_row2.visit_count());
EXPECT_EQ(update_row2.last_visit_time(), new_row2.last_visit());
visits.clear();
EXPECT_TRUE(history_db_.GetVisitsForURL(new_row2.id(), &visits));
EXPECT_EQ(10u, visits.size());
AndroidURLRow android_url_row2;
ASSERT_TRUE(history_db_.GetAndroidURLRow(new_row2.id(), &android_url_row2));
// Android URL ID shouldn't change.
EXPECT_EQ(id2, android_url_row2.id);
ASSERT_TRUE(history_db_.GetVisitsForURL(new_row2.id(), &visits));
ASSERT_EQ(10u, visits.size());
EXPECT_EQ(update_row2.created(), visits[0].visit_time);
EXPECT_EQ(update_row2.last_visit_time(), visits[9].visit_time);
}
TEST_F(AndroidProviderBackendTest, UpdateVisitCount) {
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_is_bookmark(false);
row2.set_title(UTF8ToUTF16("example"));
std::vector<unsigned char> data;
data.push_back('1');
row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
ASSERT_TRUE(id1);
AndroidURLID id2 = backend->InsertHistoryAndBookmark(row2);
ASSERT_TRUE(id2);
int update_count;
std::vector<string16> update_args;
// Update the visit_count to a value less than current one.
HistoryAndBookmarkRow update_row1;
update_row1.set_visit_count(5);
update_args.push_back(UTF8ToUTF16(row1.raw_url()));
delegate_.ResetDetails();
ASSERT_TRUE(backend->UpdateHistoryAndBookmarks(update_row1, "url = ?",
update_args, &update_count));
// Verify notifications, Update involves modified URL.
EXPECT_FALSE(delegate_.deleted_details());
ASSERT_TRUE(delegate_.modified_details());
ASSERT_EQ(1u, delegate_.modified_details()->changed_urls.size());
EXPECT_EQ(row1.url(),
delegate_.modified_details()->changed_urls[0].url());
EXPECT_EQ(ToDatabaseTime(row1.last_visit_time()),
ToDatabaseTime(
delegate_.modified_details()->changed_urls[0].last_visit()));
EXPECT_EQ(update_row1.visit_count(),
delegate_.modified_details()->changed_urls[0].visit_count());
EXPECT_FALSE(delegate_.favicon_details());
// All visits should be removed, and 5 new visit insertted.
URLRow new_row1;
ASSERT_TRUE(history_db_.GetRowForURL(row1.url(), &new_row1));
EXPECT_EQ(5, new_row1.visit_count());
VisitVector visits;
ASSERT_TRUE(history_db_.GetVisitsForURL(new_row1.id(), &visits));
ASSERT_EQ(5u, visits.size());
EXPECT_EQ(row1.last_visit_time(), visits[4].visit_time);
EXPECT_GT(row1.last_visit_time(), visits[0].visit_time);
// Update the visit_count to a value equal to current one.
HistoryAndBookmarkRow update_row2;
update_row2.set_visit_count(1);
update_args.clear();
update_args.push_back(UTF8ToUTF16(row2.raw_url()));
ASSERT_TRUE(backend->UpdateHistoryAndBookmarks(update_row2, "url = ?",
update_args, &update_count));
// All shouldn't have any change.
URLRow new_row2;
ASSERT_TRUE(history_db_.GetRowForURL(row2.url(), &new_row2));
EXPECT_EQ(1, new_row2.visit_count());
ASSERT_TRUE(history_db_.GetVisitsForURL(new_row2.id(), &visits));
ASSERT_EQ(1u, visits.size());
EXPECT_EQ(row2.last_visit_time(), visits[0].visit_time);
}
TEST_F(AndroidProviderBackendTest, UpdateLastVisitTime) {
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_is_bookmark(false);
row2.set_title(UTF8ToUTF16("example"));
std::vector<unsigned char> data;
data.push_back('1');
row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
ASSERT_TRUE(id1);
AndroidURLID id2 = backend->InsertHistoryAndBookmark(row2);
ASSERT_TRUE(id2);
int update_count;
std::vector<string16> update_args;
// Update the last visit time to a value greater than current one.
HistoryAndBookmarkRow update_row1;
update_row1.set_last_visit_time(Time::Now());
update_args.push_back(UTF8ToUTF16(row1.raw_url()));
delegate_.ResetDetails();
ASSERT_TRUE(backend->UpdateHistoryAndBookmarks(update_row1, "url = ?",
update_args, &update_count));
// Verify notifications, Update involves modified URL.
EXPECT_FALSE(delegate_.deleted_details());
ASSERT_TRUE(delegate_.modified_details());
ASSERT_EQ(1u, delegate_.modified_details()->changed_urls.size());
EXPECT_EQ(row1.url(),
delegate_.modified_details()->changed_urls[0].url());
EXPECT_EQ(ToDatabaseTime(update_row1.last_visit_time()),
ToDatabaseTime(
delegate_.modified_details()->changed_urls[0].last_visit()));
EXPECT_FALSE(delegate_.favicon_details());
URLRow new_row1;
ASSERT_TRUE(history_db_.GetRowForURL(row1.url(), &new_row1));
EXPECT_EQ(11, new_row1.visit_count());
EXPECT_EQ(update_row1.last_visit_time(), new_row1.last_visit());
VisitVector visits;
ASSERT_TRUE(history_db_.GetVisitsForURL(new_row1.id(), &visits));
// 1 new visit insertted.
ASSERT_EQ(11u, visits.size());
EXPECT_EQ(update_row1.last_visit_time(), visits[10].visit_time);
EXPECT_EQ(row1.last_visit_time(), visits[9].visit_time);
// Update the visit_tim to a value less than to current one.
HistoryAndBookmarkRow update_row2;
update_row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
update_args.clear();
update_args.push_back(UTF8ToUTF16(row1.raw_url()));
ASSERT_FALSE(backend->UpdateHistoryAndBookmarks(update_row2, "url = ?",
update_args, &update_count));
}
TEST_F(AndroidProviderBackendTest, UpdateFavicon) {
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
ASSERT_TRUE(id1);
int update_count;
std::vector<string16> update_args;
// Update the last visit time to a value greater than current one.
HistoryAndBookmarkRow update_row1;
// Set favicon.
std::vector<unsigned char> data;
data.push_back('1');
update_row1.set_favicon(base::RefCountedBytes::TakeVector(&data));
update_args.push_back(UTF8ToUTF16(row1.raw_url()));
delegate_.ResetDetails();
ASSERT_TRUE(backend->UpdateHistoryAndBookmarks(update_row1, "url = ?",
update_args, &update_count));
// Verify notifications.
EXPECT_FALSE(delegate_.deleted_details());
EXPECT_FALSE(delegate_.modified_details());
ASSERT_TRUE(delegate_.favicon_details());
ASSERT_EQ(1u, delegate_.favicon_details()->urls.size());
ASSERT_TRUE(delegate_.favicon_details()->urls.end() !=
delegate_.favicon_details()->urls.find(row1.url()));
std::vector<IconMapping> icon_mappings;
EXPECT_TRUE(thumbnail_db_.GetIconMappingsForPageURL(
row1.url(), chrome::FAVICON, &icon_mappings));
EXPECT_EQ(1u, icon_mappings.size());
std::vector<FaviconBitmap> favicon_bitmaps;
EXPECT_TRUE(thumbnail_db_.GetFaviconBitmaps(icon_mappings[0].icon_id,
&favicon_bitmaps));
EXPECT_EQ(1u, favicon_bitmaps.size());
EXPECT_TRUE(favicon_bitmaps[0].bitmap_data.get());
EXPECT_EQ(1u, favicon_bitmaps[0].bitmap_data->size());
EXPECT_EQ('1', *favicon_bitmaps[0].bitmap_data->front());
// Remove favicon.
HistoryAndBookmarkRow update_row2;
// Set favicon.
update_row1.set_favicon(new base::RefCountedBytes());
update_args.clear();
update_args.push_back(UTF8ToUTF16(row1.raw_url()));
delegate_.ResetDetails();
ASSERT_TRUE(backend->UpdateHistoryAndBookmarks(update_row1, "url = ?",
update_args, &update_count));
// Verify notifications.
EXPECT_FALSE(delegate_.deleted_details());
EXPECT_FALSE(delegate_.modified_details());
ASSERT_TRUE(delegate_.favicon_details());
ASSERT_EQ(1u, delegate_.favicon_details()->urls.size());
ASSERT_TRUE(delegate_.favicon_details()->urls.end() !=
delegate_.favicon_details()->urls.find(row1.url()));
EXPECT_FALSE(thumbnail_db_.GetIconMappingsForPageURL(
row1.url(), chrome::FAVICON, NULL));
}
TEST_F(AndroidProviderBackendTest, UpdateSearchTermTable) {
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
// Insert a keyword search item to verify if the update succeeds.
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_title(UTF8ToUTF16("cnn"));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
string16 term = UTF8ToUTF16("Search term 1");
URLID url_id = history_db_.GetRowForURL(row1.url(), NULL);
ASSERT_TRUE(url_id);
ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term));
ASSERT_TRUE(backend->UpdateSearchTermTable());
SearchTermRow keyword_cache;
SearchTermID id = history_db_.GetSearchTerm(term, &keyword_cache);
ASSERT_TRUE(id);
EXPECT_EQ(term, keyword_cache.term);
EXPECT_EQ(ToDatabaseTime(row1.last_visit_time()),
ToDatabaseTime(keyword_cache.last_visit_time));
// Add another row.
HistoryAndBookmarkRow row2;
row2.set_raw_url("google.com");
row2.set_url(GURL("http://google.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(2));
row2.set_title(UTF8ToUTF16("cnn"));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row2));
url_id = history_db_.GetRowForURL(row2.url(), NULL);
ASSERT_TRUE(url_id);
string16 term2 = UTF8ToUTF16("Search term 2");
ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term2));
ASSERT_TRUE(backend->UpdateSearchTermTable());
SearchTermID search_id1 = history_db_.GetSearchTerm(term,
&keyword_cache);
// The id shouldn't changed.
ASSERT_EQ(id, search_id1);
EXPECT_EQ(term, keyword_cache.term);
EXPECT_EQ(ToDatabaseTime(row1.last_visit_time()),
ToDatabaseTime(keyword_cache.last_visit_time));
// Verify the row just inserted.
SearchTermID id2 = history_db_.GetSearchTerm(term2, &keyword_cache);
ASSERT_TRUE(id2);
EXPECT_EQ(term2, keyword_cache.term);
EXPECT_EQ(ToDatabaseTime(row2.last_visit_time()),
ToDatabaseTime(keyword_cache.last_visit_time));
// Add 3rd row and associate it with term.
HistoryAndBookmarkRow row3;
row3.set_raw_url("search.com");
row3.set_url(GURL("http://search.com"));
row3.set_last_visit_time(Time::Now());
row3.set_title(UTF8ToUTF16("search"));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row3));
url_id = history_db_.GetRowForURL(row3.url(), NULL);
ASSERT_TRUE(url_id);
ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term));
ASSERT_TRUE(backend->UpdateSearchTermTable());
// Verify id not changed and last_visit_time updated.
ASSERT_EQ(search_id1, history_db_.GetSearchTerm(term, &keyword_cache));
EXPECT_EQ(ToDatabaseTime(row3.last_visit_time()),
ToDatabaseTime(keyword_cache.last_visit_time));
// The id of term2 wasn't changed.
EXPECT_EQ(id2, history_db_.GetSearchTerm(term2, NULL));
// Remove the term.
ASSERT_TRUE(history_db_.DeleteKeywordSearchTerm(term));
ASSERT_TRUE(backend->UpdateSearchTermTable());
// The cache of term should removed.
ASSERT_FALSE(history_db_.GetSearchTerm(term, NULL));
// The id of term2 wasn't changed.
EXPECT_EQ(id2, history_db_.GetSearchTerm(term2, NULL));
}
TEST_F(AndroidProviderBackendTest, QuerySearchTerms) {
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
// Insert a keyword search item to verify if we can find it.
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_title(UTF8ToUTF16("cnn"));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
string16 term = UTF8ToUTF16("Search term 1");
URLID url_id = history_db_.GetRowForURL(row1.url(), NULL);
ASSERT_TRUE(url_id);
ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term));
std::vector<SearchRow::ColumnID> projections;
projections.push_back(SearchRow::ID);
projections.push_back(SearchRow::SEARCH_TERM);
projections.push_back(SearchRow::SEARCH_TIME);
scoped_ptr<AndroidStatement> statement(backend->QuerySearchTerms(
projections, std::string(), std::vector<string16>(), std::string()));
ASSERT_TRUE(statement.get());
ASSERT_TRUE(statement->statement()->Step());
EXPECT_TRUE(statement->statement()->ColumnInt64(0));
EXPECT_EQ(term, statement->statement()->ColumnString16(1));
EXPECT_EQ(ToDatabaseTime(row1.last_visit_time()),
statement->statement()->ColumnInt64(2));
EXPECT_FALSE(statement->statement()->Step());
}
TEST_F(AndroidProviderBackendTest, UpdateSearchTerms) {
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
// Insert a keyword.
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_title(UTF8ToUTF16("cnn"));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
string16 term = UTF8ToUTF16("Search term 1");
URLID url_id = history_db_.GetRowForURL(row1.url(), NULL);
ASSERT_TRUE(url_id);
ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term));
// Get the SearchTermID of the row we just inserted.
std::vector<SearchRow::ColumnID> projections;
projections.push_back(SearchRow::ID);
projections.push_back(SearchRow::SEARCH_TIME);
projections.push_back(SearchRow::SEARCH_TERM);
std::vector<string16> args;
args.push_back(term);
scoped_ptr<AndroidStatement> statement(backend->QuerySearchTerms(
projections, "search = ?", args, std::string()));
ASSERT_TRUE(statement.get());
ASSERT_TRUE(statement->statement()->Step());
SearchTermID id = statement->statement()->ColumnInt64(0);
ASSERT_TRUE(id);
EXPECT_FALSE(statement->statement()->Step());
// Update the search term and time.
string16 update_term = UTF8ToUTF16("Update search term");
args.clear();
args.push_back(term);
SearchRow search_row;
search_row.set_search_term(update_term);
search_row.set_url(GURL("http://google.com"));
search_row.set_template_url_id(1);
search_row.set_search_time(Time::Now() - TimeDelta::FromHours(1));
int update_count = 0;
ASSERT_TRUE(backend->UpdateSearchTerms(search_row, "search = ?", args,
&update_count));
EXPECT_EQ(1, update_count);
// Verify if the search term updated.
// The origin term should be removed.
std::vector<KeywordSearchTermRow> rows;
ASSERT_TRUE(history_db_.GetKeywordSearchTermRows(term, &rows));
EXPECT_TRUE(rows.empty());
// The new term should be inserted.
ASSERT_TRUE(history_db_.GetKeywordSearchTermRows(update_term, &rows));
ASSERT_EQ(1u, rows.size());
// The history of urls shouldn't be removed.
ASSERT_TRUE(history_db_.GetRowForURL(row1.url(), NULL));
// The new URL is inserted.
ASSERT_TRUE(history_db_.GetRowForURL(search_row.url(), NULL));
// Verfiy the AndoridSearchID isn't changed.
args.clear();
args.push_back(update_term);
statement.reset(backend->QuerySearchTerms(projections, "search = ?", args,
std::string()));
ASSERT_TRUE(statement.get());
ASSERT_TRUE(statement->statement()->Step());
// The id didn't change.
EXPECT_EQ(id, statement->statement()->ColumnInt64(0));
// The search time was updated.
EXPECT_EQ(ToDatabaseTime(search_row.search_time()),
statement->statement()->ColumnInt64(1));
// The search term was updated.
EXPECT_EQ(update_term, statement->statement()->ColumnString16(2));
EXPECT_FALSE(statement->statement()->Step());
// Only update the search time.
SearchRow update_time;
update_time.set_search_time(Time::Now());
// Update it by id.
args.clear();
std::ostringstream oss;
oss << id;
args.push_back(UTF8ToUTF16(oss.str()));
update_count = 0;
ASSERT_TRUE(backend->UpdateSearchTerms(update_time, "_id = ?", args,
&update_count));
EXPECT_EQ(1, update_count);
// Verify the update.
statement.reset(backend->QuerySearchTerms(projections, "_id = ?", args,
std::string()));
ASSERT_TRUE(statement.get());
ASSERT_TRUE(statement->statement()->Step());
// The id didn't change.
EXPECT_EQ(id, statement->statement()->ColumnInt64(0));
// The search time was updated.
EXPECT_EQ(ToDatabaseTime(update_time.search_time()),
statement->statement()->ColumnInt64(1));
// The search term didn't change.
EXPECT_EQ(update_term, statement->statement()->ColumnString16(2));
EXPECT_FALSE(statement->statement()->Step());
}
TEST_F(AndroidProviderBackendTest, DeleteSearchTerms) {
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
// Insert a keyword.
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_title(UTF8ToUTF16("cnn"));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
string16 term = UTF8ToUTF16("Search term 1");
URLID url_id = history_db_.GetRowForURL(row1.url(), NULL);
ASSERT_TRUE(url_id);
ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term));
// Get the SearchTermID of the row we just inserted.
std::vector<SearchRow::ColumnID> projections;
projections.push_back(SearchRow::ID);
projections.push_back(SearchRow::SEARCH_TIME);
projections.push_back(SearchRow::SEARCH_TERM);
std::vector<string16> args;
args.push_back(term);
scoped_ptr<AndroidStatement> statement(backend->QuerySearchTerms(
projections, "search = ?", args, std::string()));
ASSERT_TRUE(statement.get());
ASSERT_TRUE(statement->statement()->Step());
SearchTermID id1 = statement->statement()->ColumnInt64(0);
ASSERT_TRUE(id1);
EXPECT_FALSE(statement->statement()->Step());
// Insert a keyword.
HistoryAndBookmarkRow row2;
row2.set_raw_url("google.com");
row2.set_url(GURL("http://google.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row2.set_title(UTF8ToUTF16("google"));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row2));
string16 term2 = UTF8ToUTF16("Search term 2");
URLID url_id2 = history_db_.GetRowForURL(row2.url(), NULL);
ASSERT_TRUE(url_id2);
ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id2, 1, term2));
// Get the SearchTermID of the row we just inserted.
projections.clear();
projections.push_back(SearchRow::ID);
projections.push_back(SearchRow::SEARCH_TIME);
projections.push_back(SearchRow::SEARCH_TERM);
args.clear();
args.push_back(term2);
statement.reset(backend->QuerySearchTerms(projections, "search = ?", args,
std::string()));
ASSERT_TRUE(statement.get());
ASSERT_TRUE(statement->statement()->Step());
SearchTermID id2 = statement->statement()->ColumnInt64(0);
ASSERT_TRUE(id2);
EXPECT_FALSE(statement->statement()->Step());
// Delete the first one.
args.clear();
args.push_back(term);
int deleted_count = 0;
ASSERT_TRUE(backend->DeleteSearchTerms("search = ?", args, &deleted_count));
EXPECT_EQ(1, deleted_count);
std::vector<KeywordSearchTermRow> rows;
ASSERT_TRUE(history_db_.GetKeywordSearchTermRows(term, &rows));
EXPECT_TRUE(rows.empty());
// Verify we can't get the first term.
args.clear();
std::ostringstream oss;
oss << id1;
args.push_back(UTF8ToUTF16(oss.str()));
statement.reset(backend->QuerySearchTerms(projections, "_id = ?", args,
std::string()));
ASSERT_TRUE(statement.get());
EXPECT_FALSE(statement->statement()->Step());
// The second one is still there.
args.clear();
std::ostringstream oss1;
oss1 << id2;
args.push_back(UTF8ToUTF16(oss1.str()));
statement.reset(backend->QuerySearchTerms(projections, "_id = ?", args,
std::string()));
ASSERT_TRUE(statement.get());
EXPECT_TRUE(statement->statement()->Step());
EXPECT_EQ(id2, statement->statement()->ColumnInt64(0));
EXPECT_FALSE(statement->statement()->Step());
// Remove all search terms in no condition.
deleted_count = 0;
args.clear();
ASSERT_TRUE(backend->DeleteSearchTerms(std::string(), args, &deleted_count));
EXPECT_EQ(1, deleted_count);
// Verify the second one was removed.
args.clear();
args.push_back(UTF8ToUTF16(oss1.str()));
statement.reset(backend->QuerySearchTerms(projections, "_id = ?", args,
std::string()));
ASSERT_TRUE(statement.get());
EXPECT_FALSE(statement->statement()->Step());
}
TEST_F(AndroidProviderBackendTest, InsertSearchTerm) {
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
SearchRow search_row;
search_row.set_search_term(UTF8ToUTF16("google"));
search_row.set_url(GURL("http://google.com"));
search_row.set_template_url_id(1);
search_row.set_search_time(Time::Now() - TimeDelta::FromHours(1));
SearchTermID id = backend->InsertSearchTerm(search_row);
ASSERT_TRUE(id);
std::vector<SearchRow::ColumnID> projections;
projections.push_back(SearchRow::ID);
projections.push_back(SearchRow::SEARCH_TIME);
projections.push_back(SearchRow::SEARCH_TERM);
std::vector<string16> args;
std::ostringstream oss;
oss << id;
args.push_back(UTF8ToUTF16(oss.str()));
scoped_ptr<AndroidStatement> statement(backend->QuerySearchTerms(
projections, "_id = ?", args, std::string()));
ASSERT_TRUE(statement.get());
ASSERT_TRUE(statement->statement()->Step());
EXPECT_EQ(id, statement->statement()->ColumnInt64(0));
EXPECT_EQ(ToDatabaseTime(search_row.search_time()),
statement->statement()->ColumnInt64(1));
EXPECT_EQ(search_row.search_term(),
statement->statement()->ColumnString16(2));
EXPECT_FALSE(statement->statement()->Step());
}
TEST_F(AndroidProviderBackendTest, DeleteHistory) {
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_is_bookmark(false);
row2.set_title(UTF8ToUTF16("example"));
std::vector<unsigned char> data;
data.push_back('1');
row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
ASSERT_TRUE(id1);
AndroidURLID id2 = backend->InsertHistoryAndBookmark(row2);
ASSERT_TRUE(id2);
// Verify the row1 has been added in bookmark model.
content::RunAllPendingInMessageLoop();
ASSERT_EQ(1, bookmark_model_->mobile_node()->child_count());
const BookmarkNode* child = bookmark_model_->mobile_node()->GetChild(0);
ASSERT_TRUE(child);
EXPECT_EQ(row1.title(), child->GetTitle());
EXPECT_EQ(row1.url(), child->url());
// Delete history
int deleted_count = 0;
ASSERT_TRUE(backend->DeleteHistory(std::string(), std::vector<string16>(),
&deleted_count));
EXPECT_EQ(2, deleted_count);
// The row2 was deleted.
EXPECT_FALSE(history_db_.GetRowForURL(row2.url(), NULL));
// Still find the row1.
URLRow url_row;
ASSERT_TRUE(history_db_.GetRowForURL(row1.url(), &url_row));
// The visit_count was reset.
EXPECT_EQ(0, url_row.visit_count());
EXPECT_EQ(Time::UnixEpoch(), url_row.last_visit());
// Verify the row1 is still in bookmark model.
content::RunAllPendingInMessageLoop();
ASSERT_EQ(1, bookmark_model_->mobile_node()->child_count());
const BookmarkNode* child1 = bookmark_model_->mobile_node()->GetChild(0);
ASSERT_TRUE(child1);
EXPECT_EQ(row1.title(), child1->GetTitle());
EXPECT_EQ(row1.url(), child1->url());
// Verify notification
ASSERT_TRUE(delegate_.deleted_details());
ASSERT_EQ(2u, delegate_.deleted_details()->rows.size());
EXPECT_EQ(row1.url(),
delegate_.modified_details()->changed_urls[0].url());
EXPECT_EQ(Time::UnixEpoch(),
delegate_.modified_details()->changed_urls[0].last_visit());
EXPECT_EQ(1u, delegate_.favicon_details()->urls.size());
}
TEST_F(AndroidProviderBackendTest, TestMultipleNestingTransaction) {
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
// Create the nested transactions.
history_db_.BeginTransaction();
history_db_.BeginTransaction();
history_db_.BeginTransaction();
thumbnail_db_.BeginTransaction();
thumbnail_db_.BeginTransaction();
int history_transaction = history_db_.transaction_nesting();
int thumbnail_transaction = thumbnail_db_.transaction_nesting();
// Insert a row to verify the transaction number are not changed
// after a transaction commit.
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_title(UTF8ToUTF16("cnn"));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
EXPECT_EQ(history_transaction, history_db_.transaction_nesting());
EXPECT_EQ(thumbnail_transaction, thumbnail_db_.transaction_nesting());
// Insert the same URL, it should failed. The transaction are still same
// after a rollback.
ASSERT_FALSE(backend->InsertHistoryAndBookmark(row1));
EXPECT_EQ(history_transaction, history_db_.transaction_nesting());
EXPECT_EQ(thumbnail_transaction, thumbnail_db_.transaction_nesting());
// Insert another row to verify we are still fine after the previous
// rollback.
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_is_bookmark(false);
row2.set_title(UTF8ToUTF16("example"));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row2));
EXPECT_EQ(history_transaction, history_db_.transaction_nesting());
EXPECT_EQ(thumbnail_transaction, thumbnail_db_.transaction_nesting());
}
TEST_F(AndroidProviderBackendTest, TestAndroidCTSComplianceForZeroVisitCount) {
// This is to verify the last visit time and created time are same when visit
// count is 0.
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
URLRow url_row(GURL("http://www.google.com"));
url_row.set_last_visit(Time::Now());
url_row.set_visit_count(0);
history_db_.AddURL(url_row);
std::vector<HistoryAndBookmarkRow::ColumnID> projections;
projections.push_back(HistoryAndBookmarkRow::ID);
projections.push_back(HistoryAndBookmarkRow::URL);
projections.push_back(HistoryAndBookmarkRow::TITLE);
projections.push_back(HistoryAndBookmarkRow::CREATED);
projections.push_back(HistoryAndBookmarkRow::LAST_VISIT_TIME);
projections.push_back(HistoryAndBookmarkRow::VISIT_COUNT);
projections.push_back(HistoryAndBookmarkRow::FAVICON);
projections.push_back(HistoryAndBookmarkRow::BOOKMARK);
scoped_ptr<AndroidStatement> statement(backend->QueryHistoryAndBookmarks(
projections, std::string(), std::vector<string16>(),
std::string("url ASC")));
ASSERT_TRUE(statement->statement()->Step());
EXPECT_EQ(ToDatabaseTime(url_row.last_visit()),
statement->statement()->ColumnInt64(3));
EXPECT_EQ(ToDatabaseTime(url_row.last_visit()),
statement->statement()->ColumnInt64(4));
EXPECT_EQ(url_row.visit_count(), statement->statement()->ColumnInt(5));
}
TEST_F(AndroidProviderBackendTest, AndroidCTSComplianceFolderColumnExists) {
// This is test is used to verify the 'folder' column exists, all bookmarks
// returned when folder is 0 and the non bookmark rows returned when folder
// is 1.
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
&thumbnail_db_, bookmark_model_, &delegate_));
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_is_bookmark(false);
row2.set_title(UTF8ToUTF16("example"));
std::vector<unsigned char> data;
data.push_back('1');
row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
ASSERT_TRUE(id1);
AndroidURLID id2 = backend->InsertHistoryAndBookmark(row2);
ASSERT_TRUE(id2);
content::RunAllPendingInMessageLoop();
// Query by folder=0, the row1 should returned.
std::vector<HistoryAndBookmarkRow::ColumnID> projections;
projections.push_back(HistoryAndBookmarkRow::URL);
scoped_ptr<AndroidStatement> statement(backend->QueryHistoryAndBookmarks(
projections, std::string("folder=0"), std::vector<string16>(),
std::string("url ASC")));
ASSERT_TRUE(statement->statement()->Step());
EXPECT_EQ(row1.raw_url(), statement->statement()->ColumnString(0));
EXPECT_FALSE(statement->statement()->Step());
// Query by folder=1, the row2 should returned.
statement.reset(backend->QueryHistoryAndBookmarks(
projections, std::string("folder=1"), std::vector<string16>(),
std::string("url ASC")));
ASSERT_TRUE(statement->statement()->Step());
EXPECT_EQ(row2.url(), GURL(statement->statement()->ColumnString(0)));
EXPECT_FALSE(statement->statement()->Step());
}
TEST_F(AndroidProviderBackendTest, QueryWithoutThumbnailDB) {
GURL url1("http://www.cnn.com");
URLID url_id1 = 0;
const string16 title1(UTF8ToUTF16("cnn"));
std::vector<VisitInfo> visits1;
Time last_visited1 = Time::Now() - TimeDelta::FromDays(1);
Time created1 = last_visited1 - TimeDelta::FromDays(20);
visits1.push_back(VisitInfo(created1, content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(last_visited1 - TimeDelta::FromDays(1),
content::PAGE_TRANSITION_LINK));
visits1.push_back(VisitInfo(last_visited1, content::PAGE_TRANSITION_LINK));
GURL url2("http://www.example.com");
URLID url_id2 = 0;
std::vector<VisitInfo> visits2;
const string16 title2(UTF8ToUTF16("example"));
Time last_visited2 = Time::Now();
Time created2 = last_visited2 - TimeDelta::FromDays(10);
visits2.push_back(VisitInfo(created2, content::PAGE_TRANSITION_LINK));
visits2.push_back(VisitInfo(last_visited2 - TimeDelta::FromDays(5),
content::PAGE_TRANSITION_LINK));
visits2.push_back(VisitInfo(last_visited2, content::PAGE_TRANSITION_LINK));
// Only use the HistoryBackend to generate the test data.
// HistoryBackend will shutdown after that.
{
scoped_refptr<HistoryBackend> history_backend;
history_backend = new HistoryBackend(temp_dir_.path(), 0,
new AndroidProviderBackendDelegate(), bookmark_model_);
history_backend->Init(std::string(), false);
history_backend->AddVisits(url1, visits1, history::SOURCE_SYNCED);
history_backend->AddVisits(url2, visits2, history::SOURCE_SYNCED);
URLRow url_row;
ASSERT_TRUE(history_backend->GetURL(url1, &url_row));
url_id1 = url_row.id();
url_row.set_title(title1);
ASSERT_TRUE(history_backend->UpdateURL(url_id1, url_row));
ASSERT_TRUE(history_backend->GetURL(url2, &url_row));
url_id2 = url_row.id();
url_row.set_title(title2);
ASSERT_TRUE(history_backend->UpdateURL(url_id2, url_row));
// Set favicon to url2.
std::vector<unsigned char> data;
data.push_back('1');
chrome::FaviconBitmapData bitmap_data_element;
bitmap_data_element.bitmap_data = new base::RefCountedBytes(data);
bitmap_data_element.pixel_size = gfx::Size();
bitmap_data_element.icon_url = GURL();
std::vector<chrome::FaviconBitmapData> favicon_bitmap_data;
favicon_bitmap_data.push_back(bitmap_data_element);
history_backend->SetFavicons(url2, chrome::FAVICON, favicon_bitmap_data);
history_backend->Closing();
}
// The history_db_name and thumbnail_db_name files should be created by
// HistoryBackend. We need to open the same database files.
ASSERT_TRUE(base::PathExists(history_db_name_));
ASSERT_TRUE(base::PathExists(thumbnail_db_name_));
// Only creates the history database
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
// Set url1 as bookmark.
AddBookmark(url1);
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_, NULL,
bookmark_model_, &delegate_));
std::vector<HistoryAndBookmarkRow::ColumnID> projections;
projections.push_back(HistoryAndBookmarkRow::ID);
projections.push_back(HistoryAndBookmarkRow::URL);
projections.push_back(HistoryAndBookmarkRow::TITLE);
projections.push_back(HistoryAndBookmarkRow::CREATED);
projections.push_back(HistoryAndBookmarkRow::LAST_VISIT_TIME);
projections.push_back(HistoryAndBookmarkRow::VISIT_COUNT);
projections.push_back(HistoryAndBookmarkRow::FAVICON);
projections.push_back(HistoryAndBookmarkRow::BOOKMARK);
scoped_ptr<AndroidStatement> statement(backend->QueryHistoryAndBookmarks(
projections, std::string(), std::vector<string16>(),
std::string("url ASC")));
ASSERT_TRUE(statement->statement()->Step());
ASSERT_EQ(url1, GURL(statement->statement()->ColumnString(1)));
EXPECT_EQ(title1, statement->statement()->ColumnString16(2));
EXPECT_EQ(ToDatabaseTime(created1),
statement->statement()->ColumnInt64(3));
EXPECT_EQ(ToDatabaseTime(last_visited1),
statement->statement()->ColumnInt64(4));
EXPECT_EQ(3, statement->statement()->ColumnInt(5));
EXPECT_EQ(6, statement->favicon_index());
// No favicon.
EXPECT_EQ(0, statement->statement()->ColumnByteLength(6));
EXPECT_TRUE(statement->statement()->ColumnBool(7));
ASSERT_TRUE(statement->statement()->Step());
EXPECT_EQ(title2, statement->statement()->ColumnString16(2));
ASSERT_EQ(url2, GURL(statement->statement()->ColumnString(1)));
EXPECT_EQ(ToDatabaseTime(created2),
statement->statement()->ColumnInt64(3));
EXPECT_EQ(ToDatabaseTime(last_visited2),
statement->statement()->ColumnInt64(4));
EXPECT_EQ(3, statement->statement()->ColumnInt(5));
std::vector<unsigned char> favicon2;
EXPECT_EQ(6, statement->favicon_index());
// No favicon because thumbnail database wasn't initialized.
EXPECT_EQ(0, statement->statement()->ColumnByteLength(6));
EXPECT_FALSE(statement->statement()->ColumnBool(7));
// No more row.
EXPECT_FALSE(statement->statement()->Step());
}
TEST_F(AndroidProviderBackendTest, InsertWithoutThumbnailDB) {
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_is_bookmark(false);
row2.set_title(UTF8ToUTF16("example"));
std::vector<unsigned char> data;
data.push_back('1');
row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_, NULL,
bookmark_model_, &delegate_));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
EXPECT_FALSE(delegate_.deleted_details());
ASSERT_TRUE(delegate_.modified_details());
ASSERT_EQ(1u, delegate_.modified_details()->changed_urls.size());
EXPECT_EQ(row1.url(), delegate_.modified_details()->changed_urls[0].url());
EXPECT_EQ(row1.last_visit_time(),
delegate_.modified_details()->changed_urls[0].last_visit());
EXPECT_EQ(row1.visit_count(),
delegate_.modified_details()->changed_urls[0].visit_count());
EXPECT_EQ(row1.title(),
delegate_.modified_details()->changed_urls[0].title());
EXPECT_FALSE(delegate_.favicon_details());
content::RunAllPendingInMessageLoop();
ASSERT_EQ(1, bookmark_model_->mobile_node()->child_count());
const BookmarkNode* child = bookmark_model_->mobile_node()->GetChild(0);
ASSERT_TRUE(child);
EXPECT_EQ(row1.title(), child->GetTitle());
EXPECT_EQ(row1.url(), child->url());
delegate_.ResetDetails();
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row2));
EXPECT_FALSE(delegate_.deleted_details());
ASSERT_TRUE(delegate_.modified_details());
ASSERT_EQ(1u, delegate_.modified_details()->changed_urls.size());
EXPECT_EQ(row2.url(), delegate_.modified_details()->changed_urls[0].url());
EXPECT_EQ(row2.last_visit_time(),
delegate_.modified_details()->changed_urls[0].last_visit());
EXPECT_EQ(row2.title(),
delegate_.modified_details()->changed_urls[0].title());
// Favicon details is still false because thumbnail database wasn't
// initialized, we ignore any changes of favicon.
ASSERT_FALSE(delegate_.favicon_details());
}
TEST_F(AndroidProviderBackendTest, DeleteWithoutThumbnailDB) {
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
HistoryAndBookmarkRow row2;
row2.set_raw_url("http://www.example.com");
row2.set_url(GURL("http://www.example.com"));
row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(10));
row2.set_is_bookmark(false);
row2.set_title(UTF8ToUTF16("example"));
std::vector<unsigned char> data;
data.push_back('1');
row2.set_favicon(base::RefCountedBytes::TakeVector(&data));
{
HistoryDatabase history_db;
ThumbnailDatabase thumbnail_db;
ASSERT_EQ(sql::INIT_OK, history_db.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db,
&thumbnail_db, bookmark_model_, &delegate_));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row1));
ASSERT_TRUE(backend->InsertHistoryAndBookmark(row2));
// Verify the row1 has been added in bookmark model.
content::RunAllPendingInMessageLoop();
ASSERT_EQ(1, bookmark_model_->mobile_node()->child_count());
const BookmarkNode* child = bookmark_model_->mobile_node()->GetChild(0);
ASSERT_TRUE(child);
EXPECT_EQ(row1.title(), child->GetTitle());
EXPECT_EQ(row1.url(), child->url());
}
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_,
NULL, bookmark_model_, &delegate_));
// Delete all rows.
std::vector<string16> args;
int deleted_count = 0;
delegate_.ResetDetails();
ASSERT_TRUE(backend->DeleteHistoryAndBookmarks("Favicon IS NULL", args,
&deleted_count));
// All rows were deleted.
EXPECT_EQ(2, deleted_count);
// Verify the rows was removed from bookmark model.
content::RunAllPendingInMessageLoop();
ASSERT_EQ(0, bookmark_model_->mobile_node()->child_count());
// Verify notifications
ASSERT_TRUE(delegate_.deleted_details());
EXPECT_FALSE(delegate_.modified_details());
EXPECT_EQ(2u, delegate_.deleted_details()->rows.size());
// No favicon has been deleted.
EXPECT_FALSE(delegate_.favicon_details());
// No row exists.
std::vector<HistoryAndBookmarkRow::ColumnID> projections;
projections.push_back(HistoryAndBookmarkRow::ID);
projections.push_back(HistoryAndBookmarkRow::URL);
projections.push_back(HistoryAndBookmarkRow::TITLE);
projections.push_back(HistoryAndBookmarkRow::CREATED);
projections.push_back(HistoryAndBookmarkRow::LAST_VISIT_TIME);
projections.push_back(HistoryAndBookmarkRow::VISIT_COUNT);
projections.push_back(HistoryAndBookmarkRow::FAVICON);
projections.push_back(HistoryAndBookmarkRow::BOOKMARK);
scoped_ptr<AndroidStatement> statement1(backend->QueryHistoryAndBookmarks(
projections, std::string(), std::vector<string16>(),
std::string("url ASC")));
ASSERT_FALSE(statement1->statement()->Step());
}
TEST_F(AndroidProviderBackendTest, UpdateFaviconWithoutThumbnail) {
HistoryAndBookmarkRow row1;
row1.set_raw_url("cnn.com");
row1.set_url(GURL("http://cnn.com"));
row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
row1.set_created(Time::Now() - TimeDelta::FromDays(20));
row1.set_visit_count(10);
row1.set_is_bookmark(true);
row1.set_title(UTF8ToUTF16("cnn"));
{
HistoryDatabase history_db;
ThumbnailDatabase thumbnail_db;
ASSERT_EQ(sql::INIT_OK, history_db.Init(history_db_name_));
ASSERT_EQ(sql::INIT_OK, thumbnail_db.Init(thumbnail_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db,
&thumbnail_db, bookmark_model_, &delegate_));
AndroidURLID id1 = backend->InsertHistoryAndBookmark(row1);
ASSERT_TRUE(id1);
}
ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_));
scoped_ptr<AndroidProviderBackend> backend(
new AndroidProviderBackend(android_cache_db_name_, &history_db_, NULL,
bookmark_model_, &delegate_));
int update_count;
std::vector<string16> update_args;
// Update the last visit time to a value greater than current one.
HistoryAndBookmarkRow update_row1;
// Set visit count.
update_row1.set_visit_count(5);
// Set favicon.
std::vector<unsigned char> data;
data.push_back('1');
update_row1.set_favicon(base::RefCountedBytes::TakeVector(&data));
update_args.push_back(UTF8ToUTF16(row1.raw_url()));
delegate_.ResetDetails();
ASSERT_TRUE(backend->UpdateHistoryAndBookmarks(update_row1, "url = ?",
update_args, &update_count));
// Verify notifications.
EXPECT_FALSE(delegate_.deleted_details());
ASSERT_TRUE(delegate_.modified_details());
ASSERT_EQ(1u, delegate_.modified_details()->changed_urls.size());
// No favicon will be updated as thumbnail database is missing.
EXPECT_FALSE(delegate_.favicon_details());
}
} // namespace history