| Index: components/history/core/browser/typed_url_sync_bridge_unittest.cc
|
| diff --git a/components/history/core/browser/typed_url_sync_bridge_unittest.cc b/components/history/core/browser/typed_url_sync_bridge_unittest.cc
|
| index 4a36d3aca536b3f152cbd48166ddb3955b99e1ab..e598864298f05885c687647202797e69de33a01d 100644
|
| --- a/components/history/core/browser/typed_url_sync_bridge_unittest.cc
|
| +++ b/components/history/core/browser/typed_url_sync_bridge_unittest.cc
|
| @@ -4,6 +4,7 @@
|
|
|
| #include "components/history/core/browser/typed_url_sync_bridge.h"
|
|
|
| +#include "base/big_endian.h"
|
| #include "base/files/scoped_temp_dir.h"
|
| #include "base/message_loop/message_loop.h"
|
| #include "base/strings/utf_string_conversions.h"
|
| @@ -26,12 +27,19 @@ using syncer::EntityData;
|
| using syncer::EntityDataPtr;
|
| using syncer::KeyAndData;
|
| using syncer::MetadataBatch;
|
| +using syncer::MetadataChangeList;
|
| using syncer::RecordingModelTypeChangeProcessor;
|
|
|
| namespace history {
|
|
|
| namespace {
|
|
|
| +// Constants used to limit size of visits processed. See
|
| +// equivalent constants in typed_url_sync_bridge.cc for descriptions.
|
| +const int kMaxTypedUrlVisits = 100;
|
| +const int kVisitThrottleThreshold = 10;
|
| +const int kVisitThrottleMultiple = 10;
|
| +
|
| // Visits with this timestamp are treated as expired.
|
| const int kExpiredVisit = -1;
|
|
|
| @@ -41,6 +49,49 @@ const char kTitle2[] = "cookie";
|
| const char kURL[] = "http://pie.com/";
|
| const char kURL2[] = "http://cookie.com/";
|
|
|
| +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 URLsEqual(URLRow& lhs, URLRow& rhs) {
|
| + // Only compare synced fields (ignore typed_count and visit_count as those
|
| + // are maintained by the history subsystem).
|
| + return (lhs.url().spec().compare(rhs.url().spec()) == 0) &&
|
| + (lhs.title().compare(rhs.title()) == 0) &&
|
| + (lhs.hidden() == rhs.hidden());
|
| +}
|
| +
|
| +void AddNewestVisit(ui::PageTransition transition,
|
| + int64_t visit_time,
|
| + URLRow* url,
|
| + VisitVector* visits) {
|
| + base::Time time = base::Time::FromInternalValue(visit_time);
|
| + visits->insert(visits->begin(), VisitRow(url->id(), time, 0, transition, 0));
|
| +
|
| + if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED)) {
|
| + url->set_typed_count(url->typed_count() + 1);
|
| + }
|
| +
|
| + url->set_last_visit(time);
|
| + url->set_visit_count(visits->size());
|
| +}
|
| +
|
| +void AddOldestVisit(ui::PageTransition transition,
|
| + int64_t visit_time,
|
| + URLRow* url,
|
| + VisitVector* visits) {
|
| + base::Time time = base::Time::FromInternalValue(visit_time);
|
| + visits->push_back(VisitRow(url->id(), time, 0, transition, 0));
|
| +
|
| + if (ui::PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED)) {
|
| + url->set_typed_count(url->typed_count() + 1);
|
| + }
|
| +
|
| + url->set_visit_count(visits->size());
|
| +}
|
| +
|
| // Create a new row object and the typed visit çorresponding with the time at
|
| // |last_visit| in the |visits| vector.
|
| URLRow MakeTypedUrlRow(const std::string& url,
|
| @@ -104,6 +155,12 @@ void VerifyDataBatch(std::map<std::string, TypedUrlSpecifics> expected,
|
| EXPECT_TRUE(expected.empty());
|
| }
|
|
|
| +std::string IntToStroageKey(int id) {
|
| + std::string storage_key(sizeof(URLID), 0);
|
| + base::WriteBigEndian<URLID>(&storage_key[0], id);
|
| + return storage_key;
|
| +}
|
| +
|
| class TestHistoryBackendDelegate : public HistoryBackend::Delegate {
|
| public:
|
| TestHistoryBackendDelegate() {}
|
| @@ -149,16 +206,17 @@ class TestHistoryBackend : public HistoryBackend {
|
| }
|
|
|
| void SetVisitsForUrl(URLRow& new_url, const VisitVector visits) {
|
| - std::vector<history::VisitInfo> added_visits;
|
| - URLRows new_urls;
|
| - DeleteURL(new_url.url());
|
| + if (!GetURL(new_url.url(), nullptr)) {
|
| + URLRows new_urls;
|
| + new_urls.push_back(new_url);
|
| + AddPagesWithDetails(new_urls, SOURCE_SYNCED);
|
| + }
|
| +
|
| + std::vector<VisitInfo> added_visits;
|
| for (const auto& visit : visits) {
|
| - added_visits.push_back(
|
| - history::VisitInfo(visit.visit_time, visit.transition));
|
| + added_visits.push_back(VisitInfo(visit.visit_time, visit.transition));
|
| }
|
| - new_urls.push_back(new_url);
|
| - AddPagesWithDetails(new_urls, history::SOURCE_SYNCED);
|
| - AddVisits(new_url.url(), added_visits, history::SOURCE_SYNCED);
|
| + AddVisits(new_url.url(), added_visits, SOURCE_SYNCED);
|
| new_url.set_id(GetIdByUrl(new_url.url()));
|
| }
|
|
|
| @@ -189,7 +247,10 @@ class TypedURLSyncBridgeTest : public testing::Test {
|
| fake_history_backend_->SetTypedURLSyncBridgeForTest(std::move(bridge));
|
| }
|
|
|
| - void TearDown() override { fake_history_backend_->Closing(); }
|
| + void TearDown() override {
|
| + VerifyProcessorReceivedValidEntityData();
|
| + fake_history_backend_->Closing();
|
| + }
|
|
|
| // Starts sync for |typed_url_sync_bridge_| with |initial_data| as the
|
| // initial sync data.
|
| @@ -202,6 +263,80 @@ class TypedURLSyncBridgeTest : public testing::Test {
|
| EXPECT_FALSE(error);
|
| }
|
|
|
| + bool BuildAndPushLocalChanges(unsigned int num_typed_urls,
|
| + unsigned int num_reload_urls,
|
| + const std::vector<std::string>& urls,
|
| + URLRows* rows,
|
| + std::vector<VisitVector>* visit_vectors) {
|
| + unsigned int total_urls = num_typed_urls + num_reload_urls;
|
| + DCHECK(urls.size() >= total_urls);
|
| + if (!bridge())
|
| + return false;
|
| +
|
| + if (total_urls) {
|
| + // Create new URL rows, populate the mock backend with its visits, and
|
| + // send to the sync service.
|
| + 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], kTitle, typed, i + 3, false,
|
| + &visit_vectors->back()));
|
| + fake_history_backend_->SetVisitsForUrl(rows->back(),
|
| + visit_vectors->back());
|
| + changed_urls.push_back(rows->back());
|
| + }
|
| +
|
| + bridge()->OnURLsModified(fake_history_backend_.get(), changed_urls);
|
| + }
|
| +
|
| + // Check that communication with sync was successful.
|
| + if (num_typed_urls != processor().put_multimap().size())
|
| + return false;
|
| + return true;
|
| + }
|
| +
|
| + VisitVector ApplyUrlAndVisitsChange(const std::string& url,
|
| + const std::string& title,
|
| + int typed_count,
|
| + int64_t last_visit,
|
| + bool hidden,
|
| + EntityChange::ChangeType change_type) {
|
| + VisitVector visits;
|
| + URLRow row =
|
| + MakeTypedUrlRow(url, title, typed_count, last_visit, hidden, &visits);
|
| + sync_pb::TypedUrlSpecifics typed_url_specifics;
|
| + WriteToTypedUrlSpecifics(row, visits, &typed_url_specifics);
|
| + std::unique_ptr<MetadataChangeList> metadata_changes =
|
| + bridge()->CreateMetadataChangeList();
|
| + EntityChangeList entity_changes;
|
| + switch (change_type) {
|
| + case EntityChange::ACTION_ADD:
|
| + entity_changes.push_back(EntityChange::CreateAdd(
|
| + std::string(), SpecificsToEntity(typed_url_specifics)));
|
| + break;
|
| + case EntityChange::ACTION_UPDATE:
|
| + entity_changes.push_back(
|
| + EntityChange::CreateUpdate(GetStorageKey(typed_url_specifics.url()),
|
| + SpecificsToEntity(typed_url_specifics)));
|
| + break;
|
| + case EntityChange::ACTION_DELETE:
|
| + entity_changes.push_back(EntityChange::CreateDelete(
|
| + GetStorageKey(typed_url_specifics.url())));
|
| + break;
|
| + }
|
| + bridge()->ApplySyncChanges(std::move(metadata_changes), entity_changes);
|
| + return visits;
|
| + }
|
| +
|
| + void AddObserver() {
|
| + bridge()->history_backend_observer_.Add(fake_history_backend_.get());
|
| + }
|
| +
|
| + void RemoveObserver() { bridge()->history_backend_observer_.RemoveAll(); }
|
| +
|
| // Fills |specifics| with the sync data for |url| and |visits|.
|
| static bool WriteToTypedUrlSpecifics(const URLRow& url,
|
| const VisitVector& visits,
|
| @@ -209,9 +344,8 @@ class TypedURLSyncBridgeTest : public testing::Test {
|
| return TypedURLSyncBridge::WriteToTypedUrlSpecifics(url, visits, specifics);
|
| }
|
|
|
| - std::string GetStorageKey(const TypedUrlSpecifics& specifics) {
|
| - std::string key = bridge()->GetStorageKeyInternal(specifics.url());
|
| - return key;
|
| + std::string GetStorageKey(const std::string& url) {
|
| + return bridge()->GetStorageKeyInternal(url);
|
| }
|
|
|
| EntityDataPtr SpecificsToEntity(const TypedUrlSpecifics& specifics) {
|
| @@ -226,7 +360,7 @@ class TypedURLSyncBridgeTest : public testing::Test {
|
| EntityChangeList entity_change_list;
|
| for (const auto& specifics : specifics_vector) {
|
| entity_change_list.push_back(EntityChange::CreateAdd(
|
| - GetStorageKey(specifics), SpecificsToEntity(specifics)));
|
| + GetStorageKey(specifics.url()), SpecificsToEntity(specifics)));
|
| }
|
| return entity_change_list;
|
| }
|
| @@ -235,15 +369,86 @@ class TypedURLSyncBridgeTest : public testing::Test {
|
| const std::vector<TypedUrlSpecifics>& specifics_vector) {
|
| std::map<std::string, TypedUrlSpecifics> map;
|
| for (const auto& specifics : specifics_vector) {
|
| - map[GetStorageKey(specifics)] = specifics;
|
| + map[GetStorageKey(specifics.url())] = specifics;
|
| }
|
| return map;
|
| }
|
|
|
| - void VerifyLocalHistoryData(const std::vector<TypedUrlSpecifics>& expected) {
|
| + void VerifyAllLocalHistoryData(
|
| + const std::vector<TypedUrlSpecifics>& expected) {
|
| bridge()->GetAllData(base::Bind(&VerifyDataBatch, ExpectedMap(expected)));
|
| }
|
|
|
| + void VerifyGetData(TypedURLSyncBridge::StorageKeyList storage_keys,
|
| + const std::vector<TypedUrlSpecifics>& expected) {
|
| + bridge()->GetData(storage_keys,
|
| + base::Bind(&VerifyDataBatch, ExpectedMap(expected)));
|
| + }
|
| +
|
| + void VerifyProcessorReceivedValidEntityData() {
|
| + for (const auto& it : processor().put_multimap()) {
|
| + EXPECT_GT(TypedURLSyncMetadataDatabase::StorageKeyToURLID(it.first), 0);
|
| + EXPECT_TRUE(it.second->specifics.has_typed_url());
|
| + }
|
| + }
|
| +
|
| + sync_pb::TypedUrlSpecifics GetLastUpdateForURL(const std::string& url) {
|
| + const std::string storage_key = GetStorageKey(url);
|
| + auto eq_range = processor().put_multimap().equal_range(storage_key);
|
| + if (eq_range.first == eq_range.second)
|
| + return sync_pb::TypedUrlSpecifics();
|
| +
|
| + auto recorded_specifics_iterator = --eq_range.second;
|
| + EXPECT_NE(processor().put_multimap().end(), recorded_specifics_iterator);
|
| + EXPECT_TRUE(recorded_specifics_iterator->second->specifics.has_typed_url());
|
| +
|
| + return recorded_specifics_iterator->second->specifics.typed_url();
|
| + }
|
| +
|
| + static void DiffVisits(const VisitVector& history_visits,
|
| + const sync_pb::TypedUrlSpecifics& sync_specifics,
|
| + std::vector<VisitInfo>* new_visits,
|
| + VisitVector* removed_visits) {
|
| + TypedURLSyncBridge::DiffVisits(history_visits, sync_specifics, new_visits,
|
| + removed_visits);
|
| + }
|
| +
|
| + static VisitRow CreateVisit(ui::PageTransition type, int64_t timestamp) {
|
| + return VisitRow(0, base::Time::FromInternalValue(timestamp), 0, type, 0);
|
| + }
|
| +
|
| + static TypedURLSyncBridge::MergeResult MergeUrls(
|
| + const sync_pb::TypedUrlSpecifics& typed_url,
|
| + const URLRow& url,
|
| + VisitVector* visits,
|
| + URLRow* new_url,
|
| + std::vector<VisitInfo>* new_visits) {
|
| + return TypedURLSyncBridge::MergeUrls(typed_url, url, visits, new_url,
|
| + new_visits);
|
| + }
|
| +
|
| + static sync_pb::TypedUrlSpecifics MakeTypedUrlSpecifics(const char* url,
|
| + const char* title,
|
| + int64_t last_visit,
|
| + bool hidden) {
|
| + sync_pb::TypedUrlSpecifics typed_url;
|
| + typed_url.set_url(url);
|
| + typed_url.set_title(title);
|
| + typed_url.set_hidden(hidden);
|
| + typed_url.add_visits(last_visit);
|
| + typed_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED);
|
| + return typed_url;
|
| + }
|
| +
|
| + static const TypedURLSyncBridge::MergeResult DIFF_NONE =
|
| + TypedURLSyncBridge::DIFF_NONE;
|
| + static const TypedURLSyncBridge::MergeResult DIFF_UPDATE_NODE =
|
| + TypedURLSyncBridge::DIFF_UPDATE_NODE;
|
| + static const TypedURLSyncBridge::MergeResult DIFF_LOCAL_ROW_CHANGED =
|
| + TypedURLSyncBridge::DIFF_LOCAL_ROW_CHANGED;
|
| + static const TypedURLSyncBridge::MergeResult DIFF_LOCAL_VISITS_ADDED =
|
| + TypedURLSyncBridge::DIFF_LOCAL_VISITS_ADDED;
|
| +
|
| TypedURLSyncBridge* bridge() { return typed_url_sync_bridge_; }
|
|
|
| TypedURLSyncMetadataDatabase* metadata_store() {
|
| @@ -277,7 +482,26 @@ TEST_F(TypedURLSyncBridgeTest, GetAllData) {
|
| WriteToTypedUrlSpecifics(row2, visits2, &typed_url2);
|
|
|
| // Check that the local cache is still correct.
|
| - VerifyLocalHistoryData({typed_url1, typed_url2});
|
| + VerifyAllLocalHistoryData({typed_url1, typed_url2});
|
| +}
|
| +
|
| +// Add two typed urls locally and verify bridge can get them from GetData.
|
| +TEST_F(TypedURLSyncBridgeTest, GetData) {
|
| + // Add two urls to backend.
|
| + VisitVector visits1, visits2;
|
| + URLRow row1 = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits1);
|
| + URLRow row2 = MakeTypedUrlRow(kURL2, kTitle2, 2, 4, false, &visits2);
|
| + fake_history_backend_->SetVisitsForUrl(row1, visits1);
|
| + fake_history_backend_->SetVisitsForUrl(row2, visits2);
|
| +
|
| + // Create the same data in sync.
|
| + TypedUrlSpecifics typed_url1, typed_url2;
|
| + WriteToTypedUrlSpecifics(row1, visits1, &typed_url1);
|
| + WriteToTypedUrlSpecifics(row2, visits2, &typed_url2);
|
| +
|
| + // Check that the local cache is still correct.
|
| + VerifyGetData({IntToStroageKey(1)}, {typed_url1});
|
| + VerifyGetData({IntToStroageKey(2)}, {typed_url2});
|
| }
|
|
|
| // Add a typed url locally and one to sync with the same data. Starting sync
|
| @@ -296,8 +520,19 @@ TEST_F(TypedURLSyncBridgeTest, MergeUrlNoChange) {
|
| StartSyncing({*typed_url});
|
| EXPECT_TRUE(processor().put_multimap().empty());
|
|
|
| + // Even Sync already know the url, bridge still need to tell sync about
|
| + // storage keys.
|
| + EXPECT_EQ(1u, processor().update_multimap().size());
|
| +
|
| + // Verify processor receive correct upate storage key.
|
| + const auto& it = processor().update_multimap().begin();
|
| + EXPECT_EQ(it->first, IntToStroageKey(1));
|
| + EXPECT_TRUE(it->second->specifics.has_typed_url());
|
| + EXPECT_EQ(it->second->specifics.typed_url().url(), kURL);
|
| + EXPECT_EQ(it->second->specifics.typed_url().title(), kTitle);
|
| +
|
| // Check that the local cache was is still correct.
|
| - VerifyLocalHistoryData({*typed_url});
|
| + VerifyAllLocalHistoryData({*typed_url});
|
| }
|
|
|
| // Add a corupted typed url locally, has typed url count 1, but no real typed
|
| @@ -332,12 +567,12 @@ TEST_F(TypedURLSyncBridgeTest, MergeUrlEmptySync) {
|
| sync_pb::EntitySpecifics entity_specifics;
|
| sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
|
| WriteToTypedUrlSpecifics(row, visits, typed_url);
|
| - VerifyLocalHistoryData({*typed_url});
|
| + VerifyAllLocalHistoryData({*typed_url});
|
|
|
| // Check that the server was updated correctly.
|
| ASSERT_EQ(1U, processor().put_multimap().size());
|
| auto recorded_specifics_iterator =
|
| - processor().put_multimap().find(GetStorageKey(*typed_url));
|
| + processor().put_multimap().find(GetStorageKey(typed_url->url()));
|
| EXPECT_NE(processor().put_multimap().end(), recorded_specifics_iterator);
|
| TypedUrlSpecifics recorded_specifics =
|
| recorded_specifics_iterator->second->specifics.typed_url();
|
| @@ -361,6 +596,14 @@ TEST_F(TypedURLSyncBridgeTest, MergeUrlEmptyLocal) {
|
|
|
| StartSyncing({*typed_url});
|
| EXPECT_EQ(0u, processor().put_multimap().size());
|
| + EXPECT_EQ(1u, processor().update_multimap().size());
|
| +
|
| + // Verify processor receive correct upate storage key.
|
| + const auto& it = processor().update_multimap().begin();
|
| + EXPECT_EQ(it->first, IntToStroageKey(1));
|
| + EXPECT_TRUE(it->second->specifics.has_typed_url());
|
| + EXPECT_EQ(it->second->specifics.typed_url().url(), kURL);
|
| + EXPECT_EQ(it->second->specifics.typed_url().title(), kTitle);
|
|
|
| // Check that the backend was updated correctly.
|
| VisitVector all_visits;
|
| @@ -374,6 +617,129 @@ TEST_F(TypedURLSyncBridgeTest, MergeUrlEmptyLocal) {
|
| all_visits[0].transition, visits[0].transition));
|
| }
|
|
|
| +// Add a url to the local and sync data before sync begins, with the sync data
|
| +// having more recent visits. Check that starting sync updates the backend
|
| +// with the sync visit, while the older local visit is not pushed to sync.
|
| +// The title should be updated to the sync version due to the more recent
|
| +// timestamp.
|
| +TEST_F(TypedURLSyncBridgeTest, MergeUrlOldLocal) {
|
| + // Add a url to backend.
|
| + VisitVector visits;
|
| + URLRow local_row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
|
| + fake_history_backend_->SetVisitsForUrl(local_row, visits);
|
| +
|
| + // Create sync data for the same url with a more recent visit.
|
| + VisitVector server_visits;
|
| + URLRow server_row =
|
| + MakeTypedUrlRow(kURL, kTitle2, 1, 6, false, &server_visits);
|
| + server_row.set_id(fake_history_backend_->GetIdByUrl(GURL(kURL)));
|
| + sync_pb::EntitySpecifics entity_specifics;
|
| + sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
|
| + WriteToTypedUrlSpecifics(server_row, server_visits, typed_url);
|
| + StartSyncing({*typed_url});
|
| +
|
| + // Check that the backend was updated correctly.
|
| + VisitVector all_visits;
|
| + base::Time server_time = base::Time::FromInternalValue(6);
|
| + URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
|
| + ASSERT_NE(0, url_id);
|
| + fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
|
| + ASSERT_EQ(2U, all_visits.size());
|
| + EXPECT_EQ(server_time, all_visits.back().visit_time);
|
| + EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
|
| + all_visits.back().transition, server_visits[0].transition));
|
| + URLRow url_row;
|
| + EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
|
| + EXPECT_EQ(kTitle2, base::UTF16ToUTF8(url_row.title()));
|
| +
|
| + // Check that the sync was updated correctly.
|
| + // The local history visit should not be added to sync because it is older
|
| + // than sync's oldest visit.
|
| + ASSERT_EQ(1U, processor().put_multimap().size());
|
| +
|
| + sync_pb::TypedUrlSpecifics url_specifics = GetLastUpdateForURL(kURL);
|
| + ASSERT_EQ(1, url_specifics.visits_size());
|
| + EXPECT_EQ(6, url_specifics.visits(0));
|
| + ASSERT_EQ(1, url_specifics.visit_transitions_size());
|
| + EXPECT_EQ(static_cast<const int>(visits[0].transition),
|
| + url_specifics.visit_transitions(0));
|
| +}
|
| +
|
| +// Add a url to the local and sync data before sync begins, with the local data
|
| +// having more recent visits. Check that starting sync updates the sync
|
| +// with the local visits, while the older sync visit is not pushed to the
|
| +// backend. Sync's title should be updated to the local version due to the more
|
| +// recent timestamp.
|
| +TEST_F(TypedURLSyncBridgeTest, MergeUrlOldSync) {
|
| + // Add a url to backend.
|
| + VisitVector visits;
|
| + URLRow local_row = MakeTypedUrlRow(kURL, kTitle2, 1, 3, false, &visits);
|
| + fake_history_backend_->SetVisitsForUrl(local_row, visits);
|
| +
|
| + // Create sync data for the same url with an older visit.
|
| + VisitVector server_visits;
|
| + URLRow server_row =
|
| + MakeTypedUrlRow(kURL, kTitle, 1, 2, false, &server_visits);
|
| + sync_pb::EntitySpecifics entity_specifics;
|
| + sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
|
| + WriteToTypedUrlSpecifics(server_row, server_visits, typed_url);
|
| + StartSyncing({*typed_url});
|
| +
|
| + // Check that the backend was not updated.
|
| + VisitVector all_visits;
|
| + base::Time local_visit_time = base::Time::FromInternalValue(3);
|
| + URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
|
| + ASSERT_NE(0, url_id);
|
| + fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
|
| + ASSERT_EQ(1U, all_visits.size());
|
| + EXPECT_EQ(local_visit_time, all_visits[0].visit_time);
|
| +
|
| + // Check that the server was updated correctly.
|
| + // The local history visit should not be added to sync because it is older
|
| + // than sync's oldest visit.
|
| + ASSERT_EQ(1U, processor().put_multimap().size());
|
| +
|
| + sync_pb::TypedUrlSpecifics url_specifics = GetLastUpdateForURL(kURL);
|
| + ASSERT_EQ(1, url_specifics.visits_size());
|
| + EXPECT_EQ(3, url_specifics.visits(0));
|
| + EXPECT_EQ(kTitle2, url_specifics.title());
|
| + ASSERT_EQ(1, url_specifics.visit_transitions_size());
|
| + EXPECT_EQ(static_cast<const int>(visits[0].transition),
|
| + url_specifics.visit_transitions(0));
|
| +}
|
| +
|
| +// Check that there is no crash during start sync, if history backend and sync
|
| +// have same url, but sync has username/password in it.
|
| +// Also check sync will not accept url with username and password.
|
| +TEST_F(TypedURLSyncBridgeTest, MergeUrlsWithUsernameAndPassword) {
|
| + const char kURLWithUsernameAndPassword[] =
|
| + "http://username:password@pie.com/";
|
| +
|
| + // Add a url to backend.
|
| + VisitVector visits;
|
| + URLRow local_row = MakeTypedUrlRow(kURL, kTitle2, 1, 3, false, &visits);
|
| + fake_history_backend_->SetVisitsForUrl(local_row, visits);
|
| +
|
| + // Create sync data for the same url but contain username and password.
|
| + VisitVector server_visits;
|
| + URLRow server_row = MakeTypedUrlRow(kURLWithUsernameAndPassword, kTitle, 1, 3,
|
| + false, &server_visits);
|
| + sync_pb::EntitySpecifics entity_specifics;
|
| + sync_pb::TypedUrlSpecifics* typed_url = entity_specifics.mutable_typed_url();
|
| + WriteToTypedUrlSpecifics(server_row, server_visits, typed_url);
|
| +
|
| + // Make sure there is no crash when merge two urls.
|
| + StartSyncing({*typed_url});
|
| +
|
| + // Notify typed url sync service of the update.
|
| + bridge()->OnURLVisited(fake_history_backend_.get(), ui::PAGE_TRANSITION_TYPED,
|
| + server_row, RedirectList(),
|
| + base::Time::FromInternalValue(7));
|
| +
|
| + // Check username/password url is not synced.
|
| + ASSERT_EQ(1U, processor().put_multimap().size());
|
| +}
|
| +
|
| // Starting sync with both local and sync have same typed URL, but different
|
| // visit. After merge, both local and sync should have two same visits.
|
| TEST_F(TypedURLSyncBridgeTest, SimpleMerge) {
|
| @@ -406,4 +772,795 @@ TEST_F(TypedURLSyncBridgeTest, SimpleMerge) {
|
| all_visits[1].transition, visits2[0].transition));
|
| }
|
|
|
| +// Create a local typed URL with one TYPED visit after sync has started. Check
|
| +// that sync is sent an ADD change for the new URL.
|
| +TEST_F(TypedURLSyncBridgeTest, AddLocalTypedUrl) {
|
| + // Create a local typed URL (simulate a typed visit) that is not already
|
| + // in sync. Check that sync is sent an ADD change for the existing URL.
|
| + URLRows url_rows;
|
| + std::vector<VisitVector> visit_vectors;
|
| + std::vector<std::string> urls;
|
| + urls.push_back(kURL);
|
| +
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| + ASSERT_TRUE(BuildAndPushLocalChanges(1, 0, urls, &url_rows, &visit_vectors));
|
| +
|
| + URLRow url_row = url_rows.front();
|
| + VisitVector visits = visit_vectors.front();
|
| +
|
| + // Check change processor.
|
| + ASSERT_EQ(1U, processor().put_multimap().size());
|
| +
|
| + // Get typed url specifics.
|
| + auto it = processor().put_multimap().begin();
|
| + sync_pb::TypedUrlSpecifics url_specifics = it->second->specifics.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));
|
| +}
|
| +
|
| +// Update a local typed URL that is already synced. Check that sync is sent an
|
| +// UPDATE for the existing url, but RELOAD visits aren't synced.
|
| +TEST_F(TypedURLSyncBridgeTest, UpdateLocalTypedUrl) {
|
| + URLRows url_rows;
|
| + std::vector<VisitVector> visit_vectors;
|
| + std::vector<std::string> urls;
|
| + urls.push_back(kURL);
|
| +
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| +
|
| + // Update the URL row, adding another typed visit to the visit vector.
|
| + URLRows changed_urls;
|
| + VisitVector visits;
|
| + URLRow url_row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
|
| + AddNewestVisit(ui::PAGE_TRANSITION_TYPED, 7, &url_row, &visits);
|
| + AddNewestVisit(ui::PAGE_TRANSITION_RELOAD, 8, &url_row, &visits);
|
| + AddNewestVisit(ui::PAGE_TRANSITION_LINK, 9, &url_row, &visits);
|
| + fake_history_backend_->SetVisitsForUrl(url_row, visits);
|
| + changed_urls.push_back(url_row);
|
| +
|
| + // Notify typed url sync service of the update.
|
| + const auto& changes_multimap = processor().put_multimap();
|
| + ASSERT_EQ(0U, changes_multimap.size());
|
| + bridge()->OnURLsModified(fake_history_backend_.get(), changed_urls);
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| +
|
| + sync_pb::TypedUrlSpecifics url_specifics = GetLastUpdateForURL(kURL);
|
| + EXPECT_TRUE(URLsEqual(url_row, url_specifics));
|
| + ASSERT_EQ(3, url_specifics.visits_size());
|
| +
|
| + // Check that each visit has been translated/communicated correctly.
|
| + // Note that the specifics record visits in chronological order, and the
|
| + // visits from the db are in reverse chronological order.
|
| + EXPECT_EQ(visits[0].visit_time.ToInternalValue(), url_specifics.visits(2));
|
| + EXPECT_EQ(static_cast<const int>(visits[0].transition),
|
| + url_specifics.visit_transitions(2));
|
| + EXPECT_EQ(visits[2].visit_time.ToInternalValue(), url_specifics.visits(1));
|
| + EXPECT_EQ(static_cast<const int>(visits[2].transition),
|
| + url_specifics.visit_transitions(1));
|
| + EXPECT_EQ(visits[3].visit_time.ToInternalValue(), url_specifics.visits(0));
|
| + EXPECT_EQ(static_cast<const int>(visits[3].transition),
|
| + url_specifics.visit_transitions(0));
|
| +}
|
| +
|
| +// Append a RELOAD visit to a typed url that is already synced. Check that sync
|
| +// does not receive any updates.
|
| +TEST_F(TypedURLSyncBridgeTest, ReloadVisitLocalTypedUrl) {
|
| + URLRows url_rows;
|
| + std::vector<VisitVector> visit_vectors;
|
| + std::vector<std::string> urls;
|
| + urls.push_back(kURL);
|
| +
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| + ASSERT_TRUE(BuildAndPushLocalChanges(1, 0, urls, &url_rows, &visit_vectors));
|
| + const auto& changes_multimap = processor().put_multimap();
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| +
|
| + // Update the URL row, adding another typed visit to the visit vector.
|
| + URLRow url_row = url_rows.front();
|
| + URLRows changed_urls;
|
| + VisitVector new_visits;
|
| + AddNewestVisit(ui::PAGE_TRANSITION_RELOAD, 7, &url_row, &new_visits);
|
| + fake_history_backend_->SetVisitsForUrl(url_row, new_visits);
|
| + changed_urls.push_back(url_row);
|
| +
|
| + // Notify typed url sync service of the update.
|
| + bridge()->OnURLVisited(fake_history_backend_.get(),
|
| + ui::PAGE_TRANSITION_RELOAD, url_row, RedirectList(),
|
| + base::Time::FromInternalValue(7));
|
| + // No change pass to processor
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| +}
|
| +
|
| +// Appends a LINK visit to an existing typed url. Check that sync does not
|
| +// receive any changes.
|
| +TEST_F(TypedURLSyncBridgeTest, LinkVisitLocalTypedUrl) {
|
| + URLRows url_rows;
|
| + std::vector<VisitVector> visit_vectors;
|
| + std::vector<std::string> urls;
|
| + urls.push_back(kURL);
|
| +
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| + ASSERT_TRUE(BuildAndPushLocalChanges(1, 0, urls, &url_rows, &visit_vectors));
|
| + const auto& changes_multimap = processor().put_multimap();
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| +
|
| + // Update the URL row, adding a non-typed visit to the visit vector.
|
| + URLRow url_row = url_rows.front();
|
| + VisitVector new_visits;
|
| + AddNewestVisit(ui::PAGE_TRANSITION_LINK, 6, &url_row, &new_visits);
|
| + fake_history_backend_->SetVisitsForUrl(url_row, new_visits);
|
| +
|
| + ui::PageTransition transition = ui::PAGE_TRANSITION_LINK;
|
| + // Notify typed url sync service of non-typed visit, expect no change.
|
| + bridge()->OnURLVisited(fake_history_backend_.get(), transition, url_row,
|
| + RedirectList(), base::Time::FromInternalValue(6));
|
| + // No change pass to processor
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| +}
|
| +
|
| +// Appends a series of LINK visits followed by a TYPED one to an existing typed
|
| +// url. Check that sync receives an UPDATE with the newest visit data.
|
| +TEST_F(TypedURLSyncBridgeTest, TypedVisitLocalTypedUrl) {
|
| + URLRows url_rows;
|
| + std::vector<VisitVector> visit_vectors;
|
| + std::vector<std::string> urls;
|
| + urls.push_back(kURL);
|
| +
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| +
|
| + // Update the URL row, adding another typed visit to the visit vector.
|
| + VisitVector visits;
|
| + URLRow url_row = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits);
|
| + AddOldestVisit(ui::PAGE_TRANSITION_LINK, 1, &url_row, &visits);
|
| + AddNewestVisit(ui::PAGE_TRANSITION_LINK, 6, &url_row, &visits);
|
| + AddNewestVisit(ui::PAGE_TRANSITION_TYPED, 7, &url_row, &visits);
|
| + fake_history_backend_->SetVisitsForUrl(url_row, visits);
|
| +
|
| + // Notify typed url sync service of typed visit.
|
| + const auto& changes_multimap = processor().put_multimap();
|
| + ASSERT_EQ(0U, changes_multimap.size());
|
| + ui::PageTransition transition = ui::PAGE_TRANSITION_TYPED;
|
| + bridge()->OnURLVisited(fake_history_backend_.get(), transition, url_row,
|
| + RedirectList(), base::Time::Now());
|
| +
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| + sync_pb::TypedUrlSpecifics url_specifics = GetLastUpdateForURL(kURL);
|
| +
|
| + EXPECT_TRUE(URLsEqual(url_row, url_specifics));
|
| + EXPECT_EQ(4, url_specifics.visits_size());
|
| +
|
| + // Check that each visit has been translated/communicated correctly.
|
| + // Note that the specifics record visits in chronological order, and the
|
| + // visits from the db are in reverse chronological order.
|
| + 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));
|
| + }
|
| +}
|
| +
|
| +// Delete several (but not all) local typed urls. Check that sync receives the
|
| +// DELETE changes, and the non-deleted urls remain synced.
|
| +TEST_F(TypedURLSyncBridgeTest, DeleteLocalTypedUrl) {
|
| + URLRows url_rows;
|
| + std::vector<VisitVector> visit_vectors;
|
| + std::vector<std::string> 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/");
|
| +
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| + ASSERT_TRUE(BuildAndPushLocalChanges(4, 1, urls, &url_rows, &visit_vectors));
|
| + const auto& changes_multimap = processor().put_multimap();
|
| + ASSERT_EQ(4U, changes_multimap.size());
|
| +
|
| + // Simulate visit expiry of typed visit, no syncing is done
|
| + // This is to test that sync relies on the in-memory cache to know
|
| + // which urls were typed and synced, and should be deleted.
|
| + url_rows[0].set_typed_count(0);
|
| + VisitVector visits;
|
| + fake_history_backend_->SetVisitsForUrl(url_rows[0], visits);
|
| +
|
| + // Delete some urls from backend and create deleted row vector.
|
| + URLRows rows;
|
| + std::set<std::string> deleted_storage_keys;
|
| + for (size_t i = 0; i < 3u; ++i) {
|
| + std::string storage_key = GetStorageKey(url_rows[i].url().spec());
|
| + deleted_storage_keys.insert(storage_key);
|
| + fake_history_backend_->DeleteURL(url_rows[i].url());
|
| + rows.push_back(url_rows[i]);
|
| + }
|
| +
|
| + // Notify typed url sync service.
|
| + bridge()->OnURLsDeleted(fake_history_backend_.get(), false, false, rows,
|
| + std::set<GURL>());
|
| +
|
| + const auto& delete_set = processor().delete_set();
|
| + ASSERT_EQ(3U, delete_set.size());
|
| + for (const std::string& storage_key : delete_set) {
|
| + EXPECT_TRUE(deleted_storage_keys.find(storage_key) !=
|
| + deleted_storage_keys.end());
|
| + deleted_storage_keys.erase(storage_key);
|
| + }
|
| + ASSERT_TRUE(deleted_storage_keys.empty());
|
| +}
|
| +
|
| +// Saturate the visits for a typed url with both TYPED and LINK navigations.
|
| +// Check that no more than kMaxTypedURLVisits are synced, and that LINK visits
|
| +// are dropped rather than TYPED ones.
|
| +TEST_F(TypedURLSyncBridgeTest, MaxVisitLocalTypedUrl) {
|
| + URLRows url_rows;
|
| + std::vector<VisitVector> visit_vectors;
|
| + std::vector<std::string> urls;
|
| + urls.push_back(kURL);
|
| +
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| + ASSERT_TRUE(BuildAndPushLocalChanges(0, 1, urls, &url_rows, &visit_vectors));
|
| + const auto& changes_multimap = processor().put_multimap();
|
| + ASSERT_EQ(0U, changes_multimap.size());
|
| +
|
| + URLRow url_row = url_rows.front();
|
| + VisitVector visits;
|
| +
|
| + // Add |kMaxTypedUrlVisits| + 10 visits to the url. The 10 oldest
|
| + // non-typed visits are expected to be skipped.
|
| + int i = 1;
|
| + for (; i <= kMaxTypedUrlVisits - 20; ++i)
|
| + AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
|
| + for (; i <= kMaxTypedUrlVisits; ++i)
|
| + AddNewestVisit(ui::PAGE_TRANSITION_LINK, i, &url_row, &visits);
|
| + for (; i <= kMaxTypedUrlVisits + 10; ++i)
|
| + AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
|
| +
|
| + fake_history_backend_->SetVisitsForUrl(url_row, visits);
|
| +
|
| + // Notify typed url sync service of typed visit.
|
| + ui::PageTransition transition = ui::PAGE_TRANSITION_TYPED;
|
| + bridge()->OnURLVisited(fake_history_backend_.get(), transition, url_row,
|
| + RedirectList(), base::Time::Now());
|
| +
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| + sync_pb::TypedUrlSpecifics url_specifics = GetLastUpdateForURL(kURL);
|
| + ASSERT_EQ(kMaxTypedUrlVisits, url_specifics.visits_size());
|
| +
|
| + // Check that each visit has been translated/communicated correctly.
|
| + // Note that the specifics records visits in chronological order, and the
|
| + // visits from the db are in reverse chronological order.
|
| + 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) ==
|
| + static_cast<int32_t>(ui::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);
|
| +}
|
| +
|
| +// Add enough visits to trigger throttling of updates to a typed url. Check that
|
| +// sync does not receive an update until the proper throttle interval has been
|
| +// reached.
|
| +TEST_F(TypedURLSyncBridgeTest, ThrottleVisitLocalTypedUrl) {
|
| + URLRows url_rows;
|
| + std::vector<VisitVector> visit_vectors;
|
| + std::vector<std::string> urls;
|
| + urls.push_back(kURL);
|
| +
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| + ASSERT_TRUE(BuildAndPushLocalChanges(0, 1, urls, &url_rows, &visit_vectors));
|
| + const auto& changes_multimap = processor().put_multimap();
|
| + ASSERT_EQ(0U, changes_multimap.size());
|
| +
|
| + URLRow url_row = url_rows.front();
|
| + VisitVector visits;
|
| +
|
| + // Add enough visits to the url so that typed count is above the throttle
|
| + // limit, and not right on the interval that gets synced.
|
| + int i = 1;
|
| + for (; i < kVisitThrottleThreshold + kVisitThrottleMultiple / 2; ++i)
|
| + AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
|
| + fake_history_backend_->SetVisitsForUrl(url_row, visits);
|
| +
|
| + // Notify typed url sync service of typed visit.
|
| + ui::PageTransition transition = ui::PAGE_TRANSITION_TYPED;
|
| + bridge()->OnURLVisited(fake_history_backend_.get(), transition, url_row,
|
| + RedirectList(), base::Time::Now());
|
| +
|
| + // Should throttle, so sync and local cache should not update.
|
| + ASSERT_EQ(0U, changes_multimap.size());
|
| +
|
| + visits.clear();
|
| + for (; i % kVisitThrottleMultiple != 1; ++i)
|
| + AddNewestVisit(ui::PAGE_TRANSITION_TYPED, i, &url_row, &visits);
|
| + --i; // Account for the increment before the condition ends.
|
| + fake_history_backend_->SetVisitsForUrl(url_row, visits);
|
| +
|
| + // Notify typed url sync service of typed visit.
|
| + bridge()->OnURLVisited(fake_history_backend_.get(), transition, url_row,
|
| + RedirectList(), base::Time::Now());
|
| +
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| + sync_pb::TypedUrlSpecifics url_specifics = GetLastUpdateForURL(kURL);
|
| + ASSERT_EQ(i, url_specifics.visits_size());
|
| +}
|
| +
|
| +// Create a remote typed URL and visit, then send to sync bridge after sync
|
| +// has started. Check that local DB is received the new URL and visit.
|
| +TEST_F(TypedURLSyncBridgeTest, AddUrlAndVisits) {
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| + VisitVector visits = ApplyUrlAndVisitsChange(kURL, kTitle, 1, 3, false,
|
| + EntityChange::ACTION_ADD);
|
| +
|
| + ASSERT_EQ(0U, processor().put_multimap().size());
|
| + ASSERT_EQ(1U, processor().update_multimap().size());
|
| + ASSERT_EQ(0U, processor().untrack_set().size());
|
| +
|
| + // Verify processor receive correct upate storage key.
|
| + const auto& it = processor().update_multimap().begin();
|
| + EXPECT_EQ(it->first, IntToStroageKey(1));
|
| + EXPECT_TRUE(it->second->specifics.has_typed_url());
|
| + EXPECT_EQ(it->second->specifics.typed_url().url(), kURL);
|
| + EXPECT_EQ(it->second->specifics.typed_url().title(), kTitle);
|
| +
|
| + base::Time visit_time = base::Time::FromInternalValue(3);
|
| + VisitVector all_visits;
|
| + URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
|
| + ASSERT_NE(0, url_id);
|
| + fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
|
| + EXPECT_EQ(1U, all_visits.size());
|
| + EXPECT_EQ(visit_time, all_visits[0].visit_time);
|
| + EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
|
| + all_visits[0].transition, visits[0].transition));
|
| + URLRow url_row;
|
| + EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
|
| + EXPECT_EQ(kTitle, base::UTF16ToUTF8(url_row.title()));
|
| +}
|
| +
|
| +// Create a remote typed URL with expired visit, then send to sync bridge after
|
| +// sync has started. Check that local DB did not receive the expired URL and
|
| +// visit.
|
| +TEST_F(TypedURLSyncBridgeTest, AddExpiredUrlAndVisits) {
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| + VisitVector visits = ApplyUrlAndVisitsChange(kURL, kTitle, 1, kExpiredVisit,
|
| + false, EntityChange::ACTION_ADD);
|
| +
|
| + ASSERT_EQ(0U, processor().put_multimap().size());
|
| + ASSERT_EQ(0U, processor().update_multimap().size());
|
| + ASSERT_EQ(1U, processor().untrack_set().size());
|
| +
|
| + URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
|
| + ASSERT_EQ(0, url_id);
|
| +}
|
| +
|
| +// Update a remote typed URL and create a new visit that is already synced, then
|
| +// send the update to sync bridge. Check that local DB is received an
|
| +// UPDATE for the existing url and new visit.
|
| +TEST_F(TypedURLSyncBridgeTest, UpdateUrlAndVisits) {
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| +
|
| + VisitVector visits = ApplyUrlAndVisitsChange(kURL, kTitle, 1, 3, false,
|
| + EntityChange::ACTION_ADD);
|
| + base::Time visit_time = base::Time::FromInternalValue(3);
|
| + VisitVector all_visits;
|
| + URLRow url_row;
|
| +
|
| + URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
|
| + ASSERT_NE(0, url_id);
|
| +
|
| + fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
|
| +
|
| + EXPECT_EQ(1U, all_visits.size());
|
| + EXPECT_EQ(visit_time, all_visits[0].visit_time);
|
| + EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
|
| + all_visits[0].transition, visits[0].transition));
|
| + EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
|
| + EXPECT_EQ(kTitle, base::UTF16ToUTF8(url_row.title()));
|
| +
|
| + VisitVector new_visits = ApplyUrlAndVisitsChange(kURL, kTitle2, 2, 6, false,
|
| + EntityChange::ACTION_UPDATE);
|
| +
|
| + base::Time new_visit_time = base::Time::FromInternalValue(6);
|
| + url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
|
| + ASSERT_NE(0, url_id);
|
| + fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
|
| +
|
| + EXPECT_EQ(2U, all_visits.size());
|
| + EXPECT_EQ(new_visit_time, all_visits.back().visit_time);
|
| + EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
|
| + all_visits.back().transition, new_visits[0].transition));
|
| + EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
|
| + EXPECT_EQ(kTitle2, base::UTF16ToUTF8(url_row.title()));
|
| +}
|
| +
|
| +// Delete a typed urls which already synced. Check that local DB receives the
|
| +// DELETE changes.
|
| +TEST_F(TypedURLSyncBridgeTest, DeleteUrlAndVisits) {
|
| + URLRows url_rows;
|
| + std::vector<VisitVector> visit_vectors;
|
| + std::vector<std::string> urls;
|
| + urls.push_back(kURL);
|
| +
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| + ASSERT_TRUE(BuildAndPushLocalChanges(1, 0, urls, &url_rows, &visit_vectors));
|
| + const auto& changes_multimap = processor().put_multimap();
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| +
|
| + base::Time visit_time = base::Time::FromInternalValue(3);
|
| + VisitVector all_visits;
|
| + URLID url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
|
| + ASSERT_NE(0, url_id);
|
| + fake_history_backend_->GetVisitsForURL(url_id, &all_visits);
|
| + EXPECT_EQ(1U, all_visits.size());
|
| + EXPECT_EQ(visit_time, all_visits[0].visit_time);
|
| + EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
|
| + all_visits[0].transition, visit_vectors[0][0].transition));
|
| + URLRow url_row;
|
| + EXPECT_TRUE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
|
| + EXPECT_EQ(kTitle, base::UTF16ToUTF8(url_row.title()));
|
| +
|
| + // Add observer back to check if TypedUrlSyncBridge receive delete
|
| + // changes back from fake_history_backend_.
|
| + AddObserver();
|
| +
|
| + ApplyUrlAndVisitsChange(kURL, kTitle, 1, 3, false,
|
| + EntityChange::ACTION_DELETE);
|
| +
|
| + EXPECT_FALSE(fake_history_backend_->GetURL(GURL(kURL), &url_row));
|
| + url_id = fake_history_backend_->GetIdByUrl(GURL(kURL));
|
| + ASSERT_EQ(0, url_id);
|
| +
|
| + // Check TypedUrlSyncBridge did not receive update since the update is
|
| + // trigered by it.
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| +}
|
| +
|
| +// Create two set of visits for history DB and sync DB, two same set of visits
|
| +// are same. Check DiffVisits will return empty set of diff visits.
|
| +TEST_F(TypedURLSyncBridgeTest, DiffVisitsSame) {
|
| + VisitVector old_visits;
|
| + sync_pb::TypedUrlSpecifics new_url;
|
| +
|
| + const int64_t visits[] = {1024, 2065, 65534, 1237684};
|
| +
|
| + for (int64_t visit : visits) {
|
| + old_visits.push_back(VisitRow(0, base::Time::FromInternalValue(visit), 0,
|
| + ui::PAGE_TRANSITION_TYPED, 0));
|
| + new_url.add_visits(visit);
|
| + new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED);
|
| + }
|
| +
|
| + std::vector<VisitInfo> new_visits;
|
| + VisitVector removed_visits;
|
| +
|
| + DiffVisits(old_visits, new_url, &new_visits, &removed_visits);
|
| + EXPECT_TRUE(new_visits.empty());
|
| + EXPECT_TRUE(removed_visits.empty());
|
| +}
|
| +
|
| +// Create two set of visits for history DB and sync DB. Check DiffVisits will
|
| +// return correct set of diff visits.
|
| +TEST_F(TypedURLSyncBridgeTest, DiffVisitsRemove) {
|
| + VisitVector old_visits;
|
| + sync_pb::TypedUrlSpecifics new_url;
|
| +
|
| + const int64_t visits_left[] = {1, 2, 1024, 1500, 2065,
|
| + 6000, 65534, 1237684, 2237684};
|
| + const int64_t visits_right[] = {1024, 2065, 65534, 1237684};
|
| +
|
| + // DiffVisits will not remove the first visit, because we never delete visits
|
| + // from the start of the array (since those visits can get truncated by the
|
| + // size-limiting code).
|
| + const int64_t visits_removed[] = {1500, 6000, 2237684};
|
| +
|
| + for (int64_t visit : visits_left) {
|
| + old_visits.push_back(VisitRow(0, base::Time::FromInternalValue(visit), 0,
|
| + ui::PAGE_TRANSITION_TYPED, 0));
|
| + }
|
| +
|
| + for (int64_t visit : visits_right) {
|
| + new_url.add_visits(visit);
|
| + new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED);
|
| + }
|
| +
|
| + std::vector<VisitInfo> new_visits;
|
| + VisitVector removed_visits;
|
| +
|
| + DiffVisits(old_visits, new_url, &new_visits, &removed_visits);
|
| + EXPECT_TRUE(new_visits.empty());
|
| + ASSERT_EQ(removed_visits.size(), arraysize(visits_removed));
|
| + for (size_t i = 0; i < arraysize(visits_removed); ++i) {
|
| + EXPECT_EQ(removed_visits[i].visit_time.ToInternalValue(),
|
| + visits_removed[i]);
|
| + }
|
| +}
|
| +
|
| +// Create two set of visits for history DB and sync DB. Check DiffVisits will
|
| +// return correct set of diff visits.
|
| +TEST_F(TypedURLSyncBridgeTest, DiffVisitsAdd) {
|
| + VisitVector old_visits;
|
| + sync_pb::TypedUrlSpecifics new_url;
|
| +
|
| + const int64_t visits_left[] = {1024, 2065, 65534, 1237684};
|
| + const int64_t visits_right[] = {1, 1024, 1500, 2065,
|
| + 6000, 65534, 1237684, 2237684};
|
| +
|
| + const int64_t visits_added[] = {1, 1500, 6000, 2237684};
|
| +
|
| + for (int64_t visit : visits_left) {
|
| + old_visits.push_back(VisitRow(0, base::Time::FromInternalValue(visit), 0,
|
| + ui::PAGE_TRANSITION_TYPED, 0));
|
| + }
|
| +
|
| + for (int64_t visit : visits_right) {
|
| + new_url.add_visits(visit);
|
| + new_url.add_visit_transitions(ui::PAGE_TRANSITION_TYPED);
|
| + }
|
| +
|
| + std::vector<VisitInfo> new_visits;
|
| + VisitVector removed_visits;
|
| +
|
| + DiffVisits(old_visits, new_url, &new_visits, &removed_visits);
|
| + EXPECT_TRUE(removed_visits.empty());
|
| + ASSERT_TRUE(new_visits.size() == arraysize(visits_added));
|
| + for (size_t i = 0; i < arraysize(visits_added); ++i) {
|
| + EXPECT_EQ(new_visits[i].first.ToInternalValue(), visits_added[i]);
|
| + EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
|
| + new_visits[i].second, ui::PAGE_TRANSITION_TYPED));
|
| + }
|
| +}
|
| +
|
| +// Create three visits, check RELOAD visit is removed by
|
| +// WriteToTypedUrlSpecifics so it won't apply to sync DB.
|
| +TEST_F(TypedURLSyncBridgeTest, WriteTypedUrlSpecifics) {
|
| + VisitVector visits;
|
| + visits.push_back(CreateVisit(ui::PAGE_TRANSITION_TYPED, 1));
|
| + visits.push_back(CreateVisit(ui::PAGE_TRANSITION_RELOAD, 2));
|
| + visits.push_back(CreateVisit(ui::PAGE_TRANSITION_LINK, 3));
|
| +
|
| + URLRow url(MakeTypedUrlRow(kURL, kTitle, 0, 100, false, &visits));
|
| + sync_pb::TypedUrlSpecifics typed_url;
|
| + WriteToTypedUrlSpecifics(url, visits, &typed_url);
|
| + // RELOAD visits should be removed.
|
| + EXPECT_EQ(2, typed_url.visits_size());
|
| + EXPECT_EQ(typed_url.visit_transitions_size(), typed_url.visits_size());
|
| + EXPECT_EQ(1, typed_url.visits(0));
|
| + EXPECT_EQ(3, typed_url.visits(1));
|
| + EXPECT_EQ(static_cast<int32_t>(ui::PAGE_TRANSITION_TYPED),
|
| + typed_url.visit_transitions(0));
|
| + EXPECT_EQ(static_cast<int32_t>(ui::PAGE_TRANSITION_LINK),
|
| + typed_url.visit_transitions(1));
|
| +}
|
| +
|
| +// Create 101 visits, check WriteToTypedUrlSpecifics will only keep 100 visits.
|
| +TEST_F(TypedURLSyncBridgeTest, TooManyVisits) {
|
| + VisitVector visits;
|
| + int64_t timestamp = 1000;
|
| + visits.push_back(CreateVisit(ui::PAGE_TRANSITION_TYPED, timestamp++));
|
| + for (int i = 0; i < 100; ++i) {
|
| + visits.push_back(CreateVisit(ui::PAGE_TRANSITION_LINK, timestamp++));
|
| + }
|
| + URLRow url(MakeTypedUrlRow(kURL, kTitle, 0, timestamp++, false, &visits));
|
| + sync_pb::TypedUrlSpecifics typed_url;
|
| + WriteToTypedUrlSpecifics(url, visits, &typed_url);
|
| + // # visits should be capped at 100.
|
| + EXPECT_EQ(100, typed_url.visits_size());
|
| + EXPECT_EQ(typed_url.visit_transitions_size(), typed_url.visits_size());
|
| + EXPECT_EQ(1000, typed_url.visits(0));
|
| + // Visit with timestamp of 1001 should be omitted since we should have
|
| + // skipped that visit to stay under the cap.
|
| + EXPECT_EQ(1002, typed_url.visits(1));
|
| + EXPECT_EQ(static_cast<int32_t>(ui::PAGE_TRANSITION_TYPED),
|
| + typed_url.visit_transitions(0));
|
| + EXPECT_EQ(static_cast<int32_t>(ui::PAGE_TRANSITION_LINK),
|
| + typed_url.visit_transitions(1));
|
| +}
|
| +
|
| +// Create 306 visits, check WriteToTypedUrlSpecifics will only keep 100 typed
|
| +// visits.
|
| +TEST_F(TypedURLSyncBridgeTest, TooManyTypedVisits) {
|
| + VisitVector visits;
|
| + int64_t timestamp = 1000;
|
| + for (int i = 0; i < 102; ++i) {
|
| + visits.push_back(CreateVisit(ui::PAGE_TRANSITION_TYPED, timestamp++));
|
| + visits.push_back(CreateVisit(ui::PAGE_TRANSITION_LINK, timestamp++));
|
| + visits.push_back(CreateVisit(ui::PAGE_TRANSITION_RELOAD, timestamp++));
|
| + }
|
| + URLRow url(MakeTypedUrlRow(kURL, kTitle, 0, timestamp++, false, &visits));
|
| + sync_pb::TypedUrlSpecifics typed_url;
|
| + WriteToTypedUrlSpecifics(url, visits, &typed_url);
|
| + // # visits should be capped at 100.
|
| + EXPECT_EQ(100, typed_url.visits_size());
|
| + EXPECT_EQ(typed_url.visit_transitions_size(), typed_url.visits_size());
|
| + // First two typed visits should be skipped.
|
| + EXPECT_EQ(1006, typed_url.visits(0));
|
| +
|
| + // Ensure there are no non-typed visits since that's all that should fit.
|
| + for (int i = 0; i < typed_url.visits_size(); ++i) {
|
| + EXPECT_EQ(static_cast<int32_t>(ui::PAGE_TRANSITION_TYPED),
|
| + typed_url.visit_transitions(i));
|
| + }
|
| +}
|
| +
|
| +// Create a typed url without visit, check WriteToTypedUrlSpecifics will return
|
| +// false for it.
|
| +TEST_F(TypedURLSyncBridgeTest, NoTypedVisits) {
|
| + VisitVector visits;
|
| + URLRow url(MakeTypedUrlRow(kURL, kTitle, 0, 1000, false, &visits));
|
| + sync_pb::TypedUrlSpecifics typed_url;
|
| + EXPECT_FALSE(WriteToTypedUrlSpecifics(url, visits, &typed_url));
|
| + // URLs with no typed URL visits should not been written to specifics.
|
| + EXPECT_EQ(0, typed_url.visits_size());
|
| +}
|
| +
|
| +TEST_F(TypedURLSyncBridgeTest, MergeUrls) {
|
| + VisitVector visits1;
|
| + URLRow row1(MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &visits1));
|
| + sync_pb::TypedUrlSpecifics specs1(
|
| + MakeTypedUrlSpecifics(kURL, kTitle, 3, false));
|
| + URLRow new_row1((GURL(kURL)));
|
| + std::vector<VisitInfo> new_visits1;
|
| + EXPECT_TRUE(TypedURLSyncBridgeTest::MergeUrls(specs1, row1, &visits1,
|
| + &new_row1, &new_visits1) ==
|
| + TypedURLSyncBridgeTest::DIFF_NONE);
|
| +
|
| + VisitVector visits2;
|
| + URLRow row2(MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &visits2));
|
| + sync_pb::TypedUrlSpecifics specs2(
|
| + MakeTypedUrlSpecifics(kURL, kTitle, 3, true));
|
| + VisitVector expected_visits2;
|
| + URLRow expected2(
|
| + MakeTypedUrlRow(kURL, kTitle, 2, 3, true, &expected_visits2));
|
| + URLRow new_row2((GURL(kURL)));
|
| + std::vector<VisitInfo> new_visits2;
|
| + EXPECT_TRUE(TypedURLSyncBridgeTest::MergeUrls(specs2, row2, &visits2,
|
| + &new_row2, &new_visits2) ==
|
| + TypedURLSyncBridgeTest::DIFF_LOCAL_ROW_CHANGED);
|
| + EXPECT_TRUE(URLsEqual(new_row2, expected2));
|
| +
|
| + VisitVector visits3;
|
| + URLRow row3(MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &visits3));
|
| + sync_pb::TypedUrlSpecifics specs3(
|
| + MakeTypedUrlSpecifics(kURL, kTitle2, 3, true));
|
| + VisitVector expected_visits3;
|
| + URLRow expected3(
|
| + MakeTypedUrlRow(kURL, kTitle2, 2, 3, true, &expected_visits3));
|
| + URLRow new_row3((GURL(kURL)));
|
| + std::vector<VisitInfo> new_visits3;
|
| + EXPECT_EQ(TypedURLSyncBridgeTest::DIFF_LOCAL_ROW_CHANGED |
|
| + TypedURLSyncBridgeTest::DIFF_NONE,
|
| + TypedURLSyncBridgeTest::MergeUrls(specs3, row3, &visits3, &new_row3,
|
| + &new_visits3));
|
| + EXPECT_TRUE(URLsEqual(new_row3, expected3));
|
| +
|
| + // Create one node in history DB with timestamp of 3, and one node in sync
|
| + // DB with timestamp of 4. Result should contain one new item (4).
|
| + VisitVector visits4;
|
| + URLRow row4(MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &visits4));
|
| + sync_pb::TypedUrlSpecifics specs4(
|
| + MakeTypedUrlSpecifics(kURL, kTitle2, 4, false));
|
| + VisitVector expected_visits4;
|
| + URLRow expected4(
|
| + MakeTypedUrlRow(kURL, kTitle2, 2, 4, false, &expected_visits4));
|
| + URLRow new_row4((GURL(kURL)));
|
| + std::vector<VisitInfo> new_visits4;
|
| + EXPECT_EQ(TypedURLSyncBridgeTest::DIFF_UPDATE_NODE |
|
| + TypedURLSyncBridgeTest::DIFF_LOCAL_ROW_CHANGED |
|
| + TypedURLSyncBridgeTest::DIFF_LOCAL_VISITS_ADDED,
|
| + TypedURLSyncBridgeTest::MergeUrls(specs4, row4, &visits4, &new_row4,
|
| + &new_visits4));
|
| + EXPECT_EQ(1U, new_visits4.size());
|
| + EXPECT_EQ(specs4.visits(0), new_visits4[0].first.ToInternalValue());
|
| + EXPECT_TRUE(URLsEqual(new_row4, expected4));
|
| + EXPECT_EQ(2U, visits4.size());
|
| +
|
| + VisitVector visits5;
|
| + URLRow row5(MakeTypedUrlRow(kURL, kTitle, 1, 4, false, &visits5));
|
| + sync_pb::TypedUrlSpecifics specs5(
|
| + MakeTypedUrlSpecifics(kURL, kTitle, 3, false));
|
| + VisitVector expected_visits5;
|
| + URLRow expected5(
|
| + MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &expected_visits5));
|
| + URLRow new_row5((GURL(kURL)));
|
| + std::vector<VisitInfo> new_visits5;
|
| +
|
| + // UPDATE_NODE should be set because row5 has a newer last_visit timestamp.
|
| + EXPECT_EQ(TypedURLSyncBridgeTest::DIFF_UPDATE_NODE |
|
| + TypedURLSyncBridgeTest::DIFF_NONE,
|
| + TypedURLSyncBridgeTest::MergeUrls(specs5, row5, &visits5, &new_row5,
|
| + &new_visits5));
|
| + EXPECT_TRUE(URLsEqual(new_row5, expected5));
|
| + EXPECT_EQ(0U, new_visits5.size());
|
| +}
|
| +
|
| +// Tests to ensure that we don't resurrect expired URLs (URLs that have been
|
| +// deleted from the history DB but still exist in the sync DB).
|
| +TEST_F(TypedURLSyncBridgeTest, MergeUrlsAfterExpiration) {
|
| + // First, create a history row that has two visits, with timestamps 2 and 3.
|
| + VisitVector(history_visits);
|
| + history_visits.push_back(VisitRow(0, base::Time::FromInternalValue(2), 0,
|
| + ui::PAGE_TRANSITION_TYPED, 0));
|
| + URLRow history_url(
|
| + MakeTypedUrlRow(kURL, kTitle, 2, 3, false, &history_visits));
|
| +
|
| + // Now, create a sync node with visits at timestamps 1, 2, 3, 4.
|
| + sync_pb::TypedUrlSpecifics node(
|
| + MakeTypedUrlSpecifics(kURL, kTitle, 1, false));
|
| + node.add_visits(2);
|
| + node.add_visits(3);
|
| + node.add_visits(4);
|
| + node.add_visit_transitions(2);
|
| + node.add_visit_transitions(3);
|
| + node.add_visit_transitions(4);
|
| + URLRow new_history_url(history_url.url());
|
| + std::vector<VisitInfo> new_visits;
|
| + EXPECT_EQ(
|
| + TypedURLSyncBridgeTest::DIFF_NONE |
|
| + TypedURLSyncBridgeTest::DIFF_LOCAL_VISITS_ADDED,
|
| + TypedURLSyncBridgeTest::MergeUrls(node, history_url, &history_visits,
|
| + &new_history_url, &new_visits));
|
| + EXPECT_TRUE(URLsEqual(history_url, new_history_url));
|
| + EXPECT_EQ(1U, new_visits.size());
|
| + EXPECT_EQ(4U, new_visits[0].first.ToInternalValue());
|
| + // We should not sync the visit with timestamp #1 since it is earlier than
|
| + // any other visit for this URL in the history DB. But we should sync visit
|
| + // #4.
|
| + EXPECT_EQ(3U, history_visits.size());
|
| + EXPECT_EQ(2U, history_visits[0].visit_time.ToInternalValue());
|
| + EXPECT_EQ(3U, history_visits[1].visit_time.ToInternalValue());
|
| + EXPECT_EQ(4U, history_visits[2].visit_time.ToInternalValue());
|
| +}
|
| +
|
| +// Create a local typed URL with one expired TYPED visit,
|
| +// MergeSyncData should not pass it to sync. And then add a non
|
| +// expired visit, OnURLsModified should only send the non expired visit to sync.
|
| +TEST_F(TypedURLSyncBridgeTest, LocalExpiredTypedUrlDoNotSync) {
|
| + URLRow row;
|
| + URLRows changed_urls;
|
| + VisitVector visits;
|
| +
|
| + // Add an expired typed URL to local.
|
| + row = MakeTypedUrlRow(kURL, kTitle, 1, kExpiredVisit, false, &visits);
|
| + fake_history_backend_->SetVisitsForUrl(row, visits);
|
| +
|
| + StartSyncing(std::vector<TypedUrlSpecifics>());
|
| +
|
| + // Check change processor did not receive expired typed URL.
|
| + const auto& changes_multimap = processor().put_multimap();
|
| + ASSERT_EQ(0U, changes_multimap.size());
|
| +
|
| + // Add a non expired typed URL to local.
|
| + row = MakeTypedUrlRow(kURL, kTitle, 2, 1, false, &visits);
|
| + fake_history_backend_->SetVisitsForUrl(row, visits);
|
| +
|
| + changed_urls.push_back(row);
|
| + // Notify typed url sync service of the update.
|
| + bridge()->OnURLsModified(fake_history_backend_.get(), changed_urls);
|
| +
|
| + // Check change processor did not receive expired typed URL.
|
| + ASSERT_EQ(1U, changes_multimap.size());
|
| +
|
| + // Get typed url specifics. Verify only a non-expired visit received.
|
| + sync_pb::TypedUrlSpecifics url_specifics = GetLastUpdateForURL(kURL);
|
| +
|
| + EXPECT_TRUE(URLsEqual(row, url_specifics));
|
| + ASSERT_EQ(1, url_specifics.visits_size());
|
| + ASSERT_EQ(static_cast<const int>(visits.size() - 1),
|
| + url_specifics.visits_size());
|
| + 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));
|
| +}
|
| +
|
| } // namespace history
|
|
|