OLD | NEW |
| (Empty) |
1 // Copyright 2016 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/reading_list/ios/reading_list_store.h" | |
6 | |
7 #include <map> | |
8 #include <set> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/memory/ptr_util.h" | |
12 #include "base/message_loop/message_loop.h" | |
13 #include "base/run_loop.h" | |
14 #include "base/test/simple_test_clock.h" | |
15 #include "components/reading_list/ios/reading_list_model_impl.h" | |
16 #include "components/sync/model/fake_model_type_change_processor.h" | |
17 #include "components/sync/model/model_type_store_test_util.h" | |
18 #include "testing/gtest/include/gtest/gtest.h" | |
19 | |
20 namespace { | |
21 | |
22 // Tests that the transition from |entryA| to |entryB| is possible (|possible| | |
23 // is true) or not. | |
24 void ExpectAB(const sync_pb::ReadingListSpecifics& entryA, | |
25 const sync_pb::ReadingListSpecifics& entryB, | |
26 bool possible) { | |
27 EXPECT_EQ(ReadingListStore::CompareEntriesForSync(entryA, entryB), possible); | |
28 std::unique_ptr<ReadingListEntry> a = | |
29 ReadingListEntry::FromReadingListSpecifics(entryA, | |
30 base::Time::FromTimeT(10)); | |
31 std::unique_ptr<ReadingListEntry> b = | |
32 ReadingListEntry::FromReadingListSpecifics(entryB, | |
33 base::Time::FromTimeT(10)); | |
34 a->MergeWithEntry(*b); | |
35 std::unique_ptr<sync_pb::ReadingListSpecifics> mergedEntry = | |
36 a->AsReadingListSpecifics(); | |
37 if (possible) { | |
38 // If transition is possible, the merge should be B. | |
39 EXPECT_EQ(entryB.SerializeAsString(), mergedEntry->SerializeAsString()); | |
40 } else { | |
41 // If transition is not possible, the transition shold be possible to the | |
42 // merged state. | |
43 EXPECT_TRUE(ReadingListStore::CompareEntriesForSync(entryA, *mergedEntry)); | |
44 EXPECT_TRUE(ReadingListStore::CompareEntriesForSync(entryB, *mergedEntry)); | |
45 } | |
46 } | |
47 | |
48 base::Time AdvanceAndGetTime(base::SimpleTestClock* clock) { | |
49 clock->Advance(base::TimeDelta::FromMilliseconds(10)); | |
50 return clock->Now(); | |
51 } | |
52 | |
53 } // namespace | |
54 | |
55 class FakeModelTypeChangeProcessorObserver { | |
56 public: | |
57 virtual void Put(const std::string& client_tag, | |
58 std::unique_ptr<syncer::EntityData> entity_data, | |
59 syncer::MetadataChangeList* metadata_change_list) = 0; | |
60 | |
61 virtual void Delete(const std::string& client_tag, | |
62 syncer::MetadataChangeList* metadata_change_list) = 0; | |
63 }; | |
64 | |
65 class TestModelTypeChangeProcessor | |
66 : public syncer::FakeModelTypeChangeProcessor { | |
67 public: | |
68 void SetObserver(FakeModelTypeChangeProcessorObserver* observer) { | |
69 observer_ = observer; | |
70 } | |
71 | |
72 void Put(const std::string& client_tag, | |
73 std::unique_ptr<syncer::EntityData> entity_data, | |
74 syncer::MetadataChangeList* metadata_change_list) override { | |
75 observer_->Put(client_tag, std::move(entity_data), metadata_change_list); | |
76 } | |
77 | |
78 void Delete(const std::string& client_tag, | |
79 syncer::MetadataChangeList* metadata_change_list) override { | |
80 observer_->Delete(client_tag, metadata_change_list); | |
81 } | |
82 | |
83 private: | |
84 FakeModelTypeChangeProcessorObserver* observer_; | |
85 }; | |
86 | |
87 class ReadingListStoreTest : public testing::Test, | |
88 public FakeModelTypeChangeProcessorObserver, | |
89 public ReadingListStoreDelegate { | |
90 protected: | |
91 ReadingListStoreTest() | |
92 : store_(syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) { | |
93 ClearState(); | |
94 reading_list_store_ = base::MakeUnique<ReadingListStore>( | |
95 base::Bind(&syncer::ModelTypeStoreTestUtil::MoveStoreToCallback, | |
96 base::Passed(&store_)), | |
97 base::Bind(&ReadingListStoreTest::CreateModelTypeChangeProcessor, | |
98 base::Unretained(this))); | |
99 auto clock = base::MakeUnique<base::SimpleTestClock>(); | |
100 clock_ = clock.get(); | |
101 model_ = base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr, | |
102 std::move(clock)); | |
103 reading_list_store_->SetReadingListModel(model_.get(), this, clock_); | |
104 | |
105 base::RunLoop().RunUntilIdle(); | |
106 } | |
107 | |
108 std::unique_ptr<syncer::ModelTypeChangeProcessor> | |
109 CreateModelTypeChangeProcessor(syncer::ModelType type, | |
110 syncer::ModelTypeSyncBridge* service) { | |
111 auto processor = base::MakeUnique<TestModelTypeChangeProcessor>(); | |
112 processor->SetObserver(this); | |
113 return std::move(processor); | |
114 } | |
115 | |
116 void Put(const std::string& storage_key, | |
117 std::unique_ptr<syncer::EntityData> entity_data, | |
118 syncer::MetadataChangeList* metadata_changes) override { | |
119 put_multimap_.insert(std::make_pair(storage_key, std::move(entity_data))); | |
120 put_called_++; | |
121 } | |
122 | |
123 void Delete(const std::string& storage_key, | |
124 syncer::MetadataChangeList* metadata_changes) override { | |
125 delete_set_.insert(storage_key); | |
126 delete_called_++; | |
127 } | |
128 | |
129 void AssertCounts(int put_called, | |
130 int delete_called, | |
131 int sync_add_called, | |
132 int sync_remove_called, | |
133 int sync_merge_called) { | |
134 EXPECT_EQ(put_called, put_called_); | |
135 EXPECT_EQ(delete_called, delete_called_); | |
136 EXPECT_EQ(sync_add_called, sync_add_called_); | |
137 EXPECT_EQ(sync_remove_called, sync_remove_called_); | |
138 EXPECT_EQ(sync_merge_called, sync_merge_called_); | |
139 } | |
140 | |
141 void ClearState() { | |
142 delete_called_ = 0; | |
143 put_called_ = 0; | |
144 delete_set_.clear(); | |
145 put_multimap_.clear(); | |
146 sync_add_called_ = 0; | |
147 sync_remove_called_ = 0; | |
148 sync_merge_called_ = 0; | |
149 sync_added_.clear(); | |
150 sync_removed_.clear(); | |
151 sync_merged_.clear(); | |
152 } | |
153 | |
154 // These three mathods handle callbacks from a ReadingListStore. | |
155 void StoreLoaded(std::unique_ptr<ReadingListEntries> entries) override {} | |
156 | |
157 // Handle sync events. | |
158 void SyncAddEntry(std::unique_ptr<ReadingListEntry> entry) override { | |
159 sync_add_called_++; | |
160 sync_added_[entry->URL().spec()] = entry->IsRead(); | |
161 } | |
162 | |
163 void SyncRemoveEntry(const GURL& gurl) override { | |
164 sync_remove_called_++; | |
165 sync_removed_.insert(gurl.spec()); | |
166 } | |
167 | |
168 ReadingListEntry* SyncMergeEntry( | |
169 std::unique_ptr<ReadingListEntry> entry) override { | |
170 sync_merge_called_++; | |
171 sync_merged_[entry->URL().spec()] = entry->IsRead(); | |
172 return model_->SyncMergeEntry(std::move(entry)); | |
173 } | |
174 | |
175 // In memory model type store needs a MessageLoop. | |
176 base::MessageLoop message_loop_; | |
177 | |
178 std::unique_ptr<syncer::ModelTypeStore> store_; | |
179 std::unique_ptr<ReadingListModelImpl> model_; | |
180 base::SimpleTestClock* clock_; | |
181 std::unique_ptr<ReadingListStore> reading_list_store_; | |
182 int put_called_; | |
183 int delete_called_; | |
184 int sync_add_called_; | |
185 int sync_remove_called_; | |
186 int sync_merge_called_; | |
187 std::map<std::string, std::unique_ptr<syncer::EntityData>> put_multimap_; | |
188 std::set<std::string> delete_set_; | |
189 std::map<std::string, bool> sync_added_; | |
190 std::set<std::string> sync_removed_; | |
191 std::map<std::string, bool> sync_merged_; | |
192 }; | |
193 | |
194 TEST_F(ReadingListStoreTest, CheckEmpties) { | |
195 EXPECT_EQ(0ul, model_->size()); | |
196 } | |
197 | |
198 TEST_F(ReadingListStoreTest, SaveOneRead) { | |
199 ReadingListEntry entry(GURL("http://read.example.com/"), "read title", | |
200 AdvanceAndGetTime(clock_)); | |
201 entry.SetRead(true, AdvanceAndGetTime(clock_)); | |
202 AdvanceAndGetTime(clock_); | |
203 reading_list_store_->SaveEntry(entry); | |
204 AssertCounts(1, 0, 0, 0, 0); | |
205 syncer::EntityData* data = put_multimap_["http://read.example.com/"].get(); | |
206 const sync_pb::ReadingListSpecifics& specifics = | |
207 data->specifics.reading_list(); | |
208 EXPECT_EQ(specifics.title(), "read title"); | |
209 EXPECT_EQ(specifics.url(), "http://read.example.com/"); | |
210 EXPECT_EQ(specifics.status(), sync_pb::ReadingListSpecifics::READ); | |
211 } | |
212 | |
213 TEST_F(ReadingListStoreTest, SaveOneUnread) { | |
214 ReadingListEntry entry(GURL("http://unread.example.com/"), "unread title", | |
215 AdvanceAndGetTime(clock_)); | |
216 reading_list_store_->SaveEntry(entry); | |
217 AssertCounts(1, 0, 0, 0, 0); | |
218 syncer::EntityData* data = put_multimap_["http://unread.example.com/"].get(); | |
219 const sync_pb::ReadingListSpecifics& specifics = | |
220 data->specifics.reading_list(); | |
221 EXPECT_EQ(specifics.title(), "unread title"); | |
222 EXPECT_EQ(specifics.url(), "http://unread.example.com/"); | |
223 EXPECT_EQ(specifics.status(), sync_pb::ReadingListSpecifics::UNSEEN); | |
224 } | |
225 | |
226 TEST_F(ReadingListStoreTest, SyncMergeOneEntry) { | |
227 syncer::EntityDataMap remote_input; | |
228 ReadingListEntry entry(GURL("http://read.example.com/"), "read title", | |
229 AdvanceAndGetTime(clock_)); | |
230 entry.SetRead(true, AdvanceAndGetTime(clock_)); | |
231 std::unique_ptr<sync_pb::ReadingListSpecifics> specifics = | |
232 entry.AsReadingListSpecifics(); | |
233 | |
234 syncer::EntityData data; | |
235 data.client_tag_hash = "http://read.example.com/"; | |
236 *data.specifics.mutable_reading_list() = *specifics; | |
237 | |
238 remote_input["http://read.example.com/"] = data.PassToPtr(); | |
239 | |
240 std::unique_ptr<syncer::MetadataChangeList> metadata_changes( | |
241 reading_list_store_->CreateMetadataChangeList()); | |
242 auto error = reading_list_store_->MergeSyncData(std::move(metadata_changes), | |
243 remote_input); | |
244 AssertCounts(0, 0, 1, 0, 0); | |
245 EXPECT_EQ(sync_added_.size(), 1u); | |
246 EXPECT_EQ(sync_added_.count("http://read.example.com/"), 1u); | |
247 EXPECT_EQ(sync_added_["http://read.example.com/"], true); | |
248 } | |
249 | |
250 TEST_F(ReadingListStoreTest, ApplySyncChangesOneAdd) { | |
251 syncer::EntityDataMap remote_input; | |
252 ReadingListEntry entry(GURL("http://read.example.com/"), "read title", | |
253 AdvanceAndGetTime(clock_)); | |
254 entry.SetRead(true, AdvanceAndGetTime(clock_)); | |
255 std::unique_ptr<sync_pb::ReadingListSpecifics> specifics = | |
256 entry.AsReadingListSpecifics(); | |
257 syncer::EntityData data; | |
258 data.client_tag_hash = "http://read.example.com/"; | |
259 *data.specifics.mutable_reading_list() = *specifics; | |
260 | |
261 syncer::EntityChangeList add_changes; | |
262 | |
263 add_changes.push_back(syncer::EntityChange::CreateAdd( | |
264 "http://read.example.com/", data.PassToPtr())); | |
265 auto error = reading_list_store_->ApplySyncChanges( | |
266 reading_list_store_->CreateMetadataChangeList(), add_changes); | |
267 AssertCounts(0, 0, 1, 0, 0); | |
268 EXPECT_EQ(sync_added_.size(), 1u); | |
269 EXPECT_EQ(sync_added_.count("http://read.example.com/"), 1u); | |
270 EXPECT_EQ(sync_added_["http://read.example.com/"], true); | |
271 } | |
272 | |
273 TEST_F(ReadingListStoreTest, ApplySyncChangesOneMerge) { | |
274 syncer::EntityDataMap remote_input; | |
275 AdvanceAndGetTime(clock_); | |
276 model_->AddEntry(GURL("http://unread.example.com/"), "unread title", | |
277 reading_list::ADDED_VIA_CURRENT_APP); | |
278 | |
279 ReadingListEntry new_entry(GURL("http://unread.example.com/"), "unread title", | |
280 AdvanceAndGetTime(clock_)); | |
281 new_entry.SetRead(true, AdvanceAndGetTime(clock_)); | |
282 std::unique_ptr<sync_pb::ReadingListSpecifics> specifics = | |
283 new_entry.AsReadingListSpecifics(); | |
284 syncer::EntityData data; | |
285 data.client_tag_hash = "http://unread.example.com/"; | |
286 *data.specifics.mutable_reading_list() = *specifics; | |
287 | |
288 syncer::EntityChangeList add_changes; | |
289 add_changes.push_back(syncer::EntityChange::CreateAdd( | |
290 "http://unread.example.com/", data.PassToPtr())); | |
291 auto error = reading_list_store_->ApplySyncChanges( | |
292 reading_list_store_->CreateMetadataChangeList(), add_changes); | |
293 AssertCounts(1, 0, 0, 0, 1); | |
294 EXPECT_EQ(sync_merged_.size(), 1u); | |
295 EXPECT_EQ(sync_merged_.count("http://unread.example.com/"), 1u); | |
296 EXPECT_EQ(sync_merged_["http://unread.example.com/"], true); | |
297 } | |
298 | |
299 TEST_F(ReadingListStoreTest, ApplySyncChangesOneIgnored) { | |
300 // Read entry but with unread URL as it must update the other one. | |
301 ReadingListEntry old_entry(GURL("http://unread.example.com/"), | |
302 "old unread title", AdvanceAndGetTime(clock_)); | |
303 old_entry.SetRead(true, AdvanceAndGetTime(clock_)); | |
304 | |
305 syncer::EntityDataMap remote_input; | |
306 AdvanceAndGetTime(clock_); | |
307 model_->AddEntry(GURL("http://unread.example.com/"), "new unread title", | |
308 reading_list::ADDED_VIA_CURRENT_APP); | |
309 AssertCounts(0, 0, 0, 0, 0); | |
310 | |
311 std::unique_ptr<sync_pb::ReadingListSpecifics> specifics = | |
312 old_entry.AsReadingListSpecifics(); | |
313 syncer::EntityData data; | |
314 data.client_tag_hash = "http://unread.example.com/"; | |
315 *data.specifics.mutable_reading_list() = *specifics; | |
316 | |
317 syncer::EntityChangeList add_changes; | |
318 add_changes.push_back(syncer::EntityChange::CreateAdd( | |
319 "http://unread.example.com/", data.PassToPtr())); | |
320 auto error = reading_list_store_->ApplySyncChanges( | |
321 reading_list_store_->CreateMetadataChangeList(), add_changes); | |
322 AssertCounts(1, 0, 0, 0, 1); | |
323 EXPECT_EQ(sync_merged_.size(), 1u); | |
324 } | |
325 | |
326 TEST_F(ReadingListStoreTest, ApplySyncChangesOneRemove) { | |
327 syncer::EntityChangeList delete_changes; | |
328 delete_changes.push_back( | |
329 syncer::EntityChange::CreateDelete("http://read.example.com/")); | |
330 auto error = reading_list_store_->ApplySyncChanges( | |
331 reading_list_store_->CreateMetadataChangeList(), delete_changes); | |
332 AssertCounts(0, 0, 0, 1, 0); | |
333 EXPECT_EQ(sync_removed_.size(), 1u); | |
334 EXPECT_EQ(sync_removed_.count("http://read.example.com/"), 1u); | |
335 } | |
336 | |
337 TEST_F(ReadingListStoreTest, CompareEntriesForSync) { | |
338 sync_pb::ReadingListSpecifics entryA; | |
339 sync_pb::ReadingListSpecifics entryB; | |
340 entryA.set_entry_id("http://foo.bar/"); | |
341 entryB.set_entry_id("http://foo.bar/"); | |
342 entryA.set_url("http://foo.bar/"); | |
343 entryB.set_url("http://foo.bar/"); | |
344 entryA.set_title("Foo Bar"); | |
345 entryB.set_title("Foo Bar"); | |
346 entryA.set_status(sync_pb::ReadingListSpecifics::UNREAD); | |
347 entryB.set_status(sync_pb::ReadingListSpecifics::UNREAD); | |
348 entryA.set_creation_time_us(10); | |
349 entryB.set_creation_time_us(10); | |
350 entryA.set_first_read_time_us(50); | |
351 entryB.set_first_read_time_us(50); | |
352 entryA.set_update_time_us(100); | |
353 entryB.set_update_time_us(100); | |
354 entryA.set_update_title_time_us(110); | |
355 entryB.set_update_title_time_us(110); | |
356 // Equal entries can be submitted. | |
357 ExpectAB(entryA, entryB, true); | |
358 ExpectAB(entryB, entryA, true); | |
359 | |
360 // Try to update each field. | |
361 | |
362 // You cannot change the URL of an entry. | |
363 entryA.set_url("http://foo.foo/"); | |
364 EXPECT_FALSE(ReadingListStore::CompareEntriesForSync(entryA, entryB)); | |
365 EXPECT_FALSE(ReadingListStore::CompareEntriesForSync(entryB, entryA)); | |
366 entryA.set_url("http://foo.bar/"); | |
367 | |
368 // You can set a title to a title later in alphabetical order if the | |
369 // update_title_time is the same. If a title has been more recently updated, | |
370 // the only possible transition is to this one. | |
371 entryA.set_title(""); | |
372 ExpectAB(entryA, entryB, true); | |
373 ExpectAB(entryB, entryA, false); | |
374 entryA.set_update_title_time_us(109); | |
375 ExpectAB(entryA, entryB, true); | |
376 ExpectAB(entryB, entryA, false); | |
377 entryA.set_update_title_time_us(110); | |
378 | |
379 entryA.set_title("Foo Aar"); | |
380 ExpectAB(entryA, entryB, true); | |
381 ExpectAB(entryB, entryA, false); | |
382 entryA.set_update_title_time_us(109); | |
383 ExpectAB(entryA, entryB, true); | |
384 ExpectAB(entryB, entryA, false); | |
385 entryA.set_update_title_time_us(110); | |
386 | |
387 entryA.set_title("Foo Ba"); | |
388 ExpectAB(entryA, entryB, true); | |
389 ExpectAB(entryB, entryA, false); | |
390 entryA.set_update_title_time_us(109); | |
391 ExpectAB(entryA, entryB, true); | |
392 ExpectAB(entryB, entryA, false); | |
393 entryA.set_update_title_time_us(110); | |
394 | |
395 entryA.set_title("Foo Bas"); | |
396 ExpectAB(entryA, entryB, false); | |
397 ExpectAB(entryB, entryA, true); | |
398 entryA.set_update_title_time_us(109); | |
399 ExpectAB(entryA, entryB, true); | |
400 ExpectAB(entryB, entryA, false); | |
401 entryA.set_update_title_time_us(110); | |
402 entryA.set_title("Foo Bar"); | |
403 | |
404 // Update times. | |
405 entryA.set_creation_time_us(9); | |
406 ExpectAB(entryA, entryB, true); | |
407 ExpectAB(entryB, entryA, false); | |
408 entryA.set_first_read_time_us(51); | |
409 ExpectAB(entryA, entryB, true); | |
410 ExpectAB(entryB, entryA, false); | |
411 entryA.set_first_read_time_us(49); | |
412 ExpectAB(entryA, entryB, true); | |
413 ExpectAB(entryB, entryA, false); | |
414 entryA.set_first_read_time_us(0); | |
415 ExpectAB(entryA, entryB, true); | |
416 ExpectAB(entryB, entryA, false); | |
417 entryA.set_first_read_time_us(50); | |
418 entryB.set_first_read_time_us(0); | |
419 ExpectAB(entryA, entryB, true); | |
420 ExpectAB(entryB, entryA, false); | |
421 entryB.set_first_read_time_us(50); | |
422 entryA.set_creation_time_us(10); | |
423 entryA.set_first_read_time_us(51); | |
424 ExpectAB(entryA, entryB, true); | |
425 ExpectAB(entryB, entryA, false); | |
426 entryA.set_first_read_time_us(0); | |
427 ExpectAB(entryA, entryB, true); | |
428 ExpectAB(entryB, entryA, false); | |
429 entryA.set_first_read_time_us(50); | |
430 | |
431 entryA.set_update_time_us(99); | |
432 ExpectAB(entryA, entryB, true); | |
433 ExpectAB(entryB, entryA, false); | |
434 sync_pb::ReadingListSpecifics::ReadingListEntryStatus status_oder[3] = { | |
435 sync_pb::ReadingListSpecifics::UNSEEN, | |
436 sync_pb::ReadingListSpecifics::UNREAD, | |
437 sync_pb::ReadingListSpecifics::READ}; | |
438 for (int index_a = 0; index_a < 3; index_a++) { | |
439 entryA.set_status(status_oder[index_a]); | |
440 for (int index_b = 0; index_b < 3; index_b++) { | |
441 entryB.set_status(status_oder[index_b]); | |
442 ExpectAB(entryA, entryB, true); | |
443 ExpectAB(entryB, entryA, false); | |
444 } | |
445 } | |
446 entryA.set_update_time_us(100); | |
447 for (int index_a = 0; index_a < 3; index_a++) { | |
448 entryA.set_status(status_oder[index_a]); | |
449 entryB.set_status(status_oder[index_a]); | |
450 ExpectAB(entryA, entryB, true); | |
451 ExpectAB(entryB, entryA, true); | |
452 for (int index_b = index_a + 1; index_b < 3; index_b++) { | |
453 entryB.set_status(status_oder[index_b]); | |
454 ExpectAB(entryA, entryB, true); | |
455 ExpectAB(entryB, entryA, false); | |
456 } | |
457 } | |
458 } | |
OLD | NEW |