| Index: components/sync/driver/model_type_controller_unittest.cc
|
| diff --git a/components/sync/driver/model_type_controller_unittest.cc b/components/sync/driver/model_type_controller_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2c3c55daa0a6d1c620be4335f3219ea9789123b3
|
| --- /dev/null
|
| +++ b/components/sync/driver/model_type_controller_unittest.cc
|
| @@ -0,0 +1,401 @@
|
| +// Copyright 2016 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 "components/sync/driver/model_type_controller.h"
|
| +
|
| +#include <utility>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/memory/weak_ptr.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/sequenced_task_runner.h"
|
| +#include "base/test/test_simple_task_runner.h"
|
| +#include "base/threading/thread.h"
|
| +#include "base/threading/thread_task_runner_handle.h"
|
| +#include "components/sync/api/fake_model_type_change_processor.h"
|
| +#include "components/sync/api/stub_model_type_service.h"
|
| +#include "components/sync/core/activation_context.h"
|
| +#include "components/sync/core/model_type_processor_proxy.h"
|
| +#include "components/sync/core/shared_model_type_processor.h"
|
| +#include "components/sync/core/test/fake_model_type_processor.h"
|
| +#include "components/sync/driver/backend_data_type_configurer.h"
|
| +#include "components/sync/driver/fake_sync_client.h"
|
| +#include "components/sync/engine/commit_queue.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace syncer {
|
| +
|
| +namespace {
|
| +
|
| +const ModelType kTestModelType = AUTOFILL;
|
| +
|
| +// A change processor for testing that connects using a thread-jumping proxy,
|
| +// tracks connected state, and counts DisableSync calls.
|
| +class TestModelTypeProcessor : public FakeModelTypeChangeProcessor,
|
| + public FakeModelTypeProcessor {
|
| + public:
|
| + explicit TestModelTypeProcessor(int* disable_sync_call_count)
|
| + : disable_sync_call_count_(disable_sync_call_count),
|
| + weak_factory_(this) {}
|
| +
|
| + // ModelTypeChangeProcessor implementation.
|
| + void OnSyncStarting(std::unique_ptr<DataTypeErrorHandler> error_handler,
|
| + const StartCallback& callback) override {
|
| + std::unique_ptr<ActivationContext> activation_context =
|
| + base::MakeUnique<ActivationContext>();
|
| + activation_context->type_processor =
|
| + base::MakeUnique<ModelTypeProcessorProxy>(
|
| + weak_factory_.GetWeakPtr(), base::ThreadTaskRunnerHandle::Get());
|
| + callback.Run(SyncError(), std::move(activation_context));
|
| + }
|
| + void DisableSync() override { (*disable_sync_call_count_)++; }
|
| +
|
| + // ModelTypeProcessor implementation.
|
| + void ConnectSync(std::unique_ptr<CommitQueue> commit_queue) override {
|
| + is_connected_ = true;
|
| + }
|
| + void DisconnectSync() override { is_connected_ = false; }
|
| +
|
| + bool is_connected() { return is_connected_; }
|
| +
|
| + private:
|
| + bool is_connected_ = false;
|
| + int* disable_sync_call_count_;
|
| + base::WeakPtrFactory<TestModelTypeProcessor> weak_factory_;
|
| + DISALLOW_COPY_AND_ASSIGN(TestModelTypeProcessor);
|
| +};
|
| +
|
| +// A BackendDataTypeConfigurer that just connects USS types.
|
| +class TestBackendDataTypeConfigurer : public BackendDataTypeConfigurer {
|
| + public:
|
| + TestBackendDataTypeConfigurer() {}
|
| + ~TestBackendDataTypeConfigurer() override {}
|
| +
|
| + ModelTypeSet ConfigureDataTypes(
|
| + ConfigureReason reason,
|
| + const DataTypeConfigStateMap& config_state_map,
|
| + const base::Callback<void(ModelTypeSet, ModelTypeSet)>& ready_task,
|
| + const base::Callback<void()>& retry_callback) override {
|
| + NOTREACHED() << "Not implemented.";
|
| + return ModelTypeSet();
|
| + }
|
| +
|
| + void ActivateDirectoryDataType(ModelType type,
|
| + ModelSafeGroup group,
|
| + ChangeProcessor* change_processor) override {
|
| + NOTREACHED() << "Not implemented.";
|
| + }
|
| +
|
| + void DeactivateDirectoryDataType(ModelType type) override {
|
| + NOTREACHED() << "Not implemented.";
|
| + }
|
| +
|
| + void ActivateNonBlockingDataType(
|
| + ModelType type,
|
| + std::unique_ptr<ActivationContext> activation_context) override {
|
| + DCHECK_EQ(kTestModelType, type);
|
| + DCHECK(!processor_);
|
| + processor_ = std::move(activation_context->type_processor);
|
| + processor_->ConnectSync(nullptr);
|
| + }
|
| +
|
| + void DeactivateNonBlockingDataType(ModelType type) override {
|
| + DCHECK_EQ(kTestModelType, type);
|
| + DCHECK(processor_);
|
| + processor_->DisconnectSync();
|
| + processor_.reset();
|
| + }
|
| +
|
| + private:
|
| + std::unique_ptr<ModelTypeProcessor> processor_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class ModelTypeControllerTest : public testing::Test, public FakeSyncClient {
|
| + public:
|
| + ModelTypeControllerTest()
|
| + : model_thread_("modelthread"), sync_prefs_(GetPrefService()) {}
|
| +
|
| + void SetUp() override {
|
| + model_thread_.Start();
|
| + InitializeModelTypeService();
|
| + controller_.reset(new ModelTypeController(
|
| + kTestModelType, base::Closure(), this, model_thread_.task_runner()));
|
| + }
|
| +
|
| + void TearDown() override {
|
| + ClearModelTypeService();
|
| + PumpUIThread();
|
| + }
|
| +
|
| + base::WeakPtr<ModelTypeService> GetModelTypeServiceForType(
|
| + ModelType type) override {
|
| + return service_->AsWeakPtr();
|
| + }
|
| +
|
| + void LoadModels() {
|
| + controller_->LoadModels(base::Bind(&ModelTypeControllerTest::LoadModelsDone,
|
| + base::Unretained(this)));
|
| + }
|
| +
|
| + void RegisterWithBackend() { controller_->RegisterWithBackend(&configurer_); }
|
| +
|
| + void StartAssociating() {
|
| + controller_->StartAssociating(base::Bind(
|
| + &ModelTypeControllerTest::AssociationDone, base::Unretained(this)));
|
| + // The callback is expected to be promptly called.
|
| + EXPECT_TRUE(association_callback_called_);
|
| + }
|
| +
|
| + void DeactivateDataTypeAndStop() {
|
| + controller_->DeactivateDataType(&configurer_);
|
| + controller_->Stop();
|
| + }
|
| +
|
| + // These threads can ping-pong for a bit so we run the model thread twice.
|
| + void RunAllTasks() {
|
| + PumpModelThread();
|
| + PumpUIThread();
|
| + PumpModelThread();
|
| + }
|
| +
|
| + // Runs any tasks posted on the model thread.
|
| + void PumpModelThread() {
|
| + base::RunLoop run_loop;
|
| + model_thread_.task_runner()->PostTaskAndReply(
|
| + FROM_HERE, base::Bind(&base::DoNothing), run_loop.QuitClosure());
|
| + run_loop.Run();
|
| + }
|
| +
|
| + void ExpectProcessorConnected(bool is_connected) {
|
| + if (model_thread_.task_runner()->BelongsToCurrentThread()) {
|
| + DCHECK(processor_);
|
| + EXPECT_EQ(is_connected, processor_->is_connected());
|
| + } else {
|
| + model_thread_.task_runner()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ModelTypeControllerTest::ExpectProcessorConnected,
|
| + base::Unretained(this), is_connected));
|
| + PumpModelThread();
|
| + }
|
| + }
|
| +
|
| + void ExpectHasChangeProcessor(bool has_processor) {
|
| + if (model_thread_.task_runner()->BelongsToCurrentThread()) {
|
| + DCHECK(service_);
|
| + EXPECT_EQ(has_processor, !!service_->change_processor());
|
| + } else {
|
| + model_thread_.task_runner()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ModelTypeControllerTest::ExpectHasChangeProcessor,
|
| + base::Unretained(this), has_processor));
|
| + PumpModelThread();
|
| + }
|
| + }
|
| +
|
| + SyncPrefs* sync_prefs() { return &sync_prefs_; }
|
| + DataTypeController* controller() { return controller_.get(); }
|
| + int load_models_done_count() { return load_models_done_count_; }
|
| + int disable_sync_call_count() { return disable_sync_call_count_; }
|
| + SyncError load_models_last_error() { return load_models_last_error_; }
|
| +
|
| + private:
|
| + // Runs any tasks posted on the UI thread.
|
| + void PumpUIThread() { base::RunLoop().RunUntilIdle(); }
|
| +
|
| + void LoadModelsDone(ModelType type, const SyncError& error) {
|
| + load_models_done_count_++;
|
| + load_models_last_error_ = error;
|
| + }
|
| +
|
| + void AssociationDone(DataTypeController::ConfigureResult result,
|
| + const SyncMergeResult& local_merge_result,
|
| + const SyncMergeResult& syncer_merge_result) {
|
| + EXPECT_FALSE(association_callback_called_);
|
| + EXPECT_EQ(DataTypeController::OK, result);
|
| + association_callback_called_ = true;
|
| + }
|
| +
|
| + std::unique_ptr<ModelTypeChangeProcessor> CreateProcessor(
|
| + ModelType type,
|
| + ModelTypeService* service) {
|
| + std::unique_ptr<TestModelTypeProcessor> processor =
|
| + base::MakeUnique<TestModelTypeProcessor>(&disable_sync_call_count_);
|
| + processor_ = processor.get();
|
| + return std::move(processor);
|
| + }
|
| +
|
| + void InitializeModelTypeService() {
|
| + if (model_thread_.task_runner()->BelongsToCurrentThread()) {
|
| + service_.reset(new StubModelTypeService(base::Bind(
|
| + &ModelTypeControllerTest::CreateProcessor, base::Unretained(this))));
|
| + } else {
|
| + model_thread_.task_runner()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ModelTypeControllerTest::InitializeModelTypeService,
|
| + base::Unretained(this)));
|
| + PumpModelThread();
|
| + }
|
| + }
|
| +
|
| + void ClearModelTypeService() {
|
| + if (model_thread_.task_runner()->BelongsToCurrentThread()) {
|
| + service_.reset();
|
| + } else {
|
| + model_thread_.task_runner()->PostTask(
|
| + FROM_HERE, base::Bind(&ModelTypeControllerTest::ClearModelTypeService,
|
| + base::Unretained(this)));
|
| + PumpModelThread();
|
| + }
|
| + }
|
| +
|
| + int load_models_done_count_ = 0;
|
| + int disable_sync_call_count_ = 0;
|
| + bool association_callback_called_ = false;
|
| + SyncError load_models_last_error_;
|
| +
|
| + base::MessageLoop message_loop_;
|
| + base::Thread model_thread_;
|
| + SyncPrefs sync_prefs_;
|
| + TestBackendDataTypeConfigurer configurer_;
|
| + std::unique_ptr<StubModelTypeService> service_;
|
| + std::unique_ptr<ModelTypeController> controller_;
|
| + TestModelTypeProcessor* processor_;
|
| +};
|
| +
|
| +TEST_F(ModelTypeControllerTest, InitialState) {
|
| + EXPECT_EQ(kTestModelType, controller()->type());
|
| + EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state());
|
| +}
|
| +
|
| +TEST_F(ModelTypeControllerTest, LoadModelsOnBackendThread) {
|
| + LoadModels();
|
| + EXPECT_EQ(DataTypeController::MODEL_STARTING, controller()->state());
|
| + RunAllTasks();
|
| + EXPECT_EQ(DataTypeController::MODEL_LOADED, controller()->state());
|
| + EXPECT_EQ(1, load_models_done_count());
|
| + EXPECT_FALSE(load_models_last_error().IsSet());
|
| + ExpectProcessorConnected(false);
|
| +}
|
| +
|
| +TEST_F(ModelTypeControllerTest, LoadModelsTwice) {
|
| + LoadModels();
|
| + RunAllTasks();
|
| + LoadModels();
|
| + EXPECT_EQ(DataTypeController::MODEL_LOADED, controller()->state());
|
| + // The second LoadModels call should set the error.
|
| + EXPECT_TRUE(load_models_last_error().IsSet());
|
| +}
|
| +
|
| +TEST_F(ModelTypeControllerTest, ActivateDataTypeOnBackendThread) {
|
| + LoadModels();
|
| + RunAllTasks();
|
| + EXPECT_EQ(DataTypeController::MODEL_LOADED, controller()->state());
|
| + RegisterWithBackend();
|
| + ExpectProcessorConnected(true);
|
| +
|
| + StartAssociating();
|
| + EXPECT_EQ(DataTypeController::RUNNING, controller()->state());
|
| +}
|
| +
|
| +TEST_F(ModelTypeControllerTest, Stop) {
|
| + LoadModels();
|
| + RunAllTasks();
|
| + RegisterWithBackend();
|
| + ExpectProcessorConnected(true);
|
| +
|
| + StartAssociating();
|
| +
|
| + DeactivateDataTypeAndStop();
|
| + EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state());
|
| +}
|
| +
|
| +// Test emulates normal browser shutdown. Ensures that DisableSync is not
|
| +// called.
|
| +TEST_F(ModelTypeControllerTest, StopWhenDatatypeEnabled) {
|
| + // Enable datatype through preferences.
|
| + sync_prefs()->SetFirstSetupComplete();
|
| + sync_prefs()->SetKeepEverythingSynced(true);
|
| +
|
| + LoadModels();
|
| + RunAllTasks();
|
| + StartAssociating();
|
| +
|
| + controller()->Stop();
|
| + RunAllTasks();
|
| + EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state());
|
| + // Ensure that DisableSync is not called and service still has valid change
|
| + // processor.
|
| + EXPECT_EQ(0, disable_sync_call_count());
|
| + ExpectHasChangeProcessor(true);
|
| + ExpectProcessorConnected(false);
|
| +}
|
| +
|
| +// Test emulates scenario when user disables datatype. DisableSync should be
|
| +// called.
|
| +TEST_F(ModelTypeControllerTest, StopWhenDatatypeDisabled) {
|
| + // Enable datatype through preferences.
|
| + sync_prefs()->SetFirstSetupComplete();
|
| + sync_prefs()->SetKeepEverythingSynced(true);
|
| + LoadModels();
|
| + RunAllTasks();
|
| + StartAssociating();
|
| +
|
| + // Disable datatype through preferences.
|
| + sync_prefs()->SetKeepEverythingSynced(false);
|
| + sync_prefs()->SetPreferredDataTypes(ModelTypeSet(kTestModelType),
|
| + ModelTypeSet());
|
| +
|
| + controller()->Stop();
|
| + EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state());
|
| + // Ensure that DisableSync is called and change processor is reset.
|
| + PumpModelThread();
|
| + EXPECT_EQ(1, disable_sync_call_count());
|
| + ExpectHasChangeProcessor(false);
|
| +}
|
| +
|
| +// Test emulates disabling sync by signing out. DisableSync should be called.
|
| +TEST_F(ModelTypeControllerTest, StopWithInitialSyncPrefs) {
|
| + // Enable datatype through preferences.
|
| + sync_prefs()->SetFirstSetupComplete();
|
| + sync_prefs()->SetKeepEverythingSynced(true);
|
| + LoadModels();
|
| + RunAllTasks();
|
| + StartAssociating();
|
| +
|
| + // Clearing preferences emulates signing out.
|
| + sync_prefs()->ClearPreferences();
|
| + controller()->Stop();
|
| + EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state());
|
| + // Ensure that DisableSync is called and change processor is reset.
|
| + PumpModelThread();
|
| + EXPECT_EQ(1, disable_sync_call_count());
|
| + ExpectHasChangeProcessor(false);
|
| +}
|
| +
|
| +// Test emulates disabling sync when datatype is not loaded yet. DisableSync
|
| +// should not be called as service is potentially not ready to handle it.
|
| +TEST_F(ModelTypeControllerTest, StopBeforeLoadModels) {
|
| + // Enable datatype through preferences.
|
| + sync_prefs()->SetFirstSetupComplete();
|
| + sync_prefs()->SetKeepEverythingSynced(true);
|
| + EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state());
|
| +
|
| + // Clearing preferences emulates signing out.
|
| + sync_prefs()->ClearPreferences();
|
| + controller()->Stop();
|
| + EXPECT_EQ(DataTypeController::NOT_RUNNING, controller()->state());
|
| + // Ensure that DisableSync is not called.
|
| + EXPECT_EQ(0, disable_sync_call_count());
|
| + // A change processor was never created.
|
| + ExpectHasChangeProcessor(false);
|
| +}
|
| +
|
| +} // namespace syncer
|
|
|