Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(92)

Side by Side Diff: components/reading_list/ios/reading_list_store_unittest.mm

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

Powered by Google App Engine
This is Rietveld 408576698