blob: 86fe0ae3e9b2ee56f761d76729e13d87eb8d2c8f [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/sync/model/fake_model_type_sync_bridge.h"
#include <set>
#include <utility>
#include "base/bind.h"
#include "base/stl_util.h"
#include "components/sync/base/hash_util.h"
#include "components/sync/model/conflict_resolution.h"
#include "components/sync/model/model_type_store.h"
#include "components/sync/model/mutable_data_batch.h"
#include "components/sync/model_impl/in_memory_metadata_change_list.h"
#include "testing/gtest/include/gtest/gtest.h"
using sync_pb::EntitySpecifics;
using sync_pb::EntityMetadata;
using sync_pb::ModelTypeState;
namespace syncer {
namespace {
// MetadataChangeList implementaton that forwards writes metadata to a store.
class TestMetadataChangeList : public MetadataChangeList {
public:
explicit TestMetadataChangeList(FakeModelTypeSyncBridge::Store* db)
: db_(db) {}
~TestMetadataChangeList() override {}
// MetadataChangeList implementation.
void UpdateModelTypeState(
const sync_pb::ModelTypeState& model_type_state) override {
db_->set_model_type_state(model_type_state);
}
void ClearModelTypeState() override {
db_->set_model_type_state(ModelTypeState());
}
void UpdateMetadata(const std::string& storage_key,
const sync_pb::EntityMetadata& metadata) override {
DCHECK(!storage_key.empty());
db_->PutMetadata(storage_key, metadata);
}
void ClearMetadata(const std::string& storage_key) override {
DCHECK(!storage_key.empty());
db_->RemoveMetadata(storage_key);
}
private:
FakeModelTypeSyncBridge::Store* db_;
};
} // namespace
// static
std::string FakeModelTypeSyncBridge::ClientTagFromKey(const std::string& key) {
return "ClientTag_" + key;
}
// static
std::string FakeModelTypeSyncBridge::TagHashFromKey(const std::string& key) {
return GenerateSyncableHash(PREFERENCES,
FakeModelTypeSyncBridge::ClientTagFromKey(key));
}
// static
EntitySpecifics FakeModelTypeSyncBridge::GenerateSpecifics(
const std::string& key,
const std::string& value) {
EntitySpecifics specifics;
specifics.mutable_preference()->set_name(key);
specifics.mutable_preference()->set_value(value);
return specifics;
}
// static
std::unique_ptr<EntityData> FakeModelTypeSyncBridge::GenerateEntityData(
const std::string& key,
const std::string& value) {
std::unique_ptr<EntityData> entity_data = std::make_unique<EntityData>();
entity_data->client_tag_hash = TagHashFromKey(key);
entity_data->specifics = GenerateSpecifics(key, value);
entity_data->non_unique_name = key;
return entity_data;
}
FakeModelTypeSyncBridge::Store::Store() {}
FakeModelTypeSyncBridge::Store::~Store() {}
void FakeModelTypeSyncBridge::Store::PutData(const std::string& key,
const EntityData& data) {
data_change_count_++;
data_store_[key] = CopyEntityData(data);
}
void FakeModelTypeSyncBridge::Store::PutMetadata(
const std::string& key,
const EntityMetadata& metadata) {
metadata_change_count_++;
metadata_store_[key] = metadata;
}
void FakeModelTypeSyncBridge::Store::RemoveData(const std::string& key) {
data_change_count_++;
data_store_.erase(key);
}
void FakeModelTypeSyncBridge::Store::ClearAllData() {
data_change_count_++;
data_store_.clear();
}
void FakeModelTypeSyncBridge::Store::RemoveMetadata(const std::string& key) {
metadata_change_count_++;
metadata_store_.erase(key);
}
bool FakeModelTypeSyncBridge::Store::HasData(const std::string& key) const {
return data_store_.find(key) != data_store_.end();
}
bool FakeModelTypeSyncBridge::Store::HasMetadata(const std::string& key) const {
return metadata_store_.find(key) != metadata_store_.end();
}
const EntityData& FakeModelTypeSyncBridge::Store::GetData(
const std::string& key) const {
return *data_store_.find(key)->second;
}
const std::string& FakeModelTypeSyncBridge::Store::GetValue(
const std::string& key) const {
return GetData(key).specifics.preference().value();
}
const sync_pb::EntityMetadata& FakeModelTypeSyncBridge::Store::GetMetadata(
const std::string& key) const {
return metadata_store_.find(key)->second;
}
std::unique_ptr<MetadataBatch>
FakeModelTypeSyncBridge::Store::CreateMetadataBatch() const {
std::unique_ptr<MetadataBatch> metadata_batch(new MetadataBatch());
metadata_batch->SetModelTypeState(model_type_state_);
for (const auto& kv : metadata_store_) {
metadata_batch->AddMetadata(kv.first, kv.second);
}
return metadata_batch;
}
void FakeModelTypeSyncBridge::Store::Reset() {
data_change_count_ = 0;
metadata_change_count_ = 0;
data_store_.clear();
metadata_store_.clear();
model_type_state_.Clear();
}
FakeModelTypeSyncBridge::FakeModelTypeSyncBridge(
std::unique_ptr<ModelTypeChangeProcessor> change_processor)
: ModelTypeSyncBridge(std::move(change_processor)),
db_(std::make_unique<Store>()) {}
FakeModelTypeSyncBridge::~FakeModelTypeSyncBridge() {
EXPECT_FALSE(error_next_);
}
EntitySpecifics FakeModelTypeSyncBridge::WriteItem(const std::string& key,
const std::string& value) {
std::unique_ptr<EntityData> entity_data = GenerateEntityData(key, value);
EntitySpecifics specifics_copy = entity_data->specifics;
WriteItem(key, std::move(entity_data));
return specifics_copy;
}
// Overloaded form to allow passing of custom entity data.
void FakeModelTypeSyncBridge::WriteItem(
const std::string& key,
std::unique_ptr<EntityData> entity_data) {
db_->PutData(key, *entity_data);
if (change_processor()->IsTrackingMetadata()) {
auto change_list = CreateMetadataChangeList();
change_processor()->Put(key, std::move(entity_data), change_list.get());
ApplyMetadataChangeList(std::move(change_list));
}
}
void FakeModelTypeSyncBridge::DeleteItem(const std::string& key) {
db_->RemoveData(key);
if (change_processor()->IsTrackingMetadata()) {
auto change_list = CreateMetadataChangeList();
change_processor()->Delete(key, change_list.get());
ApplyMetadataChangeList(std::move(change_list));
}
}
void FakeModelTypeSyncBridge::MimicBugToLooseItemWithoutNotifyingProcessor(
const std::string& key) {
db_->RemoveData(key);
}
std::unique_ptr<MetadataChangeList>
FakeModelTypeSyncBridge::CreateMetadataChangeList() {
return std::make_unique<InMemoryMetadataChangeList>();
}
base::Optional<ModelError> FakeModelTypeSyncBridge::MergeSyncData(
std::unique_ptr<MetadataChangeList> metadata_change_list,
EntityChangeList entity_data) {
if (error_next_) {
error_next_ = false;
return ModelError(FROM_HERE, "boom");
}
std::set<std::string> remote_storage_keys;
// Store any new remote entities.
for (const auto& change : entity_data) {
EXPECT_FALSE(change.data().is_deleted());
EXPECT_EQ(EntityChange::ACTION_ADD, change.type());
std::string storage_key = change.storage_key();
EXPECT_NE(SupportsGetStorageKey(), storage_key.empty());
if (storage_key.empty()) {
storage_key = GetStorageKeyImpl(change.data());
if (base::ContainsKey(keys_to_ignore_, storage_key)) {
change_processor()->UntrackEntityForClientTagHash(
change.data().client_tag_hash);
} else {
change_processor()->UpdateStorageKey(change.data(), storage_key,
metadata_change_list.get());
}
}
remote_storage_keys.insert(storage_key);
db_->PutData(storage_key, change.data());
}
// Commit any local entities that aren't being overwritten by the server.
for (const auto& kv : db_->all_data()) {
if (remote_storage_keys.find(kv.first) == remote_storage_keys.end()) {
change_processor()->Put(kv.first, CopyEntityData(*kv.second),
metadata_change_list.get());
}
}
ApplyMetadataChangeList(std::move(metadata_change_list));
return {};
}
base::Optional<ModelError> FakeModelTypeSyncBridge::ApplySyncChanges(
std::unique_ptr<MetadataChangeList> metadata_changes,
EntityChangeList entity_changes) {
if (error_next_) {
error_next_ = false;
return ModelError(FROM_HERE, "boom");
}
for (const EntityChange& change : entity_changes) {
switch (change.type()) {
case EntityChange::ACTION_ADD: {
std::string storage_key = change.storage_key();
EXPECT_NE(SupportsGetStorageKey(), storage_key.empty());
if (storage_key.empty()) {
storage_key = GetStorageKeyImpl(change.data());
change_processor()->UpdateStorageKey(change.data(), storage_key,
metadata_changes.get());
}
EXPECT_FALSE(db_->HasData(storage_key));
db_->PutData(storage_key, change.data());
break;
}
case EntityChange::ACTION_UPDATE:
EXPECT_TRUE(db_->HasData(change.storage_key()));
db_->PutData(change.storage_key(), change.data());
break;
case EntityChange::ACTION_DELETE:
EXPECT_TRUE(db_->HasData(change.storage_key()));
db_->RemoveData(change.storage_key());
break;
}
}
ApplyMetadataChangeList(std::move(metadata_changes));
return {};
}
void FakeModelTypeSyncBridge::ApplyMetadataChangeList(
std::unique_ptr<MetadataChangeList> mcl) {
InMemoryMetadataChangeList* in_memory_mcl =
static_cast<InMemoryMetadataChangeList*>(mcl.get());
// Use TestMetadataChangeList to commit all metadata changes to the store.
TestMetadataChangeList db_mcl(db_.get());
in_memory_mcl->TransferChangesTo(&db_mcl);
}
void FakeModelTypeSyncBridge::GetData(StorageKeyList keys,
DataCallback callback) {
if (error_next_) {
error_next_ = false;
change_processor()->ReportError({FROM_HERE, "boom"});
return;
}
auto batch = std::make_unique<MutableDataBatch>();
for (const std::string& key : keys) {
if (db_->HasData(key)) {
batch->Put(key, CopyEntityData(db_->GetData(key)));
} else {
DLOG(WARNING) << "No data for " << key;
}
}
std::move(callback).Run(std::move(batch));
}
void FakeModelTypeSyncBridge::GetAllDataForDebugging(DataCallback callback) {
if (error_next_) {
error_next_ = false;
change_processor()->ReportError({FROM_HERE, "boom"});
return;
}
auto batch = std::make_unique<MutableDataBatch>();
for (const auto& kv : db_->all_data()) {
batch->Put(kv.first, CopyEntityData(*kv.second));
}
std::move(callback).Run(std::move(batch));
}
std::string FakeModelTypeSyncBridge::GetClientTag(
const EntityData& entity_data) {
return ClientTagFromKey(entity_data.specifics.preference().name());
}
std::string FakeModelTypeSyncBridge::GetStorageKey(
const EntityData& entity_data) {
DCHECK(supports_get_storage_key_);
return GetStorageKeyImpl(entity_data);
}
std::string FakeModelTypeSyncBridge::GetStorageKeyImpl(
const EntityData& entity_data) {
return entity_data.specifics.preference().name();
}
bool FakeModelTypeSyncBridge::SupportsGetStorageKey() const {
return supports_get_storage_key_;
}
void FakeModelTypeSyncBridge::SetSupportsGetStorageKey(
bool supports_get_storage_key) {
supports_get_storage_key_ = supports_get_storage_key;
}
ConflictResolution FakeModelTypeSyncBridge::ResolveConflict(
const EntityData& local_data,
const EntityData& remote_data) const {
DCHECK(conflict_resolution_);
return std::move(*conflict_resolution_);
}
ModelTypeSyncBridge::StopSyncResponse
FakeModelTypeSyncBridge::ApplyStopSyncChanges(
std::unique_ptr<MetadataChangeList> delete_metadata_change_list) {
ModelTypeSyncBridge::ApplyStopSyncChanges(
std::move(delete_metadata_change_list));
return stop_sync_response_;
}
void FakeModelTypeSyncBridge::SetConflictResolution(
ConflictResolution resolution) {
conflict_resolution_ =
std::make_unique<ConflictResolution>(std::move(resolution));
}
void FakeModelTypeSyncBridge::SetStopSyncResponse(StopSyncResponse response) {
stop_sync_response_ = response;
}
void FakeModelTypeSyncBridge::ErrorOnNextCall() {
EXPECT_FALSE(error_next_);
error_next_ = true;
}
std::unique_ptr<EntityData> FakeModelTypeSyncBridge::CopyEntityData(
const EntityData& old_data) {
std::unique_ptr<EntityData> new_data(new EntityData());
new_data->id = old_data.id;
new_data->client_tag_hash = old_data.client_tag_hash;
new_data->non_unique_name = old_data.non_unique_name;
new_data->specifics = old_data.specifics;
new_data->creation_time = old_data.creation_time;
new_data->modification_time = old_data.modification_time;
return new_data;
}
void FakeModelTypeSyncBridge::SetKeyToIgnore(const std::string key) {
keys_to_ignore_.insert(key);
}
} // namespace syncer