| // Copyright 2014 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/engine/mock_model_type_processor.h" |
| |
| #include <utility> |
| |
| #include "base/base64.h" |
| #include "base/bind.h" |
| #include "base/guid.h" |
| #include "base/hash/sha1.h" |
| #include "components/sync/base/client_tag_hash.h" |
| #include "components/sync/engine/commit_queue.h" |
| #include "components/sync/protocol/entity_specifics.pb.h" |
| |
| namespace syncer { |
| |
| MockModelTypeProcessor::MockModelTypeProcessor() : is_synchronous_(true) {} |
| |
| MockModelTypeProcessor::~MockModelTypeProcessor() = default; |
| |
| void MockModelTypeProcessor::ConnectSync( |
| std::unique_ptr<CommitQueue> commit_queue) {} |
| |
| void MockModelTypeProcessor::DisconnectSync() { |
| if (!disconnect_callback_.is_null()) { |
| std::move(disconnect_callback_).Run(); |
| } |
| } |
| |
| void MockModelTypeProcessor::GetLocalChanges(size_t max_entries, |
| GetLocalChangesCallback callback) { |
| get_local_changes_call_count_++; |
| |
| // Truncation may be needed due to |max_entries|. |
| CommitRequestDataList remaining_changes; |
| if (commit_request_.size() > max_entries) { |
| for (size_t i = max_entries; i < commit_request_.size(); ++i) { |
| remaining_changes.push_back(std::move(commit_request_[i])); |
| } |
| commit_request_.resize(max_entries); |
| } |
| |
| CommitRequestDataList returned_changes = std::move(commit_request_); |
| commit_request_ = std::move(remaining_changes); |
| std::move(callback).Run(std::move(returned_changes)); |
| } |
| |
| void MockModelTypeProcessor::OnCommitCompleted( |
| const sync_pb::ModelTypeState& type_state, |
| const CommitResponseDataList& committed_response_list, |
| const FailedCommitResponseDataList& error_response_list) { |
| pending_tasks_.push_back(base::BindOnce( |
| &MockModelTypeProcessor::OnCommitCompletedImpl, base::Unretained(this), |
| type_state, committed_response_list, error_response_list)); |
| if (is_synchronous_) |
| RunQueuedTasks(); |
| } |
| |
| void MockModelTypeProcessor::OnCommitFailed(SyncCommitError commit_error) { |
| ++commit_failures_count_; |
| } |
| |
| void MockModelTypeProcessor::OnUpdateReceived( |
| const sync_pb::ModelTypeState& type_state, |
| UpdateResponseDataList response_list) { |
| pending_tasks_.push_back(base::BindOnce( |
| &MockModelTypeProcessor::OnUpdateReceivedImpl, base::Unretained(this), |
| type_state, std::move(response_list))); |
| if (is_synchronous_) |
| RunQueuedTasks(); |
| } |
| |
| void MockModelTypeProcessor::SetSynchronousExecution(bool is_synchronous) { |
| is_synchronous_ = is_synchronous; |
| } |
| |
| void MockModelTypeProcessor::RunQueuedTasks() { |
| for (base::OnceClosure& pending_task : pending_tasks_) { |
| std::move(pending_task).Run(); |
| } |
| pending_tasks_.clear(); |
| } |
| |
| std::unique_ptr<CommitRequestData> MockModelTypeProcessor::CommitRequest( |
| const ClientTagHash& tag_hash, |
| const sync_pb::EntitySpecifics& specifics) { |
| const std::string server_id = HasServerAssignedId(tag_hash) |
| ? GetServerAssignedId(tag_hash) |
| : base::GenerateGUID(); |
| return CommitRequest(tag_hash, specifics, server_id); |
| } |
| |
| std::unique_ptr<CommitRequestData> MockModelTypeProcessor::CommitRequest( |
| const ClientTagHash& tag_hash, |
| const sync_pb::EntitySpecifics& specifics, |
| const std::string& server_id) { |
| const int64_t base_version = GetBaseVersion(tag_hash); |
| |
| auto data = std::make_unique<syncer::EntityData>(); |
| data->id = server_id; |
| data->client_tag_hash = tag_hash; |
| data->specifics = specifics; |
| |
| // These fields are not really used for much, but we set them anyway |
| // to make this item look more realistic. |
| data->creation_time = base::Time::UnixEpoch() + base::Days(1); |
| data->modification_time = data->creation_time + base::Seconds(base_version); |
| data->name = "Name: " + tag_hash.value(); |
| |
| DCHECK(!data->is_deleted()); |
| |
| auto request_data = std::make_unique<CommitRequestData>(); |
| request_data->entity = std::move(data); |
| request_data->sequence_number = GetNextSequenceNumber(tag_hash); |
| request_data->base_version = base_version; |
| base::Base64Encode(base::SHA1HashString(specifics.SerializeAsString()), |
| &request_data->specifics_hash); |
| |
| return request_data; |
| } |
| |
| std::unique_ptr<CommitRequestData> MockModelTypeProcessor::DeleteRequest( |
| const ClientTagHash& tag_hash) { |
| const int64_t base_version = GetBaseVersion(tag_hash); |
| |
| auto data = std::make_unique<syncer::EntityData>(); |
| |
| if (HasServerAssignedId(tag_hash)) { |
| data->id = GetServerAssignedId(tag_hash); |
| } |
| |
| data->client_tag_hash = tag_hash; |
| |
| // These fields have little or no effect on behavior. We set them anyway to |
| // make the test more realistic. |
| data->creation_time = base::Time::UnixEpoch() + base::Days(1); |
| data->name = "Name deleted"; |
| |
| data->modification_time = data->creation_time + base::Seconds(base_version); |
| |
| auto request_data = std::make_unique<CommitRequestData>(); |
| request_data->entity = std::move(data); |
| request_data->sequence_number = GetNextSequenceNumber(tag_hash); |
| request_data->base_version = base_version; |
| |
| pending_deleted_hashes_.insert(tag_hash); |
| |
| return request_data; |
| } |
| |
| size_t MockModelTypeProcessor::GetNumCommitFailures() const { |
| return commit_failures_count_; |
| } |
| |
| size_t MockModelTypeProcessor::GetNumUpdateResponses() const { |
| return received_update_responses_.size(); |
| } |
| |
| std::vector<const UpdateResponseData*> |
| MockModelTypeProcessor::GetNthUpdateResponse(size_t n) const { |
| DCHECK_LT(n, GetNumUpdateResponses()); |
| std::vector<const UpdateResponseData*> nth_update_responses; |
| for (const UpdateResponseData& response : received_update_responses_[n]) { |
| nth_update_responses.push_back(&response); |
| } |
| return nth_update_responses; |
| } |
| |
| sync_pb::ModelTypeState MockModelTypeProcessor::GetNthUpdateState( |
| size_t n) const { |
| DCHECK_LT(n, GetNumUpdateResponses()); |
| return type_states_received_on_update_[n]; |
| } |
| |
| size_t MockModelTypeProcessor::GetNumCommitResponses() const { |
| return received_commit_responses_.size(); |
| } |
| |
| CommitResponseDataList MockModelTypeProcessor::GetNthCommitResponse( |
| size_t n) const { |
| DCHECK_LT(n, GetNumCommitResponses()); |
| return received_commit_responses_[n]; |
| } |
| |
| sync_pb::ModelTypeState MockModelTypeProcessor::GetNthCommitState( |
| size_t n) const { |
| DCHECK_LT(n, GetNumCommitResponses()); |
| return type_states_received_on_commit_[n]; |
| } |
| |
| bool MockModelTypeProcessor::HasUpdateResponse( |
| const ClientTagHash& tag_hash) const { |
| auto it = update_response_items_.find(tag_hash); |
| return it != update_response_items_.end(); |
| } |
| |
| const UpdateResponseData& MockModelTypeProcessor::GetUpdateResponse( |
| const ClientTagHash& tag_hash) const { |
| DCHECK(HasUpdateResponse(tag_hash)); |
| auto it = update_response_items_.find(tag_hash); |
| return *it->second; |
| } |
| |
| bool MockModelTypeProcessor::HasCommitResponse( |
| const ClientTagHash& tag_hash) const { |
| auto it = commit_response_items_.find(tag_hash); |
| return it != commit_response_items_.end(); |
| } |
| |
| CommitResponseData MockModelTypeProcessor::GetCommitResponse( |
| const ClientTagHash& tag_hash) const { |
| DCHECK(HasCommitResponse(tag_hash)); |
| auto it = commit_response_items_.find(tag_hash); |
| return it->second; |
| } |
| |
| void MockModelTypeProcessor::SetDisconnectCallback( |
| DisconnectCallback callback) { |
| disconnect_callback_ = std::move(callback); |
| } |
| |
| void MockModelTypeProcessor::SetCommitRequest( |
| CommitRequestDataList commit_request) { |
| commit_request_ = std::move(commit_request); |
| } |
| |
| void MockModelTypeProcessor::AppendCommitRequest( |
| const ClientTagHash& tag_hash, |
| const sync_pb::EntitySpecifics& specifics) { |
| const std::string server_id = HasServerAssignedId(tag_hash) |
| ? GetServerAssignedId(tag_hash) |
| : base::GenerateGUID(); |
| AppendCommitRequest(tag_hash, specifics, server_id); |
| } |
| |
| void MockModelTypeProcessor::AppendCommitRequest( |
| const ClientTagHash& tag_hash, |
| const sync_pb::EntitySpecifics& specifics, |
| const std::string& server_id) { |
| commit_request_.push_back(CommitRequest(tag_hash, specifics, server_id)); |
| } |
| |
| int MockModelTypeProcessor::GetLocalChangesCallCount() const { |
| return get_local_changes_call_count_; |
| } |
| |
| void MockModelTypeProcessor::OnCommitCompletedImpl( |
| const sync_pb::ModelTypeState& type_state, |
| const CommitResponseDataList& committed_response_list, |
| const FailedCommitResponseDataList& error_response_list) { |
| received_commit_responses_.push_back(committed_response_list); |
| type_states_received_on_commit_.push_back(type_state); |
| for (const CommitResponseData& response : committed_response_list) { |
| const ClientTagHash& tag_hash = response.client_tag_hash; |
| commit_response_items_.insert(std::make_pair(tag_hash, response)); |
| |
| if (pending_deleted_hashes_.find(tag_hash) != |
| pending_deleted_hashes_.end()) { |
| // Delete request was committed on the server. Erase information we track |
| // about the entity. |
| sequence_numbers_.erase(tag_hash); |
| base_versions_.erase(tag_hash); |
| assigned_ids_.erase(tag_hash); |
| pending_deleted_hashes_.erase(tag_hash); |
| } else { |
| // Server wins. Set the model's base version. |
| SetBaseVersion(tag_hash, response.response_version); |
| SetServerAssignedId(tag_hash, response.id); |
| } |
| } |
| } |
| |
| void MockModelTypeProcessor::OnUpdateReceivedImpl( |
| const sync_pb::ModelTypeState& type_state, |
| UpdateResponseDataList response_list) { |
| type_states_received_on_update_.push_back(type_state); |
| for (const UpdateResponseData& response : response_list) { |
| const ClientTagHash& client_tag_hash = response.entity.client_tag_hash; |
| // Server wins. Set the model's base version. |
| SetBaseVersion(client_tag_hash, response.response_version); |
| SetServerAssignedId(client_tag_hash, response.entity.id); |
| |
| update_response_items_.insert(std::make_pair(client_tag_hash, &response)); |
| } |
| received_update_responses_.push_back(std::move(response_list)); |
| } |
| |
| // Fetches the sequence number as of the most recent update request. |
| int64_t MockModelTypeProcessor::GetCurrentSequenceNumber( |
| const ClientTagHash& tag_hash) const { |
| auto it = sequence_numbers_.find(tag_hash); |
| if (it == sequence_numbers_.end()) { |
| return 0; |
| } else { |
| return it->second; |
| } |
| } |
| |
| // The model thread should be sending us items with strictly increasing |
| // sequence numbers. Here's where we emulate that behavior. |
| int64_t MockModelTypeProcessor::GetNextSequenceNumber( |
| const ClientTagHash& tag_hash) { |
| int64_t sequence_number = GetCurrentSequenceNumber(tag_hash); |
| sequence_number++; |
| sequence_numbers_[tag_hash] = sequence_number; |
| return sequence_number; |
| } |
| |
| int64_t MockModelTypeProcessor::GetBaseVersion( |
| const ClientTagHash& tag_hash) const { |
| auto it = base_versions_.find(tag_hash); |
| if (it == base_versions_.end()) { |
| return kUncommittedVersion; |
| } else { |
| return it->second; |
| } |
| } |
| |
| void MockModelTypeProcessor::SetBaseVersion(const ClientTagHash& tag_hash, |
| int64_t version) { |
| base_versions_[tag_hash] = version; |
| } |
| |
| bool MockModelTypeProcessor::HasServerAssignedId( |
| const ClientTagHash& tag_hash) const { |
| return assigned_ids_.find(tag_hash) != assigned_ids_.end(); |
| } |
| |
| const std::string& MockModelTypeProcessor::GetServerAssignedId( |
| const ClientTagHash& tag_hash) const { |
| DCHECK(HasServerAssignedId(tag_hash)); |
| return assigned_ids_.find(tag_hash)->second; |
| } |
| |
| void MockModelTypeProcessor::SetServerAssignedId(const ClientTagHash& tag_hash, |
| const std::string& id) { |
| assigned_ids_[tag_hash] = id; |
| } |
| |
| } // namespace syncer |