summaryrefslogtreecommitdiff
path: root/chromium/components/reading_list/core/reading_list_model_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/reading_list/core/reading_list_model_unittest.cc')
-rw-r--r--chromium/components/reading_list/core/reading_list_model_unittest.cc747
1 files changed, 747 insertions, 0 deletions
diff --git a/chromium/components/reading_list/core/reading_list_model_unittest.cc b/chromium/components/reading_list/core/reading_list_model_unittest.cc
new file mode 100644
index 00000000000..a2b2b57fe46
--- /dev/null
+++ b/chromium/components/reading_list/core/reading_list_model_unittest.cc
@@ -0,0 +1,747 @@
+// Copyright 2016 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/reading_list/core/reading_list_model.h"
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/simple_test_clock.h"
+#include "components/reading_list/core/reading_list_model_impl.h"
+#include "components/reading_list/core/reading_list_model_storage.h"
+#include "components/reading_list/core/reading_list_store_delegate.h"
+#include "components/sync/model/metadata_change_list.h"
+#include "components/sync/model/model_error.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const GURL callback_url("http://example.com");
+const std::string callback_title("test title");
+
+base::Time AdvanceAndGetTime(base::SimpleTestClock* clock) {
+ clock->Advance(base::TimeDelta::FromMilliseconds(10));
+ return clock->Now();
+}
+
+class TestReadingListStorageObserver {
+ public:
+ virtual void ReadingListDidSaveEntry() = 0;
+ virtual void ReadingListDidRemoveEntry() = 0;
+};
+
+class TestReadingListStorage : public ReadingListModelStorage {
+ public:
+ TestReadingListStorage(TestReadingListStorageObserver* observer,
+ base::SimpleTestClock* clock)
+ : ReadingListModelStorage(
+ base::Bind(&syncer::ModelTypeChangeProcessor::Create,
+ base::RepeatingClosure()),
+ syncer::READING_LIST),
+ entries_(new ReadingListStoreDelegate::ReadingListEntries()),
+ observer_(observer),
+ clock_(clock) {}
+
+ void AddSampleEntries() {
+ // Adds timer and interlace read/unread entry creation to avoid having two
+ // entries with the same creation timestamp.
+ ReadingListEntry unread_a(GURL("http://unread_a.com"), "unread_a",
+ AdvanceAndGetTime(clock_));
+ entries_->insert(
+ std::make_pair(GURL("http://unread_a.com"), std::move(unread_a)));
+
+ ReadingListEntry read_a(GURL("http://read_a.com"), "read_a",
+ AdvanceAndGetTime(clock_));
+ read_a.SetRead(true, AdvanceAndGetTime(clock_));
+ entries_->insert(
+ std::make_pair(GURL("http://read_a.com"), std::move(read_a)));
+
+ ReadingListEntry unread_b(GURL("http://unread_b.com"), "unread_b",
+ AdvanceAndGetTime(clock_));
+ entries_->insert(
+ std::make_pair(GURL("http://unread_b.com"), std::move(unread_b)));
+
+ ReadingListEntry read_b(GURL("http://read_b.com"), "read_b",
+ AdvanceAndGetTime(clock_));
+ read_b.SetRead(true, AdvanceAndGetTime(clock_));
+ entries_->insert(
+ std::make_pair(GURL("http://read_b.com"), std::move(read_b)));
+
+ ReadingListEntry unread_c(GURL("http://unread_c.com"), "unread_c",
+ AdvanceAndGetTime(clock_));
+ entries_->insert(
+ std::make_pair(GURL("http://unread_c.com"), std::move(unread_c)));
+
+ ReadingListEntry read_c(GURL("http://read_c.com"), "read_c",
+ AdvanceAndGetTime(clock_));
+ read_c.SetRead(true, AdvanceAndGetTime(clock_));
+ entries_->insert(
+ std::make_pair(GURL("http://read_c.com"), std::move(read_c)));
+
+ ReadingListEntry unread_d(GURL("http://unread_d.com"), "unread_d",
+ AdvanceAndGetTime(clock_));
+ entries_->insert(
+ std::make_pair(GURL("http://unread_d.com"), std::move(unread_d)));
+ }
+
+ void SetReadingListModel(ReadingListModel* model,
+ ReadingListStoreDelegate* delegate,
+ base::Clock* clock) override {
+ delegate->StoreLoaded(std::move(entries_));
+ clock_ = static_cast<base::SimpleTestClock*>(clock);
+ }
+
+ // Saves or updates an entry. If the entry is not yet in the database, it is
+ // created.
+ void SaveEntry(const ReadingListEntry& entry) override {
+ observer_->ReadingListDidSaveEntry();
+ }
+
+ // Removes an entry from the storage.
+ void RemoveEntry(const ReadingListEntry& entry) override {
+ observer_->ReadingListDidRemoveEntry();
+ }
+
+ std::unique_ptr<ScopedBatchUpdate> EnsureBatchCreated() override {
+ return std::unique_ptr<ScopedBatchUpdate>();
+ }
+
+ // Syncing is not used in this test class.
+ std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+ override {
+ NOTREACHED();
+ return std::unique_ptr<syncer::MetadataChangeList>();
+ }
+
+ base::Optional<syncer::ModelError> MergeSyncData(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityDataMap entity_data_map) override {
+ NOTREACHED();
+ return {};
+ }
+
+ base::Optional<syncer::ModelError> ApplySyncChanges(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_changes) override {
+ NOTREACHED();
+ return {};
+ }
+
+ void GetData(StorageKeyList storage_keys, DataCallback callback) override {
+ NOTREACHED();
+ return;
+ }
+
+ void GetAllData(DataCallback callback) override {
+ NOTREACHED();
+ return;
+ }
+
+ std::string GetClientTag(const syncer::EntityData& entity_data) override {
+ NOTREACHED();
+ return "";
+ }
+
+ std::string GetStorageKey(const syncer::EntityData& entity_data) override {
+ NOTREACHED();
+ return "";
+ }
+
+ private:
+ std::unique_ptr<ReadingListStoreDelegate::ReadingListEntries> entries_;
+ TestReadingListStorageObserver* observer_;
+ base::SimpleTestClock* clock_;
+};
+
+class ReadingListModelTest : public ReadingListModelObserver,
+ public TestReadingListStorageObserver,
+ public testing::Test {
+ public:
+ ReadingListModelTest() : callback_called_(false) {
+ auto clock = base::MakeUnique<base::SimpleTestClock>();
+ clock_ = clock.get();
+ model_ = base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr,
+ std::move(clock));
+ ClearCounts();
+ model_->AddObserver(this);
+ }
+ ~ReadingListModelTest() override {}
+
+ void SetStorage(std::unique_ptr<TestReadingListStorage> storage,
+ std::unique_ptr<base::SimpleTestClock> clock) {
+ clock_ = clock.get();
+ model_ = base::MakeUnique<ReadingListModelImpl>(std::move(storage), nullptr,
+ std::move(clock));
+ ClearCounts();
+ model_->AddObserver(this);
+ }
+
+ void ClearCounts() {
+ observer_loaded_ = observer_started_batch_update_ =
+ observer_completed_batch_update_ = observer_deleted_ =
+ observer_remove_ = observer_move_ = observer_add_ =
+ observer_did_add_ = observer_update_ = observer_did_apply_ =
+ storage_saved_ = storage_removed_ = 0;
+ }
+
+ void AssertObserverCount(int observer_loaded,
+ int observer_started_batch_update,
+ int observer_completed_batch_update,
+ int observer_deleted,
+ int observer_remove,
+ int observer_move,
+ int observer_add,
+ int observer_update,
+ int observer_did_apply) {
+ ASSERT_EQ(observer_loaded, observer_loaded_);
+ ASSERT_EQ(observer_started_batch_update, observer_started_batch_update_);
+ ASSERT_EQ(observer_completed_batch_update,
+ observer_completed_batch_update_);
+ ASSERT_EQ(observer_deleted, observer_deleted_);
+ ASSERT_EQ(observer_remove, observer_remove_);
+ ASSERT_EQ(observer_move, observer_move_);
+ // Add and did_add should be the same.
+ ASSERT_EQ(observer_add, observer_add_);
+ ASSERT_EQ(observer_add, observer_did_add_);
+ ASSERT_EQ(observer_update, observer_update_);
+ ASSERT_EQ(observer_did_apply, observer_did_apply_);
+ }
+
+ void AssertStorageCount(int storage_saved, int storage_removed) {
+ ASSERT_EQ(storage_saved, storage_saved_);
+ ASSERT_EQ(storage_removed, storage_removed_);
+ }
+
+ // ReadingListModelObserver
+ void ReadingListModelLoaded(const ReadingListModel* model) override {
+ observer_loaded_ += 1;
+ }
+ void ReadingListModelBeganBatchUpdates(
+ const ReadingListModel* model) override {
+ observer_started_batch_update_ += 1;
+ }
+ void ReadingListModelCompletedBatchUpdates(
+ const ReadingListModel* model) override {
+ observer_completed_batch_update_ += 1;
+ }
+ void ReadingListModelBeingDeleted(const ReadingListModel* model) override {
+ observer_deleted_ += 1;
+ }
+ void ReadingListWillRemoveEntry(const ReadingListModel* model,
+ const GURL& url) override {
+ observer_remove_ += 1;
+ }
+ void ReadingListWillMoveEntry(const ReadingListModel* model,
+ const GURL& url) override {
+ observer_move_ += 1;
+ }
+ void ReadingListWillAddEntry(const ReadingListModel* model,
+ const ReadingListEntry& entry) override {
+ observer_add_ += 1;
+ }
+ void ReadingListDidAddEntry(const ReadingListModel* model,
+ const GURL& url,
+ reading_list::EntrySource entry_source) override {
+ observer_did_add_ += 1;
+ }
+ void ReadingListWillUpdateEntry(const ReadingListModel* model,
+ const GURL& url) override {
+ observer_update_ += 1;
+ }
+ void ReadingListDidApplyChanges(ReadingListModel* model) override {
+ observer_did_apply_ += 1;
+ }
+
+ void ReadingListDidSaveEntry() override { storage_saved_ += 1; }
+ void ReadingListDidRemoveEntry() override { storage_removed_ += 1; }
+
+ size_t UnreadSize() {
+ size_t size = 0;
+ for (const auto& url : model_->Keys()) {
+ const ReadingListEntry* entry = model_->GetEntryByURL(url);
+ if (!entry->IsRead()) {
+ size++;
+ }
+ }
+ DCHECK_EQ(size, model_->unread_size());
+ return size;
+ }
+
+ size_t ReadSize() {
+ size_t size = 0;
+ for (const auto& url : model_->Keys()) {
+ const ReadingListEntry* entry = model_->GetEntryByURL(url);
+ if (entry->IsRead()) {
+ size++;
+ }
+ }
+ return size;
+ }
+
+ void Callback(const ReadingListEntry& entry) {
+ EXPECT_EQ(callback_url, entry.URL());
+ EXPECT_EQ(callback_title, entry.Title());
+ callback_called_ = true;
+ }
+
+ bool CallbackCalled() { return callback_called_; }
+
+ protected:
+ int observer_loaded_;
+ int observer_started_batch_update_;
+ int observer_completed_batch_update_;
+ int observer_deleted_;
+ int observer_remove_;
+ int observer_move_;
+ int observer_add_;
+ int observer_did_add_;
+ int observer_update_;
+ int observer_did_apply_;
+ int storage_saved_;
+ int storage_removed_;
+ bool callback_called_;
+
+ std::unique_ptr<ReadingListModelImpl> model_;
+ // Owned by |model_|;
+ base::SimpleTestClock* clock_;
+};
+
+// Tests creating an empty model.
+TEST_F(ReadingListModelTest, EmptyLoaded) {
+ EXPECT_TRUE(model_->loaded());
+ AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ model_->Shutdown();
+ EXPECT_FALSE(model_->loaded());
+ AssertObserverCount(1, 0, 0, 1, 0, 0, 0, 0, 0);
+}
+
+// Tests load model.
+TEST_F(ReadingListModelTest, ModelLoaded) {
+ ClearCounts();
+ auto clock = base::MakeUnique<base::SimpleTestClock>();
+ auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get());
+ storage->AddSampleEntries();
+ SetStorage(std::move(storage), std::move(clock));
+
+ AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
+ std::map<GURL, std::string> loaded_entries;
+ int size = 0;
+ for (const auto& url : model_->Keys()) {
+ size++;
+ const ReadingListEntry* entry = model_->GetEntryByURL(url);
+ loaded_entries[url] = entry->Title();
+ }
+ EXPECT_EQ(size, 7);
+ EXPECT_EQ(loaded_entries[GURL("http://unread_a.com")], "unread_a");
+ EXPECT_EQ(loaded_entries[GURL("http://unread_b.com")], "unread_b");
+ EXPECT_EQ(loaded_entries[GURL("http://unread_c.com")], "unread_c");
+ EXPECT_EQ(loaded_entries[GURL("http://unread_d.com")], "unread_d");
+ EXPECT_EQ(loaded_entries[GURL("http://read_a.com")], "read_a");
+ EXPECT_EQ(loaded_entries[GURL("http://read_b.com")], "read_b");
+ EXPECT_EQ(loaded_entries[GURL("http://read_c.com")], "read_c");
+}
+
+// Tests adding entry.
+TEST_F(ReadingListModelTest, AddEntry) {
+ auto clock = base::MakeUnique<base::SimpleTestClock>();
+ auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get());
+ SetStorage(std::move(storage), std::move(clock));
+ ClearCounts();
+
+ const ReadingListEntry& entry =
+ model_->AddEntry(GURL("http://example.com"), "\n \tsample Test ",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ EXPECT_EQ(GURL("http://example.com"), entry.URL());
+ EXPECT_EQ("sample Test", entry.Title());
+
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 1, 0, 1);
+ AssertStorageCount(1, 0);
+ EXPECT_EQ(1ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_TRUE(model_->GetLocalUnseenFlag());
+
+ const ReadingListEntry* other_entry =
+ model_->GetEntryByURL(GURL("http://example.com"));
+ EXPECT_NE(other_entry, nullptr);
+ EXPECT_FALSE(other_entry->IsRead());
+ EXPECT_EQ(GURL("http://example.com"), other_entry->URL());
+ EXPECT_EQ("sample Test", other_entry->Title());
+}
+
+// Tests addin entry from sync.
+TEST_F(ReadingListModelTest, SyncAddEntry) {
+ auto clock = base::MakeUnique<base::SimpleTestClock>();
+ auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get());
+ SetStorage(std::move(storage), std::move(clock));
+ auto entry = base::MakeUnique<ReadingListEntry>(
+ GURL("http://example.com"), "sample", AdvanceAndGetTime(clock_));
+ entry->SetRead(true, AdvanceAndGetTime(clock_));
+ ClearCounts();
+
+ model_->SyncAddEntry(std::move(entry));
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 1, 0, 1);
+ AssertStorageCount(0, 0);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ ClearCounts();
+}
+
+// Tests updating entry from sync.
+TEST_F(ReadingListModelTest, SyncMergeEntry) {
+ auto clock = base::MakeUnique<base::SimpleTestClock>();
+ auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get());
+ SetStorage(std::move(storage), std::move(clock));
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ const base::FilePath distilled_path(FILE_PATH_LITERAL("distilled/page.html"));
+ const GURL distilled_url("http://example.com/distilled");
+ int64_t size = 50;
+ int64_t time = 100;
+ model_->SetEntryDistilledInfo(GURL("http://example.com"), distilled_path,
+ distilled_url, size,
+ base::Time::FromTimeT(time));
+ const ReadingListEntry* local_entry =
+ model_->GetEntryByURL(GURL("http://example.com"));
+ int64_t local_update_time = local_entry->UpdateTime();
+
+ auto sync_entry = base::MakeUnique<ReadingListEntry>(
+ GURL("http://example.com"), "sample", AdvanceAndGetTime(clock_));
+ sync_entry->SetRead(true, AdvanceAndGetTime(clock_));
+ ASSERT_GT(sync_entry->UpdateTime(), local_update_time);
+ int64_t sync_update_time = sync_entry->UpdateTime();
+ EXPECT_TRUE(sync_entry->DistilledPath().empty());
+
+ EXPECT_EQ(1ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+
+ ReadingListEntry* merged_entry =
+ model_->SyncMergeEntry(std::move(sync_entry));
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ EXPECT_EQ(merged_entry->DistilledPath(),
+ base::FilePath(FILE_PATH_LITERAL("distilled/page.html")));
+ EXPECT_EQ(merged_entry->UpdateTime(), sync_update_time);
+ EXPECT_EQ(size, merged_entry->DistillationSize());
+ EXPECT_EQ(time * base::Time::kMicrosecondsPerSecond,
+ merged_entry->DistillationTime());
+}
+
+// Tests deleting entry.
+TEST_F(ReadingListModelTest, RemoveEntryByUrl) {
+ auto clock = base::MakeUnique<base::SimpleTestClock>();
+ auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get());
+ SetStorage(std::move(storage), std::move(clock));
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ ClearCounts();
+ EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+ EXPECT_EQ(1ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ model_->RemoveEntryByURL(GURL("http://example.com"));
+ AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1);
+ AssertStorageCount(0, 1);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(GURL("http://example.com"), true);
+ ClearCounts();
+ EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ model_->RemoveEntryByURL(GURL("http://example.com"));
+ AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1);
+ AssertStorageCount(0, 1);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+}
+
+// Tests deleting entry from sync.
+TEST_F(ReadingListModelTest, RemoveSyncEntryByUrl) {
+ auto clock = base::MakeUnique<base::SimpleTestClock>();
+ auto storage = base::MakeUnique<TestReadingListStorage>(this, clock.get());
+ SetStorage(std::move(storage), std::move(clock));
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ ClearCounts();
+ EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+ EXPECT_EQ(1ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ model_->SyncRemoveEntry(GURL("http://example.com"));
+ AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1);
+ AssertStorageCount(0, 0);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(GURL("http://example.com"), true);
+ ClearCounts();
+ EXPECT_NE(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ model_->SyncRemoveEntry(GURL("http://example.com"));
+ AssertObserverCount(0, 0, 0, 0, 1, 0, 0, 0, 1);
+ AssertStorageCount(0, 0);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_EQ(model_->GetEntryByURL(GURL("http://example.com")), nullptr);
+}
+
+// Tests marking entry read.
+TEST_F(ReadingListModelTest, ReadEntry) {
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+
+ ClearCounts();
+ model_->SetReadStatus(GURL("http://example.com"), true);
+ AssertObserverCount(0, 0, 0, 0, 0, 1, 0, 0, 1);
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ EXPECT_EQ(0ul, model_->unseen_size());
+
+ const ReadingListEntry* other_entry =
+ model_->GetEntryByURL(GURL("http://example.com"));
+ EXPECT_NE(other_entry, nullptr);
+ EXPECT_TRUE(other_entry->IsRead());
+ EXPECT_EQ(GURL("http://example.com"), other_entry->URL());
+ EXPECT_EQ("sample", other_entry->Title());
+}
+
+// Tests accessing existing entry.
+TEST_F(ReadingListModelTest, EntryFromURL) {
+ GURL url1("http://example.com");
+ GURL url2("http://example2.com");
+ std::string entry1_title = "foo bar qux";
+ model_->AddEntry(url1, entry1_title, reading_list::ADDED_VIA_CURRENT_APP);
+
+ // Check call with nullptr |read| parameter.
+ const ReadingListEntry* entry1 = model_->GetEntryByURL(url1);
+ EXPECT_NE(nullptr, entry1);
+ EXPECT_EQ(entry1_title, entry1->Title());
+
+ entry1 = model_->GetEntryByURL(url1);
+ EXPECT_NE(nullptr, entry1);
+ EXPECT_EQ(entry1_title, entry1->Title());
+ EXPECT_EQ(entry1->IsRead(), false);
+ model_->SetReadStatus(url1, true);
+ entry1 = model_->GetEntryByURL(url1);
+ EXPECT_NE(nullptr, entry1);
+ EXPECT_EQ(entry1_title, entry1->Title());
+ EXPECT_EQ(entry1->IsRead(), true);
+
+ const ReadingListEntry* entry2 = model_->GetEntryByURL(url2);
+ EXPECT_EQ(nullptr, entry2);
+}
+
+// Tests mark entry unread.
+TEST_F(ReadingListModelTest, UnreadEntry) {
+ // Setup.
+ model_->AddEntry(GURL("http://example.com"), "sample",
+ reading_list::ADDED_VIA_CURRENT_APP);
+ EXPECT_TRUE(model_->GetLocalUnseenFlag());
+ model_->SetReadStatus(GURL("http://example.com"), true);
+ ClearCounts();
+ EXPECT_EQ(0ul, UnreadSize());
+ EXPECT_EQ(1ul, ReadSize());
+ EXPECT_FALSE(model_->GetLocalUnseenFlag());
+
+ // Action.
+ model_->SetReadStatus(GURL("http://example.com"), false);
+
+ // Tests.
+ AssertObserverCount(0, 0, 0, 0, 0, 1, 0, 0, 1);
+ EXPECT_EQ(1ul, UnreadSize());
+ EXPECT_EQ(0ul, ReadSize());
+ EXPECT_FALSE(model_->GetLocalUnseenFlag());
+
+ const ReadingListEntry* other_entry =
+ model_->GetEntryByURL(GURL("http://example.com"));
+ EXPECT_NE(other_entry, nullptr);
+ EXPECT_FALSE(other_entry->IsRead());
+ EXPECT_EQ(GURL("http://example.com"), other_entry->URL());
+ EXPECT_EQ("sample", other_entry->Title());
+}
+
+// Tests batch updates observers are called.
+TEST_F(ReadingListModelTest, BatchUpdates) {
+ auto token = model_->BeginBatchUpdates();
+ AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(model_->IsPerformingBatchUpdates());
+
+ delete token.release();
+ AssertObserverCount(1, 1, 1, 0, 0, 0, 0, 0, 0);
+ EXPECT_FALSE(model_->IsPerformingBatchUpdates());
+}
+
+// Tests batch updates are reentrant.
+TEST_F(ReadingListModelTest, BatchUpdatesReentrant) {
+ // When two updates happen at the same time, the notification is only sent
+ // for beginning of first update and completion of last update.
+ EXPECT_FALSE(model_->IsPerformingBatchUpdates());
+
+ auto token = model_->BeginBatchUpdates();
+ AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(model_->IsPerformingBatchUpdates());
+
+ auto second_token = model_->BeginBatchUpdates();
+ AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(model_->IsPerformingBatchUpdates());
+
+ delete token.release();
+ AssertObserverCount(1, 1, 0, 0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(model_->IsPerformingBatchUpdates());
+
+ delete second_token.release();
+ AssertObserverCount(1, 1, 1, 0, 0, 0, 0, 0, 0);
+ EXPECT_FALSE(model_->IsPerformingBatchUpdates());
+
+ // Consequent updates send notifications.
+ auto third_token = model_->BeginBatchUpdates();
+ AssertObserverCount(1, 2, 1, 0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(model_->IsPerformingBatchUpdates());
+
+ delete third_token.release();
+ AssertObserverCount(1, 2, 2, 0, 0, 0, 0, 0, 0);
+ EXPECT_FALSE(model_->IsPerformingBatchUpdates());
+}
+
+// Tests setting title on unread entry.
+TEST_F(ReadingListModelTest, UpdateEntryTitle) {
+ const GURL gurl("http://example.com");
+ const ReadingListEntry& entry =
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ ClearCounts();
+
+ model_->SetEntryTitle(gurl, "ping");
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ("ping", entry.Title());
+}
+// Tests setting distillation state on unread entry.
+TEST_F(ReadingListModelTest, UpdateEntryDistilledState) {
+ const GURL gurl("http://example.com");
+ const ReadingListEntry& entry =
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ ClearCounts();
+
+ model_->SetEntryDistilledState(gurl, ReadingListEntry::PROCESSING);
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ(ReadingListEntry::PROCESSING, entry.DistilledState());
+}
+
+// Tests setting distillation info on unread entry.
+TEST_F(ReadingListModelTest, UpdateDistilledInfo) {
+ const GURL gurl("http://example.com");
+ const ReadingListEntry& entry =
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ ClearCounts();
+
+ const base::FilePath distilled_path(FILE_PATH_LITERAL("distilled/page.html"));
+ const GURL distilled_url("http://example.com/distilled");
+ int64_t size = 50;
+ int64_t time = 100;
+ model_->SetEntryDistilledInfo(GURL("http://example.com"), distilled_path,
+ distilled_url, size,
+ base::Time::FromTimeT(time));
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ(ReadingListEntry::PROCESSED, entry.DistilledState());
+ EXPECT_EQ(distilled_path, entry.DistilledPath());
+ EXPECT_EQ(distilled_url, entry.DistilledURL());
+ EXPECT_EQ(size, entry.DistillationSize());
+ EXPECT_EQ(time * base::Time::kMicrosecondsPerSecond,
+ entry.DistillationTime());
+}
+
+// Tests setting title on read entry.
+TEST_F(ReadingListModelTest, UpdateReadEntryTitle) {
+ const GURL gurl("http://example.com");
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(gurl, true);
+ const ReadingListEntry* entry = model_->GetEntryByURL(gurl);
+ ClearCounts();
+
+ model_->SetEntryTitle(gurl, "ping");
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ("ping", entry->Title());
+}
+
+// Tests setting distillation state on read entry.
+TEST_F(ReadingListModelTest, UpdateReadEntryState) {
+ const GURL gurl("http://example.com");
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(gurl, true);
+ const ReadingListEntry* entry = model_->GetEntryByURL(gurl);
+ ClearCounts();
+
+ model_->SetEntryDistilledState(gurl, ReadingListEntry::PROCESSING);
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ(ReadingListEntry::PROCESSING, entry->DistilledState());
+}
+
+// Tests setting distillation info on read entry.
+TEST_F(ReadingListModelTest, UpdateReadDistilledInfo) {
+ const GURL gurl("http://example.com");
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(gurl, true);
+ const ReadingListEntry* entry = model_->GetEntryByURL(gurl);
+ ClearCounts();
+
+ const base::FilePath distilled_path(FILE_PATH_LITERAL("distilled/page.html"));
+ const GURL distilled_url("http://example.com/distilled");
+ int64_t size = 50;
+ int64_t time = 100;
+ model_->SetEntryDistilledInfo(GURL("http://example.com"), distilled_path,
+ distilled_url, size,
+ base::Time::FromTimeT(time));
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ(ReadingListEntry::PROCESSED, entry->DistilledState());
+ EXPECT_EQ(distilled_path, entry->DistilledPath());
+ EXPECT_EQ(distilled_url, entry->DistilledURL());
+ EXPECT_EQ(size, entry->DistillationSize());
+ EXPECT_EQ(time * base::Time::kMicrosecondsPerSecond,
+ entry->DistillationTime());
+}
+
+// Tests setting ContentSuggestionsExtra info on entry.
+TEST_F(ReadingListModelTest, UpdateContentSuggestionsExtra) {
+ const GURL gurl("http://example.com");
+ model_->AddEntry(gurl, "sample", reading_list::ADDED_VIA_CURRENT_APP);
+ const ReadingListEntry* entry = model_->GetEntryByURL(gurl);
+ ClearCounts();
+
+ reading_list::ContentSuggestionsExtra extra;
+ extra.dismissed = true;
+
+ model_->SetContentSuggestionsExtra(gurl, extra);
+ AssertObserverCount(0, 0, 0, 0, 0, 0, 0, 1, 1);
+ EXPECT_EQ(extra.dismissed, entry->ContentSuggestionsExtra()->dismissed);
+}
+
+// Tests that ReadingListModel calls CallbackModelBeingDeleted when destroyed.
+TEST_F(ReadingListModelTest, CallbackModelBeingDeleted) {
+ AssertObserverCount(1, 0, 0, 0, 0, 0, 0, 0, 0);
+ model_.reset();
+ AssertObserverCount(1, 0, 0, 1, 0, 0, 0, 0, 0);
+}
+
+// Tests that new line characters and spaces are collapsed in title.
+TEST_F(ReadingListModelTest, TestTrimmingTitle) {
+ const GURL gurl("http://example.com");
+ std::string title = "\n This\ttitle \n contains new line \n characters ";
+ model_->AddEntry(gurl, title, reading_list::ADDED_VIA_CURRENT_APP);
+ model_->SetReadStatus(gurl, true);
+ const ReadingListEntry* entry = model_->GetEntryByURL(gurl);
+ EXPECT_EQ(entry->Title(), "This title contains new line characters");
+ model_->SetEntryTitle(gurl, "test");
+ EXPECT_EQ(entry->Title(), "test");
+ model_->SetEntryTitle(gurl, title);
+ EXPECT_EQ(entry->Title(), "This title contains new line characters");
+}
+
+} // namespace