diff options
author | pvalenzuela@chromium.org <pvalenzuela@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-16 21:26:11 +0000 |
---|---|---|
committer | pvalenzuela@chromium.org <pvalenzuela@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-16 21:26:11 +0000 |
commit | 6732a39e054cfd28e6518cc34fe542bd56773750 (patch) | |
tree | e86a51a5bb9802840a9ba2b3dee9fe552c1bd298 /sync/test | |
parent | b32c76fc04ba5e17ae394f026542883cf8037a57 (diff) | |
download | chromium_src-6732a39e054cfd28e6518cc34fe542bd56773750.zip chromium_src-6732a39e054cfd28e6518cc34fe542bd56773750.tar.gz chromium_src-6732a39e054cfd28e6518cc34fe542bd56773750.tar.bz2 |
Basic implementation of the Sync C++ fake server
This CL provides just enough functionality so that
PrototypeFakeServerTest will pass.
BUG=323265
Review URL: https://codereview.chromium.org/115243007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245326 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync/test')
-rw-r--r-- | sync/test/fake_server/fake_server.cc | 325 | ||||
-rw-r--r-- | sync/test/fake_server/fake_server.h | 79 | ||||
-rw-r--r-- | sync/test/fake_server/fake_server_http_post_provider.cc | 80 | ||||
-rw-r--r-- | sync/test/fake_server/fake_server_http_post_provider.h | 71 | ||||
-rw-r--r-- | sync/test/fake_server/fake_server_network_resources.cc | 30 | ||||
-rw-r--r-- | sync/test/fake_server/fake_server_network_resources.h | 38 |
6 files changed, 623 insertions, 0 deletions
diff --git a/sync/test/fake_server/fake_server.cc b/sync/test/fake_server/fake_server.cc new file mode 100644 index 0000000..5f4457d --- /dev/null +++ b/sync/test/fake_server/fake_server.cc @@ -0,0 +1,325 @@ +// 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 "sync/test/fake_server/fake_server.h" + +#include <limits> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.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 char kRootParentTag[] = "0"; + +namespace syncer { +namespace { + +// A filter used during GetUpdates calls to determine what information to +// send back to the client. There is a 1:1 correspondence between any given +// GetUpdates call and an UpdateSieve instance. +class UpdateSieve { + public: + ~UpdateSieve() { } + + // Factory method for creating an UpdateSieve. + static scoped_ptr<UpdateSieve> Create( + const sync_pb::GetUpdatesMessage& get_updates_message); + + // Sets the progress markers in |get_updates_response| given the progress + // markers from the original GetUpdatesMessage and |new_version| (the latest + // version in the entries sent back). + void UpdateProgressMarkers( + int64 new_version, + sync_pb::GetUpdatesResponse* get_updates_response) const { + ModelTypeToVersionMap::const_iterator it; + for (it = request_from_version_.begin(); it != request_from_version_.end(); + ++it) { + sync_pb::DataTypeProgressMarker* new_marker = + get_updates_response->add_new_progress_marker(); + new_marker->set_data_type_id( + GetSpecificsFieldNumberFromModelType(it->first)); + + int64 version = std::max(new_version, it->second); + 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) const { + ModelTypeToVersionMap::const_iterator it = + request_from_version_.find(GetModelType(entity)); + + return it == request_from_version_.end() ? + false : it->second < entity.version(); + } + + // Returns the mininum version seen across all types. + int64 GetMinVersion() const { + return min_version_; + } + + // Returns the data type IDs of types being synced for the first time. + std::vector<ModelType> GetFirstTimeTypes() const { + std::vector<ModelType> types; + + ModelTypeToVersionMap::const_iterator it; + for (it = request_from_version_.begin(); it != request_from_version_.end(); + ++it) { + if (it->second == 0) + types.push_back(it->first); + } + + return types; + } + + private: + typedef std::map<ModelType, int64> ModelTypeToVersionMap; + + // Creates an UpdateSieve. + UpdateSieve(const ModelTypeToVersionMap request_from_version, + const int64 min_version) + : request_from_version_(request_from_version), + min_version_(min_version) { } + + // Maps data type IDs to the latest version seen for that type. + const ModelTypeToVersionMap request_from_version_; + + // The minimum version seen among all data types. + const int min_version_; +}; + +scoped_ptr<UpdateSieve> UpdateSieve::Create( + const sync_pb::GetUpdatesMessage& get_updates_message) { + DCHECK_GT(get_updates_message.from_progress_marker_size(), 0); + + UpdateSieve::ModelTypeToVersionMap request_from_version; + 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 (!marker.has_token() || marker.token().empty()) { + // Initialize the version on the first request for this type. + version = 0; + } else { + DCHECK(base::StringToInt64(marker.token(), &version)); + } + + ModelType model_type = GetModelTypeFromSpecificsFieldNumber( + marker.data_type_id()); + request_from_version[model_type] = version; + + if (version < min_version) + min_version = version; + } + + return scoped_ptr<UpdateSieve>( + new UpdateSieve(request_from_version, 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: + // Any nonzero int will indicate a failure here. This one happens to + // be net::ERR_NOT_IMPLEMENTED. + return -11; + } + + *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); + + scoped_ptr<UpdateSieve> sieve = UpdateSieve::Create(message.get_updates()); + CreateDefaultPermanentItems(sieve->GetFirstTimeTypes()); + + int64 min_version = sieve->GetMinVersion(); + + bool send_encryption_keys_based_on_nigori = false; + int64 max_response_version = 0; + for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); + ++it) { + sync_pb::SyncEntity entity = it->second; + if (entity.version() > min_version && sieve->ClientWantsItem(entity)) { + sync_pb::SyncEntity* response_entity = + get_updates_response->add_entries(); + response_entity->CopyFrom(entity); + max_response_version = std::max(max_response_version, + response_entity->version()); + + if (response_entity->name() == ModelTypeToString(NIGORI)) { + send_encryption_keys_based_on_nigori = + response_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); + } + } + + sieve->UpdateProgressMarkers(max_response_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 diff --git a/sync/test/fake_server/fake_server.h b/sync/test/fake_server/fake_server.h new file mode 100644 index 0000000..bf3affc --- /dev/null +++ b/sync/test/fake_server/fake_server.h @@ -0,0 +1,79 @@ +// 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. + +#ifndef SYNC_TEST_FAKE_SERVER_FAKE_SERVER_H_ +#define SYNC_TEST_FAKE_SERVER_FAKE_SERVER_H_ + +#include <map> +#include <string> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/protocol/sync.pb.h" + +namespace syncer { + +// A fake version of the Sync server used for testing. +class FakeServer { + public: + FakeServer(); + virtual ~FakeServer(); + + // Handles a /command POST to the server. If the return value is 0 (success), + // |response_code| and |response| will be set. Otherwise, the return value + // will be a network error code. + int HandleCommand(std::string request, + int* response_code, + std::string* response); + + private: + typedef std::map<std::string, sync_pb::SyncEntity> EntityMap; + + // Processes a GetUpdates call. + sync_pb::ClientToServerResponse HandleGetUpdatesRequest( + const sync_pb::ClientToServerMessage& message); + + // Processes a Commit call. + sync_pb::ClientToServerResponse HandleCommitRequest( + const sync_pb::ClientToServerMessage& message); + + // Inserts the appropriate permanent items in |entities_|. + void CreateDefaultPermanentItems( + const std::vector<ModelType>& first_time_types); + + // Creates and saves a SyncEntity of the given |model_type|. The entity's + // id_string and server_defined_unique_tag fields are set to |id|. The + // non_unique_name and name fields are set to |name|. Since tags are used as + // ids, the parent_id_string field is set to |parent_tag|. + void CreateSyncEntity(ModelType model_type, + const std::string& id, + const std::string& name, + const std::string& parent_tag); + + // Saves a |entity| to |entities_|. + void SaveEntity(sync_pb::SyncEntity entity); + + // Commits a client-side |entity| to |entities_|. + sync_pb::SyncEntity CommitEntity(sync_pb::SyncEntity entity, + std::string guid); + + // This is the last version number assigned to an entity. The next entity will + // have a version number of version_ + 1. + int64 version_; + + // The current birthday value. + std::string birthday_; + + // All SyncEntity objects saved by the server. The key value is the entity's + // id string. + EntityMap entities_; + + // All Keystore keys known to the server. + std::vector<std::string> keystore_keys_; +}; + +} // namespace syncer + +#endif // SYNC_TEST_FAKE_SERVER_FAKE_SERVER_H_ diff --git a/sync/test/fake_server/fake_server_http_post_provider.cc b/sync/test/fake_server/fake_server_http_post_provider.cc new file mode 100644 index 0000000..feb87a4 --- /dev/null +++ b/sync/test/fake_server/fake_server_http_post_provider.cc @@ -0,0 +1,80 @@ +// 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 "sync/test/fake_server/fake_server_http_post_provider.h" + +#include <string> + +#include "sync/test/fake_server/fake_server.h" + +namespace syncer { + +FakeServerHttpPostProviderFactory::FakeServerHttpPostProviderFactory( + FakeServer* fake_server) : fake_server_(fake_server) { } + +FakeServerHttpPostProviderFactory::~FakeServerHttpPostProviderFactory() { } + +void FakeServerHttpPostProviderFactory::Init(const std::string& user_agent) { } + +HttpPostProviderInterface* FakeServerHttpPostProviderFactory::Create() { + FakeServerHttpPostProvider* http = + new FakeServerHttpPostProvider(fake_server_); + http->AddRef(); + return http; +} + +void FakeServerHttpPostProviderFactory::Destroy( + HttpPostProviderInterface* http) { + static_cast<FakeServerHttpPostProvider*>(http)->Release(); +} + +FakeServerHttpPostProvider::FakeServerHttpPostProvider( + FakeServer* fake_server) : fake_server_(fake_server) { } + +FakeServerHttpPostProvider::~FakeServerHttpPostProvider() { } + +void FakeServerHttpPostProvider::SetExtraRequestHeaders(const char* headers) { + // TODO(pvalenzuela): Add assertions on this value. + extra_request_headers_.assign(headers); +} + +void FakeServerHttpPostProvider::SetURL(const char* url, int port) { + // TODO(pvalenzuela): Add assertions on these values. + request_url_.assign(url); + request_port_ = port; +} + +void FakeServerHttpPostProvider::SetPostPayload(const char* content_type, + int content_length, + const char* content) { + request_content_type_.assign(content_type); + request_content_.assign(content, content_length); +} + +bool FakeServerHttpPostProvider::MakeSynchronousPost(int* error_code, + int* response_code) { + // This assumes that a POST is being made to /command. + *error_code = fake_server_->HandleCommand(request_content_, + response_code, + &response_); + return (*error_code == 0); +} + +int FakeServerHttpPostProvider::GetResponseContentLength() const { + return response_.length(); +} + +const char* FakeServerHttpPostProvider::GetResponseContent() const { + return response_.c_str(); +} + +const std::string FakeServerHttpPostProvider::GetResponseHeaderValue( + const std::string& name) const { + return ""; +} + +void FakeServerHttpPostProvider::Abort() { +} + +} // namespace syncer diff --git a/sync/test/fake_server/fake_server_http_post_provider.h b/sync/test/fake_server/fake_server_http_post_provider.h new file mode 100644 index 0000000..dbbbab2 --- /dev/null +++ b/sync/test/fake_server/fake_server_http_post_provider.h @@ -0,0 +1,71 @@ +// 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. + +#ifndef SYNC_TEST_FAKE_SERVER_FAKE_SERVER_HTTP_POST_PROVIDER_H_ +#define SYNC_TEST_FAKE_SERVER_FAKE_SERVER_HTTP_POST_PROVIDER_H_ + +#include <string> + +#include "base/memory/ref_counted.h" +#include "sync/internal_api/public/http_post_provider_factory.h" +#include "sync/internal_api/public/http_post_provider_interface.h" + +namespace syncer { + +class FakeServer; + +class FakeServerHttpPostProvider + : public HttpPostProviderInterface, + public base::RefCountedThreadSafe<FakeServerHttpPostProvider> { + public: + FakeServerHttpPostProvider(FakeServer* fake_server); + + // HttpPostProviderInterface implementation. + virtual void SetExtraRequestHeaders(const char* headers) OVERRIDE; + virtual void SetURL(const char* url, int port) OVERRIDE; + virtual void SetPostPayload(const char* content_type, int content_length, + const char* content) OVERRIDE; + virtual bool MakeSynchronousPost(int* error_code, + int* response_code) OVERRIDE; + virtual void Abort() OVERRIDE; + virtual int GetResponseContentLength() const OVERRIDE; + virtual const char* GetResponseContent() const OVERRIDE; + virtual const std::string GetResponseHeaderValue( + const std::string& name) const OVERRIDE; + + protected: + friend class base::RefCountedThreadSafe<FakeServerHttpPostProvider>; + virtual ~FakeServerHttpPostProvider(); + + private: + FakeServer* const fake_server_; + std::string response_; + std::string request_url_; + int request_port_; + std::string request_content_; + std::string request_content_type_; + std::string extra_request_headers_; + + DISALLOW_COPY_AND_ASSIGN(FakeServerHttpPostProvider); +}; + +class FakeServerHttpPostProviderFactory : public HttpPostProviderFactory { + public: + FakeServerHttpPostProviderFactory(FakeServer* fake_server); + virtual ~FakeServerHttpPostProviderFactory(); + + // HttpPostProviderFactory: + virtual void Init(const std::string& user_agent) OVERRIDE; + virtual HttpPostProviderInterface* Create() OVERRIDE; + virtual void Destroy(HttpPostProviderInterface* http) OVERRIDE; + + private: + FakeServer* const fake_server_; + + DISALLOW_COPY_AND_ASSIGN(FakeServerHttpPostProviderFactory); +}; + +} // namespace syncer + +#endif // SYNC_TEST_FAKE_SERVER_FAKE_SERVER_HTTP_POST_PROVIDER_H_ diff --git a/sync/test/fake_server/fake_server_network_resources.cc b/sync/test/fake_server/fake_server_network_resources.cc new file mode 100644 index 0000000..1f5d308 --- /dev/null +++ b/sync/test/fake_server/fake_server_network_resources.cc @@ -0,0 +1,30 @@ +// 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 "sync/test/fake_server/fake_server_network_resources.h" + +#include "base/memory/scoped_ptr.h" +#include "sync/internal_api/public/base/cancelation_signal.h" +#include "sync/internal_api/public/http_post_provider_factory.h" +#include "sync/internal_api/public/network_time_update_callback.h" +#include "sync/test/fake_server/fake_server.h" +#include "sync/test/fake_server/fake_server_http_post_provider.h" + +namespace syncer { + +FakeServerNetworkResources::FakeServerNetworkResources(FakeServer* fake_server) + : fake_server_(fake_server) { } + +FakeServerNetworkResources::~FakeServerNetworkResources() {} + +scoped_ptr<HttpPostProviderFactory> + FakeServerNetworkResources::GetHttpPostProviderFactory( + net::URLRequestContextGetter* baseline_context_getter, + const NetworkTimeUpdateCallback& network_time_update_callback, + CancelationSignal* cancelation_signal) { + return make_scoped_ptr<HttpPostProviderFactory>( + new FakeServerHttpPostProviderFactory(fake_server_)); +} + +} // namespace syncer diff --git a/sync/test/fake_server/fake_server_network_resources.h b/sync/test/fake_server/fake_server_network_resources.h new file mode 100644 index 0000000..eda2fdf --- /dev/null +++ b/sync/test/fake_server/fake_server_network_resources.h @@ -0,0 +1,38 @@ +// 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. + +#ifndef SYNC_TEST_FAKE_SERVER_FAKE_SERVER_NETWORK_RESOURCES_H_ +#define SYNC_TEST_FAKE_SERVER_FAKE_SERVER_NETWORK_RESOURCES_H_ + +#include "base/memory/scoped_ptr.h" +#include "sync/internal_api/public/network_resources.h" +#include "sync/internal_api/public/network_time_update_callback.h" + +namespace net { +class URLRequestContextGetter; +} // namespace net + +namespace syncer { + +class FakeServer; +class HttpPostProviderFactory; + +class FakeServerNetworkResources : public NetworkResources { + public: + FakeServerNetworkResources(FakeServer* fake_server); + virtual ~FakeServerNetworkResources(); + + // NetworkResources + virtual scoped_ptr<HttpPostProviderFactory> GetHttpPostProviderFactory( + net::URLRequestContextGetter* baseline_context_getter, + const NetworkTimeUpdateCallback& network_time_update_callback, + CancelationSignal* cancelation_signal) OVERRIDE; + + private: + FakeServer* const fake_server_; +}; + +} // namespace syncer + +#endif // SYNC_TEST_FAKE_SERVER_FAKE_SERVER_NETWORK_RESOURCES_H_ |