From 973a446349b8b98f8ebb35da2ae204ee71047d0c Mon Sep 17 00:00:00 2001 From: "rlarocque@chromium.org" Date: Wed, 25 Jun 2014 05:52:32 +0000 Subject: sync: Mass rename of non-blocking sync classes Renames many of the classes involved in non-blocking sync: - Renames SyncCore, SyncCoreProxy to SyncContext and SyncContextProxy. - Renames NonBlockingTypeProcessor and NonBlockingTypeProcessorInterface to ModelTypeSyncProxyImpl and ModelTypeSyncProxy, respectively. - Renames NonBlockingTypeProcessorCore and NonBlockingTypeProcessorCoreInterface to ModelTypeSyncWorkerImpl and ModelTypeSyncWorker, respectively. - Renames ModelThreadSyncEntity to ModelTypeEntity. - Renames SyncThreadSyncEntity to EntityTracker. Renames any Mock, Test, Wrapper, or Impl classes associated with the above, too. This is only the first part of the planned refactoring. The second part, which involves some changes to the inheritance hierarchy, will be implemented in a future CL. BUG=351005 Review URL: https://codereview.chromium.org/351523003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@279618 0039d316-1c4b-4281-b951-d872f2087c98 --- sync/engine/entity_tracker.cc | 237 ++++++++ sync/engine/entity_tracker.h | 154 ++++++ sync/engine/entity_tracker_unittest.cc | 163 ++++++ sync/engine/model_thread_sync_entity.cc | 172 ------ sync/engine/model_thread_sync_entity.h | 192 ------- sync/engine/model_thread_sync_entity_unittest.cc | 178 ------ sync/engine/model_type_entity.cc | 170 ++++++ sync/engine/model_type_entity.h | 192 +++++++ sync/engine/model_type_entity_unittest.cc | 173 ++++++ sync/engine/model_type_sync_proxy.cc | 15 + sync/engine/model_type_sync_proxy.h | 29 + sync/engine/model_type_sync_proxy_impl.cc | 247 +++++++++ sync/engine/model_type_sync_proxy_impl.h | 137 +++++ sync/engine/model_type_sync_proxy_impl_unittest.cc | 463 ++++++++++++++++ sync/engine/model_type_sync_worker.cc | 15 + sync/engine/model_type_sync_worker.h | 23 + sync/engine/model_type_sync_worker_impl.cc | 284 ++++++++++ sync/engine/model_type_sync_worker_impl.h | 128 +++++ .../engine/model_type_sync_worker_impl_unittest.cc | 598 ++++++++++++++++++++ sync/engine/non_blocking_sync_common.h | 2 +- .../non_blocking_type_commit_contribution.cc | 8 +- .../engine/non_blocking_type_commit_contribution.h | 8 +- sync/engine/non_blocking_type_processor.cc | 250 --------- sync/engine/non_blocking_type_processor.h | 138 ----- sync/engine/non_blocking_type_processor_core.cc | 286 ---------- sync/engine/non_blocking_type_processor_core.h | 131 ----- .../non_blocking_type_processor_core_interface.cc | 16 - .../non_blocking_type_processor_core_interface.h | 24 - .../non_blocking_type_processor_core_unittest.cc | 604 --------------------- .../non_blocking_type_processor_interface.cc | 15 - .../engine/non_blocking_type_processor_interface.h | 28 - .../engine/non_blocking_type_processor_unittest.cc | 464 ---------------- sync/engine/sync_thread_sync_entity.cc | 242 --------- sync/engine/sync_thread_sync_entity.h | 155 ------ sync/engine/sync_thread_sync_entity_unittest.cc | 164 ------ sync/internal_api/public/sync_context_proxy.h | 45 ++ sync/internal_api/public/sync_core_proxy.h | 45 -- sync/internal_api/public/sync_manager.h | 6 +- sync/internal_api/public/test/fake_sync_manager.h | 6 +- .../public/test/null_sync_context_proxy.h | 33 ++ .../public/test/null_sync_core_proxy.h | 33 -- sync/internal_api/sync_context.cc | 39 ++ sync/internal_api/sync_context.h | 64 +++ sync/internal_api/sync_context_proxy.cc | 15 + sync/internal_api/sync_context_proxy_impl.cc | 48 ++ sync/internal_api/sync_context_proxy_impl.h | 60 ++ .../sync_context_proxy_impl_unittest.cc | 89 +++ sync/internal_api/sync_core.cc | 37 -- sync/internal_api/sync_core.h | 64 --- sync/internal_api/sync_core_proxy.cc | 13 - sync/internal_api/sync_core_proxy_impl.cc | 47 -- sync/internal_api/sync_core_proxy_impl.h | 60 -- sync/internal_api/sync_core_proxy_impl_unittest.cc | 93 ---- sync/internal_api/sync_manager_impl.cc | 22 +- sync/internal_api/sync_manager_impl.h | 10 +- sync/internal_api/sync_rollback_manager_base.cc | 2 +- sync/internal_api/sync_rollback_manager_base.h | 2 +- sync/internal_api/test/fake_sync_manager.cc | 4 +- sync/internal_api/test/null_sync_context_proxy.cc | 30 + sync/internal_api/test/null_sync_core_proxy.cc | 28 - sync/sessions/model_type_registry.cc | 124 +++-- sync/sessions/model_type_registry.h | 18 +- sync/sessions/model_type_registry_unittest.cc | 40 +- sync/sync_core.gypi | 34 +- sync/sync_internal_api.gypi | 16 +- sync/sync_tests.gypi | 26 +- sync/test/engine/injectable_sync_context_proxy.cc | 49 ++ sync/test/engine/injectable_sync_context_proxy.h | 48 ++ sync/test/engine/injectable_sync_core_proxy.cc | 51 -- sync/test/engine/injectable_sync_core_proxy.h | 47 -- sync/test/engine/mock_model_type_sync_proxy.cc | 253 +++++++++ sync/test/engine/mock_model_type_sync_proxy.h | 136 +++++ sync/test/engine/mock_model_type_sync_worker.cc | 171 ++++++ sync/test/engine/mock_model_type_sync_worker.h | 80 +++ .../engine/mock_non_blocking_type_processor.cc | 258 --------- .../test/engine/mock_non_blocking_type_processor.h | 136 ----- .../mock_non_blocking_type_processor_core.cc | 175 ------ .../engine/mock_non_blocking_type_processor_core.h | 82 --- 78 files changed, 4351 insertions(+), 4393 deletions(-) create mode 100644 sync/engine/entity_tracker.cc create mode 100644 sync/engine/entity_tracker.h create mode 100644 sync/engine/entity_tracker_unittest.cc delete mode 100644 sync/engine/model_thread_sync_entity.cc delete mode 100644 sync/engine/model_thread_sync_entity.h delete mode 100644 sync/engine/model_thread_sync_entity_unittest.cc create mode 100644 sync/engine/model_type_entity.cc create mode 100644 sync/engine/model_type_entity.h create mode 100644 sync/engine/model_type_entity_unittest.cc create mode 100644 sync/engine/model_type_sync_proxy.cc create mode 100644 sync/engine/model_type_sync_proxy.h create mode 100644 sync/engine/model_type_sync_proxy_impl.cc create mode 100644 sync/engine/model_type_sync_proxy_impl.h create mode 100644 sync/engine/model_type_sync_proxy_impl_unittest.cc create mode 100644 sync/engine/model_type_sync_worker.cc create mode 100644 sync/engine/model_type_sync_worker.h create mode 100644 sync/engine/model_type_sync_worker_impl.cc create mode 100644 sync/engine/model_type_sync_worker_impl.h create mode 100644 sync/engine/model_type_sync_worker_impl_unittest.cc delete mode 100644 sync/engine/non_blocking_type_processor.cc delete mode 100644 sync/engine/non_blocking_type_processor.h delete mode 100644 sync/engine/non_blocking_type_processor_core.cc delete mode 100644 sync/engine/non_blocking_type_processor_core.h delete mode 100644 sync/engine/non_blocking_type_processor_core_interface.cc delete mode 100644 sync/engine/non_blocking_type_processor_core_interface.h delete mode 100644 sync/engine/non_blocking_type_processor_core_unittest.cc delete mode 100644 sync/engine/non_blocking_type_processor_interface.cc delete mode 100644 sync/engine/non_blocking_type_processor_interface.h delete mode 100644 sync/engine/non_blocking_type_processor_unittest.cc delete mode 100644 sync/engine/sync_thread_sync_entity.cc delete mode 100644 sync/engine/sync_thread_sync_entity.h delete mode 100644 sync/engine/sync_thread_sync_entity_unittest.cc create mode 100644 sync/internal_api/public/sync_context_proxy.h delete mode 100644 sync/internal_api/public/sync_core_proxy.h create mode 100644 sync/internal_api/public/test/null_sync_context_proxy.h delete mode 100644 sync/internal_api/public/test/null_sync_core_proxy.h create mode 100644 sync/internal_api/sync_context.cc create mode 100644 sync/internal_api/sync_context.h create mode 100644 sync/internal_api/sync_context_proxy.cc create mode 100644 sync/internal_api/sync_context_proxy_impl.cc create mode 100644 sync/internal_api/sync_context_proxy_impl.h create mode 100644 sync/internal_api/sync_context_proxy_impl_unittest.cc delete mode 100644 sync/internal_api/sync_core.cc delete mode 100644 sync/internal_api/sync_core.h delete mode 100644 sync/internal_api/sync_core_proxy.cc delete mode 100644 sync/internal_api/sync_core_proxy_impl.cc delete mode 100644 sync/internal_api/sync_core_proxy_impl.h delete mode 100644 sync/internal_api/sync_core_proxy_impl_unittest.cc create mode 100644 sync/internal_api/test/null_sync_context_proxy.cc delete mode 100644 sync/internal_api/test/null_sync_core_proxy.cc create mode 100644 sync/test/engine/injectable_sync_context_proxy.cc create mode 100644 sync/test/engine/injectable_sync_context_proxy.h delete mode 100644 sync/test/engine/injectable_sync_core_proxy.cc delete mode 100644 sync/test/engine/injectable_sync_core_proxy.h create mode 100644 sync/test/engine/mock_model_type_sync_proxy.cc create mode 100644 sync/test/engine/mock_model_type_sync_proxy.h create mode 100644 sync/test/engine/mock_model_type_sync_worker.cc create mode 100644 sync/test/engine/mock_model_type_sync_worker.h delete mode 100644 sync/test/engine/mock_non_blocking_type_processor.cc delete mode 100644 sync/test/engine/mock_non_blocking_type_processor.h delete mode 100644 sync/test/engine/mock_non_blocking_type_processor_core.cc delete mode 100644 sync/test/engine/mock_non_blocking_type_processor_core.h (limited to 'sync') diff --git a/sync/engine/entity_tracker.cc b/sync/engine/entity_tracker.cc new file mode 100644 index 0000000..c9738b2 --- /dev/null +++ b/sync/engine/entity_tracker.cc @@ -0,0 +1,237 @@ +// 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/engine/entity_tracker.h" + +#include "base/logging.h" +#include "sync/engine/non_blocking_sync_common.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/syncable/syncable_util.h" +#include "sync/util/time.h" + +namespace syncer { + +EntityTracker* EntityTracker::FromServerUpdate( + const std::string& id_string, + const std::string& client_tag_hash, + int64 received_version) { + return new EntityTracker(id_string, client_tag_hash, 0, received_version); +} + +EntityTracker* EntityTracker::FromCommitRequest( + const std::string& id_string, + const std::string& client_tag_hash, + int64 sequence_number, + int64 base_version, + base::Time ctime, + base::Time mtime, + const std::string& non_unique_name, + bool deleted, + const sync_pb::EntitySpecifics& specifics) { + return new EntityTracker(id_string, + client_tag_hash, + 0, + 0, + true, + sequence_number, + base_version, + ctime, + mtime, + non_unique_name, + deleted, + specifics); +} + +// Constructor that does not set any pending commit fields. +EntityTracker::EntityTracker(const std::string& id, + const std::string& client_tag_hash, + int64 highest_commit_response_version, + int64 highest_gu_response_version) + : id_(id), + client_tag_hash_(client_tag_hash), + highest_commit_response_version_(highest_commit_response_version), + highest_gu_response_version_(highest_gu_response_version), + is_commit_pending_(false), + sequence_number_(0), + base_version_(0), + deleted_(false) { +} + +EntityTracker::EntityTracker(const std::string& id, + const std::string& client_tag_hash, + int64 highest_commit_response_version, + int64 highest_gu_response_version, + bool is_commit_pending, + int64 sequence_number, + int64 base_version, + base::Time ctime, + base::Time mtime, + const std::string& non_unique_name, + bool deleted, + const sync_pb::EntitySpecifics& specifics) + : id_(id), + client_tag_hash_(client_tag_hash), + highest_commit_response_version_(highest_commit_response_version), + highest_gu_response_version_(highest_gu_response_version), + is_commit_pending_(is_commit_pending), + sequence_number_(sequence_number), + base_version_(base_version), + ctime_(ctime), + mtime_(mtime), + non_unique_name_(non_unique_name), + deleted_(deleted), + specifics_(specifics) { +} + +EntityTracker::~EntityTracker() { +} + +bool EntityTracker::IsCommitPending() const { + return is_commit_pending_; +} + +void EntityTracker::PrepareCommitProto(sync_pb::SyncEntity* commit_entity, + int64* sequence_number) const { + // Set ID if we have a server-assigned ID. Otherwise, it will be up to + // our caller to assign a client-unique initial ID. + if (base_version_ != kUncommittedVersion) { + commit_entity->set_id_string(id_); + } + + commit_entity->set_client_defined_unique_tag(client_tag_hash_); + commit_entity->set_version(base_version_); + commit_entity->set_deleted(deleted_); + commit_entity->set_folder(false); + commit_entity->set_name(non_unique_name_); + if (!deleted_) { + commit_entity->set_ctime(TimeToProtoTime(ctime_)); + commit_entity->set_mtime(TimeToProtoTime(mtime_)); + commit_entity->mutable_specifics()->CopyFrom(specifics_); + } + + *sequence_number = sequence_number_; +} + +void EntityTracker::RequestCommit(const std::string& id, + const std::string& client_tag_hash, + int64 sequence_number, + int64 base_version, + base::Time ctime, + base::Time mtime, + const std::string& non_unique_name, + bool deleted, + const sync_pb::EntitySpecifics& specifics) { + DCHECK_GE(base_version, base_version_) + << "Base version should never decrease"; + + DCHECK_GE(sequence_number, sequence_number_) + << "Sequence number should never decrease"; + + // Update our book-keeping counters. + base_version_ = base_version; + sequence_number_ = sequence_number; + + // Do our counter values indicate a conflict? If so, don't commit. + // + // There's no need to inform the model thread of the conflict. The + // conflicting update has already been posted to its task runner; it will + // figure it out as soon as it runs that task. + is_commit_pending_ = true; + if (IsInConflict()) { + ClearPendingCommit(); + return; + } + + // We don't commit deletions of server-unknown items. + if (deleted && !IsServerKnown()) { + ClearPendingCommit(); + return; + } + + // Otherwise, we should store the data associated with this pending commit + // so we're ready to commit at the next possible opportunity. + + // We intentionally don't update the id_ here. Good ID values come from the + // server and always pass through the sync thread first. There's no way the + // model thread could have a better ID value than we do. + + // This entity is identified by its client tag. That value can never change. + DCHECK_EQ(client_tag_hash_, client_tag_hash); + + // Set the fields for the pending commit. + ctime_ = ctime; + mtime_ = mtime; + non_unique_name_ = non_unique_name; + deleted_ = deleted; + specifics_ = specifics; +} + +void EntityTracker::ReceiveCommitResponse(const std::string& response_id, + int64 response_version, + int64 sequence_number) { + // Commit responses, especially after the first commit, can update our ID. + id_ = response_id; + + DCHECK_GT(response_version, highest_commit_response_version_) + << "Had expected higher response version." + << " id: " << id_; + + // Commits are synchronous, so there's no reason why the sequence numbers + // wouldn't match. + DCHECK_EQ(sequence_number_, sequence_number) + << "Unexpected sequence number mismatch." + << " id: " << id_; + + highest_commit_response_version_ = response_version; + + // Because an in-progress commit blocks the sync thread, we can assume that + // the item we just committed successfully is exactly the one we have now. + // Nothing changed it while the commit was happening. Since we're now in + // sync with the server, we can clear the pending commit. + ClearPendingCommit(); +} + +void EntityTracker::ReceiveUpdate(int64 version) { + highest_gu_response_version_ = + std::max(highest_gu_response_version_, version); + + if (IsInConflict()) { + // Incoming update clobbers the pending commit on the sync thread. + // The model thread can re-request this commit later if it wants to. + ClearPendingCommit(); + } +} + +bool EntityTracker::IsInConflict() const { + if (!is_commit_pending_) + return false; + + if (highest_gu_response_version_ <= highest_commit_response_version_) { + // The most recent server state was created in a commit made by this + // client. We're fully up to date, and therefore not in conflict. + return false; + } else { + // The most recent server state was written by someone else. + // Did the model thread have the most up to date version when it issued the + // commit request? + if (base_version_ >= highest_gu_response_version_) { + return false; // Yes. + } else { + return true; // No. + } + } +} + +bool EntityTracker::IsServerKnown() const { + return base_version_ != kUncommittedVersion; +} + +void EntityTracker::ClearPendingCommit() { + is_commit_pending_ = false; + + // Clearing the specifics might free up some memory. It can't hurt to try. + specifics_.Clear(); +} + +} // namespace syncer diff --git a/sync/engine/entity_tracker.h b/sync/engine/entity_tracker.h new file mode 100644 index 0000000..db2d09b --- /dev/null +++ b/sync/engine/entity_tracker.h @@ -0,0 +1,154 @@ +// 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_ENGINE_ENTITY_TRACKER_H_ +#define SYNC_ENGINE_ENTITY_TRACKER_H_ + +#include + +#include "base/basictypes.h" +#include "base/time/time.h" +#include "sync/base/sync_export.h" +#include "sync/protocol/sync.pb.h" + +namespace syncer { + +// Manages the pending commit and update state for an entity on the sync +// thread. +// +// It should be considered a helper class internal to the +// ModelTypeSyncWorker. +// +// Maintains the state associated with a particular sync entity which is +// necessary for decision-making on the sync thread. It can track pending +// commit state, received update state, and can detect conflicts. +// +// This object may or may not contain state associated with a pending commit. +// If no commit is pending, the |is_commit_pending_| flag will be set to false +// and many of this object's fields will be cleared. +class SYNC_EXPORT EntityTracker { + public: + ~EntityTracker(); + + // Initialize a new entity based on an update response. + static EntityTracker* FromServerUpdate(const std::string& id_string, + const std::string& client_tag_hash, + int64 version); + + // Initialize a new entity based on a commit request. + static EntityTracker* FromCommitRequest( + const std::string& id_string, + const std::string& client_tag_hash, + int64 sequence_number, + int64 base_version, + base::Time ctime, + base::Time mtime, + const std::string& non_unique_name, + bool deleted, + const sync_pb::EntitySpecifics& specifics); + + // Returns true if this entity should be commited to the server. + bool IsCommitPending() const; + + // Populates a sync_pb::SyncEntity for a commit. Also sets the + // |sequence_number|, so we can track it throughout the commit process. + void PrepareCommitProto(sync_pb::SyncEntity* commit_entity, + int64* sequence_number) const; + + // Updates this entity with data from the latest version that the + // model asked us to commit. May clobber state related to the + // model's previous commit attempt(s). + void RequestCommit(const std::string& id, + const std::string& client_tag_hash, + int64 sequence_number, + int64 base_version, + base::Time ctime, + base::Time mtime, + const std::string& non_unique_name, + bool deleted, + const sync_pb::EntitySpecifics& specifics); + + // Handles the receipt of a commit response. + // + // Since commits happen entirely on the sync thread, we can safely assume + // that our item's state at the end of the commit is the same as it was at + // the start. + void ReceiveCommitResponse(const std::string& response_id, + int64 response_version, + int64 sequence_number); + + // Handles receipt of an update from the server. + void ReceiveUpdate(int64 version); + + private: + // Initializes received update state. Does not initialize state related to + // pending commits and sets |is_commit_pending_| to false. + EntityTracker(const std::string& id, + const std::string& client_tag_hash, + int64 highest_commit_response_version, + int64 highest_gu_response_version); + + // Initializes all fields. Sets |is_commit_pending_| to true. + EntityTracker(const std::string& id, + const std::string& client_tag_hash, + int64 highest_commit_response_version, + int64 highest_gu_response_version, + bool is_commit_pending, + int64 sequence_number, + int64 base_version, + base::Time ctime, + base::Time mtime, + const std::string& non_unique_name, + bool deleted, + const sync_pb::EntitySpecifics& specifics); + + // Checks if the current state indicates a conflict. + // + // This can be true only while a call to this object is in progress. + // Conflicts are always cleared before the method call ends. + bool IsInConflict() const; + + // Checks if the server knows about this item. + bool IsServerKnown() const; + + // Clears flag and optionally clears state associated with a pending commit. + void ClearPendingCommit(); + + // The ID for this entry. May be empty if the entry has never been committed. + std::string id_; + + // The hashed client tag for this entry. + std::string client_tag_hash_; + + // The highest version seen in a commit response for this entry. + int64 highest_commit_response_version_; + + // The highest version seen in a GU response for this entry. + int64 highest_gu_response_version_; + + // Flag that indicates whether or not we're waiting for a chance to commit + // this item. + bool is_commit_pending_; + + // Used to track in-flight commit requests on the model thread. All we need + // to do here is return it back to the model thread when the pending commit + // is completed and confirmed. Not valid if no commit is pending. + int64 sequence_number_; + + // The following fields are valid only when a commit is pending. + // This is where we store the data that is to be sent up to the server + // at the next possible opportunity. + int64 base_version_; + base::Time ctime_; + base::Time mtime_; + std::string non_unique_name_; + bool deleted_; + sync_pb::EntitySpecifics specifics_; + + DISALLOW_COPY_AND_ASSIGN(EntityTracker); +}; + +} // namespace syncer + +#endif // SYNC_ENGINE_ENTITY_TRACKER_H_ diff --git a/sync/engine/entity_tracker_unittest.cc b/sync/engine/entity_tracker_unittest.cc new file mode 100644 index 0000000..7a1b04b --- /dev/null +++ b/sync/engine/entity_tracker_unittest.cc @@ -0,0 +1,163 @@ + +// 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/engine/entity_tracker.h" + +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/syncable/syncable_util.h" +#include "sync/util/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncer { + +// Some simple tests for the EntityTracker. +// +// The EntityTracker is an implementation detail of the ModelTypeSyncWorker. +// As such, it doesn't make much sense to test it exhaustively, since it +// already gets a lot of test coverage from the ModelTypeSyncWorker unit tests. +// +// These tests are intended as a basic sanity check. Anything more complicated +// would be redundant. +class EntityTrackerTest : public ::testing::Test { + public: + EntityTrackerTest() + : kServerId("ServerID"), + kClientTag("some.sample.tag"), + kClientTagHash(syncable::GenerateSyncableHash(PREFERENCES, kClientTag)), + kCtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(10)), + kMtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(20)) { + specifics.mutable_preference()->set_name(kClientTag); + specifics.mutable_preference()->set_value("pref.value"); + } + + virtual ~EntityTrackerTest() {} + + const std::string kServerId; + const std::string kClientTag; + const std::string kClientTagHash; + const base::Time kCtime; + const base::Time kMtime; + sync_pb::EntitySpecifics specifics; +}; + +// Construct a new entity from a server update. Then receive another update. +TEST_F(EntityTrackerTest, FromServerUpdate) { + scoped_ptr entity( + EntityTracker::FromServerUpdate(kServerId, kClientTagHash, 10)); + EXPECT_FALSE(entity->IsCommitPending()); + + entity->ReceiveUpdate(20); + EXPECT_FALSE(entity->IsCommitPending()); +} + +// Construct a new entity from a commit request. Then serialize it. +TEST_F(EntityTrackerTest, FromCommitRequest) { + scoped_ptr entity( + EntityTracker::FromCommitRequest(kServerId, + kClientTagHash, + 22, + 33, + kCtime, + kMtime, + kClientTag, + false, + specifics)); + + ASSERT_TRUE(entity->IsCommitPending()); + sync_pb::SyncEntity pb_entity; + int64 sequence_number = 0; + entity->PrepareCommitProto(&pb_entity, &sequence_number); + EXPECT_EQ(22, sequence_number); + EXPECT_EQ(kServerId, pb_entity.id_string()); + EXPECT_EQ(kClientTagHash, pb_entity.client_defined_unique_tag()); + EXPECT_EQ(33, pb_entity.version()); + EXPECT_EQ(kCtime, ProtoTimeToTime(pb_entity.ctime())); + EXPECT_EQ(kMtime, ProtoTimeToTime(pb_entity.mtime())); + EXPECT_FALSE(pb_entity.deleted()); + EXPECT_EQ(specifics.preference().name(), + pb_entity.specifics().preference().name()); + EXPECT_EQ(specifics.preference().value(), + pb_entity.specifics().preference().value()); +} + +// Start with a server initiated entity. Commit over top of it. +TEST_F(EntityTrackerTest, RequestCommit) { + scoped_ptr entity( + EntityTracker::FromServerUpdate(kServerId, kClientTagHash, 10)); + + entity->RequestCommit(kServerId, + kClientTagHash, + 1, + 10, + kCtime, + kMtime, + kClientTag, + false, + specifics); + + EXPECT_TRUE(entity->IsCommitPending()); +} + +// Start with a server initiated entity. Fail to request a commit because of +// an out of date base version. +TEST_F(EntityTrackerTest, RequestCommitFailure) { + scoped_ptr entity( + EntityTracker::FromServerUpdate(kServerId, kClientTagHash, 10)); + EXPECT_FALSE(entity->IsCommitPending()); + + entity->RequestCommit(kServerId, + kClientTagHash, + 23, + 5, // Version 5 < 10 + kCtime, + kMtime, + kClientTag, + false, + specifics); + EXPECT_FALSE(entity->IsCommitPending()); +} + +// Start with a pending commit. Clobber it with an incoming update. +TEST_F(EntityTrackerTest, UpdateClobbersCommit) { + scoped_ptr entity( + EntityTracker::FromCommitRequest(kServerId, + kClientTagHash, + 22, + 33, + kCtime, + kMtime, + kClientTag, + false, + specifics)); + + EXPECT_TRUE(entity->IsCommitPending()); + + entity->ReceiveUpdate(400); // Version 400 > 33. + EXPECT_FALSE(entity->IsCommitPending()); +} + +// Start with a pending commit. Send it a reflected update that +// will not override the in-progress commit. +TEST_F(EntityTrackerTest, ReflectedUpdateDoesntClobberCommit) { + scoped_ptr entity( + EntityTracker::FromCommitRequest(kServerId, + kClientTagHash, + 22, + 33, + kCtime, + kMtime, + kClientTag, + false, + specifics)); + + EXPECT_TRUE(entity->IsCommitPending()); + + entity->ReceiveUpdate(33); // Version 33 == 33. + EXPECT_TRUE(entity->IsCommitPending()); +} + +} // namespace syncer diff --git a/sync/engine/model_thread_sync_entity.cc b/sync/engine/model_thread_sync_entity.cc deleted file mode 100644 index 053a6c8..0000000 --- a/sync/engine/model_thread_sync_entity.cc +++ /dev/null @@ -1,172 +0,0 @@ -// 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/engine/model_thread_sync_entity.h" -#include "sync/syncable/syncable_util.h" - -namespace syncer { - -scoped_ptr ModelThreadSyncEntity::NewLocalItem( - const std::string& client_tag, - const sync_pb::EntitySpecifics& specifics, - base::Time now) { - return scoped_ptr(new ModelThreadSyncEntity( - 1, - 0, - 0, - kUncommittedVersion, - true, - std::string(), // Sync thread will assign the initial ID. - syncable::GenerateSyncableHash(GetModelTypeFromSpecifics(specifics), - client_tag), - client_tag, // As non-unique name. - specifics, - false, - now, - now)); -} - -scoped_ptr ModelThreadSyncEntity::FromServerUpdate( - const std::string& id, - const std::string& client_tag_hash, - const std::string& non_unique_name, - int64 version, - const sync_pb::EntitySpecifics& specifics, - bool deleted, - base::Time ctime, - base::Time mtime) { - return scoped_ptr( - new ModelThreadSyncEntity(0, - 0, - 0, - version, - true, - id, - client_tag_hash, - non_unique_name, - specifics, - deleted, - ctime, - mtime)); -} - -ModelThreadSyncEntity::ModelThreadSyncEntity( - int64 sequence_number, - int64 commit_requested_sequence_number, - int64 acked_sequence_number, - int64 base_version, - bool is_dirty, - const std::string& id, - const std::string& client_tag_hash, - const std::string& non_unique_name, - const sync_pb::EntitySpecifics& specifics, - bool deleted, - base::Time ctime, - base::Time mtime) - : sequence_number_(sequence_number), - commit_requested_sequence_number_(commit_requested_sequence_number), - acked_sequence_number_(acked_sequence_number), - base_version_(base_version), - is_dirty_(is_dirty), - id_(id), - client_tag_hash_(client_tag_hash), - non_unique_name_(non_unique_name), - specifics_(specifics), - deleted_(deleted), - ctime_(ctime), - mtime_(mtime) { -} - -ModelThreadSyncEntity::~ModelThreadSyncEntity() { -} - -bool ModelThreadSyncEntity::IsWriteRequired() const { - return is_dirty_; -} - -bool ModelThreadSyncEntity::IsUnsynced() const { - return sequence_number_ > acked_sequence_number_; -} - -bool ModelThreadSyncEntity::RequiresCommitRequest() const { - return sequence_number_ > commit_requested_sequence_number_; -} - -bool ModelThreadSyncEntity::UpdateIsReflection(int64 update_version) const { - return base_version_ >= update_version; -} - -bool ModelThreadSyncEntity::UpdateIsInConflict(int64 update_version) const { - return IsUnsynced() && !UpdateIsReflection(update_version); -} - -void ModelThreadSyncEntity::ApplyUpdateFromServer( - int64 update_version, - bool deleted, - const sync_pb::EntitySpecifics& specifics, - base::Time mtime) { - // There was a conflict and the server just won it. - // This implicitly acks all outstanding commits because a received update - // will clobber any pending commits on the sync thread. - acked_sequence_number_ = sequence_number_; - commit_requested_sequence_number_ = sequence_number_; - - base_version_ = update_version; - specifics_ = specifics; - mtime_ = mtime; -} - -void ModelThreadSyncEntity::MakeLocalChange( - const sync_pb::EntitySpecifics& specifics) { - sequence_number_++; - specifics_ = specifics; -} - -void ModelThreadSyncEntity::Delete() { - sequence_number_++; - specifics_.Clear(); - deleted_ = true; -} - -void ModelThreadSyncEntity::InitializeCommitRequestData( - CommitRequestData* request) const { - request->id = id_; - request->client_tag_hash = client_tag_hash_; - request->sequence_number = sequence_number_; - request->base_version = base_version_; - request->ctime = ctime_; - request->mtime = mtime_; - request->non_unique_name = non_unique_name_; - request->deleted = deleted_; - request->specifics.CopyFrom(specifics_); -} - -void ModelThreadSyncEntity::SetCommitRequestInProgress() { - commit_requested_sequence_number_ = sequence_number_; -} - -void ModelThreadSyncEntity::ReceiveCommitResponse(const std::string& id, - int64 sequence_number, - int64 response_version) { - id_ = id; // The server can assign us a new ID in a commit response. - acked_sequence_number_ = sequence_number; - base_version_ = response_version; -} - -void ModelThreadSyncEntity::ClearTransientSyncState() { - // If we have any unacknowledged commit requests outstatnding, they've been - // dropped and we should forget about them. - commit_requested_sequence_number_ = acked_sequence_number_; -} - -void ModelThreadSyncEntity::ClearSyncState() { - base_version_ = kUncommittedVersion; - is_dirty_ = true; - sequence_number_ = 1; - commit_requested_sequence_number_ = 0; - acked_sequence_number_ = 0; - id_.clear(); -} - -} // namespace syncer diff --git a/sync/engine/model_thread_sync_entity.h b/sync/engine/model_thread_sync_entity.h deleted file mode 100644 index 03a6d27..0000000 --- a/sync/engine/model_thread_sync_entity.h +++ /dev/null @@ -1,192 +0,0 @@ -// 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_ENGINE_MODEL_THREAD_SYNC_ENTITY_H_ -#define SYNC_ENGINE_MODEL_THREAD_SYNC_ENTITY_H_ - -#include - -#include "base/memory/scoped_ptr.h" -#include "base/time/time.h" -#include "sync/base/sync_export.h" -#include "sync/engine/non_blocking_sync_common.h" -#include "sync/protocol/sync.pb.h" - -namespace syncer { - -// This is the model thread's representation of a SyncEntity. -// -// The model thread sync entity receives updates from the model itself and -// (asynchronously) from the sync server via the sync thread. From the point -// of view of this class, updates from the server take precedence over local -// changes, though the model may be given an opportunity to overwrite this -// decision later. -// -// Sync will try to commit this entity's data to the sync server and local -// storage. -// -// Most of the logic related to those processes live outside this class. This -// class helps out a bit by offering some functions to serialize its data to -// various formats and query the entity's status. -class SYNC_EXPORT_PRIVATE ModelThreadSyncEntity { - public: - // Construct an instance representing a new locally-created item. - static scoped_ptr NewLocalItem( - const std::string& client_tag, - const sync_pb::EntitySpecifics& specifics, - base::Time now); - - // Construct an instance representing an item newly received from the server. - static scoped_ptr FromServerUpdate( - const std::string& id, - const std::string& client_tag_hash, - const std::string& non_unique_name, - int64 version, - const sync_pb::EntitySpecifics& specifics, - bool deleted, - base::Time ctime, - base::Time mtime); - - // TODO(rlarocque): Implement FromDisk constructor when we implement storage. - - ~ModelThreadSyncEntity(); - - // Returns true if this data is out of sync with local storage. - bool IsWriteRequired() const; - - // Returns true if this data is out of sync with the server. - // A commit may or may not be in progress at this time. - bool IsUnsynced() const; - - // Returns true if this data is out of sync with the sync thread. - // - // There may or may not be a commit in progress for this item, but there's - // definitely no commit in progress for this (most up to date) version of - // this item. - bool RequiresCommitRequest() const; - - // Returns true if the specified update version does not contain new data. - bool UpdateIsReflection(int64 update_version) const; - - // Returns true if the specified update version conflicts with local changes. - bool UpdateIsInConflict(int64 update_version) const; - - // Applies an update from the sync server. - // - // Overrides any local changes. Check UpdateIsInConflict() before calling - // this function if you want to handle conflicts differently. - void ApplyUpdateFromServer(int64 update_version, - bool deleted, - const sync_pb::EntitySpecifics& specifics, - base::Time mtime); - - // Applies a local change to this item. - void MakeLocalChange(const sync_pb::EntitySpecifics& specifics); - - // Applies a local deletion to this item. - void Delete(); - - // Initializes a message representing this item's uncommitted state - // to be forwarded to the sync server for committing. - void InitializeCommitRequestData(CommitRequestData* request) const; - - // Notes that the current version of this item has been queued for commit. - void SetCommitRequestInProgress(); - - // Receives a successful commit response. - // - // Sucssful commit responses can overwrite an item's ID. - // - // Note that the receipt of a successful commit response does not necessarily - // unset IsUnsynced(). If many local changes occur in quick succession, it's - // possible that the committed item was already out of date by the time it - // reached the server. - void ReceiveCommitResponse(const std::string& id, - int64 sequence_number, - int64 response_version); - - // Clears any in-memory sync state associated with outstanding commits. - void ClearTransientSyncState(); - - // Clears all sync state. Invoked when a user signs out. - void ClearSyncState(); - - private: - ModelThreadSyncEntity(int64 sequence_number, - int64 commit_requested_sequence_number, - int64 acked_sequence_number, - int64 base_version, - bool is_dirty, - const std::string& id, - const std::string& client_tag_hash, - const std::string& non_unique_name, - const sync_pb::EntitySpecifics& specifics, - bool deleted, - base::Time ctime, - base::Time mtime); - - // A sequence number used to track in-progress commits. Each local change - // increments this number. - int64 sequence_number_; - - // The sequence number of the last item sent to the sync thread. - int64 commit_requested_sequence_number_; - - // The sequence number of the last item known to be successfully committed. - int64 acked_sequence_number_; - - // The server version on which this item is based. - // - // If there are no local changes, this is the version of the entity as we see - // it here. - // - // If there are local changes, this is the version of the entity on which - // those changes are based. - int64 base_version_; - - // True if this entity is out of sync with local storage. - bool is_dirty_; - - // The entity's ID. - // - // Most of the time, this is a server-assigned value. - // - // Prior to the item's first commit, we leave this value as an empty string. - // The initial ID for a newly created item has to meet certain uniqueness - // requirements, and we handle those on the sync thread. - std::string id_; - - // A hash based on the client tag and model type. - // Used for various map lookups. Should always be available. - std::string client_tag_hash_; - - // A non-unique name associated with this entity. - // - // It is sometimes used for debugging. It gets saved to and restored from - // the sync server. - // - // Its value is often related to the item's unhashed client tag, though this - // is not guaranteed and should not be relied on. May be hidden when - // encryption is enabled. - std::string non_unique_name_; - - // A protobuf filled with type-specific information. Contains the most - // up-to-date specifics, whether it be from the server or a locally modified - // version. - sync_pb::EntitySpecifics specifics_; - - // Whether or not the item is deleted. The |specifics_| field may be empty - // if this flag is true. - bool deleted_; - - // Entity creation and modification timestamps. - // Assigned by the client and synced by the server, though the server usually - // doesn't bother to inspect their values. - base::Time ctime_; - base::Time mtime_; -}; - -} // namespace syncer - -#endif // SYNC_ENGINE_MODEL_THREAD_SYNC_ENTITY_H_ diff --git a/sync/engine/model_thread_sync_entity_unittest.cc b/sync/engine/model_thread_sync_entity_unittest.cc deleted file mode 100644 index 6b158dd..0000000 --- a/sync/engine/model_thread_sync_entity_unittest.cc +++ /dev/null @@ -1,178 +0,0 @@ -// 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/engine/model_thread_sync_entity.h" - -#include "base/memory/scoped_ptr.h" -#include "base/time/time.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/protocol/sync.pb.h" -#include "sync/syncable/syncable_util.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace syncer { - -// Some simple sanity tests for the ModelThreadSyncEntity. -// -// A lot of the more complicated sync logic is implemented in the -// NonBlockingTypeProcessor that owns the ModelThreadSyncEntity. We -// can't unit test it here. -// -// Instead, we focus on simple tests to make sure that variables are getting -// properly intialized and flags properly set. Anything more complicated would -// be a redundant and incomplete version of the NonBlockingTypeProcessor tests. -class ModelThreadSyncEntityTest : public ::testing::Test { - public: - ModelThreadSyncEntityTest() - : kServerId("ServerID"), - kClientTag("sample.pref.name"), - kClientTagHash(syncable::GenerateSyncableHash(PREFERENCES, kClientTag)), - kCtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(10)), - kMtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(20)) { - sync_pb::PreferenceSpecifics* pref_specifics = - specifics.mutable_preference(); - pref_specifics->set_name(kClientTag); - pref_specifics->set_value("pref.value"); - } - - const std::string kServerId; - const std::string kClientTag; - const std::string kClientTagHash; - const base::Time kCtime; - const base::Time kMtime; - sync_pb::EntitySpecifics specifics; -}; - -TEST_F(ModelThreadSyncEntityTest, NewLocalItem) { - scoped_ptr entity( - ModelThreadSyncEntity::NewLocalItem("asdf", specifics, kCtime)); - - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_TRUE(entity->IsUnsynced()); - EXPECT_FALSE(entity->UpdateIsReflection(1)); - EXPECT_TRUE(entity->UpdateIsInConflict(1)); -} - -TEST_F(ModelThreadSyncEntityTest, FromServerUpdate) { - scoped_ptr entity( - ModelThreadSyncEntity::FromServerUpdate( - kServerId, - kClientTagHash, - kClientTag, // As non-unique name. - 10, - specifics, - false, - kCtime, - kMtime)); - - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_FALSE(entity->IsUnsynced()); - EXPECT_TRUE(entity->UpdateIsReflection(9)); - EXPECT_TRUE(entity->UpdateIsReflection(10)); - EXPECT_FALSE(entity->UpdateIsReflection(11)); - EXPECT_FALSE(entity->UpdateIsInConflict(11)); -} - -// Tombstones should behave just like regular updates. Mostly. The strangest -// thing about them is that they don't have specifics, so it can be hard to -// detect their type. Fortunately, this class doesn't care about types in -// received updates. -TEST_F(ModelThreadSyncEntityTest, TombstoneUpdate) { - scoped_ptr entity( - ModelThreadSyncEntity::FromServerUpdate( - kServerId, - kClientTagHash, - kClientTag, // As non-unique name. - 10, - sync_pb::EntitySpecifics(), - true, - kCtime, - kMtime)); - - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_FALSE(entity->IsUnsynced()); - EXPECT_TRUE(entity->UpdateIsReflection(9)); - EXPECT_TRUE(entity->UpdateIsReflection(10)); - EXPECT_FALSE(entity->UpdateIsReflection(11)); - EXPECT_FALSE(entity->UpdateIsInConflict(11)); -} - -// Apply a deletion update. -TEST_F(ModelThreadSyncEntityTest, ApplyUpdate) { - scoped_ptr entity( - ModelThreadSyncEntity::FromServerUpdate( - kServerId, - kClientTagHash, - kClientTag, // As non-unique name. - 10, - specifics, - false, - kCtime, - kMtime)); - - // A deletion update one version later. - entity->ApplyUpdateFromServer(11, - true, - sync_pb::EntitySpecifics(), - kMtime + base::TimeDelta::FromSeconds(10)); - - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_FALSE(entity->IsUnsynced()); - EXPECT_TRUE(entity->UpdateIsReflection(11)); - EXPECT_FALSE(entity->UpdateIsReflection(12)); -} - -TEST_F(ModelThreadSyncEntityTest, LocalChange) { - scoped_ptr entity( - ModelThreadSyncEntity::FromServerUpdate( - kServerId, - kClientTagHash, - kClientTag, // As non-unique name. - 10, - specifics, - false, - kCtime, - kMtime)); - - sync_pb::EntitySpecifics specifics2; - specifics2.CopyFrom(specifics); - specifics2.mutable_preference()->set_value("new.pref.value"); - - entity->MakeLocalChange(specifics2); - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_TRUE(entity->IsUnsynced()); - - EXPECT_TRUE(entity->UpdateIsReflection(10)); - EXPECT_FALSE(entity->UpdateIsInConflict(10)); - - EXPECT_FALSE(entity->UpdateIsReflection(11)); - EXPECT_TRUE(entity->UpdateIsInConflict(11)); -} - -TEST_F(ModelThreadSyncEntityTest, LocalDeletion) { - scoped_ptr entity( - ModelThreadSyncEntity::FromServerUpdate( - kServerId, - kClientTagHash, - kClientTag, // As non-unique name. - 10, - specifics, - false, - kCtime, - kMtime)); - - entity->Delete(); - - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_TRUE(entity->IsUnsynced()); - - EXPECT_TRUE(entity->UpdateIsReflection(10)); - EXPECT_FALSE(entity->UpdateIsInConflict(10)); - - EXPECT_FALSE(entity->UpdateIsReflection(11)); - EXPECT_TRUE(entity->UpdateIsInConflict(11)); -} - -} // namespace syncer diff --git a/sync/engine/model_type_entity.cc b/sync/engine/model_type_entity.cc new file mode 100644 index 0000000..5acf5db --- /dev/null +++ b/sync/engine/model_type_entity.cc @@ -0,0 +1,170 @@ +// 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/engine/model_type_entity.h" +#include "sync/syncable/syncable_util.h" + +namespace syncer { + +scoped_ptr ModelTypeEntity::NewLocalItem( + const std::string& client_tag, + const sync_pb::EntitySpecifics& specifics, + base::Time now) { + return scoped_ptr(new ModelTypeEntity( + 1, + 0, + 0, + kUncommittedVersion, + true, + std::string(), // Sync thread will assign the initial ID. + syncable::GenerateSyncableHash(GetModelTypeFromSpecifics(specifics), + client_tag), + client_tag, // As non-unique name. + specifics, + false, + now, + now)); +} + +scoped_ptr ModelTypeEntity::FromServerUpdate( + const std::string& id, + const std::string& client_tag_hash, + const std::string& non_unique_name, + int64 version, + const sync_pb::EntitySpecifics& specifics, + bool deleted, + base::Time ctime, + base::Time mtime) { + return scoped_ptr(new ModelTypeEntity(0, + 0, + 0, + version, + true, + id, + client_tag_hash, + non_unique_name, + specifics, + deleted, + ctime, + mtime)); +} + +ModelTypeEntity::ModelTypeEntity(int64 sequence_number, + int64 commit_requested_sequence_number, + int64 acked_sequence_number, + int64 base_version, + bool is_dirty, + const std::string& id, + const std::string& client_tag_hash, + const std::string& non_unique_name, + const sync_pb::EntitySpecifics& specifics, + bool deleted, + base::Time ctime, + base::Time mtime) + : sequence_number_(sequence_number), + commit_requested_sequence_number_(commit_requested_sequence_number), + acked_sequence_number_(acked_sequence_number), + base_version_(base_version), + is_dirty_(is_dirty), + id_(id), + client_tag_hash_(client_tag_hash), + non_unique_name_(non_unique_name), + specifics_(specifics), + deleted_(deleted), + ctime_(ctime), + mtime_(mtime) { +} + +ModelTypeEntity::~ModelTypeEntity() { +} + +bool ModelTypeEntity::IsWriteRequired() const { + return is_dirty_; +} + +bool ModelTypeEntity::IsUnsynced() const { + return sequence_number_ > acked_sequence_number_; +} + +bool ModelTypeEntity::RequiresCommitRequest() const { + return sequence_number_ > commit_requested_sequence_number_; +} + +bool ModelTypeEntity::UpdateIsReflection(int64 update_version) const { + return base_version_ >= update_version; +} + +bool ModelTypeEntity::UpdateIsInConflict(int64 update_version) const { + return IsUnsynced() && !UpdateIsReflection(update_version); +} + +void ModelTypeEntity::ApplyUpdateFromServer( + int64 update_version, + bool deleted, + const sync_pb::EntitySpecifics& specifics, + base::Time mtime) { + // There was a conflict and the server just won it. + // This implicitly acks all outstanding commits because a received update + // will clobber any pending commits on the sync thread. + acked_sequence_number_ = sequence_number_; + commit_requested_sequence_number_ = sequence_number_; + + base_version_ = update_version; + specifics_ = specifics; + mtime_ = mtime; +} + +void ModelTypeEntity::MakeLocalChange( + const sync_pb::EntitySpecifics& specifics) { + sequence_number_++; + specifics_ = specifics; +} + +void ModelTypeEntity::Delete() { + sequence_number_++; + specifics_.Clear(); + deleted_ = true; +} + +void ModelTypeEntity::InitializeCommitRequestData( + CommitRequestData* request) const { + request->id = id_; + request->client_tag_hash = client_tag_hash_; + request->sequence_number = sequence_number_; + request->base_version = base_version_; + request->ctime = ctime_; + request->mtime = mtime_; + request->non_unique_name = non_unique_name_; + request->deleted = deleted_; + request->specifics.CopyFrom(specifics_); +} + +void ModelTypeEntity::SetCommitRequestInProgress() { + commit_requested_sequence_number_ = sequence_number_; +} + +void ModelTypeEntity::ReceiveCommitResponse(const std::string& id, + int64 sequence_number, + int64 response_version) { + id_ = id; // The server can assign us a new ID in a commit response. + acked_sequence_number_ = sequence_number; + base_version_ = response_version; +} + +void ModelTypeEntity::ClearTransientSyncState() { + // If we have any unacknowledged commit requests outstatnding, they've been + // dropped and we should forget about them. + commit_requested_sequence_number_ = acked_sequence_number_; +} + +void ModelTypeEntity::ClearSyncState() { + base_version_ = kUncommittedVersion; + is_dirty_ = true; + sequence_number_ = 1; + commit_requested_sequence_number_ = 0; + acked_sequence_number_ = 0; + id_.clear(); +} + +} // namespace syncer diff --git a/sync/engine/model_type_entity.h b/sync/engine/model_type_entity.h new file mode 100644 index 0000000..a457cb3 --- /dev/null +++ b/sync/engine/model_type_entity.h @@ -0,0 +1,192 @@ +// 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_ENGINE_MODEL_TYPE_ENTITY_H_ +#define SYNC_ENGINE_MODEL_TYPE_ENTITY_H_ + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "sync/base/sync_export.h" +#include "sync/engine/non_blocking_sync_common.h" +#include "sync/protocol/sync.pb.h" + +namespace syncer { + +// This is the model thread's representation of a SyncEntity. +// +// The model type entity receives updates from the model itself and +// (asynchronously) from the sync server via the sync thread. From the point +// of view of this class, updates from the server take precedence over local +// changes, though the model may be given an opportunity to overwrite this +// decision later. +// +// Sync will try to commit this entity's data to the sync server and local +// storage. +// +// Most of the logic related to those processes live outside this class. This +// class helps out a bit by offering some functions to serialize its data to +// various formats and query the entity's status. +class SYNC_EXPORT_PRIVATE ModelTypeEntity { + public: + // Construct an instance representing a new locally-created item. + static scoped_ptr NewLocalItem( + const std::string& client_tag, + const sync_pb::EntitySpecifics& specifics, + base::Time now); + + // Construct an instance representing an item newly received from the server. + static scoped_ptr FromServerUpdate( + const std::string& id, + const std::string& client_tag_hash, + const std::string& non_unique_name, + int64 version, + const sync_pb::EntitySpecifics& specifics, + bool deleted, + base::Time ctime, + base::Time mtime); + + // TODO(rlarocque): Implement FromDisk constructor when we implement storage. + + ~ModelTypeEntity(); + + // Returns true if this data is out of sync with local storage. + bool IsWriteRequired() const; + + // Returns true if this data is out of sync with the server. + // A commit may or may not be in progress at this time. + bool IsUnsynced() const; + + // Returns true if this data is out of sync with the sync thread. + // + // There may or may not be a commit in progress for this item, but there's + // definitely no commit in progress for this (most up to date) version of + // this item. + bool RequiresCommitRequest() const; + + // Returns true if the specified update version does not contain new data. + bool UpdateIsReflection(int64 update_version) const; + + // Returns true if the specified update version conflicts with local changes. + bool UpdateIsInConflict(int64 update_version) const; + + // Applies an update from the sync server. + // + // Overrides any local changes. Check UpdateIsInConflict() before calling + // this function if you want to handle conflicts differently. + void ApplyUpdateFromServer(int64 update_version, + bool deleted, + const sync_pb::EntitySpecifics& specifics, + base::Time mtime); + + // Applies a local change to this item. + void MakeLocalChange(const sync_pb::EntitySpecifics& specifics); + + // Applies a local deletion to this item. + void Delete(); + + // Initializes a message representing this item's uncommitted state + // to be forwarded to the sync server for committing. + void InitializeCommitRequestData(CommitRequestData* request) const; + + // Notes that the current version of this item has been queued for commit. + void SetCommitRequestInProgress(); + + // Receives a successful commit response. + // + // Sucssful commit responses can overwrite an item's ID. + // + // Note that the receipt of a successful commit response does not necessarily + // unset IsUnsynced(). If many local changes occur in quick succession, it's + // possible that the committed item was already out of date by the time it + // reached the server. + void ReceiveCommitResponse(const std::string& id, + int64 sequence_number, + int64 response_version); + + // Clears any in-memory sync state associated with outstanding commits. + void ClearTransientSyncState(); + + // Clears all sync state. Invoked when a user signs out. + void ClearSyncState(); + + private: + ModelTypeEntity(int64 sequence_number, + int64 commit_requested_sequence_number, + int64 acked_sequence_number, + int64 base_version, + bool is_dirty, + const std::string& id, + const std::string& client_tag_hash, + const std::string& non_unique_name, + const sync_pb::EntitySpecifics& specifics, + bool deleted, + base::Time ctime, + base::Time mtime); + + // A sequence number used to track in-progress commits. Each local change + // increments this number. + int64 sequence_number_; + + // The sequence number of the last item sent to the sync thread. + int64 commit_requested_sequence_number_; + + // The sequence number of the last item known to be successfully committed. + int64 acked_sequence_number_; + + // The server version on which this item is based. + // + // If there are no local changes, this is the version of the entity as we see + // it here. + // + // If there are local changes, this is the version of the entity on which + // those changes are based. + int64 base_version_; + + // True if this entity is out of sync with local storage. + bool is_dirty_; + + // The entity's ID. + // + // Most of the time, this is a server-assigned value. + // + // Prior to the item's first commit, we leave this value as an empty string. + // The initial ID for a newly created item has to meet certain uniqueness + // requirements, and we handle those on the sync thread. + std::string id_; + + // A hash based on the client tag and model type. + // Used for various map lookups. Should always be available. + std::string client_tag_hash_; + + // A non-unique name associated with this entity. + // + // It is sometimes used for debugging. It gets saved to and restored from + // the sync server. + // + // Its value is often related to the item's unhashed client tag, though this + // is not guaranteed and should not be relied on. May be hidden when + // encryption is enabled. + std::string non_unique_name_; + + // A protobuf filled with type-specific information. Contains the most + // up-to-date specifics, whether it be from the server or a locally modified + // version. + sync_pb::EntitySpecifics specifics_; + + // Whether or not the item is deleted. The |specifics_| field may be empty + // if this flag is true. + bool deleted_; + + // Entity creation and modification timestamps. + // Assigned by the client and synced by the server, though the server usually + // doesn't bother to inspect their values. + base::Time ctime_; + base::Time mtime_; +}; + +} // namespace syncer + +#endif // SYNC_ENGINE_MODEL_TYPE_ENTITY_H_ diff --git a/sync/engine/model_type_entity_unittest.cc b/sync/engine/model_type_entity_unittest.cc new file mode 100644 index 0000000..c605732 --- /dev/null +++ b/sync/engine/model_type_entity_unittest.cc @@ -0,0 +1,173 @@ +// 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/engine/model_type_entity.h" + +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/protocol/sync.pb.h" +#include "sync/syncable/syncable_util.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncer { + +// Some simple sanity tests for the ModelTypeEntity. +// +// A lot of the more complicated sync logic is implemented in the +// ModelTypeSyncProxyImpl that owns the ModelTypeEntity. We can't unit test it +// here. +// +// Instead, we focus on simple tests to make sure that variables are getting +// properly intialized and flags properly set. Anything more complicated would +// be a redundant and incomplete version of the ModelTypeSyncProxyImpl tests. +class ModelTypeEntityTest : public ::testing::Test { + public: + ModelTypeEntityTest() + : kServerId("ServerID"), + kClientTag("sample.pref.name"), + kClientTagHash(syncable::GenerateSyncableHash(PREFERENCES, kClientTag)), + kCtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(10)), + kMtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(20)) { + sync_pb::PreferenceSpecifics* pref_specifics = + specifics.mutable_preference(); + pref_specifics->set_name(kClientTag); + pref_specifics->set_value("pref.value"); + } + + const std::string kServerId; + const std::string kClientTag; + const std::string kClientTagHash; + const base::Time kCtime; + const base::Time kMtime; + sync_pb::EntitySpecifics specifics; +}; + +TEST_F(ModelTypeEntityTest, NewLocalItem) { + scoped_ptr entity( + ModelTypeEntity::NewLocalItem("asdf", specifics, kCtime)); + + EXPECT_TRUE(entity->IsWriteRequired()); + EXPECT_TRUE(entity->IsUnsynced()); + EXPECT_FALSE(entity->UpdateIsReflection(1)); + EXPECT_TRUE(entity->UpdateIsInConflict(1)); +} + +TEST_F(ModelTypeEntityTest, FromServerUpdate) { + scoped_ptr entity( + ModelTypeEntity::FromServerUpdate(kServerId, + kClientTagHash, + kClientTag, // As non-unique name. + 10, + specifics, + false, + kCtime, + kMtime)); + + EXPECT_TRUE(entity->IsWriteRequired()); + EXPECT_FALSE(entity->IsUnsynced()); + EXPECT_TRUE(entity->UpdateIsReflection(9)); + EXPECT_TRUE(entity->UpdateIsReflection(10)); + EXPECT_FALSE(entity->UpdateIsReflection(11)); + EXPECT_FALSE(entity->UpdateIsInConflict(11)); +} + +// Tombstones should behave just like regular updates. Mostly. The strangest +// thing about them is that they don't have specifics, so it can be hard to +// detect their type. Fortunately, this class doesn't care about types in +// received updates. +TEST_F(ModelTypeEntityTest, TombstoneUpdate) { + scoped_ptr entity( + ModelTypeEntity::FromServerUpdate(kServerId, + kClientTagHash, + kClientTag, // As non-unique name. + 10, + sync_pb::EntitySpecifics(), + true, + kCtime, + kMtime)); + + EXPECT_TRUE(entity->IsWriteRequired()); + EXPECT_FALSE(entity->IsUnsynced()); + EXPECT_TRUE(entity->UpdateIsReflection(9)); + EXPECT_TRUE(entity->UpdateIsReflection(10)); + EXPECT_FALSE(entity->UpdateIsReflection(11)); + EXPECT_FALSE(entity->UpdateIsInConflict(11)); +} + +// Apply a deletion update. +TEST_F(ModelTypeEntityTest, ApplyUpdate) { + scoped_ptr entity( + ModelTypeEntity::FromServerUpdate(kServerId, + kClientTagHash, + kClientTag, // As non-unique name. + 10, + specifics, + false, + kCtime, + kMtime)); + + // A deletion update one version later. + entity->ApplyUpdateFromServer(11, + true, + sync_pb::EntitySpecifics(), + kMtime + base::TimeDelta::FromSeconds(10)); + + EXPECT_TRUE(entity->IsWriteRequired()); + EXPECT_FALSE(entity->IsUnsynced()); + EXPECT_TRUE(entity->UpdateIsReflection(11)); + EXPECT_FALSE(entity->UpdateIsReflection(12)); +} + +TEST_F(ModelTypeEntityTest, LocalChange) { + scoped_ptr entity( + ModelTypeEntity::FromServerUpdate(kServerId, + kClientTagHash, + kClientTag, // As non-unique name. + 10, + specifics, + false, + kCtime, + kMtime)); + + sync_pb::EntitySpecifics specifics2; + specifics2.CopyFrom(specifics); + specifics2.mutable_preference()->set_value("new.pref.value"); + + entity->MakeLocalChange(specifics2); + EXPECT_TRUE(entity->IsWriteRequired()); + EXPECT_TRUE(entity->IsUnsynced()); + + EXPECT_TRUE(entity->UpdateIsReflection(10)); + EXPECT_FALSE(entity->UpdateIsInConflict(10)); + + EXPECT_FALSE(entity->UpdateIsReflection(11)); + EXPECT_TRUE(entity->UpdateIsInConflict(11)); +} + +TEST_F(ModelTypeEntityTest, LocalDeletion) { + scoped_ptr entity( + ModelTypeEntity::FromServerUpdate(kServerId, + kClientTagHash, + kClientTag, // As non-unique name. + 10, + specifics, + false, + kCtime, + kMtime)); + + entity->Delete(); + + EXPECT_TRUE(entity->IsWriteRequired()); + EXPECT_TRUE(entity->IsUnsynced()); + + EXPECT_TRUE(entity->UpdateIsReflection(10)); + EXPECT_FALSE(entity->UpdateIsInConflict(10)); + + EXPECT_FALSE(entity->UpdateIsReflection(11)); + EXPECT_TRUE(entity->UpdateIsInConflict(11)); +} + +} // namespace syncer diff --git a/sync/engine/model_type_sync_proxy.cc b/sync/engine/model_type_sync_proxy.cc new file mode 100644 index 0000000..974dc61 --- /dev/null +++ b/sync/engine/model_type_sync_proxy.cc @@ -0,0 +1,15 @@ +// 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/engine/model_type_sync_proxy.h" + +namespace syncer { + +ModelTypeSyncProxy::ModelTypeSyncProxy() { +} + +ModelTypeSyncProxy::~ModelTypeSyncProxy() { +} + +} // namespace syncer diff --git a/sync/engine/model_type_sync_proxy.h b/sync/engine/model_type_sync_proxy.h new file mode 100644 index 0000000..2851a09 --- /dev/null +++ b/sync/engine/model_type_sync_proxy.h @@ -0,0 +1,29 @@ +// 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_ENGINE_MODEL_TYPE_SYNC_PROXY_H_ +#define SYNC_ENGINE_MODEL_TYPE_SYNC_PROXY_H_ + +#include "sync/base/sync_export.h" +#include "sync/engine/non_blocking_sync_common.h" + +namespace syncer { + +// Interface used by sync backend to issue requests to a synced data type. +class SYNC_EXPORT_PRIVATE ModelTypeSyncProxy { + public: + ModelTypeSyncProxy(); + virtual ~ModelTypeSyncProxy(); + + virtual void ReceiveCommitResponse( + const DataTypeState& type_state, + const CommitResponseDataList& response_list) = 0; + virtual void ReceiveUpdateResponse( + const DataTypeState& type_state, + const UpdateResponseDataList& response_list) = 0; +}; + +} // namespace syncer + +#endif // SYNC_ENGINE_MODEL_TYPE_SYNC_PROXY_H_ diff --git a/sync/engine/model_type_sync_proxy_impl.cc b/sync/engine/model_type_sync_proxy_impl.cc new file mode 100644 index 0000000..d865998 --- /dev/null +++ b/sync/engine/model_type_sync_proxy_impl.cc @@ -0,0 +1,247 @@ +// 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/engine/model_type_sync_proxy_impl.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "sync/engine/model_type_entity.h" +#include "sync/engine/model_type_sync_worker.h" +#include "sync/internal_api/public/sync_context_proxy.h" +#include "sync/syncable/syncable_util.h" + +namespace syncer { + +ModelTypeSyncProxyImpl::ModelTypeSyncProxyImpl(ModelType type) + : type_(type), + is_preferred_(false), + is_connected_(false), + entities_deleter_(&entities_), + weak_ptr_factory_for_ui_(this), + weak_ptr_factory_for_sync_(this) { +} + +ModelTypeSyncProxyImpl::~ModelTypeSyncProxyImpl() { +} + +bool ModelTypeSyncProxyImpl::IsPreferred() const { + DCHECK(CalledOnValidThread()); + return is_preferred_; +} + +bool ModelTypeSyncProxyImpl::IsConnected() const { + DCHECK(CalledOnValidThread()); + return is_connected_; +} + +ModelType ModelTypeSyncProxyImpl::GetModelType() const { + DCHECK(CalledOnValidThread()); + return type_; +} + +void ModelTypeSyncProxyImpl::Enable( + scoped_ptr sync_context_proxy) { + DCHECK(CalledOnValidThread()); + DVLOG(1) << "Asked to enable " << ModelTypeToString(type_); + + is_preferred_ = true; + + // TODO(rlarocque): At some point, this should be loaded from storage. + data_type_state_.progress_marker.set_data_type_id( + GetSpecificsFieldNumberFromModelType(type_)); + + sync_context_proxy_ = sync_context_proxy.Pass(); + sync_context_proxy_->ConnectTypeToSync( + GetModelType(), + data_type_state_, + weak_ptr_factory_for_sync_.GetWeakPtr()); +} + +void ModelTypeSyncProxyImpl::Disable() { + DCHECK(CalledOnValidThread()); + is_preferred_ = false; + Disconnect(); + + ClearSyncState(); +} + +void ModelTypeSyncProxyImpl::Disconnect() { + DCHECK(CalledOnValidThread()); + DVLOG(1) << "Asked to disconnect " << ModelTypeToString(type_); + is_connected_ = false; + + if (sync_context_proxy_) { + sync_context_proxy_->Disconnect(GetModelType()); + sync_context_proxy_.reset(); + } + + weak_ptr_factory_for_sync_.InvalidateWeakPtrs(); + worker_.reset(); + + ClearTransientSyncState(); +} + +base::WeakPtr ModelTypeSyncProxyImpl::AsWeakPtrForUI() { + DCHECK(CalledOnValidThread()); + return weak_ptr_factory_for_ui_.GetWeakPtr(); +} + +void ModelTypeSyncProxyImpl::OnConnect(scoped_ptr worker) { + DCHECK(CalledOnValidThread()); + DVLOG(1) << "Successfully connected " << ModelTypeToString(type_); + + is_connected_ = true; + worker_ = worker.Pass(); + + FlushPendingCommitRequests(); +} + +void ModelTypeSyncProxyImpl::Put(const std::string& client_tag, + const sync_pb::EntitySpecifics& specifics) { + DCHECK_EQ(type_, GetModelTypeFromSpecifics(specifics)); + + const std::string client_tag_hash( + syncable::GenerateSyncableHash(type_, client_tag)); + + EntityMap::iterator it = entities_.find(client_tag_hash); + if (it == entities_.end()) { + scoped_ptr entity(ModelTypeEntity::NewLocalItem( + client_tag, specifics, base::Time::Now())); + entities_.insert(std::make_pair(client_tag_hash, entity.release())); + } else { + ModelTypeEntity* entity = it->second; + entity->MakeLocalChange(specifics); + } + + FlushPendingCommitRequests(); +} + +void ModelTypeSyncProxyImpl::Delete(const std::string& client_tag) { + const std::string client_tag_hash( + syncable::GenerateSyncableHash(type_, client_tag)); + + EntityMap::iterator it = entities_.find(client_tag_hash); + if (it == entities_.end()) { + // That's unusual, but not necessarily a bad thing. + // Missing is as good as deleted as far as the model is concerned. + DLOG(WARNING) << "Attempted to delete missing item." + << " client tag: " << client_tag; + } else { + ModelTypeEntity* entity = it->second; + entity->Delete(); + } + + FlushPendingCommitRequests(); +} + +void ModelTypeSyncProxyImpl::FlushPendingCommitRequests() { + CommitRequestDataList commit_requests; + + // Don't bother sending anything if there's no one to send to. + if (!IsConnected()) + return; + + // Don't send anything if the type is not ready to handle commits. + if (!data_type_state_.initial_sync_done) + return; + + // TODO(rlarocque): Do something smarter than iterate here. + for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); + ++it) { + if (it->second->RequiresCommitRequest()) { + CommitRequestData request; + it->second->InitializeCommitRequestData(&request); + commit_requests.push_back(request); + it->second->SetCommitRequestInProgress(); + } + } + + if (!commit_requests.empty()) + worker_->RequestCommits(commit_requests); +} + +void ModelTypeSyncProxyImpl::OnCommitCompletion( + const DataTypeState& type_state, + const CommitResponseDataList& response_list) { + data_type_state_ = type_state; + + for (CommitResponseDataList::const_iterator list_it = response_list.begin(); + list_it != response_list.end(); + ++list_it) { + const CommitResponseData& response_data = *list_it; + const std::string& client_tag_hash = response_data.client_tag_hash; + + EntityMap::iterator it = entities_.find(client_tag_hash); + if (it == entities_.end()) { + NOTREACHED() << "Received commit response for missing item." + << " type: " << type_ << " client_tag: " << client_tag_hash; + return; + } else { + it->second->ReceiveCommitResponse(response_data.id, + response_data.sequence_number, + response_data.response_version); + } + } +} + +void ModelTypeSyncProxyImpl::OnUpdateReceived( + const DataTypeState& data_type_state, + const UpdateResponseDataList& response_list) { + bool initial_sync_just_finished = + !data_type_state_.initial_sync_done && data_type_state.initial_sync_done; + + data_type_state_ = data_type_state; + + for (UpdateResponseDataList::const_iterator list_it = response_list.begin(); + list_it != response_list.end(); + ++list_it) { + const UpdateResponseData& response_data = *list_it; + const std::string& client_tag_hash = response_data.client_tag_hash; + + EntityMap::iterator it = entities_.find(client_tag_hash); + if (it == entities_.end()) { + scoped_ptr entity = + ModelTypeEntity::FromServerUpdate(response_data.id, + response_data.client_tag_hash, + response_data.non_unique_name, + response_data.response_version, + response_data.specifics, + response_data.deleted, + response_data.ctime, + response_data.mtime); + entities_.insert(std::make_pair(client_tag_hash, entity.release())); + } else { + ModelTypeEntity* entity = it->second; + entity->ApplyUpdateFromServer(response_data.response_version, + response_data.deleted, + response_data.specifics, + response_data.mtime); + // TODO: Do something special when conflicts are detected. + } + } + + if (initial_sync_just_finished) + FlushPendingCommitRequests(); + + // TODO: Inform the model of the new or updated data. +} + +void ModelTypeSyncProxyImpl::ClearTransientSyncState() { + for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); + ++it) { + it->second->ClearTransientSyncState(); + } +} + +void ModelTypeSyncProxyImpl::ClearSyncState() { + for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); + ++it) { + it->second->ClearSyncState(); + } + + data_type_state_ = DataTypeState(); +} + +} // namespace syncer diff --git a/sync/engine/model_type_sync_proxy_impl.h b/sync/engine/model_type_sync_proxy_impl.h new file mode 100644 index 0000000..0091112 --- /dev/null +++ b/sync/engine/model_type_sync_proxy_impl.h @@ -0,0 +1,137 @@ +// 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_ENGINE_MODEL_TYPE_SYNC_PROXY_IMPL_H_ +#define SYNC_ENGINE_MODEL_TYPE_SYNC_PROXY_IMPL_H_ + +#include "base/memory/weak_ptr.h" +#include "base/stl_util.h" +#include "base/threading/non_thread_safe.h" +#include "sync/base/sync_export.h" +#include "sync/engine/non_blocking_sync_common.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/protocol/sync.pb.h" + +namespace syncer { + +class SyncContextProxy; +class ModelTypeEntity; +class ModelTypeSyncWorker; + +// A sync component embedded on the synced type's thread that helps to handle +// communication between sync and model type threads. +class SYNC_EXPORT_PRIVATE ModelTypeSyncProxyImpl : base::NonThreadSafe { + public: + ModelTypeSyncProxyImpl(ModelType type); + virtual ~ModelTypeSyncProxyImpl(); + + // Returns true if this object believes that sync is preferred for this type. + // + // By "preferred", we mean that a policy decision has been made that this + // type should be synced. Most of the time this is controlled by a user + // clicking a checkbox on the settings page. + // + // The canonical preferred state is based on SyncPrefs on the UI thread. At + // best, this value is stale and may lag behind the one set on the UI thread. + // Before this type has registered with the UI thread, it's mostly just an + // informed guess. + bool IsPreferred() const; + + // Returns true if the handshake with sync thread is complete. + bool IsConnected() const; + + // Returns the model type handled by this type sync proxy. + ModelType GetModelType() const; + + // Starts the handshake with the sync thread. + void Enable(scoped_ptr context_proxy); + + // Severs all ties to the sync thread and may delete local sync state. + // Another call to Enable() can be used to re-establish this connection. + void Disable(); + + // Severs all ties to the sync thread. + // Another call to Enable() can be used to re-establish this connection. + void Disconnect(); + + // Callback used to process the handshake response. + void OnConnect(scoped_ptr worker); + + // Requests that an item be stored in sync. + void Put(const std::string& client_tag, + const sync_pb::EntitySpecifics& specifics); + + // Deletes an item from sync. + void Delete(const std::string& client_tag); + + // Informs this object that some of its commit requests have been + // successfully serviced. + void OnCommitCompletion(const DataTypeState& type_state, + const CommitResponseDataList& response_list); + + // Informs this object that there are some incoming updates is should + // handle. + void OnUpdateReceived(const DataTypeState& type_state, + const UpdateResponseDataList& response_list); + + // Returns the long-lived WeakPtr that is intended to be registered with the + // ProfileSyncService. + base::WeakPtr AsWeakPtrForUI(); + + private: + typedef std::map EntityMap; + + // Sends all commit requests that are due to be sent to the sync thread. + void FlushPendingCommitRequests(); + + // Clears any state related to outstanding communications with the + // ModelTypeSyncWorker. Used when we want to disconnect from + // the current worker. + void ClearTransientSyncState(); + + // Clears any state related to our communications with the current sync + // account. Useful when a user signs out of the current account. + void ClearSyncState(); + + ModelType type_; + DataTypeState data_type_state_; + + // Whether or not sync is preferred for this type. This is a cached copy of + // the canonical copy information on the UI thread. + bool is_preferred_; + + // Whether or not this object has completed its initial handshake with the + // SyncContextProxy. + bool is_connected_; + + // Our link to data type management on the sync thread. + // Used for enabling and disabling sync for this type. + // + // Beware of NULL pointers: This object is uninitialized when we are not + // connected to sync. + scoped_ptr sync_context_proxy_; + + // Reference to the ModelTypeSyncWorker. + // + // The interface hides the posting of tasks across threads as well as the + // ModelTypeSyncWorker's implementation. Both of these features are + // useful in tests. + scoped_ptr worker_; + + // The set of sync entities known to this object. + EntityMap entities_; + STLValueDeleter entities_deleter_; + + // We use two different WeakPtrFactories because we want the pointers they + // issue to have different lifetimes. When asked to disconnect from the sync + // thread, we want to make sure that no tasks generated as part of the + // now-obsolete connection to affect us. But we also want the WeakPtr we + // sent to the UI thread to remain valid. + base::WeakPtrFactory weak_ptr_factory_for_ui_; + base::WeakPtrFactory weak_ptr_factory_for_sync_; +}; + +} // namespace syncer + +#endif // SYNC_ENGINE_MODEL_TYPE_SYNC_PROXY_IMPL_H_ diff --git a/sync/engine/model_type_sync_proxy_impl_unittest.cc b/sync/engine/model_type_sync_proxy_impl_unittest.cc new file mode 100644 index 0000000..810c6b6 --- /dev/null +++ b/sync/engine/model_type_sync_proxy_impl_unittest.cc @@ -0,0 +1,463 @@ +// 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/engine/model_type_sync_proxy_impl.h" + +#include "sync/engine/model_type_sync_worker.h" +#include "sync/engine/non_blocking_sync_common.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/internal_api/public/sync_context_proxy.h" +#include "sync/protocol/sync.pb.h" +#include "sync/syncable/syncable_util.h" +#include "sync/test/engine/injectable_sync_context_proxy.h" +#include "sync/test/engine/mock_model_type_sync_worker.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncer { + +static const ModelType kModelType = PREFERENCES; + +// Tests the sync engine parts of ModelTypeSyncProxyImpl. +// +// The ModelTypeSyncProxyImpl contains a non-trivial amount of code dedicated +// to turning the sync engine on and off again. That code is fairly well +// tested in the NonBlockingDataTypeController unit tests and it doesn't need +// to be re-tested here. +// +// These tests skip past initialization and focus on steady state sync engine +// behvior. This is where we test how the type sync proxy responds to the +// model's requests to make changes to its data, the messages incoming from the +// sync server, and what happens when the two conflict. +// +// Inputs: +// - Initial state from permanent storage. (TODO) +// - Create, update or delete requests from the model. +// - Update responses and commit responses from the server. +// +// Outputs: +// - Writes to permanent storage. (TODO) +// - Callbacks into the model. (TODO) +// - Requests to the sync thread. Tested with MockModelTypeSyncWorker. +class ModelTypeSyncProxyImplTest : public ::testing::Test { + public: + ModelTypeSyncProxyImplTest(); + virtual ~ModelTypeSyncProxyImplTest(); + + // Initialize with no local state. The type sync proxy will be unable to + // commit until it receives notification that initial sync has completed. + void FirstTimeInitialize(); + + // Initialize to a "ready-to-commit" state. + void InitializeToReadyState(); + + // Disconnect the ModelTypeSyncWorker from our ModelTypeSyncProxyImpl. + void Disconnect(); + + // Disable sync for this ModelTypeSyncProxyImpl. Should cause sync state to + // be discarded. + void Disable(); + + // Re-enable sync after Disconnect() or Disable(). + void ReEnable(); + + // Local data modification. Emulates signals from the model thread. + void WriteItem(const std::string& tag, const std::string& value); + void DeleteItem(const std::string& tag); + + // Emulates an "initial sync done" message from the + // ModelTypeSyncWorker. + void OnInitialSyncDone(); + + // Emulate updates from the server. + // This harness has some functionality to help emulate server behavior. + // See the definitions of these methods for more information. + void UpdateFromServer(int64 version_offset, + const std::string& tag, + const std::string& value); + void TombstoneFromServer(int64 version_offset, const std::string& tag); + + // Read emitted commit requests as batches. + size_t GetNumCommitRequestLists(); + CommitRequestDataList GetNthCommitRequestList(size_t n); + + // Read emitted commit requests by tag, most recent only. + bool HasCommitRequestForTag(const std::string& tag); + CommitRequestData GetLatestCommitRequestForTag(const std::string& tag); + + // Sends the type sync proxy a successful commit response. + void SuccessfulCommitResponse(const CommitRequestData& request_data); + + private: + static std::string GenerateTagHash(const std::string& tag); + static sync_pb::EntitySpecifics GenerateSpecifics(const std::string& tag, + const std::string& value); + + int64 GetServerVersion(const std::string& tag); + void SetServerVersion(const std::string& tag, int64 version); + + MockModelTypeSyncWorker* mock_worker_; + scoped_ptr injectable_sync_context_proxy_; + scoped_ptr type_sync_proxy_; + + DataTypeState data_type_state_; +}; + +ModelTypeSyncProxyImplTest::ModelTypeSyncProxyImplTest() + : mock_worker_(new MockModelTypeSyncWorker()), + injectable_sync_context_proxy_( + new InjectableSyncContextProxy(mock_worker_)), + type_sync_proxy_(new ModelTypeSyncProxyImpl(kModelType)) { +} + +ModelTypeSyncProxyImplTest::~ModelTypeSyncProxyImplTest() { +} + +void ModelTypeSyncProxyImplTest::FirstTimeInitialize() { + type_sync_proxy_->Enable(injectable_sync_context_proxy_->Clone()); +} + +void ModelTypeSyncProxyImplTest::InitializeToReadyState() { + // TODO(rlarocque): This should be updated to inject on-disk state. + // At the time this code was written, there was no support for on-disk + // state so this was the only way to inject a data_type_state into + // the |type_sync_proxy_|. + FirstTimeInitialize(); + OnInitialSyncDone(); +} + +void ModelTypeSyncProxyImplTest::Disconnect() { + type_sync_proxy_->Disconnect(); + injectable_sync_context_proxy_.reset(); + mock_worker_ = NULL; +} + +void ModelTypeSyncProxyImplTest::Disable() { + type_sync_proxy_->Disable(); + injectable_sync_context_proxy_.reset(); + mock_worker_ = NULL; +} + +void ModelTypeSyncProxyImplTest::ReEnable() { + DCHECK(!type_sync_proxy_->IsConnected()); + + // Prepare a new NonBlockingTypeProcesorCore instance, just as we would + // if this happened in the real world. + mock_worker_ = new MockModelTypeSyncWorker(); + injectable_sync_context_proxy_.reset( + new InjectableSyncContextProxy(mock_worker_)); + + // Re-enable sync with the new ModelTypeSyncWorker. + type_sync_proxy_->Enable(injectable_sync_context_proxy_->Clone()); +} + +void ModelTypeSyncProxyImplTest::WriteItem(const std::string& tag, + const std::string& value) { + const std::string tag_hash = GenerateTagHash(tag); + type_sync_proxy_->Put(tag, GenerateSpecifics(tag, value)); +} + +void ModelTypeSyncProxyImplTest::DeleteItem(const std::string& tag) { + type_sync_proxy_->Delete(tag); +} + +void ModelTypeSyncProxyImplTest::OnInitialSyncDone() { + data_type_state_.initial_sync_done = true; + UpdateResponseDataList empty_update_list; + + type_sync_proxy_->OnUpdateReceived(data_type_state_, empty_update_list); +} + +void ModelTypeSyncProxyImplTest::UpdateFromServer(int64 version_offset, + const std::string& tag, + const std::string& value) { + const std::string tag_hash = GenerateTagHash(tag); + UpdateResponseData data = mock_worker_->UpdateFromServer( + version_offset, tag_hash, GenerateSpecifics(tag, value)); + + UpdateResponseDataList list; + list.push_back(data); + type_sync_proxy_->OnUpdateReceived(data_type_state_, list); +} + +void ModelTypeSyncProxyImplTest::TombstoneFromServer(int64 version_offset, + const std::string& tag) { + // Overwrite the existing server version if this is the new highest version. + std::string tag_hash = GenerateTagHash(tag); + + UpdateResponseData data = + mock_worker_->TombstoneFromServer(version_offset, tag_hash); + + UpdateResponseDataList list; + list.push_back(data); + type_sync_proxy_->OnUpdateReceived(data_type_state_, list); +} + +void ModelTypeSyncProxyImplTest::SuccessfulCommitResponse( + const CommitRequestData& request_data) { + CommitResponseDataList list; + list.push_back(mock_worker_->SuccessfulCommitResponse(request_data)); + type_sync_proxy_->OnCommitCompletion(data_type_state_, list); +} + +std::string ModelTypeSyncProxyImplTest::GenerateTagHash( + const std::string& tag) { + return syncable::GenerateSyncableHash(kModelType, tag); +} + +sync_pb::EntitySpecifics ModelTypeSyncProxyImplTest::GenerateSpecifics( + const std::string& tag, + const std::string& value) { + sync_pb::EntitySpecifics specifics; + specifics.mutable_preference()->set_name(tag); + specifics.mutable_preference()->set_value(value); + return specifics; +} + +size_t ModelTypeSyncProxyImplTest::GetNumCommitRequestLists() { + return mock_worker_->GetNumCommitRequestLists(); +} + +CommitRequestDataList ModelTypeSyncProxyImplTest::GetNthCommitRequestList( + size_t n) { + return mock_worker_->GetNthCommitRequestList(n); +} + +bool ModelTypeSyncProxyImplTest::HasCommitRequestForTag( + const std::string& tag) { + const std::string tag_hash = GenerateTagHash(tag); + return mock_worker_->HasCommitRequestForTagHash(tag_hash); +} + +CommitRequestData ModelTypeSyncProxyImplTest::GetLatestCommitRequestForTag( + const std::string& tag) { + const std::string tag_hash = GenerateTagHash(tag); + return mock_worker_->GetLatestCommitRequestForTagHash(tag_hash); +} + +// Creates a new item locally. +// Thoroughly tests the data generated by a local item creation. +TEST_F(ModelTypeSyncProxyImplTest, CreateLocalItem) { + InitializeToReadyState(); + EXPECT_EQ(0U, GetNumCommitRequestLists()); + + WriteItem("tag1", "value1"); + + // Verify the commit request this operation has triggered. + EXPECT_EQ(1U, GetNumCommitRequestLists()); + ASSERT_TRUE(HasCommitRequestForTag("tag1")); + const CommitRequestData& tag1_data = GetLatestCommitRequestForTag("tag1"); + + EXPECT_TRUE(tag1_data.id.empty()); + EXPECT_EQ(kUncommittedVersion, tag1_data.base_version); + EXPECT_FALSE(tag1_data.ctime.is_null()); + EXPECT_FALSE(tag1_data.mtime.is_null()); + EXPECT_EQ("tag1", tag1_data.non_unique_name); + EXPECT_FALSE(tag1_data.deleted); + EXPECT_EQ("tag1", tag1_data.specifics.preference().name()); + EXPECT_EQ("value1", tag1_data.specifics.preference().value()); +} + +// Creates a new local item then modifies it. +// Thoroughly tests data generated by modification of server-unknown item. +TEST_F(ModelTypeSyncProxyImplTest, CreateAndModifyLocalItem) { + InitializeToReadyState(); + EXPECT_EQ(0U, GetNumCommitRequestLists()); + + WriteItem("tag1", "value1"); + EXPECT_EQ(1U, GetNumCommitRequestLists()); + ASSERT_TRUE(HasCommitRequestForTag("tag1")); + const CommitRequestData& tag1_v1_data = GetLatestCommitRequestForTag("tag1"); + + WriteItem("tag1", "value2"); + EXPECT_EQ(2U, GetNumCommitRequestLists()); + + ASSERT_TRUE(HasCommitRequestForTag("tag1")); + const CommitRequestData& tag1_v2_data = GetLatestCommitRequestForTag("tag1"); + + // Test some of the relations between old and new commit requests. + EXPECT_EQ(tag1_v1_data.specifics.preference().value(), "value1"); + EXPECT_GT(tag1_v2_data.sequence_number, tag1_v1_data.sequence_number); + + // Perform a thorough examination of the update-generated request. + EXPECT_TRUE(tag1_v2_data.id.empty()); + EXPECT_EQ(kUncommittedVersion, tag1_v2_data.base_version); + EXPECT_FALSE(tag1_v2_data.ctime.is_null()); + EXPECT_FALSE(tag1_v2_data.mtime.is_null()); + EXPECT_EQ("tag1", tag1_v2_data.non_unique_name); + EXPECT_FALSE(tag1_v2_data.deleted); + EXPECT_EQ("tag1", tag1_v2_data.specifics.preference().name()); + EXPECT_EQ("value2", tag1_v2_data.specifics.preference().value()); +} + +// Deletes an item we've never seen before. +// Should have no effect and not crash. +TEST_F(ModelTypeSyncProxyImplTest, DeleteUnknown) { + InitializeToReadyState(); + + DeleteItem("tag1"); + EXPECT_EQ(0U, GetNumCommitRequestLists()); +} + +// Creates an item locally then deletes it. +// +// In this test, no commit responses are received, so the deleted item is +// server-unknown as far as the model thread is concerned. That behavior +// is race-dependent; other tests are used to test other races. +TEST_F(ModelTypeSyncProxyImplTest, DeleteServerUnknown) { + InitializeToReadyState(); + + WriteItem("tag1", "value1"); + EXPECT_EQ(1U, GetNumCommitRequestLists()); + ASSERT_TRUE(HasCommitRequestForTag("tag1")); + const CommitRequestData& tag1_v1_data = GetLatestCommitRequestForTag("tag1"); + + DeleteItem("tag1"); + EXPECT_EQ(2U, GetNumCommitRequestLists()); + ASSERT_TRUE(HasCommitRequestForTag("tag1")); + const CommitRequestData& tag1_v2_data = GetLatestCommitRequestForTag("tag1"); + + EXPECT_GT(tag1_v2_data.sequence_number, tag1_v1_data.sequence_number); + + EXPECT_TRUE(tag1_v2_data.id.empty()); + EXPECT_EQ(kUncommittedVersion, tag1_v2_data.base_version); + EXPECT_TRUE(tag1_v2_data.deleted); +} + +// Creates an item locally then deletes it. +// +// The item is created locally then enqueued for commit. The sync thread +// successfully commits it, but, before the commit response is picked up +// by the model thread, the item is deleted by the model thread. +TEST_F(ModelTypeSyncProxyImplTest, DeleteServerUnknown_RacyCommitResponse) { + InitializeToReadyState(); + + WriteItem("tag1", "value1"); + EXPECT_EQ(1U, GetNumCommitRequestLists()); + ASSERT_TRUE(HasCommitRequestForTag("tag1")); + const CommitRequestData& tag1_v1_data = GetLatestCommitRequestForTag("tag1"); + + DeleteItem("tag1"); + EXPECT_EQ(2U, GetNumCommitRequestLists()); + ASSERT_TRUE(HasCommitRequestForTag("tag1")); + + // This commit happened while the deletion was in progress, but the commit + // response didn't arrive on our thread until after the delete was issued to + // the sync thread. It will update some metadata, but won't do much else. + SuccessfulCommitResponse(tag1_v1_data); + + // TODO(rlarocque): Verify the state of the item is correct once we get + // storage hooked up in these tests. For example, verify the item is still + // marked as deleted. +} + +// Creates two different sync items. +// Verifies that the second has no effect on the first. +TEST_F(ModelTypeSyncProxyImplTest, TwoIndependentItems) { + InitializeToReadyState(); + EXPECT_EQ(0U, GetNumCommitRequestLists()); + + WriteItem("tag1", "value1"); + + // There should be one commit request for this item only. + ASSERT_EQ(1U, GetNumCommitRequestLists()); + EXPECT_EQ(1U, GetNthCommitRequestList(0).size()); + ASSERT_TRUE(HasCommitRequestForTag("tag1")); + + WriteItem("tag2", "value2"); + + // The second write should trigger another single-item commit request. + ASSERT_EQ(2U, GetNumCommitRequestLists()); + EXPECT_EQ(1U, GetNthCommitRequestList(1).size()); + ASSERT_TRUE(HasCommitRequestForTag("tag2")); +} + +// Starts the type sync proxy with no local state. +// Verify that it waits until initial sync is complete before requesting +// commits. +TEST_F(ModelTypeSyncProxyImplTest, NoCommitsUntilInitialSyncDone) { + FirstTimeInitialize(); + + WriteItem("tag1", "value1"); + EXPECT_EQ(0U, GetNumCommitRequestLists()); + + OnInitialSyncDone(); + EXPECT_EQ(1U, GetNumCommitRequestLists()); + EXPECT_TRUE(HasCommitRequestForTag("tag1")); +} + +// Test proper handling of disconnect and reconnect. +// +// Creates items in various states of commit and verifies they re-attempt to +// commit on reconnect. +TEST_F(ModelTypeSyncProxyImplTest, Disconnect) { + InitializeToReadyState(); + + // The first item is fully committed. + WriteItem("tag1", "value1"); + ASSERT_TRUE(HasCommitRequestForTag("tag1")); + SuccessfulCommitResponse(GetLatestCommitRequestForTag("tag1")); + + // The second item has a commit request in progress. + WriteItem("tag2", "value2"); + EXPECT_TRUE(HasCommitRequestForTag("tag2")); + + Disconnect(); + + // The third item is added after disconnection. + WriteItem("tag3", "value3"); + + ReEnable(); + + EXPECT_EQ(1U, GetNumCommitRequestLists()); + EXPECT_EQ(2U, GetNthCommitRequestList(0).size()); + + // The first item was already in sync. + EXPECT_FALSE(HasCommitRequestForTag("tag1")); + + // The second item's commit was interrupted and should be retried. + EXPECT_TRUE(HasCommitRequestForTag("tag2")); + + // The third item's commit was not started until the reconnect. + EXPECT_TRUE(HasCommitRequestForTag("tag3")); +} + +// Test proper handling of disable and re-enable. +// +// Creates items in various states of commit and verifies they re-attempt to +// commit on re-enable. +TEST_F(ModelTypeSyncProxyImplTest, Disable) { + InitializeToReadyState(); + + // The first item is fully committed. + WriteItem("tag1", "value1"); + ASSERT_TRUE(HasCommitRequestForTag("tag1")); + SuccessfulCommitResponse(GetLatestCommitRequestForTag("tag1")); + + // The second item has a commit request in progress. + WriteItem("tag2", "value2"); + EXPECT_TRUE(HasCommitRequestForTag("tag2")); + + Disable(); + + // The third item is added after disable. + WriteItem("tag3", "value3"); + + // Now we re-enable. + ReEnable(); + + // There should be nothing to commit right away, since we need to + // re-initialize the client state first. + EXPECT_EQ(0U, GetNumCommitRequestLists()); + + // Once we're ready to commit, all three local items should consider + // themselves uncommitted and pending for commit. + OnInitialSyncDone(); + EXPECT_EQ(1U, GetNumCommitRequestLists()); + EXPECT_EQ(3U, GetNthCommitRequestList(0).size()); + EXPECT_TRUE(HasCommitRequestForTag("tag1")); + EXPECT_TRUE(HasCommitRequestForTag("tag2")); + EXPECT_TRUE(HasCommitRequestForTag("tag3")); +} + +} // namespace syncer diff --git a/sync/engine/model_type_sync_worker.cc b/sync/engine/model_type_sync_worker.cc new file mode 100644 index 0000000..6037a52 --- /dev/null +++ b/sync/engine/model_type_sync_worker.cc @@ -0,0 +1,15 @@ +// 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/engine/model_type_sync_worker.h" + +namespace syncer { + +ModelTypeSyncWorker::ModelTypeSyncWorker() { +} + +ModelTypeSyncWorker::~ModelTypeSyncWorker() { +} + +} // namespace syncer diff --git a/sync/engine/model_type_sync_worker.h b/sync/engine/model_type_sync_worker.h new file mode 100644 index 0000000..9add845 --- /dev/null +++ b/sync/engine/model_type_sync_worker.h @@ -0,0 +1,23 @@ +// 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_ENGINE_MODEL_TYPE_SYNC_WORKER_H_ +#define SYNC_ENGINE_MODEL_TYPE_SYNC_WORKER_H_ + +#include "sync/engine/non_blocking_sync_common.h" + +namespace syncer { + +// Interface used by a synced data type to issue requests to the sync backend. +class SYNC_EXPORT_PRIVATE ModelTypeSyncWorker { + public: + ModelTypeSyncWorker(); + virtual ~ModelTypeSyncWorker(); + + virtual void RequestCommits(const CommitRequestDataList& list) = 0; +}; + +} // namespace syncer + +#endif // SYNC_ENGINE_MODEL_TYPE_SYNC_WORKER_H_ diff --git a/sync/engine/model_type_sync_worker_impl.cc b/sync/engine/model_type_sync_worker_impl.cc new file mode 100644 index 0000000..0e2ade0 --- /dev/null +++ b/sync/engine/model_type_sync_worker_impl.cc @@ -0,0 +1,284 @@ +// 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/engine/model_type_sync_worker_impl.h" + +#include "base/bind.h" +#include "base/format_macros.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "sync/engine/commit_contribution.h" +#include "sync/engine/entity_tracker.h" +#include "sync/engine/model_type_sync_proxy.h" +#include "sync/engine/non_blocking_type_commit_contribution.h" +#include "sync/syncable/syncable_util.h" +#include "sync/util/time.h" + +namespace syncer { + +ModelTypeSyncWorkerImpl::ModelTypeSyncWorkerImpl( + ModelType type, + const DataTypeState& initial_state, + scoped_ptr type_sync_proxy) + : type_(type), + data_type_state_(initial_state), + type_sync_proxy_(type_sync_proxy.Pass()), + entities_deleter_(&entities_), + weak_ptr_factory_(this) { +} + +ModelTypeSyncWorkerImpl::~ModelTypeSyncWorkerImpl() { +} + +ModelType ModelTypeSyncWorkerImpl::GetModelType() const { + DCHECK(CalledOnValidThread()); + return type_; +} + +// UpdateHandler implementation. +void ModelTypeSyncWorkerImpl::GetDownloadProgress( + sync_pb::DataTypeProgressMarker* progress_marker) const { + DCHECK(CalledOnValidThread()); + progress_marker->CopyFrom(data_type_state_.progress_marker); +} + +void ModelTypeSyncWorkerImpl::GetDataTypeContext( + sync_pb::DataTypeContext* context) const { + DCHECK(CalledOnValidThread()); + context->CopyFrom(data_type_state_.type_context); +} + +SyncerError ModelTypeSyncWorkerImpl::ProcessGetUpdatesResponse( + const sync_pb::DataTypeProgressMarker& progress_marker, + const sync_pb::DataTypeContext& mutated_context, + const SyncEntityList& applicable_updates, + sessions::StatusController* status) { + DCHECK(CalledOnValidThread()); + + // TODO(rlarocque): Handle data type context conflicts. + data_type_state_.type_context = mutated_context; + data_type_state_.progress_marker = progress_marker; + + UpdateResponseDataList response_datas; + + for (SyncEntityList::const_iterator update_it = applicable_updates.begin(); + update_it != applicable_updates.end(); + ++update_it) { + const sync_pb::SyncEntity* update_entity = *update_it; + if (!update_entity->server_defined_unique_tag().empty()) { + // We can't commit an item unless we know its parent ID. This is where + // we learn that ID and remember it forever. + DCHECK_EQ(ModelTypeToRootTag(type_), + update_entity->server_defined_unique_tag()); + if (!data_type_state_.type_root_id.empty()) { + DCHECK_EQ(data_type_state_.type_root_id, update_entity->id_string()); + } + data_type_state_.type_root_id = update_entity->id_string(); + } else { + // Normal updates are handled here. + const std::string& client_tag_hash = + update_entity->client_defined_unique_tag(); + DCHECK(!client_tag_hash.empty()); + EntityMap::const_iterator map_it = entities_.find(client_tag_hash); + if (map_it == entities_.end()) { + EntityTracker* entity = + EntityTracker::FromServerUpdate(update_entity->id_string(), + client_tag_hash, + update_entity->version()); + entities_.insert(std::make_pair(client_tag_hash, entity)); + } else { + EntityTracker* entity = map_it->second; + entity->ReceiveUpdate(update_entity->version()); + } + + // Prepare the message for the model thread. + UpdateResponseData response_data; + response_data.id = update_entity->id_string(); + response_data.client_tag_hash = client_tag_hash; + response_data.response_version = update_entity->version(); + response_data.ctime = ProtoTimeToTime(update_entity->ctime()); + response_data.mtime = ProtoTimeToTime(update_entity->mtime()); + response_data.non_unique_name = update_entity->name(); + response_data.deleted = update_entity->deleted(); + response_data.specifics = update_entity->specifics(); + + response_datas.push_back(response_data); + } + } + + // Forward these updates to the model thread so it can do the rest. + type_sync_proxy_->ReceiveUpdateResponse(data_type_state_, response_datas); + + return SYNCER_OK; +} + +void ModelTypeSyncWorkerImpl::ApplyUpdates(sessions::StatusController* status) { + DCHECK(CalledOnValidThread()); + // This function is called only when we've finished a download cycle, ie. we + // got a response with changes_remaining == 0. If this is our first download + // cycle, we should update our state so the ModelTypeSyncProxy knows that + // it's safe to commit items now. + if (!data_type_state_.initial_sync_done) { + data_type_state_.initial_sync_done = true; + + UpdateResponseDataList empty_update_list; + type_sync_proxy_->ReceiveUpdateResponse(data_type_state_, + empty_update_list); + } +} + +void ModelTypeSyncWorkerImpl::PassiveApplyUpdates( + sessions::StatusController* status) { + NOTREACHED() + << "Non-blocking types should never apply updates on sync thread. " + << "ModelType is: " << ModelTypeToString(type_); +} + +void ModelTypeSyncWorkerImpl::EnqueueForCommit( + const CommitRequestDataList& list) { + DCHECK(CalledOnValidThread()); + + DCHECK(CanCommitItems()) + << "Asked to commit items before type was initialized. " + << "ModelType is: " << ModelTypeToString(type_); + + for (CommitRequestDataList::const_iterator it = list.begin(); + it != list.end(); + ++it) { + StorePendingCommit(*it); + } +} + +// CommitContributor implementation. +scoped_ptr ModelTypeSyncWorkerImpl::GetContribution( + size_t max_entries) { + DCHECK(CalledOnValidThread()); + + size_t space_remaining = max_entries; + std::vector sequence_numbers; + google::protobuf::RepeatedPtrField commit_entities; + + if (!CanCommitItems()) + return scoped_ptr(); + + // TODO(rlarocque): Avoid iterating here. + for (EntityMap::const_iterator it = entities_.begin(); + it != entities_.end() && space_remaining > 0; + ++it) { + EntityTracker* entity = it->second; + if (entity->IsCommitPending()) { + sync_pb::SyncEntity* commit_entity = commit_entities.Add(); + int64 sequence_number = -1; + + entity->PrepareCommitProto(commit_entity, &sequence_number); + HelpInitializeCommitEntity(commit_entity); + sequence_numbers.push_back(sequence_number); + + space_remaining--; + } + } + + if (commit_entities.size() == 0) + return scoped_ptr(); + + return scoped_ptr(new NonBlockingTypeCommitContribution( + data_type_state_.type_context, commit_entities, sequence_numbers, this)); +} + +void ModelTypeSyncWorkerImpl::StorePendingCommit( + const CommitRequestData& request) { + if (!request.deleted) { + DCHECK_EQ(type_, GetModelTypeFromSpecifics(request.specifics)); + } + + EntityMap::iterator map_it = entities_.find(request.client_tag_hash); + if (map_it == entities_.end()) { + EntityTracker* entity = + EntityTracker::FromCommitRequest(request.id, + request.client_tag_hash, + request.sequence_number, + request.base_version, + request.ctime, + request.mtime, + request.non_unique_name, + request.deleted, + request.specifics); + entities_.insert(std::make_pair(request.client_tag_hash, entity)); + } else { + EntityTracker* entity = map_it->second; + entity->RequestCommit(request.id, + request.client_tag_hash, + request.sequence_number, + request.base_version, + request.ctime, + request.mtime, + request.non_unique_name, + request.deleted, + request.specifics); + } + + // TODO: Nudge SyncScheduler. +} + +void ModelTypeSyncWorkerImpl::OnCommitResponse( + const CommitResponseDataList& response_list) { + for (CommitResponseDataList::const_iterator response_it = + response_list.begin(); + response_it != response_list.end(); + ++response_it) { + const std::string client_tag_hash = response_it->client_tag_hash; + EntityMap::iterator map_it = entities_.find(client_tag_hash); + + // There's no way we could have committed an entry we know nothing about. + if (map_it == entities_.end()) { + NOTREACHED() << "Received commit response for item unknown to us." + << " Model type: " << ModelTypeToString(type_) + << " ID: " << response_it->id; + continue; + } + + EntityTracker* entity = map_it->second; + entity->ReceiveCommitResponse(response_it->id, + response_it->response_version, + response_it->sequence_number); + } + + // Send the responses back to the model thread. It needs to know which + // items have been successfully committed so it can save that information in + // permanent storage. + type_sync_proxy_->ReceiveCommitResponse(data_type_state_, response_list); +} + +base::WeakPtr ModelTypeSyncWorkerImpl::AsWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +bool ModelTypeSyncWorkerImpl::CanCommitItems() const { + // We can't commit anything until we know the type's parent node. + // We'll get it in the first update response. + return !data_type_state_.type_root_id.empty() && + data_type_state_.initial_sync_done; +} + +void ModelTypeSyncWorkerImpl::HelpInitializeCommitEntity( + sync_pb::SyncEntity* sync_entity) { + // Initial commits need our help to generate a client ID. + if (!sync_entity->has_id_string()) { + DCHECK_EQ(kUncommittedVersion, sync_entity->version()); + const int64 id = data_type_state_.next_client_id++; + sync_entity->set_id_string( + base::StringPrintf("%s-%" PRId64, ModelTypeToString(type_), id)); + } + + // Always include enough specifics to identify the type. Do this even in + // deletion requests, where the specifics are otherwise invalid. + if (!sync_entity->has_specifics()) { + AddDefaultFieldValue(type_, sync_entity->mutable_specifics()); + } + + // We're always responsible for the parent ID. + sync_entity->set_parent_id_string(data_type_state_.type_root_id); +} + +} // namespace syncer diff --git a/sync/engine/model_type_sync_worker_impl.h b/sync/engine/model_type_sync_worker_impl.h new file mode 100644 index 0000000..a0e87b1 --- /dev/null +++ b/sync/engine/model_type_sync_worker_impl.h @@ -0,0 +1,128 @@ +// 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_ENGINE_MODEL_TYPE_SYNC_WORKER_IMPL_H_ +#define SYNC_ENGINE_MODEL_TYPE_SYNC_WORKER_IMPL_H_ + +#include "base/memory/weak_ptr.h" +#include "base/stl_util.h" +#include "base/threading/non_thread_safe.h" +#include "sync/base/sync_export.h" +#include "sync/engine/commit_contributor.h" +#include "sync/engine/non_blocking_sync_common.h" +#include "sync/engine/update_handler.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/protocol/sync.pb.h" + +namespace base { +class SingleThreadTaskRunner; +} + +namespace syncer { + +class ModelTypeSyncProxy; +class EntityTracker; + +// A smart cache for sync types that use message passing (rather than +// transactions and the syncable::Directory) to communicate with the sync +// thread. +// +// When the non-blocking sync type wants to talk with the sync server, it will +// send a message from its thread to this object on the sync thread. This +// object ensures the appropriate sync server communication gets scheduled and +// executed. The response, if any, will be returned to the non-blocking sync +// type's thread eventually. +// +// This object also has a role to play in communications in the opposite +// direction. Sometimes the sync thread will receive changes from the sync +// server and deliver them here. This object will post this information back to +// the appropriate component on the model type's thread. +// +// This object does more than just pass along messages. It understands the sync +// protocol, and it can make decisions when it sees conflicting messages. For +// example, if the sync server sends down an update for a sync entity that is +// currently pending for commit, this object will detect this condition and +// cancel the pending commit. +class SYNC_EXPORT ModelTypeSyncWorkerImpl : public UpdateHandler, + public CommitContributor, + public base::NonThreadSafe { + public: + ModelTypeSyncWorkerImpl(ModelType type, + const DataTypeState& initial_state, + scoped_ptr type_sync_proxy); + virtual ~ModelTypeSyncWorkerImpl(); + + ModelType GetModelType() const; + + // UpdateHandler implementation. + virtual void GetDownloadProgress( + sync_pb::DataTypeProgressMarker* progress_marker) const OVERRIDE; + virtual void GetDataTypeContext( + sync_pb::DataTypeContext* context) const OVERRIDE; + virtual SyncerError ProcessGetUpdatesResponse( + const sync_pb::DataTypeProgressMarker& progress_marker, + const sync_pb::DataTypeContext& mutated_context, + const SyncEntityList& applicable_updates, + sessions::StatusController* status) OVERRIDE; + virtual void ApplyUpdates(sessions::StatusController* status) OVERRIDE; + virtual void PassiveApplyUpdates(sessions::StatusController* status) OVERRIDE; + + // Entry point for the ModelTypeSyncProxy to send commit requests. + void EnqueueForCommit(const CommitRequestDataList& request_list); + + // CommitContributor implementation. + virtual scoped_ptr GetContribution( + size_t max_entries) OVERRIDE; + + // Callback for when our contribution gets a response. + void OnCommitResponse(const CommitResponseDataList& response_list); + + base::WeakPtr AsWeakPtr(); + + private: + typedef std::map EntityMap; + + // Stores a single commit request in this object's internal state. + void StorePendingCommit(const CommitRequestData& request); + + // Returns true if all data type state required for commits is available. In + // practice, this means that it returns true from the time this object first + // receives notice of a successful update fetch from the server. + bool CanCommitItems() const; + + // Initializes the parts of a commit entity that are the responsibility of + // this class, and not the EntityTracker. Some fields, like the + // client-assigned ID, can only be set by an entity with knowledge of the + // entire data type's state. + void HelpInitializeCommitEntity(sync_pb::SyncEntity* commit_entity); + + ModelType type_; + + // State that applies to the entire model type. + DataTypeState data_type_state_; + + // Pointer to the ModelTypeSyncProxy associated with this worker. + // This is NULL when no proxy is connected.. + scoped_ptr type_sync_proxy_; + + // A map of per-entity information known to this object. + // + // When commits are pending, their information is stored here. This + // information is dropped from memory when the commit succeeds or gets + // cancelled. + // + // This also stores some information related to received server state in + // order to implement reflection blocking and conflict detection. This + // information is kept in memory indefinitely. With a bit more coordination + // with the model thread, we could optimize this to reduce memory usage in + // the steady state. + EntityMap entities_; + STLValueDeleter entities_deleter_; + + base::WeakPtrFactory weak_ptr_factory_; +}; + +} // namespace syncer + +#endif // SYNC_ENGINE_MODEL_TYPE_SYNC_WORKER_IMPL_H_ diff --git a/sync/engine/model_type_sync_worker_impl_unittest.cc b/sync/engine/model_type_sync_worker_impl_unittest.cc new file mode 100644 index 0000000..09b47ad --- /dev/null +++ b/sync/engine/model_type_sync_worker_impl_unittest.cc @@ -0,0 +1,598 @@ +// 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/engine/model_type_sync_worker_impl.h" + +#include "sync/engine/commit_contribution.h" +#include "sync/engine/model_type_sync_proxy.h" +#include "sync/engine/non_blocking_sync_common.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/protocol/sync.pb.h" +#include "sync/sessions/status_controller.h" +#include "sync/syncable/syncable_util.h" +#include "sync/test/engine/mock_model_type_sync_proxy.h" +#include "sync/test/engine/single_type_mock_server.h" + +#include "testing/gtest/include/gtest/gtest.h" + +static const std::string kTypeParentId = "PrefsRootNodeID"; +static const syncer::ModelType kModelType = syncer::PREFERENCES; + +namespace syncer { + +// Tests the ModelTypeSyncWorkerImpl. +// +// This class passes messages between the model thread and sync server. +// As such, its code is subject to lots of different race conditions. This +// test harness lets us exhaustively test all possible races. We try to +// focus on just a few interesting cases. +// +// Inputs: +// - Initial data type state from the model thread. +// - Commit requests from the model thread. +// - Update responses from the server. +// - Commit responses from the server. +// +// Outputs: +// - Commit requests to the server. +// - Commit responses to the model thread. +// - Update responses to the model thread. +// - Nudges to the sync scheduler. +// +// We use the MockModelTypeSyncProxy to stub out all communication +// with the model thread. That interface is synchronous, which makes it +// much easier to test races. +// +// The interface with the server is built around "pulling" data from this +// class, so we don't have to mock out any of it. We wrap it with some +// convenience functions to we can emulate server behavior. +class ModelTypeSyncWorkerImplTest : public ::testing::Test { + public: + ModelTypeSyncWorkerImplTest(); + virtual ~ModelTypeSyncWorkerImplTest(); + + // One of these Initialize functions should be called at the beginning of + // each test. + + // Initializes with no data type state. We will be unable to perform any + // significant server action until we receive an update response that + // contains the type root node for this type. + void FirstInitialize(); + + // Initializes with some existing data type state. Allows us to start + // committing items right away. + void NormalInitialize(); + + // Initialize with a custom initial DataTypeState. + void InitializeWithState(const DataTypeState& state); + + // Modifications on the model thread that get sent to the worker under test. + void CommitRequest(const std::string& tag, const std::string& value); + void DeleteRequest(const std::string& tag); + + // Pretends to receive update messages from the server. + void TriggerTypeRootUpdateFromServer(); + void TriggerUpdateFromServer(int64 version_offset, + const std::string& tag, + const std::string& value); + void TriggerTombstoneFromServer(int64 version_offset, const std::string& tag); + + // By default, this harness behaves as if all tasks posted to the model + // thread are executed immediately. However, this is not necessarily true. + // The model's TaskRunner has a queue, and the tasks we post to it could + // linger there for a while. In the meantime, the model thread could + // continue posting tasks to the worker based on its stale state. + // + // If you want to test those race cases, then these functions are for you. + void SetModelThreadIsSynchronous(bool is_synchronous); + void PumpModelThread(); + + // Returns true if the |worker_| is ready to commit something. + bool WillCommit(); + + // Pretend to successfully commit all outstanding unsynced items. + // It is safe to call this only if WillCommit() returns true. + void DoSuccessfulCommit(); + + // Read commit messages the worker_ sent to the emulated server. + size_t GetNumCommitMessagesOnServer() const; + sync_pb::ClientToServerMessage GetNthCommitMessageOnServer(size_t n) const; + + // Read the latest version of sync entities committed to the emulated server. + bool HasCommitEntityOnServer(const std::string& tag) const; + sync_pb::SyncEntity GetLatestCommitEntityOnServer( + const std::string& tag) const; + + // Read the latest update messages received on the model thread. + // Note that if the model thread is in non-blocking mode, this data will not + // be updated until the response is actually processed by the model thread. + size_t GetNumModelThreadUpdateResponses() const; + UpdateResponseDataList GetNthModelThreadUpdateResponse(size_t n) const; + DataTypeState GetNthModelThreadUpdateState(size_t n) const; + + // Reads the latest update response datas on the model thread. + // Note that if the model thread is in non-blocking mode, this data will not + // be updated until the response is actually processed by the model thread. + bool HasUpdateResponseOnModelThread(const std::string& tag) const; + UpdateResponseData GetUpdateResponseOnModelThread( + const std::string& tag) const; + + // Read the latest commit messages received on the model thread. + // Note that if the model thread is in non-blocking mode, this data will not + // be updated until the response is actually processed by the model thread. + size_t GetNumModelThreadCommitResponses() const; + CommitResponseDataList GetNthModelThreadCommitResponse(size_t n) const; + DataTypeState GetNthModelThreadCommitState(size_t n) const; + + // Reads the latest commit response datas on the model thread. + // Note that if the model thread is in non-blocking mode, this data will not + // be updated until the response is actually processed by the model thread. + bool HasCommitResponseOnModelThread(const std::string& tag) const; + CommitResponseData GetCommitResponseOnModelThread( + const std::string& tag) const; + + // Helpers for building various messages and structures. + static std::string GenerateTagHash(const std::string& tag); + static sync_pb::EntitySpecifics GenerateSpecifics(const std::string& tag, + const std::string& value); + + private: + // The ModelTypeSyncWorkerImpl being tested. + scoped_ptr worker_; + + // Non-owned, possibly NULL pointer. This object belongs to the + // ModelTypeSyncWorkerImpl under test. + MockModelTypeSyncProxy* mock_type_sync_proxy_; + + // A mock that emulates enough of the sync server that it can be used + // a single UpdateHandler and CommitContributor pair. In this test + // harness, the |worker_| is both of them. + SingleTypeMockServer mock_server_; +}; + +ModelTypeSyncWorkerImplTest::ModelTypeSyncWorkerImplTest() + : mock_type_sync_proxy_(NULL), mock_server_(kModelType) { +} + +ModelTypeSyncWorkerImplTest::~ModelTypeSyncWorkerImplTest() { +} + +void ModelTypeSyncWorkerImplTest::FirstInitialize() { + DataTypeState initial_state; + initial_state.progress_marker.set_data_type_id( + GetSpecificsFieldNumberFromModelType(kModelType)); + initial_state.next_client_id = 0; + + InitializeWithState(initial_state); +} + +void ModelTypeSyncWorkerImplTest::NormalInitialize() { + DataTypeState initial_state; + initial_state.progress_marker.set_data_type_id( + GetSpecificsFieldNumberFromModelType(kModelType)); + initial_state.progress_marker.set_token("some_saved_progress_token"); + + initial_state.next_client_id = 10; + initial_state.type_root_id = kTypeParentId; + initial_state.initial_sync_done = true; + + InitializeWithState(initial_state); +} + +void ModelTypeSyncWorkerImplTest::InitializeWithState( + const DataTypeState& state) { + DCHECK(!worker_); + + // We don't get to own this object. The |worker_| keeps a scoped_ptr to it. + mock_type_sync_proxy_ = new MockModelTypeSyncProxy(); + scoped_ptr proxy(mock_type_sync_proxy_); + + worker_.reset(new ModelTypeSyncWorkerImpl(kModelType, state, proxy.Pass())); +} + +void ModelTypeSyncWorkerImplTest::CommitRequest(const std::string& name, + const std::string& value) { + const std::string tag_hash = GenerateTagHash(name); + CommitRequestData data = mock_type_sync_proxy_->CommitRequest( + tag_hash, GenerateSpecifics(name, value)); + CommitRequestDataList list; + list.push_back(data); + worker_->EnqueueForCommit(list); +} + +void ModelTypeSyncWorkerImplTest::DeleteRequest(const std::string& tag) { + const std::string tag_hash = GenerateTagHash(tag); + CommitRequestData data = mock_type_sync_proxy_->DeleteRequest(tag_hash); + CommitRequestDataList list; + list.push_back(data); + worker_->EnqueueForCommit(list); +} + +void ModelTypeSyncWorkerImplTest::TriggerTypeRootUpdateFromServer() { + sync_pb::SyncEntity entity = mock_server_.TypeRootUpdate(); + SyncEntityList entity_list; + entity_list.push_back(&entity); + + sessions::StatusController dummy_status; + + worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(), + mock_server_.GetContext(), + entity_list, + &dummy_status); + worker_->ApplyUpdates(&dummy_status); +} + +void ModelTypeSyncWorkerImplTest::TriggerUpdateFromServer( + int64 version_offset, + const std::string& tag, + const std::string& value) { + sync_pb::SyncEntity entity = mock_server_.UpdateFromServer( + version_offset, GenerateTagHash(tag), GenerateSpecifics(tag, value)); + SyncEntityList entity_list; + entity_list.push_back(&entity); + + sessions::StatusController dummy_status; + + worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(), + mock_server_.GetContext(), + entity_list, + &dummy_status); + worker_->ApplyUpdates(&dummy_status); +} + +void ModelTypeSyncWorkerImplTest::TriggerTombstoneFromServer( + int64 version_offset, + const std::string& tag) { + sync_pb::SyncEntity entity = + mock_server_.TombstoneFromServer(version_offset, GenerateTagHash(tag)); + SyncEntityList entity_list; + entity_list.push_back(&entity); + + sessions::StatusController dummy_status; + + worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(), + mock_server_.GetContext(), + entity_list, + &dummy_status); + worker_->ApplyUpdates(&dummy_status); +} + +void ModelTypeSyncWorkerImplTest::SetModelThreadIsSynchronous( + bool is_synchronous) { + mock_type_sync_proxy_->SetSynchronousExecution(is_synchronous); +} + +void ModelTypeSyncWorkerImplTest::PumpModelThread() { + mock_type_sync_proxy_->RunQueuedTasks(); +} + +bool ModelTypeSyncWorkerImplTest::WillCommit() { + scoped_ptr contribution( + worker_->GetContribution(INT_MAX)); + + if (contribution) { + contribution->CleanUp(); // Gracefully abort the commit. + return true; + } else { + return false; + } +} + +// Conveniently, this is all one big synchronous operation. The sync thread +// remains blocked while the commit is in progress, so we don't need to worry +// about other tasks being run between the time when the commit request is +// issued and the time when the commit response is received. +void ModelTypeSyncWorkerImplTest::DoSuccessfulCommit() { + DCHECK(WillCommit()); + scoped_ptr contribution( + worker_->GetContribution(INT_MAX)); + + sync_pb::ClientToServerMessage message; + contribution->AddToCommitMessage(&message); + + sync_pb::ClientToServerResponse response = + mock_server_.DoSuccessfulCommit(message); + + sessions::StatusController dummy_status; + contribution->ProcessCommitResponse(response, &dummy_status); + contribution->CleanUp(); +} + +size_t ModelTypeSyncWorkerImplTest::GetNumCommitMessagesOnServer() const { + return mock_server_.GetNumCommitMessages(); +} + +sync_pb::ClientToServerMessage +ModelTypeSyncWorkerImplTest::GetNthCommitMessageOnServer(size_t n) const { + DCHECK_LT(n, GetNumCommitMessagesOnServer()); + return mock_server_.GetNthCommitMessage(n); +} + +bool ModelTypeSyncWorkerImplTest::HasCommitEntityOnServer( + const std::string& tag) const { + const std::string tag_hash = GenerateTagHash(tag); + return mock_server_.HasCommitEntity(tag_hash); +} + +sync_pb::SyncEntity ModelTypeSyncWorkerImplTest::GetLatestCommitEntityOnServer( + const std::string& tag) const { + DCHECK(HasCommitEntityOnServer(tag)); + const std::string tag_hash = GenerateTagHash(tag); + return mock_server_.GetLastCommittedEntity(tag_hash); +} + +size_t ModelTypeSyncWorkerImplTest::GetNumModelThreadUpdateResponses() const { + return mock_type_sync_proxy_->GetNumUpdateResponses(); +} + +UpdateResponseDataList +ModelTypeSyncWorkerImplTest::GetNthModelThreadUpdateResponse(size_t n) const { + DCHECK_LT(n, GetNumModelThreadUpdateResponses()); + return mock_type_sync_proxy_->GetNthUpdateResponse(n); +} + +DataTypeState ModelTypeSyncWorkerImplTest::GetNthModelThreadUpdateState( + size_t n) const { + DCHECK_LT(n, GetNumModelThreadUpdateResponses()); + return mock_type_sync_proxy_->GetNthTypeStateReceivedInUpdateResponse(n); +} + +bool ModelTypeSyncWorkerImplTest::HasUpdateResponseOnModelThread( + const std::string& tag) const { + const std::string tag_hash = GenerateTagHash(tag); + return mock_type_sync_proxy_->HasUpdateResponse(tag_hash); +} + +UpdateResponseData ModelTypeSyncWorkerImplTest::GetUpdateResponseOnModelThread( + const std::string& tag) const { + const std::string tag_hash = GenerateTagHash(tag); + return mock_type_sync_proxy_->GetUpdateResponse(tag_hash); +} + +size_t ModelTypeSyncWorkerImplTest::GetNumModelThreadCommitResponses() const { + return mock_type_sync_proxy_->GetNumCommitResponses(); +} + +CommitResponseDataList +ModelTypeSyncWorkerImplTest::GetNthModelThreadCommitResponse(size_t n) const { + DCHECK_LT(n, GetNumModelThreadCommitResponses()); + return mock_type_sync_proxy_->GetNthCommitResponse(n); +} + +DataTypeState ModelTypeSyncWorkerImplTest::GetNthModelThreadCommitState( + size_t n) const { + DCHECK_LT(n, GetNumModelThreadCommitResponses()); + return mock_type_sync_proxy_->GetNthTypeStateReceivedInCommitResponse(n); +} + +bool ModelTypeSyncWorkerImplTest::HasCommitResponseOnModelThread( + const std::string& tag) const { + const std::string tag_hash = GenerateTagHash(tag); + return mock_type_sync_proxy_->HasCommitResponse(tag_hash); +} + +CommitResponseData ModelTypeSyncWorkerImplTest::GetCommitResponseOnModelThread( + const std::string& tag) const { + DCHECK(HasCommitResponseOnModelThread(tag)); + const std::string tag_hash = GenerateTagHash(tag); + return mock_type_sync_proxy_->GetCommitResponse(tag_hash); +} + +std::string ModelTypeSyncWorkerImplTest::GenerateTagHash( + const std::string& tag) { + const std::string& client_tag_hash = + syncable::GenerateSyncableHash(kModelType, tag); + return client_tag_hash; +} + +sync_pb::EntitySpecifics ModelTypeSyncWorkerImplTest::GenerateSpecifics( + const std::string& tag, + const std::string& value) { + sync_pb::EntitySpecifics specifics; + specifics.mutable_preference()->set_name(tag); + specifics.mutable_preference()->set_value(value); + return specifics; +} + +// Requests a commit and verifies the messages sent to the client and server as +// a result. +// +// This test performs sanity checks on most of the fields in these messages. +// For the most part this is checking that the test code behaves as expected +// and the |worker_| doesn't mess up its simple task of moving around these +// values. It makes sense to have one or two tests that are this thorough, but +// we shouldn't be this verbose in all tests. +TEST_F(ModelTypeSyncWorkerImplTest, SimpleCommit) { + NormalInitialize(); + + EXPECT_FALSE(WillCommit()); + EXPECT_EQ(0U, GetNumCommitMessagesOnServer()); + EXPECT_EQ(0U, GetNumModelThreadCommitResponses()); + + CommitRequest("tag1", "value1"); + + ASSERT_TRUE(WillCommit()); + DoSuccessfulCommit(); + + const std::string& client_tag_hash = GenerateTagHash("tag1"); + + // Exhaustively verify the SyncEntity sent in the commit message. + ASSERT_EQ(1U, GetNumCommitMessagesOnServer()); + EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size()); + ASSERT_TRUE(HasCommitEntityOnServer("tag1")); + const sync_pb::SyncEntity& entity = GetLatestCommitEntityOnServer("tag1"); + EXPECT_FALSE(entity.id_string().empty()); + EXPECT_EQ(kTypeParentId, entity.parent_id_string()); + EXPECT_EQ(kUncommittedVersion, entity.version()); + EXPECT_NE(0, entity.mtime()); + EXPECT_NE(0, entity.ctime()); + EXPECT_FALSE(entity.name().empty()); + EXPECT_EQ(client_tag_hash, entity.client_defined_unique_tag()); + EXPECT_EQ("tag1", entity.specifics().preference().name()); + EXPECT_FALSE(entity.deleted()); + EXPECT_EQ("value1", entity.specifics().preference().value()); + + // Exhaustively verify the commit response returned to the model thread. + ASSERT_EQ(1U, GetNumModelThreadCommitResponses()); + EXPECT_EQ(1U, GetNthModelThreadCommitResponse(0).size()); + ASSERT_TRUE(HasCommitResponseOnModelThread("tag1")); + const CommitResponseData& commit_response = + GetCommitResponseOnModelThread("tag1"); + + // The ID changes in a commit response to initial commit. + EXPECT_FALSE(commit_response.id.empty()); + EXPECT_NE(entity.id_string(), commit_response.id); + + EXPECT_EQ(client_tag_hash, commit_response.client_tag_hash); + EXPECT_LT(0, commit_response.response_version); +} + +TEST_F(ModelTypeSyncWorkerImplTest, SimpleDelete) { + NormalInitialize(); + + // We can't delete an entity that was never committed. + // Step 1 is to create and commit a new entity. + CommitRequest("tag1", "value1"); + ASSERT_TRUE(WillCommit()); + DoSuccessfulCommit(); + + ASSERT_TRUE(HasCommitResponseOnModelThread("tag1")); + const CommitResponseData& initial_commit_response = + GetCommitResponseOnModelThread("tag1"); + int64 base_version = initial_commit_response.response_version; + + // Now that we have an entity, we can delete it. + DeleteRequest("tag1"); + ASSERT_TRUE(WillCommit()); + DoSuccessfulCommit(); + + // Verify the SyncEntity sent in the commit message. + ASSERT_EQ(2U, GetNumCommitMessagesOnServer()); + EXPECT_EQ(1, GetNthCommitMessageOnServer(1).commit().entries_size()); + ASSERT_TRUE(HasCommitEntityOnServer("tag1")); + const sync_pb::SyncEntity& entity = GetLatestCommitEntityOnServer("tag1"); + EXPECT_FALSE(entity.id_string().empty()); + EXPECT_EQ(GenerateTagHash("tag1"), entity.client_defined_unique_tag()); + EXPECT_EQ(base_version, entity.version()); + EXPECT_TRUE(entity.deleted()); + + // Deletions should contain enough specifics to identify the type. + EXPECT_TRUE(entity.has_specifics()); + EXPECT_EQ(kModelType, GetModelTypeFromSpecifics(entity.specifics())); + + // Verify the commit response returned to the model thread. + ASSERT_EQ(2U, GetNumModelThreadCommitResponses()); + EXPECT_EQ(1U, GetNthModelThreadCommitResponse(1).size()); + ASSERT_TRUE(HasCommitResponseOnModelThread("tag1")); + const CommitResponseData& commit_response = + GetCommitResponseOnModelThread("tag1"); + + EXPECT_EQ(entity.id_string(), commit_response.id); + EXPECT_EQ(entity.client_defined_unique_tag(), + commit_response.client_tag_hash); + EXPECT_EQ(entity.version(), commit_response.response_version); +} + +// The server doesn't like it when we try to delete an entity it's never heard +// of before. This test helps ensure we avoid that scenario. +TEST_F(ModelTypeSyncWorkerImplTest, NoDeleteUncommitted) { + NormalInitialize(); + + // Request the commit of a new, never-before-seen item. + CommitRequest("tag1", "value1"); + EXPECT_TRUE(WillCommit()); + + // Request a deletion of that item before we've had a chance to commit it. + DeleteRequest("tag1"); + EXPECT_FALSE(WillCommit()); +} + +// Verifies the sending of an "initial sync done" signal. +TEST_F(ModelTypeSyncWorkerImplTest, SendInitialSyncDone) { + FirstInitialize(); // Initialize with no saved sync state. + EXPECT_EQ(0U, GetNumModelThreadUpdateResponses()); + + // Receive an update response that contains only the type root node. + TriggerTypeRootUpdateFromServer(); + + // Two updates: + // - One triggered by process updates to forward the type root ID. + // - One triggered by apply updates, which the worker interprets to mean + // "initial sync done". This triggers a model thread update, too. + EXPECT_EQ(2U, GetNumModelThreadUpdateResponses()); + + // The type root and initial sync done updates both contain no entities. + EXPECT_EQ(0U, GetNthModelThreadUpdateResponse(0).size()); + EXPECT_EQ(0U, GetNthModelThreadUpdateResponse(1).size()); + + const DataTypeState& state = GetNthModelThreadUpdateState(1); + EXPECT_FALSE(state.progress_marker.token().empty()); + EXPECT_FALSE(state.type_root_id.empty()); + EXPECT_TRUE(state.initial_sync_done); +} + +// Commit two new entities in two separate commit messages. +TEST_F(ModelTypeSyncWorkerImplTest, TwoNewItemsCommittedSeparately) { + NormalInitialize(); + + // Commit the first of two entities. + CommitRequest("tag1", "value1"); + ASSERT_TRUE(WillCommit()); + DoSuccessfulCommit(); + ASSERT_EQ(1U, GetNumCommitMessagesOnServer()); + EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size()); + ASSERT_TRUE(HasCommitEntityOnServer("tag1")); + const sync_pb::SyncEntity& tag1_entity = + GetLatestCommitEntityOnServer("tag1"); + + // Commit the second of two entities. + CommitRequest("tag2", "value2"); + ASSERT_TRUE(WillCommit()); + DoSuccessfulCommit(); + ASSERT_EQ(2U, GetNumCommitMessagesOnServer()); + EXPECT_EQ(1, GetNthCommitMessageOnServer(1).commit().entries_size()); + ASSERT_TRUE(HasCommitEntityOnServer("tag2")); + const sync_pb::SyncEntity& tag2_entity = + GetLatestCommitEntityOnServer("tag2"); + + EXPECT_FALSE(WillCommit()); + + // The IDs assigned by the |worker_| should be unique. + EXPECT_NE(tag1_entity.id_string(), tag2_entity.id_string()); + + // Check that the committed specifics values are sane. + EXPECT_EQ(tag1_entity.specifics().preference().value(), "value1"); + EXPECT_EQ(tag2_entity.specifics().preference().value(), "value2"); + + // There should have been two separate commit responses sent to the model + // thread. They should be uninteresting, so we don't bother inspecting them. + EXPECT_EQ(2U, GetNumModelThreadCommitResponses()); +} + +TEST_F(ModelTypeSyncWorkerImplTest, ReceiveUpdates) { + NormalInitialize(); + + const std::string& tag_hash = GenerateTagHash("tag1"); + + TriggerUpdateFromServer(10, "tag1", "value1"); + + ASSERT_EQ(1U, GetNumModelThreadUpdateResponses()); + UpdateResponseDataList updates_list = GetNthModelThreadUpdateResponse(0); + ASSERT_EQ(1U, updates_list.size()); + + ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1")); + UpdateResponseData update = GetUpdateResponseOnModelThread("tag1"); + + EXPECT_FALSE(update.id.empty()); + EXPECT_EQ(tag_hash, update.client_tag_hash); + EXPECT_LT(0, update.response_version); + EXPECT_FALSE(update.ctime.is_null()); + EXPECT_FALSE(update.mtime.is_null()); + EXPECT_FALSE(update.non_unique_name.empty()); + EXPECT_FALSE(update.deleted); + EXPECT_EQ("tag1", update.specifics.preference().name()); + EXPECT_EQ("value1", update.specifics.preference().value()); +} + +} // namespace syncer diff --git a/sync/engine/non_blocking_sync_common.h b/sync/engine/non_blocking_sync_common.h index d0e0f9d..cefccfe 100644 --- a/sync/engine/non_blocking_sync_common.h +++ b/sync/engine/non_blocking_sync_common.h @@ -47,7 +47,7 @@ struct SYNC_EXPORT_PRIVATE DataTypeState { int64 next_client_id; // This flag is set to true when the first download cycle is complete. The - // NonBlockingTypeProcessor should not attempt to commit any items until this + // ModelTypeSyncProxy should not attempt to commit any items until this // flag is set. bool initial_sync_done; }; diff --git a/sync/engine/non_blocking_type_commit_contribution.cc b/sync/engine/non_blocking_type_commit_contribution.cc index 9e48ad6..39823a5 100644 --- a/sync/engine/non_blocking_type_commit_contribution.cc +++ b/sync/engine/non_blocking_type_commit_contribution.cc @@ -4,8 +4,8 @@ #include "sync/engine/non_blocking_type_commit_contribution.h" +#include "sync/engine/model_type_sync_worker_impl.h" #include "sync/engine/non_blocking_sync_common.h" -#include "sync/engine/non_blocking_type_processor_core.h" #include "sync/protocol/proto_value_conversions.h" namespace syncer { @@ -14,8 +14,8 @@ NonBlockingTypeCommitContribution::NonBlockingTypeCommitContribution( const sync_pb::DataTypeContext& context, const google::protobuf::RepeatedPtrField& entities, const std::vector& sequence_numbers, - NonBlockingTypeProcessorCore* processor_core) - : processor_core_(processor_core), + ModelTypeSyncWorkerImpl* worker) + : worker_(worker), context_(context), entities_(entities), sequence_numbers_(sequence_numbers), @@ -90,7 +90,7 @@ SyncerError NonBlockingTypeCommitContribution::ProcessCommitResponse( // Send whatever successful responses we did get back to our parent. // It's the schedulers job to handle the failures. - processor_core_->OnCommitResponse(response_list); + worker_->OnCommitResponse(response_list); // Let the scheduler know about the failures. if (unknown_error) { diff --git a/sync/engine/non_blocking_type_commit_contribution.h b/sync/engine/non_blocking_type_commit_contribution.h index 4d415ca..d732130 100644 --- a/sync/engine/non_blocking_type_commit_contribution.h +++ b/sync/engine/non_blocking_type_commit_contribution.h @@ -13,19 +13,19 @@ namespace syncer { -class NonBlockingTypeProcessorCore; +class ModelTypeSyncWorkerImpl; // A non-blocking sync type's contribution to an outgoing commit message. // // Helps build a commit message and process its response. It collaborates -// closely with the NonBlockingTypeProcessorCore. +// closely with the ModelTypeSyncWorkerImpl. class NonBlockingTypeCommitContribution : public CommitContribution { public: NonBlockingTypeCommitContribution( const sync_pb::DataTypeContext& context, const google::protobuf::RepeatedPtrField& entities, const std::vector& sequence_numbers, - NonBlockingTypeProcessorCore* processor_core); + ModelTypeSyncWorkerImpl* worker); virtual ~NonBlockingTypeCommitContribution(); // Implementation of CommitContribution @@ -38,7 +38,7 @@ class NonBlockingTypeCommitContribution : public CommitContribution { private: // A non-owned pointer back to the object that created this contribution. - NonBlockingTypeProcessorCore* const processor_core_; + ModelTypeSyncWorkerImpl* const worker_; // The type-global context information. const sync_pb::DataTypeContext context_; diff --git a/sync/engine/non_blocking_type_processor.cc b/sync/engine/non_blocking_type_processor.cc deleted file mode 100644 index 9b80543..0000000 --- a/sync/engine/non_blocking_type_processor.cc +++ /dev/null @@ -1,250 +0,0 @@ -// 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/engine/non_blocking_type_processor.h" - -#include "base/bind.h" -#include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" -#include "sync/engine/model_thread_sync_entity.h" -#include "sync/engine/non_blocking_type_processor_core_interface.h" -#include "sync/internal_api/public/sync_core_proxy.h" -#include "sync/syncable/syncable_util.h" - -namespace syncer { - -NonBlockingTypeProcessor::NonBlockingTypeProcessor(ModelType type) - : type_(type), - is_preferred_(false), - is_connected_(false), - entities_deleter_(&entities_), - weak_ptr_factory_for_ui_(this), - weak_ptr_factory_for_sync_(this) { -} - -NonBlockingTypeProcessor::~NonBlockingTypeProcessor() { -} - -bool NonBlockingTypeProcessor::IsPreferred() const { - DCHECK(CalledOnValidThread()); - return is_preferred_; -} - -bool NonBlockingTypeProcessor::IsConnected() const { - DCHECK(CalledOnValidThread()); - return is_connected_; -} - -ModelType NonBlockingTypeProcessor::GetModelType() const { - DCHECK(CalledOnValidThread()); - return type_; -} - -void NonBlockingTypeProcessor::Enable( - scoped_ptr sync_core_proxy) { - DCHECK(CalledOnValidThread()); - DVLOG(1) << "Asked to enable " << ModelTypeToString(type_); - - is_preferred_ = true; - - // TODO(rlarocque): At some point, this should be loaded from storage. - data_type_state_.progress_marker.set_data_type_id( - GetSpecificsFieldNumberFromModelType(type_)); - - sync_core_proxy_ = sync_core_proxy.Pass(); - sync_core_proxy_->ConnectTypeToCore(GetModelType(), - data_type_state_, - weak_ptr_factory_for_sync_.GetWeakPtr()); -} - -void NonBlockingTypeProcessor::Disable() { - DCHECK(CalledOnValidThread()); - is_preferred_ = false; - Disconnect(); - - ClearSyncState(); -} - -void NonBlockingTypeProcessor::Disconnect() { - DCHECK(CalledOnValidThread()); - DVLOG(1) << "Asked to disconnect " << ModelTypeToString(type_); - is_connected_ = false; - - if (sync_core_proxy_) { - sync_core_proxy_->Disconnect(GetModelType()); - sync_core_proxy_.reset(); - } - - weak_ptr_factory_for_sync_.InvalidateWeakPtrs(); - core_interface_.reset(); - - ClearTransientSyncState(); -} - -base::WeakPtr -NonBlockingTypeProcessor::AsWeakPtrForUI() { - DCHECK(CalledOnValidThread()); - return weak_ptr_factory_for_ui_.GetWeakPtr(); -} - -void NonBlockingTypeProcessor::OnConnect( - scoped_ptr core_interface) { - DCHECK(CalledOnValidThread()); - DVLOG(1) << "Successfully connected " << ModelTypeToString(type_); - - is_connected_ = true; - core_interface_ = core_interface.Pass(); - - FlushPendingCommitRequests(); -} - -void NonBlockingTypeProcessor::Put(const std::string& client_tag, - const sync_pb::EntitySpecifics& specifics) { - DCHECK_EQ(type_, GetModelTypeFromSpecifics(specifics)); - - const std::string client_tag_hash( - syncable::GenerateSyncableHash(type_, client_tag)); - - EntityMap::iterator it = entities_.find(client_tag_hash); - if (it == entities_.end()) { - scoped_ptr entity( - ModelThreadSyncEntity::NewLocalItem( - client_tag, specifics, base::Time::Now())); - entities_.insert(std::make_pair(client_tag_hash, entity.release())); - } else { - ModelThreadSyncEntity* entity = it->second; - entity->MakeLocalChange(specifics); - } - - FlushPendingCommitRequests(); -} - -void NonBlockingTypeProcessor::Delete(const std::string& client_tag) { - const std::string client_tag_hash( - syncable::GenerateSyncableHash(type_, client_tag)); - - EntityMap::iterator it = entities_.find(client_tag_hash); - if (it == entities_.end()) { - // That's unusual, but not necessarily a bad thing. - // Missing is as good as deleted as far as the model is concerned. - DLOG(WARNING) << "Attempted to delete missing item." - << " client tag: " << client_tag; - } else { - ModelThreadSyncEntity* entity = it->second; - entity->Delete(); - } - - FlushPendingCommitRequests(); -} - -void NonBlockingTypeProcessor::FlushPendingCommitRequests() { - CommitRequestDataList commit_requests; - - // Don't bother sending anything if there's no one to send to. - if (!IsConnected()) - return; - - // Don't send anything if the type is not ready to handle commits. - if (!data_type_state_.initial_sync_done) - return; - - // TODO(rlarocque): Do something smarter than iterate here. - for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); - ++it) { - if (it->second->RequiresCommitRequest()) { - CommitRequestData request; - it->second->InitializeCommitRequestData(&request); - commit_requests.push_back(request); - it->second->SetCommitRequestInProgress(); - } - } - - if (!commit_requests.empty()) - core_interface_->RequestCommits(commit_requests); -} - -void NonBlockingTypeProcessor::OnCommitCompletion( - const DataTypeState& type_state, - const CommitResponseDataList& response_list) { - data_type_state_ = type_state; - - for (CommitResponseDataList::const_iterator list_it = response_list.begin(); - list_it != response_list.end(); - ++list_it) { - const CommitResponseData& response_data = *list_it; - const std::string& client_tag_hash = response_data.client_tag_hash; - - EntityMap::iterator it = entities_.find(client_tag_hash); - if (it == entities_.end()) { - NOTREACHED() << "Received commit response for missing item." - << " type: " << type_ << " client_tag: " << client_tag_hash; - return; - } else { - it->second->ReceiveCommitResponse(response_data.id, - response_data.sequence_number, - response_data.response_version); - } - } -} - -void NonBlockingTypeProcessor::OnUpdateReceived( - const DataTypeState& data_type_state, - const UpdateResponseDataList& response_list) { - bool initial_sync_just_finished = - !data_type_state_.initial_sync_done && data_type_state.initial_sync_done; - - data_type_state_ = data_type_state; - - for (UpdateResponseDataList::const_iterator list_it = response_list.begin(); - list_it != response_list.end(); - ++list_it) { - const UpdateResponseData& response_data = *list_it; - const std::string& client_tag_hash = response_data.client_tag_hash; - - EntityMap::iterator it = entities_.find(client_tag_hash); - if (it == entities_.end()) { - scoped_ptr entity = - ModelThreadSyncEntity::FromServerUpdate( - response_data.id, - response_data.client_tag_hash, - response_data.non_unique_name, - response_data.response_version, - response_data.specifics, - response_data.deleted, - response_data.ctime, - response_data.mtime); - entities_.insert(std::make_pair(client_tag_hash, entity.release())); - } else { - ModelThreadSyncEntity* entity = it->second; - entity->ApplyUpdateFromServer(response_data.response_version, - response_data.deleted, - response_data.specifics, - response_data.mtime); - // TODO: Do something special when conflicts are detected. - } - } - - if (initial_sync_just_finished) - FlushPendingCommitRequests(); - - // TODO: Inform the model of the new or updated data. -} - -void NonBlockingTypeProcessor::ClearTransientSyncState() { - for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); - ++it) { - it->second->ClearTransientSyncState(); - } -} - -void NonBlockingTypeProcessor::ClearSyncState() { - for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); - ++it) { - it->second->ClearSyncState(); - } - - data_type_state_ = DataTypeState(); -} - -} // namespace syncer diff --git a/sync/engine/non_blocking_type_processor.h b/sync/engine/non_blocking_type_processor.h deleted file mode 100644 index da680f1..0000000 --- a/sync/engine/non_blocking_type_processor.h +++ /dev/null @@ -1,138 +0,0 @@ -// 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_ENGINE_NON_BLOCKING_TYPE_PROCESSOR_H_ -#define SYNC_ENGINE_NON_BLOCKING_TYPE_PROCESSOR_H_ - -#include "base/memory/weak_ptr.h" -#include "base/stl_util.h" -#include "base/threading/non_thread_safe.h" -#include "sync/base/sync_export.h" -#include "sync/engine/non_blocking_sync_common.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/protocol/sync.pb.h" - -namespace syncer { - -class SyncCoreProxy; -class ModelThreadSyncEntity; -class NonBlockingTypeProcessorCoreInterface; - -// A sync component embedded on the synced type's thread that helps to handle -// communication between sync and model type threads. -class SYNC_EXPORT_PRIVATE NonBlockingTypeProcessor : base::NonThreadSafe { - public: - NonBlockingTypeProcessor(ModelType type); - virtual ~NonBlockingTypeProcessor(); - - // Returns true if this object believes that sync is preferred for this type. - // - // By "preferred", we mean that a policy decision has been made that this - // type should be synced. Most of the time this is controlled by a user - // clicking a checkbox on the settings page. - // - // The canonical preferred state is based on SyncPrefs on the UI thread. At - // best, this value is stale and may lag behind the one set on the UI thread. - // Before this type has registered with the UI thread, it's mostly just an - // informed guess. - bool IsPreferred() const; - - // Returns true if the handshake with sync thread is complete. - bool IsConnected() const; - - // Returns the model type handled by this processor. - ModelType GetModelType() const; - - // Starts the handshake with the sync thread. - void Enable(scoped_ptr core_proxy); - - // Severs all ties to the sync thread and may delete local sync state. - // Another call to Enable() can be used to re-establish this connection. - void Disable(); - - // Severs all ties to the sync thread. - // Another call to Enable() can be used to re-establish this connection. - void Disconnect(); - - // Callback used to process the handshake response. - void OnConnect( - scoped_ptr core_interface); - - // Requests that an item be stored in sync. - void Put(const std::string& client_tag, - const sync_pb::EntitySpecifics& specifics); - - // Deletes an item from sync. - void Delete(const std::string& client_tag); - - // Informs this object that some of its commit requests have been - // successfully serviced. - void OnCommitCompletion(const DataTypeState& type_state, - const CommitResponseDataList& response_list); - - // Informs this object that there are some incoming updates is should - // handle. - void OnUpdateReceived(const DataTypeState& type_state, - const UpdateResponseDataList& response_list); - - // Returns the long-lived WeakPtr that is intended to be registered with the - // ProfileSyncService. - base::WeakPtr AsWeakPtrForUI(); - - private: - typedef std::map EntityMap; - - // Sends all commit requests that are due to be sent to the sync thread. - void FlushPendingCommitRequests(); - - // Clears any state related to outstanding communications with the - // NonBlockingTypeProcessorCore. Used when we want to disconnect from - // the current core. - void ClearTransientSyncState(); - - // Clears any state related to our communications with the current sync - // account. Useful when a user signs out of the current account. - void ClearSyncState(); - - ModelType type_; - DataTypeState data_type_state_; - - // Whether or not sync is preferred for this type. This is a cached copy of - // the canonical copy information on the UI thread. - bool is_preferred_; - - // Whether or not this object has completed its initial handshake with the - // SyncCoreProxy. - bool is_connected_; - - // Our link to data type management on the sync thread. - // Used for enabling and disabling sync for this type. - // - // Beware of NULL pointers: This object is uninitialized when we are not - // connected to sync. - scoped_ptr sync_core_proxy_; - - // Reference to the NonBlockingTypeProcessorCore. - // - // The interface hides the posting of tasks across threads as well as the - // NonBlockingTypeProcessorCore's implementation. Both of these features are - // useful in tests. - scoped_ptr core_interface_; - - // The set of sync entities known to this object. - EntityMap entities_; - STLValueDeleter entities_deleter_; - - // We use two different WeakPtrFactories because we want the pointers they - // issue to have different lifetimes. When asked to disconnect from the sync - // thread, we want to make sure that no tasks generated as part of the - // now-obsolete connection to affect us. But we also want the WeakPtr we - // sent to the UI thread to remain valid. - base::WeakPtrFactory weak_ptr_factory_for_ui_; - base::WeakPtrFactory weak_ptr_factory_for_sync_; -}; - -} // namespace syncer - -#endif // SYNC_ENGINE_NON_BLOCKING_TYPE_PROCESSOR_H_ diff --git a/sync/engine/non_blocking_type_processor_core.cc b/sync/engine/non_blocking_type_processor_core.cc deleted file mode 100644 index a14a8d0..0000000 --- a/sync/engine/non_blocking_type_processor_core.cc +++ /dev/null @@ -1,286 +0,0 @@ -// 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/engine/non_blocking_type_processor_core.h" - -#include "base/bind.h" -#include "base/format_macros.h" -#include "base/logging.h" -#include "base/strings/stringprintf.h" -#include "sync/engine/commit_contribution.h" -#include "sync/engine/non_blocking_type_commit_contribution.h" -#include "sync/engine/non_blocking_type_processor_interface.h" -#include "sync/engine/sync_thread_sync_entity.h" -#include "sync/syncable/syncable_util.h" -#include "sync/util/time.h" - -namespace syncer { - -NonBlockingTypeProcessorCore::NonBlockingTypeProcessorCore( - ModelType type, - const DataTypeState& initial_state, - scoped_ptr processor_interface) - : type_(type), - data_type_state_(initial_state), - processor_interface_(processor_interface.Pass()), - entities_deleter_(&entities_), - weak_ptr_factory_(this) { -} - -NonBlockingTypeProcessorCore::~NonBlockingTypeProcessorCore() { -} - -ModelType NonBlockingTypeProcessorCore::GetModelType() const { - DCHECK(CalledOnValidThread()); - return type_; -} - -// UpdateHandler implementation. -void NonBlockingTypeProcessorCore::GetDownloadProgress( - sync_pb::DataTypeProgressMarker* progress_marker) const { - DCHECK(CalledOnValidThread()); - progress_marker->CopyFrom(data_type_state_.progress_marker); -} - -void NonBlockingTypeProcessorCore::GetDataTypeContext( - sync_pb::DataTypeContext* context) const { - DCHECK(CalledOnValidThread()); - context->CopyFrom(data_type_state_.type_context); -} - -SyncerError NonBlockingTypeProcessorCore::ProcessGetUpdatesResponse( - const sync_pb::DataTypeProgressMarker& progress_marker, - const sync_pb::DataTypeContext& mutated_context, - const SyncEntityList& applicable_updates, - sessions::StatusController* status) { - DCHECK(CalledOnValidThread()); - - // TODO(rlarocque): Handle data type context conflicts. - data_type_state_.type_context = mutated_context; - data_type_state_.progress_marker = progress_marker; - - UpdateResponseDataList response_datas; - - for (SyncEntityList::const_iterator update_it = applicable_updates.begin(); - update_it != applicable_updates.end(); - ++update_it) { - const sync_pb::SyncEntity* update_entity = *update_it; - if (!update_entity->server_defined_unique_tag().empty()) { - // We can't commit an item unless we know its parent ID. This is where - // we learn that ID and remember it forever. - DCHECK_EQ(ModelTypeToRootTag(type_), - update_entity->server_defined_unique_tag()); - if (!data_type_state_.type_root_id.empty()) { - DCHECK_EQ(data_type_state_.type_root_id, update_entity->id_string()); - } - data_type_state_.type_root_id = update_entity->id_string(); - } else { - // Normal updates are handled here. - const std::string& client_tag_hash = - update_entity->client_defined_unique_tag(); - DCHECK(!client_tag_hash.empty()); - EntityMap::const_iterator map_it = entities_.find(client_tag_hash); - if (map_it == entities_.end()) { - SyncThreadSyncEntity* entity = - SyncThreadSyncEntity::FromServerUpdate(update_entity->id_string(), - client_tag_hash, - update_entity->version()); - entities_.insert(std::make_pair(client_tag_hash, entity)); - } else { - SyncThreadSyncEntity* entity = map_it->second; - entity->ReceiveUpdate(update_entity->version()); - } - - // Prepare the message for the model thread. - UpdateResponseData response_data; - response_data.id = update_entity->id_string(); - response_data.client_tag_hash = client_tag_hash; - response_data.response_version = update_entity->version(); - response_data.ctime = ProtoTimeToTime(update_entity->ctime()); - response_data.mtime = ProtoTimeToTime(update_entity->mtime()); - response_data.non_unique_name = update_entity->name(); - response_data.deleted = update_entity->deleted(); - response_data.specifics = update_entity->specifics(); - - response_datas.push_back(response_data); - } - } - - // Forward these updates to the model thread so it can do the rest. - processor_interface_->ReceiveUpdateResponse(data_type_state_, response_datas); - - return SYNCER_OK; -} - -void NonBlockingTypeProcessorCore::ApplyUpdates( - sessions::StatusController* status) { - DCHECK(CalledOnValidThread()); - // This function is called only when we've finished a download cycle, ie. we - // got a response with changes_remaining == 0. If this is our first download - // cycle, we should update our state so the NonBlockingTypeProcessor knows - // that it's safe to commit items now. - if (!data_type_state_.initial_sync_done) { - data_type_state_.initial_sync_done = true; - - UpdateResponseDataList empty_update_list; - processor_interface_->ReceiveUpdateResponse(data_type_state_, - empty_update_list); - } -} - -void NonBlockingTypeProcessorCore::PassiveApplyUpdates( - sessions::StatusController* status) { - NOTREACHED() - << "Non-blocking types should never apply updates on sync thread. " - << "ModelType is: " << ModelTypeToString(type_); -} - -void NonBlockingTypeProcessorCore::EnqueueForCommit( - const CommitRequestDataList& list) { - DCHECK(CalledOnValidThread()); - - DCHECK(CanCommitItems()) - << "Asked to commit items before type was initialized. " - << "ModelType is: " << ModelTypeToString(type_); - - for (CommitRequestDataList::const_iterator it = list.begin(); - it != list.end(); - ++it) { - StorePendingCommit(*it); - } -} - -// CommitContributor implementation. -scoped_ptr -NonBlockingTypeProcessorCore::GetContribution(size_t max_entries) { - DCHECK(CalledOnValidThread()); - - size_t space_remaining = max_entries; - std::vector sequence_numbers; - google::protobuf::RepeatedPtrField commit_entities; - - if (!CanCommitItems()) - return scoped_ptr(); - - // TODO(rlarocque): Avoid iterating here. - for (EntityMap::const_iterator it = entities_.begin(); - it != entities_.end() && space_remaining > 0; - ++it) { - SyncThreadSyncEntity* entity = it->second; - if (entity->IsCommitPending()) { - sync_pb::SyncEntity* commit_entity = commit_entities.Add(); - int64 sequence_number = -1; - - entity->PrepareCommitProto(commit_entity, &sequence_number); - HelpInitializeCommitEntity(commit_entity); - sequence_numbers.push_back(sequence_number); - - space_remaining--; - } - } - - if (commit_entities.size() == 0) - return scoped_ptr(); - - return scoped_ptr(new NonBlockingTypeCommitContribution( - data_type_state_.type_context, commit_entities, sequence_numbers, this)); -} - -void NonBlockingTypeProcessorCore::StorePendingCommit( - const CommitRequestData& request) { - if (!request.deleted) { - DCHECK_EQ(type_, GetModelTypeFromSpecifics(request.specifics)); - } - - EntityMap::iterator map_it = entities_.find(request.client_tag_hash); - if (map_it == entities_.end()) { - SyncThreadSyncEntity* entity = - SyncThreadSyncEntity::FromCommitRequest(request.id, - request.client_tag_hash, - request.sequence_number, - request.base_version, - request.ctime, - request.mtime, - request.non_unique_name, - request.deleted, - request.specifics); - entities_.insert(std::make_pair(request.client_tag_hash, entity)); - } else { - SyncThreadSyncEntity* entity = map_it->second; - entity->RequestCommit(request.id, - request.client_tag_hash, - request.sequence_number, - request.base_version, - request.ctime, - request.mtime, - request.non_unique_name, - request.deleted, - request.specifics); - } - - // TODO: Nudge SyncScheduler. -} - -void NonBlockingTypeProcessorCore::OnCommitResponse( - const CommitResponseDataList& response_list) { - for (CommitResponseDataList::const_iterator response_it = - response_list.begin(); - response_it != response_list.end(); - ++response_it) { - const std::string client_tag_hash = response_it->client_tag_hash; - EntityMap::iterator map_it = entities_.find(client_tag_hash); - - // There's no way we could have committed an entry we know nothing about. - if (map_it == entities_.end()) { - NOTREACHED() << "Received commit response for item unknown to us." - << " Model type: " << ModelTypeToString(type_) - << " ID: " << response_it->id; - continue; - } - - SyncThreadSyncEntity* entity = map_it->second; - entity->ReceiveCommitResponse(response_it->id, - response_it->response_version, - response_it->sequence_number); - } - - // Send the responses back to the model thread. It needs to know which - // items have been successfully committed so it can save that information in - // permanent storage. - processor_interface_->ReceiveCommitResponse(data_type_state_, response_list); -} - -base::WeakPtr -NonBlockingTypeProcessorCore::AsWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); -} - -bool NonBlockingTypeProcessorCore::CanCommitItems() const { - // We can't commit anything until we know the type's parent node. - // We'll get it in the first update response. - return !data_type_state_.type_root_id.empty() && - data_type_state_.initial_sync_done; -} - -void NonBlockingTypeProcessorCore::HelpInitializeCommitEntity( - sync_pb::SyncEntity* sync_entity) { - // Initial commits need our help to generate a client ID. - if (!sync_entity->has_id_string()) { - DCHECK_EQ(kUncommittedVersion, sync_entity->version()); - const int64 id = data_type_state_.next_client_id++; - sync_entity->set_id_string( - base::StringPrintf("%s-%" PRId64, ModelTypeToString(type_), id)); - } - - // Always include enough specifics to identify the type. Do this even in - // deletion requests, where the specifics are otherwise invalid. - if (!sync_entity->has_specifics()) { - AddDefaultFieldValue(type_, sync_entity->mutable_specifics()); - } - - // We're always responsible for the parent ID. - sync_entity->set_parent_id_string(data_type_state_.type_root_id); -} - -} // namespace syncer diff --git a/sync/engine/non_blocking_type_processor_core.h b/sync/engine/non_blocking_type_processor_core.h deleted file mode 100644 index 93d4215..0000000 --- a/sync/engine/non_blocking_type_processor_core.h +++ /dev/null @@ -1,131 +0,0 @@ -// 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_ENGINE_NON_BLOCKING_TYPE_PROCESSOR_CORE_H_ -#define SYNC_ENGINE_NON_BLOCKING_TYPE_PROCESSOR_CORE_H_ - -#include "base/memory/weak_ptr.h" -#include "base/stl_util.h" -#include "base/threading/non_thread_safe.h" -#include "sync/base/sync_export.h" -#include "sync/engine/commit_contributor.h" -#include "sync/engine/non_blocking_sync_common.h" -#include "sync/engine/update_handler.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/protocol/sync.pb.h" - -namespace base { -class SingleThreadTaskRunner; -} - -namespace syncer { - -class NonBlockingTypeProcessorInterface; -class SyncThreadSyncEntity; - -// A smart cache for sync types that use message passing (rather than -// transactions and the syncable::Directory) to communicate with the sync -// thread. -// -// When the non-blocking sync type wants to talk with the sync server, it will -// send a message from its thread to this object on the sync thread. This -// object ensures the appropriate sync server communication gets scheduled and -// executed. The response, if any, will be returned to the non-blocking sync -// type's thread eventually. -// -// This object also has a role to play in communications in the opposite -// direction. Sometimes the sync thread will receive changes from the sync -// server and deliver them here. This object will post this information back to -// the appropriate component on the model type's thread. -// -// This object does more than just pass along messages. It understands the sync -// protocol, and it can make decisions when it sees conflicting messages. For -// example, if the sync server sends down an update for a sync entity that is -// currently pending for commit, this object will detect this condition and -// cancel the pending commit. -class SYNC_EXPORT NonBlockingTypeProcessorCore - : public UpdateHandler, - public CommitContributor, - public base::NonThreadSafe { - public: - NonBlockingTypeProcessorCore( - ModelType type, - const DataTypeState& initial_state, - scoped_ptr processor_interface); - virtual ~NonBlockingTypeProcessorCore(); - - ModelType GetModelType() const; - - // UpdateHandler implementation. - virtual void GetDownloadProgress( - sync_pb::DataTypeProgressMarker* progress_marker) const OVERRIDE; - virtual void GetDataTypeContext(sync_pb::DataTypeContext* context) const - OVERRIDE; - virtual SyncerError ProcessGetUpdatesResponse( - const sync_pb::DataTypeProgressMarker& progress_marker, - const sync_pb::DataTypeContext& mutated_context, - const SyncEntityList& applicable_updates, - sessions::StatusController* status) OVERRIDE; - virtual void ApplyUpdates(sessions::StatusController* status) OVERRIDE; - virtual void PassiveApplyUpdates(sessions::StatusController* status) OVERRIDE; - - // Entry point for NonBlockingTypeProcessor to send commit requests. - void EnqueueForCommit(const CommitRequestDataList& request_list); - - // CommitContributor implementation. - virtual scoped_ptr GetContribution( - size_t max_entries) OVERRIDE; - - // Callback for when our contribution gets a response. - void OnCommitResponse(const CommitResponseDataList& response_list); - - base::WeakPtr AsWeakPtr(); - - private: - typedef std::map EntityMap; - - // Stores a single commit request in this object's internal state. - void StorePendingCommit(const CommitRequestData& request); - - // Returns true if all data type state required for commits is available. In - // practice, this means that it returns true from the time this object first - // receives notice of a successful update fetch from the server. - bool CanCommitItems() const; - - // Initializes the parts of a commit entity that are the responsibility of - // this class, and not the SyncThreadSyncEntity. Some fields, like the - // client-assigned ID, can only be set by an entity with knowledge of the - // entire data type's state. - void HelpInitializeCommitEntity(sync_pb::SyncEntity* commit_entity); - - ModelType type_; - - // State that applies to the entire model type. - DataTypeState data_type_state_; - - // Abstraction around the NonBlockingTypeProcessor so this class - // doesn't need to know about its specific implementation or - // which thread it's on. This makes it easier to write tests. - scoped_ptr processor_interface_; - - // A map of per-entity information known to this object. - // - // When commits are pending, their information is stored here. This - // information is dropped from memory when the commit succeeds or gets - // cancelled. - // - // This also stores some information related to received server state in - // order to implement reflection blocking and conflict detection. This - // information is kept in memory indefinitely. With a bit more coordination - // with the model thread, we could optimize this to reduce memory usage in - // the steady state. - EntityMap entities_; - STLValueDeleter entities_deleter_; - - base::WeakPtrFactory weak_ptr_factory_; -}; - -} // namespace syncer - -#endif // SYNC_ENGINE_NON_BLOCKING_TYPE_PROCESSOR_CORE_H_ diff --git a/sync/engine/non_blocking_type_processor_core_interface.cc b/sync/engine/non_blocking_type_processor_core_interface.cc deleted file mode 100644 index 6ce74cc..0000000 --- a/sync/engine/non_blocking_type_processor_core_interface.cc +++ /dev/null @@ -1,16 +0,0 @@ -// 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/engine/non_blocking_type_processor_core_interface.h" - -namespace syncer { - -NonBlockingTypeProcessorCoreInterface::NonBlockingTypeProcessorCoreInterface() { -} - -NonBlockingTypeProcessorCoreInterface:: - ~NonBlockingTypeProcessorCoreInterface() { -} - -} // namespace syncer diff --git a/sync/engine/non_blocking_type_processor_core_interface.h b/sync/engine/non_blocking_type_processor_core_interface.h deleted file mode 100644 index 56675c2..0000000 --- a/sync/engine/non_blocking_type_processor_core_interface.h +++ /dev/null @@ -1,24 +0,0 @@ -// 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_ENGINE_NON_BLOCKING_TYPE_CORE_INTERFACE_H_ -#define SYNC_ENGINE_NON_BLOCKING_TYPE_CORE_INTERFACE_H_ - -#include "sync/engine/non_blocking_sync_common.h" - -namespace syncer { - -// An interface representing a NonBlockingTypeProcessorCore and its thread. -// This abstraction is useful in tests. -class SYNC_EXPORT_PRIVATE NonBlockingTypeProcessorCoreInterface { - public: - NonBlockingTypeProcessorCoreInterface(); - virtual ~NonBlockingTypeProcessorCoreInterface(); - - virtual void RequestCommits(const CommitRequestDataList& list) = 0; -}; - -} // namespace syncer - -#endif // SYNC_ENGINE_NON_BLOCKING_TYPE_CORE_INTERFACE_H_ diff --git a/sync/engine/non_blocking_type_processor_core_unittest.cc b/sync/engine/non_blocking_type_processor_core_unittest.cc deleted file mode 100644 index c775a33..0000000 --- a/sync/engine/non_blocking_type_processor_core_unittest.cc +++ /dev/null @@ -1,604 +0,0 @@ -// 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/engine/non_blocking_type_processor_core.h" - -#include "sync/engine/commit_contribution.h" -#include "sync/engine/non_blocking_sync_common.h" -#include "sync/engine/non_blocking_type_processor_interface.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/protocol/sync.pb.h" -#include "sync/sessions/status_controller.h" -#include "sync/syncable/syncable_util.h" -#include "sync/test/engine/mock_non_blocking_type_processor.h" -#include "sync/test/engine/single_type_mock_server.h" - -#include "testing/gtest/include/gtest/gtest.h" - -static const std::string kTypeParentId = "PrefsRootNodeID"; -static const syncer::ModelType kModelType = syncer::PREFERENCES; - -namespace syncer { - -// Tests the NonBlockingTypeProcessorCore. -// -// This class passes messages between the model thread and sync server. -// As such, its code is subject to lots of different race conditions. This -// test harness lets us exhaustively test all possible races. We try to -// focus on just a few interesting cases. -// -// Inputs: -// - Initial data type state from the model thread. -// - Commit requests from the model thread. -// - Update responses from the server. -// - Commit responses from the server. -// -// Outputs: -// - Commit requests to the server. -// - Commit responses to the model thread. -// - Update responses to the model thread. -// - Nudges to the sync scheduler. -// -// We use the MockNonBlockingTypeProcessor to stub out all communication -// with the model thread. That interface is synchronous, which makes it -// much easier to test races. -// -// The interface with the server is built around "pulling" data from this -// class, so we don't have to mock out any of it. We wrap it with some -// convenience functions to we can emulate server behavior. -class NonBlockingTypeProcessorCoreTest : public ::testing::Test { - public: - NonBlockingTypeProcessorCoreTest(); - virtual ~NonBlockingTypeProcessorCoreTest(); - - // One of these Initialize functions should be called at the beginning of - // each test. - - // Initializes with no data type state. We will be unable to perform any - // significant server action until we receive an update response that - // contains the type root node for this type. - void FirstInitialize(); - - // Initializes with some existing data type state. Allows us to start - // committing items right away. - void NormalInitialize(); - - // Initialize with a custom initial DataTypeState. - void InitializeWithState(const DataTypeState& state); - - // Modifications on the model thread that get sent to the core under test. - void CommitRequest(const std::string& tag, const std::string& value); - void DeleteRequest(const std::string& tag); - - // Pretends to receive update messages from the server. - void TriggerTypeRootUpdateFromServer(); - void TriggerUpdateFromServer(int64 version_offset, - const std::string& tag, - const std::string& value); - void TriggerTombstoneFromServer(int64 version_offset, const std::string& tag); - - // By default, this harness behaves as if all tasks posted to the model - // thread are executed immediately. However, this is not necessarily true. - // The model's TaskRunner has a queue, and the tasks we post to it could - // linger there for a while. In the meantime, the model thread could - // continue posting tasks to the core based on its stale state. - // - // If you want to test those race cases, then these functions are for you. - void SetModelThreadIsSynchronous(bool is_synchronous); - void PumpModelThread(); - - // Returns true if the |core_| is ready to commit something. - bool WillCommit(); - - // Pretend to successfully commit all outstanding unsynced items. - // It is safe to call this only if WillCommit() returns true. - void DoSuccessfulCommit(); - - // Read commit messages the core_ sent to the emulated server. - size_t GetNumCommitMessagesOnServer() const; - sync_pb::ClientToServerMessage GetNthCommitMessageOnServer(size_t n) const; - - // Read the latest version of sync entities committed to the emulated server. - bool HasCommitEntityOnServer(const std::string& tag) const; - sync_pb::SyncEntity GetLatestCommitEntityOnServer( - const std::string& tag) const; - - // Read the latest update messages received on the model thread. - // Note that if the model thread is in non-blocking mode, this data will not - // be updated until the response is actually processed by the model thread. - size_t GetNumModelThreadUpdateResponses() const; - UpdateResponseDataList GetNthModelThreadUpdateResponse(size_t n) const; - DataTypeState GetNthModelThreadUpdateState(size_t n) const; - - // Reads the latest update response datas on the model thread. - // Note that if the model thread is in non-blocking mode, this data will not - // be updated until the response is actually processed by the model thread. - bool HasUpdateResponseOnModelThread(const std::string& tag) const; - UpdateResponseData GetUpdateResponseOnModelThread( - const std::string& tag) const; - - // Read the latest commit messages received on the model thread. - // Note that if the model thread is in non-blocking mode, this data will not - // be updated until the response is actually processed by the model thread. - size_t GetNumModelThreadCommitResponses() const; - CommitResponseDataList GetNthModelThreadCommitResponse(size_t n) const; - DataTypeState GetNthModelThreadCommitState(size_t n) const; - - // Reads the latest commit response datas on the model thread. - // Note that if the model thread is in non-blocking mode, this data will not - // be updated until the response is actually processed by the model thread. - bool HasCommitResponseOnModelThread(const std::string& tag) const; - CommitResponseData GetCommitResponseOnModelThread( - const std::string& tag) const; - - // Helpers for building various messages and structures. - static std::string GenerateTagHash(const std::string& tag); - static sync_pb::EntitySpecifics GenerateSpecifics(const std::string& tag, - const std::string& value); - - private: - // The NonBlockingTypeProcessorCore being tested. - scoped_ptr core_; - - // Non-owned, possibly NULL pointer. This object belongs to the - // NonBlockingTypeProcessorCore under test. - MockNonBlockingTypeProcessor* mock_processor_; - - // A mock that emulates enough of the sync server that it can be used - // a single UpdateHandler and CommitContributor pair. In this test - // harness, the |core_| is both of them. - SingleTypeMockServer mock_server_; -}; - -NonBlockingTypeProcessorCoreTest::NonBlockingTypeProcessorCoreTest() - : mock_processor_(NULL), mock_server_(kModelType) { -} - -NonBlockingTypeProcessorCoreTest::~NonBlockingTypeProcessorCoreTest() { -} - -void NonBlockingTypeProcessorCoreTest::FirstInitialize() { - DataTypeState initial_state; - initial_state.progress_marker.set_data_type_id( - GetSpecificsFieldNumberFromModelType(kModelType)); - initial_state.next_client_id = 0; - - InitializeWithState(initial_state); -} - -void NonBlockingTypeProcessorCoreTest::NormalInitialize() { - DataTypeState initial_state; - initial_state.progress_marker.set_data_type_id( - GetSpecificsFieldNumberFromModelType(kModelType)); - initial_state.progress_marker.set_token("some_saved_progress_token"); - - initial_state.next_client_id = 10; - initial_state.type_root_id = kTypeParentId; - initial_state.initial_sync_done = true; - - InitializeWithState(initial_state); -} - -void NonBlockingTypeProcessorCoreTest::InitializeWithState( - const DataTypeState& state) { - DCHECK(!core_); - - // We don't get to own this interace. The |core_| keeps a scoped_ptr to it. - mock_processor_ = new MockNonBlockingTypeProcessor(); - scoped_ptr interface(mock_processor_); - - core_.reset( - new NonBlockingTypeProcessorCore(kModelType, state, interface.Pass())); -} - -void NonBlockingTypeProcessorCoreTest::CommitRequest(const std::string& name, - const std::string& value) { - const std::string tag_hash = GenerateTagHash(name); - CommitRequestData data = - mock_processor_->CommitRequest(tag_hash, GenerateSpecifics(name, value)); - CommitRequestDataList list; - list.push_back(data); - core_->EnqueueForCommit(list); -} - -void NonBlockingTypeProcessorCoreTest::DeleteRequest(const std::string& tag) { - const std::string tag_hash = GenerateTagHash(tag); - CommitRequestData data = mock_processor_->DeleteRequest(tag_hash); - CommitRequestDataList list; - list.push_back(data); - core_->EnqueueForCommit(list); -} - -void NonBlockingTypeProcessorCoreTest::TriggerTypeRootUpdateFromServer() { - sync_pb::SyncEntity entity = mock_server_.TypeRootUpdate(); - SyncEntityList entity_list; - entity_list.push_back(&entity); - - sessions::StatusController dummy_status; - - core_->ProcessGetUpdatesResponse(mock_server_.GetProgress(), - mock_server_.GetContext(), - entity_list, - &dummy_status); - core_->ApplyUpdates(&dummy_status); -} - -void NonBlockingTypeProcessorCoreTest::TriggerUpdateFromServer( - int64 version_offset, - const std::string& tag, - const std::string& value) { - sync_pb::SyncEntity entity = mock_server_.UpdateFromServer( - version_offset, GenerateTagHash(tag), GenerateSpecifics(tag, value)); - SyncEntityList entity_list; - entity_list.push_back(&entity); - - sessions::StatusController dummy_status; - - core_->ProcessGetUpdatesResponse(mock_server_.GetProgress(), - mock_server_.GetContext(), - entity_list, - &dummy_status); - core_->ApplyUpdates(&dummy_status); -} - -void NonBlockingTypeProcessorCoreTest::TriggerTombstoneFromServer( - int64 version_offset, - const std::string& tag) { - sync_pb::SyncEntity entity = - mock_server_.TombstoneFromServer(version_offset, GenerateTagHash(tag)); - SyncEntityList entity_list; - entity_list.push_back(&entity); - - sessions::StatusController dummy_status; - - core_->ProcessGetUpdatesResponse(mock_server_.GetProgress(), - mock_server_.GetContext(), - entity_list, - &dummy_status); - core_->ApplyUpdates(&dummy_status); -} - -void NonBlockingTypeProcessorCoreTest::SetModelThreadIsSynchronous( - bool is_synchronous) { - mock_processor_->SetSynchronousExecution(is_synchronous); -} - -void NonBlockingTypeProcessorCoreTest::PumpModelThread() { - mock_processor_->RunQueuedTasks(); -} - -bool NonBlockingTypeProcessorCoreTest::WillCommit() { - scoped_ptr contribution(core_->GetContribution(INT_MAX)); - - if (contribution) { - contribution->CleanUp(); // Gracefully abort the commit. - return true; - } else { - return false; - } -} - -// Conveniently, this is all one big synchronous operation. The sync thread -// remains blocked while the commit is in progress, so we don't need to worry -// about other tasks being run between the time when the commit request is -// issued and the time when the commit response is received. -void NonBlockingTypeProcessorCoreTest::DoSuccessfulCommit() { - DCHECK(WillCommit()); - scoped_ptr contribution(core_->GetContribution(INT_MAX)); - - sync_pb::ClientToServerMessage message; - contribution->AddToCommitMessage(&message); - - sync_pb::ClientToServerResponse response = - mock_server_.DoSuccessfulCommit(message); - - sessions::StatusController dummy_status; - contribution->ProcessCommitResponse(response, &dummy_status); - contribution->CleanUp(); -} - -size_t NonBlockingTypeProcessorCoreTest::GetNumCommitMessagesOnServer() const { - return mock_server_.GetNumCommitMessages(); -} - -sync_pb::ClientToServerMessage -NonBlockingTypeProcessorCoreTest::GetNthCommitMessageOnServer(size_t n) const { - DCHECK_LT(n, GetNumCommitMessagesOnServer()); - return mock_server_.GetNthCommitMessage(n); -} - -bool NonBlockingTypeProcessorCoreTest::HasCommitEntityOnServer( - const std::string& tag) const { - const std::string tag_hash = GenerateTagHash(tag); - return mock_server_.HasCommitEntity(tag_hash); -} - -sync_pb::SyncEntity -NonBlockingTypeProcessorCoreTest::GetLatestCommitEntityOnServer( - const std::string& tag) const { - DCHECK(HasCommitEntityOnServer(tag)); - const std::string tag_hash = GenerateTagHash(tag); - return mock_server_.GetLastCommittedEntity(tag_hash); -} - -size_t NonBlockingTypeProcessorCoreTest::GetNumModelThreadUpdateResponses() - const { - return mock_processor_->GetNumUpdateResponses(); -} - -UpdateResponseDataList -NonBlockingTypeProcessorCoreTest::GetNthModelThreadUpdateResponse( - size_t n) const { - DCHECK_LT(n, GetNumModelThreadUpdateResponses()); - return mock_processor_->GetNthUpdateResponse(n); -} - -DataTypeState NonBlockingTypeProcessorCoreTest::GetNthModelThreadUpdateState( - size_t n) const { - DCHECK_LT(n, GetNumModelThreadUpdateResponses()); - return mock_processor_->GetNthTypeStateReceivedInUpdateResponse(n); -} - -bool NonBlockingTypeProcessorCoreTest::HasUpdateResponseOnModelThread( - const std::string& tag) const { - const std::string tag_hash = GenerateTagHash(tag); - return mock_processor_->HasUpdateResponse(tag_hash); -} - -UpdateResponseData -NonBlockingTypeProcessorCoreTest::GetUpdateResponseOnModelThread( - const std::string& tag) const { - const std::string tag_hash = GenerateTagHash(tag); - return mock_processor_->GetUpdateResponse(tag_hash); -} - -size_t NonBlockingTypeProcessorCoreTest::GetNumModelThreadCommitResponses() - const { - return mock_processor_->GetNumCommitResponses(); -} - -CommitResponseDataList -NonBlockingTypeProcessorCoreTest::GetNthModelThreadCommitResponse( - size_t n) const { - DCHECK_LT(n, GetNumModelThreadCommitResponses()); - return mock_processor_->GetNthCommitResponse(n); -} - -DataTypeState NonBlockingTypeProcessorCoreTest::GetNthModelThreadCommitState( - size_t n) const { - DCHECK_LT(n, GetNumModelThreadCommitResponses()); - return mock_processor_->GetNthTypeStateReceivedInCommitResponse(n); -} - -bool NonBlockingTypeProcessorCoreTest::HasCommitResponseOnModelThread( - const std::string& tag) const { - const std::string tag_hash = GenerateTagHash(tag); - return mock_processor_->HasCommitResponse(tag_hash); -} - -CommitResponseData -NonBlockingTypeProcessorCoreTest::GetCommitResponseOnModelThread( - const std::string& tag) const { - DCHECK(HasCommitResponseOnModelThread(tag)); - const std::string tag_hash = GenerateTagHash(tag); - return mock_processor_->GetCommitResponse(tag_hash); -} - -std::string NonBlockingTypeProcessorCoreTest::GenerateTagHash( - const std::string& tag) { - const std::string& client_tag_hash = - syncable::GenerateSyncableHash(kModelType, tag); - return client_tag_hash; -} - -sync_pb::EntitySpecifics NonBlockingTypeProcessorCoreTest::GenerateSpecifics( - const std::string& tag, - const std::string& value) { - sync_pb::EntitySpecifics specifics; - specifics.mutable_preference()->set_name(tag); - specifics.mutable_preference()->set_value(value); - return specifics; -} - -// Requests a commit and verifies the messages sent to the client and server as -// a result. -// -// This test performs sanity checks on most of the fields in these messages. -// For the most part this is checking that the test code behaves as expected -// and the |core_| doesn't mess up its simple task of moving around these -// values. It makes sense to have one or two tests that are this thorough, but -// we shouldn't be this verbose in all tests. -TEST_F(NonBlockingTypeProcessorCoreTest, SimpleCommit) { - NormalInitialize(); - - EXPECT_FALSE(WillCommit()); - EXPECT_EQ(0U, GetNumCommitMessagesOnServer()); - EXPECT_EQ(0U, GetNumModelThreadCommitResponses()); - - CommitRequest("tag1", "value1"); - - ASSERT_TRUE(WillCommit()); - DoSuccessfulCommit(); - - const std::string& client_tag_hash = GenerateTagHash("tag1"); - - // Exhaustively verify the SyncEntity sent in the commit message. - ASSERT_EQ(1U, GetNumCommitMessagesOnServer()); - EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size()); - ASSERT_TRUE(HasCommitEntityOnServer("tag1")); - const sync_pb::SyncEntity& entity = GetLatestCommitEntityOnServer("tag1"); - EXPECT_FALSE(entity.id_string().empty()); - EXPECT_EQ(kTypeParentId, entity.parent_id_string()); - EXPECT_EQ(kUncommittedVersion, entity.version()); - EXPECT_NE(0, entity.mtime()); - EXPECT_NE(0, entity.ctime()); - EXPECT_FALSE(entity.name().empty()); - EXPECT_EQ(client_tag_hash, entity.client_defined_unique_tag()); - EXPECT_EQ("tag1", entity.specifics().preference().name()); - EXPECT_FALSE(entity.deleted()); - EXPECT_EQ("value1", entity.specifics().preference().value()); - - // Exhaustively verify the commit response returned to the model thread. - ASSERT_EQ(1U, GetNumModelThreadCommitResponses()); - EXPECT_EQ(1U, GetNthModelThreadCommitResponse(0).size()); - ASSERT_TRUE(HasCommitResponseOnModelThread("tag1")); - const CommitResponseData& commit_response = - GetCommitResponseOnModelThread("tag1"); - - // The ID changes in a commit response to initial commit. - EXPECT_FALSE(commit_response.id.empty()); - EXPECT_NE(entity.id_string(), commit_response.id); - - EXPECT_EQ(client_tag_hash, commit_response.client_tag_hash); - EXPECT_LT(0, commit_response.response_version); -} - -TEST_F(NonBlockingTypeProcessorCoreTest, SimpleDelete) { - NormalInitialize(); - - // We can't delete an entity that was never committed. - // Step 1 is to create and commit a new entity. - CommitRequest("tag1", "value1"); - ASSERT_TRUE(WillCommit()); - DoSuccessfulCommit(); - - ASSERT_TRUE(HasCommitResponseOnModelThread("tag1")); - const CommitResponseData& initial_commit_response = - GetCommitResponseOnModelThread("tag1"); - int64 base_version = initial_commit_response.response_version; - - // Now that we have an entity, we can delete it. - DeleteRequest("tag1"); - ASSERT_TRUE(WillCommit()); - DoSuccessfulCommit(); - - // Verify the SyncEntity sent in the commit message. - ASSERT_EQ(2U, GetNumCommitMessagesOnServer()); - EXPECT_EQ(1, GetNthCommitMessageOnServer(1).commit().entries_size()); - ASSERT_TRUE(HasCommitEntityOnServer("tag1")); - const sync_pb::SyncEntity& entity = GetLatestCommitEntityOnServer("tag1"); - EXPECT_FALSE(entity.id_string().empty()); - EXPECT_EQ(GenerateTagHash("tag1"), entity.client_defined_unique_tag()); - EXPECT_EQ(base_version, entity.version()); - EXPECT_TRUE(entity.deleted()); - - // Deletions should contain enough specifics to identify the type. - EXPECT_TRUE(entity.has_specifics()); - EXPECT_EQ(kModelType, GetModelTypeFromSpecifics(entity.specifics())); - - // Verify the commit response returned to the model thread. - ASSERT_EQ(2U, GetNumModelThreadCommitResponses()); - EXPECT_EQ(1U, GetNthModelThreadCommitResponse(1).size()); - ASSERT_TRUE(HasCommitResponseOnModelThread("tag1")); - const CommitResponseData& commit_response = - GetCommitResponseOnModelThread("tag1"); - - EXPECT_EQ(entity.id_string(), commit_response.id); - EXPECT_EQ(entity.client_defined_unique_tag(), - commit_response.client_tag_hash); - EXPECT_EQ(entity.version(), commit_response.response_version); -} - -// The server doesn't like it when we try to delete an entity it's never heard -// of before. This test helps ensure we avoid that scenario. -TEST_F(NonBlockingTypeProcessorCoreTest, NoDeleteUncommitted) { - NormalInitialize(); - - // Request the commit of a new, never-before-seen item. - CommitRequest("tag1", "value1"); - EXPECT_TRUE(WillCommit()); - - // Request a deletion of that item before we've had a chance to commit it. - DeleteRequest("tag1"); - EXPECT_FALSE(WillCommit()); -} - -// Verifies the sending of an "initial sync done" signal. -TEST_F(NonBlockingTypeProcessorCoreTest, SendInitialSyncDone) { - FirstInitialize(); // Initialize with no saved sync state. - EXPECT_EQ(0U, GetNumModelThreadUpdateResponses()); - - // Receive an update response that contains only the type root node. - TriggerTypeRootUpdateFromServer(); - - // Two updates: - // - One triggered by process updates to forward the type root ID. - // - One triggered by apply updates, which the core interprets to mean - // "initial sync done". This triggers a model thread update, too. - EXPECT_EQ(2U, GetNumModelThreadUpdateResponses()); - - // The type root and initial sync done updates both contain no entities. - EXPECT_EQ(0U, GetNthModelThreadUpdateResponse(0).size()); - EXPECT_EQ(0U, GetNthModelThreadUpdateResponse(1).size()); - - const DataTypeState& state = GetNthModelThreadUpdateState(1); - EXPECT_FALSE(state.progress_marker.token().empty()); - EXPECT_FALSE(state.type_root_id.empty()); - EXPECT_TRUE(state.initial_sync_done); -} - -// Commit two new entities in two separate commit messages. -TEST_F(NonBlockingTypeProcessorCoreTest, TwoNewItemsCommittedSeparately) { - NormalInitialize(); - - // Commit the first of two entities. - CommitRequest("tag1", "value1"); - ASSERT_TRUE(WillCommit()); - DoSuccessfulCommit(); - ASSERT_EQ(1U, GetNumCommitMessagesOnServer()); - EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size()); - ASSERT_TRUE(HasCommitEntityOnServer("tag1")); - const sync_pb::SyncEntity& tag1_entity = - GetLatestCommitEntityOnServer("tag1"); - - // Commit the second of two entities. - CommitRequest("tag2", "value2"); - ASSERT_TRUE(WillCommit()); - DoSuccessfulCommit(); - ASSERT_EQ(2U, GetNumCommitMessagesOnServer()); - EXPECT_EQ(1, GetNthCommitMessageOnServer(1).commit().entries_size()); - ASSERT_TRUE(HasCommitEntityOnServer("tag2")); - const sync_pb::SyncEntity& tag2_entity = - GetLatestCommitEntityOnServer("tag2"); - - EXPECT_FALSE(WillCommit()); - - // The IDs assigned by the |core_| should be unique. - EXPECT_NE(tag1_entity.id_string(), tag2_entity.id_string()); - - // Check that the committed specifics values are sane. - EXPECT_EQ(tag1_entity.specifics().preference().value(), "value1"); - EXPECT_EQ(tag2_entity.specifics().preference().value(), "value2"); - - // There should have been two separate commit responses sent to the model - // thread. They should be uninteresting, so we don't bother inspecting them. - EXPECT_EQ(2U, GetNumModelThreadCommitResponses()); -} - -TEST_F(NonBlockingTypeProcessorCoreTest, ReceiveUpdates) { - NormalInitialize(); - - const std::string& tag_hash = GenerateTagHash("tag1"); - - TriggerUpdateFromServer(10, "tag1", "value1"); - - ASSERT_EQ(1U, GetNumModelThreadUpdateResponses()); - UpdateResponseDataList updates_list = GetNthModelThreadUpdateResponse(0); - ASSERT_EQ(1U, updates_list.size()); - - ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1")); - UpdateResponseData update = GetUpdateResponseOnModelThread("tag1"); - - EXPECT_FALSE(update.id.empty()); - EXPECT_EQ(tag_hash, update.client_tag_hash); - EXPECT_LT(0, update.response_version); - EXPECT_FALSE(update.ctime.is_null()); - EXPECT_FALSE(update.mtime.is_null()); - EXPECT_FALSE(update.non_unique_name.empty()); - EXPECT_FALSE(update.deleted); - EXPECT_EQ("tag1", update.specifics.preference().name()); - EXPECT_EQ("value1", update.specifics.preference().value()); -} - -} // namespace syncer diff --git a/sync/engine/non_blocking_type_processor_interface.cc b/sync/engine/non_blocking_type_processor_interface.cc deleted file mode 100644 index b936239..0000000 --- a/sync/engine/non_blocking_type_processor_interface.cc +++ /dev/null @@ -1,15 +0,0 @@ -// 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/engine/non_blocking_type_processor_interface.h" - -namespace syncer { - -NonBlockingTypeProcessorInterface::NonBlockingTypeProcessorInterface() { -} - -NonBlockingTypeProcessorInterface::~NonBlockingTypeProcessorInterface() { -} - -} // namespace syncer diff --git a/sync/engine/non_blocking_type_processor_interface.h b/sync/engine/non_blocking_type_processor_interface.h deleted file mode 100644 index bafd141..0000000 --- a/sync/engine/non_blocking_type_processor_interface.h +++ /dev/null @@ -1,28 +0,0 @@ -// 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_ENGINE_NON_BLOCKING_TYPE_PROCESSOR_INTERFACE_H_ -#define SYNC_ENGINE_NON_BLOCKING_TYPE_PROCESSOR_INTERFACE_H_ - -#include "sync/base/sync_export.h" -#include "sync/engine/non_blocking_sync_common.h" - -namespace syncer { - -class SYNC_EXPORT_PRIVATE NonBlockingTypeProcessorInterface { - public: - NonBlockingTypeProcessorInterface(); - virtual ~NonBlockingTypeProcessorInterface(); - - virtual void ReceiveCommitResponse( - const DataTypeState& type_state, - const CommitResponseDataList& response_list) = 0; - virtual void ReceiveUpdateResponse( - const DataTypeState& type_state, - const UpdateResponseDataList& response_list) = 0; -}; - -} // namespace syncer - -#endif // SYNC_ENGINE_NON_BLOCKING_TYPE_PROCESSOR_INTERFACE_H_ diff --git a/sync/engine/non_blocking_type_processor_unittest.cc b/sync/engine/non_blocking_type_processor_unittest.cc deleted file mode 100644 index 2c42afc..0000000 --- a/sync/engine/non_blocking_type_processor_unittest.cc +++ /dev/null @@ -1,464 +0,0 @@ -// 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/engine/non_blocking_type_processor.h" - -#include "sync/engine/non_blocking_sync_common.h" -#include "sync/engine/non_blocking_type_processor_core_interface.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/internal_api/public/sync_core_proxy.h" -#include "sync/protocol/sync.pb.h" -#include "sync/syncable/syncable_util.h" -#include "sync/test/engine/injectable_sync_core_proxy.h" -#include "sync/test/engine/mock_non_blocking_type_processor_core.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace syncer { - -static const ModelType kModelType = PREFERENCES; - -// Tests the sync engine parts of NonBlockingTypeProcessor. -// -// The NonBlockingTypeProcessor contains a non-trivial amount of code dedicated -// to turning the sync engine on and off again. That code is fairly well -// tested in the NonBlockingDataTypeController unit tests and it doesn't need -// to be re-tested here. -// -// These tests skip past initialization and focus on steady state sync engine -// behvior. This is where we test how the processor responds to the model's -// requests to make changes to its data, the messages incoming fro the sync -// server, and what happens when the two conflict. -// -// Inputs: -// - Initial state from permanent storage. (TODO) -// - Create, update or delete requests from the model. -// - Update responses and commit responses from the server. -// -// Outputs: -// - Writes to permanent storage. (TODO) -// - Callbacks into the model. (TODO) -// - Requests to the sync thread. Tested with MockNonBlockingTypeProcessorCore. -class NonBlockingTypeProcessorTest : public ::testing::Test { - public: - NonBlockingTypeProcessorTest(); - virtual ~NonBlockingTypeProcessorTest(); - - // Initialize with no local state. The processor will be unable to commit - // until it receives notification that initial sync has completed. - void FirstTimeInitialize(); - - // Initialize to a "ready-to-commit" state. - void InitializeToReadyState(); - - // Disconnect the NonBlockingTypeProcessorCore from our - // NonBlockingTypeProcessor. - void Disconnect(); - - // Disable sync for this NonBlockingTypeProcessor. Should cause sync state - // to be discarded. - void Disable(); - - // Re-enable sync after Disconnect() or Disable(). - void ReEnable(); - - // Local data modification. Emulates signals from the model thread. - void WriteItem(const std::string& tag, const std::string& value); - void DeleteItem(const std::string& tag); - - // Emulates an "initial sync done" message from the - // NonBlockingTypeProcessorCore. - void OnInitialSyncDone(); - - // Emulate updates from the server. - // This harness has some functionality to help emulate server behavior. - // See the definitions of these methods for more information. - void UpdateFromServer(int64 version_offset, - const std::string& tag, - const std::string& value); - void TombstoneFromServer(int64 version_offset, const std::string& tag); - - // Read emitted commit requests as batches. - size_t GetNumCommitRequestLists(); - CommitRequestDataList GetNthCommitRequestList(size_t n); - - // Read emitted commit requests by tag, most recent only. - bool HasCommitRequestForTag(const std::string& tag); - CommitRequestData GetLatestCommitRequestForTag(const std::string& tag); - - // Sends the processor a successful commit response. - void SuccessfulCommitResponse(const CommitRequestData& request_data); - - private: - static std::string GenerateTagHash(const std::string& tag); - static sync_pb::EntitySpecifics GenerateSpecifics(const std::string& tag, - const std::string& value); - - int64 GetServerVersion(const std::string& tag); - void SetServerVersion(const std::string& tag, int64 version); - - MockNonBlockingTypeProcessorCore* mock_processor_core_; - scoped_ptr injectable_sync_core_proxy_; - scoped_ptr processor_; - - DataTypeState data_type_state_; -}; - -NonBlockingTypeProcessorTest::NonBlockingTypeProcessorTest() - : mock_processor_core_(new MockNonBlockingTypeProcessorCore()), - injectable_sync_core_proxy_( - new InjectableSyncCoreProxy(mock_processor_core_)), - processor_(new NonBlockingTypeProcessor(kModelType)) { -} - -NonBlockingTypeProcessorTest::~NonBlockingTypeProcessorTest() { -} - -void NonBlockingTypeProcessorTest::FirstTimeInitialize() { - processor_->Enable(injectable_sync_core_proxy_->Clone()); -} - -void NonBlockingTypeProcessorTest::InitializeToReadyState() { - // TODO(rlarocque): This should be updated to inject on-disk state. - // At the time this code was written, there was no support for on-disk - // state so this was the only way to inject a data_type_state into - // the |processor_|. - FirstTimeInitialize(); - OnInitialSyncDone(); -} - -void NonBlockingTypeProcessorTest::Disconnect() { - processor_->Disconnect(); - injectable_sync_core_proxy_.reset(); - mock_processor_core_ = NULL; -} - -void NonBlockingTypeProcessorTest::Disable() { - processor_->Disable(); - injectable_sync_core_proxy_.reset(); - mock_processor_core_ = NULL; -} - -void NonBlockingTypeProcessorTest::ReEnable() { - DCHECK(!processor_->IsConnected()); - - // Prepare a new NonBlockingTypeProcesorCore instance, just as we would - // if this happened in the real world. - mock_processor_core_ = new MockNonBlockingTypeProcessorCore(); - injectable_sync_core_proxy_.reset( - new InjectableSyncCoreProxy(mock_processor_core_)); - - // Re-enable sync with the new NonBlockingTypeProcessorCore. - processor_->Enable(injectable_sync_core_proxy_->Clone()); -} - -void NonBlockingTypeProcessorTest::WriteItem(const std::string& tag, - const std::string& value) { - const std::string tag_hash = GenerateTagHash(tag); - processor_->Put(tag, GenerateSpecifics(tag, value)); -} - -void NonBlockingTypeProcessorTest::DeleteItem(const std::string& tag) { - processor_->Delete(tag); -} - -void NonBlockingTypeProcessorTest::OnInitialSyncDone() { - data_type_state_.initial_sync_done = true; - UpdateResponseDataList empty_update_list; - - processor_->OnUpdateReceived(data_type_state_, empty_update_list); -} - -void NonBlockingTypeProcessorTest::UpdateFromServer(int64 version_offset, - const std::string& tag, - const std::string& value) { - const std::string tag_hash = GenerateTagHash(tag); - UpdateResponseData data = mock_processor_core_->UpdateFromServer( - version_offset, tag_hash, GenerateSpecifics(tag, value)); - - UpdateResponseDataList list; - list.push_back(data); - processor_->OnUpdateReceived(data_type_state_, list); -} - -void NonBlockingTypeProcessorTest::TombstoneFromServer(int64 version_offset, - const std::string& tag) { - // Overwrite the existing server version if this is the new highest version. - std::string tag_hash = GenerateTagHash(tag); - - UpdateResponseData data = - mock_processor_core_->TombstoneFromServer(version_offset, tag_hash); - - UpdateResponseDataList list; - list.push_back(data); - processor_->OnUpdateReceived(data_type_state_, list); -} - -void NonBlockingTypeProcessorTest::SuccessfulCommitResponse( - const CommitRequestData& request_data) { - CommitResponseDataList list; - list.push_back(mock_processor_core_->SuccessfulCommitResponse(request_data)); - processor_->OnCommitCompletion(data_type_state_, list); -} - -std::string NonBlockingTypeProcessorTest::GenerateTagHash( - const std::string& tag) { - return syncable::GenerateSyncableHash(kModelType, tag); -} - -sync_pb::EntitySpecifics NonBlockingTypeProcessorTest::GenerateSpecifics( - const std::string& tag, - const std::string& value) { - sync_pb::EntitySpecifics specifics; - specifics.mutable_preference()->set_name(tag); - specifics.mutable_preference()->set_value(value); - return specifics; -} - -size_t NonBlockingTypeProcessorTest::GetNumCommitRequestLists() { - return mock_processor_core_->GetNumCommitRequestLists(); -} - -CommitRequestDataList NonBlockingTypeProcessorTest::GetNthCommitRequestList( - size_t n) { - return mock_processor_core_->GetNthCommitRequestList(n); -} - -bool NonBlockingTypeProcessorTest::HasCommitRequestForTag( - const std::string& tag) { - const std::string tag_hash = GenerateTagHash(tag); - return mock_processor_core_->HasCommitRequestForTagHash(tag_hash); -} - -CommitRequestData NonBlockingTypeProcessorTest::GetLatestCommitRequestForTag( - const std::string& tag) { - const std::string tag_hash = GenerateTagHash(tag); - return mock_processor_core_->GetLatestCommitRequestForTagHash(tag_hash); -} - -// Creates a new item locally. -// Thoroughly tests the data generated by a local item creation. -TEST_F(NonBlockingTypeProcessorTest, CreateLocalItem) { - InitializeToReadyState(); - EXPECT_EQ(0U, GetNumCommitRequestLists()); - - WriteItem("tag1", "value1"); - - // Verify the commit request this operation has triggered. - EXPECT_EQ(1U, GetNumCommitRequestLists()); - ASSERT_TRUE(HasCommitRequestForTag("tag1")); - const CommitRequestData& tag1_data = GetLatestCommitRequestForTag("tag1"); - - EXPECT_TRUE(tag1_data.id.empty()); - EXPECT_EQ(kUncommittedVersion, tag1_data.base_version); - EXPECT_FALSE(tag1_data.ctime.is_null()); - EXPECT_FALSE(tag1_data.mtime.is_null()); - EXPECT_EQ("tag1", tag1_data.non_unique_name); - EXPECT_FALSE(tag1_data.deleted); - EXPECT_EQ("tag1", tag1_data.specifics.preference().name()); - EXPECT_EQ("value1", tag1_data.specifics.preference().value()); -} - -// Creates a new local item then modifies it. -// Thoroughly tests data generated by modification of server-unknown item. -TEST_F(NonBlockingTypeProcessorTest, CreateAndModifyLocalItem) { - InitializeToReadyState(); - EXPECT_EQ(0U, GetNumCommitRequestLists()); - - WriteItem("tag1", "value1"); - EXPECT_EQ(1U, GetNumCommitRequestLists()); - ASSERT_TRUE(HasCommitRequestForTag("tag1")); - const CommitRequestData& tag1_v1_data = GetLatestCommitRequestForTag("tag1"); - - WriteItem("tag1", "value2"); - EXPECT_EQ(2U, GetNumCommitRequestLists()); - - ASSERT_TRUE(HasCommitRequestForTag("tag1")); - const CommitRequestData& tag1_v2_data = GetLatestCommitRequestForTag("tag1"); - - // Test some of the relations between old and new commit requests. - EXPECT_EQ(tag1_v1_data.specifics.preference().value(), "value1"); - EXPECT_GT(tag1_v2_data.sequence_number, tag1_v1_data.sequence_number); - - // Perform a thorough examination of the update-generated request. - EXPECT_TRUE(tag1_v2_data.id.empty()); - EXPECT_EQ(kUncommittedVersion, tag1_v2_data.base_version); - EXPECT_FALSE(tag1_v2_data.ctime.is_null()); - EXPECT_FALSE(tag1_v2_data.mtime.is_null()); - EXPECT_EQ("tag1", tag1_v2_data.non_unique_name); - EXPECT_FALSE(tag1_v2_data.deleted); - EXPECT_EQ("tag1", tag1_v2_data.specifics.preference().name()); - EXPECT_EQ("value2", tag1_v2_data.specifics.preference().value()); -} - -// Deletes an item we've never seen before. -// Should have no effect and not crash. -TEST_F(NonBlockingTypeProcessorTest, DeleteUnknown) { - InitializeToReadyState(); - - DeleteItem("tag1"); - EXPECT_EQ(0U, GetNumCommitRequestLists()); -} - -// Creates an item locally then deletes it. -// -// In this test, no commit responses are received, so the deleted item is -// server-unknown as far as the model thread is concerned. That behavior -// is race-dependent; other tests are used to test other races. -TEST_F(NonBlockingTypeProcessorTest, DeleteServerUnknown) { - InitializeToReadyState(); - - WriteItem("tag1", "value1"); - EXPECT_EQ(1U, GetNumCommitRequestLists()); - ASSERT_TRUE(HasCommitRequestForTag("tag1")); - const CommitRequestData& tag1_v1_data = GetLatestCommitRequestForTag("tag1"); - - DeleteItem("tag1"); - EXPECT_EQ(2U, GetNumCommitRequestLists()); - ASSERT_TRUE(HasCommitRequestForTag("tag1")); - const CommitRequestData& tag1_v2_data = GetLatestCommitRequestForTag("tag1"); - - EXPECT_GT(tag1_v2_data.sequence_number, tag1_v1_data.sequence_number); - - EXPECT_TRUE(tag1_v2_data.id.empty()); - EXPECT_EQ(kUncommittedVersion, tag1_v2_data.base_version); - EXPECT_TRUE(tag1_v2_data.deleted); -} - -// Creates an item locally then deletes it. -// -// The item is created locally then enqueued for commit. The sync thread -// successfully commits it, but, before the commit response is picked up -// by the model thread, the item is deleted by the model thread. -TEST_F(NonBlockingTypeProcessorTest, DeleteServerUnknown_RacyCommitResponse) { - InitializeToReadyState(); - - WriteItem("tag1", "value1"); - EXPECT_EQ(1U, GetNumCommitRequestLists()); - ASSERT_TRUE(HasCommitRequestForTag("tag1")); - const CommitRequestData& tag1_v1_data = GetLatestCommitRequestForTag("tag1"); - - DeleteItem("tag1"); - EXPECT_EQ(2U, GetNumCommitRequestLists()); - ASSERT_TRUE(HasCommitRequestForTag("tag1")); - - // This commit happened while the deletion was in progress, but the commit - // response didn't arrive on our thread until after the delete was issued to - // the sync thread. It will update some metadata, but won't do much else. - SuccessfulCommitResponse(tag1_v1_data); - - // TODO(rlarocque): Verify the state of the item is correct once we get - // storage hooked up in these tests. For example, verify the item is still - // marked as deleted. -} - -// Creates two different sync items. -// Verifies that the second has no effect on the first. -TEST_F(NonBlockingTypeProcessorTest, TwoIndependentItems) { - InitializeToReadyState(); - EXPECT_EQ(0U, GetNumCommitRequestLists()); - - WriteItem("tag1", "value1"); - - // There should be one commit request for this item only. - ASSERT_EQ(1U, GetNumCommitRequestLists()); - EXPECT_EQ(1U, GetNthCommitRequestList(0).size()); - ASSERT_TRUE(HasCommitRequestForTag("tag1")); - - WriteItem("tag2", "value2"); - - // The second write should trigger another single-item commit request. - ASSERT_EQ(2U, GetNumCommitRequestLists()); - EXPECT_EQ(1U, GetNthCommitRequestList(1).size()); - ASSERT_TRUE(HasCommitRequestForTag("tag2")); -} - -// Starts the processor with no local state. -// Verify that it waits until initial sync is complete before requesting -// commits. -TEST_F(NonBlockingTypeProcessorTest, NoCommitsUntilInitialSyncDone) { - FirstTimeInitialize(); - - WriteItem("tag1", "value1"); - EXPECT_EQ(0U, GetNumCommitRequestLists()); - - OnInitialSyncDone(); - EXPECT_EQ(1U, GetNumCommitRequestLists()); - EXPECT_TRUE(HasCommitRequestForTag("tag1")); -} - -// Test proper handling of disconnect and reconnect. -// -// Creates items in various states of commit and verifies they re-attempt to -// commit on reconnect. -TEST_F(NonBlockingTypeProcessorTest, Disconnect) { - InitializeToReadyState(); - - // The first item is fully committed. - WriteItem("tag1", "value1"); - ASSERT_TRUE(HasCommitRequestForTag("tag1")); - SuccessfulCommitResponse(GetLatestCommitRequestForTag("tag1")); - - // The second item has a commit request in progress. - WriteItem("tag2", "value2"); - EXPECT_TRUE(HasCommitRequestForTag("tag2")); - - Disconnect(); - - // The third item is added after disconnection. - WriteItem("tag3", "value3"); - - ReEnable(); - - EXPECT_EQ(1U, GetNumCommitRequestLists()); - EXPECT_EQ(2U, GetNthCommitRequestList(0).size()); - - // The first item was already in sync. - EXPECT_FALSE(HasCommitRequestForTag("tag1")); - - // The second item's commit was interrupted and should be retried. - EXPECT_TRUE(HasCommitRequestForTag("tag2")); - - // The third item's commit was not started until the reconnect. - EXPECT_TRUE(HasCommitRequestForTag("tag3")); -} - -// Test proper handling of disable and re-enable. -// -// Creates items in various states of commit and verifies they re-attempt to -// commit on re-enable. -TEST_F(NonBlockingTypeProcessorTest, Disable) { - InitializeToReadyState(); - - // The first item is fully committed. - WriteItem("tag1", "value1"); - ASSERT_TRUE(HasCommitRequestForTag("tag1")); - SuccessfulCommitResponse(GetLatestCommitRequestForTag("tag1")); - - // The second item has a commit request in progress. - WriteItem("tag2", "value2"); - EXPECT_TRUE(HasCommitRequestForTag("tag2")); - - Disable(); - - // The third item is added after disable. - WriteItem("tag3", "value3"); - - // Now we re-enable. - ReEnable(); - - // There should be nothing to commit right away, since we need to - // re-initialize the client state first. - EXPECT_EQ(0U, GetNumCommitRequestLists()); - - // Once we're ready to commit, all three local items should consider - // themselves uncommitted and pending for commit. - OnInitialSyncDone(); - EXPECT_EQ(1U, GetNumCommitRequestLists()); - EXPECT_EQ(3U, GetNthCommitRequestList(0).size()); - EXPECT_TRUE(HasCommitRequestForTag("tag1")); - EXPECT_TRUE(HasCommitRequestForTag("tag2")); - EXPECT_TRUE(HasCommitRequestForTag("tag3")); -} - -} // namespace syncer diff --git a/sync/engine/sync_thread_sync_entity.cc b/sync/engine/sync_thread_sync_entity.cc deleted file mode 100644 index 21899d2..0000000 --- a/sync/engine/sync_thread_sync_entity.cc +++ /dev/null @@ -1,242 +0,0 @@ -// 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/engine/sync_thread_sync_entity.h" - -#include "base/logging.h" -#include "sync/engine/non_blocking_sync_common.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/syncable/syncable_util.h" -#include "sync/util/time.h" - -namespace syncer { - -SyncThreadSyncEntity* SyncThreadSyncEntity::FromServerUpdate( - const std::string& id_string, - const std::string& client_tag_hash, - int64 received_version) { - return new SyncThreadSyncEntity( - id_string, client_tag_hash, 0, received_version); -} - -SyncThreadSyncEntity* SyncThreadSyncEntity::FromCommitRequest( - const std::string& id_string, - const std::string& client_tag_hash, - int64 sequence_number, - int64 base_version, - base::Time ctime, - base::Time mtime, - const std::string& non_unique_name, - bool deleted, - const sync_pb::EntitySpecifics& specifics) { - return new SyncThreadSyncEntity(id_string, - client_tag_hash, - 0, - 0, - true, - sequence_number, - base_version, - ctime, - mtime, - non_unique_name, - deleted, - specifics); -} - -// Constructor that does not set any pending commit fields. -SyncThreadSyncEntity::SyncThreadSyncEntity( - const std::string& id, - const std::string& client_tag_hash, - int64 highest_commit_response_version, - int64 highest_gu_response_version) - : id_(id), - client_tag_hash_(client_tag_hash), - highest_commit_response_version_(highest_commit_response_version), - highest_gu_response_version_(highest_gu_response_version), - is_commit_pending_(false), - sequence_number_(0), - base_version_(0), - deleted_(false) { -} - -SyncThreadSyncEntity::SyncThreadSyncEntity( - const std::string& id, - const std::string& client_tag_hash, - int64 highest_commit_response_version, - int64 highest_gu_response_version, - bool is_commit_pending, - int64 sequence_number, - int64 base_version, - base::Time ctime, - base::Time mtime, - const std::string& non_unique_name, - bool deleted, - const sync_pb::EntitySpecifics& specifics) - : id_(id), - client_tag_hash_(client_tag_hash), - highest_commit_response_version_(highest_commit_response_version), - highest_gu_response_version_(highest_gu_response_version), - is_commit_pending_(is_commit_pending), - sequence_number_(sequence_number), - base_version_(base_version), - ctime_(ctime), - mtime_(mtime), - non_unique_name_(non_unique_name), - deleted_(deleted), - specifics_(specifics) { -} - -SyncThreadSyncEntity::~SyncThreadSyncEntity() { -} - -bool SyncThreadSyncEntity::IsCommitPending() const { - return is_commit_pending_; -} - -void SyncThreadSyncEntity::PrepareCommitProto( - sync_pb::SyncEntity* commit_entity, - int64* sequence_number) const { - // Set ID if we have a server-assigned ID. Otherwise, it will be up to - // our caller to assign a client-unique initial ID. - if (base_version_ != kUncommittedVersion) { - commit_entity->set_id_string(id_); - } - - commit_entity->set_client_defined_unique_tag(client_tag_hash_); - commit_entity->set_version(base_version_); - commit_entity->set_deleted(deleted_); - commit_entity->set_folder(false); - commit_entity->set_name(non_unique_name_); - if (!deleted_) { - commit_entity->set_ctime(TimeToProtoTime(ctime_)); - commit_entity->set_mtime(TimeToProtoTime(mtime_)); - commit_entity->mutable_specifics()->CopyFrom(specifics_); - } - - *sequence_number = sequence_number_; -} - -void SyncThreadSyncEntity::RequestCommit( - const std::string& id, - const std::string& client_tag_hash, - int64 sequence_number, - int64 base_version, - base::Time ctime, - base::Time mtime, - const std::string& non_unique_name, - bool deleted, - const sync_pb::EntitySpecifics& specifics) { - DCHECK_GE(base_version, base_version_) - << "Base version should never decrease"; - - DCHECK_GE(sequence_number, sequence_number_) - << "Sequence number should never decrease"; - - // Update our book-keeping counters. - base_version_ = base_version; - sequence_number_ = sequence_number; - - // Do our counter values indicate a conflict? If so, don't commit. - // - // There's no need to inform the model thread of the conflict. The - // conflicting update has already been posted to its task runner; it will - // figure it out as soon as it runs that task. - is_commit_pending_ = true; - if (IsInConflict()) { - ClearPendingCommit(); - return; - } - - // We don't commit deletions of server-unknown items. - if (deleted && !IsServerKnown()) { - ClearPendingCommit(); - return; - } - - // Otherwise, we should store the data associated with this pending commit - // so we're ready to commit at the next possible opportunity. - - // We intentionally don't update the id_ here. Good ID values come from the - // server and always pass through the sync thread first. There's no way the - // model thread could have a better ID value than we do. - - // This entity is identified by its client tag. That value can never change. - DCHECK_EQ(client_tag_hash_, client_tag_hash); - - // Set the fields for the pending commit. - ctime_ = ctime; - mtime_ = mtime; - non_unique_name_ = non_unique_name; - deleted_ = deleted; - specifics_ = specifics; -} - -void SyncThreadSyncEntity::ReceiveCommitResponse(const std::string& response_id, - int64 response_version, - int64 sequence_number) { - // Commit responses, especially after the first commit, can update our ID. - id_ = response_id; - - DCHECK_GT(response_version, highest_commit_response_version_) - << "Had expected higher response version." - << " id: " << id_; - - // Commits are synchronous, so there's no reason why the sequence numbers - // wouldn't match. - DCHECK_EQ(sequence_number_, sequence_number) - << "Unexpected sequence number mismatch." - << " id: " << id_; - - highest_commit_response_version_ = response_version; - - // Because an in-progress commit blocks the sync thread, we can assume that - // the item we just committed successfully is exactly the one we have now. - // Nothing changed it while the commit was happening. Since we're now in - // sync with the server, we can clear the pending commit. - ClearPendingCommit(); -} - -void SyncThreadSyncEntity::ReceiveUpdate(int64 version) { - highest_gu_response_version_ = - std::max(highest_gu_response_version_, version); - - if (IsInConflict()) { - // Incoming update clobbers the pending commit on the sync thread. - // The model thread can re-request this commit later if it wants to. - ClearPendingCommit(); - } -} - -bool SyncThreadSyncEntity::IsInConflict() const { - if (!is_commit_pending_) - return false; - - if (highest_gu_response_version_ <= highest_commit_response_version_) { - // The most recent server state was created in a commit made by this - // client. We're fully up to date, and therefore not in conflict. - return false; - } else { - // The most recent server state was written by someone else. - // Did the model thread have the most up to date version when it issued the - // commit request? - if (base_version_ >= highest_gu_response_version_) { - return false; // Yes. - } else { - return true; // No. - } - } -} - -bool SyncThreadSyncEntity::IsServerKnown() const { - return base_version_ != kUncommittedVersion; -} - -void SyncThreadSyncEntity::ClearPendingCommit() { - is_commit_pending_ = false; - - // Clearing the specifics might free up some memory. It can't hurt to try. - specifics_.Clear(); -} - -} // namespace syncer diff --git a/sync/engine/sync_thread_sync_entity.h b/sync/engine/sync_thread_sync_entity.h deleted file mode 100644 index 6ed9685..0000000 --- a/sync/engine/sync_thread_sync_entity.h +++ /dev/null @@ -1,155 +0,0 @@ -// 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_ENGINE_SYNC_THREAD_SYNC_ENTITY_H_ -#define SYNC_ENGINE_SYNC_THREAD_SYNC_ENTITY_H_ - -#include - -#include "base/basictypes.h" -#include "base/time/time.h" -#include "sync/base/sync_export.h" -#include "sync/protocol/sync.pb.h" - -namespace syncer { - -// Manages the pending commit and update state for an entity on the sync -// thread. -// -// It should be considered a helper class internal to the -// NonBlockingTypeProcessorCore. -// -// Maintains the state associated with a particular sync entity which is -// necessary for decision-making on the sync thread. It can track pending -// commit state, received update state, and can detect conflicts. -// -// This object may or may not contain state associated with a pending commit. -// If no commit is pending, the |is_commit_pending_| flag will be set to false -// and many of this object's fields will be cleared. -class SYNC_EXPORT SyncThreadSyncEntity { - public: - ~SyncThreadSyncEntity(); - - // Initialize a new entity based on an update response. - static SyncThreadSyncEntity* FromServerUpdate( - const std::string& id_string, - const std::string& client_tag_hash, - int64 version); - - // Initialize a new entity based on a commit request. - static SyncThreadSyncEntity* FromCommitRequest( - const std::string& id_string, - const std::string& client_tag_hash, - int64 sequence_number, - int64 base_version, - base::Time ctime, - base::Time mtime, - const std::string& non_unique_name, - bool deleted, - const sync_pb::EntitySpecifics& specifics); - - // Returns true if this entity should be commited to the server. - bool IsCommitPending() const; - - // Populates a sync_pb::SyncEntity for a commit. Also sets the - // |sequence_number|, so we can track it throughout the commit process. - void PrepareCommitProto(sync_pb::SyncEntity* commit_entity, - int64* sequence_number) const; - - // Updates this entity with data from the latest version that the - // model asked us to commit. May clobber state related to the - // model's previous commit attempt(s). - void RequestCommit(const std::string& id, - const std::string& client_tag_hash, - int64 sequence_number, - int64 base_version, - base::Time ctime, - base::Time mtime, - const std::string& non_unique_name, - bool deleted, - const sync_pb::EntitySpecifics& specifics); - - // Handles the receipt of a commit response. - // - // Since commits happen entirely on the sync thread, we can safely assume - // that our item's state at the end of the commit is the same as it was at - // the start. - void ReceiveCommitResponse(const std::string& response_id, - int64 response_version, - int64 sequence_number); - - // Handles receipt of an update from the server. - void ReceiveUpdate(int64 version); - - private: - // Initializes received update state. Does not initialize state related to - // pending commits and sets |is_commit_pending_| to false. - SyncThreadSyncEntity(const std::string& id, - const std::string& client_tag_hash, - int64 highest_commit_response_version, - int64 highest_gu_response_version); - - // Initializes all fields. Sets |is_commit_pending_| to true. - SyncThreadSyncEntity(const std::string& id, - const std::string& client_tag_hash, - int64 highest_commit_response_version, - int64 highest_gu_response_version, - bool is_commit_pending, - int64 sequence_number, - int64 base_version, - base::Time ctime, - base::Time mtime, - const std::string& non_unique_name, - bool deleted, - const sync_pb::EntitySpecifics& specifics); - - // Checks if the current state indicates a conflict. - // - // This can be true only while a call to this object is in progress. - // Conflicts are always cleared before the method call ends. - bool IsInConflict() const; - - // Checks if the server knows about this item. - bool IsServerKnown() const; - - // Clears flag and optionally clears state associated with a pending commit. - void ClearPendingCommit(); - - // The ID for this entry. May be empty if the entry has never been committed. - std::string id_; - - // The hashed client tag for this entry. - std::string client_tag_hash_; - - // The highest version seen in a commit response for this entry. - int64 highest_commit_response_version_; - - // The highest version seen in a GU response for this entry. - int64 highest_gu_response_version_; - - // Flag that indicates whether or not we're waiting for a chance to commit - // this item. - bool is_commit_pending_; - - // Used to track in-flight commit requests on the model thread. All we need - // to do here is return it back to the model thread when the pending commit - // is completed and confirmed. Not valid if no commit is pending. - int64 sequence_number_; - - // The following fields are valid only when a commit is pending. - // This is where we store the data that is to be sent up to the server - // at the next possible opportunity. - int64 base_version_; - base::Time ctime_; - base::Time mtime_; - std::string non_unique_name_; - bool deleted_; - sync_pb::EntitySpecifics specifics_; - - DISALLOW_COPY_AND_ASSIGN(SyncThreadSyncEntity); -}; - -} // namespace syncer - -#endif // SYNC_ENGINE_SYNC_THREAD_SYNC_ENTITY_H_ diff --git a/sync/engine/sync_thread_sync_entity_unittest.cc b/sync/engine/sync_thread_sync_entity_unittest.cc deleted file mode 100644 index be2604b..0000000 --- a/sync/engine/sync_thread_sync_entity_unittest.cc +++ /dev/null @@ -1,164 +0,0 @@ - -// 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/engine/sync_thread_sync_entity.h" - -#include "base/memory/scoped_ptr.h" -#include "base/time/time.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/syncable/syncable_util.h" -#include "sync/util/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace syncer { - -// Some simple tests for the SyncThreadSyncEntity. -// -// The SyncThreadSyncEntity is an implementation detail of the -// NonBlockingTypeProcessorCore. As such, it doesn't make much sense to test -// it exhaustively, since it already gets a lot of test coverage from the -// NonBlockingTypeProcessorCore unit tests. -// -// These tests are intended as a basic sanity check. Anything more complicated -// would be redundant. -class SyncThreadSyncEntityTest : public ::testing::Test { - public: - SyncThreadSyncEntityTest() - : kServerId("ServerID"), - kClientTag("some.sample.tag"), - kClientTagHash(syncable::GenerateSyncableHash(PREFERENCES, kClientTag)), - kCtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(10)), - kMtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(20)) { - specifics.mutable_preference()->set_name(kClientTag); - specifics.mutable_preference()->set_value("pref.value"); - } - - virtual ~SyncThreadSyncEntityTest() {} - - const std::string kServerId; - const std::string kClientTag; - const std::string kClientTagHash; - const base::Time kCtime; - const base::Time kMtime; - sync_pb::EntitySpecifics specifics; -}; - -// Construct a new entity from a server update. Then receive another update. -TEST_F(SyncThreadSyncEntityTest, FromServerUpdate) { - scoped_ptr entity( - SyncThreadSyncEntity::FromServerUpdate(kServerId, kClientTagHash, 10)); - EXPECT_FALSE(entity->IsCommitPending()); - - entity->ReceiveUpdate(20); - EXPECT_FALSE(entity->IsCommitPending()); -} - -// Construct a new entity from a commit request. Then serialize it. -TEST_F(SyncThreadSyncEntityTest, FromCommitRequest) { - scoped_ptr entity( - SyncThreadSyncEntity::FromCommitRequest(kServerId, - kClientTagHash, - 22, - 33, - kCtime, - kMtime, - kClientTag, - false, - specifics)); - - ASSERT_TRUE(entity->IsCommitPending()); - sync_pb::SyncEntity pb_entity; - int64 sequence_number = 0; - entity->PrepareCommitProto(&pb_entity, &sequence_number); - EXPECT_EQ(22, sequence_number); - EXPECT_EQ(kServerId, pb_entity.id_string()); - EXPECT_EQ(kClientTagHash, pb_entity.client_defined_unique_tag()); - EXPECT_EQ(33, pb_entity.version()); - EXPECT_EQ(kCtime, ProtoTimeToTime(pb_entity.ctime())); - EXPECT_EQ(kMtime, ProtoTimeToTime(pb_entity.mtime())); - EXPECT_FALSE(pb_entity.deleted()); - EXPECT_EQ(specifics.preference().name(), - pb_entity.specifics().preference().name()); - EXPECT_EQ(specifics.preference().value(), - pb_entity.specifics().preference().value()); -} - -// Start with a server initiated entity. Commit over top of it. -TEST_F(SyncThreadSyncEntityTest, RequestCommit) { - scoped_ptr entity( - SyncThreadSyncEntity::FromServerUpdate(kServerId, kClientTagHash, 10)); - - entity->RequestCommit(kServerId, - kClientTagHash, - 1, - 10, - kCtime, - kMtime, - kClientTag, - false, - specifics); - - EXPECT_TRUE(entity->IsCommitPending()); -} - -// Start with a server initiated entity. Fail to request a commit because of -// an out of date base version. -TEST_F(SyncThreadSyncEntityTest, RequestCommitFailure) { - scoped_ptr entity( - SyncThreadSyncEntity::FromServerUpdate(kServerId, kClientTagHash, 10)); - EXPECT_FALSE(entity->IsCommitPending()); - - entity->RequestCommit(kServerId, - kClientTagHash, - 23, - 5, // Version 5 < 10 - kCtime, - kMtime, - kClientTag, - false, - specifics); - EXPECT_FALSE(entity->IsCommitPending()); -} - -// Start with a pending commit. Clobber it with an incoming update. -TEST_F(SyncThreadSyncEntityTest, UpdateClobbersCommit) { - scoped_ptr entity( - SyncThreadSyncEntity::FromCommitRequest(kServerId, - kClientTagHash, - 22, - 33, - kCtime, - kMtime, - kClientTag, - false, - specifics)); - - EXPECT_TRUE(entity->IsCommitPending()); - - entity->ReceiveUpdate(400); // Version 400 > 33. - EXPECT_FALSE(entity->IsCommitPending()); -} - -// Start with a pending commit. Send it a reflected update that -// will not override the in-progress commit. -TEST_F(SyncThreadSyncEntityTest, ReflectedUpdateDoesntClobberCommit) { - scoped_ptr entity( - SyncThreadSyncEntity::FromCommitRequest(kServerId, - kClientTagHash, - 22, - 33, - kCtime, - kMtime, - kClientTag, - false, - specifics)); - - EXPECT_TRUE(entity->IsCommitPending()); - - entity->ReceiveUpdate(33); // Version 33 == 33. - EXPECT_TRUE(entity->IsCommitPending()); -} - -} // namespace syncer diff --git a/sync/internal_api/public/sync_context_proxy.h b/sync/internal_api/public/sync_context_proxy.h new file mode 100644 index 0000000..14bf14f --- /dev/null +++ b/sync/internal_api/public/sync_context_proxy.h @@ -0,0 +1,45 @@ +// 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_INTERNAL_API_PUBLIC_SYNC_CONTEXT_PROXY_H_ +#define SYNC_INTERNAL_API_PUBLIC_SYNC_CONTEXT_PROXY_H_ + +#include "base/memory/weak_ptr.h" +#include "sync/internal_api/public/base/model_type.h" + +namespace syncer { + +class ModelTypeSyncProxyImpl; +struct DataTypeState; + +// Interface for the datatype integration logic from non-sync threads. +// +// See SyncContextProxyImpl for an actual implementation. +class SYNC_EXPORT_PRIVATE SyncContextProxy { + public: + SyncContextProxy(); + virtual ~SyncContextProxy(); + + // Attempts to connect a non-blocking type to the sync context. + // + // Must be called from the thread where the data type lives. + virtual void ConnectTypeToSync( + syncer::ModelType type, + const DataTypeState& data_type_state, + const base::WeakPtr& type_sync_proxy) = 0; + + // Tells the syncer that we're no longer interested in syncing this type. + // + // Once this takes effect, the syncer can assume that it will no longer + // receive commit requests for this type. It should also stop requesting + // and applying updates for this type, too. + virtual void Disconnect(syncer::ModelType type) = 0; + + // Creates a clone of this SyncContextProxy. + virtual scoped_ptr Clone() const = 0; +}; + +} // namespace syncer + +#endif // SYNC_INTERNAL_API_PUBLIC_SYNC_CONTEXT_PROXY_H_ diff --git a/sync/internal_api/public/sync_core_proxy.h b/sync/internal_api/public/sync_core_proxy.h deleted file mode 100644 index 099b5f9..0000000 --- a/sync/internal_api/public/sync_core_proxy.h +++ /dev/null @@ -1,45 +0,0 @@ -// 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_INTERNAL_API_PUBLIC_SYNC_CORE_PROXY_H_ -#define SYNC_INTERNAL_API_PUBLIC_SYNC_CORE_PROXY_H_ - -#include "base/memory/weak_ptr.h" -#include "sync/internal_api/public/base/model_type.h" - -namespace syncer { - -class NonBlockingTypeProcessor; -struct DataTypeState; - -// Interface for the datatype integration logic from non-sync threads. -// -// See SyncCoreProxyImpl for an actual implementation. -class SYNC_EXPORT_PRIVATE SyncCoreProxy { - public: - SyncCoreProxy(); - virtual ~SyncCoreProxy(); - - // Attempts to connect a non-blocking type to the sync core. - // - // Must be called from the thread where the data type lives. - virtual void ConnectTypeToCore( - syncer::ModelType type, - const DataTypeState& data_type_state, - base::WeakPtr type_processor) = 0; - - // Tells the syncer that we're no longer interested in syncing this type. - // - // Once this takes effect, the syncer can assume that it will no longer - // receive commit requests for this type. It should also stop requesting - // and applying updates for this type, too. - virtual void Disconnect(syncer::ModelType type) = 0; - - // Creates a clone of this SyncCoreProxy. - virtual scoped_ptr Clone() const = 0; -}; - -} // namespace syncer - -#endif // SYNC_INTERNAL_API_PUBLIC_SYNC_CORE_PROXY_H_ diff --git a/sync/internal_api/public/sync_manager.h b/sync/internal_api/public/sync_manager.h index 5c9f09f..2446b68 100644 --- a/sync/internal_api/public/sync_manager.h +++ b/sync/internal_api/public/sync_manager.h @@ -22,7 +22,7 @@ #include "sync/internal_api/public/engine/model_safe_worker.h" #include "sync/internal_api/public/engine/sync_status.h" #include "sync/internal_api/public/events/protocol_event.h" -#include "sync/internal_api/public/sync_core_proxy.h" +#include "sync/internal_api/public/sync_context_proxy.h" #include "sync/internal_api/public/sync_encryption_handler.h" #include "sync/internal_api/public/util/report_unrecoverable_error_function.h" #include "sync/internal_api/public/util/unrecoverable_error_handler.h" @@ -46,7 +46,7 @@ class InternalComponentsFactory; class JsBackend; class JsEventHandler; class ProtocolEvent; -class SyncCoreProxy; +class SyncContextProxy; class SyncEncryptionHandler; class SyncScheduler; class TypeDebugInfoObserver; @@ -332,7 +332,7 @@ class SYNC_EXPORT SyncManager : public syncer::InvalidationHandler { virtual UserShare* GetUserShare() = 0; // Returns an instance of the main interface for non-blocking sync types. - virtual syncer::SyncCoreProxy* GetSyncCoreProxy() = 0; + virtual syncer::SyncContextProxy* GetSyncContextProxy() = 0; // Returns the cache_guid of the currently open database. // Requires that the SyncManager be initialized. diff --git a/sync/internal_api/public/test/fake_sync_manager.h b/sync/internal_api/public/test/fake_sync_manager.h index 31f5d412..df14a52 100644 --- a/sync/internal_api/public/test/fake_sync_manager.h +++ b/sync/internal_api/public/test/fake_sync_manager.h @@ -10,7 +10,7 @@ #include "base/memory/ref_counted.h" #include "base/observer_list.h" #include "sync/internal_api/public/sync_manager.h" -#include "sync/internal_api/public/test/null_sync_core_proxy.h" +#include "sync/internal_api/public/test/null_sync_context_proxy.h" #include "sync/internal_api/public/test/test_user_share.h" namespace base { @@ -118,7 +118,7 @@ class FakeSyncManager : public SyncManager { virtual void SaveChanges() OVERRIDE; virtual void ShutdownOnSyncThread() OVERRIDE; virtual UserShare* GetUserShare() OVERRIDE; - virtual syncer::SyncCoreProxy* GetSyncCoreProxy() OVERRIDE; + virtual syncer::SyncContextProxy* GetSyncContextProxy() OVERRIDE; virtual const std::string cache_guid() OVERRIDE; virtual bool ReceivedExperiment(Experiments* experiments) OVERRIDE; virtual bool HasUnsyncedItems() OVERRIDE; @@ -166,7 +166,7 @@ class FakeSyncManager : public SyncManager { TestUserShare test_user_share_; - NullSyncCoreProxy null_sync_core_proxy_; + NullSyncContextProxy null_sync_context_proxy_; DISALLOW_COPY_AND_ASSIGN(FakeSyncManager); }; diff --git a/sync/internal_api/public/test/null_sync_context_proxy.h b/sync/internal_api/public/test/null_sync_context_proxy.h new file mode 100644 index 0000000..808f664 --- /dev/null +++ b/sync/internal_api/public/test/null_sync_context_proxy.h @@ -0,0 +1,33 @@ +// 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_INTERNAL_API_PUBLIC_TEST_NULL_SYNC_CONTEXT_PROXY_H_ +#define SYNC_INTERNAL_API_PUBLIC_TEST_NULL_SYNC_CONTEXT_PROXY_H_ + +#include "base/memory/weak_ptr.h" +#include "sync/internal_api/public/sync_context_proxy.h" + +namespace syncer { + +class ModelTypeSyncProxyImpl; + +// A non-functional implementation of SyncContextProxy. +// +// It supports Clone(), but not much else. Useful for testing. +class NullSyncContextProxy : public SyncContextProxy { + public: + NullSyncContextProxy(); + virtual ~NullSyncContextProxy(); + + virtual void ConnectTypeToSync( + syncer::ModelType type, + const DataTypeState& data_type_state, + const base::WeakPtr& type_sync_proxy) OVERRIDE; + virtual void Disconnect(syncer::ModelType type) OVERRIDE; + virtual scoped_ptr Clone() const OVERRIDE; +}; + +} // namespace syncer + +#endif // SYNC_INTERNAL_API_PUBLIC_TEST_NULL_SYNC_CONTEXT_PROXY_H_ diff --git a/sync/internal_api/public/test/null_sync_core_proxy.h b/sync/internal_api/public/test/null_sync_core_proxy.h deleted file mode 100644 index 4c8fc67..0000000 --- a/sync/internal_api/public/test/null_sync_core_proxy.h +++ /dev/null @@ -1,33 +0,0 @@ -// 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_INTERNAL_API_PUBLIC_TEST_NULL_SYNC_CORE_PROXY_H_ -#define SYNC_INTERNAL_API_PUBLIC_TEST_NULL_SYNC_CORE_PROXY_H_ - -#include "sync/internal_api/public/sync_core_proxy.h" -#include "base/memory/weak_ptr.h" - -namespace syncer { - -class NonBlockingTypeProcessor; - -// A non-functional implementation of SyncCoreProxy. -// -// It supports Clone(), but not much else. Useful for testing. -class NullSyncCoreProxy : public SyncCoreProxy { - public: - NullSyncCoreProxy(); - virtual ~NullSyncCoreProxy(); - - virtual void ConnectTypeToCore( - syncer::ModelType type, - const DataTypeState& data_type_state, - base::WeakPtr processor) OVERRIDE; - virtual void Disconnect(syncer::ModelType type) OVERRIDE; - virtual scoped_ptr Clone() const OVERRIDE; -}; - -} // namespace syncer - -#endif // SYNC_INTERNAL_API_PUBLIC_TEST_NULL_SYNC_CORE_PROXY_H_ diff --git a/sync/internal_api/sync_context.cc b/sync/internal_api/sync_context.cc new file mode 100644 index 0000000..a1e87e2 --- /dev/null +++ b/sync/internal_api/sync_context.cc @@ -0,0 +1,39 @@ +// 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/internal_api/sync_context.h" + +#include "sync/engine/model_type_sync_worker_impl.h" +#include "sync/sessions/model_type_registry.h" + +namespace syncer { + +SyncContext::SyncContext(ModelTypeRegistry* model_type_registry) + : model_type_registry_(model_type_registry), weak_ptr_factory_(this) { +} + +SyncContext::~SyncContext() { +} + +void SyncContext::ConnectSyncTypeToWorker( + ModelType type, + const DataTypeState& data_type_state, + const scoped_refptr& task_runner, + const base::WeakPtr& type_sync_proxy) { + // Initialize the type sync proxy's sync-thread sibling and the + // ModelTypeSyncProxy <-> ModelTypeSyncWorker + // (ie. model thread <-> sync thread) communication channel. + model_type_registry_->InitializeNonBlockingType( + type, data_type_state, task_runner, type_sync_proxy); +} + +void SyncContext::Disconnect(ModelType type) { + model_type_registry_->RemoveNonBlockingType(type); +} + +base::WeakPtr SyncContext::AsWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +} // namespace syncer diff --git a/sync/internal_api/sync_context.h b/sync/internal_api/sync_context.h new file mode 100644 index 0000000..3dc2ae0 --- /dev/null +++ b/sync/internal_api/sync_context.h @@ -0,0 +1,64 @@ +// 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_INTERNAL_API_PUBLIC_SYNC_CONTEXT_H_ +#define SYNC_INTERNAL_API_PUBLIC_SYNC_CONTEXT_H_ + +#include "base/basictypes.h" +#include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner.h" +#include "sync/base/sync_export.h" +#include "sync/internal_api/public/base/model_type.h" + +namespace syncer { + +class ModelTypeRegistry; +class ModelTypeSyncProxyImpl; +struct DataTypeState; + +// An interface of the core parts of sync. +// +// In theory, this is the component that provides off-thread sync types with +// functionality to schedule and execute communication with the sync server. In +// practice, this class delegates most of the responsibilty of implemeting this +// functionality to other classes, and most of the interface is exposed not +// directly here but instead through a per-ModelType class that this class helps +// instantiate. +class SYNC_EXPORT_PRIVATE SyncContext { + public: + explicit SyncContext(ModelTypeRegistry* model_type_registry); + ~SyncContext(); + + // Initializes the connection between the sync context on the sync thread and + // a proxy for the specified non-blocking sync type that lives on the data + // type's thread. + void ConnectSyncTypeToWorker( + syncer::ModelType type, + const DataTypeState& data_type_state, + const scoped_refptr& datatype_task_runner, + const base::WeakPtr& type_sync_proxy); + + // Disconnects the syncer from the model and stops syncing the type. + // + // By the time this is called, the model thread should have already + // invalidated the WeakPtr it sent to us in the connect request. Any + // messages sent to that ModelTypeSyncProxy will not be recived. + // + // This is the sync thread's chance to clear state associated with the type. + // It also causes the syncer to stop requesting updates for this type, and to + // abort any in-progress commit requests. + void Disconnect(ModelType type); + + base::WeakPtr AsWeakPtr(); + + private: + ModelTypeRegistry* model_type_registry_; + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(SyncContext); +}; + +} // namespace syncer + +#endif // SYNC_INTERNAL_API_PUBLIC_SYNC_CONTEXT_H_ diff --git a/sync/internal_api/sync_context_proxy.cc b/sync/internal_api/sync_context_proxy.cc new file mode 100644 index 0000000..53c2bd3 --- /dev/null +++ b/sync/internal_api/sync_context_proxy.cc @@ -0,0 +1,15 @@ +// 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/internal_api/public/sync_context_proxy.h" + +namespace syncer { + +SyncContextProxy::SyncContextProxy() { +} + +SyncContextProxy::~SyncContextProxy() { +} + +} // namespace syncer diff --git a/sync/internal_api/sync_context_proxy_impl.cc b/sync/internal_api/sync_context_proxy_impl.cc new file mode 100644 index 0000000..77f3d6a --- /dev/null +++ b/sync/internal_api/sync_context_proxy_impl.cc @@ -0,0 +1,48 @@ +// 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/internal_api/sync_context_proxy_impl.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/message_loop/message_loop_proxy.h" +#include "sync/engine/non_blocking_sync_common.h" +#include "sync/internal_api/sync_context.h" + +namespace syncer { + +SyncContextProxyImpl::SyncContextProxyImpl( + const scoped_refptr& sync_task_runner, + const base::WeakPtr& sync_context) + : sync_task_runner_(sync_task_runner), sync_context_(sync_context) { +} + +SyncContextProxyImpl::~SyncContextProxyImpl() { +} + +void SyncContextProxyImpl::ConnectTypeToSync( + ModelType type, + const DataTypeState& data_type_state, + const base::WeakPtr& type_sync_proxy) { + VLOG(1) << "ConnectTypeToSync: " << ModelTypeToString(type); + sync_task_runner_->PostTask(FROM_HERE, + base::Bind(&SyncContext::ConnectSyncTypeToWorker, + sync_context_, + type, + data_type_state, + base::MessageLoopProxy::current(), + type_sync_proxy)); +} + +void SyncContextProxyImpl::Disconnect(ModelType type) { + sync_task_runner_->PostTask( + FROM_HERE, base::Bind(&SyncContext::Disconnect, sync_context_, type)); +} + +scoped_ptr SyncContextProxyImpl::Clone() const { + return scoped_ptr( + new SyncContextProxyImpl(sync_task_runner_, sync_context_)); +} + +} // namespace syncer diff --git a/sync/internal_api/sync_context_proxy_impl.h b/sync/internal_api/sync_context_proxy_impl.h new file mode 100644 index 0000000..fb7cd4f --- /dev/null +++ b/sync/internal_api/sync_context_proxy_impl.h @@ -0,0 +1,60 @@ +// 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_INTERNAL_API_SYNC_CONTEXT_PROXY_IMPL_H_ +#define SYNC_INTERNAL_API_SYNC_CONTEXT_PROXY_IMPL_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner.h" +#include "sync/base/sync_export.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/internal_api/public/sync_context_proxy.h" + +namespace syncer { + +class SyncContext; +class ModelTypeSyncProxyImpl; +struct DataTypeState; + +// Encapsulates a reference to the sync context and the thread it's running on. +// Used by sync's data types to connect with the sync context. +// +// It is expected that this object will be copied to and used on many different +// threads. It is small and safe to pass by value. +class SYNC_EXPORT_PRIVATE SyncContextProxyImpl : public SyncContextProxy { + public: + SyncContextProxyImpl( + const scoped_refptr& sync_task_runner, + const base::WeakPtr& sync_context); + virtual ~SyncContextProxyImpl(); + + // Attempts to connect a non-blocking type to the sync context. + // + // This may fail under some unusual circumstances, like shutdown. Due to the + // nature of WeakPtrs and cross-thread communication, the caller will be + // unable to distinguish a slow success from failure. + // + // Must be called from the thread where the data type lives. + virtual void ConnectTypeToSync( + syncer::ModelType type, + const DataTypeState& data_type_state, + const base::WeakPtr& sync_proxy_impl) OVERRIDE; + + // Disables syncing for the given type on the sync thread. + virtual void Disconnect(syncer::ModelType type) OVERRIDE; + + virtual scoped_ptr Clone() const OVERRIDE; + + private: + // A SequencedTaskRunner representing the thread where the SyncContext lives. + scoped_refptr sync_task_runner_; + + // The SyncContext this object is wrapping. + base::WeakPtr sync_context_; +}; + +} // namespace syncer + +#endif // SYNC_INTERNAL_API_SYNC_CONTEXT_PROXY_IMPL_H_ diff --git a/sync/internal_api/sync_context_proxy_impl_unittest.cc b/sync/internal_api/sync_context_proxy_impl_unittest.cc new file mode 100644 index 0000000..62edfec --- /dev/null +++ b/sync/internal_api/sync_context_proxy_impl_unittest.cc @@ -0,0 +1,89 @@ +// 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 "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/run_loop.h" +#include "base/sequenced_task_runner.h" +#include "sync/engine/model_type_sync_proxy_impl.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/internal_api/sync_context.h" +#include "sync/internal_api/sync_context_proxy_impl.h" +#include "sync/sessions/model_type_registry.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncer { + +class SyncContextProxyImplTest : public ::testing::Test { + public: + SyncContextProxyImplTest() + : sync_task_runner_(base::MessageLoopProxy::current()), + type_task_runner_(base::MessageLoopProxy::current()), + context_(new SyncContext(®istry_)), + context_proxy_(sync_task_runner_, context_->AsWeakPtr()) {} + + // The sync thread could be shut down at any time without warning. This + // function simulates such an event. + void DisableSync() { context_.reset(); } + + scoped_ptr GetProxy() { return context_proxy_.Clone(); } + + private: + base::MessageLoop loop_; + scoped_refptr sync_task_runner_; + scoped_refptr type_task_runner_; + ModelTypeRegistry registry_; + scoped_ptr context_; + SyncContextProxyImpl context_proxy_; +}; + +// Try to connect a type to a SyncContext that has already shut down. +TEST_F(SyncContextProxyImplTest, FailToConnect1) { + ModelTypeSyncProxyImpl themes_sync_proxy(syncer::THEMES); + DisableSync(); + themes_sync_proxy.Enable(GetProxy()); + + base::RunLoop run_loop_; + run_loop_.RunUntilIdle(); + EXPECT_FALSE(themes_sync_proxy.IsConnected()); +} + +// Try to connect a type to a SyncContext as it shuts down. +TEST_F(SyncContextProxyImplTest, FailToConnect2) { + ModelTypeSyncProxyImpl themes_sync_proxy(syncer::THEMES); + themes_sync_proxy.Enable(GetProxy()); + DisableSync(); + + base::RunLoop run_loop_; + run_loop_.RunUntilIdle(); + EXPECT_FALSE(themes_sync_proxy.IsConnected()); +} + +// Tests the case where the type's sync proxy shuts down first. +TEST_F(SyncContextProxyImplTest, TypeDisconnectsFirst) { + scoped_ptr themes_sync_proxy( + new ModelTypeSyncProxyImpl(syncer::THEMES)); + themes_sync_proxy->Enable(GetProxy()); + + base::RunLoop run_loop_; + run_loop_.RunUntilIdle(); + + EXPECT_TRUE(themes_sync_proxy->IsConnected()); + themes_sync_proxy.reset(); +} + +// Tests the case where the sync thread shuts down first. +TEST_F(SyncContextProxyImplTest, SyncDisconnectsFirst) { + scoped_ptr themes_sync_proxy( + new ModelTypeSyncProxyImpl(syncer::THEMES)); + themes_sync_proxy->Enable(GetProxy()); + + base::RunLoop run_loop_; + run_loop_.RunUntilIdle(); + + EXPECT_TRUE(themes_sync_proxy->IsConnected()); + DisableSync(); +} + +} // namespace syncer diff --git a/sync/internal_api/sync_core.cc b/sync/internal_api/sync_core.cc deleted file mode 100644 index 6707176..0000000 --- a/sync/internal_api/sync_core.cc +++ /dev/null @@ -1,37 +0,0 @@ -// 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/internal_api/sync_core.h" - -#include "sync/engine/non_blocking_type_processor_core.h" -#include "sync/sessions/model_type_registry.h" - -namespace syncer { - -SyncCore::SyncCore(ModelTypeRegistry* model_type_registry) - : model_type_registry_(model_type_registry), weak_ptr_factory_(this) {} - -SyncCore::~SyncCore() {} - -void SyncCore::ConnectSyncTypeToCore( - ModelType type, - const DataTypeState& data_type_state, - scoped_refptr task_runner, - base::WeakPtr processor) { - // Initialize the processor's sync-thread sibling and the - // processor <-> processor_core (ie. model thread <-> sync thread) - // communication channel. - model_type_registry_->InitializeNonBlockingType( - type, data_type_state, task_runner, processor); -} - -void SyncCore::Disconnect(ModelType type) { - model_type_registry_->RemoveNonBlockingType(type); -} - -base::WeakPtr SyncCore::AsWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); -} - -} // namespace syncer diff --git a/sync/internal_api/sync_core.h b/sync/internal_api/sync_core.h deleted file mode 100644 index 417a296..0000000 --- a/sync/internal_api/sync_core.h +++ /dev/null @@ -1,64 +0,0 @@ -// 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_INTERNAL_API_PUBLIC_SYNC_CORE_H_ -#define SYNC_INTERNAL_API_PUBLIC_SYNC_CORE_H_ - -#include "base/basictypes.h" -#include "base/memory/weak_ptr.h" -#include "base/sequenced_task_runner.h" -#include "sync/base/sync_export.h" -#include "sync/internal_api/public/base/model_type.h" - -namespace syncer { - -class ModelTypeRegistry; -class NonBlockingTypeProcessor; -struct DataTypeState; - -// An interface of the core parts of sync. -// -// In theory, this is the component that provides off-thread sync types with -// functionality to schedule and execute communication with the sync server. In -// practice, this class delegates most of the responsibilty of implemeting this -// functionality to other classes, and most of the interface is exposed not -// directly here but instead through a per-ModelType class that this class helps -// instantiate. -class SYNC_EXPORT_PRIVATE SyncCore { - public: - explicit SyncCore(ModelTypeRegistry* model_type_registry); - ~SyncCore(); - - // Initializes the connection between the sync core and its delegate on the - // sync client's thread. - void ConnectSyncTypeToCore( - syncer::ModelType type, - const DataTypeState& data_type_state, - scoped_refptr datatype_task_runner, - base::WeakPtr sync_client); - - // Disconnects the syncer from the model and stops syncing the type. - // - // By the time this is called, the model thread should have already - // invalidated the WeakPtr it sent to us in the connect request. Any - // messages sent to that NonBlockingTypeProcessor will not be recived. - // - // This is the sync thread's chance to clear state associated with the type. - // It also causes the syncer to stop requesting updates for this type, and to - // abort any in-progress commit requests. - void Disconnect(ModelType type); - - base::WeakPtr AsWeakPtr(); - - private: - ModelTypeRegistry* model_type_registry_; - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(SyncCore); -}; - -} // namespace syncer - -#endif // SYNC_INTERNAL_API_PUBLIC_SYNC_CORE_H_ - diff --git a/sync/internal_api/sync_core_proxy.cc b/sync/internal_api/sync_core_proxy.cc deleted file mode 100644 index 4e6f7f6..0000000 --- a/sync/internal_api/sync_core_proxy.cc +++ /dev/null @@ -1,13 +0,0 @@ -// 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/internal_api/public/sync_core_proxy.h" - -namespace syncer { - -SyncCoreProxy::SyncCoreProxy() {} - -SyncCoreProxy::~SyncCoreProxy() {} - -} // namespace syncer diff --git a/sync/internal_api/sync_core_proxy_impl.cc b/sync/internal_api/sync_core_proxy_impl.cc deleted file mode 100644 index 81c0fa2..0000000 --- a/sync/internal_api/sync_core_proxy_impl.cc +++ /dev/null @@ -1,47 +0,0 @@ -// 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/internal_api/sync_core_proxy_impl.h" - -#include "base/bind.h" -#include "base/location.h" -#include "base/message_loop/message_loop_proxy.h" -#include "sync/engine/non_blocking_sync_common.h" -#include "sync/internal_api/sync_core.h" - -namespace syncer { - -SyncCoreProxyImpl::SyncCoreProxyImpl( - scoped_refptr sync_task_runner, - base::WeakPtr sync_core) - : sync_task_runner_(sync_task_runner), - sync_core_(sync_core) {} - -SyncCoreProxyImpl::~SyncCoreProxyImpl() {} - -void SyncCoreProxyImpl::ConnectTypeToCore( - ModelType type, - const DataTypeState& data_type_state, - base::WeakPtr type_processor) { - VLOG(1) << "ConnectTypeToCore: " << ModelTypeToString(type); - sync_task_runner_->PostTask(FROM_HERE, - base::Bind(&SyncCore::ConnectSyncTypeToCore, - sync_core_, - type, - data_type_state, - base::MessageLoopProxy::current(), - type_processor)); -} - -void SyncCoreProxyImpl::Disconnect(ModelType type) { - sync_task_runner_->PostTask( - FROM_HERE, base::Bind(&SyncCore::Disconnect, sync_core_, type)); -} - -scoped_ptr SyncCoreProxyImpl::Clone() const { - return scoped_ptr( - new SyncCoreProxyImpl(sync_task_runner_, sync_core_)); -} - -} // namespace syncer diff --git a/sync/internal_api/sync_core_proxy_impl.h b/sync/internal_api/sync_core_proxy_impl.h deleted file mode 100644 index 91061fe..0000000 --- a/sync/internal_api/sync_core_proxy_impl.h +++ /dev/null @@ -1,60 +0,0 @@ -// 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_INTERNAL_API_SYNC_CORE_PROXY_IMPL_H_ -#define SYNC_INTERNAL_API_SYNC_CORE_PROXY_IMPL_H_ - -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/sequenced_task_runner.h" -#include "sync/base/sync_export.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/internal_api/public/sync_core_proxy.h" - -namespace syncer { - -class SyncCore; -class NonBlockingTypeProcessor; -struct DataTypeState; - -// Encapsulates a reference to the sync core and the thread it's running on. -// Used by sync's data types to connect with the sync core. -// -// It is epxected that this object will be copied to and used on many different -// threads. It is small and safe to pass by value. -class SYNC_EXPORT_PRIVATE SyncCoreProxyImpl : public SyncCoreProxy { - public: - SyncCoreProxyImpl( - scoped_refptr sync_task_runner, - base::WeakPtr sync_core); - virtual ~SyncCoreProxyImpl(); - - // Attempts to connect a non-blocking type to the sync core. - // - // This may fail under some unusual circumstances, like shutdown. Due to the - // nature of WeakPtrs and cross-thread communication, the caller will be - // unable to distinguish a slow success from failure. - // - // Must be called from the thread where the data type lives. - virtual void ConnectTypeToCore( - syncer::ModelType type, - const DataTypeState& data_type_state, - base::WeakPtr type_processor) OVERRIDE; - - // Disables syncing for the given type on the sync thread. - virtual void Disconnect(syncer::ModelType type) OVERRIDE; - - virtual scoped_ptr Clone() const OVERRIDE; - - private: - // A SequencedTaskRunner representing the thread where the SyncCore lives. - scoped_refptr sync_task_runner_; - - // The SyncCore this object is wrapping. - base::WeakPtr sync_core_; -}; - -} // namespace syncer - -#endif // SYNC_INTERNAL_API_SYNC_CORE_PROXY_IMPL_H_ diff --git a/sync/internal_api/sync_core_proxy_impl_unittest.cc b/sync/internal_api/sync_core_proxy_impl_unittest.cc deleted file mode 100644 index 25b3d9e..0000000 --- a/sync/internal_api/sync_core_proxy_impl_unittest.cc +++ /dev/null @@ -1,93 +0,0 @@ -// 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 "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/run_loop.h" -#include "base/sequenced_task_runner.h" -#include "sync/engine/non_blocking_type_processor.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/internal_api/sync_core.h" -#include "sync/internal_api/sync_core_proxy_impl.h" -#include "sync/sessions/model_type_registry.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace syncer { - -class SyncCoreProxyImplTest : public ::testing::Test { - public: - SyncCoreProxyImplTest() - : sync_task_runner_(base::MessageLoopProxy::current()), - type_task_runner_(base::MessageLoopProxy::current()), - core_(new SyncCore(®istry_)), - core_proxy_( - sync_task_runner_, - core_->AsWeakPtr()) {} - - // The sync thread could be shut down at any time without warning. This - // function simulates such an event. - void DisableSync() { - core_.reset(); - } - - scoped_ptr GetProxy() { return core_proxy_.Clone(); } - - private: - base::MessageLoop loop_; - scoped_refptr sync_task_runner_; - scoped_refptr type_task_runner_; - ModelTypeRegistry registry_; - scoped_ptr core_; - SyncCoreProxyImpl core_proxy_; -}; - -// Try to connect a type to a SyncCore that has already shut down. -TEST_F(SyncCoreProxyImplTest, FailToConnect1) { - NonBlockingTypeProcessor themes_processor(syncer::THEMES); - DisableSync(); - themes_processor.Enable(GetProxy()); - - base::RunLoop run_loop_; - run_loop_.RunUntilIdle(); - EXPECT_FALSE(themes_processor.IsConnected()); -} - -// Try to connect a type to a SyncCore as it shuts down. -TEST_F(SyncCoreProxyImplTest, FailToConnect2) { - NonBlockingTypeProcessor themes_processor(syncer::THEMES); - themes_processor.Enable(GetProxy()); - DisableSync(); - - base::RunLoop run_loop_; - run_loop_.RunUntilIdle(); - EXPECT_FALSE(themes_processor.IsConnected()); -} - -// Tests the case where the type's processor shuts down first. -TEST_F(SyncCoreProxyImplTest, TypeDisconnectsFirst) { - scoped_ptr themes_processor - (new NonBlockingTypeProcessor(syncer::THEMES)); - themes_processor->Enable(GetProxy()); - - base::RunLoop run_loop_; - run_loop_.RunUntilIdle(); - - EXPECT_TRUE(themes_processor->IsConnected()); - themes_processor.reset(); -} - -// Tests the case where the sync thread shuts down first. -TEST_F(SyncCoreProxyImplTest, SyncDisconnectsFirst) { - scoped_ptr themes_processor - (new NonBlockingTypeProcessor(syncer::THEMES)); - themes_processor->Enable(GetProxy()); - - base::RunLoop run_loop_; - run_loop_.RunUntilIdle(); - - EXPECT_TRUE(themes_processor->IsConnected()); - DisableSync(); -} - -} // namespace syncer diff --git a/sync/internal_api/sync_manager_impl.cc b/sync/internal_api/sync_manager_impl.cc index a5f2de0..bf73315 100644 --- a/sync/internal_api/sync_manager_impl.cc +++ b/sync/internal_api/sync_manager_impl.cc @@ -28,13 +28,13 @@ #include "sync/internal_api/public/internal_components_factory.h" #include "sync/internal_api/public/read_node.h" #include "sync/internal_api/public/read_transaction.h" -#include "sync/internal_api/public/sync_core_proxy.h" +#include "sync/internal_api/public/sync_context_proxy.h" #include "sync/internal_api/public/user_share.h" #include "sync/internal_api/public/util/experiments.h" #include "sync/internal_api/public/write_node.h" #include "sync/internal_api/public/write_transaction.h" -#include "sync/internal_api/sync_core.h" -#include "sync/internal_api/sync_core_proxy_impl.h" +#include "sync/internal_api/sync_context.h" +#include "sync/internal_api/sync_context_proxy_impl.h" #include "sync/internal_api/syncapi_internal.h" #include "sync/internal_api/syncapi_server_connection_manager.h" #include "sync/notifier/invalidation_util.h" @@ -390,15 +390,15 @@ void SyncManagerImpl::Init( model_type_registry_.reset(new ModelTypeRegistry(workers, directory())); - sync_core_.reset(new SyncCore(model_type_registry_.get())); + sync_context_.reset(new SyncContext(model_type_registry_.get())); - // Bind the SyncCore WeakPtr to this thread. This helps us crash earlier if - // the pointer is misused in debug mode. - base::WeakPtr weak_core = sync_core_->AsWeakPtr(); + // Bind the SyncContext WeakPtr to this thread. This helps us crash earlier + // if the pointer is misused in debug mode. + base::WeakPtr weak_core = sync_context_->AsWeakPtr(); weak_core.get(); - sync_core_proxy_.reset( - new SyncCoreProxyImpl(base::MessageLoopProxy::current(), weak_core)); + sync_context_proxy_.reset( + new SyncContextProxyImpl(base::MessageLoopProxy::current(), weak_core)); // Build a SyncSessionContext and store the worker in it. DVLOG(1) << "Sync is bringing up SyncSessionContext."; @@ -1022,9 +1022,9 @@ UserShare* SyncManagerImpl::GetUserShare() { return &share_; } -syncer::SyncCoreProxy* SyncManagerImpl::GetSyncCoreProxy() { +syncer::SyncContextProxy* SyncManagerImpl::GetSyncContextProxy() { DCHECK(initialized_); - return sync_core_proxy_.get(); + return sync_context_proxy_.get(); } const std::string SyncManagerImpl::cache_guid() { diff --git a/sync/internal_api/sync_manager_impl.h b/sync/internal_api/sync_manager_impl.h index dd6e191..2ca5fd6 100644 --- a/sync/internal_api/sync_manager_impl.h +++ b/sync/internal_api/sync_manager_impl.h @@ -20,7 +20,7 @@ #include "sync/internal_api/js_sync_manager_observer.h" #include "sync/internal_api/protocol_event_buffer.h" #include "sync/internal_api/public/base/invalidator_state.h" -#include "sync/internal_api/public/sync_core_proxy.h" +#include "sync/internal_api/public/sync_context_proxy.h" #include "sync/internal_api/public/sync_manager.h" #include "sync/internal_api/public/user_share.h" #include "sync/internal_api/sync_encryption_handler_impl.h" @@ -34,7 +34,7 @@ namespace syncer { class ModelTypeRegistry; class SyncAPIServerConnectionManager; -class SyncCore; +class SyncContext; class TypeDebugInfoObserver; class WriteNode; class WriteTransaction; @@ -112,7 +112,7 @@ class SYNC_EXPORT_PRIVATE SyncManagerImpl : virtual void SaveChanges() OVERRIDE; virtual void ShutdownOnSyncThread() OVERRIDE; virtual UserShare* GetUserShare() OVERRIDE; - virtual syncer::SyncCoreProxy* GetSyncCoreProxy() OVERRIDE; + virtual syncer::SyncContextProxy* GetSyncContextProxy() OVERRIDE; virtual const std::string cache_guid() OVERRIDE; virtual bool ReceivedExperiment(Experiments* experiments) OVERRIDE; virtual bool HasUnsyncedItems() OVERRIDE; @@ -308,8 +308,8 @@ class SYNC_EXPORT_PRIVATE SyncManagerImpl : scoped_ptr model_type_registry_; // The main interface for non-blocking sync types and a thread-safe wrapper. - scoped_ptr sync_core_; - scoped_ptr sync_core_proxy_; + scoped_ptr sync_context_; + scoped_ptr sync_context_proxy_; // A container of various bits of information used by the SyncScheduler to // create SyncSessions. Must outlive the SyncScheduler. diff --git a/sync/internal_api/sync_rollback_manager_base.cc b/sync/internal_api/sync_rollback_manager_base.cc index ade0422..5ce7a80 100644 --- a/sync/internal_api/sync_rollback_manager_base.cc +++ b/sync/internal_api/sync_rollback_manager_base.cc @@ -234,7 +234,7 @@ std::string SyncRollbackManagerBase::GetOwnerName() const { return ""; } -syncer::SyncCoreProxy* SyncRollbackManagerBase::GetSyncCoreProxy() { +syncer::SyncContextProxy* SyncRollbackManagerBase::GetSyncContextProxy() { return NULL; } diff --git a/sync/internal_api/sync_rollback_manager_base.h b/sync/internal_api/sync_rollback_manager_base.h index 6b4153e..17892a8e 100644 --- a/sync/internal_api/sync_rollback_manager_base.h +++ b/sync/internal_api/sync_rollback_manager_base.h @@ -86,7 +86,7 @@ class SYNC_EXPORT_PRIVATE SyncRollbackManagerBase : virtual SyncEncryptionHandler* GetEncryptionHandler() OVERRIDE; virtual void RefreshTypes(ModelTypeSet types) OVERRIDE; virtual std::string GetOwnerName() const OVERRIDE; - virtual SyncCoreProxy* GetSyncCoreProxy() OVERRIDE; + virtual SyncContextProxy* GetSyncContextProxy() OVERRIDE; virtual ScopedVector GetBufferedProtocolEvents() OVERRIDE; virtual scoped_ptr GetAllNodesForType( diff --git a/sync/internal_api/test/fake_sync_manager.cc b/sync/internal_api/test/fake_sync_manager.cc index 7526795..7b5e32f 100644 --- a/sync/internal_api/test/fake_sync_manager.cc +++ b/sync/internal_api/test/fake_sync_manager.cc @@ -214,8 +214,8 @@ UserShare* FakeSyncManager::GetUserShare() { return test_user_share_.user_share(); } -syncer::SyncCoreProxy* FakeSyncManager::GetSyncCoreProxy() { - return &null_sync_core_proxy_; +syncer::SyncContextProxy* FakeSyncManager::GetSyncContextProxy() { + return &null_sync_context_proxy_; } const std::string FakeSyncManager::cache_guid() { diff --git a/sync/internal_api/test/null_sync_context_proxy.cc b/sync/internal_api/test/null_sync_context_proxy.cc new file mode 100644 index 0000000..e209c21 --- /dev/null +++ b/sync/internal_api/test/null_sync_context_proxy.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/internal_api/public/test/null_sync_context_proxy.h" + +namespace syncer { + +NullSyncContextProxy::NullSyncContextProxy() { +} + +NullSyncContextProxy::~NullSyncContextProxy() { +} + +void NullSyncContextProxy::ConnectTypeToSync( + syncer::ModelType type, + const DataTypeState& data_type_state, + const base::WeakPtr& type_sync_proxy) { + NOTREACHED() << "NullSyncContextProxy is not meant to be used"; +} + +void NullSyncContextProxy::Disconnect(syncer::ModelType type) { + NOTREACHED() << "NullSyncContextProxy is not meant to be used"; +} + +scoped_ptr NullSyncContextProxy::Clone() const { + return scoped_ptr(new NullSyncContextProxy()); +} + +} // namespace syncer diff --git a/sync/internal_api/test/null_sync_core_proxy.cc b/sync/internal_api/test/null_sync_core_proxy.cc deleted file mode 100644 index 14d9d65..0000000 --- a/sync/internal_api/test/null_sync_core_proxy.cc +++ /dev/null @@ -1,28 +0,0 @@ -// 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/internal_api/public/test/null_sync_core_proxy.h" - -namespace syncer { - -NullSyncCoreProxy::NullSyncCoreProxy() {} - -NullSyncCoreProxy::~NullSyncCoreProxy() {} - -void NullSyncCoreProxy::ConnectTypeToCore( - syncer::ModelType type, - const DataTypeState& data_type_state, - base::WeakPtr processor) { - NOTREACHED() << "NullSyncCoreProxy is not meant to be used"; -} - -void NullSyncCoreProxy::Disconnect(syncer::ModelType type) { - NOTREACHED() << "NullSyncCoreProxy is not meant to be used"; -} - -scoped_ptr NullSyncCoreProxy::Clone() const { - return scoped_ptr(new NullSyncCoreProxy()); -} - -} // namespace syncer diff --git a/sync/sessions/model_type_registry.cc b/sync/sessions/model_type_registry.cc index 9ceac74..b8ab74b 100644 --- a/sync/sessions/model_type_registry.cc +++ b/sync/sessions/model_type_registry.cc @@ -9,24 +9,23 @@ #include "base/observer_list.h" #include "sync/engine/directory_commit_contributor.h" #include "sync/engine/directory_update_handler.h" +#include "sync/engine/model_type_sync_proxy.h" +#include "sync/engine/model_type_sync_proxy_impl.h" +#include "sync/engine/model_type_sync_worker.h" +#include "sync/engine/model_type_sync_worker_impl.h" #include "sync/engine/non_blocking_sync_common.h" -#include "sync/engine/non_blocking_type_processor.h" -#include "sync/engine/non_blocking_type_processor_core.h" -#include "sync/engine/non_blocking_type_processor_core_interface.h" -#include "sync/engine/non_blocking_type_processor_interface.h" #include "sync/sessions/directory_type_debug_info_emitter.h" namespace syncer { namespace { -class NonBlockingTypeProcessorWrapper - : public NonBlockingTypeProcessorInterface { +class ModelTypeSyncProxyWrapper : public ModelTypeSyncProxy { public: - NonBlockingTypeProcessorWrapper( - base::WeakPtr processor, - scoped_refptr processor_task_runner); - virtual ~NonBlockingTypeProcessorWrapper(); + ModelTypeSyncProxyWrapper( + const base::WeakPtr& proxy, + const scoped_refptr& processor_task_runner); + virtual ~ModelTypeSyncProxyWrapper(); virtual void ReceiveCommitResponse( const DataTypeState& type_state, @@ -36,70 +35,69 @@ class NonBlockingTypeProcessorWrapper const UpdateResponseDataList& response_list) OVERRIDE; private: - base::WeakPtr processor_; + base::WeakPtr processor_; scoped_refptr processor_task_runner_; }; -NonBlockingTypeProcessorWrapper::NonBlockingTypeProcessorWrapper( - base::WeakPtr processor, - scoped_refptr processor_task_runner) - : processor_(processor), processor_task_runner_(processor_task_runner) { +ModelTypeSyncProxyWrapper::ModelTypeSyncProxyWrapper( + const base::WeakPtr& proxy, + const scoped_refptr& processor_task_runner) + : processor_(proxy), processor_task_runner_(processor_task_runner) { } -NonBlockingTypeProcessorWrapper::~NonBlockingTypeProcessorWrapper() { +ModelTypeSyncProxyWrapper::~ModelTypeSyncProxyWrapper() { } -void NonBlockingTypeProcessorWrapper::ReceiveCommitResponse( +void ModelTypeSyncProxyWrapper::ReceiveCommitResponse( const DataTypeState& type_state, const CommitResponseDataList& response_list) { processor_task_runner_->PostTask( FROM_HERE, - base::Bind(&NonBlockingTypeProcessor::OnCommitCompletion, + base::Bind(&ModelTypeSyncProxyImpl::OnCommitCompletion, processor_, type_state, response_list)); } -void NonBlockingTypeProcessorWrapper::ReceiveUpdateResponse( +void ModelTypeSyncProxyWrapper::ReceiveUpdateResponse( const DataTypeState& type_state, const UpdateResponseDataList& response_list) { processor_task_runner_->PostTask( FROM_HERE, - base::Bind(&NonBlockingTypeProcessor::OnUpdateReceived, + base::Bind(&ModelTypeSyncProxyImpl::OnUpdateReceived, processor_, type_state, response_list)); } -class NonBlockingTypeProcessorCoreWrapper - : public NonBlockingTypeProcessorCoreInterface { +class ModelTypeSyncWorkerWrapper : public ModelTypeSyncWorker { public: - NonBlockingTypeProcessorCoreWrapper( - base::WeakPtr core, - scoped_refptr sync_thread); - virtual ~NonBlockingTypeProcessorCoreWrapper(); + ModelTypeSyncWorkerWrapper( + const base::WeakPtr& worker, + const scoped_refptr& sync_thread); + virtual ~ModelTypeSyncWorkerWrapper(); virtual void RequestCommits(const CommitRequestDataList& list) OVERRIDE; private: - base::WeakPtr core_; + base::WeakPtr worker_; scoped_refptr sync_thread_; }; -NonBlockingTypeProcessorCoreWrapper::NonBlockingTypeProcessorCoreWrapper( - base::WeakPtr core, - scoped_refptr sync_thread) - : core_(core), sync_thread_(sync_thread) { +ModelTypeSyncWorkerWrapper::ModelTypeSyncWorkerWrapper( + const base::WeakPtr& worker, + const scoped_refptr& sync_thread) + : worker_(worker), sync_thread_(sync_thread) { } -NonBlockingTypeProcessorCoreWrapper::~NonBlockingTypeProcessorCoreWrapper() { +ModelTypeSyncWorkerWrapper::~ModelTypeSyncWorkerWrapper() { } -void NonBlockingTypeProcessorCoreWrapper::RequestCommits( +void ModelTypeSyncWorkerWrapper::RequestCommits( const CommitRequestDataList& list) { sync_thread_->PostTask( FROM_HERE, - base::Bind(&NonBlockingTypeProcessorCore::EnqueueForCommit, core_, list)); + base::Bind(&ModelTypeSyncWorkerImpl::EnqueueForCommit, worker_, list)); } } // namespace @@ -186,36 +184,34 @@ void ModelTypeRegistry::SetEnabledDirectoryTypes( void ModelTypeRegistry::InitializeNonBlockingType( ModelType type, const DataTypeState& data_type_state, - scoped_refptr type_task_runner, - base::WeakPtr processor) { + const scoped_refptr& type_task_runner, + const base::WeakPtr& proxy_impl) { DVLOG(1) << "Enabling an off-thread sync type: " << ModelTypeToString(type); - // Initialize CoreProcessor -> Processor communication channel. - scoped_ptr processor_interface( - new NonBlockingTypeProcessorWrapper(processor, type_task_runner)); - scoped_ptr core( - new NonBlockingTypeProcessorCore( - type, data_type_state, processor_interface.Pass())); - - // Initialize Processor -> CoreProcessor communication channel. - scoped_ptr core_interface( - new NonBlockingTypeProcessorCoreWrapper( - core->AsWeakPtr(), - scoped_refptr( - base::MessageLoopProxy::current()))); + // Initialize Worker -> Proxy communication channel. + scoped_ptr proxy( + new ModelTypeSyncProxyWrapper(proxy_impl, type_task_runner)); + scoped_ptr worker( + new ModelTypeSyncWorkerImpl(type, data_type_state, proxy.Pass())); + + // Initialize Proxy -> Worker communication channel. + scoped_ptr wrapped_worker( + new ModelTypeSyncWorkerWrapper(worker->AsWeakPtr(), + scoped_refptr( + base::MessageLoopProxy::current()))); type_task_runner->PostTask(FROM_HERE, - base::Bind(&NonBlockingTypeProcessor::OnConnect, - processor, - base::Passed(&core_interface))); + base::Bind(&ModelTypeSyncProxyImpl::OnConnect, + proxy_impl, + base::Passed(&wrapped_worker))); DCHECK(update_handler_map_.find(type) == update_handler_map_.end()); DCHECK(commit_contributor_map_.find(type) == commit_contributor_map_.end()); - update_handler_map_.insert(std::make_pair(type, core.get())); - commit_contributor_map_.insert(std::make_pair(type, core.get())); + update_handler_map_.insert(std::make_pair(type, worker.get())); + commit_contributor_map_.insert(std::make_pair(type, worker.get())); // The container takes ownership. - non_blocking_type_processor_cores_.push_back(core.release()); + model_type_sync_workers_.push_back(worker.release()); DCHECK(Intersection(GetEnabledDirectoryTypes(), GetEnabledNonBlockingTypes()).Empty()); @@ -232,12 +228,13 @@ void ModelTypeRegistry::RemoveNonBlockingType(ModelType type) { DCHECK_EQ(1U, updaters_erased); DCHECK_EQ(1U, committers_erased); - // Remove from the ScopedVector, deleting the core in the process. - for (ScopedVector::iterator it = - non_blocking_type_processor_cores_.begin(); - it != non_blocking_type_processor_cores_.end(); ++it) { + // Remove from the ScopedVector, deleting the worker in the process. + for (ScopedVector::iterator it = + model_type_sync_workers_.begin(); + it != model_type_sync_workers_.end(); + ++it) { if ((*it)->GetModelType() == type) { - non_blocking_type_processor_cores_.erase(it); + model_type_sync_workers_.erase(it); break; } } @@ -292,9 +289,10 @@ ModelTypeSet ModelTypeRegistry::GetEnabledDirectoryTypes() const { ModelTypeSet ModelTypeRegistry::GetEnabledNonBlockingTypes() const { ModelTypeSet enabled_off_thread_types; - for (ScopedVector::const_iterator it = - non_blocking_type_processor_cores_.begin(); - it != non_blocking_type_processor_cores_.end(); ++it) { + for (ScopedVector::const_iterator it = + model_type_sync_workers_.begin(); + it != model_type_sync_workers_.end(); + ++it) { enabled_off_thread_types.Put((*it)->GetModelType()); } return enabled_off_thread_types; diff --git a/sync/sessions/model_type_registry.h b/sync/sessions/model_type_registry.h index 6d05dfc..255a841 100644 --- a/sync/sessions/model_type_registry.h +++ b/sync/sessions/model_type_registry.h @@ -25,8 +25,8 @@ class CommitContributor; class DirectoryCommitContributor; class DirectoryUpdateHandler; class DirectoryTypeDebugInfoEmitter; -class NonBlockingTypeProcessorCore; -class NonBlockingTypeProcessor; +class ModelTypeSyncWorkerImpl; +class ModelTypeSyncProxyImpl; class UpdateHandler; struct DataTypeState; @@ -51,20 +51,20 @@ class SYNC_EXPORT_PRIVATE ModelTypeRegistry { // Sets the set of enabled types. void SetEnabledDirectoryTypes(const ModelSafeRoutingInfo& routing_info); - // Enables an off-thread type for syncing. Connects the given processor - // and its task_runner to the newly created processor core. + // Enables an off-thread type for syncing. Connects the given proxy + // and its task_runner to the newly created worker. // - // Expects that the processor's ModelType is not currently enabled. + // Expects that the proxy's ModelType is not currently enabled. void InitializeNonBlockingType( syncer::ModelType type, const DataTypeState& data_type_state, - scoped_refptr type_task_runner, - base::WeakPtr processor); + const scoped_refptr& type_task_runner, + const base::WeakPtr& proxy); // Disables the syncing of an off-thread type. // // Expects that the type is currently enabled. - // Deletes the processor core associated with the type. + // Deletes the worker associated with the type. void RemoveNonBlockingType(syncer::ModelType type); // Gets the set of enabled types. @@ -93,7 +93,7 @@ class SYNC_EXPORT_PRIVATE ModelTypeRegistry { ScopedVector directory_type_debug_info_emitters_; - ScopedVector non_blocking_type_processor_cores_; + ScopedVector model_type_sync_workers_; // Maps of UpdateHandlers and CommitContributors. // They do not own any of the objects they point to. diff --git a/sync/sessions/model_type_registry_unittest.cc b/sync/sessions/model_type_registry_unittest.cc index 2d42215..d9d314e 100644 --- a/sync/sessions/model_type_registry_unittest.cc +++ b/sync/sessions/model_type_registry_unittest.cc @@ -6,7 +6,7 @@ #include "base/deferred_sequenced_task_runner.h" #include "base/message_loop/message_loop.h" -#include "sync/engine/non_blocking_type_processor.h" +#include "sync/engine/model_type_sync_proxy_impl.h" #include "sync/internal_api/public/base/model_type.h" #include "sync/sessions/model_type_registry.h" #include "sync/test/engine/fake_model_worker.h" @@ -140,8 +140,8 @@ TEST_F(ModelTypeRegistryTest, SetEnabledDirectoryTypes_OffAndOn) { } TEST_F(ModelTypeRegistryTest, NonBlockingTypes) { - NonBlockingTypeProcessor themes_processor(syncer::THEMES); - NonBlockingTypeProcessor sessions_processor(syncer::SESSIONS); + ModelTypeSyncProxyImpl themes_sync_proxy(syncer::THEMES); + ModelTypeSyncProxyImpl sessions_sync_proxy(syncer::SESSIONS); scoped_refptr task_runner = new base::DeferredSequencedTaskRunner(base::MessageLoopProxy::current()); @@ -150,14 +150,14 @@ TEST_F(ModelTypeRegistryTest, NonBlockingTypes) { registry()->InitializeNonBlockingType(syncer::THEMES, MakeInitialDataTypeState(THEMES), task_runner, - themes_processor.AsWeakPtrForUI()); + themes_sync_proxy.AsWeakPtrForUI()); EXPECT_TRUE(registry()->GetEnabledTypes().Equals( ModelTypeSet(syncer::THEMES))); registry()->InitializeNonBlockingType(syncer::SESSIONS, MakeInitialDataTypeState(SESSIONS), task_runner, - sessions_processor.AsWeakPtrForUI()); + sessions_sync_proxy.AsWeakPtrForUI()); EXPECT_TRUE(registry()->GetEnabledTypes().Equals( ModelTypeSet(syncer::THEMES, syncer::SESSIONS))); @@ -166,12 +166,12 @@ TEST_F(ModelTypeRegistryTest, NonBlockingTypes) { ModelTypeSet(syncer::SESSIONS))); // Allow ModelTypeRegistry destruction to delete the - // Sessions' NonBlockingTypeProcessorCore. + // Sessions' ModelTypeSyncWorker. } TEST_F(ModelTypeRegistryTest, NonBlockingTypesWithDirectoryTypes) { - NonBlockingTypeProcessor themes_processor(syncer::THEMES); - NonBlockingTypeProcessor sessions_processor(syncer::SESSIONS); + ModelTypeSyncProxyImpl themes_sync_proxy(syncer::THEMES); + ModelTypeSyncProxyImpl sessions_sync_proxy(syncer::SESSIONS); scoped_refptr task_runner = new base::DeferredSequencedTaskRunner(base::MessageLoopProxy::current()); @@ -187,7 +187,7 @@ TEST_F(ModelTypeRegistryTest, NonBlockingTypesWithDirectoryTypes) { registry()->InitializeNonBlockingType(syncer::THEMES, MakeInitialDataTypeState(THEMES), task_runner, - themes_processor.AsWeakPtrForUI()); + themes_sync_proxy.AsWeakPtrForUI()); current_types.Put(syncer::THEMES); EXPECT_TRUE(registry()->GetEnabledTypes().Equals(current_types)); @@ -200,7 +200,7 @@ TEST_F(ModelTypeRegistryTest, NonBlockingTypesWithDirectoryTypes) { registry()->InitializeNonBlockingType(syncer::SESSIONS, MakeInitialDataTypeState(SESSIONS), task_runner, - sessions_processor.AsWeakPtrForUI()); + sessions_sync_proxy.AsWeakPtrForUI()); current_types.Put(syncer::SESSIONS); EXPECT_TRUE(registry()->GetEnabledTypes().Equals(current_types)); @@ -217,10 +217,10 @@ TEST_F(ModelTypeRegistryTest, NonBlockingTypesWithDirectoryTypes) { } TEST_F(ModelTypeRegistryTest, DeletionOrdering) { - scoped_ptr themes_processor( - new NonBlockingTypeProcessor(syncer::THEMES)); - scoped_ptr sessions_processor( - new NonBlockingTypeProcessor(syncer::SESSIONS)); + scoped_ptr themes_sync_proxy( + new ModelTypeSyncProxyImpl(syncer::THEMES)); + scoped_ptr sessions_sync_proxy( + new ModelTypeSyncProxyImpl(syncer::SESSIONS)); scoped_refptr task_runner = new base::DeferredSequencedTaskRunner(base::MessageLoopProxy::current()); @@ -229,20 +229,20 @@ TEST_F(ModelTypeRegistryTest, DeletionOrdering) { registry()->InitializeNonBlockingType(syncer::THEMES, MakeInitialDataTypeState(THEMES), task_runner, - themes_processor->AsWeakPtrForUI()); + themes_sync_proxy->AsWeakPtrForUI()); registry()->InitializeNonBlockingType(syncer::SESSIONS, MakeInitialDataTypeState(SESSIONS), task_runner, - sessions_processor->AsWeakPtrForUI()); + sessions_sync_proxy->AsWeakPtrForUI()); EXPECT_TRUE(registry()->GetEnabledTypes().Equals( ModelTypeSet(syncer::THEMES, syncer::SESSIONS))); - // Tear down themes processing, starting with the ProcessorCore. + // Tear down themes processing, starting with the worker. registry()->RemoveNonBlockingType(syncer::THEMES); - themes_processor.reset(); + themes_sync_proxy.reset(); - // Tear down sessions processing, starting with the Processor. - sessions_processor.reset(); + // Tear down sessions processing, starting with the type sync proxy. + sessions_sync_proxy.reset(); registry()->RemoveNonBlockingType(syncer::SESSIONS); EXPECT_TRUE(registry()->GetEnabledTypes().Empty()); diff --git a/sync/sync_core.gypi b/sync/sync_core.gypi index 8772d43..5205208 100644 --- a/sync/sync_core.gypi +++ b/sync/sync_core.gypi @@ -35,11 +35,11 @@ 'engine/backoff_delay_provider.cc', 'engine/backoff_delay_provider.h', 'engine/commit.cc', - 'engine/commit.h', 'engine/commit_contribution.cc', 'engine/commit_contribution.h', 'engine/commit_contributor.cc', 'engine/commit_contributor.h', + 'engine/commit.h', 'engine/commit_processor.cc', 'engine/commit_processor.h', 'engine/commit_util.cc', @@ -54,14 +54,24 @@ 'engine/directory_commit_contributor.h', 'engine/directory_update_handler.cc', 'engine/directory_update_handler.h', + 'engine/entity_tracker.cc', + 'engine/entity_tracker.h', 'engine/get_commit_ids.cc', 'engine/get_commit_ids.h', 'engine/get_updates_delegate.cc', 'engine/get_updates_delegate.h', 'engine/get_updates_processor.cc', 'engine/get_updates_processor.h', - 'engine/model_thread_sync_entity.cc', - 'engine/model_thread_sync_entity.h', + 'engine/model_type_entity.cc', + 'engine/model_type_entity.h', + 'engine/model_type_sync_proxy.cc', + 'engine/model_type_sync_proxy.h', + 'engine/model_type_sync_proxy_impl.cc', + 'engine/model_type_sync_proxy_impl.h', + 'engine/model_type_sync_worker.cc', + 'engine/model_type_sync_worker.h', + 'engine/model_type_sync_worker_impl.cc', + 'engine/model_type_sync_worker_impl.h', 'engine/net/server_connection_manager.cc', 'engine/net/server_connection_manager.h', 'engine/net/url_translator.cc', @@ -70,14 +80,6 @@ 'engine/non_blocking_sync_common.h', 'engine/non_blocking_type_commit_contribution.cc', 'engine/non_blocking_type_commit_contribution.h', - 'engine/non_blocking_type_processor.cc', - 'engine/non_blocking_type_processor.h', - 'engine/non_blocking_type_processor_core.cc', - 'engine/non_blocking_type_processor_core.h', - 'engine/non_blocking_type_processor_core_interface.cc', - 'engine/non_blocking_type_processor_core_interface.h', - 'engine/non_blocking_type_processor_interface.cc', - 'engine/non_blocking_type_processor_interface.h', 'engine/nudge_source.cc', 'engine/nudge_source.h', 'engine/process_updates_util.cc', @@ -86,12 +88,6 @@ 'engine/sync_cycle_event.h', 'engine/sync_engine_event_listener.cc', 'engine/sync_engine_event_listener.h', - 'engine/sync_scheduler.cc', - 'engine/sync_scheduler.h', - 'engine/sync_scheduler_impl.cc', - 'engine/sync_scheduler_impl.h', - 'engine/sync_thread_sync_entity.cc', - 'engine/sync_thread_sync_entity.h', 'engine/syncer.cc', 'engine/syncer.h', 'engine/syncer_proto_util.cc', @@ -99,6 +95,10 @@ 'engine/syncer_types.h', 'engine/syncer_util.cc', 'engine/syncer_util.h', + 'engine/sync_scheduler.cc', + 'engine/sync_scheduler.h', + 'engine/sync_scheduler_impl.cc', + 'engine/sync_scheduler_impl.h', 'engine/traffic_logger.cc', 'engine/traffic_logger.h', 'engine/update_applicator.cc', diff --git a/sync/sync_internal_api.gypi b/sync/sync_internal_api.gypi index f0ae375..fc49a48f 100644 --- a/sync/sync_internal_api.gypi +++ b/sync/sync_internal_api.gypi @@ -120,7 +120,7 @@ 'internal_api/public/sessions/update_counters.cc', 'internal_api/public/sessions/update_counters.h', 'internal_api/public/sync_auth_provider.h', - 'internal_api/public/sync_core_proxy.h', + 'internal_api/public/sync_context_proxy.h', 'internal_api/public/sync_encryption_handler.cc', 'internal_api/public/sync_encryption_handler.h', 'internal_api/public/sync_manager.cc', @@ -145,20 +145,20 @@ 'internal_api/read_transaction.cc', 'internal_api/sync_backup_manager.cc', 'internal_api/sync_backup_manager.h', - 'internal_api/sync_core.cc', - 'internal_api/sync_core.h', - 'internal_api/sync_core_proxy.cc', - 'internal_api/sync_core_proxy_impl.cc', - 'internal_api/sync_core_proxy_impl.h', + 'internal_api/sync_context.cc', + 'internal_api/sync_context.h', + 'internal_api/sync_context_proxy.cc', + 'internal_api/sync_context_proxy_impl.cc', + 'internal_api/sync_context_proxy_impl.h', 'internal_api/sync_encryption_handler_impl.cc', 'internal_api/sync_encryption_handler_impl.h', 'internal_api/sync_manager_factory.cc', 'internal_api/sync_manager_impl.cc', 'internal_api/sync_manager_impl.h', - 'internal_api/sync_rollback_manager.cc', - 'internal_api/sync_rollback_manager.h', 'internal_api/sync_rollback_manager_base.cc', 'internal_api/sync_rollback_manager_base.h', + 'internal_api/sync_rollback_manager.cc', + 'internal_api/sync_rollback_manager.h', 'internal_api/syncapi_internal.cc', 'internal_api/syncapi_internal.h', 'internal_api/syncapi_server_connection_manager.cc', diff --git a/sync/sync_tests.gypi b/sync/sync_tests.gypi index 6212dd4..5ba457b 100644 --- a/sync/sync_tests.gypi +++ b/sync/sync_tests.gypi @@ -38,12 +38,14 @@ 'test/engine/fake_model_worker.h', 'test/engine/fake_sync_scheduler.cc', 'test/engine/fake_sync_scheduler.h', + 'test/engine/injectable_sync_context_proxy.cc', + 'test/engine/injectable_sync_context_proxy.h', 'test/engine/mock_connection_manager.cc', 'test/engine/mock_connection_manager.h', - 'test/engine/mock_non_blocking_type_processor_core.cc', - 'test/engine/mock_non_blocking_type_processor_core.h', - 'test/engine/mock_non_blocking_type_processor.cc', - 'test/engine/mock_non_blocking_type_processor.h', + 'test/engine/mock_model_type_sync_proxy.cc', + 'test/engine/mock_model_type_sync_proxy.h', + 'test/engine/mock_model_type_sync_worker.cc', + 'test/engine/mock_model_type_sync_worker.h', 'test/engine/mock_update_handler.cc', 'test/engine/mock_update_handler.h', 'test/engine/single_type_mock_server.cc', @@ -53,8 +55,6 @@ 'test/engine/test_id_factory.h', 'test/engine/test_syncable_utils.cc', 'test/engine/test_syncable_utils.h', - 'test/engine/injectable_sync_core_proxy.cc', - 'test/engine/injectable_sync_core_proxy.h', 'test/fake_encryptor.cc', 'test/fake_encryptor.h', 'test/fake_sync_encryption_handler.cc', @@ -200,13 +200,13 @@ 'internal_api/public/base/invalidation_test_util.cc', 'internal_api/public/base/invalidation_test_util.h', 'internal_api/public/test/fake_sync_manager.h', - 'internal_api/public/test/null_sync_core_proxy.h', + 'internal_api/public/test/null_sync_context_proxy.h', 'internal_api/public/test/sync_manager_factory_for_profile_sync_test.h', 'internal_api/public/test/test_entry_factory.h', 'internal_api/public/test/test_internal_components_factory.h', 'internal_api/public/test/test_user_share.h', 'internal_api/test/fake_sync_manager.cc', - 'internal_api/test/null_sync_core_proxy.cc', + 'internal_api/test/null_sync_context_proxy.cc', 'internal_api/test/sync_manager_factory_for_profile_sync_test.cc', 'internal_api/test/sync_manager_for_profile_sync_test.cc', 'internal_api/test/sync_manager_for_profile_sync_test.h', @@ -298,12 +298,12 @@ 'engine/backoff_delay_provider_unittest.cc', 'engine/directory_commit_contribution_unittest.cc', 'engine/directory_update_handler_unittest.cc', + 'engine/entity_tracker_unittest.cc', 'engine/get_updates_processor_unittest.cc', - 'engine/model_thread_sync_entity_unittest.cc', - 'engine/non_blocking_type_processor_core_unittest.cc', - 'engine/non_blocking_type_processor_unittest.cc', + 'engine/model_type_entity_unittest.cc', + 'engine/model_type_sync_proxy_impl_unittest.cc', + 'engine/model_type_sync_worker_impl_unittest.cc', 'engine/sync_scheduler_unittest.cc', - 'engine/sync_thread_sync_entity_unittest.cc', 'engine/syncer_proto_util_unittest.cc', 'engine/syncer_unittest.cc', 'engine/syncer_util_unittest.cc', @@ -435,7 +435,7 @@ 'internal_api/public/change_record_unittest.cc', 'internal_api/public/sessions/sync_session_snapshot_unittest.cc', 'internal_api/sync_backup_manager_unittest.cc', - 'internal_api/sync_core_proxy_impl_unittest.cc', + 'internal_api/sync_context_proxy_impl_unittest.cc', 'internal_api/sync_encryption_handler_impl_unittest.cc', 'internal_api/sync_manager_impl_unittest.cc', 'internal_api/sync_rollback_manager_base_unittest.cc', diff --git a/sync/test/engine/injectable_sync_context_proxy.cc b/sync/test/engine/injectable_sync_context_proxy.cc new file mode 100644 index 0000000..27de880 --- /dev/null +++ b/sync/test/engine/injectable_sync_context_proxy.cc @@ -0,0 +1,49 @@ +// 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/engine/injectable_sync_context_proxy.h" + +#include "sync/engine/model_type_sync_proxy_impl.h" +#include "sync/engine/model_type_sync_worker.h" + +namespace syncer { + +InjectableSyncContextProxy::InjectableSyncContextProxy( + ModelTypeSyncWorker* worker) + : is_worker_connected_(false), worker_(worker) { +} + +InjectableSyncContextProxy::~InjectableSyncContextProxy() { +} + +void InjectableSyncContextProxy::ConnectTypeToSync( + syncer::ModelType type, + const DataTypeState& data_type_state, + const base::WeakPtr& type_sync_proxy) { + // This class is allowed to participate in only one connection. + DCHECK(!is_worker_connected_); + is_worker_connected_ = true; + + // Hands off ownership of our member to the type_sync_proxy, while keeping + // an unsafe pointer to it. This is why we can only connect once. + scoped_ptr worker(worker_); + + type_sync_proxy->OnConnect(worker.Pass()); +} + +void InjectableSyncContextProxy::Disconnect(syncer::ModelType type) { + // This should delete the worker, but we don't own it. + worker_ = NULL; +} + +scoped_ptr InjectableSyncContextProxy::Clone() const { + // This confuses ownership. We trust that our callers are well-behaved. + return scoped_ptr(new InjectableSyncContextProxy(worker_)); +} + +ModelTypeSyncWorker* InjectableSyncContextProxy::GetWorker() { + return worker_; +} + +} // namespace syncer diff --git a/sync/test/engine/injectable_sync_context_proxy.h b/sync/test/engine/injectable_sync_context_proxy.h new file mode 100644 index 0000000..7c9f48d --- /dev/null +++ b/sync/test/engine/injectable_sync_context_proxy.h @@ -0,0 +1,48 @@ +// 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_ENGINE_INJECTABLE_SYNC_CONTEXT_PROXY_H_ +#define SYNC_TEST_ENGINE_INJECTABLE_SYNC_CONTEXT_PROXY_H_ + +#include "sync/internal_api/public/base/model_type.h" +#include "sync/internal_api/public/sync_context_proxy.h" + +namespace syncer { + +struct DataTypeState; +class ModelTypeSyncProxyImpl; +class ModelTypeSyncWorker; + +// A SyncContextProxy implementation that, when a connection request is made, +// initalizes a connection to a previously injected ModelTypeSyncProxyImpl. +class InjectableSyncContextProxy : public syncer::SyncContextProxy { + public: + explicit InjectableSyncContextProxy(ModelTypeSyncWorker* worker); + virtual ~InjectableSyncContextProxy(); + + virtual void ConnectTypeToSync( + syncer::ModelType type, + const DataTypeState& data_type_state, + const base::WeakPtr& type_sync_proxy) + OVERRIDE; + virtual void Disconnect(syncer::ModelType type) OVERRIDE; + virtual scoped_ptr Clone() const OVERRIDE; + + ModelTypeSyncWorker* GetWorker(); + + private: + // A flag to ensure ConnectTypeToSync is called at most once. + bool is_worker_connected_; + + // The ModelTypeSyncProxy's contract expects that it gets to own this object, + // so we can retain only a non-owned pointer to it. + // + // This is very unsafe, but we can get away with it since these tests are not + // exercising the proxy <-> worker connection code. + ModelTypeSyncWorker* worker_; +}; + +} // namespace syncer + +#endif // SYNC_TEST_ENGINE_INJECTABLE_SYNC_CONTEXT_PROXY_H_ diff --git a/sync/test/engine/injectable_sync_core_proxy.cc b/sync/test/engine/injectable_sync_core_proxy.cc deleted file mode 100644 index 4041736..0000000 --- a/sync/test/engine/injectable_sync_core_proxy.cc +++ /dev/null @@ -1,51 +0,0 @@ -// 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/engine/injectable_sync_core_proxy.h" - -#include "sync/engine/non_blocking_type_processor.h" -#include "sync/engine/non_blocking_type_processor_core_interface.h" - -namespace syncer { - -InjectableSyncCoreProxy::InjectableSyncCoreProxy( - NonBlockingTypeProcessorCoreInterface* core) - : is_core_connected_(false), processor_core_(core) { -} - -InjectableSyncCoreProxy::~InjectableSyncCoreProxy() { -} - -void InjectableSyncCoreProxy::ConnectTypeToCore( - syncer::ModelType type, - const DataTypeState& data_type_state, - base::WeakPtr type_processor) { - // This class is allowed to participate in only one connection. - DCHECK(!is_core_connected_); - is_core_connected_ = true; - - // Hands off ownership of our member to the type_processor, while keeping - // an unsafe pointer to it. This is why we can only connect once. - scoped_ptr core(processor_core_); - - type_processor->OnConnect(core.Pass()); -} - -void InjectableSyncCoreProxy::Disconnect(syncer::ModelType type) { - // This should delete the core, but we don't own it. - processor_core_ = NULL; -} - -scoped_ptr InjectableSyncCoreProxy::Clone() const { - // This confuses ownership. We trust that our callers are well-behaved. - return scoped_ptr( - new InjectableSyncCoreProxy(processor_core_)); -} - -NonBlockingTypeProcessorCoreInterface* -InjectableSyncCoreProxy::GetProcessorCore() { - return processor_core_; -} - -} // namespace syncer diff --git a/sync/test/engine/injectable_sync_core_proxy.h b/sync/test/engine/injectable_sync_core_proxy.h deleted file mode 100644 index 2dc273d..0000000 --- a/sync/test/engine/injectable_sync_core_proxy.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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_ENGINE_INJECTABLE_SYNC_CORE_PROXY_H_ -#define SYNC_TEST_ENGINE_INJECTABLE_SYNC_CORE_PROXY_H_ - -#include "sync/internal_api/public/base/model_type.h" -#include "sync/internal_api/public/sync_core_proxy.h" - -namespace syncer { - -struct DataTypeState; -class NonBlockingTypeProcessor; -class NonBlockingTypeProcessorCoreInterface; - -// A SyncCoreProxy implementation that, when a connection request is made, -// initalizes a connection to a previously injected NonBlockingTypeProcessor. -class InjectableSyncCoreProxy : public syncer::SyncCoreProxy { - public: - explicit InjectableSyncCoreProxy(NonBlockingTypeProcessorCoreInterface* core); - virtual ~InjectableSyncCoreProxy(); - - virtual void ConnectTypeToCore( - syncer::ModelType type, - const DataTypeState& data_type_state, - base::WeakPtr type_processor) OVERRIDE; - virtual void Disconnect(syncer::ModelType type) OVERRIDE; - virtual scoped_ptr Clone() const OVERRIDE; - - NonBlockingTypeProcessorCoreInterface* GetProcessorCore(); - - private: - // A flag to ensure ConnectTypeToCore is called at most once. - bool is_core_connected_; - - // The NonBlockingTypeProcessor's contract expects that it gets to own this - // object, so we can retain only a non-owned pointer to it. - // - // This is very unsafe, but we can get away with it since these tests are not - // exercising the processor <-> processor_core connection code. - NonBlockingTypeProcessorCoreInterface* processor_core_; -}; - -} // namespace syncer - -#endif // SYNC_TEST_ENGINE_INJECTABLE_SYNC_CORE_PROXY_H_ diff --git a/sync/test/engine/mock_model_type_sync_proxy.cc b/sync/test/engine/mock_model_type_sync_proxy.cc new file mode 100644 index 0000000..440269d --- /dev/null +++ b/sync/test/engine/mock_model_type_sync_proxy.cc @@ -0,0 +1,253 @@ +// 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/engine/mock_model_type_sync_proxy.h" + +#include "base/bind.h" + +namespace syncer { + +MockModelTypeSyncProxy::MockModelTypeSyncProxy() : is_synchronous_(true) { +} + +MockModelTypeSyncProxy::~MockModelTypeSyncProxy() { +} + +void MockModelTypeSyncProxy::ReceiveCommitResponse( + const DataTypeState& type_state, + const CommitResponseDataList& response_list) { + base::Closure task = + base::Bind(&MockModelTypeSyncProxy::ReceiveCommitResponseImpl, + base::Unretained(this), + type_state, + response_list); + pending_tasks_.push_back(task); + if (is_synchronous_) + RunQueuedTasks(); +} + +void MockModelTypeSyncProxy::ReceiveUpdateResponse( + const DataTypeState& type_state, + const UpdateResponseDataList& response_list) { + base::Closure task = + base::Bind(&MockModelTypeSyncProxy::ReceiveUpdateResponseImpl, + base::Unretained(this), + type_state, + response_list); + pending_tasks_.push_back(task); + if (is_synchronous_) + RunQueuedTasks(); +} + +void MockModelTypeSyncProxy::SetSynchronousExecution(bool is_synchronous) { + is_synchronous_ = is_synchronous; +} + +void MockModelTypeSyncProxy::RunQueuedTasks() { + for (std::vector::iterator it = pending_tasks_.begin(); + it != pending_tasks_.end(); + ++it) { + it->Run(); + } + pending_tasks_.clear(); +} + +CommitRequestData MockModelTypeSyncProxy::CommitRequest( + const std::string& tag_hash, + const sync_pb::EntitySpecifics& specifics) { + const int64 base_version = GetBaseVersion(tag_hash); + + CommitRequestData data; + + if (HasServerAssignedId(tag_hash)) { + data.id = GetServerAssignedId(tag_hash); + } + + data.client_tag_hash = tag_hash; + data.sequence_number = GetNextSequenceNumber(tag_hash); + data.deleted = false; + data.specifics = specifics; + data.base_version = base_version; + + // These fields are not really used for much, but we set them anyway + // to make this item look more realistic. + data.ctime = base::Time::UnixEpoch() + base::TimeDelta::FromDays(1); + data.mtime = data.ctime + base::TimeDelta::FromSeconds(base_version); + data.non_unique_name = "Name: " + tag_hash; + + return data; +} + +CommitRequestData MockModelTypeSyncProxy::DeleteRequest( + const std::string& tag_hash) { + const int64 base_version = GetBaseVersion(tag_hash); + CommitRequestData data; + + if (HasServerAssignedId(tag_hash)) { + data.id = GetServerAssignedId(tag_hash); + } + + data.client_tag_hash = tag_hash; + data.sequence_number = GetNextSequenceNumber(tag_hash); + data.base_version = base_version; + data.mtime = data.ctime + base::TimeDelta::FromSeconds(base_version); + data.deleted = true; + + // These fields have little or no effect on behavior. We set them anyway to + // make the test more realistic. + data.ctime = base::Time::UnixEpoch() + base::TimeDelta::FromDays(1); + data.non_unique_name = "Name deleted"; + + return data; +} + +size_t MockModelTypeSyncProxy::GetNumUpdateResponses() const { + return received_update_responses_.size(); +} + +UpdateResponseDataList MockModelTypeSyncProxy::GetNthUpdateResponse( + size_t n) const { + DCHECK_LT(n, GetNumUpdateResponses()); + return received_update_responses_[n]; +} + +DataTypeState MockModelTypeSyncProxy::GetNthTypeStateReceivedInUpdateResponse( + size_t n) const { + DCHECK_LT(n, GetNumUpdateResponses()); + return type_states_received_on_update_[n]; +} + +size_t MockModelTypeSyncProxy::GetNumCommitResponses() const { + return received_commit_responses_.size(); +} + +CommitResponseDataList MockModelTypeSyncProxy::GetNthCommitResponse( + size_t n) const { + DCHECK_LT(n, GetNumCommitResponses()); + return received_commit_responses_[n]; +} + +DataTypeState MockModelTypeSyncProxy::GetNthTypeStateReceivedInCommitResponse( + size_t n) const { + DCHECK_LT(n, GetNumCommitResponses()); + return type_states_received_on_commit_[n]; +} + +bool MockModelTypeSyncProxy::HasUpdateResponse( + const std::string& tag_hash) const { + std::map::const_iterator it = + update_response_items_.find(tag_hash); + return it != update_response_items_.end(); +} + +UpdateResponseData MockModelTypeSyncProxy::GetUpdateResponse( + const std::string& tag_hash) const { + DCHECK(HasUpdateResponse(tag_hash)); + std::map::const_iterator it = + update_response_items_.find(tag_hash); + return it->second; +} + +bool MockModelTypeSyncProxy::HasCommitResponse( + const std::string& tag_hash) const { + std::map::const_iterator it = + commit_response_items_.find(tag_hash); + return it != commit_response_items_.end(); +} + +CommitResponseData MockModelTypeSyncProxy::GetCommitResponse( + const std::string& tag_hash) const { + DCHECK(HasCommitResponse(tag_hash)); + std::map::const_iterator it = + commit_response_items_.find(tag_hash); + return it->second; +} + +void MockModelTypeSyncProxy::ReceiveCommitResponseImpl( + const DataTypeState& type_state, + const CommitResponseDataList& response_list) { + received_commit_responses_.push_back(response_list); + type_states_received_on_commit_.push_back(type_state); + for (CommitResponseDataList::const_iterator it = response_list.begin(); + it != response_list.end(); + ++it) { + commit_response_items_.insert(std::make_pair(it->client_tag_hash, *it)); + + // Server wins. Set the model's base version. + SetBaseVersion(it->client_tag_hash, it->response_version); + SetServerAssignedId(it->client_tag_hash, it->id); + } +} + +void MockModelTypeSyncProxy::ReceiveUpdateResponseImpl( + const DataTypeState& type_state, + const UpdateResponseDataList& response_list) { + received_update_responses_.push_back(response_list); + type_states_received_on_update_.push_back(type_state); + for (UpdateResponseDataList::const_iterator it = response_list.begin(); + it != response_list.end(); + ++it) { + update_response_items_.insert(std::make_pair(it->client_tag_hash, *it)); + + // Server wins. Set the model's base version. + SetBaseVersion(it->client_tag_hash, it->response_version); + SetServerAssignedId(it->client_tag_hash, it->id); + } +} + +// Fetches the sequence number as of the most recent update request. +int64 MockModelTypeSyncProxy::GetCurrentSequenceNumber( + const std::string& tag_hash) const { + std::map::const_iterator 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 MockModelTypeSyncProxy::GetNextSequenceNumber( + const std::string& tag_hash) { + int64 sequence_number = GetCurrentSequenceNumber(tag_hash); + sequence_number++; + sequence_numbers_[tag_hash] = sequence_number; + return sequence_number; +} + +int64 MockModelTypeSyncProxy::GetBaseVersion( + const std::string& tag_hash) const { + std::map::const_iterator it = + base_versions_.find(tag_hash); + if (it == base_versions_.end()) { + return kUncommittedVersion; + } else { + return it->second; + } +} + +void MockModelTypeSyncProxy::SetBaseVersion(const std::string& tag_hash, + int64 version) { + base_versions_[tag_hash] = version; +} + +bool MockModelTypeSyncProxy::HasServerAssignedId( + const std::string& tag_hash) const { + return assigned_ids_.find(tag_hash) != assigned_ids_.end(); +} + +const std::string& MockModelTypeSyncProxy::GetServerAssignedId( + const std::string& tag_hash) const { + DCHECK(HasServerAssignedId(tag_hash)); + return assigned_ids_.find(tag_hash)->second; +} + +void MockModelTypeSyncProxy::SetServerAssignedId(const std::string& tag_hash, + const std::string& id) { + assigned_ids_[tag_hash] = id; +} + +} // namespace syncer diff --git a/sync/test/engine/mock_model_type_sync_proxy.h b/sync/test/engine/mock_model_type_sync_proxy.h new file mode 100644 index 0000000..54283d0 --- /dev/null +++ b/sync/test/engine/mock_model_type_sync_proxy.h @@ -0,0 +1,136 @@ +// 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_ENGINE_MOCK_MODEL_TYPE_SYNC_PROXY_H_ +#define SYNC_TEST_ENGINE_MOCK_MODEL_TYPE_SYNC_PROXY_H_ + +#include + +#include "base/callback.h" +#include "base/macros.h" +#include "sync/engine/model_type_sync_proxy.h" +#include "sync/engine/non_blocking_sync_common.h" + +namespace syncer { + +// Mocks the ModelTypeSyncProxy. +// +// This mock is made simpler by not using any threads. It does still have the +// ability to defer execution if we need to test race conditions, though. +// +// It maintains some state to try to make its behavior more realistic. It +// updates this state as it creates commit requests or receives update and +// commit responses. +// +// It keeps a log of all received messages so tests can make assertions based +// on their value. +class MockModelTypeSyncProxy : public ModelTypeSyncProxy { + public: + MockModelTypeSyncProxy(); + virtual ~MockModelTypeSyncProxy(); + + // Implementation of ModelTypeSyncProxy. + virtual void ReceiveCommitResponse( + const DataTypeState& type_state, + const CommitResponseDataList& response_list) OVERRIDE; + virtual void ReceiveUpdateResponse( + const DataTypeState& type_state, + const UpdateResponseDataList& response_list) OVERRIDE; + + // By default, this object behaves as if all messages are processed + // immediately. Sometimes it is useful to defer work until later, as might + // happen in the real world if the model thread's task queue gets backed up. + void SetSynchronousExecution(bool is_synchronous); + + // Runs any work that was deferred while this class was in asynchronous mode. + // + // This function is not useful unless this object is set to synchronous + // execution mode, which is the default. + void RunQueuedTasks(); + + // Generate commit or deletion requests to be sent to the server. + // These functions update local state to keep sequence numbers consistent. + // + // A real ModelTypeSyncProxy would forward these kinds of messages + // directly to its attached ModelTypeSyncWorker. These methods + // return the value to the caller so the test framework can handle them as it + // sees fit. + CommitRequestData CommitRequest(const std::string& tag_hash, + const sync_pb::EntitySpecifics& specifics); + CommitRequestData DeleteRequest(const std::string& tag_hash); + + // Getters to access the log of received update responses. + // + // Does not includes repsonses that are in pending tasks. + size_t GetNumUpdateResponses() const; + UpdateResponseDataList GetNthUpdateResponse(size_t n) const; + DataTypeState GetNthTypeStateReceivedInUpdateResponse(size_t n) const; + + // Getters to access the log of received commit responses. + // + // Does not includes repsonses that are in pending tasks. + size_t GetNumCommitResponses() const; + CommitResponseDataList GetNthCommitResponse(size_t n) const; + DataTypeState GetNthTypeStateReceivedInCommitResponse(size_t n) const; + + // Getters to access the lastest update response for a given tag_hash. + bool HasUpdateResponse(const std::string& tag_hash) const; + UpdateResponseData GetUpdateResponse(const std::string& tag_hash) const; + + // Getters to access the lastest commit response for a given tag_hash. + bool HasCommitResponse(const std::string& tag_hash) const; + CommitResponseData GetCommitResponse(const std::string& tag_hash) const; + + private: + // Process a received commit response. + // + // Implemented as an Impl method so we can defer its execution in some cases. + void ReceiveCommitResponseImpl(const DataTypeState& type_state, + const CommitResponseDataList& response_list); + + // Process a received update response. + // + // Implemented as an Impl method so we can defer its execution in some cases. + void ReceiveUpdateResponseImpl(const DataTypeState& type_state, + const UpdateResponseDataList& response_list); + + // Getter and setter for per-item sequence number tracking. + int64 GetCurrentSequenceNumber(const std::string& tag_hash) const; + int64 GetNextSequenceNumber(const std::string& tag_hash); + + // Getter and setter for per-item base version tracking. + int64 GetBaseVersion(const std::string& tag_hash) const; + void SetBaseVersion(const std::string& tag_hash, int64 version); + + // Getters and setter for server-assigned ID values. + bool HasServerAssignedId(const std::string& tag_hash) const; + const std::string& GetServerAssignedId(const std::string& tag_hash) const; + void SetServerAssignedId(const std::string& tag_hash, const std::string& id); + + // State related to the implementation of deferred work. + // See SetSynchronousExecution() for details. + bool is_synchronous_; + std::vector pending_tasks_; + + // A log of messages received by this object. + std::vector received_commit_responses_; + std::vector received_update_responses_; + std::vector type_states_received_on_update_; + std::vector type_states_received_on_commit_; + + // Latest responses received, indexed by tag_hash. + std::map commit_response_items_; + std::map update_response_items_; + + // The per-item state maps. + std::map sequence_numbers_; + std::map base_versions_; + std::map assigned_ids_; + + DISALLOW_COPY_AND_ASSIGN(MockModelTypeSyncProxy); +}; + +} // namespace syncer + +#endif // SYNC_TEST_ENGINE_MOCK_MODEL_TYPE_SYNC_PROXY_H_ diff --git a/sync/test/engine/mock_model_type_sync_worker.cc b/sync/test/engine/mock_model_type_sync_worker.cc new file mode 100644 index 0000000..66c8242 --- /dev/null +++ b/sync/test/engine/mock_model_type_sync_worker.cc @@ -0,0 +1,171 @@ +// 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/engine/mock_model_type_sync_worker.h" + +#include "base/logging.h" + +namespace syncer { + +MockModelTypeSyncWorker::MockModelTypeSyncWorker() { +} + +MockModelTypeSyncWorker::~MockModelTypeSyncWorker() { +} + +void MockModelTypeSyncWorker::RequestCommits( + const CommitRequestDataList& list) { + commit_request_lists_.push_back(list); +} + +size_t MockModelTypeSyncWorker::GetNumCommitRequestLists() const { + return commit_request_lists_.size(); +} + +CommitRequestDataList MockModelTypeSyncWorker::GetNthCommitRequestList( + size_t n) const { + DCHECK_LT(n, GetNumCommitRequestLists()); + return commit_request_lists_[n]; +} + +bool MockModelTypeSyncWorker::HasCommitRequestForTagHash( + const std::string& tag_hash) const { + // Iterate backward through the sets of commit requests to find the most + // recent one that applies to the specified tag_hash. + for (std::vector::const_reverse_iterator lists_it = + commit_request_lists_.rbegin(); + lists_it != commit_request_lists_.rend(); + ++lists_it) { + for (CommitRequestDataList::const_iterator it = lists_it->begin(); + it != lists_it->end(); + ++it) { + if (it->client_tag_hash == tag_hash) { + return true; + } + } + } + + return false; +} + +CommitRequestData MockModelTypeSyncWorker::GetLatestCommitRequestForTagHash( + const std::string& tag_hash) const { + // Iterate backward through the sets of commit requests to find the most + // recent one that applies to the specified tag_hash. + for (std::vector::const_reverse_iterator lists_it = + commit_request_lists_.rbegin(); + lists_it != commit_request_lists_.rend(); + ++lists_it) { + for (CommitRequestDataList::const_iterator it = lists_it->begin(); + it != lists_it->end(); + ++it) { + if (it->client_tag_hash == tag_hash) { + return *it; + } + } + } + + NOTREACHED() << "Could not find commit for tag hash " << tag_hash << "."; + return CommitRequestData(); +} + +UpdateResponseData MockModelTypeSyncWorker::UpdateFromServer( + int64 version_offset, + const std::string& tag_hash, + const sync_pb::EntitySpecifics& specifics) { + // Overwrite the existing server version if this is the new highest version. + int64 old_version = GetServerVersion(tag_hash); + int64 version = old_version + version_offset; + if (version > old_version) { + SetServerVersion(tag_hash, version); + } + + UpdateResponseData data; + data.id = GenerateId(tag_hash); + data.client_tag_hash = tag_hash; + data.response_version = version; + data.deleted = false; + data.specifics = specifics; + + // These elements should have no effect on behavior, but we set them anyway + // so we can test they are properly copied around the system if we want to. + data.ctime = base::Time::UnixEpoch() + base::TimeDelta::FromDays(1); + data.mtime = data.ctime + base::TimeDelta::FromSeconds(version); + data.non_unique_name = specifics.preference().name(); + + return data; +} + +UpdateResponseData MockModelTypeSyncWorker::TombstoneFromServer( + int64 version_offset, + const std::string& tag_hash) { + int64 old_version = GetServerVersion(tag_hash); + int64 version = old_version + version_offset; + if (version > old_version) { + SetServerVersion(tag_hash, version); + } + + UpdateResponseData data; + data.id = GenerateId(tag_hash); + data.client_tag_hash = tag_hash; + data.response_version = version; + data.deleted = true; + + // These elements should have no effect on behavior, but we set them anyway + // so we can test they are properly copied around the system if we want to. + data.ctime = base::Time::UnixEpoch() + base::TimeDelta::FromDays(1); + data.mtime = data.ctime + base::TimeDelta::FromSeconds(version); + data.non_unique_name = "Name Non Unique"; + + return data; +} + +CommitResponseData MockModelTypeSyncWorker::SuccessfulCommitResponse( + const CommitRequestData& request_data) { + const std::string& client_tag_hash = request_data.client_tag_hash; + + CommitResponseData response_data; + + if (request_data.base_version == 0) { + // Server assigns new ID to newly committed items. + DCHECK(request_data.id.empty()); + response_data.id = request_data.id; + } else { + // Otherwise we reuse the ID from the request. + response_data.id = GenerateId(client_tag_hash); + } + + response_data.client_tag_hash = client_tag_hash; + response_data.sequence_number = request_data.sequence_number; + + // Increment the server version on successful commit. + int64 version = GetServerVersion(client_tag_hash); + version++; + SetServerVersion(client_tag_hash, version); + + response_data.response_version = version; + + return response_data; +} + +std::string MockModelTypeSyncWorker::GenerateId(const std::string& tag_hash) { + return "FakeId:" + tag_hash; +} + +int64 MockModelTypeSyncWorker::GetServerVersion(const std::string& tag_hash) { + std::map::const_iterator it; + it = server_versions_.find(tag_hash); + if (it == server_versions_.end()) { + return 0; + } else { + return it->second; + } +} + +void MockModelTypeSyncWorker::SetServerVersion(const std::string& tag_hash, + int64 version) { + server_versions_[tag_hash] = version; +} + +} // namespace syncer diff --git a/sync/test/engine/mock_model_type_sync_worker.h b/sync/test/engine/mock_model_type_sync_worker.h new file mode 100644 index 0000000..deb675e9 --- /dev/null +++ b/sync/test/engine/mock_model_type_sync_worker.h @@ -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. + +#ifndef SYNC_TEST_ENGINE_MOCK_MODEL_TYPE_SYNC_WORKER_H_ +#define SYNC_TEST_ENGINE_MOCK_MODEL_TYPE_SYNC_WORKER_H_ + +#include + +#include "base/macros.h" +#include "sync/engine/model_type_sync_worker.h" +#include "sync/engine/non_blocking_sync_common.h" + +namespace syncer { + +// Receives and records commit requests sent through the ModelTypeSyncWorker. +// +// This class also includes features intended to help mock out server behavior. +// It has some basic functionality to keep track of server state and generate +// plausible UpdateResponseData and CommitResponseData messages. +class MockModelTypeSyncWorker : public ModelTypeSyncWorker { + public: + MockModelTypeSyncWorker(); + virtual ~MockModelTypeSyncWorker(); + + // Implementation of ModelTypeSyncWorker. + virtual void RequestCommits(const CommitRequestDataList& list) OVERRIDE; + + // Getters to inspect the requests sent to this object. + size_t GetNumCommitRequestLists() const; + CommitRequestDataList GetNthCommitRequestList(size_t n) const; + bool HasCommitRequestForTagHash(const std::string& tag_hash) const; + CommitRequestData GetLatestCommitRequestForTagHash( + const std::string& tag_hash) const; + + // Functions to produce state as though it came from a real server and had + // been filtered through a real ModelTypeSyncWorker. + + // Returns an UpdateResponseData representing an update received from + // the server. Updates server state accordingly. + // + // The |version_offset| field can be used to emulate stale data (ie. versions + // going backwards), reflections and redeliveries (ie. several instances of + // the same version) or new updates. + UpdateResponseData UpdateFromServer( + int64 version_offset, + const std::string& tag_hash, + const sync_pb::EntitySpecifics& specifics); + + // Returns an UpdateResponseData representing a tombstone update from the + // server. Updates server state accordingly. + UpdateResponseData TombstoneFromServer(int64 version_offset, + const std::string& tag_hash); + + // Returns a commit response that indicates a successful commit of the + // given |request_data|. Updates server state accordingly. + CommitResponseData SuccessfulCommitResponse( + const CommitRequestData& request_data); + + private: + // Generate an ID string. + static std::string GenerateId(const std::string& tag_hash); + + // Retrieve or set the server version. + int64 GetServerVersion(const std::string& tag_hash); + void SetServerVersion(const std::string& tag_hash, int64 version); + + // A record of past commits requests. + std::vector commit_request_lists_; + + // Map of versions by client tag. + // This is an essential part of the mocked server state. + std::map server_versions_; + + DISALLOW_COPY_AND_ASSIGN(MockModelTypeSyncWorker); +}; + +} // namespace syncer + +#endif // SYNC_TEST_ENGINE_MOCK_MODEL_TYPE_SYNC_WORKER_H_ diff --git a/sync/test/engine/mock_non_blocking_type_processor.cc b/sync/test/engine/mock_non_blocking_type_processor.cc deleted file mode 100644 index 0c3d8c9..0000000 --- a/sync/test/engine/mock_non_blocking_type_processor.cc +++ /dev/null @@ -1,258 +0,0 @@ -// 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/engine/mock_non_blocking_type_processor.h" - -#include "base/bind.h" - -namespace syncer { - -MockNonBlockingTypeProcessor::MockNonBlockingTypeProcessor() - : is_synchronous_(true) { -} - -MockNonBlockingTypeProcessor::~MockNonBlockingTypeProcessor() { -} - -void MockNonBlockingTypeProcessor::ReceiveCommitResponse( - const DataTypeState& type_state, - const CommitResponseDataList& response_list) { - base::Closure task = - base::Bind(&MockNonBlockingTypeProcessor::ReceiveCommitResponseImpl, - base::Unretained(this), - type_state, - response_list); - pending_tasks_.push_back(task); - if (is_synchronous_) - RunQueuedTasks(); -} - -void MockNonBlockingTypeProcessor::ReceiveUpdateResponse( - const DataTypeState& type_state, - const UpdateResponseDataList& response_list) { - base::Closure task = - base::Bind(&MockNonBlockingTypeProcessor::ReceiveUpdateResponseImpl, - base::Unretained(this), - type_state, - response_list); - pending_tasks_.push_back(task); - if (is_synchronous_) - RunQueuedTasks(); -} - -void MockNonBlockingTypeProcessor::SetSynchronousExecution( - bool is_synchronous) { - is_synchronous_ = is_synchronous; -} - -void MockNonBlockingTypeProcessor::RunQueuedTasks() { - for (std::vector::iterator it = pending_tasks_.begin(); - it != pending_tasks_.end(); - ++it) { - it->Run(); - } - pending_tasks_.clear(); -} - -CommitRequestData MockNonBlockingTypeProcessor::CommitRequest( - const std::string& tag_hash, - const sync_pb::EntitySpecifics& specifics) { - const int64 base_version = GetBaseVersion(tag_hash); - - CommitRequestData data; - - if (HasServerAssignedId(tag_hash)) { - data.id = GetServerAssignedId(tag_hash); - } - - data.client_tag_hash = tag_hash; - data.sequence_number = GetNextSequenceNumber(tag_hash); - data.deleted = false; - data.specifics = specifics; - data.base_version = base_version; - - // These fields are not really used for much, but we set them anyway - // to make this item look more realistic. - data.ctime = base::Time::UnixEpoch() + base::TimeDelta::FromDays(1); - data.mtime = data.ctime + base::TimeDelta::FromSeconds(base_version); - data.non_unique_name = "Name: " + tag_hash; - - return data; -} - -CommitRequestData MockNonBlockingTypeProcessor::DeleteRequest( - const std::string& tag_hash) { - const int64 base_version = GetBaseVersion(tag_hash); - CommitRequestData data; - - if (HasServerAssignedId(tag_hash)) { - data.id = GetServerAssignedId(tag_hash); - } - - data.client_tag_hash = tag_hash; - data.sequence_number = GetNextSequenceNumber(tag_hash); - data.base_version = base_version; - data.mtime = data.ctime + base::TimeDelta::FromSeconds(base_version); - data.deleted = true; - - // These fields have little or no effect on behavior. We set them anyway to - // make the test more realistic. - data.ctime = base::Time::UnixEpoch() + base::TimeDelta::FromDays(1); - data.non_unique_name = "Name deleted"; - - return data; -} - -size_t MockNonBlockingTypeProcessor::GetNumUpdateResponses() const { - return received_update_responses_.size(); -} - -UpdateResponseDataList MockNonBlockingTypeProcessor::GetNthUpdateResponse( - size_t n) const { - DCHECK_LT(n, GetNumUpdateResponses()); - return received_update_responses_[n]; -} - -DataTypeState -MockNonBlockingTypeProcessor::GetNthTypeStateReceivedInUpdateResponse( - size_t n) const { - DCHECK_LT(n, GetNumUpdateResponses()); - return type_states_received_on_update_[n]; -} - -size_t MockNonBlockingTypeProcessor::GetNumCommitResponses() const { - return received_commit_responses_.size(); -} - -CommitResponseDataList MockNonBlockingTypeProcessor::GetNthCommitResponse( - size_t n) const { - DCHECK_LT(n, GetNumCommitResponses()); - return received_commit_responses_[n]; -} - -DataTypeState -MockNonBlockingTypeProcessor::GetNthTypeStateReceivedInCommitResponse( - size_t n) const { - DCHECK_LT(n, GetNumCommitResponses()); - return type_states_received_on_commit_[n]; -} - -bool MockNonBlockingTypeProcessor::HasUpdateResponse( - const std::string& tag_hash) const { - std::map::const_iterator it = - update_response_items_.find(tag_hash); - return it != update_response_items_.end(); -} - -UpdateResponseData MockNonBlockingTypeProcessor::GetUpdateResponse( - const std::string& tag_hash) const { - DCHECK(HasUpdateResponse(tag_hash)); - std::map::const_iterator it = - update_response_items_.find(tag_hash); - return it->second; -} - -bool MockNonBlockingTypeProcessor::HasCommitResponse( - const std::string& tag_hash) const { - std::map::const_iterator it = - commit_response_items_.find(tag_hash); - return it != commit_response_items_.end(); -} - -CommitResponseData MockNonBlockingTypeProcessor::GetCommitResponse( - const std::string& tag_hash) const { - DCHECK(HasCommitResponse(tag_hash)); - std::map::const_iterator it = - commit_response_items_.find(tag_hash); - return it->second; -} - -void MockNonBlockingTypeProcessor::ReceiveCommitResponseImpl( - const DataTypeState& type_state, - const CommitResponseDataList& response_list) { - received_commit_responses_.push_back(response_list); - type_states_received_on_commit_.push_back(type_state); - for (CommitResponseDataList::const_iterator it = response_list.begin(); - it != response_list.end(); - ++it) { - commit_response_items_.insert(std::make_pair(it->client_tag_hash, *it)); - - // Server wins. Set the model's base version. - SetBaseVersion(it->client_tag_hash, it->response_version); - SetServerAssignedId(it->client_tag_hash, it->id); - } -} - -void MockNonBlockingTypeProcessor::ReceiveUpdateResponseImpl( - const DataTypeState& type_state, - const UpdateResponseDataList& response_list) { - received_update_responses_.push_back(response_list); - type_states_received_on_update_.push_back(type_state); - for (UpdateResponseDataList::const_iterator it = response_list.begin(); - it != response_list.end(); - ++it) { - update_response_items_.insert(std::make_pair(it->client_tag_hash, *it)); - - // Server wins. Set the model's base version. - SetBaseVersion(it->client_tag_hash, it->response_version); - SetServerAssignedId(it->client_tag_hash, it->id); - } -} - -// Fetches the sequence number as of the most recent update request. -int64 MockNonBlockingTypeProcessor::GetCurrentSequenceNumber( - const std::string& tag_hash) const { - std::map::const_iterator 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 MockNonBlockingTypeProcessor::GetNextSequenceNumber( - const std::string& tag_hash) { - int64 sequence_number = GetCurrentSequenceNumber(tag_hash); - sequence_number++; - sequence_numbers_[tag_hash] = sequence_number; - return sequence_number; -} - -int64 MockNonBlockingTypeProcessor::GetBaseVersion( - const std::string& tag_hash) const { - std::map::const_iterator it = - base_versions_.find(tag_hash); - if (it == base_versions_.end()) { - return kUncommittedVersion; - } else { - return it->second; - } -} - -void MockNonBlockingTypeProcessor::SetBaseVersion(const std::string& tag_hash, - int64 version) { - base_versions_[tag_hash] = version; -} - -bool MockNonBlockingTypeProcessor::HasServerAssignedId( - const std::string& tag_hash) const { - return assigned_ids_.find(tag_hash) != assigned_ids_.end(); -} - -const std::string& MockNonBlockingTypeProcessor::GetServerAssignedId( - const std::string& tag_hash) const { - DCHECK(HasServerAssignedId(tag_hash)); - return assigned_ids_.find(tag_hash)->second; -} - -void MockNonBlockingTypeProcessor::SetServerAssignedId( - const std::string& tag_hash, - const std::string& id) { - assigned_ids_[tag_hash] = id; -} - -} // namespace syncer diff --git a/sync/test/engine/mock_non_blocking_type_processor.h b/sync/test/engine/mock_non_blocking_type_processor.h deleted file mode 100644 index 30e7670..0000000 --- a/sync/test/engine/mock_non_blocking_type_processor.h +++ /dev/null @@ -1,136 +0,0 @@ -// 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_ENGINE_MOCK_NON_BLOCKING_TYPE_PROCESSOR_H_ -#define SYNC_TEST_ENGINE_MOCK_NON_BLOCKING_TYPE_PROCESSOR_H_ - -#include - -#include "base/callback.h" -#include "base/macros.h" -#include "sync/engine/non_blocking_sync_common.h" -#include "sync/engine/non_blocking_type_processor_interface.h" - -namespace syncer { - -// Mocks the NonBlockingTypeProcessor. -// -// This mock is made simpler by not using any threads. It does still have the -// ability to defer execution if we need to test race conditions, though. -// -// It maintains some state to try to make its behavior more realistic. It -// updates this state as it creates commit requests or receives update and -// commit responses. -// -// It keeps a log of all received messages so tests can make assertions based -// on their value. -class MockNonBlockingTypeProcessor : public NonBlockingTypeProcessorInterface { - public: - MockNonBlockingTypeProcessor(); - virtual ~MockNonBlockingTypeProcessor(); - - // Implementation of NonBlockingTypeProcessorInterface. - virtual void ReceiveCommitResponse( - const DataTypeState& type_state, - const CommitResponseDataList& response_list) OVERRIDE; - virtual void ReceiveUpdateResponse( - const DataTypeState& type_state, - const UpdateResponseDataList& response_list) OVERRIDE; - - // By default, this object behaves as if all messages are processed - // immediately. Sometimes it is useful to defer work until later, as might - // happen in the real world if the model thread's task queue gets backed up. - void SetSynchronousExecution(bool is_synchronous); - - // Runs any work that was deferred while this class was in asynchronous mode. - // - // This function is not useful unless this object is set to synchronous - // execution mode, which is the default. - void RunQueuedTasks(); - - // Generate commit or deletion requests to be sent to the server. - // These functions update local state to keep sequence numbers consistent. - // - // A real NonBlockingTypeProcessor would forward these kinds of messages - // directly to its attached NonBlockingTypeProcessorCore. These methods - // return the value to the caller so the test framework can handle them as it - // sees fit. - CommitRequestData CommitRequest(const std::string& tag_hash, - const sync_pb::EntitySpecifics& specifics); - CommitRequestData DeleteRequest(const std::string& tag_hash); - - // Getters to access the log of received update responses. - // - // Does not includes repsonses that are in pending tasks. - size_t GetNumUpdateResponses() const; - UpdateResponseDataList GetNthUpdateResponse(size_t n) const; - DataTypeState GetNthTypeStateReceivedInUpdateResponse(size_t n) const; - - // Getters to access the log of received commit responses. - // - // Does not includes repsonses that are in pending tasks. - size_t GetNumCommitResponses() const; - CommitResponseDataList GetNthCommitResponse(size_t n) const; - DataTypeState GetNthTypeStateReceivedInCommitResponse(size_t n) const; - - // Getters to access the lastest update response for a given tag_hash. - bool HasUpdateResponse(const std::string& tag_hash) const; - UpdateResponseData GetUpdateResponse(const std::string& tag_hash) const; - - // Getters to access the lastest commit response for a given tag_hash. - bool HasCommitResponse(const std::string& tag_hash) const; - CommitResponseData GetCommitResponse(const std::string& tag_hash) const; - - private: - // Process a received commit response. - // - // Implemented as an Impl method so we can defer its execution in some cases. - void ReceiveCommitResponseImpl(const DataTypeState& type_state, - const CommitResponseDataList& response_list); - - // Process a received update response. - // - // Implemented as an Impl method so we can defer its execution in some cases. - void ReceiveUpdateResponseImpl(const DataTypeState& type_state, - const UpdateResponseDataList& response_list); - - // Getter and setter for per-item sequence number tracking. - int64 GetCurrentSequenceNumber(const std::string& tag_hash) const; - int64 GetNextSequenceNumber(const std::string& tag_hash); - - // Getter and setter for per-item base version tracking. - int64 GetBaseVersion(const std::string& tag_hash) const; - void SetBaseVersion(const std::string& tag_hash, int64 version); - - // Getters and setter for server-assigned ID values. - bool HasServerAssignedId(const std::string& tag_hash) const; - const std::string& GetServerAssignedId(const std::string& tag_hash) const; - void SetServerAssignedId(const std::string& tag_hash, const std::string& id); - - // State related to the implementation of deferred work. - // See SetSynchronousExecution() for details. - bool is_synchronous_; - std::vector pending_tasks_; - - // A log of messages received by this object. - std::vector received_commit_responses_; - std::vector received_update_responses_; - std::vector type_states_received_on_update_; - std::vector type_states_received_on_commit_; - - // Latest responses received, indexed by tag_hash. - std::map commit_response_items_; - std::map update_response_items_; - - // The per-item state maps. - std::map sequence_numbers_; - std::map base_versions_; - std::map assigned_ids_; - - DISALLOW_COPY_AND_ASSIGN(MockNonBlockingTypeProcessor); -}; - -} // namespace syncer - -#endif // SYNC_TEST_ENGINE_MOCK_NON_BLOCKING_TYPE_PROCESSOR_H_ diff --git a/sync/test/engine/mock_non_blocking_type_processor_core.cc b/sync/test/engine/mock_non_blocking_type_processor_core.cc deleted file mode 100644 index b164c2a..0000000 --- a/sync/test/engine/mock_non_blocking_type_processor_core.cc +++ /dev/null @@ -1,175 +0,0 @@ -// 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/engine/mock_non_blocking_type_processor_core.h" - -#include "base/logging.h" - -namespace syncer { - -MockNonBlockingTypeProcessorCore::MockNonBlockingTypeProcessorCore() { -} - -MockNonBlockingTypeProcessorCore::~MockNonBlockingTypeProcessorCore() { -} - -void MockNonBlockingTypeProcessorCore::RequestCommits( - const CommitRequestDataList& list) { - commit_request_lists_.push_back(list); -} - -size_t MockNonBlockingTypeProcessorCore::GetNumCommitRequestLists() const { - return commit_request_lists_.size(); -} - -CommitRequestDataList MockNonBlockingTypeProcessorCore::GetNthCommitRequestList( - size_t n) const { - DCHECK_LT(n, GetNumCommitRequestLists()); - return commit_request_lists_[n]; -} - -bool MockNonBlockingTypeProcessorCore::HasCommitRequestForTagHash( - const std::string& tag_hash) const { - // Iterate backward through the sets of commit requests to find the most - // recent one that applies to the specified tag_hash. - for (std::vector::const_reverse_iterator lists_it = - commit_request_lists_.rbegin(); - lists_it != commit_request_lists_.rend(); - ++lists_it) { - for (CommitRequestDataList::const_iterator it = lists_it->begin(); - it != lists_it->end(); - ++it) { - if (it->client_tag_hash == tag_hash) { - return true; - } - } - } - - return false; -} - -CommitRequestData -MockNonBlockingTypeProcessorCore::GetLatestCommitRequestForTagHash( - const std::string& tag_hash) const { - // Iterate backward through the sets of commit requests to find the most - // recent one that applies to the specified tag_hash. - for (std::vector::const_reverse_iterator lists_it = - commit_request_lists_.rbegin(); - lists_it != commit_request_lists_.rend(); - ++lists_it) { - for (CommitRequestDataList::const_iterator it = lists_it->begin(); - it != lists_it->end(); - ++it) { - if (it->client_tag_hash == tag_hash) { - return *it; - } - } - } - - NOTREACHED() << "Could not find commit for tag hash " << tag_hash << "."; - return CommitRequestData(); -} - -UpdateResponseData MockNonBlockingTypeProcessorCore::UpdateFromServer( - int64 version_offset, - const std::string& tag_hash, - const sync_pb::EntitySpecifics& specifics) { - // Overwrite the existing server version if this is the new highest version. - int64 old_version = GetServerVersion(tag_hash); - int64 version = old_version + version_offset; - if (version > old_version) { - SetServerVersion(tag_hash, version); - } - - UpdateResponseData data; - data.id = GenerateId(tag_hash); - data.client_tag_hash = tag_hash; - data.response_version = version; - data.deleted = false; - data.specifics = specifics; - - // These elements should have no effect on behavior, but we set them anyway - // so we can test they are properly copied around the system if we want to. - data.ctime = base::Time::UnixEpoch() + base::TimeDelta::FromDays(1); - data.mtime = data.ctime + base::TimeDelta::FromSeconds(version); - data.non_unique_name = specifics.preference().name(); - - return data; -} - -UpdateResponseData MockNonBlockingTypeProcessorCore::TombstoneFromServer( - int64 version_offset, - const std::string& tag_hash) { - int64 old_version = GetServerVersion(tag_hash); - int64 version = old_version + version_offset; - if (version > old_version) { - SetServerVersion(tag_hash, version); - } - - UpdateResponseData data; - data.id = GenerateId(tag_hash); - data.client_tag_hash = tag_hash; - data.response_version = version; - data.deleted = true; - - // These elements should have no effect on behavior, but we set them anyway - // so we can test they are properly copied around the system if we want to. - data.ctime = base::Time::UnixEpoch() + base::TimeDelta::FromDays(1); - data.mtime = data.ctime + base::TimeDelta::FromSeconds(version); - data.non_unique_name = "Name Non Unique"; - - return data; -} - -CommitResponseData MockNonBlockingTypeProcessorCore::SuccessfulCommitResponse( - const CommitRequestData& request_data) { - const std::string& client_tag_hash = request_data.client_tag_hash; - - CommitResponseData response_data; - - if (request_data.base_version == 0) { - // Server assigns new ID to newly committed items. - DCHECK(request_data.id.empty()); - response_data.id = request_data.id; - } else { - // Otherwise we reuse the ID from the request. - response_data.id = GenerateId(client_tag_hash); - } - - response_data.client_tag_hash = client_tag_hash; - response_data.sequence_number = request_data.sequence_number; - - // Increment the server version on successful commit. - int64 version = GetServerVersion(client_tag_hash); - version++; - SetServerVersion(client_tag_hash, version); - - response_data.response_version = version; - - return response_data; -} - -std::string MockNonBlockingTypeProcessorCore::GenerateId( - const std::string& tag_hash) { - return "FakeId:" + tag_hash; -} - -int64 MockNonBlockingTypeProcessorCore::GetServerVersion( - const std::string& tag_hash) { - std::map::const_iterator it; - it = server_versions_.find(tag_hash); - if (it == server_versions_.end()) { - return 0; - } else { - return it->second; - } -} - -void MockNonBlockingTypeProcessorCore::SetServerVersion( - const std::string& tag_hash, - int64 version) { - server_versions_[tag_hash] = version; -} - -} // namespace syncer diff --git a/sync/test/engine/mock_non_blocking_type_processor_core.h b/sync/test/engine/mock_non_blocking_type_processor_core.h deleted file mode 100644 index 2ee73a4..0000000 --- a/sync/test/engine/mock_non_blocking_type_processor_core.h +++ /dev/null @@ -1,82 +0,0 @@ -// 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_ENGINE_MOCK_NON_BLOCKING_TYPE_PROCESSOR_CORE_H_ -#define SYNC_TEST_ENGINE_MOCK_NON_BLOCKING_TYPE_PROCESSOR_CORE_H_ - -#include - -#include "base/macros.h" -#include "sync/engine/non_blocking_sync_common.h" -#include "sync/engine/non_blocking_type_processor_core_interface.h" - -namespace syncer { - -// Receives and records commit requests sent through the -// NonBlockingTypeProcessorCoreInterface. -// -// This class also includes features intended to help mock out server behavior. -// It has some basic functionality to keep track of server state and generate -// plausible UpdateResponseData and CommitResponseData messages. -class MockNonBlockingTypeProcessorCore - : public NonBlockingTypeProcessorCoreInterface { - public: - MockNonBlockingTypeProcessorCore(); - virtual ~MockNonBlockingTypeProcessorCore(); - - // Implementation of NonBlockingTypeProcessorCoreInterface. - virtual void RequestCommits(const CommitRequestDataList& list) OVERRIDE; - - // Getters to inspect the requests sent to this object. - size_t GetNumCommitRequestLists() const; - CommitRequestDataList GetNthCommitRequestList(size_t n) const; - bool HasCommitRequestForTagHash(const std::string& tag_hash) const; - CommitRequestData GetLatestCommitRequestForTagHash( - const std::string& tag_hash) const; - - // Functions to produce state as though it came from a real server and had - // been filtered through a real NonBlockinTypeProcessorCore. - - // Returns an UpdateResponseData representing an update received from - // the server. Updates server state accordingly. - // - // The |version_offset| field can be used to emulate stale data (ie. versions - // going backwards), reflections and redeliveries (ie. several instances of - // the same version) or new updates. - UpdateResponseData UpdateFromServer( - int64 version_offset, - const std::string& tag_hash, - const sync_pb::EntitySpecifics& specifics); - - // Returns an UpdateResponseData representing a tombstone update from the - // server. Updates server state accordingly. - UpdateResponseData TombstoneFromServer(int64 version_offset, - const std::string& tag_hash); - - // Returns a commit response that indicates a successful commit of the - // given |request_data|. Updates server state accordingly. - CommitResponseData SuccessfulCommitResponse( - const CommitRequestData& request_data); - - private: - // Generate an ID string. - static std::string GenerateId(const std::string& tag_hash); - - // Retrieve or set the server version. - int64 GetServerVersion(const std::string& tag_hash); - void SetServerVersion(const std::string& tag_hash, int64 version); - - // A record of past commits requests. - std::vector commit_request_lists_; - - // Map of versions by client tag. - // This is an essential part of the mocked server state. - std::map server_versions_; - - DISALLOW_COPY_AND_ASSIGN(MockNonBlockingTypeProcessorCore); -}; - -} // namespace syncer - -#endif // SYNC_TEST_ENGINE_MOCK_NON_BLOCKING_TYPE_PROCESSOR_CORE_H_ -- cgit v1.1