| Index: sync/internal_api/shared_model_type_processor_unittest.cc
|
| diff --git a/sync/internal_api/shared_model_type_processor_unittest.cc b/sync/internal_api/shared_model_type_processor_unittest.cc
|
| deleted file mode 100644
|
| index cda505a1b206606a115241fbcb8e6eec3c895151..0000000000000000000000000000000000000000
|
| --- a/sync/internal_api/shared_model_type_processor_unittest.cc
|
| +++ /dev/null
|
| @@ -1,1514 +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/shared_model_type_processor.h"
|
| -
|
| -#include <stddef.h>
|
| -#include <stdint.h>
|
| -
|
| -#include <map>
|
| -#include <vector>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/callback.h"
|
| -#include "base/memory/ptr_util.h"
|
| -#include "base/message_loop/message_loop.h"
|
| -#include "base/run_loop.h"
|
| -#include "sync/api/fake_model_type_service.h"
|
| -#include "sync/engine/commit_queue.h"
|
| -#include "sync/internal_api/public/activation_context.h"
|
| -#include "sync/internal_api/public/base/model_type.h"
|
| -#include "sync/internal_api/public/data_batch_impl.h"
|
| -#include "sync/internal_api/public/non_blocking_sync_common.h"
|
| -#include "sync/internal_api/public/simple_metadata_change_list.h"
|
| -#include "sync/internal_api/public/test/data_type_error_handler_mock.h"
|
| -#include "sync/protocol/data_type_state.pb.h"
|
| -#include "sync/protocol/sync.pb.h"
|
| -#include "sync/syncable/syncable_util.h"
|
| -#include "sync/test/engine/mock_model_type_worker.h"
|
| -#include "sync/util/time.h"
|
| -#include "testing/gtest/include/gtest/gtest.h"
|
| -
|
| -namespace syncer_v2 {
|
| -
|
| -namespace {
|
| -
|
| -const std::string kTag1 = "tag1";
|
| -const std::string kTag2 = "tag2";
|
| -const std::string kTag3 = "tag3";
|
| -const std::string kValue1 = "value1";
|
| -const std::string kValue2 = "value2";
|
| -const std::string kValue3 = "value3";
|
| -
|
| -std::string GenerateTagHash(const std::string& tag) {
|
| - return syncer::syncable::GenerateSyncableHash(syncer::PREFERENCES, tag);
|
| -}
|
| -
|
| -sync_pb::EntitySpecifics 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;
|
| -}
|
| -
|
| -std::unique_ptr<EntityData> GenerateEntityData(const std::string& tag,
|
| - const std::string& value) {
|
| - std::unique_ptr<EntityData> entity_data = base::WrapUnique(new EntityData());
|
| - entity_data->client_tag_hash = GenerateTagHash(tag);
|
| - entity_data->specifics = GenerateSpecifics(tag, value);
|
| - entity_data->non_unique_name = tag;
|
| - return entity_data;
|
| -}
|
| -
|
| -// It is intentionally very difficult to copy an EntityData, as in normal code
|
| -// we never want to. However, since we store the data as an EntityData for the
|
| -// test code here, this function is needed to manually copy it.
|
| -std::unique_ptr<EntityData> CopyEntityData(const EntityData& old_data) {
|
| - std::unique_ptr<EntityData> new_data(new EntityData());
|
| - new_data->id = old_data.id;
|
| - new_data->client_tag_hash = old_data.client_tag_hash;
|
| - new_data->non_unique_name = old_data.non_unique_name;
|
| - new_data->specifics = old_data.specifics;
|
| - new_data->creation_time = old_data.creation_time;
|
| - new_data->modification_time = old_data.modification_time;
|
| - return new_data;
|
| -}
|
| -
|
| -syncer::SyncError CreateSyncError(syncer::SyncError::ErrorType error_type) {
|
| - return syncer::SyncError(FROM_HERE, error_type, "TestError",
|
| - syncer::PREFERENCES);
|
| -}
|
| -
|
| -// A basic in-memory storage mechanism for data and metadata. This makes it
|
| -// easier to test more complex behaviors involving when entities are written,
|
| -// committed, etc. Having a separate class helps keep the main one cleaner.
|
| -class SimpleStore {
|
| - public:
|
| - void PutData(const std::string& tag, const EntityData& data) {
|
| - data_change_count_++;
|
| - data_store_[tag] = CopyEntityData(data);
|
| - }
|
| -
|
| - void PutMetadata(const std::string& tag,
|
| - const sync_pb::EntityMetadata& metadata) {
|
| - metadata_change_count_++;
|
| - metadata_store_[tag] = metadata;
|
| - }
|
| -
|
| - void RemoveData(const std::string& tag) {
|
| - data_change_count_++;
|
| - data_store_.erase(tag);
|
| - }
|
| -
|
| - void RemoveMetadata(const std::string& tag) {
|
| - metadata_change_count_++;
|
| - metadata_store_.erase(tag);
|
| - }
|
| -
|
| - bool HasData(const std::string& tag) const {
|
| - return data_store_.find(tag) != data_store_.end();
|
| - }
|
| -
|
| - bool HasMetadata(const std::string& tag) const {
|
| - return metadata_store_.find(tag) != metadata_store_.end();
|
| - }
|
| -
|
| - const std::map<std::string, std::unique_ptr<EntityData>>& GetAllData() const {
|
| - return data_store_;
|
| - }
|
| -
|
| - const EntityData& GetData(const std::string& tag) const {
|
| - return *data_store_.find(tag)->second;
|
| - }
|
| -
|
| - const std::string& GetValue(const std::string& tag) const {
|
| - return GetData(tag).specifics.preference().value();
|
| - }
|
| -
|
| - const sync_pb::EntityMetadata& GetMetadata(const std::string& tag) const {
|
| - return metadata_store_.find(tag)->second;
|
| - }
|
| -
|
| - size_t DataCount() const { return data_store_.size(); }
|
| - size_t MetadataCount() const { return metadata_store_.size(); }
|
| -
|
| - size_t DataChangeCount() const { return data_change_count_; }
|
| - size_t MetadataChangeCount() const { return metadata_change_count_; }
|
| -
|
| - const sync_pb::DataTypeState& data_type_state() const {
|
| - return data_type_state_;
|
| - }
|
| -
|
| - void set_data_type_state(const sync_pb::DataTypeState& data_type_state) {
|
| - data_type_state_ = data_type_state;
|
| - }
|
| -
|
| - std::unique_ptr<MetadataBatch> CreateMetadataBatch() const {
|
| - std::unique_ptr<MetadataBatch> metadata_batch(new MetadataBatch());
|
| - metadata_batch->SetDataTypeState(data_type_state_);
|
| - for (auto it = metadata_store_.begin(); it != metadata_store_.end(); it++) {
|
| - metadata_batch->AddMetadata(it->first, it->second);
|
| - }
|
| - return metadata_batch;
|
| - }
|
| -
|
| - void Reset() {
|
| - data_change_count_ = 0;
|
| - metadata_change_count_ = 0;
|
| - data_store_.clear();
|
| - metadata_store_.clear();
|
| - data_type_state_.Clear();
|
| - }
|
| -
|
| - private:
|
| - size_t data_change_count_ = 0;
|
| - size_t metadata_change_count_ = 0;
|
| - std::map<std::string, std::unique_ptr<EntityData>> data_store_;
|
| - std::map<std::string, sync_pb::EntityMetadata> metadata_store_;
|
| - sync_pb::DataTypeState data_type_state_;
|
| -};
|
| -
|
| -} // namespace
|
| -
|
| -// Tests the various functionality of SharedModelTypeProcessor.
|
| -//
|
| -// The processor sits between the service (implemented by this test class) and
|
| -// the worker, which is represented by a MockModelTypeWorker. This test suite
|
| -// exercises the initialization flows (whether initial sync is done, performing
|
| -// the initial merge, etc) as well as normal functionality:
|
| -//
|
| -// - Initialization before the initial sync and merge correctly performs a merge
|
| -// and initializes the metadata in storage.
|
| -// - Initialization after the initial sync correctly loads metadata and queues
|
| -// any pending commits.
|
| -// - Put and Delete calls from the service result in the correct metadata in
|
| -// storage and the correct commit requests on the worker side.
|
| -// - Updates and commit responses from the worker correctly affect data and
|
| -// metadata in storage on the service side.
|
| -class SharedModelTypeProcessorTest : public ::testing::Test,
|
| - public FakeModelTypeService {
|
| - public:
|
| - SharedModelTypeProcessorTest()
|
| - : FakeModelTypeService(
|
| - base::Bind(&SharedModelTypeProcessor::CreateAsChangeProcessor)) {}
|
| -
|
| - ~SharedModelTypeProcessorTest() override { CheckPostConditions(); }
|
| -
|
| - void InitializeToMetadataLoaded() {
|
| - CreateChangeProcessor();
|
| - sync_pb::DataTypeState data_type_state(db_.data_type_state());
|
| - data_type_state.set_initial_sync_done(true);
|
| - db_.set_data_type_state(data_type_state);
|
| - OnMetadataLoaded();
|
| - }
|
| -
|
| - // Initialize to a "ready-to-commit" state.
|
| - void InitializeToReadyState() {
|
| - InitializeToMetadataLoaded();
|
| - if (!data_callback_.is_null()) {
|
| - OnPendingCommitDataLoaded();
|
| - }
|
| - OnSyncStarting();
|
| - }
|
| -
|
| - void OnMetadataLoaded() {
|
| - type_processor()->OnMetadataLoaded(syncer::SyncError(),
|
| - db_.CreateMetadataBatch());
|
| - }
|
| -
|
| - void OnPendingCommitDataLoaded() {
|
| - DCHECK(!data_callback_.is_null());
|
| - data_callback_.Run();
|
| - data_callback_.Reset();
|
| - }
|
| -
|
| - void OnSyncStarting() {
|
| - type_processor()->OnSyncStarting(
|
| - &error_handler_,
|
| - base::Bind(&SharedModelTypeProcessorTest::OnReadyToConnect,
|
| - base::Unretained(this)));
|
| - }
|
| -
|
| - void DisconnectSync() {
|
| - type_processor()->DisconnectSync();
|
| - worker_ = nullptr;
|
| - }
|
| -
|
| - // Local data modification. Emulates signals from the model thread.
|
| - void WriteItem(const std::string& tag, const std::string& value) {
|
| - WriteItem(tag, GenerateEntityData(tag, value));
|
| - }
|
| -
|
| - // Overloaded form to allow passing of custom entity data.
|
| - void WriteItem(const std::string& tag,
|
| - std::unique_ptr<EntityData> entity_data) {
|
| - db_.PutData(tag, *entity_data);
|
| - if (type_processor()) {
|
| - std::unique_ptr<MetadataChangeList> change_list(
|
| - new SimpleMetadataChangeList());
|
| - type_processor()->Put(tag, std::move(entity_data), change_list.get());
|
| - ApplyMetadataChangeList(std::move(change_list));
|
| - }
|
| - }
|
| -
|
| - // Writes data for |tag| and simulates a commit response for it.
|
| - void WriteItemAndAck(const std::string& tag, const std::string& value) {
|
| - WriteItem(tag, value);
|
| - worker()->ExpectPendingCommits({tag});
|
| - worker()->AckOnePendingCommit();
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| - }
|
| -
|
| - void DeleteItem(const std::string& tag) {
|
| - db_.RemoveData(tag);
|
| - if (type_processor()) {
|
| - std::unique_ptr<MetadataChangeList> change_list(
|
| - new SimpleMetadataChangeList());
|
| - type_processor()->Delete(tag, change_list.get());
|
| - ApplyMetadataChangeList(std::move(change_list));
|
| - }
|
| - }
|
| -
|
| - void ResetState() {
|
| - clear_change_processor();
|
| - db_.Reset();
|
| - worker_ = nullptr;
|
| - CheckPostConditions();
|
| - }
|
| -
|
| - // Wipes existing DB and simulates a pending update of a server-known item.
|
| - void ResetStateWriteItem(const std::string& tag, const std::string& value) {
|
| - ResetState();
|
| - InitializeToReadyState();
|
| - EXPECT_EQ(0U, ProcessorEntityCount());
|
| - WriteItemAndAck(tag, "acked-value");
|
| - WriteItem(tag, value);
|
| - EXPECT_EQ(1U, ProcessorEntityCount());
|
| - clear_change_processor();
|
| - worker_ = nullptr;
|
| - }
|
| -
|
| - // Wipes existing DB and simulates a pending deletion of a server-known item.
|
| - void ResetStateDeleteItem(const std::string& tag, const std::string& value) {
|
| - ResetState();
|
| - InitializeToReadyState();
|
| - EXPECT_EQ(0U, ProcessorEntityCount());
|
| - WriteItemAndAck(tag, value);
|
| - EXPECT_EQ(1U, ProcessorEntityCount());
|
| - DeleteItem(tag);
|
| - EXPECT_EQ(1U, ProcessorEntityCount());
|
| - clear_change_processor();
|
| - worker_ = nullptr;
|
| - }
|
| -
|
| - // Simulates an initial GetUpdates response from the worker with |updates|.
|
| - void OnInitialSyncDone(UpdateResponseDataList updates) {
|
| - sync_pb::DataTypeState data_type_state(db_.data_type_state());
|
| - data_type_state.set_initial_sync_done(true);
|
| - type_processor()->OnUpdateReceived(data_type_state, updates);
|
| - }
|
| -
|
| - // Overloaded form with no updates.
|
| - void OnInitialSyncDone() { OnInitialSyncDone(UpdateResponseDataList()); }
|
| -
|
| - // Overloaded form that constructs an update for a single entity.
|
| - void OnInitialSyncDone(const std::string& tag, const std::string& value) {
|
| - UpdateResponseDataList updates;
|
| - UpdateResponseData update;
|
| - update.entity = GenerateEntityData(tag, value)->PassToPtr();
|
| - updates.push_back(update);
|
| - OnInitialSyncDone(updates);
|
| - }
|
| -
|
| - // Return the number of entities the processor has metadata for.
|
| - size_t ProcessorEntityCount() const {
|
| - DCHECK(type_processor());
|
| - return type_processor()->entities_.size();
|
| - }
|
| -
|
| - // Store a resolution for the next call to ResolveConflict. Note that if this
|
| - // is a USE_NEW resolution, the data will only exist for one resolve call.
|
| - void SetConflictResolution(ConflictResolution resolution) {
|
| - conflict_resolution_.reset(new ConflictResolution(std::move(resolution)));
|
| - }
|
| -
|
| - // Sets the error that the next fallible call to the service will generate.
|
| - void SetServiceError(syncer::SyncError::ErrorType error_type) {
|
| - DCHECK(!service_error_.IsSet());
|
| - service_error_ = CreateSyncError(error_type);
|
| - }
|
| -
|
| - // Sets the error type that OnReadyToConnect (our StartCallback) expects to
|
| - // receive.
|
| - void ExpectStartError(syncer::SyncError::ErrorType error_type) {
|
| - DCHECK(expected_start_error_ == syncer::SyncError::UNSET);
|
| - expected_start_error_ = error_type;
|
| - }
|
| -
|
| - const SimpleStore& db() const { return db_; }
|
| -
|
| - MockModelTypeWorker* worker() { return worker_; }
|
| -
|
| - SharedModelTypeProcessor* type_processor() const {
|
| - return static_cast<SharedModelTypeProcessor*>(change_processor());
|
| - }
|
| -
|
| - syncer::DataTypeErrorHandlerMock* error_handler() { return &error_handler_; }
|
| -
|
| - private:
|
| - void CheckPostConditions() {
|
| - DCHECK(data_callback_.is_null());
|
| - DCHECK(!service_error_.IsSet());
|
| - DCHECK_EQ(syncer::SyncError::UNSET, expected_start_error_);
|
| - }
|
| -
|
| - void OnReadyToConnect(syncer::SyncError error,
|
| - std::unique_ptr<ActivationContext> context) {
|
| - if (expected_start_error_ != syncer::SyncError::UNSET) {
|
| - EXPECT_TRUE(error.IsSet());
|
| - EXPECT_EQ(expected_start_error_, error.error_type());
|
| - EXPECT_EQ(nullptr, context);
|
| - expected_start_error_ = syncer::SyncError::UNSET;
|
| - return;
|
| - }
|
| -
|
| - std::unique_ptr<MockModelTypeWorker> worker(
|
| - new MockModelTypeWorker(context->data_type_state, type_processor()));
|
| - // Keep an unsafe pointer to the commit queue the processor will use.
|
| - worker_ = worker.get();
|
| - // The context contains a proxy to the processor, but this call is
|
| - // side-stepping that completely and connecting directly to the real
|
| - // processor, since these tests are single-threaded and don't need proxies.
|
| - type_processor()->ConnectSync(std::move(worker));
|
| - }
|
| -
|
| - // FakeModelTypeService overrides.
|
| -
|
| - std::string GetClientTag(const EntityData& entity_data) override {
|
| - // The tag is the preference name - see GenerateSpecifics.
|
| - return entity_data.specifics.preference().name();
|
| - }
|
| -
|
| - std::unique_ptr<MetadataChangeList> CreateMetadataChangeList() override {
|
| - return std::unique_ptr<MetadataChangeList>(new SimpleMetadataChangeList());
|
| - }
|
| -
|
| - syncer::SyncError MergeSyncData(
|
| - std::unique_ptr<MetadataChangeList> metadata_changes,
|
| - EntityDataMap data_map) override {
|
| - if (service_error_.IsSet()) {
|
| - syncer::SyncError error = service_error_;
|
| - service_error_ = syncer::SyncError();
|
| - return error;
|
| - }
|
| - // Commit any local entities that aren't being overwritten by the server.
|
| - const auto& local_data = db_.GetAllData();
|
| - for (auto it = local_data.begin(); it != local_data.end(); it++) {
|
| - if (data_map.find(it->first) == data_map.end()) {
|
| - type_processor()->Put(it->first, CopyEntityData(*it->second),
|
| - metadata_changes.get());
|
| - }
|
| - }
|
| - // Store any new remote entities.
|
| - for (auto it = data_map.begin(); it != data_map.end(); it++) {
|
| - db_.PutData(it->first, it->second.value());
|
| - }
|
| - ApplyMetadataChangeList(std::move(metadata_changes));
|
| - return syncer::SyncError();
|
| - }
|
| -
|
| - syncer::SyncError ApplySyncChanges(
|
| - std::unique_ptr<MetadataChangeList> metadata_changes,
|
| - EntityChangeList entity_changes) override {
|
| - if (service_error_.IsSet()) {
|
| - syncer::SyncError error = service_error_;
|
| - service_error_ = syncer::SyncError();
|
| - return error;
|
| - }
|
| - for (const EntityChange& change : entity_changes) {
|
| - switch (change.type()) {
|
| - case EntityChange::ACTION_ADD:
|
| - EXPECT_FALSE(db_.HasData(change.client_tag()));
|
| - db_.PutData(change.client_tag(), change.data());
|
| - break;
|
| - case EntityChange::ACTION_UPDATE:
|
| - EXPECT_TRUE(db_.HasData(change.client_tag()));
|
| - db_.PutData(change.client_tag(), change.data());
|
| - break;
|
| - case EntityChange::ACTION_DELETE:
|
| - EXPECT_TRUE(db_.HasData(change.client_tag()));
|
| - db_.RemoveData(change.client_tag());
|
| - break;
|
| - }
|
| - }
|
| - ApplyMetadataChangeList(std::move(metadata_changes));
|
| - return syncer::SyncError();
|
| - }
|
| -
|
| - void ApplyMetadataChangeList(
|
| - std::unique_ptr<MetadataChangeList> change_list) {
|
| - DCHECK(change_list);
|
| - SimpleMetadataChangeList* changes =
|
| - static_cast<SimpleMetadataChangeList*>(change_list.get());
|
| - const auto& metadata_changes = changes->GetMetadataChanges();
|
| - for (auto it = metadata_changes.begin(); it != metadata_changes.end();
|
| - it++) {
|
| - switch (it->second.type) {
|
| - case SimpleMetadataChangeList::UPDATE:
|
| - db_.PutMetadata(it->first, it->second.metadata);
|
| - break;
|
| - case SimpleMetadataChangeList::CLEAR:
|
| - EXPECT_TRUE(db_.HasMetadata(it->first));
|
| - db_.RemoveMetadata(it->first);
|
| - break;
|
| - }
|
| - }
|
| - if (changes->HasDataTypeStateChange()) {
|
| - const SimpleMetadataChangeList::DataTypeStateChange& state_change =
|
| - changes->GetDataTypeStateChange();
|
| - switch (state_change.type) {
|
| - case SimpleMetadataChangeList::UPDATE:
|
| - db_.set_data_type_state(state_change.state);
|
| - break;
|
| - case SimpleMetadataChangeList::CLEAR:
|
| - db_.set_data_type_state(sync_pb::DataTypeState());
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - void GetData(ClientTagList tags, DataCallback callback) override {
|
| - if (service_error_.IsSet()) {
|
| - data_callback_ = base::Bind(callback, service_error_, nullptr);
|
| - service_error_ = syncer::SyncError();
|
| - return;
|
| - }
|
| - std::unique_ptr<DataBatchImpl> batch(new DataBatchImpl());
|
| - for (const std::string& tag : tags) {
|
| - DCHECK(db_.HasData(tag)) << "No data for " << tag;
|
| - batch->Put(tag, CopyEntityData(db_.GetData(tag)));
|
| - }
|
| - data_callback_ =
|
| - base::Bind(callback, syncer::SyncError(), base::Passed(&batch));
|
| - }
|
| -
|
| - ConflictResolution ResolveConflict(
|
| - const EntityData& local_data,
|
| - const EntityData& remote_data) const override {
|
| - DCHECK(conflict_resolution_);
|
| - return std::move(*conflict_resolution_);
|
| - }
|
| -
|
| - std::unique_ptr<ConflictResolution> conflict_resolution_;
|
| -
|
| - // This sets ThreadTaskRunnerHandle on the current thread, which the type
|
| - // processor will pick up as the sync task runner.
|
| - base::MessageLoop sync_loop_;
|
| -
|
| - // The current mock queue, which is owned by |type_processor()|.
|
| - MockModelTypeWorker* worker_;
|
| -
|
| - // Stores the data callback between GetData() and OnPendingCommitDataLoaded().
|
| - base::Closure data_callback_;
|
| -
|
| - // Contains all of the data and metadata state for these tests.
|
| - SimpleStore db_;
|
| -
|
| - // The processor's error handler.
|
| - syncer::DataTypeErrorHandlerMock error_handler_;
|
| -
|
| - // The error to produce on the next service call.
|
| - syncer::SyncError service_error_;
|
| -
|
| - // The error to expect in OnReadyToConnect().
|
| - syncer::SyncError::ErrorType expected_start_error_ = syncer::SyncError::UNSET;
|
| -};
|
| -
|
| -// Test that an initial sync handles local and remote items properly.
|
| -TEST_F(SharedModelTypeProcessorTest, InitialSync) {
|
| - CreateChangeProcessor();
|
| - OnMetadataLoaded();
|
| - OnSyncStarting();
|
| -
|
| - // Local write before initial sync.
|
| - WriteItem(kTag1, kValue1);
|
| -
|
| - // Has data, but no metadata, entity in the processor, or commit request.
|
| - EXPECT_EQ(1U, db().DataCount());
|
| - EXPECT_EQ(0U, db().MetadataCount());
|
| - EXPECT_EQ(0U, ProcessorEntityCount());
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -
|
| - // Initial sync with one server item.
|
| - OnInitialSyncDone(kTag2, kValue2);
|
| -
|
| - // Now have data and metadata for both items, as well as a commit request for
|
| - // the local item.
|
| - EXPECT_EQ(2U, db().DataCount());
|
| - EXPECT_EQ(2U, db().MetadataCount());
|
| - EXPECT_EQ(2U, ProcessorEntityCount());
|
| - EXPECT_EQ(1, db().GetMetadata(kTag1).sequence_number());
|
| - EXPECT_EQ(0, db().GetMetadata(kTag2).sequence_number());
|
| - worker()->ExpectPendingCommits({kTag1});
|
| -}
|
| -
|
| -// Test that an error during the merge is propagated to the error handler.
|
| -TEST_F(SharedModelTypeProcessorTest, InitialSyncError) {
|
| - CreateChangeProcessor();
|
| - OnMetadataLoaded();
|
| - OnSyncStarting();
|
| -
|
| - SetServiceError(syncer::SyncError::DATATYPE_ERROR);
|
| - error_handler()->ExpectError(syncer::SyncError::DATATYPE_ERROR);
|
| - OnInitialSyncDone();
|
| -}
|
| -
|
| -// Test that errors before it's called are passed to |start_callback| correctly.
|
| -TEST_F(SharedModelTypeProcessorTest, StartErrors) {
|
| - CreateChangeProcessor();
|
| - type_processor()->OnMetadataLoaded(
|
| - CreateSyncError(syncer::SyncError::DATATYPE_ERROR), nullptr);
|
| - ExpectStartError(syncer::SyncError::DATATYPE_ERROR);
|
| - OnSyncStarting();
|
| -
|
| - // Test OnSyncStarting happening first.
|
| - ResetState();
|
| - CreateChangeProcessor();
|
| - OnSyncStarting();
|
| - ExpectStartError(syncer::SyncError::DATATYPE_ERROR);
|
| - type_processor()->OnMetadataLoaded(
|
| - CreateSyncError(syncer::SyncError::DATATYPE_ERROR), nullptr);
|
| -
|
| - // Test an error loading pending data.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - SetServiceError(syncer::SyncError::DATATYPE_ERROR);
|
| - InitializeToMetadataLoaded();
|
| - OnPendingCommitDataLoaded();
|
| - ExpectStartError(syncer::SyncError::DATATYPE_ERROR);
|
| - OnSyncStarting();
|
| -}
|
| -
|
| -// This test covers race conditions during loading pending data. All cases
|
| -// start with no processor and one acked (committed to the server) item with a
|
| -// pending commit. There are three different events that can occur in any order
|
| -// once metadata is loaded:
|
| -//
|
| -// - Pending commit data is loaded.
|
| -// - Sync gets connected.
|
| -// - Optionally, a put or delete happens to the item.
|
| -//
|
| -// This results in 2 + 12 = 14 orderings of the events.
|
| -TEST_F(SharedModelTypeProcessorTest, LoadPendingCommit) {
|
| - // Data, connect.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnPendingCommitDataLoaded();
|
| - OnSyncStarting();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
|
| -
|
| - // Connect, data.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnSyncStarting();
|
| - EXPECT_EQ(nullptr, worker());
|
| - OnPendingCommitDataLoaded();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
|
| -
|
| - // Data, connect, put.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnPendingCommitDataLoaded();
|
| - OnSyncStarting();
|
| - WriteItem(kTag1, kValue2);
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
|
| - worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
|
| -
|
| - // Data, put, connect.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnPendingCommitDataLoaded();
|
| - WriteItem(kTag1, kValue2);
|
| - OnSyncStarting();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
|
| -
|
| - // Connect, data, put.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnSyncStarting();
|
| - OnPendingCommitDataLoaded();
|
| - WriteItem(kTag1, kValue2);
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
|
| - worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
|
| -
|
| - // Connect, put, data.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnSyncStarting();
|
| - WriteItem(kTag1, kValue2);
|
| - EXPECT_EQ(nullptr, worker());
|
| - OnPendingCommitDataLoaded();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
|
| -
|
| - // Put, data, connect.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - WriteItem(kTag1, kValue2);
|
| - OnPendingCommitDataLoaded();
|
| - OnSyncStarting();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
|
| -
|
| - // Put, connect, data.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - WriteItem(kTag1, kValue2);
|
| - OnSyncStarting();
|
| - EXPECT_EQ(nullptr, worker());
|
| - OnPendingCommitDataLoaded();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
|
| -
|
| - // Data, connect, delete.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnPendingCommitDataLoaded();
|
| - OnSyncStarting();
|
| - DeleteItem(kTag1);
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
|
| - worker()->ExpectNthPendingCommit(1, kTag1, "");
|
| -
|
| - // Data, delete, connect.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnPendingCommitDataLoaded();
|
| - DeleteItem(kTag1);
|
| - OnSyncStarting();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, "");
|
| -
|
| - // Connect, data, delete.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnSyncStarting();
|
| - OnPendingCommitDataLoaded();
|
| - DeleteItem(kTag1);
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
|
| - worker()->ExpectNthPendingCommit(1, kTag1, "");
|
| -
|
| - // Connect, delete, data.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnSyncStarting();
|
| - DeleteItem(kTag1);
|
| - EXPECT_EQ(nullptr, worker());
|
| - OnPendingCommitDataLoaded();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, "");
|
| -
|
| - // Delete, data, connect.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - DeleteItem(kTag1);
|
| - OnPendingCommitDataLoaded();
|
| - OnSyncStarting();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, "");
|
| -
|
| - // Delete, connect, data.
|
| - ResetStateWriteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - DeleteItem(kTag1);
|
| - OnSyncStarting();
|
| - EXPECT_EQ(nullptr, worker());
|
| - OnPendingCommitDataLoaded();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, "");
|
| -}
|
| -
|
| -// This test covers race conditions during loading a pending delete. All cases
|
| -// start with no processor and one item with a pending delete. There are two
|
| -// different events that can occur in any order once metadata is loaded, since
|
| -// for a deletion there is no data to load:
|
| -//
|
| -// - Sync gets connected.
|
| -// - Optionally, a put or delete happens to the item (repeated deletes should be
|
| -// handled properly).
|
| -//
|
| -// This results in 1 + 4 = 5 orderings of the events.
|
| -TEST_F(SharedModelTypeProcessorTest, LoadPendingDelete) {
|
| - // Connect.
|
| - ResetStateDeleteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnSyncStarting();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, "");
|
| -
|
| - // Connect, put.
|
| - ResetStateDeleteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnSyncStarting();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - WriteItem(kTag1, kValue2);
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, "");
|
| - worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
|
| -
|
| - // Put, connect.
|
| - ResetStateDeleteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - WriteItem(kTag1, kValue2);
|
| - OnSyncStarting();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
|
| -
|
| - // Connect, delete.
|
| - ResetStateDeleteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - OnSyncStarting();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - DeleteItem(kTag1);
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, "");
|
| - worker()->ExpectNthPendingCommit(1, kTag1, "");
|
| -
|
| - // Delete, connect.
|
| - ResetStateDeleteItem(kTag1, kValue1);
|
| - InitializeToMetadataLoaded();
|
| - DeleteItem(kTag1);
|
| - OnSyncStarting();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, "");
|
| -}
|
| -
|
| -// Test that loading a committed item does not queue another commit.
|
| -TEST_F(SharedModelTypeProcessorTest, LoadCommited) {
|
| - InitializeToReadyState();
|
| - WriteItemAndAck(kTag1, kValue1);
|
| - clear_change_processor();
|
| -
|
| - // Test that a new processor loads the metadata without committing.
|
| - InitializeToReadyState();
|
| - EXPECT_EQ(1U, ProcessorEntityCount());
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -}
|
| -
|
| -// Creates a new item locally.
|
| -// Thoroughly tests the data generated by a local item creation.
|
| -TEST_F(SharedModelTypeProcessorTest, LocalCreateItem) {
|
| - InitializeToReadyState();
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -
|
| - WriteItem(kTag1, kValue1);
|
| -
|
| - // Verify the commit request this operation has triggered.
|
| - worker()->ExpectPendingCommits({kTag1});
|
| - const CommitRequestData& tag1_request_data =
|
| - worker()->GetLatestPendingCommitForTag(kTag1);
|
| - const EntityData& tag1_data = tag1_request_data.entity.value();
|
| -
|
| - EXPECT_EQ(kUncommittedVersion, tag1_request_data.base_version);
|
| - EXPECT_TRUE(tag1_data.id.empty());
|
| - EXPECT_FALSE(tag1_data.creation_time.is_null());
|
| - EXPECT_FALSE(tag1_data.modification_time.is_null());
|
| - EXPECT_EQ(kTag1, tag1_data.non_unique_name);
|
| - EXPECT_FALSE(tag1_data.is_deleted());
|
| - EXPECT_EQ(kTag1, tag1_data.specifics.preference().name());
|
| - EXPECT_EQ(kValue1, tag1_data.specifics.preference().value());
|
| -
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - const sync_pb::EntityMetadata metadata = db().GetMetadata(kTag1);
|
| - EXPECT_TRUE(metadata.has_client_tag_hash());
|
| - EXPECT_FALSE(metadata.has_server_id());
|
| - EXPECT_FALSE(metadata.is_deleted());
|
| - EXPECT_EQ(1, metadata.sequence_number());
|
| - EXPECT_EQ(0, metadata.acked_sequence_number());
|
| - EXPECT_EQ(kUncommittedVersion, metadata.server_version());
|
| - EXPECT_TRUE(metadata.has_creation_time());
|
| - EXPECT_TRUE(metadata.has_modification_time());
|
| - EXPECT_TRUE(metadata.has_specifics_hash());
|
| -
|
| - worker()->AckOnePendingCommit();
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - const sync_pb::EntityMetadata acked_metadata = db().GetMetadata(kTag1);
|
| - EXPECT_TRUE(acked_metadata.has_server_id());
|
| - EXPECT_EQ(1, acked_metadata.sequence_number());
|
| - EXPECT_EQ(1, acked_metadata.acked_sequence_number());
|
| - EXPECT_EQ(1, acked_metadata.server_version());
|
| -}
|
| -
|
| -// Test that an error applying metadata changes from a commit response is
|
| -// propagated to the error handler.
|
| -TEST_F(SharedModelTypeProcessorTest, ErrorApplyingAck) {
|
| - InitializeToReadyState();
|
| - WriteItem(kTag1, kValue1);
|
| - SetServiceError(syncer::SyncError::DATATYPE_ERROR);
|
| - error_handler()->ExpectError(syncer::SyncError::DATATYPE_ERROR);
|
| - worker()->AckOnePendingCommit();
|
| -}
|
| -
|
| -// The purpose of this test case is to test setting |client_tag_hash| and |id|
|
| -// on the EntityData object as we pass it into the Put method of the processor.
|
| -TEST_F(SharedModelTypeProcessorTest, LocalUpdateItemWithOverrides) {
|
| - const std::string kId1 = "cid1";
|
| - const std::string kId2 = "cid2";
|
| - const std::string kName1 = "name1";
|
| - const std::string kName2 = "name2";
|
| - const std::string kTag3Hash = GenerateTagHash(kTag3);
|
| -
|
| - InitializeToReadyState();
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -
|
| - std::unique_ptr<EntityData> entity_data = base::WrapUnique(new EntityData());
|
| - entity_data->specifics.mutable_preference()->set_name(kName1);
|
| - entity_data->specifics.mutable_preference()->set_value(kValue1);
|
| -
|
| - entity_data->non_unique_name = kName1;
|
| - entity_data->client_tag_hash = kTag3Hash;
|
| - entity_data->id = kId1;
|
| - WriteItem(kTag1, std::move(entity_data));
|
| -
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - ASSERT_FALSE(worker()->HasPendingCommitForTag(kTag3));
|
| - ASSERT_TRUE(worker()->HasPendingCommitForTag(kTag1));
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - const EntityData& out_entity1 =
|
| - worker()->GetLatestPendingCommitForTag(kTag1).entity.value();
|
| - const sync_pb::EntityMetadata metadata_v1 = db().GetMetadata(kTag1);
|
| -
|
| - EXPECT_EQ(kId1, out_entity1.id);
|
| - EXPECT_NE(kTag3Hash, out_entity1.client_tag_hash);
|
| - EXPECT_EQ(kValue1, out_entity1.specifics.preference().value());
|
| - EXPECT_EQ(kId1, metadata_v1.server_id());
|
| - EXPECT_EQ(metadata_v1.client_tag_hash(), out_entity1.client_tag_hash);
|
| -
|
| - entity_data.reset(new EntityData());
|
| - entity_data->specifics.mutable_preference()->set_name(kName2);
|
| - entity_data->specifics.mutable_preference()->set_value(kValue2);
|
| - entity_data->non_unique_name = kName2;
|
| - entity_data->client_tag_hash = kTag3Hash;
|
| - // Make sure ID isn't overwritten either.
|
| - entity_data->id = kId2;
|
| - WriteItem(kTag1, std::move(entity_data));
|
| -
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - ASSERT_FALSE(worker()->HasPendingCommitForTag(kTag3));
|
| - ASSERT_TRUE(worker()->HasPendingCommitForTag(kTag1));
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - const EntityData& out_entity2 =
|
| - worker()->GetLatestPendingCommitForTag(kTag1).entity.value();
|
| - const sync_pb::EntityMetadata metadata_v2 = db().GetMetadata(kTag1);
|
| -
|
| - EXPECT_EQ(kValue2, out_entity2.specifics.preference().value());
|
| - // Should still see old cid1 value, override is not respected on update.
|
| - EXPECT_EQ(kId1, out_entity2.id);
|
| - EXPECT_EQ(kId1, metadata_v2.server_id());
|
| - EXPECT_EQ(metadata_v2.client_tag_hash(), out_entity2.client_tag_hash);
|
| -
|
| - // Specifics have changed so the hashes should not match.
|
| - EXPECT_NE(metadata_v1.specifics_hash(), metadata_v2.specifics_hash());
|
| -}
|
| -
|
| -// Creates a new local item then modifies it.
|
| -// Thoroughly tests data generated by modification of server-unknown item.
|
| -TEST_F(SharedModelTypeProcessorTest, LocalUpdateItem) {
|
| - InitializeToReadyState();
|
| -
|
| - WriteItem(kTag1, kValue1);
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - worker()->ExpectPendingCommits({kTag1});
|
| -
|
| - const CommitRequestData& request_data_v1 =
|
| - worker()->GetLatestPendingCommitForTag(kTag1);
|
| - const EntityData& data_v1 = request_data_v1.entity.value();
|
| - const sync_pb::EntityMetadata metadata_v1 = db().GetMetadata(kTag1);
|
| -
|
| - WriteItem(kTag1, kValue2);
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - worker()->ExpectPendingCommits({kTag1, kTag1});
|
| -
|
| - const CommitRequestData& request_data_v2 =
|
| - worker()->GetLatestPendingCommitForTag(kTag1);
|
| - const EntityData& data_v2 = request_data_v2.entity.value();
|
| - const sync_pb::EntityMetadata metadata_v2 = db().GetMetadata(kTag1);
|
| -
|
| - // Test some of the relations between old and new commit requests.
|
| - EXPECT_GT(request_data_v2.sequence_number, request_data_v1.sequence_number);
|
| - EXPECT_EQ(data_v1.specifics.preference().value(), kValue1);
|
| -
|
| - // Perform a thorough examination of the update-generated request.
|
| - EXPECT_EQ(kUncommittedVersion, request_data_v2.base_version);
|
| - EXPECT_TRUE(data_v2.id.empty());
|
| - EXPECT_FALSE(data_v2.creation_time.is_null());
|
| - EXPECT_FALSE(data_v2.modification_time.is_null());
|
| - EXPECT_EQ(kTag1, data_v2.non_unique_name);
|
| - EXPECT_FALSE(data_v2.is_deleted());
|
| - EXPECT_EQ(kTag1, data_v2.specifics.preference().name());
|
| - EXPECT_EQ(kValue2, data_v2.specifics.preference().value());
|
| -
|
| - EXPECT_FALSE(metadata_v1.has_server_id());
|
| - EXPECT_FALSE(metadata_v1.is_deleted());
|
| - EXPECT_EQ(1, metadata_v1.sequence_number());
|
| - EXPECT_EQ(0, metadata_v1.acked_sequence_number());
|
| - EXPECT_EQ(kUncommittedVersion, metadata_v1.server_version());
|
| -
|
| - EXPECT_FALSE(metadata_v2.has_server_id());
|
| - EXPECT_FALSE(metadata_v2.is_deleted());
|
| - EXPECT_EQ(2, metadata_v2.sequence_number());
|
| - EXPECT_EQ(0, metadata_v2.acked_sequence_number());
|
| - EXPECT_EQ(kUncommittedVersion, metadata_v2.server_version());
|
| -
|
| - EXPECT_EQ(metadata_v1.client_tag_hash(), metadata_v2.client_tag_hash());
|
| - EXPECT_NE(metadata_v1.specifics_hash(), metadata_v2.specifics_hash());
|
| -}
|
| -
|
| -// Tests that a local update that doesn't change specifics doesn't generate a
|
| -// commit request.
|
| -TEST_F(SharedModelTypeProcessorTest, LocalUpdateItemRedundant) {
|
| - InitializeToReadyState();
|
| - WriteItem(kTag1, kValue1);
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - worker()->ExpectPendingCommits({kTag1});
|
| -
|
| - WriteItem(kTag1, kValue1);
|
| - worker()->ExpectPendingCommits({kTag1});
|
| -}
|
| -
|
| -// Thoroughly tests the data generated by a server item creation.
|
| -TEST_F(SharedModelTypeProcessorTest, ServerCreateItem) {
|
| - InitializeToReadyState();
|
| - worker()->UpdateFromServer(kTag1, kValue1);
|
| - EXPECT_EQ(1U, db().DataCount());
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - EXPECT_EQ(1U, ProcessorEntityCount());
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -
|
| - const EntityData& data = db().GetData(kTag1);
|
| - EXPECT_FALSE(data.id.empty());
|
| - EXPECT_EQ(kTag1, data.specifics.preference().name());
|
| - EXPECT_EQ(kValue1, data.specifics.preference().value());
|
| - EXPECT_FALSE(data.creation_time.is_null());
|
| - EXPECT_FALSE(data.modification_time.is_null());
|
| - EXPECT_EQ(kTag1, data.non_unique_name);
|
| - EXPECT_FALSE(data.is_deleted());
|
| -
|
| - const sync_pb::EntityMetadata metadata = db().GetMetadata(kTag1);
|
| - EXPECT_TRUE(metadata.has_client_tag_hash());
|
| - EXPECT_TRUE(metadata.has_server_id());
|
| - EXPECT_FALSE(metadata.is_deleted());
|
| - EXPECT_EQ(0, metadata.sequence_number());
|
| - EXPECT_EQ(0, metadata.acked_sequence_number());
|
| - EXPECT_EQ(1, metadata.server_version());
|
| - EXPECT_TRUE(metadata.has_creation_time());
|
| - EXPECT_TRUE(metadata.has_modification_time());
|
| - EXPECT_TRUE(metadata.has_specifics_hash());
|
| -}
|
| -
|
| -// Test that an error applying changes from a server update is
|
| -// propagated to the error handler.
|
| -TEST_F(SharedModelTypeProcessorTest, ErrorApplyingUpdate) {
|
| - InitializeToReadyState();
|
| - SetServiceError(syncer::SyncError::DATATYPE_ERROR);
|
| - error_handler()->ExpectError(syncer::SyncError::DATATYPE_ERROR);
|
| - worker()->UpdateFromServer(kTag1, kValue1);
|
| -}
|
| -
|
| -// Thoroughly tests the data generated by a server item creation.
|
| -TEST_F(SharedModelTypeProcessorTest, ServerUpdateItem) {
|
| - InitializeToReadyState();
|
| -
|
| - // Local add writes data and metadata; ack writes metadata again.
|
| - WriteItemAndAck(kTag1, kValue1);
|
| - EXPECT_EQ(1U, db().DataChangeCount());
|
| - EXPECT_EQ(2U, db().MetadataChangeCount());
|
| -
|
| - // Redundant update from server doesn't write data but updates metadata.
|
| - worker()->UpdateFromServer(kTag1, kValue1);
|
| - EXPECT_EQ(1U, db().DataChangeCount());
|
| - EXPECT_EQ(3U, db().MetadataChangeCount());
|
| -
|
| - // A reflection (update already received) is ignored completely.
|
| - worker()->UpdateFromServer(kTag1, kValue1, 0 /* version_offset */);
|
| - EXPECT_EQ(1U, db().DataChangeCount());
|
| - EXPECT_EQ(3U, db().MetadataChangeCount());
|
| -}
|
| -
|
| -// Tests locally deleting an acknowledged item.
|
| -TEST_F(SharedModelTypeProcessorTest, LocalDeleteItem) {
|
| - InitializeToReadyState();
|
| - WriteItemAndAck(kTag1, kValue1);
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -
|
| - const sync_pb::EntityMetadata metadata_v1 = db().GetMetadata(kTag1);
|
| - EXPECT_FALSE(metadata_v1.is_deleted());
|
| - EXPECT_EQ(1, metadata_v1.sequence_number());
|
| - EXPECT_EQ(1, metadata_v1.acked_sequence_number());
|
| - EXPECT_EQ(1, metadata_v1.server_version());
|
| -
|
| - DeleteItem(kTag1);
|
| - EXPECT_EQ(0U, db().DataCount());
|
| - // Metadata is not removed until the commit response comes back.
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - EXPECT_EQ(1U, ProcessorEntityCount());
|
| - worker()->ExpectPendingCommits({kTag1});
|
| -
|
| - const sync_pb::EntityMetadata metadata_v2 = db().GetMetadata(kTag1);
|
| - EXPECT_TRUE(metadata_v2.is_deleted());
|
| - EXPECT_EQ(2, metadata_v2.sequence_number());
|
| - EXPECT_EQ(1, metadata_v2.acked_sequence_number());
|
| - EXPECT_EQ(1, metadata_v2.server_version());
|
| -
|
| - // Ack the delete and check that the metadata is cleared.
|
| - worker()->AckOnePendingCommit();
|
| - EXPECT_EQ(0U, db().MetadataCount());
|
| - EXPECT_EQ(0U, ProcessorEntityCount());
|
| -}
|
| -
|
| -// Tests creating and deleting an item locally before receiving a commit
|
| -// response, then getting the commit responses.
|
| -TEST_F(SharedModelTypeProcessorTest, LocalDeleteItemInterleaved) {
|
| - InitializeToReadyState();
|
| - WriteItem(kTag1, kValue1);
|
| - worker()->ExpectPendingCommits({kTag1});
|
| - const CommitRequestData& data_v1 =
|
| - worker()->GetLatestPendingCommitForTag(kTag1);
|
| -
|
| - const sync_pb::EntityMetadata metadata_v1 = db().GetMetadata(kTag1);
|
| - EXPECT_FALSE(metadata_v1.is_deleted());
|
| - EXPECT_EQ(1, metadata_v1.sequence_number());
|
| - EXPECT_EQ(0, metadata_v1.acked_sequence_number());
|
| - EXPECT_EQ(kUncommittedVersion, metadata_v1.server_version());
|
| -
|
| - DeleteItem(kTag1);
|
| - EXPECT_EQ(0U, db().DataCount());
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - EXPECT_EQ(1U, ProcessorEntityCount());
|
| - worker()->ExpectPendingCommits({kTag1, kTag1});
|
| -
|
| - const CommitRequestData& data_v2 =
|
| - worker()->GetLatestPendingCommitForTag(kTag1);
|
| - EXPECT_GT(data_v2.sequence_number, data_v1.sequence_number);
|
| - EXPECT_TRUE(data_v2.entity->id.empty());
|
| - EXPECT_EQ(kUncommittedVersion, data_v2.base_version);
|
| - EXPECT_TRUE(data_v2.entity->is_deleted());
|
| -
|
| - const sync_pb::EntityMetadata metadata_v2 = db().GetMetadata(kTag1);
|
| - EXPECT_TRUE(metadata_v2.is_deleted());
|
| - EXPECT_EQ(2, metadata_v2.sequence_number());
|
| - EXPECT_EQ(0, metadata_v2.acked_sequence_number());
|
| - EXPECT_EQ(kUncommittedVersion, metadata_v2.server_version());
|
| -
|
| - // A response for the first commit doesn't change much.
|
| - worker()->AckOnePendingCommit();
|
| - EXPECT_EQ(0U, db().DataCount());
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - EXPECT_EQ(1U, ProcessorEntityCount());
|
| -
|
| - const sync_pb::EntityMetadata metadata_v3 = db().GetMetadata(kTag1);
|
| - EXPECT_TRUE(metadata_v3.is_deleted());
|
| - EXPECT_EQ(2, metadata_v3.sequence_number());
|
| - EXPECT_EQ(1, metadata_v3.acked_sequence_number());
|
| - EXPECT_EQ(1, metadata_v3.server_version());
|
| -
|
| - worker()->AckOnePendingCommit();
|
| - // The delete was acked so the metadata should now be cleared.
|
| - EXPECT_EQ(0U, db().MetadataCount());
|
| - EXPECT_EQ(0U, ProcessorEntityCount());
|
| -}
|
| -
|
| -TEST_F(SharedModelTypeProcessorTest, ServerDeleteItem) {
|
| - InitializeToReadyState();
|
| - WriteItemAndAck(kTag1, kValue1);
|
| - EXPECT_EQ(1U, ProcessorEntityCount());
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - EXPECT_EQ(1U, db().DataCount());
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -
|
| - worker()->TombstoneFromServer(kTag1);
|
| - // Delete from server should clear the data and all the metadata.
|
| - EXPECT_EQ(0U, db().DataCount());
|
| - EXPECT_EQ(0U, db().MetadataCount());
|
| - EXPECT_EQ(0U, ProcessorEntityCount());
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -}
|
| -
|
| -// Deletes an item we've never seen before.
|
| -// Should have no effect and not crash.
|
| -TEST_F(SharedModelTypeProcessorTest, LocalDeleteUnknown) {
|
| - InitializeToReadyState();
|
| - DeleteItem(kTag1);
|
| - EXPECT_EQ(0U, db().DataCount());
|
| - EXPECT_EQ(0U, db().MetadataCount());
|
| - EXPECT_EQ(0U, ProcessorEntityCount());
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -}
|
| -
|
| -// Deletes an item we've never seen before.
|
| -// Should have no effect and not crash.
|
| -TEST_F(SharedModelTypeProcessorTest, ServerDeleteUnknown) {
|
| - InitializeToReadyState();
|
| - worker()->TombstoneFromServer(kTag1);
|
| - EXPECT_EQ(0U, db().DataCount());
|
| - EXPECT_EQ(0U, db().MetadataCount());
|
| - EXPECT_EQ(0U, ProcessorEntityCount());
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -}
|
| -
|
| -// Creates two different sync items.
|
| -// Verifies that the second has no effect on the first.
|
| -TEST_F(SharedModelTypeProcessorTest, TwoIndependentItems) {
|
| - InitializeToReadyState();
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -
|
| - WriteItem(kTag1, kValue1);
|
| - EXPECT_EQ(1U, db().DataCount());
|
| - EXPECT_EQ(1U, db().MetadataCount());
|
| - const sync_pb::EntityMetadata metadata1 = db().GetMetadata(kTag1);
|
| -
|
| - // There should be one commit request for this item only.
|
| - worker()->ExpectPendingCommits({kTag1});
|
| -
|
| - WriteItem(kTag2, kValue2);
|
| - EXPECT_EQ(2U, db().DataCount());
|
| - EXPECT_EQ(2U, db().MetadataCount());
|
| - const sync_pb::EntityMetadata metadata2 = db().GetMetadata(kTag2);
|
| -
|
| - // The second write should trigger another single-item commit request.
|
| - worker()->ExpectPendingCommits({kTag1, kTag2});
|
| -
|
| - EXPECT_FALSE(metadata1.is_deleted());
|
| - EXPECT_EQ(1, metadata1.sequence_number());
|
| - EXPECT_EQ(0, metadata1.acked_sequence_number());
|
| - EXPECT_EQ(kUncommittedVersion, metadata1.server_version());
|
| -
|
| - EXPECT_FALSE(metadata2.is_deleted());
|
| - EXPECT_EQ(1, metadata2.sequence_number());
|
| - EXPECT_EQ(0, metadata2.acked_sequence_number());
|
| - EXPECT_EQ(kUncommittedVersion, metadata2.server_version());
|
| -}
|
| -
|
| -TEST_F(SharedModelTypeProcessorTest, ConflictResolutionChangesMatch) {
|
| - InitializeToReadyState();
|
| - WriteItem(kTag1, kValue1);
|
| - EXPECT_EQ(1U, db().DataChangeCount());
|
| - EXPECT_EQ(kValue1, db().GetValue(kTag1));
|
| - EXPECT_EQ(1U, db().MetadataChangeCount());
|
| - EXPECT_EQ(kUncommittedVersion, db().GetMetadata(kTag1).server_version());
|
| - worker()->ExpectPendingCommits({kTag1});
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
|
| -
|
| - // Changes match doesn't call ResolveConflict.
|
| - worker()->UpdateFromServer(kTag1, kValue1);
|
| -
|
| - // Updated metadata but not data; no new commit request.
|
| - EXPECT_EQ(1U, db().DataChangeCount());
|
| - EXPECT_EQ(1, db().GetMetadata(kTag1).server_version());
|
| - worker()->ExpectPendingCommits({kTag1});
|
| -}
|
| -
|
| -TEST_F(SharedModelTypeProcessorTest, ConflictResolutionUseLocal) {
|
| - InitializeToReadyState();
|
| - WriteItem(kTag1, kValue1);
|
| - SetConflictResolution(ConflictResolution::UseLocal());
|
| -
|
| - worker()->UpdateFromServer(kTag1, kValue2);
|
| -
|
| - // Updated metadata but not data; new commit request.
|
| - EXPECT_EQ(1U, db().DataChangeCount());
|
| - EXPECT_EQ(2U, db().MetadataChangeCount());
|
| - EXPECT_EQ(1, db().GetMetadata(kTag1).server_version());
|
| - worker()->ExpectPendingCommits({kTag1, kTag1});
|
| - worker()->ExpectNthPendingCommit(1, kTag1, kValue1);
|
| -}
|
| -
|
| -TEST_F(SharedModelTypeProcessorTest, ConflictResolutionUseRemote) {
|
| - InitializeToReadyState();
|
| - WriteItem(kTag1, kValue1);
|
| - SetConflictResolution(ConflictResolution::UseRemote());
|
| - worker()->UpdateFromServer(kTag1, kValue2);
|
| -
|
| - // Updated client data and metadata; no new commit request.
|
| - EXPECT_EQ(2U, db().DataChangeCount());
|
| - EXPECT_EQ(kValue2, db().GetValue(kTag1));
|
| - EXPECT_EQ(2U, db().MetadataChangeCount());
|
| - EXPECT_EQ(1, db().GetMetadata(kTag1).server_version());
|
| - worker()->ExpectPendingCommits({kTag1});
|
| -}
|
| -
|
| -TEST_F(SharedModelTypeProcessorTest, ConflictResolutionUseNew) {
|
| - InitializeToReadyState();
|
| - WriteItem(kTag1, kValue1);
|
| - SetConflictResolution(
|
| - ConflictResolution::UseNew(GenerateEntityData(kTag1, kValue3)));
|
| -
|
| - worker()->UpdateFromServer(kTag1, kValue2);
|
| - EXPECT_EQ(2U, db().DataChangeCount());
|
| - EXPECT_EQ(kValue3, db().GetValue(kTag1));
|
| - EXPECT_EQ(2U, db().MetadataChangeCount());
|
| - EXPECT_EQ(1, db().GetMetadata(kTag1).server_version());
|
| - worker()->ExpectPendingCommits({kTag1, kTag1});
|
| - worker()->ExpectNthPendingCommit(1, kTag1, kValue3);
|
| -}
|
| -
|
| -// 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(SharedModelTypeProcessorTest, Disconnect) {
|
| - InitializeToReadyState();
|
| -
|
| - // The first item is fully committed.
|
| - WriteItemAndAck(kTag1, kValue1);
|
| -
|
| - // The second item has a commit request in progress.
|
| - WriteItem(kTag2, kValue2);
|
| - EXPECT_TRUE(worker()->HasPendingCommitForTag(kTag2));
|
| -
|
| - DisconnectSync();
|
| -
|
| - // The third item is added after stopping.
|
| - WriteItem(kTag3, kValue3);
|
| -
|
| - // Reconnect.
|
| - OnSyncStarting();
|
| -
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - EXPECT_EQ(2U, worker()->GetNthPendingCommit(0).size());
|
| -
|
| - // The first item was already in sync.
|
| - EXPECT_FALSE(worker()->HasPendingCommitForTag(kTag1));
|
| -
|
| - // The second item's commit was interrupted and should be retried.
|
| - EXPECT_TRUE(worker()->HasPendingCommitForTag(kTag2));
|
| -
|
| - // The third item's commit was not started until the reconnect.
|
| - EXPECT_TRUE(worker()->HasPendingCommitForTag(kTag3));
|
| -}
|
| -
|
| -// 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(SharedModelTypeProcessorTest, Disable) {
|
| - InitializeToReadyState();
|
| -
|
| - // The first item is fully committed.
|
| - WriteItemAndAck(kTag1, kValue1);
|
| -
|
| - // The second item has a commit request in progress.
|
| - WriteItem(kTag2, kValue2);
|
| - EXPECT_TRUE(worker()->HasPendingCommitForTag(kTag2));
|
| -
|
| - DisableSync();
|
| -
|
| - // The third item is added after disable.
|
| - WriteItem(kTag3, kValue3);
|
| -
|
| - // Now we re-enable.
|
| - CreateChangeProcessor();
|
| - OnMetadataLoaded();
|
| - OnSyncStarting();
|
| - OnInitialSyncDone();
|
| -
|
| - // Once we're ready to commit, all three local items should consider
|
| - // themselves uncommitted and pending for commit.
|
| - worker()->ExpectPendingCommits({kTag1, kTag2, kTag3});
|
| -}
|
| -
|
| -// Test re-encrypt everything when desired encryption key changes.
|
| -TEST_F(SharedModelTypeProcessorTest, ReEncryptCommitsWithNewKey) {
|
| - InitializeToReadyState();
|
| -
|
| - // Commit an item.
|
| - WriteItemAndAck(kTag1, kValue1);
|
| - // Create another item and don't wait for its commit response.
|
| - WriteItem(kTag2, kValue2);
|
| - worker()->ExpectPendingCommits({kTag2});
|
| - EXPECT_EQ(1U, db().GetMetadata(kTag1).sequence_number());
|
| - EXPECT_EQ(1U, db().GetMetadata(kTag2).sequence_number());
|
| -
|
| - // Receive notice that the account's desired encryption key has changed.
|
| - worker()->UpdateWithEncryptionKey("k1");
|
| - // Tag 2 is recommitted immediately because the data was in memory.
|
| - ASSERT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(1, kTag2, kValue2);
|
| - // Sequence numbers in the store are updated.
|
| - EXPECT_EQ(2U, db().GetMetadata(kTag1).sequence_number());
|
| - EXPECT_EQ(2U, db().GetMetadata(kTag2).sequence_number());
|
| -
|
| - // Tag 1 needs to go to the store to load its data before recommitting.
|
| - OnPendingCommitDataLoaded();
|
| - ASSERT_EQ(3U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(2, kTag1, kValue1);
|
| -}
|
| -
|
| -// Test that an error loading pending commit data for re-encryption is
|
| -// propagated to the error handler.
|
| -TEST_F(SharedModelTypeProcessorTest, ReEncryptErrorLoadingData) {
|
| - InitializeToReadyState();
|
| - WriteItemAndAck(kTag1, kValue1);
|
| - SetServiceError(syncer::SyncError::DATATYPE_ERROR);
|
| - worker()->UpdateWithEncryptionKey("k1");
|
| - error_handler()->ExpectError(syncer::SyncError::DATATYPE_ERROR);
|
| - OnPendingCommitDataLoaded();
|
| -}
|
| -
|
| -// Test receipt of updates with new and old keys.
|
| -TEST_F(SharedModelTypeProcessorTest, ReEncryptUpdatesWithNewKey) {
|
| - InitializeToReadyState();
|
| -
|
| - // Receive an unencrypted update.
|
| - worker()->UpdateFromServer(kTag1, kValue1);
|
| - ASSERT_EQ(0U, worker()->GetNumPendingCommits());
|
| -
|
| - UpdateResponseDataList update;
|
| - // Receive an entity with old encryption as part of the update.
|
| - update.push_back(worker()->GenerateUpdateData(kTag2, kValue2, 1, "k1"));
|
| - // Receive an entity with up-to-date encryption as part of the update.
|
| - update.push_back(worker()->GenerateUpdateData(kTag3, kValue3, 1, "k2"));
|
| - // Set desired encryption key to k2 to force updates to some items.
|
| - worker()->UpdateWithEncryptionKey("k2", update);
|
| -
|
| - // kTag2 needed to be re-encrypted and had data so it was queued immediately.
|
| - worker()->ExpectPendingCommits({kTag2});
|
| - OnPendingCommitDataLoaded();
|
| - // kTag1 needed data so once that's loaded, it is also queued.
|
| - worker()->ExpectPendingCommits({kTag2, kTag1});
|
| -
|
| - // Receive a separate update that was encrypted with key k1.
|
| - worker()->UpdateFromServer("enc_k1", kValue1, 1, "k1");
|
| - // Receipt of updates encrypted with old key also forces a re-encrypt commit.
|
| - worker()->ExpectPendingCommits({kTag2, kTag1, "enc_k1"});
|
| -
|
| - // Receive an update that was encrypted with key k2.
|
| - worker()->UpdateFromServer("enc_k2", kValue1, 1, "k2");
|
| - // That was the correct key, so no re-encryption is required.
|
| - worker()->ExpectPendingCommits({kTag2, kTag1, "enc_k1"});
|
| -}
|
| -
|
| -// Test that re-encrypting enqueues the right data for USE_LOCAL conflicts.
|
| -TEST_F(SharedModelTypeProcessorTest, ReEncryptConflictResolutionUseLocal) {
|
| - InitializeToReadyState();
|
| - worker()->UpdateWithEncryptionKey("k1");
|
| - WriteItem(kTag1, kValue1);
|
| - worker()->ExpectPendingCommits({kTag1});
|
| -
|
| - SetConflictResolution(ConflictResolution::UseLocal());
|
| - // Unencrypted update needs to be re-commited with key k1.
|
| - worker()->UpdateFromServer(kTag1, kValue2, 1, "");
|
| -
|
| - // Ensure the re-commit has the correct value.
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(1, kTag1, kValue1);
|
| - EXPECT_EQ(kValue1, db().GetValue(kTag1));
|
| -}
|
| -
|
| -// Test that re-encrypting enqueues the right data for USE_REMOTE conflicts.
|
| -TEST_F(SharedModelTypeProcessorTest, ReEncryptConflictResolutionUseRemote) {
|
| - InitializeToReadyState();
|
| - worker()->UpdateWithEncryptionKey("k1");
|
| - WriteItem(kTag1, kValue1);
|
| -
|
| - SetConflictResolution(ConflictResolution::UseRemote());
|
| - // Unencrypted update needs to be re-commited with key k1.
|
| - worker()->UpdateFromServer(kTag1, kValue2, 1, "");
|
| -
|
| - // Ensure the re-commit has the correct value.
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
|
| - EXPECT_EQ(kValue2, db().GetValue(kTag1));
|
| -}
|
| -
|
| -// Test that re-encrypting enqueues the right data for USE_NEW conflicts.
|
| -TEST_F(SharedModelTypeProcessorTest, ReEncryptConflictResolutionUseNew) {
|
| - InitializeToReadyState();
|
| - worker()->UpdateWithEncryptionKey("k1");
|
| - WriteItem(kTag1, kValue1);
|
| -
|
| - SetConflictResolution(
|
| - ConflictResolution::UseNew(GenerateEntityData(kTag1, kValue3)));
|
| - // Unencrypted update needs to be re-commited with key k1.
|
| - worker()->UpdateFromServer(kTag1, kValue2, 1, "");
|
| -
|
| - // Ensure the re-commit has the correct value.
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(1, kTag1, kValue3);
|
| - EXPECT_EQ(kValue3, db().GetValue(kTag1));
|
| -}
|
| -
|
| -TEST_F(SharedModelTypeProcessorTest, ReEncryptConflictWhileLoading) {
|
| - InitializeToReadyState();
|
| - // Create item and ack so its data is no longer cached.
|
| - WriteItemAndAck(kTag1, kValue1);
|
| - // Update key so that it needs to fetch data to re-commit.
|
| - worker()->UpdateWithEncryptionKey("k1");
|
| - EXPECT_EQ(0U, worker()->GetNumPendingCommits());
|
| -
|
| - // Unencrypted update needs to be re-commited with key k1.
|
| - worker()->UpdateFromServer(kTag1, kValue2, 1, "");
|
| -
|
| - // Ensure the re-commit has the correct value.
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
|
| - EXPECT_EQ(kValue2, db().GetValue(kTag1));
|
| -
|
| - // Data load completing shouldn't change anything.
|
| - OnPendingCommitDataLoaded();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| -}
|
| -
|
| -// Tests that a real remote change wins over a local encryption-only change.
|
| -TEST_F(SharedModelTypeProcessorTest, IgnoreLocalEncryption) {
|
| - InitializeToReadyState();
|
| - WriteItemAndAck(kTag1, kValue1);
|
| - worker()->UpdateWithEncryptionKey("k1");
|
| - OnPendingCommitDataLoaded();
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue1);
|
| -
|
| - worker()->UpdateFromServer(kTag1, kValue2);
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| -}
|
| -
|
| -// Tests that a real local change wins over a remote encryption-only change.
|
| -TEST_F(SharedModelTypeProcessorTest, IgnoreRemoteEncryption) {
|
| - InitializeToReadyState();
|
| - WriteItemAndAck(kTag1, kValue1);
|
| -
|
| - WriteItem(kTag1, kValue2);
|
| - UpdateResponseDataList update;
|
| - update.push_back(worker()->GenerateUpdateData(kTag1, kValue1, 1, "k1"));
|
| - worker()->UpdateWithEncryptionKey("k1", update);
|
| -
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
|
| -}
|
| -
|
| -// Same as above but with two commit requests before one ack.
|
| -TEST_F(SharedModelTypeProcessorTest, IgnoreRemoteEncryptionInterleaved) {
|
| - InitializeToReadyState();
|
| - WriteItem(kTag1, kValue1);
|
| - WriteItem(kTag1, kValue2);
|
| - worker()->AckOnePendingCommit();
|
| - // kValue1 is now the base value.
|
| - EXPECT_EQ(1U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(0, kTag1, kValue2);
|
| -
|
| - UpdateResponseDataList update;
|
| - update.push_back(worker()->GenerateUpdateData(kTag1, kValue1, 1, "k1"));
|
| - worker()->UpdateWithEncryptionKey("k1", update);
|
| -
|
| - EXPECT_EQ(2U, worker()->GetNumPendingCommits());
|
| - worker()->ExpectNthPendingCommit(1, kTag1, kValue2);
|
| -}
|
| -
|
| -} // namespace syncer_v2
|
|
|