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 |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2453270ed3a87c2389f59ff67cb87ebe773cc6da |
--- /dev/null |
+++ b/components/history/core/browser/typed_url_sync_bridge_unittest.cc |
@@ -0,0 +1,260 @@ |
+// Copyright 2017 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 "components/history/core/browser/typed_url_sync_bridge.h" |
+ |
+#include "base/files/scoped_temp_dir.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "base/threading/thread_task_runner_handle.h" |
+#include "components/history/core/browser/history_backend.h" |
+#include "components/history/core/browser/history_backend_client.h" |
+#include "components/history/core/browser/history_database_params.h" |
+#include "components/history/core/browser/in_memory_history_backend.h" |
+#include "components/history/core/test/test_history_database.h" |
+#include "components/sync/model/data_batch.h" |
+#include "components/sync/model/recording_model_type_change_processor.h" |
+#include "components/sync/model/sync_metadata_store.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using sync_pb::TypedUrlSpecifics; |
+using syncer::DataBatch; |
+using syncer::EntityData; |
+using syncer::EntityDataPtr; |
+using syncer::KeyAndData; |
+using syncer::RecordingModelTypeChangeProcessor; |
+ |
+namespace history { |
+ |
+namespace { |
+ |
+// Visits with this timestamp are treated as expired. |
+const int kExpiredVisit = -1; |
+ |
+// Helper constants for tests. |
+const char kTitle[] = "pie"; |
+const char kTitle2[] = "cookie"; |
+const char kURL[] = "http://pie.com/"; |
+const char kURL2[] = "http://cookie.com/"; |
+ |
+// 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, |
+ const std::string& title, |
+ int typed_count, |
+ int64_t last_visit, |
+ bool hidden, |
+ VisitVector* visits) { |
+ // Give each URL a unique ID, to mimic the behavior of the real database. |
+ GURL gurl(url); |
+ URLRow history_url(gurl); |
+ 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); |
+ |
+ if (typed_count > 0) { |
+ // Add a typed visit for time |last_visit|. |
+ visits->push_back(VisitRow(history_url.id(), last_visit_time, 0, |
+ ui::PAGE_TRANSITION_TYPED, 0)); |
+ } else { |
+ // Add a non-typed visit for time |last_visit|. |
+ visits->push_back(VisitRow(history_url.id(), last_visit_time, 0, |
+ ui::PAGE_TRANSITION_RELOAD, 0)); |
+ } |
+ |
+ history_url.set_visit_count(visits->size()); |
+ return history_url; |
+} |
+ |
+void VerifyEqual(const TypedUrlSpecifics& s1, const TypedUrlSpecifics& s2) { |
+ // Instead of just comparing serialized strings, manually check fields to show |
+ // differences on failure. |
+ EXPECT_EQ(s1.url(), s2.url()); |
+ EXPECT_EQ(s1.title(), s2.title()); |
+ EXPECT_EQ(s1.hidden(), s2.hidden()); |
+ EXPECT_EQ(s1.visits_size(), s2.visits_size()); |
+ EXPECT_EQ(s1.visit_transitions_size(), s2.visit_transitions_size()); |
+ EXPECT_EQ(s1.visits_size(), s1.visit_transitions_size()); |
+ int size = s1.visits_size(); |
+ for (int i = 0; i < size; ++i) { |
+ EXPECT_EQ(s1.visits(i), s2.visits(i)) << "visits differ at index " << i; |
+ EXPECT_EQ(s1.visit_transitions(i), s2.visit_transitions(i)) |
+ << "visit_transitions differ at index " << i; |
+ } |
+} |
+ |
+void VerifyDataBatch(std::map<std::string, TypedUrlSpecifics> expected, |
+ std::unique_ptr<DataBatch> batch) { |
+ while (batch->HasNext()) { |
+ const KeyAndData& pair = batch->Next(); |
+ auto iter = expected.find(pair.first); |
+ ASSERT_NE(iter, expected.end()); |
+ VerifyEqual(iter->second, pair.second->specifics.typed_url()); |
+ // Removing allows us to verify we don't see the same item multiple times, |
+ // and that we saw everything we expected. |
+ expected.erase(iter); |
+ } |
+ EXPECT_TRUE(expected.empty()); |
+} |
+ |
+class TestHistoryBackendDelegate : public HistoryBackend::Delegate { |
+ public: |
+ TestHistoryBackendDelegate() {} |
+ |
+ void NotifyProfileError(sql::InitStatus init_status, |
+ const std::string& diagnostics) override {} |
+ void SetInMemoryBackend( |
+ std::unique_ptr<InMemoryHistoryBackend> backend) override{}; |
+ void NotifyFaviconsChanged(const std::set<GURL>& page_urls, |
+ const GURL& icon_url) override{}; |
+ void NotifyURLVisited(ui::PageTransition transition, |
+ const URLRow& row, |
+ const RedirectList& redirects, |
+ base::Time visit_time) override{}; |
+ void NotifyURLsModified(const URLRows& changed_urls) override{}; |
+ void NotifyURLsDeleted(bool all_history, |
+ bool expired, |
+ const URLRows& deleted_rows, |
+ const std::set<GURL>& favicon_urls) override{}; |
+ void NotifyKeywordSearchTermUpdated(const URLRow& row, |
+ KeywordID keyword_id, |
+ const base::string16& term) override{}; |
+ void NotifyKeywordSearchTermDeleted(URLID url_id) override{}; |
+ void DBLoaded() override{}; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(TestHistoryBackendDelegate); |
+}; |
+ |
+class TestHistoryBackend : public HistoryBackend { |
+ public: |
+ TestHistoryBackend() |
+ : HistoryBackend(new TestHistoryBackendDelegate(), |
+ nullptr, |
+ base::ThreadTaskRunnerHandle::Get()) {} |
+ |
+ bool IsExpiredVisitTime(const base::Time& time) override { |
+ return time.ToInternalValue() == kExpiredVisit; |
+ } |
+ |
+ URLID GetIdByUrl(const GURL& gurl) { |
+ return db()->GetRowForURL(gurl, nullptr); |
+ } |
+ |
+ void SetVisitsForUrl(URLRow& new_url, const VisitVector visits) { |
+ std::vector<history::VisitInfo> added_visits; |
+ URLRows new_urls; |
+ DeleteURL(new_url.url()); |
+ for (const auto& visit : visits) { |
+ added_visits.push_back( |
+ history::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); |
+ new_url.set_id(GetIdByUrl(new_url.url())); |
+ } |
+ |
+ private: |
+ ~TestHistoryBackend() override {} |
+}; |
+ |
+} // namespace |
+ |
+class TypedURLSyncBridgeTest : public testing::Test { |
+ public: |
+ TypedURLSyncBridgeTest() : typed_url_sync_bridge_(NULL) {} |
+ ~TypedURLSyncBridgeTest() override {} |
+ |
+ void SetUp() override { |
+ fake_history_backend_ = new TestHistoryBackend(); |
+ ASSERT_TRUE(test_dir_.CreateUniqueTempDir()); |
+ fake_history_backend_->Init( |
+ false, TestHistoryDatabaseParamsForPath(test_dir_.GetPath())); |
+ std::unique_ptr<TypedURLSyncBridge> bridge = |
+ base::MakeUnique<TypedURLSyncBridge>( |
+ fake_history_backend_.get(), fake_history_backend_->db(), |
+ RecordingModelTypeChangeProcessor::FactoryForBridgeTest(&processor_, |
+ false)); |
+ typed_url_sync_bridge_ = bridge.get(); |
+ typed_url_sync_bridge_->Init(); |
+ fake_history_backend_->SetTypedURLSyncBridgeForTest(std::move(bridge)); |
+ } |
+ |
+ void TearDown() override { fake_history_backend_->Closing(); } |
+ |
+ // Fills |specifics| with the sync data for |url| and |visits|. |
+ static bool WriteToTypedUrlSpecifics(const URLRow& url, |
+ const VisitVector& visits, |
+ sync_pb::TypedUrlSpecifics* specifics) { |
+ return TypedURLSyncBridge::WriteToTypedUrlSpecifics(url, visits, specifics); |
+ } |
+ |
+ std::string GetStorageKey(const TypedUrlSpecifics& specifics) { |
+ std::string key = |
+ bridge()->GetStorageKey(SpecificsToEntity(specifics).value()); |
+ EXPECT_FALSE(key.empty()); |
+ return key; |
+ } |
+ |
+ EntityDataPtr SpecificsToEntity(const TypedUrlSpecifics& specifics) { |
+ EntityData data; |
+ data.client_tag_hash = "ignored"; |
+ *data.specifics.mutable_typed_url() = specifics; |
+ return data.PassToPtr(); |
+ } |
+ |
+ std::map<std::string, TypedUrlSpecifics> ExpectedMap( |
+ const std::vector<TypedUrlSpecifics>& specifics_vector) { |
+ std::map<std::string, TypedUrlSpecifics> map; |
+ for (const auto& specifics : specifics_vector) { |
+ map[GetStorageKey(specifics)] = specifics; |
+ } |
+ return map; |
+ } |
+ |
+ void VerifyLocalHistoryData(const std::vector<TypedUrlSpecifics>& expected) { |
+ bridge()->GetAllData(base::Bind(&VerifyDataBatch, ExpectedMap(expected))); |
+ } |
+ |
+ TypedURLSyncBridge* bridge() { return typed_url_sync_bridge_; } |
+ |
+ TypedURLSyncMetadataDatabase* metadata_store() { |
+ return bridge()->sync_metadata_database_; |
+ } |
+ |
+ const RecordingModelTypeChangeProcessor& processor() { return *processor_; } |
+ |
+ protected: |
+ base::MessageLoop message_loop_; |
+ base::ScopedTempDir test_dir_; |
+ scoped_refptr<TestHistoryBackend> fake_history_backend_; |
+ TypedURLSyncBridge* typed_url_sync_bridge_; |
+ // A non-owning pointer to the processor given to the bridge. Will be null |
+ // before being given to the bridge, to make ownership easier. |
+ RecordingModelTypeChangeProcessor* processor_ = nullptr; |
+}; |
+ |
+// Add two typed urls locally and verify bridge can get them from GetAllData. |
+TEST_F(TypedURLSyncBridgeTest, GetAllData) { |
+ // 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. |
+ sync_pb::TypedUrlSpecifics typed_url1, typed_url2; |
+ WriteToTypedUrlSpecifics(row1, visits1, &typed_url1); |
+ WriteToTypedUrlSpecifics(row2, visits2, &typed_url2); |
+ |
+ // Check that the local cache is still correct. |
+ VerifyLocalHistoryData({typed_url1, typed_url2}); |
+} |
+ |
+} // namespace history |