| // Copyright 2012 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/engine/sync_backend_registrar.h" |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/run_loop.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "base/threading/thread.h" |
| #include "components/sync/engine/passive_model_worker.h" |
| #include "components/sync/engine/sequenced_model_worker.h" |
| #include "components/sync/model/change_processor_mock.h" |
| #include "components/sync/syncable/test_user_share.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace syncer { |
| |
| namespace { |
| |
| using ::testing::_; |
| using ::testing::InSequence; |
| using ::testing::Return; |
| using ::testing::StrictMock; |
| |
| class SyncBackendRegistrarTest : public testing::Test { |
| public: |
| SyncBackendRegistrarTest() |
| : db_thread_("DBThreadForTest"), |
| file_thread_("FileThreadForTest"), |
| sync_thread_("SyncThreadForTest") {} |
| |
| void SetUp() override { |
| db_thread_.StartAndWaitForTesting(); |
| file_thread_.StartAndWaitForTesting(); |
| sync_thread_.StartAndWaitForTesting(); |
| test_user_share_.SetUp(); |
| registrar_ = std::make_unique<SyncBackendRegistrar>( |
| "test", base::Bind(&SyncBackendRegistrarTest::CreateModelWorkerForGroup, |
| base::Unretained(this))); |
| } |
| |
| void TearDown() override { |
| registrar_->RequestWorkerStopOnUIThread(); |
| test_user_share_.TearDown(); |
| sync_thread_.task_runner()->DeleteSoon(FROM_HERE, registrar_.release()); |
| sync_thread_.FlushForTesting(); |
| } |
| |
| void TriggerChanges(ModelType type) { |
| registrar_->OnChangesApplied(type, 0, nullptr, ImmutableChangeRecordList()); |
| registrar_->OnChangesComplete(type); |
| } |
| |
| void ExpectRoutingInfo(const ModelSafeRoutingInfo& expected_routing_info) { |
| ModelSafeRoutingInfo actual_routing_info; |
| registrar_->GetModelSafeRoutingInfo(&actual_routing_info); |
| EXPECT_EQ(expected_routing_info, actual_routing_info); |
| } |
| |
| void ExpectHasProcessorsForTypes(ModelTypeSet types) { |
| for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) { |
| ModelType model_type = ModelTypeFromInt(i); |
| EXPECT_EQ(types.Has(model_type), |
| registrar_->IsTypeActivatedForTest(model_type)); |
| } |
| } |
| |
| size_t GetWorkersSize() { |
| std::vector<scoped_refptr<ModelSafeWorker>> workers; |
| registrar_->GetWorkers(&workers); |
| return workers.size(); |
| } |
| |
| // Part of the ActivateDeactivateNonUIDataType test below. |
| void TestNonUIDataTypeActivationAsync(ChangeProcessor* processor, |
| base::WaitableEvent* done) { |
| registrar_->ActivateDataType(AUTOFILL, GROUP_DB, processor, user_share()); |
| ExpectRoutingInfo({{AUTOFILL, GROUP_DB}}); |
| ExpectHasProcessorsForTypes(ModelTypeSet(AUTOFILL)); |
| TriggerChanges(AUTOFILL); |
| done->Signal(); |
| } |
| |
| SyncBackendRegistrar* registrar() { return registrar_.get(); } |
| UserShare* user_share() { return test_user_share_.user_share(); } |
| scoped_refptr<base::SequencedTaskRunner> db_task_runner() { |
| return db_thread_.task_runner(); |
| } |
| |
| private: |
| scoped_refptr<ModelSafeWorker> CreateModelWorkerForGroup( |
| ModelSafeGroup group) { |
| switch (group) { |
| case GROUP_UI: |
| return new SequencedModelWorker( |
| task_environment_.GetMainThreadTaskRunner(), group); |
| case GROUP_DB: |
| return new SequencedModelWorker(db_thread_.task_runner(), group); |
| case GROUP_FILE: |
| return new SequencedModelWorker(file_thread_.task_runner(), group); |
| case GROUP_PASSIVE: |
| return new PassiveModelWorker(); |
| default: |
| return nullptr; |
| } |
| } |
| |
| base::test::ScopedTaskEnvironment task_environment_; |
| base::Thread db_thread_; |
| base::Thread file_thread_; |
| base::Thread sync_thread_; |
| |
| TestUserShare test_user_share_; |
| std::unique_ptr<SyncBackendRegistrar> registrar_; |
| }; |
| |
| TEST_F(SyncBackendRegistrarTest, ConstructorEmpty) { |
| registrar()->SetInitialTypes(ModelTypeSet()); |
| EXPECT_FALSE(registrar()->IsNigoriEnabled()); |
| EXPECT_EQ(4u, GetWorkersSize()); |
| ExpectRoutingInfo(ModelSafeRoutingInfo()); |
| ExpectHasProcessorsForTypes(ModelTypeSet()); |
| } |
| |
| TEST_F(SyncBackendRegistrarTest, ConstructorNonEmpty) { |
| registrar()->RegisterNonBlockingType(BOOKMARKS); |
| registrar()->SetInitialTypes(ModelTypeSet(BOOKMARKS, NIGORI, PASSWORDS)); |
| EXPECT_TRUE(registrar()->IsNigoriEnabled()); |
| EXPECT_EQ(4u, GetWorkersSize()); |
| EXPECT_EQ(ModelTypeSet(NIGORI), registrar()->GetLastConfiguredTypes()); |
| // Bookmarks dropped because it is nonblocking. |
| // Passwords dropped because of no password store. |
| ExpectRoutingInfo({{NIGORI, GROUP_PASSIVE}}); |
| ExpectHasProcessorsForTypes(ModelTypeSet()); |
| } |
| |
| TEST_F(SyncBackendRegistrarTest, ConstructorNonEmptyReversedInitialization) { |
| // The blocking types get to set initial types before NonBlocking types here. |
| registrar()->SetInitialTypes(ModelTypeSet(BOOKMARKS, NIGORI, PASSWORDS)); |
| registrar()->RegisterNonBlockingType(BOOKMARKS); |
| EXPECT_TRUE(registrar()->IsNigoriEnabled()); |
| EXPECT_EQ(4u, GetWorkersSize()); |
| EXPECT_EQ(ModelTypeSet(NIGORI), registrar()->GetLastConfiguredTypes()); |
| // Bookmarks dropped because it is nonblocking. |
| // Passwords dropped because of no password store. |
| ExpectRoutingInfo({{NIGORI, GROUP_PASSIVE}}); |
| ExpectHasProcessorsForTypes(ModelTypeSet()); |
| } |
| |
| TEST_F(SyncBackendRegistrarTest, ConfigureDataTypes) { |
| registrar()->RegisterNonBlockingType(BOOKMARKS); |
| registrar()->SetInitialTypes(ModelTypeSet()); |
| |
| // Add. |
| const ModelTypeSet types1(BOOKMARKS, NIGORI, AUTOFILL); |
| EXPECT_EQ(types1, registrar()->ConfigureDataTypes(types1, ModelTypeSet())); |
| ExpectRoutingInfo({{BOOKMARKS, GROUP_NON_BLOCKING}, |
| {NIGORI, GROUP_PASSIVE}, |
| {AUTOFILL, GROUP_PASSIVE}}); |
| ExpectHasProcessorsForTypes(ModelTypeSet()); |
| EXPECT_EQ(types1, registrar()->GetLastConfiguredTypes()); |
| |
| // Add and remove. |
| const ModelTypeSet types2(PREFERENCES, THEMES); |
| EXPECT_EQ(types2, registrar()->ConfigureDataTypes(types2, types1)); |
| |
| ExpectRoutingInfo({{PREFERENCES, GROUP_PASSIVE}, {THEMES, GROUP_PASSIVE}}); |
| ExpectHasProcessorsForTypes(ModelTypeSet()); |
| EXPECT_EQ(types2, registrar()->GetLastConfiguredTypes()); |
| |
| // Remove. |
| EXPECT_TRUE(registrar()->ConfigureDataTypes(ModelTypeSet(), types2).Empty()); |
| ExpectRoutingInfo(ModelSafeRoutingInfo()); |
| ExpectHasProcessorsForTypes(ModelTypeSet()); |
| EXPECT_EQ(ModelTypeSet(), registrar()->GetLastConfiguredTypes()); |
| } |
| |
| TEST_F(SyncBackendRegistrarTest, ActivateDeactivateUIDataType) { |
| InSequence in_sequence; |
| registrar()->SetInitialTypes(ModelTypeSet()); |
| |
| // Should do nothing. |
| TriggerChanges(BOOKMARKS); |
| |
| StrictMock<ChangeProcessorMock> change_processor_mock; |
| EXPECT_CALL(change_processor_mock, StartImpl()); |
| EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(change_processor_mock, ApplyChangesFromSyncModel(nullptr, _, _)); |
| EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(change_processor_mock, CommitChangesFromSyncModel()); |
| EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(false)); |
| |
| const ModelTypeSet types(BOOKMARKS); |
| EXPECT_EQ(types, registrar()->ConfigureDataTypes(types, ModelTypeSet())); |
| registrar()->ActivateDataType(BOOKMARKS, GROUP_UI, &change_processor_mock, |
| user_share()); |
| ExpectRoutingInfo({{BOOKMARKS, GROUP_UI}}); |
| ExpectHasProcessorsForTypes(types); |
| |
| TriggerChanges(BOOKMARKS); |
| |
| registrar()->DeactivateDataType(BOOKMARKS); |
| ExpectRoutingInfo(ModelSafeRoutingInfo()); |
| ExpectHasProcessorsForTypes(ModelTypeSet()); |
| |
| // Should do nothing. |
| TriggerChanges(BOOKMARKS); |
| } |
| |
| TEST_F(SyncBackendRegistrarTest, ActivateDeactivateNonUIDataType) { |
| InSequence in_sequence; |
| registrar()->SetInitialTypes(ModelTypeSet()); |
| |
| // Should do nothing. |
| TriggerChanges(AUTOFILL); |
| |
| StrictMock<ChangeProcessorMock> change_processor_mock; |
| EXPECT_CALL(change_processor_mock, StartImpl()); |
| EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(change_processor_mock, ApplyChangesFromSyncModel(nullptr, _, _)); |
| EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(true)); |
| EXPECT_CALL(change_processor_mock, CommitChangesFromSyncModel()); |
| EXPECT_CALL(change_processor_mock, IsRunning()).WillRepeatedly(Return(false)); |
| |
| const ModelTypeSet types(AUTOFILL); |
| EXPECT_EQ(types, registrar()->ConfigureDataTypes(types, ModelTypeSet())); |
| |
| base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| db_task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &SyncBackendRegistrarTest::TestNonUIDataTypeActivationAsync, |
| base::Unretained(this), &change_processor_mock, &done)); |
| done.Wait(); |
| |
| registrar()->DeactivateDataType(AUTOFILL); |
| ExpectRoutingInfo(ModelSafeRoutingInfo()); |
| ExpectHasProcessorsForTypes(ModelTypeSet()); |
| |
| // Should do nothing. |
| TriggerChanges(AUTOFILL); |
| } |
| |
| // Tests that registration and configuration of non-blocking data types is |
| // handled correctly in SyncBackendRegistrar. |
| TEST_F(SyncBackendRegistrarTest, ConfigureNonBlockingDataType) { |
| registrar()->RegisterNonBlockingType(AUTOFILL); |
| registrar()->RegisterNonBlockingType(BOOKMARKS); |
| |
| ExpectRoutingInfo(ModelSafeRoutingInfo()); |
| // Simulate that initial sync was already done for AUTOFILL. |
| registrar()->AddRestoredNonBlockingType(AUTOFILL); |
| // It should be added to routing info and set of configured types. |
| EXPECT_EQ(ModelTypeSet(AUTOFILL), registrar()->GetLastConfiguredTypes()); |
| ExpectRoutingInfo({{AUTOFILL, GROUP_NON_BLOCKING}}); |
| |
| // Configure two non-blocking types. Initial sync wasn't done for BOOKMARKS so |
| // it should be included in types to be downloaded. |
| ModelTypeSet types_to_add(AUTOFILL, BOOKMARKS); |
| ModelTypeSet newly_added_types = |
| registrar()->ConfigureDataTypes(types_to_add, ModelTypeSet()); |
| EXPECT_EQ(ModelTypeSet(BOOKMARKS), newly_added_types); |
| EXPECT_EQ(types_to_add, registrar()->GetLastConfiguredTypes()); |
| ExpectRoutingInfo( |
| {{AUTOFILL, GROUP_NON_BLOCKING}, {BOOKMARKS, GROUP_NON_BLOCKING}}); |
| } |
| |
| } // namespace |
| |
| } // namespace syncer |