| Index: components/sync/engine_impl/model_type_worker_unittest.cc
|
| diff --git a/components/sync/engine_impl/model_type_worker_unittest.cc b/components/sync/engine_impl/model_type_worker_unittest.cc
|
| index ca1cc97cd78986ca0ed4dd035bc9cc5ed6c7d575..17f1ac10d9d7c51ad7ef3f5b514440ca5b3e8d48 100644
|
| --- a/components/sync/engine_impl/model_type_worker_unittest.cc
|
| +++ b/components/sync/engine_impl/model_type_worker_unittest.cc
|
| @@ -195,7 +195,7 @@ class ModelTypeWorkerTest : public ::testing::Test {
|
| }
|
|
|
| // Introduce a new key that the local cryptographer can't decrypt.
|
| - void NewForeignEncryptionKey() {
|
| + void AddPendingKey() {
|
| if (!cryptographer_) {
|
| cryptographer_ = base::MakeUnique<Cryptographer>(&fake_encryptor_);
|
| }
|
| @@ -242,7 +242,7 @@ class ModelTypeWorkerTest : public ::testing::Test {
|
| }
|
|
|
| // Update the local cryptographer with all relevant keys.
|
| - void UpdateLocalCryptographer() {
|
| + void DecryptPendingKey() {
|
| if (!cryptographer_) {
|
| cryptographer_ = base::MakeUnique<Cryptographer>(&fake_encryptor_);
|
| }
|
| @@ -281,11 +281,10 @@ class ModelTypeWorkerTest : public ::testing::Test {
|
|
|
| void TriggerTypeRootUpdateFromServer() {
|
| sync_pb::SyncEntity entity = mock_server_.TypeRootUpdate();
|
| - StatusController dummy_status;
|
| worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
|
| mock_server_.GetContext(), {&entity},
|
| - &dummy_status);
|
| - worker_->PassiveApplyUpdates(&dummy_status);
|
| + nullptr);
|
| + worker_->PassiveApplyUpdates(nullptr);
|
| }
|
|
|
| void TriggerPartialUpdateFromServer(int64_t version_offset,
|
| @@ -299,18 +298,16 @@ class ModelTypeWorkerTest : public ::testing::Test {
|
| entity.mutable_specifics());
|
| }
|
|
|
| - StatusController dummy_status;
|
| worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
|
| mock_server_.GetContext(), {&entity},
|
| - &dummy_status);
|
| + nullptr);
|
| }
|
|
|
| void TriggerUpdateFromServer(int64_t version_offset,
|
| const std::string& tag,
|
| const std::string& value) {
|
| TriggerPartialUpdateFromServer(version_offset, tag, value);
|
| - StatusController dummy_status;
|
| - worker_->ApplyUpdates(&dummy_status);
|
| + worker_->ApplyUpdates(nullptr);
|
| }
|
|
|
| void TriggerTombstoneFromServer(int64_t version_offset,
|
| @@ -323,24 +320,25 @@ class ModelTypeWorkerTest : public ::testing::Test {
|
| entity.mutable_specifics());
|
| }
|
|
|
| - StatusController dummy_status;
|
| worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
|
| mock_server_.GetContext(), {&entity},
|
| - &dummy_status);
|
| - worker_->ApplyUpdates(&dummy_status);
|
| + nullptr);
|
| + worker_->ApplyUpdates(nullptr);
|
| }
|
|
|
| + // Simulates the end of a GU sync cycle and tells the worker to flush changes
|
| + // to the processor.
|
| + void ApplyUpdates() { worker_->ApplyUpdates(nullptr); }
|
| +
|
| // Delivers specified protos as updates.
|
| //
|
| // Does not update mock server state. Should be used as a last resort when
|
| // writing test cases that require entities that don't fit the normal sync
|
| // protocol. Try to use the other, higher level methods if possible.
|
| void DeliverRawUpdates(const SyncEntityList& list) {
|
| - StatusController dummy_status;
|
| - worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(),
|
| - mock_server_.GetContext(), list,
|
| - &dummy_status);
|
| - worker_->ApplyUpdates(&dummy_status);
|
| + worker_->ProcessGetUpdatesResponse(
|
| + mock_server_.GetProgress(), mock_server_.GetContext(), list, nullptr);
|
| + worker_->ApplyUpdates(nullptr);
|
| }
|
|
|
| // By default, this harness behaves as if all tasks posted to the model
|
| @@ -385,8 +383,7 @@ class ModelTypeWorkerTest : public ::testing::Test {
|
| sync_pb::ClientToServerResponse response =
|
| mock_server_.DoSuccessfulCommit(message);
|
|
|
| - StatusController dummy_status;
|
| - contribution->ProcessCommitResponse(response, &dummy_status);
|
| + contribution->ProcessCommitResponse(response, nullptr);
|
| contribution->CleanUp();
|
| }
|
|
|
| @@ -412,7 +409,7 @@ class ModelTypeWorkerTest : public ::testing::Test {
|
|
|
| // Returns the name of the encryption key in the cryptographer last passed to
|
| // the CommitQueue. Returns an empty string if no crypgorapher is
|
| - // in use. See also: UpdateLocalCryptographer().
|
| + // in use. See also: DecryptPendingKey().
|
| std::string GetLocalCryptographerKeyName() const {
|
| if (!cryptographer_) {
|
| return std::string();
|
| @@ -695,15 +692,31 @@ TEST_F(ModelTypeWorkerTest, ReceiveMultiPartUpdates) {
|
| EXPECT_EQ(GenerateTagHash(kTag3), updates[0].entity->client_tag_hash);
|
| }
|
|
|
| +// Test that updates with no entities behave correctly.
|
| +TEST_F(ModelTypeWorkerTest, EmptyUpdates) {
|
| + NormalInitialize();
|
| +
|
| + server()->SetProgressMarkerToken("token2");
|
| + DeliverRawUpdates(SyncEntityList());
|
| + ASSERT_EQ(1U, processor()->GetNumUpdateResponses());
|
| + ASSERT_EQ(
|
| + server()->GetProgress().SerializeAsString(),
|
| + processor()->GetNthUpdateState(0).progress_marker().SerializeAsString());
|
| +}
|
| +
|
| // Test commit of encrypted updates.
|
| TEST_F(ModelTypeWorkerTest, EncryptedCommit) {
|
| NormalInitialize();
|
|
|
| ASSERT_EQ(0U, processor()->GetNumUpdateResponses());
|
|
|
| - NewForeignEncryptionKey();
|
| - UpdateLocalCryptographer();
|
| + // Tell the worker about the new encryption key but no ApplyUpdates yet.
|
| + AddPendingKey();
|
| + DecryptPendingKey();
|
| + ASSERT_EQ(0U, processor()->GetNumUpdateResponses());
|
|
|
| + // Now the information is allowed to reach the processor.
|
| + ApplyUpdates();
|
| ASSERT_EQ(1U, processor()->GetNumUpdateResponses());
|
| EXPECT_EQ(GetLocalCryptographerKeyName(),
|
| processor()->GetNthUpdateState(0).encryption_key_name());
|
| @@ -728,6 +741,30 @@ TEST_F(ModelTypeWorkerTest, EncryptedCommit) {
|
| EXPECT_FALSE(tag1_entity.specifics().preference().has_value());
|
| }
|
|
|
| +// Test that updates are not delivered to the processor when encryption is
|
| +// required but unavailable.
|
| +TEST_F(ModelTypeWorkerTest, EncryptionBlocksUpdates) {
|
| + NormalInitialize();
|
| +
|
| + // Update encryption key name, should be blocked.
|
| + AddPendingKey();
|
| + ASSERT_EQ(0U, processor()->GetNumUpdateResponses());
|
| +
|
| + // Update progress marker, should be blocked.
|
| + server()->SetProgressMarkerToken("token2");
|
| + DeliverRawUpdates(SyncEntityList());
|
| + ASSERT_EQ(0U, processor()->GetNumUpdateResponses());
|
| +
|
| + // Update local cryptographer, verify everything is pushed to processor.
|
| + DecryptPendingKey();
|
| + ApplyUpdates();
|
| + ASSERT_EQ(1U, processor()->GetNumUpdateResponses());
|
| + UpdateResponseDataList updates_list = processor()->GetNthUpdateResponse(0);
|
| + ASSERT_EQ(
|
| + server()->GetProgress().SerializeAsString(),
|
| + processor()->GetNthUpdateState(0).progress_marker().SerializeAsString());
|
| +}
|
| +
|
| // Test items are not committed when encryption is required but unavailable.
|
| TEST_F(ModelTypeWorkerTest, EncryptionBlocksCommits) {
|
| NormalInitialize();
|
| @@ -737,12 +774,12 @@ TEST_F(ModelTypeWorkerTest, EncryptionBlocksCommits) {
|
|
|
| // We know encryption is in use on this account, but don't have the necessary
|
| // encryption keys. The worker should refuse to commit.
|
| - NewForeignEncryptionKey();
|
| + AddPendingKey();
|
| EXPECT_FALSE(WillCommit());
|
|
|
| // Once the cryptographer is returned to a normal state, we should be able to
|
| // commit again.
|
| - UpdateLocalCryptographer();
|
| + DecryptPendingKey();
|
| EXPECT_TRUE(WillCommit());
|
|
|
| // Verify the committed entity was properly encrypted.
|
| @@ -764,8 +801,8 @@ TEST_F(ModelTypeWorkerTest, ReceiveDecryptableEntities) {
|
| NormalInitialize();
|
|
|
| // Create a new Nigori and allow the cryptographer to decrypt it.
|
| - NewForeignEncryptionKey();
|
| - UpdateLocalCryptographer();
|
| + AddPendingKey();
|
| + DecryptPendingKey();
|
|
|
| // First, receive an unencrypted entry.
|
| TriggerUpdateFromServer(10, kTag1, kValue1);
|
| @@ -794,8 +831,8 @@ TEST_F(ModelTypeWorkerTest, ReceiveDecryptableEntities) {
|
| // Test initializing a CommitQueue with a cryptographer at startup.
|
| TEST_F(ModelTypeWorkerTest, InitializeWithCryptographer) {
|
| // Set up some encryption state.
|
| - NewForeignEncryptionKey();
|
| - UpdateLocalCryptographer();
|
| + AddPendingKey();
|
| + DecryptPendingKey();
|
|
|
| // Then initialize.
|
| NormalInitialize();
|
| @@ -808,45 +845,94 @@ TEST_F(ModelTypeWorkerTest, InitializeWithCryptographer) {
|
| processor()->GetNthUpdateState(0).encryption_key_name());
|
| }
|
|
|
| +// Test initialzing with a cryptographer that is not ready.
|
| +TEST_F(ModelTypeWorkerTest, InitializeWithPendingCryptographer) {
|
| + // Only add a pending key, cryptographer will not be ready.
|
| + AddPendingKey();
|
| +
|
| + // Then initialize.
|
| + NormalInitialize();
|
| +
|
| + // Shouldn't be informed of the EKN, since there's a pending key.
|
| + ASSERT_EQ(0U, processor()->GetNumUpdateResponses());
|
| +
|
| + // Although the cryptographer is ready, it should wait until being told to
|
| + // update the processor.
|
| + DecryptPendingKey();
|
| + ASSERT_EQ(0U, processor()->GetNumUpdateResponses());
|
| +
|
| + ApplyUpdates();
|
| + ASSERT_EQ(1U, processor()->GetNumUpdateResponses());
|
| + EXPECT_EQ(GetLocalCryptographerKeyName(),
|
| + processor()->GetNthUpdateState(0).encryption_key_name());
|
| +}
|
| +
|
| +// Test initializing with a cryptographer on first startup.
|
| +TEST_F(ModelTypeWorkerTest, FirstInitializeWithCryptographer) {
|
| + // Set up a Cryptographer that's good to go.
|
| + AddPendingKey();
|
| + DecryptPendingKey();
|
| +
|
| + // Initialize with initial sync done to false.
|
| + FirstInitialize();
|
| +
|
| + // Shouldn't be informed of the EKN, since normal activation will drive this.
|
| + ASSERT_EQ(0U, processor()->GetNumUpdateResponses());
|
| +
|
| + // Now perform first sync and make sure the EKN makes it.
|
| + TriggerTypeRootUpdateFromServer();
|
| + ASSERT_EQ(1U, processor()->GetNumUpdateResponses());
|
| + EXPECT_EQ(GetLocalCryptographerKeyName(),
|
| + processor()->GetNthUpdateState(0).encryption_key_name());
|
| +}
|
| +
|
| // Receive updates that are initially undecryptable, then ensure they get
|
| -// delivered to the model thread when decryption becomes possible.
|
| +// delivered to the model thread upon ApplyUpdates() after decryption becomes
|
| +// possible.
|
| TEST_F(ModelTypeWorkerTest, ReceiveUndecryptableEntries) {
|
| NormalInitialize();
|
|
|
| // Receive a new foreign encryption key that we can't decrypt.
|
| - NewForeignEncryptionKey();
|
| + AddPendingKey();
|
|
|
| // Receive an encrypted with that new key, which we can't access.
|
| SetUpdateEncryptionFilter(1);
|
| TriggerUpdateFromServer(10, kTag1, kValue1);
|
|
|
| // At this point, the cryptographer does not have access to the key, so the
|
| - // updates will be undecryptable. They'll be transfered to the model thread
|
| - // for safe-keeping as pending updates.
|
| - ASSERT_EQ(1U, processor()->GetNumUpdateResponses());
|
| - UpdateResponseDataList updates_list = processor()->GetNthUpdateResponse(0);
|
| - EXPECT_EQ(0U, updates_list.size());
|
| + // updates will be undecryptable. This will block all updates.
|
| + ASSERT_EQ(0U, processor()->GetNumUpdateResponses());
|
|
|
| - // The update will be delivered as soon as decryption becomes possible.
|
| - UpdateLocalCryptographer();
|
| + // The update can be delivered once the cryptographer is ready, but it'll
|
| + // still wait for the apply call.
|
| + DecryptPendingKey();
|
| + ASSERT_EQ(0U, processor()->GetNumUpdateResponses());
|
| +
|
| + // ApplyUpdates() will cause everything to finally be delivered.
|
| + ApplyUpdates();
|
| ASSERT_TRUE(processor()->HasUpdateResponse(kHash1));
|
| UpdateResponseData update = processor()->GetUpdateResponse(kHash1);
|
| EXPECT_EQ(kTag1, update.entity->specifics.preference().name());
|
| EXPECT_EQ(kValue1, update.entity->specifics.preference().value());
|
| - EXPECT_FALSE(update.encryption_key_name.empty());
|
| + EXPECT_EQ(GetLocalCryptographerKeyName(), update.encryption_key_name);
|
| }
|
|
|
| // Ensure that even encrypted updates can cause conflicts.
|
| TEST_F(ModelTypeWorkerTest, EncryptedUpdateOverridesPendingCommit) {
|
| NormalInitialize();
|
|
|
| - // Prepeare to commit an item.
|
| + // Prepare to commit an item.
|
| CommitRequest(kTag1, kValue1);
|
| EXPECT_TRUE(WillCommit());
|
|
|
| - // Receive an encrypted update for that item.
|
| + // Receive an encrypted update for that item and the new key. The pending key
|
| + // needs to arrive during (or before) the sync cycle that contains the
|
| + // encrypted entity. This means before ApplyUpdates is called.
|
| SetUpdateEncryptionFilter(1);
|
| - TriggerUpdateFromServer(10, kTag1, kValue1);
|
| + TriggerPartialUpdateFromServer(10, kTag1, kValue1);
|
| + AddPendingKey();
|
| + DecryptPendingKey();
|
| + ApplyUpdates();
|
|
|
| // The pending commit state should be cleared.
|
| EXPECT_FALSE(WillCommit());
|
| @@ -854,7 +940,7 @@ TEST_F(ModelTypeWorkerTest, EncryptedUpdateOverridesPendingCommit) {
|
| // The encrypted update will be delivered to the model thread.
|
| ASSERT_EQ(1U, processor()->GetNumUpdateResponses());
|
| UpdateResponseDataList updates_list = processor()->GetNthUpdateResponse(0);
|
| - EXPECT_EQ(0U, updates_list.size());
|
| + EXPECT_EQ(1U, updates_list.size());
|
| }
|
|
|
| // Commit twice, both times with the kUncommittedVersion base version. Then
|
| @@ -907,8 +993,8 @@ TEST_F(ModelTypeWorkerTest, RestorePendingEntries) {
|
| ASSERT_FALSE(processor()->HasUpdateResponse(kHash1));
|
|
|
| // Update the cryptographer so it can decrypt that update.
|
| - NewForeignEncryptionKey();
|
| - UpdateLocalCryptographer();
|
| + AddPendingKey();
|
| + DecryptPendingKey();
|
|
|
| // Verify the item gets decrypted and sent back to the model thread.
|
| // TODO(maxbogue): crbug.com/529498: Uncomment when pending updates are
|
| @@ -921,8 +1007,8 @@ TEST_F(ModelTypeWorkerTest, RestorePendingEntries) {
|
| // immediately after the CommitQueue is constructed.
|
| TEST_F(ModelTypeWorkerTest, RestoreApplicableEntries) {
|
| // Update the cryptographer so it can decrypt that update.
|
| - NewForeignEncryptionKey();
|
| - UpdateLocalCryptographer();
|
| + AddPendingKey();
|
| + DecryptPendingKey();
|
|
|
| // Create a fake pending update.
|
| EntityData entity;
|
| @@ -958,20 +1044,24 @@ TEST_F(ModelTypeWorkerTest, RestoreApplicableEntries) {
|
| TEST_F(ModelTypeWorkerTest, CommitBlockedByPending) {
|
| NormalInitialize();
|
|
|
| - // Prepeare to commit an item.
|
| + // Prepare to commit an item.
|
| CommitRequest(kTag1, kValue1);
|
| EXPECT_TRUE(WillCommit());
|
|
|
| - // Receive an encrypted update for that item.
|
| + // Receive an encrypted update for that item and the new key. The pending key
|
| + // needs to arrive during (or before) the sync cycle that contains the
|
| + // encrypted entity. This means before ApplyUpdates is called.
|
| SetUpdateEncryptionFilter(1);
|
| - TriggerUpdateFromServer(10, kTag1, kValue1);
|
| + TriggerPartialUpdateFromServer(10, kTag1, kValue1);
|
| + AddPendingKey();
|
| + ApplyUpdates();
|
| +
|
| + // Update should not have made it to the processor.
|
| + ASSERT_FALSE(processor()->HasUpdateResponse(kHash1));
|
|
|
| // The pending commit state should be cleared.
|
| EXPECT_FALSE(WillCommit());
|
|
|
| - // The pending update will be delivered to the model thread.
|
| - processor()->HasUpdateResponse(kHash1);
|
| -
|
| // Pretend the update arrived too late to prevent another commit request.
|
| CommitRequest(kTag1, kValue2);
|
|
|
| @@ -982,8 +1072,8 @@ TEST_F(ModelTypeWorkerTest, CommitBlockedByPending) {
|
| TEST_F(ModelTypeWorkerTest, ReceiveCorruptEncryption) {
|
| // Initialize the worker with basic encryption state.
|
| NormalInitialize();
|
| - NewForeignEncryptionKey();
|
| - UpdateLocalCryptographer();
|
| + AddPendingKey();
|
| + DecryptPendingKey();
|
|
|
| // Manually create an update.
|
| sync_pb::SyncEntity entity;
|
|
|