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_model.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/memory/ptr_util.h" | |
9 #include "base/test/simple_test_clock.h" | |
10 #include "components/reading_list/ios/reading_list_model_impl.h" | |
11 #include "components/reading_list/ios/reading_list_model_storage.h" | |
12 #include "components/reading_list/ios/reading_list_store_delegate.h" | |
13 #include "components/sync/model/metadata_change_list.h" | |
14 #include "components/sync/model/model_error.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 | |
17 namespace { | |
18 | |
19 const GURL callback_url("http://example.com"); | |
20 const std::string callback_title("test title"); | |
21 | |
22 base::Time AdvanceAndGetTime(base::SimpleTestClock* clock) { | |
23 clock->Advance(base::TimeDelta::FromMilliseconds(10)); | |
24 return clock->Now(); | |
25 } | |
26 | |
27 class TestReadingListStorageObserver { | |
28 public: | |
29 virtual void ReadingListDidSaveEntry() = 0; | |
30 virtual void ReadingListDidRemoveEntry() = 0; | |
31 }; | |
32 | |
33 class TestReadingListStorage : public ReadingListModelStorage { | |
34 public: | |
35 TestReadingListStorage(TestReadingListStorageObserver* observer, | |
36 base::SimpleTestClock* clock) | |
37 : ReadingListModelStorage( | |
38 base::Bind(&syncer::ModelTypeChangeProcessor::Create, | |
39 base::RepeatingClosure()), | |
40 syncer::READING_LIST), | |
41 entries_(new ReadingListStoreDelegate::ReadingListEntries()), | |
42 observer_(observer), | |
43 clock_(clock) {} | |
44 | |
45 void AddSampleEntries() { | |
46 // Adds timer and interlace read/unread entry creation to avoid having two | |
47 // entries with the same creation timestamp. | |
48 ReadingListEntry unread_a(GURL("http://unread_a.com"), "unread_a", | |
49 AdvanceAndGetTime(clock_)); | |
50 entries_->insert( | |
51 std::make_pair(GURL("http://unread_a.com"), std::move(unread_a))); | |
52 | |
53 ReadingListEntry read_a(GURL("http://read_a.com"), "read_a", | |
54 AdvanceAndGetTime(clock_)); | |
55 read_a.SetRead(true, AdvanceAndGetTime(clock_)); | |
56 entries_->insert( | |
57 std::make_pair(GURL("http://read_a.com"), std::move(read_a))); | |
58 | |
59 ReadingListEntry unread_b(GURL("http://unread_b.com"), "unread_b", | |
60 AdvanceAndGetTime(clock_)); | |
61 entries_->insert( | |
62 std::make_pair(GURL("http://unread_b.com"), std::move(unread_b))); | |
63 | |
64 ReadingListEntry read_b(GURL("http://read_b.com"), "read_b", | |
65 AdvanceAndGetTime(clock_)); | |
66 read_b.SetRead(true, AdvanceAndGetTime(clock_)); | |
67 entries_->insert( | |
68 std::make_pair(GURL("http://read_b.com"), std::move(read_b))); | |
69 | |
70 ReadingListEntry unread_c(GURL("http://unread_c.com"), "unread_c", | |
71 AdvanceAndGetTime(clock_)); | |
72 entries_->insert( | |
73 std::make_pair(GURL("http://unread_c.com"), std::move(unread_c))); | |
74 | |
75 ReadingListEntry read_c(GURL("http://read_c.com"), "read_c", | |
76 AdvanceAndGetTime(clock_)); | |
77 read_c.SetRead(true, AdvanceAndGetTime(clock_)); | |
78 entries_->insert( | |
79 std::make_pair(GURL("http://read_c.com"), std::move(read_c))); | |
80 | |
81 ReadingListEntry unread_d(GURL("http://unread_d.com"), "unread_d", | |
82 AdvanceAndGetTime(clock_)); | |
83 entries_->insert( | |
84 std::make_pair(GURL("http://unread_d.com"), std::move(unread_d))); | |
85 } | |
86 | |
87 void SetReadingListModel(ReadingListModel* model, | |
88 ReadingListStoreDelegate* delegate, | |
89 base::Clock* clock) override { | |
90 delegate->StoreLoaded(std::move(entries_)); | |
91 clock_ = static_cast<base::SimpleTestClock*>(clock); | |
92 } | |
93 | |
94 // Saves or updates an entry. If the entry is not yet in the database, it is | |
95 // created. | |
96 void SaveEntry(const ReadingListEntry& entry) override { | |
97 observer_->ReadingListDidSaveEntry(); | |
98 } | |
99 | |
100 // Removes an entry from the storage. | |
101 void RemoveEntry(const ReadingListEntry& entry) override { | |
102 observer_->ReadingListDidRemoveEntry(); | |
103 } | |
104 | |
105 std::unique_ptr<ScopedBatchUpdate> EnsureBatchCreated() override { | |
106 return std::unique_ptr<ScopedBatchUpdate>(); | |
107 } | |
108 | |
109 // Syncing is not used in this test class. | |
110 std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList() | |
111 override { | |
112 NOTREACHED(); | |
113 return std::unique_ptr<syncer::MetadataChangeList>(); | |
114 } | |
115 | |
116 base::Optional<syncer::ModelError> MergeSyncData( | |
117 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, | |
118 syncer::EntityDataMap entity_data_map) override { | |
119 NOTREACHED(); | |
120 return {}; | |
121 } | |
122 | |
123 base::Optional<syncer::ModelError> ApplySyncChanges( | |
124 std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, | |
125 syncer::EntityChangeList entity_changes) override { | |
126 NOTREACHED(); | |
127 return {}; | |
128 } | |
129 | |
130 void GetData(StorageKeyList storage_keys, DataCallback callback) override { | |
131 NOTREACHED(); | |
132 return; | |
133 } | |
134 | |
135 void GetAllData(DataCallback callback) override { | |
136 NOTREACHED(); | |
137 return; | |
138 } | |
139 | |
140 std::string GetClientTag(const syncer::EntityData& entity_data) override { | |
141 NOTREACHED(); | |
142 return ""; | |
143 } | |
144 | |
145 std::string GetStorageKey(const syncer::EntityData& entity_data) override { | |
146 NOTREACHED(); | |
147 return ""; | |
148 } | |
149 | |
150 private: | |
151 std::unique_ptr<ReadingListStoreDelegate::ReadingListEntries> entries_; | |
152 TestReadingListStorageObserver* observer_; | |
153 base::SimpleTestClock* clock_; | |
154 }; | |
155 | |
156 class ReadingListModelTest : public ReadingListModelObserver, | |
157 public TestReadingListStorageObserver, | |
158 public testing::Test { | |
159 public: | |
160 ReadingListModelTest() : callback_called_(false) { | |
161 auto clock = base::MakeUnique<base::SimpleTestClock>(); | |
162 clock_ = clock.get(); | |
163 model_ = base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr, | |
164 std::move(clock)); | |
165 ClearCounts(); | |
166 model_->AddObserver(this); | |
167 } | |
168 ~ReadingListModelTest() override {} | |
169 | |
170 void SetStorage(std::unique_ptr<TestReadingListStorage> storage, | |
171 std::unique_ptr<base::SimpleTestClock> clock) { | |
172 clock_ = clock.get(); | |
173 model_ = base::MakeUnique<ReadingListModelImpl>(std::move(storage), nullptr, | |
174 std::move(clock)); | |
175 ClearCounts(); | |
176 model_->AddObserver(this); | |
177 } | |
178 | |
179 void ClearCounts() { | |
180 observer_loaded_ = observer_started_batch_update_ = | |
181 observer_completed_batch_update_ = observer_deleted_ = | |
182 observer_remove_ = observer_move_ = observer_add_ = | |
183 observer_did_add_ = observer_update_ = observer_did_apply_ = | |
184 storage_saved_ = storage_removed_ = 0; | |
185 } | |
186 | |
187 void AssertObserverCount(int observer_loaded, | |
188 int observer_started_batch_update, | |
189 int observer_completed_batch_update, | |
190 int observer_deleted, | |
191 int observer_remove, | |
192 int observer_move, | |
193 int observer_add, | |
194 int observer_update, | |
195 int observer_did_apply) { | |
196 ASSERT_EQ(observer_loaded, observer_loaded_); | |
197 ASSERT_EQ(observer_started_batch_update, observer_started_batch_update_); | |
198 ASSERT_EQ(observer_completed_batch_update, | |
199 observer_completed_batch_update_); | |
200 ASSERT_EQ(observer_deleted, observer_deleted_); | |
201 ASSERT_EQ(observer_remove, observer_remove_); | |
202 ASSERT_EQ(observer_move, observer_move_); | |
203 // Add and did_add should be the same. | |
204 ASSERT_EQ(observer_add, observer_add_); | |
205 ASSERT_EQ(observer_add, observer_did_add_); | |
206 ASSERT_EQ(observer_update, observer_update_); | |
207 ASSERT_EQ(observer_did_apply, observer_did_apply_); | |
208 } | |
209 | |
210 void AssertStorageCount(int storage_saved, int storage_removed) { | |
211 ASSERT_EQ(storage_saved, storage_saved_); | |
212 ASSERT_EQ(storage_removed, storage_removed_); | |
213 } | |
214 | |
215 // ReadingListModelObserver | |
216 void ReadingListModelLoaded(const ReadingListModel* model) override { | |
217 observer_loaded_ += 1; | |
218 } | |
219 void ReadingListModelBeganBatchUpdates( | |
220 const ReadingListModel* model) override { | |
221 observer_started_batch_update_ += 1; | |
222 } | |
223 void ReadingListModelCompletedBatchUpdates( | |
224 const ReadingListModel* model) override { | |
225 observer_completed_batch_update_ += 1; | |
226 } | |
227 void ReadingListModelBeingDeleted(const ReadingListModel* model) override { | |
228 observer_deleted_ += 1; | |
229 } | |
230 void ReadingListWillRemoveEntry(const ReadingListModel* model, | |
231 const GURL& url) override { | |
232 observer_remove_ += 1; | |
233 } | |
234 void ReadingListWillMoveEntry(const ReadingListModel* model, | |
235 const GURL& url) override { | |
236 observer_move_ += 1; | |
237 } | |
238 void ReadingListWillAddEntry(const ReadingListModel* model, | |
239 const ReadingListEntry& entry) override { | |
240 observer_add_ += 1; | |
241 } | |
242 void ReadingListDidAddEntry(const ReadingListModel* model, | |
243 const GURL& url, | |
244 reading_list::EntrySource entry_source) override { | |
245 observer_did_add_ += 1; | |
246 } | |
247 void ReadingListWillUpdateEntry(const ReadingListModel* model, | |
248 const GURL& url) override { | |
249 observer_update_ += 1; | |
250 } | |
251 void ReadingListDidApplyChanges(ReadingListModel* model) override { | |
252 observer_did_apply_ += 1; | |
253 } | |
254 | |
255 void ReadingListDidSaveEntry() override { storage_saved_ += 1; } | |
256 void ReadingListDidRemoveEntry() override { storage_removed_ += 1; } | |
257 | |
258 size_t UnreadSize() { | |
259 size_t size = 0; | |
260 for (const auto& url : model_->Keys()) { | |
261 const ReadingListEntry* entry = model_->GetEntryByURL(url); | |
262 if (!entry->IsRead()) { | |
263 size++; | |
264 } | |
265 } | |
266 DCHECK_EQ(size, model_->unread_size()); | |
267 return size; | |
268 } | |
269 | |
270 size_t ReadSize() { | |
271 size_t size = 0; | |
272 for (const auto& url : model_->Keys()) { | |
273 const ReadingListEntry* entry = model_->GetEntryByURL(url); | |
274 if (entry->IsRead()) { | |
275 size++; | |
276 } | |
277 } | |
278 return size; | |
279 } | |
280 | |
281 void Callback(const ReadingListEntry& entry) { | |
282 EXPECT_EQ(callback_url, entry.URL()); | |
283 EXPECT_EQ(callback_title, entry.Title()); | |
284 callback_called_ = true; | |
285 } | |
286 | |
287 bool CallbackCalled() { return callback_called_; } | |
288 | |
289 protected: | |
290 int observer_loaded_; | |
291 int observer_started_batch_update_; | |
292 int observer_completed_batch_update_; | |
293 int observer_deleted_; | |
294 int observer_remove_; | |
295 int observer_move_; | |
296 int observer_add_; | |
297 int observer_did_add_; | |
298 int observer_update_; | |
299 int observer_did_apply_; | |
300 int storage_saved_; | |
301 int storage_removed_; | |
302 bool callback_called_; | |
303 | |
304 std::unique_ptr<ReadingListModelImpl> model_; | |
305 // Owned by |model_|; | |
306 base::SimpleTestClock* clock_; | |
307 }; | |
308 | |
309 // Tests creating an empty model. | |
310 TEST_F(ReadingListModelTest, EmptyLoaded) { | |
311 EXPECT_TRUE(model_->loaded()); | |
312 AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0); | |
313 EXPECT_EQ(0ul, UnreadSize()); | |
314 EXPECT_EQ(0ul, ReadSize()); | |
315 model_->Shutdown(); | |
316 EXPECT_FALSE(model_->loaded()); | |
317 AssertObserverCount(1, 0, 0, 1, 0, 0, 0, 0, 0); | |
318 } | |
319 | |
320 // Tests load model. | |
321 TEST_F(ReadingListModelTest, ModelLoaded) { | |
322 ClearCounts(); | |
323 auto clock = base::MakeUnique<base::SimpleTestClock>(); | |
324 auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get()); | |
325 storage->AddSampleEntries(); | |
326 SetStorage(std::move(storage), std::move(clock)); | |
327 | |
328 AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0); | |
329 std::map<GURL, std::string> loaded_entries; | |
330 int size = 0; | |
331 for (const auto& url : model_->Keys()) { | |
332 size++; | |
333 const ReadingListEntry* entry = model_->GetEntryByURL(url); | |
334 loaded_entries[url] = entry->Title(); | |
335 } | |
336 EXPECT_EQ(size, 7); | |
337 EXPECT_EQ(loaded_entries[GURL("http://unread_a.com")], "unread_a"); | |
338 EXPECT_EQ(loaded_entries[GURL("http://unread_b.com")], "unread_b"); | |
339 EXPECT_EQ(loaded_entries[GURL("http://unread_c.com")], "unread_c"); | |
340 EXPECT_EQ(loaded_entries[GURL("http://unread_d.com")], "unread_d"); | |
341 EXPECT_EQ(loaded_entries[GURL("http://read_a.com")], "read_a"); | |
342 EXPECT_EQ(loaded_entries[GURL("http://read_b.com")], "read_b"); | |
343 EXPECT_EQ(loaded_entries[GURL("http://read_c.com")], "read_c"); | |
344 } | |
345 | |
346 // Tests adding entry. | |
347 TEST_F(ReadingListModelTest, AddEntry) { | |
348 auto clock = base::MakeUnique<base::SimpleTestClock>(); | |
349 auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get()); | |
350 SetStorage(std::move(storage), std::move(clock)); | |
351 ClearCounts(); | |
352 | |
353 const ReadingListEntry& entry = | |
354 model_->AddEntry(GURL("http://example.com"), "\n \tsample Test ", | |
355 reading_list::ADDED_VIA_CURRENT_APP); | |
356 EXPECT_EQ(GURL("http://example.com"), entry.URL()); | |
357 EXPECT_EQ("sample Test", entry.Title()); | |
358 | |
359 AssertObserverCount(0, 0, 0, 0, 0, 0, 1, 0, 1); | |
360 AssertStorageCount(1, 0); | |
361 EXPECT_EQ(1ul, UnreadSize()); | |
362 EXPECT_EQ(0ul, ReadSize()); | |
363 EXPECT_TRUE(model_->GetLocalUnseenFlag()); | |
364 | |
365 const ReadingListEntry* other_entry = | |
366 model_->GetEntryByURL(GURL("http://example.com")); | |
367 EXPECT_NE(other_entry, nullptr); | |
368 EXPECT_FALSE(other_entry->IsRead()); | |
369 EXPECT_EQ(GURL("http://example.com"), other_entry->URL()); | |
370 EXPECT_EQ("sample Test", other_entry->Title()); | |
371 } | |
372 | |
373 // Tests addin entry from sync. | |
374 TEST_F(ReadingListModelTest, SyncAddEntry) { | |
375 auto clock = base::MakeUnique<base::SimpleTestClock>(); | |
376 auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get()); | |
377 SetStorage(std::move(storage), std::move(clock)); | |
378 auto entry = base::MakeUnique<ReadingListEntry>( | |
379 GURL("http://example.com"), "sample", AdvanceAndGetTime(clock_)); | |
380 entry->SetRead(true, AdvanceAndGetTime(clock_)); | |
381 ClearCounts(); | |
382 | |
383 model_->SyncAddEntry(std::move(entry)); | |
384 AssertObserverCount(0, 0, 0, 0, 0, 0, 1, 0, 1); | |
385 AssertStorageCount(0, 0); | |
386 EXPECT_EQ(0ul, UnreadSize()); | |
387 EXPECT_EQ(1ul, ReadSize()); | |
388 ClearCounts(); | |
389 } | |
390 | |
391 // Tests updating entry from sync. | |
392 TEST_F(ReadingListModelTest, SyncMergeEntry) { | |
393 auto clock = base::MakeUnique<base::SimpleTestClock>(); | |
394 auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get()); | |
395 SetStorage(std::move(storage), std::move(clock)); | |
396 model_->AddEntry(GURL("http://example.com"), "sample", | |
397 reading_list::ADDED_VIA_CURRENT_APP); | |
398 const base::FilePath distilled_path("distilled/page.html"); | |
399 const GURL distilled_url("http://example.com/distilled"); | |
400 int64_t size = 50; | |
401 int64_t time = 100; | |
402 model_->SetEntryDistilledInfo(GURL("http://example.com"), distilled_path, | |
403 distilled_url, size, | |
404 base::Time::FromTimeT(time)); | |
405 const ReadingListEntry* local_entry = | |
406 model_->GetEntryByURL(GURL("http://example.com")); | |
407 int64_t local_update_time = local_entry->UpdateTime(); | |
408 | |
409 auto sync_entry = base::MakeUnique<ReadingListEntry>( | |
410 GURL("http://example.com"), "sample", AdvanceAndGetTime(clock_)); | |
411 sync_entry->SetRead(true, AdvanceAndGetTime(clock_)); | |
412 ASSERT_GT(sync_entry->UpdateTime(), local_update_time); | |
413 int64_t sync_update_time = sync_entry->UpdateTime(); | |
414 EXPECT_TRUE(sync_entry->DistilledPath().empty()); | |
415 | |
416 EXPECT_EQ(1ul, UnreadSize()); | |
417 EXPECT_EQ(0ul, ReadSize()); | |
418 | |
419 ReadingListEntry* merged_entry = | |
420 model_->SyncMergeEntry(std::move(sync_entry)); | |
421 EXPECT_EQ(0ul, UnreadSize()); | |
422 EXPECT_EQ(1ul, ReadSize()); | |
423 EXPECT_EQ(merged_entry->DistilledPath(), | |
424 base::FilePath("distilled/page.html")); | |
425 EXPECT_EQ(merged_entry->UpdateTime(), sync_update_time); | |
426 EXPECT_EQ(size, merged_entry->DistillationSize()); | |
427 EXPECT_EQ(time * base::Time::kMicrosecondsPerSecond, | |
428 merged_entry->DistillationTime()); | |
429 } | |
430 | |
431 // Tests deleting entry. | |
432 TEST_F(ReadingListModelTest, RemoveEntryByUrl) { | |
433 auto clock = base::MakeUnique<base::SimpleTestClock>(); | |
434 auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get()); | |
435 SetStorage(std::move(storage), std::move(clock)); | |
436 model_->AddEntry(GURL("http://example.com"), "sample", | |
437 reading_list::ADDED_VIA_CURRENT_APP); | |
438 ClearCounts(); | |
439 EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr); | |
440 EXPECT_EQ(1ul, UnreadSize()); | |
441 EXPECT_EQ(0ul, ReadSize()); | |
442 model_->RemoveEntryByURL(GURL("http://example.com")); | |
443 AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1); | |
444 AssertStorageCount(0, 1); | |
445 EXPECT_EQ(0ul, UnreadSize()); | |
446 EXPECT_EQ(0ul, ReadSize()); | |
447 EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr); | |
448 | |
449 model_->AddEntry(GURL("http://example.com"), "sample", | |
450 reading_list::ADDED_VIA_CURRENT_APP); | |
451 model_->SetReadStatus(GURL("http://example.com"), true); | |
452 ClearCounts(); | |
453 EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr); | |
454 EXPECT_EQ(0ul, UnreadSize()); | |
455 EXPECT_EQ(1ul, ReadSize()); | |
456 model_->RemoveEntryByURL(GURL("http://example.com")); | |
457 AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1); | |
458 AssertStorageCount(0, 1); | |
459 EXPECT_EQ(0ul, UnreadSize()); | |
460 EXPECT_EQ(0ul, ReadSize()); | |
461 EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr); | |
462 } | |
463 | |
464 // Tests deleting entry from sync. | |
465 TEST_F(ReadingListModelTest, RemoveSyncEntryByUrl) { | |
466 auto clock = base::MakeUnique<base::SimpleTestClock>(); | |
467 auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get()); | |
468 SetStorage(std::move(storage), std::move(clock)); | |
469 model_->AddEntry(GURL("http://example.com"), "sample", | |
470 reading_list::ADDED_VIA_CURRENT_APP); | |
471 ClearCounts(); | |
472 EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr); | |
473 EXPECT_EQ(1ul, UnreadSize()); | |
474 EXPECT_EQ(0ul, ReadSize()); | |
475 model_->SyncRemoveEntry(GURL("http://example.com")); | |
476 AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1); | |
477 AssertStorageCount(0, 0); | |
478 EXPECT_EQ(0ul, UnreadSize()); | |
479 EXPECT_EQ(0ul, ReadSize()); | |
480 EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr); | |
481 | |
482 model_->AddEntry(GURL("http://example.com"), "sample", | |
483 reading_list::ADDED_VIA_CURRENT_APP); | |
484 model_->SetReadStatus(GURL("http://example.com"), true); | |
485 ClearCounts(); | |
486 EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr); | |
487 EXPECT_EQ(0ul, UnreadSize()); | |
488 EXPECT_EQ(1ul, ReadSize()); | |
489 model_->SyncRemoveEntry(GURL("http://example.com")); | |
490 AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1); | |
491 AssertStorageCount(0, 0); | |
492 EXPECT_EQ(0ul, UnreadSize()); | |
493 EXPECT_EQ(0ul, ReadSize()); | |
494 EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr); | |
495 } | |
496 | |
497 // Tests marking entry read. | |
498 TEST_F(ReadingListModelTest, ReadEntry) { | |
499 model_->AddEntry(GURL("http://example.com"), "sample", | |
500 reading_list::ADDED_VIA_CURRENT_APP); | |
501 | |
502 ClearCounts(); | |
503 model_->SetReadStatus(GURL("http://example.com"), true); | |
504 AssertObserverCount(0, 0, 0, 0, 0, 1, 0, 0, 1); | |
505 EXPECT_EQ(0ul, UnreadSize()); | |
506 EXPECT_EQ(1ul, ReadSize()); | |
507 EXPECT_EQ(0ul, model_->unseen_size()); | |
508 | |
509 const ReadingListEntry* other_entry = | |
510 model_->GetEntryByURL(GURL("http://example.com")); | |
511 EXPECT_NE(other_entry, nullptr); | |
512 EXPECT_TRUE(other_entry->IsRead()); | |
513 EXPECT_EQ(GURL("http://example.com"), other_entry->URL()); | |
514 EXPECT_EQ("sample", other_entry->Title()); | |
515 } | |
516 | |
517 // Tests accessing existing entry. | |
518 TEST_F(ReadingListModelTest, EntryFromURL) { | |
519 GURL url1("http://example.com"); | |
520 GURL url2("http://example2.com"); | |
521 std::string entry1_title = "foo bar qux"; | |
522 model_->AddEntry(url1, entry1_title, reading_list::ADDED_VIA_CURRENT_APP); | |
523 | |
524 // Check call with nullptr |read| parameter. | |
525 const ReadingListEntry* entry1 = model_->GetEntryByURL(url1); | |
526 EXPECT_NE(nullptr, entry1); | |
527 EXPECT_EQ(entry1_title, entry1->Title()); | |
528 | |
529 entry1 = model_->GetEntryByURL(url1); | |
530 EXPECT_NE(nullptr, entry1); | |
531 EXPECT_EQ(entry1_title, entry1->Title()); | |
532 EXPECT_EQ(entry1->IsRead(), false); | |
533 model_->SetReadStatus(url1, true); | |
534 entry1 = model_->GetEntryByURL(url1); | |
535 EXPECT_NE(nullptr, entry1); | |
536 EXPECT_EQ(entry1_title, entry1->Title()); | |
537 EXPECT_EQ(entry1->IsRead(), true); | |
538 | |
539 const ReadingListEntry* entry2 = model_->GetEntryByURL(url2); | |
540 EXPECT_EQ(nullptr, entry2); | |
541 } | |
542 | |
543 // Tests mark entry unread. | |
544 TEST_F(ReadingListModelTest, UnreadEntry) { | |
545 // Setup. | |
546 model_->AddEntry(GURL("http://example.com"), "sample", | |
547 reading_list::ADDED_VIA_CURRENT_APP); | |
548 EXPECT_TRUE(model_->GetLocalUnseenFlag()); | |
549 model_->SetReadStatus(GURL("http://example.com"), true); | |
550 ClearCounts(); | |
551 EXPECT_EQ(0ul, UnreadSize()); | |
552 EXPECT_EQ(1ul, ReadSize()); | |
553 EXPECT_FALSE(model_->GetLocalUnseenFlag()); | |
554 | |
555 // Action. | |
556 model_->SetReadStatus(GURL("http://example.com"), false); | |
557 | |
558 // Tests. | |
559 AssertObserverCount(0, 0, 0, 0, 0, 1, 0, 0, 1); | |
560 EXPECT_EQ(1ul, UnreadSize()); | |
561 EXPECT_EQ(0ul, ReadSize()); | |
562 EXPECT_FALSE(model_->GetLocalUnseenFlag()); | |
563 | |
564 const ReadingListEntry* other_entry = | |
565 model_->GetEntryByURL(GURL("http://example.com")); | |
566 EXPECT_NE(other_entry, nullptr); | |
567 EXPECT_FALSE(other_entry->IsRead()); | |
568 EXPECT_EQ(GURL("http://example.com"), other_entry->URL()); | |
569 EXPECT_EQ("sample", other_entry->Title()); | |
570 } | |
571 | |
572 // Tests batch updates observers are called. | |
573 TEST_F(ReadingListModelTest, BatchUpdates) { | |
574 auto token = model_->BeginBatchUpdates(); | |
575 AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0); | |
576 EXPECT_TRUE(model_->IsPerformingBatchUpdates()); | |
577 | |
578 delete token.release(); | |
579 AssertObserverCount(1, 1, 1, 0, 0, 0, 0, 0, 0); | |
580 EXPECT_FALSE(model_->IsPerformingBatchUpdates()); | |
581 } | |
582 | |
583 // Tests batch updates are reentrant. | |
584 TEST_F(ReadingListModelTest, BatchUpdatesReentrant) { | |
585 // When two updates happen at the same time, the notification is only sent | |
586 // for beginning of first update and completion of last update. | |
587 EXPECT_FALSE(model_->IsPerformingBatchUpdates()); | |
588 | |
589 auto token = model_->BeginBatchUpdates(); | |
590 AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0); | |
591 EXPECT_TRUE(model_->IsPerformingBatchUpdates()); | |
592 | |
593 auto second_token = model_->BeginBatchUpdates(); | |
594 AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0); | |
595 EXPECT_TRUE(model_->IsPerformingBatchUpdates()); | |
596 | |
597 delete token.release(); | |
598 AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0); | |
599 EXPECT_TRUE(model_->IsPerformingBatchUpdates()); | |
600 | |
601 delete second_token.release(); | |
602 AssertObserverCount(1, 1, 1, 0, 0, 0, 0, 0, 0); | |
603 EXPECT_FALSE(model_->IsPerformingBatchUpdates()); | |
604 | |
605 // Consequent updates send notifications. | |
606 auto third_token = model_->BeginBatchUpdates(); | |
607 AssertObserverCount(1, 2, 1, 0, 0, 0, 0, 0, 0); | |
608 EXPECT_TRUE(model_->IsPerformingBatchUpdates()); | |
609 | |
610 delete third_token.release(); | |
611 AssertObserverCount(1, 2, 2, 0, 0, 0, 0, 0, 0); | |
612 EXPECT_FALSE(model_->IsPerformingBatchUpdates()); | |
613 } | |
614 | |
615 // Tests setting title on unread entry. | |
616 TEST_F(ReadingListModelTest, UpdateEntryTitle) { | |
617 const GURL gurl("http://example.com"); | |
618 const ReadingListEntry& entry = | |
619 model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP); | |
620 ClearCounts(); | |
621 | |
622 model_->SetEntryTitle(gurl, "ping"); | |
623 AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1); | |
624 EXPECT_EQ("ping", entry.Title()); | |
625 } | |
626 // Tests setting distillation state on unread entry. | |
627 TEST_F(ReadingListModelTest, UpdateEntryDistilledState) { | |
628 const GURL gurl("http://example.com"); | |
629 const ReadingListEntry& entry = | |
630 model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP); | |
631 ClearCounts(); | |
632 | |
633 model_->SetEntryDistilledState(gurl, ReadingListEntry::PROCESSING); | |
634 AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1); | |
635 EXPECT_EQ(ReadingListEntry::PROCESSING, entry.DistilledState()); | |
636 } | |
637 | |
638 // Tests setting distillation info on unread entry. | |
639 TEST_F(ReadingListModelTest, UpdateDistilledInfo) { | |
640 const GURL gurl("http://example.com"); | |
641 const ReadingListEntry& entry = | |
642 model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP); | |
643 ClearCounts(); | |
644 | |
645 const base::FilePath distilled_path("distilled/page.html"); | |
646 const GURL distilled_url("http://example.com/distilled"); | |
647 int64_t size = 50; | |
648 int64_t time = 100; | |
649 model_->SetEntryDistilledInfo(GURL("http://example.com"), distilled_path, | |
650 distilled_url, size, | |
651 base::Time::FromTimeT(time)); | |
652 AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1); | |
653 EXPECT_EQ(ReadingListEntry::PROCESSED, entry.DistilledState()); | |
654 EXPECT_EQ(distilled_path, entry.DistilledPath()); | |
655 EXPECT_EQ(distilled_url, entry.DistilledURL()); | |
656 EXPECT_EQ(size, entry.DistillationSize()); | |
657 EXPECT_EQ(time * base::Time::kMicrosecondsPerSecond, | |
658 entry.DistillationTime()); | |
659 } | |
660 | |
661 // Tests setting title on read entry. | |
662 TEST_F(ReadingListModelTest, UpdateReadEntryTitle) { | |
663 const GURL gurl("http://example.com"); | |
664 model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP); | |
665 model_->SetReadStatus(gurl, true); | |
666 const ReadingListEntry* entry = model_->GetEntryByURL(gurl); | |
667 ClearCounts(); | |
668 | |
669 model_->SetEntryTitle(gurl, "ping"); | |
670 AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1); | |
671 EXPECT_EQ("ping", entry->Title()); | |
672 } | |
673 | |
674 // Tests setting distillation state on read entry. | |
675 TEST_F(ReadingListModelTest, UpdateReadEntryState) { | |
676 const GURL gurl("http://example.com"); | |
677 model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP); | |
678 model_->SetReadStatus(gurl, true); | |
679 const ReadingListEntry* entry = model_->GetEntryByURL(gurl); | |
680 ClearCounts(); | |
681 | |
682 model_->SetEntryDistilledState(gurl, ReadingListEntry::PROCESSING); | |
683 AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1); | |
684 EXPECT_EQ(ReadingListEntry::PROCESSING, entry->DistilledState()); | |
685 } | |
686 | |
687 // Tests setting distillation info on read entry. | |
688 TEST_F(ReadingListModelTest, UpdateReadDistilledInfo) { | |
689 const GURL gurl("http://example.com"); | |
690 model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP); | |
691 model_->SetReadStatus(gurl, true); | |
692 const ReadingListEntry* entry = model_->GetEntryByURL(gurl); | |
693 ClearCounts(); | |
694 | |
695 const base::FilePath distilled_path("distilled/page.html"); | |
696 const GURL distilled_url("http://example.com/distilled"); | |
697 int64_t size = 50; | |
698 int64_t time = 100; | |
699 model_->SetEntryDistilledInfo(GURL("http://example.com"), distilled_path, | |
700 distilled_url, size, | |
701 base::Time::FromTimeT(time)); | |
702 AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1); | |
703 EXPECT_EQ(ReadingListEntry::PROCESSED, entry->DistilledState()); | |
704 EXPECT_EQ(distilled_path, entry->DistilledPath()); | |
705 EXPECT_EQ(distilled_url, entry->DistilledURL()); | |
706 EXPECT_EQ(size, entry->DistillationSize()); | |
707 EXPECT_EQ(time * base::Time::kMicrosecondsPerSecond, | |
708 entry->DistillationTime()); | |
709 } | |
710 | |
711 // Tests that ReadingListModel calls CallbackModelBeingDeleted when destroyed. | |
712 TEST_F(ReadingListModelTest, CallbackModelBeingDeleted) { | |
713 AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0); | |
714 model_.reset(); | |
715 AssertObserverCount(1, 0, 0, 1, 0, 0, 0, 0, 0); | |
716 } | |
717 | |
718 // Tests that new line characters and spaces are collapsed in title. | |
719 TEST_F(ReadingListModelTest, TestTrimmingTitle) { | |
720 const GURL gurl("http://example.com"); | |
721 std::string title = "\n This\ttitle \n contains new line \n characters "; | |
722 model_->AddEntry(gurl, title, reading_list::ADDED_VIA_CURRENT_APP); | |
723 model_->SetReadStatus(gurl, true); | |
724 const ReadingListEntry* entry = model_->GetEntryByURL(gurl); | |
725 EXPECT_EQ(entry->Title(), "This title contains new line characters"); | |
726 model_->SetEntryTitle(gurl, "test"); | |
727 EXPECT_EQ(entry->Title(), "test"); | |
728 model_->SetEntryTitle(gurl, title); | |
729 EXPECT_EQ(entry->Title(), "This title contains new line characters"); | |
730 } | |
731 | |
732 } // namespace | |
OLD | NEW |