summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpvalenzuela@chromium.org <pvalenzuela@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-16 21:26:11 +0000
committerpvalenzuela@chromium.org <pvalenzuela@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-16 21:26:11 +0000
commit6732a39e054cfd28e6518cc34fe542bd56773750 (patch)
treee86a51a5bb9802840a9ba2b3dee9fe552c1bd298
parentb32c76fc04ba5e17ae394f026542883cf8037a57 (diff)
downloadchromium_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
-rw-r--r--chrome/browser/sync/test/integration/prototype_fake_server_test.cc24
-rw-r--r--chrome/browser/sync/test/integration/sync_test.cc74
-rw-r--r--chrome/browser/sync/test/integration/sync_test.h30
-rw-r--r--chrome/chrome_tests.gypi10
-rw-r--r--sync/sync_tests.gypi25
-rw-r--r--sync/test/fake_server/fake_server.cc325
-rw-r--r--sync/test/fake_server/fake_server.h79
-rw-r--r--sync/test/fake_server/fake_server_http_post_provider.cc80
-rw-r--r--sync/test/fake_server/fake_server_http_post_provider.h71
-rw-r--r--sync/test/fake_server/fake_server_network_resources.cc30
-rw-r--r--sync/test/fake_server/fake_server_network_resources.h38
11 files changed, 753 insertions, 33 deletions
diff --git a/chrome/browser/sync/test/integration/prototype_fake_server_test.cc b/chrome/browser/sync/test/integration/prototype_fake_server_test.cc
new file mode 100644
index 0000000..cdb8f85
--- /dev/null
+++ b/chrome/browser/sync/test/integration/prototype_fake_server_test.cc
@@ -0,0 +1,24 @@
+// 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 "chrome/browser/sync/test/integration/sync_test.h"
+
+class PrototypeFakeServerTest : public SyncTest {
+ public:
+ PrototypeFakeServerTest() : SyncTest(SINGLE_CLIENT) {
+ UseFakeServer();
+ }
+
+ virtual ~PrototypeFakeServerTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrototypeFakeServerTest);
+};
+
+// TODO(pvalenzuela): Remove this test when sync_integration_tests is
+// transitioned to use the C++ fake server. This test currently exists to
+// ensure fake server functionality during development. See Bug 323265.
+IN_PROC_BROWSER_TEST_F(PrototypeFakeServerTest, Setup) {
+ ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
+}
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index b6297c7..b5b0f3f 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -60,6 +60,8 @@
#include "sync/engine/sync_scheduler_impl.h"
#include "sync/notifier/p2p_invalidator.h"
#include "sync/protocol/sync.pb.h"
+#include "sync/test/fake_server/fake_server.h"
+#include "sync/test/fake_server/fake_server_network_resources.h"
#include "url/gurl.h"
using content::BrowserThread;
@@ -143,30 +145,34 @@ void SyncTest::SetUp() {
password_ = "password";
}
- if (!cl->HasSwitch(switches::kSyncServiceURL) &&
- !cl->HasSwitch(switches::kSyncServerCommandLine)) {
- // If neither a sync server URL nor a sync server command line is
- // provided, start up a local python sync test server and point Chrome
- // to its URL. This is the most common configuration, and the only
- // one that makes sense for most developers.
- server_type_ = LOCAL_PYTHON_SERVER;
- } else if (cl->HasSwitch(switches::kSyncServiceURL) &&
- cl->HasSwitch(switches::kSyncServerCommandLine)) {
- // If a sync server URL and a sync server command line are provided,
- // start up a local sync server by running the command line. Chrome
- // will connect to the server at the URL that was provided.
- server_type_ = LOCAL_LIVE_SERVER;
- } else if (cl->HasSwitch(switches::kSyncServiceURL) &&
- !cl->HasSwitch(switches::kSyncServerCommandLine)) {
- // If a sync server URL is provided, but not a server command line,
- // it is assumed that the server is already running. Chrome will
- // automatically connect to it at the URL provided. There is nothing
- // to do here.
- server_type_ = EXTERNAL_LIVE_SERVER;
- } else {
- // If a sync server command line is provided, but not a server URL,
- // we flag an error.
- LOG(FATAL) << "Can't figure out how to run a server.";
+ // Only set |server_type_| if it hasn't already been set. This allows for
+ // IN_PROCESS_FAKE_SERVER tests to set this value in each test class.
+ if (server_type_ == SERVER_TYPE_UNDECIDED) {
+ if (!cl->HasSwitch(switches::kSyncServiceURL) &&
+ !cl->HasSwitch(switches::kSyncServerCommandLine)) {
+ // If neither a sync server URL nor a sync server command line is
+ // provided, start up a local python sync test server and point Chrome
+ // to its URL. This is the most common configuration, and the only
+ // one that makes sense for most developers.
+ server_type_ = LOCAL_PYTHON_SERVER;
+ } else if (cl->HasSwitch(switches::kSyncServiceURL) &&
+ cl->HasSwitch(switches::kSyncServerCommandLine)) {
+ // If a sync server URL and a sync server command line are provided,
+ // start up a local sync server by running the command line. Chrome
+ // will connect to the server at the URL that was provided.
+ server_type_ = LOCAL_LIVE_SERVER;
+ } else if (cl->HasSwitch(switches::kSyncServiceURL) &&
+ !cl->HasSwitch(switches::kSyncServerCommandLine)) {
+ // If a sync server URL is provided, but not a server command line,
+ // it is assumed that the server is already running. Chrome will
+ // automatically connect to it at the URL provided. There is nothing
+ // to do here.
+ server_type_ = EXTERNAL_LIVE_SERVER;
+ } else {
+ // If a sync server command line is provided, but not a server URL,
+ // we flag an error.
+ LOG(FATAL) << "Can't figure out how to run a server.";
+ }
}
if (username_.empty() || password_.empty())
@@ -316,7 +322,15 @@ void SyncTest::InitializeInstance(int index) {
// Make sure the ProfileSyncService has been created before creating the
// ProfileSyncServiceHarness - some tests expect the ProfileSyncService to
// already exist.
- ProfileSyncServiceFactory::GetForProfile(GetProfile(index));
+ ProfileSyncService* profile_sync_service =
+ ProfileSyncServiceFactory::GetForProfile(GetProfile(index));
+
+ if (server_type_ == IN_PROCESS_FAKE_SERVER) {
+ // TODO(pvalenzuela): Run the fake server via EmbeddedTestServer.
+ profile_sync_service->OverrideNetworkResourcesForTest(
+ make_scoped_ptr<syncer::NetworkResources>(
+ new syncer::FakeServerNetworkResources(fake_server_.get())));
+ }
clients_[index] =
ProfileSyncServiceHarness::CreateForIntegrationTest(
@@ -509,6 +523,11 @@ void SyncTest::SetUpTestServerIfRequired() {
LOG(FATAL) << "Failed to set up local python XMPP server";
if (!SetUpLocalTestServer())
LOG(FATAL) << "Failed to set up local test server";
+ } else if (server_type_ == IN_PROCESS_FAKE_SERVER) {
+ fake_server_.reset(new syncer::FakeServer());
+ // Similar to LOCAL_LIVE_SERVER, we must start this for XMPP.
+ SetUpLocalPythonTestServer();
+ SetupMockGaiaResponses();
} else if (server_type_ == EXTERNAL_LIVE_SERVER) {
// Nothing to do; we'll just talk to the URL we were given.
} else {
@@ -862,3 +881,8 @@ void SyncTest::SetProxyConfig(net::URLRequestContextGetter* context_getter,
make_scoped_refptr(context_getter), proxy_config));
done.Wait();
}
+
+void SyncTest::UseFakeServer() {
+ DCHECK_EQ(SERVER_TYPE_UNDECIDED, server_type_);
+ server_type_ = IN_PROCESS_FAKE_SERVER;
+}
diff --git a/chrome/browser/sync/test/integration/sync_test.h b/chrome/browser/sync/test/integration/sync_test.h
index 814f53e..e05fe4a 100644
--- a/chrome/browser/sync/test/integration/sync_test.h
+++ b/chrome/browser/sync/test/integration/sync_test.h
@@ -18,6 +18,7 @@
#include "net/url_request/url_request_status.h"
#include "sync/internal_api/public/base/model_type.h"
#include "sync/protocol/sync_protocol_error.h"
+#include "sync/test/fake_server/fake_server.h"
#include "sync/test/local_sync_test_server.h"
@@ -57,14 +58,18 @@ class SyncTest : public InProcessBrowserTest {
// The type of server we're running against.
enum ServerType {
SERVER_TYPE_UNDECIDED,
- LOCAL_PYTHON_SERVER, // The mock python server that runs locally and is
- // part of the Chromium checkout.
- LOCAL_LIVE_SERVER, // Some other server (maybe the real binary used by
- // Google's sync service) that can be started on
- // a per-test basis by running a command
- EXTERNAL_LIVE_SERVER, // A remote server that the test code has no control
- // over whatsoever; cross your fingers that the
- // account state is initially clean.
+ LOCAL_PYTHON_SERVER, // The mock python server that runs locally and is
+ // part of the Chromium checkout.
+ LOCAL_LIVE_SERVER, // Some other server (maybe the real binary used by
+ // Google's sync service) that can be started on
+ // a per-test basis by running a command
+ EXTERNAL_LIVE_SERVER, // A remote server that the test code has no control
+ // over whatsoever; cross your fingers that the
+ // account state is initially clean.
+ IN_PROCESS_FAKE_SERVER, // The fake Sync server (FakeServer) running
+ // in-process (bypassing HTTP calls). This
+ // ServerType will eventually replace
+ // LOCAL_PYTHON_SERVER.
};
// NOTE: IMPORTANT the enum here should match with
@@ -251,6 +256,12 @@ class SyncTest : public InProcessBrowserTest {
void DisableNotificationsImpl();
void EnableNotificationsImpl();
+ // Set up the test to use the in-process fake server. This must be called
+ // before SetUp().
+ // TODO(pvalenzuela): Remove this method when the C++ fake server becomes
+ // the default server type.
+ void UseFakeServer();
+
// GAIA account used by the test case.
std::string username_;
@@ -260,6 +271,9 @@ class SyncTest : public InProcessBrowserTest {
// Locally available plain text file in which GAIA credentials are stored.
base::FilePath password_file_;
+ // The FakeServer used in tests with server type IN_PROCESS_FAKE_SERVER.
+ scoped_ptr<syncer::FakeServer> fake_server_;
+
private:
// Helper to ProfileManager::CreateProfile that handles path creation.
static Profile* MakeProfile(const base::FilePath::StringType name);
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 67bb60c..15f3c83 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -2312,6 +2312,7 @@
'../skia/skia.gyp:skia',
'../sync/sync.gyp:sync',
'../sync/sync.gyp:test_support_sync_testserver',
+ '../sync/sync.gyp:test_support_sync_fake_server',
'../ui/app_list/app_list.gyp:app_list_test_support',
],
'include_dirs': [
@@ -2434,6 +2435,15 @@
'browser/sync/test/integration/multiple_client_preferences_sync_test.cc',
'browser/sync/test/integration/multiple_client_sessions_sync_test.cc',
'browser/sync/test/integration/multiple_client_typed_urls_sync_test.cc',
+ 'browser/sync/test/integration/passwords_helper.cc',
+ 'browser/sync/test/integration/passwords_helper.h',
+ 'browser/sync/test/integration/preferences_helper.cc',
+ 'browser/sync/test/integration/preferences_helper.h',
+ 'browser/sync/test/integration/prototype_fake_server_test.cc',
+ 'browser/sync/test/integration/search_engines_helper.cc',
+ 'browser/sync/test/integration/search_engines_helper.h',
+ 'browser/sync/test/integration/sessions_helper.cc',
+ 'browser/sync/test/integration/sessions_helper.h',
'browser/sync/test/integration/single_client_app_list_sync_test.cc',
'browser/sync/test/integration/single_client_apps_sync_test.cc',
'browser/sync/test/integration/single_client_bookmarks_sync_test.cc',
diff --git a/sync/sync_tests.gypi b/sync/sync_tests.gypi
index 57e2b47..bfe7e7b 100644
--- a/sync/sync_tests.gypi
+++ b/sync/sync_tests.gypi
@@ -90,6 +90,31 @@
],
},
+ # Test support files for the fake sync server.
+ {
+ 'target_name': 'test_support_sync_fake_server',
+ 'type': 'static_library',
+ 'variables': { 'enable_wexit_time_destructors': 1, },
+ 'include_dirs': [
+ '..',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'sync',
+ ],
+ 'export_dependent_settings': [
+ 'sync',
+ ],
+ 'sources': [
+ 'test/fake_server/fake_server.h',
+ 'test/fake_server/fake_server.cc',
+ 'test/fake_server/fake_server_http_post_provider.h',
+ 'test/fake_server/fake_server_http_post_provider.cc',
+ 'test/fake_server/fake_server_network_resources.h',
+ 'test/fake_server/fake_server_network_resources.cc',
+ ],
+ },
+
# Test support files for the 'sync_notifier' target.
{
'target_name': 'test_support_sync_notifier',
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_