blob: d04cd002d7f14e8d7f9f00e9e116434832c67dd3 [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/test/model/fake_model_type_sync_bridge.h"
#include <set>
#include <utility>
#include "base/bind.h"
#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "components/sync/base/client_tag_hash.h"
#include "components/sync/engine/entity_data.h"
#include "components/sync/model/conflict_resolution.h"
#include "components/sync/model/in_memory_metadata_change_list.h"
#include "components/sync/model/model_type_store.h"
#include "components/sync/model/mutable_data_batch.h"
#include "components/sync/protocol/entity_metadata.pb.h"
#include "components/sync/protocol/entity_specifics.pb.h"
#include "components/sync/protocol/model_type_state.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
using sync_pb::EntityMetadata;
using sync_pb::EntitySpecifics;
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 = default;
// 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:
raw_ptr<FakeModelTypeSyncBridge::Store> db_;
};
} // namespace
// static
std::string FakeModelTypeSyncBridge::ClientTagFromKey(const std::string& key) {
return "ClientTag_" + key;
}
// static
ClientTagHash FakeModelTypeSyncBridge::TagHashFromKey(const std::string& key) {
return ClientTagHash::FromUnhashed(
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->name = key;
return entity_data;
}
FakeModelTypeSyncBridge::Store::Store() = default;
FakeModelTypeSyncBridge::Store::~Store() = default;
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 {
DCHECK(data_store_.count(key) != 0) << " for key " << key;
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 {
auto metadata_batch = std::make_unique<MetadataBatch>();
metadata_batch->SetModelTypeState(model_type_state_);
for (const auto& [storage_key, metadata] : metadata_store_) {
metadata_batch->AddMetadata(
storage_key, std::make_unique<sync_pb::EntityMetadata>(metadata));
}
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) {
DCHECK(EntityHasClientTag(*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>();
}
absl::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()) {
if (base::Contains(values_to_ignore_,
change->data().specifics.preference().value())) {
change_processor()->UntrackEntityForClientTagHash(
change->data().client_tag_hash);
continue;
}
storage_key = GenerateStorageKey(change->data());
change_processor()->UpdateStorageKey(change->data(), storage_key,
metadata_change_list.get());
}
remote_storage_keys.insert(storage_key);
DCHECK(EntityHasClientTag(change->data()));
db_->PutData(storage_key, change->data());
}
// Commit any local entities that aren't being overwritten by the server.
for (const auto& [storage_key, entity_data] : db_->all_data()) {
if (remote_storage_keys.find(storage_key) == remote_storage_keys.end()) {
change_processor()->Put(storage_key, CopyEntityData(*entity_data),
metadata_change_list.get());
}
}
ApplyMetadataChangeList(std::move(metadata_change_list));
return {};
}
absl::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 std::unique_ptr<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 = GenerateStorageKey(change->data());
change_processor()->UpdateStorageKey(change->data(), storage_key,
metadata_changes.get());
}
DCHECK(EntityHasClientTag(change->data()));
EXPECT_FALSE(db_->HasData(storage_key));
db_->PutData(storage_key, change->data());
break;
}
case EntityChange::ACTION_UPDATE:
DCHECK(EntityHasClientTag(change->data()));
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& [storage_key, entity_data] : db_->all_data()) {
batch->Put(storage_key, CopyEntityData(*entity_data));
}
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 GenerateStorageKey(entity_data);
}
std::string FakeModelTypeSyncBridge::GenerateStorageKey(
const EntityData& entity_data) {
if (supports_get_storage_key_) {
return entity_data.specifics.preference().name();
} else {
return base::NumberToString(++last_generated_storage_key_);
}
}
bool FakeModelTypeSyncBridge::SupportsGetClientTag() const {
return supports_get_client_tag_;
}
bool FakeModelTypeSyncBridge::SupportsGetStorageKey() const {
return supports_get_storage_key_;
}
ConflictResolution FakeModelTypeSyncBridge::ResolveConflict(
const std::string& storage_key,
const EntityData& remote_data) const {
return conflict_resolution_;
}
void FakeModelTypeSyncBridge::ApplyStopSyncChanges(
std::unique_ptr<MetadataChangeList> delete_metadata_change_list) {
ModelTypeSyncBridge::ApplyStopSyncChanges(
std::move(delete_metadata_change_list));
}
void FakeModelTypeSyncBridge::SetConflictResolution(
ConflictResolution resolution) {
conflict_resolution_ = resolution;
}
void FakeModelTypeSyncBridge::ErrorOnNextCall() {
EXPECT_FALSE(error_next_);
error_next_ = true;
}
std::unique_ptr<EntityData> FakeModelTypeSyncBridge::CopyEntityData(
const EntityData& old_data) {
auto new_data = std::make_unique<EntityData>();
new_data->id = old_data.id;
new_data->client_tag_hash = old_data.client_tag_hash;
new_data->name = old_data.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::SetSupportsGetClientTag(
bool supports_get_client_tag) {
supports_get_client_tag_ = supports_get_client_tag;
}
bool FakeModelTypeSyncBridge::EntityHasClientTag(const EntityData& entity) {
return supports_get_client_tag_ || !entity.client_tag_hash.value().empty();
}
void FakeModelTypeSyncBridge::SetSupportsGetStorageKey(
bool supports_get_storage_key) {
supports_get_storage_key_ = supports_get_storage_key;
}
std::string FakeModelTypeSyncBridge::GetLastGeneratedStorageKey() const {
// Verify that GenerateStorageKey() was called at least once.
EXPECT_NE(0, last_generated_storage_key_);
return base::NumberToString(last_generated_storage_key_);
}
void FakeModelTypeSyncBridge::AddValueToIgnore(const std::string& value) {
values_to_ignore_.insert(value);
}
} // namespace syncer