| // Copyright 2023 The Chromium Authors |
| // 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/dual_reading_list_model.h" |
| |
| #include "base/files/file_path.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/simple_test_clock.h" |
| #include "base/time/time.h" |
| #include "components/reading_list/core/fake_reading_list_model_storage.h" |
| #include "components/reading_list/core/mock_reading_list_model_observer.h" |
| #include "components/reading_list/core/reading_list_entry.h" |
| #include "components/reading_list/core/reading_list_model_impl.h" |
| #include "components/reading_list/features/reading_list_switches.h" |
| #include "components/sync/base/storage_type.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace reading_list { |
| namespace { |
| |
| using testing::_; |
| using testing::IsNull; |
| using testing::NotNull; |
| using StorageStateForTesting = DualReadingListModel::StorageStateForTesting; |
| |
| MATCHER_P2(MatchesEntry, url_matcher, title_matcher, "") { |
| if (!arg) { |
| *result_listener << "which is null"; |
| return false; |
| } |
| return testing::SafeMatcherCast<GURL>(url_matcher) |
| .MatchAndExplain(arg->URL(), result_listener) && |
| testing::SafeMatcherCast<std::string>(title_matcher) |
| .MatchAndExplain(arg->Title(), result_listener); |
| } |
| |
| MATCHER_P(HasUrl, expected_url, "") { |
| return arg.URL() == expected_url; |
| } |
| |
| MATCHER_P3(HasCountersEqual, size, unseen_size, unread_size, "") { |
| return arg->size() == size && arg->unseen_size() == unseen_size && |
| arg->unread_size() == unread_size; |
| } |
| |
| class TestEntryBuilder { |
| public: |
| TestEntryBuilder(GURL url, base::Time now) : url_(url), creation_time_(now) {} |
| |
| ~TestEntryBuilder() = default; |
| |
| TestEntryBuilder& SetTitle(std::string title) { |
| title_and_update_time_ = {title, creation_time_}; |
| return *this; |
| } |
| |
| TestEntryBuilder& SetTitle(std::string title, base::Time now) { |
| title_and_update_time_ = {title, now}; |
| return *this; |
| } |
| |
| TestEntryBuilder& SetRead(bool read = true) { |
| read_ = read; |
| update_read_time_ = creation_time_; |
| return *this; |
| } |
| |
| TestEntryBuilder& SetRead(base::Time now, bool read = true) { |
| read_ = read; |
| update_read_time_ = now; |
| return *this; |
| } |
| |
| TestEntryBuilder& SetEstimatedReadTime(base::TimeDelta estimated_read_time) { |
| estimated_read_time_ = estimated_read_time; |
| return *this; |
| } |
| |
| TestEntryBuilder& SetDistilledState( |
| ReadingListEntry::DistillationState distilation_state) { |
| distilation_state_ = distilation_state; |
| return *this; |
| } |
| |
| TestEntryBuilder& SetDistilledInfo(const base::FilePath& distilation_path) { |
| distilation_path_ = distilation_path; |
| return *this; |
| } |
| |
| scoped_refptr<ReadingListEntry> Build() { |
| auto entry = base::MakeRefCounted<ReadingListEntry>(url_, "entry_title", |
| creation_time_); |
| if (title_and_update_time_.has_value()) { |
| entry->SetTitle(title_and_update_time_.value().first, |
| title_and_update_time_.value().second); |
| } |
| |
| if (update_read_time_.has_value()) { |
| entry->SetRead(read_, update_read_time_.value()); |
| } |
| |
| if (estimated_read_time_.has_value()) { |
| entry->SetEstimatedReadTime(estimated_read_time_.value()); |
| } |
| |
| if (distilation_state_.has_value()) { |
| entry->SetDistilledState(distilation_state_.value()); |
| } |
| |
| if (distilation_path_.has_value()) { |
| entry->SetDistilledInfo(distilation_path_.value(), |
| GURL("http://kDistilledURL.com/"), 1, |
| base::Time::FromTimeT(1)); |
| } |
| |
| return entry; |
| } |
| |
| private: |
| const GURL url_; |
| const base::Time creation_time_; |
| |
| absl::optional<std::pair<std::string, base::Time>> title_and_update_time_; |
| absl::optional<base::Time> update_read_time_; |
| bool read_; |
| absl::optional<base::TimeDelta> estimated_read_time_; |
| absl::optional<ReadingListEntry::DistillationState> distilation_state_; |
| absl::optional<base::FilePath> distilation_path_; |
| }; |
| |
| class DualReadingListModelTest : public testing::Test { |
| public: |
| DualReadingListModelTest() = default; |
| ~DualReadingListModelTest() override = default; |
| |
| void ResetStorage() { |
| dual_model_.reset(); |
| |
| auto local_or_syncable_model_storage = |
| std::make_unique<FakeReadingListModelStorage>(); |
| local_or_syncable_model_storage_ptr_ = |
| local_or_syncable_model_storage->AsWeakPtr(); |
| auto local_or_syncable_model = std::make_unique<ReadingListModelImpl>( |
| std::move(local_or_syncable_model_storage), |
| syncer::StorageType::kUnspecified, &clock_); |
| local_or_syncable_model_ptr_ = local_or_syncable_model.get(); |
| |
| auto account_model_storage = |
| std::make_unique<FakeReadingListModelStorage>(); |
| account_model_storage_ptr_ = account_model_storage->AsWeakPtr(); |
| auto account_model = std::make_unique<ReadingListModelImpl>( |
| std::move(account_model_storage), syncer::StorageType::kAccount, |
| &clock_); |
| account_model_ptr_ = account_model.get(); |
| |
| dual_model_ = std::make_unique<reading_list::DualReadingListModel>( |
| std::move(local_or_syncable_model), std::move(account_model)); |
| dual_model_->AddObserver(&observer_); |
| } |
| |
| bool ResetStorageAndTriggerLoadCompletion( |
| std::vector<TestEntryBuilder> initial_local_or_syncable_entries_builders = |
| {}, |
| std::vector<TestEntryBuilder> initial_account_entries_builders = {}) { |
| ResetStorage(); |
| |
| std::vector<scoped_refptr<ReadingListEntry>> |
| initial_local_or_syncable_entries; |
| for (auto entry_builder : initial_local_or_syncable_entries_builders) { |
| initial_local_or_syncable_entries.push_back(entry_builder.Build()); |
| } |
| |
| std::vector<scoped_refptr<ReadingListEntry>> initial_account_entries; |
| for (auto entry_builder : initial_account_entries_builders) { |
| initial_account_entries.push_back(entry_builder.Build()); |
| } |
| |
| return local_or_syncable_model_storage_ptr_->TriggerLoadCompletion( |
| std::move(initial_local_or_syncable_entries)) && |
| account_model_storage_ptr_->TriggerLoadCompletion( |
| std::move(initial_account_entries)); |
| } |
| |
| bool ResetStorageAndMimicSignedOut( |
| std::vector<TestEntryBuilder> initial_local_entries_builders = {}) { |
| return ResetStorageAndTriggerLoadCompletion( |
| std::move(initial_local_entries_builders), |
| /*initial_account_entries_builders=*/{}); |
| } |
| |
| bool ResetStorageAndMimicSignedInSyncDisabled( |
| std::vector<TestEntryBuilder> initial_local_entries_builders = {}, |
| std::vector<TestEntryBuilder> initial_account_entries_builders = {}) { |
| ResetStorage(); |
| |
| auto metadata_batch = std::make_unique<syncer::MetadataBatch>(); |
| sync_pb::ModelTypeState state; |
| state.set_initial_sync_state( |
| sync_pb::ModelTypeState_InitialSyncState_INITIAL_SYNC_DONE); |
| state.set_authenticated_account_id(kTestAccountId); |
| metadata_batch->SetModelTypeState(state); |
| |
| std::vector<scoped_refptr<ReadingListEntry>> initial_local_entries; |
| for (auto entry_builder : initial_local_entries_builders) { |
| initial_local_entries.push_back(entry_builder.Build()); |
| } |
| |
| std::vector<scoped_refptr<ReadingListEntry>> initial_account_entries; |
| for (auto entry_builder : initial_account_entries_builders) { |
| initial_account_entries.push_back(entry_builder.Build()); |
| } |
| |
| return local_or_syncable_model_storage_ptr_->TriggerLoadCompletion( |
| std::move(initial_local_entries)) && |
| account_model_storage_ptr_->TriggerLoadCompletion( |
| std::move(initial_account_entries), std::move(metadata_batch)); |
| } |
| |
| bool ResetStorageAndMimicSyncEnabled( |
| std::vector<TestEntryBuilder> initial_syncable_entries_builders = {}) { |
| ResetStorage(); |
| |
| auto metadata_batch = std::make_unique<syncer::MetadataBatch>(); |
| sync_pb::ModelTypeState state; |
| state.set_initial_sync_state( |
| sync_pb::ModelTypeState_InitialSyncState_INITIAL_SYNC_DONE); |
| state.set_authenticated_account_id(kTestAccountId); |
| metadata_batch->SetModelTypeState(state); |
| |
| std::vector<scoped_refptr<ReadingListEntry>> initial_syncable_entries; |
| for (auto entry_builder : initial_syncable_entries_builders) { |
| initial_syncable_entries.push_back(entry_builder.Build()); |
| } |
| |
| return local_or_syncable_model_storage_ptr_->TriggerLoadCompletion( |
| std::move(initial_syncable_entries), |
| std::move(metadata_batch)) && |
| account_model_storage_ptr_->TriggerLoadCompletion(); |
| } |
| |
| protected: |
| const GURL kUrl = GURL("http://url.com/"); |
| const std::string kTestAccountId = "TestAccountId"; |
| base::SimpleTestClock clock_; |
| testing::NiceMock<MockReadingListModelObserver> observer_; |
| base::WeakPtr<FakeReadingListModelStorage> |
| local_or_syncable_model_storage_ptr_; |
| base::WeakPtr<FakeReadingListModelStorage> account_model_storage_ptr_; |
| // Owned by `dual_model_` and guaranteed to exist while `dual_model_` exists. |
| base::raw_ptr<ReadingListModelImpl> local_or_syncable_model_ptr_; |
| base::raw_ptr<ReadingListModelImpl> account_model_ptr_; |
| std::unique_ptr<reading_list::DualReadingListModel> dual_model_; |
| }; |
| |
| // Tests creating an empty model. |
| TEST_F(DualReadingListModelTest, EmptyLoaded) { |
| EXPECT_CALL(observer_, ReadingListModelLoaded).Times(0); |
| ResetStorage(); |
| EXPECT_FALSE(dual_model_->loaded()); |
| ASSERT_TRUE(local_or_syncable_model_storage_ptr_->TriggerLoadCompletion()); |
| EXPECT_FALSE(dual_model_->loaded()); |
| // ReadingListModelLoaded() should only be called upon load completion. |
| EXPECT_CALL(observer_, ReadingListModelLoaded(dual_model_.get())); |
| ASSERT_TRUE(account_model_storage_ptr_->TriggerLoadCompletion()); |
| EXPECT_TRUE(dual_model_->loaded()); |
| } |
| |
| // Tests errors during load model. |
| TEST_F(DualReadingListModelTest, ModelLoadFailure) { |
| EXPECT_CALL(observer_, ReadingListModelLoaded).Times(0); |
| ResetStorage(); |
| ASSERT_TRUE(local_or_syncable_model_storage_ptr_->TriggerLoadCompletion( |
| base::unexpected("Fake error"))); |
| ASSERT_TRUE(account_model_storage_ptr_->TriggerLoadCompletion()); |
| EXPECT_FALSE(dual_model_->loaded()); |
| } |
| |
| TEST_F(DualReadingListModelTest, ReturnAccountModelSize) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(0ul, local_or_syncable_model_ptr_->size()); |
| ASSERT_EQ(1ul, account_model_ptr_->size()); |
| EXPECT_EQ(1ul, dual_model_->size()); |
| } |
| |
| TEST_F(DualReadingListModelTest, ReturnLocalModelSize) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{})); |
| ASSERT_EQ(1ul, local_or_syncable_model_ptr_->size()); |
| ASSERT_EQ(0ul, account_model_ptr_->size()); |
| EXPECT_EQ(1ul, dual_model_->size()); |
| } |
| |
| TEST_F(DualReadingListModelTest, ReturnKeysSize) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| GURL("https://url1.com"), clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(GURL("https://url2.com"), clock_.Now())})); |
| ASSERT_EQ(1ul, local_or_syncable_model_ptr_->size()); |
| ASSERT_EQ(1ul, account_model_ptr_->size()); |
| EXPECT_EQ(2ul, dual_model_->size()); |
| } |
| |
| TEST_F(DualReadingListModelTest, MarkAllSeen) { |
| const GURL kLocalUrl("http://local_url.com/"); |
| const GURL kAccountUrl("http://account_url.com/"); |
| const GURL kCommonUrl("http://common_url.com/"); |
| |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kLocalUrl, clock_.Now()), |
| TestEntryBuilder(kCommonUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kAccountUrl, clock_.Now()), |
| TestEntryBuilder(kCommonUrl, clock_.Now())})); |
| ASSERT_TRUE(dual_model_->loaded()); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kLocalUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kAccountUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kCommonUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_FALSE(dual_model_->GetEntryByURL(kLocalUrl)->HasBeenSeen()); |
| ASSERT_FALSE(dual_model_->GetEntryByURL(kAccountUrl)->HasBeenSeen()); |
| ASSERT_FALSE(dual_model_->GetEntryByURL(kCommonUrl)->HasBeenSeen()); |
| ASSERT_FALSE(dual_model_->GetEntryByURL(kLocalUrl)->IsRead()); |
| ASSERT_FALSE(dual_model_->GetEntryByURL(kAccountUrl)->IsRead()); |
| ASSERT_FALSE(dual_model_->GetEntryByURL(kCommonUrl)->IsRead()); |
| |
| EXPECT_CALL(observer_, ReadingListModelBeganBatchUpdates(dual_model_.get())); |
| |
| { |
| testing::InSequence seq1; |
| EXPECT_CALL(observer_, |
| ReadingListWillUpdateEntry(dual_model_.get(), kLocalUrl)); |
| EXPECT_CALL(observer_, |
| ReadingListDidUpdateEntry(dual_model_.get(), kLocalUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())) |
| .RetiresOnSaturation(); |
| } |
| |
| { |
| testing::InSequence seq2; |
| EXPECT_CALL(observer_, |
| ReadingListWillUpdateEntry(dual_model_.get(), kAccountUrl)); |
| EXPECT_CALL(observer_, |
| ReadingListDidUpdateEntry(dual_model_.get(), kAccountUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())) |
| .RetiresOnSaturation(); |
| } |
| |
| { |
| testing::InSequence seq3; |
| EXPECT_CALL(observer_, |
| ReadingListWillUpdateEntry(dual_model_.get(), kCommonUrl)); |
| EXPECT_CALL(observer_, |
| ReadingListDidUpdateEntry(dual_model_.get(), kCommonUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())) |
| .RetiresOnSaturation(); |
| } |
| |
| EXPECT_CALL(observer_, |
| ReadingListModelCompletedBatchUpdates(dual_model_.get())); |
| |
| dual_model_->MarkAllSeen(); |
| |
| EXPECT_TRUE(dual_model_->GetEntryByURL(kLocalUrl)->HasBeenSeen()); |
| EXPECT_TRUE(dual_model_->GetEntryByURL(kAccountUrl)->HasBeenSeen()); |
| EXPECT_TRUE(dual_model_->GetEntryByURL(kCommonUrl)->HasBeenSeen()); |
| EXPECT_FALSE(dual_model_->GetEntryByURL(kLocalUrl)->IsRead()); |
| EXPECT_FALSE(dual_model_->GetEntryByURL(kAccountUrl)->IsRead()); |
| EXPECT_FALSE(dual_model_->GetEntryByURL(kCommonUrl)->IsRead()); |
| } |
| |
| // Verifies that ReadingListModelBeganBatchUpdates and |
| // ReadingListModelCompletedBatchUpdates are not invoked if MarkAllSeen() is a |
| // no-op. |
| TEST_F(DualReadingListModelTest, MarkAllSeenWhenAllEntriesHasBeenSeen) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()).SetRead(false)}, |
| /*initial_account_entries_builders=*/{})); |
| ASSERT_TRUE(dual_model_->loaded()); |
| ASSERT_TRUE(dual_model_->GetEntryByURL(kUrl)->HasBeenSeen()); |
| |
| EXPECT_CALL(observer_, ReadingListModelBeganBatchUpdates).Times(0); |
| EXPECT_CALL(observer_, ReadingListModelCompletedBatchUpdates).Times(0); |
| dual_model_->MarkAllSeen(); |
| } |
| |
| // Regression test for crbug.com/1429274: verifies that there is no infinite |
| // loop if an observer calls MarkAllSeen() when |
| // ReadingListModelCompletedBatchUpdates() is called. |
| TEST_F(DualReadingListModelTest, |
| NoInfiniteLoopIfMarkallSeenCalledFromBatchUpdatesObserver) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{})); |
| ASSERT_TRUE(dual_model_->loaded()); |
| |
| testing::InSequence seq1; |
| EXPECT_CALL(observer_, ReadingListModelBeganBatchUpdates(dual_model_.get())); |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| // Mimic the behavior of one of the observers by calling MarkAllSeen() when |
| // ReadingListModelCompletedBatchUpdates() is called. |
| EXPECT_CALL(observer_, |
| ReadingListModelCompletedBatchUpdates(dual_model_.get())) |
| .WillOnce([&]() { dual_model_->MarkAllSeen(); }); |
| |
| dual_model_->MarkAllSeen(); |
| } |
| |
| TEST_F(DualReadingListModelTest, BatchUpdates) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion()); |
| EXPECT_CALL(observer_, ReadingListModelBeganBatchUpdates(dual_model_.get())); |
| std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> batch = |
| dual_model_->BeginBatchUpdates(); |
| EXPECT_TRUE(dual_model_->IsPerformingBatchUpdates()); |
| testing::Mock::VerifyAndClearExpectations(&observer_); |
| |
| EXPECT_CALL(observer_, |
| ReadingListModelCompletedBatchUpdates(dual_model_.get())); |
| batch.reset(); |
| EXPECT_FALSE(dual_model_->IsPerformingBatchUpdates()); |
| } |
| |
| // Tests batch updates are reentrant. |
| TEST_F(DualReadingListModelTest, BatchUpdatesReentrant) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion()); |
| // ReadingListModelCompletedBatchUpdates() should be invoked at the very end |
| // only, and once. |
| EXPECT_CALL(observer_, ReadingListModelCompletedBatchUpdates).Times(0); |
| |
| EXPECT_FALSE(dual_model_->IsPerformingBatchUpdates()); |
| |
| EXPECT_CALL(observer_, ReadingListModelBeganBatchUpdates(dual_model_.get())); |
| std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> batch = |
| dual_model_->BeginBatchUpdates(); |
| testing::Mock::VerifyAndClearExpectations(&observer_); |
| |
| // When two updates happen at the same time, the notification is only sent |
| // for beginning of first update and completion of last update. |
| EXPECT_CALL(observer_, ReadingListModelBeganBatchUpdates).Times(0); |
| |
| EXPECT_TRUE(dual_model_->IsPerformingBatchUpdates()); |
| |
| std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> second_batch = |
| dual_model_->BeginBatchUpdates(); |
| EXPECT_TRUE(dual_model_->IsPerformingBatchUpdates()); |
| |
| batch.reset(); |
| EXPECT_TRUE(dual_model_->IsPerformingBatchUpdates()); |
| |
| EXPECT_CALL(observer_, |
| ReadingListModelCompletedBatchUpdates(dual_model_.get())); |
| second_batch.reset(); |
| EXPECT_FALSE(dual_model_->IsPerformingBatchUpdates()); |
| testing::Mock::VerifyAndClearExpectations(&observer_); |
| |
| // Consequent updates send notifications. |
| EXPECT_CALL(observer_, ReadingListModelBeganBatchUpdates(dual_model_.get())); |
| std::unique_ptr<ReadingListModel::ScopedReadingListBatchUpdate> third_batch = |
| dual_model_->BeginBatchUpdates(); |
| EXPECT_TRUE(dual_model_->IsPerformingBatchUpdates()); |
| |
| EXPECT_CALL(observer_, |
| ReadingListModelCompletedBatchUpdates(dual_model_.get())); |
| third_batch.reset(); |
| EXPECT_FALSE(dual_model_->IsPerformingBatchUpdates()); |
| } |
| |
| TEST_F(DualReadingListModelTest, GetEntryByURL) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(GURL("http://local_url.com/"), clock_.Now()) |
| .SetTitle("local_entry"), |
| TestEntryBuilder(GURL("http://common_url1.com/"), clock_.Now()) |
| .SetTitle("merged_entry_title_from_local_entry", |
| clock_.Now() + base::Seconds(1)) |
| .SetDistilledState(ReadingListEntry::DISTILLATION_ERROR), |
| TestEntryBuilder(GURL("http://common_url2.com/"), clock_.Now()) |
| .SetTitle("merged_entry_title_from_local_entry") |
| .SetDistilledState(ReadingListEntry::DISTILLATION_ERROR)}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(GURL("http://account_url.com/"), clock_.Now()) |
| .SetTitle("account_entry"), |
| TestEntryBuilder(GURL("http://common_url1.com/"), clock_.Now()) |
| .SetTitle("merged_entry_title_from_account_entry"), |
| TestEntryBuilder(GURL("http://common_url2.com/"), clock_.Now()) |
| .SetTitle("merged_entry_title_from_account_entry", |
| clock_.Now() + base::Seconds(1))})); |
| |
| ASSERT_TRUE(dual_model_->loaded()); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting( |
| GURL("http://local_url.com/")), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting( |
| GURL("http://account_url.com/")), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting( |
| GURL("http://common_url1.com/")), |
| StorageStateForTesting::kExistsInBothModels); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting( |
| GURL("http://common_url2.com/")), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(GURL("http://local_url.com/")), |
| MatchesEntry("http://local_url.com/", "local_entry")); |
| EXPECT_THAT(dual_model_->GetEntryByURL(GURL("http://account_url.com/")), |
| MatchesEntry("http://account_url.com/", "account_entry")); |
| |
| scoped_refptr<const ReadingListEntry> merged_entry1 = |
| dual_model_->GetEntryByURL(GURL("http://common_url1.com/")); |
| scoped_refptr<const ReadingListEntry> merged_entry2 = |
| dual_model_->GetEntryByURL(GURL("http://common_url2.com/")); |
| // The expected title of the merged entry is the title that was most recently |
| // updated. |
| EXPECT_THAT(merged_entry1, |
| MatchesEntry("http://common_url1.com/", |
| "merged_entry_title_from_local_entry")); |
| EXPECT_THAT(merged_entry2, |
| MatchesEntry("http://common_url2.com/", |
| "merged_entry_title_from_account_entry")); |
| // The DistilledState() should be equal to the local one. |
| EXPECT_EQ(merged_entry1->DistilledState(), |
| ReadingListEntry::DISTILLATION_ERROR); |
| EXPECT_EQ(merged_entry2->DistilledState(), |
| ReadingListEntry::DISTILLATION_ERROR); |
| } |
| |
| TEST_F(DualReadingListModelTest, DeleteAllEntries) { |
| const GURL local_url("http://local_url.com/"); |
| const GURL account_url("http://account_url.com/"); |
| const GURL common_url("http://common_url.com/"); |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(local_url, clock_.Now()), |
| TestEntryBuilder(common_url, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(account_url, clock_.Now()), |
| TestEntryBuilder(common_url, clock_.Now())})); |
| |
| ASSERT_TRUE(dual_model_->loaded()); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(local_url), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(account_url), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(common_url), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| { |
| testing::InSequence seq1; |
| EXPECT_CALL(observer_, |
| ReadingListWillRemoveEntry(dual_model_.get(), local_url)); |
| EXPECT_CALL(observer_, |
| ReadingListDidRemoveEntry(dual_model_.get(), local_url)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())) |
| .RetiresOnSaturation(); |
| } |
| |
| { |
| testing::InSequence seq2; |
| EXPECT_CALL(observer_, |
| ReadingListWillRemoveEntry(dual_model_.get(), account_url)); |
| EXPECT_CALL(observer_, |
| ReadingListDidRemoveEntry(dual_model_.get(), account_url)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())) |
| .RetiresOnSaturation(); |
| } |
| |
| { |
| testing::InSequence seq3; |
| EXPECT_CALL(observer_, |
| ReadingListWillRemoveEntry(dual_model_.get(), common_url)); |
| EXPECT_CALL(observer_, |
| ReadingListDidRemoveEntry(dual_model_.get(), common_url)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())) |
| .RetiresOnSaturation(); |
| } |
| |
| EXPECT_TRUE(dual_model_->DeleteAllEntries()); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(local_url), IsNull()); |
| EXPECT_THAT(dual_model_->GetEntryByURL(account_url), IsNull()); |
| EXPECT_THAT(dual_model_->GetEntryByURL(common_url), IsNull()); |
| } |
| |
| TEST_F(DualReadingListModelTest, GetAccountWhereEntryIsSavedToWhenSignedOut) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedOut(/*initial_local_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| EXPECT_TRUE(dual_model_->GetAccountWhereEntryIsSavedTo(kUrl).empty()); |
| EXPECT_TRUE( |
| dual_model_ |
| ->GetAccountWhereEntryIsSavedTo(GURL("http://non_existing_url.com/")) |
| .empty()); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| GetAccountWhereEntryIsSavedToWhenSignedInSyncDisabled) { |
| const GURL kLocalUrl("http://local_url.com/"); |
| const GURL kAccountUrl("http://account_url.com/"); |
| const GURL kCommonUrl("http://common_url.com/"); |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/ |
| {TestEntryBuilder(kLocalUrl, clock_.Now()), |
| TestEntryBuilder(kCommonUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kAccountUrl, clock_.Now()), |
| TestEntryBuilder(kCommonUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kLocalUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kAccountUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kCommonUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| EXPECT_TRUE(dual_model_->GetAccountWhereEntryIsSavedTo(kLocalUrl).empty()); |
| EXPECT_EQ(dual_model_->GetAccountWhereEntryIsSavedTo(kAccountUrl).ToString(), |
| kTestAccountId); |
| EXPECT_EQ(dual_model_->GetAccountWhereEntryIsSavedTo(kCommonUrl).ToString(), |
| kTestAccountId); |
| EXPECT_TRUE( |
| dual_model_ |
| ->GetAccountWhereEntryIsSavedTo(GURL("http://non_existing_url.com/")) |
| .empty()); |
| } |
| |
| TEST_F(DualReadingListModelTest, GetAccountWhereEntryIsSavedToWhenSyncEnabled) { |
| ASSERT_TRUE( |
| ResetStorageAndMimicSyncEnabled(/*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| EXPECT_EQ(dual_model_->GetAccountWhereEntryIsSavedTo(kUrl).ToString(), |
| kTestAccountId); |
| EXPECT_TRUE( |
| dual_model_ |
| ->GetAccountWhereEntryIsSavedTo(GURL("http://non_existing_url.com/")) |
| .empty()); |
| } |
| |
| TEST_F(DualReadingListModelTest, NeedsExplicitUploadToSyncServerWhenSignedOut) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndEnableFeature( |
| switches::kReadingListEnableSyncTransportModeUponSignIn); |
| |
| ASSERT_TRUE(ResetStorageAndMimicSignedOut(/*initial_local_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| EXPECT_TRUE(dual_model_->NeedsExplicitUploadToSyncServer(kUrl)); |
| EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer( |
| GURL("http://non_existing_url.com/"))); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| NeedsExplicitUploadToSyncServerWhenSignedInSyncDisabled) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndEnableFeature( |
| switches::kReadingListEnableSyncTransportModeUponSignIn); |
| |
| const GURL kLocalURL("http://local_url.com/"); |
| const GURL kAccountURL("http://account_url.com/"); |
| const GURL kCommonURL("http://common_url.com/"); |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{TestEntryBuilder(kLocalURL, |
| clock_.Now()), |
| TestEntryBuilder(kCommonURL, |
| clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kAccountURL, clock_.Now()), |
| TestEntryBuilder(kCommonURL, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kLocalURL), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kAccountURL), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kCommonURL), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| EXPECT_TRUE(dual_model_->NeedsExplicitUploadToSyncServer(kLocalURL)); |
| EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer(kAccountURL)); |
| EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer(kCommonURL)); |
| EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer( |
| GURL("http://non_existing_url.com/"))); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| NeedsExplicitUploadToSyncServerWhenSyncEnabled) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndEnableFeature( |
| switches::kReadingListEnableSyncTransportModeUponSignIn); |
| |
| ASSERT_TRUE( |
| ResetStorageAndMimicSyncEnabled(/*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer(kUrl)); |
| EXPECT_FALSE(dual_model_->NeedsExplicitUploadToSyncServer( |
| GURL("http://non_existing_url.com/"))); |
| } |
| |
| TEST_F(DualReadingListModelTest, RemoveNonExistingEntryByUrl) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion()); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| dual_model_->RemoveEntryByURL(kUrl); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| } |
| |
| TEST_F(DualReadingListModelTest, RemoveLocalEntryByUrl) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{})); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->RemoveEntryByURL(kUrl); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| } |
| |
| TEST_F(DualReadingListModelTest, RemoveAccountEntryByUrl) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->RemoveEntryByURL(kUrl); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| } |
| |
| TEST_F(DualReadingListModelTest, RemoveCommonEntryByUrl) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->RemoveEntryByURL(kUrl); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| } |
| |
| TEST_F(DualReadingListModelTest, RemoveLocalEntryByUrlFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSyncEnabled( |
| /*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = local_or_syncable_model_ptr_->BeginBatchUpdates(); |
| local_or_syncable_model_ptr_->SyncRemoveEntry(kUrl); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| } |
| |
| TEST_F(DualReadingListModelTest, RemoveAccountEntryByUrlFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| account_model_ptr_->SyncRemoveEntry(kUrl); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| } |
| |
| TEST_F(DualReadingListModelTest, RemoveCommonEntryByUrlFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{TestEntryBuilder(kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| account_model_ptr_->SyncRemoveEntry(kUrl); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, AddEntryWhenSignedOut) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedOut()); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry).Times(0); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, |
| ReadingListWillAddEntry(dual_model_.get(), HasUrl(kUrl))); |
| EXPECT_CALL(observer_, |
| ReadingListDidAddEntry(dual_model_.get(), kUrl, |
| reading_list::ADDED_VIA_CURRENT_APP)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| scoped_refptr<const ReadingListEntry> entry = &dual_model_->AddOrReplaceEntry( |
| kUrl, "entry_title", reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(entry, MatchesEntry(kUrl, "entry_title")); |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), |
| MatchesEntry(kUrl, "entry_title")); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, AddEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled()); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry).Times(0); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, |
| ReadingListWillAddEntry(dual_model_.get(), HasUrl(kUrl))); |
| EXPECT_CALL(observer_, |
| ReadingListDidAddEntry(dual_model_.get(), kUrl, |
| reading_list::ADDED_VIA_CURRENT_APP)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| scoped_refptr<const ReadingListEntry> entry = &dual_model_->AddOrReplaceEntry( |
| kUrl, "entry_title", reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(entry, MatchesEntry(kUrl, "entry_title")); |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), |
| MatchesEntry(kUrl, "entry_title")); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, AddEntryWhenSyncEnabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSyncEnabled()); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry).Times(0); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, |
| ReadingListWillAddEntry(dual_model_.get(), HasUrl(kUrl))); |
| EXPECT_CALL(observer_, |
| ReadingListDidAddEntry(dual_model_.get(), kUrl, |
| reading_list::ADDED_VIA_CURRENT_APP)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| scoped_refptr<const ReadingListEntry> entry = &dual_model_->AddOrReplaceEntry( |
| kUrl, "entry_title", reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(entry, MatchesEntry(kUrl, "entry_title")); |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), |
| MatchesEntry(kUrl, "entry_title")); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, AddExistingEntryWhenSignedOut) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedOut(/*initial_local_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| EXPECT_CALL(observer_, |
| ReadingListWillAddEntry(dual_model_.get(), HasUrl(kUrl))); |
| EXPECT_CALL(observer_, |
| ReadingListDidAddEntry(dual_model_.get(), kUrl, |
| reading_list::ADDED_VIA_CURRENT_APP)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| scoped_refptr<const ReadingListEntry> entry = &dual_model_->AddOrReplaceEntry( |
| kUrl, "entry_title", reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(entry, MatchesEntry(kUrl, "entry_title")); |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), |
| MatchesEntry(kUrl, "entry_title")); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| AddLocalExistingEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{TestEntryBuilder(kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| EXPECT_CALL(observer_, |
| ReadingListWillAddEntry(dual_model_.get(), HasUrl(kUrl))); |
| EXPECT_CALL(observer_, |
| ReadingListDidAddEntry(dual_model_.get(), kUrl, |
| reading_list::ADDED_VIA_CURRENT_APP)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| scoped_refptr<const ReadingListEntry> entry = &dual_model_->AddOrReplaceEntry( |
| kUrl, "entry_title", reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(entry, MatchesEntry(kUrl, "entry_title")); |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), |
| MatchesEntry(kUrl, "entry_title")); |
| // Although the entry was originally local only, it has been effectively moved |
| // when the entry got replaced. |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| AddAccountExistingEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| EXPECT_CALL(observer_, |
| ReadingListWillAddEntry(dual_model_.get(), HasUrl(kUrl))); |
| EXPECT_CALL(observer_, |
| ReadingListDidAddEntry(dual_model_.get(), kUrl, |
| reading_list::ADDED_VIA_CURRENT_APP)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| scoped_refptr<const ReadingListEntry> entry = &dual_model_->AddOrReplaceEntry( |
| kUrl, "entry_title", reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(entry, MatchesEntry(kUrl, "entry_title")); |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), |
| MatchesEntry(kUrl, "entry_title")); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| AddCommonExistingEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{TestEntryBuilder(kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| EXPECT_CALL(observer_, |
| ReadingListWillAddEntry(dual_model_.get(), HasUrl(kUrl))); |
| EXPECT_CALL(observer_, |
| ReadingListDidAddEntry(dual_model_.get(), kUrl, |
| reading_list::ADDED_VIA_CURRENT_APP)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| scoped_refptr<const ReadingListEntry> entry = &dual_model_->AddOrReplaceEntry( |
| kUrl, "entry_title", reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(entry, MatchesEntry(kUrl, "entry_title")); |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), |
| MatchesEntry(kUrl, "entry_title")); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, AddExistingEntryWhenSyncEnabled) { |
| ASSERT_TRUE( |
| ResetStorageAndMimicSyncEnabled(/*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| EXPECT_CALL(observer_, |
| ReadingListWillAddEntry(dual_model_.get(), HasUrl(kUrl))); |
| EXPECT_CALL(observer_, |
| ReadingListDidAddEntry(dual_model_.get(), kUrl, |
| reading_list::ADDED_VIA_CURRENT_APP)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| scoped_refptr<const ReadingListEntry> entry = &dual_model_->AddOrReplaceEntry( |
| kUrl, "entry_title", reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(entry, MatchesEntry(kUrl, "entry_title")); |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), |
| MatchesEntry(kUrl, "entry_title")); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, AddLocalEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSyncEnabled()); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, |
| ReadingListWillAddEntry(dual_model_.get(), HasUrl(kUrl))); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry(dual_model_.get(), kUrl, |
| reading_list::ADDED_VIA_SYNC)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = local_or_syncable_model_ptr_->BeginBatchUpdates(); |
| local_or_syncable_model_ptr_->SyncAddEntry( |
| TestEntryBuilder(kUrl, clock_.Now()).Build()); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, AddAccountEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled()); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, |
| ReadingListWillAddEntry(dual_model_.get(), HasUrl(kUrl))); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry(dual_model_.get(), kUrl, |
| reading_list::ADDED_VIA_SYNC)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| account_model_ptr_->SyncAddEntry( |
| TestEntryBuilder(kUrl, clock_.Now()).Build()); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, AddLocalExistingEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| account_model_ptr_->SyncAddEntry( |
| TestEntryBuilder(kUrl, clock_.Now()).Build()); |
| |
| EXPECT_THAT(dual_model_->GetEntryByURL(kUrl), NotNull()); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| } |
| |
| TEST_F(DualReadingListModelTest, SyncMergeEntryWhenSyncEnabled) { |
| ASSERT_TRUE( |
| ResetStorageAndMimicSyncEnabled(/*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()) |
| .SetTitle("current_title") |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| |
| scoped_refptr<ReadingListEntry> sync_entry = |
| TestEntryBuilder(kUrl, clock_.Now()) |
| .SetTitle("title_comes_from_sync", clock_.Now() + base::Seconds(1)) |
| .Build(); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = local_or_syncable_model_ptr_->BeginBatchUpdates(); |
| scoped_refptr<const ReadingListEntry> merged_entry = |
| local_or_syncable_model_ptr_->SyncMergeEntry(sync_entry); |
| |
| EXPECT_THAT(merged_entry, MatchesEntry(kUrl, "title_comes_from_sync")); |
| EXPECT_TRUE(merged_entry->IsRead()); |
| |
| scoped_refptr<const ReadingListEntry> entry = |
| dual_model_->GetEntryByURL(kUrl); |
| |
| EXPECT_THAT(entry, MatchesEntry(kUrl, "title_comes_from_sync")); |
| EXPECT_TRUE(entry->IsRead()); |
| } |
| |
| TEST_F(DualReadingListModelTest, SyncMergeEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()) |
| .SetTitle("current_title") |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| |
| scoped_refptr<ReadingListEntry> sync_entry = |
| TestEntryBuilder(kUrl, clock_.Now()) |
| .SetTitle("title_comes_from_sync", clock_.Now() + base::Seconds(1)) |
| .Build(); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| scoped_refptr<const ReadingListEntry> merged_entry = |
| account_model_ptr_->SyncMergeEntry(sync_entry); |
| |
| EXPECT_THAT(merged_entry, MatchesEntry(kUrl, "title_comes_from_sync")); |
| EXPECT_TRUE(merged_entry->IsRead()); |
| |
| scoped_refptr<const ReadingListEntry> entry = |
| dual_model_->GetEntryByURL(kUrl); |
| |
| EXPECT_THAT(entry, MatchesEntry(kUrl, "title_comes_from_sync")); |
| EXPECT_TRUE(entry->IsRead()); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetReadStatusIfExistsForNonExistingEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion()); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, true); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetReadStatusIfExistsForLocalEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, true); |
| |
| EXPECT_TRUE(dual_model_->GetEntryByURL(kUrl)->IsRead()); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetReadStatusIfExistsForAccountEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, true); |
| |
| EXPECT_TRUE(dual_model_->GetEntryByURL(kUrl)->IsRead()); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetReadStatusIfExistsForLocalCommonEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetRead(clock_.Now() + base::Seconds(1))}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| // The expected read state of the merged entry is equal to the state of the |
| // entry that was most recently updated, and initially the update time of the |
| // entry is equal to the creation time. |
| ASSERT_TRUE(dual_model_->GetEntryByURL(kUrl)->IsRead()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, false); |
| |
| EXPECT_FALSE(dual_model_->GetEntryByURL(kUrl)->IsRead()); |
| EXPECT_FALSE(local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->IsRead()); |
| EXPECT_FALSE(account_model_ptr_->GetEntryByURL(kUrl)->IsRead()); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetReadStatusIfExistsForAccountCommonEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()) |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| // The expected read state of the merged entry is equal to the state of the |
| // entry that was most recently updated, and initially the update time of the |
| // entry is equal to the creation time. |
| ASSERT_TRUE(dual_model_->GetEntryByURL(kUrl)->IsRead()); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, false); |
| |
| EXPECT_FALSE(dual_model_->GetEntryByURL(kUrl)->IsRead()); |
| EXPECT_FALSE(local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->IsRead()); |
| EXPECT_FALSE(account_model_ptr_->GetEntryByURL(kUrl)->IsRead()); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetReadStatusIfExistsForMergedEntryHasSameStatus) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_FALSE(local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->IsRead()); |
| // The expected read state of the merged entry is equal to the state of the |
| // entry that was most recently updated, and initially the update time of the |
| // entry is equal to the creation time. |
| ASSERT_TRUE(dual_model_->GetEntryByURL(kUrl)->IsRead()); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, true); |
| |
| EXPECT_TRUE(local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->IsRead()); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEntryTitleIfExistsForNonExistingEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion()); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| dual_model_->SetEntryTitleIfExists(kUrl, "new_title"); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEntryTitleIfExistsForLocalEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEntryTitleIfExists(kUrl, "new_title"); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->Title(), "new_title"); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEntryTitleIfExistsForAccountEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEntryTitleIfExists(kUrl, "new_title"); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->Title(), "new_title"); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEntryTitleIfExistsForLocalCommonEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetTitle("current_title", clock_.Now() + base::Seconds(1))}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetTitle("old_title_will_be_used_again", clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| // The expected title of the merged entry is the title that was most recently |
| // updated. |
| ASSERT_EQ(dual_model_->GetEntryByURL(kUrl)->Title(), "current_title"); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEntryTitleIfExists(kUrl, "old_title_will_be_used_again"); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->Title(), |
| "old_title_will_be_used_again"); |
| EXPECT_EQ(local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->Title(), |
| "old_title_will_be_used_again"); |
| EXPECT_EQ(account_model_ptr_->GetEntryByURL(kUrl)->Title(), |
| "old_title_will_be_used_again"); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEntryTitleIfExistsForAccountCommonEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetTitle("old_title_will_be_used_again", clock_.Now())}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetTitle("current_title", clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| // The expected title of the merged entry is the title that was most recently |
| // updated. |
| ASSERT_EQ(dual_model_->GetEntryByURL(kUrl)->Title(), "current_title"); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEntryTitleIfExists(kUrl, "old_title_will_be_used_again"); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->Title(), |
| "old_title_will_be_used_again"); |
| EXPECT_EQ(local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->Title(), |
| "old_title_will_be_used_again"); |
| EXPECT_EQ(account_model_ptr_->GetEntryByURL(kUrl)->Title(), |
| "old_title_will_be_used_again"); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetEntryTitleIfExistsForMergedEntryHasSameTitle) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetTitle("entry_title", clock_.Now())}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetTitle("merge_view_title", clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_EQ(local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->Title(), |
| "entry_title"); |
| // The expected title of the merged entry is the title that was most recently |
| // updated. |
| ASSERT_EQ(dual_model_->GetEntryByURL(kUrl)->Title(), "merge_view_title"); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| dual_model_->SetEntryTitleIfExists(kUrl, "merge_view_title"); |
| |
| EXPECT_EQ(local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->Title(), |
| "merge_view_title"); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetEstimatedReadTimeIfExistsForNonExistingEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion()); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| dual_model_->SetEstimatedReadTimeIfExists(kUrl, base::Minutes(1)); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEstimatedReadTimeIfExistsForLocalEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEstimatedReadTimeIfExists(kUrl, base::Minutes(1)); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(1)); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEstimatedReadTimeIfExistsForAccountEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEstimatedReadTimeIfExists(kUrl, base::Minutes(1)); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(1)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetEstimatedReadTimeIfExistsForLocalCommonEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetEstimatedReadTime(base::Minutes(1))}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| // The estimated read time for a merged entry is the same as the read time of |
| // the newest entry, unless that is zero, in which case the local entry's |
| // estimated read time is used. |
| ASSERT_EQ(dual_model_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(1)); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEstimatedReadTimeIfExists(kUrl, base::Minutes(2)); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(2)); |
| EXPECT_EQ( |
| local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(2)); |
| EXPECT_EQ(account_model_ptr_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(2)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetEstimatedReadTimeIfExistsForAccountCommonEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetEstimatedReadTime(base::Minutes(1))}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now() + base::Seconds(1)) |
| .SetEstimatedReadTime(base::Minutes(2))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| // The estimated read time for a merged entry is the same as the read time of |
| // the newest entry, unless that is zero, in which case the local entry's |
| // estimated read time is used. |
| ASSERT_EQ(dual_model_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(2)); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEstimatedReadTimeIfExists(kUrl, base::Minutes(3)); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(3)); |
| EXPECT_EQ( |
| local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(3)); |
| EXPECT_EQ(account_model_ptr_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(3)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetEstimatedReadTimeIfExistsForMergedEntryHasSameEstimatedTime) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetEstimatedReadTime(base::Minutes(1))}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now() + base::Seconds(1)) |
| .SetEstimatedReadTime(base::Minutes(2))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_EQ( |
| local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(1)); |
| // The estimated read time for a merged entry is the same as the read time of |
| // the newest entry, unless that is zero, in which case the local entry's |
| // estimated read time is used. |
| ASSERT_EQ(dual_model_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(2)); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| dual_model_->SetEstimatedReadTimeIfExists(kUrl, base::Minutes(2)); |
| |
| EXPECT_EQ( |
| local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->EstimatedReadTime(), |
| base::Minutes(2)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetEntryDistilledStateIfExistsForNonExistingEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion()); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| dual_model_->SetEntryDistilledStateIfExists( |
| kUrl, ReadingListEntry::DISTILLATION_ERROR); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEntryDistilledStateIfExistsForLocalEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEntryDistilledStateIfExists( |
| kUrl, ReadingListEntry::DISTILLATION_ERROR); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->DistilledState(), |
| ReadingListEntry::DISTILLATION_ERROR); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetEntryDistilledStateIfExistsForAccountEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEntryDistilledStateIfExists( |
| kUrl, ReadingListEntry::DISTILLATION_ERROR); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->DistilledState(), |
| ReadingListEntry::DISTILLATION_ERROR); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEntryDistilledStateIfExistsForCommonEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetDistilledState(ReadingListEntry::WILL_RETRY)}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| // The expected distilled state of the merged entry is the distilled state of |
| // the local entry. |
| ASSERT_EQ(dual_model_->GetEntryByURL(kUrl)->DistilledState(), |
| ReadingListEntry::WILL_RETRY); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| dual_model_->SetEntryDistilledStateIfExists( |
| kUrl, ReadingListEntry::DISTILLATION_ERROR); |
| |
| EXPECT_EQ(dual_model_->GetEntryByURL(kUrl)->DistilledState(), |
| ReadingListEntry::DISTILLATION_ERROR); |
| EXPECT_EQ(local_or_syncable_model_ptr_->GetEntryByURL(kUrl)->DistilledState(), |
| ReadingListEntry::DISTILLATION_ERROR); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetEntryDistilledStateIfExistsForMergedEntryHasSameDistilledState) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetDistilledState(ReadingListEntry::DISTILLATION_ERROR)}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_EQ(account_model_ptr_->GetEntryByURL(kUrl)->DistilledState(), |
| ReadingListEntry::WAITING); |
| // The expected distilled state of the merged entry is the distilled state of |
| // the local entry. |
| ASSERT_EQ(dual_model_->GetEntryByURL(kUrl)->DistilledState(), |
| ReadingListEntry::DISTILLATION_ERROR); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| dual_model_->SetEntryDistilledStateIfExists( |
| kUrl, ReadingListEntry::DISTILLATION_ERROR); |
| |
| EXPECT_EQ(account_model_ptr_->GetEntryByURL(kUrl)->DistilledState(), |
| ReadingListEntry::DISTILLATION_ERROR); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetEntryDistilledInfoIfExistsForNonExistingEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion()); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| ASSERT_THAT(dual_model_->GetEntryByURL(kUrl), IsNull()); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| dual_model_->SetEntryDistilledInfoIfExists( |
| kUrl, base::FilePath(FILE_PATH_LITERAL("distilled/page.html")), |
| /*kDistilledURL=*/GURL(("http://example.com/distilled")), |
| /*kDistillationSize=*/50, |
| /*kDistillationTime=*/base::Time::FromTimeT(100)); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEntryDistilledInfoIfExistsForLocalEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| const base::FilePath kDistilledPath(FILE_PATH_LITERAL("distilled/page.html")); |
| const GURL kDistilledURL("http://url.com/distilled"); |
| const int64_t kDistillationSize = 50; |
| const int64_t kDistillationTime = 100; |
| dual_model_->SetEntryDistilledInfoIfExists( |
| kUrl, kDistilledPath, kDistilledURL, kDistillationSize, |
| base::Time::FromTimeT(kDistillationTime)); |
| |
| scoped_refptr<const ReadingListEntry> entry = |
| dual_model_->GetEntryByURL(kUrl); |
| EXPECT_EQ(entry->DistilledState(), ReadingListEntry::PROCESSED); |
| EXPECT_EQ(entry->DistilledPath(), kDistilledPath); |
| EXPECT_EQ(entry->DistilledURL(), kDistilledURL); |
| EXPECT_EQ(entry->DistillationSize(), kDistillationSize); |
| EXPECT_EQ(entry->DistillationTime(), |
| kDistillationTime * base::Time::kMicrosecondsPerSecond); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEntryDistilledInfoIfExistsForAccountEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| const base::FilePath kDistilledPath(FILE_PATH_LITERAL("distilled/page.html")); |
| const GURL kDistilledURL("http://url.com/distilled"); |
| const int64_t kDistillationSize = 50; |
| const int64_t kDistillationTime = 100; |
| dual_model_->SetEntryDistilledInfoIfExists( |
| kUrl, kDistilledPath, kDistilledURL, kDistillationSize, |
| base::Time::FromTimeT(kDistillationTime)); |
| |
| scoped_refptr<const ReadingListEntry> entry = |
| dual_model_->GetEntryByURL(kUrl); |
| EXPECT_EQ(entry->DistilledState(), ReadingListEntry::PROCESSED); |
| EXPECT_EQ(entry->DistilledPath(), kDistilledPath); |
| EXPECT_EQ(entry->DistilledURL(), kDistilledURL); |
| EXPECT_EQ(entry->DistillationSize(), kDistillationSize); |
| EXPECT_EQ(entry->DistillationTime(), |
| kDistillationTime * base::Time::kMicrosecondsPerSecond); |
| } |
| |
| TEST_F(DualReadingListModelTest, SetEntryDistilledInfoIfExistsForCommonEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetDistilledInfo( |
| base::FilePath(FILE_PATH_LITERAL("old_distilled/page.html")))}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| scoped_refptr<const ReadingListEntry> merged_entry = |
| dual_model_->GetEntryByURL(kUrl); |
| // The expected distilled info of the merged entry is the distilled info of |
| // the local entry. |
| ASSERT_EQ(merged_entry->DistilledState(), ReadingListEntry::PROCESSED); |
| ASSERT_EQ(merged_entry->DistilledPath(), |
| base::FilePath(FILE_PATH_LITERAL("old_distilled/page.html"))); |
| |
| testing::InSequence seq; |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry(dual_model_.get(), kUrl)); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges(dual_model_.get())); |
| |
| const base::FilePath kDistilledPath(FILE_PATH_LITERAL("distilled/page.html")); |
| const GURL kDistilledURL("http://url.com/distilled"); |
| const int64_t kDistillationSize = 50; |
| const int64_t kDistillationTime = 100; |
| dual_model_->SetEntryDistilledInfoIfExists( |
| kUrl, kDistilledPath, kDistilledURL, kDistillationSize, |
| base::Time::FromTimeT(kDistillationTime)); |
| |
| merged_entry = dual_model_->GetEntryByURL(kUrl); |
| EXPECT_EQ(merged_entry->DistilledState(), ReadingListEntry::PROCESSED); |
| EXPECT_EQ(merged_entry->DistilledPath(), kDistilledPath); |
| EXPECT_EQ(merged_entry->DistilledURL(), kDistilledURL); |
| EXPECT_EQ(merged_entry->DistillationSize(), kDistillationSize); |
| EXPECT_EQ(merged_entry->DistillationTime(), |
| kDistillationTime * base::Time::kMicrosecondsPerSecond); |
| |
| scoped_refptr<const ReadingListEntry> local_entry = |
| local_or_syncable_model_ptr_->GetEntryByURL(kUrl); |
| EXPECT_EQ(local_entry->DistilledState(), ReadingListEntry::PROCESSED); |
| EXPECT_EQ(local_entry->DistilledPath(), kDistilledPath); |
| EXPECT_EQ(local_entry->DistilledURL(), kDistilledURL); |
| EXPECT_EQ(local_entry->DistillationSize(), kDistillationSize); |
| EXPECT_EQ(local_entry->DistillationTime(), |
| kDistillationTime * base::Time::kMicrosecondsPerSecond); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| SetEntryDistilledInfoIfExistsForMergedEntryHasSameDistilledPath) { |
| const base::FilePath kDistilledPath(FILE_PATH_LITERAL("distilled/page.html")); |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()).SetDistilledInfo(kDistilledPath)}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_EQ(account_model_ptr_->GetEntryByURL(kUrl)->DistilledState(), |
| ReadingListEntry::WAITING); |
| |
| scoped_refptr<const ReadingListEntry> entry = |
| dual_model_->GetEntryByURL(kUrl); |
| // The expected distilled info of the merged entry is the distilled info of |
| // the local entry. |
| ASSERT_EQ(entry->DistilledState(), ReadingListEntry::PROCESSED); |
| ASSERT_EQ(entry->DistilledPath(), kDistilledPath); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry).Times(0); |
| EXPECT_CALL(observer_, ReadingListDidApplyChanges).Times(0); |
| |
| const GURL kDistilledURL("http://url.com/distilled"); |
| const int64_t kDistillationSize = 50; |
| const int64_t kDistillationTime = 100; |
| dual_model_->SetEntryDistilledInfoIfExists( |
| kUrl, kDistilledPath, kDistilledURL, kDistillationSize, |
| base::Time::FromTimeT(kDistillationTime)); |
| |
| scoped_refptr<const ReadingListEntry> account_entry = |
| account_model_ptr_->GetEntryByURL(kUrl); |
| EXPECT_EQ(account_entry->DistilledState(), ReadingListEntry::PROCESSED); |
| EXPECT_EQ(account_entry->DistilledPath(), kDistilledPath); |
| EXPECT_EQ(account_entry->DistilledURL(), kDistilledURL); |
| EXPECT_EQ(account_entry->DistillationSize(), kDistillationSize); |
| EXPECT_EQ(account_entry->DistillationTime(), |
| kDistillationTime * base::Time::kMicrosecondsPerSecond); |
| } |
| |
| // Tests that new line characters and spaces are collapsed in title. |
| TEST_F(DualReadingListModelTest, TestTrimmingTitle) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion()); |
| |
| std::string title = "\n This\ttitle \n contains new line \n characters "; |
| dual_model_->AddOrReplaceEntry(kUrl, title, |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| scoped_refptr<const ReadingListEntry> entry = |
| dual_model_->GetEntryByURL(kUrl); |
| EXPECT_EQ(entry->Title(), "This title contains new line characters"); |
| |
| dual_model_->SetEntryTitleIfExists(kUrl, "test"); |
| EXPECT_EQ(entry->Title(), "test"); |
| |
| dual_model_->SetEntryTitleIfExists(kUrl, title); |
| EXPECT_EQ(entry->Title(), "This title contains new line characters"); |
| } |
| |
| TEST_F(DualReadingListModelTest, ShouldMaintainCountsWhenModelLoaded) { |
| const GURL kUnseenLocalUrl("http://unseen_local_url.com/"); |
| const GURL kUnreadLocalUrl("http://unread_local_url.com/"); |
| const GURL kReadLocalUrl("http://read_local_url.com/"); |
| const GURL kUnseenAccountUrl("http://unseen_account_url.com/"); |
| const GURL kUnreadAccountUrl("http://unread_account_url.com/"); |
| const GURL kReadAccountUrl("http://read_account_url.com/"); |
| const GURL kUnreadCommonUrl("http://unread_common_url.com/"); |
| const GURL kReadCommonUrl("http://read_common_url.com/"); |
| |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUnseenLocalUrl, clock_.Now()), |
| TestEntryBuilder(kUnreadLocalUrl, clock_.Now()).SetRead(false), |
| TestEntryBuilder(kReadLocalUrl, clock_.Now()).SetRead(), |
| TestEntryBuilder(kUnreadCommonUrl, clock_.Now()).SetRead(false), |
| TestEntryBuilder(kReadCommonUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUnseenAccountUrl, clock_.Now()), |
| TestEntryBuilder(kUnreadAccountUrl, clock_.Now()).SetRead(false), |
| TestEntryBuilder(kReadAccountUrl, clock_.Now()).SetRead(), |
| TestEntryBuilder(kUnreadCommonUrl, clock_.Now()).SetRead(false), |
| TestEntryBuilder(kReadCommonUrl, clock_.Now()) |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| ASSERT_TRUE(dual_model_->loaded()); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUnseenLocalUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUnreadLocalUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kReadLocalUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUnseenAccountUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUnreadAccountUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kReadAccountUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUnreadCommonUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kReadCommonUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| EXPECT_EQ(8ul, dual_model_->size()); |
| EXPECT_EQ(2ul, dual_model_->unseen_size()); |
| EXPECT_EQ(5ul, dual_model_->unread_size()); |
| } |
| |
| TEST_F(DualReadingListModelTest, ShouldMaintainCountsWhenMarkAllSeen) { |
| const GURL kUnseenLocalUrl("http://unseen_local_url.com/"); |
| const GURL kUnreadLocalUrl("http://unread_local_url.com/"); |
| const GURL kReadLocalUrl("http://read_local_url.com/"); |
| const GURL kUnseenAccountUrl("http://unseen_account_url.com/"); |
| const GURL kUnreadAccountUrl("http://unread_account_url.com/"); |
| const GURL kReadAccountUrl("http://read_account_url.com/"); |
| const GURL kUnseenCommonUrl("http://unseen_common_url.com/"); |
| const GURL kUnreadCommonUrl("http://unread_common_url.com/"); |
| const GURL kReadCommonUrl("http://read_common_url.com/"); |
| |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUnseenLocalUrl, clock_.Now()), |
| TestEntryBuilder(kUnreadLocalUrl, clock_.Now()).SetRead(false), |
| TestEntryBuilder(kReadLocalUrl, clock_.Now()).SetRead(), |
| TestEntryBuilder(kUnseenCommonUrl, clock_.Now()), |
| TestEntryBuilder(kUnreadCommonUrl, clock_.Now()).SetRead(false), |
| TestEntryBuilder(kReadCommonUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUnseenAccountUrl, clock_.Now()), |
| TestEntryBuilder(kUnreadAccountUrl, clock_.Now()).SetRead(false), |
| TestEntryBuilder(kReadAccountUrl, clock_.Now()).SetRead(), |
| TestEntryBuilder(kUnseenCommonUrl, clock_.Now()), |
| TestEntryBuilder(kUnreadCommonUrl, clock_.Now()).SetRead(false), |
| TestEntryBuilder(kReadCommonUrl, clock_.Now()) |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| ASSERT_TRUE(dual_model_->loaded()); |
| |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUnseenLocalUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUnreadLocalUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kReadLocalUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUnseenAccountUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUnreadAccountUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kReadAccountUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUnreadCommonUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kReadCommonUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_EQ(9ul, dual_model_->size()); |
| ASSERT_EQ(3ul, dual_model_->unseen_size()); |
| ASSERT_EQ(6ul, dual_model_->unread_size()); |
| |
| dual_model_->MarkAllSeen(); |
| |
| EXPECT_EQ(9ul, dual_model_->size()); |
| EXPECT_EQ(0ul, dual_model_->unseen_size()); |
| EXPECT_EQ(6ul, dual_model_->unread_size()); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveLocalUnreadEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| dual_model_->RemoveEntryByURL(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, ShouldMaintainCountsWhenRemoveLocalReadEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| dual_model_->RemoveEntryByURL(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveAccountUnreadEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| dual_model_->RemoveEntryByURL(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveAccountReadEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| dual_model_->RemoveEntryByURL(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveCommonUnreadEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| dual_model_->RemoveEntryByURL(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveCommonReadEntry) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()) |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| dual_model_->RemoveEntryByURL(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveLocalUnreadEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSyncEnabled( |
| /*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = local_or_syncable_model_ptr_->BeginBatchUpdates(); |
| local_or_syncable_model_ptr_->SyncRemoveEntry(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveLocalReadEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSyncEnabled( |
| /*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = local_or_syncable_model_ptr_->BeginBatchUpdates(); |
| local_or_syncable_model_ptr_->SyncRemoveEntry(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveAccountUnreadEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| account_model_ptr_->SyncRemoveEntry(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveAccountReadEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| account_model_ptr_->SyncRemoveEntry(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveCommonUnreadEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{TestEntryBuilder(kUrl, clock_.Now()) |
| .SetRead()}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| account_model_ptr_->SyncRemoveEntry(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenRemoveCommonReadEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{TestEntryBuilder(kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()) |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| account_model_ptr_->SyncRemoveEntry(kUrl); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddEntryWhenSignedOut) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedOut()); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled()); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddEntryWhenSyncEnabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSyncEnabled()); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddExistingUnreadEntryWhenSignedOut) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedOut(/*initial_local_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddExistingReadEntryWhenSignedOut) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedOut(/*initial_local_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F( |
| DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddLocalExistingUnreadEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F( |
| DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddLocalExistingReadEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F( |
| DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddAccountExistingUnreadEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F( |
| DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddAccountExistingReadEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F( |
| DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddCommonExistingUnreadEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F( |
| DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddCommonExistingReadEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()) |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddExistingUnreadEntryWhenSyncEnabled) { |
| ASSERT_TRUE( |
| ResetStorageAndMimicSyncEnabled(/*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddExistingReadEntryWhenSyncEnabled) { |
| ASSERT_TRUE( |
| ResetStorageAndMimicSyncEnabled(/*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillRemoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidRemoveEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| dual_model_->AddOrReplaceEntry(kUrl, "entry_title", |
| reading_list::ADDED_VIA_CURRENT_APP, |
| /*estimated_read_time=*/base::TimeDelta()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddLocalEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSyncEnabled()); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = local_or_syncable_model_ptr_->BeginBatchUpdates(); |
| local_or_syncable_model_ptr_->SyncAddEntry( |
| TestEntryBuilder(kUrl, clock_.Now()).Build()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddAccountEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled()); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kNotFound); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillAddEntry( |
| HasCountersEqual(/*size=*/0ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidAddEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _, _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| account_model_ptr_->SyncAddEntry( |
| TestEntryBuilder(kUrl, clock_.Now()).Build()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenAddLocalExistingEntryFromSync) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillUpdateEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidUpdateEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| account_model_ptr_->SyncAddEntry( |
| TestEntryBuilder(kUrl, clock_.Now() + base::Seconds(1)).Build()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| EXPECT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetLocalUnreadEntryUnread) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, false); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetLocalUnreadEntryRead) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, true); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetLocalReadEntryUnread) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/1ul), |
| _)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, false); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetLocalReadEntryRead) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, true); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetAccountUnreadEntryUnread) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, false); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetAccountUnreadEntryRead) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, true); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetAccountReadEntryUnread) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/1ul), |
| _)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, false); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetAccountReadEntryRead) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, true); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetCommonUnreadEntryUnread) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, false); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetCommonUnreadEntryRead) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/{TestEntryBuilder( |
| kUrl, clock_.Now())}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, true); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetCommonReadEntryUnread) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()).SetRead()}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/1ul), |
| _)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, false); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| TEST_F(DualReadingListModelTest, |
| ShouldMaintainCountsWhenSetCommonReadEntryRead) { |
| ASSERT_TRUE(ResetStorageAndTriggerLoadCompletion( |
| /*initial_local_or_syncable_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()).SetRead()}, |
| /*initial_account_entries_builders=*/ |
| {TestEntryBuilder(kUrl, clock_.Now()) |
| .SetRead(clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInBothModels); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| dual_model_->SetReadStatusIfExists(kUrl, true); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F( |
| DualReadingListModelTest, |
| ShouldMaintainCountsWhenSyncMergeReadEntryWithUnreadEntryWhenSyncEnabled) { |
| ASSERT_TRUE( |
| ResetStorageAndMimicSyncEnabled(/*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now() + base::Seconds(1)).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = local_or_syncable_model_ptr_->BeginBatchUpdates(); |
| scoped_refptr<const ReadingListEntry> merged_entry = |
| local_or_syncable_model_ptr_->SyncMergeEntry( |
| TestEntryBuilder(kUrl, clock_.Now()).Build()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F( |
| DualReadingListModelTest, |
| ShouldMaintainCountsWhenSyncMergeUnreadEntryWithReadEntryWhenSyncEnabled) { |
| ASSERT_TRUE( |
| ResetStorageAndMimicSyncEnabled(/*initial_syncable_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now())})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInLocalOrSyncableModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = local_or_syncable_model_ptr_->BeginBatchUpdates(); |
| scoped_refptr<const ReadingListEntry> merged_entry = |
| local_or_syncable_model_ptr_->SyncMergeEntry( |
| TestEntryBuilder(kUrl, clock_.Now() + base::Seconds(1)) |
| .SetRead() |
| .Build()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F( |
| DualReadingListModelTest, |
| ShouldMaintainCountsWhenSyncMergeReadEntryWithUnreadEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now() + base::Seconds(1)).SetRead()})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| scoped_refptr<const ReadingListEntry> merged_entry = |
| account_model_ptr_->SyncMergeEntry( |
| TestEntryBuilder(kUrl, clock_.Now()).Build()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/0ul, |
| /*unread_size=*/0ul)); |
| } |
| |
| TEST_F( |
| DualReadingListModelTest, |
| ShouldMaintainCountsWhenSyncMergeUnreadEntryWithReadEntryWhenSignedInSyncDisabled) { |
| ASSERT_TRUE(ResetStorageAndMimicSignedInSyncDisabled( |
| /*initial_local_entries_builders=*/{}, |
| /*initial_account_entries_builders=*/{ |
| TestEntryBuilder(kUrl, clock_.Now() + base::Seconds(1))})); |
| ASSERT_EQ(dual_model_->GetStorageStateForURLForTesting(kUrl), |
| StorageStateForTesting::kExistsInAccountModelOnly); |
| |
| ASSERT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| |
| EXPECT_CALL(observer_, ReadingListWillMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| EXPECT_CALL(observer_, ReadingListDidMoveEntry( |
| HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul), |
| _)); |
| |
| // DCHECKs verify that sync updates are issued as batch updates. |
| auto token = account_model_ptr_->BeginBatchUpdates(); |
| scoped_refptr<const ReadingListEntry> merged_entry = |
| account_model_ptr_->SyncMergeEntry( |
| TestEntryBuilder(kUrl, clock_.Now()).SetRead().Build()); |
| |
| EXPECT_THAT(dual_model_, HasCountersEqual(/*size=*/1ul, /*unseen_size=*/1ul, |
| /*unread_size=*/1ul)); |
| } |
| |
| } // namespace |
| } // namespace reading_list |