| Index: ios/chrome/browser/reading_list/reading_list_store_unittest.cc
|
| diff --git a/ios/chrome/browser/reading_list/reading_list_store_unittest.cc b/ios/chrome/browser/reading_list/reading_list_store_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5edc178e39d25f1568a14166c94e85d16cd0ab74
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/reading_list/reading_list_store_unittest.cc
|
| @@ -0,0 +1,287 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "ios/chrome/browser/reading_list/reading_list_store.h"
|
| +
|
| +#include <map>
|
| +#include <set>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| +#import "base/test/ios/wait_util.h"
|
| +#include "components/sync/model/fake_model_type_change_processor.h"
|
| +#include "components/sync/model/model_type_store_test_util.h"
|
| +#include "ios/chrome/browser/reading_list/reading_list_model_impl.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +class FakeModelTypeChangeProcessorObserver {
|
| + public:
|
| + virtual void Put(const std::string& client_tag,
|
| + std::unique_ptr<syncer::EntityData> entity_data,
|
| + syncer::MetadataChangeList* metadata_change_list) = 0;
|
| +
|
| + virtual void Delete(const std::string& client_tag,
|
| + syncer::MetadataChangeList* metadata_change_list) = 0;
|
| +};
|
| +
|
| +class TestModelTypeChangeProcessor
|
| + : public syncer::FakeModelTypeChangeProcessor {
|
| + public:
|
| + void SetObserver(FakeModelTypeChangeProcessorObserver* observer) {
|
| + observer_ = observer;
|
| + }
|
| +
|
| + void Put(const std::string& client_tag,
|
| + std::unique_ptr<syncer::EntityData> entity_data,
|
| + syncer::MetadataChangeList* metadata_change_list) override {
|
| + observer_->Put(client_tag, std::move(entity_data), metadata_change_list);
|
| + }
|
| +
|
| + void Delete(const std::string& client_tag,
|
| + syncer::MetadataChangeList* metadata_change_list) override {
|
| + observer_->Delete(client_tag, metadata_change_list);
|
| + }
|
| +
|
| + private:
|
| + FakeModelTypeChangeProcessorObserver* observer_;
|
| +};
|
| +
|
| +class ReadingListStoreTest : public testing::Test,
|
| + public FakeModelTypeChangeProcessorObserver,
|
| + public ReadingListStoreDelegate {
|
| + protected:
|
| + ReadingListStoreTest()
|
| + : store_(syncer::ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {
|
| + ClearState();
|
| + reading_list_store_ = base::MakeUnique<ReadingListStore>(
|
| + base::Bind(&syncer::ModelTypeStoreTestUtil::MoveStoreToCallback,
|
| + base::Passed(&store_)),
|
| + base::Bind(&ReadingListStoreTest::CreateModelTypeChangeProcessor,
|
| + base::Unretained(this)));
|
| + model_ = base::MakeUnique<ReadingListModelImpl>(nullptr, nullptr);
|
| + reading_list_store_->SetReadingListModel(model_.get(), this);
|
| +
|
| + base::RunLoop().RunUntilIdle();
|
| + }
|
| +
|
| + std::unique_ptr<syncer::ModelTypeChangeProcessor>
|
| + CreateModelTypeChangeProcessor(syncer::ModelType type,
|
| + syncer::ModelTypeSyncBridge* service) {
|
| + auto processor = base::MakeUnique<TestModelTypeChangeProcessor>();
|
| + processor->SetObserver(this);
|
| + return std::move(processor);
|
| + }
|
| +
|
| + void Put(const std::string& storage_key,
|
| + std::unique_ptr<syncer::EntityData> entity_data,
|
| + syncer::MetadataChangeList* metadata_changes) override {
|
| + put_multimap_.insert(std::make_pair(storage_key, std::move(entity_data)));
|
| + put_called_++;
|
| + }
|
| +
|
| + void Delete(const std::string& storage_key,
|
| + syncer::MetadataChangeList* metadata_changes) override {
|
| + delete_set_.insert(storage_key);
|
| + delete_called_++;
|
| + }
|
| +
|
| + void AssertCounts(int put_called,
|
| + int delete_called,
|
| + int sync_add_called,
|
| + int sync_remove_called,
|
| + int sync_merge_called) {
|
| + EXPECT_EQ(put_called, put_called_);
|
| + EXPECT_EQ(delete_called, delete_called_);
|
| + EXPECT_EQ(sync_add_called, sync_add_called_);
|
| + EXPECT_EQ(sync_remove_called, sync_remove_called_);
|
| + EXPECT_EQ(sync_merge_called, sync_merge_called_);
|
| + }
|
| +
|
| + void ClearState() {
|
| + delete_called_ = 0;
|
| + put_called_ = 0;
|
| + delete_set_.clear();
|
| + put_multimap_.clear();
|
| + sync_add_called_ = 0;
|
| + sync_remove_called_ = 0;
|
| + sync_merge_called_ = 0;
|
| + sync_added_.clear();
|
| + sync_removed_.clear();
|
| + sync_merged_.clear();
|
| + }
|
| +
|
| + // These three mathods handle callbacks from a ReadingListStore.
|
| + void StoreLoaded(std::unique_ptr<ReadingListEntries> unread,
|
| + std::unique_ptr<ReadingListEntries> read) override {}
|
| +
|
| + // Handle sync events.
|
| + void SyncAddEntry(std::unique_ptr<ReadingListEntry> entry,
|
| + bool read) override {
|
| + sync_add_called_++;
|
| + sync_added_[entry->URL().spec()] = read;
|
| + }
|
| +
|
| + void SyncRemoveEntry(const GURL& gurl) override {
|
| + sync_remove_called_++;
|
| + sync_removed_.insert(gurl.spec());
|
| + }
|
| +
|
| + ReadingListEntry* SyncMergeEntry(std::unique_ptr<ReadingListEntry> entry,
|
| + bool read) override {
|
| + sync_merge_called_++;
|
| + sync_merged_[entry->URL().spec()] = read;
|
| + return model_->SyncMergeEntry(std::move(entry), read);
|
| + }
|
| +
|
| + // In memory model type store needs a MessageLoop.
|
| + base::MessageLoop message_loop_;
|
| +
|
| + std::unique_ptr<syncer::ModelTypeStore> store_;
|
| + std::unique_ptr<ReadingListModelImpl> model_;
|
| + std::unique_ptr<ReadingListStore> reading_list_store_;
|
| + int put_called_;
|
| + int delete_called_;
|
| + int sync_add_called_;
|
| + int sync_remove_called_;
|
| + int sync_merge_called_;
|
| + std::map<std::string, std::unique_ptr<syncer::EntityData>> put_multimap_;
|
| + std::set<std::string> delete_set_;
|
| + std::map<std::string, bool> sync_added_;
|
| + std::set<std::string> sync_removed_;
|
| + std::map<std::string, bool> sync_merged_;
|
| +};
|
| +
|
| +TEST_F(ReadingListStoreTest, CheckEmpties) {
|
| + EXPECT_EQ(0ul, model_->unread_size());
|
| + EXPECT_EQ(0ul, model_->read_size());
|
| +}
|
| +
|
| +TEST_F(ReadingListStoreTest, SaveOneRead) {
|
| + ReadingListEntry entry(GURL("http://read.example.com/"), "read title");
|
| + reading_list_store_->SaveEntry(entry, true);
|
| + AssertCounts(1, 0, 0, 0, 0);
|
| + syncer::EntityData* data = put_multimap_["http://read.example.com/"].get();
|
| + const sync_pb::ReadingListSpecifics& specifics =
|
| + data->specifics.reading_list();
|
| + EXPECT_EQ(specifics.title(), "read title");
|
| + EXPECT_EQ(specifics.url(), "http://read.example.com/");
|
| + EXPECT_EQ(specifics.status(), sync_pb::ReadingListSpecifics::READ);
|
| +}
|
| +
|
| +TEST_F(ReadingListStoreTest, SaveOneUnread) {
|
| + ReadingListEntry entry(GURL("http://unread.example.com/"), "unread title");
|
| + reading_list_store_->SaveEntry(entry, false);
|
| + AssertCounts(1, 0, 0, 0, 0);
|
| + syncer::EntityData* data = put_multimap_["http://unread.example.com/"].get();
|
| + const sync_pb::ReadingListSpecifics& specifics =
|
| + data->specifics.reading_list();
|
| + EXPECT_EQ(specifics.title(), "unread title");
|
| + EXPECT_EQ(specifics.url(), "http://unread.example.com/");
|
| + EXPECT_EQ(specifics.status(), sync_pb::ReadingListSpecifics::UNREAD);
|
| +}
|
| +
|
| +TEST_F(ReadingListStoreTest, SyncMergeOneEntry) {
|
| + syncer::EntityDataMap remote_input;
|
| + ReadingListEntry entry(GURL("http://read.example.com/"), "read title");
|
| + std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
|
| + entry.AsReadingListSpecifics(true);
|
| +
|
| + syncer::EntityData data;
|
| + data.client_tag_hash = "http://read.example.com/";
|
| + *data.specifics.mutable_reading_list() = *specifics;
|
| +
|
| + remote_input["http://read.example.com/"] = data.PassToPtr();
|
| +
|
| + std::unique_ptr<syncer::MetadataChangeList> metadata_changes(
|
| + reading_list_store_->CreateMetadataChangeList());
|
| + const syncer::SyncError error = reading_list_store_->MergeSyncData(
|
| + std::move(metadata_changes), remote_input);
|
| + AssertCounts(0, 0, 1, 0, 0);
|
| + EXPECT_EQ(sync_added_.size(), 1u);
|
| + EXPECT_EQ(sync_added_.count("http://read.example.com/"), 1u);
|
| + EXPECT_EQ(sync_added_["http://read.example.com/"], true);
|
| +}
|
| +
|
| +TEST_F(ReadingListStoreTest, ApplySyncChangesOneAdd) {
|
| + syncer::EntityDataMap remote_input;
|
| + ReadingListEntry entry(GURL("http://read.example.com/"), "read title");
|
| + std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
|
| + entry.AsReadingListSpecifics(true);
|
| + syncer::EntityData data;
|
| + data.client_tag_hash = "http://read.example.com/";
|
| + *data.specifics.mutable_reading_list() = *specifics;
|
| +
|
| + syncer::EntityChangeList add_changes;
|
| +
|
| + add_changes.push_back(syncer::EntityChange::CreateAdd(
|
| + "http://read.example.com/", data.PassToPtr()));
|
| + syncer::SyncError error = reading_list_store_->ApplySyncChanges(
|
| + reading_list_store_->CreateMetadataChangeList(), add_changes);
|
| + AssertCounts(0, 0, 1, 0, 0);
|
| + EXPECT_EQ(sync_added_.size(), 1u);
|
| + EXPECT_EQ(sync_added_.count("http://read.example.com/"), 1u);
|
| + EXPECT_EQ(sync_added_["http://read.example.com/"], true);
|
| +}
|
| +
|
| +TEST_F(ReadingListStoreTest, ApplySyncChangesOneMerge) {
|
| + syncer::EntityDataMap remote_input;
|
| + model_->AddEntry(GURL("http://unread.example.com/"), "unread title");
|
| + base::test::ios::SpinRunLoopWithMinDelay(
|
| + base::TimeDelta::FromMilliseconds(10));
|
| +
|
| + ReadingListEntry new_entry(GURL("http://unread.example.com/"),
|
| + "unread title");
|
| + std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
|
| + new_entry.AsReadingListSpecifics(true);
|
| + syncer::EntityData data;
|
| + data.client_tag_hash = "http://unread.example.com/";
|
| + *data.specifics.mutable_reading_list() = *specifics;
|
| +
|
| + syncer::EntityChangeList add_changes;
|
| + add_changes.push_back(syncer::EntityChange::CreateAdd(
|
| + "http://unread.example.com/", data.PassToPtr()));
|
| + syncer::SyncError error = reading_list_store_->ApplySyncChanges(
|
| + reading_list_store_->CreateMetadataChangeList(), add_changes);
|
| + AssertCounts(1, 0, 0, 0, 1);
|
| + EXPECT_EQ(sync_merged_.size(), 1u);
|
| + EXPECT_EQ(sync_merged_.count("http://unread.example.com/"), 1u);
|
| + EXPECT_EQ(sync_merged_["http://unread.example.com/"], true);
|
| +}
|
| +
|
| +TEST_F(ReadingListStoreTest, ApplySyncChangesOneIgnored) {
|
| + ReadingListEntry old_entry(GURL("http://unread.example.com/"),
|
| + "unread title");
|
| + base::test::ios::SpinRunLoopWithMinDelay(
|
| + base::TimeDelta::FromMilliseconds(10));
|
| + syncer::EntityDataMap remote_input;
|
| + model_->AddEntry(GURL("http://unread.example.com/"), "unread title");
|
| + AssertCounts(0, 0, 0, 0, 0);
|
| +
|
| + std::unique_ptr<sync_pb::ReadingListSpecifics> specifics =
|
| + old_entry.AsReadingListSpecifics(true);
|
| + syncer::EntityData data;
|
| + data.client_tag_hash = "http://unread.example.com/";
|
| + *data.specifics.mutable_reading_list() = *specifics;
|
| +
|
| + syncer::EntityChangeList add_changes;
|
| + add_changes.push_back(syncer::EntityChange::CreateAdd(
|
| + "http://unread.example.com/", data.PassToPtr()));
|
| + syncer::SyncError error = reading_list_store_->ApplySyncChanges(
|
| + reading_list_store_->CreateMetadataChangeList(), add_changes);
|
| + AssertCounts(1, 0, 0, 0, 0);
|
| + EXPECT_EQ(sync_merged_.size(), 0u);
|
| +}
|
| +
|
| +TEST_F(ReadingListStoreTest, ApplySyncChangesOneRemove) {
|
| + syncer::EntityChangeList delete_changes;
|
| + delete_changes.push_back(
|
| + syncer::EntityChange::CreateDelete("http://read.example.com/"));
|
| + syncer::SyncError error = reading_list_store_->ApplySyncChanges(
|
| + reading_list_store_->CreateMetadataChangeList(), delete_changes);
|
| + AssertCounts(0, 0, 0, 1, 0);
|
| + EXPECT_EQ(sync_removed_.size(), 1u);
|
| + EXPECT_EQ(sync_removed_.count("http://read.example.com/"), 1u);
|
| +}
|
|
|