Chromium Code Reviews| Index: sync/internal_api/test/fake_server.cc |
| diff --git a/sync/internal_api/test/fake_server.cc b/sync/internal_api/test/fake_server.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1a3b25f07cef136cbd73c6c52fb6c9623cb662a9 |
| --- /dev/null |
| +++ b/sync/internal_api/test/fake_server.cc |
| @@ -0,0 +1,321 @@ |
| +// Copyright 2013 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 "sync/internal_api/public/test/fake_server.h" |
| + |
| +#include <limits> |
| +#include <string> |
| +#include <vector> |
| + |
| +#include "base/basictypes.h" |
| +#include "base/logging.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "net/base/net_errors.h" |
| +#include "sync/internal_api/public/base/model_type.h" |
| +#include "sync/protocol/sync.pb.h" |
| + |
| +using std::string; |
| + |
| +// The parent tag for childen of the root node. |
| +static const std::string kRootParentTag = "0"; |
| + |
| +namespace syncer { |
| +namespace { |
| + |
| + // A filter used during GetUpdates calls to determine what information to |
|
rlarocque
2014/01/04 00:06:24
You don't need to indent for the namespace.
pval...(no longer on Chromium)
2014/01/06 19:24:32
oops, fixed
|
| + // send back to the client. There is a 1:1 correspondence between any given |
| + // GetUpdates call and an UpdateSieve instance. |
| + class UpdateSieve { |
| + public: |
| + // Populates |state_| based on |get_updates_message|. |
| + UpdateSieve(const sync_pb::GetUpdatesMessage& get_updates_message) |
| + : get_updates_message_(get_updates_message) { |
| + DCHECK_GT(get_updates_message.from_progress_marker_size(), 0); |
| + |
| + int64 min_version = std::numeric_limits<int64>::max(); |
| + for (int i = 0; i < get_updates_message_.from_progress_marker_size(); |
| + i++) { |
| + sync_pb::DataTypeProgressMarker marker = |
| + get_updates_message_.from_progress_marker(i); |
| + |
| + int64 version; |
| + if (!base::StringToInt64(marker.token(), &version)) { |
| + version = 0; |
|
rlarocque
2014/01/04 00:06:24
This is a good example of why doing work in a cons
pval...(no longer on Chromium)
2014/01/06 19:24:32
I like this idea. I'll make the change, but how ca
rlarocque
2014/01/10 20:09:41
The first request will have an empty progress toke
pval...(no longer on Chromium)
2014/01/14 01:48:09
I've added the factory method and considered the f
|
| + } |
| + |
| + ModelType model_type = GetModelTypeFromSpecificsFieldNumber( |
| + marker.data_type_id()); |
| + state_[model_type] = version; |
| + |
| + if (version < min_version) |
| + min_version = version; |
| + } |
| + |
| + min_version_ = min_version; |
| + } |
| + |
| + ~UpdateSieve() { } |
| + |
| + // Sets the progress markers in |get_updates_response| given the progress |
| + // markers from |get_updates_message_| and |new_version| (the latest |
| + // version in the entries sent back). |
| + void UpdateProgressMarkers( |
| + int64 new_version, |
| + sync_pb::GetUpdatesResponse* get_updates_response) { |
| + for (ModelTypeToVersionMap::iterator it = state_.begin(); |
| + it != state_.end(); ++it) { |
| + sync_pb::DataTypeProgressMarker* new_marker = |
| + get_updates_response->add_new_progress_marker(); |
| + new_marker->set_data_type_id( |
| + GetSpecificsFieldNumberFromModelType(it->first)); |
| + |
| + ModelType model_type = GetModelTypeFromSpecificsFieldNumber( |
| + new_marker->data_type_id()); |
| + int64 version = new_version > state_[model_type] ? |
|
rlarocque
2014/01/04 00:06:24
nit: use std::max instead?
Also, what does this d
pval...(no longer on Chromium)
2014/01/06 19:24:32
Changed to use std::max.
As for the purpose, I be
|
| + new_version : state_[model_type]; |
| + new_marker->set_token(base::Int64ToString(version)); |
| + } |
| + } |
| + |
| + // Determines whether the server should send |entity| to the client based |
| + // on its type and version. |
| + bool ClientWantsItem(const sync_pb::SyncEntity& entity) { |
| + ModelType model_type = GetModelType(entity); |
| + return state_[model_type] < entity.version(); |
| + } |
| + |
| + // Returns the mininum version seen across all types. |
| + int64 GetMinVersion() { |
| + return min_version_; |
| + } |
| + |
| + // Returns the data type IDs of types being synced for the first time. |
| + std::vector<ModelType> GetFirstTimeTypes() { |
| + std::vector<ModelType> types; |
| + |
| + for (ModelTypeToVersionMap::iterator it = state_.begin(); |
| + it != state_.end(); ++it) { |
| + if (state_[it->first] == 0) |
| + types.push_back(it->first); |
| + } |
| + |
| + return types; |
| + } |
| + |
| + private: |
| + typedef std::map<ModelType, int64> ModelTypeToVersionMap; |
| + |
| + // The GetUpdatesMessage associated with this instance. |
| + const sync_pb::GetUpdatesMessage& get_updates_message_; |
| + |
| + // Maps data type IDs to the latest version seen for that type. |
| + ModelTypeToVersionMap state_; |
|
rlarocque
2014/01/04 00:06:24
I think state_ is not a very good name. How about
pval...(no longer on Chromium)
2014/01/06 19:24:32
Done.
|
| + |
| + // The minimum version seen among all data types. |
| + int min_version_; |
| + }; |
| + |
| +} // namespace |
| + |
| +FakeServer::FakeServer() : version_(0), birthday_("1234567890") { |
| + keystore_keys_.push_back("1111111111111111"); |
| +} |
| + |
| +FakeServer::~FakeServer() { } |
| + |
| +void FakeServer::CreateDefaultPermanentItems( |
| + const std::vector<ModelType>& first_time_types) { |
| + for (std::vector<ModelType>::const_iterator it = first_time_types.begin(); |
| + it != first_time_types.end(); ++it) { |
| + if (!ModelTypeSet::All().Has(*it)) { |
| + NOTREACHED() << "An unexpected ModelType was encountered."; |
| + } |
| + |
| + ModelType model_type = *it; |
| + CreateSyncEntity(model_type, |
| + ModelTypeToRootTag(model_type), |
| + ModelTypeToString(model_type), |
| + kRootParentTag); |
| + |
| + if (model_type == BOOKMARKS) { |
| + CreateSyncEntity(BOOKMARKS, |
| + "bookmark_bar", |
| + "Bookmark Bar", |
| + ModelTypeToRootTag(BOOKMARKS)); |
| + CreateSyncEntity(BOOKMARKS, |
| + "other_bookmarks", |
| + "Other Bookmarks", |
| + ModelTypeToRootTag(BOOKMARKS)); |
| + } |
| + } |
| + |
| + |
| + // TODO(pvalenzuela): Create the mobile bookmarks folder when the fake server |
| + // is used by mobile tests. |
| +} |
| + |
| +void FakeServer::CreateSyncEntity(ModelType model_type, |
| + const std::string& id, |
| + const std::string& name, |
| + const std::string& parent_tag) { |
| + DCHECK(!id.empty()); |
| + DCHECK(!name.empty()); |
| + DCHECK(!parent_tag.empty()); |
| + |
| + sync_pb::SyncEntity entity; |
| + entity.set_id_string(id); |
| + entity.set_non_unique_name(name); |
| + entity.set_name(name); |
| + entity.set_server_defined_unique_tag(id); |
| + entity.set_folder(true); |
| + entity.set_deleted(false); |
| + |
| + entity.set_parent_id_string(parent_tag); |
| + |
| + if (parent_tag != kRootParentTag && model_type == BOOKMARKS) { |
| + // Use a dummy value here. |
| + entity.set_position_in_parent(1337); |
| + } |
| + |
| + sync_pb::EntitySpecifics* specifics = entity.mutable_specifics(); |
| + AddDefaultFieldValue(model_type, specifics); |
| + |
| + SaveEntity(entity); |
| +} |
| + |
| +void FakeServer::SaveEntity(sync_pb::SyncEntity entity) { |
| + version_++; |
| + entity.set_version(version_); |
| + entity.set_sync_timestamp(version_); |
| + |
| + sync_pb::SyncEntity original_entity = entities_[entity.id_string()]; |
| + entity.set_originator_cache_guid(original_entity.originator_cache_guid()); |
| + entity.set_originator_client_item_id( |
| + original_entity.originator_client_item_id()); |
| + |
| + entities_[entity.id_string()] = entity; |
| +} |
| + |
| +int FakeServer::HandleCommand(string request, |
| + int* response_code, |
| + string* response) { |
| + sync_pb::ClientToServerMessage message; |
| + DCHECK(message.ParseFromString(request)); |
| + |
| + sync_pb::ClientToServerResponse response_proto; |
| + switch (message.message_contents()) { |
| + case sync_pb::ClientToServerMessage::GET_UPDATES: |
| + response_proto = HandleGetUpdatesRequest(message); |
| + break; |
| + case sync_pb::ClientToServerMessage::COMMIT: |
| + response_proto = HandleCommitRequest(message); |
| + break; |
| + default: |
| + return net::ERR_NOT_IMPLEMENTED; |
| + } |
| + |
| + *response_code = 200; |
| + *response = response_proto.SerializeAsString(); |
| + return 0; |
| +} |
| + |
| +bool SyncEntityVersionComparator(const sync_pb::SyncEntity& first, |
| + const sync_pb::SyncEntity& second) { |
| + return first.version() < second.version(); |
| +} |
| + |
| +sync_pb::ClientToServerResponse FakeServer::HandleGetUpdatesRequest( |
| + const sync_pb::ClientToServerMessage& message) { |
| + sync_pb::ClientToServerResponse response; |
| + response.set_error_code(sync_pb::SyncEnums::SUCCESS); |
| + response.set_store_birthday(birthday_); |
| + |
| + sync_pb::GetUpdatesResponse* get_updates_response = |
| + response.mutable_get_updates(); |
| + // TODO(pvalenzuela): Implement batching instead of sending all information |
| + // at once. |
| + get_updates_response->set_changes_remaining(0); |
| + |
| + UpdateSieve sieve(message.get_updates()); |
| + CreateDefaultPermanentItems(sieve.GetFirstTimeTypes()); |
| + |
| + int64 min_version = sieve.GetMinVersion(); |
| + |
| + std::vector<sync_pb::SyncEntity> filtered_entities; |
| + for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); |
| + ++it) { |
| + sync_pb::SyncEntity entity = it->second; |
| + if (entity.version() > min_version) { |
| + filtered_entities.push_back(entity); |
| + } |
| + } |
| + |
| + std::sort(filtered_entities.begin(), filtered_entities.end(), |
| + SyncEntityVersionComparator); |
| + |
| + bool send_encryption_keys_based_on_nigori = false; |
| + for (std::vector<sync_pb::SyncEntity>::iterator it = |
|
rlarocque
2014/01/04 00:06:24
I don't think you gain much by iterating twice. C
pval...(no longer on Chromium)
2014/01/06 19:24:32
Ah, much nicer. Thanks.
|
| + filtered_entities.begin(); it != filtered_entities.end(); ++it) { |
| + if (sieve.ClientWantsItem(*it)) { |
| + sync_pb::SyncEntity* entity = get_updates_response->add_entries(); |
| + entity->CopyFrom(*it); |
| + |
| + if (entity->name() == ModelTypeToString(NIGORI)) { |
| + send_encryption_keys_based_on_nigori = |
| + entity->specifics().nigori().passphrase_type() == |
| + sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE; |
| + } |
| + } |
| + } |
| + |
| + if (send_encryption_keys_based_on_nigori || |
| + message.get_updates().need_encryption_key()) { |
| + for (std::vector<std::string>::iterator it = keystore_keys_.begin(); |
| + it != keystore_keys_.end(); ++it) { |
| + get_updates_response->add_encryption_keys(*it); |
| + } |
| + } |
| + |
| + int new_version = filtered_entities.empty() ? |
| + 0 : filtered_entities.back().version(); |
| + |
| + sieve.UpdateProgressMarkers(new_version, get_updates_response); |
| + |
| + return response; |
| +} |
| + |
| +sync_pb::SyncEntity FakeServer::CommitEntity(sync_pb::SyncEntity entity, |
| + string guid) { |
| + // TODO(pvalenzuela): Implement this. Right now this method cheats and |
| + // doesn't actually commit. |
| + return entity; |
| +} |
| + |
| +sync_pb::ClientToServerResponse FakeServer::HandleCommitRequest( |
| + const sync_pb::ClientToServerMessage& message) { |
| + sync_pb::ClientToServerResponse response; |
| + response.set_error_code(sync_pb::SyncEnums::SUCCESS); |
| + response.set_store_birthday(birthday_); |
| + |
| + sync_pb::CommitMessage commit = message.commit(); |
| + string guid = commit.cache_guid(); |
| + |
| + sync_pb::CommitResponse* commit_response = response.mutable_commit(); |
| + |
| + ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it; |
| + for (it = commit.entries().begin(); it != commit.entries().end(); ++it) { |
| + sync_pb::CommitResponse_EntryResponse* entry_response = |
| + commit_response->add_entryresponse(); |
| + |
| + sync_pb::SyncEntity server_entity = CommitEntity(*it, guid); |
| + |
| + entry_response->set_id_string(server_entity.id_string()); |
| + entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS); |
| + entry_response->set_version(it->version() + 1); |
| + } |
| + |
| + return response; |
| +} |
| + |
| +} // namespace syncer |