OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/history/core/browser/typed_url_sync_bridge.h" | |
6 | |
7 #include "base/files/scoped_temp_dir.h" | |
8 #include "base/message_loop/message_loop.h" | |
9 #include "base/strings/utf_string_conversions.h" | |
10 #include "base/threading/thread_task_runner_handle.h" | |
11 #include "components/history/core/browser/history_backend.h" | |
12 #include "components/history/core/browser/history_backend_client.h" | |
13 #include "components/history/core/browser/history_database_params.h" | |
14 #include "components/history/core/browser/in_memory_history_backend.h" | |
15 #include "components/history/core/test/test_history_database.h" | |
16 #include "components/sync/model/data_batch.h" | |
17 #include "components/sync/model/recording_model_type_change_processor.h" | |
18 #include "components/sync/model/sync_metadata_store.h" | |
19 #include "testing/gtest/include/gtest/gtest.h" | |
20 | |
21 using sync_pb::TypedUrlSpecifics; | |
22 using syncer::DataBatch; | |
23 using syncer::EntityData; | |
24 using syncer::EntityDataPtr; | |
25 using syncer::KeyAndData; | |
26 using syncer::RecordingModelTypeChangeProcessor; | |
27 | |
28 namespace history { | |
29 | |
30 namespace { | |
31 | |
32 // Visits with this timestamp are treated as expired. | |
33 const int kExpiredVisit = -1; | |
34 | |
35 // Helper constants for tests. | |
36 const char kTitle[] = "pie"; | |
37 const char kTitle2[] = "cookie"; | |
38 const char kURL[] = "http://pie.com/"; | |
39 const char kURL2[] = "http://cookie.com/"; | |
40 | |
41 // Create a new row object and the typed visit çorresponding with the time at | |
42 // |last_visit| in the |visits| vector. | |
43 URLRow MakeTypedUrlRow(const std::string& url, | |
44 const std::string& title, | |
45 int typed_count, | |
46 int64_t last_visit, | |
47 bool hidden, | |
48 VisitVector* visits) { | |
49 // Give each URL a unique ID, to mimic the behavior of the real database. | |
50 GURL gurl(url); | |
51 URLRow history_url(gurl); | |
52 history_url.set_title(base::UTF8ToUTF16(title)); | |
53 history_url.set_typed_count(typed_count); | |
54 history_url.set_hidden(hidden); | |
55 | |
56 base::Time last_visit_time = base::Time::FromInternalValue(last_visit); | |
57 history_url.set_last_visit(last_visit_time); | |
58 | |
59 if (typed_count > 0) { | |
60 // Add a typed visit for time |last_visit|. | |
61 visits->push_back(VisitRow(history_url.id(), last_visit_time, 0, | |
62 ui::PAGE_TRANSITION_TYPED, 0)); | |
63 } else { | |
64 // Add a non-typed visit for time |last_visit|. | |
65 visits->push_back(VisitRow(history_url.id(), last_visit_time, 0, | |
66 ui::PAGE_TRANSITION_RELOAD, 0)); | |
67 } | |
68 | |
69 history_url.set_visit_count(visits->size()); | |
70 return history_url; | |
71 } | |
72 | |
73 void VerifyEqual(const TypedUrlSpecifics& s1, const TypedUrlSpecifics& s2) { | |
74 // Instead of just comparing serialized strings, manually check fields to show | |
75 // differences on failure. | |
76 EXPECT_EQ(s1.url(), s2.url()); | |
77 EXPECT_EQ(s1.title(), s2.title()); | |
78 EXPECT_EQ(s1.hidden(), s2.hidden()); | |
79 EXPECT_EQ(s1.visits_size(), s2.visits_size()); | |
80 EXPECT_EQ(s1.visit_transitions_size(), s2.visit_transitions_size()); | |
81 EXPECT_EQ(s1.visits_size(), s1.visit_transitions_size()); | |
82 int size = s1.visits_size(); | |
83 for (int i = 0; i < size; ++i) { | |
84 EXPECT_EQ(s1.visits(i), s2.visits(i)) << "visits differ at index " << i; | |
85 EXPECT_EQ(s1.visit_transitions(i), s2.visit_transitions(i)) | |
86 << "visit_transitions differ at index " << i; | |
87 } | |
88 } | |
89 | |
90 void VerifyDataBatch(std::map<std::string, TypedUrlSpecifics> expected, | |
91 std::unique_ptr<DataBatch> batch) { | |
92 while (batch->HasNext()) { | |
93 const KeyAndData& pair = batch->Next(); | |
94 auto iter = expected.find(pair.first); | |
95 ASSERT_NE(iter, expected.end()); | |
96 VerifyEqual(iter->second, pair.second->specifics.typed_url()); | |
97 // Removing allows us to verify we don't see the same item multiple times, | |
98 // and that we saw everything we expected. | |
99 expected.erase(iter); | |
100 } | |
101 EXPECT_TRUE(expected.empty()); | |
102 } | |
103 | |
104 class TestHistoryBackendDelegate : public HistoryBackend::Delegate { | |
105 public: | |
106 TestHistoryBackendDelegate() {} | |
107 | |
108 void NotifyProfileError(sql::InitStatus init_status, | |
109 const std::string& diagnostics) override {} | |
110 void SetInMemoryBackend( | |
111 std::unique_ptr<InMemoryHistoryBackend> backend) override{}; | |
112 void NotifyFaviconsChanged(const std::set<GURL>& page_urls, | |
113 const GURL& icon_url) override{}; | |
114 void NotifyURLVisited(ui::PageTransition transition, | |
115 const URLRow& row, | |
116 const RedirectList& redirects, | |
117 base::Time visit_time) override{}; | |
118 void NotifyURLsModified(const URLRows& changed_urls) override{}; | |
119 void NotifyURLsDeleted(bool all_history, | |
120 bool expired, | |
121 const URLRows& deleted_rows, | |
122 const std::set<GURL>& favicon_urls) override{}; | |
123 void NotifyKeywordSearchTermUpdated(const URLRow& row, | |
124 KeywordID keyword_id, | |
125 const base::string16& term) override{}; | |
126 void NotifyKeywordSearchTermDeleted(URLID url_id) override{}; | |
127 void DBLoaded() override{}; | |
128 | |
129 private: | |
130 DISALLOW_COPY_AND_ASSIGN(TestHistoryBackendDelegate); | |
131 }; | |
132 | |
133 class TestHistoryBackend : public HistoryBackend { | |
134 public: | |
135 TestHistoryBackend() | |
136 : HistoryBackend(new TestHistoryBackendDelegate(), | |
137 nullptr, | |
138 base::ThreadTaskRunnerHandle::Get()) {} | |
139 | |
140 bool IsExpiredVisitTime(const base::Time& time) override { | |
141 return time.ToInternalValue() == kExpiredVisit; | |
142 } | |
143 | |
144 URLID GetIdByUrl(const GURL& gurl) { | |
145 return db()->GetRowForURL(gurl, nullptr); | |
146 } | |
147 | |
148 void SetVisitsForUrl(URLRow& new_url, const VisitVector visits) { | |
149 std::vector<history::VisitInfo> added_visits; | |
150 URLRows new_urls; | |
151 DeleteURL(new_url.url()); | |
152 for (const auto& visit : visits) { | |
153 added_visits.push_back( | |
154 history::VisitInfo(visit.visit_time, visit.transition)); | |
155 } | |
156 new_urls.push_back(new_url); | |
157 AddPagesWithDetails(new_urls, history::SOURCE_SYNCED); | |
158 AddVisits(new_url.url(), added_visits, history::SOURCE_SYNCED); | |
159 new_url.set_id(GetIdByUrl(new_url.url())); | |
160 } | |
161 | |
162 private: | |
163 ~TestHistoryBackend() override {} | |
164 }; | |
165 | |
166 } // namespace | |
167 | |
168 class TypedURLSyncBridgeTest : public testing::Test { | |
169 public: | |
170 TypedURLSyncBridgeTest() : typed_url_sync_bridge_(NULL) {} | |
171 ~TypedURLSyncBridgeTest() override {} | |
172 | |
173 void SetUp() override { | |
174 fake_history_backend_ = new TestHistoryBackend(); | |
175 ASSERT_TRUE(test_dir_.CreateUniqueTempDir()); | |
176 fake_history_backend_->Init( | |
177 false, TestHistoryDatabaseParamsForPath(test_dir_.GetPath())); | |
178 std::unique_ptr<TypedURLSyncBridge> bridge = | |
179 base::MakeUnique<TypedURLSyncBridge>( | |
180 fake_history_backend_.get(), fake_history_backend_->db(), | |
181 RecordingModelTypeChangeProcessor::FactoryForBridgeTest(&processor_, | |
182 false)); | |
183 typed_url_sync_bridge_ = bridge.get(); | |
184 fake_history_backend_->SetTypedURLSyncBridge(std::move(bridge)); | |
185 } | |
186 | |
187 void TearDown() override { fake_history_backend_->Closing(); } | |
188 | |
189 // Fills |specifics| with the sync data for |url| and |visits|. | |
190 static bool WriteToTypedUrlSpecifics(const URLRow& url, | |
191 const VisitVector& visits, | |
192 sync_pb::TypedUrlSpecifics* specifics); | |
193 | |
194 std::string GetStorageKey(const TypedUrlSpecifics& specifics) { | |
195 std::string key = | |
196 bridge()->GetStorageKey(SpecificsToEntity(specifics).value()); | |
197 EXPECT_FALSE(key.empty()); | |
198 return key; | |
199 } | |
200 | |
201 EntityDataPtr SpecificsToEntity(const TypedUrlSpecifics& specifics) { | |
202 EntityData data; | |
203 data.client_tag_hash = "ignored"; | |
204 *data.specifics.mutable_typed_url() = specifics; | |
205 return data.PassToPtr(); | |
206 } | |
207 | |
208 std::map<std::string, TypedUrlSpecifics> ExpectedMap( | |
209 const std::vector<TypedUrlSpecifics>& specifics_vector) { | |
210 std::map<std::string, TypedUrlSpecifics> map; | |
211 for (const auto& specifics : specifics_vector) { | |
212 map[GetStorageKey(specifics)] = specifics; | |
213 } | |
214 return map; | |
215 } | |
216 | |
217 void VerifyLocalHistoryData(const std::vector<TypedUrlSpecifics>& expected) { | |
218 bridge()->GetAllData(base::Bind(&VerifyDataBatch, ExpectedMap(expected))); | |
219 } | |
220 | |
221 TypedURLSyncBridge* bridge() { return typed_url_sync_bridge_; } | |
222 | |
223 TypedURLSyncMetadataDatabase* metadata_store() { | |
224 return bridge()->sync_metadata_database_; | |
225 } | |
226 | |
227 const RecordingModelTypeChangeProcessor& processor() { return *processor_; } | |
228 | |
229 protected: | |
230 base::MessageLoop message_loop_; | |
231 base::ScopedTempDir test_dir_; | |
232 scoped_refptr<TestHistoryBackend> fake_history_backend_; | |
233 TypedURLSyncBridge* typed_url_sync_bridge_; | |
234 // A non-owning pointer to the processor given to the bridge. Will be null | |
235 // before being given to the bridge, to make ownership easier. | |
236 RecordingModelTypeChangeProcessor* processor_ = nullptr; | |
237 }; | |
238 | |
239 // Static. | |
240 bool TypedURLSyncBridgeTest::WriteToTypedUrlSpecifics( | |
pavely
2017/06/01 20:49:18
You could have inlined this method along with the
Gang Wu
2017/06/02 00:00:24
Done.
| |
241 const URLRow& url, | |
242 const VisitVector& visits, | |
243 sync_pb::TypedUrlSpecifics* specifics) { | |
244 return TypedURLSyncBridge::WriteToTypedUrlSpecifics(url, visits, specifics); | |
245 } | |
246 | |
247 // Add two typed urls locally and verify bridge can get them from GetAllData. | |
248 TEST_F(TypedURLSyncBridgeTest, GetAllData) { | |
249 // Add two urls to backend. | |
250 VisitVector visits1, visits2; | |
251 URLRow row1 = MakeTypedUrlRow(kURL, kTitle, 1, 3, false, &visits1); | |
252 URLRow row2 = MakeTypedUrlRow(kURL2, kTitle2, 2, 4, false, &visits2); | |
253 fake_history_backend_->SetVisitsForUrl(row1, visits1); | |
254 fake_history_backend_->SetVisitsForUrl(row2, visits2); | |
255 | |
256 // Create the same data in sync. | |
257 sync_pb::TypedUrlSpecifics typed_url1, typed_url2; | |
258 WriteToTypedUrlSpecifics(row1, visits1, &typed_url1); | |
259 WriteToTypedUrlSpecifics(row2, visits2, &typed_url2); | |
260 | |
261 // Check that the local cache was is still correct. | |
pavely
2017/06/01 20:49:18
remove "was".
Gang Wu
2017/06/02 00:00:24
Done.
| |
262 VerifyLocalHistoryData({typed_url1, typed_url2}); | |
263 } | |
264 | |
265 } // namespace history | |
OLD | NEW |