Commit 3278a193 authored by Mohamed Amir Yosef's avatar Mohamed Amir Yosef Committed by Commit Bot

[Sync::USS] Implement NigoriModelTypeProcessor::OnUpdateReceived()

Bug: 922900
Change-Id: Ic0c140f62697c5a6cdae2ec56204aadae8c3e3cb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1575938
Commit-Queue: Mohamed Amir Yosef <mamir@chromium.org>
Reviewed-by: default avatarMikel Astiz <mastiz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#653620}
parent bedabe88
...@@ -96,7 +96,71 @@ void NigoriModelTypeProcessor::OnUpdateReceived( ...@@ -96,7 +96,71 @@ void NigoriModelTypeProcessor::OnUpdateReceived(
const sync_pb::ModelTypeState& type_state, const sync_pb::ModelTypeState& type_state,
UpdateResponseDataList updates) { UpdateResponseDataList updates) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(model_ready_to_sync_);
// If there is a model error, it must have been reported already but hasn't
// reached the sync engine yet. In this case return directly to avoid
// interactions with the bridge.
if (model_error_) {
return;
}
base::Optional<ModelError> error;
bool is_initial_sync = !model_type_state_.initial_sync_done();
model_type_state_ = type_state;
if (is_initial_sync) {
DCHECK(!entity_);
if (updates.empty()) {
error = bridge_->MergeSyncData(base::nullopt);
} else {
DCHECK(!updates[0]->entity->is_deleted());
entity_ = ProcessorEntity::CreateNew(
kNigoriStorageKey, kNigoriClientTagHash, updates[0]->entity->id,
updates[0]->entity->creation_time);
entity_->RecordAcceptedUpdate(*updates[0]);
error = bridge_->MergeSyncData(std::move(*updates[0]->entity));
}
if (error) {
// TODO(mamir): ReportError(*error);
NOTIMPLEMENTED();
}
return;
}
if (updates.empty()) {
bridge_->ApplySyncChanges(/*data=*/base::nullopt);
return;
}
DCHECK(entity_);
// We assume the bridge will issue errors in case of deletions. Therefore, we
// are adding the following DCHECK to simplify the code.
DCHECK(!updates[0]->entity->is_deleted());
if (entity_->UpdateIsReflection(updates[0]->response_version)) {
// Seen this update before; just ignore it.
bridge_->ApplySyncChanges(/*data=*/base::nullopt);
return;
}
if (entity_->IsUnsynced()) {
// TODO(mamir): conflict resolution
NOTIMPLEMENTED();
} else if (!entity_->MatchesData(*updates[0]->entity)) {
// Inform the bridge of the new or updated data.
entity_->RecordAcceptedUpdate(*updates[0]);
error = bridge_->ApplySyncChanges(std::move(*updates[0]->entity));
}
if (error) {
// TODO(mamir): ReportError(*error);
NOTIMPLEMENTED(); NOTIMPLEMENTED();
return;
}
// There may be new reasons to commit by the time this function is done.
NudgeForCommitIfNeeded();
} }
void NigoriModelTypeProcessor::OnSyncStarting( void NigoriModelTypeProcessor::OnSyncStarting(
......
...@@ -21,18 +21,52 @@ namespace syncer { ...@@ -21,18 +21,52 @@ namespace syncer {
namespace { namespace {
using testing::_;
using testing::Eq; using testing::Eq;
using testing::Ne; using testing::Ne;
using testing::NotNull; using testing::NotNull;
const char kNigoriNonUniqueName[] = "nigori"; const char kNigoriNonUniqueName[] = "nigori";
const char kNigoriServerId[] = "nigori_server_id";
const char kCacheGuid[] = "generated_id"; const char kCacheGuid[] = "generated_id";
// |*arg| must be of type base::Optional<EntityData>.
MATCHER_P(OptionalEntityDataHasDecryptorTokenKeyName, expected_key_name, "") {
return arg->specifics.nigori().keystore_decryptor_token().key_name() ==
expected_key_name;
}
void CaptureCommitRequest(CommitRequestDataList* dst, void CaptureCommitRequest(CommitRequestDataList* dst,
CommitRequestDataList&& src) { CommitRequestDataList&& src) {
*dst = std::move(src); *dst = std::move(src);
} }
sync_pb::ModelTypeState CreateDummyModelTypeState() {
sync_pb::ModelTypeState model_type_state;
model_type_state.set_cache_guid(kCacheGuid);
model_type_state.set_initial_sync_done(true);
return model_type_state;
}
// Creates a dummy Nigori UpdateResponseData that has the keystore decryptor
// token key name set.
std::unique_ptr<syncer::UpdateResponseData> CreateDummyNigoriUpdateResponseData(
const std::string keystore_decryptor_token_key_name,
int response_version) {
auto entity_data = std::make_unique<syncer::EntityData>();
entity_data->id = kNigoriServerId;
sync_pb::NigoriSpecifics* nigori_specifics =
entity_data->specifics.mutable_nigori();
nigori_specifics->mutable_keystore_decryptor_token()->set_key_name(
keystore_decryptor_token_key_name);
entity_data->non_unique_name = kNigoriNonUniqueName;
auto response_data = std::make_unique<syncer::UpdateResponseData>();
response_data->entity = std::move(entity_data);
response_data->response_version = response_version;
return response_data;
}
class MockNigoriSyncBridge : public NigoriSyncBridge { class MockNigoriSyncBridge : public NigoriSyncBridge {
public: public:
MockNigoriSyncBridge() = default; MockNigoriSyncBridge() = default;
...@@ -41,8 +75,9 @@ class MockNigoriSyncBridge : public NigoriSyncBridge { ...@@ -41,8 +75,9 @@ class MockNigoriSyncBridge : public NigoriSyncBridge {
MOCK_METHOD1( MOCK_METHOD1(
MergeSyncData, MergeSyncData,
base::Optional<ModelError>(const base::Optional<EntityData>& data)); base::Optional<ModelError>(const base::Optional<EntityData>& data));
MOCK_METHOD1(ApplySyncChanges, MOCK_METHOD1(
base::Optional<ModelError>(const EntityData& data)); ApplySyncChanges,
base::Optional<ModelError>(const base::Optional<EntityData>& data));
MOCK_METHOD0(GetData, std::unique_ptr<EntityData>()); MOCK_METHOD0(GetData, std::unique_ptr<EntityData>());
MOCK_METHOD2(ResolveConflict, MOCK_METHOD2(ResolveConflict,
ConflictResolution(const EntityData& local_data, ConflictResolution(const EntityData& local_data,
...@@ -65,18 +100,24 @@ class NigoriModelTypeProcessorTest : public testing::Test { ...@@ -65,18 +100,24 @@ class NigoriModelTypeProcessorTest : public testing::Test {
mock_commit_queue_ptr_ = mock_commit_queue_.get(); mock_commit_queue_ptr_ = mock_commit_queue_.get();
} }
void SimulateModelReadyToSyncWithInitialSyncDone() { void SimulateModelReadyToSync(bool initial_sync_done, int server_version) {
NigoriMetadataBatch nigori_metadata_batch; NigoriMetadataBatch nigori_metadata_batch;
nigori_metadata_batch.model_type_state.set_initial_sync_done(true); nigori_metadata_batch.model_type_state.set_initial_sync_done(
initial_sync_done);
nigori_metadata_batch.entity_metadata = sync_pb::EntityMetadata(); nigori_metadata_batch.entity_metadata = sync_pb::EntityMetadata();
nigori_metadata_batch.entity_metadata->set_creation_time( nigori_metadata_batch.entity_metadata->set_creation_time(
TimeToProtoTime(base::Time::Now())); TimeToProtoTime(base::Time::Now()));
nigori_metadata_batch.entity_metadata->set_sequence_number(0); nigori_metadata_batch.entity_metadata->set_sequence_number(0);
nigori_metadata_batch.entity_metadata->set_acked_sequence_number(0); nigori_metadata_batch.entity_metadata->set_acked_sequence_number(0);
nigori_metadata_batch.entity_metadata->set_server_version(server_version);
processor_.ModelReadyToSync(mock_nigori_sync_bridge(), processor_.ModelReadyToSync(mock_nigori_sync_bridge(),
std::move(nigori_metadata_batch)); std::move(nigori_metadata_batch));
} }
void SimulateModelReadyToSync(bool initial_sync_done) {
SimulateModelReadyToSync(initial_sync_done, /*server_version=*/1);
}
void SimulateConnectSync() { void SimulateConnectSync() {
processor_.ConnectSync(std::move(mock_commit_queue_)); processor_.ConnectSync(std::move(mock_commit_queue_));
} }
...@@ -132,7 +173,7 @@ TEST_F(NigoriModelTypeProcessorTest, ...@@ -132,7 +173,7 @@ TEST_F(NigoriModelTypeProcessorTest,
} }
TEST_F(NigoriModelTypeProcessorTest, ShouldIncrementSequenceNumberWhenPut) { TEST_F(NigoriModelTypeProcessorTest, ShouldIncrementSequenceNumberWhenPut) {
SimulateModelReadyToSyncWithInitialSyncDone(); SimulateModelReadyToSync(/*initial_sync_done=*/true);
base::Optional<sync_pb::EntityMetadata> entity_metadata1 = base::Optional<sync_pb::EntityMetadata> entity_metadata1 =
processor()->GetMetadata().entity_metadata; processor()->GetMetadata().entity_metadata;
ASSERT_TRUE(entity_metadata1); ASSERT_TRUE(entity_metadata1);
...@@ -152,7 +193,7 @@ TEST_F(NigoriModelTypeProcessorTest, ShouldIncrementSequenceNumberWhenPut) { ...@@ -152,7 +193,7 @@ TEST_F(NigoriModelTypeProcessorTest, ShouldIncrementSequenceNumberWhenPut) {
} }
TEST_F(NigoriModelTypeProcessorTest, ShouldGetEmptyLocalChanges) { TEST_F(NigoriModelTypeProcessorTest, ShouldGetEmptyLocalChanges) {
SimulateModelReadyToSyncWithInitialSyncDone(); SimulateModelReadyToSync(/*initial_sync_done=*/true);
CommitRequestDataList commit_request; CommitRequestDataList commit_request;
processor()->GetLocalChanges( processor()->GetLocalChanges(
/*max_entries=*/10, /*max_entries=*/10,
...@@ -161,7 +202,7 @@ TEST_F(NigoriModelTypeProcessorTest, ShouldGetEmptyLocalChanges) { ...@@ -161,7 +202,7 @@ TEST_F(NigoriModelTypeProcessorTest, ShouldGetEmptyLocalChanges) {
} }
TEST_F(NigoriModelTypeProcessorTest, ShouldGetLocalChangesWhenPut) { TEST_F(NigoriModelTypeProcessorTest, ShouldGetLocalChangesWhenPut) {
SimulateModelReadyToSyncWithInitialSyncDone(); SimulateModelReadyToSync(/*initial_sync_done=*/true);
auto entity_data = std::make_unique<syncer::EntityData>(); auto entity_data = std::make_unique<syncer::EntityData>();
entity_data->specifics.mutable_nigori(); entity_data->specifics.mutable_nigori();
...@@ -178,7 +219,7 @@ TEST_F(NigoriModelTypeProcessorTest, ShouldGetLocalChangesWhenPut) { ...@@ -178,7 +219,7 @@ TEST_F(NigoriModelTypeProcessorTest, ShouldGetLocalChangesWhenPut) {
TEST_F(NigoriModelTypeProcessorTest, TEST_F(NigoriModelTypeProcessorTest,
ShouldNudgeForCommitUponConnectSyncIfReadyToSyncAndLocalChanges) { ShouldNudgeForCommitUponConnectSyncIfReadyToSyncAndLocalChanges) {
SimulateModelReadyToSyncWithInitialSyncDone(); SimulateModelReadyToSync(/*initial_sync_done=*/true);
auto entity_data = std::make_unique<syncer::EntityData>(); auto entity_data = std::make_unique<syncer::EntityData>();
entity_data->specifics.mutable_nigori(); entity_data->specifics.mutable_nigori();
...@@ -191,7 +232,7 @@ TEST_F(NigoriModelTypeProcessorTest, ...@@ -191,7 +232,7 @@ TEST_F(NigoriModelTypeProcessorTest,
} }
TEST_F(NigoriModelTypeProcessorTest, ShouldNudgeForCommitUponPutIfReadyToSync) { TEST_F(NigoriModelTypeProcessorTest, ShouldNudgeForCommitUponPutIfReadyToSync) {
SimulateModelReadyToSyncWithInitialSyncDone(); SimulateModelReadyToSync(/*initial_sync_done=*/true);
SimulateConnectSync(); SimulateConnectSync();
auto entity_data = std::make_unique<syncer::EntityData>(); auto entity_data = std::make_unique<syncer::EntityData>();
...@@ -203,7 +244,7 @@ TEST_F(NigoriModelTypeProcessorTest, ShouldNudgeForCommitUponPutIfReadyToSync) { ...@@ -203,7 +244,7 @@ TEST_F(NigoriModelTypeProcessorTest, ShouldNudgeForCommitUponPutIfReadyToSync) {
} }
TEST_F(NigoriModelTypeProcessorTest, ShouldInvokeSyncStartCallback) { TEST_F(NigoriModelTypeProcessorTest, ShouldInvokeSyncStartCallback) {
SimulateModelReadyToSyncWithInitialSyncDone(); SimulateModelReadyToSync(/*initial_sync_done=*/true);
syncer::DataTypeActivationRequest request; syncer::DataTypeActivationRequest request;
request.error_handler = base::DoNothing(); request.error_handler = base::DoNothing();
...@@ -231,6 +272,66 @@ TEST_F(NigoriModelTypeProcessorTest, ShouldInvokeSyncStartCallback) { ...@@ -231,6 +272,66 @@ TEST_F(NigoriModelTypeProcessorTest, ShouldInvokeSyncStartCallback) {
EXPECT_TRUE(processor()->IsConnectedForTest()); EXPECT_TRUE(processor()->IsConnectedForTest());
} }
TEST_F(NigoriModelTypeProcessorTest, ShouldMergeSyncData) {
SimulateModelReadyToSync(/*initial_sync_done=*/false);
const std::string kDecryptorTokenKeyName = "key_name";
UpdateResponseDataList updates;
updates.push_back(CreateDummyNigoriUpdateResponseData(kDecryptorTokenKeyName,
/*server_version=*/1));
EXPECT_CALL(*mock_nigori_sync_bridge(),
MergeSyncData(OptionalEntityDataHasDecryptorTokenKeyName(
kDecryptorTokenKeyName)));
processor()->OnUpdateReceived(CreateDummyModelTypeState(),
std::move(updates));
}
TEST_F(NigoriModelTypeProcessorTest, ShouldApplySyncChanges) {
SimulateModelReadyToSync(/*initial_sync_done=*/true, /*server_version=*/1);
const std::string kDecryptorTokenKeyName = "key_name";
UpdateResponseDataList updates;
updates.push_back(CreateDummyNigoriUpdateResponseData(kDecryptorTokenKeyName,
/*server_version=*/2));
EXPECT_CALL(*mock_nigori_sync_bridge(),
ApplySyncChanges(OptionalEntityDataHasDecryptorTokenKeyName(
kDecryptorTokenKeyName)));
processor()->OnUpdateReceived(CreateDummyModelTypeState(),
std::move(updates));
}
TEST_F(NigoriModelTypeProcessorTest, ShouldApplySyncChangesWhenEmptyUpdates) {
const int kServerVersion = 1;
SimulateModelReadyToSync(/*initial_sync_done=*/true, kServerVersion);
// ApplySyncChanges() should still be called to trigger persistence of the
// metadata.
EXPECT_CALL(*mock_nigori_sync_bridge(), ApplySyncChanges(Eq(base::nullopt)));
processor()->OnUpdateReceived(CreateDummyModelTypeState(),
UpdateResponseDataList());
}
TEST_F(NigoriModelTypeProcessorTest, ShouldApplySyncChangesWhenReflection) {
const int kServerVersion = 1;
SimulateModelReadyToSync(/*initial_sync_done=*/true, kServerVersion);
UpdateResponseDataList updates;
updates.push_back(CreateDummyNigoriUpdateResponseData(
/*keystore_decryptor_token_key_name=*/"key_name", kServerVersion));
// ApplySyncChanges() should still be called to trigger persistence of the
// metadata.
EXPECT_CALL(*mock_nigori_sync_bridge(), ApplySyncChanges(Eq(base::nullopt)));
processor()->OnUpdateReceived(CreateDummyModelTypeState(),
std::move(updates));
}
} // namespace } // namespace
} // namespace syncer } // namespace syncer
...@@ -31,7 +31,7 @@ class NigoriSyncBridge { ...@@ -31,7 +31,7 @@ class NigoriSyncBridge {
// Apply changes from the sync server locally. // Apply changes from the sync server locally.
virtual base::Optional<ModelError> ApplySyncChanges( virtual base::Optional<ModelError> ApplySyncChanges(
const EntityData& data) = 0; const base::Optional<EntityData>& data) = 0;
// Retrieve Nigori sync data. // Retrieve Nigori sync data.
virtual std::unique_ptr<EntityData> GetData() = 0; virtual std::unique_ptr<EntityData> GetData() = 0;
......
...@@ -149,14 +149,15 @@ base::Optional<ModelError> NigoriSyncBridgeImpl::MergeSyncData( ...@@ -149,14 +149,15 @@ base::Optional<ModelError> NigoriSyncBridgeImpl::MergeSyncData(
"Received empty EntityData during initial " "Received empty EntityData during initial "
"sync of Nigori."); "sync of Nigori.");
} }
return ApplySyncChanges(*data); return ApplySyncChanges(data);
} }
base::Optional<ModelError> NigoriSyncBridgeImpl::ApplySyncChanges( base::Optional<ModelError> NigoriSyncBridgeImpl::ApplySyncChanges(
const EntityData& data) { const base::Optional<EntityData>& data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(data.specifics.has_nigori()); if (data) {
const sync_pb::NigoriSpecifics& nigori = data.specifics.nigori(); DCHECK(data->specifics.has_nigori());
const sync_pb::NigoriSpecifics& nigori = data->specifics.nigori();
// TODO(crbug.com/922900): support other passphrase types. // TODO(crbug.com/922900): support other passphrase types.
if (ProtoPassphraseTypeToEnum(nigori.passphrase_type()) != if (ProtoPassphraseTypeToEnum(nigori.passphrase_type()) !=
...@@ -186,6 +187,7 @@ base::Optional<ModelError> NigoriSyncBridgeImpl::ApplySyncChanges( ...@@ -186,6 +187,7 @@ base::Optional<ModelError> NigoriSyncBridgeImpl::ApplySyncChanges(
"decryptor token."); "decryptor token.");
} }
} }
}
// TODO(crbug.com/922900): implement updates of other data fields (e.g. // TODO(crbug.com/922900): implement updates of other data fields (e.g.
// passphrase type, encrypted types). // passphrase type, encrypted types).
NOTIMPLEMENTED(); NOTIMPLEMENTED();
......
...@@ -56,7 +56,8 @@ class NigoriSyncBridgeImpl : public KeystoreKeysHandler, ...@@ -56,7 +56,8 @@ class NigoriSyncBridgeImpl : public KeystoreKeysHandler,
// NigoriSyncBridge implementation. // NigoriSyncBridge implementation.
base::Optional<ModelError> MergeSyncData( base::Optional<ModelError> MergeSyncData(
const base::Optional<EntityData>& data) override; const base::Optional<EntityData>& data) override;
base::Optional<ModelError> ApplySyncChanges(const EntityData& data) override; base::Optional<ModelError> ApplySyncChanges(
const base::Optional<EntityData>& data) override;
std::unique_ptr<EntityData> GetData() override; std::unique_ptr<EntityData> GetData() override;
ConflictResolution ResolveConflict(const EntityData& local_data, ConflictResolution ResolveConflict(const EntityData& local_data,
const EntityData& remote_data) override; const EntityData& remote_data) override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment