Index: sync/engine/model_type_worker_unittest.cc |
diff --git a/sync/engine/model_type_worker_unittest.cc b/sync/engine/model_type_worker_unittest.cc |
deleted file mode 100644 |
index 367022975bd879ca931b970461b3367a2d235841..0000000000000000000000000000000000000000 |
--- a/sync/engine/model_type_worker_unittest.cc |
+++ /dev/null |
@@ -1,1222 +0,0 @@ |
-// Copyright 2014 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "sync/engine/model_type_worker.h" |
- |
-#include <stddef.h> |
-#include <stdint.h> |
- |
-#include <utility> |
- |
-#include "base/bind.h" |
-#include "base/memory/ptr_util.h" |
-#include "base/strings/stringprintf.h" |
-#include "sync/engine/commit_contribution.h" |
-#include "sync/internal_api/public/base/model_type.h" |
-#include "sync/internal_api/public/model_type_processor.h" |
-#include "sync/internal_api/public/non_blocking_sync_common.h" |
-#include "sync/protocol/data_type_state.pb.h" |
-#include "sync/protocol/sync.pb.h" |
-#include "sync/sessions/status_controller.h" |
-#include "sync/syncable/syncable_util.h" |
-#include "sync/test/engine/mock_model_type_processor.h" |
-#include "sync/test/engine/mock_nudge_handler.h" |
-#include "sync/test/engine/single_type_mock_server.h" |
-#include "sync/test/fake_encryptor.h" |
-#include "testing/gtest/include/gtest/gtest.h" |
- |
-static const syncer::ModelType kModelType = syncer::PREFERENCES; |
- |
-// Special constant value taken from cryptographer.cc. |
-const char kNigoriKeyName[] = "nigori-key"; |
- |
-namespace syncer_v2 { |
- |
-using syncer::Cryptographer; |
-using syncer::CommitContribution; |
-using syncer::KeyParams; |
-using syncer::Nigori; |
-using syncer::sessions::StatusController; |
- |
-// Tests the ModelTypeWorker. |
-// |
-// This class passes messages between the model thread and sync server. |
-// As such, its code is subject to lots of different race conditions. This |
-// test harness lets us exhaustively test all possible races. We try to |
-// focus on just a few interesting cases. |
-// |
-// Inputs: |
-// - Initial data type state from the model thread. |
-// - Commit requests from the model thread. |
-// - Update responses from the server. |
-// - Commit responses from the server. |
-// - The cryptographer, if encryption is enabled. |
-// |
-// Outputs: |
-// - Commit requests to the server. |
-// - Commit responses to the model thread. |
-// - Update responses to the model thread. |
-// - Nudges to the sync scheduler. |
-// |
-// We use the MockModelTypeProcessor to stub out all communication |
-// with the model thread. That interface is synchronous, which makes it |
-// much easier to test races. |
-// |
-// The interface with the server is built around "pulling" data from this |
-// class, so we don't have to mock out any of it. We wrap it with some |
-// convenience functions so we can emulate server behavior. |
-class ModelTypeWorkerTest : public ::testing::Test { |
- public: |
- ModelTypeWorkerTest(); |
- ~ModelTypeWorkerTest() override; |
- |
- // One of these Initialize functions should be called at the beginning of |
- // each test. |
- |
- // Initializes with no data type state. We will be unable to perform any |
- // significant server action until we receive an update response that |
- // contains the type root node for this type. |
- void FirstInitialize(); |
- |
- // Initializes with some existing data type state. Allows us to start |
- // committing items right away. |
- void NormalInitialize(); |
- |
- // Initialize with some saved pending updates from the model thread. |
- void InitializeWithPendingUpdates( |
- const UpdateResponseDataList& initial_pending_updates); |
- |
- // Initialize with a custom initial DataTypeState and pending updates. |
- void InitializeWithState(const sync_pb::DataTypeState& state, |
- const UpdateResponseDataList& pending_updates); |
- |
- ModelTypeWorker* worker() const { return worker_.get(); } |
- |
- // Introduce a new key that the local cryptographer can't decrypt. |
- void NewForeignEncryptionKey(); |
- |
- // Update the local cryptographer with all relevant keys. |
- void UpdateLocalCryptographer(); |
- |
- // Use the Nth nigori instance to encrypt incoming updates. |
- // The default value, zero, indicates no encryption. |
- void SetUpdateEncryptionFilter(int n); |
- |
- // Modifications on the model thread that get sent to the worker under test. |
- void CommitRequest(const std::string& tag, const std::string& value); |
- void DeleteRequest(const std::string& tag); |
- |
- // Pretends to receive update messages from the server. |
- void TriggerTypeRootUpdateFromServer(); |
- void TriggerPartialUpdateFromServer(int64_t version_offset, |
- const std::string& tag, |
- const std::string& value); |
- void TriggerUpdateFromServer(int64_t version_offset, |
- const std::string& tag, |
- const std::string& value); |
- void TriggerTombstoneFromServer(int64_t version_offset, |
- const std::string& tag); |
- |
- // 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& update_list); |
- |
- // By default, this harness behaves as if all tasks posted to the model |
- // thread are executed immediately. However, this is not necessarily true. |
- // The model's TaskRunner has a queue, and the tasks we post to it could |
- // linger there for a while. In the meantime, the model thread could |
- // continue posting tasks to the worker based on its stale state. |
- // |
- // If you want to test those race cases, then these functions are for you. |
- void SetModelThreadIsSynchronous(bool is_synchronous); |
- void PumpModelThread(); |
- |
- // Returns true if the |worker_| is ready to commit something. |
- bool WillCommit(); |
- |
- // Pretend to successfully commit all outstanding unsynced items. |
- // It is safe to call this only if WillCommit() returns true. |
- void DoSuccessfulCommit(); |
- |
- // Callback when processor got disconnected with sync. |
- void DisconnectProcessor(); |
- |
- bool IsProcessorDisconnected(); |
- void ResetWorker(); |
- |
- // Read commit messages the worker_ sent to the emulated server. |
- size_t GetNumCommitMessagesOnServer() const; |
- sync_pb::ClientToServerMessage GetNthCommitMessageOnServer(size_t n) const; |
- |
- // Read the latest version of sync entities committed to the emulated server. |
- bool HasCommitEntityOnServer(const std::string& tag) const; |
- sync_pb::SyncEntity GetLatestCommitEntityOnServer( |
- const std::string& tag) const; |
- |
- // Read the latest update messages received on the model thread. |
- // Note that if the model thread is in non-blocking mode, this data will not |
- // be updated until the response is actually processed by the model thread. |
- size_t GetNumModelThreadUpdateResponses() const; |
- UpdateResponseDataList GetNthModelThreadUpdateResponse(size_t n) const; |
- sync_pb::DataTypeState GetNthModelThreadUpdateState(size_t n) const; |
- |
- // Reads the latest update response datas on the model thread. |
- // Note that if the model thread is in non-blocking mode, this data will not |
- // be updated until the response is actually processed by the model thread. |
- bool HasUpdateResponseOnModelThread(const std::string& tag) const; |
- UpdateResponseData GetUpdateResponseOnModelThread( |
- const std::string& tag) const; |
- |
- // Read the latest commit messages received on the model thread. |
- // Note that if the model thread is in non-blocking mode, this data will not |
- // be updated until the response is actually processed by the model thread. |
- size_t GetNumModelThreadCommitResponses() const; |
- CommitResponseDataList GetNthModelThreadCommitResponse(size_t n) const; |
- sync_pb::DataTypeState GetNthModelThreadCommitState(size_t n) const; |
- |
- // Reads the latest commit response datas on the model thread. |
- // Note that if the model thread is in non-blocking mode, this data will not |
- // be updated until the response is actually processed by the model thread. |
- bool HasCommitResponseOnModelThread(const std::string& tag) const; |
- CommitResponseData GetCommitResponseOnModelThread( |
- const std::string& tag) const; |
- |
- // Returns the number of commit nudges sent to the mock nudge handler. |
- int GetNumCommitNudges() const; |
- |
- // Returns the number of initial sync nudges sent to the mock nudge handler. |
- int GetNumInitialDownloadNudges() const; |
- |
- // 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(). |
- std::string GetLocalCryptographerKeyName() const; |
- |
- // Helpers for building various messages and structures. |
- static std::string GenerateTagHash(const std::string& tag); |
- static sync_pb::EntitySpecifics GenerateSpecifics(const std::string& tag, |
- const std::string& value); |
- |
- // Returns a set of KeyParams for the cryptographer. Each input 'n' value |
- // results in a different set of parameters. |
- static KeyParams GetNthKeyParams(int n); |
- |
- // Returns the name for the given Nigori. |
- // |
- // Uses some 'white-box' knowledge to mimic the names that a real sync client |
- // would generate. It's probably not necessary to do so, but it can't hurt. |
- static std::string GetNigoriName(const Nigori& nigori); |
- |
- // Modifies the input/output parameter |specifics| by encrypting it with |
- // a Nigori intialized with the specified KeyParams. |
- static void EncryptUpdate(const KeyParams& params, |
- sync_pb::EntitySpecifics* specifics); |
- |
- private: |
- // An encryptor for our cryptographer. |
- syncer::FakeEncryptor fake_encryptor_; |
- |
- // The cryptographer itself. NULL if we're not encrypting the type. |
- std::unique_ptr<Cryptographer> cryptographer_; |
- |
- // The number of the most recent foreign encryption key known to our |
- // cryptographer. Note that not all of these will be decryptable. |
- int foreign_encryption_key_index_; |
- |
- // The number of the encryption key used to encrypt incoming updates. A zero |
- // value implies no encryption. |
- int update_encryption_filter_index_; |
- |
- // The ModelTypeWorker being tested. |
- std::unique_ptr<ModelTypeWorker> worker_; |
- |
- // Non-owned, possibly NULL pointer. This object belongs to the |
- // ModelTypeWorker under test. |
- MockModelTypeProcessor* mock_type_processor_; |
- |
- // A mock that emulates enough of the sync server that it can be used |
- // a single UpdateHandler and CommitContributor pair. In this test |
- // harness, the |worker_| is both of them. |
- syncer::SingleTypeMockServer mock_server_; |
- |
- // A mock to track the number of times the CommitQueue requests to |
- // sync. |
- syncer::MockNudgeHandler mock_nudge_handler_; |
- |
- bool is_processor_disconnected_; |
-}; |
- |
-ModelTypeWorkerTest::ModelTypeWorkerTest() |
- : foreign_encryption_key_index_(0), |
- update_encryption_filter_index_(0), |
- mock_type_processor_(NULL), |
- mock_server_(kModelType), |
- is_processor_disconnected_(false) {} |
- |
-ModelTypeWorkerTest::~ModelTypeWorkerTest() {} |
- |
-void ModelTypeWorkerTest::FirstInitialize() { |
- sync_pb::DataTypeState initial_state; |
- initial_state.mutable_progress_marker()->set_data_type_id( |
- GetSpecificsFieldNumberFromModelType(kModelType)); |
- |
- InitializeWithState(initial_state, UpdateResponseDataList()); |
-} |
- |
-void ModelTypeWorkerTest::NormalInitialize() { |
- InitializeWithPendingUpdates(UpdateResponseDataList()); |
-} |
- |
-void ModelTypeWorkerTest::InitializeWithPendingUpdates( |
- const UpdateResponseDataList& initial_pending_updates) { |
- sync_pb::DataTypeState initial_state; |
- initial_state.mutable_progress_marker()->set_data_type_id( |
- GetSpecificsFieldNumberFromModelType(kModelType)); |
- initial_state.mutable_progress_marker()->set_token( |
- "some_saved_progress_token"); |
- |
- initial_state.set_initial_sync_done(true); |
- |
- InitializeWithState(initial_state, initial_pending_updates); |
- |
- mock_nudge_handler_.ClearCounters(); |
-} |
- |
-void ModelTypeWorkerTest::InitializeWithState( |
- const sync_pb::DataTypeState& state, |
- const UpdateResponseDataList& initial_pending_updates) { |
- DCHECK(!worker_); |
- |
- // We don't get to own this object. The |worker_| keeps a unique_ptr to it. |
- mock_type_processor_ = new MockModelTypeProcessor(); |
- mock_type_processor_->SetDisconnectCallback(base::Bind( |
- &ModelTypeWorkerTest::DisconnectProcessor, base::Unretained(this))); |
- std::unique_ptr<ModelTypeProcessor> proxy(mock_type_processor_); |
- |
- std::unique_ptr<Cryptographer> cryptographer_copy; |
- if (cryptographer_) { |
- cryptographer_copy.reset(new Cryptographer(*cryptographer_)); |
- } |
- |
- // TODO(maxbogue): crbug.com/529498: Inject pending updates somehow. |
- worker_.reset(new ModelTypeWorker(kModelType, state, |
- std::move(cryptographer_copy), |
- &mock_nudge_handler_, std::move(proxy))); |
-} |
- |
-void ModelTypeWorkerTest::NewForeignEncryptionKey() { |
- if (!cryptographer_) { |
- cryptographer_.reset(new Cryptographer(&fake_encryptor_)); |
- } |
- |
- foreign_encryption_key_index_++; |
- |
- sync_pb::NigoriKeyBag bag; |
- |
- for (int i = 0; i <= foreign_encryption_key_index_; ++i) { |
- Nigori nigori; |
- KeyParams params = GetNthKeyParams(i); |
- nigori.InitByDerivation(params.hostname, params.username, params.password); |
- |
- sync_pb::NigoriKey* key = bag.add_key(); |
- |
- key->set_name(GetNigoriName(nigori)); |
- nigori.ExportKeys(key->mutable_user_key(), key->mutable_encryption_key(), |
- key->mutable_mac_key()); |
- } |
- |
- // Re-create the last nigori from that loop. |
- Nigori last_nigori; |
- KeyParams params = GetNthKeyParams(foreign_encryption_key_index_); |
- last_nigori.InitByDerivation(params.hostname, params.username, |
- params.password); |
- |
- // Serialize and encrypt the bag with the last nigori. |
- std::string serialized_bag; |
- bag.SerializeToString(&serialized_bag); |
- |
- sync_pb::EncryptedData encrypted; |
- encrypted.set_key_name(GetNigoriName(last_nigori)); |
- last_nigori.Encrypt(serialized_bag, encrypted.mutable_blob()); |
- |
- // Update the cryptographer with new pending keys. |
- cryptographer_->SetPendingKeys(encrypted); |
- |
- // Update the worker with the latest cryptographer. |
- if (worker_) { |
- worker_->UpdateCryptographer( |
- base::WrapUnique(new Cryptographer(*cryptographer_))); |
- } |
-} |
- |
-void ModelTypeWorkerTest::UpdateLocalCryptographer() { |
- if (!cryptographer_) { |
- cryptographer_.reset(new Cryptographer(&fake_encryptor_)); |
- } |
- |
- KeyParams params = GetNthKeyParams(foreign_encryption_key_index_); |
- bool success = cryptographer_->DecryptPendingKeys(params); |
- DCHECK(success); |
- |
- // Update the worker with the latest cryptographer. |
- if (worker_) { |
- worker_->UpdateCryptographer( |
- base::WrapUnique(new Cryptographer(*cryptographer_))); |
- } |
-} |
- |
-void ModelTypeWorkerTest::SetUpdateEncryptionFilter(int n) { |
- update_encryption_filter_index_ = n; |
-} |
- |
-void ModelTypeWorkerTest::CommitRequest(const std::string& name, |
- const std::string& value) { |
- const std::string tag_hash = GenerateTagHash(name); |
- CommitRequestData data = mock_type_processor_->CommitRequest( |
- tag_hash, GenerateSpecifics(name, value)); |
- CommitRequestDataList list; |
- list.push_back(data); |
- worker_->EnqueueForCommit(list); |
-} |
- |
-void ModelTypeWorkerTest::DeleteRequest(const std::string& tag) { |
- const std::string tag_hash = GenerateTagHash(tag); |
- CommitRequestData data = mock_type_processor_->DeleteRequest(tag_hash); |
- CommitRequestDataList list; |
- list.push_back(data); |
- worker_->EnqueueForCommit(list); |
-} |
- |
-void ModelTypeWorkerTest::TriggerTypeRootUpdateFromServer() { |
- sync_pb::SyncEntity entity = mock_server_.TypeRootUpdate(); |
- SyncEntityList entity_list; |
- entity_list.push_back(&entity); |
- |
- StatusController dummy_status; |
- |
- worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(), |
- mock_server_.GetContext(), entity_list, |
- &dummy_status); |
- worker_->PassiveApplyUpdates(&dummy_status); |
-} |
- |
-void ModelTypeWorkerTest::TriggerPartialUpdateFromServer( |
- int64_t version_offset, |
- const std::string& tag, |
- const std::string& value) { |
- sync_pb::SyncEntity entity = mock_server_.UpdateFromServer( |
- version_offset, GenerateTagHash(tag), GenerateSpecifics(tag, value)); |
- |
- if (update_encryption_filter_index_ != 0) { |
- EncryptUpdate(GetNthKeyParams(update_encryption_filter_index_), |
- entity.mutable_specifics()); |
- } |
- |
- SyncEntityList entity_list; |
- entity_list.push_back(&entity); |
- |
- StatusController dummy_status; |
- |
- worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(), |
- mock_server_.GetContext(), entity_list, |
- &dummy_status); |
-} |
- |
-void ModelTypeWorkerTest::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); |
-} |
- |
-void ModelTypeWorkerTest::DeliverRawUpdates(const SyncEntityList& list) { |
- StatusController dummy_status; |
- worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(), |
- mock_server_.GetContext(), list, |
- &dummy_status); |
- worker_->ApplyUpdates(&dummy_status); |
-} |
- |
-void ModelTypeWorkerTest::TriggerTombstoneFromServer(int64_t version_offset, |
- const std::string& tag) { |
- sync_pb::SyncEntity entity = |
- mock_server_.TombstoneFromServer(version_offset, GenerateTagHash(tag)); |
- |
- if (update_encryption_filter_index_ != 0) { |
- EncryptUpdate(GetNthKeyParams(update_encryption_filter_index_), |
- entity.mutable_specifics()); |
- } |
- |
- SyncEntityList entity_list; |
- entity_list.push_back(&entity); |
- |
- StatusController dummy_status; |
- |
- worker_->ProcessGetUpdatesResponse(mock_server_.GetProgress(), |
- mock_server_.GetContext(), entity_list, |
- &dummy_status); |
- worker_->ApplyUpdates(&dummy_status); |
-} |
- |
-void ModelTypeWorkerTest::SetModelThreadIsSynchronous(bool is_synchronous) { |
- mock_type_processor_->SetSynchronousExecution(is_synchronous); |
-} |
- |
-void ModelTypeWorkerTest::PumpModelThread() { |
- mock_type_processor_->RunQueuedTasks(); |
-} |
- |
-bool ModelTypeWorkerTest::WillCommit() { |
- std::unique_ptr<CommitContribution> contribution( |
- worker_->GetContribution(INT_MAX)); |
- |
- if (contribution) { |
- contribution->CleanUp(); // Gracefully abort the commit. |
- return true; |
- } else { |
- return false; |
- } |
-} |
- |
-// Conveniently, this is all one big synchronous operation. The sync thread |
-// remains blocked while the commit is in progress, so we don't need to worry |
-// about other tasks being run between the time when the commit request is |
-// issued and the time when the commit response is received. |
-void ModelTypeWorkerTest::DoSuccessfulCommit() { |
- DCHECK(WillCommit()); |
- std::unique_ptr<CommitContribution> contribution( |
- worker_->GetContribution(INT_MAX)); |
- |
- sync_pb::ClientToServerMessage message; |
- contribution->AddToCommitMessage(&message); |
- |
- sync_pb::ClientToServerResponse response = |
- mock_server_.DoSuccessfulCommit(message); |
- |
- StatusController dummy_status; |
- contribution->ProcessCommitResponse(response, &dummy_status); |
- contribution->CleanUp(); |
-} |
- |
-void ModelTypeWorkerTest::DisconnectProcessor() { |
- DCHECK(!is_processor_disconnected_); |
- is_processor_disconnected_ = true; |
-} |
- |
-bool ModelTypeWorkerTest::IsProcessorDisconnected() { |
- return is_processor_disconnected_; |
-} |
- |
-void ModelTypeWorkerTest::ResetWorker() { |
- worker_.reset(); |
-} |
- |
-size_t ModelTypeWorkerTest::GetNumCommitMessagesOnServer() const { |
- return mock_server_.GetNumCommitMessages(); |
-} |
- |
-sync_pb::ClientToServerMessage ModelTypeWorkerTest::GetNthCommitMessageOnServer( |
- size_t n) const { |
- DCHECK_LT(n, GetNumCommitMessagesOnServer()); |
- return mock_server_.GetNthCommitMessage(n); |
-} |
- |
-bool ModelTypeWorkerTest::HasCommitEntityOnServer( |
- const std::string& tag) const { |
- const std::string tag_hash = GenerateTagHash(tag); |
- return mock_server_.HasCommitEntity(tag_hash); |
-} |
- |
-sync_pb::SyncEntity ModelTypeWorkerTest::GetLatestCommitEntityOnServer( |
- const std::string& tag) const { |
- DCHECK(HasCommitEntityOnServer(tag)); |
- const std::string tag_hash = GenerateTagHash(tag); |
- return mock_server_.GetLastCommittedEntity(tag_hash); |
-} |
- |
-size_t ModelTypeWorkerTest::GetNumModelThreadUpdateResponses() const { |
- return mock_type_processor_->GetNumUpdateResponses(); |
-} |
- |
-UpdateResponseDataList ModelTypeWorkerTest::GetNthModelThreadUpdateResponse( |
- size_t n) const { |
- DCHECK_LT(n, GetNumModelThreadUpdateResponses()); |
- return mock_type_processor_->GetNthUpdateResponse(n); |
-} |
- |
-sync_pb::DataTypeState ModelTypeWorkerTest::GetNthModelThreadUpdateState( |
- size_t n) const { |
- DCHECK_LT(n, GetNumModelThreadUpdateResponses()); |
- return mock_type_processor_->GetNthTypeStateReceivedInUpdateResponse(n); |
-} |
- |
-bool ModelTypeWorkerTest::HasUpdateResponseOnModelThread( |
- const std::string& tag) const { |
- const std::string tag_hash = GenerateTagHash(tag); |
- return mock_type_processor_->HasUpdateResponse(tag_hash); |
-} |
- |
-UpdateResponseData ModelTypeWorkerTest::GetUpdateResponseOnModelThread( |
- const std::string& tag) const { |
- const std::string tag_hash = GenerateTagHash(tag); |
- return mock_type_processor_->GetUpdateResponse(tag_hash); |
-} |
- |
-size_t ModelTypeWorkerTest::GetNumModelThreadCommitResponses() const { |
- return mock_type_processor_->GetNumCommitResponses(); |
-} |
- |
-CommitResponseDataList ModelTypeWorkerTest::GetNthModelThreadCommitResponse( |
- size_t n) const { |
- DCHECK_LT(n, GetNumModelThreadCommitResponses()); |
- return mock_type_processor_->GetNthCommitResponse(n); |
-} |
- |
-sync_pb::DataTypeState ModelTypeWorkerTest::GetNthModelThreadCommitState( |
- size_t n) const { |
- DCHECK_LT(n, GetNumModelThreadCommitResponses()); |
- return mock_type_processor_->GetNthTypeStateReceivedInCommitResponse(n); |
-} |
- |
-bool ModelTypeWorkerTest::HasCommitResponseOnModelThread( |
- const std::string& tag) const { |
- const std::string tag_hash = GenerateTagHash(tag); |
- return mock_type_processor_->HasCommitResponse(tag_hash); |
-} |
- |
-CommitResponseData ModelTypeWorkerTest::GetCommitResponseOnModelThread( |
- const std::string& tag) const { |
- DCHECK(HasCommitResponseOnModelThread(tag)); |
- const std::string tag_hash = GenerateTagHash(tag); |
- return mock_type_processor_->GetCommitResponse(tag_hash); |
-} |
- |
-int ModelTypeWorkerTest::GetNumCommitNudges() const { |
- return mock_nudge_handler_.GetNumCommitNudges(); |
-} |
- |
-int ModelTypeWorkerTest::GetNumInitialDownloadNudges() const { |
- return mock_nudge_handler_.GetNumInitialDownloadNudges(); |
-} |
- |
-std::string ModelTypeWorkerTest::GetLocalCryptographerKeyName() const { |
- if (!cryptographer_) { |
- return std::string(); |
- } |
- |
- return cryptographer_->GetDefaultNigoriKeyName(); |
-} |
- |
-// static. |
-std::string ModelTypeWorkerTest::GenerateTagHash(const std::string& tag) { |
- const std::string& client_tag_hash = |
- syncer::syncable::GenerateSyncableHash(kModelType, tag); |
- return client_tag_hash; |
-} |
- |
-// static. |
-sync_pb::EntitySpecifics ModelTypeWorkerTest::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; |
-} |
- |
-// static. |
-std::string ModelTypeWorkerTest::GetNigoriName(const Nigori& nigori) { |
- std::string name; |
- if (!nigori.Permute(Nigori::Password, kNigoriKeyName, &name)) { |
- NOTREACHED(); |
- return std::string(); |
- } |
- |
- return name; |
-} |
- |
-// static. |
-KeyParams ModelTypeWorkerTest::GetNthKeyParams(int n) { |
- KeyParams params; |
- params.hostname = std::string("localhost"); |
- params.username = std::string("userX"); |
- params.password = base::StringPrintf("pw%02d", n); |
- return params; |
-} |
- |
-// static. |
-void ModelTypeWorkerTest::EncryptUpdate(const KeyParams& params, |
- sync_pb::EntitySpecifics* specifics) { |
- Nigori nigori; |
- nigori.InitByDerivation(params.hostname, params.username, params.password); |
- |
- sync_pb::EntitySpecifics original_specifics = *specifics; |
- std::string plaintext; |
- original_specifics.SerializeToString(&plaintext); |
- |
- std::string encrypted; |
- nigori.Encrypt(plaintext, &encrypted); |
- |
- specifics->Clear(); |
- AddDefaultFieldValue(kModelType, specifics); |
- specifics->mutable_encrypted()->set_key_name(GetNigoriName(nigori)); |
- specifics->mutable_encrypted()->set_blob(encrypted); |
-} |
- |
-// Requests a commit and verifies the messages sent to the client and server as |
-// a result. |
-// |
-// This test performs sanity checks on most of the fields in these messages. |
-// For the most part this is checking that the test code behaves as expected |
-// and the |worker_| doesn't mess up its simple task of moving around these |
-// values. It makes sense to have one or two tests that are this thorough, but |
-// we shouldn't be this verbose in all tests. |
-TEST_F(ModelTypeWorkerTest, SimpleCommit) { |
- NormalInitialize(); |
- |
- EXPECT_FALSE(WillCommit()); |
- EXPECT_EQ(0U, GetNumCommitMessagesOnServer()); |
- EXPECT_EQ(0U, GetNumModelThreadCommitResponses()); |
- |
- CommitRequest("tag1", "value1"); |
- |
- EXPECT_EQ(1, GetNumCommitNudges()); |
- |
- ASSERT_TRUE(WillCommit()); |
- DoSuccessfulCommit(); |
- |
- const std::string& client_tag_hash = GenerateTagHash("tag1"); |
- |
- // Exhaustively verify the SyncEntity sent in the commit message. |
- ASSERT_EQ(1U, GetNumCommitMessagesOnServer()); |
- EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size()); |
- ASSERT_TRUE(HasCommitEntityOnServer("tag1")); |
- const sync_pb::SyncEntity& entity = GetLatestCommitEntityOnServer("tag1"); |
- EXPECT_FALSE(entity.id_string().empty()); |
- EXPECT_EQ(0, entity.version()); |
- EXPECT_NE(0, entity.mtime()); |
- EXPECT_NE(0, entity.ctime()); |
- EXPECT_FALSE(entity.name().empty()); |
- EXPECT_EQ(client_tag_hash, entity.client_defined_unique_tag()); |
- EXPECT_EQ("tag1", entity.specifics().preference().name()); |
- EXPECT_FALSE(entity.deleted()); |
- EXPECT_EQ("value1", entity.specifics().preference().value()); |
- |
- // Exhaustively verify the commit response returned to the model thread. |
- ASSERT_EQ(1U, GetNumModelThreadCommitResponses()); |
- EXPECT_EQ(1U, GetNthModelThreadCommitResponse(0).size()); |
- ASSERT_TRUE(HasCommitResponseOnModelThread("tag1")); |
- const CommitResponseData& commit_response = |
- GetCommitResponseOnModelThread("tag1"); |
- |
- // The ID changes in a commit response to initial commit. |
- EXPECT_FALSE(commit_response.id.empty()); |
- EXPECT_NE(entity.id_string(), commit_response.id); |
- |
- EXPECT_EQ(client_tag_hash, commit_response.client_tag_hash); |
- EXPECT_LT(0, commit_response.response_version); |
- EXPECT_LT(0, commit_response.sequence_number); |
- EXPECT_FALSE(commit_response.specifics_hash.empty()); |
-} |
- |
-TEST_F(ModelTypeWorkerTest, SimpleDelete) { |
- NormalInitialize(); |
- |
- // We can't delete an entity that was never committed. |
- // Step 1 is to create and commit a new entity. |
- CommitRequest("tag1", "value1"); |
- EXPECT_EQ(1, GetNumCommitNudges()); |
- ASSERT_TRUE(WillCommit()); |
- DoSuccessfulCommit(); |
- |
- ASSERT_TRUE(HasCommitResponseOnModelThread("tag1")); |
- const CommitResponseData& initial_commit_response = |
- GetCommitResponseOnModelThread("tag1"); |
- int64_t base_version = initial_commit_response.response_version; |
- |
- // Now that we have an entity, we can delete it. |
- DeleteRequest("tag1"); |
- ASSERT_TRUE(WillCommit()); |
- DoSuccessfulCommit(); |
- |
- // Verify the SyncEntity sent in the commit message. |
- ASSERT_EQ(2U, GetNumCommitMessagesOnServer()); |
- EXPECT_EQ(1, GetNthCommitMessageOnServer(1).commit().entries_size()); |
- ASSERT_TRUE(HasCommitEntityOnServer("tag1")); |
- const sync_pb::SyncEntity& entity = GetLatestCommitEntityOnServer("tag1"); |
- EXPECT_FALSE(entity.id_string().empty()); |
- EXPECT_EQ(GenerateTagHash("tag1"), entity.client_defined_unique_tag()); |
- EXPECT_EQ(base_version, entity.version()); |
- EXPECT_TRUE(entity.deleted()); |
- |
- // Deletions should contain enough specifics to identify the type. |
- EXPECT_TRUE(entity.has_specifics()); |
- EXPECT_EQ(kModelType, syncer::GetModelTypeFromSpecifics(entity.specifics())); |
- |
- // Verify the commit response returned to the model thread. |
- ASSERT_EQ(2U, GetNumModelThreadCommitResponses()); |
- EXPECT_EQ(1U, GetNthModelThreadCommitResponse(1).size()); |
- ASSERT_TRUE(HasCommitResponseOnModelThread("tag1")); |
- const CommitResponseData& commit_response = |
- GetCommitResponseOnModelThread("tag1"); |
- |
- EXPECT_EQ(entity.id_string(), commit_response.id); |
- EXPECT_EQ(entity.client_defined_unique_tag(), |
- commit_response.client_tag_hash); |
- EXPECT_EQ(entity.version(), commit_response.response_version); |
-} |
- |
-// The server doesn't like it when we try to delete an entity it's never heard |
-// of before. This test helps ensure we avoid that scenario. |
-TEST_F(ModelTypeWorkerTest, NoDeleteUncommitted) { |
- NormalInitialize(); |
- |
- // Request the commit of a new, never-before-seen item. |
- CommitRequest("tag1", "value1"); |
- EXPECT_TRUE(WillCommit()); |
- EXPECT_EQ(1, GetNumCommitNudges()); |
- |
- // Request a deletion of that item before we've had a chance to commit it. |
- DeleteRequest("tag1"); |
- EXPECT_FALSE(WillCommit()); |
- EXPECT_EQ(2, GetNumCommitNudges()); |
-} |
- |
-// Verifies the sending of an "initial sync done" signal. |
-TEST_F(ModelTypeWorkerTest, SendInitialSyncDone) { |
- FirstInitialize(); // Initialize with no saved sync state. |
- EXPECT_EQ(0U, GetNumModelThreadUpdateResponses()); |
- EXPECT_EQ(1, GetNumInitialDownloadNudges()); |
- |
- EXPECT_FALSE(worker()->IsInitialSyncEnded()); |
- |
- // Receive an update response that contains only the type root node. |
- TriggerTypeRootUpdateFromServer(); |
- |
- // One update triggered by ApplyUpdates, which the worker interprets to mean |
- // "initial sync done". This triggers a model thread update, too. |
- EXPECT_EQ(1U, GetNumModelThreadUpdateResponses()); |
- |
- // The update contains no entities. |
- EXPECT_EQ(0U, GetNthModelThreadUpdateResponse(0).size()); |
- |
- const sync_pb::DataTypeState& state = GetNthModelThreadUpdateState(0); |
- EXPECT_FALSE(state.progress_marker().token().empty()); |
- EXPECT_TRUE(state.initial_sync_done()); |
- EXPECT_TRUE(worker()->IsInitialSyncEnded()); |
-} |
- |
-// Commit two new entities in two separate commit messages. |
-TEST_F(ModelTypeWorkerTest, TwoNewItemsCommittedSeparately) { |
- NormalInitialize(); |
- |
- // Commit the first of two entities. |
- CommitRequest("tag1", "value1"); |
- EXPECT_EQ(1, GetNumCommitNudges()); |
- ASSERT_TRUE(WillCommit()); |
- DoSuccessfulCommit(); |
- ASSERT_EQ(1U, GetNumCommitMessagesOnServer()); |
- EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size()); |
- ASSERT_TRUE(HasCommitEntityOnServer("tag1")); |
- const sync_pb::SyncEntity& tag1_entity = |
- GetLatestCommitEntityOnServer("tag1"); |
- |
- // Commit the second of two entities. |
- CommitRequest("tag2", "value2"); |
- EXPECT_EQ(2, GetNumCommitNudges()); |
- ASSERT_TRUE(WillCommit()); |
- DoSuccessfulCommit(); |
- ASSERT_EQ(2U, GetNumCommitMessagesOnServer()); |
- EXPECT_EQ(1, GetNthCommitMessageOnServer(1).commit().entries_size()); |
- ASSERT_TRUE(HasCommitEntityOnServer("tag2")); |
- const sync_pb::SyncEntity& tag2_entity = |
- GetLatestCommitEntityOnServer("tag2"); |
- |
- EXPECT_FALSE(WillCommit()); |
- |
- // The IDs assigned by the |worker_| should be unique. |
- EXPECT_NE(tag1_entity.id_string(), tag2_entity.id_string()); |
- |
- // Check that the committed specifics values are sane. |
- EXPECT_EQ(tag1_entity.specifics().preference().value(), "value1"); |
- EXPECT_EQ(tag2_entity.specifics().preference().value(), "value2"); |
- |
- // There should have been two separate commit responses sent to the model |
- // thread. They should be uninteresting, so we don't bother inspecting them. |
- EXPECT_EQ(2U, GetNumModelThreadCommitResponses()); |
-} |
- |
-// Test normal update receipt code path. |
-TEST_F(ModelTypeWorkerTest, ReceiveUpdates) { |
- NormalInitialize(); |
- |
- const std::string& tag_hash = GenerateTagHash("tag1"); |
- |
- TriggerUpdateFromServer(10, "tag1", "value1"); |
- |
- ASSERT_EQ(1U, GetNumModelThreadUpdateResponses()); |
- UpdateResponseDataList updates_list = GetNthModelThreadUpdateResponse(0); |
- ASSERT_EQ(1U, updates_list.size()); |
- |
- ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1")); |
- UpdateResponseData update = GetUpdateResponseOnModelThread("tag1"); |
- const EntityData& entity = update.entity.value(); |
- |
- EXPECT_FALSE(entity.id.empty()); |
- EXPECT_EQ(tag_hash, entity.client_tag_hash); |
- EXPECT_LT(0, update.response_version); |
- EXPECT_FALSE(entity.creation_time.is_null()); |
- EXPECT_FALSE(entity.modification_time.is_null()); |
- EXPECT_FALSE(entity.non_unique_name.empty()); |
- EXPECT_FALSE(entity.is_deleted()); |
- EXPECT_EQ("tag1", entity.specifics.preference().name()); |
- EXPECT_EQ("value1", entity.specifics.preference().value()); |
-} |
- |
-// Test that an update download coming in multiple parts gets accumulated into |
-// one call to the processor. |
-TEST_F(ModelTypeWorkerTest, ReceiveMultiPartUpdates) { |
- NormalInitialize(); |
- |
- // A partial update response doesn't pass anything to the processor. |
- TriggerPartialUpdateFromServer(10, "tag1", "value1"); |
- ASSERT_EQ(0U, GetNumModelThreadUpdateResponses()); |
- |
- // Trigger the completion of the update. |
- TriggerUpdateFromServer(10, "tag2", "value2"); |
- |
- // Processor received exactly one update with entities in the right order. |
- ASSERT_EQ(1U, GetNumModelThreadUpdateResponses()); |
- UpdateResponseDataList updates = GetNthModelThreadUpdateResponse(0); |
- ASSERT_EQ(2U, updates.size()); |
- EXPECT_EQ(GenerateTagHash("tag1"), updates[0].entity->client_tag_hash); |
- EXPECT_EQ(GenerateTagHash("tag2"), updates[1].entity->client_tag_hash); |
- |
- // A subsequent update doesn't pass the same entities again. |
- TriggerUpdateFromServer(10, "tag3", "value3"); |
- ASSERT_EQ(2U, GetNumModelThreadUpdateResponses()); |
- updates = GetNthModelThreadUpdateResponse(1); |
- ASSERT_EQ(1U, updates.size()); |
- EXPECT_EQ(GenerateTagHash("tag3"), updates[0].entity->client_tag_hash); |
-} |
- |
-// Test commit of encrypted updates. |
-TEST_F(ModelTypeWorkerTest, EncryptedCommit) { |
- NormalInitialize(); |
- |
- ASSERT_EQ(0U, GetNumModelThreadUpdateResponses()); |
- |
- NewForeignEncryptionKey(); |
- UpdateLocalCryptographer(); |
- |
- ASSERT_EQ(1U, GetNumModelThreadUpdateResponses()); |
- EXPECT_EQ(GetLocalCryptographerKeyName(), |
- GetNthModelThreadUpdateState(0).encryption_key_name()); |
- |
- // Normal commit request stuff. |
- CommitRequest("tag1", "value1"); |
- DoSuccessfulCommit(); |
- ASSERT_EQ(1U, GetNumCommitMessagesOnServer()); |
- EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size()); |
- ASSERT_TRUE(HasCommitEntityOnServer("tag1")); |
- const sync_pb::SyncEntity& tag1_entity = |
- GetLatestCommitEntityOnServer("tag1"); |
- |
- EXPECT_TRUE(tag1_entity.specifics().has_encrypted()); |
- |
- // The title should be overwritten. |
- EXPECT_EQ(tag1_entity.name(), "encrypted"); |
- |
- // The type should be set, but there should be no non-encrypted contents. |
- EXPECT_TRUE(tag1_entity.specifics().has_preference()); |
- EXPECT_FALSE(tag1_entity.specifics().preference().has_name()); |
- EXPECT_FALSE(tag1_entity.specifics().preference().has_value()); |
-} |
- |
-// Test items are not committed when encryption is required but unavailable. |
-TEST_F(ModelTypeWorkerTest, EncryptionBlocksCommits) { |
- NormalInitialize(); |
- |
- CommitRequest("tag1", "value1"); |
- EXPECT_TRUE(WillCommit()); |
- |
- // We know encryption is in use on this account, but don't have the necessary |
- // encryption keys. The worker should refuse to commit. |
- NewForeignEncryptionKey(); |
- EXPECT_FALSE(WillCommit()); |
- |
- // Once the cryptographer is returned to a normal state, we should be able to |
- // commit again. |
- EXPECT_EQ(1, GetNumCommitNudges()); |
- UpdateLocalCryptographer(); |
- EXPECT_EQ(2, GetNumCommitNudges()); |
- EXPECT_TRUE(WillCommit()); |
- |
- // Verify the committed entity was properly encrypted. |
- DoSuccessfulCommit(); |
- ASSERT_EQ(1U, GetNumCommitMessagesOnServer()); |
- EXPECT_EQ(1, GetNthCommitMessageOnServer(0).commit().entries_size()); |
- ASSERT_TRUE(HasCommitEntityOnServer("tag1")); |
- const sync_pb::SyncEntity& tag1_entity = |
- GetLatestCommitEntityOnServer("tag1"); |
- EXPECT_TRUE(tag1_entity.specifics().has_encrypted()); |
- EXPECT_EQ(tag1_entity.name(), "encrypted"); |
- EXPECT_TRUE(tag1_entity.specifics().has_preference()); |
- EXPECT_FALSE(tag1_entity.specifics().preference().has_name()); |
- EXPECT_FALSE(tag1_entity.specifics().preference().has_value()); |
-} |
- |
-// Test the receipt of decryptable entities. |
-TEST_F(ModelTypeWorkerTest, ReceiveDecryptableEntities) { |
- NormalInitialize(); |
- |
- // Create a new Nigori and allow the cryptographer to decrypt it. |
- NewForeignEncryptionKey(); |
- UpdateLocalCryptographer(); |
- |
- // First, receive an unencrypted entry. |
- TriggerUpdateFromServer(10, "tag1", "value1"); |
- |
- // Test some basic properties regarding the update. |
- ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1")); |
- UpdateResponseData update1 = GetUpdateResponseOnModelThread("tag1"); |
- EXPECT_EQ("tag1", update1.entity->specifics.preference().name()); |
- EXPECT_EQ("value1", update1.entity->specifics.preference().value()); |
- EXPECT_TRUE(update1.encryption_key_name.empty()); |
- |
- // Set received updates to be encrypted using the new nigori. |
- SetUpdateEncryptionFilter(1); |
- |
- // This next update will be encrypted. |
- TriggerUpdateFromServer(10, "tag2", "value2"); |
- |
- // Test its basic features and the value of encryption_key_name. |
- ASSERT_TRUE(HasUpdateResponseOnModelThread("tag2")); |
- UpdateResponseData update2 = GetUpdateResponseOnModelThread("tag2"); |
- EXPECT_EQ("tag2", update2.entity->specifics.preference().name()); |
- EXPECT_EQ("value2", update2.entity->specifics.preference().value()); |
- EXPECT_FALSE(update2.encryption_key_name.empty()); |
-} |
- |
-// Test initializing a CommitQueue with a cryptographer at startup. |
-TEST_F(ModelTypeWorkerTest, InitializeWithCryptographer) { |
- // Set up some encryption state. |
- NewForeignEncryptionKey(); |
- UpdateLocalCryptographer(); |
- |
- // Then initialize. |
- NormalInitialize(); |
- |
- // The worker should tell the model thread about encryption as soon as |
- // possible, so that it will have the chance to re-encrypt local data if |
- // necessary. |
- ASSERT_EQ(1U, GetNumModelThreadUpdateResponses()); |
- EXPECT_EQ(GetLocalCryptographerKeyName(), |
- GetNthModelThreadUpdateState(0).encryption_key_name()); |
-} |
- |
-// Receive updates that are initially undecryptable, then ensure they get |
-// delivered to the model thread when decryption becomes possible. |
-TEST_F(ModelTypeWorkerTest, ReceiveUndecryptableEntries) { |
- NormalInitialize(); |
- |
- // Receive a new foreign encryption key that we can't decrypt. |
- NewForeignEncryptionKey(); |
- |
- // Receive an encrypted with that new key, which we can't access. |
- SetUpdateEncryptionFilter(1); |
- TriggerUpdateFromServer(10, "tag1", "value1"); |
- |
- // 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, GetNumModelThreadUpdateResponses()); |
- UpdateResponseDataList updates_list = GetNthModelThreadUpdateResponse(0); |
- EXPECT_EQ(0U, updates_list.size()); |
- |
- // The update will be delivered as soon as decryption becomes possible. |
- UpdateLocalCryptographer(); |
- ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1")); |
- UpdateResponseData update = GetUpdateResponseOnModelThread("tag1"); |
- EXPECT_EQ("tag1", update.entity->specifics.preference().name()); |
- EXPECT_EQ("value1", update.entity->specifics.preference().value()); |
- EXPECT_FALSE(update.encryption_key_name.empty()); |
-} |
- |
-// Ensure that even encrypted updates can cause conflicts. |
-TEST_F(ModelTypeWorkerTest, EncryptedUpdateOverridesPendingCommit) { |
- NormalInitialize(); |
- |
- // Prepeare to commit an item. |
- CommitRequest("tag1", "value1"); |
- EXPECT_TRUE(WillCommit()); |
- |
- // Receive an encrypted update for that item. |
- SetUpdateEncryptionFilter(1); |
- TriggerUpdateFromServer(10, "tag1", "value1"); |
- |
- // The pending commit state should be cleared. |
- EXPECT_FALSE(WillCommit()); |
- |
- // The encrypted update will be delivered to the model thread. |
- ASSERT_EQ(1U, GetNumModelThreadUpdateResponses()); |
- UpdateResponseDataList updates_list = GetNthModelThreadUpdateResponse(0); |
- EXPECT_EQ(0U, updates_list.size()); |
-} |
- |
-// Test decryption of pending updates saved across a restart. |
-TEST_F(ModelTypeWorkerTest, RestorePendingEntries) { |
- // Create a fake pending update. |
- EntityData entity; |
- entity.client_tag_hash = GenerateTagHash("tag1"); |
- entity.id = "SomeID"; |
- entity.creation_time = |
- base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(10); |
- entity.modification_time = |
- base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(11); |
- entity.non_unique_name = "encrypted"; |
- entity.specifics = GenerateSpecifics("tag1", "value1"); |
- EncryptUpdate(GetNthKeyParams(1), &(entity.specifics)); |
- |
- UpdateResponseData update; |
- update.entity = entity.PassToPtr(); |
- update.response_version = 100; |
- |
- // Inject the update during CommitQueue initialization. |
- UpdateResponseDataList saved_pending_updates; |
- saved_pending_updates.push_back(update); |
- InitializeWithPendingUpdates(saved_pending_updates); |
- |
- // Update will be undecryptable at first. |
- EXPECT_EQ(0U, GetNumModelThreadUpdateResponses()); |
- ASSERT_FALSE(HasUpdateResponseOnModelThread("tag1")); |
- |
- // Update the cryptographer so it can decrypt that update. |
- NewForeignEncryptionKey(); |
- UpdateLocalCryptographer(); |
- |
- // Verify the item gets decrypted and sent back to the model thread. |
- // TODO(maxbogue): crbug.com/529498: Uncomment when pending updates are |
- // handled by the worker again. |
- // ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1")); |
-} |
- |
-// Test decryption of pending updates saved across a restart. This test |
-// differs from the previous one in that the restored updates can be decrypted |
-// immediately after the CommitQueue is constructed. |
-TEST_F(ModelTypeWorkerTest, RestoreApplicableEntries) { |
- // Update the cryptographer so it can decrypt that update. |
- NewForeignEncryptionKey(); |
- UpdateLocalCryptographer(); |
- |
- // Create a fake pending update. |
- EntityData entity; |
- entity.client_tag_hash = GenerateTagHash("tag1"); |
- entity.id = "SomeID"; |
- entity.creation_time = |
- base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(10); |
- entity.modification_time = |
- base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(11); |
- entity.non_unique_name = "encrypted"; |
- |
- entity.specifics = GenerateSpecifics("tag1", "value1"); |
- EncryptUpdate(GetNthKeyParams(1), &(entity.specifics)); |
- |
- UpdateResponseData update; |
- update.entity = entity.PassToPtr(); |
- update.response_version = 100; |
- |
- // Inject the update during CommitQueue initialization. |
- UpdateResponseDataList saved_pending_updates; |
- saved_pending_updates.push_back(update); |
- InitializeWithPendingUpdates(saved_pending_updates); |
- |
- // Verify the item gets decrypted and sent back to the model thread. |
- // TODO(maxbogue): crbug.com/529498: Uncomment when pending updates are |
- // handled by the worker again. |
- // ASSERT_TRUE(HasUpdateResponseOnModelThread("tag1")); |
-} |
- |
-// Test that undecryptable updates provide sufficient reason to not commit. |
-// |
-// This should be rare in practice. Usually the cryptographer will be in an |
-// unusable state when we receive undecryptable updates, and that alone will be |
-// enough to prevent all commits. |
-TEST_F(ModelTypeWorkerTest, CommitBlockedByPending) { |
- NormalInitialize(); |
- |
- // Prepeare to commit an item. |
- CommitRequest("tag1", "value1"); |
- EXPECT_TRUE(WillCommit()); |
- |
- // Receive an encrypted update for that item. |
- SetUpdateEncryptionFilter(1); |
- TriggerUpdateFromServer(10, "tag1", "value1"); |
- |
- // The pending commit state should be cleared. |
- EXPECT_FALSE(WillCommit()); |
- |
- // The pending update will be delivered to the model thread. |
- HasUpdateResponseOnModelThread("tag1"); |
- |
- // Pretend the update arrived too late to prevent another commit request. |
- CommitRequest("tag1", "value2"); |
- |
- EXPECT_FALSE(WillCommit()); |
-} |
- |
-// Verify that corrupted encrypted updates don't cause crashes. |
-TEST_F(ModelTypeWorkerTest, ReceiveCorruptEncryption) { |
- // Initialize the worker with basic encryption state. |
- NormalInitialize(); |
- NewForeignEncryptionKey(); |
- UpdateLocalCryptographer(); |
- |
- // Manually create an update. |
- sync_pb::SyncEntity entity; |
- entity.set_client_defined_unique_tag(GenerateTagHash("tag1")); |
- entity.set_id_string("SomeID"); |
- entity.set_version(1); |
- entity.set_ctime(1000); |
- entity.set_mtime(1001); |
- entity.set_name("encrypted"); |
- entity.set_deleted(false); |
- |
- // Encrypt it. |
- entity.mutable_specifics()->CopyFrom(GenerateSpecifics("tag1", "value1")); |
- EncryptUpdate(GetNthKeyParams(1), entity.mutable_specifics()); |
- |
- // Replace a few bytes to corrupt it. |
- entity.mutable_specifics()->mutable_encrypted()->mutable_blob()->replace( |
- 0, 4, "xyz!"); |
- |
- SyncEntityList entity_list; |
- entity_list.push_back(&entity); |
- |
- // If a corrupt update could trigger a crash, this is where it would happen. |
- DeliverRawUpdates(entity_list); |
- |
- EXPECT_FALSE(HasUpdateResponseOnModelThread("tag1")); |
- |
- // Deliver a non-corrupt update to see if the everything still works. |
- SetUpdateEncryptionFilter(1); |
- TriggerUpdateFromServer(10, "tag1", "value1"); |
- EXPECT_TRUE(HasUpdateResponseOnModelThread("tag1")); |
-} |
- |
-// Test that processor has been disconnected from Sync when worker got |
-// disconnected. |
-TEST_F(ModelTypeWorkerTest, DisconnectProcessorFromSyncTest) { |
- // Initialize the worker with basic state. |
- NormalInitialize(); |
- EXPECT_FALSE(IsProcessorDisconnected()); |
- ResetWorker(); |
- EXPECT_TRUE(IsProcessorDisconnected()); |
-} |
- |
-} // namespace syncer_v2 |