This source file includes following definitions.
- IsExpiredVisitTime
- GetMostRecentVisitsForURL
- SetVisitsForUrl
- DeleteVisitsForUrl
- URLsEqual
- SetUp
- MakeTypedUrlRow
- AddNewestVisit
- AddOldestVisit
- InitiateServerState
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
#include "chrome/browser/history/typed_url_syncable_service.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/history/history_backend.h"
#include "chrome/browser/history/history_types.h"
#include "content/public/browser/notification_types.h"
#include "sync/api/fake_sync_change_processor.h"
#include "sync/api/sync_change_processor_wrapper_for_test.h"
#include "sync/api/sync_error.h"
#include "sync/api/sync_error_factory_mock.h"
#include "sync/protocol/sync.pb.h"
#include "sync/protocol/typed_url_specifics.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
using history::HistoryBackend;
using history::URLID;
using history::URLRow;
using history::URLRows;
using history::VisitRow;
using history::VisitVector;
namespace {
const int kMaxTypedUrlVisits = 100;
const int EXPIRED_VISIT = -1;
class TestHistoryBackend : public HistoryBackend {
public:
TestHistoryBackend() : HistoryBackend(base::FilePath(), NULL, NULL) {}
virtual bool IsExpiredVisitTime(const base::Time& time) OVERRIDE {
return time.ToInternalValue() == EXPIRED_VISIT;
}
virtual bool GetMostRecentVisitsForURL(
URLID id,
int max_visits,
VisitVector* visits) OVERRIDE {
if (local_db_visits_[id].empty())
return false;
visits->insert(visits->end(),
local_db_visits_[id].begin(),
local_db_visits_[id].end());
return true;
}
void SetVisitsForUrl(URLID id, VisitVector* visits) {
if (!local_db_visits_[id].empty()) {
local_db_visits_[id].clear();
}
local_db_visits_[id].insert(local_db_visits_[id].end(),
visits->begin(),
visits->end());
}
void DeleteVisitsForUrl(const URLID& id) {
local_db_visits_.erase(id);
}
private:
virtual ~TestHistoryBackend() {}
std::map<URLID, VisitVector> local_db_visits_;
};
}
namespace history {
class TypedUrlSyncableServiceTest : public testing::Test {
public:
static URLRow MakeTypedUrlRow(const char* url,
const char* title,
int typed_count,
int64 last_visit,
bool hidden,
VisitVector* visits);
static void AddNewestVisit(URLRow* url,
VisitVector* visits,
content::PageTransition transition,
int64 visit_time);
static void AddOldestVisit(URLRow* url,
VisitVector* visits,
content::PageTransition transition,
int64 visit_time);
static bool URLsEqual(URLRow& row,
sync_pb::TypedUrlSpecifics& specifics) {
return ((row.url().spec().compare(specifics.url()) == 0) &&
(base::UTF16ToUTF8(row.title()).compare(specifics.title()) == 0) &&
(row.hidden() == specifics.hidden()));
}
bool InitiateServerState(unsigned int num_typed_urls,
unsigned int num_reload_urls,
URLRows* rows,
std::vector<VisitVector>* visit_vectors,
const std::vector<const char*>& urls);
protected:
TypedUrlSyncableServiceTest() {}
virtual ~TypedUrlSyncableServiceTest() {}
virtual void SetUp() OVERRIDE {
fake_history_backend_ = new TestHistoryBackend();
typed_url_sync_service_.reset(
new TypedUrlSyncableService(fake_history_backend_.get()));
fake_change_processor_.reset(new syncer::FakeSyncChangeProcessor);
}
scoped_refptr<HistoryBackend> fake_history_backend_;
scoped_ptr<TypedUrlSyncableService> typed_url_sync_service_;
scoped_ptr<syncer::FakeSyncChangeProcessor> fake_change_processor_;
};
URLRow TypedUrlSyncableServiceTest::MakeTypedUrlRow(
const char* url,
const char* title,
int typed_count,
int64 last_visit,
bool hidden,
VisitVector* visits) {
DCHECK(visits->empty());
static int unique_url_id = 0;
GURL gurl(url);
URLRow history_url(gurl, ++unique_url_id);
history_url.set_title(base::UTF8ToUTF16(title));
history_url.set_typed_count(typed_count);
history_url.set_hidden(hidden);
base::Time last_visit_time = base::Time::FromInternalValue(last_visit);
history_url.set_last_visit(last_visit_time);
VisitVector::iterator first = visits->begin();
if (typed_count > 0) {
visits->insert(first,
VisitRow(history_url.id(), last_visit_time, 0,
content::PAGE_TRANSITION_TYPED, 0));
} else {
visits->insert(first,
VisitRow(history_url.id(), last_visit_time, 0,
content::PAGE_TRANSITION_RELOAD, 0));
}
history_url.set_visit_count(visits->size());
return history_url;
}
void TypedUrlSyncableServiceTest::AddNewestVisit(
URLRow* url,
VisitVector* visits,
content::PageTransition transition,
int64 visit_time) {
base::Time time = base::Time::FromInternalValue(visit_time);
visits->insert(visits->begin(),
VisitRow(url->id(), time, 0, transition, 0));
if (transition == content::PAGE_TRANSITION_TYPED) {
url->set_typed_count(url->typed_count() + 1);
}
url->set_last_visit(time);
url->set_visit_count(visits->size());
}
void TypedUrlSyncableServiceTest::AddOldestVisit(
URLRow* url,
VisitVector* visits,
content::PageTransition transition,
int64 visit_time) {
base::Time time = base::Time::FromInternalValue(visit_time);
visits->push_back(VisitRow(url->id(), time, 0, transition, 0));
if (transition == content::PAGE_TRANSITION_TYPED) {
url->set_typed_count(url->typed_count() + 1);
}
url->set_visit_count(visits->size());
}
bool TypedUrlSyncableServiceTest::InitiateServerState(
unsigned int num_typed_urls,
unsigned int num_reload_urls,
URLRows* rows,
std::vector<VisitVector>* visit_vectors,
const std::vector<const char*>& urls) {
unsigned int total_urls = num_typed_urls + num_reload_urls;
DCHECK(urls.size() >= total_urls);
if (!typed_url_sync_service_.get())
return false;
syncer::SyncMergeResult result =
typed_url_sync_service_->MergeDataAndStartSyncing(
syncer::TYPED_URLS,
syncer::SyncDataList(),
scoped_ptr<syncer::SyncChangeProcessor>(
new syncer::SyncChangeProcessorWrapperForTest(
fake_change_processor_.get())),
scoped_ptr<syncer::SyncErrorFactory>(
new syncer::SyncErrorFactoryMock()));
EXPECT_FALSE(result.error().IsSet()) << result.error().message();
if (total_urls) {
URLRows changed_urls;
for (unsigned int i = 0; i < total_urls; ++i) {
int typed = i < num_typed_urls ? 1 : 0;
VisitVector visits;
visit_vectors->push_back(visits);
rows->push_back(MakeTypedUrlRow(
urls[i], "pie", typed, i + 3, false, &visit_vectors->back()));
static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
SetVisitsForUrl(rows->back().id(), &visit_vectors->back());
changed_urls.push_back(rows->back());
}
typed_url_sync_service_->OnUrlsModified(&changed_urls);
}
if (num_typed_urls != fake_change_processor_->changes().size())
return false;
return true;
}
TEST_F(TypedUrlSyncableServiceTest, AddLocalTypedUrlAndSync) {
URLRows url_rows;
std::vector<VisitVector> visit_vectors;
std::vector<const char*> urls;
urls.push_back("http://pie.com/");
ASSERT_TRUE(InitiateServerState(1, 0, &url_rows, &visit_vectors, urls));
URLRow url_row = url_rows.front();
VisitVector visits = visit_vectors.front();
const syncer::SyncChangeList& changes = fake_change_processor_->changes();
ASSERT_EQ(1u, changes.size());
ASSERT_TRUE(changes[0].IsValid());
EXPECT_EQ(syncer::TYPED_URLS, changes[0].sync_data().GetDataType());
EXPECT_EQ(syncer::SyncChange::ACTION_ADD, changes[0].change_type());
sync_pb::TypedUrlSpecifics url_specifics =
changes[0].sync_data().GetSpecifics().typed_url();
EXPECT_TRUE(URLsEqual(url_row, url_specifics));
ASSERT_EQ(1, url_specifics.visits_size());
ASSERT_EQ(static_cast<const int>(visits.size()), url_specifics.visits_size());
EXPECT_EQ(visits[0].visit_time.ToInternalValue(), url_specifics.visits(0));
EXPECT_EQ(static_cast<const int>(visits[0].transition),
url_specifics.visit_transitions(0));
std::set<GURL> sync_state;
typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
EXPECT_FALSE(sync_state.empty());
EXPECT_EQ(1u, sync_state.size());
EXPECT_TRUE(sync_state.end() != sync_state.find(url_row.url()));
}
TEST_F(TypedUrlSyncableServiceTest, UpdateLocalTypedUrlAndSync) {
URLRows url_rows;
std::vector<VisitVector> visit_vectors;
std::vector<const char*> urls;
urls.push_back("http://pie.com/");
ASSERT_TRUE(InitiateServerState(1, 0, &url_rows, &visit_vectors, urls));
syncer::SyncChangeList& changes = fake_change_processor_->changes();
changes.clear();
URLRow url_row = url_rows.front();
VisitVector visits = visit_vectors.front();
URLRows changed_urls;
AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_TYPED, 7);
static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
SetVisitsForUrl(url_row.id(), &visits);
changed_urls.push_back(url_row);
typed_url_sync_service_->OnUrlsModified(&changed_urls);
ASSERT_EQ(1u, changes.size());
ASSERT_TRUE(changes[0].IsValid());
EXPECT_EQ(syncer::TYPED_URLS, changes[0].sync_data().GetDataType());
EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
sync_pb::TypedUrlSpecifics url_specifics =
changes[0].sync_data().GetSpecifics().typed_url();
EXPECT_TRUE(URLsEqual(url_row, url_specifics));
ASSERT_EQ(2, url_specifics.visits_size());
ASSERT_EQ(static_cast<const int>(visits.size()), url_specifics.visits_size());
EXPECT_EQ(visits[0].visit_time.ToInternalValue(), url_specifics.visits(1));
EXPECT_EQ(static_cast<const int>(visits[0].transition),
url_specifics.visit_transitions(1));
EXPECT_EQ(visits[1].visit_time.ToInternalValue(), url_specifics.visits(0));
EXPECT_EQ(static_cast<const int>(visits[1].transition),
url_specifics.visit_transitions(0));
std::set<GURL> sync_state;
typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
EXPECT_FALSE(sync_state.empty());
EXPECT_EQ(1u, sync_state.size());
EXPECT_TRUE(sync_state.end() != sync_state.find(url_row.url()));
}
TEST_F(TypedUrlSyncableServiceTest, LinkVisitLocalTypedUrlAndSync) {
URLRows url_rows;
std::vector<VisitVector> visit_vectors;
std::vector<const char*> urls;
urls.push_back("http://pie.com/");
ASSERT_TRUE(InitiateServerState(1, 0, &url_rows, &visit_vectors, urls));
syncer::SyncChangeList& changes = fake_change_processor_->changes();
changes.clear();
URLRow url_row = url_rows.front();
VisitVector visits = visit_vectors.front();
AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_LINK, 6);
static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
SetVisitsForUrl(url_row.id(), &visits);
content::PageTransition transition = content::PAGE_TRANSITION_LINK;
typed_url_sync_service_->OnUrlVisited(transition, &url_row);
ASSERT_EQ(0u, changes.size());
}
TEST_F(TypedUrlSyncableServiceTest, TypedVisitLocalTypedUrlAndSync) {
URLRows url_rows;
std::vector<VisitVector> visit_vectors;
std::vector<const char*> urls;
urls.push_back("http://pie.com/");
ASSERT_TRUE(InitiateServerState(1, 0, &url_rows, &visit_vectors, urls));
syncer::SyncChangeList& changes = fake_change_processor_->changes();
changes.clear();
URLRow url_row = url_rows.front();
VisitVector visits = visit_vectors.front();
AddOldestVisit(&url_row, &visits, content::PAGE_TRANSITION_LINK, 1);
AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_LINK, 6);
AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_TYPED, 7);
static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
SetVisitsForUrl(url_row.id(), &visits);
content::PageTransition transition = content::PAGE_TRANSITION_TYPED;
typed_url_sync_service_->OnUrlVisited(transition, &url_row);
ASSERT_EQ(1u, changes.size());
ASSERT_TRUE(changes[0].IsValid());
EXPECT_EQ(syncer::TYPED_URLS, changes[0].sync_data().GetDataType());
EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, changes[0].change_type());
sync_pb::TypedUrlSpecifics url_specifics =
changes[0].sync_data().GetSpecifics().typed_url();
EXPECT_TRUE(URLsEqual(url_row, url_specifics));
ASSERT_EQ(4u, visits.size());
EXPECT_EQ(static_cast<const int>(visits.size()), url_specifics.visits_size());
int r = url_specifics.visits_size() - 1;
for (int i = 0; i < url_specifics.visits_size(); ++i, --r) {
EXPECT_EQ(visits[i].visit_time.ToInternalValue(), url_specifics.visits(r));
EXPECT_EQ(static_cast<const int>(visits[i].transition),
url_specifics.visit_transitions(r));
}
std::set<GURL> sync_state;
typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
EXPECT_FALSE(sync_state.empty());
EXPECT_EQ(1u, sync_state.size());
EXPECT_TRUE(sync_state.end() != sync_state.find(url_row.url()));
}
TEST_F(TypedUrlSyncableServiceTest, DeleteLocalTypedUrlAndSync) {
URLRows url_rows;
std::vector<VisitVector> visit_vectors;
std::vector<const char*> urls;
urls.push_back("http://pie.com/");
urls.push_back("http://cake.com/");
urls.push_back("http://google.com/");
urls.push_back("http://foo.com/");
urls.push_back("http://bar.com/");
ASSERT_TRUE(InitiateServerState(4, 1, &url_rows, &visit_vectors, urls));
syncer::SyncChangeList& changes = fake_change_processor_->changes();
changes.clear();
std::set<GURL> sync_state;
typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
EXPECT_FALSE(sync_state.empty());
EXPECT_EQ(4u, sync_state.size());
url_rows[0].set_typed_count(0);
VisitVector visits;
static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
SetVisitsForUrl(url_rows[0].id(), &visits);
URLRows rows;
for (size_t i = 0; i < 3u; ++i) {
static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
DeleteVisitsForUrl(url_rows[i].id());
rows.push_back(url_rows[i]);
}
typed_url_sync_service_->OnUrlsDeleted(false, false, &rows);
ASSERT_EQ(3u, changes.size());
for (size_t i = 0; i < changes.size(); ++i) {
ASSERT_TRUE(changes[i].IsValid());
ASSERT_EQ(syncer::TYPED_URLS, changes[i].sync_data().GetDataType());
EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, changes[i].change_type());
sync_pb::TypedUrlSpecifics url_specifics =
changes[i].sync_data().GetSpecifics().typed_url();
EXPECT_EQ(url_rows[i].url().spec(), url_specifics.url());
}
std::set<GURL> sync_state_deleted;
typed_url_sync_service_.get()->GetSyncedUrls(&sync_state_deleted);
ASSERT_EQ(1u, sync_state_deleted.size());
EXPECT_TRUE(sync_state_deleted.end() !=
sync_state_deleted.find(url_rows[3].url()));
}
TEST_F(TypedUrlSyncableServiceTest, DeleteAllLocalTypedUrlAndSync) {
URLRows url_rows;
std::vector<VisitVector> visit_vectors;
std::vector<const char*> urls;
urls.push_back("http://pie.com/");
urls.push_back("http://cake.com/");
urls.push_back("http://google.com/");
urls.push_back("http://foo.com/");
urls.push_back("http://bar.com/");
ASSERT_TRUE(InitiateServerState(4, 1, &url_rows, &visit_vectors, urls));
syncer::SyncChangeList& changes = fake_change_processor_->changes();
changes.clear();
std::set<GURL> sync_state;
typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
EXPECT_EQ(4u, sync_state.size());
for (size_t i = 0; i < 4u; ++ i) {
static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
DeleteVisitsForUrl(url_rows[i].id());
}
bool all_history = true;
typed_url_sync_service_->OnUrlsDeleted(all_history, false, NULL);
ASSERT_EQ(4u, changes.size());
for (size_t i = 0; i < changes.size(); ++i) {
ASSERT_TRUE(changes[i].IsValid());
ASSERT_EQ(syncer::TYPED_URLS, changes[i].sync_data().GetDataType());
EXPECT_EQ(syncer::SyncChange::ACTION_DELETE, changes[i].change_type());
}
std::set<GURL> sync_state_deleted;
typed_url_sync_service_.get()->GetSyncedUrls(&sync_state_deleted);
EXPECT_TRUE(sync_state_deleted.empty());
}
TEST_F(TypedUrlSyncableServiceTest, MaxVisitLocalTypedUrlAndSync) {
URLRows url_rows;
std::vector<VisitVector> visit_vectors;
std::vector<const char*> urls;
urls.push_back("http://pie.com/");
ASSERT_TRUE(InitiateServerState(0, 1, &url_rows, &visit_vectors, urls));
URLRow url_row = url_rows.front();
VisitVector visits;
int i = 1;
for (; i <= kMaxTypedUrlVisits - 20; ++i)
AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_TYPED, i);
for (; i <= kMaxTypedUrlVisits; ++i)
AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_LINK, i);
for (; i <= kMaxTypedUrlVisits + 10; ++i)
AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_TYPED, i);
static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
SetVisitsForUrl(url_row.id(), &visits);
content::PageTransition transition = content::PAGE_TRANSITION_TYPED;
typed_url_sync_service_->OnUrlVisited(transition, &url_row);
const syncer::SyncChangeList& changes = fake_change_processor_->changes();
ASSERT_EQ(1u, changes.size());
ASSERT_TRUE(changes[0].IsValid());
sync_pb::TypedUrlSpecifics url_specifics =
changes[0].sync_data().GetSpecifics().typed_url();
ASSERT_EQ(kMaxTypedUrlVisits, url_specifics.visits_size());
int num_typed_visits_synced = 0;
int num_other_visits_synced = 0;
int r = url_specifics.visits_size() - 1;
for (int i = 0; i < url_specifics.visits_size(); ++i, --r) {
if (url_specifics.visit_transitions(i) == content::PAGE_TRANSITION_TYPED) {
++num_typed_visits_synced;
} else {
++num_other_visits_synced;
}
}
EXPECT_EQ(kMaxTypedUrlVisits - 10, num_typed_visits_synced);
EXPECT_EQ(10, num_other_visits_synced);
}
TEST_F(TypedUrlSyncableServiceTest, ThrottleVisitLocalTypedUrlSync) {
URLRows url_rows;
std::vector<VisitVector> visit_vectors;
std::vector<const char*> urls;
urls.push_back("http://pie.com/");
ASSERT_TRUE(InitiateServerState(0, 1, &url_rows, &visit_vectors, urls));
URLRow url_row = url_rows.front();
VisitVector visits;
for (int i = 1; i < 42; ++i)
AddNewestVisit(&url_row, &visits, content::PAGE_TRANSITION_TYPED, i);
static_cast<TestHistoryBackend*>(fake_history_backend_.get())->
SetVisitsForUrl(url_row.id(), &visits);
content::PageTransition transition = content::PAGE_TRANSITION_TYPED;
typed_url_sync_service_->OnUrlVisited(transition, &url_row);
const syncer::SyncChangeList& changes = fake_change_processor_->changes();
ASSERT_EQ(0u, changes.size());
std::set<GURL> sync_state;
typed_url_sync_service_.get()->GetSyncedUrls(&sync_state);
EXPECT_TRUE(sync_state.empty());
}
}